2872 lines
80 KiB
C
2872 lines
80 KiB
C
|
|
/*************************************************************************************************/
|
|||
|
|
/*!
|
|||
|
|
* \file a2dp_dec.c
|
|||
|
|
*
|
|||
|
|
* \brief
|
|||
|
|
*
|
|||
|
|
* Copyright (c) 2011-2022 ZhuHai Jieli Technology Co.,Ltd.
|
|||
|
|
*
|
|||
|
|
*/
|
|||
|
|
/*************************************************************************************************/
|
|||
|
|
#include "sound_device.h"
|
|||
|
|
#include "media/bt_audio_timestamp.h"
|
|||
|
|
#include "media/a2dp_sample_detect.h"
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
#include "spatial_effect/spatial_effect.h"
|
|||
|
|
#endif /*TCFG_AUDIO_SPATIAL_EFFECT_ENABLE*/
|
|||
|
|
|
|||
|
|
#define A2DP_AUDIO_PLC_ENABLE 1
|
|||
|
|
|
|||
|
|
#if A2DP_AUDIO_PLC_ENABLE
|
|||
|
|
#include "media/tech_lib/LFaudio_plc_api.h"
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if TCFG_AUDIO_SPEAK_TO_CHAT_ENABLE
|
|||
|
|
#include "icsd_adt_app.h"
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if AUDIO_VBASS_CONFIG
|
|||
|
|
#include "application/audio_vbass.h"
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if TCFG_AUDIO_CVP_DUT_ENABLE
|
|||
|
|
#include "audio_cvp_dut.h"
|
|||
|
|
#endif /*TCFG_AUDIO_CVP_DUT_ENABLE*/
|
|||
|
|
|
|||
|
|
#include "audio_effect_develop.h"
|
|||
|
|
|
|||
|
|
#if (TCFG_AUDIO_SPATIAL_EFFECT_ENABLE)
|
|||
|
|
#define CONFIG_AUDIO_EFFECT_TASK_ENABLE TCFG_AUDIO_EFFECT_TASK_EBABLE
|
|||
|
|
#else
|
|||
|
|
#define CONFIG_AUDIO_EFFECT_TASK_ENABLE 0
|
|||
|
|
#endif /*TCFG_AUDIO_SPATIAL_EFFECT_ENABLE*/
|
|||
|
|
#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
|
|||
|
|
|
|||
|
|
#ifdef TCFG_AUDIO_MUSIC_SAMPLE_RATE
|
|||
|
|
#define A2DP_SOUND_SAMPLE_RATE TCFG_AUDIO_MUSIC_SAMPLE_RATE
|
|||
|
|
#else
|
|||
|
|
#define A2DP_SOUND_SAMPLE_RATE 0
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if TCFG_USER_TWS_ENABLE && TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
#define CONFIG_TWS_SPATIAL_AUDIO_ENABLE 1
|
|||
|
|
#else
|
|||
|
|
#define CONFIG_TWS_SPATIAL_AUDIO_ENABLE 0
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
static u8 spatial_audio_enable = 1;
|
|||
|
|
static u8 spatial_audio_head_tracked = 0;
|
|||
|
|
void a2dp_spatial_audio_head_tracked_en(u8 en);
|
|||
|
|
int aud_spatial_sensor_init();
|
|||
|
|
int aud_spatial_sensor_exit();
|
|||
|
|
int aud_spatial_sensor_run(void *priv, void *data, int len);
|
|||
|
|
#endif
|
|||
|
|
int aud_effect_push_data(void *priv, s16 *data, u16 len);
|
|||
|
|
|
|||
|
|
/**************************************************************************************************
|
|||
|
|
* A2DP音频数据流异步任务处理说明
|
|||
|
|
*
|
|||
|
|
* 1、解码后数据流的顺序
|
|||
|
|
* 通常A2DP解码后的数据流会按照以下顺序进行流数据处理:
|
|||
|
|
* PLC -> 空间音频(Spatial audio) -> 数字音量(Digtal vol) -> 环绕音效(Surround) ->
|
|||
|
|
* 虚拟音效(Vbass) -> 低音增强(bass_boost)-> 动态增益控制(ANC) -> 音频同步(Syncts) -> EQ/DRC -> 混音器(mixer) -> DAC
|
|||
|
|
*
|
|||
|
|
* 以上流程以实际形态的功能开关为准。
|
|||
|
|
* 2、异步任务处理节点方法:
|
|||
|
|
* a2dp_decoder_effect_task_enter();
|
|||
|
|
*
|
|||
|
|
* 要加入的节点
|
|||
|
|
*
|
|||
|
|
* a2dp_decoder_effect_task_output();
|
|||
|
|
*
|
|||
|
|
* 由该方式包住的节点则都会被归纳到effect task中执行
|
|||
|
|
*
|
|||
|
|
**************************************************************************************************/
|
|||
|
|
#define A2DP_NODE_BEGIN 0x1
|
|||
|
|
#define A2DP_NODE_END 0x2
|
|||
|
|
#define A2DP_NODE_BLOCK 0x4
|
|||
|
|
#define A2DP_NODE_SUB_STREAM_BEGIN 0x8
|
|||
|
|
#define A2DP_NODE_SUB_STREAM_END 0x10
|
|||
|
|
|
|||
|
|
enum a2dp_stream_id {
|
|||
|
|
A2DP_NODE_DECODER = 0x0,
|
|||
|
|
#if A2DP_AUDIO_PLC_ENABLE
|
|||
|
|
A2DP_NODE_PLC,
|
|||
|
|
#endif
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
A2DP_NODE_SPATIAL_AUDIO,
|
|||
|
|
#if TCFG_SENSOR_DATA_READ_IN_DEC_TASK
|
|||
|
|
A2DP_NODE_SENSOR_DATA_STREAM,
|
|||
|
|
#endif /*TCFG_SENSOR_DATA_READ_IN_DEC_TASK*/
|
|||
|
|
#endif /*TCFG_AUDIO_SPATIAL_EFFECT_ENABLE*/
|
|||
|
|
|
|||
|
|
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
|||
|
|
A2DP_NODE_DIGITAL_VOLUME,
|
|||
|
|
#endif
|
|||
|
|
#if (AUDIO_SURROUND_CONFIG || AUDIO_VBASS_CONFIG)
|
|||
|
|
A2DP_NODE_SURROUND_VBASS,
|
|||
|
|
#endif
|
|||
|
|
#if defined(TCFG_AUDIO_BASS_BOOST)&&TCFG_AUDIO_BASS_BOOST
|
|||
|
|
A2DP_NODE_BASS_BOOST,
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if TCFG_AUDIO_ANC_ENABLE && ANC_MUSIC_DYNAMIC_GAIN_EN
|
|||
|
|
A2DP_NODE_DYNAMIC_GAIN,
|
|||
|
|
#endif/*ANC_MUSIC_DYNAMIC_GAIN_EN*/
|
|||
|
|
|
|||
|
|
#if AUDIO_CODEC_SUPPORT_SYNC
|
|||
|
|
A2DP_NODE_BEFORE_SYNCTS,
|
|||
|
|
A2DP_NODE_SYNCTS,
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
A2DP_NODE_EFFECT_TASK_ENTER,
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if (TCFG_EQ_ENABLE && TCFG_BT_MUSIC_EQ_ENABLE)
|
|||
|
|
A2DP_NODE_EQ_DRC,
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
A2DP_NODE_EFFECT_TASK_OUT,
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if ((defined TCFG_EFFECT_DEVELOP_ENABLE) && TCFG_EFFECT_DEVELOP_ENABLE)
|
|||
|
|
A2DP_NODE_EFFECT_DEVELOP,
|
|||
|
|
#endif
|
|||
|
|
A2DP_NODE_MIXER,
|
|||
|
|
A2DP_NODE_MAX,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
struct a2dp_stream_node {
|
|||
|
|
enum a2dp_stream_id id;
|
|||
|
|
u8 attribute;
|
|||
|
|
u8 remain;
|
|||
|
|
void *priv;
|
|||
|
|
int (*data_handler)(void *priv, void *data, int len);
|
|||
|
|
void (*wakeup)(void *stream);
|
|||
|
|
int (*set_wakeup_handler)(void *priv, void *stream, void (*wakeup)(void *));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
struct a2dp_dec_hdl {
|
|||
|
|
struct audio_decoder decoder;
|
|||
|
|
struct audio_res_wait wait;
|
|||
|
|
struct audio_mixer_ch mix_ch;
|
|||
|
|
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;
|
|||
|
|
u8 dut_enable;
|
|||
|
|
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;
|
|||
|
|
u32 sample_rate;
|
|||
|
|
int timer;
|
|||
|
|
u32 coding_type;
|
|||
|
|
u16 delay_time;
|
|||
|
|
u16 detect_timer;
|
|||
|
|
u8 underrun_feedback;
|
|||
|
|
/*
|
|||
|
|
u8 underrun_count;
|
|||
|
|
u32 underrun_time;
|
|||
|
|
u32 underrun_cool_time;
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
void *spatial_audio;
|
|||
|
|
s16 spatial_data_len;
|
|||
|
|
u8 spatial_node_id;
|
|||
|
|
u16 spatial_data_offset;
|
|||
|
|
#endif
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
void *aud_effect;
|
|||
|
|
#endif
|
|||
|
|
#if TCFG_EQ_ENABLE&&TCFG_BT_MUSIC_EQ_ENABLE
|
|||
|
|
struct dec_eq_drc *eq_drc;
|
|||
|
|
#endif//TCFG_BT_MUSIC_EQ_ENABLE
|
|||
|
|
|
|||
|
|
#if AUDIO_SURROUND_CONFIG
|
|||
|
|
struct dec_sur *sur;
|
|||
|
|
#endif//AUDIO_SURROUND_CONFIG
|
|||
|
|
|
|||
|
|
#if AUDIO_VBASS_CONFIG
|
|||
|
|
vbass_hdl *vbass; //虚拟低音句柄
|
|||
|
|
#endif//AUDIO_VBASS_CONFIG
|
|||
|
|
|
|||
|
|
#if defined(TCFG_AUDIO_BASS_BOOST)&&TCFG_AUDIO_BASS_BOOST
|
|||
|
|
struct audio_drc *bass_boost;
|
|||
|
|
#endif
|
|||
|
|
#if ((defined TCFG_EFFECT_DEVELOP_ENABLE) && TCFG_EFFECT_DEVELOP_ENABLE)
|
|||
|
|
void *effect_develop;
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
struct a2dp_stream_node stream_node[A2DP_NODE_MAX + 1];
|
|||
|
|
u8 node_num;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
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;
|
|||
|
|
|
|||
|
|
static u16 a2dp_delay_time;
|
|||
|
|
static u8 a2dp_low_latency = 0;
|
|||
|
|
static u16 drop_a2dp_timer;
|
|||
|
|
static u16 a2dp_low_latency_seqn = 0;
|
|||
|
|
|
|||
|
|
|
|||
|
|
struct a2dp_dec_hdl *a2dp_dec = NULL;
|
|||
|
|
|
|||
|
|
static void a2dp_stream_node_init(struct a2dp_dec_hdl *dec);
|
|||
|
|
static void a2dp_stream_node_add(struct a2dp_dec_hdl *dec,
|
|||
|
|
void *priv,
|
|||
|
|
int (*data_handler)(void *, void *, int),
|
|||
|
|
void (*wakeup)(void *),
|
|||
|
|
int (*set_wakeup_handler)(void *, void *, void (*wakeup)(void *)),
|
|||
|
|
u8 attribute);
|
|||
|
|
static void a2dp_stream_node_wakeup(void *priv);
|
|||
|
|
|
|||
|
|
extern u32 bt_audio_sync_lat_time(void);
|
|||
|
|
extern void bt_audio_sync_nettime_select(u8 base);
|
|||
|
|
void audio_mix_ch_event_handler(void *priv, int event)
|
|||
|
|
{
|
|||
|
|
switch (event) {
|
|||
|
|
case MIXER_EVENT_CH_OPEN:
|
|||
|
|
if (priv) {
|
|||
|
|
u32 *params = (u32 *)priv;
|
|||
|
|
struct audio_mixer_ch *ch = (struct audio_mixer_ch *)params[0];
|
|||
|
|
u32 base_time = params[2];
|
|||
|
|
sound_pcm_dev_add_syncts((void *)params[1]);
|
|||
|
|
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 = sound_pcm_dev_buffered_frames();
|
|||
|
|
int slience_frames = (u64)time_diff * audio_mixer_get_sample_rate(&mixer) / 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);
|
|||
|
|
audio_mixer_ch_add_slience_samples(ch, slience_frames * sound_pcm_dev_channel_mapping(0));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case MIXER_EVENT_CH_CLOSE:
|
|||
|
|
if (priv) {
|
|||
|
|
u32 *params = (u32 *)priv;
|
|||
|
|
sound_pcm_dev_remove_syncts((void *)params[1]);
|
|||
|
|
}
|
|||
|
|
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 + (a2dp_dec->coding_type == AUDIO_CODING_AAC ? 0 : 1);
|
|||
|
|
} else {
|
|||
|
|
header_len = byte_len + ext;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (header_len > len - 1) {
|
|||
|
|
return len;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (a2dp_dec->coding_type == AUDIO_CODING_SBC) {
|
|||
|
|
for (; header_len < len; header_len++) {
|
|||
|
|
if (data[header_len] == 0x9c) {
|
|||
|
|
return header_len;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
log_e("==== find sbc header error === \n");
|
|||
|
|
put_buf(data, 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;
|
|||
|
|
|
|||
|
|
u8 ch_num = sound_pcm_dev_channel_mapping(1);
|
|||
|
|
#if TCFG_USER_TWS_ENABLE
|
|||
|
|
state = tws_api_get_tws_state();
|
|||
|
|
if (state & TWS_STA_SIBLING_CONNECTED) {
|
|||
|
|
if (ch_num == 2) {
|
|||
|
|
channel = tws_api_get_local_channel() == 'L' ? AUDIO_CH_DUAL_L : AUDIO_CH_DUAL_R;
|
|||
|
|
} else {
|
|||
|
|
channel = tws_api_get_local_channel() == 'L' ? AUDIO_CH_L : AUDIO_CH_R;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
if (ch_num == 2) {
|
|||
|
|
channel = AUDIO_CH_LR;
|
|||
|
|
} else {
|
|||
|
|
channel = AUDIO_CH_DIFF;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
dec->ch = ch_num;
|
|||
|
|
#if CONFIG_TWS_SPATIAL_AUDIO_ENABLE
|
|||
|
|
if (spatial_audio_enable || dec->spatial_audio) {
|
|||
|
|
channel = AUDIO_CH_LR;
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
dec->ch = 2;
|
|||
|
|
#endif/*CONFIG_AUDIO_EFFECT_TASK_ENABLE*/
|
|||
|
|
//printf("Spatial Effect,ch:%d,%d\n",dec->ch,channel);
|
|||
|
|
}
|
|||
|
|
#endif/*CONFIG_TWS_SPATIAL_AUDIO_ENABLE*/
|
|||
|
|
#else
|
|||
|
|
if (ch_num == 2) {
|
|||
|
|
channel = AUDIO_CH_LR;
|
|||
|
|
} else {
|
|||
|
|
channel = AUDIO_CH_DIFF;
|
|||
|
|
}
|
|||
|
|
dec->ch = ch_num;
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
|
|||
|
|
#if TCFG_APP_FM_EMITTER_EN
|
|||
|
|
channel = AUDIO_CH_LR;
|
|||
|
|
#endif
|
|||
|
|
if (channel != dec->channel) {
|
|||
|
|
printf("set_channel: %d\n", channel);
|
|||
|
|
#if CONFIG_TWS_SPATIAL_AUDIO_ENABLE
|
|||
|
|
if (dec->spatial_audio) {
|
|||
|
|
if (state & TWS_STA_SIBLING_CONNECTED) {
|
|||
|
|
spatial_audio_set_mapping_channel(dec->spatial_audio, tws_api_get_local_channel() == 'L' ? AUDIO_CH_L : AUDIO_CH_R);
|
|||
|
|
} else {
|
|||
|
|
spatial_audio_set_mapping_channel(dec->spatial_audio, AUDIO_CH_MIX_MONO);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
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//TCFG_BT_MUSIC_EQ_ENABLE
|
|||
|
|
|
|||
|
|
|
|||
|
|
/* #if TCFG_USER_TWS_ENABLE */
|
|||
|
|
#if AUDIO_SURROUND_CONFIG
|
|||
|
|
if (dec->sur) {
|
|||
|
|
audio_surround_set_ch(dec->sur, channel);
|
|||
|
|
}
|
|||
|
|
#endif//AUDIO_SURROUND_CONFIG
|
|||
|
|
/* #endif//TCFG_USER_TWS_ENABLE */
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
*
|
|||
|
|
*/
|
|||
|
|
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 (sound_pcm_dev_buffered_time() < underrun_time) {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
msecs += sound_pcm_dev_buffered_time();
|
|||
|
|
|
|||
|
|
return msecs;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
static int a2dp_dec_rx_delay_monitor(struct audio_decoder *decoder, struct rt_stream_info *info)
|
|||
|
|
{
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
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 550//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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
*********************************************************************
|
|||
|
|
* 蓝牙音乐解码输出
|
|||
|
|
* Description: a2dp高级音频解码输出句柄
|
|||
|
|
* Arguments :
|
|||
|
|
* Return : 返回后级消耗的解码数据长度
|
|||
|
|
* Note(s) : None.
|
|||
|
|
*********************************************************************
|
|||
|
|
*/
|
|||
|
|
#if 0
|
|||
|
|
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) {//异步方式eq
|
|||
|
|
return eq_drc_run(dec->eq_drc, data, len);
|
|||
|
|
}
|
|||
|
|
#endif//TCFG_BT_MUSIC_EQ_ENABLE
|
|||
|
|
|
|||
|
|
return audio_mixer_ch_write(&dec->mix_ch, data, len);
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
static int 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
|
|||
|
|
return len;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if 0
|
|||
|
|
static int a2dp_output_async_data_handler(struct a2dp_dec_hdl *dec, s16 *data, int len)
|
|||
|
|
{
|
|||
|
|
int offset = 0;
|
|||
|
|
int wlen = 0;
|
|||
|
|
int data_len = len;
|
|||
|
|
|
|||
|
|
#if (CONFIG_AUDIO_EFFECT_TASK_ENABLE == 0)
|
|||
|
|
#if CONFIG_TWS_SPATIAL_AUDIO_ENABLE
|
|||
|
|
if (dec->spatial_audio) {
|
|||
|
|
data_len = dec->spatial_data_len;
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
#endif/*CONFIG_AUDIO_EFFECT_TASK_ENABLE*/
|
|||
|
|
|
|||
|
|
#if AUDIO_CODEC_SUPPORT_SYNC
|
|||
|
|
if (dec->syncts) {
|
|||
|
|
int wlen = audio_syncts_frame_filter(dec->syncts, data, data_len);
|
|||
|
|
if (wlen < data_len) {
|
|||
|
|
audio_syncts_trigger_resume(dec->syncts, (void *)&dec->decoder, audio_filter_resume_decoder);
|
|||
|
|
}
|
|||
|
|
#if (CONFIG_AUDIO_EFFECT_TASK_ENABLE == 0)
|
|||
|
|
#if CONFIG_TWS_SPATIAL_AUDIO_ENABLE
|
|||
|
|
if (dec->spatial_audio) {
|
|||
|
|
dec->spatial_data_len -= wlen;
|
|||
|
|
}
|
|||
|
|
#endif /*CONFIG_TWS_SPATIAL_AUDIO_ENABLE*/
|
|||
|
|
#endif/*CONFIG_AUDIO_EFFECT_TASK_ENABLE*/
|
|||
|
|
dec->remain = wlen < data_len ? 1 : 0;
|
|||
|
|
return dec->remain ? wlen : len;
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
wlen = a2dp_output_after_syncts_filter(dec, data, data_len);
|
|||
|
|
#if (CONFIG_AUDIO_EFFECT_TASK_ENABLE == 0)
|
|||
|
|
#if CONFIG_TWS_SPATIAL_AUDIO_ENABLE
|
|||
|
|
dec->spatial_data_len -= wlen;
|
|||
|
|
#endif
|
|||
|
|
#endif/*CONFIG_AUDIO_EFFECT_TASK_ENABLE*/
|
|||
|
|
dec->remain = wlen < data_len ? 1 : 0;
|
|||
|
|
return dec->remain ? wlen : len;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
static void a2dp_stream_node_init(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
struct a2dp_stream_node *node = &dec->stream_node[A2DP_NODE_DECODER];
|
|||
|
|
|
|||
|
|
memset(dec->stream_node, 0x0, sizeof(dec->stream_node));
|
|||
|
|
|
|||
|
|
node->priv = (void *)&dec->decoder;
|
|||
|
|
node->id = A2DP_NODE_DECODER;
|
|||
|
|
node->wakeup = (void (*)(void *))audio_decoder_resume;
|
|||
|
|
node->attribute = A2DP_NODE_BEGIN;
|
|||
|
|
|
|||
|
|
node = &dec->stream_node[A2DP_NODE_MAX];
|
|||
|
|
node->attribute = A2DP_NODE_END;
|
|||
|
|
node->id = A2DP_NODE_MAX;
|
|||
|
|
dec->node_num = 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void a2dp_stream_node_add(struct a2dp_dec_hdl *dec,
|
|||
|
|
void *priv,
|
|||
|
|
int (*data_handler)(void *, void *, int),
|
|||
|
|
void (*wakeup)(void *),
|
|||
|
|
int (*set_wakeup_handler)(void *, void *, void (*wakeup)(void *)),
|
|||
|
|
u8 attribute)
|
|||
|
|
{
|
|||
|
|
struct a2dp_stream_node *node = &dec->stream_node[dec->node_num++];
|
|||
|
|
node->id = dec->node_num - 1;
|
|||
|
|
node->priv = priv;
|
|||
|
|
node->data_handler = data_handler;
|
|||
|
|
node->wakeup = wakeup;
|
|||
|
|
node->set_wakeup_handler = set_wakeup_handler;
|
|||
|
|
node->attribute |= attribute;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void a2dp_stream_node_wakeup(void *priv)
|
|||
|
|
{
|
|||
|
|
struct a2dp_stream_node *node = (struct a2dp_stream_node *)priv;
|
|||
|
|
struct a2dp_stream_node *prev_node = node - 1;
|
|||
|
|
while (1) {
|
|||
|
|
if (prev_node->wakeup) {
|
|||
|
|
prev_node->wakeup(prev_node->priv);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
if (prev_node->attribute & A2DP_NODE_BEGIN) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
prev_node--;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void a2dp_stream_node_block_and_remain(struct a2dp_stream_node *node)
|
|||
|
|
{
|
|||
|
|
struct a2dp_stream_node *prev_node = node - 1;
|
|||
|
|
|
|||
|
|
if (node->set_wakeup_handler) {
|
|||
|
|
node->set_wakeup_handler(node->priv, node, a2dp_stream_node_wakeup);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
while (1) {
|
|||
|
|
prev_node->remain = 1;
|
|||
|
|
if ((prev_node->attribute & A2DP_NODE_BEGIN) || (prev_node->attribute & A2DP_NODE_BLOCK)) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
prev_node--;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int a2dp_stream_node_data_handler(void *priv, void *data, int len)
|
|||
|
|
{
|
|||
|
|
struct a2dp_stream_node *node = (struct a2dp_stream_node *)priv;
|
|||
|
|
|
|||
|
|
if (node->attribute & A2DP_NODE_END) {
|
|||
|
|
return len;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
while (1) {
|
|||
|
|
node++;
|
|||
|
|
if ((node->attribute & A2DP_NODE_END)) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!node->data_handler) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((node->attribute & A2DP_NODE_BLOCK)) {
|
|||
|
|
int wlen = node->data_handler(node->priv, data, len);
|
|||
|
|
if (wlen < len) {
|
|||
|
|
a2dp_stream_node_block_and_remain(node);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return wlen;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!node->remain) {
|
|||
|
|
node->data_handler(node->priv, data, len);
|
|||
|
|
}
|
|||
|
|
node->remain = 0;
|
|||
|
|
if (node->attribute & A2DP_NODE_SUB_STREAM_END) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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);
|
|||
|
|
|
|||
|
|
return a2dp_stream_node_data_handler(&dec->stream_node[A2DP_NODE_DECODER], data, len);
|
|||
|
|
#if 0
|
|||
|
|
if (!dec->remain) {
|
|||
|
|
wlen = a2dp_decoder_slience_plc_filter(dec, data, len);
|
|||
|
|
if (wlen < len) {
|
|||
|
|
return wlen;
|
|||
|
|
}
|
|||
|
|
#if (CONFIG_AUDIO_EFFECT_TASK_ENABLE == 0)
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
if (dec->spatial_audio) {
|
|||
|
|
dec->spatial_data_len = spatial_audio_filter(dec->spatial_audio, data, len);
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
#endif/*CONFIG_AUDIO_EFFECT_TASK_ENABLE*/
|
|||
|
|
|
|||
|
|
#if (TCFG_AUDIO_SPATIAL_EFFECT_ENABLE && TCFG_SENSOR_DATA_READ_IN_DEC_TASK)
|
|||
|
|
aud_spatial_sensor_run();
|
|||
|
|
#endif /*TCFG_AUDIO_SPATIAL_EFFECT_ENABLE && TCFG_SENSOR_DATA_READ_IN_DEC_TASK*/
|
|||
|
|
|
|||
|
|
//开空间音效的时候,软件数字音量在空间音效处理后面做
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
if (!spatial_audio_enable) {
|
|||
|
|
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
|||
|
|
audio_digital_vol_run(MUSIC_DVOL, data, len);
|
|||
|
|
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
|||
|
|
}
|
|||
|
|
#endif /*CONFIG_AUDIO_EFFECT_TASK_ENABLE*/
|
|||
|
|
a2dp_surround_vbass_handler(dec, data, len);
|
|||
|
|
|
|||
|
|
#if TCFG_EQ_ENABLE&&TCFG_BT_MUSIC_EQ_ENABLE
|
|||
|
|
if (dec->eq_drc && !dec->eq_drc->async) {//同步方式eq
|
|||
|
|
eq_drc_run(dec->eq_drc, data, len);
|
|||
|
|
}
|
|||
|
|
#endif//TCFG_BT_MUSIC_EQ_ENABLE
|
|||
|
|
|
|||
|
|
a2dp_output_before_syncts_handler(dec, data, len);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if TCFG_AUDIO_ANC_ENABLE && ANC_MUSIC_DYNAMIC_GAIN_EN
|
|||
|
|
audio_anc_music_dynamic_gain_det(data, len);
|
|||
|
|
#endif/*ANC_MUSIC_DYNAMIC_GAIN_EN*/
|
|||
|
|
|
|||
|
|
return a2dp_output_async_data_handler(dec, data, len);
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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;
|
|||
|
|
/*空间音频跑异步任务时,开低延时初始延时需要多10ms*/
|
|||
|
|
#if (TCFG_AUDIO_SPATIAL_EFFECT_ENABLE && CONFIG_AUDIO_EFFECT_TASK_ENABLE)
|
|||
|
|
a2dp_delay_time += 10;
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
} 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
|
|||
|
|
a2dp_stream_node_add(dec, dec, a2dp_output_before_syncts_handler, NULL, NULL, 0);
|
|||
|
|
|
|||
|
|
struct audio_syncts_params params = {0};
|
|||
|
|
params.nch = dec->ch;
|
|||
|
|
params.pcm_device = sound_pcm_sync_device_select();//PCM_INSIDE_DAC;
|
|||
|
|
if (audio_mixer_get_sample_rate(&mixer)) {
|
|||
|
|
params.rout_sample_rate = audio_mixer_get_sample_rate(&mixer);
|
|||
|
|
} else {
|
|||
|
|
params.rout_sample_rate = sound_pcm_match_sample_rate(dec->sample_rate);
|
|||
|
|
}
|
|||
|
|
params.network = AUDIO_NETWORK_BT2_1;
|
|||
|
|
params.rin_sample_rate = dec->sample_rate;
|
|||
|
|
params.priv = (void *)&dec->stream_node[dec->node_num];
|
|||
|
|
params.factor = TIME_US_FACTOR;
|
|||
|
|
params.output = a2dp_stream_node_data_handler;//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) {
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dec->sync_step = 0;
|
|||
|
|
a2dp_stream_node_add(dec, dec->syncts, audio_syncts_frame_filter, NULL, audio_syncts_trigger_resume, A2DP_NODE_BLOCK);
|
|||
|
|
#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;
|
|||
|
|
/*开空间音频没有使用独立任务处理时*/
|
|||
|
|
#if (TCFG_AUDIO_SPATIAL_EFFECT_ENABLE && (!CONFIG_AUDIO_EFFECT_TASK_ENABLE))
|
|||
|
|
u8 ch = 2;
|
|||
|
|
#else
|
|||
|
|
u8 ch = dec->ch;
|
|||
|
|
#endif
|
|||
|
|
dec->plc_ops = get_lfaudioPLC_api();
|
|||
|
|
plc_mem_size = dec->plc_ops->need_buf(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, ch, a2dp_low_latency ? 4 : 0);
|
|||
|
|
a2dp_stream_node_add(dec, dec, a2dp_decoder_slience_plc_filter, NULL, NULL, 0);
|
|||
|
|
#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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int a2dp_decoder_spatial_data_handler(void *priv, void *data, int len)
|
|||
|
|
{
|
|||
|
|
struct a2dp_dec_hdl *dec = (struct a2dp_dec_hdl *)priv;
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
|
|||
|
|
if (!dec->spatial_data_len) {
|
|||
|
|
dec->spatial_data_len = spatial_audio_filter(dec->spatial_audio, data, len);
|
|||
|
|
dec->spatial_data_offset = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (dec->spatial_data_len) {
|
|||
|
|
int wlen = a2dp_stream_node_data_handler(&dec->stream_node[dec->spatial_node_id], (u8 *)data + dec->spatial_data_offset, dec->spatial_data_len);
|
|||
|
|
dec->spatial_data_offset += wlen;
|
|||
|
|
dec->spatial_data_len -= wlen;
|
|||
|
|
|
|||
|
|
return dec->spatial_data_len ? 0 : len;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return 0;
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void a2dp_decoder_spatial_audio_setup(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
if (spatial_audio_enable) {
|
|||
|
|
dec->spatial_audio = spatial_audio_open();
|
|||
|
|
a2dp_spatial_audio_head_tracked_en(spatial_audio_head_tracked);
|
|||
|
|
/*clk_set("sys", SYS_128M);*/
|
|||
|
|
#if CONFIG_TWS_SPATIAL_AUDIO_ENABLE
|
|||
|
|
if (tws_api_get_tws_state() & TWS_STA_SIBLING_CONNECTED) {
|
|||
|
|
spatial_audio_set_mapping_channel(dec->spatial_audio, tws_api_get_local_channel() == 'L' ? AUDIO_CH_L : AUDIO_CH_R);
|
|||
|
|
} else {
|
|||
|
|
spatial_audio_set_mapping_channel(dec->spatial_audio, AUDIO_CH_MIX_MONO);
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
a2dp_stream_node_add(dec, dec, a2dp_decoder_spatial_data_handler, NULL, NULL, A2DP_NODE_BLOCK);
|
|||
|
|
dec->spatial_node_id = dec->node_num - 1;
|
|||
|
|
}
|
|||
|
|
#endif /*TCFG_AUDIO_SPATIAL_EFFECT_ENABLE*/
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void a2dp_stream_read_spatial_audio_setup(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
#if (TCFG_AUDIO_SPATIAL_EFFECT_ENABLE && TCFG_SENSOR_DATA_READ_IN_DEC_TASK)
|
|||
|
|
if (spatial_audio_head_tracked) {
|
|||
|
|
aud_spatial_sensor_init();
|
|||
|
|
a2dp_stream_node_add(dec, NULL, aud_spatial_sensor_run, NULL, NULL, 0);
|
|||
|
|
}
|
|||
|
|
#endif /*TCFG_SENSOR_DATA_READ_IN_DEC_TASK*/
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#if (TCFG_AUDIO_SPATIAL_EFFECT_ENABLE && TCFG_SENSOR_DATA_READ_IN_DEC_TASK)
|
|||
|
|
typedef struct {
|
|||
|
|
s16 sensor_buf[512];
|
|||
|
|
cbuffer_t sensor_cbuf;
|
|||
|
|
u16 period;
|
|||
|
|
u32 next_period;
|
|||
|
|
} aud_sensor_t;
|
|||
|
|
aud_sensor_t *aud_sensor = NULL;
|
|||
|
|
|
|||
|
|
int aud_spatial_sensor_init()
|
|||
|
|
{
|
|||
|
|
int err = 0;
|
|||
|
|
aud_sensor = zalloc(sizeof(aud_sensor_t));
|
|||
|
|
printf("aud_spatial_sensor_init,need bufsize:%d\n", sizeof(aud_sensor_t));
|
|||
|
|
if (aud_sensor) {
|
|||
|
|
cbuf_init(&aud_sensor->sensor_cbuf, aud_sensor->sensor_buf, sizeof(aud_sensor->sensor_buf));
|
|||
|
|
aud_sensor->period = TCFG_SENSOR_DATA_READ_INTERVAL;
|
|||
|
|
aud_sensor->next_period = jiffies;
|
|||
|
|
}
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int aud_spatial_sensor_exit()
|
|||
|
|
{
|
|||
|
|
int err = 0;
|
|||
|
|
printf("aud_spatial_sensor_exit\n");
|
|||
|
|
if (aud_sensor) {
|
|||
|
|
free(aud_sensor);
|
|||
|
|
aud_sensor = NULL;
|
|||
|
|
}
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
extern int spatial_audio_space_data_read(void *data);
|
|||
|
|
/*定时读取传感器数据到cbuf*/
|
|||
|
|
static s16 sensor_tmp_data[320];
|
|||
|
|
int aud_spatial_sensor_run(void *priv, void *data, int len)
|
|||
|
|
{
|
|||
|
|
if (aud_sensor) {
|
|||
|
|
if (time_after(jiffies, aud_sensor->next_period)) {
|
|||
|
|
aud_sensor->next_period = jiffies + msecs_to_jiffies(aud_sensor->period);
|
|||
|
|
int len = spatial_audio_space_data_read(sensor_tmp_data);
|
|||
|
|
if (len) {
|
|||
|
|
int wlen = cbuf_write(&aud_sensor->sensor_cbuf, sensor_tmp_data, len);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return len;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*从cbuf读取传感器数据*/
|
|||
|
|
int aud_spatial_sensor_data_read(s16 *data, int len)
|
|||
|
|
{
|
|||
|
|
int rlen = 0;
|
|||
|
|
if (aud_sensor) {
|
|||
|
|
local_irq_disable();
|
|||
|
|
rlen = cbuf_read(&aud_sensor->sensor_cbuf, data, len);
|
|||
|
|
local_irq_enable();
|
|||
|
|
}
|
|||
|
|
return rlen;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*当前传感器数据量*/
|
|||
|
|
int aud_spatial_sensor_get_data_len()
|
|||
|
|
{
|
|||
|
|
int len = 0;
|
|||
|
|
if (aud_sensor) {
|
|||
|
|
len = cbuf_get_data_len(&aud_sensor->sensor_cbuf);
|
|||
|
|
}
|
|||
|
|
return len;
|
|||
|
|
}
|
|||
|
|
#endif /*(TCFG_AUDIO_SPATIAL_EFFECT_ENABLE && TCFG_SENSOR_DATA_READ_IN_DEC_TASK)*/
|
|||
|
|
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
|
|||
|
|
#if TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN
|
|||
|
|
#define AUD_EFFECT_INBUF_POINTS 1024
|
|||
|
|
#else
|
|||
|
|
#define AUD_EFFECT_INBUF_POINTS 512
|
|||
|
|
#endif /*TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN*/
|
|||
|
|
#define AUD_EFFECT_INBUF_SIZE (AUD_EFFECT_INBUF_POINTS << 1)
|
|||
|
|
#define A2DP_AUDIO_EFFECT_RUN 1
|
|||
|
|
#define A2DP_AUDIO_EFFECT_FLUSH 2
|
|||
|
|
#define A2DP_AUDIO_EFFECT_STOP 3
|
|||
|
|
#define AUDIO_EFFECT_RUN_LEN (AUD_EFFECT_INBUF_SIZE / 2)
|
|||
|
|
typedef struct {
|
|||
|
|
volatile u8 run_flush;
|
|||
|
|
volatile u8 need_wakeup;
|
|||
|
|
u8 output;
|
|||
|
|
u8 trigger_resume;
|
|||
|
|
u8 *buff;
|
|||
|
|
u16 remain;
|
|||
|
|
u16 run_len;
|
|||
|
|
s16 *run_buff;
|
|||
|
|
s16 output_buf[AUD_EFFECT_INBUF_POINTS];
|
|||
|
|
s16 source_buf[AUD_EFFECT_INBUF_POINTS * 2];
|
|||
|
|
cbuffer_t source_cbuf;
|
|||
|
|
cbuffer_t output_cbuf;
|
|||
|
|
void *entry_stream;
|
|||
|
|
int (*entry_data_handler)(void *priv, void *data, int len);
|
|||
|
|
void *output_priv;
|
|||
|
|
int (*output_data_handler)(void *priv, void *data, int len);
|
|||
|
|
void *resume_priv;
|
|||
|
|
void (*resume_handler)(void *priv);
|
|||
|
|
void *parent;
|
|||
|
|
spinlock_t lock;
|
|||
|
|
|
|||
|
|
} aud_effect_t;
|
|||
|
|
/*aud_effect_t *aud_effect = NULL;*/
|
|||
|
|
|
|||
|
|
int aud_effect_push_data(void *priv, s16 *data, u16 len)
|
|||
|
|
{
|
|||
|
|
int wlen = 0;
|
|||
|
|
aud_effect_t *effect = (struct aud_effect *)priv;
|
|||
|
|
|
|||
|
|
if (!effect) {
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*数据写入音效task的音源缓冲*/
|
|||
|
|
wlen = cbuf_write(&effect->source_cbuf, data, len);
|
|||
|
|
if (wlen < len) {
|
|||
|
|
spin_lock(&effect->lock);
|
|||
|
|
effect->trigger_resume = 1;
|
|||
|
|
spin_unlock(&effect->lock);
|
|||
|
|
} else {
|
|||
|
|
os_taskq_post_msg("aud_effect", 2, A2DP_AUDIO_EFFECT_RUN, (int)effect);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
do {
|
|||
|
|
if (!effect->output) { /*还未确认运行数据长度*/
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!effect->remain) {
|
|||
|
|
effect->run_len = AUDIO_EFFECT_RUN_LEN;
|
|||
|
|
/*上次输出缓冲已消耗完*/
|
|||
|
|
if (cbuf_get_data_len(&effect->output_cbuf) < effect->run_len) {
|
|||
|
|
/*输出缓冲不足一次音效run的长度,这里主要使用音效一次run的长度作为运行的基数*/
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
/*复用循环buffer作为输出的缓冲暂存*/
|
|||
|
|
effect->buff = cbuf_get_readptr(&effect->output_cbuf);
|
|||
|
|
effect->remain = effect->run_len;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int output_len = effect->output_data_handler(effect->output_priv, effect->buff, effect->remain);
|
|||
|
|
/*printf("%d - %d\n", effect->remain, output_len);*/
|
|||
|
|
effect->remain -= output_len;
|
|||
|
|
effect->buff += output_len;
|
|||
|
|
if (effect->remain) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
/*输出完成后更新读指针并刷新至音效task*/
|
|||
|
|
cbuf_read_updata(&effect->output_cbuf, effect->run_len);
|
|||
|
|
spin_lock(&effect->lock);
|
|||
|
|
if (effect->need_wakeup) {
|
|||
|
|
effect->need_wakeup = 0;
|
|||
|
|
os_taskq_post_msg("aud_effect", 2, A2DP_AUDIO_EFFECT_RUN, (int)effect);
|
|||
|
|
}
|
|||
|
|
spin_unlock(&effect->lock);
|
|||
|
|
} while (1);
|
|||
|
|
|
|||
|
|
return wlen;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void aud_effect_data_flush(aud_effect_t *effect)
|
|||
|
|
{
|
|||
|
|
if (effect->need_wakeup) {
|
|||
|
|
effect->need_wakeup = 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int aud_effect_output_data_handler(void *priv, void *data, int len)
|
|||
|
|
{
|
|||
|
|
aud_effect_t *effect = (aud_effect_t *)priv;
|
|||
|
|
if (!effect->output) {
|
|||
|
|
effect->output = 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
spin_lock(&effect->lock);
|
|||
|
|
if (!cbuf_is_write_able(&effect->output_cbuf, len)) {
|
|||
|
|
effect->need_wakeup = 1;
|
|||
|
|
spin_unlock(&effect->lock);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
spin_unlock(&effect->lock);
|
|||
|
|
|
|||
|
|
return cbuf_write(&effect->output_cbuf, data, len);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void aud_effect_data_handler(void *priv)
|
|||
|
|
{
|
|||
|
|
aud_effect_t *effect = (aud_effect_t *)priv;
|
|||
|
|
struct a2dp_dec_hdl *dec = (struct a2dp_dec_hdl *)effect->parent;
|
|||
|
|
|
|||
|
|
if (!effect) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
while (1) {
|
|||
|
|
if (cbuf_get_data_len(&effect->source_cbuf) < AUDIO_EFFECT_RUN_LEN) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
effect->run_buff = cbuf_get_readptr(&effect->source_cbuf);
|
|||
|
|
int run_len = AUDIO_EFFECT_RUN_LEN;
|
|||
|
|
|
|||
|
|
#if 0
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
if (dec->spatial_audio) {
|
|||
|
|
run_len = spatial_audio_filter(dec->spatial_audio, effect->run_buff, run_len);
|
|||
|
|
}
|
|||
|
|
#endif /*TCFG_AUDIO_SPATIAL_EFFECT_ENABLE*/
|
|||
|
|
#else
|
|||
|
|
if (effect->entry_data_handler) {
|
|||
|
|
run_len = effect->entry_data_handler(effect->entry_stream, effect->run_buff, run_len);
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
/*run_len = aud_effect_output_data_handler(effect, effect->run_buff, run_len);*/
|
|||
|
|
/*printf("run : %d\n", run_len);*/
|
|||
|
|
cbuf_read_updata(&effect->source_cbuf, run_len);
|
|||
|
|
spin_lock(&effect->lock);
|
|||
|
|
if (effect->trigger_resume) {
|
|||
|
|
if (run_len) {
|
|||
|
|
effect->resume_handler(effect->resume_priv);
|
|||
|
|
}
|
|||
|
|
effect->trigger_resume = 0;
|
|||
|
|
}
|
|||
|
|
spin_unlock(&effect->lock);
|
|||
|
|
if (run_len < AUDIO_EFFECT_RUN_LEN) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void aud_effect_task(void *p)
|
|||
|
|
{
|
|||
|
|
printf("===Audio Effect Task===\n");
|
|||
|
|
int wlen = 0;
|
|||
|
|
u8 pend = 1;
|
|||
|
|
int msg[16];
|
|||
|
|
int res;
|
|||
|
|
|
|||
|
|
while (1) {
|
|||
|
|
if (pend) {
|
|||
|
|
res = os_taskq_pend("taskq", msg, ARRAY_SIZE(msg));
|
|||
|
|
} else {
|
|||
|
|
res = os_taskq_accept(ARRAY_SIZE(msg), msg);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (res == OS_TASKQ) {
|
|||
|
|
switch (msg[1]) {
|
|||
|
|
case A2DP_AUDIO_EFFECT_RUN:
|
|||
|
|
aud_effect_data_handler((void *)msg[2]);
|
|||
|
|
break;
|
|||
|
|
case A2DP_AUDIO_EFFECT_FLUSH:
|
|||
|
|
aud_effect_data_flush((aud_effect_t *)msg[2]);
|
|||
|
|
break;
|
|||
|
|
case A2DP_AUDIO_EFFECT_STOP:
|
|||
|
|
os_taskq_flush();
|
|||
|
|
os_sem_post((OS_SEM *)msg[2]);
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endif /*CONFIG_AUDIO_EFFECT_TASK_ENABLE*/
|
|||
|
|
|
|||
|
|
int a2dp_decoder_effect_task_enter(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
int err = 0;
|
|||
|
|
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
aud_effect_t *aud_effect = zalloc(sizeof(aud_effect_t));
|
|||
|
|
printf("audio_dec_effect_process_start,need bufsize:%d\n", sizeof(aud_effect_t));
|
|||
|
|
if (aud_effect) {
|
|||
|
|
cbuf_init(&aud_effect->source_cbuf, aud_effect->source_buf, sizeof(aud_effect->source_buf));
|
|||
|
|
cbuf_init(&aud_effect->output_cbuf, aud_effect->output_buf, sizeof(aud_effect->output_buf));
|
|||
|
|
err = task_create(aud_effect_task, NULL, "aud_effect");
|
|||
|
|
|
|||
|
|
aud_effect->parent = (void *)dec;
|
|||
|
|
aud_effect->resume_handler = (void (*)(void *))a2dp_stream_node_wakeup;
|
|||
|
|
aud_effect->resume_priv = (void *)&dec->stream_node[dec->node_num];
|
|||
|
|
aud_effect->entry_stream = (void *)&dec->stream_node[dec->node_num];
|
|||
|
|
aud_effect->entry_data_handler = a2dp_stream_node_data_handler;
|
|||
|
|
spin_lock_init(&aud_effect->lock);
|
|||
|
|
|
|||
|
|
dec->aud_effect = aud_effect;
|
|||
|
|
a2dp_stream_node_add(dec, aud_effect, aud_effect_push_data, NULL, NULL, A2DP_NODE_SUB_STREAM_BEGIN | A2DP_NODE_BLOCK);
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int a2dp_decoder_effect_task_output(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
if (dec->aud_effect) {
|
|||
|
|
aud_effect_t *aud_effect = (aud_effect_t *)dec->aud_effect;
|
|||
|
|
aud_effect->output_data_handler = a2dp_stream_node_data_handler;
|
|||
|
|
aud_effect->output_priv = (void *)&dec->stream_node[dec->node_num];
|
|||
|
|
a2dp_stream_node_add(dec, aud_effect, aud_effect_output_data_handler, NULL, NULL, A2DP_NODE_SUB_STREAM_END | A2DP_NODE_BLOCK);
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int audio_effect_process_stop(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
int err = 0;
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
aud_effect_t *aud_effect = (aud_effect_t *)dec->aud_effect;
|
|||
|
|
printf("audio_effect_process_stop\n");
|
|||
|
|
if (aud_effect) {
|
|||
|
|
OS_SEM *sem = (OS_SEM *)malloc(sizeof(OS_SEM));
|
|||
|
|
os_sem_create(sem, 0);
|
|||
|
|
/*处理消息队列满了的情况*/
|
|||
|
|
while (os_taskq_post_msg("aud_effect", 2, A2DP_AUDIO_EFFECT_STOP, (int)sem)) {
|
|||
|
|
os_time_dly(2);
|
|||
|
|
}
|
|||
|
|
os_sem_pend(sem, 0);
|
|||
|
|
free(sem);
|
|||
|
|
task_kill("aud_effect");
|
|||
|
|
free(aud_effect);
|
|||
|
|
aud_effect = NULL;
|
|||
|
|
}
|
|||
|
|
dec->aud_effect = NULL;
|
|||
|
|
#endif
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int a2dp_decoder_volume_run(void *priv, void *data, int len)
|
|||
|
|
{
|
|||
|
|
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
|||
|
|
audio_digital_vol_run((u8)priv, data, len);
|
|||
|
|
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
|||
|
|
return len;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void a2dp_decoder_volume_setup(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
app_audio_state_switch(APP_AUDIO_STATE_MUSIC, get_max_sys_vol());
|
|||
|
|
|
|||
|
|
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
|||
|
|
audio_digital_vol_open(MUSIC_DVOL, app_audio_get_volume(APP_AUDIO_STATE_MUSIC), MUSIC_DVOL_MAX, MUSIC_DVOL_FS, -1);
|
|||
|
|
a2dp_stream_node_add(dec, (void *)MUSIC_DVOL, a2dp_decoder_volume_run, NULL, NULL, 0);
|
|||
|
|
#endif/*SYS_VOL_TYPE == VOL_TYPE_DIGITAL*/
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
static int a2dp_anc_dynamic_gain_handler(void *priv, void *data, int len)
|
|||
|
|
{
|
|||
|
|
#if TCFG_AUDIO_ANC_ENABLE && ANC_MUSIC_DYNAMIC_GAIN_EN
|
|||
|
|
audio_anc_music_dynamic_gain_det(data, len);
|
|||
|
|
#endif/*ANC_MUSIC_DYNAMIC_GAIN_EN*/
|
|||
|
|
return len;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void a2dp_anc_dynamic_gain_setup(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
#if TCFG_AUDIO_ANC_ENABLE && ANC_MUSIC_DYNAMIC_GAIN_EN
|
|||
|
|
audio_anc_music_dynamic_gain_init(dec->sample_rate);
|
|||
|
|
a2dp_stream_node_add(dec, 0, a2dp_anc_dynamic_gain_handler, NULL, NULL, 0);
|
|||
|
|
#endif/*ANC_MUSIC_DYNAMIC_GAIN_EN*/
|
|||
|
|
}
|
|||
|
|
static int a2dp_surround_vbass_handler(struct a2dp_dec_hdl *dec, void *data, int len);
|
|||
|
|
static void a2dp_surround_vbass_setup(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
#if AUDIO_SURROUND_CONFIG
|
|||
|
|
dec->sur = audio_surround_setup(dec->channel, a2dp_surround_eff);
|
|||
|
|
#endif//AUDIO_SURROUND_CONFIG
|
|||
|
|
|
|||
|
|
#if AUDIO_VBASS_CONFIG
|
|||
|
|
dec->vbass = audio_vbass_setup(dec->sample_rate, dec->ch);
|
|||
|
|
#endif//AUDIO_VBASS_CONFIG
|
|||
|
|
|
|||
|
|
#if AUDIO_SURROUND_CONFIG || AUDIO_VBASS_CONFIG
|
|||
|
|
a2dp_stream_node_add(dec, dec, a2dp_surround_vbass_handler, NULL, NULL, 0);
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int a2dp_surround_vbass_handler(struct a2dp_dec_hdl *dec, void *data, int len)
|
|||
|
|
{
|
|||
|
|
#if AUDIO_SURROUND_CONFIG
|
|||
|
|
if (dec->sur && dec->sur->surround) {
|
|||
|
|
audio_surround_run(dec->sur->surround, data, len);
|
|||
|
|
}
|
|||
|
|
#endif/*AUDIO_SURROUND_CONFIG*/
|
|||
|
|
|
|||
|
|
#if AUDIO_VBASS_CONFIG
|
|||
|
|
if (dec->vbass) {
|
|||
|
|
if (!dec->vbass->status) {
|
|||
|
|
struct virtual_bass_tool_set *parm = get_vbass_parm();
|
|||
|
|
GainProcess_16Bit(data, data, powf(10, parm->prev_gain / 20.0f), dec->ch, (dec->ch == 1 ? 1 : 2), (dec->ch == 1 ? 1 : 2), (len >> 1) / dec->ch);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
audio_vbass_run(dec->vbass, data, len);
|
|||
|
|
}
|
|||
|
|
#endif/*AUDIO_VBASS_CONFIG*/
|
|||
|
|
|
|||
|
|
return len;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void a2dp_eq_drc_setup(struct a2dp_dec_hdl *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
|
|||
|
|
|
|||
|
|
a2dp_decoder_sample_detect_setup(dec);
|
|||
|
|
int async = 1;
|
|||
|
|
#if defined(AUDIO_GAME_EFFECT_CONFIG) && AUDIO_GAME_EFFECT_CONFIG
|
|||
|
|
if (a2dp_low_latency) {
|
|||
|
|
drc_en |= BIT(1);
|
|||
|
|
async = 0;
|
|||
|
|
}
|
|||
|
|
#endif /*AUDIO_GAME_EFFECT_CONFIG*/
|
|||
|
|
|
|||
|
|
#if defined(AUDIO_SPK_EQ_CONFIG) && AUDIO_SPK_EQ_CONFIG
|
|||
|
|
async = 0;
|
|||
|
|
#endif
|
|||
|
|
u32 sample_rate = dec->sample_rate;
|
|||
|
|
if (audio_mixer_get_sample_rate(&mixer)) {
|
|||
|
|
sample_rate = audio_mixer_get_sample_rate(&mixer);
|
|||
|
|
}
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
if (1) {//spatial_audio_enable) {
|
|||
|
|
/*audio_effect_process_start(dec, &dec->mix_ch, audio_mixer_ch_write);*/
|
|||
|
|
dec->eq_drc = dec_eq_drc_setup((void *)&dec->stream_node[dec->node_num], (eq_output_cb)a2dp_stream_node_data_handler, sample_rate, dec->ch, async, drc_en);
|
|||
|
|
a2dp_stream_node_add(dec, dec->eq_drc, eq_drc_run, NULL, NULL, async ? A2DP_NODE_BLOCK : 0);
|
|||
|
|
} else
|
|||
|
|
#endif/*CONFIG_AUDIO_EFFECT_TASK_ENABLE*/
|
|||
|
|
{
|
|||
|
|
dec->eq_drc = dec_eq_drc_setup((void *)&dec->stream_node[dec->node_num], (eq_output_cb)a2dp_stream_node_data_handler, sample_rate, dec->ch, async, drc_en);
|
|||
|
|
a2dp_stream_node_add(dec, dec->eq_drc, eq_drc_run, NULL, NULL, async ? A2DP_NODE_BLOCK : 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endif//TCFG_BT_MUSIC_EQ_ENABLE
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void a2dp_bass_bost_setup(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
#if defined(TCFG_AUDIO_BASS_BOOST)&&TCFG_AUDIO_BASS_BOOST
|
|||
|
|
dec->bass_boost = audio_bass_boost_open(dec->sample_rate, dec->ch);
|
|||
|
|
a2dp_stream_node_add(dec, dec->bass_boost, audio_bass_boost_run, NULL, NULL, 0);
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int a2dp_effect_develop_setup(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
#if ((defined TCFG_EFFECT_DEVELOP_ENABLE) && TCFG_EFFECT_DEVELOP_ENABLE)
|
|||
|
|
dec->effect_develop = audio_effect_develop_open(dec->sample_rate, dec->ch, 16);
|
|||
|
|
a2dp_stream_node_add(dec, dec->effect_develop, audio_effect_develop_data_handler, NULL, NULL, 0);
|
|||
|
|
#endif
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void a2dp_effect_develop_close(struct a2dp_dec_hdl *dec)
|
|||
|
|
{
|
|||
|
|
#if ((defined TCFG_EFFECT_DEVELOP_ENABLE) && TCFG_EFFECT_DEVELOP_ENABLE)
|
|||
|
|
if (dec->effect_develop) {
|
|||
|
|
audio_effect_develop_close(dec->effect_develop);
|
|||
|
|
dec->effect_develop = NULL;
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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 (a2dp_dec->coding_type == AUDIO_CODING_AAC) {
|
|||
|
|
audio_overlay_load_code(a2dp_dec->coding_type);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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 (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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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_dec_set_output_channel(dec);
|
|||
|
|
|
|||
|
|
a2dp_stream_node_init(dec);
|
|||
|
|
|
|||
|
|
a2dp_decoder_delay_time_setup(dec);
|
|||
|
|
|
|||
|
|
a2dp_decoder_plc_setup(dec);
|
|||
|
|
|
|||
|
|
#if !CONFIG_AUDIO_EFFECT_TASK_ENABLE /*当空间音效不在异步task中时放在解码后第二个节点效率最高*/
|
|||
|
|
a2dp_decoder_spatial_audio_setup(dec);
|
|||
|
|
#endif
|
|||
|
|
a2dp_stream_read_spatial_audio_setup(dec);
|
|||
|
|
|
|||
|
|
set_source_sample_rate(fmt->sample_rate);
|
|||
|
|
|
|||
|
|
/*sound_pcm_set_underrun_params(3, dec, NULL);//a2dp_stream_underrun_feedback);*/
|
|||
|
|
|
|||
|
|
audio_mixer_ch_open(&dec->mix_ch, &mixer);
|
|||
|
|
audio_mixer_ch_set_sample_rate(&dec->mix_ch, A2DP_SOUND_SAMPLE_RATE ? A2DP_SOUND_SAMPLE_RATE : sound_pcm_match_sample_rate(fmt->sample_rate));
|
|||
|
|
|
|||
|
|
#if !CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
a2dp_decoder_volume_setup(dec);
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
a2dp_effect_develop_setup(dec);
|
|||
|
|
|
|||
|
|
a2dp_surround_vbass_setup(dec);
|
|||
|
|
a2dp_bass_bost_setup(dec);
|
|||
|
|
|
|||
|
|
a2dp_anc_dynamic_gain_setup(dec);
|
|||
|
|
|
|||
|
|
a2dp_decoder_syncts_setup(dec);
|
|||
|
|
|
|||
|
|
a2dp_eq_drc_setup(dec);
|
|||
|
|
|
|||
|
|
a2dp_decoder_effect_task_enter(dec); /*enter到output的节点全部在effect task中执行*/
|
|||
|
|
|
|||
|
|
#if CONFIG_AUDIO_EFFECT_TASK_ENABLE
|
|||
|
|
a2dp_decoder_spatial_audio_setup(dec);
|
|||
|
|
|
|||
|
|
a2dp_decoder_volume_setup(dec); /*数字音量处理放在空间音频之后处理*/
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
a2dp_decoder_effect_task_output(dec);
|
|||
|
|
|
|||
|
|
a2dp_stream_node_add(dec, &dec->mix_ch, (int (*)(void *, void *, int))audio_mixer_ch_write, NULL, NULL, A2DP_NODE_BLOCK);
|
|||
|
|
audio_mixer_ch_set_resume_handler(&dec->mix_ch, &dec->stream_node[dec->node_num - 1], a2dp_stream_node_wakeup);
|
|||
|
|
|
|||
|
|
a2dp_drop_frame_stop();
|
|||
|
|
dec->remain = 0;
|
|||
|
|
audio_codec_clock_set(AUDIO_A2DP_MODE, dec->coding_type, dec->wait.preemption);
|
|||
|
|
/* dec->state = A2DP_STREAM_START; */
|
|||
|
|
|
|||
|
|
err = audio_decoder_start(&dec->decoder);
|
|||
|
|
if (err) {
|
|||
|
|
goto __err3;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (get_sniff_out_status()) {
|
|||
|
|
clear_sniff_out_status();
|
|||
|
|
}
|
|||
|
|
sound_pcm_dev_try_power_on();
|
|||
|
|
dec->start = 1;
|
|||
|
|
|
|||
|
|
return 0;
|
|||
|
|
|
|||
|
|
__err3:
|
|||
|
|
audio_mixer_ch_close(&a2dp_dec->mix_ch);
|
|||
|
|
__err2:
|
|||
|
|
audio_decoder_close(&dec->decoder);
|
|||
|
|
__err1:
|
|||
|
|
a2dp_dec_release();
|
|||
|
|
|
|||
|
|
return err;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int a2dp_dut_dec_start(void)
|
|||
|
|
{
|
|||
|
|
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_dut_dec_start: in\n");
|
|||
|
|
|
|||
|
|
if (a2dp_dec->coding_type == AUDIO_CODING_AAC) {
|
|||
|
|
audio_overlay_load_code(a2dp_dec->coding_type);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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 (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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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_dec_set_output_channel(dec);
|
|||
|
|
a2dp_stream_node_init(dec);
|
|||
|
|
|
|||
|
|
a2dp_decoder_delay_time_setup(dec);
|
|||
|
|
|
|||
|
|
a2dp_decoder_plc_setup(dec);
|
|||
|
|
|
|||
|
|
set_source_sample_rate(fmt->sample_rate);
|
|||
|
|
|
|||
|
|
audio_mixer_ch_open(&dec->mix_ch, &mixer);
|
|||
|
|
audio_mixer_ch_set_sample_rate(&dec->mix_ch, A2DP_SOUND_SAMPLE_RATE ? A2DP_SOUND_SAMPLE_RATE : sound_pcm_match_sample_rate(fmt->sample_rate));
|
|||
|
|
|
|||
|
|
//dut 需添加数字音量处理
|
|||
|
|
a2dp_decoder_volume_setup(dec);
|
|||
|
|
|
|||
|
|
#if TCFG_AUDIO_CVP_DUT_ENABLE
|
|||
|
|
if (audio_dec_dut_en_get(0) == AUDIO_DEC_DUT_MODE_SPK_EQ) {
|
|||
|
|
a2dp_eq_drc_setup(dec);
|
|||
|
|
}
|
|||
|
|
#endif/*TCFG_AUDIO_CVP_DUT_ENABLE*/
|
|||
|
|
|
|||
|
|
a2dp_decoder_syncts_setup(dec);
|
|||
|
|
|
|||
|
|
a2dp_stream_node_add(dec, &dec->mix_ch, (int (*)(void *, void *, int))audio_mixer_ch_write, NULL, NULL, A2DP_NODE_BLOCK);
|
|||
|
|
audio_mixer_ch_set_resume_handler(&dec->mix_ch, &dec->stream_node[dec->node_num - 1], a2dp_stream_node_wakeup);
|
|||
|
|
dec->remain = 0;
|
|||
|
|
audio_codec_clock_set(AUDIO_A2DP_MODE, dec->coding_type, dec->wait.preemption);
|
|||
|
|
|
|||
|
|
err = audio_decoder_start(&dec->decoder);
|
|||
|
|
if (err) {
|
|||
|
|
goto __err3;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
sound_pcm_dev_try_power_on();
|
|||
|
|
dec->start = 1;
|
|||
|
|
|
|||
|
|
return 0;
|
|||
|
|
|
|||
|
|
__err3:
|
|||
|
|
audio_mixer_ch_close(&a2dp_dec->mix_ch);
|
|||
|
|
__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);
|
|||
|
|
audio_effect_process_stop(a2dp_dec);
|
|||
|
|
audio_mixer_ch_close(&a2dp_dec->mix_ch);
|
|||
|
|
a2dp_decoder_syncts_free(a2dp_dec);
|
|||
|
|
a2dp_decoder_plc_free(a2dp_dec);
|
|||
|
|
a2dp_effect_develop_close(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 TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
if (a2dp_dec->spatial_audio) {
|
|||
|
|
spatial_audio_close(a2dp_dec->spatial_audio);
|
|||
|
|
a2dp_dec->spatial_audio = NULL;
|
|||
|
|
}
|
|||
|
|
#if TCFG_SENSOR_DATA_READ_IN_DEC_TASK
|
|||
|
|
aud_spatial_sensor_exit();
|
|||
|
|
#endif /*TCFG_SENSOR_DATA_READ_IN_DEC_TASK*/
|
|||
|
|
#endif /*TCFG_AUDIO_SPATIAL_EFFECT_ENABLE*/
|
|||
|
|
|
|||
|
|
#if AUDIO_SURROUND_CONFIG
|
|||
|
|
if (a2dp_dec->sur) {
|
|||
|
|
audio_surround_free(a2dp_dec->sur);
|
|||
|
|
a2dp_dec->sur = NULL;
|
|||
|
|
}
|
|||
|
|
#endif//AUDIO_SURROUND_CONFIG
|
|||
|
|
|
|||
|
|
#if AUDIO_VBASS_CONFIG
|
|||
|
|
if (a2dp_dec->vbass) {
|
|||
|
|
audio_vbass_free(a2dp_dec->vbass);
|
|||
|
|
a2dp_dec->vbass = NULL;
|
|||
|
|
}
|
|||
|
|
#endif//AUDIO_VBASS_CONFIG
|
|||
|
|
|
|||
|
|
#if defined(TCFG_AUDIO_BASS_BOOST) && TCFG_AUDIO_BASS_BOOST
|
|||
|
|
if (a2dp_dec->bass_boost) {
|
|||
|
|
audio_bass_boost_close(a2dp_dec->bass_boost);
|
|||
|
|
a2dp_dec->bass_boost = NULL;
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
#if (SYS_VOL_TYPE == VOL_TYPE_DIGITAL)
|
|||
|
|
audio_digital_vol_close(MUSIC_DVOL);
|
|||
|
|
#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;
|
|||
|
|
|
|||
|
|
if (event == AUDIO_RES_GET) {
|
|||
|
|
printf("a2dp_res:Get\n");
|
|||
|
|
if (a2dp_dec->dut_enable) {
|
|||
|
|
err = a2dp_dut_dec_start();
|
|||
|
|
} else {
|
|||
|
|
err = a2dp_dec_start();
|
|||
|
|
}
|
|||
|
|
} else if (event == AUDIO_RES_PUT) {
|
|||
|
|
printf("a2dp_res:Put\n");
|
|||
|
|
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
|
|||
|
|
#define A2DP_CODEC_LDAC 0x0B
|
|||
|
|
int __a2dp_dec_open(int media_type, u8 resume, u8 dut_enable)
|
|||
|
|
{
|
|||
|
|
#if (TCFG_AUDIO_HEARING_AID_ENABLE && TCFG_AUDIO_DHA_AND_MUSIC_MUTEX)
|
|||
|
|
/*辅听与播歌互斥时,关闭辅听*/
|
|||
|
|
if (get_hearing_aid_fitting_state() == 0) {
|
|||
|
|
audio_hearing_aid_suspend();
|
|||
|
|
}
|
|||
|
|
#endif/*TCFG_AUDIO_HEARING_AID_ENABLE*/
|
|||
|
|
|
|||
|
|
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 (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;
|
|||
|
|
case A2DP_CODEC_MPEG24:
|
|||
|
|
printf("a2dp_media_type:AAC");
|
|||
|
|
dec->coding_type = AUDIO_CODING_AAC;
|
|||
|
|
break;
|
|||
|
|
case A2DP_CODEC_LDAC:
|
|||
|
|
printf("a2dp_media_type:LDAC");
|
|||
|
|
dec->coding_type = AUDIO_CODING_LDAC;
|
|||
|
|
break;
|
|||
|
|
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;
|
|||
|
|
dec->dut_enable = dut_enable;
|
|||
|
|
audio_decoder_task_add_wait(&decode_task, &dec->wait);
|
|||
|
|
|
|||
|
|
|
|||
|
|
#if (TCFG_AUDIO_HEARING_AID_ENABLE && TCFG_AUDIO_DHA_FITTING_ENABLE)
|
|||
|
|
/*辅听验配过程不允许播放歌曲*/
|
|||
|
|
extern u8 get_hearing_aid_fitting_state(void);
|
|||
|
|
if (get_hearing_aid_fitting_state()) {
|
|||
|
|
printf("hearing aid fitting : %d\n !!!", get_hearing_aid_fitting_state());
|
|||
|
|
extern a2dp_tws_dec_suspend(void *p);
|
|||
|
|
a2dp_tws_dec_suspend(NULL);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
#endif /*TCFG_AUDIO_HEARING_AID_ENABLE && TCFG_AUDIO_DHA_FITTING_ENABLE*/
|
|||
|
|
if (a2dp_dec && (a2dp_dec->start == 0)) {
|
|||
|
|
a2dp_drop_frame_start();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int a2dp_dec_open(int media_type)
|
|||
|
|
{
|
|||
|
|
#if TCFG_AUDIO_SPEAK_TO_CHAT_ENABLE
|
|||
|
|
if (get_speak_to_chat_state() == AUDIO_ADT_CHAT) {
|
|||
|
|
audio_speak_to_char_sync_suspend();
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
return __a2dp_dec_open(media_type, 0, 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int a2dp_dut_dec_open(int media_type)
|
|||
|
|
{
|
|||
|
|
return __a2dp_dec_open(media_type, 0, 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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();
|
|||
|
|
|
|||
|
|
#if TCFG_AUDIO_ANC_ENABLE && ANC_MUSIC_DYNAMIC_GAIN_EN
|
|||
|
|
audio_anc_music_dynamic_gain_reset(1);
|
|||
|
|
#endif/*ANC_MUSIC_DYNAMIC_GAIN_EN*/
|
|||
|
|
#if (TCFG_AUDIO_HEARING_AID_ENABLE && TCFG_AUDIO_DHA_AND_MUSIC_MUTEX)
|
|||
|
|
audio_hearing_aid_resume();
|
|||
|
|
#endif/*TCFG_AUDIO_HEARING_AID_ENABLE*/
|
|||
|
|
|
|||
|
|
puts("a2dp_dec_close: exit\n");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void a2dp_dec_dut_enable(u8 dut_enable)
|
|||
|
|
{
|
|||
|
|
if (!a2dp_dec) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (a2dp_dec->start) {
|
|||
|
|
a2dp_dec_close();
|
|||
|
|
if (dut_enable) {
|
|||
|
|
a2dp_dut_dec_open(0);
|
|||
|
|
} else {
|
|||
|
|
a2dp_dec_open(0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//*********************************************************************************//
|
|||
|
|
// 空间音效接口(Spatial Effect APIs) //
|
|||
|
|
//*********************************************************************************//
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
int a2dp_spatial_audio_open(void)
|
|||
|
|
{
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
spatial_audio_enable = 1;
|
|||
|
|
if (a2dp_dec && !a2dp_dec->spatial_audio) {
|
|||
|
|
a2dp_dec->spatial_audio = spatial_audio_open();
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void a2dp_spatial_audio_close(void)
|
|||
|
|
{
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
local_irq_disable();
|
|||
|
|
if (a2dp_dec && a2dp_dec->spatial_audio) {
|
|||
|
|
spatial_audio_close(a2dp_dec->spatial_audio);
|
|||
|
|
a2dp_dec->spatial_audio = NULL;
|
|||
|
|
}
|
|||
|
|
spatial_audio_enable = 0;
|
|||
|
|
spatial_audio_head_tracked = 0;
|
|||
|
|
local_irq_enable();
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
u8 get_a2dp_spatial_audio_status(void)
|
|||
|
|
{
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
if (a2dp_dec) {
|
|||
|
|
return (a2dp_dec->spatial_audio != NULL) ? 1 : 0;
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
u8 get_spatial_audio_enable()
|
|||
|
|
{
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
return spatial_audio_enable;
|
|||
|
|
#endif
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void a2dp_spatial_audio_head_tracked_en(u8 en)
|
|||
|
|
{
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
if (a2dp_dec && a2dp_dec->spatial_audio) {
|
|||
|
|
spatial_audio_head_tracked = en;
|
|||
|
|
spatial_audio_head_tracked_en(a2dp_dec->spatial_audio, en);
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
u8 get_a2dp_spatial_audio_head_tracked(void)
|
|||
|
|
{
|
|||
|
|
#if TCFG_AUDIO_SPATIAL_EFFECT_ENABLE
|
|||
|
|
return spatial_audio_head_tracked;
|
|||
|
|
#endif
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void a2dp_spatial_audio_setup(u8 en, u8 head_track)
|
|||
|
|
{
|
|||
|
|
spatial_audio_enable = en;
|
|||
|
|
spatial_audio_head_tracked = head_track;
|
|||
|
|
}
|
|||
|
|
int a2dp_tws_dec_suspend(void *p);
|
|||
|
|
void a2dp_tws_dec_resume(void);
|
|||
|
|
/*
|
|||
|
|
空间音效模式切换
|
|||
|
|
mode:
|
|||
|
|
0:关闭头部跟踪
|
|||
|
|
1:打开空间音效
|
|||
|
|
2:打开头部跟踪
|
|||
|
|
tone_play:
|
|||
|
|
1 : 切换时播放提示音
|
|||
|
|
0 : 不播放提示音
|
|||
|
|
*/
|
|||
|
|
void audio_spatial_effects_mode_switch(u8 mode, u8 tone_play)
|
|||
|
|
{
|
|||
|
|
u8 tone_index = 0;
|
|||
|
|
if (a2dp_dec) {
|
|||
|
|
/*先挂起再改变标志*/
|
|||
|
|
a2dp_tws_dec_suspend(a2dp_dec);
|
|||
|
|
if (mode == 0) {
|
|||
|
|
printf("SpatialAudio:close_fixed");
|
|||
|
|
a2dp_spatial_audio_setup(0, 0);
|
|||
|
|
tone_index = IDEX_TONE_NUM_0;
|
|||
|
|
} else if (mode == 1) {
|
|||
|
|
printf("SpatialAudio:open_fixed");
|
|||
|
|
a2dp_spatial_audio_setup(1, 0);
|
|||
|
|
tone_index = IDEX_TONE_NUM_1;
|
|||
|
|
} else if (mode == 2) {
|
|||
|
|
printf("SpatialAudio:open_tracked");
|
|||
|
|
a2dp_spatial_audio_setup(1, 1);
|
|||
|
|
tone_index = IDEX_TONE_NUM_2;
|
|||
|
|
}
|
|||
|
|
a2dp_tws_dec_resume();
|
|||
|
|
if (tone_play) {
|
|||
|
|
tone_play_index(tone_index, 1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#endif/*TCFG_AUDIO_SPATIAL_EFFECT_ENABLE*/
|
|||
|
|
|
|||
|
|
|
|||
|
|
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, 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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, 0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|