Check if song is buffering in AS3 - actionscript-3

I have the following piece of code:
var song:Sound;
var sndChannel:SoundChannel;
var context:SoundLoaderContext = new SoundLoaderContext(2000);
function songLoad():void {
song.load(new URLRequest(songs[selected]),context);
sndChannel = song.play();
}
Now I want to be able to check if the song is buffering or not. Is there a way to do this? Or should I approach it differently?

Seems like you could use the isBuffering property of the Sound object.
Maybe you could check it periodically with a Timer or an Event.EnterFrame listener, as long as the sound has not been completely downloaded (i.e. until Event.COMPLETE fires). After that point, it makes no sense to check isBuffering, for obvious reasons, so you could remove the Timer or EnterFrame.

Related

AS3 Simple way to check if sound is finished

I have a sound that plays whenever I rollover an object. To make it less annoying I want the sound to play only if it's not playing already. Since I need this with a couple of different sounds I don't want to use a timer (if not absolutely necessary). I found this:
var channel:SoundChannel = snd.play();
channel.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
public function onPlaybackComplete(event:Event)
{
trace("The sound has finished playing.");
}
but I'm not sure I can use it since I have a background music as well. Any tips?
You can use such a trick selectively. Still, if you plan to have a lot of such objects that trigger sounds on mouse-overs, you might decide to have a manager class or data table that would have an entry per such an object, and its field would have a true if the sound is played, and a listener assigned as such would clear the value in that field, deriving the correct entry from registered set of SoundChannels. A premade sound manager would do, but you'd better make a tailored one. An example:
public class SoundManager {
private var _fhOPO:Dictionary;
private var _bhOPO:Dictionary;
// going sophisticated. Names stand for "forward hash once per object" and "backward"
// forward hash stores links to SoundChannel object, backward stores link to object from
// a SoundChannel object.
// initialization code skipped
public static function psOPO(snd:Sound,ob:Object):void
{
// platy sound once per object
if (_fhOPO[ob]) return; // sound is being played
var sc:SoundChannel=snd.play();
_fhOPO[ob]=sc;
_bhOPO[sc]=ob;
sc.addEventListener(Event.SOUND_COMPLETE, _cleanup);
}
private static function _cleanup(e:Event):void
{
var sc:SoundChannel=event.target as SoundChannel;
if (!sc) return; // error handling
var ob:Object=_bhOPO[sc];
_bhOPO[sc]=null;
_fhOPO[ob]=null; // clean hashes off now obsolete references
sc.removeEventListener(Event.SOUND_COMPLETE, _cleanup);
// and clean the listener to let GC collect the SoundChannel object
}
}
Then, whenever you need to play a sound but limit to one instance of channel per object, you call SoundManager.psOPO(theSound,this); providing a button reference instead of this if need be. You can use normal Sound.play() alongside this kind of sound management should you need to, or use another type of management for BGM or other types of sounds should you need to.

From synchronous flow to asynchronous flow dilemma

