MI AEC API


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 09/08/2020
    1.1
  • Modified API
  • 10/15/2020
    1.2
  • Added IaaAec_SetRecursiveRatio function
  • 02/16/2022
    1.21
  • more detail for point number and channel
  • 08/18/2022
    1.22
  • more detail for stereo data arrangement
  • 10/25/2022
    1.23
  • Added IaaAec_ADF_Run and IaaAec_NLP_Run function
  • 03/10/2023
    1.3
  • Added IaaAec_GetAPIVersion and IaaAec_GetConfig function
  • 04/14/2023

    1. 概述


    1.1. 算法说明

    回声消除(Acoustic Echo Cancellation,简称AEC),是一种用于抑制远程回声的功能。

    回声常见于会议系统、楼宇对讲、安防监控等场景,当远程声音从喇叭播放出来后,麦克风在非常小的延时后,重新采集喇叭播放出来的声音并传输回远程,这个延迟的原始信号将导致远程听到回声。


    1.2. 关键词说明

    • Far-end signal (Sin)

      设备扬声器的信号源,远程传来的信号。

    • Near-end signal (Rin)

      设备麦克风录到的信号,此信号可能包含声学回声(Acoustic echo)和近端语者(Near-end talker)的信号

    • Acoustic echo (AE)

      从设备扬声器播放的声音通过直接或间接路径传回麦克风,产生回声;远程语者在说话后会在延迟某段时间后听到自己的回声,此回声称为声学回声。

    • Single talk

      只有设备端的喇叭播放远程信号(Sin = AE + NS当 NS =0)

    • Double talk

      设备端喇叭播放的同时近端语者也对着麦克风说话(Sin = AE + NS当 NS≠0)


    1.3. 注意

    为方便调试和确认算法效果,需要用户应用自行实现替换算法参数和抓取音频数据的逻辑。


    2. API 参考


    2.1. 功能模块API

    API名 功能
    IaaAec_GetBufferSize 获取Aec算法运行需要的内存大小
    IaaAec_Init 初始化Aec算法
    IaaAec_Config 配置Aec算法
    IaaAec_Reset 重新初始化Aec算法
    IaaAec_Free 释放Aec算法资源
    IaaAec_Run Aec算法处理
    IaaAec_GetLibVersion 获取Aec算法的版本信息
    IaaAec_SetRecursiveRatio 配置Aec算法recursive ratio参数
    IaaAec_ADF_Run 线性Aec算法处理
    IaaAec_NLP_Run 非线性Aec算法处理
    IaaAec_GetConfig 获取Aec算法当前的配置参数
    IaaAec_GetAPIVersion 获取Aec版本

    2.2. IaaAec_GetBufferSize

    • 功能

      获取Aec算法运行所需要的内存大小。

    • 语法

      unsigned int IaaAec_GetBufferSize(void);
      
    • 形参

      参数名称 描述 输入/输出
    • 返回值

      返回值为Aec算法运行所需要的内存大小

    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a

    • 注意

      该接口仅返回需要的内存大小,申请和释放内存的动作需应用来处理。

    • 举例

      无。


    2.3. IaaAec_Init

    • 功能

      初始化Aec算法需要的内存。

    • 语法

      AEC_HANDLE IaaAec_Init(char* working_buffer_address, AudioAecInit * aec_init);
      
    • 形参

      参数名称 描述 输入/输出
      working_buffer_address Aec算法使用的内存地址 输入
      aec_init Aec算法的初始化结构体指针 输入
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a

    • 注意

      • Aec算法仅支持8K/16K采样率。
    • 举例

      无。


    2.4. IaaAec_Config

    • 功能

      配置Aec算法。

    • 语法

      ALGO_AEC_RET IaaAec_Config(AEC_HANDLE handle, AudioAecConfig *aec_config);
      
    • 形参

      参数名称 描述 输入/输出
      handle Aec算法handle 输入
      aec_config Aec算法配置参数的结构体指针 输入
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a

    • 注意

      无。

    • 举例

      无。


    2.5. IaaAec_Reset

    • 功能

      重新初始化Aec算法的内存。

    • 语法

      AEC_HANDLE IaaAec_Reset(char* working_buffer_address, AudioAecInit * aec_init);
      
    • 形参

      参数名称 描述 输入/输出
      working_buffer_address Aec算法所使用的内存地址 输入
      aec_init Aec算法的初始化结构体指针 输入
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a

    • 注意

      • 针对同一个算法handle(同一内存地址)重新初始化Aec算法(即重新设置采样率和近远程的通道数)

      • 重新初始化Aec算法后,需要重新配置

    • 举例

      无。


    2.6. IaaAec_Free

    • 功能

      释放Aec算法的资源

    • 语法

      ALGO_AEC_RET IaaAec_Free(AEC_HANDLE handle);
      
    • 形参

      参数名称 描述 输入/输出
      handle Aec算法handle 输入
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a

    • 注意

      • 必须先调用IaaAec_Free,再释放供Aec算法所使用的内存。
    • 举例

      无。


    2.7. IaaAec_Run

    • 功能

      Aec算法处理

    • 语法

      ALGO_AEC_RET IaaAec_Run(AEC_HANDLE handle, short* pss_audio_near_end, short* pss_audio_far_end);
      
    • 形参

      参数名称 描述 输入/输出
      handle 算法handle 输入
      pss_audio_near_end 近端数据指针 输入/输出
      pss_audio_far_end 远程数据指针 输入
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a

    • 注意

      • 近远程的数据量必须和调用IaaAec_Init时设定的point_number(一次IaaAec_Run所需要的采样点数)相对应。

      • AEC后结果由pss_audio_near_end输出

    • 举例

      #include <stdio.h>
      #include <string.h>
      #include <sys/time.h>
      #include <stdlib.h>
      
      #include "AudioAecProcess.h"
      
      /*  0:Fixed input file  1:User input file   */
      #define IN_PARAMETER 1
      
      float AVERAGE_RUN(int a)
      {
          static unsigned int num = 0;
          static float avg = 0;
          if(0 == num)
              avg = 0;
          num++;
          avg = avg + ((float)a - avg) / ((float)num);
          return avg;
      }
      
      unsigned int _OsCounterGetMs(void)
      {
          struct timeval t1;
          gettimeofday(&t1, NULL);
          unsigned int T = ((1000000 * t1.tv_sec) + t1.tv_usec) / 1000;
          return T;
      }
      
      int main(int argc, char *argv[])
      {
          short in_output[1024];
          short input_far[1024];
          char src_file1[128] = {0};
          char src_file2[128] = {0};
          char dst_file[128] = {0};
          unsigned int workingBufferSize;
          char *workingBuffer = NULL;
          int counter = 0;
          ALGO_AEC_RET ret;
          int tempSize;
          unsigned int T0, T1;
          float avg;
          FILE *fpInFar, *fpOut, *fpInNear;
          AudioAecInit aec_init;
          AudioAecConfig aec_config;
          AEC_HANDLE handle;
          /*********************User change section start*******************/
          unsigned int supMode_band[6] = {20,40,60,80,100,120};
          unsigned int supMode[7] = {4,4,4,4,4,4,4};
          aec_init.point_number = 128;
          aec_init.nearend_channel = 1;
          aec_init.farend_channel = 1;
          aec_init.sample_rate = IAA_AEC_SAMPLE_RATE_16000;
          aec_config.delay_sample = 0;
          aec_config.comfort_noise_enable = IAA_AEC_FALSE;
          /*********************User change section end*******************/
          memcpy(&(aec_config.suppression_mode_freq[0]), supMode_band, sizeof(supMode_band));
          memcpy(&(aec_config.suppression_mode_intensity[0]), supMode, sizeof(supMode));
          //(1)IaaAec_GetBufferSize
          workingBufferSize = IaaAec_GetBufferSize();
          workingBuffer = (char*)malloc(workingBufferSize);
          if(NULL == workingBuffer)
          {
              printf("workingBuffer malloc failed !\n");
              return -1;
          }
          printf("workingBuffer malloc success !\n");
          //(2)IaaAec_Init
          handle = IaaAec_Init(workingBuffer, &aec_init);
          if (NULL == handle)
          {
              printf("AEC init failed !\r\n");
              return -1;
          }
          printf("AEC init success !\r\n");
          //(3)IaaAec_Config
          ret = IaaAec_Config(handle, &aec_config);
          if(ret)
          {
              printf("IaaAec_Config failed !\n");
              return -1;
          }
          printf("IaaAec_Config succeed !\n");
      
      #if IN_PARAMETER
          if(argc < 4)
          {
              printf("Please enter the correct parameters!\n");
              return -1;
          }
          sscanf(argv[1], "%s", src_file1);
          sscanf(argv[2], "%s", src_file2);
          sscanf(argv[3], "%s", dst_file);
      #else
          sprintf(src_file1, "%s", "./farend.wav");
          sprintf(src_file2, "%s", "./nearend.wav");
          if(argc < 2)
          {
              printf("Please enter the correct parameters!\n");
              return -1;
          }
          sscanf(argv[1], "%s", dst_file);
      #endif
      
          fpInFar = fopen(src_file1, "rb");
          if(NULL == fpInFar)
          {
              printf("src_file1 open failed !\n");
              return -1;
          }
          printf("src_file1 open succeed !\n");
          fpInNear = fopen(src_file2, "rb");
          if(NULL == fpInNear)
          {
              printf("src_file2 open failed !\n");
              return -1;
          }
          printf("src_file2 open succeed !\n");
          fpOut = fopen(dst_file, "wb");
          if(NULL == fpOut)
          {
              printf("dst_file open failed !\n");
              return -1;
          }
          printf("dst_file open succeed !\n");
      #if 1
          fread(in_output, sizeof(char), 44, fpInNear);  // Remove the 44 bytes header
          fwrite(in_output, sizeof(char), 44, fpOut);  // New file add header
          fread(input_far, sizeof(char), 44, fpInFar); // Remove the 44 bytes header
      #endif
          tempSize = aec_init.point_number * aec_init.nearend_channel;
          while(fread(in_output, sizeof(short), tempSize, fpInNear))
          {
              tempSize = aec_init.point_number * aec_init.farend_channel;
              fread(input_far, sizeof(short), tempSize, fpInFar);
              counter++;
              T0  = (long)_OsCounterGetMs();
              //(4)IaaAec_Run
              ret = IaaAec_Run(handle, in_output, input_far);
              T1  = (long)_OsCounterGetMs();
              avg = AVERAGE_RUN(T1 - T0);
              if(0 == counter % 100)
              {
                  printf("counter = %d\n", counter);
                  printf("current time = %f\n", (float)counter * aec_init.point_number / aec_init.sample_rate);
                  printf("process time = %lu(ms)\t", (long)(T1 - T0));
                  printf("AVG is %.2f ms\n", avg);
              }
              if(ret < 0)
              {
                  printf("IaaAec_Run failed !\n");
                  break;
              }
              tempSize = aec_init.point_number * aec_init.nearend_channel;
              fwrite(in_output, sizeof(short), tempSize, fpOut);
          }
          //(5)IaaAec_Free
          IaaAec_Free(handle);
          fclose(fpInFar);
          fclose(fpInNear);
          fclose(fpOut);
          free(workingBuffer);
          printf("AEC end !\n");
      
          return 0;
      }
      

    2.8. IaaAec_GetLibVersion

    • 功能

      获取Aec算法的版本信息

    • 语法

      ALGO_AEC_RET IaaAec_GetLibVersion(unsigned short *ver_year,
                          unsigned short *ver_date,
                          unsigned short *ver_time);
      
    • 形参

      参数名称 描述 输入/输出
      ver_year Aec算法库文件编译时的年份 输出
      ver_date Aec算法库文件编译时的日期 输出
      ver_time Aec算法库文件编译时的时间 输出
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a

    • 注意

      无。

    • 举例

      无。


    2.9. IaaAec_SetRecursiveRatio

    • 功能

      配置Aec算法recursive ratio参数。

    • 语法

      ALGO_AEC_RET IaaAec_SetRecursiveRatio(AEC_HANDLE handle, unsigned int recursive_ratio);
      
    • 形参

      参数名称 描述 输入/输出
      handle Aec算法handle 输入
      recursive_ratio 递归参数输入。参数设置越大,递归更新讯号速度越快,在Double talk切换到Single talk后,减少回声过度消除近端讯号情形,相对回声消除平顺度降低,背景残留回声不连续性提高。
      取值范围:1 ~ 10
      输入
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a


    2.10. IaaAec_ADF_Run

    • 功能

      线性Aec算法处理

    • 语法

      ALGO_AEC_RET IaaAec_ADF_Run(AEC_HANDLE handle, short*
      pss_audio_near_end, short* pss_audio_far_end);
      
    • 形参

      参数名称 描述 输入/输出
      handle 算法handle 输入
      pss_audio_near_end 近端数据指针 输入/输出
      pss_audio_far_end 远程数据指针 输入
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a

    • 注意

      • 近远程的数据量必须和调用IaaAec_Init时设定的point_number(一次IaaAec_ADF_Run所需要的采样点数)相对应。

      • Aec算法线性部分处理后的结果由pss_audio_near_end输出。

      • 若输入为双声道,请自行将其依据左右左右排列分割成左声道与右声道两个数组。


    2.11. IaaAec_NLP_Run

    • 功能

      非线性Aec算法处理

    • 语法

      ALGO_AEC_RET IaaAec_NLP_Run(AEC_HANDLE handle, short* pss_audio_near_end);
      
    • 形参

      参数名称 描述 输入/输出
      handle 算法handle 输入
      pss_audio_near_end 近端数据指针 输入/输出
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a

    • 注意

      • 近远程的数据量必须和调用IaaAec_Init时设定的point_number(一次IaaAec_NLP_Run所需要的采样点数)相对应。

      • 非线性Aec算法处理后的结果由pss_audio_near_end输出。


    2.12. IaaAec_GetConfig

    • 功能

      获取Aec算法当前的配置参数

    • 语法

      ALGO_AEC_RET IaaAec_GetConfig(AEC_HANDLE handle, AudioAecInit * aec_init, AudioAecConfig *aec_config);
      
    • 形参

      参数名称 描述 输入/输出
      handle Aec算法handle 输入
      aec_init Aec算法的初始化结构体指针 输出
      aec_config Aec算法中当前的配置参数 输出
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a


    2.13. IaaAec_GetAPIVersion

    • 功能

      获取AEC版本

    • 语法

      ALGO_AEC_RET IaaAec_GetAPIVersion(unsigned short* major, unsigned short* minor);
      
    • 形参

      参数名称 描述 输入/输出
      major 算法版本 输出
      minor 算法版本 输出
    • 返回值

      返回值 结果
      0 成功
      非0 失败,参照错误码
    • 依赖

      • 头文件: AudioAecProcess.h

      • 库文件: libAEC_LINUX.so/ libAEC_LINUX.a


    3. AEC 数据类型


    3.1. AEC模块相关数据类型定义

    数据类型 定义
    AudioAecInit Aec算法初始化参数数据结构体类型
    AudioAecConfig Aec算法配置参数数据结构体类型
    IAA_AEC_SAMPLE_RATE Aec算法支持的采样率类型
    IAA_AEC_BOOL Aec算法的布尔类型
    AEC_HANDLE Aec算法句柄类型

    3.2. AudioAecInit

    • 说明

      定义Aec算法的初始化参数结构体。

    • 定义

      typedef struct
      
      {
      
          unsigned int point_number;
      
          unsigned int nearend_channel;
      
          unsigned int farend_channel;
      
          IAA_AEC_SAMPLE_RATE sample_rate;
      
      }AudioAecInit;
      
    • 成员

      成员名称 描述
      point_number Aec算法处理一次的采样点数,预设可支持为128亦可透过下面指令确认可支持的处理点数
      strings libAEC_LINUX.so | grep PN
      nearend_channel 近端的信道数(即麦克风的信道数),可设定单声道或双声道
      范围:[1, 2]
      farend_channel 远端的信道数(即喇叭的通道数),可设定单声道或双声道
      范围:[1, 2]
      sample_rate Aec算法处理的采样率
    • 注意事项

      • 若使用的输入为双声道,请依照左右声道交错排列的方式给讯号,例如:左/右/左/右...

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

    • 相关数据类型及接口

      IaaAec_InitIaaAec_Reset


    3.3. AudioAecConfig

    • 说明

      定义Aec算法的配置参数结构体。

    • 定义

      typedef struct
      
      {
      
          IAA_AEC_BOOL comfort_noise_enable;
      
          short delay_sample;
      
          unsigned int suppression_mode_freq[6];
      
          unsigned int suppression_mode_intensity[7];
      
      }AudioAecConfig;
      
    • 成员

      成员名称 描述
      comfort_noise_enable 在AEC后加入舒适噪声,避免回声消除后部分段落静音,让用户有断线的感觉。 若AEC后有开启Noise reduction,建议开启comfort noise,帮助NR收敛噪声。
      delay_sample 左右声道间的回声延迟样本 当Stereo cost down 开启时,可以根据麦克风摆放位置先行设定样本延迟数,减少双声道所需运算量。 默认为0,单位是sample数 取值范围:-9-9
      suppression_mode_freq AEC处理的频段划分,将不同取样率所能解析的最高频率等分成128等分,设定六个值可切出七个段落设定不同AEC强度。 取值范围:1-127
      suppression_mode_intensity 配合suppression_mode_freq所切割出来的七个段落分别设定七种强度。 取值范围:0-25
    • 注意事项

      • comfort_noise_enable

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

      • delay_sample

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

      • suppression_mode_freq

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

        residual\ Echo\ Frequency\ Range/(sampleRate/2) \times pointNumber(128)

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

      • suppression_mode_intensity

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

    • 相关数据类型及接口

      IaaAec_Config


    3.4. IAA_AEC_SAMPLE_RATE

    • 说明

      AEC算法支持的采样率类型。

    • 定义

      typedef enum
      
      {
      
          IAA_AEC_SAMPLE_RATE_8000 = 8000 ,
      
          IAA_AEC_SAMPLE_RATE_16000 = 16000 ,
      
      }IAA_AEC_SAMPLE_RATE;
      
    • 成员

      成员名称 描述
      IAA_AEC_SAMPLE_RATE_8000 8K采样率
      IAA_AEC_SAMPLE_RATE_16000 16K采样率
    • 注意事项

    • 相关数据类型及接口

      AudioAecInit


    3.5. IAA_AEC_BOOL

    • 说明

      定义Aec算法内部使用的布尔类型。

    • 定义

      typedef enum
      
      {
      
          IAA_AEC_TRUE = 1,
      
          IAA_AEC_FALSE = 0,
      
      }IAA_AEC_BOOL;
      
    • 成员

      成员名称 描述
      IAA_AEC_TRUE True
      IAA_AEC_FALSE False
    • 注意事项

    • 相关数据类型及接口

      AudioAecConfig


    3.6. AEC_HANDLE


    4. 错误码

    AEC API 错误码如表下所示:

    错误码 宏定义 描述
    0x00000000 ALGO_AEC_RET_SUCCESS AEC运行成功
    0x10000401 ALGO_AEC_RET_INIT_ERROR AEC尚未初始化
    0x10000402 ALGO_AEC_RET_INVALID_HANDLE HANDLE无效
    0x10000403 ALGO_AEC_RET_INVALID_SAMPLE_RATE 取样频率不支持
    0x10000404 ALGO_AEC_RET_INVALID_POINT_NUMBER 每帧点数不支持
    0x10000405 ALGO_AEC_RET_INVALID_CHANNEL 通道数不支持
    0x10000406 ALGO_AEC_RET_INVALID_SUP_BAND AEC抑制频带参数设置无效
    0x10000407 ALGO_AEC_RET_INVALID_SUP_MODE AEC抑制强度参数设置无效
    0x10000408 ALGO_AEC_RET_INVALID_COMFORT_NOISE AEC舒适噪声参数设置无效
    0x10000409 ALGO_AEC_RET_INVALID_DELAY_SAMPLE AEC延迟样本数设置无效
    0x10000410 ALGO_AEC_RET_API_CONFLICT 其他API正在运行
    0x10000411 ALGO_AEC_RET_INVALID_CALLING 呼叫API顺序错误