#ifdef SUPPORT_MS_EXTENSIONS #pragma bss_seg(".audio_dvol.data.bss") #pragma data_seg(".audio_dvol.data") #pragma const_seg(".audio_dvol.text.const") #pragma code_seg(".audio_dvol.text") #endif /* **************************************************************** * AUDIO DIGITAL VOLUME * File : audio_dvol.c * By : * Notes : 数字音量模块,支持多通道音量控制 **************************************************************** */ #include "audio_dvol.h" #include "system/spinlock.h" #include "effects/audio_eq.h" #include "audio_config.h" #if 0 #define dvol_log y_printf #else #define dvol_log(...) #endif #define __AUDIO_DIGITAL_VOL_CACHE_CODE __attribute__((section(".digital_vol.text.cache.L2.run"))) #define DIGITAL_FADE_EN 1 #define DIGITAL_FADE_STEP 4 #define BG_DVOL_MAX 14 #define BG_DVOL_MID 10 #define BG_DVOL_MIN 6 #define BG_DVOL_MAX_FADE 5 /*>= BG_DVOL_MAX:自动淡出BG_DVOL_MAX_FADE*/ #define BG_DVOL_MID_FADE 3 /*>= BG_DVOL_MID:自动淡出BG_DVOL_MID_FADE*/ #define BG_DVOL_MIN_FADE 1 /*>= BG_DVOL_MIN:自动淡出BG_DVOL_MIN_FADE*/ typedef struct { u8 bg_dvol_fade_out; u8 start; u16 digital_vol_max; u16 *dig_vol_table; struct list_head dvol_head; spinlock_t lock; } dvol_t; static dvol_t dvol_attr; /* *数字音量级数 DEFAULT_DIGITAL_VOL_MAX *数组长度 DEFAULT_DIGITAL_VOL_MAX + 1 */ #define DEFAULT_DIGITAL_VOL_MAX (31) const u16 default_dig_vol_table[DEFAULT_DIGITAL_VOL_MAX + 1] = { 0, //0: None 92, // 1:-45.00 db 110, // 2:-43.50 db 130, // 3:-42.00 db 155, // 4:-40.50 db 184, // 5:-39.00 db 218, // 6:-37.50 db 260, // 7:-36.00 db 309, // 8:-34.50 db 367, // 9:-33.00 db 436, // 10:-31.50 db 518, // 11:-30.00 db 616, // 12:-28.50 db 732, // 13:-27.00 db 870, // 14:-25.50 db 1034, // 15:-24.00 db 1229, // 16:-22.50 db 1460, // 17:-21.00 db 1735, // 18:-19.50 db 2063, // 19:-18.00 db 2451, // 20:-16.50 db 2914, // 21:-15.00 db 3463, // 22:-13.50 db 4115, // 23:-12.00 db 4891, // 24:-10.50 db 5813, // 25:-9.00 db 6909, // 26:-7.50 db 8211, // 27:-6.00 db 9759, // 28:-4.50 db 11599, // 29:-3.00 db 13785, // 30:-1.50 db 16384, // 31:0.00 db }; /* ********************************************************************* * Audio Digital Volume Init * Description: 数字音量模块初始化 * Arguments : None. * Return : 0 成功 其他 失败 * Note(s) : None. ********************************************************************* */ int audio_digital_vol_init(u16 *vol_table, u16 vol_max) { memset(&dvol_attr, 0, sizeof(dvol_attr)); INIT_LIST_HEAD(&dvol_attr.dvol_head); spin_lock_init(&dvol_attr.lock); if (vol_table != NULL) { dvol_attr.dig_vol_table = vol_table; dvol_attr.digital_vol_max = vol_max; } else { dvol_attr.dig_vol_table = (u16 *)default_dig_vol_table; dvol_attr.digital_vol_max = DEFAULT_DIGITAL_VOL_MAX; } return 0; } /*背景音乐淡出使能*/ void audio_digital_vol_bg_fade(u8 fade_out) { dvol_log("audio_digital_vol_bg_fade:%d", fade_out); dvol_attr.bg_dvol_fade_out = fade_out; } /* ********************************************************************* * Audio Digital Volume Open * Description: 数字音量模块打开 * Arguments : vol 当前数字音量等级 * vol_max 最大数字音量等级 * fade_step 淡入淡出步进 * vol_limit 数字音量限制,即可以给vol_max等分的最大音量 * Return : 数字音量通道句柄 * Note(s) : fade_step一般不超过两级数字音量的最小差值 * (1)通话如果用数字音量,一般步进小一点,音量调节的时候不会有杂音 * (2)淡出的时候可以快一点,尽快淡出到0 ********************************************************************* */ /* dvol_handle *audio_digital_vol_open(u16 vol, u16 vol_max, u16 fade_step, s16 vol_limit) */ dvol_handle *audio_digital_vol_open(struct audio_vol_params params) { u16 vol = params.vol; u16 vol_max = params.vol_max; u16 fade_step = params.fade_step; s16 vol_limit = params.vol_limit; dvol_log("dvol_open:%d-%d-%d-%d\n", vol, vol_max, fade_step, vol_limit); dvol_handle *dvol = NULL; dvol = zalloc(sizeof(dvol_handle)); if (dvol) { u8 vol_level; dvol->bit_wide = params.bit_wide; dvol->fade = DIGITAL_FADE_EN; dvol->vol = (vol > vol_max) ? vol_max : vol; if (vol > vol_max) { printf("[warning]cur digital_vol(%d) > digital_vol_max(%d)!!", vol, vol_max); } dvol->vol_max = vol_max; if (vol_limit == -1) { dvol->vol_limit = dvol->vol_max; } else { dvol->vol_limit = (vol_limit > dvol->vol_max) ? dvol->vol_max : vol_limit; } vol_level = dvol->vol * dvol->vol_limit / vol_max; if (dvol->vol_table_default) { dvol->vol_target = dvol_attr.dig_vol_table[vol_level]; } else { if (vol_level == 0) { dvol->vol_target = 0; } else { if (!dvol->vol_table) { u16 step = (dvol->cfg_level_max - 1 > 0) ? (dvol->cfg_level_max - 1) : 1; float step_db = (dvol->cfg_vol_max - dvol->cfg_vol_min) / (float)step; float hdl_db = dvol->cfg_vol_min + (vol_level - 1) * step_db; float hdl_gain = eq_db2mag(hdl_db);//dB转换倍数 dvol->vol_target = (s16)(DVOL_MAX_FLOAT * hdl_gain + 0.5f); //printf("vol param calc:%d,%d,%d,%d,%d",(int)hdl_gain,dvol->vol_target,(int)step_db,dvol->cfg_level_max - vol_level,(int)hdl_db); } else { dvol->vol_target = dvol->vol_table[vol_level - 1]; } } } dvol->vol_fade = dvol->vol_target; dvol->fade_step = fade_step; dvol->toggle = 1; #if BG_DVOL_FADE_ENABLE spin_lock(&dvol_attr.lock); list_add(&dvol->entry, &dvol_attr.dvol_head); dvol->vol_bk = -1; if (dvol_attr.bg_dvol_fade_out) { dvol_handle *hdl; list_for_each_entry(hdl, &dvol_attr.dvol_head, entry) { if (hdl != dvol) { hdl->vol_bk = hdl->vol; if (hdl->vol >= BG_DVOL_MAX) { hdl->vol -= BG_DVOL_MAX_FADE; } else if (hdl->vol >= BG_DVOL_MID) { hdl->vol -= BG_DVOL_MID_FADE; } else if (hdl->vol >= BG_DVOL_MIN) { hdl->vol -= BG_DVOL_MIN_FADE; } else { hdl->vol_bk = -1; continue; } vol_level = hdl->vol * hdl->vol_limit / hdl->vol_max; if (hdl->vol_table_default) { hdl->vol_target = dvol_attr.dig_vol_table[vol_level]; } else { if (vol_level == 0) { hdl->vol_target = 0; } else { if (!hdl->vol_table) { u16 step = (hdl->cfg_level_max - 1 > 0) ? (hdl->cfg_level_max - 1) : 1; float step_db = (hdl->cfg_vol_max - hdl->cfg_vol_min) / (float)step; float hdl_db = hdl->cfg_vol_min + (vol_level - 1) * step_db; float hdl_gain = eq_db2mag(hdl_db);//dB转换倍数 hdl->vol_target = (s16)(DVOL_MAX_FLOAT * hdl_gain + 0.5f); /* printf("vol param:%d,%d,%d,%d,%d",(int)hdl_gain,hdl->vol_target,(int)step_db,hdl->cfg_level_max - vol_level,(int)hdl_db); */ } else { hdl->vol_target = hdl->vol_table[vol_level - 1]; } } } //y_printf("bg_dvol fade_out:%x,vol_bk:%d,vol_set:%d,tartget:%d",hdl,hdl->vol_bk,hdl->vol,hdl->vol_target); } } } spin_unlock(&dvol_attr.lock); #endif/*BG_DVOL_FADE_ENABLE*/ /*dvol_log("dvol_open:%x-%d-%d-%d\n", dvol, dvol->vol, dvol->vol_max, fade_step);*/ } return dvol; } /* ********************************************************************* * Audio Digital Volume Close * Description: 数字音量模块关闭 * Arguments : dvol 数字音量操作句柄,详见audio_dvol.h宏定义 * Return : None. * Note(s) : None. ********************************************************************* */ void audio_digital_vol_close(dvol_handle *dvol) { if (dvol) { #if BG_DVOL_FADE_ENABLE spin_lock(&dvol_attr.lock); list_del(&dvol->entry); dvol_handle *hdl; list_for_each_entry(hdl, &dvol_attr.dvol_head, entry) { if ((hdl != dvol) && (hdl->vol_bk >= 0)) { //y_printf("bg_dvol fade_in:%x,%d",hdl,hdl->vol_bk); hdl->vol = hdl->vol_bk; u8 vol_level = hdl->vol_bk * hdl->vol_limit / hdl->vol_max; if (hdl->vol_table_default) { hdl->vol_target = dvol_attr.dig_vol_table[vol_level]; hdl->vol_bk = -1; } else { if (vol_level == 0) { hdl->vol_target = 0; hdl->vol_bk = -1; } else { if (!hdl->vol_table) { u16 step = (hdl->cfg_level_max - 1 > 0) ? (hdl->cfg_level_max - 1) : 1; float step_db = (hdl->cfg_vol_max - hdl->cfg_vol_min) / (float)step; float hdl_db = hdl->cfg_vol_min + (vol_level - 1) * step_db; float hdl_gain = eq_db2mag(hdl_db);//dB转换倍数 hdl->vol_target = (s16)(DVOL_MAX_FLOAT * hdl_gain + 0.5f); hdl->vol_bk = -1; } else { hdl->vol_target = hdl->vol_table[vol_level - 1]; hdl->vol_bk = -1; } } } } } spin_unlock(&dvol_attr.lock); #endif free(dvol); } } /* ********************************************************************* * Audio Digital Mute Set * Description: 数字音量设置mute * Arguments : dvol 数字音量操作句柄,详见audio_dvol.h宏定义 * mute_en 是否使能mute * Return : None. * Note(s) : None. ********************************************************************* */ void audio_digital_vol_mute_set(dvol_handle *dvol, u8 mute_en) { if (dvol == NULL) { return; } dvol->mute_en = mute_en; } /* ********************************************************************* * Audio Digital Volume Set * Description: 数字音量设置 * Arguments : dvol 数字音量操作句柄,详见audio_dvol.h宏定义 * vol 目标音量等级 * Return : None. * Note(s) : None. ********************************************************************* */ void audio_digital_vol_set(dvol_handle *dvol, u16 vol) { if (dvol == NULL) { return; } if (dvol->toggle == 0) { return; } dvol->vol = (vol > dvol->vol_max) ? dvol->vol_max : vol; #if BG_DVOL_FADE_ENABLE if (dvol->vol_bk != -1) { dvol->vol_bk = vol; } #endif dvol->fade = DIGITAL_FADE_EN; u8 vol_level = dvol->vol * dvol->vol_limit / dvol->vol_max; if (dvol->vol_table_default) { dvol->vol_target = dvol_attr.dig_vol_table[vol_level]; } else { if (vol_level == 0) { dvol->vol_target = 0; } else { if (!dvol->vol_table) { u16 step = (dvol->cfg_level_max - 1 > 0) ? (dvol->cfg_level_max - 1) : 1; float step_db = (dvol->cfg_vol_max - dvol->cfg_vol_min) / (float)step; float dvol_db = dvol->cfg_vol_min + (vol_level - 1) * step_db; float dvol_gain = eq_db2mag(dvol_db);//dB转换倍数 dvol->vol_target = (s16)(DVOL_MAX_FLOAT * dvol_gain + 0.5f); /* printf("vol param:%d,%d,%d,%d,%d",(int)dvol_gain,dvol->vol_target,(int)step_db,dvol->cfg_level_max - vol_level,(int)dvol_db); */ } else { dvol->vol_target = dvol->vol_table[vol_level - 1]; } } } dvol_log("digital_vol:%d-%d-%d-%d\n", vol, vol_level, dvol->vol_fade, dvol->vol_target); } /********************************************************************* * Audio Digital Volume Set * Description: 数字音量设置,设置数字音量的当前值,不用淡入淡出 * Arguments : dvol 数字音量操作句柄,详见audio_dvol.h宏定义 * vol 目标音量等级 * Return : None. * Note(s) : None. ********************************************************************* */ void audio_digital_vol_set_no_fade(dvol_handle *dvol, u8 vol) { dvol_log("audio_digital_vol set:%p,%d\n", dvol, vol); if (dvol == NULL) { return; } if (dvol->toggle == 0) { return; } dvol->vol = (vol > dvol->vol_max) ? dvol->vol_max : vol; #if BG_DVOL_FADE_ENABLE if (dvol->vol_bk != -1) { dvol->vol_bk = vol; } #endif dvol->fade = DIGITAL_FADE_EN; u8 vol_level = dvol->vol * dvol->vol_limit / dvol->vol_max; if (dvol->vol_table_default) { dvol->vol_fade = dvol_attr.dig_vol_table[vol_level]; } else { if (vol_level == 0) { dvol->vol_target = 0; } else { if (!dvol->vol_table) { u16 step = (dvol->cfg_level_max - 1 > 0) ? (dvol->cfg_level_max - 1) : 1; float step_db = (dvol->cfg_vol_max - dvol->cfg_vol_min) / (float)step; float dvol_db = dvol->cfg_vol_min + (vol_level - 1) * step_db; float dvol_gain = eq_db2mag(dvol_db);//dB转换倍数 dvol->vol_fade = (s16)(DVOL_MAX_FLOAT * dvol_gain + 0.5); /* printf("vol param:%d,%d,%d,%d,%d",(int)dvol_gain,dvol->vol_target,(int)step_db,dvol->cfg_level_max - vol_level,(int)dvol_db); */ } else { dvol->vol_target = dvol->vol_table[vol_level - 1]; } } } dvol_log("digital_vol:%d-%d-%d-%d\n", vol, vol_level, dvol->vol_fade, dvol->vol_target); } void audio_digital_vol_reset_fade(dvol_handle *dvol) { if (dvol) { dvol->vol_fade = 0; } } /* ********************************************************************* * Audio Digital Volume Process * Description: 数字音量运算核心 * Arguments : dvol 数字音量操作句柄,详见audio_dvol.h宏定义 * data 待运算数据 * len 待运算数据长度 * Return : 0 成功 其他 失败 * Note(s) : None. ********************************************************************* */ //#define DIGITAL_VOLUME_SAT_ENABLE __AUDIO_DIGITAL_VOL_CACHE_CODE int audio_digital_vol_run(dvol_handle *dvol, void *data, u32 len) { u8 fade; s32 valuetemp; s16 *buf; if ((!dvol) || (dvol->toggle == 0)) { return -1; } s16 vol_target = dvol->vol_target; if (dvol->mute_en) { vol_target = 0; } if ((dvol->vol_fade != vol_target) && dvol->fade) { fade = 1; } else { dvol->vol_fade = vol_target; fade = 0; } if (dvol->bit_wide) { #if MEDIA_24BIT_ENABLE s32 *buf32 = data; len >>= 2; for (u32 i = 0; i < len; i += 1) { if (fade) { if (dvol->vol_fade > vol_target) { dvol->vol_fade -= dvol->fade_step; if (dvol->vol_fade < vol_target) { dvol->vol_fade = vol_target; } } else if (dvol->vol_fade < vol_target) { dvol->vol_fade += dvol->fade_step; if (dvol->vol_fade > vol_target) { dvol->vol_fade = vol_target; } } } valuetemp = buf32[i]; if (valuetemp < 0) { /*负数先转换成正数,运算完再转换回去,是为了避免负数右移位引入1的误差,增加底噪*/ valuetemp = -valuetemp; /*rounding处理(加入0.5),减少小信号时候的误差和谐波幅值*/ valuetemp = ((long long)valuetemp * (long long)dvol->vol_fade + (long long)(1 << 13)) >> DVOL_RESOLUTION ; valuetemp = -valuetemp; } else { valuetemp = ((long long)valuetemp * (long long)dvol->vol_fade + (long long)(1 << 13)) >> DVOL_RESOLUTION ; } #ifdef DIGITAL_VOLUME_SAT_ENABLE /*饱和处理*/ if (valuetemp > 8388607) { //2^23 -1 valuetemp = 8388607; } else if (valuetemp < -8388608) { //-2^23 valuetemp = -8388608; } #endif buf32[i] = valuetemp; } #endif } else { buf = data; len >>= 1; //byte to point for (u32 i = 0; i < len; i += 1) { if (fade) { if (dvol->vol_fade > vol_target) { dvol->vol_fade -= dvol->fade_step; if (dvol->vol_fade < vol_target) { dvol->vol_fade = vol_target; } } else if (dvol->vol_fade < vol_target) { dvol->vol_fade += dvol->fade_step; if (dvol->vol_fade > vol_target) { dvol->vol_fade = vol_target; } } } valuetemp = buf[i]; if (valuetemp < 0) { /*负数先转换成正数,运算完再转换回去,是为了避免负数右移位引入1的误差,增加底噪*/ valuetemp = -valuetemp; /*rounding处理(加入0.5),减少小信号时候的误差和谐波幅值*/ valuetemp = (valuetemp * dvol->vol_fade + (1 << 13)) >> DVOL_RESOLUTION ; valuetemp = -valuetemp; } else { valuetemp = (valuetemp * dvol->vol_fade + (1 << 13)) >> DVOL_RESOLUTION ; } #ifdef DIGITAL_VOLUME_SAT_ENABLE /*饱和处理*/ buf[i] = (s16)data_sat_s16(valuetemp); #else buf[i] = (s16)valuetemp; #endif } } return 0; }