利用重採樣處理設置audio delay的噪音問題

      現代智能電視中都有多種音頻輸出方式,除了喇叭之外,還有Spdif out、HDMI ARC輸出。一般來說,設計時都是排他型輸出,比如選擇spdif out輸出時喇叭會自動靜音或者直接不送音頻數據了。當然也有例外,有的設計是會同時輸出,這樣由於鏈路的差距,兩種輸出通路間會有一個延遲,延遲超過一定時間(有數據說是20ms),人耳就能明顯感受到差距。爲了解決這個問題,有的產品在設計時會有UI供用戶自己調整音頻輸出delay的時間。

     完成這個音頻delay的需求,是實現了一個circle buffer做緩存,音頻delay多少ms就將實際buffer數據延後輸出。

由於這個delay是會在播放中實時調節,調節過程中,buffer延後,前面一段都是0數據,這樣聽起來會有明顯的噪音,正常情況下音頻數據是連續的,比如delay了10ms,circle buffer裏的數據將會是10ms的0數據拼接上緩存的有效數據,這樣一起送到底層輸出時就會聽到滋滋的噪音,用戶UI不斷調節delay,這個滋滋聲會很頻繁,嚴重影響聽感。

     爲了優化體驗,嘗試一個思路就是不讓緩存buffer中有0的空數據,那麼這多出來的空間數據從哪裏來呢?開始的時候有試過從緩衝區的有效數據中copy部分過來,等於delay時重複播放一小段數據,但是這個方法實際效果並不好,因爲重複播放一段音頻數據,數據銜接處是不連續的,從頻譜上很容易看出來有一條豎線的尖刺,人耳聽起來仍會有明顯的噪音。所以既要保證數據不爲空又要保證數據是過渡連續平滑的,就想到了重採樣。

基本思路就是將有效數據拉長或縮短至調整delay後的buffer空間。比如緩存區有50ms數據,現在delay了10ms,那麼就把50ms數據插值重採樣成60ms,回調例如從delay 50ms調整到delay 40ms,就把數據抽取壓縮。

直接上代碼,基本思路就是怎麼樣從src源數據填充到dst目標數據:

if (out->speaker_last_delay_size != delay_buffer_size) {
        int i;
        short *pbuf;
        unsigned index = 0;
        float mPhaseFraction = 0;
        float mPhaseFraction1 = 0;
        float PhaseFraction  = 0;
        int in_frame = 0;
        int left_byte_size = out->speaker_delay_buf.size -(out->speaker_delay_buf.rd - out->speaker_delay_buf.start_add );
        short *sample = (short *)out->speaker_delay_buf.rd;
        pbuf = (short*)data_temp_src;
        memset(data_temp_src, 0 ,MAX_DELAY_SIZE+FRAME_SIZE);
        memset(data_temp_dst, 0 ,MAX_DELAY_SIZE+FRAME_SIZE);
        in_frame = delay_buffer_size >> 2;
        PhaseFraction =(float) out->speaker_last_delay_size /delay_buffer_size ;
        if (out->speaker_delay_buf.wr != out->speaker_delay_buf.rd && out->speaker_last_delay_size != 0) {
            read_from_buffer(out->speaker_delay_buf.rd, pbuf, out->speaker_last_delay_size,
                out->speaker_delay_buf.start_add, out->speaker_delay_buf.size);
            if (PhaseFraction > 0.0 && in_frame > 0) {
                for (i = 0; i < in_frame; i++) {
                    data_temp_dst[2 * i] =	( pbuf[index * 2] + (short)((pbuf[(index + 1) * 2] - pbuf[index * 2]) * mPhaseFraction1));
                    data_temp_dst[2 * i + 1] = (pbuf[index * 2 + 1] + (short)((pbuf[(index + 1) * 2 + 1] - pbuf[index * 2 + 1]) * mPhaseFraction1));
                    mPhaseFraction += PhaseFraction;
                    index =  mPhaseFraction;
                    mPhaseFraction1 = mPhaseFraction - index;
                }
                write_to_buffer(out->speaker_delay_buf.rd, data_temp_dst, delay_buffer_size,
                    out->speaker_delay_buf.start_add, out->speaker_delay_buf.size);
            }
        }
        out->speaker_last_delay_size = delay_buffer_size;
    }

 

重採樣的算法也是從android源碼裏找到移過來的,比較簡單的插值算法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章