乐趣区

关于webrtc:详解-WebRTC-高音质低延时的背后-AGC自动增益控制

后面咱们介绍了 WebRTC 音频 3A 中的声学回声打消(AEC:Acoustic Echo Cancellation)的基本原理与优化方向,这一章咱们接着聊另外一个 “A” — 自动增益管制(AGC:Auto Gain Control)。本文将联合实例全面解析 WebRTC AGC 的根本框架,一起摸索其基本原理、模式的差别、存在的问题以及优化方向。

作者|珞神
审校|泰一

前言

自动增益管制(AGC:Auto Gain Control)是我认为链路最长,最影响音质和主观听感的音频算法模块,一方面是 AGC 必须作用于发送端来应答挪动端与 PC 端多样的采集设施,另一方面 AGC 也常被作为压限器作用于接收端,平衡混音信号避免爆音。设施的多样性最间接的体现就是音频采集的差别,个别体现为音量过大导致爆音,采集音量过小对端听起来很吃力。

在音视频通话的事实场景中,不同的参会人谈话音量各有不同,参会用户须要频繁的调整播放音量来满足听感的须要,戴耳机的用户随时接受着大音量对耳朵的“暴击”。因而,对发送端音量的平衡在上述场景中显得尤为重要,优良的自动增益控制算法可能对立音频音量大小,极大地缓解了由设施采集差别、谈话人音量大小、间隔远近等因素导致的音量的差别。

AGC 在 WebRTC 中的地位

在讲 AGC 音频流解决框架之前,咱们先看看 AGC 在音视频实时通信中的地位,如图 1 展现了同一设施作为发送端音频数据从采集到编码,以及作为接收端音频数据从解码到播放的过程。AGC 在发送端作为均衡器和压限器调整推流音量,在接收端仅作为压限器避免混音之后播放的音频数据爆音,实践上推流端 AGC 做的足够鲁棒之后,拉流端仅作为压限器是足够的,有的厂家为了进一步减小混音之后不同人声的音量差别也会再做一次 AGC。

图 1 WebRTC 中音频信号上下行解决流程框图

AGC 的外围参数

先科普一下样本点幅度值 Sample 与分贝 dB 之间的关系,以 16bit 量化的音频采样点为例:dB = 20 * log10(Sample / 32768.0),与 Adobe Audition 右侧纵坐标刻度统一。
幅度值示意:16bit 采样最小值为 0,最大值绝对值为 32768(幅度值如下图左边栏纵坐标)。

分贝示意:最大值为 0 分贝(分贝值如下图左边栏纵坐标),个别音量达到 -3dB 曾经比拟大了,3 也常常设置为 AGC 指标音量。

外围参数有:

typedef struct {
  int16_t targetLevelDbfs;    // 指标音量
  int16_t compressionGaindB;  // 增益能力
  uint8_t limiterEnable;      // 压限器开关
} AliyunAgcConfig;

指标音量 – targetLevelDbfs:示意音量平衡后果的目标值,如设置为 1 示意输入音量的目标值为 – 1dB;

增益能力 – compressionGaindB:示意音频最大的增益能力,如设置为 12dB,最大能够被晋升 12dB;

压限器开关 – limiterEnable:个别与 targetLevelDbfs 配合应用,compressionGaindB 是调节小音量的增益范畴,limiter 则是对超过 targetLevelDbfs 的局部进行限度,防止数据爆音。

AGC 的外围模式

除了以上三个外围的参数外,针对不同的接入设施 WebRTC AGC 提供了以下三种模式:

enum {
  kAgcModeUnchanged,
  kAgcModeAdaptiveAnalog,  // 自适应模仿模式
  kAgcModeAdaptiveDigital, // 自适应数字增益模式
  kAgcModeFixedDigital  // 固定数字增益模式
};

以下咱们会联合实例从基本功能,实用场景,信号流图以及存在的问题等方面论述这三个模式。

