views:

573

answers:

4

I'm trying to sync animation to music at a specific BPM. I've tried using the Timer but it isn't accurate when dealing with small intervals in milliseconds. I did some reading and found an alternate method that uses a small silent audio file and the SOUND_COMPLETE event as a Timer.

I used 167ms long sound file with this code.

package
{
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.net.URLRequest;

    public class BetterTimer extends EventDispatcher
    {
        private var silentSound:Sound;

        public function BetterTimer(silentSoundUrl:String):void {
            super();
            silentSound = new Sound( new URLRequest(silentSoundUrl) );
            silentSound.addEventListener(Event.COMPLETE, start);
        }
        public function start():void {
            this.timerFired( new Event("start") );
        }
        private function timerFired(e:Event):void {
            dispatchEvent( new Event("fire") );
            var channel:SoundChannel = silentSound.play();
            channel.addEventListener(Event.SOUND_COMPLETE, timerFired, false, 0, true);
        }
    }
}

This still doesn't stay on beat. Is the Flash Player capable of accuracy with sound?

+1  A: 

Setting the frame rate so that the event interval is a multiple of the frame rate might help (for example, 167ms equals 6 fps; 12, 18, 24 etc. are then also ok).

If I understood correctly, better solution would be to use the enterframe event. Instead of determining the position of the animation by counting the events, calculate it using elapsed time (getTimer or sound position). This would also make the animation work on slower computers that have lag.

Skrim
+5  A: 

You can also use the new Sound API with the SampleDataEvent and basically play your MP3 manually using Sound.extract(). In that case you know the latency up front and can even count up to the sample when your (delayed) event should happen.

This is what we do in the AudioTool and it works very well.

Joa Ebert
+2  A: 

This is very tricky to get right! There's a small lib called BeatTimer that tries to do this. I haven't tried this code myself, but if it does what it claims it should be exactly what you need.

grapefrukt
I just gave that BeatTimer a try. It was written to kick an event on quarter beats at a specific BPM. I tested it with eighths and sixteenths and it stays accurate for the duration of my 2 min loop. I would like to also try Joa's solution but for my purposes this BeatTimer works well. Thanks.
knuckfubuck
A: 

I was looking through the popforge library's AudioBuffer and tried using one of the approach. That's the create a sync sound. The following is what i did.

var syncSamples:ByteArray = new ByteArray(); 
syncSamples.length = (2646000 / _bpm) << 1; /*44100*60=2646000*/
SoundFactory.fromByteArray(syncSamples, Audio.MONO, Audio.BIT16, Audio.RATE44100, soundInit);

The ms delay is pretty close, eg: at 120 BPM, it's between 509 - 512ms. The question is, am I going in the right direction?

Gerald Yeo