C語言 解析lrc歌詞檔案
阿新 • • 發佈:2018-12-31
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會變亂碼)
同步播放的效果
按條匯出的效果