#include "audio_decoder.h" #include "FreeRTOS.h" #include "task.h" #include "co_list.h" #include "codec.h" #define FIX_44100_TEMP 1 #define AUDIO_DECODER_USER_DRAM 1 #if FIX_44100_TEMP static bool add_sample = false; static uint32_t record_samples = 0; #endif #define AUDIO_DECODER_PCM_MIXED_BUFFER_DUR 80 // ms #define AUDIO_DECODER_REQUEST_DATA_THD 60 // ms #define AUDIO_DECODER_PCM_MIXED_BUFFER_FULL_THD 5 // ms #define AUDIO_DECODER_PCM_RSV_AT_BEGINNING 40 // ms #define AUDIO_DECODER_MIXED_STORE_UPPER (audio_decoder_env.rd_ptr == 0 ? audio_decoder_env.pcm_total_samples - 1 : audio_decoder_env.rd_ptr - 1) #if AUDIO_DECODER_PCM_RSV_AT_BEGINNING >= AUDIO_DECODER_REQUEST_DATA_THD #error("AUDIO_DECODER_PCM_RSV_AT_BEGINNING should be smaller than AUDIO_DECODER_REQUEST_DATA_THD\r\n") #endif #if AUDIO_DECODER_USER_DRAM __attribute__((section("dram_section"))) static uint8_t decoder_pcm[16*1024]; #endif enum { AUDIO_DECODER_STATE_IDLE, AUDIO_DECODER_STATE_DECODING, } audio_decoder_state_t; typedef struct { uint8_t used; uint8_t decoder_started; uint8_t decoder_count; uint8_t out_ch_num; uint32_t out_sample_rate; struct co_list decoder_list; struct co_list output_list; /* current write pointer in Mixed pcm buffer, unit is sample */ uint32_t wr_ptr; /* current read pointer from Mixed pcm buffer, unit is sample */ uint32_t rd_ptr; /* used to store data after mixed */ int16_t *pcm; uint32_t pcm_total_samples; uint32_t request_data_thd; uint32_t mixed_buffer_almost_full_thd; } audio_decoder_env_t; const static int16_t ratio_table[] = {0, 0x8000}; static audio_decoder_env_t audio_decoder_env; void print_int16(uint16_t value) { const static char *hex2char = "0123456789abcdef"; fputc(hex2char[(value >> 12)&0xf], &__stdout); fputc(hex2char[(value >> 8)&0xf], &__stdout); fputc(hex2char[(value >> 4)&0xf], &__stdout); fputc(hex2char[(value >> 0)&0xf], &__stdout); } /* update write pointer after decoding a new frame or andy decoeder is removed */ static bool update_wr_ptr(void) { uint32_t min_distance = 0xffffffff; audio_decoder_t *tmp; bool ret; /* update wr_ptr according to minimum distance */ tmp = (void *)co_list_pick(&audio_decoder_env.decoder_list); while (tmp) { uint32_t distance; uint32_t wr_ptr = tmp->wr_ptr; if (wr_ptr == audio_decoder_env.wr_ptr) { min_distance = 0; break; } else if (wr_ptr > audio_decoder_env.wr_ptr) { distance = wr_ptr - audio_decoder_env.wr_ptr; } else { distance = audio_decoder_env.pcm_total_samples + wr_ptr - audio_decoder_env.wr_ptr; } min_distance = min_distance < distance ? min_distance : distance; tmp = (void *)tmp->hdr.next; } if (min_distance != 0) { audio_decoder_env.wr_ptr += min_distance; if (audio_decoder_env.wr_ptr >= audio_decoder_env.pcm_total_samples) { audio_decoder_env.wr_ptr -= audio_decoder_env.pcm_total_samples; } ret = true; } else { ret = false; } // printf("update_wr_ptr: "); // print_int16(audio_decoder_env.wr_ptr); //// fputc(' ', NULL); //// tmp = (void *)co_list_pick(&audio_decoder_env.decoder_list); //// while (tmp) { //// print_int16(tmp->wr_ptr); //// fputc(' ', NULL); //// tmp = (void *)tmp->hdr.next; //// } //// print_int16(audio_decoder_env.rd_ptr); // printf("\r\n"); return ret; } /* update read pointer after PCM is fetched by an output or andy output is removed */ static bool update_rd_ptr(void) { audio_decoder_output_t *tmp; uint32_t min_distance = 0xffffffff; bool ret; uint32_t curr_rd_ptr; GLOBAL_INT_DISABLE(); curr_rd_ptr = audio_decoder_env.rd_ptr; /* update rd_ptr according to minimum distance */ tmp = (void *)co_list_pick(&audio_decoder_env.output_list); while (tmp) { uint32_t distance, rd_ptr; rd_ptr = tmp->rd_ptr; if (rd_ptr == curr_rd_ptr) { min_distance = 0; break; } else if (rd_ptr > curr_rd_ptr) { distance = rd_ptr - curr_rd_ptr; } else { distance = audio_decoder_env.pcm_total_samples + rd_ptr - curr_rd_ptr; } min_distance = min_distance < distance ? min_distance : distance; tmp = (void *)tmp->hdr.next; } if (min_distance == 0) { ret = false; } else { audio_decoder_env.rd_ptr = curr_rd_ptr + min_distance; if (audio_decoder_env.rd_ptr >= audio_decoder_env.pcm_total_samples) { audio_decoder_env.rd_ptr -= audio_decoder_env.pcm_total_samples; } ret = true; } GLOBAL_INT_RESTORE(); // audio_decoder_output_t *_tmp; // printf("rd: "); // print_int16(audio_decoder_env.rd_ptr); //// _tmp = (void *)co_list_pick(&audio_decoder_env.output_list); //// while (_tmp) { //// print_int16(_tmp->rd_ptr); //// _tmp = (void *)_tmp->hdr.next; //// } // printf("\r\n"); return ret; } static audio_ret_t pcm_buffer_status(uint32_t wr_ptr) { uint32_t available_space; uint32_t wr_limit = AUDIO_DECODER_MIXED_STORE_UPPER; if (wr_ptr >= wr_limit) { available_space = wr_limit + audio_decoder_env.pcm_total_samples - wr_ptr; } else { available_space = wr_limit - wr_ptr; } if (available_space >= audio_decoder_env.request_data_thd) { return AUDIO_RET_NEED_MORE; } else if (available_space < audio_decoder_env.mixed_buffer_almost_full_thd) { return AUDIO_RET_OUTPUT_ALMOTE_FULL; } else { return AUDIO_RET_OK; } } /* Request decoder directly. this function will be called in audio_decoder_get_pcm when decoder is not started */ static void request_raw_data(void) { audio_decoder_t *tmp; audio_ret_t ret; GLOBAL_INT_DISABLE(); tmp = (void *)co_list_pick(&audio_decoder_env.decoder_list); while (tmp) { if (tmp->state == AUDIO_DECODER_STATE_IDLE) { if (tmp->req_dec_cb) { tmp->req_dec_cb(tmp); } } tmp = (void *)tmp->hdr.next; } GLOBAL_INT_RESTORE(); } /* After rd_ptr is updated, new space is available for decoder */ static void check_request_raw_data(void) { audio_decoder_t *tmp; audio_ret_t ret; GLOBAL_INT_DISABLE(); tmp = (void *)co_list_pick(&audio_decoder_env.decoder_list); while (tmp) { if (tmp->state == AUDIO_DECODER_STATE_IDLE) { ret = pcm_buffer_status(tmp->wr_ptr); if (ret == AUDIO_RET_NEED_MORE) { if (tmp->req_dec_cb) { tmp->req_dec_cb(tmp); } } } tmp = (void *)tmp->hdr.next; } GLOBAL_INT_RESTORE(); } static void *add_to_mixed_buffer(int16_t *mixed_pcm_ptr, int16_t *src, uint32_t samples) { if (audio_decoder_env.out_ch_num == 1) { for (uint32_t i=0; i= add_start_index) { add_samples = copy_start_index - add_start_index; } else { add_samples = copy_start_index + audio_decoder_env.pcm_total_samples - add_start_index; } if (end_index == 0) { end_index = audio_decoder_env.pcm_total_samples - 1; } else { end_index--; } if (samples > add_samples) { if (end_index >= copy_start_index) { copy_samples = end_index - copy_start_index; } else { copy_samples = end_index + audio_decoder_env.pcm_total_samples - copy_start_index; } if (copy_samples > (samples - add_samples)) { copy_samples = samples - add_samples; } } else { add_samples = samples; } dealed_samples = copy_samples + add_samples; dealing_index = add_start_index; mixed_pcm_ptr = &audio_decoder_env.pcm[add_start_index * audio_decoder_env.out_ch_num]; if (add_samples) { uint32_t last_samples = audio_decoder_env.pcm_total_samples - dealing_index; if (last_samples <= add_samples) { pcm = add_to_mixed_buffer(mixed_pcm_ptr, pcm, last_samples); add_samples -= last_samples; dealing_index = 0; mixed_pcm_ptr = &audio_decoder_env.pcm[0]; } if (add_samples) { pcm = add_to_mixed_buffer(mixed_pcm_ptr, pcm, add_samples); } } dealing_index = copy_start_index; mixed_pcm_ptr = &audio_decoder_env.pcm[copy_start_index * audio_decoder_env.out_ch_num]; if (copy_samples) { uint32_t last_samples = audio_decoder_env.pcm_total_samples - dealing_index; if (last_samples <= copy_samples) { pcm = copy_to_mixed_buffer(mixed_pcm_ptr, pcm, last_samples); copy_samples -= last_samples; dealing_index = 0; mixed_pcm_ptr = &audio_decoder_env.pcm[0]; } if (copy_samples) { pcm = copy_to_mixed_buffer(mixed_pcm_ptr, pcm, copy_samples); } } return dealed_samples; } /* update mixed PCM buffer with PCM data from pcm list of coresponding decoder. */ static void mix_decoded_data(audio_decoder_t *decoder) { uint32_t max_distance; uint32_t min_distance = 0; uint32_t fastest_wr_ptr = 0; audio_decoder_t *tmp; audio_decoder_pcm_data_t *pcm_data; pcm_data = (void *)co_list_pick(&decoder->pcm_list); if (pcm_data == NULL) { return; } // vTaskSuspendAll(); /* search the fastest wr_ptr in decoder list */ max_distance = 0; fastest_wr_ptr = audio_decoder_env.wr_ptr; tmp = (void *)co_list_pick(&audio_decoder_env.decoder_list); while (tmp) { uint32_t distance; if (tmp->wr_ptr >= audio_decoder_env.wr_ptr) { distance = tmp->wr_ptr - audio_decoder_env.wr_ptr; } else { distance = audio_decoder_env.pcm_total_samples + audio_decoder_env.wr_ptr - tmp->wr_ptr; } if (max_distance < distance) { max_distance = distance; fastest_wr_ptr = tmp->wr_ptr; } tmp = (void *)tmp->hdr.next; } /* save incomming decoded data into mixed buffer */ pcm_data = (void *)co_list_pick(&decoder->pcm_list); while (pcm_data) { uint32_t dealed_samples; uint32_t distance; dealed_samples = save_data_to_mixed_buffer(decoder->wr_ptr, fastest_wr_ptr, AUDIO_DECODER_MIXED_STORE_UPPER, &pcm_data->pcm[pcm_data->offset*audio_decoder_env.out_ch_num], pcm_data->samples - pcm_data->offset); pcm_data->offset += dealed_samples; decoder->wr_ptr += dealed_samples; if (decoder->wr_ptr >= audio_decoder_env.pcm_total_samples) { decoder->wr_ptr -= audio_decoder_env.pcm_total_samples; } if (pcm_data->offset > pcm_data->samples) { __disable_irq(); while(1); } if (pcm_data->offset == pcm_data->samples) { co_list_pop_front(&decoder->pcm_list); vPortFree(pcm_data); pcm_data = (void *)co_list_pick(&decoder->pcm_list); /* update max_distance for following processing */ if (decoder->wr_ptr >= audio_decoder_env.wr_ptr) { distance = decoder->wr_ptr - audio_decoder_env.wr_ptr; } else { distance = audio_decoder_env.pcm_total_samples + audio_decoder_env.wr_ptr - decoder->wr_ptr; } if (distance > max_distance) { fastest_wr_ptr = decoder->wr_ptr; } } else { break; } } update_wr_ptr(); // ( void ) xTaskResumeAll(); } /* * save PCM data into pcm list of corresponding decoder, the sample rate * and channels of saved PCM data are the same with settings in * audio_decoder_env */ static void save_decoded_data(audio_decoder_t *decoder, uint8_t *decoder_out_ptr, uint32_t decoder_out_length, uint8_t channels) { audio_decoder_pcm_data_t *pcm_data; int16_t *src, *dst; uint32_t sample_count; int16_t ratio; int16_t value; ratio = ratio_table[audio_decoder_env.decoder_count - 1]; if (channels == audio_decoder_env.out_ch_num) { #if FIX_44100_TEMP if (add_sample) { decoder_out_length += 4; } #endif pcm_data = pvPortMalloc(sizeof(audio_decoder_pcm_data_t) + decoder_out_length); sample_count = decoder_out_length / (channels * sizeof(int16_t)); pcm_data->samples = sample_count; pcm_data->offset = 0; if (audio_decoder_env.decoder_count == 1) { memcpy((void *)&pcm_data->pcm[0], decoder_out_ptr, decoder_out_length); #if FIX_44100_TEMP if (add_sample) { add_sample = false; memcpy((void *)&pcm_data->pcm[(decoder_out_length-4)>>1], (void *)&pcm_data->pcm[(decoder_out_length-8)>>1], 4); } #endif } else { src = (void *)decoder_out_ptr; dst = (void *)&pcm_data->pcm[0]; for (uint32_t i=0; i> 16); *dst++ = value; } } } else { if (channels == 1) { /* source: mono; destination: stereo */ pcm_data = pvPortMalloc(sizeof(audio_decoder_pcm_data_t) + decoder_out_length * 2); sample_count = decoder_out_length / sizeof(int16_t); pcm_data->samples = sample_count; pcm_data->offset = 0; src = (void *)decoder_out_ptr; dst = (void *)&pcm_data->pcm[0]; if (audio_decoder_env.decoder_count == 1) { for (uint32_t i=0; i> 16); *dst++ = value; *dst++ = value; } } } else { /* source: stereo; destination: mono */ pcm_data = pvPortMalloc(sizeof(audio_decoder_pcm_data_t) + decoder_out_length / 2); sample_count = decoder_out_length / sizeof(int16_t) / 2; pcm_data->samples = sample_count; pcm_data->offset = 0; src = (void *)decoder_out_ptr; dst = (void *)&pcm_data->pcm[0]; if (audio_decoder_env.decoder_count == 1) { for (uint32_t i=0; i> 16); *dst++ = value; src += 2; } } } } GLOBAL_INT_DISABLE(); co_list_push_back(&decoder->pcm_list, &pcm_data->hdr); GLOBAL_INT_RESTORE(); } void audio_decoder_start(audio_decoder_t *decoder) { uint32_t max_distance; uint32_t fastest_wr_ptr = 0; audio_decoder_t *tmp; GLOBAL_INT_DISABLE(); /* search the fastest wr_ptr in decoder list */ max_distance = 0; fastest_wr_ptr = audio_decoder_env.wr_ptr; tmp = (void *)co_list_pick(&audio_decoder_env.decoder_list); while (tmp) { uint32_t distance; if (tmp->wr_ptr >= audio_decoder_env.wr_ptr) { distance = tmp->wr_ptr - audio_decoder_env.wr_ptr; } else { distance = audio_decoder_env.pcm_total_samples + audio_decoder_env.wr_ptr - tmp->wr_ptr; } max_distance = max_distance > distance ? max_distance : distance; if (max_distance < distance) { max_distance = distance; fastest_wr_ptr = tmp->wr_ptr; } tmp = (void *)tmp->hdr.next; } decoder->wr_ptr = fastest_wr_ptr; co_list_push_back(&audio_decoder_env.decoder_list, &decoder->hdr); audio_decoder_env.decoder_count++; GLOBAL_INT_RESTORE(); } void audio_decoder_stop(audio_decoder_t *decoder) { bool extracted; GLOBAL_INT_DISABLE(); extracted = co_list_extract(&audio_decoder_env.decoder_list, &decoder->hdr); if (extracted) { audio_decoder_env.decoder_count--; update_wr_ptr(); } GLOBAL_INT_RESTORE(); } audio_decoder_t *audio_decoder_add(audio_type_t type, void (*req_dec_cb)(audio_decoder_t *)) { audio_decoder_t *decoder; if (audio_decoder_env.used == false) { return NULL; } decoder = pvPortMalloc(sizeof(audio_decoder_t)); if (decoder) { switch (type) { case AUDIO_TYPE_SBC: decoder->decoder = codec_decoder_init(CODEC_DECODER_TYPE_SBC, NULL); break; case AUDIO_TYPE_AAC: { struct aac_decoder_param aac_param; aac_param.PcmWidth = 16; decoder->decoder = codec_decoder_init(CODEC_DECODER_TYPE_AAC, (void *)&aac_param); } break; case AUDIO_TYPE_MP3: decoder->decoder = codec_decoder_init(CODEC_DECODER_TYPE_MP3, NULL); break; case AUDIO_TYPE_MSBC: decoder->decoder = codec_decoder_init(CODEC_DECODER_TYPE_MSBC, NULL); break; case AUDIO_TYPE_CVSD: { struct cvsd_decoder_param cvsd_param; cvsd_param.accum_decay = 0.96875; cvsd_param.step_decay = 0.9990234375; decoder->decoder = codec_decoder_init(CODEC_DECODER_TYPE_CVSD, (void *)&cvsd_param); } break; case AUDIO_TYPE_LC3: break; case AUDIO_TYPE_PCM: { struct pcm_decoder_param pcm_param; pcm_param.frame_size = 120; pcm_param.sample_rate = 8000; decoder->decoder = codec_decoder_init(CODEC_DECODER_TYPE_PCM, &pcm_param); } break; default: vPortFree(decoder); return NULL; } decoder->req_dec_cb = req_dec_cb; decoder->current_sample_rate = 0; decoder->resample = NULL; co_list_init(&decoder->pcm_list); decoder->state = AUDIO_DECODER_STATE_IDLE; } return decoder; } void audio_decoder_remove(audio_decoder_t *decoder) { audio_decoder_pcm_data_t *pcm_data; audio_decoder_stop(decoder); do { pcm_data = (void *)co_list_pop_front(&decoder->pcm_list); if (pcm_data) { vPortFree(pcm_data); } } while(pcm_data); if (decoder->resample) { resample_destroy(decoder->resample); } codec_decoder_destroy(decoder->decoder); vPortFree(decoder); } audio_decoder_output_t *audio_decoder_output_add(uint8_t immediate, uint8_t channels) { if ((audio_decoder_env.used == false) || (audio_decoder_env.out_ch_num != channels)) { return NULL; } audio_decoder_output_t *output = pvPortMalloc(sizeof(audio_decoder_output_t)); if (output) { GLOBAL_INT_DISABLE(); output->immediate = immediate; output->rd_ptr = audio_decoder_env.rd_ptr; output->missed_samples = 0; co_list_push_back(&audio_decoder_env.output_list, &output->hdr); GLOBAL_INT_RESTORE(); } return output; } void audio_decoder_output_remove(audio_decoder_output_t *output) { if (output == NULL) { return; } GLOBAL_INT_DISABLE(); if (co_list_extract(&audio_decoder_env.output_list, &output->hdr)) { update_rd_ptr(); vPortFree(output); } GLOBAL_INT_RESTORE(); } int audio_decoder_decode(audio_decoder_t *decoder, const uint8_t *buffer, uint32_t *length) { uint32_t decoder_in_length; uint8_t *decoder_out_ptr; uint32_t decoder_out_length; uint32_t input_length, consumed_length; if (co_list_pick(&decoder->pcm_list)) { audio_ret_t ret; mix_decoded_data(decoder); ret = pcm_buffer_status(decoder->wr_ptr); if (ret == AUDIO_RET_OUTPUT_ALMOTE_FULL) { *length = 0; return ret; } } input_length = *length; consumed_length = 0; while (input_length) { decoder->state = AUDIO_DECODER_STATE_DECODING; decoder_in_length = input_length; decoder_out_ptr = NULL; if (input_length != AUDIO_SPECIAL_LENGTH_FOR_PLC) { codec_decoder_decode(decoder->decoder, buffer, &decoder_in_length, &decoder_out_ptr, &decoder_out_length); } else { codec_decoder_plc(decoder->decoder, &decoder_out_ptr, &decoder_out_length); // decoder_out_length = 240; decoder_in_length = input_length; } if (decoder_out_length) { uint32_t sample_rate; uint8_t channels; // uint8_t *ptr = decoder_out_ptr; // for(uint32_t i=0;idecoder, &sample_rate, &channels); } else { sample_rate = decoder->current_sample_rate; channels = audio_decoder_env.out_ch_num; } #if FIX_44100_TEMP if (sample_rate == 44100) { record_samples += decoder_out_length; if (record_samples >= 2450*4) { add_sample = true; record_samples -= 2450*4; } } #endif if (decoder->current_sample_rate != sample_rate) { decoder->current_sample_rate = sample_rate; if (sample_rate == audio_decoder_env.out_sample_rate) { if (decoder->resample) { resample_destroy(decoder->resample); } decoder->resample = 0; } else { enum resample_type type; if (decoder->resample) { resample_destroy(decoder->resample); } type = resample_get_type(sample_rate, audio_decoder_env.out_sample_rate); if (type != RESAMPLE_TYPE_INVALID) { decoder->resample = resample_init(type, channels); } else { /// TODO decoder->resample = 0; } } } if (decoder->resample) { while (decoder_out_length) { uint32_t dealed_length = decoder_out_length; uint32_t out_length; uint8_t *out_buf = NULL; resample_exec(decoder->resample, (const uint8_t *)&decoder_out_ptr[0], &dealed_length, &out_buf, &out_length); if (out_length) { save_decoded_data(decoder, out_buf, out_length, channels); } decoder_out_length -= dealed_length; decoder_out_ptr += dealed_length; } } else { save_decoded_data(decoder, decoder_out_ptr, decoder_out_length, channels); } audio_decoder_env.decoder_started = true; mix_decoded_data(decoder); } input_length -= decoder_in_length; buffer += decoder_in_length; consumed_length += decoder_in_length; decoder->state = AUDIO_DECODER_STATE_IDLE; } *length = consumed_length; return pcm_buffer_status(decoder->wr_ptr); } static int16_t *copy_pcm(int16_t *dst, int16_t *src, uint32_t samples, uint8_t channels) { if (audio_decoder_env.out_ch_num == AUDIO_CHANNELS_MONO) { if (channels == AUDIO_CHANNELS_MONO) { for (uint32_t i=0; i= output->rd_ptr) { available_samples = current_wr_ptr - output->rd_ptr; } else { available_samples = current_wr_ptr + audio_decoder_env.pcm_total_samples - output->rd_ptr; } if (output->missed_samples) { uint32_t discard_samples; if (output->missed_samples < available_samples) { discard_samples = output->missed_samples; } else { discard_samples = available_samples; } output->rd_ptr += discard_samples; if (output->rd_ptr >= audio_decoder_env.pcm_total_samples) { output->rd_ptr -= audio_decoder_env.pcm_total_samples; } available_samples -= discard_samples; output->missed_samples -= discard_samples; } if (available_samples > samples) { available_samples = samples; fill_zero_samples = 0; valid_samples = samples; } else { if (output->immediate) { fill_zero_samples = samples - available_samples; valid_samples = samples; } else { fill_zero_samples = 0; valid_samples = available_samples; } } mixed_pcm_ptr = &audio_decoder_env.pcm[output->rd_ptr * audio_decoder_env.out_ch_num]; tail_samples = audio_decoder_env.pcm_total_samples - output->rd_ptr; if (available_samples >= tail_samples) { pcm = copy_pcm(pcm, mixed_pcm_ptr, tail_samples, channels); available_samples -= tail_samples; output->rd_ptr = 0; mixed_pcm_ptr = &audio_decoder_env.pcm[0]; } if (available_samples) { pcm = copy_pcm(pcm, mixed_pcm_ptr, available_samples, channels); output->rd_ptr += available_samples; } if (fill_zero_samples) { output->missed_samples += fill_zero_samples; if (channels == AUDIO_CHANNELS_MONO) { for (uint32_t i=0; i