MI AEC API
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个频段一一对应。强度越大,消除的细节越多,声音越不自然。
-
-
相关数据类型及接口
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采样率 -
注意事项
无
-
相关数据类型及接口
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 -
注意事项
无
-
相关数据类型及接口
3.6. AEC_HANDLE¶
-
说明
定义Aec算法的句柄类型。
-
定义
typedef void* AEC_HANDLE;
-
成员
成员名称 描述 -
注意事项
无
-
相关数据类型及接口
IaaAec_Init、IaaAec_Config 、IaaAec_Reset、IaaAec_Free、IaaAec_Run
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顺序错误 |