Complex gapless sound loop in AS3 - actionscript-3

I have a WAV music file that I want to loop in Flash Pro. However, I am not sure how to work with this track, which has an opening part and a looping part.
The audio file would be 'divided' in three points: A, B and C. The looping part would be all the audio between points B and C, while the audio between A and B would be the opening part, which would be played only once.
I tried to divide the WAV into two files: A-B would be the intro file, and the B-C the looping file, so that right after the intro sound finishes, the looping sound begins.
Both files are on the library (right now I'm only focusing on local files).
To summarize, here's the AS3 code:
import flash.media.Sound;
import flash.events.Event;
import flash.media.SoundChannel;
var introMusic:Sound = new heroIntro();
var loopingMusic:Sound = new heroLoop();
var musicChannel:SoundChannel = new SoundChannel();
musicChannel = introMusic.play();
musicChannel.addEventListener(Event.SOUND_COMPLETE,continueMusic);
function continueMusic(e:Event):void{
musicChannel.removeEventListener(Event.SOUND_COMPLETE,continueMusic);
musicChannel= introMusic.play();
musicChannel.addEventListener(Event.SOUND_COMPLETE,continueMusic);
}
The loop is completely gapless, but the transition between the intro and the looping parts makes an annoying gap sound. How could I make that transition gapless?

You might be able to avoid the gap sound by playing the 2nd Sound in a different SoundChannel a second or so before the 1st Sound finishes playing. It's not an elegant solution, but it might work. Perhaps something like this:
import flash.media.Sound;
import flash.events.Event;
import flash.media.SoundChannel;
var introMusic:Sound = new heroIntro();
var loopingMusic:Sound = new heroLoop();
var introChannel:SoundChannel = new SoundChannel();
var loopingChannel:SoundChannel = new SoundChannel();
var hasBegunLooping:Boolean = false;
introChannel = introMusic.play();
function update():void{ //called every tick
if(!hasBegunLooping && introChannel.position > introMusic.length - 1000){
loopingChannel = loopingMusic.play(0, int.MAX_VALUE);
hasBegunLooping = true;
}
}

Related

AS3 Playing different sounds on different scenes

I hava a .fla file with 2 scenes, on the first scene i have a song playing in the back which stops whenever an user presses a button(btnP). The button takes the user to another scene(Scene2).
The first scene works fine and when the program goes to the other Scene the music stops. But, i want a different sound on Scene2. If i try to use the same technique on Scene2, nothing happpens. Both my sound files are imported to the library.
Heres my code for Scene1 (This works).
import flash.media.Sound;
import flash.media.SoundMixer;
stop();
btnP.addEventListener(MouseEvent.CLICK, petterVelg);
var snd:Sound =
new Sound(new URLRequest("lifted.mp3"));
snd.play();
function petterVelg(e:MouseEvent){
gotoAndPlay(1, "Scene 2");
SoundMixer.stopAll();
}
Here is mye code for Scene 2( This does not play any audio at all.... )
import flash.media.Sound;
var snd2:Sound = new Sound(new URLRequest("drikke.mp3"));
snd2.play();
You should use the SoundChannel class:
stop();
var snd:Sound = new Sound(new URLRequest("lifted.mp3"));
var piste1:SoundChannel = new SoundChannel();
piste1 = snd.play();
btnP.addEventListener(MouseEvent.CLICK, petterVelg);
function petterVelg(e:MouseEvent):void
{
gotoAndStop(1, "Scene 2");
piste1.stop();
}

AS3 maintime delay script while external swf loads and plays

I'm having an issue with playing an external SWFs inside the main SWF. Everything loads great, except not on cue. I'm using a simple delay actionscript to pause the main timeline until the external SWFs load, but it doesn't always sync up when testing in browsers.
Is there an AS3 code that I can use to pause the main timeline until the external SWF is finish loading and playing?
I need to do this multiple times through the movie, btw...
Below is the delay and the loadmovie array I'm using.
<--------------//Timer//--------------->
var timer4:Timer = new Timer(1500, 1);
timer4.addEventListener(
TimerEvent.TIMER,
function(evt:TimerEvent):void {
play();
}
);
timer4.start();
<--------------//loadMovie//--------------->
function startLoad()
{
var mLoader:Loader = new Loader();
var mRequest:URLRequest = new URLRequest('flip/note_flip.swf');
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onCompleteHandler);
mLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgressHandler);
mLoader.load(mRequest);
}
function onCompleteHandler(loadEvent:Event)
{
addChild(loadEvent.currentTarget.content);
}
function onProgressHandler(mProgress:ProgressEvent)
{
var percent:Number = mProgress.bytesLoaded/mProgress.bytesTotal;
trace(percent);
}
startLoad();
The load() method of the loader object is asynchronous. It means that the process will continue to execute next lines of code and loading is done independently. That's why you can see it's not on cue.
The simple way to fix your problem is to stop() all the loaded movies upon Event.COMPLETE and play() them once all the movies are loaded.
Use an array to store the loaded movies and address them once all movies are loaded.
And yeah, the timer hack is pointless, you won't know how long it will take the file to load, it can be different every time.
EDIT:
Let's assume that you've got N movie .swf files that makes a complete scene. You would like to load all of them, and once they're all loaded - play them.
Let's build a simple step-by-step logics:
1) First of all, we need a list of the URL pointing to where the external SWFs are.
2) Then we want to start loading the SWFs one by one (and not altogether) to prevent Flash Player bug of not loading some of the content AND keeping the order of movie clips aka z-index, depth, layer order etc.
3) On each loaded movie we count how many are still left to be loaded.
4) Once all movies are loaded, we play() all these movies.
Here's the code for you (written in a frame in case if you're not using classes and not familiar with OOP):
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.display.MovieClip;
var linkArray:Array = ["1.swf", "2.swf", "3.swf"];
var loadedMovieArray:Array = [];
var totalMovies:int = linkArray.length;
var loadedMovies:int = 0;
var urlRequest:URLRequest = new URLRequest();
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(Event.COMPLETE, onMovieLoaded);
loadMovies();
function loadMovies():void
{
var movieIndex:int = loadedMovies;
var movieURL:String = linkArray[movieIndex];
urlRequest.url = movieURL;
l.load(urlRequest);
}
function onMovieLoaded(e:Event):void
{
var loadedMC:MovieClip = l.content as MovieClip;
loadedMC.stop();
loadedMovieArray.push( loadedMC );
loadedMovies++;
if (loadedMovies == totalMovies)
onAllMoviesLoaded();
else
loadMovies();
}
function onAllMoviesLoaded():void
{
for (var i:int = 0; i < loadedMovieArray.length; i++) {
var mc:MovieClip = loadedMovieArray[i];
addChild(mc);
mc.play();
}
}