As a PHP programmer I'm very habituated to the fact that the program flow goes line by line, if I call function1() that have to return some value I know the program wont continue until that function makes a return.
Now, im developing a AS3 app using AIR, I need to download some images if some conditions are met, don't know how many, so im using a For like this:
for (var w:int=0; w < newData_array[2]['content'].length; w++ ) {
for (var j:int=0; j < oldData_array[2]['content'].length; j++ ) {
if((oldData_array[2]['content'][j]['id'] == newData_array[2]['content'][w]['id']) && (oldData_array[2]['content'][j]['last_mod'] != newData_array[2]['content'][w]['last_mod'])){
//need to download a image...
}
}
}
As you can see im just comparing each element from each array (newData_array and oldData_array). If the condition met, I need to download something, for that I'm using URLloader and as you know this function is asynchronous, adding a listener, an event will be triggered when the download is complete, the problem is very clear, since the urlloader wont stop the for cycle (like I would expect on a PHP alike language) I'm just going to download a bunch of images at the same time creating a disaster because I wont know when to save. So I need to use by any mean the listener, but since I'm not very habituated to this kind of procedures I'm pretty munch stuck.
Im not interested on the save to disk routines, that part is pretty much done, I just want to understand how I should structure this algorithm to make this work.
This annoyed me too, but one has to realize that you can't do something to something that doesn't exist in memory yet, and these loading operations can take quite some time. Being a single-threaded stack, Flash Player's load operations would require that the entire interface to freeze during the load (if restricted to the loop).
Obviously there's data available to the loop that dictates what you do with the image. The solution I've been using is create a custom load function that starts the load, and returns a proxy image . Your loop gets a container back and you can freely place your image where you want.
In the meantime, your loader listener has kept track of the active images loading, and their associated proxies. When loaded, swap the image in.
Your loop would look something like this...
for (var w:int in newData_array[2]['content']) {
for (var j:int in oldData_array[2]['content']) {
if ((oldData_array[2]['content'][j]['id'] == newData_array[2]['content'][w]['id']) && (oldData_array[2]['content'][j]['last_mod'] != newData_array[2]['content'][w]['last_mod'])){
var proxy:MovieClip = loadImage("image/path.jpg");
// Do stuff to proxy.
}
}
}
And your function + listener would look something like this...
var proxies:Object = {};
function loadImage(path:String):MovieClip {
// load Image
var proxy:MovieClip = new MovieClip();
proxies.path = proxy;
// Load Image
}
function imageLoaded(e:Event):void {
// Once loaded, use the filename as the proxy object's key to find the MovieClip to attach the image.
proxies[e.data.loaderInfo.filename].addChild(e.data);
}
This is just off the top of my head, so you'll need to find the actual variable names, but I hope that makes sense.
I am not really sure if I understood your problem but it seems to me that you simply add the according listener in your inner loop.
//take care this is pseudocode
var parent = ... // some stage object
for (var w:int=0; w < size1; w++ ) {
for (var j:int=0; j < size2; j++ ) {
if(some_condition){
request = new URLRequest(getNextImagePath();
urlLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, onComplete);
urlLoader.load(request);
}
}
}
function onComplete(event:Event) {
parent.addChild(createImageFromData(this.data));
}
I solved the problem using an third party library that pretty munch handles this problem.
https://github.com/arthur-debert/BulkLoader

as3 Sound Class Unloading Sound

I have tried this for HOURS. There must be a simple solution to stop the sound and unload it in as3.
This is not all of my code, but in short I am loading random sounds. I need certian vars to be outside of the function so I can reference them with other functions for a progress bar.
How do I unload a sound so I can load a new one using the same names without getting an error the second time its called?
I have two buttons in this test. A play Sound and a Stop Sound Button.
here is my code:
var TheSound:Sound = new Sound();
var mySoundChannel:SoundChannel = new SoundChannel();
PlayButton.addEventListener(MouseEvent.CLICK, PlaySound);
StopButton.addEventListener(MouseEvent.CLICK, Stopsound);
function PlaySound(e:MouseEvent)
{
TheSound.load(new URLRequest("http://www.MyWebsite.com/Noel.mp3"));
mySoundChannel = TheSound.play(0);
}
function StopSound(e:MouseEvent)
{
delete TheSound;
}
HERE IS THE ERROR I GET:
Error: Error #2037: Functions called in incorrect sequence, or earlier call was unsuccessful.
at flash.media::Sound/_load()
at flash.media::Sound/load()
at Untitled_fla::MainTimeline/PlaySound()[Untitled_fla.MainTimeline::frame1:21]
UPDATE.... I TRIED STOPING THE SOUND AND THEN UNLOADING IT AS FOLLOWS
mySoundChannel.stop();
TheSound.close();
BUT NOW I GET THIS ERROR:
Error: Error #2029: This URLStream object does not have a stream opened.
at flash.media::Sound/close()
at Untitled_fla::MainTimeline/shut1()[Untitled_fla.MainTimeline::frame1:35]
I believe I am closer. Thanks so much for your help so far.
In order to stop the sound from playing, you first have to tell the SoundChannel instance to stop like so :
mySoundChannel.stop();
Once you did that, you can close the stream used by the sound instance by invoking the close method like so :
TheSound.close();
Also, the delete keyword is rarely used in as3 and you should not it while some methods are trying to access the variable you are deleting. If you want to dispose of the instance that is currently assigned to your TheSound variable, you should set its value to null. This way, flash will properly garbage collect the old Sound instance that is no longer used when it finds the appropriate time to do so.
You can initialise the variable outside of the function, but define it as a new Sound object each time the function is called. That way it has global scope, and you can load a new URL at any time.
var TheSound:Sound;
var mySoundChannel:SoundChannel = new SoundChannel();
PlayButton.addEventListener(MouseEvent.CLICK, PlaySound);
StopButton.addEventListener(MouseEvent.CLICK, StopSound);
function PlaySound(e:MouseEvent)
{
TheSound = new Sound();
TheSound.load(new URLRequest("http://www.MyWebsite.com/Noel.mp3"));
mySoundChannel = TheSound.play(0);
}
function StopSound(e:MouseEvent)
{
mySoundChannel.stop();
TheSound.close()
}

