版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ganfanzhou/article/details/85068553
RK3399 在 Android 系统 7.1.2,出现 AudioTrack 有时无法播放 PCM 音频的问题
问题背景:
切换 rk3399 后,搭载系统为 Android 7.1.2,如果单独使用 audiotrack 播放 PCM 音频是没有问题,但是应用在智能家居上,一般都需要和语音识别打交道,在开启录音后,很大的几率出现播放 PCM 音频不出声,查看log日志发现在录音短暂关闭后能恢复声音播放,此时重启录音功能,能做到语音识别和 PCM 音频播放。在 4.4.4 系统没出现这个现象。
目前,RK 厂家还没有给出解决方案,不确定是否为系统驱动问题,现在只能从应用层去绕开。
问题重现
- 语音唤醒后(多轮交互,单轮交互会在识别后关闭录音,不在考虑范围),能识别语音,并播放PCM音频
- 语音唤醒后,能识别语音,但不能播放语音合成的 PCM 音频,几次交互后,还是不能播放
- 语音唤醒后,能识别语音,但不能播放语音合成的 PCM 音频,几次交互后,能恢复播放音频
- 在后台开启音乐播放(另一个APP如网易云播放歌曲),在唤醒交互,能准确播放出语音合成 PCM
问题分析
- 我这边测试的语音识别用的讯飞 AIUI 平台,查看源码唤醒后的录音线程等级设置为10,有可能线程争夺导致 audio 进程降级,但可能性不大。
- 系统问题,在启用录音后,播放器被短暂关闭。目前这个可能性最大,在语音唤起交互 PCM 无法播放出声音,此时用第三方音乐播放器同样不出声(进度条显示播放正常),多次暂停播放操作才有可能重新出声。
解决方法
很明显,在录音启动后,再通过语音合成来播放音频,失败几率太大而且不可知,你无法在应用层得知喇叭有没有出声,不出声是一个物理现象,无法监控,所以只能从问题重现的第4点来解决,在开启录音之前,先启动一个播放操作来抢占焦点,然后再开启录音,经测试能有效解决这个不出声的问题。当然最好的解决方法是系统层面来解决这个问题。
下面是部分代码:
- AudioTrack 初始化部分
//目前语音合成都有提供合成并播放的调用,但不适用这场景
//只能合成 PCM 音频后,自己使用 audioTrack 来播放
private AudioTrack audioTrack;
//初始化
private void init() {
if (audioTrack == null) {
int bufferSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
AudioFormat audioFormat = new AudioFormat.Builder().setChannelMask(AudioFormat.CHANNEL_IN_STEREO).setSampleRate(8000).build();
audioTrack = new AudioTrack(new AudioAttributes.Builder().build(), audioFormat, bufferSize, AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE);
}
}
}
//语音唤醒时先启用播放
protected void wake() {
if (audioTrack != null) {
audioTrack.play();
}
}
//在调用停止播放调用,清空未播放缓存
protected void sleep() {
if (audioTrack != null) {
audioTrack.pause();//暂停
audioTrack.flush();//清空未播放数据
}
}
- 调用伪代码
//语音唤醒后回调
public void wakeupCallback(){
...
wake();//启动音频播放抢占
startListener();//开启拾音
//aiui 要发送 startRecord
//非 aiui 要启用startListener(xxx)
}
//语音合成
public void speak(String text){
//百度
mSpeechSynthesizer.synthesize(text)
//百度在 SpeechSynthesizerListener 里做audiotrack 写入操作
//aiui
String p = "xiaoyan,speed=50,pitch=50,volume=50";
AIUIMessage startTts = new AIUIMessage(AIUIConstant.CMD_TTS, AIUIConstant.START, 0, p, ttsData);
aiuiAgent.sendMessage(startTts);
//aiui 在 onEvent 类型为 tts 做数据写入
}
希望能够解决启动录音后不播放合成音问题,有问题可以在评论区一起交流。
注:这个文仅做记录,解决一个猜想,在测试方面并不非常充分,所以若项目使用,请考虑存在风险再使用。