Audio算法介绍(SSL&BF&NR&AEC&SE)

1. 概述

APC(Audio Process Chain)音频处理链路,是一个包含降噪、均衡器和自动增益控制的算法组合。APC 的主要目的是提高音频质量。APC 的内部的算法串接流程为 ANR → EQ/HPF → AGC/DRC。透过降噪消除噪声提升 SNR,再根据客户所需曲线调整 EQ/HPF,最后输出透过 AGC/DRC 放大或压抑。

音频算法信号处理链路主要对音频输入和输出进行处理,其流程框图如下所示:

alg

其他非信号处理的算法如声音事件检测(Sound Event Detection, SED),语音唤醒(Keyword Spotting, KWS),命令词(Speech Commands,SC),语音变换(Voice Conversion, VC)等未在图中画出,可以根据实际情况在图中对应结点处插入相关算法,比如对于KWS算法可以采用SSL+BF+KWS链路。

2. APC 模块

APC 整个模块支持单模块使用,也支持多模块组合使用,组合使用的时候,只需要一个 apc_handle 即可

组合使用流程:

    handle = IaaApc_Init((char *)working_buf_ptr, &apc_init, &apc_switch);
    ... ...
    if(IaaApc_Config(handle, &anr_config, &eq_config, &hpf_config, NULL, NULL, &agc_config) == -1)
    {
        printf("Config Error!");
        return -1;
    }
    ... ...
    ret = IaaApc_Run(handle, buffer);
    ... ...
    IaaApc_Free(handle);

2.1 ANR

ANR(Acoustic Noise Reduction),降噪可以降低语音信号中从环境中持续出现一段时间并持续存在的背景噪声。

2.1.1 调用流程

    IaaAnr_Init                // 获取 handle
    IaaAnr_Config              // config 配置参数
        |
        V
    IaaAnr_Run                 // playback 过程中,调用算法处理
        |
        V
    IaaAnr_Free                // 释放算法占用资源

2.1.2 注意事项

2.1.3 调试方法

  1. 初始化

    AudioApcBufferConfig apc_switch;
    apc_switch.anr_enable   = 1;
    int Buffer_size         = IaaAnr_GetBufferSize();
    char *working_buf_ptr   = (char*)malloc(Buffer_size);
    
    int PN=128;
    apc_init.point_number   = PN;
    apc_init.channel        = 1;
    apc_init.sample_rate    = IAA_APC_SAMPLE_RATE_16000;
    
    anrHandle = IaaAnr_Init((char *)working_buf_ptr, &apc_init);
    if(anrHandle == NULL)
    {
        printf("ANR init error\r\n");
        return -1;
    }
    else
    {
        printf("ANR init succeed\r\n");
    }
    
    • anr_enable:是否使能 ANR 算法,当 anr_enable 为 FALSE 时,其他的 ANR 参数也不会起作用。
  2. 配置参数

    int intensity_band[6]     = {3,24,40,64,80,128};
    int intensity[7]         = {0,30,0,30,0,30,0};
    anr_config.anr_enable = apc_switch.anr_enable;
    anr_config.user_mode = 1;
    anr_config.anr_filter_mode = 4;
    memcpy(anr_config.anr_intensity_band, intensity_band, 6*sizeof(int));
    memcpy(anr_config.anr_intensity, intensity, 7*sizeof(int));
    anr_config.anr_smooth_level = 10;
    anr_config.anr_converge_speed = 2;
    
    if(IaaAnr_Config(anrHandle, &anr_config) != ALGO_ANR_RET_SUCCESS)
    {
        printf("ANR Config Error!");
        return -1;
    }
    
    • anr_enable:是否使能Anr算法。当anr_enable为FALSE时,其他的Anr参数也不会起作用。

    • user_mode:Anr算法运行的模式。0,表示Anr算法完全不使用其他的Anr参数,而是使用Anr算法内部的设定;1或2表示完全使用应用下的Anr参数

    • anr_intensity_band:降噪频率范围,后一个元素必须大于等于前1个元素。

      如:u32NrIntensityBand[0] = 10, 则:u32NrIntensityBand[1]必须大于等于10。

      当前采样率对应的最高频率平均分成 128 份,频率范围则是对应多少份组成一个频带。

      如:当前采样率为16K,对应的最大频率为8K,每一份为8000 / 128 ≈ 62.5Hz。

      如在{4,6,36, 49,50,51}的设定下,降噪频率范围为{0 ~ 4 * 62.5Hz, 4 ~ 6 * 62.5Hz, 6 ~ 36 * 62.5Hz, 36 ~ 49 * 62.5Hz, 49 ~ 50 * 62.5Hz, 50 ~ 51 * 62.5Hz, 51-127 * 62.5Hz} = {0 ~ 250Hz, 250 ~ 375Hz, 375 ~ 2250Hz, 2250 ~ 3062.5Hz, 3062.5 ~ 3125Hz, 3125 ~ 3187.5Hz, 3187.5Hz ~ 8000Hz},

    • anr_intensity:为降噪强度,可根据anr_intensity_band的频带划分,针对各个频带的噪声情况设置不同的参数。

      NR强度支持7个不同频段;强度30是最高级别的噪声抑制,但它会带来更多细节损失/损坏。建议您观察输入数据的频谱,判断哪个频段需要更大的强度。与参数 NR Intensity Frequency Band一起,您可以选择所需的任何频率区域。

    • anr_smooth_level:Anr算法处理频域的平滑程度,避免噪声估计时,在相邻频率上的压抑差距过大导致声音损伤。

      信号频域平滑电平;值越大,信号越平滑。取值范围:[0, 10]。 最小单位为 1。建议值为 10。

    • anr_converge_speed:Anr算法的收敛速度,更新噪声的速度快慢,设定越快降噪收敛越快,但副作用是带来细节的丢失/损伤。

      噪声估计更新速度;然而,较大的值会导致NR检测噪声更快,但代价是更多的细节损失/损坏。取值范围:[0, 1, 2]。 建议值为 1。

    • anr_filter_mode:指定Anr算法。0为原算法,1 ~ 4为不同噪音估测之降噪算法。

      0:默认噪声估计模式

      1:修正噪声估计模式

      2:积极噪声估计类型 1 模式

      3:积极噪声估计类型 2 模式

      4:快速噪声估计模式

      5:深度学习模式模式

      0 到模式 4基于传统的信号处理方法。模式 5 基于深度学习方法。考虑到计算成本从大到小依次为模式5、模式1、模式2、模式3、模式0和模式4。推荐模式为模式0。在某些场景下,如冲击噪声和非平稳噪声,推荐模式为5。

      注意:如果 NR Filter Mode为5,NR Converge Speed应设置为2。5: Deep learning mode

  3. 运行

    即在 playback 把 data 送给驱动 alsa 前,把 data 先送给 IaaAnr_Run 算法

    • 入参:

      buffer:输入数据的 buffer bytes:输入数据的字节长度

    • 返回:

      ret:成功的时候,返回更新后的数据 buffer

      ​ 失败的时候,请参考 API 错误码

      channelCount = apcInfo->apcInit.channel;
      count = bytes / (2 * PN * channelCount);
      for (j = 0; j < count; j++)
      {
          ret = IaaAnr_Run(apcInfo->anrHandle, (short*)buffer + j * PN * channelCount);
          if (ret != ALGO_ANR_RET_SUCCESS)
          {
              printf("anr run error: 0x%x\n", ret);
          }
      }
      

    注意:ANR 算法运行后,会直接更新源数据 buffer 后返回

  4. 释放资源

    if (apcInfo->anrHandle) {
        ALGO_ANR_RET anrRet = IaaAnr_Free(apcInfo->anrHandle);
        if (anrRet != ALGO_ANR_RET_SUCCESS) {
            printf("anr free error: 0x%x", anrRet);
        }
    }
    if (apcInfo->anrWorkingBuffer) {
        free(apcInfo->anrWorkingBuffer);
        apcInfo->anrWorkingBuffer = NULL;
    }
    

    注意:必须先调用 IaaAnr_Free,再释放供 ANR 算法所使用的内存。

