Files
player/Project/Src/MP3/mp3play.c
2025-07-06 17:11:38 +08:00

659 lines
19 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 "mp3play.h"
#include "dac.h"
#include "delay.h"
#include "ftfile.h"
#include "mymem.h"
#include "rtthread.h"
#include "stm32f4xx.h"
#include "string.h"
#include "usart.h"
// 包含这个文件用于文字编码转换
#include "char_encode.h"
//////////////////////////////////////////////////////////////////////////////////
// 本程序移植自helix MP3解码库
// ALIENTEK STM32F407开发板
// MP3 解码代码
// 正点原子@ALIENTEK
// 技术论坛:www.openedv.com
// 创建日期:2014/6/29
// 版本V1.0
//********************************************************************************
// V1.0 说明
// 1,支持16位单声道/立体声MP3的解码
// 2,支持CBR/VBR格式MP3解码
// 3,支持ID3V1和ID3V2标签解析
// 4,支持所有比特率(MP3最高是320Kbps)解码
//////////////////////////////////////////////////////////////////////////////////
static __mp3ctrl *mp3ctrl; // mp3控制结构体
static __audiodev audiodev; // 音频控制结构体
static vu8 g_buff_Invalid = 0; // 数据无效时要重新填充数据
static vu8 g_buff_useing; // 当前DMA在使用哪个缓冲区
static int g_vol = 5; // 音量 0~10级
#define MP3_VOL_MAX 10
// MP3 DMA发送回调函数
static void dma_tx_callback(DAC_UserStruct *dac) {
u16 i;
if (dac->buff_useing == 1) {
if ((audiodev.status & 0X01) == 0) // 暂停了,填充0
{
for (i = 0; i < 2304 * 2; i++)
audiodev.i2sbuf1[i] = 0;
}
} else {
if ((audiodev.status & 0X01) == 0) // 暂停了,填充0
{
for (i = 0; i < 2304 * 2; i++)
audiodev.i2sbuf2[i] = 0;
}
}
g_buff_useing = dac->buff_useing;
g_buff_Invalid = 1;
}
// 填充PCM数据到DAC
// buf:PCM数据首地址
// size:pcm数据量(16位为单位)
// nch:声道数(1,单声道,2立体声)
void mp3_fill_buffer(u16 *buf, u16 size, u8 nch) {
u16 i;
s16 *p;
while (g_buff_Invalid == 0) // 等待传输完成
{
rt_thread_delay(5); //
};
if (g_buff_useing == 0) {
p = (s16 *)audiodev.i2sbuf2;
} else {
p = (s16 *)audiodev.i2sbuf1;
}
if (nch == 2) {
for (i = 0; i < size; i++) {
// p[i]=((buf[i]+0x8000)>>4);
int temp = (int16_t)buf[i];
temp = temp * g_vol / MP3_VOL_MAX;
p[i] = ((temp + 0x8000) >> 4);
}
} else // 单声道
{
for (i = 0; i < size; i++) {
int temp = (int16_t)buf[i];
temp = temp * g_vol / MP3_VOL_MAX;
p[2 * i] = ((temp + 0x8000) >> 4);
p[2 * i + 1] = p[2 * i];
}
}
// 填充了数据之后设置为有效
g_buff_Invalid = 0;
}
static void mp3_get_string(u8 type, u8 *out, u8 *in, int size);
// 解析ID3V1
// buf:输入数据缓存区(大小固定是128字节)
// pctrl:MP3控制器
// 返回值:0,获取正常
// 其他,获取失败
u8 mp3_id3v1_decode(u8 *buf, __mp3ctrl *pctrl) {
ID3V1_Tag *id3v1tag;
id3v1tag = (ID3V1_Tag *)buf;
if (strncmp("TAG", (char *)id3v1tag->id, 3) == 0) // 是MP3 ID3V1 TAG
{
mp3_get_string(0, pctrl->title, id3v1tag->title, 30);
mp3_get_string(0, pctrl->artist, id3v1tag->artist, 30);
} else
return 1;
return 0;
}
static void mp3_get_string(u8 type, u8 *out, u8 *in, int size) {
if (type == 1) // UNICODE16
{
u8 *char_uni = mymalloc(128);
mymemset(char_uni, 0, 128);
strncpy((char *)char_uni, (char *)(in), size);
// Unicode转GBK
uni2gbk_str(char_uni, out);
myfree(char_uni);
} else if (type == 3) // UTF8
{
u8 *char_utf8 = mymalloc(128);
mymemset(char_utf8, 0, 128);
strncpy((char *)char_utf8, (char *)(in), size);
// UTF8转GBK
utf82gbk_str(char_utf8, out);
myfree(char_utf8);
} else //(type==0)//ASCII
{
strncpy((char *)out, (char *)(in), size);
}
}
// 解析ID3V2
// buf:输入数据缓存区
// size:数据大小
// pctrl:MP3控制器
// 返回值:0,获取正常
// 其他,获取失败
u8 mp3_id3v2_decode(u8 *buf, u32 size, __mp3ctrl *pctrl) {
ID3V2_TagHead *taghead;
ID3V23_FrameHead *framehead;
u32 t;
u32 tagsize; // tag大小
u32 frame_size; // 帧大小
taghead = (ID3V2_TagHead *)buf;
if (strncmp("ID3", (const char *)taghead->id, 3) == 0) // 存在ID3?
{
tagsize = ((u32)taghead->size[0] << 21) | ((u32)taghead->size[1] << 14) |
((u16)taghead->size[2] << 7) | taghead->size[3]; // 得到tag 大小
pctrl->datastart = tagsize; // 得到mp3数据开始的偏移量
if (tagsize > size)
tagsize = size; // tagsize大于输入bufsize的时候,只处理输入size大小的数据
if (taghead->mversion < 3) {
printf("not supported mversion!\r\n");
return 1;
}
t = 10;
while (t < tagsize) {
framehead = (ID3V23_FrameHead *)(buf + t);
frame_size =
((u32)framehead->size[0] << 24) | ((u32)framehead->size[1] << 16) |
((u32)framehead->size[2] << 8) | framehead->size[3]; // 得到帧大小
if (strncmp("TT2", (char *)framehead->id, 3) == 0 ||
strncmp("TIT2", (char *)framehead->id, 4) ==
0) // 找到歌曲标题帧,不支持unicode格式!!
{
u8 *dat_ptr = (buf + t + sizeof(ID3V23_FrameHead));
int size = AUDIO_MIN(frame_size - 1, MP3_TITSIZE_MAX - 1);
mp3_get_string(dat_ptr[0], pctrl->title, dat_ptr + 1, size);
}
if (strncmp("TP1", (char *)framehead->id, 3) == 0 ||
strncmp("TPE1", (char *)framehead->id, 4) == 0) // 找到歌曲艺术家帧
{
u8 *dat_ptr = (buf + t + sizeof(ID3V23_FrameHead));
int size = AUDIO_MIN(frame_size - 1, MP3_ARTSIZE_MAX - 1);
mp3_get_string(dat_ptr[0], pctrl->artist, dat_ptr + 1, size);
}
// if (strncmp("APIC",(char*)framehead->id,4)==0)
// {
// //专辑封面
// u8
//*dat_ptr=(buf+t+sizeof(ID3V23_FrameHead)); if (pctrl->jpeg_data)
// myfree(pctrl->jpeg_data);
// pctrl->jpeg_data=mymalloc(frame_size); pctrl->jpeg_len=frame_size;
// mymemcpy
//(pctrl->jpeg_data,dat_ptr,frame_size);
// }
t += frame_size + sizeof(ID3V23_FrameHead);
}
return 0;
} else {
pctrl->datastart = 0; // 不存在ID3,mp3数据是从0开始
return (u8)-1;
}
}
// 获取MP3基本信息
// pname:MP3文件路径
// pctrl:MP3控制信息结构体
// 返回值:0,成功
// 其他,失败
u8 mp3_get_info(u8 *data, u32 size, __mp3ctrl *pctrl) {
HMP3Decoder decoder = {0};
MP3FrameInfo frame_info = {0};
MP3_FrameXing *fxing = 0;
MP3_FrameVBRI *fvbri = 0;
u8 *buf = 0;
u8 *data_ptr = 0;
// u32 br;
u8 res = 0;
int offset = 0;
u32 p = 0;
short samples_per_frame = 0; // 一帧的采样个数
u32 totframes = 0; // 总帧数
buf = mymalloc(MP3_FILE_BUF_SZ); // 申请5K内存
if (buf) // 内存申请成功
{
data_ptr = data;
mymemcpy(buf, data_ptr, MP3_FILE_BUF_SZ);
if (res == 0) // 读取文件成功,开始解析ID3V2/ID3V1以及获取MP3信息
{
if (mp3_id3v2_decode(buf, MP3_FILE_BUF_SZ, pctrl) != 0) // 解析ID3V2数据
{
data_ptr = data + size - 128;
mymemcpy(buf, data_ptr, 128);
mp3_id3v1_decode(buf, pctrl); // 解析ID3V1数据
}
decoder = MP3InitDecoder(); // MP3解码申请内存
data_ptr = data + pctrl->datastart;
mymemcpy(buf, data_ptr, MP3_FILE_BUF_SZ);
offset = MP3FindSyncWord(buf, MP3_FILE_BUF_SZ); // 查找帧同步信息
if (offset >= 0 &&
MP3GetNextFrameInfo(decoder, &frame_info, &buf[offset]) ==
0) // 找到帧同步信息了,且下一阵信息获取正常
{
p = offset + 4 + 32;
fvbri = (MP3_FrameVBRI *)(buf + p);
if (strncmp("VBRI", (char *)fvbri->id, 4) == 0) // 存在VBRI帧(VBR格式)
{
if (frame_info.version == MPEG1)
samples_per_frame = 1152; // MPEG1,layer3每帧采样数等于1152
else
samples_per_frame = 576; // MPEG2/MPEG2.5,layer3每帧采样数等于576
totframes =
((u32)fvbri->frames[0] << 24) | ((u32)fvbri->frames[1] << 16) |
((u16)fvbri->frames[2] << 8) | fvbri->frames[3]; // 得到总帧数
pctrl->totsec = totframes * samples_per_frame /
frame_info.samprate; // 得到文件总长度
} else // 不是VBRI帧,尝试是不是Xing帧(VBR格式)
{
if (frame_info.version == MPEG1) // MPEG1
{
p = frame_info.nChans == 2 ? 32 : 17;
samples_per_frame = 1152; // MPEG1,layer3每帧采样数等于1152
} else {
p = frame_info.nChans == 2 ? 17 : 9;
samples_per_frame = 576; // MPEG2/MPEG2.5,layer3每帧采样数等于576
}
p += offset + 4;
fxing = (MP3_FrameXing *)(buf + p);
if (strncmp("Xing", (char *)fxing->id, 4) == 0 ||
strncmp("Info", (char *)fxing->id, 4) == 0) // 是Xng帧
{
if (fxing->flags[3] & 0X01) // 存在总frame字段
{
totframes = ((u32)fxing->frames[0] << 24) |
((u32)fxing->frames[1] << 16) |
((u16)fxing->frames[2] << 8) |
fxing->frames[3]; // 得到总帧数
pctrl->totsec = totframes * samples_per_frame /
frame_info.samprate; // 得到文件总长度
} else // 不存在总frames字段
{
pctrl->totsec = size / (frame_info.bitrate / 8);
}
} else // CBR格式,直接计算总播放时间
{
pctrl->totsec = size / (frame_info.bitrate / 8);
}
}
pctrl->bitrate = frame_info.bitrate; // 得到当前帧的码率
mp3ctrl->samplerate = frame_info.samprate; // 得到采样率.
if (frame_info.nChans == 2)
mp3ctrl->outsamples = frame_info.outputSamps; // 输出PCM数据量大小
else
mp3ctrl->outsamples =
frame_info.outputSamps *
2; // 输出PCM数据量大小,对于单声道MP3,直接*2,补齐为双声道输出
} else
res = 0XFE; // 未找到同步帧
MP3FreeDecoder(decoder); // 释放内存
}
} else
res = 0XFF;
myfree(buf);
return res;
}
// 得到当前播放时间
// fx:文件指针
// mp3x:mp3播放控制器
// void mp3_get_curtime(FIL*fx, FILINFO *finfo,__mp3ctrl *mp3x)
//{
// u32 fpos=0;
// if(fx->fptr>mp3x->datastart)fpos=fx->fptr-mp3x->datastart;
////得到当前文件播放到的地方
// mp3x->cursec=fpos*mp3x->totsec/(finfo->fsize-mp3x->datastart);
////当前播放到第多少秒了?
// }
// mp3文件快进快退函数
// pos:需要定位到的文件位置
// 返回值:当前文件位置(即定位后的结果)
u32 mp3_file_seek(u32 pos) {
// if(pos>audiodev.file->fsize)
// {
// pos=audiodev.file->fsize;
// }
// f_lseek(audiodev.file,pos);
// return audiodev.file->fptr;
return pos;
}
// 判断mp3是否支持
// 读取 MP3_FILE_BUF_SZ 大小的数据就行了
// 返回0 支持非0 不支持
int mp3_get_support(u8 *data, u32 size) {
int res = 0;
__mp3ctrl *mp3ctrl = mymalloc(sizeof(__mp3ctrl));
mymemset(mp3ctrl, 0, sizeof(__mp3ctrl)); // 数据清零
res = mp3_get_info(data, size, mp3ctrl);
if (mp3ctrl->jpeg_data)
myfree(mp3ctrl->jpeg_data);
myfree(mp3ctrl);
mp3ctrl = 0;
return res;
}
// 返回0 支持非0 不支持
int mp3_get_support_name(const char *name) {
// 定义返回变量
int res = 0;
// 初始化mp3结构体
__mp3ctrl *mp3ctrl = mymalloc(sizeof(__mp3ctrl));
mymemset(mp3ctrl, 0, sizeof(__mp3ctrl)); // 数据清零
// 申请缓存
u32 buf_size = 1024 * 5;
u8 *buf = mymalloc(buf_size);
// 打开文件
ft_file_struct *file = ft_if_fopen(name, "rw");
// 获取头大小
ft_if_fseek(file, 0, SEEK_SET);
ft_if_fread(buf, 1, buf_size, file);
ID3V2_TagHead *taghead;
u32 tagsize = 0; // tag大小
taghead = (ID3V2_TagHead *)buf;
if (strncmp("ID3", (const char *)taghead->id, 3) == 0) // 存在ID3?
{
tagsize = ((u32)taghead->size[0] << 21) | ((u32)taghead->size[1] << 14) |
((u16)taghead->size[2] << 7) | taghead->size[3]; // 得到tag 大小
}
// 校验格式是否支持
HMP3Decoder decoder = {0};
MP3FrameInfo frame_info = {0};
int offset = 0;
decoder = MP3InitDecoder(); // MP3解码申请内存
ft_if_fseek(file, tagsize, SEEK_SET);
ft_if_fread(buf, 1, buf_size, file);
offset = MP3FindSyncWord(buf, buf_size); // 查找帧同步信息
if (offset >= 0 && MP3GetNextFrameInfo(decoder, &frame_info, &buf[offset]) ==
0) // 找到帧同步信息了,且下一阵信息获取正常
{
res = 0;
} else
res = -1;
MP3FreeDecoder(decoder); // 释放内存
// 关闭文件
ft_if_fclose(file);
// 释放缓存
myfree(buf);
// 释放mp3结构体
myfree(mp3ctrl);
// 返回结果
return res;
}
// 播放一曲MP3音乐
// fname:MP3文件路径.
// 返回值:0,正常播放完成
//[b7]:0,正常状态;1,错误状态
//[b6:0]:b7=0时,表示操作码
// b7=1时,表示有错误(这里不判定具体错误,0X80~0XFF,都算是错误)
u8 mp3_play_song(u8 *data, u32 size) {
if (mp3ctrl)
return AP_ERR; // 不可重入!
HMP3Decoder mp3decoder = {0};
MP3FrameInfo mp3frameinfo = {0};
u8 res = 0;
u8 *buffer = 0; // 输入buffer
u8 *readptr = 0; // MP3解码读指针
u8 *data_ptr = 0;
int offset = 0; // 偏移量
int outofdata = 0; // 超出数据范围
int bytesleft = 0; // buffer还剩余的有效数据
u32 br = 0;
int err = 0;
DAC_UserStruct dac = {0};
mp3ctrl = mymalloc(sizeof(__mp3ctrl));
buffer = mymalloc(MP3_FILE_BUF_SZ); // 申请解码buf大小
audiodev.i2sbuf1 = mymalloc(2304 * 2);
audiodev.i2sbuf2 = mymalloc(2304 * 2);
audiodev.tbuf = mymalloc(2304 * 2);
audiodev.file_seek = mp3_file_seek;
if (!mp3ctrl || !buffer || !audiodev.i2sbuf1 || !audiodev.i2sbuf2 ||
!audiodev.tbuf) // 内存申请失败
{
myfree(mp3ctrl);
myfree(buffer);
myfree(audiodev.i2sbuf1);
myfree(audiodev.i2sbuf2);
myfree(audiodev.tbuf);
return AP_ERR; // 错误
}
mymemset(audiodev.i2sbuf1, 0, 2304 * 2); // 数据清零
mymemset(audiodev.i2sbuf2, 0, 2304 * 2); // 数据清零
mymemset(mp3ctrl, 0, sizeof(__mp3ctrl)); // 数据清零
res = mp3_get_info(data, size, mp3ctrl);
if (res == 0) {
// 初始化DAC
dac.buff1 = (u32 *)audiodev.i2sbuf1;
dac.buff2 = (u32 *)audiodev.i2sbuf2;
// dac.buff_size=2304*2;
dac.buff_size = mp3ctrl->outsamples * 2;
dac.rate = DAC_GetRate(mp3ctrl->samplerate);
dac.call_back = dma_tx_callback;
DAC_NormalInit(&dac);
mp3decoder = MP3InitDecoder(); // MP3解码申请内存
audiodev.status |= 1 << 1; // 开启播放
audiodev.status |= 1 << 0; // 非暂停状态
}
if (res == 0 && mp3decoder != 0) // 打开文件成功
{
// f_lseek(audiodev.file,mp3ctrl->datastart); //跳过文件头中tag信息
data_ptr = data + mp3ctrl->datastart;
// audio_start();
// //开始播放
while (res == 0) {
readptr = buffer; // MP3读指针指向buffer
offset = 0; // 偏移量为0
outofdata = 0; // 数据正常
bytesleft = 0;
// res=f_read(audiodev.file,buffer,MP3_FILE_BUF_SZ,&br);//一次读取MP3_FILE_BUF_SZ字节
br = MP3_FILE_BUF_SZ;
if (br > data + size - data_ptr)
br = data + size - data_ptr;
mymemcpy(buffer, data_ptr, br);
data_ptr += br;
if (res) // 读数据出错了
{
res = AP_ERR;
break;
}
if (br == 0) // 读数为0,说明解码完成了.
{
res = AP_OK; // 播放完成
break;
}
bytesleft += br; // buffer里面有多少有效MP3数据?
err = 0;
while (!outofdata) // 没有出现数据异常(即可否找到帧同步字符)
{
offset = MP3FindSyncWord(readptr,
bytesleft); // 在readptr位置,开始查找同步字符
if (offset < 0) // 没有找到同步字符,跳出帧解码循环
{
outofdata = 1; // 没找到帧同步字符
} else // 找到同步字符了
{
readptr += offset; // MP3读指针偏移到同步字符处.
bytesleft -= offset; // buffer里面的有效数据个数,必须减去偏移量
err = MP3Decode(mp3decoder, &readptr, &bytesleft,
(short *)audiodev.tbuf, 0); // 解码一帧MP3数据
if (err != 0) {
// printf("decode error:%d\r\n",err);
break;
} else {
MP3GetLastFrameInfo(mp3decoder,
&mp3frameinfo); // 得到刚刚解码的MP3帧信息
if (mp3ctrl->bitrate != mp3frameinfo.bitrate) // 更新码率
{
mp3ctrl->bitrate = mp3frameinfo.bitrate;
}
mp3_fill_buffer((u16 *)audiodev.tbuf, mp3frameinfo.outputSamps,
mp3frameinfo.nChans); // 填充pcm数据
}
if (bytesleft <
MAINBUF_SIZE *
2) // 当数组内容小于2倍MAINBUF_SIZE的时候,必须补充新的数据进来.
{
memmove(
buffer, readptr,
bytesleft); // 移动readptr所指向的数据到buffer里面,数据量大小为:bytesleft
// f_read(audiodev.file,buffer+bytesleft,MP3_FILE_BUF_SZ-bytesleft,&br);//补充余下的数据
br = MP3_FILE_BUF_SZ - bytesleft;
if (br > data + size - data_ptr)
br = data + size - data_ptr;
mymemcpy(buffer + bytesleft, data_ptr, br);
data_ptr += br;
if (br < MP3_FILE_BUF_SZ - bytesleft) {
mymemset(buffer + bytesleft + br, 0,
MP3_FILE_BUF_SZ - bytesleft - br);
}
bytesleft = MP3_FILE_BUF_SZ;
readptr = buffer;
}
while (audiodev.status & (1 << 1)) // 正常播放中
{
rt_thread_delay(5);
// mp3_get_curtime(audiodev.file,mp3ctrl);
u32 fpos = 0;
if (data_ptr > data)
fpos = data_ptr - data; // 得到当前文件播放到的地方
mp3ctrl->cursec =
fpos * mp3ctrl->totsec /
(size - mp3ctrl->datastart); // 当前播放到第多少秒了?
audiodev.totsec = mp3ctrl->totsec; // 参数传递
audiodev.cursec = mp3ctrl->cursec;
audiodev.bitrate = mp3ctrl->bitrate;
audiodev.samplerate = mp3ctrl->samplerate;
audiodev.bps = 16; // MP3仅支持16位
if (audiodev.status & 0X01)
break; // 没有按下暂停
}
if ((audiodev.status & (1 << 1)) == 0) // 请求结束播放/播放完成
{
res = AP_NEXT; // 跳出上上级循环
outofdata = 1; // 跳出上一级循环
break;
}
}
}
}
// audio_stop();//关闭音频输出
} else
res = AP_ERR; // 错误
MP3FreeDecoder(mp3decoder); // 释放内存
if (mp3ctrl->jpeg_data)
myfree(mp3ctrl->jpeg_data);
myfree(mp3ctrl);
myfree(buffer);
myfree(audiodev.i2sbuf1);
myfree(audiodev.i2sbuf2);
myfree(audiodev.tbuf);
DAC_NormalDeInit(&dac);
mp3ctrl = 0;
audiodev.cursec = 0;
audiodev.totsec = 0;
return res;
}
// 获取当前播放的曲目名返回非0成功
char *mp3_get_name(void) {
if (mp3ctrl)
return (char *)mp3ctrl->title;
else
return 0;
}
// 获取当前播放的艺术家返回非0成功
char *mp3_get_artist(void) {
if (mp3ctrl)
return (char *)mp3ctrl->artist;
else
return 0;
}
// 获取当前播放的时间返回1成功
int mp3_get_time(int *totsec, int *cursec) {
if (mp3ctrl) {
if (totsec)
*totsec = mp3ctrl->totsec;
if (cursec)
*cursec = mp3ctrl->cursec;
return 1;
} else {
return 0;
}
}
__audiodev *mp3_getAudiodev(void) { return &audiodev; }
__mp3ctrl *mp3_getMp3Info(void) { return mp3ctrl; }
void mp3_stop(void) { mp3_getAudiodev()->status = 0; }
void mp3_play(void) {
// 暂停中
if (mp3_getAudiodev()->status & 1) {
mp3_getAudiodev()->status &= (~1);
}
// 播放中
else {
mp3_getAudiodev()->status |= 1;
}
}
void mp3_suspend(void) { mp3_getAudiodev()->status |= 1; }
// 设置音量
int mp3_set_vol(int vol) {
if (vol > MP3_VOL_MAX)
vol = MP3_VOL_MAX;
else if (vol < 0)
vol = 0;
g_vol = vol;
return 0;
}
// 增加音量
int mp3_add_val(int vol) {
vol = vol + g_vol;
if (vol > MP3_VOL_MAX)
vol = MP3_VOL_MAX;
else if (vol < 0)
vol = 0;
g_vol = vol;
return 0;
}
// 减少音量
int mp3_sub_val(int vol) {
vol = g_vol - vol;
if (vol > MP3_VOL_MAX)
vol = MP3_VOL_MAX;
else if (vol < 0)
vol = 0;
g_vol = vol;
return 0;
}
// 获取音量
int mp3_get_vol(void) { return g_vol; }