Categories Displayed in Flash

Archive for the 'Sound' Category

AS3: Caching Sound, Error #2037: Functions called in incorrect sequence, or earlier call was unsuccessful.

Sound object is a single use item. Once it's been loaded you can't use it again, regardless if it's the same mp3 or a different one, or null, you'll get the cryptic error "Functions called in incorrect sequence"

Example: do this:

s = new Sound(req);

///do a bunch of stuff, and at some time later try to load a new mp3 in
s.load(req);

You will get

Error #2037: Functions called in incorrect sequence, or earlier call was unsuccessful.

For each new mp3 you need to create a new Sound object. Note that SoundChannel is like a playhead, more than one of them can be accessing the Sound data, so you can create chorus/echo effects by triggering play() multiple times at different time/offsets. Here's a sound player that cycles through 2 mp3s with no errors, and caches the results.

import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.events.*;
import com.troyworks.core.persistance.CacheEvent;

var mp3s:Array = ["test.mp3","BT - Satellite.mp3"];

var cache:Object = new Object();

playNext_mc.addEventListener(MouseEvent.CLICK, playSound);

var s:Sound;
var _channel:SoundChannel;

function getNextAudioURL():String {
var curURL:String = mp3s.shift();
mp3s.push(curURL);
return curURL;
}
function playSound(evt:Event = null) {
var audioSwfURL:String = getNextAudioURL();
trace("***************************************************************");
trace("playSound " +audioSwfURL);
//trace(" attempting to restore " + audioSwfURL + " into AUDIO");
try {
if (_channel != null) {
//_channel.stop(); //UNCOMMENT ME TO STOP THE PREVIOUS PLAYING TRACK
//s.load(); //won't work with progressive
//s.close(); //won't work with progressive
}
if (audioSwfURL != null && cache[audioSwfURL] == null) {

//////////// normal audio loading ////////////
var req:URLRequest = new URLRequest(audioSwfURL);
s = new Sound();
//speaker_mc.display_txt.text =".";
s.addEventListener(Event.COMPLETE, onSoundLoaded);
s.addEventListener(IOErrorEvent.IO_ERROR, onSoundFailedToLoad);
s.load(req);
}else{

//////////// use cached audio ////////////////
trace("hitting cache");
var cevt:CacheEvent = new CacheEvent(Event.COMPLETE);
cevt.target = cache[audioSwfURL];
onSoundLoaded(cevt);
}
} catch (err:IOError) {
trace(err.toString());
} catch (err:Error) {
trace(err.toString());
}
}

function onSoundFailedToLoad(Event:IOErrorEvent):void {
trace("onSoundLoaded **FAILED**");
//speaker_mc.display_txt.text = "!";
}
function onSoundLoaded(event:Event ):void {
//speaker_mc.display_txt.text = "";

var localSound:Sound = event.target as Sound;

////////////// parse the key mp3 name /////////////////

//NOTE: a cleaner approach way would to be a Proxy that passes arguments along with the event, created during //the listener

var url:String = localSound.url;
var a:Number = url.lastIndexOf("/");
var b:Number = url.lastIndexOf("\\");
var c:Number = Math.max(a,b);
var url2:String = url.substr(c+1, url.length);
trace("onSoundLoaded" + url2);

//////////// cache it ////////////////////////
cache[url2] = localSound;
_channel = localSound.play();
localSound.addEventListener(Event.SOUND_COMPLETE, onPlayComplete);

}
function onPlayComplete(evt:Event):void {
//if has more sounds play them else, move to next slide
//content_mc.play();
}
playSound();

Caching Sounds...or anything for that matter

Once you've loaded a Sound(Bitmap etc) it's in memory so if your reusing it (e.g. Sound FX) there's no need to reload it. Here you can use a  cache, using the name of the sound as a key, and a CustomEvent to mimic the loaded event, as the default Event target is readonly, so you can't mimic the call coming from the cache. The solution is to override it, and call the listeners directly if you can't redispatch using the original sound.  The advantage is the loading event, don't know the difference between the real or loaded. The cache might also be used for offline/online type activities. But of course be aware you're memory useage for caching like this can get large quickly.

package com.troyworks.core.persistance {
import flash.events.*;
public class CacheEvent extends Event {

private var _overriddenTarget:Object;

public function CacheEvent(type:String,bubbles:Boolean=false, cancelable:Boolean=false) {
super(type, bubbles, cancelable);
}
override public function get target():Object{
return _overriddenTarget;
}
public function set target(target:Object):void{
_overriddenTarget = target;
}

}

}