1075 lines
35 KiB
C++
1075 lines
35 KiB
C++
#include "audio_prompt_sbc.h"
|
|
#include "hal_trace.h"
|
|
#include "string.h"
|
|
#include "app_bt_stream.h"
|
|
#include "app_bt_media_manager.h"
|
|
#include "app_utils.h"
|
|
#include "cmsis.h"
|
|
|
|
|
|
#include "bt_drv_interface.h"
|
|
#include "audioflinger.h"
|
|
#include "app_media_player.h"
|
|
#if defined(IBRT)
|
|
#include "app_tws_ibrt.h"
|
|
#include "app_tws_ibrt_cmd_handler.h"
|
|
#include "app_tws_ctrl_thread.h"
|
|
#include "app_ibrt_if.h"
|
|
#endif
|
|
#include "app_audio.h"
|
|
#include "apps.h"
|
|
#ifdef MIX_AUDIO_PROMPT_WITH_A2DP_MEDIA_ENABLED
|
|
|
|
#define AUDIO_PROMPT_RESAMPLE_ITER_NUM 256
|
|
|
|
#ifdef TWS_PROMPT_SYNC
|
|
extern void tws_sync_mix_prompt_start_handling(void);
|
|
extern void tws_reset_mix_prompt_trigger_ticks(void);
|
|
extern void tws_enable_mix_prompt(bool isEnable);
|
|
#endif
|
|
|
|
|
|
extern void app_stop_a2dp_media_stream(uint8_t devId);
|
|
extern void app_stop_sco_media_stream(uint8_t devId);
|
|
extern void app_bt_stream_copy_track_one_to_two_16bits(int16_t *dst_buf, int16_t *src_buf, uint32_t src_len);
|
|
|
|
static uint32_t audio_prompt_sbc_decode(uint8_t* pcm_buffer, uint32_t expectedOutputSize, uint8_t isReset);
|
|
|
|
static btif_sbc_decoder_t audio_prompt_sbc_decoder;
|
|
|
|
static float audio_prompt_sbc_eq_band_gain[8] = {1, 1, 1, 1, 1, 1, 1, 1};
|
|
|
|
// keep the current volume
|
|
#define KEEP_CURRENT_VOLUME_FOR_MIX_PROMPT -1
|
|
|
|
// from TGT_VOLUME_LEVEL_T or KEEP_CURRENT_VOLUME_FOR_MIX_PROMPT
|
|
#define DEFAULT_VOLUME_FOR_MIX_PROMPT KEEP_CURRENT_VOLUME_FOR_MIX_PROMPT
|
|
|
|
#define DEFAULT_COEFF_FOR_MIX_PROMPT_FOR_MUSIC 1.0
|
|
#define DEFAULT_COEFF_FOR_MIX_MUSIC_FOR_MUSIC 0.4
|
|
#define DEFAULT_COEFF_FOR_MIX_PROMPT_FOR_CALL 1.0
|
|
#define DEFAULT_COEFF_FOR_MIX_CALL_FOR_CALL 0.4
|
|
|
|
#define DEFAULT_OVERLAP_LENGTH 128
|
|
|
|
static int audio_prompt_sbc_init_decoder(void)
|
|
{
|
|
btif_sbc_init_decoder(&audio_prompt_sbc_decoder);
|
|
return 0;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
uint32_t wholeEncodedDataLen;
|
|
uint32_t leftEncodedDataLen;
|
|
uint8_t* promptDataBuf;
|
|
uint16_t tmpSourcePcmDataLen;
|
|
uint16_t tmpSourcePcmDataOutIndex;
|
|
uint8_t* tmpSourcePcmDataBuf;
|
|
uint8_t* tmpTargetPcmDataBuf;
|
|
CQueue pcmDataQueue;
|
|
uint8_t isResetDecoder;
|
|
uint8_t isAudioPromptDecodingDone;
|
|
uint8_t targetBytesCntPerSample;
|
|
uint32_t targetSampleRate;
|
|
struct APP_RESAMPLE_T * resampler;
|
|
uint32_t targetPcmChunkSize;
|
|
float resampleRatio;
|
|
uint8_t* bufForResampler;
|
|
uint32_t resampleBufLen;
|
|
uint8_t pendingStopOp;
|
|
int8_t savedStoppedStreamId;
|
|
uint8_t targetChannelCnt;
|
|
uint8_t mixType;
|
|
uint16_t promptId;
|
|
int16_t volume_level_override;
|
|
uint16_t promptPram;
|
|
float coeff_for_mix_prompt_for_music;
|
|
float coeff_for_mix_music_for_music;
|
|
float coeff_for_mix_prompt_for_call;
|
|
float coeff_for_mix_call_for_call;
|
|
uint8_t isMixPromptOn;
|
|
int16_t currentVol;
|
|
uint16_t mergeInOverlapLength;
|
|
uint16_t mergeOutOverlapLength;
|
|
float mergeInWeight;
|
|
float mergeOutWeight;
|
|
float mergeStep;
|
|
} AUDIO_PROMPT_ENV_T;
|
|
|
|
static AUDIO_PROMPT_ENV_T audio_prompt_env;
|
|
|
|
static void audio_prompt_set_pending_stop_op(uint8_t op)
|
|
{
|
|
audio_prompt_env.pendingStopOp = op;
|
|
TRACE(1,"pendingStopOp is set to %d", op);
|
|
}
|
|
|
|
static uint8_t audio_prompt_get_pending_stop_op(void)
|
|
{
|
|
return audio_prompt_env.pendingStopOp;
|
|
}
|
|
|
|
static void audio_prompt_set_saved_stopped_stream_id(int8_t id)
|
|
{
|
|
audio_prompt_env.savedStoppedStreamId = id;
|
|
TRACE(1,"savedStoppedStreamId is set to %d", id);
|
|
}
|
|
|
|
static int8_t audio_prompt_get_saved_stopped_stream_id(void)
|
|
{
|
|
return audio_prompt_env.savedStoppedStreamId;
|
|
}
|
|
|
|
void audio_prompt_init_handler(void)
|
|
{
|
|
memset((uint8_t *)&audio_prompt_env, 0, sizeof(audio_prompt_env));
|
|
audio_prompt_set_saved_stopped_stream_id(-1);
|
|
}
|
|
|
|
bool audio_prompt_is_allow_update_volume(void)
|
|
{
|
|
bool isAllow = true;
|
|
|
|
uint32_t lock = int_lock_global();
|
|
|
|
isAllow= (!(audio_prompt_env.isMixPromptOn))||
|
|
(KEEP_CURRENT_VOLUME_FOR_MIX_PROMPT ==
|
|
audio_prompt_env.volume_level_override);
|
|
|
|
int_unlock_global(lock);
|
|
|
|
return isAllow;
|
|
}
|
|
|
|
bool audio_prompt_is_playing_ongoing(void)
|
|
{
|
|
bool isPlayingOnGoing = false;
|
|
uint32_t lock = int_lock_global();
|
|
if (audio_prompt_env.isMixPromptOn)
|
|
{
|
|
isPlayingOnGoing = true;
|
|
}
|
|
int_unlock_global(lock);
|
|
return isPlayingOnGoing;
|
|
}
|
|
|
|
#ifdef TWS_PROMPT_SYNC
|
|
|
|
#define MEDIA_SHORT_TRIGGER_DELAY 8
|
|
#define MEDIA_LONG_TRIGGER_DELAY 12
|
|
|
|
#define PROMPT_TICKS_OFFSET_TO_TRIGGER_MIX 10 // 3.25 ms
|
|
|
|
static uint32_t mix_prompt_trigger_ticks = 0;
|
|
static uint32_t playback_interval_in_ticks = 0;
|
|
static uint32_t playback_last_irq_ticks = 0;
|
|
static uint8_t isStartMixPrompt = false;
|
|
|
|
static uint32_t get_prompt_trigger_delay(uint16_t promptId)
|
|
{
|
|
switch (promptId)
|
|
{
|
|
case AUDIO_ID_BT_GSOUND_MIC_OPEN:
|
|
case AUDIO_ID_BT_GSOUND_MIC_CLOSE:
|
|
// TODO: add more case for specific prompts here if you wanna it played sooner
|
|
return MEDIA_SHORT_TRIGGER_DELAY;
|
|
default:
|
|
return MEDIA_LONG_TRIGGER_DELAY;
|
|
}
|
|
}
|
|
|
|
bool tws_calculate_mix_prompt_trigger_ticks(uint16_t promptId)
|
|
{
|
|
if (0 == mix_prompt_trigger_ticks)
|
|
{
|
|
if ((playback_interval_in_ticks > 0)&&(playback_last_irq_ticks > 0))
|
|
{
|
|
mix_prompt_trigger_ticks = playback_last_irq_ticks+
|
|
get_prompt_trigger_delay(promptId)*playback_interval_in_ticks;
|
|
//TRACE(1,"playback_last_irq_ticks %d",playback_last_irq_ticks);
|
|
//TRACE(2,"playback_interval_in_ticks %d mix_prompt_trigger_ticks %d",playback_interval_in_ticks, mix_prompt_trigger_ticks);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void tws_enable_mix_prompt(bool isEnable)
|
|
{
|
|
TRACE(1,"isStartMixPrompt to %d.", isEnable);
|
|
isStartMixPrompt = isEnable;
|
|
}
|
|
|
|
void tws_set_mix_prompt_trigger_ticks(uint32_t ticks)
|
|
{
|
|
mix_prompt_trigger_ticks = ticks;
|
|
}
|
|
|
|
void tws_sync_mix_prompt_start_handling(void)
|
|
{
|
|
if (!app_tws_ibrt_tws_link_connected())
|
|
{
|
|
tws_enable_mix_prompt(true);
|
|
}
|
|
else
|
|
{
|
|
tws_enable_mix_prompt(false);
|
|
}
|
|
}
|
|
|
|
bool tws_is_mix_prompt_allowed_to_start(void)
|
|
{
|
|
return isStartMixPrompt;
|
|
}
|
|
|
|
void app_ibrt_send_mix_prompt_req(void)
|
|
{
|
|
if (app_tws_ibrt_tws_link_connected())
|
|
{
|
|
APP_TWS_CMD_MIX_PROMPT_SYNC_T req;
|
|
req.promptId = audio_prompt_get_prompt_id();
|
|
req.promptPram = audio_prompt_env.promptPram;
|
|
req.trigger_time = mix_prompt_trigger_ticks;
|
|
req.sampleRate = audio_prompt_get_sample_rate();
|
|
tws_ctrl_send_cmd(APP_TWS_CMD_SYNC_MIX_PROMPT_REQ,(uint8_t*)&req,sizeof(APP_TWS_CMD_MIX_PROMPT_SYNC_T));
|
|
}
|
|
}
|
|
|
|
bool tws_sync_mix_prompt_handling(void)
|
|
{
|
|
if (!tws_is_mix_prompt_allowed_to_start())
|
|
{
|
|
if (tws_calculate_mix_prompt_trigger_ticks(audio_prompt_get_prompt_id()))
|
|
{
|
|
// get the trigger ticks, send request to slave
|
|
app_ibrt_send_mix_prompt_req();
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
uint32_t tws_get_mix_prompt_trigger_ticks(void)
|
|
{
|
|
return mix_prompt_trigger_ticks;
|
|
}
|
|
|
|
void tws_reset_mix_prompt_trigger_ticks(void)
|
|
{
|
|
mix_prompt_trigger_ticks = 0;
|
|
playback_interval_in_ticks = 0;
|
|
playback_last_irq_ticks = 0;
|
|
isStartMixPrompt = false;
|
|
}
|
|
|
|
void app_tws_stop_peer_prompt(void)
|
|
{
|
|
uint8_t stub_param = 0;
|
|
|
|
tws_ctrl_send_cmd(APP_TWS_CMD_STOP_PEER_PROMPT_REQ, &stub_param, sizeof(stub_param));
|
|
}
|
|
|
|
void tws_playback_ticks_check_for_mix_prompt(void)
|
|
{
|
|
if ((!audio_prompt_is_playing_ongoing()) || tws_is_mix_prompt_allowed_to_start())
|
|
{
|
|
//TRACE(0,"check for mix prompt<1>");
|
|
return;
|
|
}
|
|
|
|
uint32_t curr_ticks = 0;
|
|
uint16_t conhandle = INVALID_HANDLE;
|
|
ibrt_ctrl_t *p_ibrt_ctrl = app_tws_ibrt_get_bt_ctrl_ctx();
|
|
|
|
uint32_t btclk;
|
|
uint16_t btcnt;
|
|
int32_t offset;
|
|
|
|
if (app_tws_ibrt_mobile_link_connected()){
|
|
conhandle = p_ibrt_ctrl->mobile_conhandle;
|
|
}else if (app_tws_ibrt_slave_ibrt_link_connected()){
|
|
conhandle = p_ibrt_ctrl->ibrt_conhandle;
|
|
}else{
|
|
TRACE(0,"check for mix prompt<2>");
|
|
tws_enable_mix_prompt(true);
|
|
return;
|
|
}
|
|
|
|
bt_drv_reg_op_dma_tc_clkcnt_get(&btclk, &btcnt);
|
|
if(conhandle>=0x80){
|
|
offset = bt_syn_get_offset_ticks(conhandle);
|
|
}else{
|
|
offset = 0;
|
|
}
|
|
curr_ticks = (2*btclk + offset) & 0x0fffffff;
|
|
|
|
if (mix_prompt_trigger_ticks > 0)
|
|
{
|
|
if (curr_ticks >= mix_prompt_trigger_ticks)
|
|
{
|
|
TRACE(2,"ticks<1> %d - trigger ticks %d", curr_ticks,mix_prompt_trigger_ticks);
|
|
tws_enable_mix_prompt(true);
|
|
}
|
|
else if ((curr_ticks < mix_prompt_trigger_ticks) &&
|
|
((mix_prompt_trigger_ticks - curr_ticks) < PROMPT_TICKS_OFFSET_TO_TRIGGER_MIX))
|
|
{
|
|
TRACE(2,"ticks<2> %d - trigger ticks %d", curr_ticks,mix_prompt_trigger_ticks);
|
|
tws_enable_mix_prompt(true);
|
|
}
|
|
}
|
|
|
|
if (0 != playback_last_irq_ticks)
|
|
{
|
|
playback_interval_in_ticks = curr_ticks-playback_last_irq_ticks;
|
|
}
|
|
|
|
playback_last_irq_ticks = curr_ticks;
|
|
}
|
|
|
|
void app_tws_cmd_sync_mix_prompt_req_handler(uint8_t* ptrParam, uint16_t paramLen)
|
|
{
|
|
uint16_t promptPram = 0xFFFF;
|
|
uint32_t curr_ticks = 0xFFFFFFFF;
|
|
ibrt_ctrl_t *p_ibrt_ctrl = app_tws_ibrt_get_bt_ctrl_ctx();
|
|
|
|
APP_TWS_CMD_MIX_PROMPT_SYNC_T* pReq = (APP_TWS_CMD_MIX_PROMPT_SYNC_T *)ptrParam;
|
|
TRACE(2,"promptId, trigger_time:0x%x, %d",
|
|
pReq->promptId, pReq->trigger_time);
|
|
|
|
audio_prompt_stop_playing();
|
|
|
|
if (app_tws_ibrt_slave_ibrt_link_connected())
|
|
{
|
|
curr_ticks = bt_syn_get_curr_ticks(p_ibrt_ctrl->ibrt_conhandle);
|
|
}
|
|
|
|
if ((!app_tws_ibrt_slave_ibrt_link_connected()) ||
|
|
((curr_ticks > pReq->trigger_time) &&
|
|
((curr_ticks - pReq->trigger_time) > PROMPT_TICKS_OFFSET_TO_TRIGGER_MIX)))
|
|
{
|
|
TRACE(0,"return directly.");
|
|
return;
|
|
}
|
|
|
|
tws_set_mix_prompt_trigger_ticks(pReq->trigger_time);
|
|
promptPram = pReq->promptId | pReq->promptPram;
|
|
audio_prompt_start_playing(promptPram, pReq->sampleRate);
|
|
if (curr_ticks >= pReq->trigger_time)
|
|
{
|
|
TRACE(1,"Instant passed %d",curr_ticks);
|
|
tws_enable_mix_prompt(true);
|
|
}
|
|
else if ((curr_ticks < pReq->trigger_time) &&
|
|
((pReq->trigger_time - curr_ticks) < PROMPT_TICKS_OFFSET_TO_TRIGGER_MIX)){
|
|
TRACE(1,"Instant near %d",curr_ticks);
|
|
tws_enable_mix_prompt(true);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int audio_prompt_resample_iter(uint8_t *buf, uint32_t len)
|
|
{
|
|
if (!buf)
|
|
{
|
|
TRACE(0, "NULL pointer received in %s", __func__);
|
|
return -1;
|
|
}
|
|
|
|
uint32_t leftLen = audio_prompt_env.tmpSourcePcmDataLen - audio_prompt_env.tmpSourcePcmDataOutIndex;
|
|
uint32_t lenToFetch;
|
|
|
|
if (leftLen >= len)
|
|
{
|
|
lenToFetch = len;
|
|
}
|
|
else
|
|
{
|
|
lenToFetch = leftLen;
|
|
}
|
|
|
|
memcpy(buf, audio_prompt_env.tmpSourcePcmDataBuf + audio_prompt_env.tmpSourcePcmDataOutIndex,
|
|
lenToFetch);
|
|
audio_prompt_env.tmpSourcePcmDataOutIndex += lenToFetch;
|
|
|
|
memset(buf + lenToFetch, 0, len - lenToFetch);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void audio_prompt_buffer_config(uint8_t mixType,
|
|
uint8_t channel_cnt,
|
|
uint8_t bitNumPerSample,
|
|
uint8_t *tmpSourcePcmDataBuf,
|
|
uint8_t *tmpTargetPcmDataBuf,
|
|
uint8_t *pcmDataBuf,
|
|
uint32_t pcmBufLen,
|
|
uint8_t *bufForResampler,
|
|
uint32_t resampleBufLen)
|
|
{
|
|
af_lock_thread();
|
|
audio_prompt_env.mixType = mixType;
|
|
audio_prompt_env.targetChannelCnt = channel_cnt;
|
|
if (24 == bitNumPerSample)
|
|
{
|
|
audio_prompt_env.targetBytesCntPerSample = 4;
|
|
}
|
|
else if (16 == bitNumPerSample)
|
|
{
|
|
audio_prompt_env.targetBytesCntPerSample = 2;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(false, "bitNumPerSample %d is not supported by prompt mixer yet!", bitNumPerSample);
|
|
}
|
|
|
|
audio_prompt_env.tmpSourcePcmDataBuf = tmpSourcePcmDataBuf;
|
|
audio_prompt_env.tmpTargetPcmDataBuf = tmpTargetPcmDataBuf;
|
|
audio_prompt_env.bufForResampler = bufForResampler;
|
|
audio_prompt_env.resampleBufLen = resampleBufLen;
|
|
|
|
InitCQueue(&(audio_prompt_env.pcmDataQueue), pcmBufLen, (CQItemType *)(pcmDataBuf));
|
|
af_unlock_thread();
|
|
}
|
|
|
|
|
|
uint16_t audio_prompt_get_prompt_id(void)
|
|
{
|
|
return audio_prompt_env.promptId;
|
|
}
|
|
|
|
uint32_t audio_prompt_get_sample_rate(void)
|
|
{
|
|
return audio_prompt_env.targetSampleRate;
|
|
}
|
|
|
|
bool audio_prompt_start_playing(uint16_t promptPram, uint32_t targetSampleRate)
|
|
{
|
|
uint16_t promptId = PROMPT_ID_FROM_ID_VALUE(promptPram);
|
|
|
|
if (audio_prompt_is_playing_ongoing())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TRACE(0, "[%s]", __func__);
|
|
|
|
#ifdef TWS_PROMPT_SYNC
|
|
bool isPlayingLocally = false;
|
|
if (app_tws_ibrt_tws_link_connected())
|
|
{
|
|
isPlayingLocally = false;
|
|
}
|
|
else
|
|
{
|
|
isPlayingLocally = true;
|
|
}
|
|
#endif
|
|
|
|
promptId = PROMPT_ID_FROM_ID_VALUE(promptId);
|
|
|
|
uint32_t lock = int_lock_global();
|
|
audio_prompt_env.isMixPromptOn = true;
|
|
|
|
uint8_t* promptDataPtr = NULL;
|
|
uint32_t promptDataLen = 0;
|
|
PROMPT_MIX_PROPERTY_T* pPromptProperty = NULL;
|
|
|
|
#ifdef MEDIA_PLAYER_SUPPORT
|
|
media_runtime_audio_prompt_update(promptId, &promptDataPtr, &promptDataLen);
|
|
pPromptProperty = get_prompt_mix_property(promptId);
|
|
#endif
|
|
|
|
if (NULL == pPromptProperty)
|
|
{
|
|
TRACE(0,"use default mix property");
|
|
|
|
// use default property
|
|
audio_prompt_env.volume_level_override = DEFAULT_VOLUME_FOR_MIX_PROMPT;
|
|
|
|
audio_prompt_env.coeff_for_mix_prompt_for_call = DEFAULT_COEFF_FOR_MIX_PROMPT_FOR_CALL;
|
|
audio_prompt_env.coeff_for_mix_call_for_call = DEFAULT_COEFF_FOR_MIX_CALL_FOR_CALL;
|
|
audio_prompt_env.coeff_for_mix_prompt_for_music = DEFAULT_COEFF_FOR_MIX_PROMPT_FOR_MUSIC;
|
|
audio_prompt_env.coeff_for_mix_music_for_music = DEFAULT_COEFF_FOR_MIX_MUSIC_FOR_MUSIC;
|
|
}
|
|
else
|
|
{
|
|
audio_prompt_env.volume_level_override =
|
|
pPromptProperty->volume_level_override;
|
|
|
|
audio_prompt_env.coeff_for_mix_prompt_for_call =
|
|
pPromptProperty->coeff_for_mix_prompt_for_call;
|
|
audio_prompt_env.coeff_for_mix_call_for_call =
|
|
pPromptProperty->coeff_for_mix_call_for_call;
|
|
audio_prompt_env.coeff_for_mix_prompt_for_music =
|
|
pPromptProperty->coeff_for_mix_prompt_for_music;
|
|
audio_prompt_env.coeff_for_mix_music_for_music =
|
|
pPromptProperty->coeff_for_mix_music_for_music;
|
|
}
|
|
|
|
if (KEEP_CURRENT_VOLUME_FOR_MIX_PROMPT != audio_prompt_env.volume_level_override)
|
|
{
|
|
// if the prompt's volume is smaller than the current volume, don't change it
|
|
if (audio_prompt_env.volume_level_override <= app_bt_stream_local_volume_get())
|
|
{
|
|
audio_prompt_env.volume_level_override = KEEP_CURRENT_VOLUME_FOR_MIX_PROMPT;
|
|
}
|
|
}
|
|
|
|
audio_prompt_env.promptId = promptId;
|
|
audio_prompt_env.promptPram = PROMPT_PRAM_FROM_ID_VALUE(promptPram);
|
|
audio_prompt_env.promptDataBuf = promptDataPtr;
|
|
audio_prompt_env.isResetDecoder = true;
|
|
audio_prompt_env.isAudioPromptDecodingDone = false;
|
|
audio_prompt_env.wholeEncodedDataLen = promptDataLen;
|
|
audio_prompt_env.leftEncodedDataLen = audio_prompt_env.wholeEncodedDataLen;
|
|
audio_prompt_env.targetSampleRate = targetSampleRate;
|
|
|
|
audio_prompt_env.resampleRatio = ((float)AUDIO_PROMPT_SBC_SAMPLE_RATE_VALUE) / audio_prompt_env.targetSampleRate;
|
|
audio_prompt_env.targetPcmChunkSize = (uint32_t)(AUDIO_PROMPT_SBC_PCM_DATA_SIZE_PER_FRAME/audio_prompt_env.resampleRatio);
|
|
|
|
audio_prompt_env.resampler = app_playback_resample_any_open_with_pre_allocated_buffer(( enum AUD_CHANNEL_NUM_T )AUDIO_PROMPT_SBC_CHANNEL_COUNT,
|
|
audio_prompt_resample_iter,
|
|
AUDIO_PROMPT_RESAMPLE_ITER_NUM,
|
|
audio_prompt_env.resampleRatio,
|
|
audio_prompt_env.bufForResampler,
|
|
audio_prompt_env.resampleBufLen);
|
|
|
|
// audio resample out size should be even
|
|
uint16_t targetOverlapLength = ((uint16_t)(DEFAULT_OVERLAP_LENGTH / audio_prompt_env.resampleRatio) / 2 * 2);
|
|
audio_prompt_env.mergeInOverlapLength = targetOverlapLength * audio_prompt_env.targetChannelCnt;
|
|
audio_prompt_env.mergeOutOverlapLength = targetOverlapLength * audio_prompt_env.targetChannelCnt;
|
|
audio_prompt_env.mergeInWeight = 0.f;
|
|
audio_prompt_env.mergeOutWeight = 1.f;
|
|
audio_prompt_env.mergeStep = 1.f / (targetOverlapLength - 1);
|
|
|
|
audio_prompt_set_saved_stopped_stream_id(-1);
|
|
|
|
int_unlock_global(lock);
|
|
|
|
TRACE(1,"start audio prompt. target sample rate %d", targetSampleRate);
|
|
|
|
app_sysfreq_req(APP_SYSFREQ_USER_PROMPT_MIXER, APP_SYSFREQ_104M);
|
|
|
|
#ifdef TWS_PROMPT_SYNC
|
|
if (!isPlayingLocally)
|
|
{
|
|
tws_sync_mix_prompt_start_handling();
|
|
}
|
|
else
|
|
{
|
|
tws_enable_mix_prompt(true);
|
|
}
|
|
#endif
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
void audio_prompt_forcefully_stop(void)
|
|
{
|
|
app_playback_resample_close(audio_prompt_env.resampler);
|
|
audio_prompt_set_saved_stopped_stream_id(-1);
|
|
audio_prompt_env.isAudioPromptDecodingDone = true;
|
|
audio_prompt_env.leftEncodedDataLen = 0;
|
|
app_sysfreq_req(APP_SYSFREQ_USER_PROMPT_MIXER, APP_SYSFREQ_32K);
|
|
#if defined(IBRT) && defined(MEDIA_PLAYER_SUPPORT)
|
|
app_tws_sync_prompt_check();
|
|
#endif
|
|
}
|
|
|
|
bool audio_prompt_check_on_stopping_stream(uint8_t pendingStopOp, uint8_t deviceId)
|
|
{
|
|
uint32_t lock = int_lock_global();
|
|
if (audio_prompt_env.isMixPromptOn)
|
|
{
|
|
if (bt_is_playback_triggered())
|
|
{
|
|
TRACE(1,"Prompt mixing ongoing, pending op:%d", pendingStopOp);
|
|
audio_prompt_env.pendingStopOp = pendingStopOp;
|
|
audio_prompt_set_saved_stopped_stream_id(deviceId);
|
|
int_unlock_global(lock);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
int_unlock_global(lock);
|
|
audio_prompt_stop_playing();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
int_unlock_global(lock);
|
|
return true;
|
|
}
|
|
|
|
bool audio_prompt_clear_pending_stream(uint8_t op)
|
|
{
|
|
bool isToClearActiveMedia = false;
|
|
|
|
TRACE(4,
|
|
"%s stop_id %d pendingStopOp %d op %d",
|
|
__func__,
|
|
audio_prompt_get_saved_stopped_stream_id(),
|
|
audio_prompt_env.pendingStopOp,
|
|
op);
|
|
|
|
if (-1 != audio_prompt_get_saved_stopped_stream_id())
|
|
{
|
|
uint32_t lock = int_lock_global();
|
|
if ((PENDING_TO_STOP_A2DP_STREAMING == op) &&
|
|
(PENDING_TO_STOP_A2DP_STREAMING == audio_prompt_get_pending_stop_op()))
|
|
{
|
|
audio_prompt_set_saved_stopped_stream_id(-1);
|
|
audio_prompt_set_pending_stop_op(PENDING_TO_STOP_STREAM_INVALID);
|
|
isToClearActiveMedia = true;
|
|
}
|
|
else if ((PENDING_TO_STOP_SCO_STREAMING == op) &&
|
|
(PENDING_TO_STOP_SCO_STREAMING == audio_prompt_get_pending_stop_op()))
|
|
{
|
|
audio_prompt_set_saved_stopped_stream_id(-1);
|
|
audio_prompt_set_pending_stop_op(PENDING_TO_STOP_STREAM_INVALID);
|
|
isToClearActiveMedia = true;
|
|
}
|
|
int_unlock_global(lock);
|
|
}
|
|
|
|
return isToClearActiveMedia;
|
|
}
|
|
|
|
void audio_prompt_stop_playing(void)
|
|
{
|
|
if (!audio_prompt_env.isMixPromptOn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TRACE(0,"Stop audio prompt.");
|
|
|
|
app_playback_resample_close(audio_prompt_env.resampler);
|
|
|
|
uint32_t lock = int_lock_global();
|
|
|
|
audio_prompt_env.leftEncodedDataLen = 0;
|
|
audio_prompt_env.isMixPromptOn = false;
|
|
|
|
#ifdef TWS_PROMPT_SYNC
|
|
tws_reset_mix_prompt_trigger_ticks();
|
|
#endif
|
|
|
|
int_unlock_global(lock);
|
|
|
|
if (KEEP_CURRENT_VOLUME_FOR_MIX_PROMPT != audio_prompt_env.volume_level_override)
|
|
{
|
|
// restore the volume
|
|
app_bt_stream_volumeset_handler(app_bt_stream_local_volume_get());
|
|
}
|
|
|
|
lock = int_lock_global();
|
|
|
|
uint8_t pendingStopOp;
|
|
int8_t savedStoppedStreamId;
|
|
|
|
pendingStopOp = audio_prompt_env.pendingStopOp;
|
|
savedStoppedStreamId = audio_prompt_get_saved_stopped_stream_id();
|
|
|
|
audio_prompt_set_saved_stopped_stream_id(-1);
|
|
|
|
int_unlock_global(lock);
|
|
|
|
if (savedStoppedStreamId >= 0)
|
|
{
|
|
if (PENDING_TO_STOP_A2DP_STREAMING == pendingStopOp)
|
|
{
|
|
TRACE(0,"Stop the pending stopped a2dp media stream.");
|
|
app_stop_a2dp_media_stream(savedStoppedStreamId);
|
|
}
|
|
else if (PENDING_TO_STOP_SCO_STREAMING == pendingStopOp)
|
|
{
|
|
TRACE(0,"Stop the pending stopped sco media stream.");
|
|
app_stop_sco_media_stream(savedStoppedStreamId);
|
|
}
|
|
}
|
|
|
|
app_sysfreq_req(APP_SYSFREQ_USER_PROMPT_MIXER, APP_SYSFREQ_32K);
|
|
|
|
#if defined(IBRT) && defined(MEDIA_PLAYER_SUPPORT)
|
|
app_tws_sync_prompt_check();
|
|
#endif
|
|
//check if there is any pending prompt need to play
|
|
APP_AUDIO_STATUS status_next;
|
|
APP_AUDIO_STATUS aud_status;
|
|
aud_status.id = APP_PLAY_BACK_AUDIO;
|
|
if (app_audio_list_rmv_callback(&aud_status, &status_next,APP_BT_SETTING_Q_POS_HEAD, true))
|
|
{
|
|
TRACE(4,"%s next id: 0x%x%s, aud_id %d", __func__, status_next.id, player2str(status_next.id), status_next.aud_id);
|
|
#if defined(IBRT)
|
|
app_ibrt_if_voice_report_handler(status_next.aud_id, true);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
template<typename DstType>
|
|
static inline DstType prompt_data_ssat(int32_t in)
|
|
{
|
|
DstType out;
|
|
|
|
if (sizeof(DstType) == 2) {
|
|
out = __SSAT(in, 16);
|
|
} else {
|
|
out = __SSAT(in, 24);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
template<typename DstType>
|
|
static inline DstType prompt_data_extend(int16_t in)
|
|
{
|
|
DstType out;
|
|
|
|
if (sizeof(DstType) == 2) {
|
|
out = in;
|
|
} else {
|
|
out = (in << 8);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
template<typename DstType>
|
|
static void audio_prompt_crossfade(DstType *dst_buf, DstType *src_buf1, int16_t *src_buf0,
|
|
float coeff_for_source, float coeff_for_prompt,
|
|
uint32_t merge_in_end, uint32_t merge_out_start, uint32_t src_len)
|
|
{
|
|
uint32_t i = 0;
|
|
if (audio_prompt_env.targetChannelCnt == 2) {
|
|
float weight = audio_prompt_env.mergeInWeight;
|
|
for (; i < merge_in_end; i += 2) {
|
|
/* TODO: increase coeff by step instead of calcualted every step */
|
|
float coeff0 = 1 - weight + weight * coeff_for_source;
|
|
float coeff1 = weight * coeff_for_prompt;
|
|
|
|
float tmp0 = coeff0 * src_buf1[i] + coeff1 * prompt_data_extend<DstType>(src_buf0[i]);
|
|
float tmp1 = coeff0 * src_buf1[i + 1] + coeff1 * prompt_data_extend<DstType>(src_buf0[i + 1]);
|
|
dst_buf[i] = prompt_data_ssat<DstType>((int32_t)tmp0);
|
|
dst_buf[i + 1] = prompt_data_ssat<DstType>((int32_t)tmp1);
|
|
|
|
weight += audio_prompt_env.mergeStep;
|
|
}
|
|
audio_prompt_env.mergeInOverlapLength -= merge_in_end;
|
|
audio_prompt_env.mergeInWeight = weight;
|
|
} else if (audio_prompt_env.targetChannelCnt == 1) {
|
|
float weight = audio_prompt_env.mergeInWeight;
|
|
for (; i < merge_in_end; i++) {
|
|
float coeff0 = 1 - weight + weight * coeff_for_source;
|
|
float coeff1 = weight * coeff_for_prompt;
|
|
|
|
float tmp0 = coeff0 * src_buf1[i] + coeff1 * prompt_data_extend<DstType>(src_buf0[i]);
|
|
dst_buf[i] = prompt_data_ssat<DstType>((int32_t)tmp0);
|
|
|
|
weight += audio_prompt_env.mergeStep;
|
|
}
|
|
audio_prompt_env.mergeInOverlapLength -= merge_in_end;
|
|
audio_prompt_env.mergeInWeight = weight;
|
|
} else {
|
|
ASSERT(0, "[%s] channel number %d not supported", __FUNCTION__, audio_prompt_env.targetChannelCnt);
|
|
}
|
|
for (; i < merge_out_start; i++) {
|
|
float mix_value = coeff_for_prompt * prompt_data_extend<DstType>(src_buf0[i]) +
|
|
coeff_for_source * src_buf1[i];
|
|
dst_buf[i] = prompt_data_ssat<DstType>((int32_t)mix_value);
|
|
}
|
|
if (audio_prompt_env.targetChannelCnt == 2) {
|
|
float weight = audio_prompt_env.mergeOutWeight;
|
|
for (; i < src_len; i += 2) {
|
|
float coeff0 = (1 - weight + weight * coeff_for_source);
|
|
float coeff1 = weight * coeff_for_prompt;
|
|
|
|
float tmp0 = coeff0 * src_buf1[i] + coeff1 * prompt_data_extend<DstType>(src_buf0[i]);
|
|
float tmp1 = coeff0 * src_buf1[i + 1] + coeff1 * prompt_data_extend<DstType>(src_buf0[i + 1]);
|
|
dst_buf[i] = prompt_data_ssat<DstType>((int32_t)tmp0);
|
|
dst_buf[i + 1] = prompt_data_ssat<DstType>((int32_t)tmp1);
|
|
|
|
weight -= audio_prompt_env.mergeStep;
|
|
}
|
|
audio_prompt_env.mergeOutOverlapLength -= src_len - merge_out_start;
|
|
audio_prompt_env.mergeOutWeight = weight;
|
|
} else if (audio_prompt_env.targetChannelCnt == 1) {
|
|
float weight = audio_prompt_env.mergeOutWeight;
|
|
for (; i < src_len; i++) {
|
|
float coeff0 = (1 - weight + weight * coeff_for_source);
|
|
float coeff1 = weight * coeff_for_prompt;
|
|
|
|
float tmp0 = coeff0 * src_buf1[i] + coeff1 * prompt_data_extend<DstType>(src_buf0[i]);
|
|
dst_buf[i] = prompt_data_ssat<DstType>((int32_t)tmp0);
|
|
|
|
weight -= audio_prompt_env.mergeStep;
|
|
}
|
|
audio_prompt_env.mergeOutOverlapLength -= src_len - merge_out_start;
|
|
audio_prompt_env.mergeOutWeight = weight;
|
|
} else {
|
|
ASSERT(0, "[%s] channel number %d not supported", __FUNCTION__, audio_prompt_env.targetChannelCnt);
|
|
}
|
|
}
|
|
|
|
static void audio_prompt_processing_handler_func(uint32_t acquiredPcmDataLen, uint8_t* pcmDataToMerge)
|
|
{
|
|
#ifdef TWS_PROMPT_SYNC
|
|
uint16_t prompt_chnlsel = PROMPT_CHNLSEl_FROM_ID_VALUE(audio_prompt_env.promptPram);
|
|
#endif
|
|
|
|
uint32_t pcmDataToGetFromPrompt = acquiredPcmDataLen/(audio_prompt_env.targetChannelCnt*audio_prompt_env.targetBytesCntPerSample/2);
|
|
|
|
while ((uint32_t)LengthOfCQueue(&(audio_prompt_env.pcmDataQueue)) < pcmDataToGetFromPrompt)
|
|
{
|
|
if (audio_prompt_env.isAudioPromptDecodingDone)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// decode the audio prompt
|
|
uint32_t returnedPcmDataLen = audio_prompt_sbc_decode(audio_prompt_env.tmpSourcePcmDataBuf,
|
|
AUDIO_PROMPT_SBC_PCM_DATA_SIZE_PER_FRAME,
|
|
audio_prompt_env.isResetDecoder);
|
|
|
|
if (returnedPcmDataLen < AUDIO_PROMPT_SBC_PCM_DATA_SIZE_PER_FRAME)
|
|
{
|
|
audio_prompt_env.isAudioPromptDecodingDone = true;
|
|
}
|
|
|
|
audio_prompt_env.isResetDecoder = false;
|
|
audio_prompt_env.tmpSourcePcmDataLen = returnedPcmDataLen;
|
|
audio_prompt_env.tmpSourcePcmDataOutIndex = 0;
|
|
|
|
// do resmpling
|
|
if (audio_prompt_env.targetSampleRate != AUDIO_PROMPT_SBC_SAMPLE_RATE_VALUE)
|
|
{
|
|
uint32_t targetPcmSize = returnedPcmDataLen;
|
|
if (AUDIO_PROMPT_SBC_PCM_DATA_SIZE_PER_FRAME == returnedPcmDataLen)
|
|
{
|
|
targetPcmSize = audio_prompt_env.targetPcmChunkSize;
|
|
}
|
|
else
|
|
{
|
|
targetPcmSize = (uint32_t)(returnedPcmDataLen/audio_prompt_env.resampleRatio);
|
|
}
|
|
|
|
targetPcmSize = (targetPcmSize/4)*4;
|
|
|
|
app_playback_resample_run(audio_prompt_env.resampler,
|
|
audio_prompt_env.tmpTargetPcmDataBuf,
|
|
targetPcmSize);
|
|
|
|
// fill into pcm data queue
|
|
EnCQueue(&(audio_prompt_env.pcmDataQueue), audio_prompt_env.tmpTargetPcmDataBuf, targetPcmSize);
|
|
}
|
|
else
|
|
{
|
|
EnCQueue(&(audio_prompt_env.pcmDataQueue), audio_prompt_env.tmpSourcePcmDataBuf, returnedPcmDataLen);
|
|
}
|
|
}
|
|
|
|
uint32_t pcmDataLenToMerge = pcmDataToGetFromPrompt;
|
|
if ((uint32_t)LengthOfCQueue(&(audio_prompt_env.pcmDataQueue)) < pcmDataToGetFromPrompt)
|
|
{
|
|
pcmDataLenToMerge = LengthOfCQueue(&(audio_prompt_env.pcmDataQueue));
|
|
}
|
|
|
|
if (pcmDataLenToMerge == 0)
|
|
goto exit;
|
|
|
|
// copy to multiple channel if needed
|
|
if (audio_prompt_env.targetChannelCnt > 1)
|
|
{
|
|
// get the data
|
|
DeCQueue(&(audio_prompt_env.pcmDataQueue), audio_prompt_env.tmpSourcePcmDataBuf, pcmDataLenToMerge);
|
|
app_bt_stream_copy_track_one_to_two_16bits(( int16_t * )audio_prompt_env.tmpTargetPcmDataBuf,
|
|
( int16_t * )audio_prompt_env.tmpSourcePcmDataBuf,
|
|
pcmDataLenToMerge / sizeof(uint16_t));
|
|
}
|
|
else
|
|
{
|
|
DeCQueue(&(audio_prompt_env.pcmDataQueue), audio_prompt_env.tmpTargetPcmDataBuf, pcmDataLenToMerge);
|
|
}
|
|
|
|
#ifdef TWS_PROMPT_SYNC
|
|
if (IS_PROMPT_CHNLSEl_ALL(prompt_chnlsel) || app_ibrt_voice_report_is_me(prompt_chnlsel))
|
|
#endif
|
|
{
|
|
// merge the data
|
|
int16_t *src_buf0 = (int16_t *)audio_prompt_env.tmpTargetPcmDataBuf;
|
|
uint32_t src_len = pcmDataLenToMerge*audio_prompt_env.targetChannelCnt/sizeof(uint16_t);
|
|
|
|
float coeff_for_prompt;
|
|
float coeff_for_source;
|
|
|
|
if (MIX_WITH_A2DP_STREAMING == audio_prompt_env.mixType) {
|
|
coeff_for_prompt = audio_prompt_env.coeff_for_mix_prompt_for_music;
|
|
coeff_for_source = audio_prompt_env.coeff_for_mix_music_for_music;
|
|
} else {
|
|
coeff_for_prompt = audio_prompt_env.coeff_for_mix_prompt_for_call;
|
|
coeff_for_source = audio_prompt_env.coeff_for_mix_call_for_call;
|
|
}
|
|
|
|
/*
|
|
* 0 --------------- merge_in_end --- merge_out_start --- src_len
|
|
* |---- merge in ---------|---- merge ------|----- merge out ---|
|
|
* Split data into three segments, merge in stands for merge begin crossfade, merge out for merge end crossfade.
|
|
* In merge begin crossfade stage, old data is music/sco pcm data, new data is merged data.
|
|
* In merge end crossfade stage, old data is merged data, new data is music/sco data.
|
|
* Assume music/soc is a, ring is b, then merged data is x = m * a + p * b,
|
|
* merge begin final data y = (1 - w) * a + w * x = (1 - w + w * m) * a + w * p * b for w = 0:step:1,
|
|
* merge end final data y = (1 - w) * a + w * x = (1 - w + w * m) * a + w * p * b for w = 1:-step:0.
|
|
*/
|
|
uint32_t merge_in_end = 0;
|
|
if (audio_prompt_env.mergeInOverlapLength > 0) {
|
|
TRACE(3, "[%s] bytes_per_sample %d, channel_num %d", __FUNCTION__,
|
|
audio_prompt_env.targetBytesCntPerSample, audio_prompt_env.targetChannelCnt);
|
|
TRACE(2, "[%s] merge start, remain %d", __FUNCTION__, audio_prompt_env.mergeInOverlapLength);
|
|
merge_in_end = MIN(audio_prompt_env.mergeInOverlapLength, src_len);
|
|
}
|
|
|
|
uint32_t merge_out_start = src_len;
|
|
/* TODO: calc remain decoded pcm samples for DEFAULT_OVERLAP_LENGTH > 128 */
|
|
if (audio_prompt_env.leftEncodedDataLen == 0) {
|
|
if (LengthOfCQueue(&(audio_prompt_env.pcmDataQueue)) < audio_prompt_env.mergeOutOverlapLength / audio_prompt_env.targetChannelCnt * (int32_t)sizeof(uint16_t)) {
|
|
TRACE(2, "[%s] merge end, remain %d", __FUNCTION__, audio_prompt_env.mergeOutOverlapLength);
|
|
merge_out_start = src_len - (audio_prompt_env.mergeOutOverlapLength - LengthOfCQueue(&(audio_prompt_env.pcmDataQueue)) / sizeof(uint16_t) * audio_prompt_env.targetChannelCnt);
|
|
}
|
|
}
|
|
|
|
//TRACE(3, "[%s] merge_in_end = %d, merge_out_start = %d", __FUNCTION__, merge_in_end, merge_out_start);
|
|
//TRACE(3, "[%s] pcm queue size = %d, src_len = %d", __FUNCTION__, LengthOfCQueue(&(audio_prompt_env.pcmDataQueue)), src_len);
|
|
|
|
if (2 == audio_prompt_env.targetBytesCntPerSample)
|
|
{
|
|
int16_t *src_buf1 = (int16_t *)pcmDataToMerge;
|
|
int16_t *dst_buf = (int16_t *)pcmDataToMerge;
|
|
|
|
audio_prompt_crossfade<int16_t>(dst_buf, src_buf1, src_buf0,
|
|
coeff_for_source, coeff_for_prompt,
|
|
merge_in_end, merge_out_start, src_len);
|
|
}
|
|
else if (4 == audio_prompt_env.targetBytesCntPerSample)
|
|
{
|
|
int32_t *src_buf1 = (int32_t *)pcmDataToMerge;
|
|
int32_t *dst_buf = (int32_t *)pcmDataToMerge;
|
|
|
|
audio_prompt_crossfade<int32_t>(dst_buf, src_buf1, src_buf0,
|
|
coeff_for_source, coeff_for_prompt,
|
|
merge_in_end, merge_out_start, src_len);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (audio_prompt_env.mergeOutOverlapLength == 0)
|
|
{
|
|
// prompt playing is completed
|
|
audio_prompt_stop_playing();
|
|
app_sysfreq_req(APP_SYSFREQ_USER_PROMPT_MIXER, APP_SYSFREQ_32K);
|
|
}
|
|
|
|
}
|
|
|
|
void audio_prompt_processing_handler(uint32_t acquiredPcmDataLen, uint8_t* pcmDataToMerge)
|
|
{
|
|
#ifdef TWS_PROMPT_SYNC
|
|
if (!tws_sync_mix_prompt_handling())
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
af_lock_thread();
|
|
|
|
if (audio_prompt_env.leftEncodedDataLen ==
|
|
audio_prompt_env.wholeEncodedDataLen)
|
|
{
|
|
if (KEEP_CURRENT_VOLUME_FOR_MIX_PROMPT != audio_prompt_env.volume_level_override)
|
|
{
|
|
// first entering, coordinate the volume here
|
|
app_bt_stream_volumeset_handler(audio_prompt_env.volume_level_override);
|
|
}
|
|
}
|
|
|
|
uint32_t gotDataLen = 0;
|
|
while (gotDataLen < acquiredPcmDataLen)
|
|
{
|
|
uint32_t lenToGet;
|
|
if ((acquiredPcmDataLen - gotDataLen) > AUDIO_PROMPT_PCM_FILL_UNIT_SIZE)
|
|
{
|
|
lenToGet = AUDIO_PROMPT_PCM_FILL_UNIT_SIZE;
|
|
}
|
|
else
|
|
{
|
|
lenToGet = acquiredPcmDataLen - gotDataLen;
|
|
}
|
|
|
|
audio_prompt_processing_handler_func(lenToGet, pcmDataToMerge+gotDataLen);
|
|
gotDataLen += lenToGet;
|
|
}
|
|
af_unlock_thread();
|
|
}
|
|
|
|
static uint32_t audio_prompt_sbc_decode(uint8_t* pcm_buffer, uint32_t expectedOutputSize, uint8_t isReset)
|
|
{
|
|
if (isReset)
|
|
{
|
|
audio_prompt_sbc_init_decoder();
|
|
}
|
|
|
|
uint32_t sbcDataBytesToDecode;
|
|
unsigned int pcm_offset = 0;
|
|
uint16_t byte_decode;
|
|
int8_t ret;
|
|
btif_sbc_pcm_data_t audio_prompt_PcmDecData;
|
|
|
|
get_again:
|
|
audio_prompt_PcmDecData.data = pcm_buffer+pcm_offset;
|
|
audio_prompt_PcmDecData.dataLen = 0;
|
|
|
|
if (audio_prompt_env.leftEncodedDataLen > AUDIO_PROMPT_SBC_ENCODED_DATA_SIZE_PER_FRAME)
|
|
{
|
|
sbcDataBytesToDecode = AUDIO_PROMPT_SBC_ENCODED_DATA_SIZE_PER_FRAME;
|
|
}
|
|
else
|
|
{
|
|
sbcDataBytesToDecode = audio_prompt_env.leftEncodedDataLen;
|
|
}
|
|
|
|
ret = btif_sbc_decode_frames(&audio_prompt_sbc_decoder,
|
|
audio_prompt_env.promptDataBuf+audio_prompt_env.wholeEncodedDataLen-audio_prompt_env.leftEncodedDataLen,
|
|
sbcDataBytesToDecode, &byte_decode,
|
|
&audio_prompt_PcmDecData, expectedOutputSize-pcm_offset,
|
|
audio_prompt_sbc_eq_band_gain);
|
|
|
|
|
|
audio_prompt_env.leftEncodedDataLen -= byte_decode;
|
|
|
|
pcm_offset += audio_prompt_PcmDecData.dataLen;
|
|
|
|
if (0 == audio_prompt_env.leftEncodedDataLen)
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
if (expectedOutputSize == pcm_offset)
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
if ((ret == BT_STS_CONTINUE) || (ret == BT_STS_SUCCESS)) {
|
|
goto get_again;
|
|
}
|
|
|
|
exit:
|
|
return pcm_offset;
|
|
}
|
|
#endif // MIX_AUDIO_PROMPT_WITH_A2DP_MEDIA_ENABLED
|