Using AS3WavSound to play WAV - cannot stop instantly

I'm using the AS3WavSound (http://code.google.com/p/as3wavsound/) class to playback externally loaded wavs. This is working successfully. The library is simple and effective.
After decoding the Wav ByteArray the method the library employs for playback is using the SampleDataEvent.SAMPLE_DATA event and then writing the mixed samples to the output stream.
player.addEventListener(SampleDataEvent.SAMPLE_DATA, onSamplesCallback);
private function onSamplesCallback( evt : SampleDataEvent ):void
{
for (var i:int = 0; i < samplesLength; i++)
{
if(_mute == false){
outputStream.writeFloat(samplesLeft[i]);
outputStream.writeFloat(samplesRight[i]);
}
}
}
My problem is that I need to silence this audio output immediately but whatever method I have tried there is a distinct (1 second approx) delay before the silence takes effect.
As you can see I've attempted to add a boolean to block any samples being written to the output stream but this has had no effect on the delay.
My suspicion is that this is a fundamental part of how the samples are buffered and then written out. Essentially by the time a user action on screen (clicking a mute button) has been called and the _mute boolean is set to true there are already samples waiting to be written to the output that cannot be affected.
Any advice or confirmation of my suspicion would be greatly appreciated.
Thanks,
gfte.
Your suspicion is probably right - but why stop it at that level? If you want to turn off the sound, would it not be better to set the volume on the soundTransform-property on the SoundChannel-object returned by the play method? (I assume the wav library returns this in some way)
It looks like the library you are using has a similar design to the native Flash Sound API wherein a SoundChannel object is returned from the play() method. This SoundChannel instance has a stop() method which should stop the sound right away.
var sound:WavSoundPlayer = new WavSoundPlayer();
var channel:WavSoundChannel = new WavSoundChannel();
sound.addEventListener( SampleDataEvent.SAMPLE_DATA, onSampleData );
channel = sound.play();
private function onSamplesData( evt : SampleDataEvent ):void
{
for (var i:int = 0; i < samplesLength; i++)
{
outputStream.writeFloat(samplesLeft[i]);
outputStream.writeFloat(samplesRight[i]);
}
}
channel.stop()
The _mute variable in your example will only be able to change either before or after the loop, not while it is looping.

How do you loop a sound in flash AS3 when it ends?

