160 lines
4.3 KiB
C
160 lines
4.3 KiB
C
#include "audio_flac.h"
|
|
#include "main.h"
|
|
#include "ff.h"
|
|
#include "FreeRTOS.h"
|
|
#include "queue.h"
|
|
#include "elog.h"
|
|
#include "heap.h"
|
|
|
|
#define DR_FLAC_IMPLEMENTATION
|
|
#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); //从文件中读取新的内容
|
|
|
|
elog_verbose(TAG, "flac_read: reading %d bytes, %d bytes read", 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;
|
|
HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin);
|
|
|
|
if (origin == drflac_seek_origin_start) {
|
|
f_lseek(file, offset);
|
|
elog_debug(TAG, "flac_seek: drflac_seek_origin_start, offset=%d, fptr=%d", offset, f_tell(file));
|
|
} else {
|
|
uint32_t fptr = f_tell(file);
|
|
if (fptr + offset > f_size(file)) {
|
|
elog_warn(TAG, "flac_seek: seeking beyond the end of the file");
|
|
return false;
|
|
}
|
|
f_lseek(file, fptr + offset);
|
|
elog_debug(TAG, "flac_seek: drflac_seek_origin_current, offset=%d, fptr original=%d, fptr=%d", offset, fptr, f_tell((FIL*)pUserData));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void* flac_malloc(size_t sz, void* pUserData)
|
|
{
|
|
UNUSED(pUserData);
|
|
|
|
void *ptr = malloc(sz);
|
|
|
|
elog_warn(TAG, "flac_malloc: %d bytes", sz);
|
|
tlsf_pool_statistics *dtcm_heap_statistics = get_dtcm_heap_statistics();
|
|
tlsf_pool_statistics *ahb_heap_statistics = get_ahb_heap_statistics();
|
|
elog_verbose(TAG, "free heap: DTCM: %d, AXI: %d", dtcm_heap_statistics->free_size, ahb_heap_statistics->free_size);
|
|
|
|
return ptr;
|
|
}
|
|
void* flac_realloc(void* p, size_t sz, void* pUserData)
|
|
{
|
|
UNUSED(pUserData);
|
|
|
|
void *ptr = realloc(p, sz);
|
|
|
|
elog_warn(TAG, "flac_realloc: %d bytes", sz);
|
|
return ptr;
|
|
}
|
|
|
|
void flac_free(void* p, void* pUserData)
|
|
{
|
|
UNUSED(pUserData);
|
|
|
|
elog_warn(TAG, "flac_free");
|
|
free(p);
|
|
}
|
|
|
|
static const drflac_allocation_callbacks callbacks = {
|
|
.onMalloc = flac_malloc,
|
|
.onFree = flac_free,
|
|
.onRealloc = flac_realloc,
|
|
.pUserData = NULL
|
|
};
|
|
|
|
// static void flac_on_meta(void* pUserData, drflac_metadata* pMetadata)
|
|
// {
|
|
// elog_warn(TAG, "on metadata %d", pMetadata->type);
|
|
// }
|
|
|
|
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;
|
|
}
|
|
|
|
/* 创建并初始化解码器 */
|
|
// drflac* pFlac = drflac_open_with_metadata(flac_read, flac_seek, flac_on_meta, file, NULL);
|
|
drflac* pFlac = drflac_open(flac_read, flac_seek, file, &callbacks);
|
|
if (pFlac == NULL) {
|
|
elog_error(TAG, "error while creating opening flac file");
|
|
goto exit;
|
|
}
|
|
|
|
/* 初始化SAI */
|
|
uint32_t frame_pcm_size = pFlac->maxBlockSizeInPCMFrames * pFlac->channels;
|
|
audio_hal_start(frame_pcm_size, pFlac->bitsPerSample / 8);
|
|
|
|
elog_info(TAG, "start playing");
|
|
|
|
while(1) {
|
|
/* 解码新一帧FLAC数据 */
|
|
drflac_int16 *sample = malloc(frame_pcm_size * (pFlac->bitsPerSample / 8));
|
|
if (sample == NULL) {
|
|
elog_error(TAG, "memory allocation failed");
|
|
goto exit;
|
|
}
|
|
uint32_t frame_count = drflac_read_pcm_frames_s16(pFlac, frame_pcm_size, sample);
|
|
/* 将解码后的PCM数据写入SAI */
|
|
if (frame_count != 0) {
|
|
// elog_verbose(TAG, "writing %d bytes of pcm data", frame_count * sizeof(drflac_int16));
|
|
audio_hal_write(sample, frame_count * pFlac->channels);
|
|
} else { //文件已经读取完毕
|
|
free(sample);
|
|
elog_info(TAG, "play done");
|
|
break; //结束播放
|
|
}
|
|
}
|
|
|
|
audio_hal_stop();
|
|
|
|
/* 关闭文件 */
|
|
f_close(file);
|
|
free(file);
|
|
|
|
exit:
|
|
if (pFlac) {
|
|
drflac_close(pFlac);
|
|
}
|
|
|
|
tlsf_pool_statistics *dtcm_heap_statistics = get_dtcm_heap_statistics();
|
|
tlsf_pool_statistics *ahb_heap_statistics = get_ahb_heap_statistics();
|
|
elog_verbose(TAG, "free heap: DTCM: %d, AXI: %d", dtcm_heap_statistics->free_size, ahb_heap_statistics->free_size);
|
|
}
|