stm32h743_player/User/audio/audio_flac.c

192 lines
6.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "audio_flac.h"
#include "main.h"
#include <string.h>
#include "ff.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "elog.h"
#include "heap.h"
#include "bsp_aic3204.h"
#include "dr_flac.h"
const static char *TAG = "audio_flac";
static size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
HAL_GPIO_TogglePin(LED_Y_GPIO_Port, LED_Y_Pin);
UINT br;
FRESULT f_result = f_read(pUserData, pBufferOut, bytesToRead, &br); //从文件中读取新的内容
if (f_result != FR_OK) {
elog_error(TAG, "file read error");
return 0;
}
return br;
}
static drflac_bool32 flac_seek(void* pUserData, int offset, drflac_seek_origin origin)
{
FIL *file = (FIL*)pUserData;
uint32_t fptr = f_tell(file);
uint32_t fsize = f_size(file);
HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin);
switch (origin) {
case drflac_seek_origin_start:
if (offset < fsize) { //未超过文件末尾
f_lseek(file, offset);
return DRFLAC_TRUE;
} else {
elog_warn(TAG, "flac_seek: seeking beyond the end of the file");
return DRFLAC_FALSE;
}
case drflac_seek_origin_current:
if (fptr + offset < fsize) { //未超过文件末尾
f_lseek(file, fptr + offset);
return DRFLAC_TRUE;
} else {
elog_warn(TAG, "flac_seek: seeking beyond the end of the file");
return DRFLAC_FALSE;
}
default:
elog_warn(TAG, "flac_seek: unimplemented seek origin type: %d", origin);
return DRFLAC_FALSE;
}
}
static void* flac_malloc(size_t sz, void* pUserData)
{
UNUSED(pUserData);
void *ptr = malloc(sz);
// tlsf_pool_statistics *axi_heap_statistics = get_axi_heap_statistics();
// tlsf_pool_statistics *ahb_heap_statistics = get_ahb_heap_statistics();
// elog_debug(TAG, "flac_malloc: allocated %d bytes memory @%p, free memory: AXI %d bytes, AHB %d bytes",
// sz, ptr, axi_heap_statistics->free_size, ahb_heap_statistics->free_size);
if (ptr == NULL) {
elog_error(TAG, "flac_malloc: error while allocating %d bytes of memory", sz);
}
return ptr;
}
static void* flac_realloc(void* p, size_t sz, void* pUserData)
{
UNUSED(pUserData);
void *ptr = realloc(p, sz);
if (ptr == NULL) {
elog_error(TAG, "flac_realloc: error while allocating %d bytes of new memory", sz);
}
return ptr;
}
static void flac_free(void* p, void* pUserData)
{
UNUSED(pUserData);
// tlsf_pool_statistics *axi_heap_statistics = get_axi_heap_statistics();
// tlsf_pool_statistics *ahb_heap_statistics = get_ahb_heap_statistics();
// elog_debug(TAG, "flac_free: memory freed @%p, free memory: AXI %d bytes, AHB %d bytes",
// p, axi_heap_statistics->free_size, ahb_heap_statistics->free_size);
free(p);
}
static const drflac_allocation_callbacks flac_allocation_cbs = {
.onMalloc = flac_malloc,
.onFree = flac_free,
.onRealloc = flac_realloc,
.pUserData = NULL
};
void music_flac_play(const char *file_name)
{
FIL *file = malloc(sizeof(FIL));
if (file == NULL) {
elog_error(TAG, "error while allocating file object");
return;
}
FRESULT f_result = f_open(file, file_name, FA_READ);
if (f_result == FR_OK) {
elog_info(TAG, "successfully opened %s for playing", file_name);
} else {
elog_error(TAG, "error while opening %s, error code=%d", file_name, f_result);
free(file);
return;
}
/*
* TODO: 该函数用于打开文件的缓冲区位于栈中且栈位于DTCM中。该函数将缓冲区用于SD卡读取这会导致访存失败。
* 因打开文件时该函数所读取的数据均未超出第一个扇区FATFS只需将其内部位于AXI RAM的缓冲区返回给该函数打开文件时读取
* 并未产生实际SD卡读取操作所以该函数暂时能正常运行。此操作极其危险未来需要考虑如何优化。
*/
drflac* pFlac = drflac_open(flac_read, flac_seek, file, &flac_allocation_cbs); //创建flac解码器
if (pFlac == NULL) {
elog_error(TAG, "error while opening flac file");
goto exit;
}
elog_info(TAG, "music_flac_play: maxBlockSizeInPCMFrames=%d, channels=%d, bitsPerSample=%d",
pFlac->maxBlockSizeInPCMFrames, pFlac->channels, pFlac->bitsPerSample);
/* TODO: 判断flac文件参数并返回错误 */
bsp_aic3204_set_fs(&hi2c1, pFlac->sampleRate); //设置采样率
/* 初始化SAI */
uint32_t pcm_sample_count = pFlac->maxBlockSizeInPCMFrames * pFlac->channels; //PCM块中所有声道总的采样数量
uint32_t pcm_sample_size = pFlac->bitsPerSample / 8; //PCM块中每个采样的字节数(采样精度)
audio_hal_start(pcm_sample_count, pcm_sample_size); //初始化SAI
while(1) {
/* 如果当前是最后一个frame 则frame_count可能小于maxBlockSizeInPCMFrames 因此需要将pcm_sample初始化为全0 将空白区域静音 */
drflac_int16 *pcm_sample = malloc(pcm_sample_count * pcm_sample_size); //申请内存 存储1个PCM块
if (pcm_sample == NULL) {
elog_error(TAG, "music_flac_play: sample buffer allocation failed, size=%d bytes", pcm_sample_count * pcm_sample_size);
break;
}
memset(pcm_sample, 0, pcm_sample_count * pcm_sample_size);
/* 此处的framesToRead指的是PCM块中采样的数量(可以理解为时间层面的采样数) 不考虑声道数量与每个采样的字节数 */
uint32_t frame_count = drflac_read_pcm_frames_s16(pFlac, pFlac->maxBlockSizeInPCMFrames, pcm_sample); //解码一帧FLAC数据
if (frame_count != 0) {
/* 即使frame_count小于maxBlockSizeInPCMFrames也写入完整的一块 缓冲区后面空缺的部分在前面已经清0 */
audio_hal_write(pcm_sample, pcm_sample_count * pcm_sample_size); //将解码后的PCM数据写入SAI
} else { //文件已经读取完毕
free(pcm_sample);
elog_info(TAG, "play done");
break; //结束播放
}
if (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET) {
vTaskDelay(pdMS_TO_TICKS(100));
while (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET) {
vTaskDelay(pdMS_TO_TICKS(1));
}
break;
}
}
exit:
audio_hal_stop(); //停止SAI播放
if (file != NULL) {
f_close(file); //关闭文件
free(file);
}
if (pFlac != NULL) { //释放解码器
drflac_close(pFlac);
}
}