插播 OOP讲解 (转)

经过一段时间的学习,对于 OOP ,对于 AS3 已经有了初步的认识与了
解,后面的教程都会用 ActionScript 3.0 语言进行讲解。对于学习者
来说掌握 OOP 设计思想是非常重要的,但这确实需要一些时间进行学
习。这对于程序员也提出了更高的要求,不仅要有抽象思维,还要有形
象化思维(后面的教程会举一些具体的例子)。有人说“ AS3.0 最适
合作为入门 OOP 语言来学习。”,说明我们不仅学习了 AS3 这门语言
本身,而且也能以它作为学习其它 OOP 语言的踏板。
    这些迹象都表明,学习 AS3.0 还是有价值的。虽然它让小程序(玩
具程序)变得复杂,但是它使得大项目带来的改变是显而易见的。虽然
每一次改革都是一把双刃剑,但是当利大于弊的时候我们有理由接受
它。
    这里是我们迈向 AS3.0 的起点,让我们共同努力,进入这个全新
的世界。
    最后原网名“FL 基理大师”更名为“FL 车在臣”,一来是为了迎
接奥运会的召开,二来是为了适应国际化市场。呵呵,越说越不沾边了。
    在这个 3F 时代(浮躁,腐败,肤浅),要在默念切勿沾染这些三
俗的气节。学习就要耐得住寂寞。是的,这也需要锻炼。
    ——呵呵,半年报就写到这里,不搞形式主义了。

 

as3制作 LRC 歌词同步

 

一、准备工作
    既然要制作歌词同步程序,首先要准备一首歌,我们就以“周杰伦-青花瓷”为例。首
先要下载这首“青花瓷.mp3”,保存为“C:\My Player\Music\青花瓷.mp3”。还要下载青
花瓷的 LRC 文件,大家可以到网上下载(地址见附录),将文本内容保存为“C:\My
Player\LRC\青花瓷.lrc”。我们的程序(类和 FLA)则保存在“C:\My Player\”文件夹下。
青花瓷.lrc 文件:
[ti:青花瓷]
[ar:周杰伦]
[al:我很忙]
[by:张琪]
[00:00.00]发送短信 18 到 291199 下载该歌曲到手机
[00:01.11]青花瓷
[03:36.49]
[00:21.39]素眉勾勒秋千话北风龙转丹
[00:26.08]屏层鸟绘的牡丹一如你梳妆
[00:30.46]黯然腾香透过窗心事我了然
[00:34.93]宣纸上皱边直尺各一半
[00:39.49]油色渲染侍女图因为被失藏
[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]就当我为遇见你伏笔
[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]掩盖了结局
[03:28.33][02:52.54][01:23.72]如传世的青花瓷在独自美丽
[03:32.30][02:56.67][01:27.65]你眼的笑意
[01:50.25]色白花青的景已跃然于碗底
[01:54.69]临摹宋体落款时却惦记着你
[01:59.22]你隐藏在药效里一千年的秘密
[02:03.75]急溪里犹如羞花沾落地
[02:08.32]林外芭蕉 惹咒语
[02:10.57]梦幻的铜绿
[02:12.84]而我路过那江南小镇的等你
[02:17.19]在泼墨山水画里
[02:19.75]你从墨色深处被隐去
    大家也可以把这个文本内容复制下来,然后在“C:\My Player\LRC\”下创建一个文本
文档,将内容粘贴上去,再将文档保存为“青花瓷.lrc”,注意扩展名是“.lrc”。
二、LRC 内容分析
    准备工作完成了,下面分析一下这个 LRC 文件。之所以叫 LRC ,是因为它是 Lyric (歌
词) 的缩写。这种格式真是一目了然,前面“[ ]”中的数字表示其后歌词的开始时间。例
如,“[01:50.25]色白花青的景已跃然于碗底”表示在 1 分 50.25 秒时,歌词内容是“色白
花青的景已跃然于碗底”。
    还有一种形式是“[03:01.92][02:25.63][00:56.90]天正在等烟雨”  这种形式常用于赋
格部分(俗称:歌曲的高潮部分),它表示在 03:01.92, 02:25.63, 00:56.90 时的歌词都
是“天正在等烟雨”。由于这种形式的存在,使后面的编程稍显复杂,不过没关系,凭借各
位的聪明智慧一定没问题。

三、预备知识
1. ActionScript 3 中默认使用 Unicode 来解码外部文件,如果读取的文本不是 Unicode
编码,而是按照操作系统代码页编写的,比如 GB2312,那么需要先导入
flash.system.System 类,并在加载外部文本的语句前将 System.useCodePage 设为 true,
默认情况下为 false,即默认不使用操作系统页解码。
    如果 System.useCodePage = false 且外部 LRC 文件编码格式是 ANSI 的话,那么显
示的中文歌词会是乱码。解决办法有两个:一是,将外部 LRC 文件编码格式改为 Unicode;
二是,不改变外部文件编码格式,只在文档类中加入一句 System.useCodePage=true 即可。
由于后一种方法使用简便,我们就采用第二种方法。

