blob: 93bd35584c3f864878a2ff8c02ff288455867e32 [file] [log] [blame]
/**
*******************************************************************************
*
* @file rc_atvv.c
*
* @brief ATVV application part.
*
* Copyright (C) Atmosic 2021
*
*******************************************************************************
*/
#include "arch.h"
#include "app_config.h"
#include "rc_atvv.h"
#include "rc_gap.h"
#include <string.h>
#include "rc_mmi.h"
#include "atm_debug.h"
#include "co_endian.h"
#include "rc_pdm.h"
#include "sw_event.h"
#ifdef CFG_ATVRC_AUDIO
#include "bridge_audio.h"
static bool atvv_is8k;
#else // CFG_ATVRC_AUDIO
#define RF_BITS 4
#define RF_SIZE (1 << RF_BITS)
#define RF_MASK (RF_SIZE - 1)
#define RF_NEXT(__p) (((__p) + 1) & RF_MASK)
static ble_atvv_payload_t *report_fifo[RF_SIZE];
static uint8_t rf_head, rf_tail;
static uint16_t atvv_seqn;
static bool atvv_is8k;
static struct atvv_eid_context {
sw_event_id_t id;
bool is_first;
} atvv_eid_ctx;
#ifdef CFG_PDM_LOCAL_TEST
bool fake_atvv_bad_link;
static sw_timer_id_t wait_buffer_tid;
#endif // CFG_PDM_LOCAL_TEST
// Supply fifo as fast as possible
__FAST
static bool pdm_supply_fifo(ble_atvv_payload_t *pre_alloc)
{
for (uint8_t rf_next = RF_NEXT(rf_head); rf_next != rf_tail; rf_next =
RF_NEXT(rf_next)) {
ble_atvv_payload_t *req;
#ifdef CFG_PDM_LOCAL_TEST
if (fake_atvv_bad_link) {
break;
}
#endif // CFG_PDM_LOCAL_TEST
if (pre_alloc) {
req = pre_alloc;
pre_alloc = NULL;
} else {
req = ble_atvvs_claim_audio_buf();
}
if (!req) {
break;
}
req->id = 0;
report_fifo[rf_head] = req;
rf_head = rf_next;
}
if (rf_head == rf_tail) {
return false;
} else {
if (rc_pdm_is_paused()) {
#ifdef CFG_PDM_LOCAL_TEST
DEBUG_TRACE("pdm_resume");
#endif
// Enable data processing
rc_pdm_resume();
}
}
return !pre_alloc;
}
// pdm event:
// 1. Supply audio packet fifo
// 2. If pause, checking for possibility of resuming.
__FAST
static void atvv_event(sw_event_id_t event_id, void const *ctx)
{
struct atvv_eid_context *eid_ctx = CONTEXT_VOID_P(ctx);
sw_event_clear(event_id);
pdm_supply_fifo(NULL);
if (eid_ctx->is_first) {
eid_ctx->is_first = false;
ble_atvv_payload_t *req = report_fifo[rf_tail];
req->seqn = co_bswap16(atvv_seqn++);
req->prevp = co_bswap16(rc_pdm_get_adpcm_index(&req->idx));
}
}
static void pdm_reset_fifo(void)
{
if (rf_tail != rf_head) {
report_fifo[rf_tail]->id = 0;
}
}
#ifdef CFG_PDM_LOCAL_TEST
static void wait_buffer_ok(sw_timer_id_t tid, void const *ctx)
{
fake_atvv_bad_link = false;
sw_event_set(atvv_eid_ctx.id);
}
#endif // CFG_PDM_LOCAL_TEST
#endif // CFG_ATVRC_AUDIO
// INTP on: [Thread context]
// rc_pdm_event->rc_atvv_fill_pcm
// INTP off: [IRQ_PRI_UI interrupt context]
// rc_pdm_pcm_2_atvv->rc_atvv_fill_pcm
__FAST
void rc_atvv_fill_pcm(uint8_t pcm)
{
#ifdef CFG_ATVRC_AUDIO
bridge_audio_write_voice_data(pcm);
#else
ASSERT_ERR(rf_tail != rf_head);
ble_atvv_payload_t *req = report_fifo[rf_tail];
req->val[req->id++] = pcm;
if (req->id == BLE_ATVV_REPORT_SIZE) {
rf_tail = RF_NEXT(rf_tail);
if (ble_atvvs_state() != BLE_ATVVS_STREAMING) {
ble_atvvs_free_audio_buf(req);
} else {
ble_atvvs_send_audio_buf(req);
}
sw_event_set(atvv_eid_ctx.id);
if (rf_tail == rf_head) {
#ifdef CFG_PDM_LOCAL_TEST
#define MAX_RECOVER_TIME_CS 1000
sw_timer_set(wait_buffer_tid, rand() % MAX_RECOVER_TIME_CS);
DEBUG_TRACE("pdm paused");
#endif
rc_pdm_pause();
return;
} else {
req = report_fifo[rf_tail];
req->seqn = co_bswap16(atvv_seqn++);
req->prevp = co_bswap16(rc_pdm_get_adpcm_index(&req->idx));
}
}
#endif // CFG_ATVRC_AUDIO
}
#ifdef CFG_ATVV_VER_100
#include "nvds.h"
#define CUST_TAG_ATVV_CONFIGURED 0xC0
#endif
#ifdef CFG_ATVRC_AUDIO
void rc_atvv_ready(uint8_t ready)
{
rc_mmi_transition(ready ? MMI_OP_ATVV_READY : MMI_OP_ATVV_UNREADY);
}
void rc_atvv_mic_open(bool is8k)
{
DEBUG_TRACE("%s 8k: %d", __func__, is8k);
atvv_is8k = is8k;
rc_mmi_transition(MMI_OP_OPEN_MIC);
}
void rc_atvv_mic_close(void)
{
if (!rc_atvv_is_htt_model()) {
rc_pdm_stop();
rc_mmi_transition(MMI_OP_CLOSE_MIC);
}
}
uint16_t rc_atvv_get_pred_val(uint8_t *idx)
{
return rc_pdm_get_adpcm_index(idx);
}
#else // CFG_ATVRC_AUDIO
static bool rc_atvv_query_configured(uint8_t conidx)
{
#ifdef CFG_ATVV_VER_100
// Typically, we should use conidx to get peer's address and compare.
// But current design is only support 1 bond.
bool atvv_configured;
nvds_tag_len_t len = sizeof(bool);
if (nvds_get(CUST_TAG_ATVV_CONFIGURED, &len, (uint8_t *)&atvv_configured) !=
NVDS_OK) {
return false;
}
return atvv_configured;
#else
return false;
#endif
}
static void rc_atvv_ready(uint8_t conidx, uint8_t state)
{
uint8_t is_ready = state == BLE_ATVVS_READY;
rc_mmi_transition(is_ready ? MMI_OP_ATVV_READY : MMI_OP_ATVV_UNREADY);
#ifdef CFG_ATVV_VER_100
if (state == BLE_ATVVS_IDLE) {
return;
}
// Typically, we should use conidx to get peer's address and save.
// But current design is only support 1 bond.
nvds_tag_len_t len = sizeof(bool);
nvds_put(CUST_TAG_ATVV_CONFIGURED, len, &is_ready);
#endif
}
static void rc_atvv_mic_close_ind(uint8_t conidx)
{
rc_pdm_stop(); // Close pdm earlier
rc_mmi_transition(MMI_OP_CLOSE_MIC);
pdm_reset_fifo();
}
static void rc_atvv_mic_open_ind(uint8_t conidx, uint16_t codecs)
{
if (!(codecs & CODEC_ADPCM_8K_16K_16BIT)) {
ble_atvvs_mic_open_error(ATVV_ERROR_CODEC_NOT_SUPPORTED);
} else {
// This state will open mic
atvv_seqn = 0;
atvv_eid_ctx.is_first = true;
sw_event_set(atvv_eid_ctx.id);
atvv_is8k = !(codecs & CODEC_ADPCM_16K_16BIT);
rc_mmi_transition(MMI_OP_OPEN_MIC);
ble_atvvs_audio_start(BLE_ATVV_AUDIO_START_BY_MIC_OPEN, codecs);
}
}
ble_atvvs_param_t const rc_atvvs_parm = {
.cb_atvv_mic_close_ind = rc_atvv_mic_close_ind,
.cb_atvv_mic_open_ind = rc_atvv_mic_open_ind,
.cb_atvv_ready = rc_atvv_ready,
.cb_atvv_query_configured = rc_atvv_query_configured,
};
ble_atvvs_param_t const *rc_atvv_param(void)
{
return &rc_atvvs_parm;
}
#endif // CFG_ATVRC_AUDIO
void rc_atvv_start_search(void)
{
#ifdef CFG_ATVRC_AUDIO
bridge_audio_start_search();
#else
ble_atvvs_start_search();
#endif
}
void rc_atvv_dpad_select(void)
{
#ifdef CFG_ATVRC_AUDIO
bridge_audio_dpad_select();
#else
ble_atvvs_dpad_select();
#endif
}
void rc_atvv_stop_search(void)
{
rc_pdm_stop(); // Close pdm earlier
rc_mmi_transition(MMI_OP_CLOSE_MIC);
#ifdef CFG_ATVRC_AUDIO
bridge_audio_stop();
#else
pdm_reset_fifo();
uint8_t reason = rc_atvv_is_htt_model() ?
BLE_ATVV_AUDIO_STOP_BY_HTT_BUTTON : BLE_ATVV_AUDIO_STOP_BY_OTHER_REASON;
ble_atvvs_audio_stop(reason);
#endif
}
#ifndef CFG_ATVRC_AUDIO
ble_atvvs_state_t rc_atvv_state(void)
{
return ble_atvvs_state();
}
#endif
bool rc_atvv_is8k(void)
{
return atvv_is8k;
}
bool rc_atvv_is_legacy_model(void)
{
#ifdef CFG_ATVRC_AUDIO
return bridge_audio_is_legacy_model();
#else
return ble_atvvs_asst_model() == BLE_ATVV_ASST_MODEL_LEGACY;
#endif
}
bool rc_atvv_is_htt_model(void)
{
#ifdef CFG_ATVRC_AUDIO
return bridge_audio_is_htt_model();
#else
return ble_atvvs_asst_model() == BLE_ATVV_ASST_MODEL_HTT;
#endif
}
void rc_atvv_init(void)
{
#ifndef CFG_ATVRC_AUDIO
atvv_eid_ctx.id = sw_event_alloc(atvv_event, &atvv_eid_ctx);
ble_atvvs_reg_replish_callback(pdm_supply_fifo);
#endif
}
#ifdef CFG_ATVV_VER_100
void rc_atvv_del_config(void)
{
nvds_del(CUST_TAG_ATVV_CONFIGURED);
}
#endif
#ifdef CFG_PDM_LOCAL_TEST
void rc_atvv_test_start(void)
{
fake_atvv_bad_link = false;
atvv_eid_ctx.is_first = true;
sw_event_set(atvv_eid_ctx.id);
}
void rc_atvv_fake_bad_link(void)
{
if (!fake_atvv_bad_link) {
fake_atvv_bad_link = rand() & 1;
}
}
static rep_vec_err_t rc_atvv_test_init(void)
{
wait_buffer_tid = sw_timer_alloc(wait_buffer_ok, NULL);
return RV_NEXT;
}
__attribute__((constructor))
static void rc_atvv_test_init_cstr(void)
{
RV_APPM_INIT_ADD(rc_atvv_test_init);
}
#endif // CFG_PDM_LOCAL_TEST