Files
99_7018_lmx/apps/earphone/aec/br28/audio_aec_demo.c
2025-10-29 13:10:02 +08:00

633 lines
19 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
***************************************************************************
* AUDIO AEC DEMO
* File : audio_aec_demo.c
* By : GZR
* Notes : 1.可用内存
* (1)aec_hdl_mem: 静态变量
* (2)free_ram: 动态内存(mem_stats:physics memory size xxxx bytes)
* 2.demo默认将输入数据copy到输出相关处理只需在运算函数
* audio_aec_run()实现即可
* 3.双mic ENC开发只需打开对应板级以下配置即可
* #define TCFG_AUDIO_DUAL_MIC_ENABLE ENABLE_THIS_MOUDLE
* 4.建议算法开发者使用宏定义将自己的代码模块包起来
* 5.算法处理完的数据如有需要可以增加EQ处理AEC_UL_EQ_EN
* 6.开发阶段默认使用芯片最高主频160MHz可以通过修改AEC_CLK来修改
运行频率。
***************************************************************************
*/
#include "aec_user.h"
#include "system/includes.h"
#include "media/includes.h"
#include "application/eq_config.h"
#include "circular_buf.h"
#include "overlay_code.h"
#include "audio_config.h"
#include "debug.h"
#if TCFG_AUDIO_CVP_DUT_ENABLE
#include "audio_cvp_dut.h"
#endif // TCFG_AUDIO_CVP_DUT_ENABLE
#if defined(TCFG_CVP_DEVELOP_ENABLE) && (TCFG_CVP_DEVELOP_ENABLE == CVP_CFG_USER_DEFINED)
#define AEC_CLK (160 * 1000000L) /*模块运行时钟(MaxFre:160MHz)*/
#define AEC_FRAME_POINTS 256 /*AEC处理帧长跟mic采样长度关联*/
#define AEC_FRAME_SIZE (AEC_FRAME_POINTS << 1)
#if TCFG_AUDIO_TRIPLE_MIC_ENABLE
#define AEC_MIC_NUM 3 /*3 mic*/
#elif TCFG_AUDIO_DUAL_MIC_ENABLE
#define AEC_MIC_NUM 2 /*双mic*/
#else
#define AEC_MIC_NUM 1 /*单mic*/
#endif/*TCFG_AUDIO_DUAL_MIC_ENABLE*/
/*上行数据eq*/
#define AEC_UL_EQ_EN 0
#define AEC_USER_MALLOC_ENABLE 1 /*是否使用动态内存*/
#ifdef AUDIO_PCM_DEBUG
/*AEC串口数据导出*/
const u8 CONST_AEC_EXPORT = 1;
#else
const u8 CONST_AEC_EXPORT = 0;
#endif/*AUDIO_PCM_DEBUG*/
extern int audio_dac_read_reset(void);
extern int audio_dac_read(s16 points_offset, void *data, int len, u8 read_channel);
extern void esco_enc_resume(void);
extern int aec_uart_init();
extern int aec_uart_open(u8 nch, u16 single_size);
extern int aec_uart_fill(u8 ch, void *buf, u16 size);
extern void aec_uart_write(void);
extern int aec_uart_close(void);
/*AEC输入buf复用mic_adc采样buf*/
#define MIC_BULK_MAX 3
struct mic_bulk {
struct list_head entry;
s16 *addr;
u16 len;
u16 used;
};
struct audio_aec_hdl {
volatile u8 start; //aec模块状态
volatile u8 busy;
u8 output_sel; //数据输出通道选择
u8 output_buf[1000]; //aec数据输出缓存
cbuffer_t output_cbuf;
s16 *mic; /*主mic数据地址*/
s16 *mic_ref; /*参考mic数据地址*/
s16 *mic_ref_1; /*参考mic数据地址*/
s16 spk_ref[AEC_FRAME_POINTS]; /*扬声器参考数据*/
s16 out[AEC_FRAME_POINTS]; /*运算输出地址*/
OS_SEM sem;
/*数据复用相关数据结构*/
struct mic_bulk in_bulk[MIC_BULK_MAX];
struct mic_bulk inref_bulk[MIC_BULK_MAX];
struct mic_bulk inref_1_bulk[MIC_BULK_MAX];
struct list_head in_head;
struct list_head inref_head;
struct list_head inref_1_head;
#if AEC_UL_EQ_EN
struct audio_eq *ul_eq;
#endif/*AEC_UL_EQ_EN*/
int (*output_handle)(s16 *dat, u16 len);
};
#if AEC_USER_MALLOC_ENABLE
struct audio_aec_hdl *aec_hdl = NULL;
#else
struct audio_aec_hdl aec_handle;
struct audio_aec_hdl *aec_hdl = &aec_handle;
#endif /*AEC_USER_MALLOC_ENABLE*/
/*
*********************************************************************
* Audio AEC Output Read
* Description: 读取aec模块的输出数据
* Arguments : buf 读取数据存放地址
* len 读取数据长度
* Return : 数据读取长度
* Note(s) : None.
*********************************************************************
*/
int audio_aec_output_read(s16 *buf, u16 len)
{
//printf("rlen:%d-%d\n",len,aec_hdl.output_cbuf.data_len);
local_irq_disable();
if (!aec_hdl || !aec_hdl->start) {
printf("audio_aec close now");
local_irq_enable();
return -EINVAL;
}
u16 rlen = cbuf_read(&aec_hdl->output_cbuf, buf, len);
if (rlen == 0) {
//putchar('N');
}
local_irq_enable();
return rlen;
}
/*
*********************************************************************
* Audio AEC Output Handle
* Description: AEC模块数据输出回调
* Arguments : data 输出数据地址
* len 输出数据长度
* Return : 数据输出消耗长度
* Note(s) : None.
*********************************************************************
*/
static int audio_aec_output(s16 *data, u16 len)
{
u16 wlen = 0;
if (aec_hdl && aec_hdl->start) {
wlen = cbuf_write(&aec_hdl->output_cbuf, data, len);
//printf("wlen:%d-%d\n",len,aec_hdl.output_cbuf.data_len);
if (wlen != len) {
printf("aec_out_full:%d,%d\n", len, wlen);
}
esco_enc_resume();
}
return wlen;
}
/*
*跟踪系统内存使用情况:physics memory size xxxx bytes
*正常的系统运行过程应该至少有3k bytes的剩余空间给到系统调度开销
*/
static void sys_memory_trace(void)
{
static int cnt = 0;
if (cnt++ > 200) {
cnt = 0;
mem_stats();
}
}
/*
*********************************************************************
* Audio AEC RUN
* Description: AEC数据处理核心
* Arguments : in 主mic数据
* inref 参考mic数据(双mic降噪有用)
* inref1 参考mic数据(3 mic降噪有用)
* ref speaker参考数据
* out 数据输出
* Return : 数据运算输出长度
* Note(s) : 在这里实现AEC_core
*********************************************************************
*/
static int audio_aec_run(s16 *in, s16 *inref, s16 *inref1, s16 *ref, s16 *out, u16 points)
{
int out_size = 0;
putchar('.');
memcpy(out, in, (points << 1));
//memcpy(out, inref, (points << 1));
out_size = points << 1;
#if AEC_UL_EQ_EN
if (aec_hdl->ul_eq) {
audio_eq_run(aec_hdl->ul_eq, out, out_size);
}
#endif/*AEC_UL_EQ_EN*/
#if TCFG_AUDIO_CVP_DUT_ENABLE
switch (aec_hdl->output_sel) {
case CVP_3MIC_OUTPUT_SEL_MASTER:
memcpy(out, in, (points << 1));
break;
case CVP_3MIC_OUTPUT_SEL_SLAVE:
memcpy(out, inref, (points << 1));
break;
case CVP_3MIC_OUTPUT_SEL_FBMIC:
memcpy(out, inref1, (points << 1));
break;
default:
break;
}
#endif/*TCFG_AUDIO_CVP_DUT_ENABLE*/
sys_memory_trace();
return out_size;
}
/*
*********************************************************************
* Audio AEC Task
* Description: AEC任务
* Arguments : priv 私用参数
* Return : None.
* Note(s) : None.
*********************************************************************
*/
static void audio_aec_task(void *priv)
{
printf("==Audio AEC Task==\n");
struct mic_bulk *bulk = NULL;
struct mic_bulk *bulk_ref = NULL;
struct mic_bulk *bulk_ref_1 = NULL;
u8 pend = 1;
while (1) {
if (pend) {
os_sem_pend(&aec_hdl->sem, 0);
}
pend = 1;
if (aec_hdl->start) {
if (!list_empty(&aec_hdl->in_head)) {
aec_hdl->busy = 1;
local_irq_disable();
/*1.获取主mic数据*/
bulk = list_first_entry(&aec_hdl->in_head, struct mic_bulk, entry);
list_del(&bulk->entry);
aec_hdl->mic = bulk->addr;
#if (AEC_MIC_NUM > 1)
/*2.获取参考mic数据*/
bulk_ref = list_first_entry(&aec_hdl->inref_head, struct mic_bulk, entry);
list_del(&bulk_ref->entry);
aec_hdl->mic_ref = bulk_ref->addr;
#endif/*Dual_Microphone*/
#if (AEC_MIC_NUM > 2)
/*获取参考mic1数据*/
bulk_ref_1 = list_first_entry(&aec_hdl->inref_1_head, struct mic_bulk, entry);
list_del(&bulk_ref_1->entry);
aec_hdl->mic_ref_1 = bulk_ref_1->addr;
#endif/*3_Microphone*/
local_irq_enable();
/*3.获取speaker参考数据*/
audio_dac_read(60, aec_hdl->spk_ref, AEC_FRAME_SIZE, 1);
/*4.算法处理*/
int out_len = audio_aec_run(aec_hdl->mic, aec_hdl->mic_ref, aec_hdl->mic_ref_1, aec_hdl->spk_ref, aec_hdl->out, AEC_FRAME_POINTS);
/*5.结果输出*/
if (aec_hdl->output_handle) {
aec_hdl->output_handle(aec_hdl->out, out_len);
}
/*6.数据导出*/
if (CONST_AEC_EXPORT) {
aec_uart_fill(0, aec_hdl->mic, 512); //主mic数据
aec_uart_fill(1, aec_hdl->mic_ref, 512); //副mic数据
aec_uart_fill(2, aec_hdl->spk_ref, 512); //扬声器数据
#if (AEC_MIC_NUM > 2)
aec_uart_fill(2, aec_hdl->mic_ref_1, 512); //扬声器数据
aec_uart_fill(3, aec_hdl->spk_ref, 512); //扬声器数据
aec_uart_fill(4, aec_hdl->out, out_len); //算法运算结果
#endif /*3_Microphone*/
aec_uart_write();
}
bulk->used = 0;
#if (AEC_MIC_NUM > 1)
bulk_ref->used = 0;
#endif/*Dual_Microphone*/
#if (AEC_MIC_NUM > 2)
bulk_ref_1->used = 0;
#endif /*3_Microphone*/
aec_hdl->busy = 0;
pend = 0;
}
}
}
}
/*
*********************************************************************
* Audio AEC Open
* Description: 初始化AEC模块
* Arguments : sr 采样率(8000/16000)
* enablebit 使能模块(AEC/NLP/AGC/ANS...)
* out_hdl 自定义回调函数NULL则用默认的回调
* Return : 0 成功 其他 失败
* Note(s) : 该接口是对audio_aec_init的扩展支持自定义使能模块以及
* 数据输出回调函数
*********************************************************************
*/
int audio_aec_open(u16 sample_rate, s16 enablebit, int (*out_hdl)(s16 *data, u16 len))
{
printf("audio_aec_open,sr = %d\n", sample_rate);
mem_stats();
if (aec_hdl) {
printf("audio aec is already open!\n");
return -1;
}
overlay_load_code(OVERLAY_AEC);
#if AEC_USER_MALLOC_ENABLE
aec_hdl = zalloc(sizeof(struct audio_aec_hdl));
if (aec_hdl == NULL) {
printf("aec_hdl malloc failed");
return -ENOMEM;
}
#endif /*AEC_USER_MALLOC_ENABLE*/
#if TCFG_AUDIO_CVP_DUT_ENABLE
aec_hdl->output_sel = CVP_3MIC_OUTPUT_SEL_DEFAULT;
#endif/*TCFG_AUDIO_CVP_DUT_ENABLE*/
aec_hdl->output_handle = audio_aec_output;
printf("aec_hdl size:%d\n", sizeof(struct audio_aec_hdl));
clk_set("sys", AEC_CLK);
INIT_LIST_HEAD(&aec_hdl->in_head);
INIT_LIST_HEAD(&aec_hdl->inref_head);
INIT_LIST_HEAD(&aec_hdl->inref_1_head);
cbuf_init(&aec_hdl->output_cbuf, aec_hdl->output_buf, sizeof(aec_hdl->output_buf));
if (CONST_AEC_EXPORT) {
#if (AEC_MIC_NUM > 2)
aec_uart_open(5, 512);
#elif (AEC_MIC_NUM > 1)
aec_uart_open(3, 512);
#endif /*AEC_MIC_NUM*/
}
if (out_hdl) {
aec_hdl->output_handle = out_hdl;
}
#if AEC_UL_EQ_EN
struct audio_eq_param ul_eq_param = {0};
ul_eq_param.sr = sample_rate;
ul_eq_param.channels = 1;
ul_eq_param.online_en = 1;
ul_eq_param.mode_en = 0;
ul_eq_param.remain_en = 0;
ul_eq_param.max_nsection = EQ_SECTION_MAX;
ul_eq_param.cb = aec_ul_eq_filter;
ul_eq_param.eq_name = aec_eq_mode;
aec_hdl->ul_eq = audio_dec_eq_open(&ul_eq_param);
#endif/*AEC_UL_EQ_EN*/
os_sem_create(&aec_hdl->sem, 0);
task_create(audio_aec_task, NULL, "aec");
audio_dac_read_reset();
aec_hdl->start = 1;
mem_stats();
#ifdef MUX_RX_BULK_TEST_DEMO
mux_rx_bulk_test = zalloc_mux_rx_bulk(MUX_RX_BULK_MAX);
if (mux_rx_bulk_test) {
printf("mux_rx_bulk_test:0x%x\n", mux_rx_bulk_test);
free_mux_rx_bulk(mux_rx_bulk_test);
mux_rx_bulk_test = NULL;
}
#endif/*MUX_RX_BULK_TEST_DEMO*/
printf("audio_aec_open succ\n");
return 0;
}
/*
*********************************************************************
* Audio AEC Open
* Description: 初始化AEC模块
* Arguments : sr 采样率(8000/16000)
* Return : 0 成功 其他 失败
* Note(s) : None.
*********************************************************************
*/
int audio_aec_init(u16 sample_rate)
{
return audio_aec_open(sample_rate, -1, NULL);
}
/*
*********************************************************************
* Audio AEC Reboot
* Description: AEC模块复位接口
* Arguments : reduce 复位/恢复标志
* Return : None.
* Note(s) : None.
*********************************************************************
*/
void audio_aec_reboot(u8 reduce)
{
}
/*
*********************************************************************
* Audio AEC Close
* Description: 关闭AEC模块
* Arguments : None.
* Return : None.
* Note(s) : None.
*********************************************************************
*/
void audio_aec_close(void)
{
printf("audio_aec_close:%x", (u32)aec_hdl);
if (aec_hdl) {
aec_hdl->start = 0;
while (aec_hdl->busy) {
os_time_dly(2);
}
task_kill("aec");
if (CONST_AEC_EXPORT) {
aec_uart_close();
}
#if AEC_UL_EQ_EN
if (aec_hdl->ul_eq) {
audio_dec_eq_close(aec_hdl->ul_eq);
aec_hdl->ul_eq = NULL;
}
#endif/*AEC_UL_EQ_EN*/
local_irq_disable();
#if AEC_USER_MALLOC_ENABLE
free(aec_hdl);
#endif /*AEC_USER_MALLOC_ENABLE*/
aec_hdl = NULL;
local_irq_enable();
printf("audio_aec_close succ\n");
}
}
/*
*********************************************************************
* Audio AEC Status
* Description: AEC模块当前状态
* Arguments : None.
* Return : 0 关闭 其他 打开
* Note(s) : None.
*********************************************************************
*/
u8 audio_aec_status(void)
{
if (aec_hdl) {
return aec_hdl->start;
}
return 0;
}
/*
*********************************************************************
* Audio AEC Input
* Description: AEC源数据输入
* Arguments : buf 输入源数据地址
* len 输入源数据长度
* Return : None.
* Note(s) : 输入一帧数据唤醒一次运行任务处理数据默认帧长256点
*********************************************************************
*/
void audio_aec_inbuf(s16 *buf, u16 len)
{
if (aec_hdl && aec_hdl->start) {
int i = 0;
for (i = 0; i < MIC_BULK_MAX; i++) {
if (aec_hdl->in_bulk[i].used == 0) {
break;
}
}
#if TCFG_AUDIO_CVP_DUT_ENABLE
if (cvp_dut_mode_get() == CVP_DUT_MODE_BYPASS) {
audio_aec_output(buf, len);
return;
}
#endif/*TCFG_AUDIO_CVP_DUT_ENABLE*/
if (i < MIC_BULK_MAX) {
aec_hdl->in_bulk[i].addr = buf;
aec_hdl->in_bulk[i].used = 0x55;
aec_hdl->in_bulk[i].len = len;
list_add_tail(&aec_hdl->in_bulk[i].entry, &aec_hdl->in_head);
} else {
printf(">>>aec_in_full\n");
/*align reset*/
struct mic_bulk *bulk;
list_for_each_entry(bulk, &aec_hdl->in_head, entry) {
bulk->used = 0;
__list_del_entry(&bulk->entry);
}
return;
}
os_sem_set(&aec_hdl->sem, 0);
os_sem_post(&aec_hdl->sem);
}
}
/*
*********************************************************************
* Audio AEC Input Reference
* Description: AEC源参考数据输入
* Arguments : buf 输入源数据地址
* len 输入源数据长度
* Return : None.
* Note(s) : 双mic ENC的参考mic数据输入,单mic的无须调用该接口
*********************************************************************
*/
void audio_aec_inbuf_ref(s16 *buf, u16 len)
{
if (aec_hdl && aec_hdl->start) {
int i = 0;
for (i = 0; i < MIC_BULK_MAX; i++) {
if (aec_hdl->inref_bulk[i].used == 0) {
break;
}
}
if (i < MIC_BULK_MAX) {
aec_hdl->inref_bulk[i].addr = buf;
aec_hdl->inref_bulk[i].used = 0x55;
aec_hdl->inref_bulk[i].len = len;
list_add_tail(&aec_hdl->inref_bulk[i].entry, &aec_hdl->inref_head);
} else {
printf(">>>aec_inref_full\n");
/*align reset*/
struct mic_bulk *bulk;
list_for_each_entry(bulk, &aec_hdl->inref_head, entry) {
bulk->used = 0;
__list_del_entry(&bulk->entry);
}
return;
}
}
}
/*
*********************************************************************
* Audio AEC Input Reference
* Description: AEC源参考数据输入
* Arguments : buf 输入源数据地址
* len 输入源数据长度
* Return : None.
* Note(s) : 双mic ENC的参考mic数据输入,单mic的无须调用该接口
*********************************************************************
*/
void audio_aec_inbuf_ref_1(s16 *buf, u16 len)
{
if (aec_hdl && aec_hdl->start) {
int i = 0;
for (i = 0; i < MIC_BULK_MAX; i++) {
if (aec_hdl->inref_1_bulk[i].used == 0) {
break;
}
}
if (i < MIC_BULK_MAX) {
aec_hdl->inref_1_bulk[i].addr = buf;
aec_hdl->inref_1_bulk[i].used = 0x55;
aec_hdl->inref_1_bulk[i].len = len;
list_add_tail(&aec_hdl->inref_1_bulk[i].entry, &aec_hdl->inref_1_head);
} else {
printf(">>>aec_inref_1_full\n");
/*align reset*/
struct mic_bulk *bulk;
list_for_each_entry(bulk, &aec_hdl->inref_1_head, entry) {
bulk->used = 0;
__list_del_entry(&bulk->entry);
}
return;
}
}
}
/*
*********************************************************************
* Audio AEC Reference
* Description: AEC模块参考数据输入
* Arguments : buf 输入参考数据地址
* len 输入参考数据长度
* Return : None.
* Note(s) : 声卡设备是DAC默认不用外部提供参考数据
*********************************************************************
*/
void audio_aec_refbuf(s16 *buf, u16 len)
{
if (aec_hdl && aec_hdl->start) {
}
}
/*
*********************************************************************
* Audio AEC Output Sel
* Description: AEC输出数据选择
* Arguments : sel 选择输出/算法输出/talk/ff/fb原始数据
* agc NULL
* Return : None.
* Note(s) : None.
*********************************************************************
*/
void audio_aec_output_sel(CVP_OUTPUT_ENUM sel, u8 agc)
{
if (aec_hdl) {
aec_hdl->output_sel = sel;
}
}
/*
*********************************************************************
* Audio AEC Toggle Set
* Description: AEC模块算法开关使能
* Arguments : toggle 0 关闭算法 1 打开算法
* Return : None.
* Note(s) : None.
*********************************************************************
*/
int audio_aec_toggle_set(u8 toggle)
{
if (aec_hdl) {
aec_hdl->output_sel = (toggle) ? 0 : 1;
}
}
#endif /*TCFG_CVP_DEVELOP_ENABLE*/