| /** |
| ******************************************************************************* |
| * |
| * @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 |