/** * @file minimp3.c * * Copyright (c) 2020 Semidrive Semiconductor. * All rights reserved. * * Description: * * Revision History: * ----------------- */ #include #include #include #include "projdefs.h" #include "portable.h" #ifdef __FATFS_SUPPORTED__ #include "ff.h" #endif /* the follow 3 MACROS must be defined before incude minimp3.h, and the these MACROS can be defined only one time */ #define MINIMP3_IMPLEMENTATION #define MINIMP3_NO_SIMD #define MINIMP3_ALLOW_MONO_STEREO_TRANSITION #define MINIMP3_NO_STDIO #include "mp3.h" #define SAMPLE_PER_FRAME 1152 /* the max samples of per frame */ static inline size_t mp3_read_cb(void *buff, size_t nbyte, void *user_data) { size_t ret = 0; mp3_dec_context *context = (mp3_dec_context *)user_data; #ifdef __FATFS_SUPPORTED__ if (context->src_type == MP3_IO_SOURCE_DISK) { uint8_t *temp_buff = pvPortMallocAligned(nbyte, 32); /* wrap the f_read here, a direct read may failed, because the decoder may read at any address not aligned with 32 */ f_read((FIL *)context->bit_stream, temp_buff, nbyte, &ret); memcpy(buff, temp_buff, ret); vPortFree(temp_buff); } else #endif { if (context->bits_offset + nbyte > context->bit_size) { nbyte = context->bit_size - context->bits_offset; } memcpy(buff, (uint8_t *)context->bit_stream + context->bits_offset, nbyte); context->bits_offset += nbyte; ret = nbyte; } return ret; } static inline int mp3_seek_cb(uint64_t position, void *user_data) { int ret = 0; mp3_dec_context *context = (mp3_dec_context *)user_data; if (position > context->bit_size) { position = context->bit_size; } #ifdef __FATFS_SUPPORTED__ if (context->src_type == MP3_IO_SOURCE_DISK) { ret = f_lseek((FIL *)context->bit_stream, position); } else #endif { context->bits_offset = position; } return ret; } mp3_dec_context *mp3_init_dec(io_source_type type, void *data, uint32_t size) { mp3_dec_context *context = NULL; if (!data) return NULL; context = (mp3_dec_context *)pvPortMalloc(sizeof(mp3_dec_context)); if (!context) return NULL; memset(context, 0x00, sizeof(mp3_dec_context)); context->src_type = type; context->bits_offset = 0; context->repeat_count = 0; if (context->src_type == MP3_IO_SOURCE_C_ARRAY) { // input data is stored in the ram already context->bit_stream = (uint8_t *)data; context->bit_size = size; } #ifdef __FATFS_SUPPORTED__ else if (context->src_type == MP3_IO_SOURCE_DISK_PRELOAD) { size_t count = 0; FIL fp = {0}; FRESULT rc = FR_OK; char file_name[128] = {0}; strcpy(file_name, (char *)data); rc = f_open(&fp, file_name, FA_READ); if (rc != FR_OK) { vPortFree(context); return NULL; } context->bit_size = f_size(&fp); context->bit_stream = (uint8_t *)pvPortMallocAligned(context->bit_size, 32); f_read(&fp, context->bit_stream, context->bit_size, &count); f_close(&fp); if (count != context->bit_size) { vPortFree(context->bit_stream); vPortFree(context); return NULL; } } else if (context->src_type == MP3_IO_SOURCE_DISK) { FRESULT rc = FR_OK; char file_name[128] = {0}; strcpy(file_name, (char *)data); context->bit_stream = pvPortMalloc(sizeof(FIL)); rc = f_open((FIL *)context->bit_stream, file_name, FA_READ); if (rc != FR_OK) { vPortFree(context->bit_stream); vPortFree(context); return NULL; } context->bit_size = f_size((FIL *)context->bit_stream); } #endif return context; } int mp3_open_dec(mp3_dec_context *context) { if (!context) return -1; context->io_context.read = mp3_read_cb; context->io_context.seek = mp3_seek_cb; context->io_context.read_data = context; context->io_context.seek_data = context; mp3dec_ex_open_cb(&context->dec_context, &context->io_context, MP3D_SEEK_TO_SAMPLE); context->duration = (context->dec_context.samples * 1000) / (context->dec_context.info.channels * context->dec_context.info.hz); return 0; } int mp3_close_dec(mp3_dec_context *context) { if (!context) return -1; #ifdef __FATFS_SUPPORTED__ if (context->src_type == MP3_IO_SOURCE_DISK) { f_close((FIL *)context->bit_stream); vPortFree(context->bit_stream); } else if (context->src_type == MP3_IO_SOURCE_DISK_PRELOAD) vPortFree(context->bit_stream); #endif mp3dec_ex_close(&context->dec_context); vPortFree(context); return 0; } int mp3_dec_get_frame_info(mp3_dec_context *context, mp3_frame_info *frame_info) { if (!context || !frame_info) return -1; frame_info->channels = context->dec_context.info.channels; frame_info->bitrate = context->dec_context.info.bitrate_kbps; frame_info->samples = frame_info->channels * SAMPLE_PER_FRAME; frame_info->duration = context->duration; frame_info->samplerate = context->dec_context.info.hz; #ifndef MINIMP3_FLOAT_OUTPUT frame_info->format = MP3_SAMPLE_FMT_S16; #else frame_info->format = MP3_SAMPLE_FMT_FLT; #endif return 0; } int mp3_dec_seek_frame(mp3_dec_context *context, uint64_t position) { int ret = 0; uint64_t sample_position = 0; if (!context) return -1; // convert timestamp to sample position sample_position = context->dec_context.samples * position / context->duration; ret = mp3dec_ex_seek(&context->dec_context, sample_position); return ret; } int mp3_dec_set_repeat_count(mp3_dec_context *context, uint32_t count) { if (!context) return -1; context->repeat_count = count; return 0; } static int mp3_dec_rewind(mp3_dec_context *context) { int ret = 0; // check whether the end of file is reached if (!context || (context->dec_context.cur_sample < context->dec_context.samples)) return -1; // if repeat count is not zero, rewind count times if (context->repeat_count && --context->repeat_count > 0) { ret = mp3_dec_seek_frame(context, 0); } return ret; } int mp3_dec_receive_frame(mp3_dec_context *context, mp3d_sample_t *buf, uint32_t samples) { size_t readed = 0; if (!context) return -1; readed = mp3dec_ex_read(&context->dec_context, buf, samples); mp3_dec_rewind(context); return readed; }