What AS3 code is used to loop a sound using AS3?
This won't give you perfect, gapless playback but it will cause the sound to loop.
var sound:Sound = new Sound();
var soundChannel:SoundChannel;
sound.addEventListener(Event.COMPLETE, onSoundLoadComplete);
sound.load("yourmp3.mp3");
// we wait until the sound finishes loading and then play it, storing the
// soundchannel so that we can hear when it "completes".
function onSoundLoadComplete(e:Event):void{
sound.removeEventListener(Event.COMPLETE, onSoundLoadComplete);
soundChannel = sound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundChannelSoundComplete);
}
// this is called when the sound channel completes.
function onSoundChannelSoundComplete(e:Event):void{
e.currentTarget.removeEventListener(Event.SOUND_COMPLETE, onSoundChannelSoundComplete);
soundChannel = sound.play();
}
If you want the sound to loop many times with a flawless, gapless playback, you can call
sound.play(0, 9999); // 9999 means to loop 9999 times
But you still would need to set up a soundcomplete listener if you want infinite playback after the 9999th play. The problem with this way of doing things is if you have to pause/restart the sound. This will create a soundChannel whose duration is 9999 times longer than the actual sound file's duration, and calling play(duration) when duration is longer than the sound's length causes a horrible crash.
var sound:Sound = whateverSoundYouNeedToPlay;
function playSound():void
{
var channel:SoundChannel = sound.play();
channel.addEventListener(Event.SOUND_COMPLETE, onComplete);
}
function onComplete(event:Event):void
{
SoundChannel(event.target).removeEventListener(event.type, onComplete);
playSound();
}
import flash.media.Sound;
import flash.media.SoundChannel;
var mySound:Sound = new Bgm(); //Bgm() is the class of the internal sound which can be done in the library panel.
playSound();
function playSound():void
{
var channel:SoundChannel = mySound.play();
channel.addEventListener(Event.SOUND_COMPLETE, onComplete);
}
function onComplete(event:Event):void
{
SoundChannel(event.target).removeEventListener(event.type, onComplete);
playSound();
}
This works perfectly.
To expand on #scriptocalypse's gapless playback a bit:
The problem of not having proper gapless playback comes from mp3 including information about the file in either the head or the tail of the file (id3 tags etc), hence the small pause when you try to loop it. There are a few things you can do depending on your situation.
Ignore it, just play as normal, with a small pause at the end of every file. You can also try and mask it with another sound (a beat drop yo), or fade out and fade in.
If your sounds are embedded, and not streaming, then create a fla file, drag your mp3 in there, and set them to export (the same way you'd add a linkage name for a MovieClip etc). It seems that when you export sounds like this, Flash takes the delay into account, or strips it out when it creates the Sound object. Either way, you can just do a simple play() passing the loops that you want for a gapless playback (I've found using a loops parameter is better than waiting on the SOUND_COMPLETE event and playing it again).
You can try some of the ogg libraries to use .ogg files instead of .mp3. A simple google search for "as3 ogg lib" will turn up what you need. Personally, I found them a bit awkward to use, and I couldn't afford the overhead added (as opposed to mp3 decoding, which is done in the player).
If your mp3 files are streaming, then the only way to get gapless playback is to layer them. Determine the gap (depending on what you used to encode them, it'll be different - my files has a gap of about 330ms), and when you reach it, start playing the overlay. It's a proper pain if you're doing fading, but when it works, it works quite nicely. Worst case scenario, you end up with situation (1)
I guess this what you looking for in case the voice/music file is in the library:
var mysound:my_sound = new my_sound();
mysound.play(0,2); // this will repeat the sound 2 times.
This appears to have worked for me:
var nowTime:Number = (new Date()).time;
var timeElapsed:Number = nowTime - _lastTime;
_lastTime = nowTime;
_musicTimeElapsed+=timeElapsed;
if(_musicTimeElapsed >= _musicA.length - GAP_LENGTH)
{
_musicTimeElapsed = 0;
_musicA.play(0);
}
The other answers are great, however if you do not want to use code (for whatever reason), you can put the sound in a movieclip, set the sound property to "Stream", and then add as many frames as you like to the movie clip to ensure it plays fully.
This, of course, is a less preferred way, but for animators I'm sure it may be preferable in some situations (for example when synced with mouth animations that the animator wants looped).
this work for me :
import flash.media.Sound;
import flash.media.SoundChannel;
var soundUrl:String ="music.mp3";
var soundChannel:SoundChannel = new SoundChannel();
var sound:Sound = new Sound();
sound.load(new URLRequest(soundUrl));
soundChannel = sound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE,onComplete);
function onComplete(e:Event):void{
sound = new Sound();
sound.load(new URLRequest(soundUrl));
soundChannel = sound.play();
soundChannel.addEventListener(Event.SOUND_COMPLETE,onComplete);
}