本次更新的是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.2 WebRtcAgc_ProcessDigital()函数
WebRtcAgc_ProcessDigital()是WebRTC中AGC模块的核心函数,任何模式都要调用到它。该函数在采样率为8kHz时处理80个点的数据,16kHz、32kHz等都是处理160个点。
由于该函数流程过长,所以这里我会将流程图分为多块,然后逐块进行分析讲解。
如图3.2所示,第一部分首先要计算近端信号的VAD结果,并且当远端信号超过10帧(100ms)之后,使用远端的VAD结果来修正近端VAD,具体的修正公式如下:
接着就是使用V来计算出衰减decay,其计算公式为
然后接下来的具体操作在图中都有,就不再叙述。
第二部分如图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所示。
最后一部分就是gain与语音进行处理,如图3.5所示,图中描述已经非常清楚。
3.3 WebRtcAgc_ProcessAnalog()函数
该函数的作用就是根据输入信号的能量大小、饱和标志(WebRtcAgc_StaturationCtrl()函数)、零状态(WebRtcAgc_ZeroCtrl())和近端语音活度(WebRtcAgc_SpeakerInactiveCtrl())来初步调整和控制语音幅度的大小。主要有以下几个部分。
3.3.1对麦克风音量进行预处理
这一部分是对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.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.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/中的学习笔记整理而得到的,在此感谢原作者。