wch-code/ch32v307_mp3_dac/User/audio/audio_mp3.c

272 lines
9.2 KiB
C

#include "audio_mp3.h"
#include "debug.h"
#include "delay.h"
#include "bsp_led.h"
#include "ff.h"
#include "tlsf.h"
#define MINIMP3_ONLY_MP3
#define MINIMP3_IMPLEMENTATION //释放minimp3中的函数
#include "minimp3.h"
/**
* ID3v2 标签头
*/
typedef struct {
char id[3]; //若为有效ID3v2标签 ID应为"ID3"
uint8_t mversion; //主版本号
uint8_t sversion; //子版本号
uint8_t flags; //标签头标志
uint8_t size[4]; //标签信息大小 采样开始位置为ID3v2标签大小+10字节标签头大小
} audio_mp3_id3v2_head_t;
audio_hal_result_t audio_mp3_get_id3v2_size(FIL* file, uint32_t *id3v2_size)
{
if (id3v2_size == NULL) {
LOG_E("id3v2_size is null");
return audio_hal_invalid_argument;
}
FRESULT f_result = f_lseek(file, 0); //定位到文件开头
if (f_result != FR_OK) {
LOG_E("seeking file failed, error code=%d", f_result);
return audio_hal_error_read_file;
}
UINT br;
audio_mp3_id3v2_head_t id3v2_head;
f_result = f_read(file, &id3v2_head, sizeof(id3v2_head), &br); //读取ID3v2标签头
if (f_result != FR_OK) {
LOG_E("error while reading file, error code=%d", f_result);
return audio_hal_error_read_file;
}
if (strncmp("ID3", id3v2_head.id, 3) == 0) { //标签头包含字符"ID3"
*id3v2_size = ((uint32_t)(id3v2_head.size[0] & 0x7F) << 21) | ((uint32_t)(id3v2_head.size[1] & 0x7F) << 14) |
((uint32_t)(id3v2_head.size[2] & 0x7F) << 7) | (id3v2_head.size[3] & 0x7F); //得到ID3v2大小 4个字节是大端模式 只取低7位
} else {
*id3v2_size = 0; //不存在ID3v2
}
return audio_hal_ok;
}
audio_hal_result_t audio_mp3_play(const char *file_name, audio_hal_result_t (*playing_routine)(void))
{
audio_hal_result_t error_code = audio_hal_ok;
FIL *file = NULL;
mp3dec_t *mp3d = NULL;
uint8_t *file_buffer = tlsf_malloc(AUDIO_MP3_FILE_BUFFER_SIZE);
if (file_buffer == NULL) {
LOG_E("error while allocating memory for mp3 file buffer");
error_code = audio_hal_error_insufficient_memory;
goto error;
}
file = tlsf_malloc(sizeof(FIL));
if (file == NULL) {
LOG_E("error while allocating memory for mp3 file");
error_code = audio_hal_error_insufficient_memory;
goto error;
}
FRESULT f_result = f_open(file, file_name, FA_READ);
if (f_result == FR_OK) {
LOG_I("open %s successful", file_name);
} else {
LOG_E("open %s failed, error code=%d", file_name, f_result);
error_code = audio_hal_error_open_file;
goto error;
}
/* 创建解码器 */
mp3d = tlsf_malloc(sizeof(mp3dec_t));
if (mp3d == NULL) {
LOG_E("error while allocating memory for mp3 decoder");
error_code = audio_hal_error_insufficient_memory;
goto error;
}
/* 初始化解码器 */
mp3dec_init(mp3d);
uint32_t id3v2_offset;
audio_mp3_get_id3v2_size(file, &id3v2_offset);
LOG_D("skipping %d bytes of id3v2", id3v2_offset);
f_result = f_lseek(file, id3v2_offset); //跳过ID3v2 Tag
if (f_result != FR_OK) {
LOG_E("seeking file %s failed, error code=%d", file_name, f_result);
error_code = audio_hal_error_read_file;
goto error;
}
/* 填满文件缓冲区 */
UINT br;
int32_t file_buffer_available = 0; //剩余的缓冲区大小
f_result = f_read(file, file_buffer, AUDIO_MP3_FILE_BUFFER_SIZE, &br); //读满文件缓冲区
if (f_result != FR_OK) {
LOG_E("error while reading file, error code=%d", f_result);
error_code = audio_hal_error_read_file;
goto error;
}
file_buffer_available += br;
/* 尝试解码一帧MP3从而获取文件参数 */
mp3d_sample_t *sample_temp = tlsf_malloc(MINIMP3_MAX_SAMPLES_PER_FRAME * sizeof(mp3d_sample_t)); //申请临时缓冲区
if (sample_temp == NULL) {
LOG_E("error while allocating memory for temporary decoder buffer");
error_code = audio_hal_error_insufficient_memory;
goto error;
}
mp3dec_frame_info_t frame_info;
int decoded_samples = mp3dec_decode_frame(mp3d, file_buffer, file_buffer_available, sample_temp, &frame_info);
if (decoded_samples == 0) {
LOG_E("error while decoding mp3 frame");
error_code = audio_hal_error_unsupported_format;
tlsf_free(sample_temp);
goto error;
}
LOG_I("mpeg file layer: %d, bitrate: %dkbps", frame_info.layer, frame_info.bitrate_kbps);
tlsf_free(sample_temp); //释放临时缓冲区 准备正式解码
#if AUDIO_MP3_DECODE_TIME_DEBUG
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_TimeBaseInitTypeDef TimeBase_InitStruct;
TIM_DeInit(TIM4); //首先反初始化定时器
TimeBase_InitStruct.TIM_Period = 65535; //根据采样率计算定时器溢出时间
TimeBase_InitStruct.TIM_Prescaler = SystemCoreClock / 10000 - 1;
TimeBase_InitStruct.TIM_ClockDivision = 0;
TimeBase_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TimeBase_InitStruct.TIM_RepetitionCounter = 0x0000;
TIM_TimeBaseInit(TIM4, &TimeBase_InitStruct);
#endif
/* 初始化Audio HAL */
error_code = audio_hal_start(decoded_samples, sizeof(mp3d_sample_t) * 8, frame_info.hz, frame_info.channels);
if (error_code != audio_hal_ok) {
LOG_E("error while starting audio hal, error code=%d", error_code);
goto error;
}
uint8_t paused = 0;
while(1) {
/* 执行播放中用户事件处理代码 */
if (playing_routine != NULL) {
error_code = playing_routine();
if (error_code == audio_hal_result_pause) {
paused = !paused;
if (paused != 0) {
audio_hal_pause();
LOG_D("play paused");
} else {
audio_hal_resume();
LOG_D("play resumed");
}
} else if (error_code != audio_hal_ok) {
LOG_W("user routine returned %d, exiting", error_code);
break;
}
}
if (paused) {
delay_ms(10);
continue;
}
#if AUDIO_MP3_DECODE_TIME_DEBUG
TIM_SetCounter(TIM4, 0);
TIM_Cmd(TIM4, ENABLE); //使能定时器2
#endif
/* 从文件中补充数据到缓冲区 */
if (file_buffer_available < AUDIO_MP3_FILE_BUFFER_SIZE / 2) { //文件缓冲区剩余不足一半
int32_t file_buffer_consumed = (AUDIO_MP3_FILE_BUFFER_SIZE - file_buffer_available);
f_result = f_read(file, file_buffer + file_buffer_available, file_buffer_consumed, &br); //从文件中读取新的内容
if (f_result != FR_OK) {
LOG_E("error while reading file, error code=%d", f_result);
error_code = audio_hal_error_read_file;
goto error;
}
file_buffer_available += br;
// uint32_t bytes_read = file_buffer_consumed + file_buffer_available - AUDIO_MP3_FILE_BUFFER_SIZE;
// if (bytes_read > 0) {
// LOG_D("reading %d bytes from file", (int)bytes_read);
// }
// bsp_led_toggle(bsp_led_green);
if (file_buffer_available <= 0) { //文件已经读取完毕
#if AUDIO_MP3_DECODE_TIME_DEBUG
printf("\r\n");
#endif
LOG_I("play done");
break; //结束播放
}
}
#if AUDIO_MP3_DECODE_TIME_DEBUG
uint16_t read_file_time = TIM_GetCounter(TIM4);
#endif
/* 等待播放完成并获取空闲缓冲区 */
mp3d_sample_t *sample = audio_hal_get_free_buffer();
#if AUDIO_MP3_DECODE_TIME_DEBUG
uint16_t wait_play_time = TIM_GetCounter(TIM4);
#endif
/* 解码新一帧MP3数据 */
mp3dec_decode_frame(mp3d, file_buffer, file_buffer_available, sample, &frame_info);
#if AUDIO_MP3_DECODE_TIME_DEBUG
uint16_t decode_time = TIM_GetCounter(TIM4);
#endif
audio_hal_dac_postprocess(sample);
#if AUDIO_MP3_DECODE_TIME_DEBUG
uint16_t process_time = TIM_GetCounter(TIM4);
#endif
/* 从缓冲区中删除解码完毕的帧 */
file_buffer_available -= frame_info.frame_bytes; //从剩余缓冲区大小中减去被消耗的缓冲区大小
memmove(file_buffer, file_buffer + frame_info.frame_bytes, file_buffer_available); //删除已消耗的缓冲区内容
#if AUDIO_MP3_DECODE_TIME_DEBUG
uint16_t file_delete_time = TIM_GetCounter(TIM4);
TIM_Cmd(TIM4, DISABLE);
printf("read: %2d.%dms, wait: %2d.%dms, decode: %2d.%dms, process: %2d.%dms, remove: %2d.%dms\r",
read_file_time / 10, read_file_time % 10,
(wait_play_time - read_file_time) / 10, (wait_play_time - read_file_time) % 10,
(decode_time - wait_play_time) / 10, (decode_time - wait_play_time) % 10,
(process_time - decode_time) / 10, (process_time - decode_time) % 10,
(file_delete_time - process_time) / 10, (file_delete_time - process_time) % 10
);
#endif
}
error:
audio_hal_stop();
if (file) {
f_close(file);
tlsf_free(file);
}
if (file_buffer) {
tlsf_free(file_buffer);
}
if (mp3d) {
tlsf_free(mp3d);
}
return error_code;
}