3653 lines
96 KiB
C
3653 lines
96 KiB
C
#include "asm/includes.h"
|
||
#include "media/includes.h"
|
||
#include "system/includes.h"
|
||
#include "classic/tws_api.h"
|
||
#include "classic/hci_lmp.h"
|
||
#include "effectrs_sync.h"
|
||
#include "application/eq_config.h"
|
||
#include "application/audio_energy_detect.h"
|
||
#include "app_config.h"
|
||
#include "audio_config.h"
|
||
#include "aec_user.h"
|
||
#include "audio_enc.h"
|
||
#include "app_main.h"
|
||
#include "btstack/avctp_user.h"
|
||
#include "btstack/a2dp_media_codec.h"
|
||
#include "tone_player.h"
|
||
#include "audio_syncts.h"
|
||
#include "audio_dec_eff.h"
|
||
#include "audio_codec_clock.h"
|
||
#include "audio_utils.h"
|
||
#if TCFG_USER_TWS_ENABLE
|
||
#include "bt_tws.h"
|
||
#endif/*TCFG_USER_TWS_ENABLE*/
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
#include "audio_digital_vol.h"
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
|
||
#include "audio_plc.h"
|
||
#if TCFG_AUDIO_NOISE_GATE
|
||
#include "audio_noise_gate.h"
|
||
#endif/*TCFG_AUDIO_NOISE_GATE*/
|
||
|
||
#if TCFG_APP_FM_EMITTER_EN
|
||
#include "fm_emitter/fm_emitter_manage.h"
|
||
#endif
|
||
|
||
#if TCFG_AUDIO_ANC_ENABLE
|
||
#include "audio_anc.h"
|
||
#endif/*TCFG_AUDIO_ANC_ENABLE*/
|
||
|
||
#include "phone_message/phone_message.h"
|
||
|
||
|
||
|
||
#ifdef SUPPORT_MS_EXTENSIONS
|
||
/* #pragma bss_seg(".audio_decoder_bss") */
|
||
/* #pragma data_seg(".audio_decoder_data") */
|
||
#pragma const_seg(".audio_decoder_const")
|
||
#pragma code_seg(".audio_decoder_code")
|
||
#endif
|
||
|
||
#ifdef CONFIG_256K_FLASH
|
||
#define CONFIG_MINI_BT_AUDIO_ENABLE 1
|
||
#else
|
||
#define CONFIG_MINI_BT_AUDIO_ENABLE 0
|
||
#endif
|
||
|
||
#define AUDIO_CODEC_SUPPORT_SYNC 1
|
||
|
||
#define A2DP_AUDIO_PLC_ENABLE 1
|
||
|
||
#if A2DP_AUDIO_PLC_ENABLE
|
||
#include "media/tech_lib/LFaudio_plc_api.h"
|
||
#endif
|
||
|
||
#define A2DP_RX_AND_AUDIO_DELAY 1
|
||
|
||
#define ESCO_DRC_EN 0 //通话下行增加限幅器处理,默认关闭
|
||
|
||
#if (!TCFG_DRC_ENABLE || !TCFG_PHONE_EQ_ENABLE)
|
||
#undef ESCO_DRC_EN 0
|
||
#define ESCO_DRC_EN 0
|
||
#endif
|
||
|
||
#if AUDIO_OUT_EFFECT_ENABLE
|
||
int audio_out_effect_open(void *priv, u16 sample_rate);
|
||
void audio_out_effect_close(void);
|
||
int audio_out_effect_stream_clear();
|
||
struct dec_eq_drc *audio_out_effect = NULL;
|
||
#endif /*AUDIO_OUT_EFFECT_ENABLE*/
|
||
|
||
static u8 audio_out_effect_dis = 0;
|
||
static int slience_samples;
|
||
|
||
|
||
struct tone_dec_hdl {
|
||
struct audio_decoder decoder;
|
||
void (*handler)(void *, int argc, int *argv);
|
||
void *priv;
|
||
};
|
||
|
||
struct a2dp_dec_hdl {
|
||
struct audio_decoder decoder;
|
||
struct audio_res_wait wait;
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
struct audio_mixer_ch mix_ch;
|
||
#endif
|
||
enum audio_channel channel;
|
||
u8 start;
|
||
u8 ch;
|
||
s16 header_len;
|
||
u8 remain;
|
||
u8 eq_remain;
|
||
u8 fetch_lock;
|
||
u8 preempt;
|
||
u8 stream_error;
|
||
u8 new_frame;
|
||
u8 repair;
|
||
void *sample_detect;
|
||
void *syncts;
|
||
void *repair_pkt;
|
||
s16 repair_pkt_len;
|
||
u16 missed_num;
|
||
u16 repair_frames;
|
||
u16 overrun_seqn;
|
||
u16 slience_frames;
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
u8 ts_start;
|
||
u8 sync_step;
|
||
void *ts_handle;
|
||
u32 timestamp;
|
||
u32 base_time;
|
||
#endif /*AUDIO_CODEC_SUPPORT_SYNC*/
|
||
#if A2DP_AUDIO_PLC_ENABLE
|
||
LFaudio_PLC_API *plc_ops;
|
||
void *plc_mem;
|
||
#endif /*A2DP_AUDIO_PLC_ENABLE*/
|
||
u32 mix_ch_event_params[3];
|
||
u32 pending_time;
|
||
u16 seqn;
|
||
u16 sample_rate;
|
||
int timer;
|
||
u32 coding_type;
|
||
u16 delay_time;
|
||
u16 detect_timer;
|
||
u8 underrun_feedback;
|
||
|
||
#if TCFG_EQ_ENABLE&&TCFG_BT_MUSIC_EQ_ENABLE
|
||
struct dec_eq_drc *eq_drc;
|
||
#endif//TCFG_BT_MUSIC_EQ_ENABLE
|
||
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
dvol_handle *dvol;
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
};
|
||
|
||
|
||
struct esco_dec_hdl {
|
||
struct audio_decoder decoder;
|
||
struct audio_res_wait wait;
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
struct audio_mixer_ch mix_ch;
|
||
#endif
|
||
u32 coding_type;
|
||
u8 *frame;
|
||
u8 frame_len;
|
||
u8 offset;
|
||
u8 data_len;
|
||
u8 tws_mute_en;
|
||
u8 start;
|
||
u8 enc_start;
|
||
u8 frame_time;
|
||
u8 preempt;
|
||
u8 repair;
|
||
u16 slience_frames;
|
||
void *syncts;
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
void *ts_handle;
|
||
u8 ts_start;
|
||
u8 sync_step;
|
||
u8 preempt_state;
|
||
#endif
|
||
u32 mix_ch_event_params[3];
|
||
u8 esco_len;
|
||
u8 frame_err_len;
|
||
u8 remain;
|
||
|
||
int data[15];/*ALIGNED(4)*/
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
dvol_handle *dvol;
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
|
||
#if TCFG_EQ_ENABLE&&TCFG_PHONE_EQ_ENABLE
|
||
struct dec_eq_drc *eq_drc;
|
||
#endif//TCFG_PHONE_EQ_ENABLE
|
||
u32 hash;
|
||
};
|
||
|
||
#if AUDIO_OUTPUT_AUTOMUTE
|
||
void *mix_out_automute_hdl = NULL;
|
||
extern void mix_out_automute_open();
|
||
extern void mix_out_automute_close();
|
||
#endif //#if AUDIO_OUTPUT_AUTOMUTE
|
||
|
||
void *audio_sync = NULL;
|
||
|
||
#if (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_LR)
|
||
#define MIX_POINTS_NUM 256
|
||
#else
|
||
#define MIX_POINTS_NUM 128
|
||
#endif
|
||
#if A2DP_RX_AND_AUDIO_DELAY
|
||
#define AUDIO_DAC_DELAY_TIME 30
|
||
#else
|
||
#define AUDIO_DAC_DELAY_TIME 50
|
||
#endif
|
||
|
||
#if (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_LR)
|
||
s16 dac_buff[6 * 1024];
|
||
#else
|
||
s16 dac_buff[2 * 1024 - 512];
|
||
#endif
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
#define DEC_RUN_BY_ITSELF 0
|
||
#define DEC_PREEMTED_BY_PRIORITY 1
|
||
#define DEC_PREEMTED_BY_OUTSIDE 3
|
||
#define DEC_RESUME_BY_OUTSIDE 2
|
||
/* [>AAC解码需要加大同步和MIX的buffer用来提高异步效率<] */
|
||
|
||
s16 dac_sync_filt[24 * 2];
|
||
#endif
|
||
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
s16 mix_buff[MIX_POINTS_NUM];
|
||
#endif
|
||
|
||
#define MAX_SRC_NUMBER 3
|
||
s16 audio_src_hw_filt[24 * 2 * MAX_SRC_NUMBER];
|
||
|
||
#define A2DP_TO_PCM_DEVICE_FORMAT 0//48000
|
||
#define A2DP_FLUENT_STREAM_MODE 1//流畅模式
|
||
#define A2DP_FLUENT_DETECT_INTERVAL 100000//ms 流畅播放延时检测时长
|
||
#if A2DP_FLUENT_STREAM_MODE
|
||
#define A2DP_MAX_PENDING_TIME 120
|
||
#else
|
||
#define A2DP_MAX_PENDING_TIME 40
|
||
#endif
|
||
|
||
#define A2DP_STREAM_NO_ERR 0
|
||
#define A2DP_STREAM_UNDERRUN 1
|
||
#define A2DP_STREAM_OVERRUN 2
|
||
#define A2DP_STREAM_MISSED 3
|
||
#define A2DP_STREAM_DECODE_ERR 4
|
||
#define A2DP_STREAM_LOW_UNDERRUN 5
|
||
|
||
/*OS_SEM dac_sem;*/
|
||
static u16 dac_idle_delay_max = 10000;
|
||
static u16 dac_idle_delay_cnt = 10000;
|
||
static struct tone_dec_hdl *tone_dec = NULL;
|
||
struct audio_decoder_task decode_task;
|
||
struct audio_dac_hdl dac_hdl;
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
struct audio_mixer mixer;
|
||
#endif
|
||
extern struct dac_platform_data dac_data;
|
||
extern struct audio_adc_hdl adc_hdl;
|
||
|
||
static u16 a2dp_delay_time;
|
||
static u8 a2dp_low_latency = 0;
|
||
static u16 drop_a2dp_timer;
|
||
static u16 a2dp_low_latency_seqn = 0;
|
||
extern const int CONFIG_LOW_LATENCY_ENABLE;
|
||
extern const int CONFIG_A2DP_DELAY_TIME;
|
||
extern const int CONFIG_A2DP_DELAY_TIME_LO;
|
||
extern const int CONFIG_A2DP_SBC_DELAY_TIME_LO;
|
||
extern const int const_surround_en;
|
||
|
||
struct a2dp_dec_hdl *a2dp_dec = NULL;
|
||
struct esco_dec_hdl *esco_dec = NULL;
|
||
|
||
extern int platform_device_sbc_init();
|
||
|
||
int lmp_private_get_esco_remain_buffer_size();
|
||
int lmp_private_get_esco_data_len();
|
||
void *lmp_private_get_esco_packet(int *len, u32 *hash);
|
||
void lmp_private_free_esco_packet(void *packet);
|
||
extern int lmp_private_esco_suspend_resume(int flag);
|
||
extern int bt_audio_sync_nettime_select(u8 basetime);
|
||
extern u32 bt_audio_sync_lat_time(void);
|
||
void audio_syncts_add(void *priv);
|
||
void audio_syncts_del(void *priv);
|
||
void audio_output_open(void *priv, u32 sample_rate);
|
||
int audio_dac_start_pre(struct audio_dac_hdl *dac);
|
||
|
||
void *a2dp_play_sync_open(u8 channel, u32 sample_rate, u32 output_rate, u32 coding_type);
|
||
|
||
void *esco_play_sync_open(u8 channel, u16 sample_rate, u16 output_rate);
|
||
//void audio_adc_init(void);
|
||
void *audio_adc_open(void *param, const char *source);
|
||
int audio_adc_sample_start(void *adc);
|
||
void audio_fade_in_fade_out(u8 left_gain, u8 right_gain);
|
||
|
||
void set_source_sample_rate(u16 sample_rate);
|
||
u8 bt_audio_is_running(void);
|
||
|
||
void audio_resume_all_decoder(void)
|
||
{
|
||
audio_decoder_resume_all(&decode_task);
|
||
}
|
||
|
||
void audio_src_isr_deal(void)
|
||
{
|
||
audio_resume_all_decoder();
|
||
}
|
||
|
||
|
||
#define AUDIO_DECODE_TASK_WAKEUP_TIME 4 //ms
|
||
#if AUDIO_DECODE_TASK_WAKEUP_TIME
|
||
#include "timer.h"
|
||
static void audio_decoder_wakeup_timer(void *priv)
|
||
{
|
||
/*putchar('k');*/
|
||
audio_decoder_resume_all(&decode_task);
|
||
}
|
||
int audio_decoder_task_add_probe(struct audio_decoder_task *task)
|
||
{
|
||
local_irq_disable();
|
||
if (task->wakeup_timer == 0) {
|
||
task->wakeup_timer = usr_timer_add(NULL, audio_decoder_wakeup_timer, AUDIO_DECODE_TASK_WAKEUP_TIME, 1);
|
||
}
|
||
local_irq_enable();
|
||
log_i("audio_decoder_task_add_probe:%d\n", task->wakeup_timer);
|
||
return 0;
|
||
}
|
||
int audio_decoder_task_del_probe(struct audio_decoder_task *task)
|
||
{
|
||
log_i("audio_decoder_task_del_probe\n");
|
||
if (audio_decoder_task_wait_state(task) > 0) {
|
||
/*解码任务列表还有任务*/
|
||
return 0;
|
||
}
|
||
|
||
local_irq_disable();
|
||
if (task->wakeup_timer) {
|
||
log_i("audio_decoder_task_del_probe:%d\n", task->wakeup_timer);
|
||
usr_timer_del(task->wakeup_timer);
|
||
task->wakeup_timer = 0;
|
||
}
|
||
local_irq_enable();
|
||
return 0;
|
||
}
|
||
|
||
int audio_decoder_wakeup_modify(int msecs)
|
||
{
|
||
if (decode_task.wakeup_timer) {
|
||
usr_timer_modify(decode_task.wakeup_timer, msecs);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* DA输出源启动后可使用DAC irq做唤醒,省去hi_timer
|
||
*/
|
||
int audio_decoder_wakeup_select(u8 source)
|
||
{
|
||
if (source == 0) {
|
||
/*唤醒源为hi_timer*/
|
||
local_irq_disable();
|
||
if (!decode_task.wakeup_timer) {
|
||
decode_task.wakeup_timer = usr_timer_add(NULL, audio_decoder_wakeup_timer, AUDIO_DECODE_TASK_WAKEUP_TIME, 1);
|
||
}
|
||
local_irq_enable();
|
||
} else if (source == 1) {
|
||
#if TCFG_AUDIO_OUTPUT_IIS
|
||
/*唤醒源为输出目标中断*/
|
||
if (!audio_iis_is_working()) {
|
||
return audio_decoder_wakeup_select(0);
|
||
}
|
||
|
||
int err = audio_iis_irq_resume_decoder(a2dp_low_latency ? 2 : AUDIO_DECODE_TASK_WAKEUP_TIME, NULL, audio_decoder_wakeup_timer);
|
||
#else
|
||
/*唤醒源为输出目标中断*/
|
||
if (!audio_dac_is_working(&dac_hdl)) {
|
||
return audio_decoder_wakeup_select(0);
|
||
}
|
||
|
||
/*int err = audio_dac_set_irq_time(&dac_hdl, a2dp_low_latency ? 2 : AUDIO_DECODE_TASK_WAKEUP_TIME);*/
|
||
int err = audio_dac_irq_enable(&dac_hdl, a2dp_low_latency ? 2 : AUDIO_DECODE_TASK_WAKEUP_TIME,
|
||
NULL, audio_decoder_wakeup_timer);
|
||
#endif
|
||
if (err) {
|
||
return audio_decoder_wakeup_select(0);
|
||
}
|
||
|
||
local_irq_disable();
|
||
if (decode_task.wakeup_timer) {
|
||
usr_timer_del(decode_task.wakeup_timer);
|
||
decode_task.wakeup_timer = 0;
|
||
}
|
||
local_irq_enable();
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#endif/*AUDIO_DECODE_TASK_WAKEUP_TIME*/
|
||
|
||
static u8 bt_dec_idle_query()
|
||
{
|
||
if (a2dp_dec || esco_dec) {
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
REGISTER_LP_TARGET(bt_dec_lp_target) = {
|
||
.name = "bt_dec",
|
||
.is_idle = bt_dec_idle_query,
|
||
};
|
||
|
||
|
||
___interrupt
|
||
static void audio_irq_handler()
|
||
{
|
||
/* putchar('A'); */
|
||
if (JL_AUDIO->DAC_CON & BIT(7)) {
|
||
JL_AUDIO->DAC_CON |= BIT(6);
|
||
if (JL_AUDIO->DAC_CON & BIT(5)) {
|
||
audio_decoder_resume_all(&decode_task);
|
||
audio_dac_irq_handler(&dac_hdl);
|
||
/*r_printf("resuem\n");*/
|
||
}
|
||
}
|
||
|
||
if (JL_AUDIO->ADC_CON & BIT(7)) {
|
||
JL_AUDIO->ADC_CON |= BIT(6);
|
||
if ((JL_AUDIO->ADC_CON & BIT(5)) && (JL_AUDIO->ADC_CON & BIT(4))) {
|
||
audio_adc_irq_handler(&adc_hdl);
|
||
}
|
||
}
|
||
}
|
||
|
||
static int _audio_output_handler(s16 *data, u16 len)
|
||
{
|
||
|
||
int rlen = len;
|
||
int wlen = 0;
|
||
|
||
/*os_sem_set(&dac_sem, 0);*/
|
||
/* audio_aec_refbuf(data, len); */
|
||
#if AUDIO_OUTPUT_AUTOMUTE
|
||
audio_energy_detect_run(mix_out_automute_hdl, data, len);
|
||
#endif //#if AUDIO_OUTPUT_AUTOMUTE
|
||
|
||
#if TCFG_AUDIO_OUTPUT_IIS
|
||
u8 dac_connect_mode = audio_dac_get_channel(&dac_hdl);
|
||
if (dac_connect_mode == DAC_OUTPUT_LR) {
|
||
wlen = audio_iis_output_write(data, len);
|
||
} else {
|
||
s16 *mono_data = (s16 *)data;
|
||
s16 point_num = 0;
|
||
u16 remain_points = (len >> 1);
|
||
s16 two_ch_data[IIS_MONO_TO_DUAL_POINTS * 2];
|
||
|
||
do {
|
||
point_num = IIS_MONO_TO_DUAL_POINTS;
|
||
if (point_num >= remain_points) {
|
||
point_num = remain_points;
|
||
}
|
||
audio_pcm_mono_to_dual(two_ch_data, mono_data, point_num);
|
||
int tmp_len = audio_iis_output_write(two_ch_data, point_num << 2);
|
||
wlen += tmp_len;
|
||
remain_points -= (tmp_len >> 2);
|
||
if (tmp_len < (point_num << 2)) {
|
||
break;
|
||
}
|
||
mono_data += point_num;
|
||
} while (remain_points);
|
||
wlen = wlen >> 1;
|
||
}
|
||
|
||
#elif TCFG_APP_FM_EMITTER_EN
|
||
fm_emitter_cbuf_write((u8 *)data, len);
|
||
#else
|
||
wlen = audio_dac_write(&dac_hdl, data, len);
|
||
#endif
|
||
|
||
return wlen;
|
||
}
|
||
|
||
|
||
static int audio_output_handler(s16 *data, u16 len)
|
||
{
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
return _audio_output_handler(data, len);
|
||
#else
|
||
int wlen = 0;
|
||
if (slience_samples) {
|
||
u16 points = ARRAY_SIZE(dac_buff);
|
||
int slience_len = (slience_samples < points ? slience_samples : points) * 2;
|
||
wlen = _audio_output_handler(dac_buff, slience_len);
|
||
slience_samples -= wlen / 2;
|
||
wlen = 0;
|
||
}
|
||
if (slience_samples == 0) {
|
||
wlen = _audio_output_handler(data, len);
|
||
}
|
||
return wlen;
|
||
|
||
#endif
|
||
}
|
||
|
||
#if AUDIO_OUT_EFFECT_ENABLE
|
||
static int audio_output_effect_handler(s16 *data, u16 len)
|
||
{
|
||
if (!audio_out_effect) {
|
||
return audio_output_handler(data, len);
|
||
}
|
||
|
||
if (audio_out_effect && audio_out_effect->async) {
|
||
return eq_drc_run(audio_out_effect, data, len);
|
||
}
|
||
|
||
if (!audio_out_effect->remain) {
|
||
if (audio_out_effect && !audio_out_effect->async) {
|
||
eq_drc_run(audio_out_effect, data, len);
|
||
}
|
||
}
|
||
int wlen = audio_output_handler(data, len);
|
||
if (wlen == len) {
|
||
audio_out_effect->remain = 0;
|
||
} else {
|
||
audio_out_effect->remain = 1;
|
||
}
|
||
return wlen;
|
||
}
|
||
#endif
|
||
|
||
int audio_output_data(s16 *data, u16 len)
|
||
{
|
||
#if AUDIO_OUT_EFFECT_ENABLE
|
||
return audio_output_effect_handler(data, len);
|
||
#else
|
||
return audio_output_handler(data, len);
|
||
#endif
|
||
}
|
||
int audio_output_data_a2dp(void *p, s16 *data, u16 len)
|
||
{
|
||
#if !AUDIO_OUT_MIXER_ENABLE
|
||
struct a2dp_dec_hdl *dec = p;
|
||
if (dec->start == 1) {
|
||
dec->start = 2;
|
||
audio_output_open(NULL, dec->sample_rate);
|
||
audio_syncts_add(dec->mix_ch_event_params);
|
||
}
|
||
#endif
|
||
return audio_output_data(data, len);
|
||
}
|
||
|
||
int audio_output_data_esco(void *p, s16 *data, u16 len)
|
||
{
|
||
#if !AUDIO_OUT_MIXER_ENABLE
|
||
struct esco_dec_hdl *dec = (struct esco_dec_hdl *)p;
|
||
if (dec->start == 1) {
|
||
dec->start = 2;
|
||
audio_output_open(NULL, dec->decoder.fmt.sample_rate);
|
||
audio_syncts_add(dec->mix_ch_event_params);
|
||
}
|
||
#endif
|
||
return audio_output_data(data, len);
|
||
}
|
||
|
||
|
||
|
||
#if AUDIO_OUTPUT_AUTOMUTE
|
||
|
||
void audio_mix_out_automute_mute(u8 mute)
|
||
{
|
||
printf(">>>>>>>>>>>>>>>>>>>> %s\n", mute ? ("MUTE") : ("UNMUTE"));
|
||
if (mute) {
|
||
app_audio_mute(AUDIO_MUTE_DEFAULT);
|
||
} else {
|
||
app_audio_mute(AUDIO_UNMUTE_DEFAULT);
|
||
}
|
||
}
|
||
|
||
/* #define AUDIO_E_DET_UNMUTE (0x00) */
|
||
/* #define AUDIO_E_DET_MUTE (0x01) */
|
||
void mix_out_automute_handler(u8 event, u8 ch)
|
||
{
|
||
printf(">>>> ch:%d %s\n", ch, event ? ("MUTE") : ("UNMUTE"));
|
||
if (ch == dac_hdl.channel) {
|
||
audio_mix_out_automute_mute(event);
|
||
}
|
||
}
|
||
|
||
void mix_out_automute_skip(u8 skip)
|
||
{
|
||
u8 mute = !skip;
|
||
if (mix_out_automute_hdl) {
|
||
audio_energy_detect_skip(mix_out_automute_hdl, 0xFFFF, skip);
|
||
audio_mix_out_automute_mute(mute);
|
||
}
|
||
}
|
||
|
||
void mix_out_automute_open()
|
||
{
|
||
if (mix_out_automute_hdl) {
|
||
printf("mix_out_automute is already open !\n");
|
||
return;
|
||
}
|
||
audio_energy_detect_param e_det_param = {0};
|
||
e_det_param.mute_energy = 5;
|
||
e_det_param.unmute_energy = 10;
|
||
e_det_param.mute_time_ms = 1000;
|
||
e_det_param.unmute_time_ms = 50;
|
||
e_det_param.count_cycle_ms = 10;
|
||
e_det_param.sample_rate = 44100;
|
||
e_det_param.event_handler = mix_out_automute_handler;
|
||
e_det_param.ch_total = dac_hdl.channel;
|
||
e_det_param.dcc = 1;
|
||
mix_out_automute_hdl = audio_energy_detect_open(&e_det_param);
|
||
}
|
||
|
||
void mix_out_automute_close()
|
||
{
|
||
if (mix_out_automute_hdl) {
|
||
audio_energy_detect_close(mix_out_automute_hdl);
|
||
}
|
||
}
|
||
#endif //#if AUDIO_OUTPUT_AUTOMUTE
|
||
|
||
#if AUDIO_OUT_EFFECT_ENABLE
|
||
|
||
int audio_out_eq_spec_set_gain(u8 idx, int gain)
|
||
{
|
||
if (!audio_out_effect) {
|
||
return -1;
|
||
}
|
||
audio_out_eq_set_gain(audio_out_effect, idx, gain);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int mix_output_handler(struct audio_mixer *mixer, s16 *data, u16 len);
|
||
static void audio_out_effect_close(void)
|
||
{
|
||
if (!audio_out_effect) {
|
||
return ;
|
||
}
|
||
audio_out_eq_drc_free(audio_out_effect);
|
||
audio_out_effect = NULL;
|
||
}
|
||
|
||
static int audio_out_effect_stream_clear()
|
||
{
|
||
if (!audio_out_effect) {
|
||
return 0;
|
||
}
|
||
|
||
if (audio_out_effect->eq && audio_out_effect->async) {
|
||
audio_eq_async_data_clear(audio_out_effect->eq);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int audio_out_effect_open(void *priv, u16 sample_rate)
|
||
{
|
||
audio_out_effect_close();
|
||
|
||
u8 ch_num;
|
||
#if TCFG_APP_FM_EMITTER_EN
|
||
ch_num = 2;
|
||
#else
|
||
u8 dac_connect_mode = audio_dac_get_channel(&dac_hdl);
|
||
if (dac_connect_mode == DAC_OUTPUT_LR) {
|
||
ch_num = 2;
|
||
} else {
|
||
ch_num = 1;
|
||
}
|
||
#endif//TCFG_APP_FM_EMITTER_EN
|
||
|
||
audio_out_effect = audio_out_eq_drc_setup(priv, (eq_output_cb)mix_output_handler, sample_rate, ch_num, 1, 0);
|
||
return 0;
|
||
}
|
||
|
||
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
static int mix_output_effect_handler(struct audio_mixer *mixer, s16 *data, u16 len)
|
||
{
|
||
if (!audio_out_effect) {
|
||
return mix_output_handler(mixer, data, len);
|
||
}
|
||
|
||
if (audio_out_effect && audio_out_effect->async) {
|
||
return eq_drc_run(audio_out_effect, data, len);
|
||
}
|
||
|
||
if (!audio_out_effect->remain) {
|
||
if (audio_out_effect && !audio_out_effect->async) {
|
||
eq_drc_run(audio_out_effect, data, len);
|
||
}
|
||
}
|
||
int wlen = mix_output_handler(mixer, data, len);
|
||
if (wlen == len) {
|
||
audio_out_effect->remain = 0;
|
||
} else {
|
||
audio_out_effect->remain = 1;
|
||
}
|
||
return wlen;
|
||
}
|
||
#endif
|
||
#endif/*AUDIO_OUT_EFFECT_ENABLE*/
|
||
|
||
extern void audio_adc_mic_demo(u8 mic_idx, u8 gain, u8 mic_2_dac);
|
||
extern void dac_analog_power_control(u8 en);
|
||
extern void audio_dac_output_enable(struct audio_dac_hdl *dac);
|
||
extern void audio_dac_output_disable(struct audio_dac_hdl *dac);
|
||
|
||
u8 audio_fast_mode = 0;
|
||
void audio_fast_mode_test()
|
||
{
|
||
audio_fast_mode = 1;
|
||
audio_dac_set_volume(&dac_hdl, app_audio_get_volume(APP_AUDIO_CURRENT_STATE));
|
||
dac_analog_power_control(1);////将关闭基带,不开可发现,不可连接
|
||
audio_dac_start(&dac_hdl);
|
||
audio_adc_mic_demo(AUDIO_ADC_MIC_CH, 10, 1);
|
||
|
||
}
|
||
|
||
u8 dac_power_off_flag = 1;
|
||
u8 is_dac_power_off()
|
||
{
|
||
if (a2dp_dec || esco_dec || get_call_status() != BT_CALL_HANGUP) {
|
||
return 1;
|
||
}
|
||
return dac_power_off_flag;
|
||
}
|
||
|
||
void dac_analog_power_control(u8 en)
|
||
{
|
||
#ifdef CONFIG_CURRENT_SOUND_DEAL_ENABLE
|
||
if (en) {
|
||
dac_power_off_flag = 0;
|
||
audio_dac_output_enable(&dac_hdl);
|
||
} else {
|
||
#if TCFG_AUDIO_ANC_ENABLE
|
||
if (!anc_status_get()) {
|
||
puts("anc dac_power_off");
|
||
/*dac_power_off();*/
|
||
audio_dac_output_disable(&dac_hdl);
|
||
}
|
||
#else
|
||
puts("dac_power_off");
|
||
audio_dac_output_disable(&dac_hdl);
|
||
/*dac_power_off();*/
|
||
#endif
|
||
dac_power_off_flag = 1;
|
||
}
|
||
#endif
|
||
}
|
||
int audio_dac_add_slience_samples(int samples)
|
||
{
|
||
slience_samples = samples;
|
||
return 0;
|
||
}
|
||
|
||
void audio_output_open(void *priv, u32 sample_rate)
|
||
{
|
||
slience_samples = 0;
|
||
#if AUDIO_OUTPUT_AUTOMUTE
|
||
audio_energy_detect_sample_rate_update(mix_out_automute_hdl, sample_rate);
|
||
#endif //#if AUDIO_OUTPUT_AUTOMUTE
|
||
|
||
#if TCFG_AUDIO_OUTPUT_IIS
|
||
audio_iis_output_set_srI(audio_mixer_get_sample_rate(priv));
|
||
audio_iis_output_start(1);
|
||
#elif TCFG_APP_FM_EMITTER_EN
|
||
|
||
#else
|
||
if (A2DP_TO_PCM_DEVICE_FORMAT && a2dp_dec && a2dp_dec->start) {
|
||
audio_dac_set_sample_rate(&dac_hdl, A2DP_TO_PCM_DEVICE_FORMAT);
|
||
} else {
|
||
audio_dac_set_sample_rate(&dac_hdl, sample_rate);
|
||
}
|
||
audio_dac_set_volume(&dac_hdl, app_audio_get_volume(APP_AUDIO_CURRENT_STATE));
|
||
audio_dac_start(&dac_hdl);
|
||
dac_analog_power_control(1);
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
g_printf("MIXER_EVENT_CH_OPEN=%d\n", dac_power_off_flag);
|
||
audio_dac_set_input_correct_callback(&dac_hdl,
|
||
(void *)priv,
|
||
(void (*)(void *, int))audio_mixer_position_correct);
|
||
#endif
|
||
|
||
#endif
|
||
#if AUDIO_OUT_EFFECT_ENABLE
|
||
if (!audio_out_effect_dis) {
|
||
audio_out_effect_open(priv, sample_rate);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void audio_output_close(void *priv)
|
||
{
|
||
audio_dac_set_input_correct_callback(&dac_hdl, NULL, NULL);
|
||
#if AUDIO_OUT_EFFECT_ENABLE
|
||
audio_out_effect_close();
|
||
#endif
|
||
|
||
#if TCFG_AUDIO_OUTPUT_IIS
|
||
audio_iis_output_stop();
|
||
#elif TCFG_APP_FM_EMITTER_EN
|
||
|
||
#else
|
||
audio_dac_set_protect_time(&dac_hdl, 0, NULL, NULL);
|
||
audio_dac_stop(&dac_hdl);
|
||
dac_analog_power_control(0);
|
||
g_printf("MIXER_EVENT_CH_CLOSE=%d\n", dac_power_off_flag);
|
||
if (audio_fast_mode) {
|
||
audio_fast_mode_test();
|
||
}
|
||
#endif
|
||
}
|
||
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
static void mixer_event_handler(struct audio_mixer *mixer, int event)
|
||
{
|
||
switch (event) {
|
||
case MIXER_EVENT_CH_OPEN:
|
||
if (audio_mixer_get_start_ch_num(mixer) == 1) {
|
||
audio_output_open(mixer, audio_mixer_get_sample_rate(mixer));
|
||
}
|
||
break;
|
||
case MIXER_EVENT_CH_CLOSE:
|
||
if (audio_mixer_get_ch_num(mixer) == 0) {
|
||
audio_output_close(mixer);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
static int mix_probe_handler(struct audio_mixer *mixer)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
#define IIS_MONO_TO_DUAL_POINTS 64
|
||
static inline void audio_pcm_mono_to_dual(s16 *dual_pcm, s16 *mono_pcm, int points);
|
||
static int mix_output_handler(struct audio_mixer *mixer, s16 *data, u16 len)
|
||
{
|
||
return audio_output_handler(data, len);
|
||
}
|
||
|
||
static const struct audio_mix_handler mix_handler = {
|
||
.mix_probe = mix_probe_handler,
|
||
|
||
#if AUDIO_OUT_EFFECT_ENABLE
|
||
.mix_output = mix_output_effect_handler,
|
||
#else
|
||
.mix_output = mix_output_handler,
|
||
#endif
|
||
};
|
||
|
||
void audio_syncts_add(void *priv)
|
||
{
|
||
if (priv) {
|
||
u32 *params = (u32 *)priv;
|
||
struct audio_mixer_ch *ch = (struct audio_mixer_ch *)params[0];
|
||
u32 base_time = params[2];
|
||
#if defined(TCFG_AUDIO_OUTPUT_IIS) && TCFG_AUDIO_OUTPUT_IIS
|
||
audio_iis_add_syncts_handle((void *)params[1]);
|
||
#else
|
||
audio_dac_add_syncts_handle(&dac_hdl, (void *)params[1]);
|
||
#endif
|
||
u32 current_time = (bt_audio_sync_lat_time() * 625 * TIME_US_FACTOR);
|
||
u32 time_diff = ((base_time - current_time) & 0xffffffff) / TIME_US_FACTOR;
|
||
/*printf("-----base time : %u, current_time : %u------\n", base_time, current_time);*/
|
||
if (time_diff < 500000) {
|
||
int buf_frame = audio_dac_buffered_frames(&dac_hdl);
|
||
#if defined(TCFG_AUDIO_OUTPUT_IIS) && TCFG_AUDIO_OUTPUT_IIS
|
||
buf_frame = audio_iis_buffered_frames();
|
||
#endif
|
||
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
int sample_rate = audio_mixer_get_sample_rate(&mixer);
|
||
#else
|
||
int sample_rate = audio_dac_get_sample_rate(&dac_hdl);
|
||
#endif
|
||
int slience_frames = (u64)time_diff * sample_rate / 1000000 - buf_frame;
|
||
if (slience_frames < 0) {
|
||
slience_frames = 0;
|
||
}
|
||
/*printf("-------slience_frames : %d-------\n", slience_frames);*/
|
||
sound_pcm_update_frame_num((void *)params[1], -slience_frames);
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
audio_mixer_ch_add_slience_samples(ch, slience_frames * dac_hdl.channel);
|
||
#else
|
||
audio_dac_add_slience_samples(slience_frames * dac_hdl.channel);
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
|
||
void audio_syncts_del(void *priv)
|
||
{
|
||
if (priv) {
|
||
u32 *params = (u32 *)priv;
|
||
#if defined(TCFG_AUDIO_OUTPUT_IIS) && TCFG_AUDIO_OUTPUT_IIS
|
||
audio_iis_remove_syncts_handle((void *)params[1]);
|
||
#else
|
||
audio_dac_remove_syncts_handle(&dac_hdl, (void *)params[1]);
|
||
#endif
|
||
}
|
||
}
|
||
void audio_mix_ch_event_handler(void *priv, int event)
|
||
{
|
||
switch (event) {
|
||
case MIXER_EVENT_CH_OPEN:
|
||
audio_syncts_add(priv);
|
||
break;
|
||
case MIXER_EVENT_CH_CLOSE:
|
||
audio_syncts_del(priv);
|
||
break;
|
||
}
|
||
}
|
||
|
||
#define RB16(b) (u16)(((u8 *)b)[0] << 8 | (((u8 *)b))[1])
|
||
#define RB32(b) (u32)(((u8 *)b)[0] << 24 | (((u8 *)b))[1] << 16 | (((u8 *)b))[2] << 8 | (((u8 *)b))[3])
|
||
|
||
static int get_rtp_header_len(u8 new_frame, u8 *buf, int len)
|
||
{
|
||
int ext, csrc;
|
||
int byte_len;
|
||
int header_len = 0;
|
||
u8 *data = buf;
|
||
|
||
csrc = buf[0] & 0x0f;
|
||
ext = buf[0] & 0x10;
|
||
|
||
byte_len = 12 + 4 * csrc;
|
||
buf += byte_len;
|
||
|
||
if (ext) {
|
||
ext = (RB16(buf + 2) + 1) << 2;
|
||
}
|
||
|
||
if (new_frame) {
|
||
header_len = byte_len + ext + 1;
|
||
} else {
|
||
header_len = byte_len + ext;
|
||
}
|
||
if (a2dp_dec->coding_type == AUDIO_CODING_SBC) {
|
||
while (data[header_len] != 0x9c) {
|
||
if (++header_len > len) {
|
||
return len;
|
||
}
|
||
}
|
||
}
|
||
|
||
return header_len;
|
||
}
|
||
|
||
__attribute__((weak))
|
||
int audio_dac_get_channel(struct audio_dac_hdl *p)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
void __a2dp_drop_frame(void *p)
|
||
{
|
||
int len;
|
||
u8 *frame;
|
||
|
||
#if 0
|
||
int num = a2dp_media_get_packet_num();
|
||
if (num > 1) {
|
||
for (int i = 0; i < num; i++) {
|
||
len = a2dp_media_get_packet(&frame);
|
||
if (len <= 0) {
|
||
break;
|
||
}
|
||
//printf("a2dp_drop_frame: %d\n", len);
|
||
a2dp_media_free_packet(frame);
|
||
}
|
||
}
|
||
#else
|
||
while (1) {
|
||
len = a2dp_media_try_get_packet(&frame);
|
||
if (len <= 0) {
|
||
break;
|
||
}
|
||
a2dp_media_free_packet(frame);
|
||
}
|
||
|
||
#endif
|
||
|
||
}
|
||
|
||
static void __a2dp_clean_frame_by_number(struct a2dp_dec_hdl *dec, u16 num)
|
||
{
|
||
u16 end_seqn = dec->seqn + num;
|
||
if (end_seqn == 0) {
|
||
end_seqn++;
|
||
}
|
||
/*__a2dp_drop_frame(NULL);*/
|
||
/*dec->drop_seqn = end_seqn;*/
|
||
a2dp_media_clear_packet_before_seqn(end_seqn);
|
||
}
|
||
|
||
static void a2dp_tws_clean_frame(void *arg)
|
||
{
|
||
u8 master = 0;
|
||
#if TCFG_USER_TWS_ENABLE
|
||
if (tws_api_get_role() == TWS_ROLE_MASTER) {
|
||
master = 1;
|
||
}
|
||
#else
|
||
master = 1;
|
||
#endif
|
||
if (!master) {
|
||
return;
|
||
}
|
||
|
||
int msecs = a2dp_media_get_remain_play_time(0);
|
||
if (msecs <= 0) {
|
||
return;
|
||
}
|
||
|
||
if (a2dp_dec && a2dp_dec->fetch_lock) {
|
||
return;
|
||
}
|
||
int len = 0;
|
||
u16 seqn = 0;
|
||
u8 *packet = a2dp_media_fetch_packet(&len, NULL);
|
||
if (!packet) {
|
||
return;
|
||
}
|
||
seqn = RB16(packet + 2) + 10;
|
||
if (seqn == 0) {
|
||
seqn = 1;
|
||
}
|
||
a2dp_media_clear_packet_before_seqn(seqn);
|
||
}
|
||
|
||
static u8 a2dp_suspend = 0;
|
||
static u32 a2dp_resume_time = 0;
|
||
|
||
|
||
|
||
int a2dp_decoder_pause(void)
|
||
{
|
||
if (a2dp_dec) {
|
||
return audio_decoder_pause(&(a2dp_dec->decoder));
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int a2dp_decoder_start(void)
|
||
{
|
||
if (a2dp_dec) {
|
||
return audio_decoder_start(&(a2dp_dec->decoder));
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
u8 get_a2dp_drop_frame_flag()
|
||
{
|
||
if (a2dp_dec) {
|
||
return a2dp_dec->timer;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void a2dp_drop_frame_start()
|
||
{
|
||
if (a2dp_dec && (a2dp_dec->timer == 0)) {
|
||
a2dp_dec->timer = sys_timer_add(NULL, __a2dp_drop_frame, 50);
|
||
a2dp_tws_audio_conn_offline();
|
||
}
|
||
}
|
||
|
||
void a2dp_drop_frame_stop()
|
||
{
|
||
if (a2dp_dec && a2dp_dec->timer) {
|
||
sys_timer_del(a2dp_dec->timer);
|
||
a2dp_tws_audio_conn_delete();
|
||
a2dp_dec->timer = 0;
|
||
}
|
||
}
|
||
|
||
static void a2dp_dec_set_output_channel(struct a2dp_dec_hdl *dec)
|
||
{
|
||
int state = 0;
|
||
enum audio_channel channel;
|
||
u8 dac_connect_mode = 0;
|
||
|
||
#if TCFG_USER_TWS_ENABLE
|
||
state = tws_api_get_tws_state();
|
||
if (state & TWS_STA_SIBLING_CONNECTED) {
|
||
channel = tws_api_get_local_channel() == 'L' ? AUDIO_CH_L : AUDIO_CH_R;
|
||
dec->ch = 1;
|
||
} else {
|
||
dac_connect_mode = audio_dac_get_channel(&dac_hdl);
|
||
if (dac_connect_mode == DAC_OUTPUT_LR) {
|
||
channel = AUDIO_CH_LR;
|
||
dec->ch = 2;
|
||
} else {
|
||
channel = AUDIO_CH_DIFF;
|
||
dec->ch = 1;
|
||
}
|
||
}
|
||
|
||
#else
|
||
dac_connect_mode = audio_dac_get_channel(&dac_hdl);
|
||
if (dac_connect_mode == DAC_OUTPUT_LR) {
|
||
channel = AUDIO_CH_LR;
|
||
dec->ch = 2;
|
||
} else {
|
||
channel = AUDIO_CH_DIFF;
|
||
dec->ch = 1;
|
||
}
|
||
#endif
|
||
|
||
#if TCFG_APP_FM_EMITTER_EN
|
||
channel = AUDIO_CH_LR;
|
||
#endif
|
||
if (channel != dec->channel) {
|
||
printf("set_channel: %d\n", channel);
|
||
audio_decoder_set_output_channel(&dec->decoder, channel);
|
||
dec->channel = channel;
|
||
#if TCFG_EQ_ENABLE&&TCFG_BT_MUSIC_EQ_ENABLE
|
||
if (dec->eq_drc && dec->eq_drc->eq) {
|
||
audio_eq_set_channel(dec->eq_drc->eq, dec->ch);
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/*
|
||
*
|
||
*/
|
||
static int a2dp_decoder_set_timestamp(struct a2dp_dec_hdl *dec, u16 seqn)
|
||
{
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
u32 timestamp;
|
||
|
||
timestamp = a2dp_audio_update_timestamp(dec->ts_handle, seqn, audio_syncts_get_dts(dec->syncts));
|
||
if (!dec->ts_start) {
|
||
dec->ts_start = 1;
|
||
dec->mix_ch_event_params[2] = timestamp;
|
||
} else {
|
||
audio_syncts_next_pts(dec->syncts, timestamp);
|
||
audio_syncts_update_sample_rate(dec->syncts, a2dp_audio_sample_rate(dec->ts_handle));;
|
||
}
|
||
dec->timestamp = timestamp;
|
||
/*printf("timestamp : %d, %d\n", seqn, timestamp / TIME_US_FACTOR / 625);*/
|
||
#endif
|
||
return 0;
|
||
}
|
||
|
||
static bool a2dp_audio_is_underrun(struct a2dp_dec_hdl *dec)
|
||
{
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (dec->ts_start != 2) {
|
||
return false;
|
||
}
|
||
#endif
|
||
int underrun_time = a2dp_low_latency ? 1 : 20;
|
||
#if defined(TCFG_AUDIO_OUTPUT_IIS) && TCFG_AUDIO_OUTPUT_IIS
|
||
if (audio_iis_data_time() < underrun_time) {
|
||
return true;
|
||
}
|
||
#else
|
||
if (audio_dac_data_time(&dac_hdl) < underrun_time) {
|
||
return true;
|
||
}
|
||
#endif
|
||
return false;
|
||
}
|
||
|
||
static bool a2dp_bt_rx_overrun(void)
|
||
{
|
||
return a2dp_media_get_remain_buffer_size() < 768 ? true : false;
|
||
}
|
||
|
||
static void a2dp_decoder_stream_free(struct a2dp_dec_hdl *dec, void *packet)
|
||
{
|
||
if (packet) {
|
||
a2dp_media_free_packet(packet);
|
||
}
|
||
|
||
if ((void *)packet == (void *)dec->repair_pkt) {
|
||
dec->repair_pkt = NULL;
|
||
}
|
||
|
||
if (dec->repair_pkt) {
|
||
a2dp_media_free_packet(dec->repair_pkt);
|
||
dec->repair_pkt = NULL;
|
||
}
|
||
dec->repair_pkt_len = 0;
|
||
}
|
||
|
||
static void a2dp_stream_underrun_feedback(void *priv);
|
||
static int a2dp_audio_delay_time(struct a2dp_dec_hdl *dec);
|
||
#define a2dp_seqn_before(a, b) ((a < b && (u16)(b - a) < 1000) || (a > b && (u16)(a - b) > 1000))
|
||
static int a2dp_buffered_stream_sample_rate(struct a2dp_dec_hdl *dec, u8 *from_packet, u16 *end_seqn)
|
||
{
|
||
u8 *packet = from_packet;
|
||
int len = 0;
|
||
int sample_rate = 0;
|
||
|
||
if (!dec->sample_detect) {
|
||
return dec->sample_rate;
|
||
}
|
||
|
||
a2dp_frame_sample_detect_start(dec->sample_detect, a2dp_media_dump_rx_time(packet));
|
||
while (1) {
|
||
packet = a2dp_media_fetch_packet(&len, packet);
|
||
if (!packet) {
|
||
break;
|
||
}
|
||
sample_rate = a2dp_frame_sample_detect(dec->sample_detect, packet, len, a2dp_media_dump_rx_time(packet));
|
||
*end_seqn = RB16(packet + 2);
|
||
}
|
||
|
||
/*printf("A2DP sample detect : %d - %d\n", sample_rate, dec->sample_rate);*/
|
||
return sample_rate;
|
||
}
|
||
|
||
static int a2dp_stream_overrun_handler(struct a2dp_dec_hdl *dec, u8 **frame, int *len)
|
||
{
|
||
u8 *packet = NULL;
|
||
int rlen = 0;
|
||
|
||
int msecs = 0;
|
||
int overrun = 0;
|
||
int sample_rate = 0;
|
||
u16 from_seqn = RB16(dec->repair_pkt + 2);
|
||
u16 seqn = from_seqn;
|
||
u16 end_seqn = 0;
|
||
overrun = 1;
|
||
while (1) {
|
||
msecs = a2dp_audio_delay_time(dec);
|
||
if (msecs < (CONFIG_A2DP_DELAY_TIME + 50) && !a2dp_bt_rx_overrun()) {
|
||
break;
|
||
}
|
||
overrun = 0;
|
||
rlen = a2dp_media_try_get_packet(&packet);
|
||
if (rlen <= 0) {
|
||
break;
|
||
}
|
||
|
||
sample_rate = a2dp_buffered_stream_sample_rate(dec, dec->repair_pkt, &end_seqn);
|
||
a2dp_decoder_stream_free(dec, NULL);
|
||
dec->repair_pkt = packet;
|
||
dec->repair_pkt_len = rlen;
|
||
seqn = RB16(packet + 2);
|
||
if (!a2dp_seqn_before(seqn, dec->overrun_seqn)) {
|
||
*frame = packet;
|
||
*len = rlen;
|
||
/*printf("------> end frame : %d\n", dec->overrun_seqn);*/
|
||
return 1;
|
||
}
|
||
|
||
if (/*sample_rate < (dec->sample_rate * 4 / 5) || */sample_rate > (dec->sample_rate * 4 / 3)) {
|
||
if (a2dp_seqn_before(dec->overrun_seqn, end_seqn)) {
|
||
dec->overrun_seqn = end_seqn;
|
||
}
|
||
continue;
|
||
}
|
||
}
|
||
if (overrun) {
|
||
/*putchar('+');*/
|
||
/* dec->overrun_seqn++; */
|
||
} else {
|
||
/*putchar('-');*/
|
||
}
|
||
|
||
*frame = dec->repair_pkt;
|
||
*len = dec->repair_pkt_len;
|
||
return 0;
|
||
}
|
||
|
||
static int a2dp_stream_missed_handler(struct a2dp_dec_hdl *dec, u8 **frame, int *len)
|
||
{
|
||
int msecs = a2dp_audio_delay_time(dec);
|
||
*frame = dec->repair_pkt;
|
||
*len = dec->repair_pkt_len;
|
||
if ((msecs >= (dec->delay_time + 50) || a2dp_bt_rx_overrun()) || --dec->missed_num == 0) {
|
||
/*putchar('M');*/
|
||
return 1;
|
||
}
|
||
/*putchar('m');*/
|
||
return 0;
|
||
}
|
||
|
||
static int a2dp_stream_underrun_handler(struct a2dp_dec_hdl *dec, u8 **packet)
|
||
{
|
||
|
||
if (!a2dp_audio_is_underrun(dec)) {
|
||
putchar('x');
|
||
return 0;
|
||
}
|
||
putchar('X');
|
||
if (dec->stream_error != A2DP_STREAM_UNDERRUN) {
|
||
if (!dec->stream_error) {
|
||
a2dp_stream_underrun_feedback(dec);
|
||
}
|
||
dec->stream_error = a2dp_low_latency ? A2DP_STREAM_LOW_UNDERRUN : A2DP_STREAM_UNDERRUN;
|
||
dec->repair = a2dp_low_latency ? 0 : 1;
|
||
}
|
||
*packet = dec->repair_pkt;
|
||
dec->repair_frames++;
|
||
return dec->repair_pkt_len;
|
||
}
|
||
|
||
static int a2dp_stream_error_filter(struct a2dp_dec_hdl *dec, u8 new_packet, u8 *packet, int len)
|
||
{
|
||
int err = 0;
|
||
|
||
if (dec->coding_type == AUDIO_CODING_AAC) {
|
||
dec->header_len = get_rtp_header_len(dec->new_frame, packet, len);
|
||
dec->new_frame = 0;
|
||
} else {
|
||
dec->header_len = get_rtp_header_len(1, packet, len);
|
||
}
|
||
|
||
if (dec->header_len >= len) {
|
||
printf("##A2DP header error : %d\n", dec->header_len);
|
||
a2dp_decoder_stream_free(dec, packet);
|
||
return -EFAULT;
|
||
}
|
||
|
||
u16 seqn = RB16(packet + 2);
|
||
if (new_packet) {
|
||
if (dec->stream_error == A2DP_STREAM_UNDERRUN) {
|
||
int missed_frames = (u16)(seqn - dec->seqn) - 1;
|
||
if (missed_frames > dec->repair_frames) {
|
||
dec->stream_error = A2DP_STREAM_MISSED;
|
||
dec->missed_num = missed_frames - dec->repair_frames + 1;
|
||
/*printf("case 0 : %d, %d\n", missed_frames, dec->repair_frames);*/
|
||
err = -EAGAIN;
|
||
} else if (missed_frames < dec->repair_frames) {
|
||
dec->stream_error = A2DP_STREAM_OVERRUN;
|
||
dec->overrun_seqn = seqn + dec->repair_frames - missed_frames;
|
||
/*printf("case 1 : %d, %d, seqn : %d, %d\n", missed_frames, dec->repair_frames, seqn, dec->overrun_seqn); */
|
||
err = -EAGAIN;
|
||
}
|
||
} else if (!dec->stream_error && (u16)(seqn - dec->seqn) > 1) {
|
||
dec->stream_error = A2DP_STREAM_MISSED;
|
||
if (a2dp_audio_delay_time(dec) < dec->delay_time) {
|
||
dec->missed_num = (u16)(seqn - dec->seqn);
|
||
err = -EAGAIN;
|
||
}
|
||
int pkt_len;
|
||
void *head = a2dp_media_fetch_packet(&pkt_len, NULL);
|
||
/*printf("case 2 : %d, %d, pkt : 0x%x, 0x%x\n", seqn, dec->seqn, (u32)head, (u32)packet);*/
|
||
if (dec->missed_num > 30) {
|
||
printf("##A serious mistake : A2DP stream missed too much, %d\n", dec->missed_num);
|
||
dec->missed_num = 30;
|
||
}
|
||
}
|
||
dec->repair_frames = 0;
|
||
}
|
||
if (!err && new_packet) {
|
||
dec->seqn = seqn;
|
||
}
|
||
dec->repair_pkt = packet;
|
||
dec->repair_pkt_len = len;
|
||
return err;
|
||
}
|
||
|
||
static int a2dp_dec_get_frame(struct audio_decoder *decoder, u8 **frame)
|
||
{
|
||
struct a2dp_dec_hdl *dec = container_of(decoder, struct a2dp_dec_hdl, decoder);
|
||
u8 *packet = NULL;
|
||
int len = 0;
|
||
u8 new_packet = 0;
|
||
|
||
try_again:
|
||
switch (dec->stream_error) {
|
||
case A2DP_STREAM_OVERRUN:
|
||
new_packet = a2dp_stream_overrun_handler(dec, &packet, &len);
|
||
break;
|
||
case A2DP_STREAM_MISSED:
|
||
new_packet = a2dp_stream_missed_handler(dec, &packet, &len);
|
||
break;
|
||
default:
|
||
len = a2dp_media_try_get_packet(&packet);
|
||
if (len <= 0) {
|
||
len = a2dp_stream_underrun_handler(dec, &packet);
|
||
} else {
|
||
a2dp_decoder_stream_free(dec, NULL);
|
||
new_packet = 1;
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (len <= 0) {
|
||
return 0;
|
||
}
|
||
|
||
int err = a2dp_stream_error_filter(dec, new_packet, packet, len);
|
||
if (err) {
|
||
if (-err == EAGAIN) {
|
||
dec->new_frame = 1;
|
||
goto try_again;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
*frame = packet + dec->header_len;
|
||
len -= dec->header_len;
|
||
if (dec->stream_error && new_packet) {
|
||
#if AUDIO_CODEC_SUPPORT_SYNC && TCFG_USER_TWS_ENABLE
|
||
if (dec->ts_handle) {
|
||
tws_a2dp_share_timestamp(dec->ts_handle);
|
||
}
|
||
#endif
|
||
dec->stream_error = 0;
|
||
}
|
||
|
||
if (dec->slience_frames) {
|
||
dec->slience_frames--;
|
||
}
|
||
a2dp_decoder_set_timestamp(dec, dec->seqn);
|
||
|
||
return len;
|
||
}
|
||
|
||
static void a2dp_dec_put_frame(struct audio_decoder *decoder, u8 *frame)
|
||
{
|
||
struct a2dp_dec_hdl *dec = container_of(decoder, struct a2dp_dec_hdl, decoder);
|
||
|
||
if (frame) {
|
||
if (!a2dp_media_channel_exist() || app_var.goto_poweroff_flag) {
|
||
a2dp_decoder_stream_free(dec, (void *)(frame - dec->header_len));
|
||
}
|
||
/*a2dp_media_free_packet((void *)(frame - dec->header_len));*/
|
||
}
|
||
}
|
||
|
||
static int a2dp_dec_fetch_frame(struct audio_decoder *decoder, u8 **frame)
|
||
{
|
||
struct a2dp_dec_hdl *dec = container_of(decoder, struct a2dp_dec_hdl, decoder);
|
||
u8 *packet = NULL;
|
||
int len = 0;
|
||
u32 wait_timeout = 0;
|
||
|
||
if (!dec->start) {
|
||
wait_timeout = jiffies + msecs_to_jiffies(500);
|
||
}
|
||
|
||
dec->fetch_lock = 1;
|
||
__retry_fetch:
|
||
packet = a2dp_media_fetch_packet(&len, NULL);
|
||
if (packet) {
|
||
dec->header_len = get_rtp_header_len(1, packet, len);
|
||
*frame = packet + dec->header_len;
|
||
len -= dec->header_len;
|
||
} else if (!dec->start) {
|
||
if (time_before(jiffies, wait_timeout)) {
|
||
os_time_dly(1);
|
||
goto __retry_fetch;
|
||
}
|
||
}
|
||
|
||
dec->fetch_lock = 0;
|
||
return len;
|
||
}
|
||
|
||
static const struct audio_dec_input a2dp_input = {
|
||
.coding_type = AUDIO_CODING_SBC,
|
||
.data_type = AUDIO_INPUT_FRAME,
|
||
.ops = {
|
||
.frame = {
|
||
.fget = a2dp_dec_get_frame,
|
||
.fput = a2dp_dec_put_frame,
|
||
.ffetch = a2dp_dec_fetch_frame,
|
||
}
|
||
}
|
||
};
|
||
|
||
|
||
#define bt_time_before(t1, t2) \
|
||
(((t1 < t2) && ((t2 - t1) & 0x7ffffff) < 0xffff) || \
|
||
((t1 > t2) && ((t1 - t2) & 0x7ffffff) > 0xffff))
|
||
#define bt_time_to_msecs(clk) (((clk) * 625) / 1000)
|
||
#define msecs_to_bt_time(m) (((m + 1)* 1000) / 625)
|
||
|
||
static int a2dp_audio_delay_time(struct a2dp_dec_hdl *dec)
|
||
{
|
||
/*struct a2dp_dec_hdl *dec = container_of(decoder, struct a2dp_dec_hdl, decoder);*/
|
||
int msecs = 0;
|
||
|
||
#if TCFG_USER_TWS_ENABLE
|
||
msecs = a2dp_media_get_remain_play_time(1);
|
||
#else
|
||
msecs = a2dp_media_get_remain_play_time(0);
|
||
#endif
|
||
|
||
if (dec->syncts) {
|
||
msecs += sound_buffered_between_syncts_and_device(dec->syncts, 0);
|
||
}
|
||
|
||
#if defined(TCFG_AUDIO_OUTPUT_IIS) && TCFG_AUDIO_OUTPUT_IIS
|
||
msecs += audio_iis_data_time();
|
||
#else
|
||
msecs += audio_dac_data_time(&dac_hdl);
|
||
#endif
|
||
|
||
return msecs;
|
||
}
|
||
|
||
static int a2dp_decoder_stream_delay_update(struct audio_decoder *decoder)
|
||
{
|
||
struct a2dp_dec_hdl *dec = container_of(decoder, struct a2dp_dec_hdl, decoder);
|
||
int msecs = 0;
|
||
int err = 0;
|
||
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
msecs = a2dp_audio_delay_time(dec);
|
||
if (dec->stream_error) {
|
||
return 0;
|
||
}
|
||
|
||
if (dec->sync_step == 2) {
|
||
int distance_time = msecs - a2dp_delay_time;
|
||
if (a2dp_bt_rx_overrun() && distance_time < 50) {
|
||
distance_time = 50;
|
||
}
|
||
|
||
if (dec->ts_handle) {
|
||
a2dp_audio_delay_offset_update(dec->ts_handle, distance_time);
|
||
}
|
||
}
|
||
|
||
#endif
|
||
/*printf("%d -> %dms, delay_time : %dms\n", msecs1, msecs, a2dp_delay_time);*/
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* A2DP 音频同步控制处理函数
|
||
* 1.包括音频延时浮动参数;
|
||
* 2.处理因为超时等情况丢弃音频样点;
|
||
* 3.调用与蓝牙主机音频延时做同步的功能;
|
||
* 4.处理TWS从机加入与解码被打断的情况。
|
||
*
|
||
*/
|
||
static int a2dp_decoder_audio_sync_handler(struct audio_decoder *decoder)
|
||
{
|
||
struct a2dp_dec_hdl *dec = container_of(decoder, struct a2dp_dec_hdl, decoder);
|
||
int err;
|
||
|
||
if (!dec->syncts) {
|
||
return 0;
|
||
}
|
||
|
||
|
||
err = a2dp_decoder_stream_delay_update(decoder);
|
||
if (err) {
|
||
audio_decoder_suspend(decoder, 0);
|
||
return -EAGAIN;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static u16 a2dp_max_interval = 0;
|
||
#define A2DP_EST_AUDIO_CAPACITY 350//ms
|
||
|
||
static void a2dp_stream_underrun_feedback(void *priv)
|
||
{
|
||
struct a2dp_dec_hdl *dec = (struct a2dp_dec_hdl *)priv;
|
||
|
||
dec->underrun_feedback = 1;
|
||
|
||
if (a2dp_delay_time < a2dp_max_interval + 50) {
|
||
a2dp_delay_time = a2dp_max_interval + 50;
|
||
} else {
|
||
a2dp_delay_time += 50;
|
||
}
|
||
if (a2dp_delay_time > A2DP_EST_AUDIO_CAPACITY) {
|
||
a2dp_delay_time = A2DP_EST_AUDIO_CAPACITY;
|
||
}
|
||
}
|
||
|
||
/*void a2dp_stream_interval_time_handler(int time)*/
|
||
void reset_a2dp_sbc_instant_time(u16 time)
|
||
{
|
||
if (a2dp_max_interval < time) {
|
||
a2dp_max_interval = time;
|
||
if (a2dp_max_interval > 350) {
|
||
a2dp_max_interval = 350;
|
||
}
|
||
#if A2DP_FLUENT_STREAM_MODE
|
||
if (a2dp_max_interval > a2dp_delay_time) {
|
||
a2dp_delay_time = a2dp_max_interval + 5;
|
||
if (a2dp_delay_time > A2DP_EST_AUDIO_CAPACITY) {
|
||
a2dp_delay_time = A2DP_EST_AUDIO_CAPACITY;
|
||
}
|
||
}
|
||
#endif
|
||
/*printf("Max : %dms\n", time);*/
|
||
}
|
||
}
|
||
|
||
static void a2dp_stream_stability_detect(void *priv)
|
||
{
|
||
struct a2dp_dec_hdl *dec = (struct a2dp_dec_hdl *)priv;
|
||
|
||
if (dec->underrun_feedback) {
|
||
dec->underrun_feedback = 0;
|
||
return;
|
||
}
|
||
|
||
if (a2dp_delay_time > dec->delay_time) {
|
||
if (a2dp_max_interval < a2dp_delay_time) {
|
||
a2dp_delay_time -= 50;
|
||
if (a2dp_delay_time < dec->delay_time) {
|
||
a2dp_delay_time = dec->delay_time;
|
||
}
|
||
|
||
if (a2dp_delay_time < a2dp_max_interval) {
|
||
a2dp_delay_time = a2dp_max_interval + 5;
|
||
}
|
||
}
|
||
a2dp_max_interval = dec->delay_time;
|
||
}
|
||
}
|
||
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
static void a2dp_decoder_update_base_time(struct a2dp_dec_hdl *dec)
|
||
{
|
||
int distance_time = a2dp_low_latency ? a2dp_delay_time : (a2dp_delay_time - a2dp_media_get_remain_play_time(1));
|
||
if (!a2dp_low_latency) {
|
||
distance_time = a2dp_delay_time;
|
||
} else if (distance_time < 20) {
|
||
distance_time = 20;
|
||
}
|
||
dec->base_time = bt_audio_sync_lat_time() + msecs_to_bt_time(distance_time);
|
||
}
|
||
|
||
#endif
|
||
static int a2dp_decoder_stream_is_available(struct a2dp_dec_hdl *dec)
|
||
{
|
||
int err = 0;
|
||
u8 *packet = NULL;
|
||
int len = 0;
|
||
int drop = 0;
|
||
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (dec->sync_step) {
|
||
return 0;
|
||
}
|
||
|
||
packet = (u8 *)a2dp_media_fetch_packet(&len, NULL);
|
||
if (!packet) {
|
||
return -EINVAL;
|
||
}
|
||
|
||
dec->seqn = RB16(packet + 2);
|
||
if (dec->ts_handle) {
|
||
#if TCFG_USER_TWS_ENABLE
|
||
if (!tws_network_audio_was_started() && !a2dp_audio_timestamp_is_available(dec->ts_handle, dec->seqn, 0, &drop)) {
|
||
if (drop) {
|
||
local_irq_disable();
|
||
u8 *check_packet = (u8 *)a2dp_media_fetch_packet(&len, NULL);
|
||
if (check_packet && RB16(check_packet + 2) == dec->seqn) {
|
||
a2dp_media_free_packet(packet);
|
||
}
|
||
local_irq_enable();
|
||
a2dp_decoder_update_base_time(dec);
|
||
a2dp_audio_set_base_time(dec->ts_handle, dec->base_time);
|
||
}
|
||
return -EINVAL;
|
||
}
|
||
#endif
|
||
}
|
||
dec->sync_step = 2;
|
||
#endif
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int a2dp_dec_probe_handler(struct audio_decoder *decoder)
|
||
{
|
||
struct a2dp_dec_hdl *dec = container_of(decoder, struct a2dp_dec_hdl, decoder);
|
||
int err = 0;
|
||
|
||
err = a2dp_decoder_stream_is_available(dec);
|
||
if (err) {
|
||
audio_decoder_suspend(decoder, 0);
|
||
return err;
|
||
}
|
||
|
||
err = a2dp_decoder_audio_sync_handler(decoder);
|
||
if (err) {
|
||
audio_decoder_suspend(decoder, 0);
|
||
return err;
|
||
}
|
||
|
||
dec->new_frame = 1;
|
||
|
||
a2dp_dec_set_output_channel(dec);
|
||
|
||
return err;
|
||
}
|
||
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
void a2dp_digital_vol_set(u8 vol)
|
||
{
|
||
if (a2dp_dec) {
|
||
audio_digital_vol_set(a2dp_dec->dvol, vol);
|
||
}
|
||
}
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
|
||
|
||
static int a2dp_output_after_syncts_filter(void *priv, void *data, int len)
|
||
{
|
||
int wlen = 0;
|
||
int drop_len = 0;
|
||
struct a2dp_dec_hdl *dec = (struct a2dp_dec_hdl *)priv;
|
||
|
||
#if TCFG_EQ_ENABLE&&TCFG_BT_MUSIC_EQ_ENABLE
|
||
if (dec->eq_drc && dec->eq_drc->async) {
|
||
return eq_drc_run(dec->eq_drc, data, len);//异步eq
|
||
}
|
||
#endif
|
||
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
return audio_mixer_ch_write(&dec->mix_ch, data, len);
|
||
#else
|
||
return audio_output_data_a2dp(dec, data, len);
|
||
#endif
|
||
}
|
||
|
||
static void a2dp_output_before_syncts_handler(struct a2dp_dec_hdl *dec, void *data, int len)
|
||
{
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (dec->ts_start == 1) {
|
||
u32 timestamp = dec->timestamp;
|
||
u32 current_time = ((bt_audio_sync_lat_time() + (30000 / 625)) & 0x7ffffff) * 625 * TIME_US_FACTOR;
|
||
#define time_after_timestamp(t1, t2) \
|
||
((t1 > t2 && ((t1 - t2) < (10000000L * TIME_US_FACTOR))) || (t1 < t2 && ((t2 - t1) > (10000000L * TIME_US_FACTOR))))
|
||
if (!time_after_timestamp(timestamp, current_time)) {//时间戳与当前解码时间相差太近,直接丢掉
|
||
audio_syncts_resample_suspend(dec->syncts);
|
||
} else {
|
||
audio_syncts_resample_resume(dec->syncts);
|
||
dec->mix_ch_event_params[2] = timestamp;
|
||
dec->ts_start = 2;
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
static void audio_filter_resume_decoder(void *priv)
|
||
{
|
||
struct audio_decoder *decoder = (struct audio_decoder *)priv;
|
||
|
||
audio_decoder_resume(decoder);
|
||
}
|
||
|
||
static int a2dp_decoder_slience_plc_filter(struct a2dp_dec_hdl *dec, void *data, int len)
|
||
{
|
||
if (len == 0) {
|
||
a2dp_decoder_stream_free(dec, NULL);
|
||
if (!dec->stream_error) {
|
||
dec->stream_error = A2DP_STREAM_DECODE_ERR;
|
||
dec->repair = 1;
|
||
}
|
||
return 0;
|
||
}
|
||
if (dec->stream_error) {
|
||
memset(data, 0x0, len);
|
||
}
|
||
#if TCFG_USER_TWS_ENABLE
|
||
u8 tws_pairing = 0;
|
||
if (dec->preempt || tws_network_audio_was_started()) {
|
||
/*a2dp播放中副机加入,声音淡入进去*/
|
||
tws_network_local_audio_start();
|
||
dec->preempt = 0;
|
||
tws_pairing = 1;
|
||
memset(data, 0x0, len);
|
||
dec->slience_frames = 30;
|
||
}
|
||
#endif
|
||
#if A2DP_AUDIO_PLC_ENABLE
|
||
if (dec->plc_ops) {
|
||
if (dec->slience_frames) {
|
||
dec->plc_ops->run(dec->plc_mem, data, data, len >> 1, 2);
|
||
} else if (dec->stream_error) {
|
||
dec->plc_ops->run(dec->plc_mem, data, data, len >> 1, dec->repair ? 1 : 2);
|
||
dec->repair = 0;
|
||
} else {
|
||
dec->plc_ops->run(dec->plc_mem, data, data, len >> 1, 0);
|
||
}
|
||
}
|
||
#else
|
||
if (dec->slience_frames) {
|
||
memset(data, 0x0, len);
|
||
}
|
||
#endif
|
||
return len;
|
||
}
|
||
|
||
static int a2dp_dec_output_handler(struct audio_decoder *decoder, s16 *data, int len, void *priv)
|
||
{
|
||
int wlen = 0;
|
||
struct a2dp_dec_hdl *dec = container_of(decoder, struct a2dp_dec_hdl, decoder);
|
||
if (!dec->remain) {
|
||
wlen = a2dp_decoder_slience_plc_filter(dec, data, len);
|
||
if (wlen < len) {
|
||
return wlen;
|
||
}
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
audio_digital_vol_run(dec->dvol, data, len);
|
||
#endif
|
||
|
||
#if TCFG_DIG_PHASE_INVERTER_EN
|
||
digital_phase_inverter_s16(data, len);
|
||
#endif/*TCFG_DIG_PHASE_INVERTER_EN*/
|
||
|
||
#if TCFG_EQ_ENABLE&&TCFG_BT_MUSIC_EQ_ENABLE
|
||
if (dec->eq_drc && !dec->eq_drc->async) {
|
||
eq_drc_run(dec->eq_drc, data, len);//同步eq
|
||
}
|
||
#endif
|
||
|
||
a2dp_output_before_syncts_handler(dec, data, len);
|
||
}
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (dec->syncts) {
|
||
int wlen = audio_syncts_frame_filter(dec->syncts, data, len);
|
||
if (wlen < len) {
|
||
audio_syncts_trigger_resume(dec->syncts, (void *)decoder, audio_filter_resume_decoder);
|
||
}
|
||
dec->remain = wlen < len ? 1 : 0;
|
||
return wlen;
|
||
}
|
||
#endif
|
||
wlen = a2dp_output_after_syncts_filter(dec, data, len);
|
||
dec->remain = wlen < len ? 1 : 0;
|
||
return wlen;
|
||
}
|
||
|
||
|
||
static int a2dp_dec_post_handler(struct audio_decoder *decoder)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static const struct audio_dec_handler a2dp_dec_handler = {
|
||
.dec_probe = a2dp_dec_probe_handler,
|
||
.dec_output = a2dp_dec_output_handler,
|
||
.dec_post = a2dp_dec_post_handler,
|
||
};
|
||
|
||
void a2dp_dec_close();
|
||
|
||
static void a2dp_dec_release()
|
||
{
|
||
audio_decoder_task_del_wait(&decode_task, &a2dp_dec->wait);
|
||
a2dp_drop_frame_stop();
|
||
|
||
local_irq_disable();
|
||
free(a2dp_dec);
|
||
a2dp_dec = NULL;
|
||
local_irq_enable();
|
||
}
|
||
|
||
static void a2dp_dec_event_handler(struct audio_decoder *decoder, int argc, int *argv)
|
||
{
|
||
switch (argv[0]) {
|
||
case AUDIO_DEC_EVENT_END:
|
||
puts("AUDIO_DEC_EVENT_END\n");
|
||
a2dp_dec_close();
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void a2dp_decoder_delay_time_setup(struct a2dp_dec_hdl *dec)
|
||
{
|
||
#if TCFG_USER_TWS_ENABLE
|
||
int a2dp_low_latency = tws_api_get_low_latency_state();
|
||
#endif
|
||
if (a2dp_low_latency) {
|
||
a2dp_delay_time = a2dp_dec->coding_type == AUDIO_CODING_AAC ? CONFIG_A2DP_DELAY_TIME_LO : CONFIG_A2DP_SBC_DELAY_TIME_LO;
|
||
} else {
|
||
a2dp_delay_time = CONFIG_A2DP_DELAY_TIME;
|
||
}
|
||
a2dp_max_interval = 0;
|
||
|
||
dec->delay_time = a2dp_delay_time;
|
||
|
||
dec->detect_timer = sys_timer_add((void *)dec, a2dp_stream_stability_detect, A2DP_FLUENT_DETECT_INTERVAL);
|
||
}
|
||
|
||
|
||
|
||
static int a2dp_decoder_syncts_setup(struct a2dp_dec_hdl *dec)
|
||
{
|
||
int err = 0;
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
struct audio_syncts_params params = {0};
|
||
|
||
params.nch = dec->ch;
|
||
#if defined(TCFG_AUDIO_OUTPUT_IIS) && TCFG_AUDIO_OUTPUT_IIS
|
||
params.pcm_device = PCM_OUTSIDE_DAC;
|
||
params.rout_sample_rate = TCFG_IIS_SAMPLE_RATE;
|
||
#else
|
||
params.pcm_device = PCM_INSIDE_DAC;
|
||
params.rout_sample_rate = dec->sample_rate;
|
||
#endif
|
||
params.network = AUDIO_NETWORK_BT2_1;
|
||
params.rin_sample_rate = dec->sample_rate;
|
||
params.priv = dec;
|
||
params.factor = TIME_US_FACTOR;
|
||
params.output = a2dp_output_after_syncts_filter;
|
||
|
||
bt_audio_sync_nettime_select(0);//0 - a2dp主机,1 - tws, 2 - BLE
|
||
|
||
a2dp_decoder_update_base_time(dec);
|
||
dec->ts_handle = a2dp_audio_timestamp_create(dec->sample_rate, dec->base_time, TIME_US_FACTOR);
|
||
|
||
err = audio_syncts_open(&dec->syncts, ¶ms);
|
||
if (!err) {
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
dec->mix_ch_event_params[0] = (u32)&dec->mix_ch;
|
||
dec->mix_ch_event_params[1] = (u32)dec->syncts;
|
||
dec->mix_ch_event_params[2] = dec->base_time * 625 * TIME_US_FACTOR;
|
||
audio_mixer_ch_set_event_handler(&dec->mix_ch, (void *)dec->mix_ch_event_params, audio_mix_ch_event_handler);
|
||
#else
|
||
dec->mix_ch_event_params[1] = (u32)dec->syncts;
|
||
dec->mix_ch_event_params[2] = dec->base_time * 625 * TIME_US_FACTOR;
|
||
#endif
|
||
}
|
||
|
||
dec->sync_step = 0;
|
||
#endif
|
||
return err;
|
||
}
|
||
|
||
static void a2dp_decoder_syncts_free(struct a2dp_dec_hdl *dec)
|
||
{
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (dec->ts_handle) {
|
||
a2dp_audio_timestamp_close(dec->ts_handle);
|
||
dec->ts_handle = NULL;
|
||
}
|
||
if (dec->syncts) {
|
||
audio_syncts_close(dec->syncts);
|
||
dec->syncts = NULL;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
static int a2dp_decoder_plc_setup(struct a2dp_dec_hdl *dec)
|
||
{
|
||
#if A2DP_AUDIO_PLC_ENABLE
|
||
int plc_mem_size;
|
||
dec->plc_ops = get_lfaudioPLC_api();
|
||
plc_mem_size = dec->plc_ops->need_buf(dec->ch); // 3660bytes,请衡量是否使用该空间换取PLC处理
|
||
dec->plc_mem = malloc(plc_mem_size);
|
||
if (!dec->plc_mem) {
|
||
dec->plc_ops = NULL;
|
||
return -ENOMEM;
|
||
}
|
||
dec->plc_ops->open(dec->plc_mem, dec->ch, a2dp_low_latency ? 0 : 4);
|
||
#endif
|
||
return 0;
|
||
}
|
||
|
||
static void a2dp_decoder_plc_free(struct a2dp_dec_hdl *dec)
|
||
{
|
||
#if A2DP_AUDIO_PLC_ENABLE
|
||
if (dec->plc_mem) {
|
||
free(dec->plc_mem);
|
||
dec->plc_mem = NULL;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void a2dp_decoder_sample_detect_setup(struct a2dp_dec_hdl *dec)
|
||
{
|
||
dec->sample_detect = a2dp_sample_detect_open(dec->sample_rate, dec->coding_type);
|
||
}
|
||
|
||
void a2dp_decoder_sample_detect_free(struct a2dp_dec_hdl *dec)
|
||
{
|
||
if (dec->sample_detect) {
|
||
a2dp_sample_detect_close(dec->sample_detect);
|
||
dec->sample_detect = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
void audio_overlay_load_code(u32 type);
|
||
int a2dp_dec_start()
|
||
{
|
||
int err;
|
||
struct audio_fmt *fmt;
|
||
enum audio_channel channel;
|
||
struct a2dp_dec_hdl *dec = a2dp_dec;
|
||
|
||
if (!a2dp_dec) {
|
||
return -EINVAL;
|
||
}
|
||
|
||
a2dp_drop_frame_stop();
|
||
puts("a2dp_dec_start: in\n");
|
||
#if TCFG_USER_TWS_ENABLE
|
||
int a2dp_low_latency = tws_api_get_low_latency_state();
|
||
#endif
|
||
|
||
#if CONFIG_MINI_BT_AUDIO_ENABLE
|
||
a2dp_delay_time = a2dp_low_latency ? CONFIG_A2DP_SBC_DELAY_TIME_LO : CONFIG_A2DP_DELAY_TIME;
|
||
#else
|
||
if (a2dp_low_latency) {
|
||
a2dp_delay_time = a2dp_dec->coding_type == AUDIO_CODING_AAC ? CONFIG_A2DP_DELAY_TIME_LO : CONFIG_A2DP_SBC_DELAY_TIME_LO;
|
||
} else {
|
||
a2dp_delay_time = CONFIG_A2DP_DELAY_TIME;
|
||
}
|
||
a2dp_max_interval = 0;
|
||
|
||
dec->delay_time = a2dp_delay_time;
|
||
if (a2dp_dec->coding_type == AUDIO_CODING_AAC) {
|
||
audio_overlay_load_code(a2dp_dec->coding_type);
|
||
}
|
||
#endif /*CONFIG_MINI_BT_AUDIO_ENABLE*/
|
||
|
||
err = audio_decoder_open(&dec->decoder, &a2dp_input, &decode_task);
|
||
if (err) {
|
||
goto __err1;
|
||
}
|
||
dec->channel = AUDIO_CH_MAX;
|
||
|
||
audio_decoder_set_handler(&dec->decoder, &a2dp_dec_handler);
|
||
audio_decoder_set_event_handler(&dec->decoder, a2dp_dec_event_handler, 0);
|
||
|
||
#if !CONFIG_MINI_BT_AUDIO_ENABLE
|
||
if (a2dp_dec->coding_type != a2dp_input.coding_type) {
|
||
struct audio_fmt f = {0};
|
||
f.coding_type = a2dp_dec->coding_type;
|
||
err = audio_decoder_set_fmt(&dec->decoder, &f);
|
||
if (err) {
|
||
goto __err2;
|
||
}
|
||
}
|
||
#endif /*CONFIG_MINI_BT_AUDIO_ENABLE*/
|
||
|
||
err = audio_decoder_get_fmt(&dec->decoder, &fmt);
|
||
if (err) {
|
||
goto __err2;
|
||
}
|
||
|
||
if (fmt->sample_rate == 0) {
|
||
log_w("A2DP stream maybe error\n");
|
||
goto __err2;
|
||
}
|
||
//dac_hdl.dec_channel_num = fmt->channel;
|
||
dec->sample_rate = fmt->sample_rate;
|
||
|
||
a2dp_decoder_delay_time_setup(dec);
|
||
set_source_sample_rate(fmt->sample_rate);
|
||
a2dp_dec_set_output_channel(dec);
|
||
|
||
/*audio_dac_set_protect_time(&dac_hdl, 3, dec, NULL);//a2dp_stream_underrun_feedback);*/
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
audio_mixer_ch_open(&dec->mix_ch, &mixer);
|
||
#if defined(TCFG_AUDIO_OUTPUT_IIS) && TCFG_AUDIO_OUTPUT_IIS
|
||
audio_mixer_ch_set_sample_rate(&dec->mix_ch, TCFG_IIS_SAMPLE_RATE);
|
||
#else
|
||
audio_mixer_ch_set_sample_rate(&dec->mix_ch, fmt->sample_rate);
|
||
#endif
|
||
audio_mixer_ch_set_resume_handler(&dec->mix_ch, (void *)&dec->decoder, (void (*)(void *))audio_decoder_resume);
|
||
#endif
|
||
|
||
app_audio_state_switch(APP_AUDIO_STATE_MUSIC, get_max_sys_vol());
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
a2dp_dec->dvol = audio_digital_vol_open(app_audio_get_volume(APP_AUDIO_STATE_MUSIC), SYS_MAX_VOL, 2);
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
a2dp_decoder_sample_detect_setup(dec);
|
||
#if TCFG_EQ_ENABLE&&TCFG_BT_MUSIC_EQ_ENABLE
|
||
u8 drc_en = 0;
|
||
#if TCFG_DRC_ENABLE&&TCFG_BT_MUSIC_DRC_ENABLE
|
||
drc_en = 1;
|
||
#endif//TCFG_BT_MUSIC_DRC_ENABLE
|
||
|
||
#if defined(AUDIO_GAME_EFFECT_CONFIG) && AUDIO_GAME_EFFECT_CONFIG
|
||
if (a2dp_low_latency) {
|
||
drc_en |= BIT(1);
|
||
}
|
||
#endif /*AUDIO_GAME_EFFECT_CONFIG*/
|
||
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
dec->eq_drc = dec_eq_drc_setup(&dec->mix_ch, (eq_output_cb)audio_mixer_ch_write, fmt->sample_rate, dec->ch, a2dp_low_latency ? 0 : 1, drc_en);
|
||
#else
|
||
dec->eq_drc = dec_eq_drc_setup(dec, (eq_output_cb)audio_output_data_a2dp, fmt->sample_rate, dec->ch, a2dp_low_latency ? 0 : 1, drc_en);
|
||
#endif
|
||
#endif//TCFG_BT_MUSIC_EQ_ENABLE
|
||
|
||
a2dp_decoder_syncts_setup(dec);
|
||
a2dp_decoder_plc_setup(dec);
|
||
|
||
dec->remain = 0;
|
||
#if 0
|
||
if (dec->coding_type == AUDIO_CODING_SBC) {
|
||
#if (TCFG_BT_MUSIC_EQ_ENABLE && ((EQ_SECTION_MAX >= 10) && AUDIO_OUT_EFFECT_ENABLE))
|
||
clk_set_sys_lock(a2dp_low_latency ? SYS_64M : SYS_48M, 0) ;
|
||
#elif (TCFG_BT_MUSIC_EQ_ENABLE && ((EQ_SECTION_MAX >= 10) || AUDIO_OUT_EFFECT_ENABLE))
|
||
#if A2DP_AUDIO_PLC_ENABLE
|
||
clk_set_sys_lock(a2dp_low_latency ? SYS_64M : SYS_48M, 0) ;
|
||
#else
|
||
clk_set_sys_lock(a2dp_low_latency ? SYS_48M : SYS_32M, 0) ;
|
||
#endif//A2DP_AUDIO_PLC_ENABLE
|
||
#else
|
||
#if (!TCFG_AUDIO_ANC_ENABLE)
|
||
clk_set_sys_lock(a2dp_low_latency ? SYS_24M : SYS_16M, 0) ;
|
||
#if A2DP_AUDIO_PLC_ENABLE
|
||
clk_set_sys_lock(SYS_32M, 0) ;
|
||
#endif//A2DP_AUDIO_PLC_ENABLE
|
||
#endif
|
||
#endif
|
||
#if !CONFIG_MINI_BT_AUDIO_ENABLE
|
||
} else if (dec->coding_type == AUDIO_CODING_AAC) {
|
||
#if (TCFG_BT_MUSIC_EQ_ENABLE && ((EQ_SECTION_MAX >= 10) && AUDIO_OUT_EFFECT_ENABLE))
|
||
clk_set_sys_lock(SYS_64M, 0) ;
|
||
#elif (TCFG_BT_MUSIC_EQ_ENABLE && ((EQ_SECTION_MAX >= 10) || AUDIO_OUT_EFFECT_ENABLE))
|
||
clk_set_sys_lock(a2dp_low_latency ? SYS_64M : SYS_48M, 0) ;
|
||
#else
|
||
clk_set_sys_lock(a2dp_low_latency ? SYS_48M : SYS_48M, 0) ;
|
||
#endif
|
||
#endif /*CONFIG_MINI_BT_AUDIO_ENABLE*/
|
||
}
|
||
#else
|
||
audio_codec_clock_set(AUDIO_A2DP_MODE, dec->coding_type, dec->wait.preemption);
|
||
#endif
|
||
|
||
err = audio_decoder_start(&dec->decoder);
|
||
if (err) {
|
||
goto __err3;
|
||
}
|
||
if (get_sniff_out_status()) {
|
||
clear_sniff_out_status();
|
||
audio_dac_start_pre(&dac_hdl);
|
||
}
|
||
|
||
dec->start = 1;
|
||
|
||
return 0;
|
||
|
||
__err3:
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
audio_mixer_ch_close(&a2dp_dec->mix_ch);
|
||
#else
|
||
audio_output_close(NULL);
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (a2dp_dec->syncts) {
|
||
u32 mix_ch_event_params[3] = {0};
|
||
mix_ch_event_params[1] = (u32)a2dp_dec->syncts;
|
||
audio_syncts_del(mix_ch_event_params);
|
||
}
|
||
#endif
|
||
#endif
|
||
a2dp_decoder_syncts_free(a2dp_dec);
|
||
|
||
__err2:
|
||
audio_decoder_close(&dec->decoder);
|
||
__err1:
|
||
a2dp_dec_release();
|
||
|
||
return err;
|
||
}
|
||
|
||
static int __a2dp_audio_res_close(void)
|
||
{
|
||
a2dp_dec->start = 0;
|
||
if (a2dp_dec->detect_timer) {
|
||
sys_timer_del(a2dp_dec->detect_timer);
|
||
}
|
||
a2dp_decoder_sample_detect_free(a2dp_dec);
|
||
audio_decoder_close(&a2dp_dec->decoder);
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
audio_mixer_ch_close(&a2dp_dec->mix_ch);
|
||
#else
|
||
audio_output_close(NULL);
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (a2dp_dec->syncts) {
|
||
u32 mix_ch_event_params[3] = {0};
|
||
mix_ch_event_params[1] = (u32)a2dp_dec->syncts;
|
||
audio_syncts_del(mix_ch_event_params);
|
||
}
|
||
#endif
|
||
|
||
#endif
|
||
|
||
a2dp_decoder_syncts_free(a2dp_dec);
|
||
a2dp_decoder_plc_free(a2dp_dec);
|
||
a2dp_decoder_stream_free(a2dp_dec, NULL);
|
||
#if TCFG_EQ_ENABLE&&TCFG_BT_MUSIC_EQ_ENABLE
|
||
if (a2dp_dec->eq_drc) {
|
||
dec_eq_drc_free(a2dp_dec->eq_drc);
|
||
a2dp_dec->eq_drc = NULL;
|
||
}
|
||
#endif//TCFG_BT_MUSIC_EQ_ENABLE
|
||
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
audio_digital_vol_close(a2dp_dec->dvol);
|
||
a2dp_dec->dvol = NULL;
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
#if 0
|
||
clk_set_sys_lock(BT_NORMAL_HZ, 0);
|
||
#else
|
||
audio_codec_clock_del(AUDIO_A2DP_MODE);
|
||
#endif
|
||
|
||
a2dp_dec->preempt = 1;
|
||
app_audio_state_exit(APP_AUDIO_STATE_MUSIC);
|
||
return 0;
|
||
}
|
||
|
||
static int a2dp_wait_res_handler(struct audio_res_wait *wait, int event)
|
||
{
|
||
int err = 0;
|
||
|
||
printf("a2dp_wait_res_handler: %d\n", event);
|
||
|
||
if (event == AUDIO_RES_GET) {
|
||
err = a2dp_dec_start();
|
||
} else if (event == AUDIO_RES_PUT) {
|
||
if (a2dp_dec->start) {
|
||
__a2dp_audio_res_close();
|
||
a2dp_drop_frame_start();
|
||
}
|
||
}
|
||
return err;
|
||
}
|
||
|
||
#define A2DP_CODEC_SBC 0x00
|
||
#define A2DP_CODEC_MPEG12 0x01
|
||
#define A2DP_CODEC_MPEG24 0x02
|
||
int __a2dp_dec_open(int media_type, u8 resume)
|
||
{
|
||
struct a2dp_dec_hdl *dec;
|
||
|
||
if (strcmp(os_current_task(), "app_core") != 0) {
|
||
log_e("a2dp dec open in task : %s\n", os_current_task());
|
||
}
|
||
|
||
#if TCFG_AUDIO_ANC_ENABLE
|
||
#ifdef CONFIG_ANC_OVERLAY
|
||
if (anc_train_open_query()) {
|
||
printf("anc_train_open,suspend a2dp_dec\n");
|
||
if (drop_a2dp_timer == 0) {
|
||
drop_a2dp_timer = sys_timer_add(NULL, __a2dp_drop_frame, 50);
|
||
}
|
||
return 0;
|
||
}
|
||
#endif/*CONFIG_ANC_OVERLAY*/
|
||
#endif /*TCFG_AUDIO_ANC_ENABLE*/
|
||
|
||
if (a2dp_suspend) {
|
||
if (tws_api_get_role() == TWS_ROLE_MASTER) {
|
||
if (drop_a2dp_timer == 0) {
|
||
drop_a2dp_timer = sys_timer_add(NULL,
|
||
a2dp_media_clear_packet_before_seqn, 100);
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
if (a2dp_dec) {
|
||
return 0;
|
||
}
|
||
|
||
media_type = a2dp_media_get_codec_type();
|
||
printf("a2dp_dec_open: %d\n", media_type);
|
||
|
||
dec = zalloc(sizeof(*dec));
|
||
if (!dec) {
|
||
return -ENOMEM;
|
||
}
|
||
|
||
switch (media_type) {
|
||
case A2DP_CODEC_SBC:
|
||
printf("a2dp_media_type:SBC");
|
||
dec->coding_type = AUDIO_CODING_SBC;
|
||
break;
|
||
#if !CONFIG_MINI_BT_AUDIO_ENABLE
|
||
case A2DP_CODEC_MPEG24:
|
||
printf("a2dp_media_type:AAC");
|
||
dec->coding_type = AUDIO_CODING_AAC;
|
||
break;
|
||
#endif /*CONFIG_MINI_BT_AUDIO_ENABLE*/
|
||
default:
|
||
printf("a2dp_media_type unsupoport:%d", media_type);
|
||
free(dec);
|
||
return -EINVAL;
|
||
}
|
||
|
||
#if TCFG_USER_TWS_ENABLE
|
||
if (CONFIG_LOW_LATENCY_ENABLE) {
|
||
a2dp_low_latency = tws_api_get_low_latency_state();
|
||
}
|
||
#endif
|
||
a2dp_dec = dec;
|
||
dec->preempt = resume ? 1 : 0;
|
||
dec->wait.priority = 0;
|
||
dec->wait.preemption = 1;
|
||
dec->wait.handler = a2dp_wait_res_handler;
|
||
audio_decoder_task_add_wait(&decode_task, &dec->wait);
|
||
|
||
if (a2dp_dec && (a2dp_dec->start == 0)) {
|
||
a2dp_drop_frame_start();
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int a2dp_dec_open(int media_type)
|
||
{
|
||
return __a2dp_dec_open(media_type, 0);
|
||
}
|
||
|
||
void a2dp_dec_close()
|
||
{
|
||
if (!a2dp_dec) {
|
||
if (CONFIG_LOW_LATENCY_ENABLE) {
|
||
a2dp_low_latency_seqn = 0;
|
||
}
|
||
if (drop_a2dp_timer) {
|
||
sys_timer_del(drop_a2dp_timer);
|
||
drop_a2dp_timer = 0;
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (a2dp_dec->start) {
|
||
__a2dp_audio_res_close();
|
||
}
|
||
|
||
a2dp_dec_release();
|
||
|
||
puts("a2dp_dec_close: exit\n");
|
||
}
|
||
|
||
|
||
static void a2dp_low_latency_clear_a2dp_packet(u8 *data, int len, int rx)
|
||
{
|
||
if (rx) {
|
||
a2dp_low_latency_seqn = (data[0] << 8) | data[1];
|
||
}
|
||
}
|
||
REGISTER_TWS_FUNC_STUB(audio_dec_clear_a2dp_packet) = {
|
||
.func_id = 0x132A6578,
|
||
.func = a2dp_low_latency_clear_a2dp_packet,
|
||
};
|
||
|
||
|
||
static void low_latency_drop_a2dp_frame(void *p)
|
||
{
|
||
int len;
|
||
|
||
/*y_printf("low_latency_drop_a2dp_frame\n");*/
|
||
|
||
if (a2dp_low_latency_seqn == 0) {
|
||
a2dp_media_clear_packet_before_seqn(0);
|
||
return;
|
||
}
|
||
while (1) {
|
||
u8 *packet = a2dp_media_fetch_packet(&len, NULL);
|
||
if (!packet) {
|
||
return;
|
||
}
|
||
u16 seqn = (packet[2] << 8) | packet[3];
|
||
if (seqn_after(seqn, a2dp_low_latency_seqn)) {
|
||
printf("clear_end: %d\n", seqn);
|
||
break;
|
||
}
|
||
a2dp_media_free_packet(packet);
|
||
/*printf("clear: %d\n", seqn);*/
|
||
}
|
||
|
||
if (drop_a2dp_timer) {
|
||
sys_timer_del(drop_a2dp_timer);
|
||
drop_a2dp_timer = 0;
|
||
}
|
||
int type = a2dp_media_get_codec_type();
|
||
if (type >= 0) {
|
||
/*a2dp_dec_open(type);*/
|
||
__a2dp_dec_open(type, 1);
|
||
}
|
||
|
||
if (a2dp_low_latency == 0) {
|
||
tws_api_auto_role_switch_enable();
|
||
}
|
||
|
||
printf("a2dp_delay: %d\n", a2dp_media_get_remain_play_time(1));
|
||
}
|
||
|
||
|
||
int earphone_a2dp_codec_set_low_latency_mode(int enable, int msec)
|
||
{
|
||
int ret = 0;
|
||
int len, err;
|
||
|
||
if (CONFIG_LOW_LATENCY_ENABLE == 0) {
|
||
return -EINVAL;
|
||
}
|
||
|
||
if (esco_dec) {
|
||
return -EINVAL;
|
||
}
|
||
if (drop_a2dp_timer) {
|
||
return -EINVAL;
|
||
}
|
||
if (a2dp_suspend) {
|
||
return -EINVAL;
|
||
}
|
||
|
||
a2dp_low_latency = enable;
|
||
a2dp_low_latency_seqn = 0;
|
||
|
||
r_printf("a2dp_low_latency: %d, %d, %d\n", a2dp_dec->seqn, a2dp_delay_time, enable);
|
||
|
||
if (!a2dp_dec || a2dp_dec->start == 0) {
|
||
#if TCFG_USER_TWS_ENABLE
|
||
tws_api_low_latency_enable(enable);
|
||
#endif
|
||
return 0;
|
||
}
|
||
|
||
if (a2dp_dec->coding_type == AUDIO_CODING_SBC) {
|
||
a2dp_low_latency_seqn = a2dp_dec->seqn + (msec + a2dp_delay_time) / 15;
|
||
} else {
|
||
a2dp_low_latency_seqn = a2dp_dec->seqn + (msec + a2dp_delay_time) / 20;
|
||
}
|
||
|
||
#if TCFG_USER_TWS_ENABLE
|
||
u8 data[2];
|
||
data[0] = a2dp_low_latency_seqn >> 8;
|
||
data[1] = a2dp_low_latency_seqn;
|
||
err = tws_api_send_data_to_slave(data, 2, 0x132A6578);
|
||
if (err == -ENOMEM) {
|
||
return -EINVAL;
|
||
}
|
||
#endif
|
||
|
||
a2dp_dec_close();
|
||
|
||
a2dp_media_clear_packet_before_seqn(0);
|
||
|
||
#if TCFG_USER_TWS_ENABLE
|
||
if (enable) {
|
||
tws_api_auto_role_switch_disable();
|
||
}
|
||
tws_api_low_latency_enable(enable);
|
||
#endif
|
||
|
||
drop_a2dp_timer = sys_timer_add(NULL, low_latency_drop_a2dp_frame, 40);
|
||
|
||
/*r_printf("clear_to_seqn: %d\n", a2dp_low_latency_seqn);*/
|
||
|
||
return 0;
|
||
}
|
||
|
||
int earphone_a2dp_codec_get_low_latency_mode()
|
||
{
|
||
#if TCFG_USER_TWS_ENABLE
|
||
return tws_api_get_low_latency_state();
|
||
#endif
|
||
return a2dp_low_latency;
|
||
}
|
||
|
||
|
||
int a2dp_tws_dec_suspend(void *p)
|
||
{
|
||
r_printf("a2dp_tws_dec_suspend\n");
|
||
/*mem_stats();*/
|
||
|
||
if (a2dp_suspend) {
|
||
return -EINVAL;
|
||
}
|
||
a2dp_suspend = 1;
|
||
|
||
if (a2dp_dec) {
|
||
a2dp_dec_close();
|
||
a2dp_media_clear_packet_before_seqn(0);
|
||
if (tws_api_get_role() == 0) {
|
||
drop_a2dp_timer = sys_timer_add(NULL, (void (*)(void *))a2dp_media_clear_packet_before_seqn, 100);
|
||
}
|
||
}
|
||
|
||
int err = audio_decoder_fmt_lock(&decode_task, AUDIO_CODING_AAC);
|
||
if (err) {
|
||
log_e("AAC_dec_lock_faild\n");
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
|
||
void a2dp_tws_dec_resume(void)
|
||
{
|
||
r_printf("a2dp_tws_dec_resume\n");
|
||
|
||
if (a2dp_suspend) {
|
||
a2dp_suspend = 0;
|
||
|
||
if (drop_a2dp_timer) {
|
||
sys_timer_del(drop_a2dp_timer);
|
||
drop_a2dp_timer = 0;
|
||
}
|
||
|
||
audio_decoder_fmt_unlock(&decode_task, AUDIO_CODING_AAC);
|
||
|
||
int type = a2dp_media_get_codec_type();
|
||
printf("codec_type: %d\n", type);
|
||
if (type >= 0) {
|
||
if (tws_api_get_role() == 0) {
|
||
a2dp_media_clear_packet_before_seqn(0);
|
||
}
|
||
a2dp_resume_time = jiffies + msecs_to_jiffies(80);
|
||
/*a2dp_dec_open(type);*/
|
||
__a2dp_dec_open(type, 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
static const u8 msbc_mute_data[] = {
|
||
0xAD, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6D, 0xB6, 0xDD, 0xDB, 0x6D, 0xB7, 0x76,
|
||
0xDB, 0x6D, 0xDD, 0xB6, 0xDB, 0x77, 0x6D, 0xB6, 0xDD, 0xDB, 0x6D, 0xB7, 0x76, 0xDB, 0x6D, 0xDD,
|
||
0xB6, 0xDB, 0x77, 0x6D, 0xB6, 0xDD, 0xDB, 0x6D, 0xB7, 0x76, 0xDB, 0x6D, 0xDD, 0xB6, 0xDB, 0x77,
|
||
0x6D, 0xB6, 0xDD, 0xDB, 0x6D, 0xB7, 0x76, 0xDB, 0x6C, 0x00,
|
||
};
|
||
|
||
static int headcheck(u8 *buf)
|
||
{
|
||
int sync_word = buf[0] | ((buf[1] & 0x0f) << 8);
|
||
return (sync_word == 0x801) && (buf[2] == 0xAD);
|
||
}
|
||
|
||
#define MSBC_FRAME_LEN 58
|
||
static int esco_dump_rts_info(struct rt_stream_info *pkt)
|
||
{
|
||
u32 hash = 0xffffffff;
|
||
int read_len = 0;
|
||
pkt->baddr = lmp_private_get_esco_packet(&read_len, &hash);
|
||
pkt->seqn = hash;
|
||
/* printf("hash0=%d,%d ",hash,pkt->baddr ); */
|
||
if (pkt->baddr && read_len) {
|
||
pkt->remain_len = lmp_private_get_esco_remain_buffer_size();
|
||
pkt->data_len = lmp_private_get_esco_data_len();
|
||
return 0;
|
||
}
|
||
if (read_len == -EINVAL) {
|
||
//puts("----esco close\n");
|
||
return -EINVAL;
|
||
}
|
||
if (read_len < 0) {
|
||
|
||
return -ENOMEM;
|
||
}
|
||
return ENOMEM;
|
||
}
|
||
|
||
static int esco_frame_head_check(struct esco_dec_hdl *dec)
|
||
{
|
||
if (dec->coding_type != AUDIO_CODING_MSBC || dec->data_len) {
|
||
return 0;
|
||
}
|
||
/*新的一帧数据*/
|
||
int len = dec->frame_len - dec->offset;
|
||
u8 *data = (u8 *)dec->frame + dec->offset;
|
||
int offset;
|
||
|
||
for (offset = 0; offset < len; offset++) {
|
||
if (data[offset] == 0xAD) {
|
||
if (offset >= 2 && !headcheck(data + offset - 2)) {
|
||
continue;
|
||
}
|
||
goto check_success;
|
||
}
|
||
}
|
||
|
||
return -EINVAL;
|
||
check_success:
|
||
dec->offset += offset;
|
||
return 0;
|
||
}
|
||
|
||
|
||
static int esco_dec_get_frame(struct audio_decoder *decoder, u8 **frame)
|
||
{
|
||
int len = 0;
|
||
u32 hash = 0;
|
||
struct esco_dec_hdl *dec = container_of(decoder, struct esco_dec_hdl, decoder);
|
||
|
||
__again:
|
||
if (dec->repair && dec->coding_type == AUDIO_CODING_MSBC) {
|
||
memcpy((u8 *)dec->data, msbc_mute_data, sizeof(msbc_mute_data));
|
||
*frame = (u8 *)dec->data;
|
||
return dec->esco_len;
|
||
}
|
||
|
||
if (dec->frame) {
|
||
int err = esco_frame_head_check(dec);
|
||
if (err) {
|
||
dec->frame_err_len += dec->frame_len;
|
||
if (dec->frame_err_len >= dec->esco_len) {
|
||
lmp_private_free_esco_packet(dec->frame);
|
||
dec->frame = NULL;
|
||
dec->repair = 1;
|
||
dec->frame_err_len = 0;
|
||
goto __again;
|
||
}
|
||
} else {
|
||
dec->frame_err_len = 0;
|
||
int len = dec->frame_len - dec->offset;
|
||
if (len > dec->esco_len - dec->data_len) {
|
||
len = dec->esco_len - dec->data_len;
|
||
}
|
||
/*memcpy((u8 *)dec->data + dec->data_len, msbc_mute_data, sizeof(msbc_mute_data));*/
|
||
memcpy((u8 *)dec->data + dec->data_len, dec->frame + dec->offset, len);
|
||
dec->offset += len;
|
||
dec->data_len += len;
|
||
}
|
||
|
||
if (dec->offset == dec->frame_len) {
|
||
lmp_private_free_esco_packet(dec->frame);
|
||
dec->frame = NULL;
|
||
}
|
||
}
|
||
|
||
if (dec->data_len < dec->esco_len) {
|
||
dec->frame = lmp_private_get_esco_packet(&len, &hash);
|
||
/* printf("hash1=%d,%d ",hash,dec->frame ); */
|
||
if (len <= 0) {
|
||
printf("rlen=%d ", len);
|
||
return -EIO;
|
||
}
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
u32 timestamp;
|
||
if (dec->ts_handle) {
|
||
timestamp = esco_audio_timestamp_update(dec->ts_handle, hash);
|
||
if (dec->syncts && (((hash - dec->hash) & 0x7ffffff) == dec->frame_time)) {
|
||
audio_syncts_next_pts(dec->syncts, timestamp);
|
||
}
|
||
dec->hash = hash;
|
||
if (!dec->ts_start) {
|
||
dec->ts_start = 1;
|
||
dec->mix_ch_event_params[2] = timestamp;
|
||
}
|
||
}
|
||
#endif
|
||
dec->repair = 0;
|
||
#if (defined(TCFG_PHONE_MESSAGE_ENABLE) && (TCFG_PHONE_MESSAGE_ENABLE))
|
||
phone_message_enc_write(dec->frame + 2, len - 2);
|
||
#endif
|
||
if (dec->data_len) {
|
||
dec->offset = 0;
|
||
} else {
|
||
dec->offset = dec->coding_type == AUDIO_CODING_MSBC ? 2 : 0;
|
||
}
|
||
dec->frame_len = len;
|
||
u32 *error_num = (u32 *)dec->frame;
|
||
if (error_num[0] == 0xAAAA5555 && error_num[1] == 0xAAAAAAAA) {
|
||
if (dec->coding_type == AUDIO_CODING_MSBC) {
|
||
lmp_private_free_esco_packet(dec->frame);
|
||
dec->frame = NULL;
|
||
}
|
||
dec->repair = 1;
|
||
}
|
||
goto __again;
|
||
}
|
||
*frame = (u8 *)dec->data;
|
||
return dec->esco_len;
|
||
}
|
||
|
||
static void esco_dec_put_frame(struct audio_decoder *decoder, u8 *frame)
|
||
{
|
||
struct esco_dec_hdl *dec = container_of(decoder, struct esco_dec_hdl, decoder);
|
||
|
||
dec->data_len = 0;
|
||
dec->repair = 0;
|
||
/*lmp_private_free_esco_packet((void *)frame);*/
|
||
}
|
||
|
||
static const struct audio_dec_input esco_input = {
|
||
.coding_type = AUDIO_CODING_MSBC,
|
||
.data_type = AUDIO_INPUT_FRAME,
|
||
.ops = {
|
||
.frame = {
|
||
.fget = esco_dec_get_frame,
|
||
.fput = esco_dec_put_frame,
|
||
}
|
||
}
|
||
};
|
||
|
||
u32 lmp_private_clear_sco_packet(u8 clear_num);
|
||
static void esco_dec_clear_all_packet(struct esco_dec_hdl *dec)
|
||
{
|
||
lmp_private_clear_sco_packet(0xff);
|
||
}
|
||
|
||
static int esco_dec_rx_delay_monitor(struct audio_decoder *decoder, struct rt_stream_info *info)
|
||
{
|
||
struct esco_dec_hdl *dec = container_of(decoder, struct esco_dec_hdl, decoder);
|
||
|
||
#if 0
|
||
if (!dec->sync_step) {
|
||
if (info->data_len <= 0) {
|
||
return -EAGAIN;
|
||
}
|
||
#if 0//TCFG_USER_TWS_ENABLE
|
||
int state = tws_api_get_tws_state();
|
||
if (state & TWS_STA_SIBLING_CONNECTED) {
|
||
if (tws_api_get_role() == TWS_ROLE_SLAVE) {
|
||
audio_dac_sound_reset(&dac_hdl, 150);
|
||
}
|
||
}
|
||
#endif
|
||
dec->sync_step = 2;
|
||
}
|
||
|
||
if (info->data_len <= 120) {
|
||
info->distance_time = -15;
|
||
} else if (info->data_len > 240) {
|
||
info->distance_time = 15;
|
||
}
|
||
|
||
#endif
|
||
/*printf("%d - %d\n", info->data_len, info->remain_len);*/
|
||
return 0;
|
||
}
|
||
|
||
static int esco_dec_probe_handler(struct audio_decoder *decoder)
|
||
{
|
||
struct esco_dec_hdl *dec = container_of(decoder, struct esco_dec_hdl, decoder);
|
||
int err = 0;
|
||
int find_packet = 0;
|
||
struct rt_stream_info rts_info = {0};
|
||
err = esco_dump_rts_info(&rts_info);
|
||
if (err == -EINVAL) {
|
||
return err;
|
||
}
|
||
if (err || !dec->enc_start) {
|
||
audio_decoder_suspend(decoder, 0);
|
||
return -EAGAIN;
|
||
}
|
||
|
||
#if TCFG_USER_TWS_ENABLE
|
||
if (tws_network_audio_was_started()) {
|
||
/*清除从机声音加入标志*/
|
||
dec->slience_frames = 20;
|
||
tws_network_local_audio_start();
|
||
}
|
||
#endif
|
||
if (dec->preempt) {
|
||
dec->preempt = 0;
|
||
dec->slience_frames = 20;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
|
||
|
||
static int audio_esco_int2short_convert_output(void *priv, s16 *data, int len)
|
||
{
|
||
/* put_buf(data,len); */
|
||
return audio_dac_write(&dac_hdl, data, len);
|
||
|
||
}
|
||
static int audio_esco_short2int_convert_output(void *priv, void *data, int len)
|
||
{
|
||
#if 0//AUDIO_CODEC_SUPPORT_SYNC
|
||
if (audio_sync) {
|
||
audio_decoder_sync_run(audio_sync, data, len);
|
||
#if TCFG_PHONE_EQ_ENABLE
|
||
} else if (esco_eq) {
|
||
return audio_eq_run(esco_eq, data, len);
|
||
#endif
|
||
} else if (int2short_convert) {
|
||
/*return int2short_convert_run(int2short_convert, data, len);*/
|
||
}
|
||
#endif
|
||
return len;
|
||
}
|
||
|
||
/*level:0~15*/
|
||
static const u16 esco_dvol_tab[] = {
|
||
0, //0
|
||
111,//1
|
||
161,//2
|
||
234,//3
|
||
338,//4
|
||
490,//5
|
||
708,//6
|
||
1024,//7
|
||
1481,//8
|
||
2142,//9
|
||
3098,//10
|
||
4479,//11
|
||
6477,//12
|
||
9366,//13
|
||
14955,//14
|
||
16384 //15
|
||
};
|
||
#define MONO_TO_DUAL_POINTS 30
|
||
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
static inline void audio_pcm_mono_to_dual(s16 *dual_pcm, s16 *mono_pcm, int points)
|
||
{
|
||
s16 *mono = mono_pcm;
|
||
int i = 0;
|
||
u8 j = 0;
|
||
|
||
for (i = 0; i < points; i++, mono++) {
|
||
*dual_pcm++ = *mono;
|
||
*dual_pcm++ = *mono;
|
||
}
|
||
}
|
||
|
||
static int esco_output_after_syncts_filter(void *priv, s16 *data, int len)
|
||
{
|
||
struct esco_dec_hdl *dec = (struct esco_dec_hdl *)priv;
|
||
int wlen = 0;
|
||
|
||
#if TCFG_AUDIO_OUTPUT_IIS
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
wlen = audio_mixer_ch_write(&dec->mix_ch, data, len);
|
||
#else
|
||
wlen = audio_output_data_esco(dec, data, len);
|
||
#endif
|
||
#else
|
||
s16 two_ch_data[MONO_TO_DUAL_POINTS * 2];
|
||
s16 point_num = 0;
|
||
u8 mono_to_dual = 0;
|
||
s16 *mono_data = (s16 *)data;
|
||
u16 remain_points = (len >> 1);
|
||
/*
|
||
*如果dac输出是双声道,因为esco解码出来时单声道
|
||
*所以这里要根据dac通道确定是否做单变双
|
||
*/
|
||
|
||
#if (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_LR)
|
||
mono_to_dual = 1;
|
||
#endif
|
||
if (mono_to_dual) {
|
||
do {
|
||
point_num = MONO_TO_DUAL_POINTS;
|
||
if (point_num >= remain_points) {
|
||
point_num = remain_points;
|
||
}
|
||
audio_pcm_mono_to_dual(two_ch_data, mono_data, point_num);
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
int tmp_len = audio_mixer_ch_write(&dec->mix_ch, two_ch_data, point_num << 2);
|
||
#else
|
||
int tmp_len = audio_output_data_esco(dec, two_ch_data, point_num << 2);
|
||
#endif
|
||
|
||
wlen += tmp_len;
|
||
remain_points -= (tmp_len >> 2);
|
||
if (tmp_len < (point_num << 2)) {
|
||
break;
|
||
}
|
||
mono_data += point_num;
|
||
} while (remain_points);
|
||
wlen >>= 1;
|
||
} else {
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
wlen = audio_mixer_ch_write(&dec->mix_ch, data, len);
|
||
#else
|
||
wlen = audio_output_data_esco(dec, data, len);
|
||
#endif
|
||
}
|
||
#endif
|
||
|
||
return wlen;
|
||
|
||
}
|
||
|
||
static int esco_dec_output_handler(struct audio_decoder *decoder, s16 *buf, int size, void *priv)
|
||
{
|
||
int wlen = 0;
|
||
int ret_len = size;
|
||
int len = size;
|
||
short *data = buf;
|
||
struct esco_dec_hdl *dec = container_of(decoder, struct esco_dec_hdl, decoder);
|
||
|
||
/*非上次残留数据,进行后处理*/
|
||
if (!dec->remain) {
|
||
if (dec->slience_frames) {
|
||
memset(data, 0x0, len);
|
||
dec->slience_frames--;
|
||
}
|
||
#if (defined(TCFG_PHONE_MESSAGE_ENABLE) && (TCFG_PHONE_MESSAGE_ENABLE))
|
||
phone_message_call_api_esco_out_data(data, len);
|
||
#endif/*TCFG_PHONE_MESSAGE_ENABLE*/
|
||
if (priv) {
|
||
audio_plc_run(data, len, *(u8 *)priv);
|
||
}
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
audio_digital_vol_run(esco_dec->dvol, data, len);
|
||
u16 dvol_val = esco_dvol_tab[app_var.aec_dac_gain];
|
||
for (u16 i = 0; i < len / 2; i++) {
|
||
s32 tmp_data = data[i];
|
||
if (tmp_data < 0) {
|
||
tmp_data = -tmp_data;
|
||
tmp_data = (tmp_data * dvol_val) >> 14;
|
||
tmp_data = -tmp_data;
|
||
} else {
|
||
tmp_data = (tmp_data * dvol_val) >> 14;
|
||
}
|
||
data[i] = tmp_data;
|
||
}
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
|
||
#if TCFG_DIG_PHASE_INVERTER_EN
|
||
digital_phase_inverter_s16(data, len);
|
||
#endif/*TCFG_DIG_PHASE_INVERTER_EN*/
|
||
|
||
#if TCFG_AUDIO_NOISE_GATE
|
||
/*来电去电铃声不做处理*/
|
||
if (get_call_status() == BT_CALL_ACTIVE) {
|
||
audio_noise_gate_run(data, data, len);
|
||
}
|
||
#endif/*TCFG_AUDIO_NOISE_GATE*/
|
||
|
||
#if TCFG_EQ_ENABLE&&TCFG_PHONE_EQ_ENABLE
|
||
eq_drc_run(dec->eq_drc, data, len);
|
||
#endif//TCFG_PHONE_EQ_ENABLE
|
||
|
||
}
|
||
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (dec->syncts) {
|
||
wlen = audio_syncts_frame_filter(dec->syncts, data, len);
|
||
if (wlen < len) {
|
||
audio_syncts_trigger_resume(dec->syncts, (void *)decoder, audio_filter_resume_decoder);
|
||
}
|
||
goto ret_handle;
|
||
}
|
||
#endif
|
||
wlen = esco_output_after_syncts_filter(dec, data, len);
|
||
|
||
ret_handle:
|
||
|
||
dec->remain = wlen == len ? 0 : 1;
|
||
|
||
return wlen;
|
||
}
|
||
|
||
static int esco_dec_post_handler(struct audio_decoder *decoder)
|
||
{
|
||
|
||
return 0;
|
||
}
|
||
|
||
static const struct audio_dec_handler esco_dec_handler = {
|
||
.dec_probe = esco_dec_probe_handler,
|
||
.dec_output = esco_dec_output_handler,
|
||
.dec_post = esco_dec_post_handler,
|
||
};
|
||
|
||
void esco_dec_release()
|
||
{
|
||
audio_decoder_task_del_wait(&decode_task, &esco_dec->wait);
|
||
free(esco_dec);
|
||
esco_dec = NULL;
|
||
}
|
||
|
||
void esco_dec_close();
|
||
|
||
static void esco_dec_event_handler(struct audio_decoder *decoder, int argc, int *argv)
|
||
{
|
||
switch (argv[0]) {
|
||
case AUDIO_DEC_EVENT_END:
|
||
puts("AUDIO_DEC_EVENT_END\n");
|
||
esco_dec_close();
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
static void esco_dec_set_output_channel(struct esco_dec_hdl *dec)
|
||
{
|
||
u8 dac_connect_mode = audio_dac_get_channel(&dac_hdl);
|
||
}
|
||
|
||
u16 source_sr;
|
||
void set_source_sample_rate(u16 sample_rate)
|
||
{
|
||
source_sr = sample_rate;
|
||
}
|
||
|
||
u16 get_source_sample_rate()
|
||
{
|
||
if (bt_audio_is_running()) {
|
||
return source_sr;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
void esco_digital_vol_set(u8 vol)
|
||
{
|
||
if (esco_dec) {
|
||
audio_digital_vol_set(esco_dec->dvol, vol);
|
||
}
|
||
}
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
|
||
int esco_dec_dac_gain_set(u8 gain)
|
||
{
|
||
app_var.aec_dac_gain = gain;
|
||
if (esco_dec) {
|
||
audio_dac_set_analog_vol(&dac_hdl, gain);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int esco_decoder_syncts_setup(struct esco_dec_hdl *dec)
|
||
{
|
||
int err = 0;
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
#define ESCO_DELAY_TIME 60
|
||
struct audio_syncts_params params = {0};
|
||
int sample_rate = dec->decoder.fmt.sample_rate;
|
||
|
||
params.nch = dec->decoder.fmt.channel;
|
||
#if defined(TCFG_AUDIO_OUTPUT_IIS) && TCFG_AUDIO_OUTPUT_IIS
|
||
params.pcm_device = PCM_OUTSIDE_DAC;
|
||
params.rout_sample_rate = TCFG_IIS_SAMPLE_RATE;
|
||
#else
|
||
params.pcm_device = PCM_INSIDE_DAC;
|
||
params.rout_sample_rate = sample_rate;
|
||
#endif
|
||
params.network = AUDIO_NETWORK_BT2_1;
|
||
params.rin_sample_rate = sample_rate;
|
||
params.priv = dec;
|
||
params.factor = TIME_US_FACTOR;
|
||
params.output = (int (*)(void *, void *, int))esco_output_after_syncts_filter;
|
||
|
||
bt_audio_sync_nettime_select(0);//0 - 主机,1 - tws, 2 - BLE
|
||
u8 frame_clkn = dec->esco_len >= MSBC_FRAME_LEN ? 12 : 6;
|
||
dec->ts_handle = esco_audio_timestamp_create(frame_clkn, ESCO_DELAY_TIME, TIME_US_FACTOR);
|
||
dec->frame_time = frame_clkn;
|
||
audio_syncts_open(&dec->syncts, ¶ms);
|
||
if (!err) {
|
||
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
dec->mix_ch_event_params[0] = (u32)&dec->mix_ch;
|
||
dec->mix_ch_event_params[1] = (u32)dec->syncts;
|
||
audio_mixer_ch_set_event_handler(&dec->mix_ch, (void *)dec->mix_ch_event_params, audio_mix_ch_event_handler);
|
||
#else
|
||
dec->mix_ch_event_params[1] = (u32)dec->syncts;
|
||
#endif
|
||
}
|
||
dec->ts_start = 0;
|
||
#endif
|
||
return err;
|
||
}
|
||
|
||
static void esco_decoder_syncts_free(struct esco_dec_hdl *dec)
|
||
{
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (dec->ts_handle) {
|
||
esco_audio_timestamp_close(dec->ts_handle);
|
||
dec->ts_handle = NULL;
|
||
}
|
||
if (dec->syncts) {
|
||
audio_syncts_close(dec->syncts);
|
||
dec->syncts = NULL;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
int esco_dec_start()
|
||
{
|
||
int err;
|
||
struct audio_fmt f;
|
||
enum audio_channel channel;
|
||
struct esco_dec_hdl *dec = esco_dec;
|
||
u16 mix_buf_len_fix = 240;
|
||
|
||
if (!esco_dec) {
|
||
return -EINVAL;
|
||
}
|
||
|
||
err = audio_decoder_open(&dec->decoder, &esco_input, &decode_task);
|
||
if (err) {
|
||
goto __err1;
|
||
}
|
||
|
||
audio_decoder_set_handler(&dec->decoder, &esco_dec_handler);
|
||
audio_decoder_set_event_handler(&dec->decoder, esco_dec_event_handler, 0);
|
||
|
||
if (dec->coding_type == AUDIO_CODING_MSBC) {
|
||
f.coding_type = AUDIO_CODING_MSBC;
|
||
f.sample_rate = 16000;
|
||
f.channel = 1;
|
||
} else if (dec->coding_type == AUDIO_CODING_CVSD) {
|
||
f.coding_type = AUDIO_CODING_CVSD;
|
||
f.sample_rate = 8000;
|
||
f.channel = 1;
|
||
mix_buf_len_fix = 120;
|
||
}
|
||
|
||
set_source_sample_rate(f.sample_rate);
|
||
esco_dec_set_output_channel(esco_dec);
|
||
|
||
err = audio_decoder_set_fmt(&dec->decoder, &f);
|
||
if (err) {
|
||
goto __err2;
|
||
}
|
||
|
||
/*
|
||
*虽然mix有直通的处理,但是如果混合第二种声音进来的时候,就会按照mix_buff
|
||
*的大小来混合输出,该buff太大,回导致dac没有连续的数据播放
|
||
*/
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
/*audio_mixer_set_output_buf(&mixer, mix_buff, sizeof(mix_buff) / 8);*/
|
||
audio_mixer_set_output_buf(&mixer, mix_buff, mix_buf_len_fix);
|
||
audio_mixer_ch_open(&dec->mix_ch, &mixer);
|
||
#if defined(TCFG_AUDIO_OUTPUT_IIS) && TCFG_AUDIO_OUTPUT_IIS
|
||
audio_mixer_ch_set_sample_rate(&dec->mix_ch, TCFG_IIS_SAMPLE_RATE);
|
||
#else
|
||
audio_mixer_ch_set_sample_rate(&dec->mix_ch, f.sample_rate);
|
||
#endif
|
||
audio_mixer_ch_set_resume_handler(&dec->mix_ch, (void *)&dec->decoder, (void (*)(void *))audio_decoder_resume);
|
||
#endif
|
||
|
||
app_audio_state_switch(APP_AUDIO_STATE_CALL, app_var.aec_dac_gain);
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
esco_dec->dvol = audio_digital_vol_open(app_audio_get_volume(APP_AUDIO_STATE_CALL), 15, 4);
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
printf("max_vol:%d,call_vol:%d", app_var.aec_dac_gain, app_audio_get_volume(APP_AUDIO_STATE_CALL));
|
||
app_audio_set_volume(APP_AUDIO_STATE_CALL, app_var.call_volume, 1);
|
||
|
||
audio_plc_open(f.sample_rate);
|
||
|
||
#if TCFG_AUDIO_NOISE_GATE
|
||
/*限幅器上限*/
|
||
#define LIMITER_THR -10000 /*-12000 = -12dB,放大1000倍,(-10000参考)*/
|
||
/*小于CONST_NOISE_GATE的当成噪声处理,防止清0近端声音*/
|
||
#define LIMITER_NOISE_GATE -40000 /*-12000 = -12dB,放大1000倍,(-30000参考)*/
|
||
/*低于噪声门限阈值的增益 */
|
||
#define LIMITER_NOISE_GAIN (0 << 30) /*(0~1)*2^30*/
|
||
audio_noise_gate_open(f.sample_rate, LIMITER_THR, LIMITER_NOISE_GATE, LIMITER_NOISE_GAIN);
|
||
#endif/*TCFG_AUDIO_NOISE_GATE*/
|
||
#if TCFG_EQ_ENABLE&&TCFG_PHONE_EQ_ENABLE
|
||
u8 drc_en = 0;
|
||
#if TCFG_DRC_ENABLE&&ESCO_DRC_EN
|
||
drc_en = 1;
|
||
#endif//ESCO_DRC_EN
|
||
|
||
dec->eq_drc = esco_eq_drc_setup(NULL, NULL, f.sample_rate, f.channel, 0, drc_en);
|
||
#endif//TCFG_PHONE_EQ_ENABLE
|
||
|
||
|
||
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
esco_decoder_syncts_setup(dec);
|
||
#endif/*AUDIO_CODEC_SUPPORT_SYNC*/
|
||
|
||
audio_dac_set_delay_time(&dac_hdl, 30, 50);
|
||
lmp_private_esco_suspend_resume(2);
|
||
err = audio_decoder_start(&dec->decoder);
|
||
if (err) {
|
||
goto __err3;
|
||
}
|
||
if (get_sniff_out_status()) {
|
||
clear_sniff_out_status();
|
||
audio_dac_start_pre(&dac_hdl);
|
||
}
|
||
audio_out_effect_dis = 1;//通话模式关闭高低音
|
||
|
||
dec->start = 1;
|
||
dec->remain = 0;
|
||
|
||
err = audio_aec_init(f.sample_rate);
|
||
if (err) {
|
||
printf("audio_aec_init failed:%d", err);
|
||
//goto __err3;
|
||
}
|
||
err = esco_enc_open(dec->coding_type, dec->esco_len);
|
||
if (err) {
|
||
printf("audio_enc_open failed:%d", err);
|
||
goto __err3;
|
||
}
|
||
|
||
audio_codec_clock_set(AUDIO_ESCO_MODE, dec->coding_type, dec->wait.preemption);
|
||
#if ESCO_DRC_EN
|
||
clk_set_sys_lock(96 * (1000000L), 0);
|
||
#endif
|
||
dec->enc_start = 1; //该函数所在任务优先级低可能未open编码就开始解码,加入enc开始的标志防止解码过快输出
|
||
printf("esco_dec_start ok\n");
|
||
|
||
return 0;
|
||
|
||
__err3:
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
audio_mixer_ch_close(&dec->mix_ch);
|
||
#else
|
||
audio_output_close(NULL);
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (dec->syncts) {
|
||
u32 mix_ch_event_params[3] = {0};
|
||
mix_ch_event_params[1] = (u32)dec->syncts;
|
||
audio_syncts_del(mix_ch_event_params);
|
||
}
|
||
#endif
|
||
#endif
|
||
esco_decoder_syncts_free(dec);
|
||
|
||
__err2:
|
||
audio_decoder_close(&dec->decoder);
|
||
__err1:
|
||
esco_dec_release();
|
||
return err;
|
||
}
|
||
|
||
static int __esco_dec_res_close(void)
|
||
{
|
||
if (!esco_dec->start) {
|
||
return 0;
|
||
}
|
||
esco_dec->start = 0;
|
||
esco_dec->enc_start = 0;
|
||
esco_dec->preempt = 1;
|
||
|
||
audio_aec_close();
|
||
esco_enc_close();
|
||
app_audio_state_exit(APP_AUDIO_STATE_CALL);
|
||
audio_decoder_close(&esco_dec->decoder);
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
audio_mixer_ch_close(&esco_dec->mix_ch);
|
||
#else
|
||
audio_output_close(NULL);
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
if (esco_dec->syncts) {
|
||
u32 mix_ch_event_params[3] = {0};
|
||
mix_ch_event_params[1] = (u32)esco_dec->syncts;
|
||
audio_syncts_del(mix_ch_event_params);
|
||
}
|
||
#endif
|
||
#endif
|
||
|
||
#if A2DP_RX_AND_AUDIO_DELAY
|
||
audio_dac_set_delay_time(&dac_hdl, 20, AUDIO_DAC_DELAY_TIME);
|
||
#else
|
||
audio_dac_set_delay_time(&dac_hdl, 30, AUDIO_DAC_DELAY_TIME);
|
||
#endif
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
esco_decoder_syncts_free(esco_dec);
|
||
esco_dec->sync_step = 0;
|
||
esco_dec->preempt_state = DEC_PREEMTED_BY_PRIORITY;
|
||
#endif
|
||
#if TCFG_EQ_ENABLE&&TCFG_PHONE_EQ_ENABLE
|
||
if (esco_dec->eq_drc) {
|
||
esco_eq_drc_free(esco_dec->eq_drc);
|
||
esco_dec->eq_drc = NULL;
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
audio_out_effect_dis = 0;
|
||
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
audio_digital_vol_close(esco_dec->dvol);
|
||
esco_dec->dvol = NULL;
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
|
||
audio_plc_close();
|
||
#if TCFG_AUDIO_NOISE_GATE
|
||
audio_noise_gate_close();
|
||
#endif/*TCFG_AUDIO_NOISE_GATE*/
|
||
audio_codec_clock_del(AUDIO_ESCO_MODE);
|
||
return 0;
|
||
}
|
||
static int esco_wait_res_handler(struct audio_res_wait *wait, int event)
|
||
{
|
||
int err = 0;
|
||
|
||
printf("esco_wait_res_handler:%d", event);
|
||
if (event == AUDIO_RES_GET) {
|
||
err = esco_dec_start();
|
||
} else if (event == AUDIO_RES_PUT) {
|
||
err = __esco_dec_res_close();
|
||
lmp_private_esco_suspend_resume(1);
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
int esco_dec_open(void *param, u8 mute)
|
||
{
|
||
int err;
|
||
struct esco_dec_hdl *dec;
|
||
u32 esco_param = *(u32 *)param;
|
||
int esco_len = esco_param >> 16;
|
||
int codec_type = esco_param & 0x000000ff;
|
||
|
||
#if TCFG_AUDIO_ANC_ENABLE
|
||
#ifdef CONFIG_ANC_OVERLAY
|
||
if (anc_train_open_query()) {
|
||
printf("anc_train_open,suspend esco_dec\n");
|
||
lmp_private_esco_suspend_resume(1);
|
||
return 0;
|
||
}
|
||
#endif/*CONFIG_ANC_OVERLAY*/
|
||
#endif /*TCFG_AUDIO_ANC_ENABLE*/
|
||
|
||
printf("esco_dec_open, type=%d,len=%d\n", codec_type, esco_len);
|
||
|
||
dec = zalloc(sizeof(*dec));
|
||
if (!dec) {
|
||
return -ENOMEM;
|
||
}
|
||
esco_dec = dec;
|
||
|
||
if (codec_type == 3) {
|
||
dec->coding_type = AUDIO_CODING_MSBC;
|
||
dec->esco_len = MSBC_FRAME_LEN;
|
||
} else if (codec_type == 2) {
|
||
dec->coding_type = AUDIO_CODING_CVSD;
|
||
dec->esco_len = esco_len;
|
||
}
|
||
|
||
dec->tws_mute_en = mute;
|
||
|
||
dec->wait.priority = 2;
|
||
dec->wait.preemption = 1;
|
||
dec->wait.handler = esco_wait_res_handler;
|
||
err = audio_decoder_task_add_wait(&decode_task, &dec->wait);
|
||
if (esco_dec && esco_dec->start == 0) {
|
||
lmp_private_esco_suspend_resume(1);
|
||
}
|
||
|
||
#if AUDIO_OUTPUT_AUTOMUTE
|
||
mix_out_automute_skip(1);
|
||
#endif
|
||
return err;
|
||
}
|
||
|
||
|
||
void esco_dec_close()
|
||
{
|
||
if (!esco_dec) {
|
||
return;
|
||
}
|
||
|
||
__esco_dec_res_close();
|
||
esco_dec_release();
|
||
#if AUDIO_OUTPUT_AUTOMUTE
|
||
mix_out_automute_skip(0);
|
||
#endif
|
||
|
||
#if (defined(TCFG_PHONE_MESSAGE_ENABLE) && (TCFG_PHONE_MESSAGE_ENABLE))
|
||
phone_message_call_api_stop();
|
||
#endif
|
||
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
audio_mixer_set_output_buf(&mixer, mix_buff, sizeof(mix_buff));
|
||
#endif
|
||
printf("esco_dec_close succ\n");
|
||
}
|
||
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
u8 bt_audio_is_running(void)
|
||
{
|
||
return (a2dp_dec || esco_dec);
|
||
}
|
||
u8 bt_media_is_running(void)
|
||
{
|
||
return a2dp_dec != NULL;
|
||
}
|
||
u8 bt_phone_dec_is_running()
|
||
{
|
||
return esco_dec != NULL;
|
||
}
|
||
|
||
static void audio_dac_trim_init(int arg)
|
||
{
|
||
struct audio_dac_trim dac_trim;
|
||
|
||
audio_dac_do_trim(&dac_hdl, &dac_trim, 0);
|
||
syscfg_write(CFG_DAC_TRIM_INFO, (void *)&dac_trim, sizeof(dac_trim));
|
||
|
||
audio_dac_set_trim_value(&dac_hdl, &dac_trim);
|
||
}
|
||
|
||
extern u32 read_capless_DTB(void);
|
||
static u8 audio_dec_inited = 0;
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
int audio_dec_init()
|
||
{
|
||
int err;
|
||
|
||
printf("audio_dec_init\n");
|
||
|
||
#if TCFG_WAV_TONE_MIX_ENABLE
|
||
wav_decoder_init();
|
||
#endif/*TCFG_WAV_TONE_MIX_ENABLE*/
|
||
|
||
tone_play_init();
|
||
|
||
#if TCFG_KEY_TONE_EN
|
||
// 按键音初始化
|
||
audio_key_tone_init();
|
||
#endif
|
||
|
||
/*os_sem_create(&dac_sem, 0);*/
|
||
err = audio_decoder_task_create(&decode_task, "audio_dec");
|
||
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
||
audio_digital_vol_init(NULL, 0);
|
||
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
||
audio_dac_init(&dac_hdl, &dac_data);
|
||
#if TCFG_AUDIO_ANC_ENABLE
|
||
audio_dac_anc_set(&dac_hdl, 1);
|
||
#endif/*TCFG_AUDIO_ANC_ENABLE*/
|
||
|
||
u32 dacr32 = read_capless_DTB();
|
||
audio_dac_set_capless_DTB(&dac_hdl, dacr32);
|
||
audio_dac_set_buff(&dac_hdl, dac_buff, sizeof(dac_buff));
|
||
#if A2DP_RX_AND_AUDIO_DELAY
|
||
audio_dac_set_delay_time(&dac_hdl, 10, AUDIO_DAC_DELAY_TIME);
|
||
#else
|
||
audio_dac_set_delay_time(&dac_hdl, 10, AUDIO_DAC_DELAY_TIME);
|
||
#endif/*A2DP_RX_AND_AUDIO_DELAY*/
|
||
audio_dac_set_analog_vol(&dac_hdl, 0);
|
||
|
||
request_irq(IRQ_AUDIO_IDX, 2, audio_irq_handler, 0);
|
||
struct audio_dac_trim dac_trim;
|
||
int len = syscfg_read(CFG_DAC_TRIM_INFO, (void *)&dac_trim, sizeof(dac_trim));
|
||
if (len != sizeof(dac_trim)) {
|
||
#if 0 //DAC 异步trim
|
||
int argv[8];
|
||
argv[0] = (int)audio_dac_trim_init;
|
||
argv[1] = 2;
|
||
do {
|
||
int err = os_taskq_post_type("audio_dec", Q_CALLBACK, 4, argv);
|
||
if (err == OS_ERR_NONE) {
|
||
break;
|
||
}
|
||
if (err != OS_Q_FULL) {
|
||
break;
|
||
}
|
||
os_time_dly(2);
|
||
} while (1);
|
||
#else
|
||
audio_dac_trim_init(0);
|
||
#endif
|
||
} else {
|
||
audio_dac_set_trim_value(&dac_hdl, &dac_trim);
|
||
}
|
||
#if TCFG_MC_BIAS_AUTO_ADJUST
|
||
mic_trim_run();
|
||
#endif/*TCFG_MC_BIAS_AUTO_ADJUST*/
|
||
audio_dac_set_fade_handler(&dac_hdl, NULL, audio_fade_in_fade_out);
|
||
#if AUDIO_CODEC_SUPPORT_SYNC
|
||
audio_sync_resample_set_filt(dac_sync_filt, sizeof(dac_sync_filt));
|
||
#endif
|
||
|
||
/*硬件SRC模块滤波器buffer设置,可根据最大使用数量设置整体buffer*/
|
||
audio_src_base_filt_init(audio_src_hw_filt, sizeof(audio_src_hw_filt));
|
||
|
||
|
||
#if AUDIO_OUT_MIXER_ENABLE
|
||
audio_mixer_open(&mixer);
|
||
audio_mixer_set_handler(&mixer, &mix_handler);
|
||
audio_mixer_set_event_handler(&mixer, mixer_event_handler);
|
||
audio_mixer_set_output_buf(&mixer, mix_buff, sizeof(mix_buff));
|
||
#else
|
||
printf("\n\n\n no support mixer !!! \n\n\n");
|
||
#endif
|
||
|
||
#if AUDIO_OUTPUT_AUTOMUTE
|
||
mix_out_automute_open();
|
||
#endif //#if AUDIO_OUTPUT_AUTOMUTE
|
||
|
||
audio_dec_inited = 1;
|
||
return err;
|
||
}
|
||
|
||
static u8 audio_dec_init_complete()
|
||
{
|
||
/*不支持Audio功能,返回idle*/
|
||
#if (defined TCFG_AUDIO_ENABLE && (TCFG_AUDIO_ENABLE == 0))
|
||
return 1;
|
||
#endif/*TCFG_AUDIO_ENABLE*/
|
||
if (!audio_dec_inited) {
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
REGISTER_LP_TARGET(audio_dec_init_lp_target) = {
|
||
.name = "audio_dec_init",
|
||
.is_idle = audio_dec_init_complete,
|
||
};
|
||
|
||
|
||
|
||
/* ----------------------debug_function---------------------------- */
|
||
/* ----------------------debug_function---------------------------- */
|
||
#include "tone_player.h"
|
||
static const char *num0_9[] = {
|
||
TONE_NUM_0,
|
||
TONE_NUM_1,
|
||
TONE_NUM_2,
|
||
TONE_NUM_3,
|
||
TONE_NUM_4,
|
||
TONE_NUM_5,
|
||
TONE_NUM_6,
|
||
TONE_NUM_7,
|
||
TONE_NUM_8,
|
||
TONE_NUM_9,
|
||
};
|
||
|
||
static void a2dp_test_tone_warning(int error, int is_seqn)
|
||
{
|
||
return ;
|
||
|
||
const char *tone_seqn[16];
|
||
if (is_seqn) {
|
||
int ten_num = 0;
|
||
int num = error;
|
||
while (num < 10) {
|
||
num /= 10;
|
||
ten_num++;
|
||
}
|
||
|
||
int list_i = 0;
|
||
int i;
|
||
tone_seqn[list_i++] = TONE_RING;
|
||
for (i = ten_num; i > 0; i--) {
|
||
tone_seqn[list_i++] = num0_9[((error / (ten_num * 10)) % 10)];
|
||
}
|
||
tone_seqn[list_i] = num0_9[(error % 10)];
|
||
tone_file_list_play(tone_seqn, 1);
|
||
} else {
|
||
tone_play_index(error, 1);
|
||
}
|
||
}
|
||
|
||
static void a2dp_error_tone_warning(int error)
|
||
{
|
||
return;
|
||
|
||
int argv[8];
|
||
argv[0] = (int)a2dp_test_tone_warning;
|
||
argv[1] = 2;
|
||
argv[2] = (int)IDEX_TONE_NUM_0 + error;
|
||
argv[3] = (int)0;
|
||
|
||
do {
|
||
int err = os_taskq_post_type("app_core", Q_CALLBACK, 4, argv);
|
||
if (err == OS_ERR_NONE) {
|
||
break;
|
||
}
|
||
|
||
os_time_dly(2);
|
||
} while (1);
|
||
}
|
||
|
||
void rx_full_tone_warning(void)
|
||
{
|
||
/* if (rx_full == 0) { */
|
||
/* rx_full = 1; */
|
||
/* } */
|
||
}
|
||
|
||
|
||
|
||
|
||
/*----------------------------------------------------------------------------*/
|
||
/**@brief 获取输出默认采样率
|
||
@param
|
||
@return 0: 采样率可变
|
||
@return 非0: 固定采样率
|
||
@note
|
||
*/
|
||
/*----------------------------------------------------------------------------*/
|
||
u32 audio_output_nor_rate(void)
|
||
{
|
||
#if (TCFG_IIS_ENABLE && TCFG_AUDIO_OUTPUT_IIS)
|
||
return TCFG_IIS_SAMPLE_RATE;
|
||
#endif
|
||
#if AUDIO_OUTPUT_INCLUDE_DAC
|
||
|
||
#if (TCFG_MIC_EFFECT_ENABLE)
|
||
return TCFG_REVERB_SAMPLERATE_DEFUAL;
|
||
#endif
|
||
/* return app_audio_output_samplerate_select(input_rate, 1); */
|
||
#elif (AUDIO_OUTPUT_WAY == AUDIO_OUTPUT_WAY_BT)
|
||
|
||
#elif (AUDIO_OUTPUT_WAY == AUDIO_OUTPUT_WAY_FM)
|
||
return 41667;
|
||
#else
|
||
return 44100;
|
||
#endif
|
||
|
||
/* #if TCFG_VIR_UDISK_ENABLE */
|
||
/* return 44100; */
|
||
/* #endif */
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*******************************************************
|
||
* Function name : app_audio_output_samplerate_select
|
||
* Description : 将输入采样率与输出采样率进行匹配对比
|
||
* Parameter :
|
||
* @sample_rate 输入采样率
|
||
* @high: 0 - 低一级采样率,1 - 高一级采样率
|
||
* Return : 匹配后的采样率
|
||
********************* -HB ******************************/
|
||
int app_audio_output_samplerate_select(u32 sample_rate, u8 high)
|
||
{
|
||
return audio_dac_sample_rate_select(&dac_hdl, sample_rate, high);
|
||
}
|
||
|
||
|
||
|
||
/*----------------------------------------------------------------------------*/
|
||
/**@brief 获取输出采样率
|
||
@param input_rate: 输入采样率
|
||
@return 输出采样率
|
||
@note
|
||
*/
|
||
/*----------------------------------------------------------------------------*/
|
||
u32 audio_output_rate(int input_rate)
|
||
{
|
||
u32 out_rate = audio_output_nor_rate();
|
||
if (out_rate) {
|
||
return out_rate;
|
||
}
|
||
|
||
#if (TCFG_REVERB_ENABLE || TCFG_MIC_EFFECT_ENABLE)
|
||
if (input_rate > 48000) {
|
||
return 48000;
|
||
}
|
||
#endif
|
||
return app_audio_output_samplerate_select(input_rate, 1);
|
||
}
|
||
|
||
|