📄 mp3.h~
字号:
#ifndef _MP3_INCLUDED_
#define _MP3_INCLUDED_
//正点原子@scut 08/09/11
//很多部分参考网友(波仔)的
//实现功能:播放mp3/wma/wav/midi,支持mp3快进,前一首后一首选择,
//同时显示时间比特率和进度条
#include "vs1003.h"
#include "fat.h"
#include "snake.h"
void deal_dsp(uchar keys);
uchar MaxFile=0;//此批次中,最大的文件个数
bit bt30MS=0;//30MS 时间到
//定时器1中断 30ms一次
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
//static unsigned char *pp=pbuffer;//指向长文件名
TCNT1H=0xe2;
TCNT1L=0xb3;//30MS
if(En_backlight)//使能背光打开
{
lasttime++;
if(MenuOper==Ebook_Mode||(LYRIC&&ENLRC)||GAME_PLAY)lasttime=0;//TXT阅读/歌词显示模式下,背光一直打开
LCD_LED=1;
if(lasttime>150) //持续点亮4.5m
{
En_backlight=0;
lasttime=0;
LCD_LED=0;//关闭背光
}
}
bt30MS=1;//标记中断
}
//定时器1,30ms中断
void timer_init()
{
TCCR1A=0x00;
TCCR1B=0x03;
TCNT1H=0xe2;
TCNT1L=0xb3;
TIMSK=0x04;
#asm("sei")
}
//在当前的目录中查找歌词文件,找到置相关数据至lyric中
//入口: 1.dircluster 当前目录的首簇号,若是FAT12或FAT16的根目录则为0
void FindLrcFile(unsigned long dircluster,unsigned char *strName)
{
FIND_FILE_INFO fp;
direntry *de=0;
unsigned char name[8];
unsigned char i=0;
for(i=0;i<8;i++)name[i]=strName[i];//取前面八个字节
InitSetPath(&fp,dircluster);//初始化查找目录地址
ReadBlock(fp.Sector); //重新读取目录表
do
{
if(!ReadNextDirEntry(&fp)) {lyric.Clust=0;return;}//没找到任何歌词
de=(direntry *) BUFFER;
de+=fp.Index;
if(*de->deName != 0xe5)
{
if((de->deAttributes&ATTR_LONG_FILENAME)!=ATTR_LONG_FILENAME)
{
if((de->deAttributes& ATTR_DIRECTORY)!= ATTR_DIRECTORY)// is it a directory ?
{
for(i=0;i<8;i++)if(name[i]!=de->deName[i])break;
if(i==8) //主文件名比较正确
{
for(i=0;i<3;i++)//比较后缀
{
if((filetype[4][i])!=de->deExtension[i])break;//不符合
}
if(i==3)break;//找到了符合的文件
}
}
}
}
de++;
fp.Index++;
if(de->deName[0]==0) { lyric.Clust=0; return;} //文件的最后一项
}while(1);
LYRIC=1; //标记找到歌词
lyric.first=1;
lyric.t_sec=lyric.t_ms=0;//初始化为0
lyric.so=0;
lyric.sl=0;
lyric.wl=0; //初始位置
lyric.filelen=(unsigned int)de->deFileSize; //保存歌词文件大小
lyric.Clust=(unsigned long)((unsigned long)de->deHighClust<<16)+de->deStartCluster;//大小
}
//check ok 08/09/06
//读取当前歌词文件的下一个字节
//支持自动换簇换扇区
unsigned char ReadChar(unsigned char *buf)
{
if(lyric.filelen>0)lyric.filelen--;//歌词长度减少
if(lyric.so!=64)//buffer溢出
{
return buf[lyric.so++];
}else//读取下一批数据
{
lyric.wl+=64;//扇区内偏移增加 64
if(lyric.wl>=512)//已跨扇区
{
lyric.wl=0;
if(++lyric.sl>=SectorsPerCluster)//跨簇,几率不大
{
lyric.sl=0;//簇内偏移增加
lyric.Clust=fatNextCluster(lyric.Clust);
if(lyric.Clust==CLUST_EOFE)return 0xff; //文件结束
}
}
SD_Read_Bytes(fatClustToSect(lyric.Clust)+lyric.sl,buf,lyric.wl,64);//从当前位置向后读取64字节
lyric.so=0;
return buf[lyric.so++];//先得到值,再加加!!!
}
}
//读取一句有效的歌词记录
//check ok 08/09/06
//只支持单向时间
//如[xx:xx.xx][xx:xx.xx].... 则不支持
void ReadLrcFileData(void)
{
unsigned char *lbuf=pbuffer+200;
unsigned char c;
unsigned char i,bDot;
uchar work;
unsigned int tt;
unsigned char *pp=pbuffer;//指向长文件名
lyric.t_sec=0xffff;lyric.t_ms=0; //时间清空
if(lyric.filelen==0) return;//歌词读取完了
//从当前的位置读取歌词文件中的64个字节至缓冲区lbuf
SD_Read_Bytes(fatClustToSect(lyric.Clust)+lyric.sl,lbuf,lyric.wl,64);
if(!lyric.first)//歌词还没有更新
{
i=0;
while(lyric.filelen>0)
{
c=ReadChar(lbuf);//从缓冲里面读取出一个字节
if(c!=0x0d)//没读到回车符号
{
if(++i<=50)*pp++=c;//读取二十八个字节到
}else//读到回车符号了
{
if(lyric.filelen>0) ReadChar(lbuf); //去除0x0a
break;
}
}
*pp='\0';
pp=pbuffer;
Cleardisplay(1);Cleardisplay(2);//清屏
LCD_write_String(1,0,pp,2); //显示歌词
}
//读取下一句歌词
lyric.first=0;
tt=0;work=0;
bDot=0;
while(1)//歌词信息读取信息
{
if(lyric.filelen==0)return;
c=ReadChar(lbuf); //读取一个字节
if(work==0) //支持 [xx:xx.xx] 及[xx:xx]
{
if(c=='[')work++;//开始读取时间
}else if(work==1)
{
if((c>='0')&&(c<='9'))//得到数据
{
tt=tt*10+(c-'0');//读取第一批时间
}else if(c==':') //上次读取的是分钟
{
lyric.t_sec=tt*60;tt=0;
}else if(c=='.')//上次读取的是秒钟
{
lyric.t_sec+=tt;tt=0;
bDot = 1;
}else if(c==']')//读取结束
{
if(bDot==0)
{
lyric.t_sec+=tt;
lyric.t_ms=0;
}else lyric.t_ms=tt/3;
c=ReadChar(lbuf);
lyric.filelen++;//长度补偿
lyric.so--;
if(c!='['&&lyric.t_sec>=timer)break;//跳出歌词寻找
tt=0;work=0; bDot=0;
}else//读到非法数据
{
while(ReadChar(lbuf)!=0x0d);//非法数据忽略
work=0;
}
}
}
}
//check ok 08/09/06
// 歌词显示控制
//同时控制显示解码时间
//mnum 当前文件的序号 0~3
//注意:只支持单排时间的lrc正常显示
void LyricDisplayCtrl(uchar mnum)
{
static unsigned int time=0xffff;
static unsigned char ms=0;
unsigned long lenth=0;
unsigned int t1;
if(!bt30MS) return;//30ms未到
ms+=3;//ms++
bt30MS=0;
t1=GetDecodeTime(); //读取播放时间
if(t1!=time)//秒钟不合
{
time=t1;ms=0;//同步时间
timer=time; //修改同步时间计数器(这个是歌词的准确计时器,不是显示的时间!)
//如果这个乱了的话,歌词也会乱,所以快进后显示不了歌词
//得到比特率
t1=GetHeadInfo();
//显示歌曲长度
if(t1!=file_bps&&t1>0)//只有当kbps变化时才执行,提高系统速度
{
//显示比特率Kbps
Show_mininum(6,93,t1/10);//显示高两位
show_minichar(6,105,t1%10);//显示最低位
file_bps=t1;//保存此次信息长度;
//printf("BIT Rate is:%d\n",file_bps);
lenth=m_c[mnum].FileLen/125;//得到文件长度xxxx Kbps
t1=lenth/t1;
show_minichar(7,50,11);
Show_mininum(7,56,t1/60);//显示分钟
show_minichar(7,68,10);
Show_mininum(7,74,t1%60);//显示秒钟
}
return;
}
if(!ENLRC||!LYRIC)return;//无歌词或者歌词被禁止使用
if(((t1==lyric.t_sec)&&(ms>=lyric.t_ms))||(t1>lyric.t_sec))
{
ReadLrcFileData(); //读取歌曲数据并显示
}
}
//显示歌曲名字
//num:歌曲序号
void music_name(uchar mnum)
{
uchar i;
if(!ENLRC||!LYRIC)
{
while(m_c[mnum].LongName[i]!='\0')//把短名字放中间显示
{
i++;
if(i>21){i=22;break;}
}
i=66-3*i;//对13164液晶而言
LCD_write_String(1,i,m_c[mnum].LongName,2);//无歌词/歌词显示不使能
}
}
//播放音乐(选中的)
//支持类型:mp3/wma/wav/midi
//check ok 08/09/05
//返回状态值:next,用户要求播放下一首
//prev:用户要求播放上一首
//quit:结束播放
uchar play(uchar mnum)
{
unsigned long bfactor=0;
unsigned long bcluster=0;
unsigned long prgpos=0;
uchar count=0;
uint i;
uchar n;
bit pause=1;
bcluster=m_c[mnum].Clust;
PLAYING=1;//播放模式标记
Audio_Ch=2;//选择mp3通道
LYRIC=0;//歌词标记清除
bfactor=fatClustToSect(bcluster);
vs1003_Reset();//软复位VS1003
FindLrcFile(CurDir.Clust,m_c[mnum].ShortName);//从当前目录找LRC
Play_GUI(); //显示播放时的GUI
MP3_msg(pause);//显示mp3的基本信息
music_name(mnum); //显示歌名
keyval=0;//键值清零
while(1)
{
ReadBlock(bfactor);//放音乐
i=0;
do //主播放循环
{
if((PINB&MP3_DATA_REQ)&&pause)
{
for(n=0;n<32;n++)
{
vs1003_data_write(BUFFER[i++]);
}
}
if(READY)//有按键响应
{
keyval=key_process();//得到键值
switch(keyval)
{
case PREV://播放上一首
{
if(times>3)
{
for(i=0;i<20;i++)//向后偏移三十个簇
{
bcluster=fatNextCluster(bcluster);//偏移一个簇
if(bcluster==0x00)//读到文件簇尾
{
vs1003_Reset();//软复位
if(!SINGLE)return NEXT; //播放结束,播放下一首
else return 0;//单曲循环
}
}
i=512;//快进
count=8;
bfactor=SectorsPerCluster;//大于65536
prgpos+=bfactor*10752; //progpos递增
prgpos-=512;
break;
}
}
case NEXT://播放下一首
case QUIT://退出
{
vs1003_Reset(); //软复位
PLAYING=0; //非播放模式
return keyval; //返回
}
case PLAY:pause=!pause;break;//暂停/播放
case 168://歌词控制
{
ENLRC=!ENLRC;//歌词显示使能与否
Cleardisplay(1);Cleardisplay(2);//清除中间两行数据
music_name(mnum);//不使能
break;
}
case 106: //DSP保存
case 242:
case 114:
case 176:
case 112:
{
deal_dsp(keyval);
music_name(mnum);
break;
}
}
MP3_msg(pause);//改变后的界面信息
keyval=0;//清除键值
}
}while(i<511);
LyricDisplayCtrl(mnum);//显示歌词控制
LED=!LED;//观看系统相应速度
prgpos+=512;//进度条增加
Pro_msg(prgpos,mnum);//显示进度条
count++;
bfactor++; //扇区加
if(count>=SectorsPerCluster)//一个簇结束,换簇
{
count=0;
bcluster=fatNextCluster(bcluster);
if(bcluster==0x00)break; //文件结束
bfactor=fatClustToSect(bcluster);
}
}
vs1003_Reset();//软复位
PLAYING=0; //非播放模式
if(!SINGLE)return NEXT;//播放结束,播放下一首
else return 0;//单曲循环
}
void deal_dsp(uchar keys)
{
uchar t;
uchar d=0;
switch(keys)
{
case 106:d=0;break;
case 242:d=1;break;
case 114:d=2;break;
case 176:d=3;break;
case 112:d=4;break;
}
for(t=0;t<4;t++)
{
if(times>3)DSP[d][t]=voltemp[t];//保存dsp数值?
else voltemp[t]=DSP[d][t];//调出dsp?
}
dspram=d;//dsp改变
if(times>3)//显示界面,其实这个时候都已经操作完了
{
Cleardisplay(4);//清屏
LCD_write_cstr(1,40,"DSP:");
Show_char(1,74,d+'1',);
save_show();
Play_GUI();//恢复先前的界面
READY=0;
}
init_cd3315(Audio_Ch);//改变音量
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -