188 lines
5.3 KiB
C
188 lines
5.3 KiB
C
#include "audio_mp3.h"
|
|
#include "main.h"
|
|
#include "ff.h"
|
|
#include "FreeRTOS.h"
|
|
#include "queue.h"
|
|
#include "elog.h"
|
|
#include "heap.h"
|
|
#include "bsp_aic3204.h"
|
|
#include "dr_mp3.h"
|
|
|
|
const static char *TAG = "player_mp3";
|
|
|
|
static size_t mp3_read(void* pUserData, void* pBufferOut, size_t bytesToRead)
|
|
{
|
|
UINT br;
|
|
FRESULT f_result = f_read(pUserData, pBufferOut, bytesToRead, &br); //从文件中读取新的内容
|
|
|
|
HAL_GPIO_TogglePin(LED_Y_GPIO_Port, LED_Y_Pin);
|
|
|
|
if (f_result != FR_OK) {
|
|
elog_error(TAG, "file read error");
|
|
return 0;
|
|
}
|
|
|
|
return br;
|
|
}
|
|
|
|
static drmp3_bool32 mp3_seek(void* pUserData, int offset, drmp3_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 drmp3_seek_origin_start:
|
|
if (offset < fsize) { //未超过文件末尾
|
|
f_lseek(file, offset);
|
|
return DRMP3_TRUE;
|
|
} else {
|
|
elog_warn(TAG, "flac_seek: seeking beyond the end of the file");
|
|
return DRMP3_FALSE;
|
|
}
|
|
case drmp3_seek_origin_current:
|
|
if (fptr + offset < fsize) { //未超过文件末尾
|
|
f_lseek(file, fptr + offset);
|
|
return DRMP3_TRUE;
|
|
} else {
|
|
elog_warn(TAG, "flac_seek: seeking beyond the end of the file");
|
|
return DRMP3_FALSE;
|
|
}
|
|
|
|
default:
|
|
elog_warn(TAG, "flac_seek: unimplemented seek origin type: %d", origin);
|
|
return DRMP3_FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static void* mp3_malloc(size_t sz, void* pUserData)
|
|
{
|
|
UNUSED(pUserData);
|
|
|
|
if (sz == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
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, "mp3_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, "mp3_malloc: error while allocating %d bytes of memory", sz);
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static void* mp3_realloc(void* p, size_t sz, void* pUserData)
|
|
{
|
|
UNUSED(pUserData);
|
|
|
|
void *ptr = realloc(p, sz);
|
|
|
|
tlsf_pool_statistics *axi_heap_statistics = get_axi_heap_statistics();
|
|
tlsf_pool_statistics *ahb_heap_statistics = get_ahb_heap_statistics();
|
|
elog_debug(TAG, "mp3_realloc: allocated %d bytes new 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, "mp3_realloc: error while allocating %d bytes of new memory", sz);
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static void mp3_free(void* p, void* pUserData)
|
|
{
|
|
UNUSED(pUserData);
|
|
|
|
if (p == NULL) {
|
|
return;
|
|
}
|
|
|
|
tlsf_pool_statistics *axi_heap_statistics = get_axi_heap_statistics();
|
|
tlsf_pool_statistics *ahb_heap_statistics = get_ahb_heap_statistics();
|
|
elog_debug(TAG, "mp3_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 drmp3_allocation_callbacks mp3_allocation_cbs = {
|
|
.onMalloc = mp3_malloc,
|
|
.onFree = mp3_free,
|
|
.onRealloc = mp3_realloc,
|
|
.pUserData = NULL
|
|
};
|
|
|
|
void music_mp3_play(const char *file_name)
|
|
{
|
|
FIL *file = malloc(sizeof(FIL));
|
|
if (file == NULL) {
|
|
elog_error(TAG, "music_mp3_play: error while allocating file object");
|
|
return;
|
|
}
|
|
|
|
FRESULT f_result = f_open(file, file_name, FA_READ);
|
|
if (f_result == FR_OK) {
|
|
elog_info(TAG, "music_mp3_play: successfully opened %s for playing", file_name);
|
|
} else {
|
|
elog_error(TAG, "music_mp3_play: error while opening %s, error code=%d", file_name, f_result);
|
|
free(file);
|
|
return;
|
|
}
|
|
|
|
drmp3 mp3;
|
|
drmp3_init(&mp3, mp3_read, mp3_seek, file, &mp3_allocation_cbs); //创建并初始化MP3解码器
|
|
|
|
elog_info(TAG, "music_mp3_play: channels=%d, sampleRate=%d", mp3.channels, mp3.sampleRate);
|
|
|
|
/* TODO: 判断mp3文件参数并返回错误 */
|
|
|
|
bsp_aic3204_set_fs(&hi2c1, mp3.sampleRate); //设置采样率
|
|
|
|
audio_hal_start(DRMP3_MAX_SAMPLES_PER_FRAME, sizeof(drmp3_int16)); //初始化SAI
|
|
|
|
while(1) {
|
|
drmp3_int16 *pcm_sample = malloc(DRMP3_MAX_SAMPLES_PER_FRAME * sizeof(drmp3_int16));
|
|
if (pcm_sample == NULL) {
|
|
elog_error(TAG, "music_mp3_play: sample buffer allocation failed, size=%d bytes", DRMP3_MAX_SAMPLES_PER_FRAME * sizeof(drmp3_int16));
|
|
break;
|
|
}
|
|
|
|
/* 此处的framesToRead指的是PCM块中采样的数量(可以理解为时间层面的采样数) 不考虑声道数量与每个采样的字节数 */
|
|
uint32_t sample_count = drmp3_read_pcm_frames_s16(&mp3, DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME, pcm_sample); //解码新一帧MP3数据
|
|
|
|
if (sample_count != 0) {
|
|
audio_hal_write(pcm_sample, DRMP3_MAX_SAMPLES_PER_FRAME * sizeof(drmp3_int16)); //将解码后的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;
|
|
}
|
|
}
|
|
|
|
audio_hal_stop(); //停止SAI播放
|
|
|
|
if (file != NULL) {
|
|
f_close(file); //关闭文件
|
|
free(file);
|
|
}
|
|
|
|
drmp3_uninit(&mp3); //释放解码器内部申请的文件缓冲区
|
|
}
|