?? 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 + -