Как остановить звук в AS3 для воспроизведения другого звука? - PullRequest
0 голосов
/ 20 июня 2009

У меня есть код

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

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

но если пользователь нажимает несколько раз быстро, вспышка немного ждет, а затем быстро издает звуковой сигнал. Я не уверен, что происходит не так. Есть идеи?

Ответы [ 2 ]

3 голосов
/ 20 июня 2009

попробуйте это:

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

_snd = new SoundBeep();
_channel = _snd.play();
1 голос
/ 23 июня 2009

У меня была эта проблема - это сводило с ума! В других языках (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.

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

            }
        }

    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...