使用 RK3399 搭載 Android 系統7.1.2,出現 AudioTrack 有時無法播放 PCM 音訊
阿新 • • 發佈:2018-12-23
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 做資料寫入
}
希望能夠解決啟動錄音後不播放合成音問題,有問題可以在評論區一起交流。
注:這個文僅做記錄,解決一個猜想,在測試方面並不非常充分,所以若專案使用,請考慮存在風險再使用。