1. 程式人生 > >webRTC中音訊相關的netEQ(五):DSP處理 webRTC中音訊相關的netEQ(四):控制命令決策 webRTC中音訊相關的netEQ(二):資料結構)

webRTC中音訊相關的netEQ(五):DSP處理 webRTC中音訊相關的netEQ(四):控制命令決策 webRTC中音訊相關的netEQ(二):資料結構)

上篇(webRTC中音訊相關的netEQ(四):控制命令決策)講了MCU模組是怎麼根據網路延時、抖動緩衝延時和反饋報告等來決定給DSP模組發什麼控制命令的。DSP模組根據收到的命令進行相關處理,處理簡要流程圖如下。

 

從上圖看出如果有語音包從packet buffer裡取出來先要做解碼得到PCM資料,沒有就不用做解碼了。編解碼也是數字訊號處理演算法的一種,是個相當大的topic,不是本文所關注的,本文關注的是對解碼後的PCM資料做數字訊號處理,如加減速。如果命令是非Normal命令,就要根據命令做DSP處理,是Normal命令就不用做了。最後取出一幀資料用於播放。

 

MCU發給DSP的主要的控制命令有正常播放(normal)、加速播放(accelerate)、減速播放(preemptive expand)、丟包補償(PLC,程式碼中叫expand)、融合(merge)等。正常播放就是不需要做額外的DSP處理。加減速也就是改變語音時長,即在不改變語音的音調並保證良好音質的情況下使語音在時間軸上壓縮或者拉伸,或者叫變速不變調。語音時長調整演算法可分為時域調整和頻域調整,時域調整以重疊區波形相似性(WSOLA)演算法為代表,通常用在語音通訊中。頻域調整通常音樂資料中。丟包補償就是基於先前的語音資料生成當前丟掉的語音資料。融合處理髮生在上一播放的幀與當前解碼的幀不是連續的情況下,需要來銜接和平滑一下。這些都是非常專業的演算法,本文不會涉及,本文是講工程上的一些實現,主要是buffer的處理。

 

在講這些處理之前先看netEQ裡相關的幾塊buffer,分別是decodedBuffer(用於放解碼後的語音資料)、algorithmBuffer(用於放DSP演算法處理後的語音資料)、speechBuffer(用於放將要播放的語音資料,這個在前面的文章(webRTC中音訊相關的netEQ(二):資料結構)中講過)和speechHistoryBuffer(用於放丟包補償的歷史語音資料,即靠這些資料來產生補償的語音資料)。

  

先看加速處理。它主要用於加速播放,是抖動延遲過大時在不丟包的情況下儘量減少抖動延遲的關鍵措施。它的處理流程如下:

1,看decodedBuffer裡是否有30Ms的語音資料(語音資料量要大於等於30Ms才能做加速處理),如果沒有就需要向speechBuffer裡未播放的語音資料借,使滿足大於等於30Ms的條件。下圖示意了借的步驟:

先算出decodedBuffer裡缺的樣本數(記為nsamples, 等於30Ms的樣本數減去buffer裡已有的樣本數),即需要向speechBuffer借的樣本數。然後在decodedBuffer裡將已有的樣本數右移nsamples,同時從speechBuffer裡end處開始取出nsamples個樣本,將其放在decodedBuffer裡開始處空出來的地方。

 

2,做加速演算法處理,輸入是decodedBuffer裡的30Ms語音資料,輸出放在algorithmBuffer裡。如果壓縮後的樣本數小於向speechBuffer借的樣本個數nsamples(假設小msamples),不僅要把這些壓縮後的樣本拷進speechBuffer裡(從end位置處向前放),同時還要把從cur到pos處的樣本數向後移msamples,cur指標也向後移msamples個數。下圖給出了示意:

如果壓縮後的樣本數大於向speechBuffer借的樣本個數(假設大qsamples),先要把從cur到pos處的樣本數向前移qsamples(cur和pos指標都要向前移qsamples個數),然後把這些壓縮後的樣本拷進speechBuffer裡(從pos位置處向後放)。下圖給出了示意:

 

3,從speechBuffer裡取出一幀語音資料播放,同時把cur指標向後移一幀的位置。

 

減速處理的流程跟加速是類似的, 這裡就不詳細講了。下面開始講丟包補償,它的處理流程如下:

1,基於speechHistoryBuffer利用丟包補償演算法生成補償的語音資料(記樣本數為nsamples)放在algorithmBuffer裡,同時還要更新speechHistoryBuffer裡的資料為下次做丟包補償做準備。示意圖如下:

先把speechHistoryBuffer裡的資料左移nsamples,然後把algorithmBuffer裡的nsamples個樣本放在speechHistoryBuffer的尾部。

2,把algorithmBuffer裡生成的資料放到speechBuffer裡。示意圖如下:

先將speechBuffer裡的資料左移nsamples,然後把algorithmBuffer裡的nsamples個樣本放在speechBuffer的尾部,同時cur指標也要左移nsamples。

3,從speechBuffer裡取出一幀語音資料播放,同時把cur指標向後移一幀的位置。

 

至於merge中buffer的處理,相對簡單,這裡就不講了。至此我覺得netEQ的主要核心點都講完了,共5篇,算一個系列吧。理解了這些核心點後要想對netEQ有更深的認識就得去實際的除錯了,把一些細節搞得更清楚。netEQ裡面的細節特別多,要想全部搞清楚是要花不少時間的。要是全部搞清楚了對語音接收側處理的認識會有一個質的提升。