一、背景介绍
netEQ是webrtc中动态抖动缓冲区和错误隐藏的算法,用来消除因为网络抖动或者丢包。在保持高质量通话的同时,兼顾数据的低延时。其中,两大模块分别为MCU、DSP。
MCU(Micro Control Unit)模块是抖动缓冲区的微控制单元,由于抖动缓冲区作用是暂存接收到的数据包,因此 MCU 的主要作用是安排数据包的插入并控制数据包的输出。数据包的插入主要是确定来自网络的新到达的数据包在缓冲区中的插入位置,而控制数据包的输出则要考虑什么时候需要输出数据,以及输出哪一个插槽的数据包。
DSP(digital signal processing)模块是信号处理单元,主要负责对从 MCU 中提取出来的 PCM 源数据包进行数字信号处理。
本文将针对DSP模块中设计到的相关算法以及具体处理过程做详细介绍和分析。
二、模块流程
这里解释一下DSP处理中几个操作类型的意义:
加速 Accelerate:变声不变调的加速播放算法
慢速 PreemptiveExpand:变声不变调的减速播放算法
正常 Normal:正常的解码播放,不额外引入假数据
融合 Merge:如果上一次是 Expand 造假出来的数据,那为了听起来更舒服一些,会跟正常数据包做一次融合算法
丢包隐藏 Expand(Packet Loss Concealment):丢包补偿,最重要的无中生有算法模块,解决 “真丢包” 时没数据的问题,造假专业户
舒适噪音 ComfortNoise:是用来产生舒适噪声的,比单纯的静音包听起来会更舒服的静音状态
三、算法解析
1. 基础概念
a. 基音,指的是物体震动时所发出的频率最低的音,其余为泛音。也就是发音体整段震动,它携带着语音中的大部分能量。
b. 基音周期,声音震动波形的周期,其频率则为基频。基音周期是语音处理算法中的基本单位,是语音估计中的关键参数。
c. 基音检测,是对基音周期的估计,目的是得出和声音震动频率完全一致的基音周期长度。
d. 短时自相关函数法,webrtc中用于基因检测的方法。经典的短时自相关函数法进行基音检测时,是使用一个窗函数,窗不动,语音信号移动。通过比较原始信号和他位移后的信号之间的相似性来确定基音周期,如果移位距离等于基音周期,那么两个信号便具有最大相似性。窗口长度N的选择至少要大于基音周期的两倍,N越大,得出的基音周期越准确,但计算量也会相应增加。反之,N越小,误差越大,计算量越小。
e. WSOLA,Waveform Similarity Over-Lap Add,波形相似重叠相加法。在不改变语音音调并保证音质的前提下,使语音在时间轴上被拉伸或者压缩,即变速不变调。
采用分解合成的思想,将原始语音以L为帧间距,以N为帧长进行拆分,以aL为帧间距进行合成,其中a为调整因子。为防止频谱断裂或相位不连续,合成时在原始语音信号的采样点处,相邻区域[-max, +max]内移动,寻找信号波形相关最大的波形,确定合成位置。
图中是通过直接拷贝的方式实现慢速播放,造成了时域波形不连续。波形相似叠加法避免了上述问题的出现。
2. 加速(Accelerate)
加速处理用来解决数据包在jitterbuffer中累积造成延时过大的情况。使用WSOLA算法在时域上压缩语音信号。
a. 处理流程
已上图为例,长度为110个样本。其中B区域为短时自相关函数法中的x(n),长度相同的移动窗(A区域)为x(n-τ),以τ为10开始移动,最大为100。在此过程中以抛物线拟合的方式求出相关性最大时的移动距离τ,进而得到该帧的基音周期P。
计算该数据流,中心点,前后两个基音周期的相关性bestCorr。当相关性大于0.9,将两个基音周期交叉混合并输出;否则,按照正常处理直接输出。
加速处理就是将两个基音混合成一个个并代替原有的两个基音来缩短语音长度。
加速后的语音数据存于neteq算法缓冲区algorithm_buffer中。
b. 代码位置
neteq/accelerate.cc
PreemptiveExpand::ReturnCodes PreemptiveExpand::Process(
const int16_t* input, size_t input_length, size_t old_data_length, AudioMultiVector* output, size_t* length_change_samples)
3. 减速(PreemptiveExpand)
减速处理用来解决网络状况不好而导致音频数据比较少时,为了人耳听觉的连续性,使用WSOLA算法在时域上拉伸信号,来延长网络等待时间。
a. 处理流程
过程与加速过程类似
- 根据短时自相关函数法计算经过解码的一帧音频数据的基音周期;
- 计算该数据流中间时间戳位置前后两个基音周期的相关性BestCorr;
- 当相关性大于0.9时,将两个基音周期交叉混合并输出;否则按正常处理直接输出;
减速处理是将两个基音混合成一个,并插入到两个基音中间来延长语音长度。因此,经过减速处理的语音帧增加了一个基音周期的时长。
减速后的语音数据存于neteq算法缓冲区algorithm_buffer中。
b. 代码位置
neteq/preemptive_expand.cc
PreemptiveExpand::ReturnCodes PreemptiveExpand::Process( const int16_t* input, size_t input_length, size_t old_data_length, AudioMultiVector* output, size_t* length_change_samples)
4. 丢包隐藏(Expand)
当音频数据丢失,会利用参考数据在算法缓冲区中创建、补齐缺失内容,实现丢包隐藏,保证听觉体验。
a. 处理流程
上图中,丢包隐藏使用语音缓冲区中最新的256个样本作为参考数据源,并将这些数据记为speechHistory(历史数据)。speechHistory用于连续PLC的场景。
b. 代码位置
neteq/expand.cc
Expand::Expand(BackgroundNoise* background_noise, SyncBuffer* sync_buffer, RandomVector* random_vector, StatisticsCalculator* statistics, int fs, size_t num_channels)
5. 融合(Merge)
融合处理发生在播放的上一帧与当前数据帧不连续的情况。比如,上一帧为PLC帧,当前帧为正常帧。
a. 处理流程
- 首先,进行丢包隐藏,得到长度为202个样本的补偿序列expanded[202];
- 确定该补偿序列第几个样本开始与解码后的数据序列相关性最大。设相关性最大时,滑动窗口移动了P个样本。且,P必须满足两个条件:小于10ms、小于speech buffer中未播放的数据量;
-
对解码端的数据进行平滑处理,见下图:
image.png
解码数据分为两部分,一部分用于与PLC的数据进行混合并平滑(上图B区域),另一部只需完成平滑即可(上图D区域);
- PLC的数据采用递减的方式对数据进行平滑,而merge采用递增的方式进行平滑,这种方法在数学上完成了很好的衔接。即首端与PLC完美衔接,尾端与待播放数据完美衔接。
b. 代码位置
neteq/merge.cc
Merge::Merge(int fs_hz,size_t num_channels,Expand* expand,SyncBuffer* sync_buffer)
6. 正常(Normal)
正常处理一般用于提取的数据包刚好符合播放要求,然后将此包解码后直接输出到speech buffer等待播放。如果上次处理是PLC,还需要进行平滑。
a. 处理流程
b. 代码位置
neteq/normal.cc
int Normal::Process(const int16_t* input, size_t length, Modes last_mode, AudioMultiVector* output)
7. 舒适噪音(ComfortNoise)
rfc 3389。结合语音活动检测算法的舒适噪音生成可快速确定静音出现的时间,并在出现静音时产生人工噪音,直到语音活动重新恢复为止。产生的人工噪音可形成传输流不间断的假象,因此电话中的背景声音会从始至终保持连续,接听者不会有电话掉线的感觉。
a. 代码位置
neteq/comfort_noise.cc
int ComfortNoise::Generate(size_t requested_length, AudioMultiVector* output)
参考
https://blog.csdn.net/liuxiaoheng1992/article/details/79379514
https://nemocdz.github.io/post/浅谈-webrtc-neteq/#丢包补偿-1
《WebRTC语音引擎中NetEq技术的研究》吴江锐