如何在AS3中停止播放声音以播放其他声音?

时间:2009-06-19 20:00:54

标签: actionscript-3 audio

我有代码

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

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

但是如果用户快速点击几次,闪光灯会稍稍等一下,然后快速发出哔哔声。我不确定出了什么问题。有什么想法吗?

2 个答案:

答案 0 :(得分:3)

试试这个:

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

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

答案 1 :(得分:1)

我遇到了这个问题 - 这令人抓狂!在其他语言(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。它引用了另一个未显示的类--Configuration,但该类只处理从配置文件获取音频文件的路径,并从另一个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);    
                }

            }
        }

    }

}