2.2 AGC

AGC(Automatic Gain Control),自动增益控制,用于控制数字输出增益。原理是根据输入音量大小计算对应的增益,使输出稳定。

2.2.1 调用流程

IaaAgc_Init                // 获取 handle
IaaAgc_Config              // config 配置参数
    |
    V
IaaAgc_Run                 // playback 过程中,调用算法处理
    |
    V
IaaAgc_Free                // 释放算法占用资源

2.2.2 注意事项

  • 压缩率曲线

    压缩比曲线是输入功率电平(dBFS)和输出功率电平(dBFS)之间的关系(比率)。压缩率会压缩音频的动态范围。 您最多可以在输入输出功率电平的关系坐标上设置五个坐标点来确定您想要的曲线。这七个包含六个转折点的点可以确定六个不同的斜率。例如,如果压缩比设置如下:

    compression_ratio_input[7] = {-80, -60, -50,-40,-30,-12,0}
    compression_ratio_output[7] = {-80, -45, -36, -27, -18, -9, -6}
    

    即表示:当输入为-80 dBFS时,Target Level将设置为-80 dBFS;当输入为-60 dBFS时,目标电平将设置为-45 dBFS,依此类推。

    如果不需要六个转折点,可以将其余参数设置为零:

    compression_ratio_input[7] =  {-70, -60, -30, 0,0,0,0};
    compression_ratio_output[7] =     {-60, -50, -10, -3, 0,0,0};
    
  • Noise Gate Threshold

    低于 Noise Gate Threshold 值时,AGC会逐渐将增益降为0,下降速度根据 attack time 相同。设置 Noise Gate Threshold 之前请先测量纯噪声信号功率电平,并设置合适的噪声门阈值,AGC会得到更好的效果。Noise Gate Threshold 的优先级高于压缩率曲线。无论压缩率曲线设置多少,当输入的功率电平低于噪声门阈值时,信号都会被判断为噪声。最小单位为1dB。取值范围:[-80dBFS, 0dBFS]。建议值为-60 dBFS。

    如果用户将噪声门阈值设置为正值,在相应的噪声门值以下增益将于上一帧相同。例如,如果用户将噪声门设置为 60,如果输入数据的功率低于-60 dBFS,增益将保持不变。

    用户也可以通过设置指定的曲线来达到想要的效果。如果功率低于-60dBFS噪声门限,我们的 AGC 将维持上一帧的增益。

  • Attack Time

    两次增益递减之间的最短时间。最小单位为 4ms。这意味着增益将在帧与帧之间降低 0.5dB。建议值为1。如果设置为2,Attack time为 8ms,以此类推。

  • Release Time

    两次增益上升的最短时间。最小单位为 4ms。这意味着增益将在帧与帧之间增加 0.5dB。 建议值为5。如果设置为2,Release time为 8ms,以此类推。

    如果 Release Time 设置的太短,增益会快速增加。 它可能会导致高机率截波。

  • Drop Gain Max

    指防止输出饱和的增益下降最大值。由于AGC会在帧与帧之间平滑数据,因此不确定每个点是否都没有被截波。 所以我们建议您同时透过压缩率曲线和释放时间,以确保没有数据截波。 最小单位为 1dB。 值范围:[0 dB, 60 dB]。 建议值为 12 dB。

    如果Drop Gain Max值设置得太大,帧和帧之间的连接处可能会出现明显的不平滑连接。另一方面,如果 Drop Gain Max值设置得太小,输出可能会有很多截波点。因此,您应该谨慎调整此参数以满足要求。

  • Noise Gate Attenuation

    噪声门衰减是指输入功率在 Noise gate 以下时衰减的百分比。 当输入信号的功率低于Noise gate时,信号会立即衰减指定百分比。

    最小单位为1。取值范围:[0, 100]。建议值为0。我们建议您避免使用噪声门衰减,以防止输出信号听起来像是连接中断。此外,语音的尾音和起始音也高机率会被衰减,使语音变得不自然。

  • Gain Step

    增益上升或下降的速度. 如果设为1, 帧与帧之间升降增益的单位是 ±0.5dB。

    若设定值越高,音量升降速度越快,最小单位为1。取值范围:[0, 10]。建议值为1。

  • user_mode

    当用户设置模式为 2 时,我们的 AGC 支持按频段调整增益。用户最多可以设置不同的 3 个频段。例如:

    int freqBand[3] = {3000,6000,8000};
    IaaApc_SetAgcFreqBand(handle, freqBand);
    

    然后,用户可以为每个频段设置不同的压缩比曲线,如下示例代码:

    int compressionRatioArrayLowInput[7]   = {-80,-60,-40,-20,0,0,0};
    int compressionRatioArrayLowOutput[7]  = {-5,-5,-5,-5,-5,-5,-5};
    int compressionRatioArrayMidInput[7]   = {-80,-60,-40,-20,0,0,0};
    int compressionRatioArrayMidOutput[7]  = {-80,-60,-40,-20,0,0,0};
    int compressionRatioArrayHighInput[7]  = {-80,-60,-40,-20,0,0,0};
    int compressionRatioArrayHighOutput[7] = {-80,-60,-40,-20,0,0,0};
    
    IaaApc_SetLowFreqCompressionRatioCurve(handle, compressionRatioArrayLowInput, compressionRatioArrayLowOutput);
    IaaApc_SetMidFreqCompressionRatioCurve(handle, compressionRatioArrayMidInput, compressionRatioArrayMidOutput);
    IaaApc_SetHighFreqCompressionRatioCurve(handle, compressionRatioArrayHighInput, compressionRatioArrayHighOutput);
    

    AGC 会对不同的频段应用不同的输入输出关系曲线。

