#include #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(); }