Lrc歌词文件格式及其C++代码实现

合集下载

lrc的格式

lrc的格式

lrc的格式
LRC文件是一种歌词文件格式,用于显示歌曲的歌词。

其标准格式如下:
1. [ti:歌曲标题] - 歌曲标题,以"ti:"开头,后面跟着歌曲的标题。

2. [ar:歌手名] - 歌手名,以"ar:"开头,后面跟着歌手的名字。

3. [al:专辑名] - 专辑名,以"al:"开头,后面跟着专辑的名字。

4. [by:制作人] - 制作人,以"by:"开头,后面跟着制作人的名字。

5. [offset:时间偏移量] - 时间偏移量,以"offset:"开头,后面跟着一个整数,表示歌词时间与音乐时间的偏移量,单位是毫秒。

6. [length:歌曲长度] - 歌曲长度,以"length:"开头,后面跟着歌曲的长度,格式为"mm:ss"。

7. [00:]歌词内容- 歌词内容,以"[开始时间]"开头,后面是歌词的具体内容。

时间格式为"mm:",表示歌词出现的时间点。

用记事本按照上述格式写好后,将扩展名改为lrc即可做出“文件名.LRC”
的歌词文件。

请注意,外文名lrc、定义歌词文件的扩展名、全称lyric、编码方式ANSI
与UTF-8居多等其他格式可能与标准格式略有不同。

建议咨询专业的技术
从业者获取更多具体信息。

歌词解析器代码详情

歌词解析器代码详情

歌词解析器代码详情共6个文件,一个文件夹。

Dir目录如下。

驱动器F 中的卷没有标签。

卷的序列号是18A6-FCB5F:\W.E\C语言编程\歌词解析器的目录2013-01-22 16:27.2013-01-22 16:27..2013-01-22 16:27lrc2012-11-19 15:29 9,215 lrc.c2011-12-12 12:17 644 lrc.h2012-09-24 15:18 2,083 main.c2013-01-22 16:27pos3 个文件11,942 字节4 个目录171,449,978,880 可用字节Pos下有文件:console.c console.h resource.h现将所有文件内容罗列于下,自行分开.c.h Main.c#include#include#include#include#include "lrc.h"#include "./pos/console.h"typedef struct time{ //表示日期时间的数据结构char minute;char second;}TIME;TIME m_time; //存储模拟时间char * song_msg[4]; //存储歌曲的信息/************************************************************** ************ 函数功能:歌词解析器整体的控制* 参数:无* 返回值:无*************************************************************** ***********/int main(){int file_length=0; // 存储lrc文件内容的大小char * file_buf=NULL; // 指向malloc的空间,其中存放着lrc 文件中所有的内容char * lrc_text[200]={NULL}; // 指向,按照\r\n 切割后的一行歌词int lrc_line=0; // lrc文件中的行数int i=0;LRC * head=NULL,*p; // head记录歌词链表头int s_time=0; // 模拟时间变量file_buf=read_src_file(&file_length,"./lrc/兰亭序.lrc"); // 从lrc 文件中读出歌词内容lrc_line=strtok_lrc_buf(lrc_text,file_buf); // 按行切割歌曲内容lrc_head_resolve(song_msg,lrc_text); // 切割歌曲信息print_song_message(song_msg); // 显示歌曲信息head=lrc_resolve(lrc_line,lrc_text); // 保存时间:歌词到链表中// print(head); // 遍历链表,用来测试链表// ShellExecute( NULL, NULL, "TTPlayer.exe","兰亭序.mp3",NULL,1); //开启千千静听m_time.second=0;m_time.minute=0;while(1) //每秒钟到链表里面去找相应的歌词,如果找到那么就显示{/** 1.定位光标,显示歌词*/GoToXY(20,5);printf("%02d:%02d",m_time.minute,m_time.second);p=search(head,s_time); //到连表里查找当前时间点对应的歌词,如果有那么就返回歌词的首地址if(p!=NULL){lrc_show(p);}/** 1.运行模拟时间*/Sleep(500); //延时,注意在windows平台Sleep(1);延时1毫秒s_time=s_time+1;++m_time.second;if(m_time.second==60){m_time.second=0;++m_time.minute;if(m_time.minute==60)m_time.minute=0;}}return 0;}/************************************************************** ***************/ lrc.h#ifndef __LRC_H__#define __LRC_H__typedef struct lrc{int time;char lrc_buf[200];struct lrc *next;}LRC;extern char * read_src_file(int *file_length,char *src_file_name);extern int strtok_lrc_buf(char *lrc_text[],char *file_buf);extern void lrc_head_resolve(char *lrc_head_info[],char * lrc_text[]);extern void print_song_message(char * msg_info[]);extern LRC * lrc_resolve(int lrc_line,char * lrc_text[]);extern LRC *InsertList(LRC* head,LRC *lrc_link);extern void print(LRC * head);extern LRC *search (LRC *head,int time);extern void lrc_show(LRC * pi);extern void print_lrc(char * lrc);#endif/************************************************************** ***************/lrc.c#include#include#include#include#include "lrc.h"#include "./pos/console.h"/************************************************************** *********** * 函数功能:读出文件内容* 参数:* file_length:整型指针,此地址中保存文件字节数。

C语言课程设计歌曲信息管理源代码

C语言课程设计歌曲信息管理源代码

C语言课程设计歌曲信息管理源代码#include "stdio.h"#include "string.h"#include "conio.h"#include "stdlib.h"struct music_list{char name[10];char singer[25];char ci[25];char qu[25];char zhuanji[25];char fxtime[25];char fxgongsi[25];int flag;};int count=0;const int MAX_COUNT = 100;void new_music(struct music_list musics[]);void delete_music_byname(struct music_list musics[],char* name); void search_music(struct music_list musics[],char*name);void show_all(struct music_list musics[]);int main(){ int choice;char name[10];struct music_list musics[MAX_COUNT];int i;for(i=0;i<MAX_COUNT;i++){musics[i].flag = 0;}T:do{printf("*******************************************************\n");printf("\nselect:1:new music 2:search 3:delete 4:show all 0:Exit\n");printf("*******************************************************\n");printf("please:");scanf("%d",&choice);switch(choice){case 1:new_music(musics);break;case 2:printf("Name:");scanf("%s",name);search_music(musics,name);break;case 3:printf("Name:");scanf("%s",name);delete_music_byname(musics,name);break;case 4:show_all(musics);break;case 0:break;default:choice = 0;}}while(choice !=0);printf("Thanks,Bye!\n");getch();return 0;}void new_music(struct music_list musics[]) {if(count==MAX_COUNT){printf("address list is full!\n");return;}int i,j;for(i=0;i<MAX_COUNT;i++){if(musics[i].flag == 0){j = i;break;}}printf("name:");scanf("%s",musics[j].name);printf("geshou:");scanf("%s",musics[j].singer);printf("zuoci:");scanf("%s",musics[j].ci);printf("zuoqu:");scanf("%s",musics[j].qu);printf("zhuanji:");scanf("%s",musics[j].zhuanji);printf("chubanshijian:");scanf("%s",musics[j].fxtime);printf("chubangongsi:");scanf("%s",musics[j].fxgongsi);musics[j].flag = 1;count++;}void search_music(struct music_list musics[],char*name){int i,j,flag=0;if(count==0){printf("Music list is empty!\n");return;}for(i=0;i<MAX_COUNT;i++){if(strcmp(name,musics[i].name)==0 && musics[i].flag == 1){if(flag == 0) {printf("====================================================== =\n");printf("name\t",musics[i].name);printf("singer\t",musics[i].singer);printf("ci\t",musics[i].ci);printf("qu\t",musics[i].qu);printf("zhuanji\t",musics[i].zhuanji);printf("fxtime\t",musics[i].fxtime);printf("fxgongsi\t",musics[i].fxgongsi);printf("\n==================================================== ===\n");flag=1;}printf("%s\t",musics[i].name);printf("%s\t",musics[i].singer);printf("%s\t",musics[i].ci);printf("%s\t",musics[i].qu);printf("%s\t",musics[i].zhuanji);printf("%s\t",musics[i].fxtime);printf("%s\t\n",musics[i].fxgongsi);}}if(flag!=1){printf("No such a music!");}}void show_all(struct music_list musics[]){if(count==0){printf("Music list is empty!\n");return;}int i,flag=0;;for(i=0;i<MAX_COUNT;i++){if(musics[i].flag == 1){if(flag == 0) {printf("====================================================== =\n");printf("name\t",musics[i].name);printf("singer\t",musics[i].singer);printf("ci\t",musics[i].ci);printf("qu\t",musics[i].qu);printf("zhuanji\t",musics[i].zhuanji);printf("fxtime\t",musics[i].fxtime);printf("\n==================================================== ===\n");flag=1;}printf("%s\t",musics[i].name);printf("%s\t",musics[i].singer);printf("%s\t",musics[i].ci);printf("%s\t",musics[i].qu);printf("%s\t",musics[i].zhuanji);printf("%s\t",musics[i].fxtime);printf("%s\t\n",musics[i].fxgongsi);}}}void delete_music_byname(struct music_list musics[],char* name){int i,j,flag=0;if(count==0){printf("Music list is empty!\n");return;}for(i=0;i<MAX_COUNT;i++){if(strcmp(name,musics[i].name)==0 && musics[i].flag == 1){musics[i].flag = 0;count --;if(flag == 0) {printf("The music \" %s \" was deleted.\n",musics[i].name);printf("====================================================== =\n");printf("name\t",musics[i].name);printf("singer\t",musics[i].singer);printf("ci\t",musics[i].ci);printf("qu\t",musics[i].qu);printf("zhuanji\t",musics[i].zhuanji);printf("fxtime\t",musics[i].fxtime);printf("\n==================================================== ===\n");flag=1;}printf("%s\t",musics[i].name);printf("%s\t",musics[i].singer);printf("%s\t",musics[i].ci);printf("%s\t",musics[i].qu);printf("%s\t",musics[i].zhuanji);printf("%s\t",musics[i].fxtime);printf("%s\t\n",musics[i].fxgongsi);}}if(flag!=1){printf("No such a music!");}}。

C语言同步显示lrc歌词

C语言同步显示lrc歌词

C语言同步显示lrc歌词C语言技术报告班级:学号:姓名:项目说明:1、本程序在VC环境下调试成功环境:需安装千千静听到默认路径下,同时安装vc6.0环境2、本程序完成功能:完成LRC格式歌词的文件读取、解析、结构体创建等,同时在屏幕上将解析出的歌词进行实时显示基本功能实现:1、读取文件,能正确打印到屏幕上。

2、切割字符串,正确存到一个结构体数组中。

3、能正确判断歌曲的歌名与演唱者,并打印出来。

4、实现模拟时钟的功能。

5、按顺序将歌词输出。

6、能滚动显示歌词。

7、能与时间匹配突出显示歌词的颜色。

项目流程:1、首先将歌词文件内容通过fopen全部读到一个数组song_word[][]中,然后将歌词内容保存到数组song_word1[][]。

2、通过函数message_song1()判断歌曲的歌名与演唱者,并打印出前4行的歌曲信息。

3、用函数message_song2()和strtok将同一行的歌词和时间一一对应的进行切割,并分别保存在数组lyric[][]和time[][]中,并通过Sleep(1000)将时间标签转换为以s为单位的模拟时钟。

4、在每一秒内对将时钟的时间与歌词前面的时间相比较,如果相等那么就输出当前时间后面的歌词,实现了歌词与时间的同步显示。

5、歌词的前5行依次输出,当歌词大于5行时,用函数MoveText(22,6,20,5,22,5);实现将歌词整块移动并每次显示5行。

7、GoToXY(22,m)和SetText_Color(2)实现将光标定位在当前行并且显示相应的颜色,使已显示的歌词变为白色,而刚出现的歌词变为相应的颜色。

6、在主程序运行时即可启动千千静听ShellExecute( NULL, NULL, "TTPlayer.exe",".\\LRC\\简单爱.mp3",NULL,1);代码:#include#include#include#include#include#include"console.h"//跳转到光标指定位置void GoToXY(int x,int y){HANDLE h1;COORD pos;pos.X=x;pos.Y=y;h1=GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(h1,pos);}//屏幕整块移动//参数:startx、starty:要移动的块的起始坐标// sizex、sizey:块的大小// destx、desty: 目标坐标,即要移动到得位置void MoveText(int startx, int starty, int sizex, int sizey, int destx, int desty) {SMALL_RECT rc = {startx, starty,startx+sizex, starty+sizey};COORD crDest = {destx, desty};CHAR_INFO chFill;HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_SCREEN_BUFFER_INFO bInfo;GetConsoleScreenBufferInfo(hout, &bInfo);chFill.Attributes = bInfo.wAttributes;chFill.Char.AsciiChar =' ';ScrollConsoleScreenBuffer(hout, &rc, NULL, crDest, &chFill);}/*第一个值为背景色,第二个值为前景色:0 = 黑8 = 灰1 = 蓝9 = 淡蓝2 = 绿 A = 淡绿3 = 湖蓝B = 淡浅绿4 = 红 C = 淡红5 = 紫 D = 淡紫6 = 黄 E = 淡黄7 = 白 F = 亮白*///设置接下来终端显示文本的背景色和文本颜色void SetText_Color(int color){HANDLE hStdout;hStdout = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleTextAttribute(hStdout,color);}void message_song1(char song[4][100])//切割前四行{int i;char delims[]="[:]";char *result=NULL;char *sign[4]={"歌名","歌手","制作","专辑"};for(i=0;i<4;i++){strtok(song[i],delims);result= strtok(NULL,delims);printf("%s %s\n",sign[i],result);}printf("\n\n");}void message_song2(char song1[100][100],char time[100][100] ,char lyric[100][100] )//切割歌词和时间{int i,j,k1=0,k2=0,min=0,tsec=0,gsec=0,b=6;int flag=0,m=5;char delims1[]="[]";char *result1=NULL;for(j=0;song1[j][0]!='\0';j++){result1=strtok(song1[j],delims1);for(i=0;result1!=NULL;i++){if(i%2==0){strcpy(time[k1],result1);result1=strtok(NULL,delims1);k1++;}if(i%2==1){strcpy(lyric[k2],result1);result1=strtok(NULL,delims1);k2++;}}}j=0;for(i=0;;i++){GoToXY(22,5);printf("The time is:0%d:%d%d ",min,tsec,gsec); Sleep(1000);gsec++;if(gsec==10){tsec++;gsec=0;}if(tsec==6){min++;tsec=0;}for(j=0;j<50;j++){if((min==time[j][1]-48)&&(tsec==time[j][3]-48)&&(gsec==time[j][4]-48)){if(m<10) m+=1;else m=10;GoToXY(22,m);SetText_Color(2);printf("%s\t\t\t\t\n",lyric[j-1]);GoToXY(22,m-1);SetText_Color(7);printf("%s\t\t\t\t\n",lyric[j-2]);flag++;if(flag>=5)MoveText(22,6,20,5,22,5);}}}}void main(){FILE*fp;int i=0;char time[100][100]={0};char lyric[100][100]={0};charinfile[1000]={0},song_word[100][100]={0},song_word1[100][100]={0};char *result=NULL;char delims1[]="\n";ShellExecute( NULL, NULL, "TTPlayer.exe","spring.mp3",NULL,1); //执行千千静听if((fp=fopen("spring.lrc","rb"))==NULL){printf("can not open file\n");exit(0);}while(!feof(fp)){infile[i]=fgetc(fp);i++;}infile[i]='\0';fclose(fp);printf("\n");result=strtok(infile,delims1);for(i=0;result!=NULL;i++){strcpy(song_word[i],result);result++;result=strtok(NULL,delims1);}for(i=0;i<44;i++)strcpy(song_word1[i],song_word[i+4]);message_song1(song_word);message_song2(song_word1,time,lyric);}bug调试:刚开始对切割函数strtok含有多个标识符的使用理解不透彻,经过多次改变标识符观察输出结果,也就正确理解了。

lrc使用指南

lrc使用指南

lrc使用指南(实用版)目录1.LRC 文件概述2.LRC 文件的结构3.LRC 文件的编写方法4.LRC 文件的应用场景5.LRC 文件的优缺点正文【LRC 文件概述】LRC(Lyric Repository Configuration)文件是一种用于存储歌词信息的文本文件,通常与音乐文件(如 MP3、WAV 等)配套使用。

LRC 文件的主要功能是为音乐文件提供同步歌词显示,使用户在播放音乐时能够方便地查看和跟随歌词。

【LRC 文件的结构】一个 LRC 文件主要包括以下几个部分:1.文件头:包括文件的唯一标识符、版本号、字符编码等基本信息。

2.歌词块:每个歌词块包含了一行或多行歌词,以及与音乐文件对应的时间戳。

每行歌词由一个或多个单词组成,单词之间用空格分隔。

歌词块的格式为“[行号:时间戳:歌词内容]”,如“[00:00:00] 歌手 - 歌曲名 [00:00:00] 第一句歌词 [00:00:00] 第二句歌词”。

3.标签:LRC 文件支持多种标签,如歌手、专辑、风格等。

标签位于歌词块之前,用中括号 [] 包围,如“[歌手]”、“[专辑]”等。

【LRC 文件的编写方法】编写 LRC 文件可以采用文本编辑器,如 Notepad、Sublime Text 等。

以下是一个简单的 LRC 文件编写步骤:1.新建一个文本文件,并将其命名为“歌曲名.lrc”。

2.在文件中输入文件头信息,如“[lyric-file-name:M3UA:1.0]”。

3.编写歌词块,按照歌词的顺序和时间戳进行排列。

每个歌词块之间用空行分隔。

4.如果需要添加标签,将标签放在对应的歌词块之前。

5.保存文件,并将其与音乐文件放在同一目录下。

【LRC 文件的应用场景】LRC 文件广泛应用于音乐播放器、在线音乐平台等场景。

用户在播放音乐时,可以通过 LRC 文件查看同步歌词,提高音乐体验。

此外,LRC 文件也可以用于歌词共享、歌词制作等领域。

LRC歌词编辑攻略

LRC歌词编辑攻略

LRC歌词编辑攻略一、相关概念:1.LRC歌词格式LRC 歌词是一种包含着“[*:*]”形式的“标签(tag)”的、基于纯文本的歌词专用格式。

最早由郭祥祥先生(Djohan)提出并在其程序中得到应用。

这种歌词文件既可以用来实现卡拉OK功能(需要专门程序),又能以普通的文字处理软件查看、编辑。

当然,实际操作时通常是用专门的LRC歌词编辑软件进行高效编辑的。

以下具体介绍LRC格式中的“标签”。

2.时间标签(Time-tag)形式为“[mm:ss]”或“[mm:ss.fff]”(分钟数:秒数)。

数字须为非负整数,比如“[12:34.5]”是有效的,而“[0x0C:-34.5]”无效。

它可以位于某行歌词中的任意位置。

一行歌词可以包含多个时间标签(比如歌词中的迭句部分)。

根据这些时间标签,用户端程序会按顺序依次高亮显示歌词,从而实现卡拉OK功能。

另外,标签无须排序。

3.标识标签(ID-tags)其格式为“[标识名:值]”。

大小写等价。

以下是预定义的标签。

[ar:艺人名][ti:曲名][al:专辑名][by:编者(指编辑LRC歌词的人)][offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。

这是用于总体调整显示快慢的样例[ar:陈明][ti:《快乐老家》][al:娱乐天地][by:truly][01:02.355][00:00]This line should be sung twice[00:05.7]And this one... once only.4.LYR文件格式LYR 文件是自行定义的一种同步文件格式。

采用相同的文件名称来实现歌词同步,如:同时下载《快乐老家》.mp3 和《快乐老家》.lyr两个文件到MP3播放机中即可实现同步。

LYR文件可使用歌词编辑软件编辑的TLY文件或LRC文件转化而来。

5.LYR 使用注意事项:∙LYR 必须与待同步MP3使用相同的文件名∙LYR 与MP3的文件名称尽量控制在8个字节(即4汉字或者8个英文字符)。

歌词制作(lrc krc)全解

歌词制作(lrc krc)全解
(歌词做完后一定要保存,不然会变成历史)
我到论坛来看了一下,有很多人问krc歌词怎么做呀?还有人问谁有krc歌词的教程和副本吗?也有人说各位大神谁会制作歌词,教教我?
我也真服了你们,所以,我就写了这些来喂你们这些可怜的毛毛虫。人家花了半个晚上睡觉的时间,早上起来脸上都长了两颗圆圆的痘痘,你们要对我负责啊!(开玩笑)
现在我们来看看krc格式的歌词制作方法。
首先,我需要制作歌词的软件来帮助我。
制作软件:酷狗(电脑版)
(其实,我也希望手机能制作krc格式的歌词,因为我是手机控嘛,电脑不方便呀)
酷狗音乐,是大家非常常用的音乐播放器和音乐下载器,也是很多制作爱好者的歌词制作器。
这个制作krc格式的歌词,说来也简单(至少比lrc格式的歌词简单)。前面说到了酷狗音乐,一定是有用的,现在我就教大家用酷狗音乐制作krc格式的歌词。
......不不不是,是你们要对我的痘痘负责啊!(这个不是开玩笑的,我真的长红痘痘了啊,十几天才好了呢)
要是你们会了,不谢我的话,我就将他(不包括她)
碎~~尸~~万~~断~~ 哈哈哈哈
呵呵,吓着了吧?
[03:31.23]我 因為瞭解才分開
[03:35.84]甜蜜不能夠倒帶 怎麼重來
[03:41.38]是我辜負了妳的存在
[03:49.99]
[03:50.00]最新最好聽的傷感流行音樂與你共分享
如果你制作完成了,可以自己拿来先用一下,自己觉得还可以。你就可以通过USB或U盘上传到电脑,利用电脑的网络上传到各个通用的音乐播放器,别人将可以从你上传过的那个音乐播放器下载你上传的歌词。
[02:47.81]心疼妳為我等待 選擇離開
[02:53.47]是我一個人的悲哀
[02:57.92]我 因為瞭解才分開

LRC歌词格式简介

LRC歌词格式简介

LRC歌词格式简介
扩展名lrc的文件,是MP3播放器唯一能够识别的歌词文件。

在MP3数码播放器或千千静听中可以同步显示歌词。

lrc歌词下载到本地电脑后用“记事本”打开。

lrc歌词文本中含有两类标签:
一、标识标签其格式为“[标识名:值]”
[ar:歌手名]-artist艺术家、演唱者
[ti:歌曲名]-title题目,标题,曲目
[al:专辑名]-album唱片集、专辑
[by:编辑者]-(介词)制作者、编辑人员(一般指lrc歌词的制作人) [offset:时间补偿值]-其单位是毫秒,正值表示延迟,负值表示提前。

用于整体调整歌词显示慢(快)于音乐播放。

二时间标签,格式为“[mm:ss]”或“[mm:ss.fff]”
[mm:ss]-分钟数:秒数
[mm:ss.fff]-分钟数:秒数.毫秒数
时间标签位于某行歌词中的句首部分,一行歌词可以包含多个时间标签(比如歌词中的重迭句部分)。

三、lrc歌词实例
[ar:蔡琴]
[ti:绿岛小夜曲]
[al:蔡琴不了情2007经典歌曲香港演唱会]
[by:william王子]
[02:53.84][00:07.74]蔡琴不了情2007经典歌曲香港演唱会
[01:53.72][00:01.28]蔡琴-绿岛小夜曲
[01:58.23][00:12.85]
[00:14.66]这绿岛像一只船
[00:20.88]在月夜里摇啊摇
......。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Lrc歌词文件格式说明扩展名为Lrc格式的文件,是MP3播放器唯一能够识别的歌词文件,在MP3数码播放器或千千静听中可以区同步显示歌词。

它是一种包含着“[*:*]”形式的“标签(tag)”的、基于纯文本的歌词专用格式。

最早由郭祥祥先生(Djohan)提出并在其程序中得到应用。

这种歌词文件既可以用来实现卡拉OK 功能(需要专门程序),又能以普通的文字处理软件查看、编辑。

当然,实际操作时通常是用专门的LRC歌词编辑软件进行高效编辑的。

Lrc歌词文本中含有两类标签:一是标识标签,其格式为“[标识名:值]”主要包含以下预定义的标签:[ar:歌手名]、[ti:歌曲名]、[al:专辑名]、[by:编辑者(指lrc歌词的制作人)]、[offset:时间补偿值] (其单位是毫秒,正值表示整体提前,负值相反。

这是用于总体调整显示快慢的,但多数的MP3可能不会支持这种标签)。

二是时间标签,形式为“[mm:ss]”或“[mm:ss.fff]”(分钟数:秒数:毫秒数),时间标签需位于某行歌词中的句首部分,一行歌词可以包含多个时间标签(比如歌词中的迭句部分)。

当歌曲播放到达某一时间点时,MP3就会寻找对应的时间标签并显示标签后面的歌词文本,这样就完成了“歌词同步”的功能。

■时间标签(Time-tag)形式为"[mm:ss]"或"[mm:ss.fff]"(分钟数:秒数)。

数字须为非负整数,比如"[12:34.5]"是有效的,而"[0x0C:-34.5]"无效。

它可以位于某行歌词中的任意位置。

一行歌词可以包含多个时间标签(比如歌词中的迭句部分)。

根据这些时间标签,用户端程序会按顺序依次高亮显示歌词,从而实现卡拉OK 功能。

另外,标签无须排序。

■标识标签(ID-tags)其格式为"[标识名:值]"。

大小写等价。

以下是预定义的标签。

[ar:艺人名][ti:曲名][al:专辑名][by:编者(指编辑LRC歌词的人)][offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。

这是用于总体调整显示快慢的。

分钟:秒.毫秒]歌词用记事本按照上述格式写好后,将拓展名改为lrc即可做出该文件.LRC歌词是一种通过编辑器把歌词按歌曲歌词出现的时间编辑成一个文件,在播放歌曲时同步依次显示出来的一种歌词文件。

把歌曲和LRC歌词命为相同的文件名放在同一目录下,用带显示歌词功能的播放器播放歌曲时歌词就可以同步显示显示,方便查看和学歌。

(供程序员阅读参考)以下列出了开发支持LRC格式的软件时应遵守的一些标准。

无论是否在行首,行内凡具有“[*:*]”形式的都应认为是标签。

(注意:其中的冒号并非全角字符“:”)凡是标签都不应显示。

凡是标签,且被冒号分隔的两部分都为非负数,则应认为是时间标签。

因此,对于非标准形式(非“[mm:ss]”)的时间标签也应能识别(如“[0:0]”)。

凡是标签,且非时间标签的,应认为是标识标签。

标识名中大小写等价。

·为了向后兼容,应对未定义的新标签作忽略处理。

另应对注释标签([:])后的同一行内容作忽略处理。

应允许一行中存在多个标签,并能正确处理。

应能正确处理未排序的标签。

以下附上C++实现代码,支持ANSI和Unicode格式,超强纠错。

-----------------------------------------------------Lyric.h----------------------------------------------------- typedef struct{DWORD dwStartTime; // The unit is millisecondCString csLyric;} LineLyric, *PLineLyric;class CLyric{public:CLyric();~CLyric();CString m_csArtist;CString m_csTitle;CString m_csAlbum;CString m_csBy;LONG m_lOffset; // Lead time of the lyricCString m_csKey;public:BOOL Load(LPCWSTR szFileName, LONG lUserOffset = 0); // If lUserOffset > 0: ahead; if < 0: put offvoid UnLoad();BOOL GetItemByID(DWORD dwItemID, LineLyric &lyric); // Get the lyric data by dwItemIDDWORD GetTimeByID(DWORD dwItemID); // Get the lyric's start time by dwItemIDLONG GetItemIDByTime(DWORD dwTime, DWORD dwStartLineID = 0);// Search the line index by time(in milliseconds) begin at nStartLineIDDWORD GetTotalNum() { return m_dwLineNum; }BOOL IsValid() { return m_bValid; }private:BOOL m_bValid;CArray<LineLyric> m_LyricList;DWORD m_dwLineNum;BOOL m_bAnalyseTag;LONG m_lUserOffset;__forceinline BOOL ReadALine(FILE *fp, CHAR *buff, INT bufflen, INT &readlen);__forceinline void AnalyseLine(CHAR *szLine, INT nLength);__forceinline BOOL CheckTag(CHAR *szText);__forceinline LONG GetStartTime(CHAR *szTime);__forceinline BOOL ReadALineW(FILE *fp, WCHAR *buff, INT bufflen, INT&readlen);__forceinline void AnalyseLineW(WCHAR *szLine, INT nLength);__forceinline BOOL CheckTagW(WCHAR *szText);__forceinline LONG GetStartTimeW(WCHAR *szTime);__forceinline DWORD Find(DWORD dwTime); // return the line ID matched the time};-----------------------------------------------------Lyric.cpp----------------------------------------------------- #include"Lyric.h"#define MAX_LYRIC_LINE_LEN512#define MAX_LYRIC_LEN128#define MAX_LYRIC_LINE_NUM5000#define MAX_LYRIC_TIME_LEN30CLyric::CLyric(){m_bValid = FALSE;m_lUserOffset = 0;}CLyric::~CLyric(){}BOOL CLyric::Load(LPCWSTR szFileName, LONG lUserOffset){UnLoad();FILE*fp;INT readlen;m_lUserOffset = lUserOffset;fp = _tfopen(szFileName, _T("r"));if (fp == NULL){return FALSE;}WORD wUnicode_tag = 0;DWORD dwReadLen = 0;try{dwReadLen = fread((void *)&wUnicode_tag, sizeof(WCHAR), 1, fp);}catch (...){printf("CLyric::Load: fread Exception: %s!\r\n", szFileName);fclose(fp);return FALSE;}if (dwReadLen != 1){printf("CLyric::Load: read file %s failed!\r\n", szFileName);fclose(fp);return FALSE;}if (wUnicode_tag == 0xfeff) // unicode file{fclose(fp);fp = _tfopen(szFileName, _T("rb"));if (fp == NULL){return FALSE;}fseek(fp, sizeof(WCHAR), SEEK_SET);WCHAR wstr[MAX_LYRIC_LINE_LEN];do{if (!ReadALineW(fp, wstr, MAX_LYRIC_LINE_LEN, readlen)) break;if (readlen <= 0)continue;AnalyseLineW(wstr, readlen);}while (m_dwLineNum < MAX_LYRIC_LINE_NUM);}else{fseek(fp, 0L, SEEK_SET);CHAR str[MAX_LYRIC_LINE_LEN];do{if (!ReadALine(fp, str, MAX_LYRIC_LINE_LEN, readlen)) break;if (readlen <= 0)continue;AnalyseLine(str, readlen);}while (m_dwLineNum < MAX_LYRIC_LINE_NUM);}fclose(fp);m_bValid = TRUE;return TRUE;}void CLyric::UnLoad(){m_bValid = FALSE;m_csArtist.Empty();m_csTitle.Empty();m_csAlbum.Empty();m_csBy.Empty();m_lOffset = 0;m_csKey.Empty();m_LyricList.RemoveAll();m_dwLineNum = 0;m_bAnalyseTag = TRUE;m_lUserOffset = 0;}BOOL CLyric::GetItemByID(DWORD dwItemID, LineLyric &lyric) {if (dwItemID < (DWORD)m_LyricList.GetSize()){lyric = m_LyricList[dwItemID];return TRUE;}else{return FALSE;}}DWORD CLyric::GetTimeByID(DWORD dwItemID){DWORD dwTotalItem = (DWORD)m_LyricList.GetSize();if (dwTotalItem == 0)return 0;if (dwItemID < dwTotalItem){return m_LyricList[dwItemID].dwStartTime;}else{return m_LyricList[dwTotalItem-1].dwStartTime;}}LONG CLyric::GetItemIDByTime(DWORD dwTime, DWORD dwStartLineID){DWORD dwLineNum = m_LyricList.GetSize();if (dwTime <= 0 || dwLineNum == 0)return -1;for (DWORD i = dwStartLineID; i < dwLineNum; i++){if (m_LyricList[i].dwStartTime > dwTime){return i-1;}}return dwLineNum-1;}__forceinline BOOL CLyric::ReadALine(FILE *fp, CHAR *buff, INT bufflen, INT &readlen) {readlen = 0;if (feof(fp)) /* End or file, return -1 */return FALSE;int ch;do{ch = fgetc(fp);if (ch == '\r' || ch == '\n' || ch == EOF)break;if (readlen >= bufflen-1)continue; // Use continue for ignoring the remanent character of the line*buff = ch;buff++;readlen++;} while (!feof(fp));if (readlen > 0 && *(buff-1) == '\r')*(buff-1) = 0;else*buff = 0;return TRUE;}__forceinline void CLyric::AnalyseLine(CHAR *szLine, INT nLength){WCHAR szLyric[MAX_LYRIC_LEN];CHAR szTime[MAX_LYRIC_TIME_LEN];CHAR *szTimeBegin = NULL; // Begin at the next char of '['CHAR *szTimeEnd = NULL; // End at ']'CHAR *szNextTimeBegin = strchr(szLine, '[');if (szNextTimeBegin == NULL)return;szNextTimeBegin++;CHAR *szNextTimeEnd = strchr(szNextTimeBegin, ']');if (szNextTimeEnd == NULL)return;LineLyric lyric;LONG lStartTime;while (szNextTimeBegin != NULL && szNextTimeEnd != NULL)// Support multi time/tag in one line{szTimeBegin = szNextTimeBegin;szTimeEnd = szNextTimeEnd;szNextTimeBegin = strchr(szTimeEnd, '[');if (szNextTimeBegin != NULL){szNextTimeEnd = strchr(szNextTimeBegin, ']');if (szNextTimeEnd != NULL){// Find a next Time in the same line*szNextTimeBegin = 0; // Set '[' as the end flag of the stringszNextTimeBegin++;}}if (szTimeEnd > szTimeBegin+1){INT nTimeLen = min(MAX_LYRIC_TIME_LEN-1,szTimeEnd-szTimeBegin);strncpy(szTime, szTimeBegin, nTimeLen);szTime[nTimeLen] = 0;if (m_bAnalyseTag){if (CheckTag(szTime))continue;}lStartTime = GetStartTime(szTime);if (lStartTime < 0)continue;lyric.dwStartTime = max((lStartTime - m_lOffset - m_lUserOffset), 0);MultiByteToWideChar(Core_GetDefCodePage(), 0, szTimeEnd+1, -1, szLyric, MAX_LYRIC_LEN-1);lyric.csLyric = szLyric;if (lyric.csLyric.IsEmpty() && szTimeEnd+2 != 0){// To get MultiTimeLyricCHAR *szMultiTimeLyric = szTimeEnd+2;szMultiTimeLyric = strchr(szMultiTimeLyric, ']');if (szMultiTimeLyric != NULL){CHAR *szMultiTimeLyricBegin = szMultiTimeLyric+1;while (1){szMultiTimeLyric = strchr(szMultiTimeLyricBegin, '[');if (szMultiTimeLyric == NULL) // Find MultiTimeLyric, it's at the end of the line{MultiByteToWideChar(Core_GetDefCodePage(), 0, szMultiTimeLyricBegin, -1, szLyric, MAX_LYRIC_LEN-1);lyric.csLyric = szLyric;break;}else if (szMultiTimeLyric-szMultiTimeLyricBegin > 0) // Find MultiTimeLyric, it's at the middle of the line{MultiByteToWideChar(Core_GetDefCodePage(), 0, szMultiTimeLyricBegin, -1, szLyric, MAX_LYRIC_LEN-1);lyric.csLyric = szLyric;lyric.csLyric = lyric.csLyric.Mid(0,szMultiTimeLyric-szMultiTimeLyricBegin);break;}szMultiTimeLyric = strchr(szMultiTimeLyric+1, ']');if (szMultiTimeLyric == NULL) // Invalid format, has '[', but no ']'{MultiByteToWideChar(Core_GetDefCodePage(), 0, szMultiTimeLyricBegin, -1, szLyric, MAX_LYRIC_LEN-1);lyric.csLyric = szLyric;break;}szMultiTimeLyricBegin = szMultiTimeLyric+1;}}}if (m_dwLineNum > 0 || !lyric.csLyric.IsEmpty()) // First line must no be empty{if (m_dwLineNum > 0 && lyric.dwStartTime >=m_LyricList.GetAt(m_dwLineNum-1).dwStartTime)m_LyricList.Add(lyric);elsem_LyricList.InsertAt(Find(lyric.dwStartTime), lyric); // Auto sort by timem_dwLineNum++;}m_bAnalyseTag = FALSE; // Consider Tag info is at the beginning of the file}}}BOOL CLyric::CheckTag(CHAR *szText){if (_strnicmp(szText, "ar", 2) == 0){m_csArtist = szText+3;return TRUE;}if (_strnicmp(szText, "ti", 2) == 0){m_csTitle = szText+3;return TRUE;}if (_strnicmp(szText, "al", 2) == 0){m_csAlbum = szText+3;return TRUE;}if (_strnicmp(szText, "by", 2) == 0){m_csBy = szText+3;return TRUE;}if (_strnicmp(szText, "offset", 6) == 0){m_lOffset = atol(szText+7);return TRUE;}if (_strnicmp(szText, "key", 3) == 0){m_csKey = szText+4;return TRUE;}return FALSE;}__forceinline LONG CLyric::GetStartTime(CHAR *szTime){INT nMinute = 0;INT nSecond = 0;INT nMilliSecond = 0;CHAR *szMinuteEnd = strrchr(szTime, ':');if (szMinuteEnd == NULL){nMinute = 0;szMinuteEnd = szTime;}else{CHAR szMinute[10];INT nMinuteLen = min(9, szMinuteEnd-szTime);strncpy(szMinute, szTime, nMinuteLen);if (nMinuteLen > 0 && (szMinute[0] < '0' || szMinute[0] > '9')) return -1;nMinute = atoi(szMinute);if (nMinute < 0)nMinute = 0;szMinuteEnd++;}CHAR *szSecondEnd = strrchr(szMinuteEnd, '.');if (szSecondEnd == NULL){nSecond = atoi(szMinuteEnd);if (nSecond < 0)nSecond = 0;}else{CHAR szSecond[10];INT nSecondLen = min(9, szSecondEnd-szMinuteEnd);strncpy(szSecond, szMinuteEnd, nSecondLen);if (nSecondLen > 0 && (szSecond[0] < '0' || szSecond[0] > '9'))return -1;nSecond = atoi(szSecond);if (nSecond < 0)nSecond = 0;szSecondEnd++;INT nMilliSecondLen = strlen(szSecondEnd);if (nMilliSecondLen == 1)nMilliSecond = atoi(szSecondEnd) * 100;else if (nMilliSecondLen == 2)nMilliSecond = atoi(szSecondEnd) * 10;else if (nMilliSecondLen == 3)nMilliSecond = atoi(szSecondEnd);if (nMilliSecond < 0)nMilliSecond = 0;}return nMinute*60000+nSecond*1000+nMilliSecond;}__forceinline BOOL CLyric::ReadALineW(FILE *fp, WCHAR *buff, INT bufflen, INT &readlen){WCHAR *pReturn = fgetws(buff, bufflen, fp);if (pReturn == NULL)return FALSE;readlen = lstrlen(buff);if (readlen > 0 && buff[readlen-1] == _T('\n')){buff[readlen-1] = 0;readlen--;}if (readlen > 0 && buff[readlen-1] == _T('\r')){buff[readlen-1] = 0;readlen--;}return TRUE;}__forceinline void CLyric::AnalyseLineW(WCHAR *szLine, INT nLength){WCHAR szTime[MAX_LYRIC_TIME_LEN];WCHAR *szTimeBegin = NULL; // Begin at the next char of '['WCHAR *szTimeEnd = NULL; // End at ']'WCHAR *szNextTimeBegin = wcschr(szLine, _T('['));if (szNextTimeBegin == NULL)return;szNextTimeBegin++;WCHAR *szNextTimeEnd = wcschr(szNextTimeBegin, _T(']'));if (szNextTimeEnd == NULL)return;LineLyric lyric;LONG lStartTime;while (szNextTimeBegin != NULL && szNextTimeEnd != NULL)// Support multi time/tag in one line{szTimeBegin = szNextTimeBegin;szTimeEnd = szNextTimeEnd;szNextTimeBegin = wcschr(szTimeEnd, _T('['));if (szNextTimeBegin != NULL){szNextTimeEnd = wcschr(szNextTimeBegin, _T(']'));if (szNextTimeEnd != NULL){// Find a next Time in the same line*szNextTimeBegin = 0; // Set '[' as the end flag of the stringszNextTimeBegin++;}}if (szTimeEnd > szTimeBegin+1){INT nTimeLen = min(MAX_LYRIC_TIME_LEN-1,szTimeEnd-szTimeBegin);wcsncpy(szTime, szTimeBegin, nTimeLen);szTime[nTimeLen] = 0;if (m_bAnalyseTag){if (CheckTagW(szTime))continue;}lStartTime = GetStartTimeW(szTime);if (lStartTime < 0)continue;lyric.dwStartTime = max((lStartTime - m_lOffset - m_lUserOffset), 0);lyric.csLyric = szTimeEnd+1;if (lyric.csLyric.IsEmpty() && szTimeEnd+2 != 0){// To get MultiTimeLyricWCHAR *szMultiTimeLyric = szTimeEnd+2;szMultiTimeLyric = wcschr(szMultiTimeLyric, _T(']'));if (szMultiTimeLyric != NULL){WCHAR *szMultiTimeLyricBegin = szMultiTimeLyric+1;while (1){szMultiTimeLyric = wcschr(szMultiTimeLyricBegin, _T('['));if (szMultiTimeLyric == NULL) // Find MultiTimeLyric, it's at the end of the line{lyric.csLyric = szMultiTimeLyricBegin;break;}else if (szMultiTimeLyric-szMultiTimeLyricBegin > 0) // Find MultiTimeLyric, it's at the middle of the line{lyric.csLyric = szMultiTimeLyricBegin;lyric.csLyric = lyric.csLyric.Mid(0,szMultiTimeLyric-szMultiTimeLyricBegin);break;}szMultiTimeLyric = wcschr(szMultiTimeLyric+1, _T(']'));if (szMultiTimeLyric == NULL) // Invalid format, has '[', but no ']'{lyric.csLyric = szMultiTimeLyricBegin;break;}szMultiTimeLyricBegin = szMultiTimeLyric+1;}}}if (m_dwLineNum > 0 || !lyric.csLyric.IsEmpty()) // First line must no be empty{if (m_dwLineNum > 0 && lyric.dwStartTime >=m_LyricList.GetAt(m_dwLineNum-1).dwStartTime)m_LyricList.Add(lyric);elsem_LyricList.InsertAt(Find(lyric.dwStartTime), lyric); // Auto sort by timem_dwLineNum++;}m_bAnalyseTag = FALSE; // Consider Tag info is at the beginning of the file}}}BOOL CLyric::CheckTagW(WCHAR *szText){if (_wcsnicmp(szText, _T("ar"), 2) == 0){m_csArtist = szText+3;return TRUE;}if (_wcsnicmp(szText, _T("ti"), 2) == 0){m_csTitle = szText+3;return TRUE;}if (_wcsnicmp(szText, _T("al"), 2) == 0){m_csAlbum = szText+3;return TRUE;}if (_wcsnicmp(szText, _T("by"), 2) == 0){m_csBy = szText+3;return TRUE;}if (_wcsnicmp(szText, _T("offset"), 6) == 0){m_lOffset = _wtol(szText+7);return TRUE;}if (_wcsnicmp(szText, _T("key"), 3) == 0){m_csKey = szText+4;return TRUE;}return FALSE;}__forceinline LONG CLyric::GetStartTimeW(WCHAR *szTime){INT nMinute = 0;INT nSecond = 0;INT nMilliSecond = 0;WCHAR *szMinuteEnd = wcsrchr(szTime, ':');if (szMinuteEnd == NULL){nMinute = 0;szMinuteEnd = szTime;}else{WCHAR szMinute[10];INT nMinuteLen = min(9, szMinuteEnd-szTime);wcsncpy(szMinute, szTime, nMinuteLen);if (nMinuteLen > 0 && (szMinute[0] < _T('0') || szMinute[0] > _T('9'))) return -1;nMinute = _wtoi(szMinute);if (nMinute < 0)nMinute = 0;szMinuteEnd++;}WCHAR *szSecondEnd = wcsrchr(szMinuteEnd, _T('.'));if (szSecondEnd == NULL){nSecond = _wtoi(szMinuteEnd);if (nSecond < 0)nSecond = 0;}else{WCHAR szSecond[10];INT nSecondLen = min(9, szSecondEnd-szMinuteEnd);wcsncpy(szSecond, szMinuteEnd, nSecondLen);if (nSecondLen > 0 && (szSecond[0] < _T('0') || szSecond[0] > _T('9'))) return -1;nSecond = _wtoi(szSecond);if (nSecond < 0)nSecond = 0;szSecondEnd++;INT nMilliSecondLen = lstrlen(szSecondEnd);if (nMilliSecondLen == 1)nMilliSecond = _wtoi(szSecondEnd) * 100;else if (nMilliSecondLen == 2)nMilliSecond = _wtoi(szSecondEnd) * 10;else if (nMilliSecondLen == 3)nMilliSecond = _wtoi(szSecondEnd);if (nMilliSecond < 0)nMilliSecond = 0;}return nMinute*60000+nSecond*1000+nMilliSecond;}__forceinline DWORD CLyric::Find(DWORD dwTime){DWORD i = 0;for (i = 0; i < (DWORD)m_LyricList.GetSize(); i++){if (m_LyricList[i].dwStartTime > dwTime){break;}}return i; }。

相关文档
最新文档