MI AEC API

Version 1.1


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算法的版本信息

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

  • 注意

    无。

  • 举例

    无。


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算法处理一次的采样点数
    nearend_channel 远程的信道数(即麦克风的信道数)
    farend_channel 近端的信道数(即喇叭的通道数)
    sample_rate Aec算法处理的采样率
  • 注意事项


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顺序错误