HTML5音樂播放器同步顯示歌詞


演示地址:KKBlog在線音樂

歌詞文件的格式

實現之前,當然得了解一下歌詞文件的格式了。常規歌詞文件的格式基本是一句一行,每行由兩部分組成,前面是中括號括起來的時間軸,后面緊跟歌詞,像下面這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[ti:記蘭生襄鈴]
[ar:HITA]
[al:在線熱搜(華語)系列30]
[offset:0]
[00:05.91]記蘭生襄鈴
[00:07.54]填詞:三日月
[00:09.41]原曲:夏川里美
[00:11.48]演唱:HITA 肉肉
[00:13.35]后期:HITA
[00:27.16]青石路 青石橋 書生哼着江南調
[00:33.60]誰家女兒顏色嬌 穿花撲蝶尚年少
[00:40.17]金鈴響 金鈴搖 黃衣少女拍手笑
[00:46.66]呆瓜呆瓜瞧一瞧 天邊大鷹正飛高
[00:53.35]都說當時年紀小 無憂無慮樂逍遙
[00:59.73]芳梅林邊花盛放 珍珠灘旁看江潮
[01:06.60]江都城外茶水香 再嘗一口桂花糕
[01:12.85]清風吹入夢一遭 曾有仙人上九霄

這樣挺有規律的,用正則可以很方便地將時間與歌詞提取分離。

但凡事得多個心眼啊。事后發生的事情證明這句話有多正確。我在整理歌詞時還發現了另外一種形式,像下面這樣:

1
2
3
4
5
6
7
8
9
10
11
[00:02.08]偏愛
[00:04.94]作詞:葛大為 作曲:陳偉
[00:06.35]演唱:張芸京
[00:08.25]
[00:14.37][01:36.77]把昨天都作廢 
[00:18.21][01:40.35]現在你在我眼前
[00:21.94][01:44.15]我想愛 請給我機會
[00:28.38][01:50.35]如果我錯了也承擔 
[00:32.35][01:54.54]認定你就是答案
[00:36.96][01:59.22]我不怕誰嘲笑我極端
[00:40.60][02:02.80]

這種形式的歌詞把歌詞內容相同但時間不同的部分合並,節省了篇幅。

所以,現在知道的歌詞其實有兩種寫法了,不過都還算規律,用正則可以搞定,只是對於第二種,處理時得將時間再次分割。

具體思路

  1. 首先將LRC文件讀取為文本

  2. 用String.prototype.split('\n');將整個文本以換行符為單位分隔成一行一行的文本,保存到一個數組中

  3. 然后將開頭部分不屬於歌詞的文本去掉,得到只有時間與歌詞的干凈文件

  4. 對於每一行,匹配出時間與文字,分別存入數組[time,text],然后將每行得到的這樣的數組存入一個大的數組[[time,text],[time,text]…]

  5. 利用Audio標簽的ontimeupdate事件,不斷比較當然播放時間audio.currentTime與數組中每個元素中時間,如果當前時間大於某個歌詞中的時間,則顯示該歌詞

文件讀取

在具體處理歌詞前,需要解決一個問題就是如何把歌詞文件讀取到代碼中。對於文件讀取,JavaScript中可以用FileReader,但它需要手動選擇文件,也就是你得在頁面放一個file類型的input或者實現文件拖拽操作,顯示不可能讓用戶聽歌的時候自己去找歌詞然后上傳,多麻煩。但JavaScript是沒有辦法操作本地文件的能力的,那就只能通過XMLHttpRequest(Ajax)發起一個到服務器的請求來獲得文件了,這樣一來,我們的程序就必需得運程在服務器上面。所以當你從GitHub下載了本文的源碼后是無法直接運行的,請掛到本地服務器上觀看效果。

