192 lines
6.2 KiB
C
192 lines
6.2 KiB
C
#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);
|
||
}
|
||
}
|