リアルタイム DSP を実行できるように、マイクのオーディオ ストリームを録音したいと考えています。
.read()
スレッドを使用したり、新しいオーディオ データを待機している間にブロックしたりせずに、そうしたいと考えています。
更新/回答: Android のバグです。4.2.2 にはまだ問題がありますが、5.01 は修正されています! 違いがどこにあるかはわかりませんが、それが話です。
注: 「スレッドを使用してください」とは言わないでください。スレッドは問題ありませんが、これはスレッドに関するものではありません。Android 開発者は、スレッドを指定したり read() のブロックに対処したりすることなく、AudioRecord を完全に使用できるようにすることを意図していました。ありがとうございました!
これが私が見つけたものです:
AudioRecord オブジェクトが初期化されると、独自の内部リング タイプ バッファが作成されます。が.start()
呼び出されると、上記のリング バッファ (または実際の種類は何でも) への記録を開始します。
が.read()
呼び出されると、bufferSize の半分または指定されたバイト数 (どちらか少ない方) を読み取ってから戻ります。
内部バッファに十分な数のオーディオ サンプルがある場合、read() はデータとともに即座に戻ります。まだ十分でない場合、read() は十分になるまで待機し、データを返します。
.setRecordPositionUpdateListener()
を使用してリスナーを設定し、 を.setPositionNotificationPeriod()
使用.setNotificationMarkerPosition()
して通知期間と位置をそれぞれ設定できます。
ただし、特定の要件が満たされない限り、リスナーは呼び出されないようです。
1: 期間または位置は、bufferSize/2 または (bufferSize/2)-1 と等しくなければなりません。
2: .read()
Period または Position タイマーがカウントを開始する前に、 a を呼び出す必要があります。つまり、呼び出した.start()
後、 も呼び出し.read()
、リスナーが呼び出されるたびに、.read()
再度呼び出します。
3:.read()
毎回少なくとも bufferSize の半分を読み取る必要があります。
したがって、これらのルールを使用して、コールバック/リスナーを機能させることができますが、何らかの理由で読み取りがまだブロックされており、完全な読み取りの価値がある場合にのみリスナーを呼び出す方法がわかりません。
クリックして読み取るようにボタン ビューを設定すると、それをタップして、すばやくタップするとブロックを読み取ることができます。しかし、オーディオ バッファがいっぱいになるのを待つと、最初のタップは瞬時に行われます (読み取りはすぐに返されます) が、read() が待機する必要があるため、後続の高速タップはブロックされると思います。
read() がすぐに返すのに十分なデータがあるときにリスナーが呼び出されるように、リスナーを意図したとおりに機能させる方法についての洞察をいただければ幸いです。
以下は私のコードの関連部分です。
私のコードには、logcat に文字列を送信するログ ステートメントがいくつかあります。これにより、各コマンドの所要時間を確認できます。これにより、read() がブロックされていることがわかります。(そして、私の単純なテスト アプリのボタンも、読み取りを繰り返しているときに応答が非常に遅くなりますが、CPU は固定されていません。)
ありがとう、〜ジェシー
私の OnCreate() で:
bufferSize=AudioRecord.getMinBufferSize(samplerate,AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT)*4;
recorder = new AudioRecord (AudioSource.MIC,samplerate,AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT,bufferSize);
recorder.setRecordPositionUpdateListener(mRecordListener);
recorder.setPositionNotificationPeriod(bufferSize/2);
//recorder.setNotificationMarkerPosition(bufferSize/2);
audioData = new short [bufferSize];
recorder.startRecording();
samplesread=recorder.read(audioData,0,bufferSize);//This triggers it to start doing the callback.
次に、ここに私のリスナーがあります:
public OnRecordPositionUpdateListener mRecordListener = new OnRecordPositionUpdateListener()
{
public void onPeriodicNotification(AudioRecord recorder) //This one gets called every period.
{
Log.d("TimeTrack", "AAA");
samplesread=recorder.read(audioData,0,bufferSize);
Log.d("TimeTrack", "BBB");
//player.write(audioData, 0, samplesread);
//Log.d("TimeTrack", "CCC");
reads++;
}
@Override
public void onMarkerReached(AudioRecord recorder) //This one gets called only once -- when the marker is reached.
{
Log.d("TimeTrack", "AAA");
samplesread=recorder.read(audioData,0,bufferSize);
Log.d("TimeTrack", "BBB");
//player.write(audioData, 0, samplesread);
//Log.d("TimeTrack", "CCC");
}
};
更新: Android 2.2.3、2.3.4、そして現在は 4.0.3 でこれを試しましたが、すべて同じように動作します。また、code.google には未解決のバグがあります。2012 年に他の誰かが開始したエントリと、2013 年に私が開始したエントリがあります (最初のエントリについては知りませんでした)。
更新 2016: ああ、それが私なのかアンドロイドなのか何年も疑問に思っていたのですが、ついに答えが出ました! 上記のコードを 4.2.2 で試しましたが、同じ問題がありました。上記のコードを 5.01 で試してみましたが、うまくいきました!!! また、最初の .read() 呼び出しも不要になりました。.setPositionNotificationPeriod() と .StartRecording() が呼び出されると、mRecordListener() は魔法のようにデータが利用可能になるたびに呼び出されるようになり、ブロックされなくなります。これは、十分なデータが記録されるまでコールバックが呼び出されないためです。 . 正しく記録されているかどうかを知るためにデータを聞いていませんが、コールバックは正常に発生しており、以前のようにアクティビティをブロックしていません!
http://code.google.com/p/android/issues/detail?id=53996
http://code.google.com/p/android/issues/detail?id=25138
このバグを気にかけている人がログインしてバグに投票したりコメントしたりすれば、おそらく Google によってより早く対処されるでしょう。