本次更新的是WebRTC中AGC模块的具体函数的流程图和介绍,内容较多,所以可能错误也比较多,如果有问题,可以留言给我指出。非常感谢大家的支持!

3.AGC的函数介绍

3.1 WebRtcAgc_Process()函数

这一部分是WebRTC的自动增益控制模块的核心程序,如图3.1所示,主要分为以下5个步骤:

首先要判断采样点数是否符合规定的要求。即若采样率为8000Hz,则一帧长度必须为80个点;若采样率为16000Hz或32kHz或48kHz,则长度必须为160个点。否则就会返回错误退出。

其次要初始化saturationWarning为0,初始化输出的语音的麦克风级别为输入的语音的麦克风级别。

然后执行WebRtcAgc_ProcessDigital()函数,它是AGC的核心函数,无论什么模式都要调用到,具体功能在后续会进行介绍。该函数返回值取值为0或-1,若为-1则代表出错,返回,否则为正确。

再然后需要判断是否符合条件1。条件1的内容如下:

stt->agcMode < kAgcModeFixedDigital && (stt->lowLevelSignal == 0 || stt->agcMode != kAgcModeAdaptiveDigital

也就是说必须要同时符合两个条件:

①agcMode为kAgcModeUnchanged、kAgcModeAdaptiveAnalog或kAgcModeAdaptiveDigital。

②lowLevelSignal为0,或者agcMode不是kAgcModeAdaptiveDigital。

    符合以上两个条件的话,就去执行WebRtcAgc_ProcessAnalog(),该函数的功能是根据能量的大小、饱和标志(WebRtcAgc_StaturationCtrl)、零状态(WebRtcAgc_ZeroCtrl),和近端语音活度(WebRtcAgc_SpeakerInactiveCtrl)的结果,来初步控制输入语音的大小。具体介绍后续会有。

    最后,完成上述操作以后,更新inQueue。

图3.1 Process函数的流程图

3.2 WebRtcAgc_ProcessDigital()函数

    WebRtcAgc_ProcessDigital()是WebRTC中AGC模块的核心函数,任何模式都要调用到它。该函数在采样率为8kHz时处理80个点的数据,16kHz、32kHz等都是处理160个点。

由于该函数流程过长,所以这里我会将流程图分为多块,然后逐块进行分析讲解。

如图3.2所示,第一部分首先要计算近端信号的VAD结果,并且当远端信号超过10帧(100ms)之后,使用远端的VAD结果来修正近端VAD,具体的修正公式如下:

    接着就是使用V来计算出衰减decay,其计算公式为

    然后接下来的具体操作在图中都有,就不再叙述。

图3.2 ProcessDigital()函数流程图1

第二部分如图3.3中的蓝色部分。该部分通过快、慢包络和增益计算每个子帧的增益数组gain。首先计算了快慢包络,如下所示

然后取两个包络的最大值作为level。最后使用对数的分段线性函数把cur_level转换成gain。对数函数的整数部分是cur_level前面0的个数,如果0越少,说明数值越大,最多是31个0,最少1个0(有符号数)。小数部分用线性差值的方法,找到gain[zeros],gain[zeros-1]中间的量。

    第三部分如图3.3中的橘色部分,是来计算门限gate。gate意味着衰减原来的增益。gate的计算可以看成两部分,第一部分是基于快慢包络计算出的似然比,相当于快包络与慢包络能量的倍数。第二部分是近端信号的短时方差。然后计算门限

在计算完gate之后,确定gate是否小于0,若小于,则gatePrevious=0,否则就平滑门限。

平滑后,把门限转换成gain_adj。当gate最小的时候为0(语音),gain_adj取到最大,此时不使用gainTable[0]的值作为参考;当gate最大的时候为2500(噪声),gain_adj取到最小,此时g[k+1]要取到相对于gainTable[0]的值的70%;当gate处于最大最小值之间,g[k+1]在gainTable[0]和g[k+1]确定的这条直线上移动。这一部分如图3.4所示。

图3.3 ProcessDigital()函数流程图2
图3.4 ProcessDigital()函数流程图3

最后一部分就是gain与语音进行处理,如图3.5所示,图中描述已经非常清楚。

图3.5 ProcessDigital()函数流程图4

3.3 WebRtcAgc_ProcessAnalog()函数

    该函数的作用就是根据输入信号的能量大小、饱和标志(WebRtcAgc_StaturationCtrl()函数)、零状态(WebRtcAgc_ZeroCtrl())和近端语音活度(WebRtcAgc_SpeakerInactiveCtrl())来初步调整和控制语音幅度的大小。主要有以下几个部分。

3.3.1对麦克风音量进行预处理

图3.6 ProcessAnalog()函数框图第一部分

这一部分是对micVol进行处理。micVol的大小决定了模拟初步调节的音量。要注意一点,在在kAgcModeAdaptiveAnalog模式下,不调用AddVirtualMic()函数;同时在kAgcModeAdaptiveDigital()下,micVol只是一个第一步粗调节的中间变量,起到了一个初步调节的作用。

3.3.2 判断信号是否饱和

    该部分是基于信号的包络计算信号是否饱和,使用的是WebRtcAgc_SaturationCtrl()函数。

实际上系统在Webrtc_AddMic()函数中计算了包络env的值。对于16kHz的数据来说,10ms的数据是160个采样点,要分成10块,这样每一块是就是16个采样点,然后对求每块16个点求平方,值最大的点作为包络。

如图3.7所示,当有一块值大于875的时候,就为将envSum累加一次tmpW16。循环完之后,如果没有累计超过25000,则算饱和,将saturated设置为1,然后将envSum清零。如果envSum没有大于25000,则按照0.99的值衰减envSum。

图3.7 ProcessAnalog()函数框图第二部分

若信号饱和,则执行如图3.8中橘色部分所示的操作,具体在图中已经详细叙述,不再累述。

若信号不饱和(或信号饱和后执行的操作完成),则进行零信号检查和近端语音活度检查,如图3.8中蓝色的框图。

3.3.3 零信号检查

    WebRtcAgc_ZeroCtrl()该函数用来计算信号的大小,并且用msZero来记录,用来控制语音活度(actgiveSpeech)和块低频能量最大值(Rxx16_LPw32Max),这两个变量后续影响计算低频能量Rxx160_LPw32。

3.3.4 近端语音活度检查

    该部分使用的函数名称为WebRtcAgc_SpeakerInactiveCtrl()。检查近端扬声器是否处于非活动状态。如果是这种情况,由于VAD语音模型在长时间静音后对任何声音更敏感,因此VAD阈值增加。该部分较为简单,不再累述。代码如下:

void WebRtcAgc_SpeakerInactiveCtrl(LegacyAgc *stt) { int32_t tmp32; int16_t vadThresh; if (stt->vadMic.stdLongTerm < 2500) { stt->vadThreshold = 1500; } else { vadThresh = kNormalVadThreshold; if (stt->vadMic.stdLongTerm < 4500) { /* Scale between min and max threshold */ vadThresh += (4500 - stt->vadMic.stdLongTerm) / 2; } /* stt->vadThreshold = (31 * stt->vadThreshold + vadThresh) / 32; */ tmp32 = vadThresh + 31 * stt->vadThreshold; stt->vadThreshold = (int16_t) (tmp32 >> 5); }}
图3.8 ProcessAnalog()函数框图第三部分

3.3.5 计算子带能量和总能量

计算子带能量和总能量如图3.8中粉红色部分所示。流程图中叙述非常清晰,不再描述。

3.3.6 根据vadMic.logRatio判断是否进入条件流程

    然后根据vadMic.logRatio判断是否进入调节流程,若是,则进行进一步处理,否则继续返回计算能量部分。如图3.8紫色部分所示。

3.3.7 调节流程

    在前面计算完全帧能量Rxx160LPw32之后,该变量会根据4个阈值划分为5个范围。四个阈值分别是:

①upperSecondaryLimit

②upperLimit

③lowerLimit

④lowerSecondaryLimit

    该四个阈值由上到下递减。该部分核心是对micVol进行调整,保证一个帧的能量在[upperLimit,lowerLimit]之间。

3.3.8 检查回声等最后处理步骤

该部分保证回声状态和零状态下不会被放大,同时限制增益在一定范围之内,限制音量最大值,如图3.8中白色部分。

部分内容是我阅读https://blog.csdn.net/ssdzdk/中的学习笔记整理而得到的,在此感谢原作者。