Androboiceってアプリを作ったときのメモ
前回の続き。
で、Androidでどうやって録音するか。
AudioRecordクラスでは前回の音データが取得できる。以下コード。
// サンプルレート 8kHz
int SAMPLE_RATE = 8000;
// フラグ
isRecoding = true;
// プロセスの優先度を上げる
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
// バッファサイズを求める サンプルレート8kHz モノラル 16ビット/サンプル
int audioBuf = AudioRecord.getMinBufferSize(SAMPLE_RATE,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT) * 2;
// レコーダの取得 サンプルレート8kHz モノラル 16ビット/サンプル
AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
SAMPLE_RATE,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
audioBuf);
// バッファ
byte[] buffer = new byte[audioBuf];
// 録音用ファイル取得 SDカード状態チェック略
File recFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/rec.raw");
recFile.createNewFile();
// ファイル書き込み 例外処理略
FileOutputStream out = new FileOutputStream(recFile);
// 録音開始
audioRecord.startRecording();
// フラグが落ちるまでループ 例外処理略
while(isRecoding) {
// バッファを読み込んで書き込む
audioRecord.read(buffer, 0, audioBuf);
}
// 終了処理 例外処理略
audioRecord.stop();
out.close();
out = null;
実際はスレッドなんかで実行してフラグで録音ループを抜ける。これでSDカードにrec.rawってファイルができる。
ファイルの中身はサンプルレート8000Hz、1サンプルあたり16ビット、モノラルのバイトデータ。ヘッダ情報のないWindowsのwavファイルのデータチャンクのところ。これにRIFFとかfmtチャンクを加えればwavファイルの出来上がり。
逆に言うと、サンプルレート8000Hz、1サンプルあたり16ビット、モノラルのwavファイルの先頭からヘッダ情報の40バイトをちぎったのと同じデータなのだ。
んで、1サンプル16ビット=2バイトずつデータが並んでいて、16ビットなので65536段階の強弱の信号が記録されている。
16ビットでサンプルした場合は、符号付き(-32768 ~ +32767)であらわすらしい。 無音は 0。
8ビットでサンプルした場合は、符号なし(0~ +255)無音は128であらわす。
8ビットの場合はそのままJavaのbyte型の変数の配列に1サンプルずつデータが入っているが、
16ビットでサンプルした場合はエンディアンに気をつけないといけない。
取得できるデータはwavファイルと同じリトルエンディアンであるらしく、前8ビットには下の桁、後ろ8ビットには上の桁(2進数的に)
が収まっている。
byte[] buff = new buff[2]; リトルエンディアン 16bitの1 前8ビット 後ろ8ビット 00000001 00000000 // 2進数 0x0100 // 16進数 buff[0] == 0x01 buff[1] == 0x00 リトルエンディアン 16bitの32767 11111111 01111111 // 2進数 0xFF7F // 16進数 buff[0] == 0xFF buff[1] == 0xF7 リトルエンディアン 16bitの-32768 00000000 10000000 // 2進数 0x0080 // 16進数 buff[0] == 0x00 buff[1] == 0x80
このようにバイナリエディタなんかでダンプしただけではパッと見て分かりにくいデータなのだ。
これが分かるまで苦労した。バイナリデータは触ったことなかったし。
Windowsの電卓ソフトの関数電卓モードを活用させてもらった。
んで、上のサンプルで保存したrec.rawファイルにヘッダをつけてwavファイルにする。以下コード
// WAVヘッダをつけて保存
// 各種例外処理略 チェック処理略
public void addWavHeader() {
// 録音したファイル
File recFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/rec.raw");
// WAVファイル
File wavFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/rec.wav");
// ストリーム
FileInputStream in = new FileInputStream(recFile);
FileOutputStream out = new FileOutputStream(wavFile);
// ヘッダ作成 サンプルレート8kHz
byte[] header = createHeader(8000, (int)recFile.length());
// ヘッダの書き出し
out.write(header);
// 録音したファイルのバイトデータ読み込み
int n = 0,offset = 0;
byte[] buffer = new byte[(int)recFile.length()];
while (offset < buffer.length
&& (n = in.read(buffer, offset, buffer.length - offset)) >= 0) {
offset += n;
}
// バイトデータ書き込み
out.write(buffer);
// 終了
in.close();
out.close();
}
// Wavファイルのヘッダを作成する(PCM16ビット モノラル)
// sampleRate サンプルレート
// datasize データサイズ
// これなんかもっとキレイに書けると思うが 。。 Ringroidのソースなんかキレイかも
public static byte[] createHeader(int sampleRate, int datasize) {
byte[] byteRIFF = {'R', 'I', 'F', 'F'};
byte[] byteFilesizeSub8 = intToBytes((datasize + 36)); // ファイルサイズ-8バイト数
byte[] byteWAVE = {'W', 'A', 'V', 'E'};
byte[] byteFMT_ = {'f', 'm', 't', ' '};
byte[] byte16bit = intToBytes(16); // fmtチャンクのバイト数
byte[] byteSamplerate = intToBytes(sampleRate); // サンプルレート
byte[] byteBytesPerSec = intToBytes(sampleRate * 2); // バイト/秒 = サンプルレート x 1チャンネル x 2バイト
byte[] bytePcmMono = {0x01, 0x00, 0x01, 0x00}; // フォーマットID 1 =リニアPCM , チャンネル 1 = モノラル
byte[] byteBlockBit = {0x02, 0x00, 0x10, 0x00}; // ブロックサイズ2バイト サンプルあたりのビット数16ビット
byte[] byteDATA = {'d', 'a', 't', 'a'};
byte[] byteDatasize = intToBytes(datasize); // データサイズ
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
out.write(byteRIFF);
out.write(byteFilesizeSub8);
out.write(byteWAVE);
out.write(byteFMT_);
out.write(byte16bit);
out.write(bytePcmMono);
out.write(byteSamplerate);
out.write(byteBytesPerSec);
out.write(byteBlockBit);
out.write(byteDATA);
out.write(byteDatasize);
} catch (IOException e) {
return out.toByteArray();
}
return out.toByteArray();
}
// int型32ビットデータをリトルエンディアンのバイト配列にする
public static byte[] intToBytes(int value) {
byte[] bt = new byte[4];
bt[0] = (byte)(value & 0x000000ff);
bt[1] = (byte)((value & 0x0000ff00) >> 8);
bt[2] = (byte)((value & 0x00ff0000) >> 16);
bt[3] = (byte)((value & 0xff000000) >> 24);
return bt;
}
これでSDカードにrec.wavってファイルができるはず。あとは標準の音楽ソフトかパソコンで再生すればOK牧場。
WAVファイルを保存してゴニョゴニョできるソフトはそんなに多くないので、うまくやれば人気のアプリになりそう。
3gppとちがって加工しやすいし。3gpのフォーマット読んだだけで心が折れそうになったし・・。
PCでWAVファイルとかゴニョゴニョしてた人たちには大したことないかもしれないが、苦労した。
良い勉強になった。
はて、次はAudioTrackで再生の予定。

