前言
今天在写一个使用Android NDK支持的OpenSLES的录音和播放demo时,当我在运行项目时,发现在创建播放引擎时,我调用了如下代码,实际上是调用了一个native方法,将参数传了进去:
mAudioJNI.initRecord(50000, 1, 1024, file.getAbsolutePath());
- 5000表示采样率,1表示输入声道数,1024表示每个音频帧的数据流大小,最后一个参数是文件路径,格式是.pcm。
但是我发现播放没有声音,并在控制台输出了如下信息:
AUDIO_OUTPUT_FLAG_FAST denied by client ...
于是我开始了我的排坑之旅,看了好多解决方案发现都没有用,我仔细一看,FAST?会不会是采样率的问题,于是顺着这个线,最终我找到了问题所在。
首先我们要知道每台安卓设备所支持的音频最大采样率都是不一样的,所以每个音频帧的数据流大小范围也是不一样的,所以我首先想到了去看看我的设备所支持的对应的数据大小,于是我找到了AudioManager这个类,顺便打印一下
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String sampleRate = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
String PROPERTY_OUTPUT_FRAMES_PER_BUFFER = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
Log.d("txOpenSLES","sampleRate:"+sampleRate+" PROPERTY_OUTPUT_FRAMES_PER_BUFFER:"+PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
打印结果如下
D/txOpenSLES: sampleRate:48000 PROPERTY_OUTPUT_FRAMES_PER_BUFFER:144
所以我的设备采样率最大只支持48000,每帧数据流大小设定是144,所以回头看,我调用的方法设置的这两个参数都不符合设备要求,所以我更改了一下
mAudioJNI.initRecord(48000, 1, 144, file.getAbsolutePath());
修改对应参数以后,这个问题就解决了。
但在录音过程中我发现另一个问题,播放时声音会变快,原因也找到了。在录制过程中当数据到达缓冲区,缓冲区写进文件的过程中,如果数据已经到了缓冲区,但文件写入数据跟不上,那么缓冲区的数据就可能会覆盖一部分,所以在这里给大家提供两个初步解决方案。
- 降低采样率,把每秒的输入数据大小(采样率*采样位数)变小,让文件来得及写入,就可以解决,但这样会降低音频的音质,下下策。
- 合理设计缓冲区域,可以考虑使用多个,来充分的接收数据,再写入文件,这样设计比较考验能力,我暂时没有做过实践,如果有空会给大家贴出来。
该项目使用了OpenSLES做了简单的音频录音和播放,暂时没有上传github,如果大家有需要会后续传上去。