本文系做者原創。如轉載,請註明出處。 謝謝!算法
在作語音類APP時,語音留言(以碼流形式)會被保存下來。當聽時想快點聽完,因而就有了語音加速播放功能。同時還有個需求,能實時切換播放速率,即當1.5倍
速播放時切兩倍速,就要馬上兩倍速播放。函數
首先作了一番調研,看幾倍速後就基本上聽不清說什麼了。找來了一款能在PC上運行的有加速播放功能的軟件,試驗下來兩倍速以上就基本上聽不清說什麼了。因而除
了正常速率外,只有兩個速率可調:1.5倍速和兩倍速。同時搜了一下變速相關的開源算法(算法原理請看其餘相關文章),發現主打變聲功能的sound touch使用率較高。它能夠改變音調(pitch)和語速(rate)等,好多軟件拿它作一些趣味音頻。基於sound touch作了一個應用程序看加速效果,同時要兼顧男聲和女聲。試驗下來發現語速加快了後音調就變了,也就是還要同時調音調。改了音調作了一番調試後只能接近原聲,能兼顧男女聲。再試試在PC上運行的軟件,加速後也是隻能接近原聲。作了這些後就決定用sound touch來作了,在不一樣的速率下音調(pitch)和語速(rate)參數值也肯定了下來(男女聲在一個速率下用一組參數值)。線程
再看咱們的相關代碼,系統支持ILBC和OPUS兩種codec,ILBC每幀30ms, OPUS每幀20ms, 播放線程每20ms運行一次,即每次取20ms PCM數據播放。要想播放語音留言,首先要解碼碼流成PCM 數據放在buffer1中,再看是否要加速播放,要加速的話就把buffer1中的語音數據調加速函數處理後放在buffer2中,不加速就把buffer1中的語音數據直接拷進buffer2中,等待buffer2中的數據被取走播放。根據這些通過一番嘗試後用了分段循環處理的實現方法。一個循環內取固定時間長度的原始音頻幀,取出的這段音頻幀無論是否加速在這個循環內都要正好播放完,同時兼顧ILBC和OPUS兩種不一樣的幀長和1.5倍速兩倍速兩種不一樣的速率。ILBC時,每幀30ms,每次取20ms播放,有1.5倍速兩倍速兩種速率,最小的原始PCM數據長度(即最小固定長度)是120ms(原始ILBC幀數是4幀,1.5倍速後是80ms,4次可取完播放,2倍速後是60ms,3次可取完播放)。OPUS時,每幀20ms,每次取20ms播放,有1.5倍速兩倍速兩種速率,最小的原始PCM數據長度也是120ms(原始OPUS幀數是6幀,1.5倍速後是80ms,4次可取完播放,2倍速後是60ms,3次可取完播放)。因此一個循環內取的最小原始音頻的固定長度是120ms. 一個循環結束後正好播放完,清空buffer2,而後取下一段原始音頻幀解碼加速後播放。爲了在循環內播放時聲音不斷斷續續,buffer2裏要有足夠的數據供取走播放。這樣在每一個循環剛開始的時候,要儘量多的解碼幀數加速後放在buffer2中等取走播放。至於幾回解碼完所需的碼流幀數,這要根據cpu來定。通常一個循環的前一兩次就把碼流解碼加速作完了,後面的次數裏只須要從加速後的buffer2裏取數據播放就能夠了。這樣循環內每次播放線程運行時都能取到數據播放,下一循環開始時也能取到數據播放,不存在斷斷續續的狀況。調試
當要求實時切換語速時,要把這120ms原始語音數據播放完在下一個循環裏才能改變語速。因爲120ms很短,用戶基本無感知,能夠認爲是實時的,不影響用戶體驗。
當到語音結尾時,有多少幀就解碼加速多少幀供播放,當從加速後的buffer裏不能取到20ms數據就可認爲播放結束了。這時用零補足20ms數據,把這些數據播放出去,從而結束播放。code
跟上層的UI聯調後效果不錯。這樣APP內的語音加速方案就搞定了。it