C语言 解析lrc歌词文件

来源:互联网 发布:淘宝企业店铺年费 编辑:程序博客网 时间:2024/05/16 09:25

LRC文件如上图所示。

格式为 [mm:ss.ms]歌词

但是也有单行多个时间轴的情况,即 [mm:ss.ms][mm:ss.ms]歌词

对于解析来说,比较困难的正是单行多个时间轴的情况。我的解决方法是:

  1. 对单独一行检索]后没有[的位置,即该行最后一个[]框,将指针指向下一元素,就是这行歌词的第一个字。
  2. 编写函数将最后一个[]里面mm:ss.ms格式的时间轴化为long的ms。
  3. 删除最后一个[]及里面的内容,将后面的歌词并上来重新构成该行(整个算法的亮点就在这里)
  4. 重新检索该行最右边的[],直到没有[]为止
  5. 开始检索下一行,重复(1)的操作,直到下一行也没有[]为止。
  6. 整个歌词用链表储存,获取所有时间轴和歌词之后,按时间轴对链表进行排序。

我还只是初学者,代码可能不太规范,请见谅。

//.lrc Analysis//Copyright (c) 2016 Equim.  All rights reserved.#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>#include<windows.h>#include<mmsystem.h>#pragma comment(lib, "WINMM.LIB")#define LEN sizeof(lyric)typedef struct _lyric{long timeLine;char verse[256];struct _lyric* next;}lyric;lyric* Head=NULL;long ms(char origin[9])//将mm:ss.ms化为毫秒 {long result=0;result=atoi(origin)*60*1000+atoi(origin+3)*1000+atoi(origin+6)*10;return result;}void OutputLyrics()/*   输出模块   */ {lyric *p;for(p=Head;p!=NULL;p=p->next)printf("%ld >> %s\n",p->timeLine,p->verse);}void Play()/*   播放模块   */ {clock_t e,start;lyric *p;system("mode con cols=150 lines=3");mciSendString("play D:\\C\\Jukebox\\Release\\audio\\ebbandflow.mp3",NULL, 0, 0);mciSendString("setaudio D:\\C\\Jukebox\\Release\\audio\\ebbandflow.mp3 volume to 150",NULL, 0, 0);start=clock();while(1){Sleep(200);e=clock()-start;printf("%ld\t",e);p=Head;while(!(p->next==NULL||(p->next)->timeLine>=e))//检索当前应显示的歌词,要么是最后一句,要么下一句的时间轴在当前时间之后 p=p->next;printf("%ld >> %-130.130s\r",p->timeLine,p->verse);}}int main(){FILE *lrc=fopen("D:\\C\\Jukebox\\Release\\audio\\ebbandflow.lrc","r");char linePointer[256];int i;lyric *p,*q,temp,*tempMin;/*让linePointer指向第一个歌词串*/do{fgets(linePointer,256,lrc);}while(!(linePointer[2]>='0'&&linePointer[2]<='9'));/*对每一行歌词进行操作*/ do{if(linePointer[strlen(linePointer)-1]=='\n')linePointer[strlen(linePointer)-1]='\0';//删除末尾的回车 do{i=0;do{i++;}while(!(linePointer[i-1]==']'&&linePointer[i]!='['));//让i指向最右边的[]右侧]的右边,即指向该行歌词的第一个字 p=(lyric*)malloc(LEN);p->timeLine=ms(linePointer+i-9);strcpy(p->verse,linePointer+i);if(Head==NULL)Head=p;elseq->next=p;q=p;linePointer[i-10]='\0';//删除最后的括号strcat(linePointer,q->verse);//巧妙地接回去便于继续解析 }while(linePointer[0]=='[');//解析到没有括号为止 }while(!(fgets(linePointer,256,lrc)==NULL||linePointer[0]!='['));//到最后一行 q->next=NULL;fclose(lrc);/*按时间轴排序*/ for(p=Head;p!=NULL;p=p->next)//比较笨拙的选择排序,按时间轴从小到大 {tempMin=p;for(q=p->next;q!=NULL;q=q->next)if(tempMin->timeLine>q->timeLine)//找最小的 tempMin=q;//寻找比最小还要小的,用临时指针标记 if(tempMin!=p)//如果确实找到了更小的 {temp=*tempMin;*tempMin=*p;*p=temp;temp.next=tempMin->next;tempMin->next=p->next;p->next=temp.next;}}/*播放测试或打印到屏幕*/ Play();//OutputLyrics();return 0;}


在我这边的环境下目前仅支持ANSI编码的lrc文件(Unicode和UTF-8会变乱码)


同步播放的效果


按条导出的效果

1 0
原创粉丝点击