2.2.3 调试方法

  1. 初始化

    AudioApcBufferConfig apc_switch;
    apc_switch.agc_enable   = 1;
    int Buffer_size         = IaaAgc_GetBufferSize();
    char *working_buf_ptr   = (char*)malloc(Buffer_size);
    
    int PN=128;
    apc_init.point_number   = PN;
    apc_init.channel        = 1;
    apc_init.sample_rate    = IAA_APC_SAMPLE_RATE_16000;
    
    agcHandle = IaaAgc_Init((char*)working_buf_ptr, &apc_init);
    if(agcHandle==NULL)
    {
        printf("AGC init error\r\n");
        return -1;
    }
    else
    {
        printf("AGC init succeed\r\n");
    }
    
    • agc_enable:是否使能 AGC 算法,当 agc_enable 为 FALSE 时,其他的 AGC 参数也不会起作用。
  2. 配置参数

    short compression_ratio_input[_AGC_CR_NUM] = {-65,-55,-48,-25,-18,-12,0};
    short compression_ratio_output[_AGC_CR_NUM] = {-65,-50,-27,-12,-1,-1,-1};
    int compressionRatioArrayLowInput[_AGC_CR_NUM]         = {-80,-60,-40,-20,0,0,0};
    int compressionRatioArrayLowOutput[_AGC_CR_NUM] = {-5,-5,-5,-5,-5,-5,-5};
    int compressionRatioArrayMidInput[_AGC_CR_NUM] = {-80,-60,-40,-20,0,0,0};
    int compressionRatioArrayMidOutput[_AGC_CR_NUM] = {-80,-60,-40,-20,0,0,0};
    int compressionRatioArrayHighInput[_AGC_CR_NUM] = {-80,-60,-40,-20,0,0,0};
    int compressionRatioArrayHighOutput[_AGC_CR_NUM] = {-80,-60,-40,-20,0,0,0};
    
    agc_config.agc_enable            = apc_switch.agc_enable;
    agc_config.user_mode             = 2;
    agc_config.gain_info.gain_max    = 40;
    agc_config.gain_info.gain_min    = -10;
    agc_config.gain_info.gain_init   = 12;
    agc_config.drop_gain_max         = 36;
    agc_config.gain_step             = 1;
    agc_config.attack_time           = 1;
    agc_config.release_time          = 1;
    agc_config.noise_gate_db         = -80;
    memcpy(agc_config.compression_ratio_input, compression_ratio_input,_AGC_CR_NUM*sizeof(short));
    memcpy(agc_config.compression_ratio_output, compression_ratio_output,_AGC_CR_NUM*sizeof(short));
    agc_config.noise_gate_attenuation_db   = 0;
    agc_config.drop_gain_threshold         = -5;
    
    ALGO_AGC_RET agcRet = IaaAgc_Config(agcHandle, &agc_config);
    if (agcRet != ALGO_AGC_RET_SUCCESS) {
        printf("agc config error: 0x%x", agcRet);
    }
    
    agcRet = IaaAgc_SetLowFreqCompressionRatioCurve(agcHandle,
                                                    compressionRatioArrayLowInput,
                                                    compressionRatioArrayLowOutput);
    if (agcRet != ALGO_AGC_RET_SUCCESS) {
        printf("agc low config error: 0x%x", agcRet);
    }
    
    agcRet = IaaAgc_SetMidFreqCompressionRatioCurve(agcHandle,
                                                    compressionRatioArrayMidInput,
                                                    compressionRatioArrayMidOutput);
    if (agcRet != ALGO_AGC_RET_SUCCESS) {
        printf("agc mid config error: 0x%x", agcRet);
    }
    
    agcRet = IaaAgc_SetHighFreqCompressionRatioCurve(agcHandle,
                                                    compressionRatioArrayHighInput,
                                                    compressionRatioArrayHighOutput);
    if (agcRet != ALGO_AGC_RET_SUCCESS) {
        printf("agc high config error: 0x%x", agcRet);
    }
    
    • agc_enable:是否使能 Agc 算法,当 agc_enable 为 FALSE 时,其他的 Agc 参数也不会起作用。

    • user_mode:Agc 算法的运行模式。0,表示Agc算法完全不使用其他的Agc参数,而是使用Agc算法内部的设定;1,表示完全使用应用下的Agc参数。

    • gain_info:Agc 算法的增益信息,定义 AGC 增益的最大、最小和初始值

      • gain_max:增益最大值 范围 [0,60];步长1
      • gain_min:增益最小值 范围 [-20,30];步长1
      • gain_init:增益初始值 范围 [-20,60];步长1
    • drop_gain_max:瞬间增益下降的最大值,防止输出饱和,若输出加上当前 Gain 超出 drop_gain_threshold 所设定的 dB 值,Agc 会瞬间降低 Gain 避免当前信号的峰值超过 drop_gain_threshold。 范围[0,60];步长1 注意:此值仅代表能下降的范围,但具体能下降到何值还需参考AGC增益的最小值。

    • attack_time:增益下降的时间步长,以4毫秒为1单位,若设定2则为8毫秒判断一次是否降Gain 范围[1,20];步长1

    • release_time:增益增加的时间步长,以4毫秒为1单位,若设定2则为8毫秒判断一次是否升Gain 范围[1, 20] ;步长1

    • compression_ratio_input:配合 compression_ratio_output 使用,透过多个转折点实现多斜率的曲线,得到input power level跟output power level之间的关系。 范围[-80,0]dBFS;步长1

    • compression_ratio_output:配合 compression_ratio_input使用,透过多个转折点实现多斜率的曲线,得到input power level跟output power level之间的关系。 范围[-80,0]dBFS;步长1

    • drop_gain_threshold:衰减阈值,当信号峰值幅度超过此值后,会瞬间衰减,衰减幅度受drop_gain_max跟gain_info所限制。 范围[-80,0]dB;步长1

    • noise_gate_db:噪声阈值,当信号小于此值时,当作噪声处理, Case1: 若设定noise_gate_db 从-80到0,当前gain值会根据release/attack time的时间将Gain值收敛成0。 Case2: 若设定noise_gate_db 从1到80,当信号小于此值时,Gain值将不会做更改,会保留前一帧的Gain值。 范围[-80,80];步长1

    • noise_gate_attenuation_db:当噪声阈值起效果时,输入源的衰减百分比 范围[0,100];步长1

      Noise gate设定有两种模式,可以将Gain收敛到0或维持Gain值不变,收敛到0的状况可避免小于noise gate的信号被放大,维持噪声稳定度,维持Gain值不变可避免语音的起头与尾音消失的"呼吸现象"。

    • gain_step:套用增益的速率,以0.5dB为一个单位,若设定为1,则每帧依照需求套用±0.5dB。 此值设定的越高,拉升和降低音量的速率越快。 范围[1,10]; 步长1

  3. 运行

    即在 playback 把 data 送给驱动 alsa 前,把 data 先送给 IaaAgc_Run 算法

    • 入参:

      buffer:输入数据的 buffer bytes:输入数据的字节长度

    • 返回:

      ret:成功的时候,返回更新后的数据 buffer

      ​ 失败的时候,请参考 API 错误码

      channelCount  = apcInfo->apcInit.channel;
      count         = bytes / (2 * PN * channelCount);
      for (j = 0; j < count; j++)
      {
          ret = IaaAgc_Run(apcInfo->agcHandle, (short*)buffer + j * PN * channelCount);
          if (ret != ALGO_AGC_RET_SUCCESS)
          {
              printf("agc run error: 0x%x\n", ret);
          }
      }
      

    注意:AGC 算法运行后,会直接更新源数据 buffer 后返回

  4. 释放资源

    if (apcInfo->agcHandle) {
        ALGO_AGC_RET agcRet = IaaAgc_Free(apcInfo->agcHandle);
        if (agcRet != ALGO_AGC_RET_SUCCESS) {
            printf("agc free error: 0x%x", agcRet);
        }
    }
    if (apcInfo->agcWorkingBuffer) {
        free(apcInfo->agcWorkingBuffer);
        apcInfo->agcWorkingBuffer = NULL;
    }
    

    注意:必须先调用 IaaAgc_Free,再释放供 AGC 算法所使用的内存。