固定数字增益 – FixedDigital

固定数字增益模式最根底的增益模式也是 AGC 的外围,其余两种模式都是在此基础上扩大失去。次要是对信号进行固定增益的放大,最大增益不超过设置的增益能力 compressionGaindB,联合 limiter 应用的时候下限不超过设置的指标音量 targetLevelDbfs

固定数字增益模式下仅依附外围函数 WebRtcAgc_ProcessDigital 对输出信号音量进行平衡,因为没有反馈机制,其信号处理流程也是极其简略,设置好参数之后信号会通过如下流程:

固定数字增益模式是最外围的模式,次要有如下两个方面值得咱们深刻学习:

语音检测模块 WebRtcAgc_ProcessVad 的根本思维

在实时通信的场景中,麦克风采集的近端信号中会存在远端的信号的成分,流程中会先通过 WebRtcAgc_ProcessVad 函数对远端信号进行剖析,在探测理论近端信号包络的时候须要剔除远端信号这个烦扰项,防止因残留的回声信号影响了近端信号包络等参数的统计。最传统的 VAD 会基于能量,过零率和噪声门限等指标辨别语音段和无话段,WebRTC AGC 中为粗略的辨别语音段提供了新的思路:

  1. 计算短时均值和方差,形容语音包络刹时变动,可能精确反映语音的包络,如 图 2 左红色曲线

    // update short-term estimate of mean energy level (Q10)
    tmp32 = state->meanShortTerm * 15 + dB;
    state->meanShortTerm = (int16_t)(tmp32 >> 4);
      
    // update short-term estimate of variance in energy level (Q8)
    tmp32 = (dB * dB) >> 12;
    tmp32 += state->varianceShortTerm * 15;
    state->varianceShortTerm = tmp32 / 16;
      
    // update short-term estimate of standard deviation in energy level (Q10)
    tmp32 = state->meanShortTerm * state->meanShortTerm;
    tmp32 = (state->varianceShortTerm << 12) - tmp32;
    state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
  2. 计算长时均值和方差,形容信号整体迟缓的变化趋势,勾画信号的“重心线”,比拟平滑有利于利用门限值作为检测条件,如 图 2 左蓝色曲线

    // update long-term estimate of mean energy level (Q10)
    tmp32 = state->meanLongTerm * state->counter + dB;
    state->meanLongTerm = WebRtcSpl_DivW32W16ResW16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
    // update long-term estimate of variance in energy level (Q8)
    tmp32 += state->varianceLongTerm * state->counter;
    state->varianceLongTerm = WebRtcSpl_DivW32W16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
  3. 计算 标准分数,形容短时均值与“重心线”的偏差,位于核心之上的局部能够认为产生语音流动的可能性极大;

    tmp32 = tmp16 * (int16_t)(dB - state->meanLongTerm);
    tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm);
    state->logRatio = (int16_t)(tmp32 >> 6);


    图 2 左:长短时均值与方差 右:输出与 vad 检测门限

    WebRtcAgc_ProcessDigital 如何对音频数据进行增益

  4. 个外围参数都是围绕固定数字增益模式开展的,咱们须要搞清楚的是 WebRTC AGC 中外围函数 – WebRtcAgc_ProcessDigital 是如何对音频数据进行增益的。
  5. 依据指定的 targetLevelDbfs 和 compressionGaindB,计算增益表 gainTable;

    /* 依据设置的指标增益与增益能力,计算增益表 gainTable */
    if (WebRtcAgc_CalculateGainTable(&(stt->digitalAgc.gainTable[0]), stt->compressionGaindB, stt->targetLevelDbfs, stt->limiterEnable, stt->analogTarget) == -1) {return -1;}

    这一步中增益表 gainTable 能够了解为对信号能量值(幅值的平方)的量化,咱们先固定 targetLevelDbfs,别离设置 compressionGaindB 为 3dB~15dB,所对应的增益表曲线如下,能够看到增益能力设置越大,曲线越高,如下图。

