收藏本站腾讯微博新浪微博

经典论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

蓝色理想 最新研发动态 网站开通淘帖功能 - 蓝色理想插件 论坛内容导读一页看论坛 - 给官方提建议

论坛活动及任务 地图和邮件任务 请多用悬赏提问 热夏来袭,选一款蓝色理想的个性T恤吧!

手机上论坛,使用APP获得更好体验 急需前端攻城狮,获得内部推荐机会 论坛开通淘帖功能,收藏终于可以分类了!

搜索
查看: 3330|回复: 4

[AS3] 跟kaka做播放器(一) ————LRC歌词解析篇

[复制链接]
发表于 2008-9-17 16:40:05 | 显示全部楼层 |阅读模式
播放器演示地址:http://bbs.actionscript3.cn/thread-12211-1-1.html

写了很多的代码,该把思路整理一下了..
之所以把歌词解析放在第一篇来写,是因为这个的内容最少,最容易说明  而且和其他部分的关联程度低,是做得比较完善的一个部分了
这是kaka第一次写教程,不知道会不会写的不好,大家看不懂呢..  所以先易后难,就把歌词解析放在最前面来说吧
  下面开始:

LRC歌词解析是老生常谈的问题了,很久很久以前就有高人写过这方面的东西,去年的这个时候kaka刚接触AS2不久,就看到了这个教程
  1. http://space.flash8.net/bbs/thread-265001-1-74.html
复制代码
于是突发奇想:我是不是该用FLASH来做一个网页版的千千静听呢。。?  然后就出现了这个播放器的AS2版本,只不过由于当时的技术条件限制,我没有能够实现的很完美,因此网页版千千静听的计划就无疾而终了..

现在,我们有了AS3的强大支持,kaka也跟着升级啦~~  做这个小小的播放器自然不在话下了(好像有点太罗嗦了...臭鸡蛋番茄已经满天飞了)

好,下面开始:

  歌词解析的常用方法是用数组,对它的元素进行解析解析再解析,这样就要用到很多个数组,而且效率不敢恭维。
AS3给我们提供了强大而邪恶的武器——正则  为什么不用呢?



首先我们假设已经得到一个LRC歌词文件的文本,存储在l:String这个对象中
  1. [00:00.00]歌曲:烟雨烟雨
  2. [00:04.90]歌手:aimini 专辑:爱情的功课
  3. [00:07.61]
  4. [00:09.41]词 曲:wing
  5. [00:13.73]
  6. [00:21.23]看镜子里的思念化成雨
  7. [00:25.47]飘落到心底淋湿了我所有的勇气
  8. [00:32.66]oh~爱你的梦里的那出戏主角是你
  9. [00:42.49]
  10. [00:42.74]rap:我看着镜子里的自己憔悴得可以
  11. [00:44.70]全都是因为我爱上了你整夜翻来覆去
  12. [00:49.05][02:18.09]梦里的那出戏主角是你
  13. [00:53.01][02:20.07]我想是思念化成雨飘落到心底
  14. [00:55.54][02:21.29]淋湿了我所有的勇气
  15. [00:57.18][02:23.62][02:25.41]该如何下决定
  16. [00:58.92]该如何靠近爱你
  17. [01:01.44]说我爱你
复制代码
我们需要的最终结果是一个数组,它的每一个子元素也是一个小数组,小数组的第一个元素为时间信息,
小数组的第二个元素为该时间所对应的歌词文本。  这样就可以根据每一项的时间来显示歌词了

那么最先要做的是取出每一句时间+歌词的文本,然后再对取出的每一句做解析..
那么,怎么取出来呢?

看下面这个正则表达式:
  1. var reg_take:RegExp =/\[\d\d:\d\d\.\d\d\].*/g;
复制代码
它的意思是匹配所有开头为"[" 然后紧跟两个数字,再":"再紧跟两个数字  再"."再紧跟两个数字  然后是"]"  在后面有或者没有内容都可以的一种匹配
如果你看的很晕  那么建议你先去补习一下正则再来拜读此文,以防走火入魔...


这样,我们就可以匹配每一行的类似"[01:40.83]反正我的主角是你"   这样的格式的文本了

那么,下一步
var arr_lyrics:Array = l.match(reg_take);       
这样我们就把所有匹配到的结果都以字符串形式放进arr_lyrics这个数组里面啦

但是先别高兴,还早着呢..

这样的信息离我们的目标还有一段距离,下面我们分两条线路走:

一条线路是提取每一个元素中的歌词信息,过滤掉时间信息(线路一)
另一条线路刚好相反,是提取每一个元素中的时间信息,过滤掉歌词信息(线路二)


关于线路一
就有了下面这个正则表达式的出场:
var reg_replacetime:RegExp =/(\[\d\d:\d\d\.\d\d\])+/g;
它的作用是匹配一个或者多个连续的类似"[01:40.83]"这样的时间格式,为什么要连续呢?  因为LRC歌词里面有"[00:57.18][02:23.62][02:25.41]该如何下决定"  这样的时间格式  在不同时间播放的都是同样的歌词。

不管怎样,我们先把时间信息给Replace掉
  1. for each(var ly:String in arr_lyrics)
  2. {
  3. //去除时间信息,只保留歌词
  4. ly = ly.replace(reg_replacetime, "");
  5. }
复制代码
这样我们得到的每一项的结果都是去掉了"[00:57.18]"这些时间标签的纯歌词了

这是我们就可以创建一个最终的结果数组来保存它了
于是上面的代码变成了这样:
  1. //存储最终结果的数组
  2. var arr_splitedLyrics:Array = new Array();

  3. for each(var ly:String in arr_lyrics)
  4. {
  5. //每一个包括时间和歌词信息的数组
  6. var arr_single:Array = new Array();
  7. //去除时间信息,只保留歌词
  8. arr_single[1] = ly.replace(reg_replacetime, "");
  9. //将每一个子项添加进最终数组
  10. arr_splitedLyrics.push(arr_single);
  11. }
复制代码
这样,我们得到了一个数组,它的每一项元素都是一个小数组,小数组的第二个元素就是歌词,只差时间信息了,我们的目标已经完成一半啦


休息一下,待续...



线路一走完了,下面开始走线路二(提取每一个元素中的时间信息,过滤掉歌词信息)

思路:提取各子项中的每一个"[01:12.35]"这样的字符串,然后得到01(分钟)  12.35(秒) ,转换成AS3能表示的时间(秒或者毫秒)

现在我们解析"[00:57.18][02:23.62][02:25.41]该如何下决定" 这句子项
  1. //提取每一个时间信息的正则
  2. var reg_gettimes:RegExp =/\[\d\d:\d\d\.\d\d\]/g;
  3. //获取具体的分、秒
  4. var reg_readtime:RegExp =/\[(\d\d):(\d\d\.\d\d)\]/;

  5. var str_lrc:String = "[00:57.18][02:23.62][02:25.41]该如何下决定";

  6. //获取所有的时间信息
  7. var arr_tmptime:Array = str_lrc.match(reg_gettimes);
  8. //这里我们得到的数组各项分别是
  9. [00:57.18],[02:23.62],[02:25.41]

  10. for (var i:int = 0; i < arr_tmptime.length; i++)
  11. {
  12. //每一个包括时间和歌词信息的数组
  13. var arr_single:Array = new Array();
  14. //将时间信息解析为Number类型
  15. var t:String = arr_tmptime[i];
  16. var min:String = t.replace(reg_readtime, "$1");
  17. var sec:String = t.replace(reg_readtime, "$2");
  18. var time:Number = Number(min) * 60 + Number(sec);
  19. //数组第一个元素为时间
  20. arr_single[0] = time;

  21. //将每一行添加进总的歌词数组
  22. arr_splitedLyrics.push(arr_single)
  23. }
复制代码
这样,我们得到的结果数组就是一个每一个子项的第一个元素为时间信息的数组啦~
线路二也完成了,但是似乎和线路一分离了..  那就把他们合起来吧

下面是完整的解析代码
  1. var reg_take:RegExp =/\[\d\d:\d\d\.\d\d\].*/g;
  2.                        
  3. //获取歌词,去除歌曲信息
  4. var arr_lyrics:Array = l.match(reg_take);                       
  5. //过滤时间信息的正则
  6. var reg_replacetime:RegExp =/(\[\d\d:\d\d\.\d\d\])+/g;
  7. //获取时间信息的正则
  8. var reg_gettimes:RegExp =/\[\d\d:\d\d\.\d\d\]/g;
  9. var reg_readtime:RegExp =/\[(\d\d):(\d\d\.\d\d)\]/;

  10. var arr_splitedLyrics:Array = new Array();
  11.                        
  12. for each(var ly:String in arr_lyrics)
  13. {
  14. //获取所有的时间信息
  15. var arr_tmptime:Array = ly.match(reg_gettimes);
  16. //去除时间信息,只保留歌词以便后面形成数组
  17. ly = ly.replace(reg_replacetime, "");
  18. for (var i:int = 0; i < arr_tmptime.length; i++)
  19. {
  20. //每一个包括时间和歌词信息的数组
  21. var arr_single:Array = new Array();
  22. //解析时间为Number
  23. var t:String = arr_tmptime[i];
  24. var min:String = t.replace(reg_readtime, "$1");
  25. var sec:String = t.replace(reg_readtime, "$2");
  26. var time:Number = Number(min) * 60 + Number(sec);
  27. //数组第一个元素为时间
  28. arr_single[0] = time;
  29. //第二个元素为歌词
  30. arr_single[1] = ly;
  31. //将每一行添加进总的歌词数组
  32. arr_splitedLyrics.push(arr_single);
  33. }
  34. }
复制代码
trace出来看看吧,是不是发现歌词的顺序不太对?  因为有"[00:57.18][02:23.62][02:25.41]淋湿了我所有的勇气"  这样的歌词格式存在嘛
排一下序好了
  1. //按照每一项的第一个元素进行数值排序(每一项的第一个元素正好是时间信息)
  2. arr_splitedLyrics.sortOn("0", Array.NUMERIC);
复制代码
这样我们得到的就已经是近乎完美的歌词数组了,数组的每一项都是一个小数组,小数组的第一个元素为该句歌词所对应的时间,第二个元素为该句歌词。

如果你想再完美一点,那可以尝试把
[ti:Moonlight Shadow]
[ar:Groove Coverage]
[al:Cover Girl]
[by:kaka]

这些标签也给解析了。  这个任务就留给你啦,可惜我的源代码里面已经实现了这个解析,你要是想自己挑战一下的话就把那段多出来的给删掉自己写吧~~

[[i] 本帖最后由 linchenrr 于 2008-9-19 11:50 编辑 ]
 楼主| 发表于 2008-9-17 16:40:33 | 显示全部楼层
SplitLyrics.as类整个代码
  1. package lyrics
  2. {       
  3.         import flash.text.TextField;
  4.         import flash.text.TextFieldAutoSize;
  5.         /**
  6.         * ...
  7.         * @author kaka
  8.         * @version 0.2
  9.         * @date 08/9/15
  10.         */
  11.         public class SplitLyrics
  12.         {
  13.                 static public function split(lyrics:String):Array
  14.                 {
  15.                         var l:String = lyrics;
  16.                        
  17.                         //获取歌曲信息
  18.                         //歌曲名
  19.                         var reg_soundname:RegExp =/\[ti:(.+?)\]/i;
  20.                         //歌手
  21.                         var reg_singer:RegExp =/\[ar:(.+?)\]/i;
  22.                         //专辑
  23.                         var reg_CD:RegExp =/\[al:(.+?)\]/i;
  24.                         //歌词作者
  25.                         var reg_maker:RegExp =/\[(by:.+?)\]/i;
  26.                        
  27.                         var arr_soundname:Array = l.match(reg_soundname);
  28.                         var arr_singer:Array = l.match(reg_singer);
  29.                         var arr_CD:Array = l.match(reg_CD);
  30.                         var arr_maker:Array = l.match(reg_maker);
  31.                        
  32.                         //必须有一个空格,否则影响后面歌词拖动类的检测
  33.                         var info:String = " ";
  34.                         if (arr_soundname != null)
  35.                         {
  36.                                 info += "歌曲:" + arr_soundname[1] + "  ";
  37.                         }
  38.                         if (arr_singer != null)
  39.                         {
  40.                                 info += "歌手:" + arr_singer[1] + "  ";
  41.                         }
  42.                         if (arr_CD != null)
  43.                         {
  44.                                 info += "专辑:" + arr_CD[1] + "  ";
  45.                         }
  46.                         if (arr_maker != null)
  47.                         {
  48.                                 info += arr_maker[1] + "  ";
  49.                         }                       
  50.                        
  51.                        
  52.                         var reg_take:RegExp =/\[\d\d:\d\d\.\d\d\].*/g;
  53.                        
  54.                         //获取歌词,去除歌曲信息
  55.                         var arr_lyrics:Array = l.match(reg_take);                       
  56.                         //过滤时间信息的正则
  57.                         var reg_replacetime:RegExp =/(\[\d\d:\d\d\.\d\d\])+/g;
  58.                         //获取时间信息的正则
  59.                         var reg_gettimes:RegExp =/\[\d\d:\d\d\.\d\d\]/g;
  60.                         var reg_readtime:RegExp =/\[(\d\d):(\d\d\.\d\d)\]/;
  61.                         //均衡歌词间距的正则
  62.                         var reg_dis:RegExp =/ $/;
  63.                        
  64.                        
  65.                         var arr_splitedLyrics:Array = new Array();
  66.                        
  67.                         //将歌曲信息放在头一个
  68.                         var arr_info:Array = [0, info];
  69.                         arr_splitedLyrics.push(arr_info);
  70.                        
  71.                         for each(var ly:String in arr_lyrics)
  72.                         {
  73.                                 //获取所有的时间信息
  74.                                 var arr_tmptime:Array = ly.match(reg_gettimes);
  75.                                 //去除时间信息,只保留歌词以便后面形成数组
  76.                                 ly = ly.replace(reg_replacetime, "");
  77.                                 for (var i:int = 0; i < arr_tmptime.length; i++)
  78.                                 {
  79.                                         //每一个包括时间和歌词信息的数组
  80.                                         var arr_single:Array = new Array();
  81.                                         //解析时间为Number
  82.                                         var t:String = arr_tmptime[i];
  83.                                         var min:String = t.replace(reg_readtime, "$1");
  84.                                         var sec:String = t.replace(reg_readtime, "$2");
  85.                                         var time:Number = Number(min) * 60 + Number(sec);
  86.                                         //数组第一个元素为时间
  87.                                         arr_single[0] = time;
  88.                                         //第二个元素为歌词
  89.                                         if (!reg_dis.test(ly))
  90.                                         {
  91.                                                 ly += " ";
  92.                                         }
  93.                                         arr_single[1] = ly;
  94.                                         //将每一行添加进总的歌词数组
  95.                                         arr_splitedLyrics.push(arr_single);
  96.                                 }
  97.                         }
  98.                         //歌词排序
  99.                         arr_splitedLyrics.sortOn("0", Array.NUMERIC);
  100.                         return arr_splitedLyrics;                       
  101.                 }
  102.         }       
  103. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2008-9-18 08:52:04 | 显示全部楼层
这个帖子面熟~,在那见过.................
回复 支持 反对

使用道具 举报

发表于 2008-9-18 22:32:09 | 显示全部楼层
看那个正则表达一直很头疼,现在有这么详细解说,太好了。。。。非常感谢。
回复 支持 反对

使用道具 举报

发表于 2008-9-19 09:57:17 | 显示全部楼层
原帖由 [i]bat0906 于 2008-9-18 08:52 发表
这个帖子面熟~,在那见过.................

在天地会见过吧~
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|小黑屋|Archiver|手机版|blueidea.com ( ICP05002321 )  

GMT+8, 2019-12-10 10:08 , Processed in 0.093512 second(s), 9 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表