Flash AS制作LRC歌詞同步的詳細(xì)教程

相關(guān)文章:Flash AS3用于讀取LRC同步歌詞的類。
一、準(zhǔn)備工作
既然要制作歌詞同步程序,首先要準(zhǔn)備一首歌,我們就以“周杰倫-青花瓷”為例。首先要下載這首“青花瓷.mp3”,保存為“C:\My Player\Music\青花瓷.mp3”。還要下載青花瓷的 LRC 文件,大家可以到網(wǎng)上下載(地址見附錄),將文本內(nèi)容保存為“C:\My Player\LRC\青花瓷.lrc”。我們的程序(類和FLA)則保存在“C:\My Player\”文件夾下。
青花瓷.lrc 文件:
--------------------------------------------------------------------------------
[ti:青花瓷]
[ar:周杰倫]
[al:我很忙]
[by:張琪]
[00:00.00]發(fā)送短信18到291199下載該歌曲到手機(jī)
[00:01.11]青花瓷
[03:36.49]
[00:21.39]素眉勾勒秋千話北風(fēng)龍轉(zhuǎn)丹
[00:26.08]屏層鳥繪的牡丹一如你梳妝
[00:30.46]黯然騰香透過窗心事我了然
[00:34.93]宣紙上皺邊直尺各一半
[00:39.49]油色渲染侍女圖因?yàn)楸皇Р亍?br />[00:43.83]而你嫣然的一笑如含苞待放
[00:48.30]你的美一縷飄散
[00:50.77]去到我去不了的地方
[02:23.97][00:55.77]
[03:01.92][02:25.63][00:56.90]天正在等煙雨
[03:03.57][02:27.91][00:58.99]而我在等你
[03:05.92][02:30.44][01:00.93]炊煙裊裊升起
[03:07.76][02:32.25][01:03.49]隔江千萬 里
[03:10.36][02:34.85][01:05.84]在平地書刻你房間上的飄影
[03:14.67][02:38.73][01:09.87]就當(dāng)我為遇見你伏筆
[03:18.83][02:43.35][01:14.34]天正在等煙雨
[03:21.20][02:45.60][01:16.68]而我在等你
[03:23.71][02:48.01][01:18.99]月色被打撈起
[03:25.74][02:50.10][01:21.18]掩蓋了結(jié)局
[03:28.33][02:52.54][01:23.72]如傳世的青花瓷在獨(dú)自美麗
[03:32.30][02:56.67][01:27.65]你眼的笑意
[01:50.25]色白花青的景已躍然于碗底
[01:54.69]臨摹宋體落款時(shí)卻惦記著你
[01:59.22]你隱藏在藥效里一千年的秘密
[02:03.75]急溪里猶如羞花沾落地
[02:08.32]林外芭蕉 惹咒語
[02:10.57]夢幻的銅綠
[02:12.84]而我路過那江南小鎮(zhèn)的等你
[02:17.19]在潑墨山水畫里
[02:19.75]你從墨色深處被隱去
--------------------------------------------------------------------------------
大家也可以把這個(gè)文本內(nèi)容復(fù)制下來,然后在“C:\My Player\LRC\”下創(chuàng)建一個(gè)文本文檔,將內(nèi)容粘貼上去,再將文檔保存為“青花瓷.lrc”,注意擴(kuò)展名是“.lrc”。
二、LRC 內(nèi)容分析
準(zhǔn)備工作完成了,下面分析一下這個(gè) LRC 文件。之所以叫 LRC ,是因?yàn)樗?Lyric (歌詞) 的縮寫。這種格式真是一目了然,前面“[ ]”中的數(shù)字表示其后歌詞的開始時(shí)間。例如,“[01:50.25]色白花青的景已躍然于碗底”表示在1分50.25秒時(shí),歌詞內(nèi)容是“色白花青的景已躍然于碗底”。
還有一種形式是“[03:01.92][02:25.63][00:56.90]天正在等煙雨”這種形式常用于賦格部分(俗稱:歌曲的高x部分),它表示在 03:01.92, 02:25.63, 00:56.90 時(shí)的歌詞都是“天正在等煙雨”。由于這種形式的存在,使后面的編程稍顯復(fù)雜,不過沒關(guān)系,憑借各位的聰明智**
四、LRC 的讀取與存儲(chǔ)轉(zhuǎn)換(使用文檔類設(shè)計(jì))
1.讀取 LRC 文件,這一步非常簡單與讀取普通的文本文件是一樣的;
CODE:
public function LRCPlayer() {
var loader:URLLoader=new URLLoader();
loader.load(new URLRequest("LRC/青花瓷.lrc"));
loader.addEventListener(Event.COMPLETE,LoadFinish);
}
private function LoadFinish(evt:Event):void {
trace(evt.target.data);
}--------------------------------------------------------------------------------
2.將讀取的 LRC 數(shù)據(jù)按行分割( "\n" 為換行符),數(shù)組的每一個(gè)元素代表 LRC 的一行內(nèi)容;
CODE:
function LoadFinish(evt:Event):void {
var list:String=evt.target.data;
var listarray:Array=list.split("\n");
trace(listarray);
}--------------------------------------------------------------------------------
3.在數(shù)組中提取每一行的時(shí)間及歌詞,解決單時(shí)間序列的問題;(注意!此段代碼只作講解,不以應(yīng)用)
LRC 內(nèi)容如下:
QUOTE:
[00:43.83]而你嫣然的一笑如含苞待放
[00:48.30]你的美一縷飄散
[00:50.77]去到我去不了的地方
[03:01.92]天正在等煙雨
[03:03.57]而我在等你
[03:05.92]炊煙裊裊升起
[03:07.76]隔江千萬 里
代碼如下:
CODE:
function LoadFinish(evt:Event):void {
var list:String=evt.target.data;
var listarray:Array=list.split("\n");
for (var i=0; i<listarray.length; i ) {
var info:String=listarray[i];
//提取每行內(nèi)容,用變量 info 保存
var lyric:String=info.substr(10);
//將歌詞內(nèi)容提取到 lyric 變量中
var ctime:String =info.substr(0,10);
//提取時(shí)間序列字串
var ntime:Number=Number(ctime.substr(1,2))*60 Number(ctime.substr(4,5));
//將時(shí)間字串轉(zhuǎn)換為計(jì)算機(jī)可讀取的時(shí)間
var obj:Object=new Object();
obj.timer=ntime*1000;
obj.lyric=lyric;
LRCarray.push(obj);
//將時(shí)間與歌詞保存到一個(gè) Object 中,并壓入LRCarray 數(shù)組
trace(obj.timer,obj.lyric);
}
}輸出結(jié)果:
QUOTE:
43830 而你嫣然的一笑如含苞待放
48300 你的美一縷飄散
50770 去到我去不了的地方
181920 天正在等煙雨
183570 而我在等你
185920 炊煙裊裊升起
187760 隔江千萬 里
--------------------------------------------------------------------------------
4.在LRC文件,還有多時(shí)間序列的存在,所以單時(shí)間序列算法不能滿足實(shí)際需要,下面就來解決多時(shí)間序列問題;
LRC 內(nèi)容如下:
QUOTE:
[00:43.83]而你嫣然的一笑如含苞待放
[00:48.30]你的美一縷飄散
[00:50.77]去到我去不了的地方
[03:01.92][02:25.63][00:56.90]天正在等煙雨
[03:03.57][02:27.91][00:58.99]而我在等你
[03:05.92][02:30.44][01:00.93]炊煙裊裊升起
[03:07.76][02:32.25][01:03.49]隔江千萬 里
代碼如下:
CODE:
function LoadFinish(evt:Event):void {
var list:String=evt.target.data;
var listarray:Array=list.split("\n");
var reg:RegExp=/\[[0-5][0-9]:[0-5][0-9].[0-9][0-9]\]/g;
//建立正則表達(dá)式,范圍:[00:00.00]~[59:59.99]
for (var i=0; i<listarray.length; i ) {
var info:String=listarray[i];
//提取每行內(nèi)容,用變量 info 保存
var len:int=info.match(reg).length;
//該行擁有時(shí)間序列的個(gè)數(shù)
var timeAry:Array=info.match(reg);
//將匹配的時(shí)間序列保存到 timeAry 數(shù)組中
var lyric:String=info.substr(len*10);
//根據(jù)每個(gè)時(shí)間序列占10個(gè)字符,找出歌詞內(nèi)容的起點(diǎn)
//將歌詞提取到 lyric 變量中
for (var k:int=0; k<timeAry.length; k ) {
var obj:Object=new Object();
var ctime:String=timeAry[k];
var ntime:Number=Number(ctime.substr(1,2))*60 Number(ctime.substr(4,5));
obj.timer=ntime*1000;
obj.lyric=lyric;
LRCarray.push(obj);
trace(obj.timer,obj.lyric);
}
//將時(shí)間序列轉(zhuǎn)換為毫秒并與歌詞一起保存為一個(gè)數(shù)組元素
}
}輸出結(jié)果:
QUOTE:
43830 而你嫣然的一笑如含苞待放
48300 你的美一縷飄散
50770 去到我去不了的地方
181920 天正在等煙雨
145630 天正在等煙雨
56900 天正在等煙雨
183570 而我在等你
147910 而我在等你
58990 而我在等你
185920 炊煙裊裊升起
150440 炊煙裊裊升起
60930 炊煙裊裊升起
187760 隔江千萬 里
152250 隔江千萬 里
63490 隔江千萬 里
--------------------------------------------------------------------------------
5.將獲得的 LRCarray 數(shù)組按起始時(shí)間排序,這對于按序讀取歌詞有重要意義;
CODE:
LRCarray.sort(compare);
private function compare(paraA:Object,paraB:Object):int {
if (paraA.timer>paraB.timer) {
return 1;
}
if (paraA.timer<paraB.timer) {
return -1;
}
return 0;
}結(jié)果如下:
QUOTE:
43830 而你嫣然的一笑如含苞待放
48300 你的美一縷飄散
50770 去到我去不了的地方
56900 天正在等煙雨
58990 而我在等你
60930 炊煙裊裊升起
63490 隔江千萬 里
145630 天正在等煙雨
147910 而我在等你
150440 炊煙裊裊升起
152250 隔江千萬 里
181920 天正在等煙雨
183570 而我在等你
185920 炊煙裊裊升起
187760 隔江千萬 里
--------------------------------------------------------------------------------
6.最后,隨著音樂的播放,讀取播放時(shí)間段內(nèi)的歌詞。用當(dāng)前播放時(shí)間與 LRCarray 中的時(shí)間相比較,如果當(dāng)前時(shí)間小于 LRCarray.timer 的時(shí)間,那么就顯示 LRCarray[i-1].lyric 的歌詞。為什么要顯示 [i-1] 的歌詞呢?比如說當(dāng)前播放到第 500 秒,讀取的 LRCarray[20].timer 時(shí)間是 400 秒,那么 i 。下一次讀取的 LRCarray[21].timer 時(shí)間是 700 秒,這時(shí)當(dāng)前播放時(shí)間小于讀取的這個(gè)時(shí)間,就說明當(dāng)前的第 500 秒仍處于 LRCarray[20].timer 的時(shí)間范圍內(nèi)。
CODE:
var lrc_txt:TextField=new TextField();
var LRCarray:Array=new Array();
var sc:SoundChannel;
public function LRCPlayer() {
lrc_txt.width=500;
lrc_txt.selectable=false;
addChild(lrc_txt);
//歌詞在文本 lrc_txt 中顯示
var loader:URLLoader=new URLLoader();
loader.load(new URLRequest("LRC/青花瓷.lrc"));
loader.addEventListener(Event.COMPLETE,LoadFinish);
var sound:Sound=new Sound();
sound.load(new URLRequest("Music/青花瓷.mp3"));
sc=sound.play();
//播放聲音,并生成 sc 變量,SoundChannel 類的實(shí)例
stage.addEventListener(Event.ENTER_FRAME,SoundPlaying);
//實(shí)時(shí)刷新歌詞
}
function SoundPlaying(evt:Event):void {
for (var i=1; i<LRCarray.length; i ) {
if (sc.position<LRCarray[i].timer) {
lrc_txt.text=LRCarray[i-1].lyric;
break;
//找到歌詞,跳出循環(huán)體
}
lrc_txt.text=LRCarray[LRCarray.length-1].lyric;
//找不到歌詞,說明已超出了最后一句的時(shí)間,因此顯示最后一句歌詞
}
}五、全部代碼(文檔類 LRCPlayer.as):
CODE:
package {
import flash.display.Sprite;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.events.Event;
import flash.text.TextField;
import flash.system.System;
public class LRCPlayer extends Sprite {
var lrc_txt:TextField=new TextField();
var LRCarray:Array=new Array();
var sc:SoundChannel;
public function LRCPlayer() {
System.useCodePage=true;
lrc_txt.width=500;
lrc_txt.selectable=false;
addChild(lrc_txt);
var loader:URLLoader=new URLLoader();
loader.load(new URLRequest("LRC/青花瓷.lrc"));
loader.addEventListener(Event.COMPLETE,LoadFinish);
var sound:Sound=new Sound();
sound.load(new URLRequest("Music/青花瓷.mp3"));
sc=sound.play();
stage.addEventListener(Event.ENTER_FRAME,SoundPlaying);
}
function SoundPlaying(evt:Event):void {
for (var i=1; i<LRCarray.length; i ) {
if (sc.position<LRCarray[i].timer) {
lrc_txt.text=LRCarray[i-1].lyric;
break;
}
lrc_txt.text=LRCarray[LRCarray.length-1].lyric;
}
}
function LoadFinish(evt:Event):void {
var list:String=evt.target.data;
var listarray:Array=list.split("\n");
var reg:RegExp=/\[[0-5][0-9]:[0-5][0-9].[0-9][0-9]\]/g;
for (var i=0; i<listarray.length; i ) {
var info:String=listarray[i];
var len:int=info.match(reg).length;
var timeAry:Array=info.match(reg);
var lyric:String=info.substr(len*10);
for (var k:int=0; k<timeAry.length; k ) {
var obj:Object=new Object();
var ctime:String=timeAry[k];
var ntime:Number=Number(ctime.substr(1,2))*60 Number(ctime.substr(4,5));
obj.timer=ntime*1000;
obj.lyric=lyric;
LRCarray.push(obj);
}
}
LRCarray.sort(compare);
}
private function compare(paraA:Object,paraB:Object):int {
if (paraA.timer>paraB.timer) {
return 1;
}
if (paraA.timer<paraB.timer) {
return -1;
}
return 0;
}
}
}
六、*無處不在的優(yōu)化
至此,該程序已經(jīng)可以順利執(zhí)行了,此處只討論一下優(yōu)化問題,看不懂可以跳過。
以這段代碼為例:
CODE:
function SoundPlaying(evt:Event):void {
for (var i=1; i<LRCarray.length; i ) {
if (sc.position<LRCarray[i].timer) {
lrc_txt.text=LRCarray[i-1].lyric;
break;
}
lrc_txt.text=LRCarray[LRCarray.length-1].lyric;
}
}如果要進(jìn)行優(yōu)化,那么這個(gè) for 循環(huán),應(yīng)該寫成:
CODE:
for (var i=1,j=LRCarray.length; i<j; i ) {… …}這樣在執(zhí)行判斷時(shí),不必每次都進(jìn)行 LRCarray.length 操作,該操用于讀取數(shù)組長度,執(zhí)行 Array 類的 length 方法,屬于高級操作,花費(fèi)的時(shí)間要比低級操作多。其實(shí),只要讀取一次長度,然后將結(jié)果保存在變量 j 中,每次判斷時(shí)讀取 j 的值即可。取值與賦值都屬于低級別的操作,速度較快。同樣的道理,在
CODE:
if (sc.position<LRCarray[i].timer) {… …} 中的 sc.position 在每次判斷時(shí)都要讀取一遍,這時(shí)就應(yīng)將它在循環(huán)之前保存到一個(gè)變量里,這段代碼優(yōu)化后應(yīng)是這樣:
CODE:
function SoundPlaying(evt:Event):void {
var now:Number=sc.position;
for (var i=1,j=LRCarray.length; i<j; i ) {
if (now<LRCarray[i].timer) {
lrc_txt.text=LRCarray[i-1].lyric;
break;
}
lrc_txt.text=LRCarray[j-1].lyric;
}
}在我們的文檔類中還有幾個(gè)地方用到了 for 循環(huán),請大家按照上述方法自行優(yōu)化。
其實(shí),代碼優(yōu)化無處不在,其中的學(xué)問不勝枚舉,有興趣的朋友可以到我的博客中看一下關(guān)于代碼優(yōu)化的總結(jié)貼,見附錄。
七、附錄
1.LRC 文件下載地址:
http://lrc.bzmtv.com/
http://www.5ilrc.com/
2.至于 MP3 的下載,我想大家比我在行,用百度或酷狗都可以。
3.代碼優(yōu)化總結(jié)貼地址:
http://www.dbjr.com.cn/flash/actionscript-5898.html
4.整個(gè)文件包括(歌曲、歌詞、LRCPlayer.as 、FLA 文件)打包下載:
http://www.fs2you.com/zh-cn/files/cf760b0f-01a0-11dd-9174-0014221f3995/
八、結(jié)束語
恭喜您堅(jiān)持到了現(xiàn)在,確實(shí)內(nèi)容比較長,同時(shí)也涉及了一些知識(shí)點(diǎn)。其實(shí)做法肯定不只這一種,所以希望大家多多發(fā)揮主觀能動(dòng)性,結(jié)合上述內(nèi)容繼續(xù)將這個(gè)播放程序做大做強(qiáng)。好了,就到這里,再次感謝。
相關(guān)文章
flash cs6鼠標(biāo)跟隨效果實(shí)現(xiàn)代碼分享
flash cs6想要實(shí)現(xiàn)鼠標(biāo)跟隨效果?該怎么制作呢?今天我們就來看看使用as2.0實(shí)現(xiàn)鼠標(biāo)跟隨效果的教程,需要的朋友可以參考下2019-05-19- Flash cs6怎么使用代碼輸入中英文文本?Flash cs6中可以使用文字工具直接輸入文本,也可以使用代碼來輸入文本,該怎么使用代碼輸入文本呢?請看下文詳細(xì)的教程,需要的朋友2018-03-11
- flash as3.0抽象類怎么定義? as3.0中有很多抽象類,該怎么定義抽象類和抽象方法呢?下面我們就來看看簡單的例子,需要的朋友可以參考下http://www.dbjr.com.cn/softs/408402.2018-02-28
flash cs6中怎么使用ActionScript3.0?
flash cs6中怎么使用ActionScript3.0?flash cs6中想要使用ActionScript3.0功能,該怎么使用呢?下面我們就來看看詳細(xì)的教程,需要的朋友可以參考下2018-01-25Flash中怎么實(shí)現(xiàn)鼠標(biāo)點(diǎn)擊決定圖像位置?
本教程給大家分享一個(gè)Flash小教程,教大家在Flash CS6中怎么實(shí)現(xiàn)鼠標(biāo)點(diǎn)擊決定圖像位置?方法很簡單,感興趣的朋友歡迎前來一起分享學(xué)習(xí)2018-01-12Flash中如何用代碼將圖片放在自己想要的舞臺(tái)位置?
本教程教腳本之家的ActionScript教程學(xué)習(xí)者在Flash中如何用代碼將圖片放在自己想要的舞臺(tái)位置,教程講解的詳細(xì),感興趣的朋友歡迎前來分享學(xué)習(xí)2017-11-20在Flash CS6中使用with函數(shù)繪制背景圖教程
本教程教腳本之家的ActionScript教程學(xué)習(xí)者如何在Flash CS6中使用with函數(shù)繪制背景圖?教程一步步講解的挺詳細(xì),方法也不難,非常適合Flash新手入門學(xué)習(xí)2017-11-18Flash怎么設(shè)置元件坐標(biāo)?flash使用代碼設(shè)置元件的坐標(biāo)的教程
Flash怎么設(shè)置元件坐標(biāo)?flash中導(dǎo)如的元件需要添加坐標(biāo),該怎么定位元件坐標(biāo)呢?下面我們就來看看flash使用代碼設(shè)置元件的坐標(biāo)的教程,需要的朋友可以參考下2017-10-11Flash怎么制作來回?fù)u擺的花朵的動(dòng)畫?
Flash怎么制作來回?fù)u擺的花朵的動(dòng)畫?Flash中想要給花朵制作一段搖擺的動(dòng)畫效果,該怎么制作呢?下面我們就來看看詳細(xì)的教程,很簡單,需要的朋友可以參考下2017-05-23- Flash怎么制作流動(dòng)七彩色的文字?想要讓文字動(dòng)起來,該怎么使用flash給文字制作一個(gè)流動(dòng)七彩色的動(dòng)畫呢?下面我們就來看看詳細(xì)的教程,需要的朋友可以參考下2017-04-23