1023 lines
33 KiB
C++
1023 lines
33 KiB
C++
#include "audio_prompt_sbc.h"
|
|
#include "app_bt_media_manager.h"
|
|
#include "app_bt_stream.h"
|
|
#include "app_utils.h"
|
|
#include "cmsis.h"
|
|
#include "hal_trace.h"
|
|
#include "string.h"
|
|
|
|
#include "app_media_player.h"
|
|
#include "audioflinger.h"
|
|
#include "bt_drv_interface.h"
|
|
#if defined(IBRT)
|
|
#include "app_ibrt_if.h"
|
|
#include "app_tws_ctrl_thread.h"
|
|
#include "app_tws_ibrt.h"
|
|
#include "app_tws_ibrt_cmd_handler.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
|