我希望录制麦克风音频流,以便可以对其进行实时 DSP。
我想这样做而不必使用线程并且.read()
在等待新的音频数据时没有阻塞。
更新/回答:这是 Android 中的一个错误。4.2.2 仍然有问题,但 5.01 已修复!我不确定分歧在哪里,但这就是故事。
注意:请不要说“只使用线程”。线程很好,但这与它们无关,Android 开发人员打算让 AudioRecord 完全可用,而无需我指定线程,也无需我处理阻塞 read()。谢谢!
这是我发现的:
当 AudioRecord 对象初始化时,它会创建自己的内部环形缓冲区。当.start()
被调用时,它开始记录到所述环形缓冲区(或任何它真正的类型。)
当.read()
被调用时,它读取缓冲区大小的一半或指定的字节数(以较小者为准),然后返回。
如果内部缓冲区中有足够多的音频样本,则 read() 会立即返回数据。如果还不够,则 read() 等到有,然后返回数据。
.setRecordPositionUpdateListener()
可用于设置监听器, 和.setPositionNotificationPeriod()
可.setNotificationMarkerPosition()
用于分别设置通知周期和位置。
但是,除非满足某些要求,否则似乎永远不会调用 Listener:
1:Period 或 Position 必须等于 bufferSize/2 或 (bufferSize/2)-1。
2:.read()
必须在 Period 或 Position 计时器开始计数之前调用 A - 换句话说,在调用.start()
之后 then 还调用.read()
,并且每次调用 Listener 时,.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
如果关心这个错误的人登录并投票和/或评论这个错误,谷歌可能会尽快解决它。