國內智能音箱的問世早於國外,但由於國內對智能化概念普及程度較低,初期智能音箱並沒有受到很多關注。但近幾年國內智能音箱行業經歷了從百花齊放到三足鼎立的發展階段,來自RT-Thread的黃天翔將從佔據主流市場的三個廠商脫穎而出的秘訣開始,分享RT-Thread在智能音箱在音頻方面的內容。
文 / 黃天翔
整理 / LiveVideoStack
智能音箱現狀
2014年10月,Alexa一款名為 Echo 的智能音箱出現,智能音箱行業開始火爆並受到極大關注。2015年年底,全球智能音箱銷量達到250萬臺。
國內智能音箱的問世早於國外,但由於國內對智能化概念普及程度較低,初期智能音箱並沒有受到很多關注。2015年京東叮咚系列音箱問世,2016年國內的美的、酷狗等多家公司涉足智能音箱行業,到2017年智能音箱市場全面爆發,2018年各大廠商已完成智能音箱的全面佈局。
國內智能音箱行業經歷了從百花齊放到三足鼎立的發展階段,到2018年底,阿里、小米、百度三家獨佔鰲頭,佔據主流市場。
我們分析了三個廠商能脫穎而出的秘訣:首先百度用低價爆款的策略,以輕量化、小巧的產品迅速衝擊市場。
其次,如上右側圖所示,在兩年內國內廠商推出了多款智能音箱產品。從這些產品可以發現智能音箱大熱還有一個重要因素:使用高性能芯片以及使用Linux系統方案。智能音箱涉及多方面難點技術,選用成本較高的方案快速迭代是市場得以推進的重要原因。
上圖所示的是兩種主流智能音箱的方案。百度小度智能音箱選用了Amlogic A113X1.5G 芯片配套 Linux 方案。小米音箱則是選用了全志 R16 A7四核心芯片。
基於現有方案可以預測,後續各廠商將會尋找低成本且同時也能滿足快捷開發、穩定的方案替代,越來越多中端廠商在考慮能否使用RTOS方案代替Linux方案。
Linux向RTOS方案遷移分析
如上圖所示的兩種方案,當前方案中使用的是高端芯片,未來則會選擇一些中低端甚至ARM9芯片完成智能音箱系統的開發。我們可以看到,目前方案從外接WIFI、藍牙芯片轉為使用內置芯片,包括芯片、MCU、DSP等都發生了變化。從封裝來看,它從BGA封裝變為QFN封裝,生產成本明顯降低。RTOS主打實時系統,開機速度降低到一兩秒,此外,它還有功耗低等特性。RTOS在智能音箱領域有一定優勢,例如在ACE回採時,我們會做主動喚醒,有固定的時間窗口使回採算法更可靠,時間不固定時回採數據不及時,RTOS可對時間窗口做極大保證。
上圖是通用方案啟動速度對比。我們可以看出系統分為Boot、OS、APP啟動三個層面。從三個層面整體來看,Linux系統啟動需要10秒左右,而使用RTOS方案啟動時間只有一兩秒。
如上圖,我們從四個方面做了對比。如大家所知,Linux所需的RAM、Flash是比較高的。ARM cortex-A方案是主流高端方案需要32MB RAM 和64MB Flash 的消耗。據我們統計,遷移 RTOS 方案 後可做到2MB RAM 和 4MB Flash 左右的消耗。由於 RTOS 系統比較輕巧,我們可以使用更小的RAM、Flash 芯片。
除了以上優勢,RTOS也有生態劣勢。智能音箱的操作系統更需要涉及到網絡、音頻相關的內容。Linux系統有成熟穩定的網絡框架、音頻子系統以及ffmpeg、Curl等開源軟件。RTOS調度器則更多的使用了輕量級網絡協議棧,在音頻方面比較空缺,公司各有私有的方案,成本比較高。
我們使用RTOS研發音頻播放系統鑑於成本趨勢、系統資源問題和開發成本的綜合考慮,希望能完成一套比較完整成熟的音頻系統。
RT-Thread 網絡音頻播放器設計的迭代
如上圖是我們第一版音頻播放器方案框架。早期設計這一款播放器,沒有考慮網絡播放的相關的功能。這版邏輯比較簡單:獲取音頻數據後直接做解碼,解碼過程是一個循環邏輯,單線程等待外部響應事件,包括seek事件、暫停恢復事件、停止事件完成播放器邏輯,最後將解析出的數據寫入底層音頻驅動。
由於第一版不滿足網絡播放的市場需求,我們將網絡組件、網絡功能添加到了系統中。如上圖中紅色邏輯處理部分。在這部分我們對框架思路做了修改,將文件、網絡資源放到同一層,選擇本地或網絡下載音頻資源,整體呈單線程模式。無論打開的URL資源是本地資源還是網絡資源,都是獲得資源後做解碼播放 。
這版播放器隨著在項目中越來越多的使用,逐漸的出現了很多噪音卡頓拖慢等問題。如上圖是我們PCM項目回採得出的數據分析結果。我們在播放音頻出現了短暫噪音之後繼續播放的情況,後期多方面分析發現,這是由於網絡情況不穩定,解碼器短暫接收不到數據造成的。
如上圖所示網絡不可靠的原因有多種,很多網絡不穩定是網絡硬件造成的波動,軟件層面是無法完全避免的,我們只能通過軟件算法和思路減少這些問題造成的影響。
如上圖是我們第三版改進後的播放器框架圖。左側是一樣的,依然是獲取數據進行解碼,唯一不同的是我會從網絡緩存區獲取數據。啟動播放後,我們會啟動一個新線程將本地數據或網絡音頻寫入緩存區,將下載與解碼器分離。只要緩存區有數據,解碼播放便不會出現卡頓。
我們採用了帶RTOS 喚醒調度機制且具有水位線管理的 pipe 作為第三版的音頻緩衝區 。例如我們設置了一個512KB的緩存區,通過HTTP連接下載數據。如果緩存區中沒有數據,我們可以簡單認為下載與解碼同時進行的。解碼時緩存區沒有數據時會等待直到音頻數據高於水位線。水位線即可開始解碼的最低緩存數據量。我們做了一個可動態調節的水位線機制。
改進過後,我們做了一個測試。如上圖左側是我們的測試環境數據。V2版本中,理論上音樂碼率大於1411kbps時才支持播放。而V3版本中,當下載速度大於播放速度時會導致水位上漲,一定會出現高於水位線情況。當網絡出現卡頓時,緩存數據是高於水位線的,解碼器依然可以拿到數據。
在另一種測試環境時,當下載速度一直低於播放速度。這是一種極端情況,下載達不到規定碼率,無論如何播放都絕對不會流暢。V2版本中音頻會一直間隔卡頓導致用戶無法聽清內容。在水位線機制中,當碼率較低,緩存不夠時是不會發出聲音的,會有一秒的緩存時間,緩存過後播放的聲音是較長時間連續的。,這樣我們能夠提升一定會卡頓情況下的用戶體驗,讓在非常卡頓的網絡情況下音頻不再發出刺耳的噪音。
有時我們會播放一些相聲、新聞等實時音頻電臺流內容。和音樂文件有一些不同,這時會出現推送流碼率和播放流碼率相同的情況。
這種情況的解決涉及到變速不變調算法的使用,即我們會改變語音播放速度而不改變語義語調,改變較小時人耳不會聽到差別。如上圖我們做了測試。當下載碼率與播放碼率相同時,我們通過變速不變調算法降低音頻的播放碼率,下載速度會始終大於播放速度。如圖中所示,雖然我們會進行緩存,但是由於下載速度較小,水位線漲不上去,依然會出現一定卡頓。經處理後,下載速度大於播放速度後,水位線會持續上漲,開始播放後便可以降低出現卡頓的情況。
基於以上,我們完成了第四版的改進。我們在寫入底層播放驅動前做對每一幀做變速不變調算法處理,當然這是可以選擇開啟的功能。
除了可以做變速不變調處理,我們還可以在相同位置EQ算法均衡器等其它處理,實現流行音樂音效、超低音音效等效果。
於是我們又做了一次改進。我們將變速不變調做了剝離,以插件的形式動態選擇不同音效。
在智能音箱領域,客戶會使用多種容器、協議以及編碼格式。我們需要支持多種組合。
我們在原基礎上做了改進,改進點如上圖紅色部分所示。在之前版本中,我們會將數據直接下載緩存到緩存區進行解碼。改進後,我們將解碼和解容器進行分離,在下載中加入瞭解容器,播放過程中解碼。解容器以插件形式接入系統,在播放過程中探測它的格式,選用合適的容器解碼格式。在這個過程中,不僅可以實現了多格式容器解碼,也實現了多協議解碼。我們將下載線程進行分類,針對不同協議做下載邏輯。將容器、協議、解碼器剝離後,播放器框架可實現多種組合應用場景。
混音框架設計
接下來我將介紹智能音箱設計過程中遇到的另一個重要問題。如上圖左側部分,音箱服務器推送了一個音頻,在播放過程中突然需要播放提示音,通常我們需要將音頻暫停播放,插入提示音,播放完成後音頻恢復播放。在這種情況下,設備需要維護播放狀態的。如圖中原始音頻是44K,採樣率是16K,中間有采樣率切換的過程。切換採樣率的過程中,需要注意它的實時性,因為我們控制內部芯片會產生一定時延。另一個就是pop音問題,當還有音頻在播放時,切換採樣率會有噪音出現。對此,我們做出了部分改進,採用混音的思路:將原音頻音量降低,再採用混音的方式將提示音混入,提示音播放完成後恢復音頻音量。這種思路不需要考慮播放器播放狀態的維護,而且兩路音頻完全獨立,開發者邏輯代碼編寫也清晰簡單。
我們在做這個方案時評估了Linux下的一個成熟算法。算法採用了線性重採樣算法。如上圖,它的庫裡有五種模式,默認使用最低模式。我們使用ARM-CotexM4芯片做了測試,發現最多模式會佔用百分之八十CPU。
libsamplerate算法輸入的數據是浮點類型,使用此算法先將數據切為單精度浮點數,內部使用雙精度浮點數做計算採樣以確保採樣效果。如圖中48K音頻採樣耗費了115%CPU,重採樣過程花費三百多秒。我們對此做了改進。我們對輸入參數使用整形定點算法,這時佔用CPU降到了79%。我們又將內部雙精度浮點數強制降為單精度後,CPU佔用率降到了49.5%。最終,我們做成了全整形數,這時CPU佔用只有3.8%。另外,由於重採樣算法由C語言寫成,我們從彙編層面對它做了優化,之前的操作造成了採樣效果變差,通過彙編優化將32位整型數改為了64位,整體效果雖不及浮點數,但整體效果提升了很多。
市面上主流混音算法模型有幾種,第一種是兩個聲道數據直接加和,當某一通道的數據幅度較大時混音後任意出現音頻數據溢出,從而音頻失真。第二種是加和後再除以音道數防止溢出,這樣會造成音道內音量衰減,並且音軌越多衰減越多。還有一種是加和箝位,即相加超過最大值時進行限幅,這樣音頻也會失真。另外還有飽和處理、歸一化處理等。考慮到RTOS 方案應用場景是一個音軌音量高一個音量稍低,我們並需要兩個聲音同時聽清,我們只需要保證一個音軌的質量。最終我們選擇了圖中第二種算法。雖然會產生一些衰減,但是在這個場景下只保證兩個音道中一個聲音清晰,衰減是可以忽略的。但是其它算法可能會出現失真,這是不能接受的。這種方案在實際應用中效果很好的。
上圖是我們測試結果。圖中是一幀數據、20毫秒的窗口,我們做了重採樣混音算法。優化過後,主音軌重採樣耗了大概1.288毫秒,副音軌耗了1.296毫秒,混音用時1.281毫秒,在ARM9 120MHZ的系統中耗費了大概20%的CPU消耗。
閱讀更多 LiveVideoStack 的文章