Avatar

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で再生の予定。

Tagged with: , , , .
Avatar

Androboiceってアプリを作ったときのメモ

AndroidではAudioRecordとAudioTrackという方法でマイクの音を音データにしたり、音データをスピーカーに送ることができる。
その前にまず、そもそも音データってなに?ってところから始まる。他のOSとかで音データを触ったことがないから。

音ってのは空気の振動で、コンピュータで音データを扱うということは空気の振動を0と1に置き換えること、0と1の信号を使って空気を振動させることになる。 私の理解では。

大きな音がマイクに入ると、大きな電流がコンピュータに流れ、小さな音だと小さな電流がコンピュータに流れる。

コンピュータは流れてきた電流を一定時間ごとにデータを記録していく。大・小・大・小・中・大・小・・・・などと。

実際には20,000 ・-20,000 ・ 200 ・100 ・ -100 ・・・・みたいな感じで。

記録した音データに何らかの処理をして、マイクの逆パターンでスピーカーに大・小・大・小・中・大・小・・・・と電流を送れば音が出る。

サンプリングレート

一定時間ごとにマイクからの電流を計測して記録する(サンプリングする)けど、どれくらいの間隔で記録するかは『1秒間に何回記録するか「Hz ヘルツ」』であらわす。

音楽CDだと1秒間に44,100回=44100Hz。他にも22,050Hzとか16,000Hzとか11,025Hzとか8,000Hzとかがよく使われるみたい。実際の音波はもっと細かいけど連続毎秒44,100回記録しておけば、それ以上記録してても人間の耳には違いが分からないらしい。

ビット/サンプル  BitPerSample

マイクから流れてくる電流を記録するときどれくらいの細かさで記録するかは16ビットとか8ビットとかがよく使われるらしい。

それぞれ、65,535段階か256段階。16ビットとか8ビットはコンピュータでデータを扱いやすい。音楽CDは16ビットらしい。

だから、モノラルでサンプリングレート44100Hzで16Bit/Sampleで10秒間録音したら、データサイズは

1チャンネル x 44,100回 x 10秒 x 16Bit = 7,056,000Bit = 882キロバイトになると。

ステレオでサンプリングレート44100Hzで16Bit/Sampleで80分間録音したら、データサイズは

2チャンネル x 44,100回 x 4,800秒 x 16Bit = 6,773,760,000Bit =846,720キロバイト(約800MB)になると。

80分のCD-Rの容量と大体あったので、こんな考えで間違ってはいないだろう。

これらのデータを切ったりつなげたりすれば、マイクからの音にエフェクトをかけられると思った。つづく

Tagged with: , , .
Avatar

Biglobeのカマンジ製Android端末のモニターの抽選結果が来た。
結果は落選だった。残念無念。

定員100名のところ3,472名もの応募が合った様子。かなりの倍率である。
まぁダメ元だったのでそんなに残念ではないが、負け惜しみをつらつらと・・

iPadが発表された後では若干見劣りするスペックのこの端末。
コンテンツで勝負したいけど、いまのところiPadに分がありそう。

Camangi WebStation
390グラム 7インチ800×400ピクセルタッチパネル詳細
価格は39,800円

Apple iPad
680グラム 9.7インチ1024×768のタッチパネル詳細
価格 $499

と、スペック的にはiPadが良い感じ。CPUもApple独自のA4プロセッサ1GHzを使っているみたい。多分ARM系ってことはsnapdragonみたいなもんかな。
Androidタブレットでも高速なCPUを搭載したモデルが出てきそうなので今後に期待だな。

Tagged with: , , .
« Previous Page