1. 程式人生 > >C語言 解析lrc歌詞檔案

C語言 解析lrc歌詞檔案

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;
			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會變亂碼)

同步播放的效果

按條匯出的效果