3491 lines
94 KiB
C++
3491 lines
94 KiB
C++
/***************************************************************************
|
|
*
|
|
* Copyright 2015-2019 BES.
|
|
* All rights reserved. All unpublished rights reserved.
|
|
*
|
|
* No part of this work may be used or reproduced in any form or by any
|
|
* means, or stored in a database or retrieval system, without prior written
|
|
* permission of BES.
|
|
*
|
|
* Use of this work is governed by a license granted by BES.
|
|
* This work contains confidential and proprietary information of
|
|
* BES. which is protected by copyright, trade secret,
|
|
* trademark and other intellectual property rights.
|
|
*
|
|
****************************************************************************/
|
|
#if (A2DP_DECODER_VER < 2)
|
|
|
|
#ifdef MBED
|
|
#include "mbed.h"
|
|
#include "rtos.h"
|
|
#endif
|
|
// Standard C Included Files
|
|
#include "tgt_hardware.h"
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifdef MBED
|
|
#include "SDFileSystem.h"
|
|
#endif
|
|
#include "analog.h"
|
|
#include "app_audio.h"
|
|
#include "app_overlay.h"
|
|
#include "audioflinger.h"
|
|
#include "cqueue.h"
|
|
#include "hal_codec.h"
|
|
#include "hal_timer.h"
|
|
#include "hal_trace.h"
|
|
#include "hal_uart.h"
|
|
#ifdef RTOS
|
|
#include "cmsis_os.h"
|
|
#endif
|
|
|
|
extern "C" {
|
|
#if defined(A2DP_LHDC_ON)
|
|
#include "hal_sysfreq.h"
|
|
#include "lhdcUtil.h"
|
|
#endif
|
|
}
|
|
|
|
extern "C" {
|
|
#if defined(A2DP_LDAC_ON)
|
|
#include "hal_sysfreq.h"
|
|
// #include "speech_memory.h"
|
|
#include "ldacBT.h"
|
|
#define MED_MEM_HEAP_SIZE (1024 * 20)
|
|
HANDLE_LDAC_BT hLdacData = NULL;
|
|
#endif
|
|
}
|
|
|
|
#include "a2dp_api.h"
|
|
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
#include "audio_resample_ex.h"
|
|
#include "hal_chipid.h"
|
|
#include "hal_sysfreq.h"
|
|
#endif
|
|
|
|
#include "btapp.h"
|
|
#include "cmsis.h"
|
|
#include "hal_location.h"
|
|
|
|
#define TEXT_A2DP_LOC_A(n, l) __attribute__((section(#n "." #l)))
|
|
#define TEXT_A2DP_LOC(n, l) TEXT_A2DP_LOC_A(n, l)
|
|
|
|
#define TEXT_SBC_LOC TEXT_A2DP_LOC(.overlay_a2dp_sbc, __LINE__)
|
|
#define TEXT_AAC_LOC TEXT_A2DP_LOC(.overlay_a2dp_aac, __LINE__)
|
|
#define TEXT_SSC_LOC TEXT_A2DP_LOC(.overlay_a2dp_ssc, __LINE__)
|
|
#define TEXT_LDAC_LOC TEXT_A2DP_LOC(.overlay_a2dp_ldac, __LINE__)
|
|
#define TEXT_LHDC_LOC TEXT_A2DP_LOC(.overlay_a2dp_lhdc, __LINE__)
|
|
|
|
// #define A2DP_AUDIO_SYNC_WITH_LOCAL (1)
|
|
|
|
#define A2DP_AUDIO_SYNC_TRACE(s, ...)
|
|
// TRACE(s, ##__VA_ARGS__)
|
|
|
|
#ifdef A2DP_AUDIO_SYNC_WITH_LOCAL
|
|
#define A2DP_AUDIO_SYNC_WITH_LOCAL_SAMPLERATE_DEFAULT (0)
|
|
#define A2DP_AUDIO_SYNC_WITH_LOCAL_SAMPLERATE_INC (2)
|
|
#define A2DP_AUDIO_SYNC_WITH_LOCAL_SAMPLERATE_DEC (-2)
|
|
#define A2DP_AUDIO_SYNC_WITH_LOCAL_SAMPLERATE_STEP (0.00005f)
|
|
#define A2DPPLAY_SYNC_STATUS_SET (0x01)
|
|
#define A2DPPLAY_SYNC_STATUS_RESET (0x02)
|
|
#define A2DPPLAY_SYNC_STATUS_PROC (0x04)
|
|
|
|
enum A2DP_AUDIO_SYNC_STATUS {
|
|
A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_DEC,
|
|
A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_INC,
|
|
A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_DEFAULT,
|
|
};
|
|
#endif
|
|
|
|
#define A2DPPLAY_CACHE_OK_THRESHOLD (sbc_frame_size << 6)
|
|
|
|
enum A2DPPLAY_STRTEAM_T {
|
|
A2DPPLAY_STRTEAM_PUT = 0,
|
|
A2DPPLAY_STRTEAM_GET,
|
|
A2DPPLAY_STRTEAM_QTY,
|
|
};
|
|
|
|
/* sbc queue */
|
|
#define SBC_TEMP_BUFFER_SIZE 128
|
|
#define SBC_QUEUE_SIZE_DEFAULT (SBC_TEMP_BUFFER_SIZE * 64)
|
|
#define SBC_QUEUE_SIZE (1024 * 11)
|
|
|
|
/* sbc decoder */
|
|
static bool need_init_decoder = true;
|
|
static btif_sbc_decoder_t *sbc_decoder = NULL;
|
|
|
|
static float sbc_eq_band_gain[CFG_HW_AUD_EQ_NUM_BANDS];
|
|
|
|
CQueue sbc_queue;
|
|
|
|
static uint32_t g_sbc_queue_size = SBC_QUEUE_SIZE_DEFAULT;
|
|
static uint16_t sbc_frame_size = SBC_TEMP_BUFFER_SIZE;
|
|
static uint16_t sbc_frame_rev_len = 0;
|
|
static uint16_t sbc_frame_num = 0;
|
|
static uint32_t assumed_mtu_interval = 0;
|
|
|
|
static uint32_t dec_start_time;
|
|
static uint32_t last_dec_time;
|
|
#ifdef A2DP_TRACE_DEC_TIME
|
|
static const bool dec_trace_time = true;
|
|
#else
|
|
static bool dec_trace_time;
|
|
#endif
|
|
static bool dec_reset_queue;
|
|
|
|
static enum APP_AUDIO_CACHE_T a2dp_cache_status = APP_AUDIO_CACHE_QTY;
|
|
|
|
#define A2DP_SYNC_WITH_GET_MUTUX_TIMEROUT_CNT (5)
|
|
#define A2DP_SYNC_WITH_GET_MUTUX_TIMEROUT_MS (20)
|
|
static osThreadId a2dp_put_thread_tid = NULL;
|
|
static bool a2dp_get_need_sync = false;
|
|
|
|
extern enum AUD_SAMPRATE_T a2dp_sample_rate;
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
#ifdef CHIP_BEST1000
|
|
static bool allow_resample = false;
|
|
#else
|
|
static const bool allow_resample = true;
|
|
#endif
|
|
#endif
|
|
|
|
extern int a2dp_timestamp_parser_needsync(void);
|
|
|
|
#define A2DP_SYNC_WITH_GET_MUTUX_ALLOC() \
|
|
do { \
|
|
if (a2dp_put_thread_tid == NULL) { \
|
|
a2dp_put_thread_tid = osThreadGetId(); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define A2DP_SYNC_WITH_GET_MUTUX_FREE() \
|
|
do { \
|
|
a2dp_put_thread_tid = NULL; \
|
|
} while (0)
|
|
|
|
#define A2DP_SYNC_WITH_GET_MUTUX_WAIT() \
|
|
do { \
|
|
a2dp_get_need_sync = true; \
|
|
if (a2dp_put_thread_tid) { \
|
|
osSignalClear(a2dp_put_thread_tid, 0x80); \
|
|
osSignalWait(0x80, A2DP_SYNC_WITH_GET_MUTUX_TIMEROUT_MS); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define A2DP_SYNC_WITH_GET_MUTUX_SET() \
|
|
do { \
|
|
if (a2dp_get_need_sync) { \
|
|
a2dp_get_need_sync = false; \
|
|
if (a2dp_put_thread_tid) { \
|
|
osSignalSet(a2dp_put_thread_tid, 0x80); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
// #define A2DP_SYNC_WITH_PUT_MUTUX (1)
|
|
#define A2DP_SYNC_WITH_PUT_MUTUX_TIMEROUT_CNT (1)
|
|
#define A2DP_SYNC_WITH_PUT_MUTUX_TIMEROUT_MS (3)
|
|
static osThreadId a2dp_get_thread_tid = NULL;
|
|
static bool a2dp_get_put_sync = false;
|
|
|
|
#define A2DP_SYNC_WITH_PUT_MUTUX_ALLOC() \
|
|
do { \
|
|
if (a2dp_get_thread_tid == NULL) { \
|
|
a2dp_get_thread_tid = osThreadGetId(); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define A2DP_SYNC_WITH_PUT_MUTUX_FREE() \
|
|
do { \
|
|
a2dp_get_thread_tid = NULL; \
|
|
} while (0)
|
|
|
|
#define A2DP_SYNC_WITH_PUT_MUTUX_WAIT() \
|
|
do { \
|
|
a2dp_get_put_sync = true; \
|
|
if (a2dp_get_thread_tid) { \
|
|
osSignalClear(a2dp_get_thread_tid, 0x80); \
|
|
osSignalWait(0x5, A2DP_SYNC_WITH_PUT_MUTUX_TIMEROUT_MS); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define A2DP_SYNC_WITH_PUT_MUTUX_SET() \
|
|
do { \
|
|
if (a2dp_get_put_sync) { \
|
|
a2dp_get_put_sync = false; \
|
|
if (a2dp_get_thread_tid) { \
|
|
osSignalSet(a2dp_get_thread_tid, 0x80); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
int a2dp_audio_sbc_set_frame_info(int rcv_len, int frame_num) {
|
|
if ((!rcv_len) || (!frame_num)) {
|
|
return 0;
|
|
}
|
|
|
|
if (sbc_frame_rev_len != rcv_len || sbc_frame_num != frame_num) {
|
|
sbc_frame_rev_len = rcv_len;
|
|
sbc_frame_num = frame_num;
|
|
sbc_frame_size = rcv_len / frame_num;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern struct BT_DEVICE_T app_bt_device;
|
|
extern uint8_t a2dp_channel_num[BT_DEVICE_NUM];
|
|
void expand_1_channel_to_2_channels_16bits(unsigned char *in,
|
|
unsigned int in_len) {
|
|
int cnt = 0;
|
|
int len = in_len;
|
|
short *ptr = (short *)in + in_len / 2;
|
|
short *ptr_out = (short *)in + in_len;
|
|
|
|
while (len > 0) {
|
|
*(ptr_out - 2 * cnt - 1) = *(ptr - cnt);
|
|
*(ptr_out - 2 * cnt - 2) = *(ptr - cnt);
|
|
cnt += 1;
|
|
len -= 2;
|
|
}
|
|
}
|
|
|
|
#ifdef A2DP_AUDIO_SYNC_WITH_LOCAL
|
|
static enum A2DP_AUDIO_SYNC_STATUS sync_status =
|
|
A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_DEFAULT;
|
|
|
|
static int a2dp_audio_sync_proc(uint8_t status, int shift) {
|
|
#if !(defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE))
|
|
struct AF_STREAM_CONFIG_T *cfg;
|
|
|
|
bool need_shift = false;
|
|
static int cur_shift = 0;
|
|
static int dest_shift = 0;
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
if (status & A2DPPLAY_SYNC_STATUS_RESET) {
|
|
sync_status = A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_DEFAULT;
|
|
cur_shift = 0;
|
|
dest_shift = 0;
|
|
}
|
|
if (status & A2DPPLAY_SYNC_STATUS_SET) {
|
|
dest_shift = shift;
|
|
}
|
|
if (cur_shift > dest_shift) {
|
|
cur_shift--;
|
|
need_shift = true;
|
|
}
|
|
if (cur_shift < dest_shift) {
|
|
cur_shift++;
|
|
need_shift = true;
|
|
}
|
|
if (need_shift) {
|
|
A2DP_AUDIO_SYNC_TRACE(1, "a2dp_audio_sync_proc shift:%d\n", cur_shift);
|
|
uint32_t ret =
|
|
af_stream_get_cfg(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK, &cfg, false);
|
|
if (0 == ret) {
|
|
af_codec_tune(AUD_STREAM_PLAYBACK,
|
|
A2DP_AUDIO_SYNC_WITH_LOCAL_SAMPLERATE_STEP * cur_shift);
|
|
}
|
|
}
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
bool a2dp_audio_isrunning(enum A2DPPLAY_STRTEAM_T stream, bool run) {
|
|
static bool stream_running[A2DPPLAY_STRTEAM_QTY] = {false, false};
|
|
|
|
if (stream >= A2DPPLAY_STRTEAM_QTY)
|
|
return false;
|
|
|
|
stream_running[stream] = run;
|
|
|
|
if (stream_running[A2DPPLAY_STRTEAM_PUT] &&
|
|
stream_running[A2DPPLAY_STRTEAM_GET])
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
#if defined(A2DP_AAC_ON)
|
|
#define AAC_READBUF_SIZE (2048)
|
|
|
|
uint32_t aac_maxreadBytes = AAC_READBUF_SIZE;
|
|
uint32_t aac_frame_mute = 0;
|
|
|
|
int decode_aac_frame(unsigned char *pcm_buffer, unsigned int pcm_len);
|
|
#endif
|
|
|
|
#if defined(A2DP_LHDC_ON)
|
|
#include "hal_timer.h"
|
|
#define A2DP_LHDC_DEFAULT_LATENCY 1
|
|
uint8_t latencyIndex = 0;
|
|
uint8_t latencyUpdated = 0;
|
|
|
|
uint16_t latencyTable[] = {50, 150, 300};
|
|
|
|
uint8_t magicTag[] = {'l', 'h', 'd', 'c'};
|
|
typedef struct {
|
|
uint8_t tag[4];
|
|
uint32_t packetLen;
|
|
uint8_t dptr[0];
|
|
} LHDC_HEADER;
|
|
|
|
typedef struct {
|
|
uint16_t frame_len;
|
|
bool isSplit;
|
|
bool isLeft;
|
|
} LHDC_FRAME_HDR;
|
|
|
|
#define LHDC_READBUF_SIZE \
|
|
1024 * 4 /* pick something big enough to hold a bunch of frames */
|
|
uint8_t lhdcTempBuf[LHDC_READBUF_SIZE];
|
|
uint8_t lhdc_input_mid_buf[LHDC_READBUF_SIZE];
|
|
#define L2CAP_MTU 672
|
|
#define PACKET_MTU_SIZE (L2CAP_MTU - 12)
|
|
|
|
void initial_lhdc_assemble_packet(bool splitFlg);
|
|
/**
|
|
* get lhdc frame header
|
|
*/
|
|
bool get_lhdc_header(uint8_t *in, LHDC_FRAME_HDR *h);
|
|
|
|
uint32_t get_lhdc_frame(uint8_t *frame_q, size_t q_available_size,
|
|
uint8_t *out_buf, uint32_t *out_size,
|
|
uint32_t *out_frames);
|
|
/**
|
|
* Grabe lhdc data from queue
|
|
*/
|
|
int get_lhdc_data(unsigned char *frame, unsigned int len);
|
|
int assemble_lhdc_packet(uint8_t *input, uint32_t input_len, uint8_t **pLout,
|
|
uint32_t *pLlen, uint8_t **pRout, uint32_t *pRlen);
|
|
|
|
#endif
|
|
#if defined(A2DP_LDAC_ON)
|
|
#define LDAC_FRAME_LEN_INDEX_106 106
|
|
#define LDAC_FRAME_LEN_INDEX_128 128
|
|
#define LDAC_FRAME_LEN_INDEX_160 160
|
|
#define LDAC_FRAME_LEN_INDEX_216 216
|
|
#define LDAC_FRAME_LEN_INDEX_326 326
|
|
|
|
uint8_t get_ldac_frame_num(uint16_t frame_length_index) {
|
|
uint8_t frame_num = 0;
|
|
switch (frame_length_index) {
|
|
case LDAC_FRAME_LEN_INDEX_106:
|
|
frame_num = 6;
|
|
break;
|
|
case LDAC_FRAME_LEN_INDEX_128:
|
|
frame_num = 5;
|
|
break;
|
|
case LDAC_FRAME_LEN_INDEX_160:
|
|
frame_num = 4;
|
|
break;
|
|
case LDAC_FRAME_LEN_INDEX_216:
|
|
frame_num = 3;
|
|
break;
|
|
case LDAC_FRAME_LEN_INDEX_326:
|
|
frame_num = 2;
|
|
break;
|
|
default:
|
|
ASSERT(0, "Unknown ldac frame format: %d !!!!!", frame_length_index);
|
|
break;
|
|
}
|
|
return frame_num;
|
|
}
|
|
#endif
|
|
|
|
#define DELAY_MTU_LIMIT 8
|
|
|
|
static volatile int delay_mtu_limit = DELAY_MTU_LIMIT;
|
|
static volatile int mtu_count = 0;
|
|
static volatile int delay_mtu_count = 0;
|
|
bool app_bt_stream_trigger_onprocess(void);
|
|
void app_bt_stream_trigger_start(uint8_t offset);
|
|
|
|
void a2dp_audio_set_mtu_limit(uint8_t mut) { delay_mtu_limit = mut; }
|
|
|
|
static uint32_t old_t = 0;
|
|
static uint32_t ssb_err = 0;
|
|
static uint32_t ssb_err_max = 0;
|
|
static uint32_t ssb_err_min = 0xffffffff;
|
|
static uint32_t lagest_mtu_in1s = 0;
|
|
static uint32_t least_mtu_in1s = 0xffffffff;
|
|
static uint32_t check_interval = 50;
|
|
|
|
int store_sbc_buffer(unsigned char *buf, unsigned int len) {
|
|
int POSSIBLY_UNUSED size;
|
|
int cnt = 0;
|
|
int nRet = 0;
|
|
#if defined(A2DP_LHDC_ON)
|
|
uint32_t newLen = 0;
|
|
bool mtu_plus = false;
|
|
#endif
|
|
#if defined(A2DP_LDAC_ON)
|
|
uint16_t frame_length_index = 0;
|
|
#endif
|
|
uint32_t now_t = TICKS_TO_MS(hal_sys_timer_get());
|
|
ssb_err = now_t - old_t;
|
|
old_t = now_t;
|
|
|
|
if (ssb_err < 500) {
|
|
if (ssb_err_max < ssb_err)
|
|
ssb_err_max = ssb_err;
|
|
if (ssb_err_min > ssb_err)
|
|
ssb_err_min = ssb_err;
|
|
}
|
|
|
|
if (!a2dp_audio_isrunning(A2DPPLAY_STRTEAM_PUT, true)) {
|
|
TRACE(3, "%s not ready:%d cache_status:%d", __func__, len,
|
|
a2dp_cache_status);
|
|
}
|
|
uint8_t overlay_id = app_get_current_overlay();
|
|
|
|
if (overlay_id == APP_OVERLAY_A2DP) {
|
|
// TRACE(8,"sbc %d %x %x %x %x mtu_count:%d
|
|
// sbcqueue:%d,sbc_frame_rev_len=%d", len, buf[0],
|
|
// buf[1],buf[2],buf[3],mtu_count,APP_AUDIO_LengthOfCQueue(&sbc_queue),sbc_frame_rev_len);
|
|
}
|
|
#if defined(A2DP_LDAC_ON)
|
|
else if (overlay_id == APP_OVERLAY_A2DP_LDAC) {
|
|
uint16_t data1 = (uint16_t)(buf[1] & 0x07);
|
|
uint16_t data2 = (uint16_t)buf[2];
|
|
frame_length_index = ((data1 << 6 & 0xFFFF) | (data2 >> 2 & 0xFFFF));
|
|
sbc_frame_num = get_ldac_frame_num(frame_length_index);
|
|
// TRACE(10,"ldac %d %d %x %x %x %x mtu_count:%d
|
|
// sbcqueue:%d,sbc_frame_rev_len=%d,sbc_frame_num %d", len,
|
|
// sbc_frame_size,buf[0],
|
|
// buf[1],buf[2],buf[3],mtu_count,APP_AUDIO_LengthOfCQueue(&sbc_queue),sbc_frame_rev_len,sbc_frame_num);
|
|
}
|
|
#endif
|
|
else {
|
|
// TRACE(8,"AAC %d %x %x %x %x mtu_count:%d
|
|
// sbcqueue:%d,sbc_frame_rev_len=%d", len, buf[0],
|
|
// buf[1],buf[2],buf[3],mtu_count,APP_AUDIO_LengthOfCQueue(&sbc_queue),sbc_frame_rev_len);
|
|
}
|
|
|
|
switch (a2dp_cache_status) {
|
|
case APP_AUDIO_CACHE_CACHEING: {
|
|
#if defined(A2DP_LHDC_ON)
|
|
newLen = 0;
|
|
if (overlay_id == APP_OVERLAY_A2DP_LHDC) {
|
|
uint32_t lSize = 0, rSize = 0;
|
|
uint8_t *lPTR = NULL, *rPTR = NULL;
|
|
// TRACE(2,"%s:APP_AUDIO_CACHE_CACHEING Enter len = %d",__func__, len);
|
|
// TRACE(2,"%s: input len(%d)",__func__, len);
|
|
if (assemble_lhdc_packet(buf, len, &lPTR, &lSize, &rPTR, &rSize) > 0) {
|
|
if (lPTR != NULL && lSize != 0) {
|
|
newLen = lSize + sizeof(LHDC_HEADER);
|
|
memcpy(lhdcTempBuf + sizeof(LHDC_HEADER), lPTR, lSize);
|
|
memcpy(lhdcTempBuf, magicTag, sizeof(magicTag));
|
|
memcpy(lhdcTempBuf + sizeof(magicTag), &lSize, sizeof(unsigned int));
|
|
mtu_plus = true;
|
|
}
|
|
} else {
|
|
nRet = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
#if defined(A2DP_LHDC_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_LHDC) {
|
|
nRet = APP_AUDIO_EnCQueue(&sbc_queue, lhdcTempBuf, newLen);
|
|
size = APP_AUDIO_LengthOfCQueue(&sbc_queue);
|
|
} else
|
|
#endif
|
|
|
|
#if defined(A2DP_LDAC_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_LDAC) {
|
|
// if(bt_sbc_player_get_codec_type() == BTIF_AVDTP_CODEC_TYPE_NON_A2DP){
|
|
nRet = APP_AUDIO_EnCQueue(&sbc_queue, buf, len);
|
|
size = APP_AUDIO_LengthOfCQueue(&sbc_queue);
|
|
} else
|
|
#endif
|
|
|
|
#if defined(A2DP_AAC_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_AAC) {
|
|
nRet = AvailableOfCQueue(&sbc_queue);
|
|
if (nRet >= (int)(len + 2)) {
|
|
nRet = APP_AUDIO_EnCQueue(&sbc_queue, (uint8_t *)&len, 2);
|
|
nRet = APP_AUDIO_EnCQueue(&sbc_queue, buf, len);
|
|
} else
|
|
nRet = CQ_ERR;
|
|
} else
|
|
#endif
|
|
|
|
{
|
|
nRet = APP_AUDIO_EnCQueue(&sbc_queue, buf, len);
|
|
}
|
|
size = APP_AUDIO_LengthOfCQueue(&sbc_queue);
|
|
#if defined(A2DP_LHDC_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_LHDC) {
|
|
if (mtu_plus) {
|
|
if (delay_mtu_count == 0)
|
|
mtu_count = 0;
|
|
delay_mtu_count++;
|
|
mtu_count++;
|
|
mtu_plus = false;
|
|
}
|
|
} else {
|
|
#endif
|
|
if (overlay_id == APP_OVERLAY_A2DP
|
|
#if defined(A2DP_LDAC_ON)
|
|
|| overlay_id == APP_OVERLAY_A2DP_LDAC
|
|
#endif
|
|
) {
|
|
if (delay_mtu_count == 0)
|
|
mtu_count = 0;
|
|
delay_mtu_count += sbc_frame_num;
|
|
mtu_count += sbc_frame_num;
|
|
// TRACE(1,"+mtu_count =
|
|
//%d",mtu_count);
|
|
} else {
|
|
if (delay_mtu_count == 0)
|
|
mtu_count = 0;
|
|
delay_mtu_count++;
|
|
mtu_count++;
|
|
}
|
|
#if defined(A2DP_LHDC_ON)
|
|
}
|
|
#endif
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
bool flag = 0;
|
|
#if defined(A2DP_AAC_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_AAC)
|
|
flag = 1;
|
|
#endif
|
|
#if defined(A2DP_SCALABLE_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_SCALABLE)
|
|
flag = 1;
|
|
#endif
|
|
#if defined(A2DP_LHDC_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_LHDC)
|
|
flag = 1;
|
|
#endif
|
|
#if defined(A2DP_LDAC_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_LDAC) {
|
|
flag = 1;
|
|
TRACE(0, "y# ldac cache");
|
|
}
|
|
#endif
|
|
|
|
if (flag) {
|
|
#ifdef __A2DP_PLAYER_USE_BT_TRIGGER__
|
|
if (app_bt_stream_trigger_onprocess() && mtu_count) {
|
|
TRACE(0, "cache ok use dma trigger\n");
|
|
a2dp_cache_status = APP_AUDIO_CACHE_OK;
|
|
app_bt_stream_trigger_start(0);
|
|
}
|
|
#else
|
|
if (delay_mtu_count >= delay_mtu_limit) {
|
|
TRACE(2, "aac cache ok:%d,mtu_count=%d\n", size, mtu_count);
|
|
a2dp_cache_status = APP_AUDIO_CACHE_OK;
|
|
}
|
|
#endif
|
|
} else {
|
|
#ifdef __A2DP_PLAYER_USE_BT_TRIGGER__
|
|
if (app_bt_stream_trigger_onprocess() && mtu_count) {
|
|
TRACE(0, "cache ok use dma trigger\n");
|
|
a2dp_cache_status = APP_AUDIO_CACHE_OK;
|
|
app_bt_stream_trigger_start(0);
|
|
}
|
|
#else
|
|
if (delay_mtu_count >= delay_mtu_limit) {
|
|
TRACE(4, "cache ok:%d,%d,%d,mtu_count=%d\n", size, len, sbc_frame_size,
|
|
mtu_count);
|
|
a2dp_cache_status = APP_AUDIO_CACHE_OK;
|
|
} else if (sbc_frame_size && (size >= A2DPPLAY_CACHE_OK_THRESHOLD)) {
|
|
TRACE(2, "cache ok:%d,mtu_count=%d\n", size, mtu_count);
|
|
a2dp_cache_status = APP_AUDIO_CACHE_OK;
|
|
} else {
|
|
TRACE(1, "cache add:%d\n", len);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (nRet == CQ_ERR) {
|
|
TRACE(0, "cache add overflow\n");
|
|
a2dp_cache_status = APP_AUDIO_CACHE_OK;
|
|
}
|
|
if (a2dp_cache_status == APP_AUDIO_CACHE_OK) {
|
|
old_t = 0;
|
|
ssb_err = 0;
|
|
ssb_err_max = 0;
|
|
ssb_err_min = 0xffffffff;
|
|
lagest_mtu_in1s = 0;
|
|
least_mtu_in1s = 0xffffffff;
|
|
check_interval = 50;
|
|
#ifdef __LOCK_AUDIO_THREAD__
|
|
af_unlock_thread();
|
|
#endif
|
|
A2DP_SYNC_WITH_GET_MUTUX_ALLOC();
|
|
A2DP_SYNC_WITH_GET_MUTUX_WAIT();
|
|
#ifdef __LOCK_AUDIO_THREAD__
|
|
af_lock_thread();
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case APP_AUDIO_CACHE_OK: {
|
|
delay_mtu_count = 0;
|
|
#if defined(A2DP_LHDC_ON)
|
|
newLen = 0;
|
|
if (overlay_id == APP_OVERLAY_A2DP_LHDC) {
|
|
uint32_t lSize = 0, rSize = 0;
|
|
uint8_t *lPTR = NULL, *rPTR = NULL;
|
|
// TRACE(2,"%s: input len(%d)",__func__, len);
|
|
if (assemble_lhdc_packet(buf, len, &lPTR, &lSize, &rPTR, &rSize) > 0) {
|
|
if (lPTR != NULL && lSize != 0) {
|
|
newLen = lSize + sizeof(LHDC_HEADER);
|
|
memcpy(lhdcTempBuf + sizeof(LHDC_HEADER), lPTR, lSize);
|
|
memcpy(lhdcTempBuf, magicTag, sizeof(magicTag));
|
|
memcpy(lhdcTempBuf + sizeof(magicTag), &lSize, sizeof(unsigned int));
|
|
mtu_plus = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
do {
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
#if defined(A2DP_LHDC_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_LHDC) {
|
|
nRet = APP_AUDIO_EnCQueue(&sbc_queue, lhdcTempBuf, newLen);
|
|
} else
|
|
#endif
|
|
#if defined(A2DP_AAC_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_AAC) {
|
|
nRet = AvailableOfCQueue(&sbc_queue);
|
|
if (nRet >= (int)(len + 2)) {
|
|
nRet = APP_AUDIO_EnCQueue(&sbc_queue, (uint8_t *)&len, 2);
|
|
nRet = APP_AUDIO_EnCQueue(&sbc_queue, buf, len);
|
|
} else
|
|
nRet = CQ_ERR;
|
|
} else
|
|
#endif
|
|
|
|
{
|
|
nRet = APP_AUDIO_EnCQueue(&sbc_queue, buf, len);
|
|
}
|
|
if (CQ_OK == nRet) {
|
|
#if defined(A2DP_LHDC_ON)
|
|
if (overlay_id == APP_OVERLAY_A2DP_LHDC) {
|
|
if (mtu_plus) {
|
|
mtu_count++;
|
|
mtu_plus = false;
|
|
}
|
|
} else {
|
|
#endif
|
|
if (overlay_id == APP_OVERLAY_A2DP
|
|
#if defined(A2DP_LDAC_ON)
|
|
|| overlay_id == APP_OVERLAY_A2DP_LDAC
|
|
#endif
|
|
) {
|
|
mtu_count += sbc_frame_num;
|
|
// TRACE(1,"+mtu_count
|
|
//= %d",mtu_count);
|
|
} else {
|
|
mtu_count++;
|
|
}
|
|
#if defined(A2DP_LHDC_ON)
|
|
}
|
|
#endif
|
|
}
|
|
// size = APP_AUDIO_LengthOfCQueue(&sbc_queue);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
// TRACE(3,"cache add:%d %d/%d \n", len, size,
|
|
// g_sbc_queue_size);
|
|
if (CQ_OK == nRet) {
|
|
nRet = 0;
|
|
break;
|
|
} else {
|
|
TRACE(1, "cache flow control:%d\n", cnt);
|
|
#ifdef A2DP_AUDIO_SYNC_WITH_LOCAL
|
|
a2dp_audio_sync_proc(A2DPPLAY_SYNC_STATUS_SET, 0);
|
|
#endif
|
|
nRet = -1;
|
|
#ifdef __LOCK_AUDIO_THREAD__
|
|
af_unlock_thread();
|
|
#endif
|
|
A2DP_SYNC_WITH_GET_MUTUX_ALLOC();
|
|
A2DP_SYNC_WITH_GET_MUTUX_WAIT();
|
|
#ifdef __LOCK_AUDIO_THREAD__
|
|
af_lock_thread();
|
|
#endif
|
|
}
|
|
} while (cnt++ < A2DP_SYNC_WITH_GET_MUTUX_TIMEROUT_CNT);
|
|
break;
|
|
}
|
|
case APP_AUDIO_CACHE_QTY:
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef A2DP_SYNC_WITH_PUT_MUTUX
|
|
A2DP_SYNC_WITH_PUT_MUTUX_SET();
|
|
#endif
|
|
|
|
if (nRet) {
|
|
TRACE(0, "cache overflow\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if defined(A2DP_LDAC_ON)
|
|
|
|
#define LDAC_READBUF_SIZE \
|
|
1024 /* pick something big enough to hold a bunch of frames */
|
|
uint8_t ldac_input_mid_buf[LDAC_READBUF_SIZE + 2];
|
|
|
|
/*
|
|
* Grabe lhdc data from queue
|
|
*/
|
|
TEXT_LDAC_LOC
|
|
int get_ldac_data(unsigned char *frame, unsigned int len) {
|
|
int status;
|
|
unsigned int len1 = 0, len2 = 0;
|
|
unsigned char *e1 = NULL, *e2 = NULL;
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
unsigned int queu_len = (unsigned int)LengthOfCQueue(&sbc_queue);
|
|
len = queu_len < len ? queu_len : len;
|
|
status = PeekCQueue(&sbc_queue, len, &e1, &len1, &e2, &len2);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
if (status != CQ_OK) {
|
|
return 0;
|
|
}
|
|
|
|
if (e1) {
|
|
memcpy(frame, e1, len1);
|
|
}
|
|
if (e2) {
|
|
memcpy(frame + len1, e2, len2);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* Decode LDAC data...
|
|
*/
|
|
// #include "os_tcb.h"
|
|
extern const char *get_error_code_string(int error_code);
|
|
extern int app_audio_mempool_force_set_buff_used(uint32_t size);
|
|
extern uint32_t ldac_buffer_used;
|
|
uint32_t skip_ldac_frame = 2;
|
|
static bool ldac_recovery;
|
|
|
|
TEXT_LDAC_LOC
|
|
int ldac_dec_init(void) {
|
|
if (hLdacData) {
|
|
return 0;
|
|
}
|
|
|
|
int sample_rate = bt_get_ladc_sample_rate();
|
|
int channel_mode = bt_ldac_player_get_channelmode();
|
|
TRACE(2, "a2dp_audio_init sample Rate=%d, channel_mode = %d\n", sample_rate,
|
|
channel_mode);
|
|
// TRACE(1,"sys freq calc : %d\n", hal_sys_timer_calc_cpu_freq(0));
|
|
TRACE(0, "ldac need init here!!!! \n");
|
|
if ((hLdacData = ldacBT_get_handle()) == (HANDLE_LDAC_BT)NULL) {
|
|
TRACE(0, "Error: Can not Get LDAC Handle!\n");
|
|
return 1;
|
|
}
|
|
int result =
|
|
ldacBT_init_handle_decode(hLdacData, channel_mode, sample_rate, 0, 0, 0);
|
|
if (result) {
|
|
TRACE(1, "[ERR] Initializing LDAC Handle for synthesis! Error code %s\n",
|
|
get_error_code_string(ldacBT_get_error_code(hLdacData)));
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
TEXT_LDAC_LOC
|
|
int ldac_dec_deinit(void) {
|
|
ldacBT_free_handle(hLdacData);
|
|
hLdacData = NULL;
|
|
app_audio_mempool_force_set_buff_used(ldac_buffer_used);
|
|
return 0;
|
|
}
|
|
|
|
TEXT_LDAC_LOC
|
|
int ldac_seek_header(uint32_t n_read_bytes, uint32_t *p_parse_bytes) {
|
|
int channel_mode = 0;
|
|
int sample_rate = 0;
|
|
|
|
if (p_parse_bytes) {
|
|
*p_parse_bytes = 0;
|
|
}
|
|
|
|
if (n_read_bytes < 2) {
|
|
return 1;
|
|
}
|
|
|
|
/* update used_bytes to skip current ldac frame */
|
|
sample_rate = bt_get_ladc_sample_rate();
|
|
channel_mode = bt_ldac_player_get_channelmode();
|
|
|
|
if (1) {
|
|
unsigned char *pStream, *p1st, *p2nd, *pTail;
|
|
unsigned char sync2 = 0;
|
|
unsigned char cci = 0;
|
|
p1st = p2nd = NULL;
|
|
pStream = ldac_input_mid_buf;
|
|
pTail = ldac_input_mid_buf + n_read_bytes - 2;
|
|
switch (channel_mode) {
|
|
case LDACBT_CHANNEL_MODE_MONO:
|
|
cci = LDAC_CCI_MONO;
|
|
break;
|
|
case LDACBT_CHANNEL_MODE_DUAL_CHANNEL:
|
|
cci = LDAC_CCI_DUAL_CHANNEL;
|
|
break;
|
|
case LDACBT_CHANNEL_MODE_STEREO:
|
|
default:
|
|
cci = LDAC_CCI_STEREO;
|
|
break;
|
|
}
|
|
if (0) {
|
|
;
|
|
} else if (sample_rate == 1 * 44100) {
|
|
sync2 = (0 << 5) | (cci << 3);
|
|
} else if (sample_rate == 1 * 48000) {
|
|
sync2 = (1 << 5) | (cci << 3);
|
|
} else if (sample_rate == 2 * 44100) {
|
|
sync2 = (2 << 5) | (cci << 3);
|
|
} else if (sample_rate == 2 * 48000) {
|
|
sync2 = (3 << 5) | (cci << 3);
|
|
}
|
|
while (pStream < pTail) {
|
|
if (pStream[0] == 0xAA) { /* syncword */
|
|
if ((pStream[1] & 0xF8) == sync2) {
|
|
if (p1st == NULL) {
|
|
p1st = pStream;
|
|
} /* 1st syncword found */
|
|
else { /* 2nd syncword found, this frame must decode next time */
|
|
p2nd = pStream;
|
|
if (p_parse_bytes) {
|
|
*p_parse_bytes = pStream - ldac_input_mid_buf;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
++pStream;
|
|
}
|
|
if (p2nd == NULL) {
|
|
/* faild to find next frame header */
|
|
TRACE(0, "Error: Can not get next LDAC frame_header!\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
TEXT_LDAC_LOC
|
|
int load_ldac_frame(uint8_t **p_data, uint32_t *p_len) {
|
|
#if 1
|
|
uint8_t retry = 0;
|
|
int len;
|
|
int result = 0;
|
|
|
|
if (hLdacData == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
while (true) {
|
|
len = get_ldac_data(ldac_input_mid_buf, LDAC_READBUF_SIZE);
|
|
if (len == 0) {
|
|
if (retry++ >= A2DP_SYNC_WITH_PUT_MUTUX_TIMEROUT_CNT) {
|
|
TRACE(0, "ldac No Data return");
|
|
result = 1;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (ldac_recovery) {
|
|
uint32_t parse_bytes;
|
|
|
|
result = ldac_seek_header(len, &parse_bytes);
|
|
if (result == 0) {
|
|
ldac_recovery = false;
|
|
}
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
DeCQueue(&sbc_queue, 0, parse_bytes);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (skip_ldac_frame > 0) {
|
|
skip_ldac_frame--;
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
TEXT_LDAC_LOC
|
|
int decode_ldac_frame(uint8_t *out, uint32_t out_max, uint32_t *p_out_len,
|
|
const uint8_t *in, uint32_t in_len,
|
|
uint32_t *p_consume_len) {
|
|
int result;
|
|
int n_read_bytes, used_bytes, wrote_bytes;
|
|
|
|
n_read_bytes = LDAC_READBUF_SIZE;
|
|
|
|
result =
|
|
ldacBT_decode(hLdacData, ldac_input_mid_buf, out, LDACBT_SMPL_FMT_S16,
|
|
n_read_bytes, &used_bytes, &wrote_bytes);
|
|
|
|
if (p_out_len) {
|
|
*p_out_len = wrote_bytes;
|
|
}
|
|
if (p_consume_len) {
|
|
*p_consume_len = used_bytes;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
TEXT_LDAC_LOC
|
|
int consume_ldac_frame(const uint8_t *data, uint32_t len) {
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
DeCQueue(&sbc_queue, 0, len);
|
|
mtu_count--;
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
|
|
return 0;
|
|
}
|
|
|
|
TEXT_LDAC_LOC
|
|
void decode_ldac_end(uint32_t expect_out_len, uint32_t out_len,
|
|
uint32_t in_len) {
|
|
if (expect_out_len != out_len) {
|
|
if (hLdacData) {
|
|
int error_code;
|
|
error_code = ldacBT_get_error_code(hLdacData);
|
|
TRACE(6, "error_code = %4d, %4d, %4d,FrameHeader[%02x:%02x:%02x]\n",
|
|
LDACBT_API_ERR(error_code), LDACBT_HANDLE_ERR(error_code),
|
|
LDACBT_BLOCK_ERR(error_code), ldac_input_mid_buf[0],
|
|
ldac_input_mid_buf[1], ldac_input_mid_buf[2]);
|
|
if (LDACBT_FATAL(error_code)) {
|
|
ldac_dec_deinit();
|
|
}
|
|
}
|
|
if (hLdacData == NULL) {
|
|
/* Recovery Process */
|
|
int result;
|
|
result = ldac_dec_init();
|
|
if (result) {
|
|
ldac_dec_deinit();
|
|
}
|
|
ldac_recovery = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(A2DP_AAC_ON)
|
|
#include "aacdecoder_lib.h"
|
|
#include "aacenc_lib.h"
|
|
#include "heap_api.h"
|
|
#define DECODE_AAC_PCM_FRAME_LENGTH (1024 * 4)
|
|
|
|
HANDLE_AACDECODER aacDec_handle = NULL;
|
|
#define AAC_MEMPOLL_SIZE (40596)
|
|
heap_handle_t aac_memhandle = NULL;
|
|
uint8_t *aac_mempoll = NULL;
|
|
|
|
static unsigned char aac_input_mid_buf[AAC_READBUF_SIZE];
|
|
STATIC_ASSERT(sizeof(aac_input_mid_buf) >= 2048, "aac_input_mid_buf too small");
|
|
static uint32_t byte_in_buffer = 0;
|
|
|
|
int aac_meminit() {
|
|
int ret = 1;
|
|
if (aac_mempoll == NULL)
|
|
ret =
|
|
app_audio_mempool_get_buff((uint8_t **)&aac_mempoll, AAC_MEMPOLL_SIZE);
|
|
if (ret > 0 && aac_memhandle == NULL) {
|
|
aac_memhandle = heap_register(aac_mempoll, AAC_MEMPOLL_SIZE);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void aac_memdeinit() {
|
|
extern int total_calloc;
|
|
aac_mempoll = 0;
|
|
aac_memhandle = 0;
|
|
total_calloc = 0;
|
|
byte_in_buffer = 0;
|
|
}
|
|
|
|
volatile int aac_slave_codec_patch_bytes = 0;
|
|
|
|
int aacdec_init(void) {
|
|
if (aacDec_handle == NULL) {
|
|
TRANSPORT_TYPE transportFmt = TT_MP4_LATM_MCP1;
|
|
aacDec_handle = aacDecoder_Open(transportFmt, 1 /* nrOfLayers */);
|
|
|
|
if (!aacDec_handle) {
|
|
TRACE(1, "%s Error initializing AAC decoder", __func__);
|
|
return 1;
|
|
}
|
|
aacDecoder_SetParam(aacDec_handle, AAC_PCM_LIMITER_ENABLE, 0);
|
|
aacDecoder_SetParam(aacDec_handle, AAC_DRC_ATTENUATION_FACTOR, 0);
|
|
aacDecoder_SetParam(aacDec_handle, AAC_DRC_BOOST_FACTOR, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int aacdec_deinit(void) {
|
|
if (aacDec_handle != NULL) {
|
|
aacDecoder_Close(aacDec_handle);
|
|
aacDec_handle = NULL;
|
|
size_t total = 0, used = 0, max_used = 0;
|
|
heap_memory_info(aac_memhandle, &total, &used, &max_used);
|
|
TRACE(3, "AAC MALLOC MEM: total - %d, used - %d, max_used - %d.", total,
|
|
used, max_used);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
TEXT_AAC_LOC
|
|
static int pcm_buffer_read_no_protction(CQueue *queue, uint8_t *buff,
|
|
uint16_t len) {
|
|
uint8_t *e1 = NULL, *e2 = NULL;
|
|
unsigned int len1 = 0, len2 = 0;
|
|
int status;
|
|
status = PeekCQueue(queue, len, &e1, &len1, &e2, &len2);
|
|
// TRACE(5,"pcm_buffer_read_no_protction
|
|
// len=%d,len1=%d,len2=%d,e1=0x%x,e2=0x%x",len,len1,len2,e1,e2);
|
|
if (len == (len1 + len2)) {
|
|
memcpy(buff, e1, len1);
|
|
memcpy(buff + len1, e2, len2);
|
|
DeCQueue(queue, 0, len);
|
|
} else {
|
|
memset(buff, 0x00, len);
|
|
status = -1;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
TEXT_AAC_LOC
|
|
int load_aac_frame(uint8_t **p_data, uint32_t *p_len) {
|
|
// uint32_t lock;
|
|
unsigned short aac_len = 0;
|
|
int status;
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
status = pcm_buffer_read_no_protction(&sbc_queue, (uint8_t *)&aac_len, 2);
|
|
if (status != CQ_OK) {
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
// a2dp_cache_status = APP_AUDIO_CACHE_CACHEING;
|
|
return 1;
|
|
}
|
|
|
|
if (aac_len < 64) {
|
|
aac_maxreadBytes = 64;
|
|
} else if (aac_len < 128) {
|
|
aac_maxreadBytes = 128;
|
|
} else if (aac_len < 256) {
|
|
aac_maxreadBytes = 256;
|
|
} else if (aac_len < 512) {
|
|
aac_maxreadBytes = 512;
|
|
} else if (aac_len < 1024) {
|
|
aac_maxreadBytes = 1024;
|
|
} else {
|
|
if (aac_len >= 2048) {
|
|
// Header is bad!
|
|
dec_reset_queue = true;
|
|
return 2;
|
|
}
|
|
aac_maxreadBytes = 2048;
|
|
}
|
|
|
|
status = pcm_buffer_read_no_protction(&sbc_queue, aac_input_mid_buf, aac_len);
|
|
if (status != CQ_OK) {
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
// a2dp_cache_status = APP_AUDIO_CACHE_CACHEING;
|
|
return 1;
|
|
}
|
|
mtu_count--;
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
|
|
// uint32_t a2dp_queue_bytes = LengthOfCQueue(&sbc_queue);
|
|
// TRACE(3,"d aac_len=%d, a2dp_bytes=%d, aac_maxreadBytes=%d\n", aac_len,
|
|
// a2dp_queue_bytes, aac_maxreadBytes);
|
|
|
|
if (p_data) {
|
|
*p_data = (unsigned char *)aac_input_mid_buf;
|
|
}
|
|
if (p_len) {
|
|
*p_len = aac_maxreadBytes;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
TEXT_AAC_LOC
|
|
int decode_aac_frame(uint8_t *out, uint32_t out_max, uint32_t *p_out_len,
|
|
const uint8_t *in, uint32_t in_len,
|
|
uint32_t *p_consume_len) {
|
|
unsigned int bufferSize = in_len;
|
|
unsigned int bytesValid = in_len;
|
|
AAC_DECODER_ERROR err = AAC_DEC_OK;
|
|
CStreamInfo *info = NULL;
|
|
|
|
// at leat should large than AAC_MAX_NSAMPS*2 (one channel)
|
|
if (out_max < 2048) {
|
|
TRACE(2, "%s daac pcm_len = %d \n", __func__, out_max);
|
|
return 1;
|
|
}
|
|
|
|
if (a2dp_channel_num[app_bt_device.curr_a2dp_stream_id] == 1) {
|
|
out_max >>= 1;
|
|
}
|
|
|
|
// int tt = hal_sys_timer_get();
|
|
err =
|
|
aacDecoder_Fill(aacDec_handle, (uint8_t **)&in, &bufferSize, &bytesValid);
|
|
if (err != AAC_DEC_OK) {
|
|
TRACE(1, "aacDecoder_Fill failed:0x%x", err);
|
|
// if aac failed reopen it again
|
|
if (is_aacDecoder_Close(aacDec_handle)) {
|
|
aacDec_handle = NULL;
|
|
aacdec_init();
|
|
TRACE(1, "%s reopen aac codec \n", __func__);
|
|
}
|
|
return 2;
|
|
}
|
|
/* decode one AAC frame */
|
|
err = aacDecoder_DecodeFrame(aacDec_handle, (short *)out, out_max / 2,
|
|
0 /* flags */);
|
|
|
|
#if 0
|
|
TRACE_IMM(0," ");
|
|
for(uint32_t i=0;i<aac_len;i=i+32)
|
|
{
|
|
DUMP8("%02x", &(aac_input_mid_buf[i]), 32);
|
|
}
|
|
#endif
|
|
|
|
if (err != AAC_DEC_OK) {
|
|
TRACE(1, "aacDecoder_DecodeFrame failed:0x%x", err);
|
|
// if aac failed reopen it again
|
|
if (is_aacDecoder_Close(aacDec_handle)) {
|
|
aacDec_handle = NULL;
|
|
aacdec_init();
|
|
TRACE(1, "%s reopen aac codec \n", __func__);
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
info = aacDecoder_GetStreamInfo(aacDec_handle);
|
|
|
|
if (!info || info->sampleRate <= 0) {
|
|
TRACE(2, "%s Invalid stream info %d", __func__, info->sampleRate);
|
|
return 4;
|
|
}
|
|
int frame_len =
|
|
info->frameSize * info->numChannels * 2; // sizeof(pcm_buffer[0]);
|
|
// TRACE(4,"aac: %d,frameSize=%d,numChannels=%d use %d ms\n",
|
|
// frame_len,info->frameSize,info->numChannels,TICKS_TO_MS(hal_sys_timer_get()-tt));
|
|
|
|
if (p_out_len) {
|
|
*p_out_len = frame_len;
|
|
}
|
|
if (p_consume_len) {
|
|
*p_consume_len = in_len - bytesValid;
|
|
}
|
|
|
|
if (a2dp_channel_num[app_bt_device.curr_a2dp_stream_id] == 1) {
|
|
expand_1_channel_to_2_channels_16bits(out, frame_len);
|
|
*p_out_len = frame_len << 1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef A2DP_EQ_24BIT
|
|
static void convert_16bit_to_24bit(int32_t *out, int16_t *in, int len) {
|
|
for (int i = len - 1; i >= 0; i--) {
|
|
out[i] = ((int32_t)in[i] << 8);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(A2DP_SCALABLE_ON)
|
|
|
|
extern "C" {
|
|
#include "ssc.h"
|
|
}
|
|
|
|
unsigned char *scalable_input_mid_buf = NULL;
|
|
unsigned char *scalable_decoder_place = NULL;
|
|
unsigned char *scalable_decoder_temp_buf = NULL;
|
|
void *hSSDecoder = NULL;
|
|
|
|
#if 0
|
|
uint8_t ss_dump[484*10];
|
|
uint32_t ss_dump_index = 0;
|
|
#endif
|
|
extern "C" int ssc_decoder_init(void *s, int channels, int Fs);
|
|
|
|
TEXT_SSC_LOC
|
|
void ss_to_24bit_buf(int32_t *out, int32_t *in, int size) {
|
|
for (int i = 0; i < size; i++) {
|
|
out[i] = in[i];
|
|
}
|
|
}
|
|
|
|
TEXT_SSC_LOC
|
|
void ss_to_16bit_buf(int16_t *out, int32_t *in, int size) {
|
|
for (int i = 0; i < size; i++) {
|
|
out[i] = in[i];
|
|
}
|
|
}
|
|
|
|
static int ss_pcm_buff[SCALABLE_FRAME_SIZE << 1];
|
|
static int scalable_uhq_flag = 0;
|
|
|
|
TEXT_SSC_LOC
|
|
int load_scalable_frame(uint8_t **p_data, uint32_t *p_len) {
|
|
int hw_tmp, len, bitrate_bps, frame_size;
|
|
int r = 0;
|
|
unsigned char *e1 = NULL, *e2 = NULL;
|
|
unsigned int len1 = 0, len2 = 0;
|
|
int sampling_rate = 44100;
|
|
int extends_flag;
|
|
|
|
TRACE(0, "##decode_scalable_frame");
|
|
uint8_t head[4];
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
len1 = len2 = 0;
|
|
e1 = e2 = 0;
|
|
r = PeekCQueue(&sbc_queue, SCALABLE_HEAD_SIZE, &e1, &len1, &e2, &len2);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
if (r == CQ_ERR) {
|
|
// osDelay(2);
|
|
TRACE(2, "no data head xxx %d/%d", LengthOfCQueue(&sbc_queue),
|
|
AvailableOfCQueue(&sbc_queue));
|
|
// goto get_scalable_head_again;
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
DeCQueue(&sbc_queue, head, 4);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
hal_trace_dump("sss %01x", 1, 4, head);
|
|
return 1;
|
|
} else {
|
|
// normal
|
|
if (e1) {
|
|
memcpy(scalable_input_mid_buf, e1, len1);
|
|
}
|
|
if (e2) {
|
|
memcpy(scalable_input_mid_buf + len1, e2, len2);
|
|
}
|
|
}
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
DeCQueue(&sbc_queue, 0, 4);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
|
|
scalable_uhq_flag = 0;
|
|
|
|
extends_flag = ((scalable_input_mid_buf[3] >> 3) & 1);
|
|
switch ((scalable_input_mid_buf[3] & 0xf7)) {
|
|
case 0xF0:
|
|
bitrate_bps = 88000;
|
|
break;
|
|
case 0xF1:
|
|
bitrate_bps = 96000;
|
|
break;
|
|
case 0xF2:
|
|
bitrate_bps = 128000;
|
|
break;
|
|
case 0xF3:
|
|
bitrate_bps = 192000;
|
|
break;
|
|
case 0xF5:
|
|
scalable_uhq_flag = 1;
|
|
bitrate_bps = 328000;
|
|
sampling_rate = 96000;
|
|
break;
|
|
default:
|
|
bitrate_bps = 88000;
|
|
break;
|
|
}
|
|
|
|
frame_size = SCALABLE_FRAME_SIZE;
|
|
|
|
len = bitrate_bps * frame_size / sampling_rate / 8;
|
|
if (scalable_uhq_flag == 0) {
|
|
hw_tmp = (len * 3) >> 7;
|
|
len = hw_tmp + len;
|
|
len = len + ((len & 1) ^ 1);
|
|
} else {
|
|
len = 369; // 744/2-4+1
|
|
}
|
|
TRACE(4, "len %d,uhq:%d ext:%d extbitrate_bps %d", len, scalable_uhq_flag,
|
|
extends_flag, bitrate_bps);
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
len1 = len2 = 0;
|
|
e1 = e2 = 0;
|
|
r = PeekCQueue(&sbc_queue, len - 1, &e1, &len1, &e2, &len2);
|
|
if (r == CQ_ERR) {
|
|
// osDelay(2);
|
|
TRACE(0, "no data ");
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
return 2;
|
|
} else {
|
|
// normal
|
|
if (e1) {
|
|
memcpy(scalable_input_mid_buf + 4, e1, len1);
|
|
}
|
|
if (e2) {
|
|
memcpy(scalable_input_mid_buf + 4 + len1, e2, len2);
|
|
}
|
|
}
|
|
// UNLOCK_APP_AUDIO_QUEUE();
|
|
|
|
// LOCK_APP_AUDIO_QUEUE();
|
|
DeCQueue(&sbc_queue, 0, len - 1);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
// TRACE(2,"len1 %d, len2 %d\n", len1, len2);
|
|
|
|
if (p_data) {
|
|
*p_data = scalable_input_mid_buf;
|
|
}
|
|
if (p_len) {
|
|
*p_len = len - 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
TEXT_SSC_LOC
|
|
int decode_scalable_frame(uint8_t *out, uint32_t out_max, uint32_t *p_out_len,
|
|
const uint8_t *in, uint32_t in_len,
|
|
uint32_t *p_consume_len) {
|
|
uint32_t out_len;
|
|
int decoder_size;
|
|
int output_samples;
|
|
int err;
|
|
|
|
out_len = SCALABLE_FRAME_SIZE * 2;
|
|
|
|
if (hSSDecoder == NULL) {
|
|
hSSDecoder = (void *)scalable_decoder_place;
|
|
// err = ssc_decoder_create(44100, 2, hSSDecoder);
|
|
decoder_size = ssc_decoder_get_size(2, 96000);
|
|
err = ssc_decoder_init(hSSDecoder, 2, 96000);
|
|
TRACE(2, "decoder_size %d init ret %d\n", decoder_size, err);
|
|
}
|
|
|
|
if (a2dp_channel_num[app_bt_device.curr_a2dp_stream_id] == 1) {
|
|
ASSERT(0, "UHQ should check audio channel number");
|
|
}
|
|
output_samples =
|
|
ssc_decode(hSSDecoder, scalable_input_mid_buf, (int *)ss_pcm_buff,
|
|
SCALABLE_FRAME_SIZE, scalable_decoder_temp_buf);
|
|
if (scalable_uhq_flag) {
|
|
ss_to_24bit_buf((int32_t *)out, (int32_t *)ss_pcm_buff, out_len);
|
|
out_len *= 4;
|
|
} else {
|
|
ss_to_16bit_buf((int16_t *)out, (int32_t *)ss_pcm_buff, out_len);
|
|
out_len *= 2;
|
|
}
|
|
|
|
TRACE(2, "pcm_len %d o:%d", out_len, output_samples);
|
|
|
|
if (p_out_len) {
|
|
*p_out_len = out_len;
|
|
}
|
|
if (p_consume_len) {
|
|
*p_consume_len = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static unsigned int sbc_next_frame_size;
|
|
static btif_sbc_pcm_data_t sbc_pcm_data;
|
|
static uint8_t sbc_underflow;
|
|
|
|
TEXT_SBC_LOC
|
|
void decode_sbc_begin(void) { sbc_underflow = 0; }
|
|
|
|
TEXT_SBC_LOC
|
|
int load_sbc_frame(uint8_t **p_data, uint32_t *p_len) {
|
|
unsigned char retry = 0;
|
|
int r = 0;
|
|
unsigned char *e1 = NULL, *e2 = NULL;
|
|
unsigned int len1 = 0, len2 = 0;
|
|
|
|
if (!sbc_next_frame_size) {
|
|
sbc_next_frame_size = sbc_frame_size;
|
|
}
|
|
|
|
if (need_init_decoder) {
|
|
sbc_next_frame_size = sbc_frame_size;
|
|
}
|
|
|
|
get_again:
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
len1 = len2 = 0;
|
|
r = PeekCQueue(&sbc_queue, sbc_next_frame_size, &e1, &len1, &e2, &len2);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
if (r == CQ_ERR) {
|
|
#ifdef __LOCK_AUDIO_THREAD__
|
|
TRACE(1, "cache sbc_underflow retry:%d\n", retry);
|
|
goto exit;
|
|
#elif defined(A2DP_SYNC_WITH_PUT_MUTUX)
|
|
int size;
|
|
A2DP_SYNC_WITH_PUT_MUTUX_ALLOC();
|
|
A2DP_SYNC_WITH_PUT_MUTUX_WAIT();
|
|
if (retry++ < A2DP_SYNC_WITH_PUT_MUTUX_TIMEROUT_CNT) {
|
|
goto get_again;
|
|
} else {
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
size = LengthOfCQueue(&sbc_queue);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
sbc_underflow = 1;
|
|
TRACE(2, "cache sbc_underflow size:%d retry:%d\n", size, retry);
|
|
goto exit;
|
|
}
|
|
#else
|
|
#if 1
|
|
TRACE(1, "cache sbc_underflow retry:%d\n", retry);
|
|
goto exit;
|
|
#else
|
|
int size;
|
|
osDelay(2);
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
size = LengthOfCQueue(&sbc_queue);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
if (retry++ < 12) {
|
|
goto get_again;
|
|
} else {
|
|
sbc_underflow = 1;
|
|
TRACE(2, "cache sbc_underflow size:%d retry:%d\n", size, retry);
|
|
goto exit;
|
|
}
|
|
#endif
|
|
#endif
|
|
} else {
|
|
retry = 0;
|
|
}
|
|
|
|
if (!len1) {
|
|
TRACE(2, "len1 sbc_underflow %d/%d\n", len1, len2);
|
|
goto get_again;
|
|
}
|
|
|
|
if (p_data) {
|
|
*p_data = e1;
|
|
}
|
|
if (p_len) {
|
|
*p_len = len1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
return 1;
|
|
}
|
|
|
|
TEXT_SBC_LOC
|
|
int decode_sbc_frame(uint8_t *out, uint32_t out_max, uint32_t *p_out_len,
|
|
const uint8_t *in, uint32_t in_len,
|
|
uint32_t *p_consume_len) {
|
|
uint16_t parse_len;
|
|
static uint16_t parse_len_total_frame = 0;
|
|
bt_status_t ret;
|
|
|
|
if (need_init_decoder) {
|
|
parse_len_total_frame = 0;
|
|
btif_sbc_init_decoder(sbc_decoder);
|
|
}
|
|
|
|
need_init_decoder = false;
|
|
|
|
sbc_pcm_data.data = out;
|
|
sbc_pcm_data.dataLen = 0;
|
|
|
|
if (a2dp_channel_num[app_bt_device.curr_a2dp_stream_id] == 1) {
|
|
out_max >>= 1;
|
|
}
|
|
|
|
uint32_t lock = int_lock();
|
|
ret = btif_sbc_decode_frames(sbc_decoder, (uint8_t *)in, in_len, &parse_len,
|
|
&sbc_pcm_data, out_max, sbc_eq_band_gain);
|
|
int_unlock(lock);
|
|
parse_len_total_frame += parse_len;
|
|
if (parse_len_total_frame >= sbc_frame_size) {
|
|
mtu_count--;
|
|
// TRACE(1,"-mtu_count = %d",mtu_count);
|
|
parse_len_total_frame -= sbc_frame_size;
|
|
}
|
|
if (ret == BT_STS_SUCCESS) {
|
|
sbc_next_frame_size = sbc_frame_size;
|
|
} else {
|
|
sbc_next_frame_size = (sbc_frame_size > parse_len)
|
|
? (sbc_frame_size - parse_len)
|
|
: sbc_frame_size;
|
|
|
|
if (ret == BT_STS_FAILED) {
|
|
need_init_decoder = true;
|
|
TRACE(1, "err mutelen:%d\n", sbc_pcm_data.dataLen);
|
|
} else if (ret == BT_STS_NO_RESOURCES) {
|
|
TRACE(1, "no_res mutelen:%d\n", sbc_pcm_data.dataLen);
|
|
}
|
|
}
|
|
|
|
if (p_out_len) {
|
|
*p_out_len = sbc_pcm_data.dataLen;
|
|
}
|
|
if (p_consume_len) {
|
|
*p_consume_len = parse_len;
|
|
}
|
|
|
|
if (a2dp_channel_num[app_bt_device.curr_a2dp_stream_id] == 1) {
|
|
expand_1_channel_to_2_channels_16bits(sbc_pcm_data.data,
|
|
sbc_pcm_data.dataLen);
|
|
sbc_pcm_data.dataLen <<= 1;
|
|
*p_out_len = sbc_pcm_data.dataLen;
|
|
}
|
|
|
|
return (ret == BT_STS_CONTINUE || ret == BT_STS_SUCCESS) ? 0 : 1;
|
|
}
|
|
|
|
TEXT_SBC_LOC
|
|
int consume_sbc_frame(const uint8_t *data, uint32_t len) {
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
DeCQueue(&sbc_queue, 0, len);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
|
|
return 0;
|
|
}
|
|
|
|
TEXT_SBC_LOC
|
|
void decode_sbc_end(uint32_t expect_out_len, uint32_t out_len,
|
|
uint32_t in_len) {
|
|
#ifndef A2DP_TRACE_DEC_TIME
|
|
if (sbc_underflow || need_init_decoder) {
|
|
dec_trace_time = true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef A2DP_AUDIO_SYNC_WITH_LOCAL
|
|
if (expect_out_len == out_len) {
|
|
int size;
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
size = LengthOfCQueue(&sbc_queue);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
// TRACE(2,"decode_sbc_frame Queue remain size:%d
|
|
// frame_size:%d\n",size, sbc_frame_size);
|
|
A2DP_AUDIO_SYNC_TRACE(6,
|
|
"sync status:%d qsize:%d/%d fsize:%d, thr:%d used:%d",
|
|
sync_status, size, g_sbc_queue_size, sbc_frame_size,
|
|
A2DPPLAY_CACHE_OK_THRESHOLD, in_len);
|
|
/*
|
|
nor:
|
|
if (size>7488+1170*2)
|
|
goto inc
|
|
if (size<7488-1170)
|
|
goto dec
|
|
dec:
|
|
if (size>=7488+1170)
|
|
goto nor
|
|
inc:
|
|
if (size<=7488)
|
|
goto nor
|
|
*/
|
|
switch (sync_status) {
|
|
case A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_DEC:
|
|
if (size >= (A2DPPLAY_CACHE_OK_THRESHOLD + in_len)) {
|
|
a2dp_audio_sync_proc(A2DPPLAY_SYNC_STATUS_SET,
|
|
A2DP_AUDIO_SYNC_WITH_LOCAL_SAMPLERATE_DEFAULT);
|
|
sync_status = A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_DEFAULT;
|
|
A2DP_AUDIO_SYNC_TRACE(0, "default pll freq");
|
|
} else {
|
|
a2dp_audio_sync_proc(A2DPPLAY_SYNC_STATUS_PROC, 0);
|
|
}
|
|
break;
|
|
case A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_INC:
|
|
if (size <= (A2DPPLAY_CACHE_OK_THRESHOLD)) {
|
|
a2dp_audio_sync_proc(A2DPPLAY_SYNC_STATUS_SET,
|
|
A2DP_AUDIO_SYNC_WITH_LOCAL_SAMPLERATE_DEFAULT);
|
|
sync_status = A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_DEFAULT;
|
|
A2DP_AUDIO_SYNC_TRACE(0, "default pll freq");
|
|
} else {
|
|
a2dp_audio_sync_proc(A2DPPLAY_SYNC_STATUS_PROC, 0);
|
|
}
|
|
break;
|
|
case A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_DEFAULT:
|
|
default:
|
|
if (size > (A2DPPLAY_CACHE_OK_THRESHOLD + (in_len << 1))) {
|
|
a2dp_audio_sync_proc(A2DPPLAY_SYNC_STATUS_SET,
|
|
A2DP_AUDIO_SYNC_WITH_LOCAL_SAMPLERATE_INC);
|
|
sync_status = A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_INC;
|
|
A2DP_AUDIO_SYNC_TRACE(0, "inc pll freq");
|
|
} else if (size < (A2DPPLAY_CACHE_OK_THRESHOLD - in_len)) {
|
|
a2dp_audio_sync_proc(A2DPPLAY_SYNC_STATUS_SET,
|
|
A2DP_AUDIO_SYNC_WITH_LOCAL_SAMPLERATE_DEC);
|
|
sync_status = A2DP_AUDIO_SYNC_STATUS_SAMPLERATE_DEC;
|
|
A2DP_AUDIO_SYNC_TRACE(0, "dec pll freq");
|
|
} else {
|
|
a2dp_audio_sync_proc(A2DPPLAY_SYNC_STATUS_PROC, 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined(A2DP_LHDC_ON)
|
|
#define PACKET_BUFFER_LENGTH 4 * 1024
|
|
|
|
uint8_t serial_no;
|
|
bool is_synced;
|
|
bool is_splited;
|
|
ASM_PKT_STATUS asm_pkt_st;
|
|
uint8_t packet_buffer[PACKET_BUFFER_LENGTH];
|
|
uint32_t packet_buf_len = 0;
|
|
uint32_t frames_nb_in_buffer = 0;
|
|
uint32_t total_frame_nb = 0;
|
|
void reset_lhdc_assmeble_packet() {
|
|
is_synced = false;
|
|
asm_pkt_st = ASM_PKT_WAT_STR;
|
|
packet_buf_len = 0;
|
|
total_frame_nb = 0;
|
|
}
|
|
|
|
void initial_lhdc_assemble_packet(bool splitFlg) {
|
|
memset(packet_buffer, 0, PACKET_BUFFER_LENGTH);
|
|
reset_lhdc_assmeble_packet();
|
|
serial_no = 0xff;
|
|
is_splited = splitFlg;
|
|
TRACE(1, "is_splited = %s\n", splitFlg ? "true" : "false");
|
|
}
|
|
|
|
/**
|
|
* get lhdc frame header
|
|
*/
|
|
bool get_lhdc_header(uint8_t *in, LHDC_FRAME_HDR *h) {
|
|
#define LHDC_HDR_LEN 4
|
|
uint32_t hdr = 0;
|
|
bool ret = false;
|
|
memcpy(&hdr, in, LHDC_HDR_LEN);
|
|
h->frame_len = (int)((hdr >> 8) & 0x1fff);
|
|
h->isSplit = ((hdr & 0x00600000) == 0x00600000);
|
|
h->isLeft = ((hdr & 0xf) == 0);
|
|
|
|
if ((hdr & 0xff000000) != 0x4c000000) {
|
|
TRACE(0, "lhdc hdr err!\n");
|
|
ret = false;
|
|
} else {
|
|
// TRACE(4,"frame_len = %d, g_lhdc_split = %s, g_lhdc_channel = %s(%d)",
|
|
// h->frame_len, h->isSplit ? "true" : "false", h->isLeft ? "left" :
|
|
// "right", (hdr & 0xf) );
|
|
ret = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
*Get splited lhdc frames fit to MTU size
|
|
*/
|
|
uint32_t get_lhdc_frame(uint8_t *frame_q, size_t q_available_size,
|
|
uint8_t *out_buf, uint32_t *out_size,
|
|
uint32_t *out_frames) {
|
|
LHDC_FRAME_HDR hdr;
|
|
size_t emptSize =
|
|
q_available_size >= PACKET_MTU_SIZE ? PACKET_MTU_SIZE : q_available_size;
|
|
uint32_t *offset = out_size;
|
|
// uint32_t * frame_cnt = out_frames;
|
|
bool start = TRUE;
|
|
*offset = 0; // *frame_cnt = 0;
|
|
while (start) {
|
|
|
|
if (get_lhdc_header(frame_q + *offset, &hdr) == false) {
|
|
start = false;
|
|
continue;
|
|
}
|
|
|
|
if (emptSize < hdr.frame_len) {
|
|
start = false;
|
|
continue;
|
|
}
|
|
|
|
memcpy(out_buf + *offset, frame_q + *offset, hdr.frame_len);
|
|
emptSize -= hdr.frame_len;
|
|
*offset += hdr.frame_len;
|
|
(*out_frames)++;
|
|
}
|
|
return *offset;
|
|
}
|
|
/**
|
|
* Grabe lhdc data from queue
|
|
*/
|
|
int get_lhdc_data(unsigned char *frame, unsigned int len) {
|
|
int status;
|
|
unsigned int len1 = 0, len2 = 0;
|
|
unsigned char *e1 = NULL, *e2 = NULL;
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
unsigned int queu_len = (unsigned int)LengthOfCQueue(&sbc_queue);
|
|
len = queu_len < len ? queu_len : len;
|
|
status = PeekCQueue(&sbc_queue, len, &e1, &len1, &e2, &len2);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
if (status != CQ_OK) {
|
|
return 0;
|
|
}
|
|
|
|
if (e1) {
|
|
memcpy(frame, e1, len1);
|
|
}
|
|
if (e2) {
|
|
memcpy(frame + len1, e2, len2);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
int assemble_lhdc_packet(uint8_t *input, uint32_t input_len, uint8_t **pLout,
|
|
uint32_t *pLlen, uint8_t **pRout, uint32_t *pRlen) {
|
|
uint8_t hdr = 0, seqno = 0xff;
|
|
int ret = -1;
|
|
uint32_t status = 0;
|
|
|
|
hdr = (*input);
|
|
input++;
|
|
seqno = (*input);
|
|
input++;
|
|
input_len -= 2;
|
|
|
|
status = hdr & A2DP_LHDC_HDR_LATENCY_MASK;
|
|
if (latencyUpdated != (uint8_t)status) {
|
|
latencyUpdated = (uint8_t)status;
|
|
TRACE(1, "New latency setting = 0x%02x", latencyUpdated);
|
|
}
|
|
|
|
if (is_synced) {
|
|
if (seqno != serial_no) {
|
|
reset_lhdc_assmeble_packet();
|
|
if ((hdr & A2DP_LHDC_HDR_FLAG_MSK) == 0 ||
|
|
(hdr & A2DP_LHDC_HDR_S_MSK) != 0) {
|
|
goto lhdc_start;
|
|
} else
|
|
TRACE(1, "drop packet No. %u", seqno);
|
|
return 0;
|
|
}
|
|
serial_no = seqno + 1;
|
|
}
|
|
|
|
lhdc_start:
|
|
switch (asm_pkt_st) {
|
|
case ASM_PKT_WAT_STR: {
|
|
if ((hdr & A2DP_LHDC_HDR_FLAG_MSK) == 0) {
|
|
memcpy(&packet_buffer[0], input, input_len);
|
|
if (pLlen && pLout) {
|
|
*pLlen = input_len;
|
|
*pLout = packet_buffer;
|
|
}
|
|
if (pRout && pRlen) {
|
|
*pRout = NULL;
|
|
*pRlen = 0;
|
|
}
|
|
// TRACE(1,"Single payload size = %d", *pLlen);
|
|
asm_pkt_st = ASM_PKT_WAT_STR;
|
|
packet_buf_len = 0; //= packet_buf_left_len = packet_buf_right_len = 0;
|
|
total_frame_nb = 0;
|
|
ret = 1;
|
|
} else if (hdr & A2DP_LHDC_HDR_S_MSK) {
|
|
ret = 0;
|
|
if (packet_buf_len + input_len >= PACKET_BUFFER_LENGTH) {
|
|
packet_buf_len = 0;
|
|
asm_pkt_st = ASM_PKT_WAT_STR;
|
|
TRACE(1, "ASM_PKT_WAT_STR:Frame buffer overflow!(%d)", packet_buf_len);
|
|
break;
|
|
}
|
|
memcpy(&packet_buffer, input, input_len);
|
|
packet_buf_len = input_len;
|
|
asm_pkt_st = ASM_PKT_WAT_LST;
|
|
// TRACE(1,"multi:first payload size = %d", input_len);
|
|
} else
|
|
ret = -1;
|
|
|
|
if (ret >= 0) {
|
|
if (!is_synced) {
|
|
is_synced = true;
|
|
serial_no = seqno + 1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ASM_PKT_WAT_LST: {
|
|
if (packet_buf_len + input_len >= PACKET_BUFFER_LENGTH) {
|
|
packet_buf_len = 0;
|
|
asm_pkt_st = ASM_PKT_WAT_STR;
|
|
TRACE(1, "ASM_PKT_WAT_LST:Frame buffer overflow(%d)", packet_buf_len);
|
|
break;
|
|
}
|
|
memcpy(&packet_buffer[packet_buf_len], input, input_len);
|
|
// TRACE(1,"multi:payload size = %d", input_len);
|
|
packet_buf_len += input_len;
|
|
ret = 0;
|
|
|
|
if (hdr & A2DP_LHDC_HDR_L_MSK) {
|
|
|
|
if (pLlen && pLout) {
|
|
*pLlen = packet_buf_len;
|
|
*pLout = packet_buffer;
|
|
}
|
|
// TRACE(1,"multi: all payload size = %d", packet_buf_len);
|
|
packet_buf_len = 0; // packet_buf_left_len = packet_buf_right_len = 0;
|
|
total_frame_nb = 0;
|
|
ret = 1;
|
|
asm_pkt_st = ASM_PKT_WAT_STR;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
ret = 0;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
/**
|
|
* Decode LHDC data...
|
|
*/
|
|
TEXT_LHDC_LOC
|
|
int load_lhdc_frame(uint8_t **p_data, uint32_t *p_len) {
|
|
int ret;
|
|
LHDC_HEADER *ph = NULL;
|
|
uint8_t retry = 0;
|
|
int status;
|
|
uint8_t *_buff = NULL;
|
|
uint32_t _buff_len = 0;
|
|
|
|
ret = 1;
|
|
|
|
A2DP_SYNC_WITH_PUT_MUTUX_ALLOC();
|
|
|
|
if (latencyIndex != latencyUpdated) {
|
|
latencyIndex = latencyUpdated;
|
|
goto _exit;
|
|
}
|
|
|
|
while (true) {
|
|
int result = lhdcReadyForInput();
|
|
if (result == 1) {
|
|
/* code */
|
|
status = get_lhdc_data(lhdc_input_mid_buf, sizeof(LHDC_HEADER));
|
|
if (status == 0) {
|
|
// No data return...
|
|
if (retry++ >= A2DP_SYNC_WITH_PUT_MUTUX_TIMEROUT_CNT) {
|
|
TRACE(1, "%s:No Data return", __func__);
|
|
break;
|
|
}
|
|
A2DP_SYNC_WITH_PUT_MUTUX_WAIT();
|
|
continue;
|
|
}
|
|
retry = 0;
|
|
ph = (LHDC_HEADER *)&lhdc_input_mid_buf[0];
|
|
result = memcmp((const char *)magicTag, (const char *)ph->tag,
|
|
sizeof(magicTag));
|
|
if (result != 0) {
|
|
uint8_t tmp[sizeof(magicTag) + 1];
|
|
size_t i;
|
|
for (i = 0; i < sizeof(magicTag); i++) {
|
|
sprintf((char *)&tmp[i], "%c", (char)ph->tag[i]);
|
|
}
|
|
tmp[i] = 0;
|
|
continue;
|
|
}
|
|
|
|
status = get_lhdc_data(lhdc_input_mid_buf,
|
|
sizeof(LHDC_HEADER) + ph->packetLen);
|
|
if (status == 0) {
|
|
// No data return...
|
|
TRACE(1, "Fetch data error[lenght = %d]", ph->packetLen);
|
|
continue;
|
|
}
|
|
ph = (LHDC_HEADER *)&lhdc_input_mid_buf[0];
|
|
|
|
// TRACE(2,"%s:Get packet[%d]", __func__, ph->packetLen);
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
DeCQueue(&sbc_queue, 0, sizeof(LHDC_HEADER) + ph->packetLen);
|
|
mtu_count--;
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
_buff = ph->dptr;
|
|
_buff_len = ph->packetLen;
|
|
ret = 0;
|
|
} else {
|
|
_buff = NULL;
|
|
_buff_len = 0;
|
|
ret = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
_exit:
|
|
A2DP_SYNC_WITH_PUT_MUTUX_FREE();
|
|
|
|
if (p_data) {
|
|
*p_data = _buff;
|
|
}
|
|
if (p_len) {
|
|
*p_len = _buff_len;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
TEXT_LHDC_LOC
|
|
int decode_lhdc_frame(uint8_t *out, uint32_t out_max, uint32_t *p_out_len,
|
|
const uint8_t *in, uint32_t in_len,
|
|
uint32_t *p_consume_len) {
|
|
uint32_t need_again;
|
|
uint32_t out_len;
|
|
|
|
if (a2dp_channel_num[app_bt_device.curr_a2dp_stream_id] == 1) {
|
|
ASSERT(0, "lhdc should check audio channel number");
|
|
}
|
|
|
|
out_len = lhdcDecodeProcess(out, (uint8_t *)in, in_len, &need_again);
|
|
|
|
if (p_out_len) {
|
|
*p_out_len = (out_len >= 0) ? out_len : 0;
|
|
}
|
|
if (p_consume_len) {
|
|
*p_consume_len = (out_len >= 0) ? in_len : 0;
|
|
}
|
|
|
|
return (out_len >= 0) ? 0 : 1;
|
|
}
|
|
|
|
TEXT_LHDC_LOC
|
|
void decode_lhdc_end(uint32_t expect_out_len, uint32_t out_len,
|
|
uint32_t in_len) {
|
|
if (expect_out_len != out_len) {
|
|
dec_reset_queue = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
#include "cp_accel.h"
|
|
|
|
#define CP_CACHE_ATTR ALIGNED(4) CP_BSS_LOC
|
|
#define CP_DEC_SLOT_CNT 3
|
|
|
|
enum CP_DEC_STATE_T {
|
|
CP_DEC_STATE_IDLE,
|
|
CP_DEC_STATE_WORKING,
|
|
CP_DEC_STATE_DONE,
|
|
CP_DEC_STATE_FAILED,
|
|
};
|
|
|
|
static CP_CACHE_ATTR uint8_t cp_in_cache[1024 * 10];
|
|
static CP_CACHE_ATTR uint8_t cp_out_cache[1024 * 16];
|
|
static CP_BSS_LOC uint32_t cp_in_wpos;
|
|
static CP_BSS_LOC uint32_t cp_in_rpos;
|
|
static CP_BSS_LOC uint32_t cp_dec_start_time;
|
|
static CP_BSS_LOC uint32_t cp_last_dec_time;
|
|
#ifdef A2DP_TRACE_CP_DEC_TIME
|
|
static CP_DATA_LOC const bool cp_dec_trace_time = true;
|
|
#else
|
|
static CP_BSS_LOC bool cp_dec_trace_time;
|
|
#endif
|
|
static CP_BSS_LOC uint8_t cp_dec_idx;
|
|
|
|
static bool mcu_dec_inited;
|
|
static enum APP_OVERLAY_ID_T cp_overlay_type;
|
|
static enum CP_DEC_STATE_T cp_dec_state[CP_DEC_SLOT_CNT];
|
|
static uint8_t cp_dec_mtu_cnt[CP_DEC_SLOT_CNT];
|
|
static uint32_t cp_out_len[CP_DEC_SLOT_CNT];
|
|
static uint8_t mcu_dec_idx;
|
|
static uint32_t cp_in_size;
|
|
static uint32_t cp_out_size;
|
|
static uint32_t cp_out_loop_size;
|
|
|
|
static uint32_t cp_get_in_cache_data_len(uint32_t in_wpos, uint32_t in_rpos);
|
|
static void cp_get_in_cache_data(uint8_t *data, uint32_t len, uint32_t in_wpos,
|
|
uint32_t in_rpos);
|
|
static uint32_t cp_update_in_cache_pos(uint32_t in_pos, uint32_t len);
|
|
|
|
#if defined(A2DP_LDAC_ON)
|
|
static CP_BSS_LOC bool cp_ldac_fatal_error;
|
|
|
|
TEXT_LDAC_LOC
|
|
int cp_load_ldac_frame(uint8_t **p_data, uint32_t *p_len) {
|
|
uint32_t len;
|
|
uint32_t in_rpos;
|
|
uint32_t in_wpos;
|
|
|
|
if (hLdacData == NULL || cp_ldac_fatal_error) {
|
|
return 1;
|
|
}
|
|
|
|
in_rpos = cp_in_rpos;
|
|
in_wpos = cp_in_wpos;
|
|
|
|
_get_hdr:
|
|
len = cp_get_in_cache_data_len(in_wpos, in_rpos);
|
|
if (len == 0) {
|
|
return 2;
|
|
}
|
|
if (len > LDAC_READBUF_SIZE) {
|
|
len = LDAC_READBUF_SIZE;
|
|
}
|
|
|
|
cp_get_in_cache_data(&ldac_input_mid_buf[0], len, in_wpos, in_rpos);
|
|
|
|
if (ldac_recovery) {
|
|
uint32_t parse_bytes;
|
|
int result;
|
|
|
|
result = ldac_seek_header(len, &parse_bytes);
|
|
if (result == 0) {
|
|
ldac_recovery = false;
|
|
}
|
|
in_rpos = cp_update_in_cache_pos(in_rpos, parse_bytes);
|
|
goto _get_hdr;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
TEXT_LDAC_LOC
|
|
static int cp_consume_ldac_frame(const uint8_t *data, uint32_t len) {
|
|
cp_in_rpos = cp_update_in_cache_pos(cp_in_rpos, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
TEXT_LDAC_LOC
|
|
void cp_decode_ldac_end(uint32_t expect_out_len, uint32_t out_len,
|
|
uint32_t in_len) {
|
|
if (expect_out_len != out_len) {
|
|
if (hLdacData) {
|
|
int error_code;
|
|
error_code = ldacBT_get_error_code(hLdacData);
|
|
TRACE(6, "error_code = %4d, %4d, %4d,FrameHeader[%02x:%02x:%02x]\n",
|
|
LDACBT_API_ERR(error_code), LDACBT_HANDLE_ERR(error_code),
|
|
LDACBT_BLOCK_ERR(error_code), ldac_input_mid_buf[0],
|
|
ldac_input_mid_buf[1], ldac_input_mid_buf[2]);
|
|
if (LDACBT_FATAL(error_code)) {
|
|
cp_ldac_fatal_error = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEXT_LDAC_LOC
|
|
void mcu_decode_ldac_end(uint32_t expect_out_len, uint32_t out_len,
|
|
uint32_t in_len) {
|
|
if (expect_out_len != out_len) {
|
|
if (cp_ldac_fatal_error) {
|
|
ldac_dec_deinit();
|
|
}
|
|
if (hLdacData == NULL) {
|
|
/* Recovery Process */
|
|
int result;
|
|
result = ldac_dec_init();
|
|
if (result) {
|
|
ldac_dec_deinit();
|
|
}
|
|
ldac_recovery = true;
|
|
cp_ldac_fatal_error = false;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(A2DP_LHDC_ON)
|
|
TEXT_LHDC_LOC
|
|
int cp_load_lhdc_frame(uint8_t **p_data, uint32_t *p_len) {
|
|
int ret;
|
|
LHDC_HEADER ph;
|
|
uint32_t len;
|
|
uint32_t in_rpos;
|
|
uint32_t in_wpos;
|
|
uint8_t *frame_buf = NULL;
|
|
uint32_t frame_len = 0;
|
|
int result;
|
|
|
|
ret = 1;
|
|
|
|
if (latencyIndex != latencyUpdated) {
|
|
latencyIndex = latencyUpdated;
|
|
goto _exit;
|
|
}
|
|
|
|
result = lhdcReadyForInput();
|
|
if (result == 0) {
|
|
ret = 0;
|
|
goto _exit;
|
|
}
|
|
|
|
in_rpos = cp_in_rpos;
|
|
in_wpos = cp_in_wpos;
|
|
|
|
_get_hdr:
|
|
len = cp_get_in_cache_data_len(in_wpos, in_rpos);
|
|
if (len < sizeof(ph)) {
|
|
goto _exit;
|
|
}
|
|
|
|
cp_get_in_cache_data(&lhdc_input_mid_buf[0], sizeof(ph), in_wpos, in_rpos);
|
|
|
|
memcpy(&ph, &lhdc_input_mid_buf[0], sizeof(ph));
|
|
result =
|
|
memcmp((const char *)magicTag, (const char *)ph.tag, sizeof(magicTag));
|
|
if (result != 0) {
|
|
in_rpos = cp_update_in_cache_pos(in_rpos, 1);
|
|
goto _get_hdr;
|
|
}
|
|
|
|
if (len < sizeof(ph) + ph.packetLen) {
|
|
goto _exit;
|
|
}
|
|
|
|
in_rpos = cp_update_in_cache_pos(in_rpos, sizeof(ph));
|
|
|
|
cp_get_in_cache_data(&lhdc_input_mid_buf[sizeof(ph)], ph.packetLen, in_wpos,
|
|
in_rpos);
|
|
|
|
in_rpos = cp_update_in_cache_pos(in_rpos, ph.packetLen);
|
|
cp_in_rpos = in_rpos;
|
|
|
|
frame_buf = &((LHDC_HEADER *)&lhdc_input_mid_buf[0])->dptr[0];
|
|
frame_len = ph.packetLen;
|
|
|
|
ret = 0;
|
|
|
|
_exit:
|
|
if (p_data) {
|
|
*p_data = frame_buf;
|
|
}
|
|
if (p_len) {
|
|
*p_len = frame_len;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if defined(A2DP_AAC_ON)
|
|
TEXT_AAC_LOC
|
|
int cp_load_aac_frame(uint8_t **p_data, uint32_t *p_len) {
|
|
uint32_t len;
|
|
unsigned short aac_len = 0;
|
|
uint32_t in_rpos;
|
|
uint32_t in_wpos;
|
|
|
|
in_rpos = cp_in_rpos;
|
|
in_wpos = cp_in_wpos;
|
|
|
|
len = cp_get_in_cache_data_len(in_wpos, in_rpos);
|
|
if (len < 2) {
|
|
return 1;
|
|
}
|
|
|
|
if (in_wpos > in_rpos || cp_in_size >= in_rpos + 2) {
|
|
aac_len = cp_in_cache[in_rpos] | (cp_in_cache[in_rpos + 1] << 8);
|
|
} else {
|
|
aac_len = cp_in_cache[in_rpos] | (cp_in_cache[0] << 8);
|
|
}
|
|
in_rpos = cp_update_in_cache_pos(in_rpos, 2);
|
|
|
|
if (aac_len < 64) {
|
|
aac_maxreadBytes = 64;
|
|
} else if (aac_len < 128) {
|
|
aac_maxreadBytes = 128;
|
|
} else if (aac_len < 256) {
|
|
aac_maxreadBytes = 256;
|
|
} else if (aac_len < 512) {
|
|
aac_maxreadBytes = 512;
|
|
} else if (aac_len < 1024) {
|
|
aac_maxreadBytes = 1024;
|
|
} else {
|
|
if (aac_len >= 2048) {
|
|
// Header is bad!
|
|
dec_reset_queue = true;
|
|
return 2;
|
|
}
|
|
aac_maxreadBytes = 2048;
|
|
}
|
|
|
|
if (len < 2 + (uint32_t)aac_len) {
|
|
return 3;
|
|
}
|
|
|
|
cp_dec_mtu_cnt[cp_dec_idx]++;
|
|
|
|
cp_get_in_cache_data(&aac_input_mid_buf[0], aac_len, in_wpos, in_rpos);
|
|
// Consume the packet
|
|
in_rpos = cp_update_in_cache_pos(in_rpos, aac_len);
|
|
cp_in_rpos = in_rpos;
|
|
|
|
if (p_data) {
|
|
*p_data = (unsigned char *)aac_input_mid_buf;
|
|
}
|
|
if (p_len) {
|
|
*p_len = aac_maxreadBytes;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
TEXT_SBC_LOC
|
|
int cp_load_sbc_frame(uint8_t **p_data, uint32_t *p_len) {
|
|
uint32_t len;
|
|
uint32_t in_wpos;
|
|
uint32_t in_rpos;
|
|
|
|
if (!sbc_next_frame_size) {
|
|
sbc_next_frame_size = sbc_frame_size;
|
|
}
|
|
|
|
if (need_init_decoder) {
|
|
sbc_next_frame_size = sbc_frame_size;
|
|
}
|
|
|
|
in_wpos = cp_in_wpos;
|
|
in_rpos = cp_in_rpos;
|
|
|
|
if (in_wpos == in_rpos) {
|
|
return 1;
|
|
}
|
|
|
|
if (in_wpos > in_rpos) {
|
|
len = in_wpos - in_rpos;
|
|
} else {
|
|
len = cp_in_size - in_rpos;
|
|
}
|
|
|
|
if (len > sbc_next_frame_size) {
|
|
len = sbc_next_frame_size;
|
|
}
|
|
|
|
if (p_data) {
|
|
*p_data = &cp_in_cache[in_rpos];
|
|
}
|
|
if (p_len) {
|
|
*p_len = len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
TEXT_SBC_LOC
|
|
static int cp_consume_sbc_frame(const uint8_t *data, uint32_t len) {
|
|
cp_in_rpos = cp_update_in_cache_pos(cp_in_rpos, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
CP_TEXT_SRAM_LOC
|
|
static uint32_t cp_get_in_cache_data_len(uint32_t in_wpos, uint32_t in_rpos) {
|
|
uint32_t len;
|
|
|
|
if (in_wpos >= in_rpos) {
|
|
len = in_wpos - in_rpos;
|
|
} else {
|
|
len = cp_in_size - in_rpos + in_wpos;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
CP_TEXT_SRAM_LOC
|
|
static void cp_get_in_cache_data(uint8_t *data, uint32_t len, uint32_t in_wpos,
|
|
uint32_t in_rpos) {
|
|
// Assuming cp_get_in_cache_data_len(in_wpos, in_rpos) >= len
|
|
|
|
if (in_wpos > in_rpos || cp_in_size >= in_rpos + len) {
|
|
memcpy(&data[0], &cp_in_cache[in_rpos], len);
|
|
} else {
|
|
uint32_t copy_len;
|
|
|
|
copy_len = cp_in_size - in_rpos;
|
|
memcpy(&data[0], &cp_in_cache[in_rpos], copy_len);
|
|
memcpy(&data[copy_len], &cp_in_cache[0], len - copy_len);
|
|
}
|
|
}
|
|
|
|
CP_TEXT_SRAM_LOC
|
|
static uint32_t cp_update_in_cache_pos(uint32_t in_pos, uint32_t len) {
|
|
// Consume the packet
|
|
in_pos += len;
|
|
if (in_pos >= cp_in_size) {
|
|
in_pos -= cp_in_size;
|
|
}
|
|
|
|
return in_pos;
|
|
}
|
|
|
|
CP_TEXT_SRAM_LOC
|
|
static int cp_decode_a2dp_begin(enum APP_OVERLAY_ID_T overlay_type) {
|
|
cp_dec_start_time = hal_fast_sys_timer_get();
|
|
#ifndef A2DP_TRACE_CP_DEC_TIME
|
|
cp_dec_trace_time = false;
|
|
#endif
|
|
|
|
if (cp_dec_state[cp_dec_idx] != CP_DEC_STATE_IDLE &&
|
|
cp_dec_state[cp_dec_idx] != CP_DEC_STATE_WORKING) {
|
|
return 1;
|
|
}
|
|
|
|
if (cp_dec_state[cp_dec_idx] == CP_DEC_STATE_IDLE) {
|
|
cp_dec_state[cp_dec_idx] = CP_DEC_STATE_WORKING;
|
|
cp_out_len[cp_dec_idx] = 0;
|
|
cp_dec_mtu_cnt[cp_dec_idx] = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
CP_TEXT_SRAM_LOC
|
|
static int cp_load_a2dp_encoded_data(enum APP_OVERLAY_ID_T overlay_type,
|
|
uint8_t **p_data, uint32_t *p_len) {
|
|
int ret = -1;
|
|
|
|
if (0) {
|
|
#if defined(A2DP_LDAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
ret = cp_load_ldac_frame(p_data, p_len);
|
|
#endif
|
|
#if defined(A2DP_LHDC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LHDC) {
|
|
ret = cp_load_lhdc_frame(p_data, p_len);
|
|
#endif
|
|
#if defined(A2DP_AAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_AAC) {
|
|
ret = cp_load_aac_frame(p_data, p_len);
|
|
#endif
|
|
} else if (overlay_type == APP_OVERLAY_A2DP) {
|
|
ret = cp_load_sbc_frame(p_data, p_len);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
CP_TEXT_SRAM_LOC
|
|
static int cp_decode_a2dp_process(enum APP_OVERLAY_ID_T overlay_type,
|
|
uint8_t *out, uint32_t out_max,
|
|
uint32_t *p_out_len, const uint8_t *in,
|
|
uint32_t in_len, uint32_t *p_consume_len) {
|
|
int ret = -1;
|
|
|
|
if (p_out_len) {
|
|
*p_out_len = 0;
|
|
}
|
|
if (p_consume_len) {
|
|
*p_consume_len = 0;
|
|
}
|
|
|
|
if (0) {
|
|
#if defined(A2DP_LDAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
ret = decode_ldac_frame(out, out_max, p_out_len, in, in_len, p_consume_len);
|
|
#endif
|
|
#if defined(A2DP_AAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_AAC) {
|
|
ret = decode_aac_frame(out, out_max, p_out_len, in, in_len, p_consume_len);
|
|
#endif
|
|
#if defined(A2DP_SCALABLE_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_SCALABLE) {
|
|
ret = decode_scalable_frame(out, out_max, p_out_len, in, in_len,
|
|
p_consume_len);
|
|
#endif
|
|
#if defined(A2DP_LHDC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LHDC) {
|
|
ret = decode_lhdc_frame(out, out_max, p_out_len, in, in_len, p_consume_len);
|
|
#endif
|
|
} else if (overlay_type == APP_OVERLAY_A2DP) {
|
|
ret = decode_sbc_frame(out, out_max, p_out_len, in, in_len, p_consume_len);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
CP_TEXT_SRAM_LOC
|
|
static int cp_consume_a2dp_encoded_data(enum APP_OVERLAY_ID_T overlay_type,
|
|
const uint8_t *data, uint32_t len) {
|
|
if (0) {
|
|
#if defined(A2DP_LDAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
cp_consume_ldac_frame(data, len);
|
|
#endif
|
|
} else if (overlay_type == APP_OVERLAY_A2DP) {
|
|
cp_consume_sbc_frame(data, len);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
CP_TEXT_SRAM_LOC
|
|
static void cp_decode_a2dp_end(enum APP_OVERLAY_ID_T overlay_type,
|
|
uint32_t expect_out_len, uint32_t out_len,
|
|
uint32_t in_len, bool retry) {
|
|
uint32_t etime;
|
|
uint8_t old_cp_dec_idx;
|
|
|
|
if (0) {
|
|
#if defined(A2DP_LDAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
cp_decode_ldac_end(expect_out_len, out_len, in_len);
|
|
#endif
|
|
}
|
|
|
|
old_cp_dec_idx = cp_dec_idx;
|
|
|
|
cp_out_len[cp_dec_idx] += out_len;
|
|
|
|
if (!retry) {
|
|
if (expect_out_len == out_len) {
|
|
cp_dec_state[cp_dec_idx] = CP_DEC_STATE_DONE;
|
|
} else {
|
|
cp_dec_state[cp_dec_idx] = CP_DEC_STATE_FAILED;
|
|
}
|
|
|
|
cp_dec_idx++;
|
|
if (cp_dec_idx >= CP_DEC_SLOT_CNT) {
|
|
cp_dec_idx = 0;
|
|
}
|
|
}
|
|
|
|
etime = hal_fast_sys_timer_get();
|
|
if (cp_dec_trace_time) {
|
|
TRACE(4, "cp_decode[%u]: %u us in %u us (r=%u)", old_cp_dec_idx,
|
|
FAST_TICKS_TO_US(etime - cp_dec_start_time),
|
|
FAST_TICKS_TO_US(etime - cp_last_dec_time), retry);
|
|
}
|
|
#ifdef A2DP_TRACE_CP_ACCEL
|
|
if (retry) {
|
|
TRACE(3, "cp_decode[%u]:retry: cp_out_len=%u out_len=%u", old_cp_dec_idx,
|
|
cp_out_len[old_cp_dec_idx], out_len);
|
|
}
|
|
#endif
|
|
cp_last_dec_time = etime;
|
|
}
|
|
|
|
CP_TEXT_SRAM_LOC
|
|
static int cp_decode_a2dp_frame(enum APP_OVERLAY_ID_T overlay_type,
|
|
unsigned char *pcm_buffer,
|
|
unsigned int pcm_len) {
|
|
uint32_t all_out_len = 0, all_in_len = 0;
|
|
int ret, ret2;
|
|
uint32_t out_len;
|
|
uint8_t *enc_data;
|
|
uint32_t enc_len;
|
|
uint32_t consume_len;
|
|
bool retry = false;
|
|
|
|
ret = cp_decode_a2dp_begin(overlay_type);
|
|
if (ret) {
|
|
return 0;
|
|
}
|
|
|
|
while (all_out_len < pcm_len) {
|
|
ret = cp_load_a2dp_encoded_data(overlay_type, &enc_data, &enc_len);
|
|
if (ret) {
|
|
retry = true;
|
|
break;
|
|
}
|
|
|
|
ret = cp_decode_a2dp_process(overlay_type, &pcm_buffer[all_out_len],
|
|
pcm_len - all_out_len, &out_len, enc_data,
|
|
enc_len, &consume_len);
|
|
|
|
ret2 = cp_consume_a2dp_encoded_data(overlay_type, enc_data, consume_len);
|
|
|
|
all_out_len += out_len;
|
|
|
|
if (ret || ret2) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
cp_decode_a2dp_end(overlay_type, pcm_len, all_out_len, all_in_len, retry);
|
|
|
|
return all_out_len;
|
|
}
|
|
|
|
CP_TEXT_SRAM_LOC
|
|
unsigned int cp_a2dp_main(uint8_t event) {
|
|
uint32_t out_wpos;
|
|
uint32_t out_len;
|
|
uint32_t pcm_len;
|
|
|
|
do {
|
|
if (cp_dec_state[cp_dec_idx] == CP_DEC_STATE_WORKING) {
|
|
if (cp_out_len[cp_dec_idx] < cp_out_loop_size) {
|
|
pcm_len = cp_out_loop_size - cp_out_len[cp_dec_idx];
|
|
} else {
|
|
cp_out_len[cp_dec_idx] = 0;
|
|
pcm_len = cp_out_loop_size;
|
|
}
|
|
} else {
|
|
pcm_len = cp_out_loop_size;
|
|
}
|
|
out_wpos = cp_out_loop_size * cp_dec_idx + cp_out_loop_size - pcm_len;
|
|
|
|
out_len = cp_decode_a2dp_frame(cp_overlay_type, &cp_out_cache[out_wpos],
|
|
cp_out_loop_size);
|
|
} while (out_len == pcm_len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
FRAM_TEXT_LOC
|
|
static void mcu_decode_a2dp_init(uint32_t out_max) {
|
|
uint32_t i;
|
|
|
|
if (mcu_dec_inited == false) {
|
|
mcu_dec_inited = true;
|
|
|
|
cp_in_size = sizeof(cp_in_cache);
|
|
cp_out_size = out_max * CP_DEC_SLOT_CNT;
|
|
ASSERT(cp_out_size <= sizeof(cp_out_cache),
|
|
"%s: cp_out_cache too small (should >= %u)", __func__, cp_out_size);
|
|
cp_out_loop_size = out_max;
|
|
|
|
cp_overlay_type = app_get_current_overlay();
|
|
|
|
for (i = 0; i < CP_DEC_SLOT_CNT; i++) {
|
|
cp_dec_state[i] = CP_DEC_STATE_IDLE;
|
|
cp_dec_mtu_cnt[i] = 0;
|
|
cp_out_len[i] = 0;
|
|
}
|
|
|
|
#if defined(A2DP_LHDC_ON)
|
|
// Increase latency to improve data buffering
|
|
mcu_dec_idx = 1;
|
|
#else
|
|
mcu_dec_idx = CP_DEC_SLOT_CNT - 1;
|
|
#endif
|
|
for (i = mcu_dec_idx; i < CP_DEC_SLOT_CNT; i++) {
|
|
cp_dec_state[i] = CP_DEC_STATE_DONE;
|
|
cp_out_len[i] = cp_out_loop_size;
|
|
}
|
|
}
|
|
}
|
|
|
|
FRAM_TEXT_LOC
|
|
static int mcu_load_a2dp_encoded_data(uint8_t **p_data, uint32_t *p_len) {
|
|
uint32_t free_len;
|
|
uint32_t in_wpos, in_rpos;
|
|
uint32_t size;
|
|
int r = 0;
|
|
unsigned char *e1 = NULL, *e2 = NULL;
|
|
unsigned int len1 = 0, len2 = 0;
|
|
|
|
in_wpos = cp_in_wpos;
|
|
in_rpos = cp_in_rpos;
|
|
|
|
if (in_wpos < in_rpos) {
|
|
free_len = in_rpos - in_wpos - 1;
|
|
} else {
|
|
free_len = cp_in_size - in_wpos + in_rpos - 1;
|
|
}
|
|
|
|
if (free_len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
size = LengthOfCQueue(&sbc_queue);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
|
|
if (size > free_len) {
|
|
size = free_len;
|
|
}
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
r = PeekCQueue(&sbc_queue, size, &e1, &len1, &e2, &len2);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
|
|
if (r == CQ_ERR) {
|
|
TRACE(2, "%s: Failed to get %u from sbc_queue", __func__, size);
|
|
return 1;
|
|
}
|
|
|
|
if (in_wpos < in_rpos) {
|
|
if (len1) {
|
|
memcpy(&cp_in_cache[in_wpos], e1, len1);
|
|
in_wpos += len1;
|
|
}
|
|
if (len2) {
|
|
memcpy(&cp_in_cache[in_wpos], e2, len2);
|
|
in_wpos += len2;
|
|
}
|
|
} else {
|
|
free_len = cp_in_size - in_wpos;
|
|
if (len1) {
|
|
if (free_len > len1) {
|
|
free_len = len1;
|
|
}
|
|
memcpy(&cp_in_cache[in_wpos], e1, free_len);
|
|
len1 -= free_len;
|
|
e1 += free_len;
|
|
in_wpos = cp_update_in_cache_pos(in_wpos, free_len);
|
|
if (len1) {
|
|
memcpy(&cp_in_cache[in_wpos], e1, len1);
|
|
in_wpos = cp_update_in_cache_pos(in_wpos, len1);
|
|
}
|
|
if (in_wpos < in_rpos) {
|
|
free_len = in_rpos - in_wpos - 1;
|
|
} else {
|
|
free_len = cp_in_size - in_wpos;
|
|
}
|
|
}
|
|
if (len2) {
|
|
if (free_len > len2) {
|
|
free_len = len2;
|
|
}
|
|
memcpy(&cp_in_cache[in_wpos], e2, free_len);
|
|
len2 -= free_len;
|
|
e2 += free_len;
|
|
in_wpos = cp_update_in_cache_pos(in_wpos, free_len);
|
|
if (len2) {
|
|
memcpy(&cp_in_cache[in_wpos], e2, len2);
|
|
in_wpos = cp_update_in_cache_pos(in_wpos, len2);
|
|
}
|
|
}
|
|
}
|
|
|
|
cp_in_wpos = in_wpos;
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
DeCQueue(&sbc_queue, NULL, size);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
|
|
return 0;
|
|
}
|
|
|
|
FRAM_TEXT_LOC
|
|
static int mcu_decode_a2dp_process(uint8_t *out, uint32_t out_max,
|
|
uint32_t *p_out_len, const uint8_t *in,
|
|
uint32_t in_len, uint32_t *p_consume_len) {
|
|
int ret = 0;
|
|
uint32_t out_rpos;
|
|
|
|
#ifdef A2DP_TRACE_CP_ACCEL
|
|
TRACE(3, "mcu_dec_proc[%u]: state=%u len=%u", mcu_dec_idx,
|
|
cp_dec_state[mcu_dec_idx], cp_out_len[mcu_dec_idx]);
|
|
#endif
|
|
|
|
cp_accel_send_event_mcu2cp(
|
|
CP_BUILD_ID(CP_TASK_A2DP_DECODE, CP_EVENT_A2DP_DECODE));
|
|
|
|
if (cp_dec_state[mcu_dec_idx] != CP_DEC_STATE_DONE) {
|
|
ret = 1;
|
|
goto _exit;
|
|
}
|
|
if (cp_out_len[mcu_dec_idx] != out_max) {
|
|
ret = 2;
|
|
goto _exit;
|
|
}
|
|
|
|
out_rpos = cp_out_loop_size * mcu_dec_idx;
|
|
|
|
memcpy(out, &cp_out_cache[out_rpos], out_max);
|
|
|
|
if (p_out_len) {
|
|
*p_out_len = out_max;
|
|
}
|
|
|
|
_exit:
|
|
if (cp_dec_state[mcu_dec_idx] == CP_DEC_STATE_DONE ||
|
|
cp_dec_state[mcu_dec_idx] == CP_DEC_STATE_FAILED) {
|
|
if (mtu_count > cp_dec_mtu_cnt[mcu_dec_idx]) {
|
|
mtu_count -= cp_dec_mtu_cnt[mcu_dec_idx];
|
|
} else {
|
|
mtu_count = 0;
|
|
}
|
|
|
|
cp_dec_state[mcu_dec_idx] = CP_DEC_STATE_IDLE;
|
|
|
|
mcu_dec_idx++;
|
|
if (mcu_dec_idx >= CP_DEC_SLOT_CNT) {
|
|
mcu_dec_idx = 0;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct cp_task_desc TASK_DESC_A2DP = {CP_ACCEL_STATE_CLOSED,
|
|
cp_a2dp_main, NULL, NULL, NULL};
|
|
void cp_a2dp_init(void) {
|
|
mcu_dec_inited = false;
|
|
norflash_api_flush_disable(NORFLASH_API_USER_CP,
|
|
(uint32_t)cp_accel_init_done);
|
|
cp_accel_open(CP_TASK_A2DP_DECODE, &TASK_DESC_A2DP);
|
|
while (cp_accel_init_done() == false) {
|
|
hal_sys_timer_delay_us(100);
|
|
}
|
|
norflash_api_flush_enable(NORFLASH_API_USER_CP);
|
|
}
|
|
|
|
void cp_a2dp_deinit(void) { cp_accel_close(CP_TASK_A2DP_DECODE); }
|
|
#endif
|
|
|
|
FRAM_TEXT_LOC
|
|
static int decode_a2dp_begin(enum APP_OVERLAY_ID_T overlay_type) {
|
|
#ifdef TIMER1_BASE
|
|
dec_start_time = hal_fast_sys_timer_get();
|
|
#else
|
|
dec_start_time = hal_sys_timer_get();
|
|
#endif
|
|
#ifndef A2DP_TRACE_DEC_TIME
|
|
dec_trace_time = false;
|
|
#endif
|
|
|
|
#ifndef A2DP_CP_ACCEL
|
|
if (overlay_type == APP_OVERLAY_A2DP) {
|
|
decode_sbc_begin();
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
FRAM_TEXT_LOC
|
|
static int load_a2dp_encoded_data(enum APP_OVERLAY_ID_T overlay_type,
|
|
uint8_t **p_data, uint32_t *p_len) {
|
|
int ret = -1;
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
ret = mcu_load_a2dp_encoded_data(p_data, p_len);
|
|
#else
|
|
if (0) {
|
|
#if defined(A2DP_LDAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
ret = load_ldac_frame(p_data, p_len);
|
|
#endif
|
|
#if defined(A2DP_AAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_AAC) {
|
|
ret = load_aac_frame(p_data, p_len);
|
|
#endif
|
|
#if defined(A2DP_SCALABLE_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_SCALABLE) {
|
|
ret = load_scalable_frame(p_data, p_len);
|
|
#endif
|
|
#if defined(A2DP_LHDC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LHDC) {
|
|
ret = load_lhdc_frame(p_data, p_len);
|
|
#endif
|
|
} else if (overlay_type == APP_OVERLAY_A2DP) {
|
|
ret = load_sbc_frame(p_data, p_len);
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
FRAM_TEXT_LOC
|
|
static int decode_a2dp_process(enum APP_OVERLAY_ID_T overlay_type, uint8_t *out,
|
|
uint32_t out_max, uint32_t *p_out_len,
|
|
const uint8_t *in, uint32_t in_len,
|
|
uint32_t *p_consume_len) {
|
|
int ret = -1;
|
|
|
|
if (p_out_len) {
|
|
*p_out_len = 0;
|
|
}
|
|
if (p_consume_len) {
|
|
*p_consume_len = 0;
|
|
}
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
ret = mcu_decode_a2dp_process(out, out_max, p_out_len, in, in_len,
|
|
p_consume_len);
|
|
#else
|
|
if (0) {
|
|
#if defined(A2DP_LDAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
ret = decode_ldac_frame(out, out_max, p_out_len, in, in_len, p_consume_len);
|
|
#endif
|
|
#if defined(A2DP_AAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_AAC) {
|
|
ret = decode_aac_frame(out, out_max, p_out_len, in, in_len, p_consume_len);
|
|
#endif
|
|
#if defined(A2DP_SCALABLE_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_SCALABLE) {
|
|
ret = decode_scalable_frame(out, out_max, p_out_len, in, in_len,
|
|
p_consume_len);
|
|
#endif
|
|
#if defined(A2DP_LHDC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LHDC) {
|
|
ret = decode_lhdc_frame(out, out_max, p_out_len, in, in_len, p_consume_len);
|
|
#endif
|
|
} else if (overlay_type == APP_OVERLAY_A2DP) {
|
|
ret = decode_sbc_frame(out, out_max, p_out_len, in, in_len, p_consume_len);
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
FRAM_TEXT_LOC
|
|
static int consume_a2dp_encoded_data(enum APP_OVERLAY_ID_T overlay_type,
|
|
const uint8_t *data, uint32_t len) {
|
|
#ifndef A2DP_CP_ACCEL
|
|
if (0) {
|
|
#if defined(A2DP_LDAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
consume_ldac_frame(data, len);
|
|
#endif
|
|
} else if (overlay_type == APP_OVERLAY_A2DP) {
|
|
consume_sbc_frame(data, len);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
FRAM_TEXT_LOC
|
|
static void decode_a2dp_end(enum APP_OVERLAY_ID_T overlay_type,
|
|
uint32_t expect_out_len, uint32_t out_len,
|
|
uint32_t in_len) {
|
|
uint32_t etime;
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
|
|
if (0) {
|
|
#if defined(A2DP_LDAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
mcu_decode_ldac_end(expect_out_len, out_len, in_len);
|
|
#endif
|
|
}
|
|
|
|
#else
|
|
|
|
if (0) {
|
|
#if defined(A2DP_LDAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
decode_ldac_end(expect_out_len, out_len, in_len);
|
|
#endif
|
|
#if defined(A2DP_LHDC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LHDC) {
|
|
decode_lhdc_end(expect_out_len, out_len, in_len);
|
|
#endif
|
|
} else if (overlay_type == APP_OVERLAY_A2DP) {
|
|
decode_sbc_end(expect_out_len, out_len, in_len);
|
|
}
|
|
|
|
#endif
|
|
|
|
if (dec_reset_queue) {
|
|
dec_reset_queue = false;
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
ResetCQueue(&sbc_queue);
|
|
TRACE(0, "Reset queue");
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
}
|
|
|
|
#ifdef TIMER1_BASE
|
|
etime = hal_fast_sys_timer_get();
|
|
if (dec_trace_time) {
|
|
TRACE(2, "a2dp decode: %u us in %u us",
|
|
FAST_TICKS_TO_US(etime - dec_start_time),
|
|
FAST_TICKS_TO_US(etime - last_dec_time));
|
|
}
|
|
#else
|
|
etime = hal_sys_timer_get();
|
|
if (dec_trace_time) {
|
|
TRACE(2, "a2dp decode: %u ms in %u ms", TICKS_TO_MS(etime - dec_start_time),
|
|
TICKS_TO_MS(etime - last_dec_time));
|
|
}
|
|
#endif
|
|
last_dec_time = etime;
|
|
}
|
|
|
|
FRAM_TEXT_LOC
|
|
static int decode_a2dp_frame(enum APP_OVERLAY_ID_T overlay_type,
|
|
unsigned char *pcm_buffer, unsigned int pcm_len) {
|
|
uint32_t all_out_len = 0, all_in_len = 0;
|
|
int ret, ret2;
|
|
uint32_t out_len;
|
|
uint8_t *enc_data;
|
|
uint32_t enc_len;
|
|
uint32_t consume_len;
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
mcu_decode_a2dp_init(pcm_len);
|
|
#endif
|
|
|
|
ret = decode_a2dp_begin(overlay_type);
|
|
if (ret) {
|
|
return 0;
|
|
}
|
|
|
|
while (all_out_len < pcm_len) {
|
|
ret = load_a2dp_encoded_data(overlay_type, &enc_data, &enc_len);
|
|
if (ret) {
|
|
break;
|
|
}
|
|
|
|
ret = decode_a2dp_process(overlay_type, pcm_buffer + all_out_len,
|
|
pcm_len - all_out_len, &out_len, enc_data,
|
|
enc_len, &consume_len);
|
|
|
|
ret2 = consume_a2dp_encoded_data(overlay_type, enc_data, consume_len);
|
|
|
|
all_out_len += out_len;
|
|
all_in_len += consume_len;
|
|
|
|
if (ret || ret2) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
decode_a2dp_end(overlay_type, pcm_len, all_out_len, all_in_len);
|
|
|
|
return all_out_len;
|
|
}
|
|
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
#define AAC_RESAMPLE_ITER_LEN (1024 * 2 * 2)
|
|
#define SBC_RESAMPLE_ITER_LEN (128 * 2 * 2)
|
|
|
|
static struct APP_RESAMPLE_T *a2dp_resample;
|
|
|
|
FRAM_TEXT_LOC
|
|
static int a2dp_resample_iter(uint8_t *buf, uint32_t len) {
|
|
enum APP_OVERLAY_ID_T overlay_type;
|
|
|
|
overlay_type = app_get_current_overlay();
|
|
|
|
uint32_t l = decode_a2dp_frame(overlay_type, buf, len);
|
|
if (l != len) {
|
|
TRACE(2, "a2dp_audio_more_data decode err %d/%d", l, len);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
extern bool process_delay(int32_t delay_ms);
|
|
|
|
void static attempt_slow_samplerate(uint8_t overlay_type) {
|
|
uint32_t current_mtu_conut = 0;
|
|
if (check_interval > 0)
|
|
check_interval--;
|
|
assumed_mtu_interval = 20;
|
|
current_mtu_conut = mtu_count;
|
|
|
|
if (lagest_mtu_in1s < current_mtu_conut)
|
|
lagest_mtu_in1s = current_mtu_conut;
|
|
if (least_mtu_in1s > current_mtu_conut)
|
|
least_mtu_in1s = current_mtu_conut;
|
|
|
|
if (check_interval == 0) {
|
|
int32_t delay_ms = 0;
|
|
if (overlay_type == APP_OVERLAY_A2DP) {
|
|
delay_ms = (delay_mtu_limit - (int)lagest_mtu_in1s) * 128 * 1000 /
|
|
a2dp_sample_rate;
|
|
} else
|
|
#if defined(A2DP_AAC_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_AAC) {
|
|
delay_ms = (delay_mtu_limit - (int)lagest_mtu_in1s) * 1024 * 1000 /
|
|
a2dp_sample_rate;
|
|
} else
|
|
#endif
|
|
#if defined(A2DP_SCALABLE_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_SCALABLE) {
|
|
delay_ms = (delay_mtu_limit - (int)lagest_mtu_in1s) * 864 * 1000 /
|
|
a2dp_sample_rate;
|
|
} else
|
|
#endif
|
|
#if defined(A2DP_LHDC_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_LHDC) {
|
|
delay_ms = (delay_mtu_limit - (int)lagest_mtu_in1s) * 512 * 1000 /
|
|
a2dp_sample_rate;
|
|
} else
|
|
#endif
|
|
#if defined(A2DP_LDAC_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
delay_ms = (delay_mtu_limit - (int)lagest_mtu_in1s) * 256 * 1000 /
|
|
a2dp_sample_rate;
|
|
} else
|
|
#endif
|
|
{
|
|
delay_ms =
|
|
(delay_mtu_limit - (int)lagest_mtu_in1s) * assumed_mtu_interval;
|
|
}
|
|
TRACE(6,
|
|
"store_interval=%d,lagest_mtu_in1s=%d,least_mtu_in1s=%d,delay_ms=%d,"
|
|
"frame_len=%d,mtu_count=%d",
|
|
ssb_err_max, lagest_mtu_in1s, least_mtu_in1s, delay_ms,
|
|
sbc_frame_rev_len, mtu_count);
|
|
process_delay(delay_ms);
|
|
check_interval = 50;
|
|
lagest_mtu_in1s = 0;
|
|
least_mtu_in1s = 0xffffffff;
|
|
}
|
|
}
|
|
|
|
// A2DP_EQ_24BIT == 1, buf should be 32bit(24bit)
|
|
// A2DP_EQ_24BIT == 0, buf should be 16bit
|
|
uint32_t a2dp_audio_more_data(uint8_t overlay_type, uint8_t *buf,
|
|
uint32_t len) {
|
|
uint32_t l = 0;
|
|
|
|
if (!a2dp_audio_isrunning(A2DPPLAY_STRTEAM_GET, true)) {
|
|
}
|
|
|
|
#ifdef A2DP_EQ_24BIT
|
|
// Change len to 16-bit sample buffer length
|
|
if ((bt_sbc_player_get_sample_bit() == 16)
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
|| allow_resample
|
|
#endif
|
|
) {
|
|
len = len / (sizeof(int32_t) / sizeof(int16_t));
|
|
}
|
|
#endif
|
|
|
|
if (a2dp_cache_status == APP_AUDIO_CACHE_CACHEING) {
|
|
TRACE(1, "a2dp_audio_more_data cache not ready skip frame %d\n",
|
|
overlay_type);
|
|
} else {
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
if (allow_resample) {
|
|
app_playback_resample_run(a2dp_resample, buf, len);
|
|
} else
|
|
#endif
|
|
{
|
|
l = decode_a2dp_frame((enum APP_OVERLAY_ID_T)overlay_type, buf, len);
|
|
if (l != len) {
|
|
TRACE(2, "a2dp_audio_more_data decode err %d/%d", l, len);
|
|
if (l < len) {
|
|
memset(buf + l, 0, len - l);
|
|
// a2dp_cache_status = APP_AUDIO_CACHE_CACHEING;
|
|
TRACE(0, "set to APP_AUDIO_CACHE_CACHEING");
|
|
}
|
|
} else {
|
|
// TRACE(2,"a2dp_audio_more_data decode success %d/%d", l, len);
|
|
}
|
|
}
|
|
if (a2dp_cache_status == APP_AUDIO_CACHE_OK) {
|
|
attempt_slow_samplerate(overlay_type);
|
|
}
|
|
}
|
|
|
|
#ifdef A2DP_EQ_24BIT
|
|
if ((bt_sbc_player_get_sample_bit() == 16)
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
|| allow_resample
|
|
#endif
|
|
) {
|
|
convert_16bit_to_24bit((int32_t *)buf, (int16_t *)buf,
|
|
len / sizeof(int16_t));
|
|
// Restore len to 24-bit sample buffer length
|
|
len = len * (sizeof(int32_t) / sizeof(int16_t));
|
|
}
|
|
#endif
|
|
|
|
A2DP_SYNC_WITH_GET_MUTUX_SET();
|
|
|
|
return len;
|
|
}
|
|
|
|
#if defined(A2DP_LDAC_ON)
|
|
/* Convert LDAC Error Code to string */
|
|
#define CASE_RETURN_STR(const) \
|
|
case const: \
|
|
return #const;
|
|
static const char *ldac_ErrCode2Str(int ErrCode) {
|
|
switch (ErrCode) {
|
|
CASE_RETURN_STR(LDACBT_ERR_NONE);
|
|
CASE_RETURN_STR(LDACBT_ERR_NON_FATAL);
|
|
CASE_RETURN_STR(LDACBT_ERR_BIT_ALLOCATION);
|
|
CASE_RETURN_STR(LDACBT_ERR_NOT_IMPLEMENTED);
|
|
CASE_RETURN_STR(LDACBT_ERR_NON_FATAL_ENCODE);
|
|
CASE_RETURN_STR(LDACBT_ERR_FATAL);
|
|
CASE_RETURN_STR(LDACBT_ERR_SYNTAX_BAND);
|
|
CASE_RETURN_STR(LDACBT_ERR_SYNTAX_GRAD_A);
|
|
CASE_RETURN_STR(LDACBT_ERR_SYNTAX_GRAD_B);
|
|
CASE_RETURN_STR(LDACBT_ERR_SYNTAX_GRAD_C);
|
|
CASE_RETURN_STR(LDACBT_ERR_SYNTAX_GRAD_D);
|
|
CASE_RETURN_STR(LDACBT_ERR_SYNTAX_GRAD_E);
|
|
CASE_RETURN_STR(LDACBT_ERR_SYNTAX_IDSF);
|
|
CASE_RETURN_STR(LDACBT_ERR_SYNTAX_SPEC);
|
|
CASE_RETURN_STR(LDACBT_ERR_BIT_PACKING);
|
|
CASE_RETURN_STR(LDACBT_ERR_ALLOC_MEMORY);
|
|
CASE_RETURN_STR(LDACBT_ERR_FATAL_HANDLE);
|
|
CASE_RETURN_STR(LDACBT_ERR_ILL_SYNCWORD);
|
|
CASE_RETURN_STR(LDACBT_ERR_ILL_SMPL_FORMAT);
|
|
CASE_RETURN_STR(LDACBT_ERR_ILL_PARAM);
|
|
CASE_RETURN_STR(LDACBT_ERR_ASSERT_SAMPLING_FREQ);
|
|
CASE_RETURN_STR(LDACBT_ERR_ASSERT_SUP_SAMPLING_FREQ);
|
|
CASE_RETURN_STR(LDACBT_ERR_CHECK_SAMPLING_FREQ);
|
|
CASE_RETURN_STR(LDACBT_ERR_ASSERT_CHANNEL_CONFIG);
|
|
CASE_RETURN_STR(LDACBT_ERR_CHECK_CHANNEL_CONFIG);
|
|
CASE_RETURN_STR(LDACBT_ERR_ASSERT_FRAME_LENGTH);
|
|
CASE_RETURN_STR(LDACBT_ERR_ASSERT_SUP_FRAME_LENGTH);
|
|
CASE_RETURN_STR(LDACBT_ERR_ASSERT_FRAME_STATUS);
|
|
CASE_RETURN_STR(LDACBT_ERR_ASSERT_NSHIFT);
|
|
CASE_RETURN_STR(LDACBT_ERR_ASSERT_CHANNEL_MODE);
|
|
CASE_RETURN_STR(LDACBT_ERR_ENC_INIT_ALLOC);
|
|
CASE_RETURN_STR(LDACBT_ERR_ENC_ILL_GRADMODE);
|
|
CASE_RETURN_STR(LDACBT_ERR_ENC_ILL_GRADPAR_A);
|
|
CASE_RETURN_STR(LDACBT_ERR_ENC_ILL_GRADPAR_B);
|
|
CASE_RETURN_STR(LDACBT_ERR_ENC_ILL_GRADPAR_C);
|
|
CASE_RETURN_STR(LDACBT_ERR_ENC_ILL_GRADPAR_D);
|
|
CASE_RETURN_STR(LDACBT_ERR_ENC_ILL_NBANDS);
|
|
CASE_RETURN_STR(LDACBT_ERR_PACK_BLOCK_FAILED);
|
|
CASE_RETURN_STR(LDACBT_ERR_DEC_INIT_ALLOC);
|
|
CASE_RETURN_STR(LDACBT_ERR_INPUT_BUFFER_SIZE);
|
|
CASE_RETURN_STR(LDACBT_ERR_UNPACK_BLOCK_FAILED);
|
|
CASE_RETURN_STR(LDACBT_ERR_UNPACK_BLOCK_ALIGN);
|
|
CASE_RETURN_STR(LDACBT_ERR_UNPACK_FRAME_ALIGN);
|
|
CASE_RETURN_STR(LDACBT_ERR_FRAME_LENGTH_OVER);
|
|
CASE_RETURN_STR(LDACBT_ERR_FRAME_ALIGN_OVER);
|
|
CASE_RETURN_STR(LDACBT_ERR_ALTER_EQMID_LIMITED);
|
|
CASE_RETURN_STR(LDACBT_ERR_ILL_EQMID);
|
|
CASE_RETURN_STR(LDACBT_ERR_ILL_SAMPLING_FREQ);
|
|
CASE_RETURN_STR(LDACBT_ERR_ILL_NUM_CHANNEL);
|
|
CASE_RETURN_STR(LDACBT_ERR_ILL_MTU_SIZE);
|
|
CASE_RETURN_STR(LDACBT_ERR_HANDLE_NOT_INIT);
|
|
default:
|
|
return "unknown-error-code";
|
|
}
|
|
}
|
|
|
|
char a_ErrorCodeStr[128];
|
|
const char *get_error_code_string(int error_code) {
|
|
int errApi, errHdl, errBlk;
|
|
|
|
errApi = LDACBT_API_ERR(error_code);
|
|
errHdl = LDACBT_HANDLE_ERR(error_code);
|
|
errBlk = LDACBT_BLOCK_ERR(error_code);
|
|
|
|
a_ErrorCodeStr[0] = '\0';
|
|
strcat(a_ErrorCodeStr, "API:");
|
|
strcat(a_ErrorCodeStr, ldac_ErrCode2Str(errApi));
|
|
strcat(a_ErrorCodeStr, " Handle:");
|
|
strcat(a_ErrorCodeStr, ldac_ErrCode2Str(errHdl));
|
|
strcat(a_ErrorCodeStr, " Block:");
|
|
strcat(a_ErrorCodeStr, ldac_ErrCode2Str(errBlk));
|
|
return a_ErrorCodeStr;
|
|
}
|
|
#endif
|
|
|
|
extern uint8_t bt_sbc_player_get_bitsDepth(void);
|
|
#if defined(A2DP_LDAC_ON)
|
|
uint32_t ldac_buffer_used = 0;
|
|
#endif
|
|
|
|
static const char *a2dp_get_codec_name(enum APP_OVERLAY_ID_T overlay_type) {
|
|
const char *codec_name;
|
|
|
|
if (0) {
|
|
#if defined(A2DP_LDAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
codec_name = "LDAC";
|
|
#endif
|
|
#if defined(A2DP_AAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_AAC) {
|
|
codec_name = "AAC";
|
|
#endif
|
|
#if defined(A2DP_SCALABLE_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_SCALABLE) {
|
|
codec_name = "SCALABLE";
|
|
#endif
|
|
#if defined(A2DP_LHDC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LHDC) {
|
|
codec_name = "LHDC";
|
|
#endif
|
|
} else if (overlay_type == APP_OVERLAY_A2DP) {
|
|
codec_name = "SBC";
|
|
} else {
|
|
codec_name = "UNKNOWN";
|
|
}
|
|
|
|
return codec_name;
|
|
}
|
|
|
|
static void a2dp_trace_codec_name(const char *prefix,
|
|
enum APP_OVERLAY_ID_T overlay_type) {
|
|
TRACE(3, "\n\n%s: codecType=%x->:%s\n", prefix, overlay_type,
|
|
a2dp_get_codec_name(overlay_type));
|
|
}
|
|
|
|
int a2dp_audio_init(void) {
|
|
const float EQLevel[25] = {
|
|
0.0630957, 0.0794328, 0.1, 0.1258925, 0.1584893,
|
|
0.1995262, 0.2511886, 0.3162278, 0.398107, 0.5011872,
|
|
0.6309573, 0.794328, 1, 1.258925, 1.584893,
|
|
1.995262, 2.5118864, 3.1622776, 3.9810717, 5.011872,
|
|
6.309573, 7.943282, 10, 12.589254, 15.848932}; //-12~12
|
|
uint8_t *buff = NULL;
|
|
uint8_t i;
|
|
|
|
enum APP_OVERLAY_ID_T overlay_type;
|
|
|
|
overlay_type = app_get_current_overlay();
|
|
|
|
a2dp_trace_codec_name(__func__, overlay_type);
|
|
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
APP_RESAMPLE_ITER_CALLBACK iter_cb;
|
|
|
|
uint32_t iter_len;
|
|
|
|
iter_cb = a2dp_resample_iter;
|
|
|
|
if (0) {
|
|
#if defined(A2DP_AAC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_AAC) {
|
|
iter_len = AAC_RESAMPLE_ITER_LEN;
|
|
#endif
|
|
} else if (overlay_type == APP_OVERLAY_A2DP) {
|
|
iter_len = SBC_RESAMPLE_ITER_LEN;
|
|
} else {
|
|
ASSERT(false, "%s: Bad app overlay type for play resample: %d", __func__,
|
|
overlay_type);
|
|
}
|
|
|
|
#ifdef CHIP_BEST1000
|
|
if (hal_get_chip_metal_id() >= HAL_CHIP_METAL_ID_2 &&
|
|
hal_sysfreq_get() <= HAL_CMU_FREQ_52M) {
|
|
allow_resample = true;
|
|
}
|
|
#endif
|
|
if (allow_resample) {
|
|
#ifdef RESAMPLE_ANY_SAMPLE_RATE
|
|
float ratio;
|
|
if (a2dp_sample_rate % AUD_SAMPRATE_8000) {
|
|
ratio = (float)CODEC_FREQ_22P5792M *
|
|
((float)a2dp_sample_rate / AUD_SAMPRATE_44100) / CODEC_FREQ_26M;
|
|
} else {
|
|
ratio = (float)CODEC_FREQ_24P576M *
|
|
((float)a2dp_sample_rate / AUD_SAMPRATE_48000) / CODEC_FREQ_26M;
|
|
}
|
|
a2dp_resample = app_playback_resample_any_open(AUD_CHANNEL_NUM_2, iter_cb,
|
|
iter_len, ratio);
|
|
#else
|
|
a2dp_resample = app_playback_resample_open(
|
|
a2dp_sample_rate, AUD_CHANNEL_NUM_2, iter_cb, iter_len);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < sizeof(sbc_eq_band_gain) / sizeof(float); i++) {
|
|
sbc_eq_band_gain[i] = EQLevel[cfg_aud_eq_sbc_band_settings[i] + 12];
|
|
}
|
|
|
|
A2DP_SYNC_WITH_GET_MUTUX_FREE();
|
|
A2DP_SYNC_WITH_PUT_MUTUX_FREE();
|
|
if (overlay_type == APP_OVERLAY_A2DP) {
|
|
app_audio_mempool_get_buff((uint8_t **)&sbc_decoder,
|
|
sizeof(btif_sbc_decoder_t));
|
|
}
|
|
|
|
#if defined(A2DP_AAC_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_AAC) {
|
|
int ret;
|
|
aac_meminit();
|
|
ret = aacdec_init();
|
|
ASSERT(ret == 0, "%s: aacdec_init ERROR", __func__);
|
|
}
|
|
#endif
|
|
|
|
#if defined(A2DP_SCALABLE_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_SCALABLE) {
|
|
app_audio_mempool_get_buff((uint8_t **)&scalable_input_mid_buf,
|
|
SCALABLE_READBUF_SIZE);
|
|
app_audio_mempool_get_buff((uint8_t **)&scalable_decoder_place,
|
|
SCALABLE_DECODER_SIZE);
|
|
app_audio_mempool_get_buff((uint8_t **)&scalable_decoder_temp_buf,
|
|
SCALABLE_FRAME_SIZE * 16);
|
|
}
|
|
hSSDecoder = NULL;
|
|
#endif
|
|
|
|
#if defined(A2DP_LHDC_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_LHDC) {
|
|
uint8_t bits_depth = bt_sbc_player_get_bitsDepth();
|
|
AUD_SAMPRATE_T sample_rate = bt_get_sbc_sample_rate();
|
|
TRACE(2, "a2dp_audio_init sample Rate=%d, bits_depth = %d\n", sample_rate,
|
|
bits_depth);
|
|
TRACE(1, "sys freq calc : %d\n", hal_sys_timer_calc_cpu_freq(5, 0));
|
|
|
|
// lhdcInit(bits_depth, sample_rate, bits_depth >= 24 ? 1 : 0);
|
|
// extern const char __testkey_start[];
|
|
extern const char testkey_bin[];
|
|
lhdcSetLicenseKeyTable((uint8_t *)testkey_bin);
|
|
|
|
TRACE(0, "lhdcSetLicenseKeyTable,testkey_bin:\n");
|
|
for (uint8_t i = 0; i < 128; i++) {
|
|
TRACE(1, "i = %d", i);
|
|
DUMP8("0x%02x ", &testkey_bin[i], 32);
|
|
i += 31;
|
|
}
|
|
lhdcInit(bits_depth, sample_rate, 0);
|
|
initial_lhdc_assemble_packet(false);
|
|
}
|
|
#endif
|
|
|
|
#ifdef SBC_QUEUE_SIZE
|
|
if (0) {
|
|
#if defined(A2DP_AAC_ON) || defined(A2DP_SCALABLE_ON) || defined(A2DP_LHDC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_AAC) {
|
|
#if defined(MIX_MIC_DURING_MUSIC)
|
|
g_sbc_queue_size = app_audio_mempool_free_buff_size() - 4096;
|
|
#else
|
|
g_sbc_queue_size = app_audio_mempool_free_buff_size();
|
|
#endif
|
|
#endif
|
|
#if defined(A2DP_SCALABLE_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_SCALABLE) {
|
|
#if defined(MIX_MIC_DURING_MUSIC)
|
|
g_sbc_queue_size = app_audio_mempool_free_buff_size() - 4096;
|
|
#else
|
|
g_sbc_queue_size = app_audio_mempool_free_buff_size();
|
|
#endif
|
|
#endif
|
|
#if defined(A2DP_LHDC_ON)
|
|
} else if (overlay_type == APP_OVERLAY_A2DP_LHDC) {
|
|
#if defined(MIX_MIC_DURING_MUSIC)
|
|
g_sbc_queue_size = app_audio_mempool_free_buff_size() - 4096;
|
|
#else
|
|
g_sbc_queue_size = app_audio_mempool_free_buff_size();
|
|
#endif
|
|
#endif
|
|
} else {
|
|
g_sbc_queue_size = SBC_QUEUE_SIZE;
|
|
}
|
|
#else
|
|
g_sbc_queue_size = app_audio_mempool_free_buff_size();
|
|
#endif
|
|
|
|
#if defined(A2DP_LDAC_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
// if((bt_sbc_player_get_codec_type() == BTIF_AVDTP_CODEC_TYPE_NON_A2DP)){
|
|
// app_audio_mempool_get_buff(&g_medMemHeap, MED_MEM_HEAP_SIZE);
|
|
// speech_heap_init((uint8_t *)(&g_medMemHeap[0]), MED_MEM_HEAP_SIZE);
|
|
g_sbc_queue_size = app_audio_mempool_free_buff_size() - MED_MEM_HEAP_SIZE;
|
|
TRACE(1, "A2DP_LDAC_ON g_sbc_queue_size %d\n", g_sbc_queue_size);
|
|
}
|
|
#endif
|
|
|
|
app_audio_mempool_get_buff(&buff, g_sbc_queue_size);
|
|
ASSERT((buff != NULL), "%s:get sbc queue buff fail, size %d bytes!", __func__,
|
|
g_sbc_queue_size);
|
|
memset(buff, 0, g_sbc_queue_size);
|
|
a2dp_audio_isrunning(A2DPPLAY_STRTEAM_PUT, false);
|
|
a2dp_audio_isrunning(A2DPPLAY_STRTEAM_GET, false);
|
|
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
APP_AUDIO_InitCQueue(&sbc_queue, g_sbc_queue_size, buff);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
|
|
#if defined(A2DP_LDAC_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
ldac_buffer_used =
|
|
app_audio_mempool_total_buf() - app_audio_mempool_free_buff_size();
|
|
ldac_recovery = false;
|
|
ldac_dec_init();
|
|
}
|
|
#endif
|
|
|
|
#ifdef A2DP_AUDIO_SYNC_WITH_LOCAL
|
|
a2dp_audio_sync_proc(A2DPPLAY_SYNC_STATUS_SET | A2DPPLAY_SYNC_STATUS_RESET,
|
|
A2DP_AUDIO_SYNC_WITH_LOCAL_SAMPLERATE_DEFAULT);
|
|
#endif
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
cp_a2dp_init();
|
|
#endif
|
|
|
|
a2dp_cache_status = APP_AUDIO_CACHE_CACHEING;
|
|
need_init_decoder = true;
|
|
dec_reset_queue = false;
|
|
|
|
sbc_frame_rev_len = 0;
|
|
sbc_frame_num = 0;
|
|
sbc_frame_size = SBC_TEMP_BUFFER_SIZE;
|
|
mtu_count = 0;
|
|
assumed_mtu_interval = 0;
|
|
return 0;
|
|
}
|
|
|
|
int a2dp_audio_deinit(void) {
|
|
enum APP_OVERLAY_ID_T overlay_type;
|
|
|
|
overlay_type = app_get_current_overlay();
|
|
|
|
a2dp_trace_codec_name(__func__, overlay_type);
|
|
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
if (allow_resample) {
|
|
app_playback_resample_close(a2dp_resample);
|
|
a2dp_resample = NULL;
|
|
#ifdef CHIP_BEST1000
|
|
allow_resample = false;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#ifdef A2DP_CP_ACCEL
|
|
cp_a2dp_deinit();
|
|
#endif
|
|
|
|
#if defined(A2DP_LDAC_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_LDAC) {
|
|
ldac_dec_deinit();
|
|
}
|
|
#endif
|
|
|
|
#if defined(A2DP_LHDC_ON)
|
|
if (overlay_type == APP_OVERLAY_A2DP_LHDC) {
|
|
lhdcDestroy();
|
|
}
|
|
#endif
|
|
|
|
#if defined(A2DP_AAC_ON)
|
|
aacdec_deinit();
|
|
aac_memdeinit();
|
|
#endif
|
|
|
|
A2DP_SYNC_WITH_GET_MUTUX_SET();
|
|
A2DP_SYNC_WITH_PUT_MUTUX_SET();
|
|
|
|
A2DP_SYNC_WITH_GET_MUTUX_FREE();
|
|
A2DP_SYNC_WITH_PUT_MUTUX_FREE();
|
|
|
|
a2dp_audio_isrunning(A2DPPLAY_STRTEAM_PUT, false);
|
|
a2dp_audio_isrunning(A2DPPLAY_STRTEAM_GET, false);
|
|
|
|
a2dp_cache_status = APP_AUDIO_CACHE_QTY;
|
|
need_init_decoder = true;
|
|
|
|
sbc_frame_rev_len = 0;
|
|
sbc_frame_num = 0;
|
|
sbc_frame_size = SBC_TEMP_BUFFER_SIZE;
|
|
mtu_count = 0;
|
|
assumed_mtu_interval = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
float a2dp_audio_latency_factor_get(void) { return 1.0f; }
|
|
#endif
|