BefittingTheorem
BefittingTheorem

Reputation: 10629

Prefered method for looping sound flash as3

I'm having some issues with looping a sound in flash AS3, in that when I tell the sound to loop I get a slight delay at the end/beginning of the audio.

The audio is clipped correctly and will play without a gap on garage band.

I know that there are issues with sound in general in flash, bugs with encodings and the inaccuracies with the SOUND_COMPLETE event (And Adobe should be embarrassed with their handling of these issues)

I have tried to use the built in loop argument in the play method on the Sound class and also react on the SOUND_COMPLETE event, but both cause a delay.

But has anyone come up with a technique for looping a sound without any noticeable gap?

Upvotes: 7

Views: 16927

Answers (6)

charlesclements
charlesclements

Reputation: 86

I wanted to create a library for looping audio that didnt rely on COMPLETE events. I decided to create my own standingwave3 addons library. Check out the original standingwave3 project by MaxL0rd. Mine a work in progress and it gets the job done, on the byte level for that matter (No loop timers or anything). It works by taking in a Sound with beginning and ending loop points. Then it clones the loop sample a bunch of times based on the time provided in seconds. It should be straight forward to use. This is the code for the Main.as class in the "looping" file in the "examples" folder:

package
{

    // Imports.
    import com.greensock.events.LoaderEvent;
    import com.greensock.loading.LoaderMax;
    import com.greensock.loading.MP3Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import com.greensock.TweenMax;
    import com.SW3.gadget.LoopGadget;
    import flash.media.Sound;


    // Class.
    public class Main extends Sprite
    {

        // Vars.
        private var loader:LoaderMax;// Using LoaderMax for ease of use.


        // Constructor.
        public function Main()
        {

            trace("Main");

            loader = new LoaderMax( { name:"audio", onComplete:onSoundsLoaded } );
            loader.append( new MP3Loader( "assets/Beat.mp3", { autoPlay:false } ) );
            loader.append( new MP3Loader( "assets/Clap.mp3", { autoPlay:false } ) );
            loader.append( new MP3Loader( "assets/Boom.mp3", { autoPlay:false } ) );
            loader.load();

        }


        private function onSoundsLoaded(e:LoaderEvent):void
        {

            trace("onSoundsLoaded");
            var looping:LoopGadget = new LoopGadget;
            looping.addLoopSound( "Beat", e.currentTarget.content[ 0 ] as Sound, 0, 10 );
            looping.addLoopSound( "Clap", e.currentTarget.content[ 1 ] as Sound, 0, 10 );
            //looping.addLoopSound( "Boom", e.currentTarget.content[ 2 ] as Sound, 0, 10 ); // Commented out to test possible error.

            looping.playLoop( "Beat" );// Play the "Beat" loop.
            looping.playLoop( "Clap" );// Play the "Clap" loop.
            looping.stopLoop( "Beat" );// Stop the "Beat" loop.
            looping.playLoop( "Beat" );// Play the "Beat" loop.
            looping.playLoop( "Beat" );// Play the "Beat" loop again to test if it would error out..

            looping.stopAllLoops();// Stop all the loops.
            looping.playLoops( [ "Beat", "Clap", "Boom" ] );// Play all the loops. Test to see if "Boom" will error out.

        }

    }

}

Check out the source files over here: https://github.com/charlesclements/standingwave3-addons

Upvotes: 0

Eyal Katz
Eyal Katz

Reputation: 165

I use this great tool: http://www.compuphase.com/mp3/mp3loop.zip

You give it a WAV file and it returns an MP3 file which can be perfectly (and gaplessly!) played using Sound's standard play method - e.g. :

var manyLoops:int = 1000*1000; // a Million is a lot :)
mySound.play(0, manyLoops); 

You can also take the output MP3 file and re-convert it to a WAV (using AudioCity for example) - then you have a gapless WAV (which can work great inside your FLA file)

Hope it helps

Upvotes: 1

grapefrukt
grapefrukt

Reputation: 27045

Gapless looping of mp3's is not trivial due to the way the format works. To simplify a bit; the sound is fitted to a number of frames, this number cannot be chosen arbitrarily, instead some padding (with silence) is required. Mp3 has no way of storing how much padding was added, so this information is lost once the file is encoded.

The flash IDE get's around this by embedding this metadata, and you can too. You just need to know how much delay is added by the encoder.

Andre Michelle explains this way better than I can on his blog.

Upvotes: 5

aaaidan
aaaidan

Reputation: 7346

According to this guy, you have to import the music loop as a wav, and have the Flash IDE itself compress to mp3. Having Flash use imported mp3 data means that it won't know how to loop it properly.

Upvotes: 3

Branden Hall
Branden Hall

Reputation: 4468

The most reliable method, if you can use Flash Player 10, is to use the new SampleDataEvent.SAMPLE_DATA event.

Specifically, what you do is to first instantiate the sound you want, then use the new extract method to convert the sound into raw PCM data encoded in a ByteArray. Then you can create a new Sound object, and setup to listen for it's SampleDataEvent.SAMPLE_DATA event. When that event is called you'll push 2-8k (a lower amount reduces latency, but increases the possibility of audible artifacts) of data from the ByteArray. You'll just make sure that as you run off the end of the ByteArray you'll just loop back to the beginning.

This method ensures that you'll have fully gapless playback.

Upvotes: 7

Mercer Traieste
Mercer Traieste

Reputation: 4678

Here is how id did it, with no noticeable delay. Main app:

package {

import flash.display.MovieClip; 
import flash.events.*;
import flash.utils.*;

public class MainApp extends MovieClip {
  private var player:Player;
  ..........

  public function MainApp() {
      .......
      player = new Player();
      player.addEventListener(Player.EVENT_SOUND_COMPLETED, handleSoundCompleted);
      ......
  }

  private function handleSoundCompleted(event:Event):void {
      player.setPosition(0);
      player.play();
  }

  .................

Player class:

package {

import flash.events.*;
import flash.media.*;
import flash.net.*;

public class Player extends EventDispatcher {

    private var sound:Sound;
    private var channel:SoundChannel;
    private var position:Number;

    static const SOUND_VOLUME:Number = 0.75;
    static const EVENT_SOUND_COMPLETED:String = "SOUND_COMPLETED";

    public function Player() {

        // init
        sound = new ThemeSong();
        position = 0;

        // listeners
        sound.addEventListener(IOErrorEvent.IO_ERROR, function(event:Event){trace(event)});

        trace("Player initialized...");
    }

    public function play():void {
        channel = sound.play(position);
        channel.soundTransform = new SoundTransform(SOUND_VOLUME);
        channel.addEventListener(Event.SOUND_COMPLETE, function(event:Event){dispatchEvent(new Event(EVENT_SOUND_COMPLETED));});
        trace("Player playing..");
    }

    public function pause():void {
        if (channel != null) {
            channel.stop();
            position = channel.position;
        }
        trace("Player paused..");
    }

    public function setPosition(pos:Number):void {
        position = pos;
    }

    public function getPosition():Number {
        if (channel == null) {
            return 0;
        } else {
            return channel.position;
        }
    }
}
}

You did say that the mp3 file has no delay at beginning/end, but I suggest opening it with audacity, and make sure there is no delay.

Upvotes: 1

Related Questions