Files
99_7018_lmx/apps/common/audio/audio_dvol.c

451 lines
14 KiB
C
Raw Normal View History

2025-10-29 13:10:02 +08:00
/*
****************************************************************
* 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;
}