2009-06-19 6 views
0

J'ai le codeComment arrêter un son dans AS3 pour reproduire un autre son?

if(_snd !=null) 
{ 
    _channel.stop(); 
} 

_snd = new SoundBeep(); 
_channel = new SoundChannel(); 
_channel = _snd.play(); 

mais si l'utilisateur clique plusieurs fois rapide, flash attend un peu, puis émet un signal sonore rapide. Je ne suis pas sûr de ce qui ne va pas. Des idées?

Répondre

3

essayez ceci:

if(_channel != null) 
{ 
    _channel.stop(); 
} 

_snd = new SoundBeep(); 
_channel = _snd.play(); 
+0

Ceci est beaucoup plus concis que cette autre folie @Paul avait documenté. – charlesclements

1

J'ai eu ce problème - il était affolant! Dans d'autres langages (java, C#) j'aurais utilisé la synchronisation autour d'une section critique. En Flash, j'ai dû faire semblant. Ce n'est pas parfait - encore une petite chance que le second son joue sur le premier, mais il est très petit.

Voici la clé: implémenter une combinaison des modèles d'usine et de singleton. Une découverte importante que j'ai faite est que j'ai besoin de copier ma variable membre dans une variable temporaire avant que j'agisse dessus, au cas où un autre appel à l'objet essaye d'échanger un nouveau son avant que je finisse de nettoyer le le précédent. Voici un extrait qui fait que:

/** 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; 
     }     
    } 

Notez que je non seulement copier AudioChannel à tempAudioChannel, mais je dois null sur AudioChannel jusqu'à ce que la méthode est presque complète, puis réglez de nouveau sa valeur. Je suis à l'abri des erreurs de synchronisation pendant tout sauf la courte période entre le moment où je teste la valeur de l'audioChannel et quand j'ai terminé la copie et l'annulation, ce qui devrait être extrêmement bref.

Retour à mon commentaire sur les modèles d'usine et de singleton. Pendant qu'un son est en train d'être nettoyé - arrêté et mis au rebut - un autre peut commencer, j'ai donc besoin de plus d'un SoundManager. Ainsi, je crée un SoundManager par nom de fichier audio. Chaque fois que j'ai besoin d'agir sur un fichier audio, la classe SoundManager me dit quelle instance de gestionnaire appeler, donc je n'entends pas les doublons du même son commencé en conséquence.

Le code suivant est un SoundManager complet de mon projet commercial actuel. Il fait référence à une autre classe non représentée - Configuration, mais cette classe gère uniquement l'obtention des chemins d'accès aux fichiers audio à partir d'un fichier de configuration et l'obtention des valeurs d'amplification à partir d'un autre fichier XML. (Le client fournissait des fichiers qui n'étaient pas normalisés au même niveau de volume, donc nous devions le faire pour eux.) Vous devriez donc éditer toutes les références à l'objet config hors de ce code.

NOTE: Je fais usage de l'astuce variable temporaire ALL OVER THE PLACE, sur au moins deux variables membres, audio et audioChannel.

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);  
       } 

      } 
     } 

    } 

} 
Questions connexes