⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lrcpro.c

📁 基于stm32的MP3播放器
💻 C
字号:
#include "COMM.h"					    			  
//歌词控制部分
//支持格式[XX:XX]/[XX:XX:XX]/[XX:XX.X]
//同时也支持多个并排的的歌词格式
//如[XX:XX.XX][XX:XX.XX]也是支持的	 
//正点原子@HYW
//2009.04.30
//V1.1	 		

//设计思想:
//第一步:把lrc文件的tag全部复制出来,存放到tag_table(最大支持426个tag,多出的部分将不予理会)
//第二步:把得到的tag,按顺序排列.得到标准的时间tag,保存在tag_table里面
//第三步:再按照tag的时间顺序,遍历tag,同时读取lrc文件的歌词,当读到符合条件的时候,复制tag及歌词到
//       歌词保存的地址buffer1,buffer2中.这样就得到了整个歌词
//注意:buffer1+buffer2的大小为6144个字节,超出部分将丢失.

					  
u16 lrc_tag_cnt=0; //记录tag的总数
u8  lrctagtype=0;  //记录lrc的类型 0:[XX:XX] 1:[XX:XX.XX]	  
//歌词文件的信息存放地址		   
FileInfoStruct *LrcFile=&F_Info[1];//开辟暂存空间.	 
u8 *tag_table=(u8 *)MCUBuffer;     //LRC时间戳暂存地址,后面将用于存放一句LRC歌词 
u8 bt30MS=0;                       //30ms到了的标志
//LRC歌词的信息		 
typedef struct{ 
	u8   first;    //标志歌词是否更新了
	u16  t_sec;    //暂存歌词的时间(秒)
	u8   t_ms;     //保存32MS计数脉冲			 			    
    u16  lrcoff;   //歌词文件读取的长度
	u8   tagcnt;   //歌词结束标记
}LYRIC_STRUCT;
LYRIC_STRUCT lyric; //歌词信息
///////////////////////////////////////////////////////////////
		  
u8 WriteByteLrcBuf(u16 addr,u8 data);//写入一个字节数据
u8 ReadByteLrcBuf(u16 addr);         //读取一个字节数据
u8 Get_Byte(u8 rst);//从sd得到一个字节
u8 Get_Num(void);   //得到一个数字
u8 Get_Tag(void); //得到tag列表
void Init_Tag(void);//对得到的tag排序

