MXC-A36_2024.04.18/fr3092_mcu/components/modules/audio/audio_scene.c

649 lines
25 KiB
C
Raw Normal View History

2024-04-17 19:45:26 +08:00
#include <assert.h>
#include "audio_scene.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "co_list.h"
#include "codec.h"
#include "algorithm.h"
#include "dsp_mem.h"
#include "dram_mem.h"
/*
* How to implement a audio scene
* 1. define audio_scene_param_xxx_t, add it into audio_scene_t->param
* 2. define xxx_env_t, add it into audio_scene_env_t->env
* 3. implement audio scene task xxx_task and xxx_init
* 4. add corresponding statement in function audio_scene_create
* 5. implement destroy_xxx
* 6. add corresponding statement in function destroy_task
*/
#define A2DP_SOURCE_SINGLE_FRAME_DUR 10 // unit: ms
#define AUDIO_ALGO_ADC_IN_BUFFER_FRAME_COUNT 8 // unit: algo_frame_size
#define DECODER_DATA_READY_THD 6
audio_scene_env_t audio_scene_env = {0};
static SemaphoreHandle_t wait_for_delete;
static void send_event(audio_scene_evt_t *evt)
{
if (evt == NULL) {
return;
}
GLOBAL_INT_DISABLE();
co_list_push_back(&audio_scene_env.evt_list, &evt->hdr);
GLOBAL_INT_RESTORE();
if(xPortIsInsideInterrupt()) {
vTaskNotifyGiveFromISR(audio_scene_env.audio_task_handle, NULL);
}
else {
xTaskNotifyGive(audio_scene_env.audio_task_handle);
}
}
/* this function may be called from interrupt, just send message to audio_scene_task */
static void decoder_request_raw_data_cb(audio_decoder_t *decoder)
{
audio_scene_evt_t *evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
if (evt) {
evt->type = AUDIO_SCENE_EVT_TYPE_REQ_RAW_DATA;
evt->p.decoder = decoder;
send_event(evt);
}
}
static void hw_request_a2dp_sink_pcm_cb(void *pcm, uint32_t samples, uint8_t channels)
{
uint32_t avaliable_data;
/* request PCM data from audio decoder */
avaliable_data = audio_decoder_get_pcm(audio_scene_env.env.a2dp_sink.decoder_to_hw, pcm, samples, channels);
}
static void hw_request_sco_pcm_cb(void *pcm, uint32_t samples, uint8_t channels)
{
/* request PCM data by speaker, these data are output of Audio algorithm */
audio_decoder_get_pcm(audio_scene_env.env.sco.decoder_to_hw, pcm, samples, channels);
}
static void hw_receive_adc_pcm_cb(uint32_t samples)
{
audio_scene_evt_t *evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
if (evt) {
evt->type = AUDIO_SCENE_EVT_TYPE_ADC_NEW_SAMPLES;
evt->p.adc_new_samples = samples;
send_event(evt);
}
}
static void init_a2dp_sink(void)
{
audio_scene_param_a2dp_sink_t *param;
/* initialize audio decoder module */
param = &audio_scene_env.scene->param.a2dp_sink;
audio_decoder_init(param->channels, param->sample_rate);
/* create and start a decoder */
audio_scene_env.env.a2dp_sink.decoder = audio_decoder_add(param->audio_type, decoder_request_raw_data_cb);
assert(audio_scene_env.env.a2dp_sink.decoder != NULL);
audio_decoder_start(audio_scene_env.env.a2dp_sink.decoder);
/* add an output to initialized audio decoder module */
audio_scene_env.env.a2dp_sink.decoder_to_hw = audio_decoder_output_add(true, AUDIO_CHANNELS_STEREO);
/* initialize audio hardware */
audio_scene_env.env.a2dp_sink.audio_hw = audio_hw_create(param->hw_type,
hw_request_a2dp_sink_pcm_cb,
param->hw_base_addr,
AUDIO_HW_DIR_OUT,
param->sample_rate,
param->channels);
co_list_init(&audio_scene_env.env.a2dp_sink.a2dp_data_list);
audio_scene_env.env.a2dp_sink.a2dp_data_counter = 0;
audio_scene_env.env.a2dp_sink.start_thd = DECODER_DATA_READY_THD;
}
static void init_sco(void)
{
audio_scene_param_sco_t *param;
sco_env_t *env;
env = &audio_scene_env.env.sco;
param = &audio_scene_env.scene->param.sco;
/* create audio algorithm instance */
/*
enum
{
kAgcModeUnchanged,
kAgcModeAdaptiveAnalog,
kAgcModeAdaptiveDigital,
kAgcModeFixedDigital
};
*/
env->audio_algo_handle = algorithm_init(/*AUDIO_ALGO_SEL_AGC | AUDIO_ALGO_SEL_NS | */AUDIO_ALGO_SEL_AEC,
param->sample_rate,
3, 3,
&env->algo_frame_size);
/* request buffers */
env->decoder_output = dsp_mem_alloc(env->algo_frame_size * sizeof(int16_t));
env->decoder_output_wr_ptr = 0;
env->adc_input = dsp_mem_alloc(env->algo_frame_size * sizeof(int16_t) * AUDIO_ALGO_ADC_IN_BUFFER_FRAME_COUNT);
env->adc_input_size = env->algo_frame_size * AUDIO_ALGO_ADC_IN_BUFFER_FRAME_COUNT;
env->adc_input_wr_ptr = 0;
env->adc_input_rd_ptr = 0;
/* initialize audio decoder module */
audio_decoder_init(AUDIO_CHANNELS_MONO, param->sample_rate);
/* create and start a decoder */
env->decoder = audio_decoder_add(param->audio_type, decoder_request_raw_data_cb);
audio_decoder_start(env->decoder);
/* add an output to initialized audio decoder module */
env->decoder_to_algo = audio_decoder_output_add(false, AUDIO_CHANNELS_MONO);
/* add an output to initialized audio decoder module */
env->decoder_to_hw = audio_decoder_output_add(true, AUDIO_CHANNELS_MONO);
/* create encoder */
struct msbc_encoder_param _param;
_param.i_bitrate = 128000;
_param.i_samp_freq = 16000;
if (param->audio_type == AUDIO_TYPE_MSBC) {
env->encoder = audio_encoder_init(param->audio_type, AUDIO_CHANNELS_MONO, param->sample_rate, 57, &_param);
}
else {
env->encoder = audio_encoder_init(param->audio_type, AUDIO_CHANNELS_MONO, param->sample_rate, 256, &_param);
}
/* initialize audio hardware */
env->audio_hw = audio_hw_create(param->hw_type,
hw_request_sco_pcm_cb,
param->hw_base_addr,
AUDIO_HW_DIR_INOUT,
param->sample_rate,
AUDIO_CHANNELS_STEREO);
env->audio_hw_output = audio_hw_output_add(env->audio_hw, hw_receive_adc_pcm_cb);
co_list_init(&env->sco_data_list);
env->sco_data_counter = 0;
env->start_thd = DECODER_DATA_READY_THD;
}
static void destroy_a2dp_sink(void)
{
if (audio_scene_env.scene
&& (audio_scene_env.scene->type == AUDIO_SCENE_TYPE_A2DP_SINK)) {
/* release audio hardware */
audio_hw_destroy(audio_scene_env.env.a2dp_sink.audio_hw);
/* release audio decoder module */
audio_decoder_destroy();
audio_data_element_t *elt = (void *)co_list_pop_front(&audio_scene_env.env.a2dp_sink.a2dp_data_list);
while (elt) {
dram_free(elt);
elt = (void *)co_list_pop_front(&audio_scene_env.env.a2dp_sink.a2dp_data_list);
}
}
}
static void destroy_sco(void)
{
if (audio_scene_env.scene
&& (audio_scene_env.scene->type == AUDIO_SCENE_TYPE_SCO)) {
sco_env_t *env = &audio_scene_env.env.sco;
/* release audio hardware */
audio_hw_destroy(env->audio_hw);
/* release audio encoder */
audio_encoder_destroy(env->encoder);
/* release audio decoder module */
audio_decoder_destroy();
/* release audio algorithm instance */
algorithm_destroy(env->audio_algo_handle);
/* free allocated buffer */
dsp_mem_free(env->decoder_output);
dsp_mem_free(env->adc_input);
audio_data_element_t *elt = (void *)co_list_pop_front(&audio_scene_env.env.sco.sco_data_list);
while (elt) {
dram_free(elt);
elt = (void *)co_list_pop_front(&audio_scene_env.env.sco.sco_data_list);
}
}
}
static void a2dp_sink_evt_handler(audio_scene_evt_t *evt)
{
audio_scene_t *scene = audio_scene_env.scene;
a2dp_sink_env_t *a2dp_sink_env = &audio_scene_env.env.a2dp_sink;
switch (evt->type) {
case AUDIO_SCENE_EVT_TYPE_REQ_RAW_DATA:
{
uint32_t length = 0;
if (evt->p.decoder == a2dp_sink_env->decoder) {
if (audio_decoder_decode(a2dp_sink_env->decoder,
NULL, &length) != AUDIO_RET_OUTPUT_ALMOTE_FULL) {
if (a2dp_sink_env->start_thd == 0) {
audio_data_element_t *elt;
int ret;
elt = (void *)co_list_pick(&a2dp_sink_env->a2dp_data_list);
while (elt) {
uint32_t length = elt->length - elt->offset;
ret = audio_decoder_decode(a2dp_sink_env->decoder, &elt->buffer[elt->offset], &length);
elt->offset += length;
if (elt->length == elt->offset) {
elt = (void *)co_list_pop_front(&a2dp_sink_env->a2dp_data_list);
a2dp_sink_env->a2dp_data_counter--;
dram_free(elt);
elt = (void *)co_list_pick(&a2dp_sink_env->a2dp_data_list);
}
if (ret == AUDIO_RET_OUTPUT_ALMOTE_FULL) {
break;
}
}
}
}
}
else if (evt->p.decoder == audio_scene_env.tone.decoder) {
// fputc('T', NULL);
if (audio_decoder_decode(audio_scene_env.tone.decoder,
NULL, &length) != AUDIO_RET_OUTPUT_ALMOTE_FULL) {
/* send request to app layer after checking PCM list */
void (*req_dec_cb)(audio_decoder_t *) = audio_scene_env.tone.req_dec_cb;
if (req_dec_cb) {
req_dec_cb(audio_scene_env.tone.decoder);
}
}
}
}
break;
case AUDIO_SCENE_EVT_TYPE_RECV_RAW_DATA:
co_list_push_back(&audio_scene_env.env.a2dp_sink.a2dp_data_list, &evt->p.raw_data->hdr);
a2dp_sink_env->a2dp_data_counter++;
if (a2dp_sink_env->start_thd) {
a2dp_sink_env->start_thd--;
}
break;
case AUDIO_SCENE_EVT_TYPE_TONE_ADD:
audio_scene_env.tone.decoder = audio_decoder_add(evt->p.tone_add.type, decoder_request_raw_data_cb);
if (audio_scene_env.tone.decoder) {
audio_scene_env.tone.req_dec_cb = evt->p.tone_add.req_dec_cb;
audio_decoder_start(audio_scene_env.tone.decoder);
}
break;
case AUDIO_SCENE_EVT_TYPE_TONE_REMOVE:
if (audio_scene_env.tone.decoder == evt->p.decoder) {
audio_decoder_remove(audio_scene_env.tone.decoder);
audio_scene_env.tone.decoder = NULL;
}
break;
default:
while(1);
break;
}
}
static void sco_evt_handler(audio_scene_evt_t *evt)
{
audio_scene_t *scene = audio_scene_env.scene;
sco_env_t *sco_env = &audio_scene_env.env.sco;
switch (evt->type) {
case AUDIO_SCENE_EVT_TYPE_REQ_RAW_DATA:
{
uint32_t length = 0;
if (evt->p.decoder == sco_env->decoder) {
if (audio_decoder_decode(sco_env->decoder,
NULL, &length) != AUDIO_RET_OUTPUT_ALMOTE_FULL) {
if (sco_env->start_thd == 0) {
audio_data_element_t *elt;
int ret;
elt = (void *)co_list_pick(&sco_env->sco_data_list);
while (elt) {
uint32_t length = elt->length - elt->offset;
if (elt->valid) {
uint32_t length = elt->length - elt->offset;
ret = audio_decoder_decode(sco_env->decoder, &elt->buffer[elt->offset], &length);
elt->offset += length;
}
else {
uint32_t length = AUDIO_SPECIAL_LENGTH_FOR_PLC;
ret = audio_decoder_decode(sco_env->decoder, NULL, &length);
elt->offset += length;
}
if (elt->length == elt->offset) {
elt = (void *)co_list_pop_front(&sco_env->sco_data_list);
sco_env->sco_data_counter--;
dram_free(elt);
elt = (void *)co_list_pick(&sco_env->sco_data_list);
}
if (ret == AUDIO_RET_OUTPUT_ALMOTE_FULL) {
break;
}
}
}
}
}
else if (evt->p.decoder == audio_scene_env.tone.decoder) {
if (audio_decoder_decode(audio_scene_env.tone.decoder,
NULL, &length) != AUDIO_RET_OUTPUT_ALMOTE_FULL) {
/* send request to app layer after checking PCM list */
void (*req_dec_cb)(audio_decoder_t *) = audio_scene_env.tone.req_dec_cb;
if (req_dec_cb) {
req_dec_cb(audio_scene_env.tone.decoder);
}
}
}
}
break;
case AUDIO_SCENE_EVT_TYPE_RECV_RAW_DATA:
co_list_push_back(&audio_scene_env.env.sco.sco_data_list, &evt->p.raw_data->hdr);
sco_env->sco_data_counter++;
if (sco_env->start_thd) {
sco_env->start_thd--;
}
break;
case AUDIO_SCENE_EVT_TYPE_TONE_ADD:
audio_scene_env.tone.decoder = audio_decoder_add(evt->p.tone_add.type, decoder_request_raw_data_cb);
if (audio_scene_env.tone.decoder) {
audio_scene_env.tone.req_dec_cb = evt->p.tone_add.req_dec_cb;
audio_decoder_start(audio_scene_env.tone.decoder);
}
break;
case AUDIO_SCENE_EVT_TYPE_TONE_REMOVE:
if (audio_scene_env.tone.decoder == evt->p.decoder) {
audio_decoder_remove(audio_scene_env.tone.decoder);
audio_scene_env.tone.decoder = NULL;
}
break;
case AUDIO_SCENE_EVT_TYPE_ADC_NEW_SAMPLES:
{
uint32_t sco_in_length;
sco_env_t *sco = &audio_scene_env.env.sco;
uint32_t left_space;
uint32_t adc_samples = evt->p.adc_new_samples;
uint32_t available_samples;
/* save adc data into framebuffer */
left_space = sco->adc_input_size - sco->adc_input_wr_ptr;
if (left_space > adc_samples) {
audio_hw_read_pcm(sco->audio_hw_output, &sco->adc_input[sco->adc_input_wr_ptr], adc_samples, AUDIO_CHANNELS_MONO);
sco->adc_input_wr_ptr += adc_samples;
}
else {
audio_hw_read_pcm(sco->audio_hw_output, &sco->adc_input[sco->adc_input_wr_ptr], left_space, AUDIO_CHANNELS_MONO);
adc_samples -= left_space;
sco->adc_input_wr_ptr = 0;
if (adc_samples) {
audio_hw_read_pcm(sco->audio_hw_output, &sco->adc_input[0], adc_samples, AUDIO_CHANNELS_MONO);
sco->adc_input_wr_ptr = adc_samples;
}
}
/* check whether received ADC data are enough for audio algorithm */
if (sco->adc_input_wr_ptr >= sco->adc_input_rd_ptr) {
available_samples = sco->adc_input_wr_ptr - sco->adc_input_rd_ptr;
}
else {
available_samples = sco->adc_input_size + sco->adc_input_wr_ptr - sco->adc_input_rd_ptr;
}
if (sco->decoder_output_wr_ptr < sco->algo_frame_size) {
/* request decoded PCM data (from ESCO IN), and send data to audio algorithim */
sco_in_length = audio_decoder_get_pcm(sco->decoder_to_algo,
&sco->decoder_output[sco->decoder_output_wr_ptr],
sco->algo_frame_size - sco->decoder_output_wr_ptr,
AUDIO_CHANNELS_MONO);
sco->decoder_output_wr_ptr += sco_in_length;
}
if (available_samples >= sco->algo_frame_size) {
// /* request decoded PCM data (from ESCO IN), and send data to audio algorithim */
// sco_in_length = audio_decoder_get_pcm(sco->decoder_to_algo,
// &sco->decoder_output[sco->decoder_output_wr_ptr],
// sco->algo_frame_size - sco->decoder_output_wr_ptr,
// AUDIO_CHANNELS_MONO);
// sco->decoder_output_wr_ptr += sco_in_length;
if (sco->decoder_output_wr_ptr >= sco->algo_frame_size) {
int16_t *output = NULL;
uint32_t encoded_frame;
uint32_t aec_out_samples = sco->algo_frame_size;
sco->decoder_output_wr_ptr = 0;
/* request start algorithim */
algorithm_launch(sco->audio_algo_handle,
&sco->decoder_output[0],
&sco->adc_input[sco->adc_input_rd_ptr],
&output);
sco->adc_input_rd_ptr += sco->algo_frame_size;
if (sco->adc_input_rd_ptr >= sco->adc_input_size) {
sco->adc_input_rd_ptr = 0;
}
/* use AEC output to do encoder, send encoded SCO frame to remote device */
audio_encoder_encode(sco->encoder,
(const uint8_t *)output,
sizeof(int16_t) * aec_out_samples,
AUDIO_CHANNELS_MONO,
audio_scene_env.scene->param.sco.sample_rate);
/* send encoded frame to peer device */
encoded_frame = audio_encoder_get_frame_count(sco->encoder);
while (encoded_frame--) {
audio_encoder_frame_t *frame;
frame = audio_encoder_frame_pop(sco->encoder);
if (audio_scene_env.scene->param.sco.report_enc_cb) {
audio_scene_env.scene->param.sco.report_enc_cb(audio_scene_env.scene->param.sco.report_enc_arg, frame->data, frame->length);
}
audio_encoder_frame_release(frame);
}
}
}
}
break;
default:
while(1);
break;
}
}
void _audio_scene_create(void)
{
audio_scene_t *audio_scene = audio_scene_env.scene;
if (audio_scene == NULL) {
return;
}
switch (audio_scene->type) {
case AUDIO_SCENE_TYPE_A2DP_SINK:
init_a2dp_sink();
break;
case AUDIO_SCENE_TYPE_SCO:
init_sco();
break;
default:
break;
}
}
static void _audio_scene_destroy(void)
{
audio_scene_t *scene = audio_scene_env.scene;
if (scene == NULL) {
return;
}
switch (scene->type) {
case AUDIO_SCENE_TYPE_A2DP_SINK:
destroy_a2dp_sink();
break;
case AUDIO_SCENE_TYPE_SCO:
destroy_sco();
break;
default:
break;
}
vPortFree(audio_scene_env.scene);
audio_scene_env.scene = NULL;
}
static void audio_scene_task(void *arg)
{
audio_scene_evt_t *evt;
audio_scene_t *scene;
while (1) {
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
GLOBAL_INT_DISABLE();
evt = (void *)co_list_pop_front(&audio_scene_env.evt_list);
GLOBAL_INT_RESTORE();
if (evt) {
switch (evt->type) {
case AUDIO_SCENE_EVT_TYPE_CREATE:
if (audio_scene_env.scene == NULL) {
audio_scene_env.scene = evt->p.scene;
_audio_scene_create();
}
else {
assert(0);
}
break;
case AUDIO_SCENE_EVT_TYPE_DESTROY:
if (audio_scene_env.scene == evt->p.scene) {
_audio_scene_destroy();
}
else {
assert(0);
}
break;
default:
scene = audio_scene_env.scene;
if (scene) {
switch (scene->type) {
case AUDIO_SCENE_TYPE_A2DP_SINK:
a2dp_sink_evt_handler(evt);
break;
case AUDIO_SCENE_TYPE_SCO:
sco_evt_handler(evt);
break;
default:
break;
}
}
else {
if (evt->type == AUDIO_SCENE_EVT_TYPE_RECV_RAW_DATA) {
dram_free(evt->p.raw_data);
}
}
break;
}
vPortFree(evt);
}
}
}
void audio_scene_init(uint32_t stack_size, uint8_t priority)
{
co_list_init(&audio_scene_env.evt_list);
audio_scene_env.scene = NULL;
xTaskCreate(audio_scene_task, "AUDIO_SCENE_TASK", stack_size, NULL, priority, &audio_scene_env.audio_task_handle);
}
audio_scene_t *audio_scene_create(audio_scene_type_t type, void *param)
{
audio_scene_evt_t *evt;
audio_scene_t *audio_scene;
evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
audio_scene = (void *)pvPortMalloc(sizeof(audio_scene_t));
audio_scene->type = type;
switch (type) {
case AUDIO_SCENE_TYPE_A2DP_SINK:
memcpy((void *)&audio_scene->param.a2dp_sink, param, sizeof(audio_scene_param_a2dp_sink_t));
break;
case AUDIO_SCENE_TYPE_SCO:
memcpy((void *)&audio_scene->param.sco, param, sizeof(audio_scene_param_sco_t));
break;
default:
goto __err;
}
evt->type = AUDIO_SCENE_EVT_TYPE_CREATE;
evt->p.scene = audio_scene;
send_event(evt);
return audio_scene;
__err:
vPortFree(audio_scene);
vPortFree(evt);
return NULL;
}
void audio_scene_destroy(audio_scene_t *scene)
{
audio_scene_evt_t *evt;
evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
evt->type = AUDIO_SCENE_EVT_TYPE_DESTROY;
evt->p.scene = scene;
send_event(evt);
}
void audio_scene_recv_raw_data(audio_scene_t *scene, bool valid, uint8_t *buffer, uint32_t length)
{
audio_data_element_t *elt;
audio_scene_evt_t *evt;
evt = (void *)pvPortMalloc(sizeof(audio_scene_evt_t));
evt->type = AUDIO_SCENE_EVT_TYPE_RECV_RAW_DATA;
if (valid) {
elt = (void *)dram_malloc(sizeof(audio_data_element_t) + length);
elt->valid = true;
elt->length = length;
elt->offset = 0;
memcpy((void *)&elt->buffer[0], buffer, length);
}
else {
elt = (void *)dram_malloc(sizeof(audio_data_element_t));
elt->valid = false;
elt->length = AUDIO_SPECIAL_LENGTH_FOR_PLC;
elt->offset = 0;
}
evt->p.raw_data = elt;
send_event(evt);
}
bool audio_scene_dac_is_ready(audio_scene_t *scene)
{
return audio_decoder_is_started();
}