if(_snd !=null)
{
_channel.stop();
}
_snd = new SoundBeep();
_channel = new SoundChannel();
_channel = _snd.play();
,但如果用戶點擊了幾次快速,閃存等待了一下,然後發出蜂鳴聲他們快。我不確定發生了什麼問題。有任何想法嗎?
if(_snd !=null)
{
_channel.stop();
}
_snd = new SoundBeep();
_channel = new SoundChannel();
_channel = _snd.play();
,但如果用戶點擊了幾次快速,閃存等待了一下,然後發出蜂鳴聲他們快。我不確定發生了什麼問題。有任何想法嗎?
試試這個:
if(_channel != null)
{
_channel.stop();
}
_snd = new SoundBeep();
_channel = _snd.play();
我有這個問題 - 這是鬱悶!在其他語言(Java,C#)中,我會在關鍵部分使用同步。在Flash中,我不得不僞造它。它並不完美 - 第二個聲音在第一聲中播放的可能性很小,但它非常小。
這裏是關鍵:實現工廠和單件模式的組合。
我做出的一個重要發現是,在我採取行動之前,我需要將我的成員變量複製到一個臨時變量中,以防其他一些對該對象的調用在完成清理之前嘗試在新聲音中進行交換前一個。這裏是做一個片段:
/** Seek into the audio to the given position in milliseconds. */
public function seek(position:Number, resumePlay:Boolean) {
trace("--> Seek(" + position + ") " + name);
var tempAudioChannel:SoundChannel = audioChannel;
audioChannel = null;
if (tempAudioChannel != null) {
tempAudioChannel.stop();
tempAudioChannel.removeEventListener(Event.SOUND_COMPLETE, onAudioCompleteHandler);
}
audioLastPosition = position;
if (resumePlay) {
tempAudioChannel = audio.play(audioLastPosition);
tempAudioChannel.addEventListener(Event.SOUND_COMPLETE, onAudioCompleteHandler);
audioChannel = tempAudioChannel;
}
}
注意如何我不僅從AudioChannel中複製到tempAudioChannel,但我必須空出AudioChannel中,直到該方法已接近完成,然後再設置它的值。除了在測試audioChannel的值和完成複製和清零之間的短時間段內,我可以安全地避免同步錯誤,這應該非常簡短。
回到我對工廠和單件模式的評論。當一個聲音被清除 - 停止並被丟棄 - 另一個聲音可能正在開始,所以我需要多個SoundManager。因此,我爲每個音頻文件名創建一個SoundManager。每次我需要對一個音頻文件進行操作時,SoundManager類會告訴我要調用哪個管理器實例,因此我不會聽到因此而開始的相同聲音的重複。
以下代碼是我當前商業項目中的完整SoundManager。它引用另一個未示出的類 - 配置,但該類僅處理從配置文件獲取音頻文件的路徑,並從另一個XML文件獲取放大值。 (客戶端提供的文件沒有被歸一化到相同的音量級別,所以我們必須爲它們做這件事。)因此,您必須編輯所有對此配置對象的引用。
注意:我至少使用了兩個成員變量audio和audioChannel中的臨時變量技巧ALL OVER THE PLACE。
package com.vpg.rns.audio {
import flash.system.System;
import flash.events.*;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.net.NetStream;
import flash.net.NetConnection;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.errors.IOError;
/** Load a Sound from an MP3 file, then hold onto it and mediate playing and disposing of the object, and keep the several Sounds from interfering with one another.
* This class stores each SoundManager instance in a collection keyed by the step name.
*
* Do not call the Constructor directly to acquire a SoundManager. Instead, use getInstance.
*/
public class SoundManager {
private static var allSounds:Object = new Object();
public static function getInstance(name:String, config:Configuration) {
var soundMgr:SoundManager = allSounds[name];
if (soundMgr == null) {
soundMgr = new SoundManager(name, config);
addSoundManager(soundMgr);
}
return soundMgr;
}
private static function addSoundManager(soundMgr:SoundManager) {
allSounds[soundMgr.name] = soundMgr;
}
private static function removeSoundManager(name) {
var soundMgr:SoundManager = allSounds[name] as SoundManager;
allSounds[name] = null;
if (soundMgr != null) {
soundMgr.dispose();
}
}
public static function removeAllManagers() {
var allSoundsTemp:Object = allSounds;
allSounds = new Object();
for (var prop:String in allSoundsTemp){
var soundMgr:SoundManager = allSoundsTemp[prop] as SoundManager;
if (soundMgr != null) {
soundMgr.dispose();
}
}
}
public static function stopManagers(exceptMgrName:String) {
for (var prop:String in allSounds){
var soundMgr:SoundManager = allSounds[prop] as SoundManager;
if (soundMgr != null && soundMgr.name != exceptMgrName) {
soundMgr.stop();
}
}
}
private var mgrName:String;
public function get name():String { return mgrName; }
public var config:Configuration;
public var audio:Sound;
public var audioChannel:SoundChannel;
public var audioLoadStatus:String; // States: no audio, loading, loaded, ready. "loaded" means loaded enough to start playing, but possibly still loading more.
private var rootPath:String;
private var dataPath:String;
private var mediaPath:String;
public var audioFilename:String;
private var onLoadHandler:Function; // Called When loading file is complete
private var onAudioCompleteHandler:Function; // Called When playing audio is complete
public var duration:Number;
public var audioLastPosition:Number;
private var volumeAdjustment:Number;
/** Construct a SoundManager. Do not call this directy. Use the factory method getInstance instead. */
function SoundManager(name:String, config:Configuration) {
mgrName = name;
this.config = config;
audioLoadStatus = "no audio";
duration = 0;
audioLastPosition = 0;
volumeAdjustment = 1;
}
/*
* Load the audio, then tigger the loading of the optional cue point xml file, and initialization of the controls.
*
* @param rootDirectory ...... Directory containing the config file.
* @param dataDirectory ...... Directory where cuepoint data is located. Expect the cuepoints file to be in the xml/cuepoints subdirectory.
* @param mediaDirectory ..... Directory where audio files are located.
* @param audioFile .......... Name of audio file with extension. Does not include path.
* @param onLoadHandler ...... Called once the audio is loaded, so the caller can start playing it.
*/
public function loadAudio(rootDirectory:String, dataDirectory:String, mediaDirectory:String, audioFile:String, onLoadHandler:Function, onAudioCompleteHandler:Function) {
audioLoadStatus = "loading";
//Load the audio file.
this.rootPath = rootDirectory;
this.dataPath = dataDirectory;
this.mediaPath = mediaDirectory;
this.audioFilename = audioFile;
this.onLoadHandler = onLoadHandler;
this.onAudioCompleteHandler = onAudioCompleteHandler;
this.volumeAdjustment = config.getAmplification(this.audioFilename);
var mySoundReq:URLRequest = new URLRequest(config.osSpecificPath(mediaPath + "/" + audioFilename));
audio = new Sound();
audio.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void{ trace("SoundLoader.loadAudio ERROR!!!"); trace(e); });
if (config.platform == "Flash_IDE") {
audio.addEventListener(Event.COMPLETE, audioReady);
}
else {
// We can't afford to wait for whole audio to load, so wait until some of it is loaded.
audio.addEventListener(ProgressEvent.PROGRESS, audioProgress1);
audio.addEventListener(Event.COMPLETE, audioCompletelyLoaded);
}
audio.load(mySoundReq);
}
// A sufficient portion of the audio has loaded, so start playing.
private function audioProgress1(evt:ProgressEvent) {
var loadPercent:Number = Math.round(100 * evt.bytesLoaded/evt.bytesTotal);
if (loadPercent > 10 && audioLoadStatus == "loading") { //TODO: Deduce a better threshold.
var audioTemp:Sound = audio;
audioTemp.removeEventListener(ProgressEvent.PROGRESS, audioProgress1);
audioTemp.addEventListener(ProgressEvent.PROGRESS, audioProgress2);
audioLoaded();
}
}
// As the audio continues to load, the duration lengthens, affecting the scrubber thumb position.
private function audioProgress2(evt:ProgressEvent) {
var loadPercent:Number = Math.round(100 * evt.bytesLoaded/evt.bytesTotal);
if (audioLoadStatus == "loading" || audioLoadStatus == "loaded") {
var audioTemp:Sound = audio;
if (audioTemp != null) {
duration = audioTemp.length/1000; // Convert from milliseconds to seconds.
}
}
}
private function audioCompletelyLoaded(evt:Event) {
var audioTemp:Sound = audio;
if (audioTemp != null) {
audioTemp.removeEventListener(Event.COMPLETE, audioCompletelyLoaded);
audioTemp.removeEventListener(ProgressEvent.PROGRESS, audioProgress1);
audioTemp.removeEventListener(ProgressEvent.PROGRESS, audioProgress2);
duration = audioTemp.length/1000;
}
}
private function audioReady(evt:Event) {
var audioTemp:Sound = audio;
if (audioTemp != null) {
audioTemp.removeEventListener(Event.COMPLETE, audioReady);
audioLoaded();
}
}
private function audioLoaded() {
audioLoadStatus = "loaded";
var audioTemp:Sound = audio;
if (audioTemp != null) {
duration = audioTemp.length/1000; // Convert from milliseconds to seconds.
var audioChannelTemp:SoundChannel;
audioChannelTemp = audioTemp.play();
audioChannelTemp.stop();
audioChannel = null;
audioLastPosition = 0;
audioLoadStatus = "ready";
onLoadHandler(this);
}
}
public function play() {
pause();
trace("--> Play " + name);
audioChannel = audio.play(audioLastPosition);
audioChannel.addEventListener(Event.SOUND_COMPLETE, onAudioCompleteHandler);
}
/** Seek into the audio to the given position in milliseconds. */
public function seek(position:Number, resumePlay:Boolean) {
trace("--> Seek(" + position + ") " + name);
var tempAudioChannel:SoundChannel = audioChannel;
audioChannel = null;
if (tempAudioChannel != null) {
tempAudioChannel.stop();
tempAudioChannel.removeEventListener(Event.SOUND_COMPLETE, onAudioCompleteHandler);
}
audioLastPosition = position;
if (resumePlay) {
tempAudioChannel = audio.play(audioLastPosition);
tempAudioChannel.addEventListener(Event.SOUND_COMPLETE, onAudioCompleteHandler);
audioChannel = tempAudioChannel;
}
}
public function pause() {
trace("--> Pause " + name);
if (audioChannel != null) {
audioLastPosition = audioChannel.position;
audioChannel.stop();
audioChannel.removeEventListener(Event.SOUND_COMPLETE, onAudioCompleteHandler);
audioChannel = null;
}
}
public function stop() {
trace("--> Stop " + name);
audioLastPosition = 0;
if (audioChannel != null) {
audioChannel.stop();
audioChannel.removeEventListener(Event.SOUND_COMPLETE, onAudioCompleteHandler);
audioChannel = null;
}
}
/** Elapsed time of audio in seconds. */
public function get audioElapsed():Number {
if (audioLoadStatus == "ready") {
if (audioChannel != null) {
return audioChannel.position/1000.0;
}
else {
return audioLastPosition/1000.0;
}
}
else {
return 0;
}
}
/** Set the audio volume to a number between zero (mute) and one (loud). */
public function setVolume(volume:Number, soundTransform:SoundTransform = null) {
if (audioChannel != null) {
if (soundTransform == null) {
soundTransform = new SoundTransform();
}
if (volumeAdjustment != 1.0) {
trace("setVolume using volume adjustment of " + volumeAdjustment);
}
soundTransform.volume = volume * volumeAdjustment;
audioChannel.soundTransform = soundTransform;
}
}
public function unloadAudio() {
dispose();
}
private function dispose() {
audioLoadStatus = "no audio";
var audioTemp:Sound = audio;
audio = null;
stop();
if (audioTemp != null) {
try {
audioTemp.close();
}
catch (error:IOError) {
trace("Error: Couldn't close audio stream: " + error.message);
}
}
}
}
}
這比其他瘋狂的@Paul記錄的方式更簡潔。 – charlesclements 2015-02-09 20:20:15