//从lrcbufer里面读取一个字节 
//读数错误,返回0xff     
//addr:地址
//返回值:读到的数据
u8 ReadByteLrcBuf(u16 addr)
{						  
	u8 *buffer1=(u8 *)iclip;//得到第一个buf存放的地址
	u8 *buffer2=(u8 *)code_value_table;//第二个buf地址
										    
	if(addr<4096)return buffer1[addr];
	else 
	{				 
		if(addr>6144)return 0xff;
		return buffer2[addr];//返回数据
	}
}
//向lrcbufer里面写一个字节,地址自动增加
//超出范围,返回0xff
//data:要写入的数据  
//addr:地址
//返回值:操作标志
//最多存储6Kbyte大小的歌词.
u8 WriteByteLrcBuf(u16 addr,u8 data)
{						  
	u8 *buffer1=(u8 *)iclip;//得到第一个buf存放的地址
	u8 *buffer2=(u8 *)code_value_table;//第二个buf地址	  							  
	if(addr<4096)buffer1[addr]=data; //写入数据到buffer1
	else 
	{			 
		if(addr>6144)return 0xff;
		buffer2[addr-4097]=data;  //写入数据到buffer2
	}
	return 1;
}
//复制一句歌词
//tmin,tsec,tmms:要复制的歌词tag
//返回值:0xff,结束.1,复制成功	  
u8 CopyString(u8 tmin,u8 tsec,u8 tmms)
{
	static u16 tagpos=0;   //歌词tag标志
	static u16 lrcbufpos=0;//歌词缓存地址
	u8 temp;
	u8 temp1;
	u16 tagcnt=0;

	if(tmin==0xff){tagpos=0;lrcbufpos=0;return 1;}//复位
	if(lrctagtype)tagcnt=(lrc_tag_cnt-1)*3;
	else tagcnt=(lrc_tag_cnt-1)*2;	
	if(tagpos>tagcnt)//读取结束了
	{
		WriteByteLrcBuf(lrcbufpos,'\0');//写入结束符
		return 0xff;
	}	 
	if(tmin==tag_table[tagpos]&&tsec==tag_table[tagpos+1]&&lrctagtype==0)//[XX:XX]类型歌词
	{	  
		tagpos+=2;
	}else if(tmin==tag_table[tagpos]&&tsec==tag_table[tagpos+1]&&tmms==tag_table[tagpos+2]&&lrctagtype==1) //类型2
	{
		tagpos+=3;
	}else return 1;
	WriteByteLrcBuf(lrcbufpos++,'['); //写一个字节
	WriteByteLrcBuf(lrcbufpos++,tmin);//写入分钟
	WriteByteLrcBuf(lrcbufpos++,':'); 
	WriteByteLrcBuf(lrcbufpos++,tsec);//写入秒钟
	if(lrctagtype)//XX:XX.XX格式
	{
		WriteByteLrcBuf(lrcbufpos++,'.'); 
		WriteByteLrcBuf(lrcbufpos++,tmms);//写入秒钟
	}
	WriteByteLrcBuf(lrcbufpos++,']');     //写入最后的结束标志
	 
	while(1)//一直等到得到tag结束标志
	{
		temp=Get_Byte(0);
		if(temp=='[') //可能意味着下一个tag的到来
		{
			WriteByteLrcBuf(lrcbufpos,temp);//先记录下来,以防不是
			temp1=Get_Byte(0);
			temp=temp1;//复制
			temp1-='0';
			if(temp1<10)//如果是数据,肯定是下一个tag了
			{
				while(1)
				{
					temp1=Get_Byte(0);
					if(temp1==']'||temp1==0xff)break;//读到tag末尾
				}
			}else  //不是tag,是数据
			{
				lrcbufpos++;
				WriteByteLrcBuf(lrcbufpos++,temp);//记录下来	  
				break;//读到有效数据了
			}
		}else if(temp!=' ')	//忽略空格
		{
			if(temp!=0XFF&&temp!=0X00)WriteByteLrcBuf(lrcbufpos++,temp);//记录下来 
			break;//读到了有效数据
		} 							   
	}						 
	while(1)//开始复制文本
	{
		temp=Get_Byte(0);//得到一个字符
		if(temp!=0XFF&&temp!=0X00)WriteByteLrcBuf(lrcbufpos++,temp);//复制合法字符      
		if(temp==0X0A||temp==0XFF||temp==0X00)//本句读取结束
		{
			WriteByteLrcBuf(lrcbufpos++,temp);
			break;
		}	    
	}
	return 1;
}   
//复制LRC歌词到ram里面
void CopyLrcToRam(void)
{					    		 
	u8 numx;
	u8 temp;
	u8 min;
	u8 sec;
	u8 mms;		
					    
	CopyString(0xff,0,0);//复位
	Get_Byte(1);//复位 
	while(1)
	{
		while(1)//一直等到得到tag开始标志
		{
			temp=Get_Byte(0);	 
			if(temp=='[')break;
			else if(temp==0xff) //所有数据读取结束了
			{
				Get_Byte(1);//复位 重新读
			}			   
		}
		numx=Get_Num();//得到第一个数字	  
		if(Get_Byte(0)!=':'||numx==0xff)continue;
		min=numx;
		numx=Get_Num();//得到第二个数字
		temp=Get_Byte(0);
		if(temp!='.'&&temp!=']'||numx==0xff)continue;
		sec=numx;	    
		if(temp!='.')  //[XX:XX]格式
		{				     
			if(CopyString(min,sec,0)==0XFF)return ;//复制完毕	 		    
			continue;
		}			   
		numx=Get_Num();//得到第三个数字
		temp=Get_Byte(0);
		if(temp!=']'||numx==0xff)continue;//[XX:XX.XX]格式
		mms=numx;
		if(CopyString(min,sec,mms)==0XFF)return ;//复制完毕	 
	}
}	
//歌词存放地址,借用JPEG解码部分里面的RAM	   
u8 *LRC_DATA=(u8 *)QtZzMCUBuffer; 

