pinebuds/services/bt_app/audio_prompt_sbc.cpp

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