/* **************************************************************** * Audio Hearing-Aid * File : audio_hearing_aid.c * By : * Notes : DHA = Digital Hearing-Aid * Usage : **************************************************************** */ #include "generic/typedef.h" #include "board_config.h" #include "media/includes.h" #include "audio_config.h" #include "sound_device.h" #include "audio_hearing_aid.h" #include "howling_api.h" #include "audio_codec_clock.h" #include "application/eq_config.h" #include "application/audio_drc.h" #include "audio_dec_eff.h" #include "audio_enc.h" #include "audio_llns.h" #include "audio_dvol.h" #include "circular_buf.h" #include "audio_gain_process.h" #include "bt_tws.h" #include "audio_noise_gate.h" #include "amplitude_statistic.h" #include "audio_hearing_aid_lp.h" #include "media/audio_howling.h" #if TCFG_AUTO_SHUT_DOWN_TIME #include "bt_edr_fun.h" #endif /*TCFG_AUTO_SHUT_DOWN_TIME*/ #if TCFG_AUDIO_HEARING_AID_ENABLE extern struct audio_dac_hdl dac_hdl; extern struct audio_adc_hdl adc_hdl; #define HEARING_AID_TASK_NAME "HearingAid" //*********************************************************************************// // 数字信号处理配置(Digital Hearing_Aid[DHA] Process Config) // //*********************************************************************************// /*mic声音淡入使能*/ #define DHA_FADEIN_ENABLE 1 /*均衡器*/ #define DHA_EQ_ENABLE 1 /*均衡器2*/ #define DHA_EQ2_ENABLE 0 /*动态范围控制*/ #define DHA_DRC_ENABLE 1 /*[啸叫抑制-移频法]Howling Suppress - FreqShift*/ #define DHA_HS_FS_ENABLE 1 /*[啸叫抑制-陷波法]Howling Suppress - Notch*/ #define DHA_HS_NOTCH_ENABLE 1 /*噪声抑制Noise Suppress*/ #define DHA_NS_ENABLE 0 /*变采样模块:如果输入输出采样率一致,条件编译自动关闭使能*/ #define DHA_SRC_ENABLE 1 /*音量控制模块*/ #define DHA_VOLUME_ENABLE 1 /*Noise-Gate噪声门限控制*/ #define DHA_NOISE_GATE_ENABLE 0 //*********************************************************************************// // 数据结构定义 // //*********************************************************************************// typedef struct { u16 mic_ch_sel; u16 sample_rate; u16 adc_irq_points; u16 adc_buf_num; u16 dac_delay; u16 mic_gain; } hearing_adda_param_t; /*Microphone采样率和采样点数配置*/ #if DHA_NS_ENABLE #define HEARING_AID_CLK (160 * 1000000L) #define DHA_MIC_SAMPLE_RATE TCFG_AUDIO_DHA_MIC_SAMPLE_RATE #define DHA_MIC_SAMPLE_POINT (DHA_MIC_SAMPLE_RATE / 200) #else #define HEARING_AID_CLK (160 * 1000000L) #define DHA_MIC_SAMPLE_RATE TCFG_AUDIO_DHA_MIC_SAMPLE_RATE #define DHA_MIC_SAMPLE_POINT 64 #endif/*DHA_NS_ENABLE*/ #if (DHA_MIC_SAMPLE_RATE == TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE) /*输入输出采样率一致,条件编译自动关闭SRC使能*/ #undef DHA_SRC_ENABLE #define DHA_SRC_ENABLE 0 #endif/*DHA_MIC_SAMPLE_RATE*/ static const hearing_adda_param_t hearing_adda_param = { .mic_ch_sel = TCFG_AD2DA_LOW_LATENCY_MIC_CHANNEL, .mic_gain = 12, .sample_rate = DHA_MIC_SAMPLE_RATE,//采样率 .adc_irq_points = DHA_MIC_SAMPLE_POINT,//一次处理数据的数据单元, 单位点 4对齐(要配合mic起中断点数修改) .adc_buf_num = 3, #if (TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE == 44100) .dac_delay = (DHA_MIC_SAMPLE_POINT *TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE / DHA_MIC_SAMPLE_RATE) / (TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE / 1000) + 5,//dac硬件混响延时, 单位ms 要大于 point_unit*2 #else .dac_delay = (DHA_MIC_SAMPLE_POINT *TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE / DHA_MIC_SAMPLE_RATE) / (TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE / 1000) + 3,//dac硬件混响延时, 单位ms 要大于 point_unit*2 #endif }; //*********************************************************************************// // mic能量检测处理配置 // //*********************************************************************************// typedef struct { struct audio_dac_channel dac_ch; struct adc_mic_ch mic_ch; struct audio_adc_output_hdl adc_output; s16 *adc_dma_buf; OS_SEM sem; #if DHA_MIC_DATA_CBUF_ENABLE s16 *run_buf; u8 *data_buf; cbuffer_t mic_cbuf; #else s16 *mic_data_addr; s32 mic_data_len; #endif /*DHA_MIC_DATA_CBUF_ENABLE*/ u8 task_release; u8 task_exit; volatile u8 task_busy; u16 fade_in_gain; #if DHA_HS_FS_ENABLE HOWLING_API_STRUCT *hs_fs; HOWLING_API_STRUCT *hs_fs_1; #endif /*DHA_HS_FS_ENABLE*/ #if DHA_HS_NOTCH_ENABLE HOWLING_API_STRUCT *hs_notch; HOWLING_API_STRUCT *hs_notch_1; #endif /*DHA_HS_NOTCH_ENABLE*/ #if (DHA_EQ_ENABLE || DHA_DRC_ENABLE) struct dec_eq_drc *hearing_eq_drc; #endif /*(DHA_EQ_ENABLE || DHA_DRC_ENABLE)*/ #if (DHA_EQ2_ENABLE) struct audio_eq *hearing_eq2; #endif /*(DHA_EQ2_ENABLE)*/ #if DHA_NS_ENABLE void *llns; void *llns_1; #endif /*DHA_NS_ENABLE*/ #if DHA_SRC_ENABLE #if DHA_SRC_USE_HW_ENABLE struct audio_src_handle *hw_src; #else s16 src_out_data[480]; #endif/*DHA_SRC_USE_HW_ENABLE*/ #endif /*DHA_SRC_ENABLE*/ #if (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_LR) s16 mono_to_dual[(DHA_MIC_SAMPLE_POINT * TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE / DHA_MIC_SAMPLE_RATE) * 2 + 8]; #endif/*TCFG_AUDIO_DAC_CONNECT_MODE*/ /*双声道处理双变单,单变双使用*/ s16 *tmp_buf; s16 *tmp_buf_1; u8 channel; } hearing_aid_t; static hearing_aid_t *hearing_hdl = NULL; static u32 start_ts = 0; static u32 end_ts = 0; enum { DHA_STATE_CLOSE, DHA_STATE_OPEN, DHA_STATE_SUSPEND, }; typedef struct { volatile u8 state; /*辅听状态*/ volatile u8 fitting_state; /*验配状态*/ u8 fitting; /*听力验配模式标识*/ u8 volume; u16 timer; } hearing_aid_schedule_t; hearing_aid_schedule_t dha_schedule = {0}; /*保留EQ段,做自定义滤波使用*/ #define DHA_REV0_FREQ 100 #define DHA_REV1_FREQ 16000 #define MIC_EQ_SECTION 8 static struct eq_seg_info audio_mic_eq_tab[MIC_EQ_SECTION] = { {0, EQ_IIR_TYPE_BAND_PASS, DHA_CH0_FREQ, 0, 1.0f}, {1, EQ_IIR_TYPE_BAND_PASS, DHA_CH1_FREQ, 0, 1.0f}, {2, EQ_IIR_TYPE_BAND_PASS, DHA_CH2_FREQ, 0, 1.4f}, {3, EQ_IIR_TYPE_BAND_PASS, DHA_CH3_FREQ, 0, 1.4f}, {4, EQ_IIR_TYPE_BAND_PASS, DHA_CH4_FREQ, 0, 1.4f}, {5, EQ_IIR_TYPE_BAND_PASS, DHA_CH5_FREQ, 0, 2.0f}, {6, EQ_IIR_TYPE_HIGH_PASS, DHA_REV0_FREQ, 0, 1.0f}, {7, EQ_IIR_TYPE_LOW_PASS, DHA_REV1_FREQ, 0, 0.7f}, }; /*听力验配频点列表*/ static u16 sine_freq[] = { DHA_CH0_FREQ, DHA_CH1_FREQ, DHA_CH2_FREQ, DHA_CH3_FREQ, DHA_CH4_FREQ, DHA_CH5_FREQ }; static u8 sine_idx = 2; static u16 dac_digital_vol = 0; int audio_dac_set_digital_vol(struct audio_dac_hdl *dac, u16 vol); u16 audio_dac_get_digital_vol(void) { u16 vol = 0; #if (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_MONO_L) vol = JL_AUDIO->DAC_VL0 & 0x0000ffff; #else vol = (JL_AUDIO->DAC_VL0 >> 16) & 0x0000ffff; #endif return vol; } extern void put_float(double fv); /* ********************************************************************* * Audio Hearing-aid Fitting * Description: Hearing Level Fitting * Arguments : NULL * Return : NULL * Note(s) : 听力验配 ********************************************************************* */ dha_fitting_adjust_t dha_fitting_result[DHA_FITTING_CHANNEL_MAX]; int fitting_param_write(u8 LR, void *buf); extern u32 hearing_aid_rcsp_response(u8 *data, u16 data_len); extern u32 hearing_aid_rcsp_notify(u8 *data, u16 data_len); static int hearing_aid_eq_filter_updata(float *EQGain); int hearing_aid_fitting_parse(u8 *data, u16 len) { u8 tmp[4]; u8 cmd = data[0]; u16 data_len = (data[2] << 8) | data[1]; printf("cmd %x, data_len %d\n", cmd, data_len); switch (cmd) { case DHA_FITTING_CMD_INFO: break; case DHA_FITTING_CMD_ADJUST: printf("DHA_FITTING_CMD_ADJUST\n"); dha_fitting_adjust_t adjust; if (data_len > sizeof(dha_fitting_adjust_t)) { /*数据长度错误*/ printf("data_len err !!!"); return 0; } memcpy(&adjust, &data[3], data_len); hearing_aid_fitting_start(adjust.sine); if (adjust.sine) {//开始验配,需要波单频音 audio_dac_set_digital_vol(&dac_hdl, (u16)(16384 * adjust.gain / 100)); printf("dval : %d\n", (u16)(16384 * adjust.gain / 100)); #if TCFG_USER_TWS_ENABLE enum audio_channel channel = tws_api_get_local_channel() == 'R' ? AUDIO_CH_R : AUDIO_CH_L; if ((channel == AUDIO_CH_L) && (adjust.channel == 0)) { //当前是左耳,并且数据是设置左耳时 for (int i = 0; i < DHA_FITTING_CHANNEL_MAX; i++) { if (audio_mic_eq_tab[i].freq == adjust.freq) { /* audio_mic_eq_tab[i].gain = adjust.gain; */ sine_idx = i; printf("L idx %d, freq %d, gain %d\n", sine_idx, audio_mic_eq_tab[i].freq, (int)adjust.gain); break; } } } else if ((channel == AUDIO_CH_R) && (adjust.channel == 1)) { //当前是右耳,并且数据是设置右耳时 for (int i = 0; i < DHA_FITTING_CHANNEL_MAX; i++) { if (audio_mic_eq_tab[i].freq == adjust.freq) { /* audio_mic_eq_tab[i].gain = adjust.gain; */ sine_idx = i; printf("R idx %d, freq %d, gain %d\n", sine_idx, audio_mic_eq_tab[i].freq, (int)adjust.gain); break; } } } #else //没有TWS时,不判断左右耳直接设置单频音 for (int i = 0; i < DHA_FITTING_CHANNEL_MAX; i++) { if (audio_mic_eq_tab[i].freq == adjust.freq) { /* audio_mic_eq_tab[i].gain = adjust.gain; */ sine_idx = i; printf("L idx %d, freq %d, gain %d\n", sine_idx, audio_mic_eq_tab[i].freq, (int)adjust.gain); break; } } #endif /*TCFG_USER_TWS_ENABLE*/ } /*回复辅听状态信息*/ get_hearing_aid_state_cmd_info(tmp); hearing_aid_rcsp_response(tmp, 4); break; case DHA_FITTING_CMD_UPDATE: printf("DHA_FITTING_CMD_UPDATE\n"); float EQGain[12]; if (data_len > sizeof(EQGain)) { /*数据长度错误*/ printf("data_len err !!!"); return 0; } /*回复辅听状态信息*/ get_hearing_aid_state_cmd_info(tmp); hearing_aid_rcsp_response(tmp, 4); /*退出验配状态*/ dha_schedule.fitting = 0; dha_schedule.fitting_state = 0; u8 LR = data[3]; //左右耳数据标志 memcpy(EQGain, &data[4], data_len); /*更新验配数据到辅听*/ hearing_aid_eq_filter_updata(EQGain); /*保存验配数据到vm*/ fitting_param_write(LR, EQGain); for (int i = 0; i < data_len / 4; i++) { put_float(EQGain[i]); } /*恢复允许播歌*/ if (get_hearing_aid_fitting_state() == 0) { extern void a2dp_tws_dec_resume(void); a2dp_tws_dec_resume(); } break; } return 0; } /*获取辅听左右耳状态信息*/ int get_hearing_aid_state_cmd_info(u8 *data) { dha_fitting_state_t dha_fitting_state; #if TCFG_USER_TWS_ENABLE if (get_bt_tws_connect_status()) { //对耳已经配对 dha_fitting_state.state_left = dha_schedule.state == DHA_STATE_OPEN ? 1 : 0; dha_fitting_state.state_right = dha_schedule.state == DHA_STATE_OPEN ? 1 : 0; } else { /*tws 单只耳时*/ enum audio_channel channel = tws_api_get_local_channel() == 'R' ? AUDIO_CH_R : AUDIO_CH_L; if (channel == AUDIO_CH_L) { //当前是左耳 dha_fitting_state.state_left = dha_schedule.state == DHA_STATE_OPEN ? 1 : 0; dha_fitting_state.state_right = 0; } else if (channel == AUDIO_CH_R) { //当前是右耳 dha_fitting_state.state_left = 0; dha_fitting_state.state_right = dha_schedule.state == DHA_STATE_OPEN ? 1 : 0; } } #else /*没有开tws单只耳当作左耳*/ dha_fitting_state.state_left = dha_schedule.state == DHA_STATE_OPEN ? 1 : 0; dha_fitting_state.state_right = 0; #endif /*TCFG_USER_TWS_ENABLE*/ data[0] = DHA_FITTING_CMD_STATE; data[1] = sizeof(dha_fitting_state); data[2] = 0; data[3] = ((u8 *)&dha_fitting_state)[0]; put_buf(data, 4); return 0; } /*获取辅听验配状态*/ u8 get_hearing_aid_fitting_state(void) { return dha_schedule.fitting || dha_schedule.fitting_state ? 1 : 0; } /*获取验配信息*/ int get_hearing_aid_fitting_info(u8 *data) { hearing_aid_t *hdl = (hearing_aid_t *)hearing_hdl; if (data && hearing_hdl) { dha_fitting_info_t dha_info = { .version = DHA_FITTING_VERSION, .ch_num = DHA_FITTING_CHANNEL_MAX, .ch_freq[0] = DHA_CH0_FREQ, .ch_freq[1] = DHA_CH1_FREQ, .ch_freq[2] = DHA_CH2_FREQ, .ch_freq[3] = DHA_CH3_FREQ, .ch_freq[4] = DHA_CH4_FREQ, .ch_freq[5] = DHA_CH5_FREQ, }; memcpy(data, &dha_info, sizeof(dha_info)); return sizeof(dha_info); } else { return 0; } } /*判断是否播放单频音 * en: * BIT(0) = 1 左耳播单频音,BIT(0) = 0 左耳静音 * BIT(1) = 1 右耳播单频音,BIT(1) = 0 右耳静音 * */ int hearing_aid_fitting_start(u8 en) { hearing_aid_t *hdl = (hearing_aid_t *)hearing_hdl; printf("fitting en: %d", en); if (hdl) { #if TCFG_USER_TWS_ENABLE if (en) { //需要播单频音 enum audio_channel channel = tws_api_get_local_channel() == 'R' ? AUDIO_CH_R : AUDIO_CH_L; if (channel == AUDIO_CH_L) { //当前是左耳 //判断是否设置左耳 if (en & BIT(0)) { //设置左耳时左耳播放单频音 dha_schedule.fitting = 1; } else { //设置右耳时左耳静音 dha_schedule.fitting = 2; } } else if (channel == AUDIO_CH_R) { //当前是右耳 //判断是否设置右耳 if (en & BIT(1)) { //设置右耳时右耳播放单频音 dha_schedule.fitting = 1; } else { //设置左耳时右耳静音 dha_schedule.fitting = 2; } } } else { //关闭单频音 dha_schedule.fitting = en; } #else //没有TWS时 dha_schedule.fitting = en ? 1 : 0; #endif /*TCFG_USER_TWS_ENABLE*/ if (dha_schedule.fitting == 0) { audio_dac_set_digital_vol(&dac_hdl, dac_digital_vol); } dha_schedule.fitting_state = dha_schedule.fitting; printf("dha_schedule.fitting : %d", dha_schedule.fitting); return dha_schedule.fitting; } return -1; } u8 set_hearing_aid_fitting_state(u8 state) { dha_schedule.fitting_state = state; return dha_schedule.fitting_state; } /*同步关闭辅听验配*/ void audio_dha_fitting_sync_close(void) { #if TCFG_USER_TWS_ENABLE if (get_tws_sibling_connect_state()) { if (tws_api_get_role() == TWS_ROLE_MASTER) { printf("[tws_master]fitting close"); bt_tws_play_tone_at_same_time(SYNC_TONE_DHA_FITTING_CLOSE, 400); } } else { hearing_aid_fitting_start(0); } #else hearing_aid_fitting_start(0); #endif/*TCFG_USER_TWS_ENABLE*/ set_hearing_aid_fitting_state(0); } /* * 保存验配参数 * LR: 0 保存左耳,1 保存右耳,2 保存对耳 * buf:需要保存的参数 */ int fitting_param_write(u8 LR, void *buf) { float EQGain[DHA_FITTING_CHANNEL_MAX * 2]; int ret = 0; ret = syscfg_read(CFG_DHA_FITTING_ID, EQGain, DHA_FITTING_CHANNEL_MAX * 2 * 4); if (ret != DHA_FITTING_CHANNEL_MAX * 2 * 4) { printf("vm fitting param empty\n"); } if (LR == 0) { /*写左耳数据*/ memcpy(EQGain, (u8 *)buf, DHA_FITTING_CHANNEL_MAX * 4); } else if (LR == 1) { /*写右耳数据*/ memcpy(EQGain + DHA_FITTING_CHANNEL_MAX, (u8 *)buf, DHA_FITTING_CHANNEL_MAX * 4); } else if (LR == 2) { /*写对耳数据*/ memcpy(EQGain, (u8 *)buf, DHA_FITTING_CHANNEL_MAX * 2 * 4); } syscfg_write(CFG_DHA_FITTING_ID, EQGain, sizeof(EQGain)); return 0; } /*读取当前耳机(左耳/右耳)验配参数*/ int fitting_param_read(void *buf) { float EQGain[DHA_FITTING_CHANNEL_MAX * 2]; int ret = 0; ret = syscfg_read(CFG_DHA_FITTING_ID, EQGain, DHA_FITTING_CHANNEL_MAX * 2 * 4); if (ret != DHA_FITTING_CHANNEL_MAX * 2 * 4) { printf("vm fitting param read err\n"); return 0; } /*判断是否清除过数据,全是0xFF表示是清除过数据*/ u8 *data = (u8 *)EQGain; u8 cnt = 0; for (u8 i = 0; i < 4; i++) { if (data[i] == 0xFF) { cnt++; } } if (cnt >= 4) { return 0; } #if TCFG_USER_TWS_ENABLE if (get_bt_tws_connect_status()) { //对耳已经配对 enum audio_channel channel = tws_api_get_local_channel() == 'R' ? AUDIO_CH_R : AUDIO_CH_L; if (channel == AUDIO_CH_L) { //当前是左耳,使用第一组数据 memcpy((u8 *)buf, EQGain, DHA_FITTING_CHANNEL_MAX * 4); } else if (channel == AUDIO_CH_R) { //当前是右耳,使用第二组数据 memcpy((u8 *)buf, EQGain + DHA_FITTING_CHANNEL_MAX, DHA_FITTING_CHANNEL_MAX * 4); } } else #endif /*TCFG_USER_TWS_ENABLE*/ { //没有配对时,使用第一组数据 memcpy((u8 *)buf, EQGain, DHA_FITTING_CHANNEL_MAX * 4); } return ret >> 1; } /*清除验配参数 * LR: 0 清除左耳,1 清除右耳,2 清除对耳 */ int clear_fitting_patam(u8 LR) { float EQGain[DHA_FITTING_CHANNEL_MAX * 2]; int ret = 0; ret = syscfg_read(CFG_DHA_FITTING_ID, EQGain, DHA_FITTING_CHANNEL_MAX * 2 * 4); if (ret != DHA_FITTING_CHANNEL_MAX * 2 * 4) { printf("vm fitting param empty\n"); } if (LR == 0) { /*清楚左耳参数*/ memset(EQGain, 0xFF, DHA_FITTING_CHANNEL_MAX * 4); } else if (LR == 1) { /*清楚右耳参数*/ memset(EQGain + DHA_FITTING_CHANNEL_MAX, 0xFF, DHA_FITTING_CHANNEL_MAX * 4); } else if (LR == 2) { /*清楚对耳参数*/ memset(EQGain, 0xFF, DHA_FITTING_CHANNEL_MAX * 2 * 4); } syscfg_write(CFG_DHA_FITTING_ID, EQGain, sizeof(EQGain)); return 0; } #if DHA_SRC_ENABLE #include "Resample_api.h" static RS_STUCT_API *sw_src_api = NULL; static u8 *sw_src_buf = NULL; int hearing_output(hearing_aid_t *hdl, s16 *data, u16 len); static int hearing_aid_hw_src_output_handler(void *priv, s16 *data, int len); static int hearing_src_init(hearing_aid_t *hdl, u8 nch, u16 insample, u16 outsample) { printf("hearing_src_init,insr:%d,outsr:%d\n", insample, outsample); #if DHA_SRC_USE_HW_ENABLE if (insample != TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE) { printf("hw src in %d, out %d\n", insample, TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE); hdl->hw_src = zalloc(sizeof(struct audio_src_handle)); if (hdl->hw_src) { u8 channel = nch; audio_hw_src_open(hdl->hw_src, channel, AUDIO_RESAMPLE_SYNC_OUTPUT); audio_hw_src_set_rate(hdl->hw_src, insample, TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE); audio_src_set_output_handler(hdl->hw_src, hdl, (void (*)(void *, void *, int))hearing_output); printf("audio hw src open succ %x", hdl->hw_src); } else { printf("hdl->hw_src malloc fail !!!\n"); } } #else if (insample != outsample) { sw_src_api = get_rs16_context(); /* sw_src_api = get_rsfast_context(); */ printf("sw_src_api:0x%x\n", sw_src_api); ASSERT(sw_src_api); u32 sw_src_need_buf = sw_src_api->need_buf(); printf("sw_src_buf:%d\n", sw_src_need_buf); sw_src_buf = zalloc(sw_src_need_buf); ASSERT(sw_src_buf); RS_PARA_STRUCT rs_para_obj; rs_para_obj.nch = nch; rs_para_obj.new_insample = insample; rs_para_obj.new_outsample = outsample; printf("sw src,in = %d,out = %d\n", rs_para_obj.new_insample, rs_para_obj.new_outsample); sw_src_api->open(sw_src_buf, &rs_para_obj); } return 0; #endif/*DHA_SRC_USE_HW_ENABLE*/ } static int hearing_src_run(hearing_aid_t *hdl, s16 *indata, s16 *outdata, u16 len) { #if DHA_SRC_USE_HW_ENABLE if (hdl->hw_src) { return audio_src_resample_write(hdl->hw_src, indata, len); } #else int outlen = len; if (sw_src_api && sw_src_buf) { outlen = sw_src_api->run(sw_src_buf, indata, len >> 1, outdata); /* ASSERT(outlen <= (sizeof(outdata) >> 1)); */ outlen = outlen << 1; /* printf("%d\n",outlen); */ } else { memcpy(outdata, indata, len); } return outlen; #endif/*DHA_SRC_USE_HW_ENABLE*/ } static void hearing_src_exit(hearing_aid_t *hdl) { #if DHA_SRC_USE_HW_ENABLE printf("[HW]hearing_src_exit\n"); if (hdl->hw_src) { audio_hw_src_stop(hdl->hw_src); audio_hw_src_close(hdl->hw_src); free(hdl->hw_src); hdl->hw_src = NULL; } #else printf("[SW]hearing_src_exit\n"); if (sw_src_buf) { free(sw_src_buf); sw_src_buf = NULL; sw_src_api = NULL; } #endif/*DHA_SRC_USE_HW_ENABLE*/ } #endif /*DHA_SRC_ENABLE*/ #if DHA_SRC_ENABLE static void hearing_volume_run(s16 *data, int len); static int hearing_aid_hw_src_output_handler(void *priv, s16 *data, int len) { hearing_aid_t *hdl = (hearing_aid_t *)hearing_hdl; if (!hdl) { return 0; } s16 *mic_data = data; int data_len = len; int wlen = 0; #if DHA_VOLUME_ENABLE hearing_volume_run(mic_data, data_len); #endif/*DHA_VOLUME_ENABLE*/ #if DHA_RUN_TIME_TRACE_ENABLE end_ts = jiffies_msec(); printf("run_time:%d - %d = %d\n", end_ts, start_ts, (end_ts - start_ts)); #endif/*DHA_RUN_TIME_TRACE_ENABLE*/ DHA_IO_DEBUG_0(); // 写入DAC wlen = sound_pcm_dev_write(&hdl->dac_ch, data, len); hdl->task_busy = 0; return len; } #endif /*DHA_SRC_ENABLE*/ #if DHA_NS_ENABLE /* * 低延时降噪只支持16k 和32k * 16k采样率时,一帧处理80个点的数据(5ms) * 32k采样率时 一帧处理160个点的数据(5ms) */ static const u16 llns_frame_size[2][2] = { {16000, 160},//80点 {32000, 320},//160点 /* {44100, 440},//220点 */ /* {48000, 480},//240点 */ }; typedef struct { char *private_buf; char *share_buf; s16 inbuf[160]; s16 in_pool[320]; cbuffer_t in_cbuf; int frame_size; void *llns; } llns_hdl_t; /* static llns_hdl_t *llns_hdl = NULL; */ static int llns_exit(void *hdl); static void *llns_init(int sr, float gainfloor, float suppress_level) { llns_hdl_t *llns_hdl = zalloc(sizeof(llns_hdl_t)); if (llns_hdl == NULL) { printf("llns_hdl zalloc err !!!!\n"); return -1; } cbuf_init(&llns_hdl->in_cbuf, llns_hdl->in_pool, sizeof(llns_hdl->in_pool)); printf("%s %d", __func__, __LINE__); int private_heap_size, share_heap_size; audio_llns_heap_query(&share_heap_size, &private_heap_size, sr); printf("private_heap_size:%d,share_heap_size:%d\n", private_heap_size, share_heap_size); llns_hdl->private_buf = (char *)zalloc(private_heap_size); llns_hdl->share_buf = (char *)zalloc(share_heap_size); /*判断是否支持的采样率*/ llns_hdl->frame_size = 0; for (int i = 0; i < ARRAY_SIZE(llns_frame_size); i++) { //printf("llns_frame_size[%d] = %d",i,llns_frame_size[i][0]); if (llns_frame_size[i][0] == sr) { //printf("llns sr math:%d\n",sr); llns_hdl->frame_size = llns_frame_size[i][1]; } } if (llns_hdl->frame_size == 0) { printf("samplerate only support 16k or 32k !!!\n"); return -1; } printf("sr: %d, gainfloor: %d/100\n", sr, (int)(gainfloor * 100)); llns_hdl->llns = audio_llns_init(llns_hdl->private_buf, private_heap_size, llns_hdl->share_buf, share_heap_size, sr, gainfloor, suppress_level); ASSERT(llns_hdl->llns); printf("llns init ok\n"); return llns_hdl; } static int llns_run(void *hdl, s16 *data, int len) { llns_hdl_t *llns_hdl = (llns_hdl_t *)hdl; int llns_outsize = 0, llns_outsize1 = 0; if (llns_hdl) { if (len != llns_hdl->frame_size) { putchar('B'); } cbuf_write(&llns_hdl->in_cbuf, data, len); if (cbuf_read(&llns_hdl->in_cbuf, llns_hdl->inbuf, llns_hdl->frame_size) == llns_hdl->frame_size) { llns_outsize = audio_llns_run(llns_hdl->llns, llns_hdl->inbuf, llns_hdl->frame_size, data); //llns_outsize = len >> 1; } } return llns_outsize << 1; } static int llns_exit(void *hdl) { llns_hdl_t *llns_hdl = (llns_hdl_t *)hdl; if (llns_hdl) { audio_llns_close(llns_hdl->llns); llns_hdl->llns = NULL; if (llns_hdl->private_buf) { free(llns_hdl->private_buf); llns_hdl->private_buf = NULL; } if (llns_hdl->share_buf) { free(llns_hdl->share_buf); llns_hdl->share_buf = NULL; } free(llns_hdl); llns_hdl = NULL; } return 0; } #endif /*DHA_NS_ENABLE*/ #if (DHA_EQ_ENABLE || DHA_DRC_ENABLE) static int hearing_aid_eq_get_filter_info(void *eq, int sr, struct audio_eq_filter_info *info) { struct audio_eq *eq_hdl = (struct audio_eq *)eq; if (!eq_hdl) { return -1; } local_irq_disable(); u8 nsection = ARRAY_SIZE(audio_mic_eq_tab); if (!eq_hdl->eq_coeff_tab) { eq_hdl->eq_coeff_tab = zalloc(sizeof(int) * 5 * nsection); } for (int i = 0; i < nsection; i++) { eq_seg_design(&audio_mic_eq_tab[i], sr, &eq_hdl->eq_coeff_tab[5 * i]); } local_irq_enable(); info->L_coeff = info->R_coeff = (void *)eq_hdl->eq_coeff_tab; info->L_gain = info->R_gain = 0; info->nsection = nsection; return 0; } /*限幅器drc*/ static float Gain_dB = 1; static struct drc_ch mic_drc_p = {0}; static int hearing_aid_drc_get_filter_info(void *drc, struct audio_drc_filter_info *info) { float th = -10.0f;//单位:dB 范围:-60dB~0dB,限幅器阈值 Gain_dB = 7.0f; /*drc压制后要放大的dB数*/ Gain_dB = powf(10.0f, Gain_dB / 20.0f); int threshold = roundf(powf(10.0f, th / 20.0f) * 32768); mic_drc_p.nband = 1; mic_drc_p.type = 1; //1:限幅器 mic_drc_p._p.limiter[0].attacktime = 5; mic_drc_p._p.limiter[0].releasetime = 500; mic_drc_p._p.limiter[0].threshold[0] = threshold; mic_drc_p._p.limiter[0].threshold[1] = 32768; info->R_pch = info->pch = &mic_drc_p; return 0; } #define FITTING_EQ_THR 30 static int hearing_aid_eq_filter_updata(float *EQGain) { hearing_aid_t *hdl = (hearing_aid_t *)hearing_hdl; if (hdl == NULL) { return -1; } float max = 100 - FITTING_EQ_THR; for (int i = 0; i < DHA_FITTING_CHANNEL_MAX; i++) { if (EQGain[i] > FITTING_EQ_THR) { audio_mic_eq_tab[i].gain = (EQGain[i] - FITTING_EQ_THR) * 12.0f / max; } else { audio_mic_eq_tab[i].gain = 0; } put_float(audio_mic_eq_tab[i].gain); } struct dec_eq_drc *eq_drc = hdl->hearing_eq_drc; struct audio_eq *eq = eq_drc->eq; if (eq_drc == NULL || eq == NULL) { return -1; } eq->updata = 1; return 0; } void *hearing_eq_drc_open(u32 sample_rate, u8 ch_num) { #if TCFG_EQ_ENABLE struct dec_eq_drc *eff = zalloc(sizeof(struct dec_eq_drc)); /* struct audio_eq_drc *eff = zalloc(sizeof(struct audio_eq_drc)); */ struct audio_eq_param eq_param = {0}; /* eff->priv = priv; */ /* eff->out_cb = eq_output_cb; */ eq_param.channels = ch_num; eq_param.online_en = 1; eq_param.mode_en = 0; eq_param.remain_en = 0; eq_param.no_wait = 0; eq_param.out_32bit = 0; #if TCFG_AUDIO_DHA_FITTING_ENABLE eq_param.max_nsection = MIC_EQ_SECTION; eq_param.cb = hearing_aid_eq_get_filter_info; #else eq_param.max_nsection = EQ_SECTION_MAX; eq_param.cb = eq_get_filter_info; #endif /*TCFG_AUDIO_DHA_FITTING_ENABLE*/ eq_param.eq_name = hearing_aid_mode; eq_param.sr = sample_rate; /* eq_param.priv = eff; */ /* eq_param.output = eq_output; */ eff->eq = audio_dec_eq_open(&eq_param); #if (TCFG_DRC_ENABLE && DHA_DRC_ENABLE) struct audio_drc_param drc_param = {0}; drc_param.sr = sample_rate; drc_param.channels = ch_num; drc_param.online_en = 1; drc_param.remain_en = 0; drc_param.out_32bit = 0; #if DHA_USE_WDRC_ENABLE drc_param.cb = drc_get_filter_info; #else drc_param.cb = hearing_aid_drc_get_filter_info; #endif /*DHA_USE_WDRC_ENABLE*/ drc_param.drc_name = hearing_aid_mode; eff->drc = audio_dec_drc_open(&drc_param); eff->async = 0; #endif //TCFG_DRC_ENABLE return eff; #else return NULL; #endif//TCFG_EQ_ENABLE } void hearing_eq_drc_run(struct dec_eq_drc *eq_drc, void *data, u32 len) { struct dec_eq_drc *eff_hdl = (struct dec_eq_drc *)eq_drc; #if DHA_EQ_ENABLE if (eff_hdl->eq) { audio_eq_run(eff_hdl->eq, data, len); } #endif/*DHA_EQ_ENABLE*/ #if DHA_DRC_ENABLE if (eff_hdl->drc) { audio_drc_run(eff_hdl->drc, data, len); #if (DHA_USE_WDRC_ENABLE == 0) GainProcess_16Bit(data, data, Gain_dB, 1, 1, 1, len >> 1); #endif /*DHA_USE_WDRC_ENABLE == 0*/ } #endif/*DHA_DRC_ENABLE*/ } void hearing_eq_drc_close(struct dec_eq_drc *eq_drc) { #if TCFG_EQ_ENABLE struct dec_eq_drc *eff_hdl = (struct dec_eq_drc *)eq_drc; /* struct audio_eq_drc *eff_hdl = (struct audio_eq_drc *)eq_drc; */ if (eff_hdl->eq) { audio_dec_eq_close(eff_hdl->eq); eff_hdl->eq = NULL; } if (eff_hdl->drc) { audio_dec_drc_close(eff_hdl->drc); eff_hdl->drc = NULL; } free(eff_hdl); #endif return; } #endif /*(DHA_EQ_ENABLE || DHA_DRC_ENABLE)*/ #if (TCFG_EQ_ENABLE && DHA_EQ2_ENABLE) void *hearing_eq2_open(u32 sample_rate, u8 ch_num) { struct audio_eq *eq; struct audio_eq_param eq_param = {0}; /* eff->priv = priv; */ /* eff->out_cb = eq_output_cb; */ eq_param.channels = ch_num; eq_param.online_en = 1; eq_param.mode_en = 0; eq_param.remain_en = 0; eq_param.no_wait = 0; eq_param.out_32bit = 0; eq_param.max_nsection = EQ_SECTION_MAX; eq_param.cb = eq_get_filter_info; eq_param.eq_name = hearing_aid_mode2; eq_param.sr = sample_rate; /* eq_param.priv = eff; */ /* eq_param.output = eq_output; */ eq = audio_dec_eq_open(&eq_param); return eq; } void hearing_eq2_run(struct audio_eq *eq, void *data, u32 len) { if (eq) { audio_eq_run(eq, data, len); } } void hearing_eq2_close(struct audio_eq *eq) { if (eq) { audio_dec_eq_close(eq); eq = NULL; } } #endif /*TCFG_EQ_ENABLE && DHA_EQ2_ENABLE*/ /* ********************************************************************* * Audio Hearing-aid Fade In * Description: * Arguments : data * points * Return : NULL * Note(s) : NULL ********************************************************************* */ #define HEARING_AID_FADEIN_SHIFT (14) #define HEARING_AID_FADEIN_GAIN_MAX (1 << HEARING_AID_FADEIN_SHIFT) static void hearing_fade_in_run(s16 *data, u16 points) { if (hearing_hdl->fade_in_gain < HEARING_AID_FADEIN_GAIN_MAX) { s32 tmp_data; for (int i = 0; i < points; i++) { tmp_data = data[i]; data[i] = (tmp_data * hearing_hdl->fade_in_gain) >> HEARING_AID_FADEIN_SHIFT; if (++hearing_hdl->fade_in_gain >= HEARING_AID_FADEIN_GAIN_MAX) { return; } } } } #if DHA_VOLUME_ENABLE /* ********************************************************************* * Audio Hearing-aid Volume * Description: * Arguments : data * points * Return : NULL * Note(s) : NULL ********************************************************************* */ /*数字音量*/ static void hearing_volume_run(s16 *data, int len) { #if 0 static u16 vol_dbg = 0; if (vol_dbg++ > 500) { vol_dbg = 0; printf("[DHA]Volume:%d\n", audio_digital_vol_get(HEARING_DVOL)); } #endif audio_digital_vol_run(HEARING_DVOL, data, len); } /*设置音量*/ static void hearing_volume_set(u8 volume) { dha_schedule.volume = volume; audio_digital_vol_set(HEARING_DVOL, volume); } /*获取当前音量大小*/ static int hearing_volume_get(void) { return audio_digital_vol_get(HEARING_DVOL); } /*打开数字音量*/ static void hearing_volume_open(u8 volume) { dha_schedule.volume = (volume > HEARING_DVOL_MAX) ? HEARING_DVOL_MAX : volume; audio_digital_vol_open(HEARING_DVOL, dha_schedule.volume, HEARING_DVOL_MAX, HEARING_DVOL_FS, -1); } /*关闭数字音量*/ static void hearing_volume_close(void) { audio_digital_vol_close(HEARING_DVOL); } #endif /*DHA_VOLUME_ENABLE*/ /* ********************************************************************* * Audio Hearing-aid TDE * Description: 全路径延时估计Time Delay Estimation * Arguments : NULL * Return : NULL * Note(s) : NULL ********************************************************************* */ typedef struct { u16 tick; u16 delay_points_max; u16 delay_time_max; } hearing_aid_tde_t; hearing_aid_tde_t dha_dte; void hearing_tde_run(void) { if (dha_dte.tick ++ > 2000) { dha_dte.tick = 0; extern int audio_dac_data_len(); u16 time_unit = DHA_MIC_SAMPLE_RATE / 1000; u16 delay_points = audio_dac_data_len(); u16 delay_time = (delay_points + (time_unit >> 1)) / time_unit; delay_time = delay_time + ((DHA_MIC_SAMPLE_POINT * 1000) / DHA_MIC_SAMPLE_RATE); if (delay_points > dha_dte.delay_points_max) { dha_dte.delay_points_max = delay_points; } if (delay_time > dha_dte.delay_time_max) { dha_dte.delay_time_max = delay_time; } printf("[DHA]delay_est:%d(points),%d(ms)\n", delay_points, delay_time); //printf("[DHA]delay_est_max:%d(points),%d(ms)\n",dha_dte.delay_points_max,dha_dte.delay_time_max); } } /* ********************************************************************* * Audio Hearing-aid Mic Raw Data Handle * Description: * Arguments : NULL * Return : NULL * Note(s) : NULL ********************************************************************* */ static void hearing_mic_output_handler(void *priv, s16 *data, int len) { hearing_aid_t *hdl = (hearing_aid_t *)priv; DHA_IO_INTERVAL(); if (hdl == NULL) { return; } len = len * hdl->channel; #if DHA_MIC_DATA_CBUF_ENABLE int wlen = cbuf_write(&hdl->mic_cbuf, data, len); if (wlen != len) { /* putchar('f'); */ printf("hearing aid mic cbuf full !!!\n"); /* audio_codec_clock_check(); */ } #else hdl->mic_data_addr = data; hdl->mic_data_len = len; /*判断上一次数据是否已经处理完了*/ if (hdl->task_busy) { putchar('&'); /* audio_codec_clock_check(); */ /* printf("hearing aid need to increase sys_clock"); */ } #endif /*DHA_MIC_DATA_CBUF_ENABLE*/ os_sem_set(&hdl->sem, 0); os_sem_post(&hdl->sem); } static int hearing_aid_mic_en(u8 en, hearing_adda_param_t *param) { hearing_aid_t *hdl = (hearing_aid_t *)hearing_hdl; if (hdl == NULL) { printf("hearing_aid_mic_en error! hdl == NULL"); return -1;; } if (en) { printf("dha mic en, ch %d, sr %d, gain %d", param->mic_ch_sel, param->sample_rate, param->mic_gain); u16 mic_ch = param->mic_ch_sel; u16 mic0_gain = param->mic_gain; u16 mic1_gain = param->mic_gain; u16 mic2_gain = param->mic_gain; u16 mic3_gain = param->mic_gain; audio_mic_pwr_ctl(MIC_PWR_ON); if (mic_ch & AUDIO_ADC_MIC_0) { audio_adc_mic_open(&hdl->mic_ch, mic_ch, &adc_hdl); audio_adc_mic_set_gain(&hdl->mic_ch, mic0_gain); } if (mic_ch & AUDIO_ADC_MIC_1) { audio_adc_mic1_open(&hdl->mic_ch, mic_ch, &adc_hdl); audio_adc_mic1_set_gain(&hdl->mic_ch, mic1_gain); } if (mic_ch & AUDIO_ADC_MIC_2) { audio_adc_mic2_open(&hdl->mic_ch, mic_ch, &adc_hdl); audio_adc_mic2_set_gain(&hdl->mic_ch, mic2_gain); } if (mic_ch & AUDIO_ADC_MIC_3) { audio_adc_mic3_open(&hdl->mic_ch, mic_ch, &adc_hdl); audio_adc_mic3_set_gain(&hdl->mic_ch, mic3_gain); } audio_adc_mic_set_sample_rate(&hdl->mic_ch, param->sample_rate); printf("adc_dma_buf: %x, size: %d", hdl->adc_dma_buf, param->adc_irq_points * 2 * param->adc_buf_num * hdl->channel); audio_adc_mic_set_buffs(&hdl->mic_ch, hdl->adc_dma_buf, param->adc_irq_points * 2, param->adc_buf_num); hdl->adc_output.handler = hearing_mic_output_handler; hdl->adc_output.priv = hdl; audio_adc_add_output_handler(&adc_hdl, &hdl->adc_output); audio_adc_mic_start(&hdl->mic_ch); } else { audio_adc_mic_close(&hdl->mic_ch); audio_adc_del_output_handler(&adc_hdl, &hdl->adc_output); audio_mic_pwr_ctl(MIC_PWR_OFF); } return 0; } /* ********************************************************************* * Audio Hearing-aid Output * Description: * Arguments : NULL * Return : NULL * Note(s) : NULL ********************************************************************* */ int hearing_output(hearing_aid_t *hdl, s16 *data, u16 len) { s16 *mic_data = data; u16 data_len = len; int wlen = 0; #if (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_LR) if (hdl->channel == 1) { /*单变双*/ for (u16 i = 0; i < (len >> 1); i++) { hdl->mono_to_dual[2 * i] = mic_data[i]; hdl->mono_to_dual[2 * i + 1] = mic_data[i]; } mic_data = hdl->mono_to_dual; data_len = len + data_len; } #else /*单声道*/ if (hdl->channel == 2) { /*双变单*/ for (u16 i = 0; i < (len >> 2); i++) { /*左右声道混合成单声道会变成杂音*/ mic_data[i] = mic_data[2 * i]; /* mic_data[i] = mic_data[2 * i + 1]; */ /* mic_data[i] = ((int)mic_data[2 * i] + (int)mic_data[2 * i + 1]) / 2; */ } data_len = len / 2; } #endif/*TCFG_AUDIO_DAC_CONNECT_MODE*/ // 写入DAC //putchar('o'); int offset = 0; int cnt = 0; do { wlen = sound_pcm_dev_write(&hdl->dac_ch, mic_data + (offset >> 1), data_len - offset); offset = offset + wlen; cnt ++; } while ((offset != data_len) && (cnt < 1000)); if (offset != data_len) { /* putchar('D'); */ printf("dac write err :%d %d\n", data_len, offset); } return len; } /* ********************************************************************* * Audio Hearing-aid Task * Description: * Arguments : NULL * Return : NULL * Note(s) : NULL ********************************************************************* */ static LOUDNESS_M_STRUCT dha_in_loudness; static LOUDNESS_M_STRUCT dha_out_loudness; static void audio_hearing_aid_task(void *p) { int err = 0; int wlen = 0; s16 *mic_data = NULL; int data_len = 0; hearing_aid_t *hdl = (hearing_aid_t *)p; while (1) { err = os_sem_pend(&hdl->sem, 0); if (err || hdl->task_release) { break; } #if DHA_RUN_TIME_TRACE_ENABLE start_ts = jiffies_msec(); #endif/*DHA_RUN_TIME_TRACE_ENABLE*/ DHA_IO_DEBUG_1(); hdl->task_busy = 1; #if DHA_MIC_DATA_CBUF_ENABLE mic_data = hdl->run_buf; data_len = cbuf_read(&hdl->mic_cbuf, mic_data, DHA_MIC_SAMPLE_POINT * sizeof(short) * hdl->channel); #else mic_data = hdl->mic_data_addr; data_len = hdl->mic_data_len; #endif /*DHA_MIC_DATA_CBUF_ENABLE*/ int clk = clk_get("sys"); if (clk < HEARING_AID_CLK) { clk_set("sys", HEARING_AID_CLK); } #if ((defined TCFG_AUDIO_DHA_LOW_POWER_ENABLE) && TCFG_AUDIO_DHA_LOW_POWER_ENABLE) audio_hearing_aid_lp_detect(NULL, mic_data, data_len); #endif /*TCFG_AUDIO_DHA_LOW_POWER_ENABLE*/ #if DHA_IN_LOUDNESS_TRACE_ENABLE loudness_meter_short(&dha_in_loudness, mic_data, data_len >> 1); #endif /*DHA_IN_LOUDNESS_TRACE_ENABLE*/ //数据导出 #if DHA_DATA_EXPORT_ENABLE aec_uart_fill(0, mic_data, data_len); #endif /*DHA_DATA_EXPORT_ENABLE*/ #if TCFG_AUDIO_DHA_FITTING_ENABLE if (dha_schedule.fitting) { if (dha_schedule.fitting == 1) { /*生成听力验配对应的频点声音*/ extern void sin_pcm_fill(int fc, int fs, void *buf, u32 len); sin_pcm_fill(sine_freq[sine_idx], DHA_MIC_SAMPLE_RATE, mic_data, data_len); } else if (dha_schedule.fitting == 2) { memset(mic_data, 0, data_len); } } else #endif /*TCFG_AUDIO_DHA_FITTING_ENABLE*/ { // 算法处理 if (hdl->channel == 1) { #if DHA_HS_FS_ENABLE if (hdl->hs_fs) { run_howling(hdl->hs_fs, mic_data, mic_data, data_len >> 1); } #endif/*DHA_HS_FS_ENABLE*/ #if DHA_HS_NOTCH_ENABLE if (hdl->hs_notch) { run_howling(hdl->hs_notch, mic_data, mic_data, data_len >> 1); } #endif/*DHA_HS_NOTCH_ENABLE*/ } else { /*双变单*/ for (int i = 0; i < data_len / 4; i++) { hdl->tmp_buf[i] = mic_data[2 * i]; hdl->tmp_buf_1[i] = mic_data[2 * i + 1]; } #if DHA_HS_FS_ENABLE if (hdl->hs_fs && hdl->hs_fs_1) { run_howling(hdl->hs_fs, hdl->tmp_buf, hdl->tmp_buf, data_len >> 2); run_howling(hdl->hs_fs_1, hdl->tmp_buf_1, hdl->tmp_buf_1, data_len >> 2); } #endif/*DHA_HS_FS_ENABLE*/ #if DHA_HS_NOTCH_ENABLE if (hdl->hs_notch && hdl->hs_notch_1) { run_howling(hdl->hs_notch, hdl->tmp_buf, hdl->tmp_buf, data_len >> 2); run_howling(hdl->hs_notch_1, hdl->tmp_buf_1, hdl->tmp_buf_1, data_len >> 2); } #endif/*DHA_HS_NOTCH_ENABLE*/ /*单变双*/ for (int i = 0; i < data_len / 4; i++) { mic_data[2 * i] = hdl->tmp_buf[i]; mic_data[2 * i + 1] = hdl->tmp_buf_1[i]; } } #if (DHA_EQ_ENABLE || DHA_DRC_ENABLE) if (hdl->hearing_eq_drc) { hearing_eq_drc_run(hdl->hearing_eq_drc, mic_data, data_len); } #endif/*DHA_EQ_ENABLE || DHA_DRC_ENABLE*/ #if (TCFG_EQ_ENABLE && DHA_EQ2_ENABLE) if (hdl->hearing_eq2) { hearing_eq2_run(hdl->hearing_eq2, mic_data, data_len); } #endif /*TCFG_EQ_ENABLE && DHA_EQ2_ENABLE*/ #if DHA_NOISE_GATE_ENABLE audio_noise_gate_run(mic_data, mic_data, data_len); #endif/*DHA_NOISE_GATE_ENABLE*/ #if DHA_NS_ENABLE if (hdl->channel == 1) { if (hdl->llns) { data_len = llns_run(hdl->llns, mic_data, data_len); } } else { /*双变单*/ for (int i = 0; i < data_len / 4; i++) { hdl->tmp_buf[i] = mic_data[2 * i]; hdl->tmp_buf_1[i] = mic_data[2 * i + 1]; } if (hdl->llns) { llns_run(hdl->llns, hdl->tmp_buf, data_len / 2); } if (hdl->llns_1) { llns_run(hdl->llns_1, hdl->tmp_buf_1, data_len / 2); } /*单变双*/ for (int i = 0; i < data_len / 4; i++) { mic_data[2 * i] = hdl->tmp_buf[i]; mic_data[2 * i + 1] = hdl->tmp_buf_1[i]; } } #endif /*DHA_NS_ENABLE*/ #if DHA_FADEIN_ENABLE hearing_fade_in_run(mic_data, (data_len >> 1)); #endif/*DHA_FADEIN_ENABLE*/ } #if DHA_VOLUME_ENABLE hearing_volume_run(mic_data, data_len); #endif/*DHA_VOLUME_ENABLE*/ //数据导出 #if DHA_DATA_EXPORT_ENABLE aec_uart_fill(1, mic_data, data_len); aec_uart_write(); #endif /*DHA_DATA_EXPORT_ENABLE*/ #if DHA_OUT_LOUDNESS_TRACE_ENABLE loudness_meter_short(&dha_out_loudness, mic_data, data_len >> 1); #endif /*DHA_OUT_LOUDNESS_TRACE_ENABLE*/ #if DHA_SRC_ENABLE #if DHA_SRC_USE_HW_ENABLE hearing_src_run(hdl, mic_data, NULL, data_len); continue; #else data_len = hearing_src_run(hdl, mic_data, hdl->src_out_data, data_len); if (data_len) { mic_data = hdl->src_out_data; } #endif/*DHA_SRC_USE_HW_ENABLE*/ #endif /*DHA_SRC_ENABLE*/ //result output hearing_output(hdl, mic_data, data_len); #if DHA_TDE_ENABLE hearing_tde_run(); #endif/*DHA_TDE_ENABLE*/ #if DHA_RUN_TIME_TRACE_ENABLE end_ts = jiffies_msec(); printf("run_time:%d - %d = %d\n", end_ts, start_ts, (end_ts - start_ts)); #endif/*DHA_RUN_TIME_TRACE_ENABLE*/ DHA_IO_DEBUG_0(); hdl->task_busy = 0; } hdl->task_exit = 1; while (1) { os_time_dly(100); } } /* ********************************************************************* * Audio Hearing-aid Open * Description: * Arguments : NULL * Return : NULL * Note(s) : NULL ********************************************************************* */ int audio_hearing_aid_open(void) { printf("audi_hearing_aid_open\n"); int err = 0; hearing_adda_param_t *param = &hearing_adda_param; hearing_aid_t *hdl = hearing_hdl; if (hdl != NULL) { printf("hdl != NULL!"); return err; } #if TCFG_AUDIO_DHA_AND_MUSIC_MUTEX /*播歌互斥时播歌不开辅听*/ extern u8 bt_media_is_running(void); if (bt_media_is_running()) { dha_schedule.state = DHA_STATE_SUSPEND; return 0; } #endif /*TCFG_AUDIO_DHA_AND_MUSIC_MUTEX*/ #if TCFG_AUDIO_DHA_AND_CALL_MUTEX /*通话互斥时通话不开辅听*/ extern u8 bt_phone_dec_is_running(); if (bt_phone_dec_is_running()) { dha_schedule.state = DHA_STATE_SUSPEND; return 0; } #endif /*TCFG_AUDIO_DHA_AND_CALL_MUTEX*/ #if TCFG_AUDIO_DHA_AND_TONE_MUTEX /*提示音互斥时播提示音不开辅听*/ extern int tone_get_status(); extern int sine_get_status(); if (tone_get_status() || sine_get_status()) { dha_schedule.state = DHA_STATE_SUSPEND; return 0; } #endif /*TCFG_AUDIO_DHA_AND_TONE_MUTEX*/ #if TCFG_AUTO_SHUT_DOWN_TIME sys_auto_shut_down_disable(); #endif/*TCFG_AUTO_SHUT_DOWN_TIME*/ mem_stats(); hdl = zalloc(sizeof(hearing_aid_t)); if (hdl == NULL) { printf("hdl malloc error!"); goto __err; } hearing_hdl = hdl; hdl->channel = 0; u8 mic_ch = hearing_adda_param.mic_ch_sel; if (mic_ch & AUDIO_ADC_MIC_0) { hdl->channel++; } if (mic_ch & AUDIO_ADC_MIC_1) { hdl->channel++; } if (mic_ch & AUDIO_ADC_MIC_2) { hdl->channel++; } if (mic_ch & AUDIO_ADC_MIC_3) { hdl->channel++; } printf(" hdl channel %d", hdl->channel); u32 adc_dma_size = param->adc_irq_points * param->adc_buf_num * sizeof(short) * hdl->channel; if (adc_dma_size == 0) { printf("adc_dma_size == NULL!"); goto __err; } hdl->adc_dma_buf = zalloc(adc_dma_size); if (hdl->adc_dma_buf == NULL) { printf("hdl->adc_dma_buf malloc error!"); goto __err; } #if DHA_MIC_DATA_CBUF_ENABLE hdl->run_buf = zalloc(DHA_MIC_SAMPLE_POINT * sizeof(short) * hdl->channel); hdl->data_buf = zalloc(DHA_MIC_SAMPLE_POINT * sizeof(short) * hdl->channel * 3); cbuf_init(&hdl->mic_cbuf, hdl->data_buf, DHA_MIC_SAMPLE_POINT * sizeof(short) * hdl->channel * 3); #endif /*DHA_MIC_DATA_CBUF_ENABLE*/ if (hdl->channel == 2) { hdl->tmp_buf = zalloc(DHA_MIC_SAMPLE_POINT * sizeof(short)); hdl->tmp_buf_1 = zalloc(DHA_MIC_SAMPLE_POINT * sizeof(short)); } clk_set("sys", HEARING_AID_CLK); /* audio_codec_clock_set(HEARING_AID_MODE, AUDIO_CODING_PCM, 0); */ #if TCFG_AUDIO_DHA_FITTING_ENABLE /*读取验配参数*/ float EQGain[DHA_FITTING_CHANNEL_MAX]; err = fitting_param_read(EQGain); if (err == DHA_FITTING_CHANNEL_MAX * 4) { put_buf(EQGain, DHA_FITTING_CHANNEL_MAX * 4); /*更新验配数据到辅听*/ hearing_aid_eq_filter_updata(EQGain); } else { printf("default fitting param !!!\n"); } #endif /*TCFG_AUDIO_DHA_FITTING_ENABLE*/ #if DHA_IN_LOUDNESS_TRACE_ENABLE loudness_meter_init(&dha_in_loudness, param->sample_rate, 50, 0); #endif /*DHA_IN_LOUDNESS_TRACE_ENABLE*/ #if DHA_OUT_LOUDNESS_TRACE_ENABLE loudness_meter_init(&dha_out_loudness, param->sample_rate, 50, 1); #endif /*DHA_IN_LOUDNESS_TRACE_ENABLE*/ //memcpy(&hdl->param, param, sizeof(struct __ad2da_low_latency_param)); os_sem_create(&hdl->sem, 0); err = task_create(audio_hearing_aid_task, (void *)hdl, HEARING_AID_TASK_NAME); if (err != OS_NO_ERR) { printf("task create error!"); goto __err; } dha_schedule.fitting = 0; dha_schedule.fitting_state = 0; //mem_stats(); #if ((defined TCFG_AUDIO_DHA_LOW_POWER_ENABLE) && TCFG_AUDIO_DHA_LOW_POWER_ENABLE) audio_hearing_aid_lp_open(1, audio_hearing_aid_open, audio_hearing_aid_close); #endif /*TCFG_AUDIO_DHA_LOW_POWER_ENABLE*/ //数据导出 #if DHA_DATA_EXPORT_ENABLE aec_uart_open(2, param->adc_irq_points * 2); #endif /*DHA_DATA_EXPORT_ENABLE*/ // 算法初始化 #if DHA_HS_FS_ENABLE ///啸叫抑制初始化 hdl->hs_fs = open_howling(NULL, param->sample_rate, 0, 1);//mode 1:移频 printf("open_howling %x\n\n\n", hdl->hs_fs); if (hdl->channel == 2) { hdl->hs_fs_1 = open_howling(NULL, param->sample_rate, 0, 1);//mode 1:移频 printf("open_howling_1 %x\n\n\n", hdl->hs_fs_1); } //mem_stats(); #endif/*DHA_HS_FS_ENABLE*/ #if DHA_HS_NOTCH_ENABLE HOWLING_PARM_SET howling_param_default = { .threshold = 25, .sample_rate = param->sample_rate, .channel = 1, .fade_time = 10, .notch_Q = 2.0f, .notch_gain = -20.0f, }; hdl->hs_notch = open_howling(&howling_param_default, param->sample_rate, 0, 0);//mode 0:陷波 printf("open_notch howling %x\n\n\n", hdl->hs_notch); if (hdl->channel == 2) { hdl->hs_notch_1 = open_howling(&howling_param_default, param->sample_rate, 0, 0);//mode 0:陷波 printf("open_notch howling_1 %x\n\n\n", hdl->hs_notch_1); } //mem_stats(); #endif/*DHA_HS_NOTCH_ENABLE*/ #if DHA_NOISE_GATE_ENABLE /*限幅器上限*/ #define LIMITER_THR -3000 /*-12000 = -12dB,放大1000倍,(-10000参考)*/ /*小于CONST_NOISE_GATE的当成噪声处理,防止清0近端声音*/ #define LIMITER_NOISE_GATE -40000 /*-12000 = -12dB,放大1000倍,(-30000参考)*/ /*低于噪声门限阈值的增益 */ #define LIMITER_NOISE_GAIN ((int) (0.2 * (1 << 30))) /*(0~1)*2^30*/ audio_noise_gate_open(param->sample_rate, LIMITER_THR, LIMITER_NOISE_GATE, LIMITER_NOISE_GAIN); #endif/*DHA_NOISE_GATE_ENABLE*/ #if (DHA_EQ_ENABLE || DHA_DRC_ENABLE) hdl->hearing_eq_drc = hearing_eq_drc_open(param->sample_rate, hdl->channel); printf("hearing_eq_drc open %x\n\n\n", hdl->hearing_eq_drc); //mem_stats(); #endif/*DHA_EQ_ENABLE || DHA_DRC_ENABLE*/ #if (TCFG_EQ_ENABLE && DHA_EQ2_ENABLE) hdl->hearing_eq2 = hearing_eq2_open(param->sample_rate, hdl->channel); printf("hearing_eq2 open %x\n\n\n", hdl->hearing_eq2); #endif /*TCFG_EQ_ENABLE && DHA_EQ2_ENABLE*/ #if DHA_NS_ENABLE hdl->llns = llns_init(param->sample_rate, 0.05f, 1.0f); if (hdl->channel == 2) { hdl->llns_1 = llns_init(param->sample_rate, 0.05f, 1.0f); } //mem_stats(); #endif /*DHA_NS_ENABLE*/ #if DHA_SRC_ENABLE hearing_src_init(hdl, hdl->channel, param->sample_rate, TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE); //mem_stats(); #endif /*DHA_SRC_ENABLE*/ app_audio_state_switch(APP_AUDIO_STATE_MUSIC, get_max_sys_vol()); app_audio_set_volume(APP_AUDIO_STATE_MUSIC, get_max_sys_vol(), 1); #if DHA_VOLUME_ENABLE hearing_volume_open(HEARING_DVOL_MAX); #endif /*DHA_VOLUME_ENABLE*/ // DAC 初始化 audio_dac_new_channel(&dac_hdl, &hdl->dac_ch); struct audio_dac_channel_attr attr; attr.delay_time = param->dac_delay; attr.protect_time = 8; attr.write_mode = WRITE_MODE_FORCE; audio_dac_channel_set_attr(&hdl->dac_ch, &attr); sound_pcm_dev_start(&hdl->dac_ch, TCFG_AD2DA_LOW_LATENCY_SAMPLE_RATE, app_audio_get_volume(APP_AUDIO_STATE_MUSIC)); #if DHA_DAC_OUTPUT_ENHANCE_ENABLE //DAC输出音量增强使能 app_audio_dac_vol_mode_set(1); #endif /*DHA_DAC_OUTPUT_ENHANCE_ENABLE*/ dac_digital_vol = audio_dac_get_digital_vol(); printf("dac_digital_vol : %d\n", dac_digital_vol); #if DHA_TDE_ENABLE memset(&dha_dte, 0, sizeof(dha_dte)); #endif/*DHA_TDE_ENABLE*/ /* hearing_volume_set(14); */ DHA_IO_DEBUG_INIT(); // ADC 初始化 hearing_aid_mic_en(1, param); dha_schedule.state = DHA_STATE_OPEN; printf("audio_hearing_aid_open success!"); mem_stats(); return 0; __err: if (hdl) { if (hdl->adc_dma_buf) { free(hdl->adc_dma_buf); } free(hdl); } hearing_hdl = NULL; #if TCFG_AUTO_SHUT_DOWN_TIME sys_auto_shut_down_enable(); #endif/*TCFG_AUTO_SHUT_DOWN_TIME*/ printf("audio_hearing_aid_open error!"); return -1; } void audio_hearing_aid_sync_open(void) { #if TCFG_USER_TWS_ENABLE if (get_tws_sibling_connect_state()) { if (tws_api_get_role() == TWS_ROLE_MASTER) { printf("[tws_master]dha open"); bt_tws_play_tone_at_same_time(SYNC_TONE_HEARING_AID_OPEN, 400); } } else { audio_hearing_aid_open(); } #else audio_hearing_aid_open(); #endif/*TCFG_USER_TWS_ENABLE*/ } /* ********************************************************************* * Audio Hearing-aid Close * Description: * Arguments : NULL * Return : NULL * Note(s) : NULL ********************************************************************* */ int audio_hearing_aid_close(void) { int err = 0; hearing_aid_t *hdl = (hearing_aid_t *)hearing_hdl; printf("[DHA]audio_hearing_aid_close\n"); if (hdl == NULL) { if (dha_schedule.state == DHA_STATE_SUSPEND) { printf("[DHA]hearing-aid suspend now\n"); dha_schedule.state = DHA_STATE_CLOSE; return 0; } printf("audio_hearing_aid_close error! hdl == NULL"); return -1; } // ADC 关闭 hearing_aid_mic_en(0, NULL); hdl->task_release = 1; os_sem_set(&hdl->sem, 0); os_sem_post(&hdl->sem); while (hdl->task_exit == 0) { os_time_dly(1); } err = task_kill(HEARING_AID_TASK_NAME); os_sem_del(&hdl->sem, 0); // DAC 关闭 sound_pcm_dev_stop(&hdl->dac_ch); #if DHA_DAC_OUTPUT_ENHANCE_ENABLE app_audio_dac_vol_mode_set(0); #endif /*DHA_DAC_OUTPUT_ENHANCE_ENABLE*/ #if DHA_HS_FS_ENABLE if (hdl->hs_fs) { printf("close_howling\n\n\n"); close_howling(hdl->hs_fs); } if (hdl->hs_fs_1) { printf("close_howling_1\n\n\n"); close_howling(hdl->hs_fs_1); } #endif/*DHA_HS_FS_ENABLE*/ #if DHA_HS_NOTCH_ENABLE if (hdl->hs_notch) { printf("close_howling\n\n\n"); close_howling(hdl->hs_notch); } if (hdl->hs_notch_1) { printf("close_howling\n\n\n"); close_howling(hdl->hs_notch_1); } #endif/*DHA_HS_NOTCH_ENABLE*/ #if DHA_NOISE_GATE_ENABLE audio_noise_gate_close(); #endif/*DHA_NOISE_GATE_ENABLE*/ #if (DHA_EQ_ENABLE || DHA_DRC_ENABLE) if (hdl->hearing_eq_drc) { hearing_eq_drc_close(hdl->hearing_eq_drc); } #endif/*DHA_EQ_ENABLE || DHA_DRC_ENABLE*/ #if (TCFG_EQ_ENABLE && DHA_EQ2_ENABLE) if (hdl->hearing_eq2) { hearing_eq2_close(hdl->hearing_eq2); } #endif /*TCFG_EQ_ENABLE && DHA_EQ2_ENABLE*/ #if DHA_NS_ENABLE if (hdl->llns) { llns_exit(hdl->llns); hdl->llns = NULL; if (hdl->llns_1) { llns_exit(hdl->llns_1); hdl->llns_1 = NULL; } } #endif /*DHA_NS_ENABLE*/ #if DHA_SRC_ENABLE hearing_src_exit(hdl); #endif /*DHA_SRC_ENABLE*/ #if DHA_VOLUME_ENABLE hearing_volume_close(); #endif /*DHA_VOLUME_ENABLE*/ //数据导出 #if DHA_DATA_EXPORT_ENABLE aec_uart_close(); #endif /*DHA_DATA_EXPORT_ENABLE*/ #if DHA_MIC_DATA_CBUF_ENABLE if (hdl->run_buf) { free(hdl->run_buf); hdl->run_buf = NULL; } if (hdl->data_buf) { free(hdl->data_buf); hdl->data_buf = NULL; } #endif /*DHA_MIC_DATA_CBUF_ENABLE*/ if (hdl->adc_dma_buf) { free(hdl->adc_dma_buf); hdl->adc_dma_buf = NULL; } if (hdl->tmp_buf) { free(hdl->tmp_buf); hdl->tmp_buf = NULL; } if (hdl->tmp_buf_1) { free(hdl->tmp_buf_1); hdl->tmp_buf_1 = NULL; } free(hdl); hearing_hdl = NULL; /* audio_codec_clock_del(HEARING_AID_MODE); */ // 算法关闭 dha_schedule.state = DHA_STATE_CLOSE; #if TCFG_AUDIO_DHA_FITTING_ENABLE /*验配过程关闭辅听主动告诉app*/ extern u8 get_rcsp_connect_status(void); if (get_rcsp_connect_status()) { /*主动推送辅听状态信息*/ u8 tmp[4]; get_hearing_aid_state_cmd_info(tmp); hearing_aid_rcsp_notify(tmp, 4); } #endif /*TCFG_AUDIO_DHA_FITTING_ENABLE*/ dha_schedule.fitting = 0; dha_schedule.fitting_state = 0; #if TCFG_AUTO_SHUT_DOWN_TIME sys_auto_shut_down_enable(); #endif/*TCFG_AUTO_SHUT_DOWN_TIME*/ printf("audio_hearing_aid_close success!\n"); return 0; } char *dha_state_str[] = { "close", "open", "suspend", "error", }; /*TWS配对后,状态同步*/ u8 get_hearing_aid_state(void) { return dha_schedule.state; } static void hearing_aid_state_sync_deal(void *param) { printf("[DHA]sync open,cur_state = %s\n", dha_state_str[dha_schedule.state]); if (dha_schedule.timer) { sys_timer_del(dha_schedule.timer); dha_schedule.timer = 0; } if (dha_schedule.state == DHA_STATE_OPEN) { audio_hearing_aid_open(); } } void hearing_aid_state_sync(u8 state) { if (dha_schedule.state != state) { dha_schedule.state = state; printf("[DHA]open, cur_state= %s,timer:0x%x\n", dha_state_str[dha_schedule.state], dha_schedule.timer); if ((dha_schedule.state == DHA_STATE_OPEN) && (dha_schedule.timer == 0)) { dha_schedule.timer = sys_timer_add(NULL, hearing_aid_state_sync_deal, 1500); } } } /* ********************************************************************* * Audio Hearing-aid Demo * Description: * Arguments : NULL * Return : NULL * Note(s) : NULL ********************************************************************* */ void audio_hearing_aid_demo(void) { hearing_aid_t *hdl = (hearing_aid_t *)hearing_hdl; printf("audio_hearing_aid_demo,toggle = %x\n", hdl); if (hdl == NULL) { audio_hearing_aid_sync_open(); } else { /*验配过程中不允许按键关闭辅听*/ if (get_hearing_aid_fitting_state()) { return; } audio_hearing_aid_close(); } } /* ********************************************************************* * Audio Hearing-aid Schedule * Description: suspend or resume * Arguments : NULL * Return : NULL * Note(s) : NULL ********************************************************************* */ void audio_hearing_aid_suspend(void) { #if DHA_AND_MEDIA_MUTEX_ENABLE printf("[DHA]suspend,cur_state = %s\n", dha_state_str[dha_schedule.state]); if (dha_schedule.timer) { printf("[DHA]schedule timer delete\n"); sys_timer_del(dha_schedule.timer); dha_schedule.timer = 0; } if (hearing_hdl) { audio_hearing_aid_close(); dha_schedule.state = DHA_STATE_SUSPEND; } #endif/*DHA_AND_MEDIA_MUTEX_ENABLE*/ } static void hearing_aid_schedule(void *priv) { printf("[DHA]schedule,cur_state = %s\n", dha_state_str[dha_schedule.state]); sys_timer_del(dha_schedule.timer); dha_schedule.timer = 0; if (dha_schedule.state == DHA_STATE_SUSPEND) { audio_hearing_aid_sync_open(); } } void audio_hearing_aid_resume(void) { #if DHA_AND_MEDIA_MUTEX_ENABLE printf("[DHA]resume, cur_state= %s,timer:0x%x\n", dha_state_str[dha_schedule.state], dha_schedule.timer); if ((dha_schedule.state == DHA_STATE_SUSPEND) && (dha_schedule.timer == 0)) { dha_schedule.timer = sys_timer_add(NULL, hearing_aid_schedule, 2500); } #endif/*DHA_AND_MEDIA_MUTEX_ENABLE*/ } static u8 hearing_aid_idle_query() { #if ((defined TCFG_AUDIO_DHA_LOW_POWER_ENABLE) && TCFG_AUDIO_DHA_LOW_POWER_ENABLE) if (audio_hearing_aid_lp_flag()) { return 1; //进入低功耗 } else { return 0; //不能进 } #else return hearing_hdl ? 0 : 1; #endif /*TCFG_AUDIO_DHA_LOW_POWER_ENABLE*/ } REGISTER_LP_TARGET(hearing_aid_lp_target) = { .name = "hearing_aid", .is_idle = hearing_aid_idle_query, }; #endif //TCFG_AUDIO_HEARING_AID_ENABLE