diff --git a/ch32v307_mp3_dac/.cproject b/ch32v307_mp3_dac/.cproject index f8a9f8e..64cb9be 100644 --- a/ch32v307_mp3_dac/.cproject +++ b/ch32v307_mp3_dac/.cproject @@ -126,7 +126,7 @@ - + diff --git a/ch32v307_mp3_dac/User/audio/audio_hal.h b/ch32v307_mp3_dac/User/audio/audio_hal.h index c23e427..76bb259 100644 --- a/ch32v307_mp3_dac/User/audio/audio_hal.h +++ b/ch32v307_mp3_dac/User/audio/audio_hal.h @@ -5,6 +5,7 @@ typedef enum { audio_hal_ok = 0, //配置成功或正常播放完毕 + audio_hal_invalid_argument, //错误的参数 audio_hal_error_open_file, //无法打开文件 audio_hal_error_read_file, //无法读取文件 audio_hal_error_insufficient_memory, //内存不足 @@ -13,6 +14,7 @@ typedef enum { audio_hal_result_next, //切换下一曲 audio_hal_result_prev, //切换上一曲 + audio_hal_result_pause, //暂停播放 audio_hal_result_random, //随机切换音乐 audio_hal_result_repeat, //重新播放当前音乐 } audio_hal_result_t; diff --git a/ch32v307_mp3_dac/User/audio/audio_mp3.c b/ch32v307_mp3_dac/User/audio/audio_mp3.c index 490826b..83143e1 100644 --- a/ch32v307_mp3_dac/User/audio/audio_mp3.c +++ b/ch32v307_mp3_dac/User/audio/audio_mp3.c @@ -1,5 +1,6 @@ #include "audio_mp3.h" #include "debug.h" +#include "delay.h" #include "bsp_led.h" #include "ff.h" #include "tlsf.h" @@ -21,8 +22,14 @@ typedef struct { 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; } @@ -30,6 +37,7 @@ audio_hal_result_t audio_mp3_get_id3v2_size(FIL* file, uint32_t *id3v2_size) 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; } @@ -89,7 +97,7 @@ audio_hal_result_t audio_mp3_play(const char *file_name, audio_hal_result_t (*pl 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); + LOG_E("seeking file %s failed, error code=%d", file_name, f_result); error_code = audio_hal_error_read_file; goto error; } @@ -146,15 +154,31 @@ audio_hal_result_t audio_mp3_play(const char *file_name, audio_hal_result_t (*pl goto error; } + uint8_t paused = 0; while(1) { /* 执行播放中用户事件处理代码 */ if (playing_routine != NULL) { error_code = playing_routine(); - if (error_code != audio_hal_ok) { + 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 diff --git a/ch32v307_mp3_dac/User/audio/audio_mp3.h b/ch32v307_mp3_dac/User/audio/audio_mp3.h index aa07b32..26365d4 100644 --- a/ch32v307_mp3_dac/User/audio/audio_mp3.h +++ b/ch32v307_mp3_dac/User/audio/audio_mp3.h @@ -6,6 +6,6 @@ #define AUDIO_MP3_DECODE_TIME_DEBUG 0 #define AUDIO_MP3_FILE_BUFFER_SIZE 8192 -audio_hal_result_t audio_mp3_play(const char *file_name, audio_hal_result_t (*playing_process)(void)); +audio_hal_result_t audio_mp3_play(const char *file_name, audio_hal_result_t (*playing_routine)(void)); #endif diff --git a/ch32v307_mp3_dac/User/audio/audio_player.c b/ch32v307_mp3_dac/User/audio/audio_player.c index 3650328..7d2d702 100644 --- a/ch32v307_mp3_dac/User/audio/audio_player.c +++ b/ch32v307_mp3_dac/User/audio/audio_player.c @@ -32,7 +32,7 @@ audio_hal_result_t audio_player_playing_routine(void) return audio_hal_result_prev; case bsp_key_pause_short: - return audio_hal_ok; + return audio_hal_result_pause; case bsp_key_next_long: return audio_hal_result_random; @@ -53,8 +53,8 @@ audio_hal_result_t audio_player_play_file(const char *path) if(!strcasecmp(ext_name, "MP3")) { return audio_mp3_play(path, audio_player_playing_routine); - // } else if(!strcasecmp(ext_name, "WAV")) { - // return audio_wav_play(path, audio_player_playing_routine); + } else if(!strcasecmp(ext_name, "WAV")) { + return audio_wav_play(path, audio_player_playing_routine); } LOG_W("unknown file format \"%s\" of %s", ext_name, path); @@ -178,7 +178,7 @@ void audio_player(void) case audio_hal_error_unsupported_format: case audio_hal_error_unsupported_parameter: //ļ֧ - if (switch_direction == 1) { //ϴлһ + if (switch_direction == 1) { //ϴлһ goto next_file; } else { goto prev_file; @@ -189,8 +189,8 @@ void audio_player(void) break; case audio_hal_ok: // - case audio_hal_result_next: next_file: + case audio_hal_result_next: switch_direction = 1; current_file_index ++; if(current_file_index >= audio_file_count) { @@ -198,8 +198,8 @@ next_file: } break; - case audio_hal_result_prev: prev_file: + case audio_hal_result_prev: switch_direction = 0; if(current_file_index != 0) { current_file_index --; diff --git a/ch32v307_mp3_dac/User/audio/audio_player.h b/ch32v307_mp3_dac/User/audio/audio_player.h index 4a1524d..e05bd56 100644 --- a/ch32v307_mp3_dac/User/audio/audio_player.h +++ b/ch32v307_mp3_dac/User/audio/audio_player.h @@ -4,7 +4,7 @@ #include #include "audio_hal.h" #include "audio_mp3.h" -//#include "audio_wav.h" +#include "audio_wav.h" void audio_player(void); diff --git a/ch32v307_mp3_dac/User/audio/audio_wav.c b/ch32v307_mp3_dac/User/audio/audio_wav.c new file mode 100644 index 0000000..69bdd0f --- /dev/null +++ b/ch32v307_mp3_dac/User/audio/audio_wav.c @@ -0,0 +1,233 @@ +#include "audio_wav.h" +#include "debug.h" +#include "delay.h" +#include "bsp_led.h" +#include "ff.h" +#include "tlsf.h" + +typedef struct __attribute__((packed)) { //RIFF块 用于指示文件类型 + uint32_t chunk_id; /* ID:"RIFF" 即0x46464952 */ + uint32_t chunk_size ;/* 集合大小 等于文件总大小-8 */ + uint32_t format;/* 格式:"WAVE" 即0x45564157 */ +} audio_wav_riff_chunk_t; + +typedef struct __attribute__((packed)) { //FMT块 用于记录WAV文件的信息 + uint32_t chunk_id; /* ID:"fmt " 即0x20746D66 */ + uint32_t chunk_size; /* 子集合大小(不包括ID和Size) */ + uint16_t audio_format; /* 音频格式 0X01:线性PCM 0X11:IMA ADPCM */ + uint16_t channel_count; /* 通道数量 1表示单声道 2表示双声道 */ + uint32_t sample_rate; /* 采样率 */ + uint32_t byte_rate; /* 字节速率 */ + uint16_t block_align; /* 块对齐(字节) */ + uint16_t sample_size; /* 单个采样数据大小;4位ADPCM,设置为4 */ +} audio_wav_fmt_chunk_t; + +typedef struct __attribute__((packed)) { //通用的Chunk头定义 用于寻找"data"块 + uint32_t chunk_id; /* ID:"data" 即0x61746164 */ + uint32_t chunk_size; /* 子集合大小(不包括ID和Size) */ +} audio_wav_hdr_chunk_t; + +typedef struct { + uint16_t format; /* 音频格式 0X01表示线性PCM 0X11表示IMA ADPCM */ + uint16_t channel_count; /* 通道数量 1表示单声道 2表示双声道 */ + uint16_t block_align; /* 块对齐(字节) */ + uint32_t bitrate; /* 比特率(位速) */ + uint32_t sample_rate; /* 采样率 */ + uint16_t sample_size; /* 位数 如16bit, 24bit, 32bit */ + uint32_t data_offset; /* 数据帧开始的位置(在文件里面的偏移) */ +} audio_wav_info_t; + +static audio_hal_result_t audio_wav_get_info(FIL* file, audio_wav_info_t* wav_info) +{ + if (wav_info == NULL) { + LOG_E("wav_info is null"); + return audio_hal_invalid_argument; + } + memset(wav_info, 0, sizeof(audio_wav_info_t)); + + uint8_t read_buffer[sizeof(audio_wav_fmt_chunk_t)]; //最大的头类型 + UINT br; + + 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; + } + + f_result = f_read(file, read_buffer, sizeof(audio_wav_riff_chunk_t), &br); + if (f_result != FR_OK) { + LOG_E("error while reading file, error code=%d", f_result); + return audio_hal_error_read_file; + } + + audio_wav_riff_chunk_t *riff_chunk = (audio_wav_riff_chunk_t*)read_buffer; //RIFF块 + + if(riff_chunk->chunk_id != 0x46464952 || riff_chunk->format != 0x45564157) { //如果块ID不是RIFF或格式不是WAVE + LOG_E("RIFF chunk mismatch, file is not valid RIFF file"); + return audio_hal_error_unsupported_format; //不是RIFF文件 + } + + uint32_t fptr = sizeof(audio_wav_riff_chunk_t); //跳过RIFF块 12bytes + + f_result = f_lseek(file, fptr); //定位到fptr处 + if (f_result != FR_OK) { + LOG_E("seeking file failed, error code=%d", f_result); + return audio_hal_error_read_file; + } + + f_result = f_read(file, read_buffer, sizeof(audio_wav_fmt_chunk_t), &br); + if (f_result != FR_OK) { + LOG_E("error while reading file, error code=%d", f_result); + return audio_hal_error_read_file; + } + + audio_wav_fmt_chunk_t *fmt_chunk = (audio_wav_fmt_chunk_t*)read_buffer; //FMT块 + if(fmt_chunk->chunk_id != 0x20746D66) { //判断是否为FMT块 + LOG_E("FMT chunk mismatch, file is not valid WAVE file"); + return audio_hal_error_unsupported_format; //文件格式不是WAVE + } + + wav_info->format = fmt_chunk->audio_format; //音频格式 + wav_info->channel_count = fmt_chunk->channel_count; //通道数 + wav_info->sample_rate = fmt_chunk->sample_rate; //采样率 + wav_info->bitrate = fmt_chunk->byte_rate*8; //位速率是8倍的字节速率 + wav_info->block_align = fmt_chunk->block_align; //块对齐 + wav_info->sample_size = fmt_chunk->sample_size; //位数 16/24/32位 + + fptr += fmt_chunk->chunk_size + 8; //去掉FMTChunk的大小 注意chunk_size不包含ID和Size的大小 + + while(1) { //找到PCM数据的开始 + f_result = f_lseek(file, fptr); + if (f_result != FR_OK) { + LOG_E("seeking file failed, error code=%d", f_result); + return audio_hal_error_read_file; + } + + f_result = f_read(file, read_buffer, sizeof(audio_wav_hdr_chunk_t), &br); + if (f_result != FR_OK) { + LOG_E("error while reading file, error code=%d", f_result); + return audio_hal_error_read_file; + } + + audio_wav_hdr_chunk_t *chunk_hdr = (audio_wav_hdr_chunk_t*)read_buffer; + + if(chunk_hdr->chunk_id == 0x61746164) { //是"data"块 + wav_info->data_offset = fptr + 8; //跳过Chunkt头 + break; + } + + fptr += chunk_hdr->chunk_size + 8; //继续寻找下一个Chunk + + if(fptr > 4096) { //找了4k还没有找到 + LOG_E("cannot find valid data chunk, file is not valid WAVE file"); + return audio_hal_error_unsupported_format; //文件格式不是WAVE + } + } + + return audio_hal_ok; +} + +audio_hal_result_t audio_wav_play(const char* file_name, audio_hal_result_t (*playing_routine)(void)) +{ + audio_hal_result_t error_code = audio_hal_ok; + FIL *file = NULL; + + 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; + } + + audio_wav_info_t wav_info; + error_code = audio_wav_get_info(file, &wav_info); //获取文件信息 + if(error_code != audio_hal_ok) { + LOG_E("error while getting wave file info of %s", file_name); + goto error; + } + + if (wav_info.format == 0x01) { //音频格式不PCM + LOG_I("wave file format: PCM, bitrate: %dkbps", wav_info.bitrate / 1000); + } else { + LOG_E("format of wave file(0x%02X) is not supported", wav_info.format); + error_code = audio_hal_error_unsupported_parameter; + goto error; + } + + /* 初始化Audio HAL */ + error_code = audio_hal_start(AUDIO_WAV_FILE_BUFFER_SIZE / wav_info.channel_count / (wav_info.sample_size / 8), + wav_info.sample_size, wav_info.sample_rate, wav_info.channel_count); + if (error_code != audio_hal_ok) { + LOG_E("error while starting audio hal, error code=%d", error_code); + goto error; + } + + f_result = f_lseek(file, wav_info.data_offset); + if (f_result != FR_OK) { + LOG_E("seeking file failed, error code=%d", f_result); + error_code = audio_hal_error_read_file; + goto error; + } + + UINT br; + 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; + } + + /* 等待播放完成并获取空闲缓冲区 */ + void *sample = audio_hal_get_free_buffer(); + f_result = f_read(file, sample, AUDIO_WAV_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; + } + + audio_hal_dac_postprocess(sample); + + if(br < AUDIO_WAV_FILE_BUFFER_SIZE) { //文件读取到末尾 + LOG_I("play done"); + break; + } + } + +error: + audio_hal_stop(); + + if (file) { + f_close(file); + tlsf_free(file); + } + + return error_code; +} diff --git a/ch32v307_mp3_dac/User/audio/audio_wav.h b/ch32v307_mp3_dac/User/audio/audio_wav.h new file mode 100644 index 0000000..af9ff8b --- /dev/null +++ b/ch32v307_mp3_dac/User/audio/audio_wav.h @@ -0,0 +1,10 @@ +#ifndef __AUDIO_WAV_H +#define __AUDIO_WAV_H + +#include "audio_hal.h" + +#define AUDIO_WAV_FILE_BUFFER_SIZE 4608 + +audio_hal_result_t audio_wav_play(const char* file_name, audio_hal_result_t (*playing_routine)(void)); + +#endif diff --git a/ch32v307_mp3_dac/User/main.c b/ch32v307_mp3_dac/User/main.c index ea877d5..1f9fc8a 100644 --- a/ch32v307_mp3_dac/User/main.c +++ b/ch32v307_mp3_dac/User/main.c @@ -9,13 +9,12 @@ #include "ff.h" #include "tlsf.h" -#define TLSF_POOL_SIZE 36 * 1024 -static uint8_t tlsf_pool[TLSF_POOL_SIZE]; +static uint8_t tlsf_pool[36 * 1024]; int main(void) { delay_init(); - init_memory_pool(TLSF_POOL_SIZE, tlsf_pool); + init_memory_pool(sizeof(tlsf_pool), tlsf_pool); audio_hal_init_dac(); bsp_usart1_init(115200);