1167 lines
34 KiB
C
1167 lines
34 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 __VOICE_OVER_BLE_ENABLED__
|
|
|
|
#include "voice_over_ble.h"
|
|
#include "Pcm8k_Cvsd.h"
|
|
#include "app.h"
|
|
#include "app_audio.h"
|
|
#include "app_ble_cmd_handler.h"
|
|
#include "app_ble_custom_cmd.h"
|
|
#include "app_datapath_server.h"
|
|
#include "app_thread.h"
|
|
#include "app_utils.h"
|
|
#include "apps.h"
|
|
#include "audioflinger.h"
|
|
#include "cmsis_os.h"
|
|
#include "hal_iomux.h"
|
|
#include "hal_trace.h"
|
|
#include "string.h"
|
|
|
|
/**
|
|
* @brief The states of the VOB state machine
|
|
*
|
|
*/
|
|
typedef enum {
|
|
VOB_STATE_IDLE = 0,
|
|
VOB_STATE_ADVERTISING,
|
|
VOB_STATE_STOPPING_ADV,
|
|
VOB_STATE_ADV_STOPPED,
|
|
VOB_STATE_CONNECTING,
|
|
VOB_STATE_STOPPING_CONNECTING,
|
|
VOB_STATE_CONNECTED,
|
|
VOB_STATE_DISCONNECTED,
|
|
VOB_STATE_VOICE_STREAM,
|
|
} VOB_STATE_E;
|
|
|
|
/**
|
|
* @brief Buffer management of the VOB
|
|
*
|
|
*/
|
|
typedef struct {
|
|
/**< for SRC: the encoder will encode the PCM data into the audio data and
|
|
fill them into encodedDataBuf for DST: the received encoded data will be
|
|
pushed into encodedDataBuf pending for decoding and playing */
|
|
uint32_t encodedDataIndexToFill;
|
|
|
|
/**< for SRC: the BLE will fetch data at this index from the encodedDataBuf
|
|
and send them for DST: the player will fetch data at this index from the
|
|
encodedDataBuf, decode them and play */
|
|
uint32_t encodedDataIndexToFetch;
|
|
|
|
uint32_t encodedDataLength;
|
|
|
|
uint8_t isCachingDone;
|
|
|
|
} VOB_DATA_BUF_MANAGEMENT_T;
|
|
|
|
/**
|
|
* @brief Format of the audio encoder function
|
|
*
|
|
* @param pcmDataPtr Pointer of the input PCM data to be encoded
|
|
* @param pcmDataLen Length of the input PCM data to be encoded
|
|
* @param encodedDataPtr Pointer of the output encoded data
|
|
* @param encodedDataLen Length of the output encoded data
|
|
*
|
|
*/
|
|
typedef void (*PCM_Encoder_Handler_t)(uint8_t *pcmDataPtr, uint32_t pcmDataLen,
|
|
encodedDataPtr, encodedDataLen);
|
|
|
|
/**< configure the voice settings */
|
|
#define VOB_VOICE_BIT_NUMBER (AUD_BITS_16)
|
|
#define VOB_VOICE_SAMPLE_RATE (AUD_SAMPRATE_8000)
|
|
#define VOB_VOICE_VOLUME (10)
|
|
|
|
/**< signal of the VOB application thread */
|
|
#define VOB_SIGNAL_GET_RESPONSE 0x80
|
|
|
|
/**< signal of the BLE thread */
|
|
#define BLE_SIGNAL_VOB_GET_ENCODED_AUDIO_DATA 0x01
|
|
#define BLE_SIGNAL_VOB_DATA_SENT_OUT 0x02
|
|
#define BLE_SIGNAL_VOB_RECEIVED_DATA 0x04
|
|
#define BLE_SIGNAL_VOB_CONNECTED 0x08
|
|
#define BLE_SIGNAL_VOB_DISCONNECTED 0x10
|
|
|
|
/**< for SRC, the buffer is used to save the collected PCM data via the MIC
|
|
for DST, the buffer is used to save the decoded PCM data pending for
|
|
playing back*/
|
|
#define VOB_VOICE_PCM_DATA_CHUNK_SIZE (2048)
|
|
#define VOB_VOICE_PCM_DATA_CHUNK_COUNT (8)
|
|
#define VOB_VOICE_PCM_DATA_STORAGE_BUF_SIZE \
|
|
(VOB_VOICE_PCM_DATA_CHUNK_SIZE * VOB_VOICE_PCM_DATA_CHUNK_SIZE)
|
|
|
|
#define VOB_ENCODED_DATA_CACHE_SIZE (2048)
|
|
|
|
/**< for SRC, the buffer is used to save the encoded audio data,
|
|
for DST, the buffer is used to save the received encoded audio data via the
|
|
BLE*/
|
|
#define VOB_ENCODED_DATA_STORAGE_BUF_SIZE (2048 * 2)
|
|
|
|
/**< timeout in ms of waiting for the response */
|
|
#define VOB_WAITING_RESPONSE_TIMEOUT_IN_MS (5000)
|
|
|
|
/**< connection parameter */
|
|
#define VOB_CONNECTION_INTERVAL_IN_MS 5
|
|
#define VOB_CONNECTION_SUPERVISION_TIMEOUT_IN_MS 5000
|
|
|
|
/**< depends on the compression ratio of the codec algrithm */
|
|
/**< now the cvsd is used, so the ratio is 50% */
|
|
#define VOB_PCM_SIZE_TO_AUDIO_SIZE(size) ((size) / 2)
|
|
#define VOB_AUDIO_SIZE_TO_PCM_SIZE(size) ((size)*2)
|
|
|
|
static osThreadId ble_app_tid;
|
|
static void ble_app_thread(void const *argument);
|
|
osThreadDef(ble_app_thread, osPriorityNormal, 1, 512, "ble_app");
|
|
|
|
/**< connecting supervisor timer, if timeout happened, the connecting try will
|
|
* be stopped */
|
|
#define VOB_CONNECTING_TIMEOUT_IN_MS (10 * 1000)
|
|
static void connecting_supervisor_timer_cb(void const *n);
|
|
osTimerDef(APP_VOB_CONNECTING_SUPERVISOR_TIMER, connecting_supervisor_timer_cb);
|
|
static osTimerId vob_connecting_supervisor_timer;
|
|
|
|
/**< state machine of the VOB */
|
|
static VOB_STATE_E vob_state;
|
|
|
|
static VOB_ROLE_E vob_role;
|
|
|
|
/**< SRC mic stream environment */
|
|
AUDIO_STREAM_ENV_T src_mic_stream_env;
|
|
|
|
/**< DST speaker stream environment */
|
|
AUDIO_STREAM_ENV_T dst_speaker_stream_env;
|
|
|
|
VOB_DATA_BUF_MANAGEMENT_T vob_data_management;
|
|
|
|
/**< mutex of the voice over ble data buffer environment */
|
|
osMutexId vob_env_mutex_id;
|
|
osMutexDef(vob_env_mutex);
|
|
|
|
#define STREAM_A2DP_SPEAKER 0
|
|
|
|
#define STREAM_A2DP_PCM_DATA_BUF_SIZE 1024
|
|
#define STREAM_A2DP_ENCODED_DATA_BUF_SIZE 1024
|
|
static const VOB_FUNCTIONALITY_CONFIG_T vob_func_config[] = {
|
|
{STREAM_A2DP_PCM_DATA_BUF_SIZE, STREAM_A2DP_ENCODED_DATA_BUF_SIZE
|
|
|
|
},
|
|
};
|
|
|
|
static VOB_DATA_BUF_MANAGEMENT_T stream_a2dp_data_management;
|
|
|
|
#define STREAM_DATA_PROCESSING_UNIT_IN_BYTES 1024
|
|
|
|
struct gap_bdaddr DST_BLE_BdAddr = {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}, 0};
|
|
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
|
|
#define RESAMPLE_ITER_LEN (128 * 2 * 2)
|
|
|
|
static struct APP_RESAMPLE_T *ble_resample;
|
|
|
|
static int ble_resample_iter(uint8_t *buf, uint32_t len) {
|
|
uint32_t acquiredPCMDataLength = decode_sbc_frame(buf, len);
|
|
|
|
if (acquiredPCMDataLength < len) {
|
|
TRACE(0, "Start encoded data caching again.");
|
|
stream_a2dp_data_management.isCachingDone = false;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
uint32_t a2dp_audio_prepare_pcm_data(uint8_t *buf, uint32_t len) {
|
|
if (stream_a2dp_data_management.isCachingDone) {
|
|
// the caching stage has been done, decode the encoded data and feed the PCM
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
if (allow_resample) {
|
|
app_playback_resample_run(ble_resample, buf, len);
|
|
} else
|
|
#endif
|
|
{
|
|
uint32_t acquiredPCMDataLength = decode_sbc_frame(buf, len);
|
|
// the queued encoded data does't meet the PCM data requirement,
|
|
// need to cache again
|
|
if (acquiredPCMDataLength < len) {
|
|
TRACE(2,
|
|
"Need %d PCM data, but the queue encoded data can only provide "
|
|
"%d bytes",
|
|
len, acquiredPCMDataLength);
|
|
|
|
memset(buf + acquiredPCMDataLength, 0, len - acquiredPCMDataLength);
|
|
|
|
TRACE(0, "Start encoded data caching again.");
|
|
stream_a2dp_data_management.isCachingDone = false;
|
|
}
|
|
}
|
|
} else {
|
|
memset(buf, 0, len);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static uint32_t stream_a2dp_speaker_more_pcm_data(uint8_t *buf, uint32_t len) {
|
|
#ifdef BT_XTAL_SYNC
|
|
#ifndef __TWS__
|
|
bt_xtal_sync(BT_XTAL_SYNC_MODE_MUSIC);
|
|
#endif // #ifndef __TWS__
|
|
#endif // #ifdef BT_XTAL_SYNC
|
|
|
|
#ifdef __TWS__
|
|
tws_audout_pcm_more_data(buf, len);
|
|
#else // __TWS__
|
|
|
|
#if defined(__AUDIO_RESAMPLE__) && defined(SW_PLAYBACK_RESAMPLE)
|
|
app_playback_resample_run(ble_resample, buf, len);
|
|
#else
|
|
a2dp_audio_more_data(buf, len);
|
|
#endif
|
|
|
|
app_ring_merge_more_data(buf, len);
|
|
|
|
#ifdef __AUDIO_OUTPUT_MONO_MODE__
|
|
merge_stereo_to_mono_16bits((int16_t *)buf, (int16_t *)buf, len / 2);
|
|
#endif // #ifdef __AUDIO_OUTPUT_MONO_MODE__
|
|
|
|
#endif // #ifdef __TWS__
|
|
|
|
#if defined(__SW_IIR_EQ_PROCESS__) || defined(__HW_FIR_EQ_PROCESS__) || \
|
|
defined(__AUDIO_DRC__)
|
|
// app_sysfreq_req(APP_SYSFREQ_USER_APP_0, APP_SYSFREQ_104M);
|
|
|
|
audio_process_run(buf, len);
|
|
|
|
// app_sysfreq_req(APP_SYSFREQ_USER_APP_0, APP_SYSFREQ_52M);
|
|
#endif // #if defined(__SW_IIR_EQ_PROCESS__) || defined(__HW_FIR_EQ_PROCESS__)
|
|
// || defined(__AUDIO_DRC__)
|
|
|
|
return len;
|
|
}
|
|
|
|
int encoded_data_received(unsigned char *buf, unsigned int len) {
|
|
int32_t ret, size;
|
|
LOCK_APP_AUDIO_QUEUE();
|
|
|
|
// push the encoded data into the queue
|
|
ret = APP_AUDIO_EnCQueueByForce(&sbc_queue, buf, len);
|
|
size = APP_AUDIO_LengthOfCQueue(&sbc_queue);
|
|
UNLOCK_APP_AUDIO_QUEUE();
|
|
|
|
// the queue is full, overwrite the oldest data
|
|
if (CQ_ERR == ret) {
|
|
TRACE(0, "Encoded data cache is overflow.");
|
|
}
|
|
|
|
if ((!(stream_a2dp_data_management.isCachingDone)) &&
|
|
(size >= stream_a2dp_data_management.initialCachedEncodedDataSize)) {
|
|
// the cached encoded data is ready, time to start decoding and feeding to
|
|
// the codec
|
|
stream_a2dp_data_management.isCachingDone = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Start the A2DP speaker output stream
|
|
*
|
|
*/
|
|
void kick_off_a2dp_speaker_stream(void) {
|
|
// clean up the data management structure
|
|
memset((uint8_t *)&stream_a2dp_data_management, 0,
|
|
sizeof(stream_a2dp_data_management));
|
|
|
|
// prepare the buffer
|
|
app_audio_mempool_init();
|
|
|
|
app_audio_mempool_get_buff(
|
|
&(stream_a2dp_data_management.pcmDataBuf),
|
|
vob_func_config[STREAM_A2DP_SPEAKER].pcmDataBufSize);
|
|
app_audio_mempool_get_buff(
|
|
&(stream_a2dp_data_management.encodedDataBuf),
|
|
vob_func_config[STREAM_A2DP_SPEAKER].encodedDataBufSize);
|
|
|
|
// acuqire the sufficient system clock
|
|
app_sysfreq_req(APP_SYSFREQ_VOICE_OVER_BLE, APP_SYSFREQ_52M);
|
|
|
|
// create the audio flinger stream
|
|
struct AF_STREAM_CONFIG_T stream_cfg;
|
|
|
|
stream_cfg.bits = stream_a2dp_data_management.pcmBitNumber;
|
|
stream_cfg.channel_num = AUD_CHANNEL_NUM_2;
|
|
stream_cfg.sample_rate = stream_a2dp_data_management.pcmSampleRate;
|
|
stream_cfg.vol = VOB_VOICE_VOLUME;
|
|
|
|
stream_cfg.device = AUD_STREAM_USE_OPTIMIZED_STREAM;
|
|
|
|
stream_cfg.io_path = AUD_OUTPUT_PATH_SPEAKER;
|
|
stream_cfg.handler = vob_func_config[STREAM_A2DP_SPEAKER].dataCallback;
|
|
|
|
stream_cfg.data_ptr =
|
|
BT_AUDIO_CACHE_2_UNCACHE(vob_data_management.pcmDataBufSize);
|
|
|
|
stream_cfg.data_size = STREAM_DATA_PROCESSING_UNIT_IN_BYTES;
|
|
stream_cfg.data_chunk_count = 2;
|
|
|
|
af_stream_open(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK, &stream_cfg);
|
|
af_stream_start(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK);
|
|
}
|
|
|
|
static osThreadId vob_ThreadId;
|
|
|
|
/**< The negotiated MTU size between SRC and DST */
|
|
static uint16_t negotiatedMTUSize;
|
|
|
|
/**
|
|
* @brief API to send data via BLE
|
|
*
|
|
* @param ptrBuf Pointer of the data to send
|
|
* @param length Length of the data in bytes
|
|
*
|
|
* @return ture if the data is successfully sent out.
|
|
*/
|
|
bool ble_send_data(uint8_t *ptrBuf, uint32_t length) {
|
|
app_datapath_server_send_data(ptrBuf, length);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief API to receive data via BLE
|
|
*
|
|
* @param ptrBuf Pointer of the data to send
|
|
* @param length Length of the data in bytes
|
|
*
|
|
* @return ture if the data is successfully sent out.
|
|
*/
|
|
bool ble_receive_data(uint8_t *ptrBuf, uint32_t length) {}
|
|
|
|
/**
|
|
* @brief Stop the MIC voice input stream
|
|
*
|
|
*/
|
|
void vob_stop_mic_input_stream(void) {
|
|
af_stream_stop(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE);
|
|
af_stream_close(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE);
|
|
|
|
// release the acquired system clock
|
|
app_sysfreq_req(APP_SYSFREQ_VOICE_OVER_BLE, APP_SYSFREQ_32K);
|
|
}
|
|
|
|
/**
|
|
* @brief Stop the speaker output stream
|
|
*
|
|
*/
|
|
void vob_stop_speaker_output_stream(void) {
|
|
af_stream_stop(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK);
|
|
af_stream_close(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK);
|
|
|
|
// release the acquired system clock
|
|
app_sysfreq_req(APP_SYSFREQ_VOICE_OVER_BLE, APP_SYSFREQ_32K);
|
|
}
|
|
|
|
/**
|
|
* @brief Stop the voice stream
|
|
*
|
|
*/
|
|
static void vob_stop_voice_stream(void) {
|
|
if (VOB_ROLE_SRC == vob_role) {
|
|
// stop the MIC voice input stream
|
|
vob_stop_mic_input_stream();
|
|
} else {
|
|
// stop the speaker output stream
|
|
vob_stop_speaker_output_stream();
|
|
}
|
|
|
|
VOB_CMD_RSP_T cmd;
|
|
|
|
// send start voice stream command to the DST
|
|
cmd.magicCode = VOB_MAGICCODE_OF_CMD_RSP;
|
|
cmd.cmdRspCode = VOB_CMD_STOP_VOICE_STREAM;
|
|
|
|
bool ret =
|
|
ble_send_data((uint8_t *)&cmd, &(((VOB_CMD_RSP_T *)0)->lengthOfParm))
|
|
ASSERT_SIMPLIFIED(ret);
|
|
|
|
// waiting until it recieves the response from the other side or timeout
|
|
// happens
|
|
osEvent event =
|
|
osSignalWait(VOB_SIGNAL_GET_RESPONSE, VOB_WAITING_RESPONSE_TIMEOUT_IN_MS);
|
|
if (osEventSignal != event.status) {
|
|
TRACE(0, "Time-out happens when waiting the response of stopping voice "
|
|
"stream from DST.");
|
|
}
|
|
|
|
// reset the state anyway
|
|
vob_state = VOB_STATE_CONNECTED;
|
|
}
|
|
|
|
/**
|
|
* @brief Handler when receiving the stop the voice stream command
|
|
*
|
|
*/
|
|
static void vob_receiving_stop_voice_stream_handler(void) {
|
|
if (VOB_ROLE_SRC == vob_role) {
|
|
// stop the MIC voice input stream
|
|
vob_stop_mic_input_stream();
|
|
} else {
|
|
// stop the speaker output stream
|
|
vob_stop_speaker_output_stream();
|
|
}
|
|
|
|
VOB_CMD_RSP_T cmd;
|
|
|
|
// send start voice stream command to the DST
|
|
|
|
cmd.magicCode = VOB_MAGICCODE_OF_CMD_RSP;
|
|
cmd.cmdRspCode = VOB_RSP_READY_TO_STOP_VOICE_STREAM;
|
|
|
|
bool ret =
|
|
ble_send_data((uint8_t *)&cmd, &(((VOB_CMD_RSP_T *)0)->lengthOfParm))
|
|
ASSERT_SIMPLIFIED(ret);
|
|
|
|
// reset the state anyway
|
|
vob_state = VOB_STATE_CONNECTED;
|
|
}
|
|
|
|
/**
|
|
* @brief Start advertising
|
|
*
|
|
*/
|
|
static void vob_start_advertising(void) {
|
|
// start advertising
|
|
appm_start_advertising();
|
|
|
|
vob_state = VOB_STATE_ADVERTISING;
|
|
}
|
|
|
|
/**
|
|
* @brief Process the recevied audio data from BLE, encode them and feed them to
|
|
* the speaker
|
|
*
|
|
* @param ptrBuf Pointer of the PCM data buffer to feed.
|
|
* @param length Length of the asked PCM data.
|
|
*
|
|
* @return uint32_t 0 means no error happens
|
|
*/
|
|
static uint32_t vob_need_more_voice_data(uint8_t *ptrBuf, uint32_t length) {
|
|
if (vob_data_management.isCachingDone) {
|
|
// caching has been done, can decoded the received encoded data to PCM and
|
|
// feed them to the buffer
|
|
uint32_t remainingPCMBytesToFeed = length;
|
|
uint32_t pcmBytesToDecode;
|
|
do {
|
|
pcmBytesToDecode =
|
|
(remainingPCMBytesToFeed >
|
|
(2 * MAXNUMOFSAMPLES * src_mic_stream_env.bitNumber / AUD_BITS_8))
|
|
? (2 * MAXNUMOFSAMPLES * src_mic_stream_env.bitNumber /
|
|
AUD_BITS_8)
|
|
: remainingPCMBytesToFeed;
|
|
|
|
// cvsd's compression ratio is 50%
|
|
if ((VOB_PCM_SIZE_TO_AUDIO_SIZE(pcmBytesToDecode) +
|
|
vob_data_management.encodedDataIndexToFetch) >
|
|
src_mic_stream_env.encodedDataBufSize) {
|
|
pcmBytesToDecode = VOB_AUDIO_SIZE_TO_PCM_SIZE(
|
|
src_mic_stream_env.encodedDataBufSize -
|
|
vob_data_management.encodedDataIndexToFetch);
|
|
}
|
|
|
|
// decode to generate the PCM data
|
|
CvsdToPcm8k(vob_data_management.encodedDataBuf +
|
|
vob_data_management.encodedDataIndexToFetch,
|
|
(short *)ptrBuf, pcmBytesToDecode / sizeof(short));
|
|
|
|
// update the index
|
|
ptrBuf += pcmBytesToDecode;
|
|
remainingPCMBytesToFeed -= pcmBytesToDecode;
|
|
|
|
vob_data_management.encodedDataIndexToFetch +=
|
|
VOB_PCM_SIZE_TO_AUDIO_SIZE(pcmBytesToDecode);
|
|
|
|
if (src_mic_stream_env.encodedDataBufSize ==
|
|
vob_data_management.encodedDataIndexToFetch) {
|
|
vob_data_management.encodedDataIndexToFetch = 0;
|
|
}
|
|
|
|
} while (remainingPCMBytesToFeed > 0);
|
|
|
|
osMutexWait(vob_env_mutex_id, osWaitForever);
|
|
|
|
// update the bytes to decode and feed to codec
|
|
vob_data_management.encodedDataLength -= VOB_PCM_SIZE_TO_AUDIO_SIZE(length);
|
|
|
|
osMutexRelease(vob_env_mutex_id);
|
|
} else {
|
|
// caching is not done yet, just fill all ZERO
|
|
memset(ptrBuf, 0, length);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* @brief Start the speaker output stream
|
|
*
|
|
*/
|
|
void vob_kick_off_speaker_output_stream(void) {
|
|
// prepare the memory buffer
|
|
app_audio_mempool_init();
|
|
// PCM data buffer
|
|
app_audio_mempool_get_buff(&(dst_speaker_stream_env.pcmDataBuf),
|
|
src_mic_stream_env.pcmDataChunkCount *
|
|
dst_speaker_stream_env.pcmDataChunkSize);
|
|
|
|
// encoded data buffer
|
|
app_audio_mempool_get_buff(&(dst_speaker_stream_env.encodedDataBuf),
|
|
dst_speaker_stream_env.encodedDataBufSize);
|
|
|
|
// encoded data queue
|
|
APP_AUDIO_InitCQueue(&(dst_speaker_stream_env.queue),
|
|
dst_speaker_stream_env.encodedDataBufSize,
|
|
dst_speaker_stream_env.encodedDataBuf);
|
|
|
|
// acuqire the sufficient system clock
|
|
app_sysfreq_req(APP_SYSFREQ_VOICE_OVER_BLE, APP_SYSFREQ_26M);
|
|
|
|
// create the audio flinger stream
|
|
struct AF_STREAM_CONFIG_T stream_cfg;
|
|
|
|
stream_cfg.bits = dst_speaker_stream_env.bitNumber;
|
|
stream_cfg.sample_rate = dst_speaker_stream_env.sampleRate;
|
|
stream_cfg.channel_num = dst_speaker_stream_env.channelCount;
|
|
stream_cfg.vol = VOB_VOICE_VOLUME;
|
|
|
|
stream_cfg.device = AUD_STREAM_USE_OPTIMIZED_STREAM;
|
|
|
|
stream_cfg.io_path = AUD_OUTPUT_PATH_SPEAKER;
|
|
stream_cfg.handler = dst_speaker_stream_env.morePcmHandler;
|
|
|
|
stream_cfg.data_ptr =
|
|
BT_AUDIO_CACHE_2_UNCACHE(dst_speaker_stream_env.pcmDataBuf);
|
|
stream_cfg.data_size = dst_speaker_stream_env.pcmDataChunkSize;
|
|
|
|
af_stream_open(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK, &stream_cfg);
|
|
af_stream_start(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK);
|
|
}
|
|
|
|
/**
|
|
* @brief Send the encoded audio data to DST via BLE
|
|
*
|
|
*/
|
|
static void vob_send_encoded_audio_data(void) {
|
|
uint32_t dataBytesToSend = 0;
|
|
uint32_t offsetInEncodedDatabuf = vob_data_management.indexToFetch;
|
|
|
|
osMutexWait(vob_env_mutex_id, osWaitForever);
|
|
|
|
if (vob_data_management.encodedDataLength > 0) {
|
|
if (vob_data_management.encodedDataLength > negotiatedMTUSize) {
|
|
dataBytesToSend = negotiatedMTUSize;
|
|
} else {
|
|
dataBytesToSend = vob_data_management.encodedDataLength;
|
|
}
|
|
|
|
if ((vob_data_management.indexToFetch + dataBytesToSend) >
|
|
src_mic_stream_env.encodedDataBufSize) {
|
|
dataBytesToSend = src_mic_stream_env.encodedDataBufSize -
|
|
vob_data_management.indexToFetch;
|
|
}
|
|
|
|
// update the index
|
|
vob_data_management.encodedDataLength += dataBytesToSend;
|
|
if (src_mic_stream_env.encodedDataBufSize ==
|
|
(vob_data_management.indexToFetch + dataBytesToSend)) {
|
|
vob_data_management.indexToFetch = 0;
|
|
} else {
|
|
vob_data_management.indexToFetch += dataBytesToSend;
|
|
}
|
|
}
|
|
|
|
osMutexRelease(vob_env_mutex_id);
|
|
|
|
if (dataBytesToSend > 0) {
|
|
// send out the data via the BLE
|
|
ble_send_data((vob_data_management.encodedDataBuf + offsetInEncodedDatabuf),
|
|
dataBytesToSend);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Hanlder of the BLE data sent out event
|
|
*
|
|
*/
|
|
void vob_data_sent_out_handler(void) { vob_send_encoded_audio_data(); }
|
|
|
|
/**
|
|
* @brief Hanlder of the response to start VOB voice stream command, called on
|
|
* the SRC side
|
|
*
|
|
*/
|
|
void vob_start_voice_stream_rsp_handler(BLE_CUSTOM_CMD_RET_STATUS_E retStatus,
|
|
uint8_t *ptrParam, uint32_t paramLen) {
|
|
|
|
if (NO_ERROR == retStatus) {
|
|
TRACE(0,"voice stream is successfully started!"
|
|
|
|
// kick off the voice input stream from MIC
|
|
vob_kick_off_mic_input_stream();
|
|
|
|
// update the state
|
|
vob_state = VOB_STATE_VOICE_STREAM;
|
|
} else {
|
|
TRACE(0,"starting voice stream failed!"
|
|
}
|
|
}
|
|
|
|
void vob_audio_data_reiceived_handler(uint8_t *ptrBuf, uint32_t length) {
|
|
// only for DST, receive the encoded audio data
|
|
if (VOB_ROLE_DST == vob_role) {
|
|
// push into the encoded data buffer
|
|
ASSERT((vob_data_management.encodedDataLength + length) <=
|
|
dst_speaker_stream_env.encodedDataBufSize,
|
|
"The left voice over ble buffer space is not suitable for the "
|
|
"coming audio data.");
|
|
|
|
uint32_t bytesToTheEnd = dst_speaker_stream_env.encodedDataBufSize -
|
|
vob_data_management.encodedDataIndexToFill;
|
|
if (length > bytesToTheEnd) {
|
|
memcpy((dst_speaker_stream_env.encodedDataBuf +
|
|
vob_data_management.encodedDataIndexToFill),
|
|
ptrBuf, length);
|
|
} else {
|
|
memcpy((dst_speaker_stream_env.encodedDataBuf +
|
|
vob_data_management.encodedDataIndexToFill),
|
|
ptrBuf, bytesToTheEnd);
|
|
memcpy(dst_speaker_stream_env.encodedDataBuf, ptrBuf + bytesToTheEnd,
|
|
length - bytesToTheEnd);
|
|
}
|
|
|
|
osMutexWait(vob_env_mutex_id, osWaitForever);
|
|
|
|
// update the bytes to transmit via BLE
|
|
vob_data_management.encodedDataLength += length;
|
|
|
|
osMutexRelease(vob_env_mutex_id);
|
|
|
|
if ((!vob_data_management.isCachingDone) &&
|
|
(vob_data_management.encodedDataLength >=
|
|
src_mic_stream_env.cachedEncodedDataSize)) {
|
|
vob_data_management.isCachingDone = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Hanlder of the start VOB voice stream command, called on the DST side
|
|
*
|
|
*/
|
|
void vob_start_voice_stream_cmd_handler(uint32_t funcCode, uint8_t *ptrParam,
|
|
uint32_t paramLen) {
|
|
BLE_CUSTOM_CMD_RET_STATUS_E retStatus = NO_ERROR;
|
|
|
|
if (VOB_STATE_CONNECTED == vob_state) {
|
|
// update the state
|
|
vob_state = VOB_STATE_VOICE_STREAM;
|
|
|
|
// start raw data xfer
|
|
BLE_control_raw_data_xfer(true);
|
|
|
|
// configure raw data handler
|
|
BLE_set_raw_data_xfer_received_callback(vob_audio_data_reiceived_handler);
|
|
|
|
// kick off the speaker stream
|
|
vob_kick_off_speaker_output_stream();
|
|
} else {
|
|
retStatus = HANDLING_FAILED;
|
|
}
|
|
|
|
BLE_send_response_to_command(funcCode, retStatus, NULL,
|
|
TRANSMISSION_VIA_WRITE_CMD);
|
|
}
|
|
|
|
/**
|
|
* @brief Hanlder of the response to stop VOB voice stream command, called on
|
|
* the SRC side
|
|
*
|
|
*/
|
|
void vob_stop_voice_stream_rsp_handler(BLE_CUSTOM_CMD_RET_STATUS_E retStatus,
|
|
uint8_t *ptrParam, uint32_t paramLen) {
|
|
// stop the MIC voice input stream
|
|
vob_stop_mic_input_stream();
|
|
|
|
vob_state = VOB_STATE_CONNECTED;
|
|
}
|
|
|
|
/**
|
|
* @brief Hanlder of the stop VOB voice stream command, called on the DST side
|
|
*
|
|
*/
|
|
void vob_stop_voice_stream_cmd_handler(uint32_t funcCode, uint8_t *ptrParam,
|
|
uint32_t paramLen) {
|
|
BLE_CUSTOM_CMD_RET_STATUS_E retStatus = NO_ERROR;
|
|
|
|
if (VOB_STATE_VOICE_STREAM == vob_state) {
|
|
// update the state
|
|
vob_state = VOB_STATE_CONNECTED;
|
|
|
|
// stop raw data xfer
|
|
BLE_control_raw_data_xfer(false);
|
|
|
|
// stop the speaker output stream
|
|
vob_stop_speaker_output_stream();
|
|
} else {
|
|
retStatus = HANDLING_FAILED;
|
|
}
|
|
|
|
BLE_send_response_to_command(funcCode, retStatus, NULL,
|
|
TRANSMISSION_VIA_WRITE_CMD);
|
|
}
|
|
|
|
static void vob_encoded_data_sent_done(void) {
|
|
if ((VOB_STATE_VOICE_STREAM == vob_state) &&
|
|
(vob_data_management.encodedDataLength > 0)) {
|
|
// inform the ble application thread to continue sending the encoded data if
|
|
// any in the buffer
|
|
osSignalSet(ble_app_tid, BLE_SIGNAL_VOB_DATA_SENT_OUT);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Hanlder of the BLE connected event
|
|
*
|
|
*/
|
|
void vob_connected_evt_handler(void) {
|
|
if (VOB_ROLE_SRC == vob_role) {
|
|
l2cap_update_param(VOB_CONNECTION_INTERVAL_IN_MS,
|
|
VOB_CONNECTION_INTERVAL_IN_MS,
|
|
VOB_CONNECTION_SUPERVISION_TIMEOUT_IN_MS);
|
|
|
|
vob_data_management.encodedDataIndexToFetch = 0;
|
|
vob_data_management.encodedDataIndexToFill = 0;
|
|
vob_data_management.encodedDataLength = 0;
|
|
|
|
// register the BLE tx done callback function
|
|
app_datapath_server_register_tx_done(vob_encoded_data_sent_done);
|
|
|
|
// delete the timer
|
|
osTimerDelete(vob_connecting_supervisor_timer);
|
|
}
|
|
|
|
vob_data_management.isCachingDone = false;
|
|
|
|
// play the connected sound
|
|
app_voice_report(APP_STATUS_INDICATION_CONNECTED, 0);
|
|
|
|
// update the state
|
|
vob_state = VOB_STATE_CONNECTED;
|
|
}
|
|
|
|
/**
|
|
* @brief Hanlder of the BLE disconnected event
|
|
*
|
|
*/
|
|
void vob_disconnected_evt_handler(void) {
|
|
ASSERT_SIMPLIFIED((VOB_STATE_VOICE_STREAM == vob_state) ||
|
|
(VOB_STATE_CONNECTED == vob_state));
|
|
|
|
vob_state = VOB_STATE_DISCONNECTED;
|
|
|
|
// stop raw data xfer
|
|
BLE_control_raw_data_xfer(false);
|
|
|
|
// play the disconnected sound
|
|
app_voice_report(APP_STATUS_INDICATION_DISCONNECTED, 0);
|
|
|
|
// stop the voice stream if it's in progress
|
|
if (VOB_STATE_VOICE_STREAM == vob_state) {
|
|
if (VOB_ROLE_SRC == vob_role) {
|
|
vob_stop_mic_input_stream();
|
|
} else {
|
|
vob_stop_speaker_output_stream();
|
|
}
|
|
}
|
|
|
|
if (VOB_ROLE_SRC == vob_role) {
|
|
// try to re-connect
|
|
vob_start_connecting();
|
|
} else {
|
|
// re-start advertising
|
|
vob_start_advertising();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief BLE application thread handler
|
|
*
|
|
* @param argument Parameter imported during the thread creation.
|
|
*
|
|
*/
|
|
static void ble_app_thread(void const *argument) {
|
|
while (1) {
|
|
osEvent evt;
|
|
// wait any signal
|
|
evt = osSignalWait(0x0, osWaitForever);
|
|
|
|
// get role from signal value
|
|
if (evt.status == osEventSignal) {
|
|
if (evt.value.signals & BLE_SIGNAL_VOB_GET_ENCODED_AUDIO_DATA) {
|
|
vob_send_encoded_audio_data();
|
|
break;
|
|
} else if (evt.value.signals & BLE_SIGNAL_VOB_DATA_SENT_OUT) {
|
|
vob_data_sent_out_handler();
|
|
break;
|
|
} else if (evt.value.signals & BLE_SIGNAL_VOB_RECEIVED_DATA) {
|
|
vob_data_reiceived_handler();
|
|
break;
|
|
} else if (evt.value.signals & BLE_SIGNAL_VOB_CONNECTED) {
|
|
vob_connected_evt_handler();
|
|
break;
|
|
} else if (evt.value.signals & BLE_SIGNAL_VOB_DISCONNECTED) {
|
|
vob_disconnected_evt_handler();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief The role of the voice over BLE
|
|
*
|
|
* @return 0 if successful, -1 if failed
|
|
*
|
|
*/
|
|
int ble_app_init(void) {
|
|
ble_app_tid = osThreadCreate(osThread(ble_app_thread), NULL);
|
|
if (ble_app_tid == NULL) {
|
|
TRACE(0, "Failed to Create ble_app_thread\n");
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Call back function of the connecting supervisor timer
|
|
*
|
|
* @param n Parameter imported during the timer creation osTimerCreate.
|
|
*
|
|
*/
|
|
static void connecting_supervisor_timer_cb(void const *n) {
|
|
if (VOB_STATE_CONNECTING == vob_state) {
|
|
// time-out happens at the time of connecting.
|
|
// it means the slave is not present or cannot be connected, so just stop
|
|
// connecting
|
|
appm_stop_connecting();
|
|
|
|
vob_state = VOB_STATE_STOPPING_CONNECTING;
|
|
}
|
|
|
|
osTimerDelete(vob_connecting_supervisor_timer);
|
|
}
|
|
|
|
/**
|
|
* @brief Start connecting to DST
|
|
* this can happen for two cases:
|
|
* 1. Key press to start connecting
|
|
* 2. Re-connecting when disconnection happened
|
|
*
|
|
*/
|
|
void vob_start_connecting(void) {
|
|
// play the connecting sound
|
|
app_voice_report(APP_STATUS_INDICATION_CONNECTING, 0);
|
|
|
|
// start connecting
|
|
appm_start_connecting(&DST_BLE_BdAddr);
|
|
|
|
// create and start a timer to supervise the connecting procedure
|
|
vob_connecting_supervisor_timer = osTimerCreate(
|
|
osTimer(APP_VOB_CONNECTING_SUPERVISOR_TIMER), osTimerOnce, NULL);
|
|
|
|
osTimerStart(vob_connecting_supervisor_timer, VOB_CONNECTING_TIMEOUT_IN_MS);
|
|
|
|
vob_state = VOB_STATE_CONNECTING;
|
|
}
|
|
|
|
/**
|
|
* @brief Process the collected PCM data from MIC
|
|
*
|
|
* @param ptrBuf Pointer of the PCM data buffer to access.
|
|
* @param length Length of the PCM data in the buffer in bytes.
|
|
*
|
|
* @return uint32_t 0 means no error happens
|
|
*/
|
|
static uint32_t vob_voice_data_come(uint8_t *ptrBuf, uint32_t length) {
|
|
// encode the voice PCM data into cvsd
|
|
uint32_t remainingBytesToEncode = length;
|
|
uint32_t pcmBytesToEncode;
|
|
do {
|
|
pcmBytesToEncode =
|
|
(remainingBytesToEncode >
|
|
(MAXNUMOFSAMPLES * src_mic_stream_env.bitNumber / AUD_BITS_8))
|
|
? (MAXNUMOFSAMPLES * src_mic_stream_env.bitNumber / AUD_BITS_8)
|
|
: remainingBytesToEncode;
|
|
|
|
// cvsd's compression ratio is 50%
|
|
if ((VOB_PCM_SIZE_TO_AUDIO_SIZE(pcmBytesToEncode) +
|
|
vob_data_management.encodedDataIndexToFill) >
|
|
src_mic_stream_env.encodedDataBufSize) {
|
|
pcmBytesToEncode = VOB_AUDIO_SIZE_TO_PCM_SIZE(
|
|
src_mic_stream_env.encodedDataBufSize -
|
|
vob_data_management.encodedDataIndexToFill);
|
|
}
|
|
|
|
// encode the PCM data
|
|
Pcm8kToCvsd((short *)ptrBuf,
|
|
vob_data_management.encodedDataBuf +
|
|
vob_data_management.encodedDataIndexToFill,
|
|
pcmBytesToEncode / sizeof(short));
|
|
|
|
// update the index
|
|
ptrBuf += pcmBytesToEncode;
|
|
remainingBytesToEncode -= pcmBytesToEncode;
|
|
|
|
vob_data_management.encodedDataIndexToFill +=
|
|
VOB_PCM_SIZE_TO_AUDIO_SIZE(pcmBytesToEncode);
|
|
|
|
if (src_mic_stream_env.encodedDataBufSize ==
|
|
vob_data_management.encodedDataIndexToFill) {
|
|
vob_data_management.encodedDataIndexToFill = 0;
|
|
}
|
|
|
|
} while (remainingBytesToEncode > 0);
|
|
|
|
osMutexWait(vob_env_mutex_id, osWaitForever);
|
|
|
|
// update the bytes to transmit via BLE
|
|
vob_data_management.encodedDataLength += VOB_PCM_SIZE_TO_AUDIO_SIZE(length);
|
|
|
|
osMutexRelease(vob_env_mutex_id);
|
|
|
|
if ((!vob_data_management.isCachingDone) &&
|
|
(vob_data_management.encodedDataLength >=
|
|
src_mic_stream_env.cachedEncodedDataSize)) {
|
|
vob_data_management.isCachingDone = true;
|
|
|
|
// inform the ble application thread to send out the encoded data
|
|
osSignalSet(ble_app_tid, BLE_SIGNAL_VOB_GET_ENCODED_AUDIO_DATA)
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Init the MIC and Speaker audio streams
|
|
*
|
|
*/
|
|
static void vob_init_audio_streams(void) {
|
|
src_mic_stream_env.bitNumber = VOB_VOICE_BIT_NUMBER;
|
|
src_mic_stream_env.sampleRate = VOB_VOICE_SAMPLE_RATE;
|
|
src_mic_stream_env.channelCount = AUD_CHANNEL_NUM_1;
|
|
src_mic_stream_env.morePcmHandler = vob_voice_data_come;
|
|
src_mic_stream_env.pcmDataChunkCount = VOB_VOICE_PCM_DATA_CHUNK_COUNT;
|
|
src_mic_stream_env.pcmDataChunkSize = VOB_VOICE_PCM_DATA_CHUNK_SIZE;
|
|
src_mic_stream_env.cachedEncodedDataSize = VOB_ENCODED_DATA_CACHE_SIZE;
|
|
src_mic_stream_env.encodedDataBufSize = VOB_ENCODED_DATA_STORAGE_BUF_SIZE;
|
|
|
|
dst_speaker_stream_env.bitNumber = VOB_VOICE_BIT_NUMBER;
|
|
dst_speaker_stream_env.sampleRate = VOB_VOICE_SAMPLE_RATE;
|
|
dst_speaker_stream_env.channelCount = AUD_CHANNEL_NUM_2;
|
|
dst_speaker_stream_env.morePcmHandler = vob_need_more_voice_data;
|
|
dst_speaker_stream_env.pcmDataChunkCount = VOB_VOICE_PCM_DATA_CHUNK_COUNT;
|
|
dst_speaker_stream_env.pcmDataChunkSize = VOB_VOICE_PCM_DATA_CHUNK_SIZE;
|
|
dst_speaker_stream_env.cachedEncodedDataSize = VOB_ENCODED_DATA_CACHE_SIZE;
|
|
dst_speaker_stream_env.encodedDataBufSize = VOB_ENCODED_DATA_STORAGE_BUF_SIZE;
|
|
}
|
|
|
|
/**
|
|
* @brief Start the voice input stream from the MIC
|
|
*
|
|
*/
|
|
void vob_kick_off_mic_input_stream(void) {
|
|
// prepare the memory buffer
|
|
app_audio_mempool_init();
|
|
// PCM data buffer
|
|
app_audio_mempool_get_buff(&(src_mic_stream_env.pcmDataBuf),
|
|
src_mic_stream_env.pcmDataChunkCount *
|
|
src_mic_stream_env.pcmDataChunkSize);
|
|
|
|
// encoded data buffer
|
|
app_audio_mempool_get_buff(&(src_mic_stream_env.encodedDataBuf),
|
|
src_mic_stream_env.encodedDataBufSize);
|
|
|
|
// encoded data queue
|
|
APP_AUDIO_InitCQueue(&(src_mic_stream_env.queue),
|
|
src_mic_stream_env.encodedDataBufSize,
|
|
src_mic_stream_env.encodedDataBuf);
|
|
|
|
// acuqire the sufficient system clock
|
|
app_sysfreq_req(APP_SYSFREQ_VOICE_OVER_BLE, APP_SYSFREQ_26M);
|
|
|
|
// create the audio flinger stream
|
|
struct AF_STREAM_CONFIG_T stream_cfg;
|
|
|
|
stream_cfg.bits = src_mic_stream_env.bitNumber;
|
|
stream_cfg.sample_rate = src_mic_stream_env.sampleRate;
|
|
stream_cfg.channel_num = src_mic_stream_env.channelCount;
|
|
stream_cfg.vol = VOB_VOICE_VOLUME;
|
|
|
|
stream_cfg.device = AUD_STREAM_USE_OPTIMIZED_STREAM;
|
|
|
|
stream_cfg.io_path = AUD_INPUT_PATH_MAINMIC;
|
|
stream_cfg.handler = src_mic_stream_env.morePcmHandler;
|
|
|
|
stream_cfg.data_ptr = BT_AUDIO_CACHE_2_UNCACHE(src_mic_stream_env.pcmDataBuf);
|
|
stream_cfg.data_size = src_mic_stream_env.pcmDataChunkSize;
|
|
|
|
af_stream_open(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE, &stream_cfg);
|
|
af_stream_start(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE);
|
|
}
|
|
|
|
/**
|
|
* @brief Start the voice stream, this can only be triggered by SRC
|
|
*
|
|
*/
|
|
static void vob_start_voice_stream(void) {
|
|
BLE_send_custom_command(OP_VOB_CMD_START_VOICE_STREAM, NULL, 0);
|
|
}
|
|
|
|
/**
|
|
* @brief Callback function called when the BLE activity(adv/connecting/scanning
|
|
* is stopped)
|
|
*
|
|
*/
|
|
static void vob_ble_activity_stopped(void) {
|
|
if (VOB_STATE_STOPPING_ADV == vob_state) {
|
|
vob_state = VOB_STATE_ADV_STOPPED;
|
|
|
|
// should switch to connecting state
|
|
vob_switch_state_handler();
|
|
} else if (VOB_STATE_STOPPING_CONNECTING == vob_state) {
|
|
// start advertising
|
|
vob_start_advertising();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief The handler of switching VOB state
|
|
*
|
|
*/
|
|
void vob_switch_state_handler(void) {
|
|
switch (vob_state) {
|
|
case VOB_STATE_ADV_STOPPED: {
|
|
if (VOB_ROLE_SRC == vob_role) {
|
|
// start connecting
|
|
vob_start_connecting();
|
|
}
|
|
break;
|
|
}
|
|
case VOB_STATE_CONNECTED: {
|
|
// start voice stream
|
|
vob_start_voice_stream();
|
|
break;
|
|
}
|
|
case VOB_STATE_VOICE_STREAM: {
|
|
// stop voice stream
|
|
vob_stop_voice_stream();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Process the message sent to the VOB application thread
|
|
*
|
|
*/
|
|
static int voice_over_ble_handler(APP_MESSAGE_BODY *msg_body) {
|
|
switch (msg_body->message_id) {
|
|
case VOB_START_AS_SRC:
|
|
// change role
|
|
vob_role = VOB_ROLE_SRC;
|
|
// stop advertising, and start connecting when adv is stopped
|
|
appm_stop_advertising();
|
|
// change state
|
|
vob_state = VOB_STATE_STOPPING_ADV;
|
|
break;
|
|
case VOB_SWITCH_STATE:
|
|
vob_switch_state_handler();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Notify VOB that it should switch to the next state:
|
|
* VOB_IDLE -> VOB_CONNECTED -> VOB_VOICE_STREAM -> VOB_CONNECTED
|
|
*
|
|
* @param ptrParam Pointer of the parameter
|
|
* @param paramLen Length of the parameter in bytes
|
|
*
|
|
*/
|
|
void notify_vob(VOB_MESSAGE_ID_E message, uint8_t *ptrParam,
|
|
uint32_t paramLen) {
|
|
APP_MESSAGE_BLOCK msg;
|
|
|
|
msg.mod_id = APP_MODUAL_VOB;
|
|
|
|
msg.msg_body.message_id = message;
|
|
|
|
/**< reserved for future usage */
|
|
// ASSERT(paramLen <= 8, "The parameter length %d exceeds the maximum
|
|
// supported length 8!", paramLen); memcpy((uint8_t
|
|
// *)&(msg.msg_body.message_Param0), ptrParam, paramLen);
|
|
|
|
app_mailbox_put(&msg);
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize the voice over BLE system
|
|
*
|
|
*/
|
|
void voice_over_ble_init(void) {
|
|
// create the mutext
|
|
vob_env_mutex_id = osMutexCreate((osMutex(vob_env_mutex)));
|
|
|
|
// initialize the audio streams
|
|
vob_init_audio_streams();
|
|
|
|
// initialize the state machine
|
|
vob_state = VOB_STATE_IDLE;
|
|
|
|
// thread id used for os signal communication between VOB thread and BLE
|
|
// thread
|
|
vob_ThreadId = osThreadGetId();
|
|
|
|
// add the voice over ble handler into the application thread
|
|
app_set_threadhandle(APP_MODUAL_VOB, voice_over_ble_handler);
|
|
|
|
// initialize the cvsd library
|
|
Pcm8k_CvsdInit();
|
|
|
|
// default role is DST
|
|
vob_role = VOB_ROLE_DST;
|
|
|
|
// register the BLE adv and connecting activity stopped callback function
|
|
app_datapath_server_register_activity_stopped_cb(vob_ble_activity_stopped);
|
|
|
|
// start advertising if DST
|
|
vob_start_advertising();
|
|
}
|
|
|
|
#endif // #if __VOICE_OVER_BLE_ENABLED__
|