2.读取声音:
  var sound:Sound=new Sound();
  sound.load(new URLRequest("Music/青花瓷.mp3"));
3.播放声音及获取当前播放时间(毫秒):
  var sc:SoundChannel;
  var sound:Sound=new Sound();
  sound.load(new URLRequest("Music/青花瓷.mp3"));
  sc=sound.play();
  stage.addEventListener(Event.ENTER_FRAME,EnterFrame);
  function EnterFrame(evt:Event):void {
    trace(sc.position);
  }
 这里将 sc 声明为全局变量(或类变量),因为在多个方法中都要使用它。
4.读取外部文件:
   var loader:URLLoader=new URLLoader();
   loader.load(new URLRequest("LRC/青花瓷.lrc"));
   loader.addEventListener(Event.COMPLETE,LoadFinish);
   function LoadFinish(evt:Event):void {
    trace(evt.target.data);
  }
5.将字符串按分隔符分隔为数组(String.split):
   var str:String="FL Basic Theory Master";
   var array:Array=str.split(" ");
  trace(array);
  //输出数组:[[FL],[Basic],[Theory],[Master]]
   str=" http://blog.sina.com.cn/yyy98";
   array=str.split(".");
  trace(array);
  //输出数组:[[http://blog],[sina,com],[cn/yyy98]]
6.简单的正则表达式应用:
1>获取匹配次数:
  var Pattern:RegExp=/Window/g;
  //意思是所有名为“Window”的字符串
  var str:String="Windows seems like a Window, so called Windows OS! ";
  trace(str.match(Pattern).length)
  //结果:3
2>获取正确匹配:
  var foo:RegExp=/[0-3][0-9]\/[0-1][0-9]\/[0-2][0-9][0-9][0-9]/g;
  //意思是所有格式为“日/月/年”的字符串
  var str:String="Date Format: 2006/12/25 2006-12-25 12/25/2007 25/12/2007"
  trace(str.match(foo))
  //结果:25/12/2007

7.字符串取子串操作(String.substr):
  var str:String="[03:01.92][02:25.63][00:56.90]天正在等烟雨";
  trace(str.substr(0,30));
  //从 0 号索引开始,取 30 个字符
  //结果:[03:01.92][02:25.63][00:56.90]
  trace(str.substr(30));
  //只写一个参数,表示从该索引处到字符串结束位置
  //结果:天正在等烟雨
8.数组排序中比较函数的应用:
  var a:Object={price:20,number:3};
  var b:Object={price:10,number:7};
  var c:Object={price:50,number:1};
  var amountAry:Array=[a,b,c];
  function compare(paraA:Object,paraB:Object):int {
    var resultA =paraA.price*paraA.number;
    var resultB =paraB.price*paraB.number;
    if (resultA > resultB) return 1;
    if (resultA < resultB) return -1;
    return 0;
  }
  amountAry.sort(compare);
  trace(amountAry[0].price); //输出:50
  trace(amountAry[1].price); //输出:20
  trace(amountAry[2].price); //输出:10
四、LRC 的读取与存储转换(使用文档类设计)
1.读取 LRC 文件,这一步非常简单与读取普通的文本文件是一样的;
  public function LRCPlayer() {
    var loader:URLLoader=new URLLoader();
    loader.load(new URLRequest("LRC/青花瓷.lrc"));
    loader.addEventListener(Event.COMPLETE,LoadFinish);
  }
  function LoadFinish(evt:Event):void {
    trace(evt.target.data);
  }
2.将读取的 LRC 数据按行分割( "\n" 为换行符),数组的每一个元素代表 LRC 的一行内
容;
  function LoadFinish(evt:Event):void {
    var list:String=evt.target.data;
    var listarray:Array=list.split("\n");
    trace(listarray);
  }
3.在数组中提取每一行的时间及歌词,解决单时间序列的问题;
(注意!此段代码只作讲解,不以应用)
LRC 内容如下:
[00:43.83]而你嫣然的一笑如含苞待放
[00:48.30]你的美一缕飘散
[00:50.77]去到我去不了的地方
[03:01.92]天正在等烟雨
[03:03.57]而我在等你
[03:05.92]炊烟袅袅升起
[03:07.76]隔江千万里
代码如下:
  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];
     //提取每行内容,用变量 info 保存
     var lyric:String=info.substr(10);
     //将歌词内容提取到 lyric 变量中
     var ctime:String =info.substr(0,10);
     //提取时间序列字串
     var ntime:Number=Number(ctime.substr(1,2))*60+Number(ctime.substr(4,5));
     //将时间字串转换为计算机可读取的时间
     var obj:Object=new Object();
     obj.timer=ntime*1000;
     obj.lyric=lyric;
     LRCarray.push(obj);
     //将时间与歌词保存到一个 Object 中,并压入 LRCarray 数组
     trace(obj.timer,obj.lyric);
    }
  }
输出结果:
43830 而你嫣然的一笑如含苞待放
48300 你的美一缕飘散
50770 去到我去不了的地方
181920 天正在等烟雨
183570 而我在等你
185920 炊烟袅袅升起
187760 隔江千万里

 

