给mp3播放器增加音乐波形显示功能

用过winamp的人都知道,winamp有一个音乐波形显示功能,当播放音乐的时候,有一些音乐波形跟着上下跳动,翩翩起舞,又好看,又专业。那么如何给自己的播放器增加这样一个波形显示器呢。

本文给出了一个完整的代码例子,并一步一步的教你实现的过程。为了,推广as2.0和as3.0,本文的例子,采用外部纯 .as文件编写,编译测试时,完全脱离flash,采用FlashDevelop工具编译。代码见附录。

本文的运行效果,可以参考http://www.iu1u.com/res/mywave.swf,本文可以随意转载,但请保证文章的完整性。

准确地说,这里实现地是一个伪波形显示功能。为什么叫做伪波形呢,真正的波形,应该来自实际的音频数据,对应时间对应频段的声音强度高时,就显示高的柱状,声音强度低时,就显示低的柱状。但是,对于大部分的用户来说,实际的音频数据对他们来说也没有什么概念,所以,用伪波形,几乎可以达到以假乱真的效果。

其实,要显示伪波形很容易,只要随机生成不同高度的柱状图,就可以了。但是,为了得到更逼真的效果,大家可以注意一下,很多播放器的波形图里,顶端都有一个小点,它们的反应往往迟钝一下,当一个柱状由高突然变低的时候,并不马上跟着变低,而是,慢慢的往下掉。如何实现这种效果呢,其实,分析一下他的原理,就很简单了。只要每次柱状高度变化的时候,记录一下这组高度,下次生成新的高度时,如果,新的高度比原高度高,那么,顶点跟着网往上走,就是,如果,新高度比原高度小,那么,就让顶点高度减掉一个常数,注意,这里只是减掉一个常数,而不是直接变成新高度。这样,实现出来的效果,就是顶点好像有些缓冲,别柱状顶上去后,是慢慢的往下掉。

本例子里的所有显示内容,都是通过BitmapData对象的画布直接画出来的。所以,也可以帮助读者复习一下BitmapData的用法,以及它如何与MovieClip关联。

在onEnterFrame事件中,调用updatePanel()函数,这个函数会重新生成新的波形,并刷新显示。其中的if (frameCount++>1) 是调整波形的刷新频率用的,不改变1这个数字,就可以实现让波形跳动的更快,或者更慢一些。

最后,因为这是一个继承自MovieClip的类。要想把这个类变成一个flash中的真正的MovieClip对象,必须通过 Object.registerClass()把它注册一下。然后,通过AttachMovie加入flash。这是as2.0里的做法,在as3.0 里,就方便很多了,直接new这个对象,然后通过addChild()加入flash即可。

附:代码

/**
 * Test class for testing mtasc swf building in FlashDevelop.
 * @mtasc -swf c:\Test.swf -header 500:400:12:EFEFEF -main -trust -trace org.flashdevelop.utils.FlashConnect.mtrace org/flashdevelop/utils/FlashConnect.as
 */
 import flash.display.*;
 import flash.geom.Rectangle;

 class Wave extends MovieClip
 {
 static var symbolName:String = "__Packages.Wave";
 static var symbolOwner:Function = Wave;
 // associate the symbol to the class when the class is defined 
 static var symbolLinked = Object.registerClass(symbolName, symbolOwner); 

 var LeftMargin, Bar_Width, Bar_Gap, Bar_Max_Height,BarCount,Client_Height: Number;
 var frameCount:Number = 0;
 var PolarBarHeight:Array;
 var img:BitmapData;
 var rect:Rectangle;
 var rectPolar: Rectangle;
 function Wave ()
 { 
 LeftMargin = 20;
 Bar_Width = 5;
 Bar_Gap = 3;
 Bar_Max_Height = 40;
 BarCount = 20;
 Client_Height = 200;

 rect = new Rectangle(0,0,100,100);
 rectPolar = new Rectangle(0,0,Bar_Width,1);
 img = new BitmapData( 256, 200, false, 0 );
 PolarBarHeight = new Array(20);
 for (var i:Number = 0;i<BarCount;i++)
 PolarBarHeight[i] = 0;
 this.attachBitmap(img,1);
 }

 function updatePanel()
 {
 trace(PolarBarHeight);
 var i:Number =0;
 img.fillRect(img.rectangle,0x000000);
 for (i=0;i<BarCount;i++)
 {
 var WaveHeight:Number = random(Bar_Max_Height); 
 rect.left = LeftMargin + i * (Bar_Width + Bar_Gap);
 rect.top = Client_Height - WaveHeight;
 rect.right = LeftMargin + i * (Bar_Width + Bar_Gap) + Bar_Width;
 rect.bottom = Client_Height;
 img.fillRect(rect,0x009900);

 if ((PolarBarHeight[i] == 0) || (PolarBarHeight[i] < WaveHeight + 5)) 
 PolarBarHeight[i] = WaveHeight + 5;
 else
 PolarBarHeight[i] = PolarBarHeight[i] - 4;
 if (PolarBarHeight[i] < WaveHeight + 5)
 PolarBarHeight[i] = WaveHeight + 5;
 rectPolar.left = LeftMargin + i * (Bar_Width + Bar_Gap);
 rectPolar.top = Client_Height - PolarBarHeight[i]; 
 rectPolar.width = Bar_Width;
 rectPolar.height = 1;

 img.fillRect(rectPolar,0xFF9900); 
 }
 }


 function onEnterFrame()
 {
 if (frameCount++>1)
 {
 updatePanel();
 frameCount = 0;
 }
 }

 // entry point
 static function main (mc)
 {
 //var wave:Wave = new Wave ();
 _root.attachMovie(Wave.symbolName, "wave", 1);
 }
 }