2009年的最后一天了,送ws的朋友们一个播放器吧!貌似很有记念意义。按惯例先看一下最终效果:呵呵,界面是我自己在ps中画的,感觉也不是很丑。
那么开始吧!大家先下载一下源文件。对着源文件的结构看教程。我将整个播放器做到一个元件中(Control_panel),再给它绑定一个类(sound_lib.Control_panel),所有的代码就写在这个类里面了。这样做的好处是:日后要用到它时,直接把Control_panel元件拖到你要用的地方就Ok了!
接下来就是code了:要做音乐播放器,最重要的两个类当然就是Sound和SoundChannel。
先试一下,怎样让一首mp3播放起来:
//建立一个Sound类 sound=new Sound() //加载mp3文件 sound.load(new URLRequest(“你的mp3路径”)) //播放 sound.play()
Ok了!
完整代码:
package sound_lib{ import flash.display.* import flash.media.Sound import flash.media.SoundChannel import flash.net.URLRequest public class Control_panel extends MovieClip{ private var sound:Sound private const sound_url:String="sound_data/m01.mp3" public function Control_panel(){ init() } private function init(){ sound_init() } function sound_init(){ sound=new Sound() sound.load(new URLRequest(sound_url)) sound.play() } } }
Ctrl+Enter一下。是不是听到音乐了?啊?没听到?看看sound_data目录下是否放了mp3文件没?如果一切正常的话,你现在应该在享受你的音乐了。
呵呵。不过别忘了大多数播放器是要用在web上的。有时候声音播放的速度可能比下载的速度还要快,这种情况下声音播放就会暂停等待数据下载,为了更好的处理这个环节,我们可以设置一个数据缓冲区,当声音数据下载到一定数量时再进行播放,这样的话即使下载速度偶尔变慢也不会影响正常播放。默认下Sound对象只创建1秒钟的缓冲,也就是说要想立即播放也需要等待1秒钟的缓冲,缓冲区的数据播放完后要想再次播放还要至少等1秒钟缓冲时间。显然如果网速较慢的话且缓冲为1秒时,在这种情况下音乐的播放将会给人很”卡”的感觉。所以我们必需手动设置一下缓冲:
设置缓冲需要用到SoundLoaderContext类:建立一个SoundLoaderContext对象,然后设置它的bufferTime属性就行了。最后将SoundLoaderContext对象传给sound对象的load()方法的参数就行了。
//建立一个SoundLoaderContext类,设置bufferTime为5秒 var buffer:SoundLoaderContext=new SoundLoaderContext(5000) sound=new Sound() sound.load(new URLRequest(sound_url),buffer) sound.play():
这样,即使放到web上也不成问题了。不过现在只是能正常播放了,我们还得控制它才行嘛!最常见的控制就是暂停与回放了。声音的暂停与回放不像视频流那样,可以直接pause和resume来操作。看看Soun类,大家可能会调用Sound对象的close( )方法可以停止播放,但是这样也停止了声音流,要想重新播放,必须再次调用load( )方法。这显然不是我们想要的。这里我们终于要用到上面所提到的SoundChannel了,其实对音频的绝大多数操作都是SoundChannel类来完成的。SoundChannel的stop()方法可以让音乐暂停而不影响声音流的中断。于是我们可以用SoundChannel对象的stop()方法与Sound对象的play()方法来暂停与回流音频流。但是,当再次调用play( )方法时,音乐会从头开始播放而不是从暂停的地方开始,这个时候就要用到SoundChannel类的position属性了:position属性是用来记录音乐播放头的。我们可以在暂停时,记录下position的值,然后再次调play()时,将它传给play()方法的参数。这样就达到了最终目:
sound_channel.stop() //记录position属性 position=sound_channel.position sound_channel=sound.play(position)
package sound_lib{ import flash.display.* import flash.media.Sound import flash.media.SoundChannel import flash.media.SoundLoaderContext import flash.events.* import flash.net.URLRequest public class Control_panel extends MovieClip{ private var sound:Sound private const sound_url:String="sound_data/m01.mp3" private var sound_channel:SoundChannel private var position:int private var is_play:Boolean public function Control_panel(){ addEventListener(Event.ADDED_TO_STAGE,add_to_stage) } private function add_to_stage(_evt:Event):void{ init() } private function init(){ sound_init() control_init() } //==================sound part====================// function sound_init(){ var buffer:SoundLoaderContext=new SoundLoaderContext(5000) sound=new Sound() sound.load(new URLRequest(sound_url),buffer) sound_channel=sound.play() is_play=true play_mc.visible=false } //===================control part=================// function control_init(){ pause_mc._btn.addEventListener(MouseEvent.CLICK,is_play_Handler) play_mc._btn.addEventListener(MouseEvent.CLICK,is_play_Handler) } private function is_play_Handler(_evt:MouseEvent):void{ if(is_play){ is_play=false position=sound_channel.position sound_channel.stop() play_mc.visible=true pause_mc.visible=false }else{ is_play=true sound_channel=sound.play(position) play_mc.visible=false pause_mc.visible=true } } } }
跟踪声音的加载进度与播放进度
要得到声音的加载进度是很简单的,Sound类本身有bytesLoaded属性与bytesTotal属性。只要在progress事件中监测到这个属性值就Ok了具体操作如下:
private function sound_loading_Handler(_evt:ProgressEvent):void{ var loaded_per=Math.ceil(100*_evt.bytesLoaded/_evt.bytesTotal)/100 scroll_bar.loading_bar.scaleX=loaded_per } // loaded_per就是加载进度的百分比了,简单吧!
嗯原理搞清楚了就来试试吧:
var loaded=sound.bytesLoaded var total=sound.bytesTotal sound_length=sound.length if(total>0){ var loaded_per=loaded/total scroll_bar.loading_bar.scaleX=loaded_per sound_length=length/loaded_per position=sound_channel.position var played_per=position/sound_length }
package sound_lib{ import flash.display.* import flash.media.Sound import flash.media.SoundChannel import flash.media.SoundLoaderContext import flash.events.* import flash.net.URLRequest public class Control_panel extends MovieClip{ private var sound:Sound private const sound_url:String="sound_data/m01.mp3" private var sound_channel:SoundChannel private var position:int private var sound_length private var is_play:Boolean private var loadEnd:Boolean=false public function Control_panel(){ addEventListener(Event.ADDED_TO_STAGE,add_to_stage) } private function add_to_stage(_evt:Event):void{ init() } private function init(){ sound_init() control_init() } //==================sound part====================// function sound_init(){ var buffer:SoundLoaderContext=new SoundLoaderContext(5000) sound=new Sound() sound.load(new URLRequest(sound_url),buffer) sound_channel=sound.play() sound.addEventListener(ProgressEvent.PROGRESS,sound_loading_Handler) is_play=true play_mc.visible=false } //===================control part=================// function control_init(){ pause_mc._btn.addEventListener(MouseEvent.CLICK,is_play_Handler) play_mc._btn.addEventListener(MouseEvent.CLICK,is_play_Handler) addEventListener(Event.ENTER_FRAME,progress_Handler) } private function is_play_Handler(_evt:MouseEvent):void{ if(is_play){ is_play=false position=sound_channel.position sound_channel.stop() play_mc.visible=true pause_mc.visible=false }else{ is_play=true sound_channel=sound.play(position) play_mc.visible=false pause_mc.visible=true } } private function sound_loading_Handler(_evt:ProgressEvent):void{ var loaded_per=Math.ceil(100*_evt.bytesLoaded/_evt.bytesTotal)/100 if(loaded_per>=100) loadEnd=true scroll_bar.loading_bar.scaleX=loaded_per } private function progress_Handler(_evt:Event):void{ var loaded=sound.bytesLoaded var total=sound.bytesTotal sound_length=sound.length if(total>0){ var loaded_per=loaded/total if(loaded_per>=1) loadEnd=true scroll_bar.loading_bar.scaleX=loaded_per sound_length=sound_length/loaded_per position=sound_channel.position var played_per=position/sound_length scroll_bar.dragger.x=(scroll_bar.scroll_bg.width-scroll_bar.dragger.width)*played_per } } } }
这才只能让用户看到播放进度而以。下面我们还要让用户能控制它。要控制它用到了我们常见的滚动条。由于滚动条是我们很常用的东东,所以我把它写成了一个类。(Sound_lib/Scroll_bar.as),类很简单,如果看不懂的话,可以参考控制音量的滚动条。其实这里用Scroll_bar.as不大合适,不过我还是想偷一下懒!
说到这里,怎么定位播放头呢?想想“声音的暂停与回放”是怎么做的,我们就可以得找出办法了用sound.play(position)。Position的值就更容易得到了: sound_length* per。但是这里值得注意的是play()之前要把之前的sound_channel 停止,要不就出现多重声音了。。。OK了。再跟据以上思路完善一下你的code吧!
package sound_lib{ import flash.display.* import flash.media.Sound import flash.media.SoundChannel import flash.media.SoundLoaderContext import flash.events.* import flash.net.URLRequest public class Control_panel extends MovieClip{ private var sound:Sound private const sound_url:String="sound_data/m01.mp3" private var sound_channel:SoundChannel private var position:int private var sound_length private var is_play:Boolean private var loadEnd:Boolean=false public function Control_panel(){ addEventListener(Event.ADDED_TO_STAGE,add_to_stage) } private function add_to_stage(_evt:Event):void{ init() } private function init(){ sound_init() control_init() } //==================sound part====================// function sound_init(){ var buffer:SoundLoaderContext=new SoundLoaderContext(5000) sound=new Sound() sound.load(new URLRequest(sound_url),buffer) sound_channel=sound.play() sound.addEventListener(ProgressEvent.PROGRESS,sound_loading_Handler) is_play=true play_mc.visible=false } //===================control part=================// function control_init(){ pause_mc._btn.addEventListener(MouseEvent.CLICK,is_play_Handler) play_mc._btn.addEventListener(MouseEvent.CLICK,is_play_Handler) addEventListener(Event.ENTER_FRAME,progress_Handler) } private function is_play_Handler(_evt:MouseEvent):void{ if(is_play){ is_play=false position=sound_channel.position sound_channel.stop() play_mc.visible=true pause_mc.visible=false }else{ is_play=true sound_channel=sound.play(position) play_mc.visible=false pause_mc.visible=true } } private function sound_loading_Handler(_evt:ProgressEvent):void{ var loaded_per=Math.ceil(100*_evt.bytesLoaded/_evt.bytesTotal)/100 if(loaded_per>=100) loadEnd=true scroll_bar.loading_bar.scaleX=loaded_per } private function progress_Handler(_evt:Event):void{ var loaded=sound.bytesLoaded var total=sound.bytesTotal sound_length=sound.length if(total>0){ var loaded_per=loaded/total if(loaded_per>=1) loadEnd=true scroll_bar.loading_bar.scaleX=loaded_per sound_length=sound_length/loaded_per position=sound_channel.position var played_per=position/sound_length scroll_bar.dragger.x=(scroll_bar.scroll_bg.width-scroll_bar.dragger.width)*played_per } } } }
要改调节音量需用到SoundChannel对象的soundTransform属性,它是SoundTransform的类实例。操用步骤:1.建立一个soundTransform对象。2.设置soundTransform对象的volume值(0-1)3.将soundTransform对象赋给SoundChannel的soundTransform属性属性。为了方便我将其写成函数
function set_volume(temp:SoundChannel,temp_value:Number){ var sound_trf:SoundTransform=new SoundTransform() sound_trf.volume=temp_value temp.soundTransform=sound_trf }
package sound_lib{ import flash.display.* import flash.events.* import flash.geom.* public class Vol_scroll_bar extends MovieClip{ public static const SCROLL:String="scroll" public var per public function Vol_scroll_bar(){ this.addEventListener(Event.ADDED_TO_STAGE,add_to_stage) } private function add_to_stage(_evt:Event):void{ init() } private function init(){ dragger._btn.addEventListener(MouseEvent.MOUSE_DOWN,press_Handler) dragger._btn.addEventListener(MouseEvent.MOUSE_UP,release_Handler) } private function press_Handler(_evt:MouseEvent):void{ dragger.startDrag(false,new Rectangle(scroll_bg.x,scroll_bg.y,scroll_bg.width-dragger.width,0)) scroll_bg.addEventListener(Event.ENTER_FRAME,move_Handler) stage.addEventListener(MouseEvent.MOUSE_UP,release_Handler) } private function release_Handler(_evt:MouseEvent):void{ dragger.stopDrag() scroll_bg.removeEventListener(Event.ENTER_FRAME,move_Handler) stage.removeEventListener(MouseEvent.MOUSE_UP,release_Handler) } private function move_Handler(_evt:Event):void{ per=Math.ceil(100*dragger.x/(scroll_bg.width-dragger.width))/100 this.dispatchEvent(new Event(SCROLL)) } } }
看到this.dispatchEvent(new Event(SCROLL))这句没?在拖动滑块的同时给该类加入了一个事件,所以我们只需在父类中贞听这个事件就可以了:
private function Vol_scroll_init(){ vol_scroll_bar.addEventListener(Vol_scroll_bar.SCROLL,get_per_Handler) } private function get_per_Handler(_evt):void{ trace(vol_scroll_bar.per) }
private function Vol_scroll_init(){ vol_scroll_bar.addEventListener(Vol_scroll_bar.SCROLL,get_per_Handler) } private function get_per_Handler(_evt):void{ set_volume(sound_channel,Number(vol_scroll_bar.per)) trace(vol_scroll_bar.per) } function set_volume(temp:SoundChannel,temp_value:Number){ var sound_trf:SoundTransform=new SoundTransform() sound_trf.volume=temp_value temp.soundTransform=sound_trf }
MP3 文件大多包含一些如songname, artist, album, genre, year等元数据,不过这些并不是都有。通过Sound对象的id3属性可获得这些数据。这个属性其实是ID3Info的类实例,它包含下列属性:artist(歌手名)、songName(歌曲名).........等。获取文件的ID3数据很简单:通过监听Sound对象的ID3事件就行了
sound.addEventListener(Event.ID3,ID3_Handler) private function ID3_Handler(_evt:Event):void{ trace(sound.id3.songName) }
private function EncodeUtf8(str : String):String { var oriByteArr : ByteArray = new ByteArray(); oriByteArr.writeUTFBytes (str); var tempByteArr : ByteArray = new ByteArray(); for (var i = 0; i<oriByteArr.length; i++) { if (oriByteArr[i] == 194) { tempByteArr.writeByte(oriByteArr[i+1]); i++; } else if (oriByteArr[i] == 195) { tempByteArr.writeByte(oriByteArr[i+1] + 64); i++; } else { tempByteArr.writeByte(oriByteArr[i]); } } tempByteArr.position = 0; return tempByteArr.readMultiByte(tempByteArr.bytesAvailable,"chinese"); }
自己看源吧。