C语言 解析lrc歌词文件
2016-01-06 15:13
465 查看
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会变乱码)
同步播放的效果
按条导出的效果
相关文章推荐
- 如何组织构建多文件 C 语言程序(二)
- 如何写好 C main 函数
- Lua和C语言的交互详解
- 完美解决MSSQL"以前的某个程序安装已在安装计算机上创建挂起的文件操作"
- 关于C语言中参数的传值问题
- 简要对比C语言中三个用于退出进程的函数
- 深入C++中API的问题详解
- 基于C语言string函数的详解
- C语言中fchdir()函数和rewinddir()函数的使用详解
- C语言内存对齐实例详解
- C语言编程中统计输入的行数以及单词个数的方法
- C语言自动生成enum值和名字映射代码
- 使用C语言判断英文字符大小写的方法
- c语言实现的带通配符匹配算法
- C语言实现顺序表基本操作汇总
- C语言中计算正弦的相关函数总结
- 使用C语言详解霍夫曼树数据结构
- 探讨C语言的那些小秘密之断言
- C语言实现BMP转换JPG的方法
- 深入探讨C语言中局部变量与全局变量在内存中的存放位置