您的位置:首页 > 编程语言 > C语言/C++

C语言 解析lrc歌词文件

2016-01-06 15:13 609 查看


LRC文件如上图所示。

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

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

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

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

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

// .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;
else
q->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会变乱码)



同步播放的效果



按条导出的效果
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息