Flash/AS3 - preload or stream embedded sounds

I'm currently working on a project in flashdevelop and I want to include some music. I'm building a game, so multiple files are not an option. Currently all resources are embedded and a simple preloader loads everything (like the flashdevelop default preloader).
I don't want to load the music at the beginning, I'd rather like to stream it when required.
Is it possible to stream embedded sounds?
If not, is it possible to embed these files inside the .swf file and load them later on?
Thanks in advance!
You can do two things. One is to start loading the sounds after the initial loading finishes and save them in a Dictionary maybe. Second is to export a RSL (Runtime Shared Library) from Flash which is a SWF file which you can then load and have access to all the classes defined there.
In the first approach you basically load every sound like this and save them to dictionary:
import flash.media.Sound;
import flash.events.Event;
import flash.net.URLRequest;
import flash.utils.Dictionary;
var mSounds:Dictionary = new Dictionary();
function loadSound(url:String, soundName:String)
{
var sound:Sound = new Sound();
sound.addEventListener(Event.COMPLETE, onSoundLoadComplete);
sound.load(new URLRequest(url));
function onSoundLoadComplete(e:Event):void
{
sound.removeEventListener(Event.COMPLETE, onSoundLoadComplete);
trace(soundName,"Sound Loaded");
mSounds[soundName] = sound; // save it to dictionary
// then you can load it from dictionary
// using the name you assigned
if(mSounds["crystalised"])
(mSounds["crystalised"] as Sound).play();
}
}
loadSound("C:\\Users\\Gio\\Desktop\\Crystalised.mp3", "crystalised");
In the second approach you have to do more steps, but you load it once. I'll list the steps here:
Make a new Flash Document (FLA)
Import all the sounds you need to the library
In the properties menu of each sound select the Actionscript tab and tick the Export for Runtime Sharing checkbox and fill in the name for output SWF
After you publish this FLA you can load it in your application or game and use it like this:
import flash.display.Loader;
import flash.system.LoaderContext;
import flash.system.ApplicationDomain;
import flash.system.SecurityDomain;
import flash.events.Event;
import flash.net.URLRequest;
import flash.media.Sound;
import flash.utils.getDefinitionByName;
function loadRSL(url:String):void
{
var loader:Loader = new Loader();
var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onRSLLoadComplete);
loader.load(new URLRequest(url), context);
function onRSLLoadComplete(e:Event):void
{
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onRSLLoadComplete);
trace("RSL Loaded");
// creating a new instance of the sound which is defined in RSL
var soundClass:Class = getDefinitionByName("Crystalised") as Class;
var sound:Sound = (new soundClass() as Sound);
sound.play();
}
}
loadRSL("SoundLibrary.swf");

AS3 SoundChannel Position of audio in function

I am having issues with variable scope. I know this must be an easy fix but I can't find it.
I need to play sound through a sound channel in a function. Then I need another function to read the sound channel position. But it is not seeing it because of the new var in the function that plays it. If I play it outside the function I can get the position from another function but if I beging the sound in the function then another function can not read the channel position. How do I fix this.
The Code
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
var lastPosition:Number = 0; var CrossAudio = new Sound();
CrossAudio.load(new URLRequest("Cross.mp3"));
function playCross() {
PapaAudioChannel = CrossAudio.play();
}
bt_play.addEventListener(MouseEvent.CLICK, playA);
function playA(event:MouseEvent):void {
trace(PapaAudioChannel.position); // Does not work
/// It can't doesn't recognize the PapaAudioChannel because
/// it began playing in another function.
}
How can I get the second function to see the position of the audio. I need to start the audio with a play and pause button so the play has to start in a function.
Thanks
AFAIK the sound can start with a little delay, so tracing sound position just after a play() statement is not a good idea.
EDIT
By the way, where is PapaAudioChannel defined, if it is? (it should since this is AS3)
If you define a var in a function, it dies with that function, at the end of function!
So you may want to define PapaAudioChannel in the global scope:
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
var PapaAudioChannel:SoundChannel; // defines variable
var lastPosition:Number = 0; var CrossAudio = new Sound();
CrossAudio.load(new URLRequest("Cross.mp3"));
function playCross() {
PapaAudioChannel = CrossAudio.play();
}
bt_play.addEventListener(MouseEvent.CLICK, playA);
function playA(event:MouseEvent):void {
trace(PapaAudioChannel.position);
}

Determining when audio is ready to play

I am currently playing an audio file progressively like this:
import flash.media.Sound;
import flash.media.SoundLoaderContext;
import flash.net.URLRequest;
var s:Sound = new Sound();
var context:SoundLoaderContext = new SoundLoaderContext(5000, false);
s.load(new URLRequest('AUDIO URL'), context);
s.play();
which works great, but I want to know when it has enough data (based on the buffer time) and starts playing the file. To clarify, I am not trying to determine when the file has completely downloaded, I know that is done with Event.COMPLETE
How about using the ProgressEvent?
s.addEventListener(ProgressEvent.PROGRESS, progressHandler);
function progressHandler(event:ProgressEvent):void {
trace( event.bytesLoaded );
}
So when the bytesloaded is equal to the buffer amount, you know it.
Edit:
Perhaps you could check for the isBuffering attribute to see if the buffer is filled or not.