//从歌词文件中得到一个LRC字符
//结束返回0XFF
//rst:1,复位,从头开始读书
//    2,偏移到上一个地址
u8 Get_Byte(u8 rst)
{		 
	static u16 bytepos;
	u8 temp;					  
	if(rst)//复位,不读取数据
	{
		if(rst==1)//复位
		{
			F_Open(LrcFile);//打开LRC文件
			F_Read(LrcFile,LRC_DATA);//读出512个字节	  
			bytepos=0;	  
		}
		else bytepos--;//偏移到上一簇 对付[XX:XX.X]格式的歌词
		return 0xff;
	}   
	if(bytepos>511)//超过一个扇区了,读下一个扇区
	{
		temp=F_Read(LrcFile,LRC_DATA);//读出512个字节	 
		bytepos=0; 
		if(temp==0)return 0xff;//读数错误	   
	} 
    return LRC_DATA[bytepos++];
}  
 
//得到一个数字
//失败返回0xff
u8 Get_Num(void)
{
	u8 t1=0;
	u8 t2=0;
	t1=Get_Byte(0);//得到第一位数据	 十位
	t2=Get_Byte(0);//得到第二位数据  各位
	if(t1<'9'&&t1>'0'&&t2==']')//遇到[XX:XX.X]这种格式
	{
		Get_Byte(2);//偏移到上一个地址  否则无法检测']'
		return (t1-'0')*10;
	}	 
	t1-='0';//得到ASCII数值
	t2-='0';
	if(t1<10&&t2<10)//得到合法数据
	{
		return t1*10+t2;
	}
	return 0XFF;
}
//得到歌词文件的所有时间戳 tag
//返回值:1,成功获取了tag。0,获取tag失败
u8 Get_Tag(void)
{	
	u16 i=0;														    
			 
	u8 numx;
	u8 temp;
	u8 min;
	u8 sec;
	u8 mms;			   
    lrc_tag_cnt=0;   //tag数清空
    lrctagtype=0;    //类型清空	  
	Get_Byte(1);     //复位 
	while(1)
	{
		if(lrc_tag_cnt>426)break;//超过了最大存储空间,后续不在保存
		while(1)//一直等到得到tag开始标志
		{
			temp=Get_Byte(0);  	   		 
			if(temp=='[')break;
			else if(temp==0xff)
			{
				if(lrc_tag_cnt)return 1;//所有数据读取结束了,偏移字节暂定为20	
				else return 0;//获取tag失败	非法的lrc文件	 
			}
		}
		numx=Get_Num();//得到第一个数字	  
		if(Get_Byte(0)!=':'||numx==0xff)continue;
		min=numx;
		numx=Get_Num();//得到第二个数字
		temp=Get_Byte(0);
		if(temp!='.'&&temp!=']'||numx==0xff)continue;
		sec=numx;		  					    
		if(temp!='.')//[XX:XX]格式
		{
			tag_table[i]=min;
			tag_table[i+1]=sec;
			i+=2;
			lrctagtype=0; //xx:xx格式
			lrc_tag_cnt++;//记录tag数
			continue;
		}
		numx=Get_Num();//得到第三个数字
		temp=Get_Byte(0);
		if(temp!=']'||numx==0xff)continue;//[XX:XX.XX]格式
		mms=numx;
		lrctagtype=1;  //xx:xx.xx格式
		lrc_tag_cnt++; //记录tag数
		tag_table[i]=min;
		tag_table[i+1]=sec;
		tag_table[i+2]=mms;
		i+=3;									 
	}
	return 1;
}