2.3 DRC

DRC(Dynamic Range Control),动态范围控制。用来调整音频信号的动态范围,保证信号不至于过大或过小。根据输入信号的大小,对其施加一个增益:当检测到大信号时,施加一个负向增益,以降低信号幅值;当检测到小信号时,施加一个正向增益,以提升信号幅值。

DRC 相关配置参数项是集合在 AGC 算法中,使用以下项来调试:

  • Drop gain threshold:

    是指设置峰值允许输出幅度的值 (dBFS)。在音频原始数据加上数字增益后,我们的 DRC 将估计峰值是否会超过此阈值。如果峰值超过阈值,数字增益将立即下降以防止截波。增益下降的最大值由参数 Drop Gain Max 决定。

  • Drop Gain Max:

    是指防止输出饱和的增益下降最大值。由于 DRC 会在帧与帧之间平滑数据,因此不确定每个点是否都没有被截波。 所以我们建议您同时透过压缩率曲线和释放时间,以确保没有数据截波。 最小单位为 1dB。 值范围:[0 dB, 60 dB]。 建议值为 12 dB。

    注意:如果输出信号数据的截波点过多,可以通过增加 Drop Gain Max 或降低 Compression Curve 的斜率或增加 Release Time。

    如果 Drop Gain Max 值设置得太大,帧和帧之间的连接处可能会出现明显的不平滑连接。另一方面,如果 Drop Gain Max 值设置得太小,输出可能会有很多截波点。因此,您应该谨慎调整此参数以满足要求。

2.4 EQ

EQ(Equalizer),均衡器处理,用于对特定频段进行增益或衰减。原理是通过对不同频段进行增益调节。

2.4.1 调用流程

IaaEq_Init                // 获取 handle
IaaEq_Config              // config 配置参数
    |
    V
IaaEq_Run                 // playback 过程中,调用算法处理
    |
    V
IaaEq_Free                // 释放算法占用资源

2.4.2 注意事项

请设置数组 eq_gain_db[_EQ_BAND_NUM],表示 EQ 频段的增益调整,然后将频率分成 129 等份。

每个值代表: 8k / 129 = 62.015 Hz,8 kHz频段仅在采样频率(s32WorkSampleRate)设置为16 kHz时有效。 每个频段的取值范围为 [–100, +20] dB。 例如,如果您需要将 -10DB 从 1560 Hz 降低到 3000 Hz,则设置 eq_gain_db 数组索引, floor(1560/62.015) 至 ceil(3000/62.015) 增益为 -10DB

2.4.3 调试方法

  1. 初始化

    AudioApcBufferConfig apc_switch;
    apc_switch.eq_enable    = 1;
    int Buffer_size         = IaaEq_GetBufferSize();
    char *working_buf_ptr   = (char*)malloc(Buffer_size);
    
    int PN=128;
    apc_init.point_number   = PN;
    apc_init.channel        = 1;
    apc_init.sample_rate    = IAA_APC_SAMPLE_RATE_16000;
    
    eqHandle = IaaEq_Init((char *)working_buf_ptr, &apc_init);
    if(eqHandle==NULL)
    {
        printf("EQ init error\r\n");
        return -1;
    }
    else
    {
        printf("EQ init succeed\r\n");
    }
    
    • eq_enable:是否使能 Eq 算法,当 eq_enable 为 FALSE 时,其他的 Eq 参数也不会起作用。
  2. 配置参数

    short eq_table[129]  = { 1, 1, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5    };
    eq_config.eq_enable   = apc_switch.eq_enable;
    eq_config.user_mode   = 1;
    memcpy(eq_config.eq_gain_db, eq_table,_EQ_BAND_NUM*sizeof(short));
    
    if(IaaEq_Config(eqHandle, &hpf_config, &eq_config) != ALGO_EQ_RET_SUCCESS)
    {
        printf("EQ Config Error!");
        return -1;
    }
    
    • eq_enable:是否使能 Eq 算法,当 eq_enable 为 FALSE 时,其他的 Eq 参数也不会起作用。

    • user_mode:Eq 算法的运行模式。0,表示Eq使用预设的参数,预设参数全为0,不做任何增益或衰减;1,表示完全使用应用下的Eq参数。

    • eq_gain_db:Eq 算法增益调节取值,将当前采样率的频率范围分成129个频率范围来进行调节,单位为1dB 范围[-50,20];步长1

      将当前采样率的频率范围分成129个频率范围来进行调节。如:当前采样率为16K,对应的最高频率为8K,8000 / 129 ≈ 62Hz,则单个调节的频率范围为62Hz,将0-8K划分成{0-1 * 62Hz,1-2 * 62Hz,2-3 * 62Hz,…,128-129 * 62Hz} = {0-62Hz,62-124Hz,124-186Hz,…,7938-8000Hz},每段对应一个增益值。

  3. 运行

    即在 playback 把 data 送给驱动 alsa 前,把 data 先送给 IaaEq_Run 算法

    • 入参:

      buffer:输入数据的 buffer bytes:输入数据的字节长度

    • 返回:

      ret:成功的时候,返回更新后的数据 buffer

      ​ 失败的时候,请参考 API 错误码

      channelCount = apcInfo->apcInit.channel;
      count = bytes / (2 * PN * channelCount);
      for (j = 0; j < count; j++)
      {
          ret = IaaEq_Run(apcInfo->eqHandle, (short*)buffer + j * PN * channelCount);
          if (ret != ALGO_EQ_RET_SUCCESS)
          {
              printf("eq run error: 0x%x\n", ret);
          }
      }
      

    注意:EQ 算法运行后,会直接更新源数据 buffer 后返回

  4. 释放资源

    if (apc_info->eqHandle) {
        ALGO_EQ_RET eqRet = IaaEq_Free(apc_info->eqHandle);
        if (eqRet != ALGO_EQ_RET_SUCCESS) {
            printf("eq free error: 0x%x", eqRet);
        }
    }
    if (apc_info->eqWorkingBuffer) {
        free(apc_info->eqWorkingBuffer);
        apc_info->eqWorkingBuffer = NULL;
    }
    

    注意:必须先调用 IaaEq_Free,再释放供 EQ 算法所使用的内存。

2.5 HPF

HPF(High-Pass Filtering),高通滤波。

2.5.1 调用流程

IaaEq_Init                // 获取 handle
IaaEq_Config              // config 配置参数
    |
    V
IaaEq_Run                 // playback 过程中,调用算法处理
    |
    V
IaaEq_Free                // 释放算法占用资源

2.5.2 注意事项

  • HPF 作为 EQ 模块中的一个配置参数项来配合使用
  • HPF 仅仅支持 8K/16K 采样率

