利用重采样处理设置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源码里找到移过来的,比较简单的插值算法

你可能感兴趣的:(信号处理)