下面展示了如何發起一個Ajax請求來獲得歌詞文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function  getLyric(url) {
     //建立一個XMLHttpRequest請求
     var  request =  new  XMLHttpRequest();
     //配置, url為歌詞地址,比如:'./content/songs/foo.lrc'
     request.open( 'GET' , url,  true );
     //因為我們需要的歌詞是純文本形式的,所以設置返回類型為文本
     request.responseType =  'text' ;
     //一旦請求成功,但得到了想要的歌詞了
     request.onload =  function () {
         //這里獲得歌詞文件
         var  lyric = request.response;
     };
     //向服務器發送請求
     request.send();}

 通過上面的代碼就可以LRC文件讀取成文本,然后就可以進行下一步處理了。

提取分離

因為時間我歌詞的分隔是很有規律的,先通過\n將所有文字分隔成一行行存入數組,然后根據文章開始分析的思路一步一步提取分離。為此寫一個解析歌詞的函數。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function  parseLyric(text) {
     //將文本分隔成一行一行,存入數組
     var  lines = text.split( '\n' ),
         //用於匹配時間的正則表達式,匹配的結果類似[xx:xx.xx]
         pattern = /\[\d{2}:\d{2}.\d{2}\]/g,
         //保存最終結果的數組
         result = [];
     //去掉不含時間的行
     while  (!pattern.test(lines[0])) {
         lines = lines.slice(1);
     };
     //上面用'\n'生成生成數組時,結果中最后一個為空元素,這里將去掉
     lines[lines.length - 1].length === 0 && lines.pop();
     lines.forEach( function (v  /*數組元素值*/  , i  /*元素索引*/  , a  /*數組本身*/  ) {
         //提取出時間[xx:xx.xx]
         var  time = v.match(pattern),
             //提取歌詞
             value = v.replace(pattern,  '' );
         //因為一行里面可能有多個時間,所以time有可能是[xx:xx.xx][xx:xx.xx][xx:xx.xx]的形式,需要進一步分隔
         time.forEach( function (v1, i1, a1) {
             //去掉時間里的中括號得到xx:xx.xx
             var  t = v1.slice(1, -1).split( ':' );
             //將結果壓入最終數組
             result.push([parseInt(t[0], 10) * 60 + parseFloat(t[1]), value]);
         });
     });
     //最后將結果數組中的元素按時間大小排序,以便保存之后正常顯示歌詞
     result.sort( function (a, b) {
         return  a[0] - b[0];
     });
     return  result;}

這一步,我們便得到 了一個總的數組,它的元素是一些小的數組,這些小數組包含兩個元素,一個是時間,並且這個時間已經由分:秒的形式轉化為了秒,一個是時間對應的歌詞[['秒數','歌詞'], ['秒數','歌詞']…]。

歌詞同步

接下來就是先把全部歌詞顯示到頁面,進行滾動式顯示,或者也可以不全部顯示,像電影字幕一樣,唱一句顯示一句。

下面看如何同步。當歌曲播放時,監聽audio標簽的ontimeupdate事件,即時更新顯示歌詞到頁面即可。

1
2
3
4
5
6
7
8
9
10
//獲取頁面上的audio標簽var audio = document.getElementsByTagName('audio'),
     //顯示歌詞的元素
     lyricContainer = document.getElementById( 'lyricContainer' ); //監聽ontimeupdate事件audio.ontimeupdate = function(e) {
     //遍歷所有歌詞,看哪句歌詞的時間與當然時間吻合
     for  ( var  i = 0, l = lyric.length; i < l; i++) {
         if  ( this .currentTime  /*當前播放的時間*/  > lyric[i][0]) {
             //顯示到頁面
             lyricContainer.textContent = that.lyric[i][1];
         };
     };};

 

上面介紹的方法同樣適用於video標簽在播放視頻時同步字幕,只是用於匹配的正則表達式需要更改,因為字幕文件的格式較歌詞又不同了。同時字幕文件也分很多種后綴,但實現起來同樣是利用media tag的ontimeupdate事件。

 

文章轉載地址:HTML5音樂播放器同步顯示歌詞


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM