ch32v307_mp3_dac: add wav player and pause function

This commit is contained in:
wangyz1997 2024-05-08 00:45:05 +08:00
parent 8a7d6b9587
commit c993ad3a75
9 changed files with 282 additions and 14 deletions

View File

@ -126,7 +126,7 @@
</toolChain>
</folderInfo>
<sourceEntries>
<entry excluding="User/3rdparty/fatfs/ffsystem.c|User/3rdparty/fatfs/ffunicode.c|User/audio/audio_wav.h|User/audio/audio_wav.c|User/audio/audio_mp3_rom.h|User/audio/audio_mp3_rom.c|User/audio_mp3_rom.h|User/audio_mp3_rom.c|Startup|Peripheral|Ld|Debug|Core" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
<entry excluding="User/3rdparty/fatfs/ffsystem.c|User/3rdparty/fatfs/ffunicode.c|User/audio/audio_mp3_rom.h|User/audio/audio_mp3_rom.c|User/audio_mp3_rom.h|User/audio_mp3_rom.c|Startup|Peripheral|Ld|Debug|Core" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Core"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Ld"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Peripheral"/>

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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 --;

View File

@ -4,7 +4,7 @@
#include <stdbool.h>
#include "audio_hal.h"
#include "audio_mp3.h"
//#include "audio_wav.h"
#include "audio_wav.h"
void audio_player(void);

View File

@ -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;
}

View File

@ -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

View File

@ -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);