4.在 LRC 文件,还有多时间序列的存在,所以单时间序列算法不能满足实际需要,下面就来
解决多时间序列问题;
LRC 内容如下:
[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]隔江千万里
代码如下:
  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;
   //建立正则表达式,范围:[00:00.00]~[59:59.99]
    for (var i=0; i < listarray.length; i++) {
     var info:String=listarray[i];
     //提取每行内容,用变量 info 保存
     var len:int=info.match(reg).length;
     //该行拥有时间序列的个数
     var timeAry:Array=info.match(reg);
     //将匹配的时间序列保存到 timeAry 数组中
     var lyric:String=info.substr(len*10);
     //根据每个时间序列占 10 个字符,找出歌词内容的起点
     //将歌词提取到 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);
     }
     //将时间序列转换为毫秒并与歌词一起保存为一个数组元素
    }
  }
输出结果:
43830 而你嫣然的一笑如含苞待放
48300 你的美一缕飘散
50770 去到我去不了的地方
181920 天正在等烟雨
145630 天正在等烟雨
56900 天正在等烟雨
183570 而我在等你
147910 而我在等你
58990 而我在等你
185920 炊烟袅袅升起
150440 炊烟袅袅升起
60930 炊烟袅袅升起
187760 隔江千万里
152250 隔江千万里
63490 隔江千万里
5.将获得的 LRCarray 数组按起始时间排序,这对于按序读取歌词有重要意义;
  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;
  }
结果如下:
43830 而你嫣然的一笑如含苞待放
48300 你的美一缕飘散
50770 去到我去不了的地方
56900 天正在等烟雨
58990 而我在等你
60930 炊烟袅袅升起
63490 隔江千万里
145630 天正在等烟雨
147910 而我在等你
150440 炊烟袅袅升起
152250 隔江千万里
181920 天正在等烟雨
183570 而我在等你
185920 炊烟袅袅升起
187760 隔江千万里
6.最后,随着音乐的播放,读取播放时间段内的歌词。用当前播放时间与 LRCarray 中的时
间相比较,如果当前时间小于 LRCarray[i].timer 的时间,那么就显示
LRCarray[i-1].lyric 的歌词。为什么要显示 [i-1] 的歌词呢?比如说当前播放到第 500
秒,读取的 LRCarray[20].timer 时间是 400 秒,那么 i++ 。下一次读取的
LRCarray[21].timer 时间是 700 秒, 这时当前播放时间小于读取的这个时间,就说明当前
的第 500 秒仍处于 LRCarray[20].timer 的时间范围内。
  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 类的实例
  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;
   //找不到歌词,说明已超出了最后一句的时间,因此显示最后一句歌词
  }
}
五、全部代码(文档类 LRCPlayer.as):
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;
   }
  }
}

 


六、*无处不在的优化
      至此,该程序已经可以顺利执行了,此处只讨论一下优化问题,看不懂可以跳过。
以这段代码为例:
  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;
   }
  }
如果要进行优化,那么这个 for 循环,应该写成:
   for (var i=1,j=LRCarray.length; i < j; i++) {… …}
这样在执行判断时,不必每次都进行 LRCarray.length 操作,该操用于读取数组长度,执
行 Array 类的 length 方法,属于高级操作,花费的时间要比低级操作多。其实,只要读
取一次长度,然后将结果保存在变量 j 中,每次判断时读取 j 的值即可。取值与赋值都属
于低级别的操作,  速度较快。同样的道理, if (sc.position < LRCarray[i].timer) {… …}
                                       
中的 sc.position 在每次判断时都要读取一遍,这时就应将它在循环之前保存到一个变量
里,这段代码优化后应是这样:
  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;
   }
  }
      在我们的文档类中还有几个地方用到了 for 循环,请大家按照上述方法自行优化。
      其实,代码优化无处不在,其中的学问不胜枚举,有兴趣的朋友可以到我的博客中看一
下关于代码优化的总结贴,见附录。
七、附录
1.LRC 文件下载地址:
http://lrc.bzmtv.com/
http://www.5ilrc.com/
2.至于 MP3 的下载,我想大家比我在行,用百度或酷狗都可以。
3.代码优化总结贴地址:
http://blog.sina.com.cn/s/blog_3ecb9b11010086wi.html
4.整个文件包括(歌曲、歌词、LRCPlayer.as 、FLA 文件)打包下载:
 http://www.fs2you.com/zh-cn/files/cf760b0f-01a0-11dd-9174-0014221f3995/
八、结束语
     恭喜您坚持到了现在,确实内容比较长,同时也涉及了一些知识点。其实做法肯定不只
这一种,所以希望大家多多发挥主观能动性,结合上述内容继续将这个播放程序做大做强。
好了,就到这里,再次感谢。

如果转载中有什么错漏请指出来 好及时更正~~

 

 

(如果要转载请注明出处http://blog.sina.com.cn/jooi,谢谢)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值