/* **************************************************************** * AUDIO DIGITAL VOLUME * File : audio_dvol.c * By : * Notes : 数字音量模块,支持多通道音量控制 **************************************************************** */ #include "audio_dvol.h" #if 0 #define dvol_log y_printf #else #define dvol_log(...) #endif #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 13 /*>= BG_DVOL_MAX:自动淡出BG_DVOL_MAX_FADE*/ #define BG_DVOL_MID_FADE 9 /*>= BG_DVOL_MID:自动淡出BG_DVOL_MID_FADE*/ #define BG_DVOL_MIN_FADE 5 /*>= BG_DVOL_MIN:自动淡出BG_DVOL_MIN_FADE*/ #define ASM_ENABLE 1 #define L_sat(b,a) __asm__ volatile("%0=sat16(%1)(s)":"=&r"(b) : "r"(a)); #define L_sat32(b,a,n) __asm__ volatile("%0=%1>>%2(s)":"=&r"(b) : "r"(a),"r"(n)); typedef struct { u8 bg_dvol_fade_out; u8 start; OS_MUTEX mutex; struct list_head dvol_head; } 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 93 , //1 111 , //2 132 , //3 158 , //4 189 , //5 226 , //6 270 , //7 323 , //8 386 , //9 462 , //10 552 , //11 660 , //12 789 , //13 943 , //14 1127, //15 1347, //16 1610, //17 1925, //18 2301, //19 2751, //20 3288, //21 3930, //22 4698, //23 5616, //24 6713, //25 8025, //26 9592, //27 11466,//28 15200,//29 16000,//30 16384 //31 }; static u16 digital_vol_max = DEFAULT_DIGITAL_VOL_MAX; static u16 *dig_vol_table = default_dig_vol_table; /* ********************************************************************* * 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); os_mutex_create(&dvol_attr.mutex); if (vol_table != NULL) { dig_vol_table = vol_table; digital_vol_max = 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 : dvol_idx 数字音量通道索引,详见audio_dvol.h宏定义 * vol 当前数字音量等级 * vol_max 最大数字音量等级 * fade_step 淡入淡出步进 * vol_limit 数字音量限制,即可以给vol_max等分的最大音量 * Return : 数字音量通道句柄 * Note(s) : fade_step一般不超过两级数字音量的最小差值 * (1)通话如果用数字音量,一般步进小一点,音量调节的时候不会有杂音 * (2)淡出的时候可以快一点,尽快淡出到0 ********************************************************************* */ dvol_handle *audio_digital_vol_open(u8 dvol_idx, u8 vol, u8 vol_max, u16 fade_step, char 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->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 = digital_vol_max; } else { dvol->vol_limit = (vol_limit > digital_vol_max) ? digital_vol_max : vol_limit; } vol_level = dvol->vol * dvol->vol_limit / vol_max; dvol->vol_target = dig_vol_table[vol_level]; dvol->vol_fade = dvol->vol_target; dvol->fade_step = fade_step; dvol->toggle = 1; dvol->idx = dvol_idx; local_irq_disable(); list_add(&dvol->entry, &dvol_attr.dvol_head); #if BG_DVOL_FADE_ENABLE 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->idx)) { 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; } u8 vol_level = hdl->vol * dvol->vol_limit / hdl->vol_max; hdl->vol_target = dig_vol_table[vol_level]; //y_printf("bg_dvol fade_out:%x,vol_bk:%d,vol_set:%d,tartget:%d",hdl,hdl->vol_bk,hdl->vol,hdl->vol_target); } } } #endif/*BG_DVOL_FADE_ENABLE*/ local_irq_enable(); dvol_log("dvol_open[%x]:%x-%d-%d-%d\n", dvol_idx, dvol, dvol->vol, dvol->vol_max, fade_step); } return dvol; } /* ********************************************************************* * Audio Digital Volume Close * Description: 数字音量模块关闭 * Arguments : dvol_idx 数字音量通道索引,详见audio_dvol.h宏定义 * Return : None. * Note(s) : None. ********************************************************************* */ void audio_digital_vol_close(u8 dvol_idx) { dvol_log("dvol_close[%x]\n", dvol_idx); dvol_handle *dvol = NULL; u8 dvol_valid = 0; list_for_each_entry(dvol, &dvol_attr.dvol_head, entry) { if (dvol && (dvol->idx == dvol_idx)) { dvol_log("dvol_close[%x]:%x", dvol_idx, dvol); dvol_valid = 1; break; } } if (dvol_valid == 0) { return; } if (dvol) { local_irq_disable(); #if BG_DVOL_FADE_ENABLE 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 * dvol->vol_limit / hdl->vol_max; hdl->vol_target = dig_vol_table[vol_level]; hdl->vol_bk = -1; } } #endif list_del(&dvol->entry); free(dvol); dvol = NULL; local_irq_enable(); } } /* ********************************************************************* * Audio Digital Volume Get * Description: 数字音量获取 * Arguments : dvol_idx 数字音量通道索引,详见audio_dvol.h宏定义 * Return : 对应的数字音量通道的当前音量等级. * Note(s) : None. ********************************************************************* */ int audio_digital_vol_get(u8 dvol_idx) { dvol_handle *dvol = NULL; u8 dvol_valid = 0; local_irq_disable(); list_for_each_entry(dvol, &dvol_attr.dvol_head, entry) { if (dvol && (dvol->idx == dvol_idx)) { dvol_valid = 1; break; } } local_irq_enable(); if (dvol && dvol_valid) { return dvol->vol; } return 0; } /* ********************************************************************* * Audio Digital Volume Set * Description: 数字音量设置 * Arguments : dvol_idx 数字音量通道索引,详见audio_dvol.h宏定义 * vol 目标音量等级 * Return : None. * Note(s) : None. ********************************************************************* */ void audio_digital_vol_set(u8 dvol_idx, u8 vol) { dvol_log("audio_digital_vol set:%d,%d\n", dvol_idx, vol); dvol_handle *dvol = NULL; u8 dvol_valid = 0; local_irq_disable(); list_for_each_entry(dvol, &dvol_attr.dvol_head, entry) { if (dvol && (dvol->idx == dvol_idx)) { dvol_log("dvol_set[%x]:%x", dvol_idx, dvol); dvol_valid = 1; break; } } local_irq_enable(); if ((dvol == NULL) || (dvol_valid == 0)) { 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; dvol->vol_target = dig_vol_table[vol_level]; 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_idx 数字音量通道索引,详见audio_dvol.h宏定义 * vol 目标音量等级 * Return : None. * Note(s) : None. ********************************************************************* */ void audio_digital_vol_set_no_fade(u8 dvol_idx, u8 vol) { dvol_log("audio_digital_vol set:%d,%d\n", dvol_idx, vol); dvol_handle *dvol = NULL; u8 dvol_valid = 0; local_irq_disable(); list_for_each_entry(dvol, &dvol_attr.dvol_head, entry) { if (dvol && (dvol->idx == dvol_idx)) { dvol_log("dvol_set[%x]:%x", dvol_idx, dvol); dvol_valid = 1; break; } } local_irq_enable(); if ((dvol == NULL) || (dvol_valid == 0)) { 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; dvol->vol_fade = dig_vol_table[vol_level]; dvol_log("digital_vol:%d-%d-%d-%d\n", vol, vol_level, dvol->vol_fade, dvol->vol_target); } void audio_digital_vol_reset_fade(u8 dvol_idx) { dvol_handle *dvol = NULL; u8 dvol_valid = 0; local_irq_disable(); list_for_each_entry(dvol, &dvol_attr.dvol_head, entry) { if (dvol && (dvol->idx == dvol_idx)) { dvol_valid = 1; break; } } local_irq_enable(); if (dvol && dvol_valid) { dvol->vol_fade = 0; } } /* ********************************************************************* * Audio Digital Volume Process * Description: 数字音量运算核心 * Arguments : dvol_idx 数字音量通道索引,详见audio_dvol.h宏定义 * data 待运算数据 * len 待运算数据长度 * Return : 0 成功 其他 失败 * Note(s) : None. ********************************************************************* */ /*rounding处理*/ #define MUL32_PSHIFT(x,y,s) (((x) * (y) + (1 << ((s)-1))) >> (s)) int audio_digital_vol_run(u8 dvol_idx, void *data, u32 len) { s32 valuetemp; s16 *buf; os_mutex_pend(&dvol_attr.mutex, 0); dvol_handle *dvol = NULL; u8 dvol_valid = 0; local_irq_disable(); list_for_each_entry(dvol, &dvol_attr.dvol_head, entry) { if (dvol && (dvol->idx == dvol_idx)) { dvol_valid = 1; #if 0 static dvol_handle *last_dvol = NULL; if (last_dvol != dvol) { dvol_log("dvol_run[%x]:%x", dvol_idx, dvol); last_dvol = dvol; } #endif break; } } local_irq_enable(); if ((dvol->toggle == 0) || (dvol_valid == 0)) { os_mutex_post(&dvol_attr.mutex); return -1; } buf = data; len >>= 1; //byte to point for (u32 i = 0; i < len; i += 2) { ///left channel if (dvol->fade) { if (dvol->vol_fade > dvol->vol_target) { dvol->vol_fade -= dvol->fade_step; if (dvol->vol_fade < dvol->vol_target) { dvol->vol_fade = dvol->vol_target; } } else if (dvol->vol_fade < dvol->vol_target) { dvol->vol_fade += dvol->fade_step; if (dvol->vol_fade > dvol->vol_target) { dvol->vol_fade = dvol->vol_target; } } } else { dvol->vol_fade = dvol->vol_target; } valuetemp = buf[i]; if (valuetemp < 0) { /*负数先转换成正数,运算完再转换回去,是为了避免负数右移位引入1的误差,增加底噪*/ valuetemp = -valuetemp; /*rounding处理(加入0.5),减少小信号时候的误差和谐波幅值*/ valuetemp = (valuetemp * dvol->vol_fade + (1 << 13)) >> 14 ; valuetemp = -valuetemp; } else { valuetemp = (valuetemp * dvol->vol_fade + (1 << 13)) >> 14 ; } /*饱和处理*/ buf[i] = (s16)data_sat_s16(valuetemp); ///right channel valuetemp = buf[i + 1]; if (valuetemp < 0) { /*负数先转换成正数,运算完再转换回去,是为了避免负数右移位引入1的误差,增加底噪*/ valuetemp = -valuetemp; valuetemp = (valuetemp * dvol->vol_fade + (1 << 13)) >> 14 ; valuetemp = -valuetemp; } else { valuetemp = (valuetemp * dvol->vol_fade + (1 << 13)) >> 14 ; } /*饱和处理*/ buf[i + 1] = (s16)data_sat_s16(valuetemp); } os_mutex_post(&dvol_attr.mutex); return 0; }