大家可能会好奇增益表 gainTable 的长度为什么只有 32 呢?32 其实示意的是一个 int 型数据的 32 位(short 型数据的能量值范畴为 [0, 32768^2] 能够用无符号 int 型数据表示),从高位到低位,为 1 的最高位具备最大的数量级称为整数局部 – intpart,后续数位组成小数局部称为 fracpart。因而 [0, 32768] 之间的任意一个数都对应数字增益表中的一个增益值。接下来咱们讲讲如何查表并利用增益值实现音量平衡。

/** 局部要害源码 */
/** 提取整数局部和小数局部 */
intPart = (uint16_t)(absInLevel >> 14);          // extract the integral part
fracPart = (uint16_t)(absInLevel & 0x00003FFF);  // extract the fractional part
......
/** 依据整数局部和小数局部生成数字增益表 */
gainTable[i] = (1 << intPart) + WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14);
  1. 依据输出信号包络在增益表 gainTable 中查找增益值,并利用增益到输出信号;

基于人耳的听觉曲线,AGC 中在利用增益是是分段的,一帧 160 个样本点会分为 10 段,每段 16 个样本点,因而会引入分段增益数组 gains,下述代码中形容了数字增益表与增益数组的关系,间接体现了查表的过程,其思维与计算增益表时类似,也是先计算整数局部与小数局部,再通过增益表组合计算出新的增益值,其中就蕴含了小数局部的弥补。

// Translate signal level into gain, using a piecewise linear approximation
    // find number of leading zeros
    zeros = WebRtcSpl_NormU32((uint32_t)cur_level);
    if (cur_level == 0) {zeros = 31;}
    tmp32 = (cur_level << zeros) & 0x7FFFFFFF;
    frac = (int16_t)(tmp32 >> 19);  // Q12.
    tmp32 = (stt->gainTable[zeros - 1] - stt->gainTable[zeros]) * frac;
    gains[k + 1] = stt->gainTable[zeros] + (tmp32 >> 12);

下述代码是依据分段增益数组 gains,右移 16 位后取得理论的增益值(之前计算增益表和增益数组都是基于样本点能量,这里右移 16 位能够了解成找到一个整数 α,使得信号幅度值 sample 乘以 α 最靠近 32768),间接乘到输入信号上(这里的输入信号在函数开始曾经被拷贝了输出信号)。

/** 增益数组 gains 作用到输入信号,实现音量平衡  */
 for (k = 1; k < 10; k++) {delta = (gains[k + 1] - gains[k]) * (1 << (4 - L2));
   gain32 = gains[k] * (1 << 4);
   // iterate over samples
   for (n = 0; n < L; n++) {for (i = 0; i < num_bands; ++i) {tmp32 = out[i][k * L + n] * (gain32 >> 4);
       out[i][k * L + n] = (int16_t)(tmp32 >> 16);
     }
     gain32 += delta;
   }
 }

咱们以 compressionGaindB = 12dB 的曲线为例,上图为计算的数字增益表 gainTable 的理论值,下图为右移 16 位之后失去的理论增益倍数。能够看到 compressionGaindB = 12dB 时,整数局部最大增益为 3,实践上增益 12dB 实际上是放大了 4 倍,这里整数局部最大能够乘上 3 倍,后续再由小数局部补充残余的 0~1.0 倍,从而能够避免爆音。简略举两个例子:

A. 幅度值为 8000 的数据,包络 cur_level = 8000^2 = 0x3D09000,通过 WebRtcSpl_NormU32 ((uint32_t) cur_level); 计算失去前置 0 有 6 个,查表失去整数局部增益为 stt->gainTable [6] = 3,即 8000 能够大胆乘以 3 倍,之后增益倍数小于 1.0 的局部由 fracpart 决定;

