我有代码
if(_snd !=null)
{
_channel.stop();
}
_snd = new SoundBeep();
_channel = new SoundChannel();
_channel = _snd.play();
但是如果用户快速点击几次,闪光灯会稍稍等一下,然后快速发出哔哔声。我不确定出了什么问题。有什么想法吗?
答案 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);
}
}
}
}
}