2.5.3 调试方法

  1. 初始化

    HPF 在 init 的时候,使用的是 eq_handle,所以没有单独的 init 方法

  2. 配置参数

    hpf_config.hpf_enable       = apc_switch.eq_enable;
    hpf_config.user_mode        = 1;
    hpf_config.cutoff_frequency = AUDIO_HPF_FREQ_150;
    
    if(IaaEq_Config(eq_handle, &hpf_config, &eq_config) == -1)
    {
        printf("Config Error!");
        return -1;
    }
    
    • hpf_enable:是否使能 Hpf 算法

      当 hpf_enable 为 FALSE 时,其他的 Hpf 参数也不会起作用。

    • user_mode:Hpf 算法的运行模式,0,表示预设的参数,不做任何衰减;1,表示使用应用下的 Hpf 参数。

    • cutoff_frequency:Hpf 的截止频率,范围: [0, 1, 2, 3, 4]。低于此频率的信号会被过滤掉。

  3. 运行

    HPF 没有单独的 run 方法,它是作为 config 参数集成在 IaaEq_Run 里面的

  4. 释放资源

    HPF 在 config 的时候,使用的是 eq_handle,所以没有单独的 free 方法

3. AEC

AEC(Acoustic Echo Cancellation),声学回声消除算法是指防止讲话者听到自己声音回声的回声消除方法,通常指当扬声器设备播放的声音通过直接或间接路径传回麦克风时,会产生回声。 因此,远程的通话者在延迟后会听到他或她自己的声音,这称为声学回声 (acoustic echo)。原理是通过自适应滤波器估计回声,然后用麦克风输入信号减去估计的回声达到回声消除目的。

3.1 调用流程

    IaaAec_GetBufferSize       // 获取 buffer 大小
    IaaAec_Init                // 获取 handle
    IaaAec_Config              // config 配置参数
        |
        V
    IaaAec_Run                 // capture 过程中,调用算法处理
        |
        V
    IaaAec_Free                // 释放算法占用资源

3.2 注意事项

3.2.1 声学机构隔离测试

外壳设计在很大程度上决定了免持设备(例如,具有免持电话模式的电话)的性能。 即使用世界上最好的 AEC 软件,也会因为设备外壳设计不当,语音质量受到严重影响。

测试环境噪音不能超过40dbA。 请使用分贝计,设置为 FAST 并使用最小刻度。 SPK 级别和 MIC 级别应确保在 SPK 播放正常语音时录音文件不会被截波。使用者需要评估以下客观值,以验证机构外壳是否足以满足音频质量。

以下部分列出了一些主要建议,以确保硬件不会成为 AEC 性能方面的绊脚石。

例如,仅当麦克风和扬声器是宽带时,宽带语音编解码器支持才有意义。 同样,如果麦克风或扬声器在回声路径中若有任何非线性,则回声的线性模型将无法有效地消除回声; 如此的话,将在全双工条件下引起失真副作用。

3.2.1.1 机构设计建议
  • 扬声器:

    后腔是必须的。 请勿仅在产品外壳中使用扬声器。 扬声器外壳前部和后部之间的紧密连接将有助于减少从后腔到扬声器前部的声音传播。 扬声器应牢固地安装在前后腔之间的接口处,因为扬声器将作为后箱体结构的一部分。 扬声器的安全安装还将确保扬声器和外壳不会产生卡嗒卡嗒的声音。 将扬声器框架安装到外壳时通常使用高密度泡沫,建立安全且紧密的机构配置。 前腔孔应至少占扬声器面积的 20%,与扬声器框架的间距为 1-2 毫米。

  • 麦克风:

    确保麦克风紧密安装在麦克风信道中,通过直接路径减少扬声器-麦克风耦合来增加 ERL(回声回波损耗)。 除此之外,还可以将麦克风包裹在外壳内的单独外壳中。 麦克风应与孔对齐。

  • 麦克风与扬声器相对位置

    扬声器和麦克风之间的距离应尽可能远离。 如果距离太近,从麦克风记录的回声很有可能因为大大声而被削波。 为了在这种情况下保持 AEC 性能,用户应将扬声器的音量设置得较低,但这样声压级可能不符合客户需求,确保麦克风与扬声器间距以避免麦克风讯号截波。

3.2.1.2 外部隔离测试
  1. 禁用麦克风和扬声器算法(AEC、NR、AGC、EQ...)
  2. 使用参考 MIC 测量 DUT 位置的扬声器音量。 外部 SPK 播放白噪声。 音量必须满足 80dBSPL。
    • Ref MIC 使用 MIC 校准器测量 1kHz 94dBSPL 正弦波 = X dB RMS
    • 预期参考 MIC 测得 1kHz 80dBSPL = X -14 dB RMS
    • 修改外部 SPK的音量,使ref MIC测量到X-14 dB RMS
    • 完成 SPK 音量校准。
  3. DUT 记录来自外部 SPK = AVGRMS(X) 的 80dBSPL 白噪声
  4. 使用粘土密封 MIC 孔,然后 DUT 记录来自外部 SPK = AVG RMS(Y) 的 80dBSPL 白噪声
  5. 规格:AVG RMS(X) - AVG RMS(Y) > 20dB RMS
    // 麦克风校正方式
                                                    |\ _    监听音箱
    [receive 80dBSPL] Ref MIC |-------------------- | | |   White Noise
                                                    |/ —    (200Hz-10kHz)
                                  Any distance
    
    // 麦克风声学隔离测量方式
                       _____
                      |     |-MIC                      |\ _    监听音箱
    [receive 80dBSPL] | DUT |     |--------------------| | |   White Noise
                      |     |-SPK                      |/ —    (200Hz-10kHz)
                       —————       Keep the distance
    
3.2.1.3 内部隔离测试
  1. 禁用麦克风和扬声器算法(AEC、NR、AGC、EQ...)
  2. 使用距离 DUT 50cm 的参考 MIC 测量 DUT SPK 的音量。 DUT SPK 回放白噪声。 音量必须满足 80dBSPL。
    • Ref MIC 使用 MIC 校准器测量 1k 94dBSPL 正弦波 = X dBRMS
    • 预期参考 MIC 测得 1k 80dBSPL = X -14 dBRMS
    • 因此,Ref MIC 将在距离 DUT 50cm 处测量 X -14dBRMS
    • 修改 DUT SPK 增益,让 Ref MIC 测量 X-14 dBRMS
    • 完成SPK 音量校准。
  3. DUT 记录来自 DUT SPK = AVGRMS(X) 的白噪声
  4. 使用粘土密封 MIC 孔,然后 DUT 记录来自 DUT SPK = AVGRMS(Y) 的白噪声
  5. 规格:AVG RMS(X) - AVG RMS(Y) > 20dB RMS
    // 扬声器校正方式
                       _____
                      |     |-MIC
     Playback    ---> | DUT |
    White Noise       |     |-SPK  |--------------------| Ref MIC [receive 80dBSPL]
                       —————       Keep the distance:50cm
    
    // 内部声学隔离测量方式
                       _____
                      |     |-MIC  <---+
     Playback    ---> | DUT |          |
    White Noise       |     |-SPK   ---+
                       —————
    

3.2.2 ALSA 通路 attach 与数据操作

因为当前 ASOC 的 AI_MCH_SEL 都是 2 channel 一组的 attach,即 AI_MCH_01_SEL 中的 0、1 channel 只能是 ADC_A 中的 1 channel 或 2 channel,而不能为 ADC_A 中的 1 channle + ECHO_01 中的 1 channel。

这样,当 app 只需要 1 channel 数据的时候,实际 control attach 的是:AI_MCH_01_SEL -> ADC_A,AI_MCH_23_SEL -> ECHO_01,即 pcm_open 的时候需要 open 4 channel,pcm_read 数据的时候需要过滤丢弃 ⅓ channel,留下 mic 0/ echo 2 channel

其中切割合并数据操作,参考以下示例:

static void split_pcm(int16_t* src_buf, uint32_t frames, uint32_t src_channel, int16_t* dst1_buf, uint32_t dst1_channel,
                    int16_t* dst2_buf, uint32_t dst2_channel)
{
    uint32_t i = 0;
    uint32_t j = 0;
    uint32_t k = 0;

    for (i = 0; i < frames; i++)
    {
        for (j = 0; j < dst1_channel; j++)
        {
            dst1_buf[dst1_channel * i + j] = src_buf[src_channel * i + j];
        }
        if (src_channel != (dst1_channel + dst2_channel))
        {
            j = src_channel / 2;
        }
        for (k = 0; k < dst2_channel; k++)
        {
            dst2_buf[dst2_channel * i + k] = src_buf[src_channel * i + j];
            j++;
        }
    }
}
static void merge_pcm(int16_t* src1_buf, uint32_t src1_channel, int16_t* src2_buf, uint32_t src2_channel,
                    uint32_t frames, int16_t* dst_buf)
{
    uint32_t i           = 0;
    uint32_t j           = 0;
    uint32_t k           = 0;
    uint32_t dst_channel = src1_channel + src2_channel;

    for (i = 0; i < frames; i++)
    {
        for (j = 0; j < src1_channel; j++)
        {
            dst_buf[dst_channel * i + j] = src1_buf[src1_channel * i + j];
        }
        for (k = 0; k < src2_channel; k++)
        {
            dst_buf[dst_channel * i + j] = src2_buf[src2_channel * i + k];
            j++;
        }
    }
}
  • 若使用的输入为双声道,请依照左右声道交错排列的方式给讯号,例如:左/右/左/右...

  • 若设定为双声道,AEC会把两个声道平均为一个声道处理,并将处理后结果复制到两个声道,如需两声道分开,请初始化两个handle,分开执行 IaaAec_Run

3.3 调试方法

  1. 初始化

    aecInit.point_number       = PN;
    aecInit.nearend_channel    = channel_count;
    aecInit.farend_channel     = channel_count;
    aecInit.sample_rate        = get_aec_sample_rate(sample_rate);
    bufferSize                 = IaaAec_GetBufferSize();
    aecWorkingBuffer = (char*)malloc(bufferSize);
    aecHandle        = IaaAec_Init((char*)aecWorkingBuffer, &aecInit);
    if (aecHandle == NULL)
    {
        printf("AEC init error\r\n");
        return -1;
    }
    else
    {
        printf("AEC init succeed\r\n");
    }
    
    • point_number:Aec算法处理一次的采样点数,每一帧长度为 128 个采样点(8ms)
    • nearend_channel:近端的信道数(即麦克风的信道数),可设定单声道或双声道,范围:[1, 2]
    • farend_channel:远端的信道数(即喇叭的通道数),可设定单声道或双声道,范围:[1, 2]
    • sample_rate:Aec算法处理的采样率
  2. 配置参数

    unsigned int supMode_band[6]             = {20, 40, 60, 80, 100, 120};
    unsigned int supMode[7]                  = {4, 4, 4, 4, 4, 4, 4};
    aecConfig.delay_sample         = 0;
    aecConfig.comfort_noise_enable = IAA_AEC_FALSE;
    memcpy(&(aecConfig.suppression_mode_freq[0]), supMode_band, sizeof(int) * 6);
    memcpy(&(aecConfig.suppression_mode_intensity[0]), supMode, sizeof(int) * 7);
    
    ALGO_AEC_RET aecRet = IaaAec_Config(aecHandle, &aecConfig);
    if (aecRet != ALGO_AEC_RET_SUCCESS)
    {
        printf("aec config error: 0x%x", aecRet);
    }
    
    • comfort_noise_enable:在AEC后加入舒适噪声,避免回声消除后部分段落静音,让用户有断线的感觉。 若AEC后有开启Noise reduction,建议开启comfort noise,帮助NR收敛噪声。

      使能该参数后,AEC算法会在没有声音时添加一些舒适噪声。当客户对稳定的底噪有要求或AEC消得太干净导致NR收敛时间太长时,可使能该功能。

    • delay_sample:左右声道间的回声延迟样本 当Stereo cost down 开启时,可以根据麦克风摆放位置先行设定样本延迟数,减少双声道所需运算量。 默认为0,单位是sample数 取值范围:-9-9

      由于麦克风和喇叭的放置位置、麦克风间的距离等会造成左右声道接收到回声的时间点不一致,两个声道之间回声延迟有差异。此值表示左声道比右声道提早多少个采样点收到回声。可以调整此值来对齐左右声道的资料,设置时,请以左声道作为参考。例如左声道的回声比右声道的回声快4个样本,则s16DelaySample设置为4,反之设置为-4。

    • suppression_mode_freq:AEC处理的频段划分,将不同取样率所能解析的最高频率等分成128等分,设定六个值可切出七个段落设定不同AEC强度。 取值范围:1-127

      该数组将当前采样率的最高频率分成7个频段来处理。以下数组元素和频率范围的转换公式:

      (residual Echo Frequency Range) / (sampleRate / 2) * pointNumber(128)
      

      数组要求每一个元素都必须比前一个元素大。

    • suppression_mode_intensity:配合suppression_mode_freq所切割出来的七个段落分别设定七种强度。 取值范围:0-25

      该数组代表各个频段的回声消除强度,各个元素与u32AecSupfreq划分出来的7个频段一一对应。强度越大,消除的细节越多,声音越不自然。

  3. 运行

    即在 capture 成功后,把读到的 data 送给 IaaAec_Run 算法

    • 入参:

      buffer:输入数据的 buffer bytes:输入数据的字节长度

    • 返回:

      ret:成功的时候,返回更新后的数据 buffer

      ​ 失败的时候,请参考 API 错误码

      channel_count = aecInit.nearend_channel;
      count         = bytes / (2 * PN * channel_count);
      for (j = 0; j < count; j++)
      {
          aecRet = IaaAec_Run(aecHandle, (short*)buffer + j * PN * channel_count,
                              (short*)echo_buffer + j * PN * channel_count);
          if (aecRet != ALGO_AEC_RET_SUCCESS)
          {
              printf("aec run error: 0x%x\n", aecRet);
          }
      }
      

    注意:AEC 算法运行后,会直接更新源数据 buffer 后返回

  4. 释放资源

    aecRet = IaaAec_Free(aecHandle);
    if (aecRet != ALGO_AEC_RET_SUCCESS)
    {
        printf("aec free error: 0x%x", aecRet);
    }
    if (aecWorkingBuffer)
    {
        free(aecWorkingBuffer);
        aecWorkingBuffer = NULL;
    }
    

    注意:必须先调用 IaaAec_Free,再释放供 AEC 算法所使用的内存。

