题外话:个别朋友总是问我同样的问题,做为一名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版:
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部分:
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部分:
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/ (安全沙箱原因,无法在播客园上正常播放,所以只能给出地址)
下载:示例源文件代码