Files
test/middleware/minimp3/minimp3.c
2025-11-07 20:19:23 +08:00

269 lines
6.6 KiB
C

/**
* @file minimp3.c
*
* Copyright (c) 2020 Semidrive Semiconductor.
* All rights reserved.
*
* Description:
*
* Revision History:
* -----------------
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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;
}