一直都对as3的声音架构设计感到困惑,as3为什么要把播放和停止方法分开呢?为什么不能用一个Sound更换载入的声音呢?这几天有空专门专研了一下。原来as3对声音的设计也是深思熟虑的。目的不是为了把问题搞得复杂,而是为了在功能强化的同时做到精简。并尽可能方便我们使用。现在把我这两天的理解拿出来共享,有什么不到位的地方请多多建议。
[1b]架构图:
[/1b]
[url=http://bbs.actionscript3.cn/attachment.php?aid=3966]
架构图.jpg
[/url]
可以看出,as3中的声音处理是独立的,不再依赖影片剪辑作为声道。as3使用多个类合作处理声音,功能被细化到对应的类中,使得逻辑更加清晰。下面对着架构图由下往上详细阐述各个类所但当的任务。
Sound类负责创建、加载、播放声音。Sound类更像BitmapData扮演的角色,用于描述数据。而不是控制声音。使用Sound创建声音有两种创建方式:
[1b]1.[/1b][1b]通过库中元件创建和载入[/1b]
库中声音是Sound的子类。要播放必须创建它们的实例。
[1b]2.[/1b][1b]通过外部加载的声音[/1b]
外部加载声音创建类型是Sound类本身。为强调一个类描述一个声音数据这种模式,Sound禁止第二次加载外部声音。加载控制符合open—progress—complete的标准。
Sound的构造函数为Sound(stream:URLRequest = null, context:SoundLoaderContext = null),创建外部声音时与load方法参数相同。如果创建库中的声音则不需要任何参数。
[1b]方法:[/1b]
load(stream:URLRequest = null, context:SoundLoaderContext = null):void
加载声音。不能使用一个Sound对象加载多个声音。当有多声音播放时,必须为每个声音创建不同的Sound对象。
play(startTime:Number = 0, loops:int = 0, sndTransform:SoundTransform = null):SoundChannel
播放声音。startTimer为开始播放事件,单位为毫秒。loops为循环次数,0和1都表示播放一次,如同flash属性面板中的次数设置。sndTransform是分配给每个通道的初始对象,如果有必要还可以在soundChannel中修改这个对象。每次播放都会选择不通的声道,对应方法返回值。多次执行会导致重叠播放。
close():void
关闭声音流。对于流式播放声音,close很重要,因为soundChannel的stop方法不足以停止流式下载的声音。可能产生继续播放的现象。
[1b]事件:[/1b]
open:
类型为Event.OPEN。开始下载时触发。
progress:
类型为PrograssEvent.PROGRESS。下载时触发。
complete:
类型为Event.COMPLETE。下载完毕时触发。
ioError:
类型为Event.IO_ERROR。发生IO错误时触发。
id3:
类型为Event.ID3。可以访问id3数据时触发。
[1b]属性:[/1b]
所有属性都是只读的,用来访问加载信息。
url:String
加载声音的url值。
bytesLoaded:uint
加载字节数。
bytesTotal:int
总字节数。
length:Number
当前声音长度,如果加载中只反应加载的长度。
id3:ID3Info
id3属性包含唱片信息。
[1b]加载播放外部声音的顺序:[/1b]
加载播放外部声音的顺序如下:
ID3Info类
ID3Info类用于描述声音信息。对应Sound类的id3属性。
[1b]
属性:
[/1b]
songName:String
歌曲名称。
track:String
曲目编号。
artist:String
歌手名称。
album:String
专辑名称。
yesr:String
录制年份。
genre:String
歌曲流派。
comment:String
相关注释。
[1b]
获取唱片信息顺序:
[/1b]
[url=http://bbs.actionscript3.cn/attachment.php?aid=3967]
获取唱片信息.jpg
[/url]
SoundChannel类
SoundChannel继承EventDispatcher,使用import flash.media.SoundChannel声明。SoundChannel用于描述声音通道。有了这个类,声音通道再不用依赖MovieClip了。多个声道可以播放一个声音,一个声道可以播放多个声音(同一时刻只能播放一个)。对声音的操控和状态描述从描述声音数据的Sound类中分离出来。声道与声音之间的关系类似Bitmap和BitmapData的关系。这种逻辑描述更加精确。但与位图不同的是声音通道不是由用户创建的而是系统生成的。这个纽带是Sound的play方法。采用这种处理方式是为了降低复杂性,因为系统自动管理声道免去我们因声道分配分散注意力。系统每次选择空余声道进行回放。当停止了一个声道的播放后,声道自动被as回收。这样一来,我们完全不必去管声道如何分配的,就如显示对象的深度管理一样。
[1b]方法:[/1b]
stop()
停止播放声音。但不能停止流式下载时播放的声音。
[1b]属性:[/1b]
position:Number
播放头位置,单位毫秒。只读属性。这表示position并不能设置播放进度。要设置播放进度只能使用play的参数。
leftPear:Number
左声道音量。范围为0-1。这个属性并不用来控制平移而是用来读取左声道峰值波幅。可以用这个属性检查是否是单声道。
rightPear:Number
右声道音量。范围为0-1。这个属性并不用来控制平移而是用来读取右声道峰值波幅。可以用这个属性检查是否是单声道。
soundTransform:SoundTransform
分配给声道的soundTransform对象。
[1b]事件:[/1b]
soundComplete
类型为Event.SoundComplete。声音播放完毕后触发。
[1b]监视和设置声音播放进度:[/1b]
[1b]监视播放进度:[/1b]
对于下载完毕的声音可以使用position监视播放进度。但对于正在下载中的声音虽然position可以正常访问,但length属性只能在下载完毕后才能获取,这给监视播放进度造成了困难。这时可以通过bytesLoaded和bytesTotal的比值估算出长度。公式为:position*bytesTotal/bytesLoaded。
[1b]设置播放进度:[/1b]
由于position属性是只读的。因此不能通过它设置播放进度。设置播放进度只能通过play方法的startTime参数。这是因为考虑到功能重复。采取这种逻辑还有个长处是不必管播放的状态。方法也只有播放和停止而没有暂停方法。少了一个状态和方法后,播放进度不必管是否播放或暂停。所以免去了playState属性。position的只读特性也避免设置进度时是否要保持播放或暂停状态。现在一切都被简化成播放和停止两个方法了。逻辑大大简化。而暂停和设置进度都由播放和停止两个方法衍生出来。方法如下:
设置进度=stop+play(startTime)
暂停播放=记录position+stop
恢复播放=play(记录position)
SoundTansform类
SoundTransform
SoundMixer类继承Object。使用import flash.media.SoundMixer声明。由于不使用MovieClip绑定声音通道。所以描述全局声音通道的不是主时间轴而是SoundMixer类。由于与任何声道无关,所以SoundMixer是个静态类。
[1b]
属性:
[/1b]
SoundMixer.bufferTime:int
全局缓冲时间。如果在Sound类的play方法中未指定缓冲则由这里决定。
SoundMixer.soundTransform:SoundTransform
全局SoundTransform对象。控制总体声音效果。当应用了单个sound的soundTransform属性后再应用总声音特效。比如对于音量控制而言,这里是总音量控制。
[1b]
方法:
[/1b]
SoundMixer.stopAll():void
停止所有通道播放声音。
SoundMixer.areSoundsInaccessible():Boolean
如果声音因为安全模型不能访问则返回true.
SoundMixer.computeSpectrum(outputArray:ByteArray, FFTMode:Boolean = false, stretchFactor:int = 0):void
返回当前组合音频快照。波形被分为256级,分为左声道和右声道。被记录在outputArray中。outputArray是个二进制数组,拥有512个元素,类型为Numer。使用readFloat方法访问。前256个浮点数描述左声道的波形,后256个浮点数描述右声道的波形。浮点数也分为两种描述类型,FFTMode参数为true时使用频谱形式描述。范围为0~2的平方根。为false时采用波形描述,范围为-1~1。strechFactor是采样率。0为44HZ,1为22.HZ,以此类推。不管采样率为多少,始终都是256级别。采样率只会影响波形是否平滑。
[1b]
生成播放特效:
[/1b]
很多时候我们很羡慕media player的视觉特效吧。也很想知道是怎么做出来的。答案就是两个字——算法。现在利用as3提供的computeSpectrum可以实现我们的梦想,这是在as2中做不到的。下面我们来看看实现的原理。
不管使用什么算法,数据都是相同的。我们可以写一段小代码,输出某个音乐播放中的浮点状态。
导入一首歌曲,库中类名改为Song。在帧上输入如下代码:
var s:Song=new Song();
s.play();
var ba:ByteArray=new ByteArray();
stage.addEventListener(MouseEvent.CLICK,onClick);
function onClick(event:MouseEvent):void
{
SoundMixer.computeSpectrum(ba);
ba.position=0;
for(var i:int=0;i
{
var num:Number=ba.readFloat();
trace(num);
}
}
看到数据的组织结构后,下一步就可以发挥你编程与艺术的完美结合,而media player中的特效就是完美最好的榜样。在这么多另人眼花缭乱的效果中,条形与波形是最直接最简单的可视效果,它是数组的直接透视。现在我们来看看FFTMode为true和false时的波形区别。
用于控制声音的音量和平移。它跟多个类有联系,如:Microphone、NetStream、SoundChannel、SoundMixer都通过它调节声音效果。再次显示了声音架构的标准统一。更可贵的的是在SimpleButton和Sprite中也有这个属性。这代表着可以通过as调整flash 工具创作的按钮和影片剪辑中嵌入的声音。这种操控能力使得我们不必因为嵌入的声音对应调整音量上的干扰。但这并不代表我们能控制帧中声音的播放和停止。SoundTransform的构造函数为new SoundTransform(vol:Number=1,panning:Number=0)。构造函数的参数是它的属性。
[1b]
属性:
[/1b]
volume:Number
音量,范围为0~1。
pan:Number
声道平移,范围为-1~1。
leftToLeft:Number
左输入在左声道中的音量。
leftToRight:Number
左输入在右声道中的音量。
rightToLeft:Number
右输入在左声道中的音量。
rightToRight:Number
右输入在右声道中的音量。
SoundMixer类
[url=http://bbs.actionscript3.cn/attachment.php?aid=3954]
[/url]
[1b]快速傅立叶变换:
[/1b]快速傅氏变换,是离散傅氏变换的快速算法,它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的。它对傅氏变换的理论并没有新的发现,但是对于在计算机系统或者说数字系统中应用离散傅立叶变换,可以说是进了一大步。
设x(n)为N项的复数序列,由DFT变换,任一X(m)的计算都需要N次复数乘法和N-1次复数加法,而一次复数乘法等于四次实数乘法和两次实数加法,一次复数加法等于两次实数加法,即使把一次复数乘法和一次复数加法定义成一次“运算”(四次实数乘法和四次实数加法),那么求出N项复数序列的X(m), 即N点DFT变换大约就需要N2次运算。当N=1024点甚至更多的时候,需要N2=1048576次运算,在FFT中,利用WN的周期性和对称性,把一个N项序列(设N=2k,k为正整数),分为两个N/2项的子序列,每个N/2点DFT变换需要(N/2)2次运算,再用N次运算把两个N/2点的DFT 变换组合成一个N点的DFT变换。这样变换以后,总的运算次数就变成N+2(N/2)2=N+N2/2。继续上面的例子,N=1024时,总的运算次数就变成了525312次,节省了大约50%的运算量。而如果我们将这种“一分为二”的思想不断进行下去,直到分成两两一组的DFT运算单元,那么N点的 DFT变换就只需要Nlog2N次的运算,N在1024点时,运算量仅有10240次,是先前的直接算法的1%,点数越多,运算量的节约就越大,这就是 FFT的优越性.
这就是快速傅立叶转换的原理,但若频繁的把波形转化成频谱,除了算法上的处理,还会会消耗一定的cpu资源,这就是as3直接提供了波形和频谱两种浮点模式的原因。
[1b]创建自定义播放效果:[/1b]
下面一个是我用矢量生成的一个特效。实际上还可以使用位图,甚至结合flash工具创建的动画来制作特效。可以说这方面的发挥空间是无穷无尽的。这里我使用256条线段来描述声道的波幅,使用25层来描述场密度。效果如下:
点击下载此效果。
[url=http://bbs.actionscript3.cn/attachment.php?aid=3956]
image007.jpg
[/url]
[url=http://bbs.actionscript3.cn/attachment.php?aid=3978][1b]
水纹效果.rar
[/1b][/url]
(2008-05-12 18:15:40, Size: 24.8 KB, Downloads: 6)
说明:由于嵌入声音造成文档太大,所以载入百度中的歌曲。歌曲地址放在界面文本框中又会影响美观,所以我在路径下放了一个songURL.txt的文本。大家可以更改网址。但同时访问本地和网络又会造成安全问题。于是找来我的flashBox。flashBox可不管什么沙箱,本地网络通吃。而且全屏播放又能加快执行速度。可以当作屏保一样。真是很适合呢。
另外由于是矢量制作的因而不可能使得密度到达media player中水纹那样的效果,我们可以使用BitmapData来达到。但以像素级来描述波形将会更慢。因为flash player是不能象c++那样调用系统API的。也不能直接使用显卡支持的driectX或openGL。无法直接操作显存。即便如此,我还是要说说使用BitmapData代替矢量呈现的方法。原理并不是很困难。使用矢量只不过是绘制线条。我们可以写一个使用位图创建线条的函数。原理如下:想象线段是一条路径,一个爬虫从一段爬到另一端,走过的路径使用像素填充。这样一来就非常简单了。结合Tween来进行像素填充,为位图创建一个lineTo函数,代码改动基本上不大。详细代码我就不写了。请大家自己尝试。
以上是我对as3架构的理解,不到之处还请见谅。谢谢!
本文转自:http://www.5uflash.com/flashjiaocheng/Flash-as3-jiaocheng/3243.html