4. SE

SE(Speech Enhancement),语音增强算法是指通过 AI 算法对输入语音进行增强处理,可以抑制稳态噪声和非稳 态噪声。原理是基于AI的方法学习区分噪声和人声,从而进行抑制。

4.1 调用流程

    IaaSe_Init                // 获取 handle
    IaaSe_Set                 // config 配置参数
        |
        V
    IaaSe_GetInputSamples     // 获取算法运行需要输入的最小 sample 数
    IaaSe_Run                 // capture 过程中,调用算法处理
        |
        V
    IaaSe_Free                // 释放算法占用资源

4.2 注意事项

  • 采样位宽只支持 16 bit
  • 采样率只支持 16k Hz
  • 每一帧长度为 128 个采样点(8ms)
  • 双通道数据按左右声道交错存放,数据格式为 L,R,L,R,L,R ...

4.3 调试方法

  1. 初始化

    seInit.sampleRate = sample_rate;
    seInit.bitWidth   = 16;
    seInit.channel    = channel_count;
    
    bufferSize         = IaaSe_GetBufferSize();
    seWorkingBuffer    = (char*)malloc(bufferSize);
    if (NULL == seWorkingBuffer)
    {
        printf("WorkingBuffer malloc failed !\n");
        return DEMO_FAIL;
    }
    PrintInfo("WorkingBuffer malloc success !\n");
    
    seHandle = IaaSe_Init((char*)seWorkingBuffer, &seInit);
    if (seHandle == NULL)
    {
        printf("SE init error !\n");
        return DEMO_FAIL;
    }
    else
    {
        PrintInfo("SE init success !\n");
    }
    
    • sampleRate:语音采样率

    • bitWidth:语音采样位宽

    • channel:语音通道数,取值范围[1, 2],

      1 表示单通道, 2 表示双通道

  2. 配置参数

    seConfig.intensity = 5;
    seConfig.normalize = 0;
    seConfig.smooth    = 5;
    seConfig.noiseType = IAA_SE_ALL_NOISE;
    
    ret = IaaSe_Set(seHandle, &seConfig);
    if (ret != 0)
    {
        printf("SE set error !\n");
        return DEMO_FAIL;
    }
    else
    {
        printf("SE set success !\n");
    }
    
    • noiseType:抑制噪声类型

    • intensity:抑制噪声强度, 取值范围[1~10], 步长为 1。

      1 表示抑制强度 最低,10 表示抑制强度最高,推荐值 5

    • normalize:标准化参数,小音量时启用,推荐值 20000; 不使用时置为 0

    • smooth:Normalize 启用时平滑因子,取值范围[1~10], 数值越大越平 滑,推荐值 5

  3. 运行

    即在 capture 成功后,把读到的 data 送给 IaaSe_Run 算法

    • 入参:

      buffer:输入数据的 buffer bytes:输入数据的字节长度

    • 返回:

      ret:成功的时候,返回更新后的数据 buffer

      ​ 失败的时候,请参考 API 错误码

      input_len = IaaSe_GetInputSamples(seHandle);
      count     = bytes / (2 * input_len * channel_count);
      for (j = 0; j < count; j++)
      {
          ret = IaaSe_Run(seHandle, (short*)buffer + j * input_len);
          if (ret != ALGO_SE_RET_SUCCESS)
          {
              printf("se run error: 0x%x\n", ret);
          }
      }
      

    注意:SE 算法运行后,会直接更新源数据 buffer 后返回

  4. 释放资源

    ret = IaaSe_Free(seHandle);
    if (ret != ALGO_SE_RET_SUCCESS)
    {
        printf("se free error: 0x%x", ret);
    }
    if (seWorkingBuffer)
    {
        free(seWorkingBuffer);
        seWorkingBuffer = NULL;
    }
    

    注意:必须先调用 IaaSe_Free,再释放供 Se 算法所使用的内存。

5. BF

BF(Beamforming),波束成形算法是对麦克风阵列获取的数据进行处理,增强指定方向声音,抑制其他方向声音的算法。原理是根据声源角度和阵列结构,保持声源方向语音不失真同时抑制其他角度的声音。

5.1 调用流程

IaaBf_Init                // 获取 handle
IaaBf_Set                 // config 配置参数
    |
    V
IaaBf_Run                 // capture 过程中,调用算法处理
    |
    V
IaaBf_Free                // 释放算法占用资源

5.2 注意事项

  • 采样位宽只支持 16 bit

  • BF 算法目前仅支持 IAA_BF_ADAPTIVE 模式

  • 所有通道采样率和采样位宽应该一致

  • 数据排列模式需要确认使用 queue mode 还是 alsa pcm mode

  • 目前最大支持 4 麦线形阵列和 4 麦圆形阵列(只支持均匀线性阵列和均匀圆形阵列)

    坐标计算方法:

    // 2 麦线形阵列坐标
                                y ^ 90'
                                    |
    180'   Chn 1                 |                 Chn 0       0'
    ------[MIC 1]----------------|----------------[MIC 0]------>
                                    |                             x
                                    | 270'
    
    // 4 麦线形阵列坐标
                                y ^ 90'
                                    |
    180'   Chn 3        Chn 2    |    Chn 1        Chn 0       0'
    ------[MIC 3]------[MIC 2]---|---[MIC 1]------[MIC 0]------>
                                    |                             x
                                    | 270'
    
    // 4 麦圆形阵列坐标
                    y ^ 90'
                    |
                    Chn 1
                    [MIC 1]
    180'   Chn 2    |    Chn 0       0'
    ------[MIC 2]---|---[MIC 0]------>
                    |                x
                    Chn 3
                    [MIC 3]
                    | 270'
    