B. 幅度值为 16000 的数据,包络 cur_level = 16000^2 = 0xF424000,通过 WebRtcSpl_NormU32 ((uint32_t) cur_level); 计算失去前置 0 有 4 个,查表失去整数局部增益为 stt->gainTable [4] = 2,此时会发现 16000 * 2 = 32000,之后平衡到指标音量的过程由 limiter 决定,细节这里不开展。

简略说就是,[0, 32768] 中的任何一个数想要增益指定的分贝且后果又不超过 32768,都能在数字增益表 gainTable 中找到确定的元素满足这个要求。

对于指标增益 targetLevelDbfsLimiter 的利用在 WebRtcAgc_ProcessDigital 以及相干函数中均有体现,这里就不开展论述,大家能够走读源码深刻学习。

上面咱们用几个 case 来看看固定数字增益模式的成果和存在的问题,先固定设置 targetLevelDbfs = 1, compressionGaindB = 12。

1. 采集音量较小,平衡后改善不显著;

设施采集音量 – 24dB, 平衡后音量只有 – 12dB,整体音量听感上会感觉偏小;

2. 采集音量较大,底噪明显增强;

设施采集音量 – 9dB, 平衡后音量达到 – 1dB,整体音量听感上失常,但语音帧间起伏减小,次要是无话段的噪声局部失去较大晋升。这个状况下次要的问题就是当采集音量自身就比拟大时,如果环境噪声较大,且降噪能力不强时,一旦 compressionGaindB 设置较大,那么语音局部会被限度在 targetLevelDbfs,然而无话段局部底噪会失去全量的晋升,对端参会人能够听到显著的噪声。

3. 采集声音起伏较大(以人为拼接的由大到小的音频为例),平衡后仍然无奈改善;

自适应模仿增益 – AdaptiveAnalog

在讲自适应模仿增益之前,咱们须要明确 PC 端影响采集音量的性能:

  1. PC 端反对调节采集音量,调节范畴为 0~1.0,WebRTC 客户端代码外部映射到了 0~255;

    /** 以 mac 为例,麦克风灵敏度被转成了 0~255 */
    int32_t AudioMixerManagerMac::MicrophoneVolume(uint32_t& volume) const {
      ......
     // vol 0.0 to 1.0 -> convert to 0 - 255
     volume = static_cast<uint32_t>(volFloat32 * 255 + 0.5);
     ......
      return 0;
    }
  2. 绝大多数 windows 笔记本设施内置了麦克风阵列,并提供麦克风阵列加强算法,降噪的同时还会额定提供 0~10dB 的增益(不同机型范畴不同,联想的设施增益高达 36dB),如图 3;

    图 3 左:MAC 端模仿增益调节 右:Windows 端麦克风阵列自带的增益能力

因为管制音量的模块过多,导致 PC 端 AGC 算法更加敏感。线上很多客户设置的默认值并不合理,这会间接影响音视频通话的体验:

  1. 采集音量过大会导致噪声被显著晋升,人声爆音;

  1. 采集音量过大会导致播放的信号回采到麦克风之后有较大的非线性失真,对回声打消算法是不小的挑战;

  1. 采集音量过小,数字增益能力无限导致对端听不清;

绝大多数用户在察觉到声音异样后并不知道 PC 设施还具备手动调节采集增益的性能,依赖于线上用户(尤其是教育场景很多是小学生)本人去调节模仿增益值简直不可能,将模仿增益值动静调节的性能做到 AGC 算法外部更可行,配合数字增益局部将近端信号平衡到现实的地位,因而,WebRTC 科学家开发设计了自适应模仿增益模式,通过反馈机制来调节原始采集音量,指标就是与数字增益模块相互配合,找到最合适的麦克风增益值并反馈给设施层,使得近端数据再通过数字增益之后达到目标增益,音频数据流框图如下:

在固定数字增益的根底上次要有两处新增:

  1. 在数字增益之后,新增了模仿增益更新模块:WebRtcAgc_ProcessAnalog,会依据以后模仿增益值 inMicLevel(WebRTC 中将尺度映射到 0~255)等两头参数,计算下一次须要调节的模仿增益值 outMicLevel,并反馈给设施层。

    // Scale from VoE to ADM level range.
    uint32_t new_voe_mic_level = shared_->transmit_mixer()->CaptureLevel();
    if (new_voe_mic_level != voe_mic_level) {
     // Return the new volume if AGC has changed the volume.
     new_mic_volume = static_cast<int>((new_voe_mic_level * max_volume +static_cast<int>(kMaxVolumeLevel / 2)) / kMaxVolumeLevel);
     return new_mic_volume;
    }
  2. 有些设施商麦克风阵列默认设置比拟小,即便将模仿增益调满采集仍然很小,此时就须要数字增益弥补局部来改善:WebRtcAgc_AddMic,能够在原始采集的根底上再放大 1.0~3.16 倍,如图 4。那么,如何判断放大不够呢?上一步中模仿增益更新模块最终输入理论为 micVol 与最大值 maxAnalog(255) 之间较小的那个:

    *outMicLevel = WEBRTC_SPL_MIN(stt->micVol, stt->maxAnalog) >> stt->scale;

    即依据相干的规定计算失去的理论值 micVol 是有可能大于规定的最大值 maxAnalog 的,也就意味着将模仿增益调整到最大也无奈达到目标音量,WebRtcAgc_AddMic 会监控这种事件的产生,并会通过查表的形式给予额定的弥补。

增益表 kGainTableAnalog:

static const uint16_t kGainTableAnalog[GAIN_TBL_LEN] = {
    4096, 4251, 4412, 4579,  4752,  4932,  5118,  5312,  5513,  5722, 5938,
    6163, 6396, 6638, 6889,  7150,  7420,  7701,  7992,  8295,  8609, 8934,
    9273, 9623, 9987, 10365, 10758, 11165, 11587, 12025, 12480, 12953};
// apply gain
sample = (in_mic[j][i] * gain) >> 12; // 通过右移之后,数组被量化到 0~3.16.

图 4 增益表的增益曲线

每次以 1 的固定步长弥补输出信号,gainTableIdx = 0 示意放大倍数为 1 倍,即什么也不做。

/* Increment through the table towards the target gain.
 * If micVol drops below maxAnalog, we allow the gain
 * to be dropped immediately. */
if (stt->gainTableIdx < targetGainIdx) {stt->gainTableIdx++;} else if (stt->gainTableIdx > targetGainIdx) {stt->gainTableIdx--;}
gain = kGainTableAnalog[stt->gainTableIdx];
// apply gain
sample = (in_mic[j][i] * gain) >> 12;

存在的问题:

  1. 无语音状态下的模仿值上调行为;
  2. 调整幅度过大,造成显著的声音起伏;
  3. 频繁调整操作系统 API,带来不必要的性能耗费,重大的会导致线程阻塞;
  4. 数字局部增益能力无限,无奈与模仿增益造成互补;
  5. 爆音检测不是很敏感,不能及时下调模仿增益;
  6. AddMic 模块精度不够,弥补过程中存在爆音的危险爆音。

    自适应数字增益 – AdaptiveDigital

    基于音频视频通信的娱乐、社交、在线教育等畛域离不开多种多样的智能手机和平板设施,然而这些挪动端并没有相似 PC 端调节模仿增益的接口。声源与设施的间隔,声源音量以及硬件采集能力等因素都会影响采集音量,单纯依赖固定数字增益成果非常无限,尤其是多人会议的时候会显著感触到不同谈话人的音量并不统一,听感上音量起伏较大。

为了解决这个问题,WebRTC 科学家仿照了 PC 端模仿增益调节的能力,基于模仿增益框架新增了虚构麦克风调节模块:WebRtcAgc_VirtualMic,利用两个长度为 128 的数组:增益曲线 – kGainTableVirtualMic 和克制曲线 – kSuppressionTableVirtualMic 来模仿 PC 端模仿增益(增益局部为枯燥递增的直线,克制局部为枯燥递加的凹曲线),前者提供 1.0~3.0 倍的增益能力,后者提供 1.0~0.1 的下压能力。

图 5 增益曲线与克制曲线

外围逻辑逻辑与自适应模仿增益统一。

  1. 与自适应模式增益模式一样,仍然利用 WebRtcAgc_ProcessAnalog 更新 micVol;
  2. 依据 micVol 在 WebRtcAgc_VirtualMic 模块中更新增益下标 gainIdx,并查表失去新的增益 gain;

    /* 设置冀望的音量程度 */
      gainIdx = stt->micVol;
      if (gainIdx > 127) {gain = kGainTableVirtualMic[gainIdx - 128];
      } else {gain = kSuppressionTableVirtualMic[127 - gainIdx];
      }
  3. 利用增益 gain,期间一旦检测到饱和,会逐渐递加 gainIdx;

    /* 饱和检测更新增益 */
    if (tmpFlt > 32767) {
     tmpFlt = 32767;
     gainIdx--;
     if (gainIdx >= 127) {gain = kGainTableVirtualMic[gainIdx - 127];
     } else {gain = kSuppressionTableVirtualMic[127 - gainIdx];
     }
    }
    if (tmpFlt < -32768) {
     tmpFlt = -32768;
     gainIdx--;
     if (gainIdx >= 127) {gain = kGainTableVirtualMic[gainIdx - 127];
     } else {gain = kSuppressionTableVirtualMic[127 - gainIdx];
     }
    }
  4. 增益后的数据传入 WebRtcAgc_AddMic,查看 micVol 是否大于最大值 maxAnalog 决定是否须要激活额定的弥补。

音频数据流框图如下:

存在的问题与自适应模式增益类似,这里须要明确说的一个问题是数字增益自适应调节灵敏度不高,当输出音量起伏时容易呈现块状拉升或压缩,用一个比拟显著的例子阐明:遇到大音量时须要调用压缩曲线,如果前面紧跟较小音量,会导致小音量进一步压缩,接着会调大增益,此时小音量后续如果接着跟大音量,会导致大音量爆音,须要 limiter 参加压限,对音质是存在失真的。

总结与优化方向

为了更好的听感体验,AGC 算法的指标就是疏忽设施采集差别,仍然可能将推流端音频音量平衡到现实地位,杜绝音量小、杜绝爆音、解决多人混音后不同人声音量起伏等外围问题。

针对上述章节提到的各个模式存在的问题,有如下几点启发:

  1. 模仿增益调节,必须修复调节频繁,步长过大等问题;
  2. AddMic 局部精度不够,能够提前预判,不要等到检测到爆音再回调;
  3. PC 端数字增益和模仿增益模块上是互相独立的,然而成果上应该是互相弥补的;
  4. AGC 对音量的平衡不应该影响 MOS,不能因为谋求灵敏度放弃了 MOS。

另外,代码中很多位运算初读起来比拟容易劝退,心愿大家抓外围代码,造成整体框架后多实际,再排汇消化。

最初,让咱们看看优化后的成果:

  1. 模仿增益调节之后,采集的音频信号音量存在起伏,通过数字局部平衡后音频包络放弃较好,音量整体统一;
  2. 语音和环境中的杂音,通过 AGC 之后语音局部音量起伏减小,杂音局部未见显著晋升;
  3. 一个比拟极其的 case,小语音局部最大晋升了 35dB,收敛工夫放弃在 10s 以内。

「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实际技术文章,在这里与音视频畛域一流工程师交换切磋。公众号后盾回复【技术】可退出阿里云视频云技术交换群,和作者一起探讨音视频技术,获取更多行业最新信息。

退出移动版