首先,我把常规的边采集边保存文件的代码贴出来
mAudioRecord.startRecording();
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(mAudioFile)));
while (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
int number = mAudioRecord.read(mAudioRecordData, 0,mAudioRecordData.length);
for (int i = 0; i < number; i++) {
dos.writeShort(mAudioRecordData[i]);//写文件操作
}
if (AudioRecord.ERROR_BAD_VALUE != number
&& AudioRecord.ERROR != number) {
Log.d("TAG", String.valueOf(number));
}
}
dos.flush();
dos.close();
上面这段代码是AudioRecord把采集到的数据放入到short[]类型缓冲区mAudioRecordData中,然后利用DataOutputStream把缓冲区的数据写入指定的文件中。
OK,下面我们来看看这段代码中对边采集边写入文件可能带来的风险进行一下分析:
1.我们可以看到每次采样存入缓冲区的数据长度是number,然后是利用for循环将short[]类型缓冲区中的数据逐个写入文件的,这里就产生了一个疑问:这种方式的文件写入,可行吗?这种写入方案应该是不被鼓励使用的,为什么?因为执行代码和写入文件比较耗时,在写入文件时如果缓冲区的数据还没有写完,而新的采集数据又填入到了缓冲区,那么上一次采集到的数据就有可能有一部分被覆盖掉。
2.有的同学可能会想,既然你用short[]保存数据,我为什么不能用byte[]保存呢,用byte[]保存之后,可以用DataOutputStream.write(byte[] data,int offset,int index)一次写入所有数据啊?这种方式当然是可以的,但是你忽略了I/O同样也是耗时操作的问题,把整个缓冲区一次写入文件也是需要时间的。问题有回归到1中提到的后来采集到的数据覆盖掉了上一次采集到的数据。
如上图所示,当2阶段的时间小于3阶段时间时,缓冲区的数据肯定会被后来的数据覆盖的,本人初步的猜想可能跟手机性能有关,有可能手机性能越突出,2阶段的耗时越小。如果有大神对AudioRecord音频采集这块比较熟悉,麻烦不吝指教。
所以说,针对上面出现的这种缓冲区数据被覆盖的情况,我的处理方案如下:
第一,写文件操作另起线程操作;
第二,在写文件时启用双缓冲区,双缓冲区交替写入文件。例如有两个缓冲区a和b,当从a中写文件时,b等待接收采集到的数据。下一次从b中写文件时,a等待采集到的数据。
做到这两点基本可以保证边采集边保存文件的数据无恙了。
最后,还有个问题,与各位大神讨论一下,希望有大神能给出解决方案,就是结束采集的时候,有没有一种可能:I/O通道已经关闭,但是AudioRecord在stop之前又采集到了一组数据,这个是不是说保存的时候丢失了一部分数据,当然这个应该不会影响播放,但是对专业的音频开发人员做数据分析会有影响的。目前我的处理方案是:AudioRecord stop,然后是500ms后执行关闭I/O通道操作。
好了整个过程看起来描述的还算比较清楚,就不上传代码了,毕竟只要思想不滑坡,办法总比困难多。哈哈……共勉之!