5.3 调试方法

  1. 初始化

    bfInit.sampleRate  = sample_rate;
    bfInit.bitWidth    = 16;
    bfInit.micNum      = channel_count;
    bfInit.soundSpeed  = 343;
    bfInit.bfAlgorithm = IAA_ADAPTIVE_BF; // current only support IAA_ADAPTIVE_BF
    bfInit.micGeometry = IAA_BF_UNIFORM_LINEAR_ARRAY;
    float coordinate[4][2]       = {{0.06, 0}, {0.02, 0}, {-0.02, 0}, {-0.06, 0}};
    memcpy(bfInit.micCoordinate, coordinate, sizeof(float) * channel_count * 2);
    
    bfHandle = IaaBf_Init(&bfInit);
    if (bfHandle == NULL)
    {
        printf("BF init error !\n");
        return DEMO_FAIL;
    }
    else
    {
        printf("BF init success !\n");
    }
    
    • sampleRate:语音采样率
    • bitWidth:语音采样位宽
    • micNum:麦克风数目
    • soundSpeed:环境声速
    • bfAlgorithm:BF 使用的算法
    • micGeometry:麦克风阵列物理结构
    • micCoordinate:麦克风阵列中麦克风的二维坐标
  2. 配置参数

    bfConfig.vadThreshold = 0.9;
    bfConfig.intensity    = 3;
    
    ret = IaaBf_Set(bfHandle, &bfConfig);
    if (ret != 0)
    {
        printf("BF Set failed !\n");
        return DEMO_FAIL;
    }
    PrintInfo("BF Set success !\n");
    
    • vadThreshold:语音活动检测阈值。 范围[0~1],推荐值 0.9。

    • intensity:BF 强度,值越大,抑制程度越强 取值范围:0~10,步长:1,推荐值 5

      注:BF 计算分为两个步骤,先抑制其他方向噪声,再进行噪声抑制,强度为 0 代表只进行方向抑制,噪声抑制可以接其他 算法

  3. 运行

    即在 capture 成功后,把读到的 data 送给 IaaBf_Run 算法

    • 入参:

      buffer:输入数据的 buffer bytes:输入数据的字节长度

    • 返回:

      ret:成功的时候,返回处理后的数据 target_buf

      ​ 失败的时候,请参考 API 错误码

      angle         = 90;
      channel_count = bfInit.micNum;
      count         = bytes / (2 * PN * channel_count);
      char* target_buf    = malloc(bytes / channel_count);
      if (!target_buf)
      {
          printf("malloc(%u) failed !\n", bytes);
          return DEMO_FAIL;
      }
      memset(target_buf, 0, bytes);
      for (j = 0; j < count; j++)
      {
          ret = IaaBf_Run(bfHandle, (short*)buffer + j * PN * channel_count,
                          (short*)target_buf + j * PN, angle);
          if (ret != ALGO_BF_RET_SUCCESS)
          {
              printf("bf run error: 0x%x\n", ret);
          }
      }
      

    注意:BF 算法运行后,返回的 target_buf 的大小是 mono 的,即输入 多channel buffer,只返回 1 channel 的数据大小

  4. 释放资源

    ret = IaaBf_Free(bfHandle);
    if (ret != ALGO_BF_RET_SUCCESS)
    {
        printf("bf free error: 0x%x", ret);
    }
    

6. SSL

SSL(Sound Source Localization),声源定位算法是对麦克风阵列获取的数据进行处理,从而获得声源方向的算法。原理是估计麦克风阵列不同通道中的时间延迟,从而根据阵列结构计算声源角度。

6.1 调用流程

IaaSsl_Init               // 获取 handle
IaaSsl_Set                // config 配置参数
    |
    V
IaaSsl_GetInputSamples    // 获取算法运行需要输入的最小 sample 数
IaaSsl_Run                // capture 过程中,调用算法处理
    |
    V
IaaSsl_Free               // 释放算法占用资源

6.2 注意事项

  • 推荐采样率大于 8k Hz

  • 每一帧长度必须最小为 4096 个采样点。

  • 数据排列模式需要确认使用 queue mode 还是 alsa pcm mode

  • 需要确认硬件通道顺序与软件通道顺序一致,否则算法结果是错误的。

    可以采取堵孔测试法来确定每个通道的顺序。测试时,每次堵住一个麦克风,收集音频中没有声音的通道即为该麦克风对应的软件通道。重复上述测试,直到确定出每个麦 克风对应的通道,如果硬件通道顺序与软件通道顺序不一致,请调整顺序后再送入算法运行。

  • 目前仅支持 4 麦线形阵列和 4 麦圆形阵列

    坐标计算方法:

        // 4 麦线形阵列坐标
                                   y ^ 90'
                                     |
        180'   Chn 3        Chn 2    |    Chn 1        Chn 0       0'
        ------[MIC 3]------[MIC 2]---|---[MIC 1]------[MIC 0]------>
                                     |                             x
                                     | 270'
    
        // 4 麦圆形阵列坐标
                      y ^ 90'
                        |
                      Chn 1
                     [MIC 1]
        180'   Chn 2    |    Chn 0       0'
        ------[MIC 2]---|---[MIC 0]------>
                        |                x
                      Chn 3
                     [MIC 3]
                        | 270'
    

6.3 调试方法

  1. 初始化

    float coordinate[4][2]        = {{0.06, 0}, {0.02, 0}, {-0.02, 0}, {-0.06, 0}};
    sslInit.sampleRate  = sample_rate;
    sslInit.soundSpeed  = 340;
    sslInit.micNum      = channel_count;
    sslInit.bitWidth    = 16;
    sslInit.micGeometry = IAA_SSL_UNIFORM_LINEAR_ARRAY;
    memcpy(sslInit.micCoordinate, coordinate, sizeof(float) * channel_count * 2);
    sslHandle               = IaaSsl_Init(&sslInit);
    if (sslHandle == NULL)
    {
        printf("SSL init error !\n");
        return FAIL;
    }
    else
    {
        printf("SSL init success !\n");
    }
    
    • sampleRate:语音采样率,推荐大于 8kHz

    • bitWidth:语音采样位宽

    • micNum:麦克风数目

    • soundSpeed:环境声速

    • micCoordinate:麦克风阵列中麦克风的二维坐标

    • micGeometry:麦克风阵列物理结构类型

  2. 配置参数

    sslConfig.sslResolution = 5;
    sslConfig.vadLevel      = 7;
    sslConfig.lowFreq       = 200;
    sslConfig.highFreq      = 8000;
    
    ret = IaaSsl_Set(sslHandle, &sslConfig);
    if (ret != 0)
    {
        printf("SSL Set failed !\n");
        return FAIL;
    }
    PrintInfo("SSL Set success !\n");
    
    • sslResolution:声源定位搜索步长,值越大,搜索步长越大,运行时间越短

      线形阵列取值范围: 1~180, 步长:1, 推荐值:5

      圆形阵列取值范围: 1~360, 步长:1, 推荐值:5

    • vadLevel:语音活动检测等级,值越小,激活算法的声音要求能量越高

      取值范围:1~7, 根据实际使用情况设置,推荐值:7

    • lowFreq:算法使用声音频率下限 取值范围:0~采样率/2, 且必须小于 highFreq

    • highFreq:算法使用声音频率上限 取值范围:0~采样率/2, 且必须大于 lowFreq

  3. 运行

    即在 capture 成功后,把读到的 data 送给 IaaSsl_Run 算法

    • 入参:

      buffer:输入数据的 buffer bytes:输入数据的字节长度

    • 返回:

      ret:成功的时候,ret 表示 角度值

      ​ 失败的时候,请参考 API 错误码

      frame_len     = 4096;
      channel_count = sslInit.micNum;
      count         = bytes / (2 * frame_len * channel_count);
      for (j = 0; j < count; j++)
      {
          ret = IaaSsl_Run(sslHandle, (short*)buffer + j * frame_len * channel_count);
          if (ret >= ALGO_SSL_RET_INVALID_LICENSE && ret <= ALGO_SSL_RET_INVALID_SSL_RESOLUTION)
          {
              printf("ssl run error: 0x%x\n", ret);
          }
          else
          {
              printf("== SSL azimuth: %d ===\n", ret);
          }
      }
      

    注意:SSL 算法不会改变 data buffer 的内容,仅仅对 data 运算,计算出语音角度值

  4. 释放资源

    ret = IaaSsl_Free(sslHandle);
    if (ret != ALGO_SSL_RET_SUCCESS)
    {
        printf("ssl free error: 0x%x", ret);
    }