//初始化所有tag
//按升序排列    
void Init_Tag(void)
{
	u16 t=0;		 
	u8 t1;
	u8 temp;
	u16 tagcnt=0;
	u8 offset;

	if(lrctagtype)//XX:XX.XX
	{
		tagcnt=(lrc_tag_cnt-1)*3;
		offset=3; //XX:XX 
	}else 	 
	{
		tagcnt=(lrc_tag_cnt-1)*2;
		offset=2;
	}	  							 
	do//将数据X升序排列
	{	
		t1=0;		  
		for(t=0;t<tagcnt;t+=offset)
		{
			if(tag_table[t]>tag_table[t+offset])//分钟数大于后者,调换所有
			{
				temp=tag_table[t+offset];		  //分钟调换
				tag_table[t+offset]=tag_table[t];
				tag_table[t]=temp;

				temp=tag_table[t+offset+1];		  //秒钟调换
				tag_table[t+offset+1]=tag_table[t+1];
				tag_table[t+1]=temp;
				if(offset==3)
				{
					temp=tag_table[t+5];		  //mm调换
					tag_table[t+5]=tag_table[t+2];
					tag_table[t+2]=temp;   
				}	   
				t1=1; 	//有改变
			}else if(tag_table[t+1]>tag_table[t+offset+1]&&tag_table[t]==tag_table[t+offset])//秒钟数大于后者,调换后两位
			{
				temp=tag_table[t+offset+1];		  //秒钟调换
				tag_table[t+offset+1]=tag_table[t+1];
				tag_table[t+1]=temp;

				if(offset==3)
				{
					temp=tag_table[t+5];		  //mm调换
					tag_table[t+5]=tag_table[t+2];
					tag_table[t+2]=temp;   
				}	   
				t1=1; 	//有改变
			}else if(offset==3)	//XX:XX.XX
			{
				if(tag_table[t+2]>tag_table[t+5]&&tag_table[t]==tag_table[t+3]&&tag_table[t+1]==tag_table[t+4])//mm数大于后者,调换后两位
				{						 
					temp=tag_table[t+5];		  //mm调换
					tag_table[t+5]=tag_table[t+2];
					tag_table[t+2]=temp;
					t1=1; 	//有改变
				}
			} 
		}
	}while(t1);
	/*for(t=0;t<=tagcnt;t+=offset)
	{
		if(offset==3)printf("[%d:%d.%d]\n",tag_table[t],tag_table[t+1],tag_table[t+2]);
		else printf("[%d:%d]\n",tag_table[t],tag_table[t+1]);
	} */
}   
//在当前的目录中查找歌词文件,找到置相关数据至lyric中
//入口: 1.dircluster    当前目录的首簇号,若是FAT12或FAT16的根目录则为0
//如果找到,则返回1
//没找到,返回0
u8 FindLrcFile(unsigned long dircluster,unsigned char *strName)
{							  
	unsigned char  name[8];       
	u16 lrc_cnt=0;//当前目录下LRC文件的个数
	u16 i;
	u8 t;							    
	FileInfoStruct *FileInfo;

	printf("START SEARCH LRC:%s\n\n",strName);
	for(i=0;i<8;i++)name[i]=strName[i];//取前面八个字节   	 						   
	lrc_cnt=0;	  
	FileInfo=&F_Info[2];//开辟LRC暂存空间.
	Get_File_Info(Cur_Dir_Cluster,FileInfo,T_LRC,&lrc_cnt);//获取当前文件夹下面的lrc文件个数  	  
	for(i=1;i<=lrc_cnt;i++)
	{
		Get_File_Info(Cur_Dir_Cluster,FileInfo,T_LRC,&i);//得到这个LRC的信息	 
        for(t=0;t<8;t++)if(name[t]!=FileInfo->F_ShortName[t])break;                    
        if(t==8) //找到歌词文件了
		{	
			printf("Find Matched LRC\n");									  
			Get_File_Info(Cur_Dir_Cluster,LrcFile,T_LRC,&i);//读取信息到LrcFile	    
			
			printf("LRC Name:%s\n",LrcFile->F_Name);			
			printf("LRC Size:%d\n",LrcFile->F_Size);						  
			
			t=Get_Tag();  //得到tag
			if(t==0)break;//读取tag失败了
			printf("Get LRC Tag OK\n");	 
			Init_Tag();	   //复制tag
			printf("Init Tag OK!!\n");	 	
			CopyLrcToRam();//复制歌词到ram

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -