Flash/Flex学习笔记(32):播放音乐并同步显示lyc歌词(适用于Silverlight)

简介: 题外话:个别朋友总是问我同样的问题,做为一名c#/silverlight程序员为啥还要学flash ? 回 答:看日本片时,就不能对照看欧美的么? 不体会日本的细腻,又怎能感觉到欧美的粗放;同样都是web相关的技术,不必报门户之见;何况这二者有很多可以相互借鉴的东西。

题外话:个别朋友总是问我同样的问题,做为一名c#/silverlight程序员为啥还要学flash ?

回 答:看日本片时,就不能对照看欧美的么? 不体会日本的细腻,又怎能感觉到欧美的粗放;同样都是web相关的技术,不必报门户之见;何况这二者有很多可以相互借鉴的东西。

注:这个例子来自[FL车在臣](在blueidea上又名“寂寞火山”)翻译的“Animation in ActionScript3.0”一书,下面的原理分析也来自他的思路

原 理:

1.先分析一下lyc的格式:

[ti:Going Home]
[ar:Sophie Zelmani]
[al:]
[by:yitian1977]
[offset:500]
[00:01.52]Going Home
[00:06.66]Sophie Zelmani
[00:11.32]
[00:36.60]Not very often have we met /
[00:42.94]But the music's been too bad
[00:48.92]Can only sense happiness
[00:55.68]if the music is sad
[00:59.34]
[01:00.29]So, I'm going home
[01:06.30]I must hurry home
[01:11.88]Where a life goes on
[01:16.05]
[01:20.14]We're too old to make a mess
[01:26.16]Dreams will keep me young
[01:32.78]Old enough to stress
[01:38.86]Only mirrors tell the time
[01:41.76]
[02:52.99][01:42.80]So, I'm going home
[02:59.19][01:48.91]I must hurry home
[03:05.33][01:55.04]So will my life go on
[03:09.29][02:00.56]
[03:17.40][02:07.45]Yes, I'm going home
[03:23.70][02:13.43]Going home alone
[03:29.80][02:19.77]And your life goes on
[03:40.16][02:34.09]
[04:05.13][03:52.01](edit by Nathan_na@msn.com)

基本上就是: [时:分.秒](一个或多个) + 本句歌词
2.不管是Flash还是Silverlight,在播放声音时,都可以得到播放进度:即当前播放到了第多少(豪)秒

3.看懂了第1,2点的分析后,可以这么干:

创建一个全局数组(或字典对象,反正只要能满足key-value结构就行),key为该句歌词对应的(豪)秒数,而value即为对应的歌词,然后将其按key(即歌词出现的时间)升序排序

注:对于"[02:52.99][01:42.80]So, I'm going home"这种写法的歌词,在数组中要拆分成二条,即相当于:

[02:52.99]So, I'm going home
[01:42.80]So, I'm going home
播放时,根据当前播放的时间进度,找到对应的数组元素,然后显示

Flash版:

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif As3代码
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;
    import flash.text.TextFieldAutoSize;
    import flash.events.MouseEvent;

    public class LycPlayer extends Sprite {

        
var  lrc_txt:TextField = new  TextField();
        
var  LRCarray:Array = new  Array(); // 全局数组
         var  sc:SoundChannel;
        
var  sound:Sound;

        public 
function  LycPlayer() {

            
// 定位歌词显示文本框 
            lrc_txt.width = 500 ;
            lrc_txt.selectable
= false ;
            lrc_txt.autoSize
= TextFieldAutoSize.CENTER;
            lrc_txt.x
= stage.stageWidth / 2-lrc_txt.width / 2 ;
            lrc_txt.y
= 0 ;
            addChild(lrc_txt);

            
// 加载歌词
             var  loader:URLLoader = new  URLLoader();
            loader.load(
new  URLRequest( " http://images.24city.com/jimmy/flash/LycPlayer/lyc/GoingHome.txt " ));
            loader.addEventListener(Event.COMPLETE,LoadFinish);

            
// 加载mp3
            sound = new  Sound();
            sound.load(
new  URLRequest( " http://www.apple520.com/templates/default/images/new/3.mp3 " ));
            
            
// 开始播放
            sc = sound.play();

            
// 监听Enter_Frame事件
            stage.addEventListener(Event.ENTER_FRAME,SoundPlaying);
            btnStop.addEventListener(MouseEvent.MOUSE_DOWN,StopMouseDownHandler);
            btnPlay.addEventListener(MouseEvent.MOUSE_DOWN,PlayMouseDownHandler);
        }

        
function  StopMouseDownHandler(e:MouseEvent) {
            
if  (sc != null ){
                sc.stop();
                sc 
=   null ;
            }
        }
        
        
function  PlayMouseDownHandler(e:MouseEvent) {
            
if  (sc == null ){
                sc 
=  sound.play();
            }
        }

        
function  SoundPlaying(evt:Event): void  {
            
if  (sc == null ){ return ;}
            
// trace(sc.position);
             for  ( var  i:uint = 2 ,j:uint = LRCarray.length - 1 ; i  < j; i ++ ) {
                
if  (sc.position < LRCarray[i].timer) {
                    lrc_txt.text
= "   " + LRCarray[i - 2 ].lyric + " \n→ " + LRCarray[i - 1 ].lyric + " \n  " + LRCarray[i].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( " \r\n " );


            
// 分析歌词,填充到全局歌词数组中
             var  reg:RegExp = / \[[0-5][0-9]:[0-5][0-9].[0-9][0-9]\] / g;
            
for  ( var  i:uint = 1 ,j:uint = listarray.length; i  < j; 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 );

                
if  (lyric.length <= 0 ) {
                    
continue ;
                }

                
for  ( var  k:uint = 0 ,t:uint = timeAry.length; k  <  t; k ++ ) {
                    
var  obj:Object  =   new  Object();
                    
var  ctime:String = timeAry[k];
                    
var  ntime:uint  =   1000   *  (parseInt(ctime.substr( 1 , 2 ), 10 ) * 60   +  parseInt(ctime.substr( 4 , 5 ), 10 ))  +  parseInt(ctime.substr( 7 , 8 ), 10 );
                    obj.timer
= ntime;
                    obj.lyric
= lyric;
                    LRCarray.push(obj);
                }

            }

            
// 数组排序
            LRCarray.sort(compare);

            
var  objFirst:Object  =   new  Object();
            objFirst.timer
= 1 ;
            objFirst.lyric
= " Loading... " ;
            LRCarray.unshift(objFirst);

            
var  objLast:Object  =   new  Object();
            objLast.timer
= 999999 ;
            objLast.lyric
= " the end. " ;
            LRCarray.push(objLast);

            
for  ( var  m:Number = 0 ,n  = LRCarray.length; m < n; m ++ ) {
                trace(LRCarray[m].timer 
+   " \t "   +  LRCarray[m].lyric);
            }
        }

        
// 数组排序辅助方法
        private  function  compare(paraA:Object,paraB:Object): int  {
            
if  (paraA.timer > paraB.timer) {
                
return   1 ;
            }
            
if  (paraA.timer < paraB.timer) {
                
return   - 1 ;
            }
            
return   0 ;
        }
    }
}

Silverlight版:

Xaml部分:

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Xaml代码
< UserControl  x:Class ="SilverlightLyricPlayer.MainPage"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d
="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc
="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable
="d"
    d:DesignHeight
="60"  d:DesignWidth ="400" >

    
< Grid  x:Name ="LayoutRoot"  Background ="White" >
        
< TextBlock  x:Name ="txtLyc"  HorizontalAlignment ="Center"  VerticalAlignment ="Center" ></ TextBlock >
        
< StackPanel  Orientation ="Horizontal"  Height ="20"  HorizontalAlignment ="Right"  Margin ="0,0,10,0" >
            
< Button  x:Name ="btnPlay"  Width ="40"  Margin ="5,0,5,0"  Content ="Play"  Click ="btnPlay_Click" ></ Button >
            
< Button  x:Name ="btnStop"  Width ="40"  Content ="Stop"  Click ="btnStop_Click" ></ Button >
        
</ StackPanel >
        
< MediaElement  AutoPlay ="True"  x:Name ="mePlayer"  Visibility ="Collapsed"   />
    
</ Grid >
</ UserControl >

cs部分:

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif Xaml.CS代码
using  System;
using  System.Collections.Generic;
using  System.Diagnostics;
using  System.IO;
using  System.Linq;
using  System.Net;
using  System.Text.RegularExpressions;
using  System.Windows;
using  System.Windows.Browser;
using  System.Windows.Controls;
using  System.Windows.Media;

namespace  SilverlightLyricPlayer
{
    
public   partial   class  MainPage : UserControl
    {
        List
< KeyValuePair < int string >>  lstLycs;

        
public  MainPage()
        {
            InitializeComponent();
            
this .Loaded  +=   new  RoutedEventHandler(MainPage_Loaded);
        }

        
void  MainPage_Loaded( object  sender, RoutedEventArgs e)
        {
            lstLycs 
=   new  List < KeyValuePair < int string >> ();
            WebClient _wcLyc 
=   new  WebClient();
            Uri _uriLyc 
=   new  Uri(HtmlPage.Document.DocumentUri,  " http://images.24city.com/jimmy/flash/LycPlayer/lyc/GoingHome.txt " );
            _wcLyc.OpenReadCompleted 
+=   new  OpenReadCompletedEventHandler(wcLyc_OpenReadCompleted);
            _wcLyc.OpenReadAsync(_uriLyc);
        }

        
void  wcLyc_OpenReadCompleted( object  sender, OpenReadCompletedEventArgs e)
        {
            
if  (e.Error  ==   null )
            {
                Dictionary
< int string >  dicLyc  =   new  Dictionary < int string > ();

                StreamReader _srReader 
=   new  StreamReader(e.Result);
                
string  _strlyc  =  _srReader.ReadToEnd().Replace(Environment.NewLine,  " \n " );
                
string [] _arrTemp  =  _strlyc.Split( ' \n ' );
                Regex _reg 
=   new  Regex( " \\[[0-5][0-9]:[0-5][0-9].[0-9][0-9]\\] " );
                
int  _key  =   - 1 ;
                
string  _lyc  =   "" ;
                
foreach  ( string  item  in  _arrTemp)
                {
                    MatchCollection _mc 
=  _reg.Matches(item);
                    
foreach  (Match m  in  _mc)
                    {
                        _key 
=   1000   *  ( int .Parse(m.Value.Substring( 1 2 ))  *   60   +   int .Parse(m.Value.Substring( 4 2 )))  +   int .Parse(m.Value.Substring( 7 2 )); // 将时间换成总毫秒数
                        _lyc  =  item.Substring(item.LastIndexOf( ' ] ' +   1 );
                        
if  ( ! dicLyc.ContainsKey(_key)  &&  _lyc.Length  >   0 )
                        {
                            dicLyc.Add(_key, _lyc);
                        }
                    }

                    
                }
                dicLyc.Add(
1 " Loading... " );
                dicLyc.Add(
999999 " the end. " );

                lstLycs 
=  dicLyc.OrderBy(c  =>  c.Key).ToList();

                
foreach  (var item  in  lstLycs)
                {
                    Debug.WriteLine(item.Key 
+   " \t "   +  item.Value);
                }

                mePlayer.Source 
=   new  Uri(HtmlPage.Document.DocumentUri,  " http://www.apple520.com/templates/default/images/new/3.mp3 " );
                mePlayer.Volume 
=   0.9 ;
                mePlayer.Play();
                CompositionTarget.Rendering 
+=   new  EventHandler(CompositionTarget_Rendering); // 相当于As3.0中的addEventListener(Enter.ENTER_FRAME,EnterFrameHandler);
            }
        }

        
void  CompositionTarget_Rendering( object  sender, EventArgs e)
        {
            
for  ( int  i  =   2 ; i  <  lstLycs.Count - 1 ; i ++ )
            {
                
if  (mePlayer.Position.TotalMilliseconds  <  lstLycs[i].Key)
                {
                    txtLyc.Text 
=   "   "   +  lstLycs[i  -   2 ].Value  +  Environment.NewLine  +   " "   +  lstLycs[i  -   1 ].Value  +  Environment.NewLine  +   "   "   +  lstLycs[i].Value;
                    
break ;
                }
                txtLyc.Text 
=  lstLycs.Last().Value;
            }
        }

        
private   void  btnPlay_Click( object  sender, RoutedEventArgs e)
        {
            mePlayer.Play();
        }

        
private   void  btnStop_Click( object  sender, RoutedEventArgs e)
        {
            mePlayer.Stop();
        }
    }
}


Silverlight演示地址:http://images.24city.com/jimmy/LycPlayer/ (安全沙箱原因,无法在播客园上正常播放,所以只能给出地址)

 

下载:示例源文件代码

目录
相关文章
|
内存技术 搜索推荐 编解码
|
API 定位技术 网络架构
ArcGIS API for Silverlight 学习笔记
ArcGIS Silverlight API:是构建在微软Silverlight平台之上,通过ArcGIS Server Rest API消费ArcGIS Server 服务,同时支持直接消费Bing地图服务的应用程序编程接口。
1026 0

热门文章

最新文章