#include "audio_mp3.h" #include "debug.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) { FRESULT f_result = f_lseek(file, 0); //定位到文件开头 if (f_result != FR_OK) { 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) { 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("seek 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; } while(1) { /* 执行播放中用户事件处理代码 */ if (playing_routine != NULL) { error_code = playing_routine(); if (error_code != audio_hal_ok) { LOG_W("user routine returned %d, exiting", error_code); break; } } #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; }