234 lines
8.3 KiB
C
234 lines
8.3 KiB
C
#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;
|
|
}
|