初级会员

- 积分
- 147
- 金钱
- 147
- 注册时间
- 2018-11-19
- 在线时间
- 40 小时
|
发表于 2018-12-10 11:32:10
|
显示全部楼层
//下面的代码是从网上找的音频软解码的程序里扣出来的,分析了以下flac的数据格式,可以获取准确的音频时长等信息
///////////////////////////////////////////////
//以下代码放在mp3player.h里
//保存FLAC文件信息
typedef struct FLACContext
{
u8 channels; //声道数
u8 bps; //采样位数
u16 samplerate; //采样率
u32 totalsamples; //全部音频样本数
u32 filesize; //总文件大小
u32 bitrate; //比特率
u32 metadatalength;//文件信息块总长度
}FLAC_Information;
//声明函数
u8 get_flac_information(FIL *file, FLAC_Information *fc);
////////////////////////////////////////////////////////////////////
//以下代码放在mp3player.c里
//获取flac文件信息
u8 get_flac_information(FIL *file, FLAC_Information *fc)
{
u16 minutes, seconds;
u8 found_streaminfo = 0;
u8 endofmetadata = 0;
u16 blocklength;
unsigned int br;
u8 *buf;
u32 *p;
buf = mymalloc(SRAMIN,256);//存储 文件信息描述块 的信息
if(!buf)return 0; //内存申请失败
//判断是否为flac文件
f_read(file, buf, 4, &br); //首次读取打开的文件前4个字节的数据
if(br < 4)//读取失败
{
myfree(SRAMIN,buf);
return 0;
}
if (memcmp(buf, "fLaC", 4) != 0) //flac文件中前四个字节为"fLaC"注意区分大小写
{
myfree(SRAMIN,buf);
return 0;//文件出错
}
fc->metadatalength = 4; //记录 文件信息描述块 总长度 当前值4
//循环读取 文件信息描述块 的信息
while (!endofmetadata)
{
f_read(file, buf, 4, &br);//继续读取四个4字节的数据
if (br < 4) //文件读完了,但是没有找到 文件信息描述块 结束标志,返回0(出错)
{
myfree(SRAMIN,buf);
return 0;
}
endofmetadata = (buf[0] & 0x80);//判断 文件信息描述块 结束标志(1xxx xxxx)
blocklength = (buf[1] << 16) | (buf[2] << 8) | buf[3];//得到这个文件信息块的长度
//记录 文件信息描述块 总长度
fc->metadatalength += blocklength + 4;
//分析文件信息块
if ((buf[0] & 0x7f) == 0)// 0 is the STREAMINFO block //低7位为0,则表示是STREAMINFO块 x000 0000
{
f_read(file, buf, blocklength, &br); //读取这个blocklength长度数据
if(br == 0) //读取失败
{
myfree(SRAMIN,buf);
return 0;
}
//提取flac文件参数
//文件总长度
fc->filesize = file->fsize;
//采样率
fc->samplerate = (buf[10] << 12) | (buf[11] << 4) | ((buf[12] & 0xf0) >> 4);
//声道数
fc->channels = ((buf[12] & 0x0e) >> 1) + 1;
//位率
fc->bps = (((buf[12] & 0x01) << 4) | ((buf[13] & 0xf0) >> 4) ) + 1;
//全部样本数
fc->totalsamples = (buf[14] << 24) | (buf[15] << 16) | (buf[16] << 8) | buf[17];
//比特率 [(总文件大小-文件信息描述块总长度 = 有效的音频数据)*8 ] = 有效音频数据总bit数
fc->bitrate = ((fc->filesize - fc->metadatalength) * 8) / ((fc->totalsamples / fc->samplerate) * 1000); //[(bit/音频时长=比特率)/1000 ]= Kbs
//全部样本数/采样率=音频总时长 totalsamples/samplerate
seconds = fc->totalsamples / fc->samplerate;
minutes = seconds / 60;
seconds %= 60;
found_streaminfo = 1; //标记找到了 STREAMINFO block
}
else // Skip to next metadata block 剩下的都是无需分析的块,只记录长度就好
{
f_lseek(file, file->fptr + blocklength); //移动读写指针到fptr + blocklength
if(file->fptr >= file->fsize) //指针长度超过文件大小,文件错误
{
myfree(SRAMIN,buf);
return 0;
}
}
}
if (found_streaminfo) //找到了 STREAMINFO block
{
printf("------------------------------\r\n");
printf("文件信息描述块长度: %d \r\n", fc->metadatalength);
printf("样本数 : %d\r\n", fc->totalsamples);
printf("采样率 : %d\r\n", fc->samplerate);
printf("位数 : %d\r\n", fc->bps);
printf("比特率 : %d Kbps \r\n", fc->bitrate);//比特率/码率
printf("时长 : %d%d:%d%d\r\n\n",minutes/10,minutes%10,seconds/10,seconds%10);
printf("声道 : ");
switch(fc->channels)
{
case 1:
printf("单声道");//printf("Mono");
break;
case 2:
printf("立体声");//printf("Stereo");
break;
default:
printf("%d Channels\r\n", fc->channels);
break;
}
myfree(SRAMIN,buf);
return 1;//成功
}
myfree(SRAMIN,buf);//失败
return 0;
}
///////////////////////////////////////////////////
对原子哥的mp3_play_song的程序有一点修改
//播放一曲指定的歌曲
//返回值:0,正常播放完成
// 1,下一曲
// 2,上一曲
// 0XFF,出现错误了
u8 mp3_play_song(u8 *pname)
{
FLAC_Information* fc;
FIL* fmp3;
u16 br;
u8 res,rval=0;
u8 *databuf;
u16 i=0;
u8 key;
fmp3=(FIL*)mymalloc(SRAMIN,sizeof(FIL));//申请内存
databuf=(u8*)mymalloc(SRAMIN,4096); //开辟4096字节的内存区域
if(databuf==NULL||fmp3==NULL)rval=0XFF ;//内存申请失败.
if(rval==0)
{
VS_Restart_Play(); //重启播放 (切歌)
VS_Set_All(); //设置音量、音效等信息
VS_Reset_DecodeTime(); //复位解码时间
res=f_typetell(pname); //得到文件类型(大类 小类 XXXX xxxx)
if(res==0x4c)//如果是flac,获取歌曲信息,之后加载patch
{
res=f_open(fmp3,(const TCHAR*)pname,FA_READ);//打开pname路径指向的文件 FA_READ-打开方式只读
if(res==0)//打开成功.
{
fc=(FLAC_Information*)mymalloc(SRAMIN,sizeof(FLAC_Information));//申请内存
get_flac_information(fmp3, fc); //获取flac文件重要数据
myfree(SRAMIN,fc);
f_close(fmp3);//关闭当前文件
}
//加载patch
VS_Load_Patch((u16*)vs1053b_patch,VS1053B_PATCHLEN);
}
res=f_open(fmp3,(const TCHAR*)pname,FA_READ);//打开pname路径指向的文件 FA_READ-打开方式只读
if(res==0)//打开成功.
{
VS_SPI_SpeedHigh(); //设置SPI高速
while(rval==0)
{
//如果要读的数据大于剩余大小,要读的出数据br就等于剩余数据大小
res=f_read(fmp3,databuf,4096,(UINT*)&br);//每次读出4096个字节
i=0;
do//主播放循环
{
if(VS_Send_MusicData(databuf+i)==0)//判断VS10XX是否需要数据,如果是,发送32字节的音频数据
{
i+=32;
}
else //发送完数据需要等VS10XX处理,VS10XX不需要接受数据了,这时可以检测按键
{
key=KEY_Scan(0);
switch(key)
{
case KEY0_PRES:
rval=1; //下一曲
break;
case KEY2_PRES:
rval=2; //上一曲
break;
case WKUP_PRES: //音量增加
if(vsset.mvol<250)
{
vsset.mvol+=5;
VS_Set_Vol(vsset.mvol); //设置音量(函数里循环等待数据请求引脚有效)
}
else
vsset.mvol=250;
mp3_vol_show((vsset.mvol-100)/5); //音量限制在:100~250,显示的时候,按照公式(vol-100)/5,显示,也就是0~30
break;
case KEY1_PRES: //音量减
if(vsset.mvol>100)
{
vsset.mvol-=5;
VS_Set_Vol(vsset.mvol); //设置音量(函数里循环等待数据请求引脚有效)
}
else
vsset.mvol=100;
mp3_vol_show((vsset.mvol-100)/5); //音量限制在:100~250,显示的时候,按照公式(vol-100)/5,显示,也就是0~30
break;
}
mp3_msg_show(fmp3->fsize);//显示播放时间,比特率 信息 fmp3->fsize-文件总大小
}
}while(i<4096);//循环发送4096个字节
if(br!=4096||res!=0)//读完了(已经发送完了数据) 或者操作按键了
{
rval=0;
break;
}
}
f_close(fmp3);//关闭当前文件
}
else
rval=0XFF;//出现错误
}
myfree(SRAMIN,databuf);
myfree(SRAMIN,fmp3);
return rval;
}
|
|