vos/ambiq-hal-sys/ambiq-sparkfun-sdk/ambiq_ble/profiles/vole/voles_main.c
2022-10-23 23:45:43 -07:00

466 lines
13 KiB
C

//*****************************************************************************
//
// voles_main.c
//! @file
//!
//! @brief This file provides the main application for the VOLE service.
//!
//
//*****************************************************************************
//*****************************************************************************
//
// Copyright (c) 2020, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
//
//*****************************************************************************
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "wsf_buf.h" //for WsfBufAlloc and WsfBufFree
#include "bstream.h"
#include "att_api.h"
#include "svc_ch.h"
#include "svc_amvole.h"
#include "app_api.h"
#include "app_hw.h"
#include "voles_api.h"
#include "am_util_debug.h"
#include "crc32.h"
#include "am_mcu_apollo.h"
#include "am_bsp.h"
#include "am_util.h"
#include "sbc.h"
#include "vole_common.h"
#include "att_defs.h"
#include "vole_board_config.h"
#include "voice_data.h"
#include "ae_api.h"
//*****************************************************************************
//
// Global variables
//
//*****************************************************************************
eVoleCodecType g_vole_codec_type;
uint8_t g_ble_data_buffer[ATT_MAX_MTU];
extern bool_t g_start_voice_send;
//*****************************************************************************
//
// Macro definitions
//
//*****************************************************************************
static sbc_t g_SBCInstance;
int g_opusErr;
uint32_t g_audioSampleRate = 16000;
int32_t g_audioChannel = 1;
/* Control block */
static struct
{
volesConn_t conn[DM_CONN_MAX]; // connection control block
bool_t txReady; // TRUE if ready to send notifications
wsfHandlerId_t appHandlerId;
VolesCfg_t cfg; // configurable parameters
voleCb_t core;
}
volesCb;
extern bool am_app_KWD_AMA_tx_ver_exchange_send(void);
extern uint16_t am_app_KWD_AMA_stream_send(uint8_t* buf, uint32_t len);
extern void am_app_led_off(void);
static void voles_conn_parameter_req(void)
{
hciConnSpec_t connSpec;
connSpec.connIntervalMin = 6; //(15/1.25);//(30/1.25);
connSpec.connIntervalMax = 12; //(15/1.25);//(30/1.25);
connSpec.connLatency = 0; //0;
connSpec.supTimeout = 400;
connSpec.minCeLen = 0;
connSpec.maxCeLen = 0xffff; //fixme
DmConnUpdate(1, &connSpec);
}
//*****************************************************************************
//
// Connection Open event
//
//*****************************************************************************
static void
voles_conn_open(dmEvt_t *pMsg)
{
hciLeConnCmplEvt_t *evt = (hciLeConnCmplEvt_t*) pMsg;
APP_TRACE_INFO0("connection opened\n");
APP_TRACE_INFO1("handle = 0x%x\n", evt->handle);
APP_TRACE_INFO1("role = 0x%x\n", evt->role);
APP_TRACE_INFO3("addrMSB = %02x%02x%02x%02x%02x%02x\n", evt->peerAddr[0], evt->peerAddr[1], evt->peerAddr[2]);
APP_TRACE_INFO3("addrLSB = %02x%02x%02x%02x%02x%02x\n", evt->peerAddr[3], evt->peerAddr[4], evt->peerAddr[5]);
APP_TRACE_INFO1("connInterval = %d x 1.25 ms\n", evt->connInterval);
APP_TRACE_INFO1("connLatency = %d\n", evt->connLatency);
APP_TRACE_INFO1("supTimeout = %d ms\n", evt->supTimeout * 10);
if ( evt->connInterval > 12 )
{
voles_conn_parameter_req();
}
}
//*****************************************************************************
//
// Connection Update event
//
//*****************************************************************************
static void
voles_conn_update(dmEvt_t *pMsg)
{
hciLeConnUpdateCmplEvt_t *evt = (hciLeConnUpdateCmplEvt_t*) pMsg;
APP_TRACE_INFO1("connection update status = 0x%x", evt->status);
APP_TRACE_INFO1("handle = 0x%x", evt->handle);
APP_TRACE_INFO1("connInterval = 0x%x", evt->connInterval);
APP_TRACE_INFO1("connLatency = 0x%x", evt->connLatency);
APP_TRACE_INFO1("supTimeout = 0x%x", evt->supTimeout);
if ( evt->connInterval > (15 / 1.25) )
{
// retry
voles_conn_parameter_req();
}
}
int32_t voles_msbc_encode_voice_data(uint8_t *input, uint8_t *output, uint16_t len)
{
int32_t i32CompressedLen = 0;
if ((input == NULL) || (output == NULL))
{
return 0;
}
sbc_encoder_encode(&g_SBCInstance, input, len,
output, CODEC_MSBC_OUTPUT_SIZE, (int *)&i32CompressedLen);
APP_TRACE_INFO2("voles encode, input len:%d, compressedLen:%d", len, i32CompressedLen);
return i32CompressedLen;
}
int voles_set_codec_type(eVoleCodecType codec_type)
{
APP_TRACE_INFO1("set codec type:%s", ((codec_type == 0) ? "MSBC code" : "OPUS codec"));
if (codec_type != VOLE_CODEC_TYPE_INVALID)
{
g_vole_codec_type = codec_type;
return codec_type;
}
else
{
return VOLE_CODEC_TYPE_INVALID;
}
}
eVoleCodecType voles_get_codec_type(void)
{
return g_vole_codec_type;
}
// get the size for each sending packet
int voles_ble_buffer_size(void)
{
eVoleCodecType codec_type = voles_get_codec_type();
int buffer_size = 0;
switch(codec_type)
{
case MSBC_CODEC_IN_USE:
buffer_size = BLE_MSBC_DATA_BUFFER_SIZE;
break;
case OPUS_CODEC_IN_USE:
buffer_size = BLE_OPUS_DATA_BUFFER_SIZE;
break;
default:
break;
}
return buffer_size;
}
void voles_transmit_voice_data(void)
{
volePacket_t *txPkt = &volesCb.core.txPkt;
uint32_t offset = txPkt->offset;
int remain_data_len = 0;
int enc_data_len = 0;
uint8_t output_data[100] = {0};
int output_len = 0;
static int index = 0;
eVoleCodecType codec_type = voles_get_codec_type();
txPkt->len = sizeof(voice_data);
remain_data_len = txPkt->len - offset;
if (codec_type == MSBC_CODEC_IN_USE)
{
enc_data_len = (remain_data_len>CODEC_MSBC_INPUT_SIZE)?CODEC_MSBC_INPUT_SIZE:remain_data_len;
}
else if (codec_type == OPUS_CODEC_IN_USE)
{
enc_data_len = (remain_data_len>CODEC_OPUS_INPUT_SIZE)?CODEC_OPUS_INPUT_SIZE:remain_data_len;
}
APP_TRACE_INFO3("send %s encode data, total len:%d, offset:%d", (codec_type == MSBC_CODEC_IN_USE) ? "mSBC" : "Opus",
txPkt->len, offset);
if (offset >= txPkt->len)
{
am_app_led_off();
g_start_voice_send = FALSE;
memset(output_data, 0x0, sizeof(output_data));
txPkt->offset = 0;
}
else
{
if (codec_type == MSBC_CODEC_IN_USE)
{
output_len = voles_msbc_encode_voice_data(&voice_data[offset + AUD_HEADER_LEN], output_data, enc_data_len);
txPkt->offset += enc_data_len;
}
else if (codec_type == OPUS_CODEC_IN_USE)
{
output_len = audio_enc_encode_frame((short *)&voice_data[offset + AUD_HEADER_LEN], enc_data_len, output_data);
APP_TRACE_INFO1("opus encode, output_len:%d", output_len);
if (output_len == (CODEC_OPUS_OUTPUT_SIZE))
{
txPkt->offset += (2*CODEC_OPUS_INPUT_SIZE);
//APP_TRACE_INFO1("opus_encode: ret:%d", output_len);
}
else
{
txPkt->offset += enc_data_len;
}
}
}
if ( output_len > 0 )
{
memcpy(&g_ble_data_buffer[index], output_data, output_len);
index += output_len;
}
if ( index >= voles_ble_buffer_size() || output_len <= 0 )
{
if (index >0)
{
am_app_KWD_AMA_stream_send(g_ble_data_buffer, index);
index = 0;
memset(g_ble_data_buffer, 0x0, sizeof(g_ble_data_buffer));
}
}
else
{
uint8_t output_size = ((voles_get_codec_type() == MSBC_CODEC_IN_USE) ? CODEC_MSBC_OUTPUT_SIZE : (CODEC_OPUS_OUTPUT_SIZE));
if (output_len == output_size)
{
voles_transmit_voice_data();
}
else if (output_len>0 && output_len<output_size)
{
am_app_KWD_AMA_stream_send(output_data, output_len);
index = 0;
}
}
}
//extern opus_encoder_t* octopus_encoder_create(int option);
/*************************************************************************************************/
/*!
* \fn volesHandleValueCnf
*
* \brief Handle a received ATT handle value confirm.
*
* \param pMsg Event message.
*
* \return None.
*/
/*************************************************************************************************/
static void
volesHandleValueCnf(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
if (g_start_voice_send)
{
voles_transmit_voice_data();
}
}
else
{
APP_TRACE_WARN2("cnf status = %d, hdl = 0x%x\n", pMsg->hdr.status, pMsg->handle);
}
}
void voles_opus_encoder_init(void)
{
//
// Opus codec init
//
audio_enc_init(0);
APP_TRACE_INFO0("Opus encoder initialization is finished!\r\n\n");
}
//*****************************************************************************
//6
//! @brief initialize vole service
//!
//! @param handlerId - connection handle
//! @param pCfg - configuration parameters
//!
//! @return None
//
//*****************************************************************************
void voles_init(wsfHandlerId_t handlerId, eVoleCodecType codec_type)
{
memset(&volesCb, 0, sizeof(volesCb));
volesCb.appHandlerId = handlerId;
volesCb.core.txPkt.data = voice_data;
if (codec_type == MSBC_CODEC_IN_USE)
{
sbc_encode_init(&g_SBCInstance, 1); //0: SBC
}
else if (codec_type == OPUS_CODEC_IN_USE)
{
voles_opus_encoder_init();
}
}
static void
voles_conn_close(dmEvt_t *pMsg)
{
hciDisconnectCmplEvt_t *evt = (hciDisconnectCmplEvt_t*) pMsg;
dmConnId_t connId = evt->hdr.param;
/* clear connection */
volesCb.conn[connId - 1].connId = DM_CONN_ID_NONE;
volesCb.conn[connId - 1].voleToSend = FALSE;
WsfTimerStop(&volesCb.core.timeoutTimer);
volesCb.core.txState = VOLE_STATE_INIT;
volesCb.core.rxState = VOLE_STATE_RX_IDLE;
volesCb.core.lastRxPktSn = 0;
volesCb.core.txPktSn = 0;
VoleResetPkt(&volesCb.core.rxPkt);
VoleResetPkt(&volesCb.core.txPkt);
VoleResetPkt(&volesCb.core.ackPkt);
}
uint8_t
voles_write_cback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
APP_TRACE_INFO3("write cb, len:%d, value %x %x", len, pValue[0], pValue[1]);
return ATT_SUCCESS;
}
void
voles_start(dmConnId_t connId, uint8_t timerEvt, uint8_t voleCccIdx)
{
//
// set conn id
//
volesCb.conn[connId - 1].connId = connId;
volesCb.conn[connId - 1].voleToSend = TRUE;
volesCb.core.timeoutTimer.msg.event = timerEvt;
volesCb.core.txState = VOLE_STATE_TX_IDLE;
volesCb.txReady = true;
volesCb.core.attMtuSize = AttGetMtu(connId);
APP_TRACE_INFO1("MTU size = %d bytes", volesCb.core.attMtuSize);
}
void
voles_stop(dmConnId_t connId)
{
//
// clear connection
//
volesCb.conn[connId - 1].connId = DM_CONN_ID_NONE;
volesCb.conn[connId - 1].voleToSend = FALSE;
volesCb.core.txState = VOLE_STATE_INIT;
volesCb.txReady = false;
}
void voles_proc_msg(wsfMsgHdr_t *pMsg)
{
if (pMsg->event == DM_CONN_OPEN_IND)
{
voles_conn_open((dmEvt_t *) pMsg);
}
else if (pMsg->event == DM_CONN_CLOSE_IND)
{
voles_conn_close((dmEvt_t *) pMsg);
}
else if (pMsg->event == DM_CONN_UPDATE_IND)
{
voles_conn_update((dmEvt_t *) pMsg);
}
else if (pMsg->event == ATTS_HANDLE_VALUE_CNF)
{
volesHandleValueCnf((attEvt_t *) pMsg);
}
}