views:

279

answers:

1

Calling channel.position on an ENTER_FRAME event, I notice that it's not being updated every frame, but it looks more like every frame and a half.

var sound:Sound = new Sound(new URLRequest('music.mp3')); 
var channel:SoundChannel = sound.play(); // assume the sound is completely,
                                         // totally, 100% loaded

addEventListener(Event.ENTER_FRAME, function(e:Event):void{
   trace(  "Position : " + channel.position 
         + " - Frame : " + int(channel.position / 30));
});

will result in something along the lines of (30 FPS)

   ...
   Position : 1439.6371882086166 - Frame : 47
   // 48 is missing
** Position : 1486.077097505669 -  Frame : 49
** Position : 1486.077097505669 -  Frame : 49
   Position : 1532.517006802721 -  Frame : 51
   Position : 1578.9569160997733 - Frame : 52
   // 53 is missing
** Position : 1625.3968253968253 - Frame : 54
** Position : 1625.3968253968253 - Frame : 54
   Position : 1671.8367346938776 - Frame : 55
   // 56 is missing
   Position : 1718.2766439909296 - Frame : 57
   ...

Has anyone noticed this behavior before? Are there any techniques for determining which 'frame' of audio is being played, knowing this inacuracy?

A: 

Yes, this is normal behaviour because events are threaded and therefore will call their delegates whenever their thread has priority. Printing to the console is also threaded so it may not always be printing messages at the right time either. Infact, the issue you're seeing is probably just a printing issue. Try boosting your framerate, see what happens.

Still, in order to be more accurate you could try using the timer class. Typically, you can make the ticks happen much faster than your frames, meaning the margin for error will be lower. Still, you're using the event so there may be some drift.

To compensate for this, you can check the time versus the frames to determine the offset. This allow you to correct for any drift.

EDIT: This example was pulled straight from this page in the ActionScript 3 documentation, notice the positionTimer they're using:

package {
    import flash.display.Sprite;
    import flash.events.*;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.net.URLRequest;
    import flash.utils.Timer;

    public class SoundChannelExample extends Sprite {
        private var url:String = "MySound.mp3";
        private var soundFactory:Sound;
        private var channel:SoundChannel;
        private var positionTimer:Timer;

        public function SoundChannelExample() {
            var request:URLRequest = new URLRequest(url);
            soundFactory = new Sound();
            soundFactory.addEventListener(Event.COMPLETE, completeHandler);
            soundFactory.addEventListener(Event.ID3, id3Handler);
            soundFactory.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            soundFactory.addEventListener(ProgressEvent.PROGRESS, progressHandler);
            soundFactory.load(request);

            channel = soundFactory.play();
            channel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);

            positionTimer = new Timer(50);
            positionTimer.addEventListener(TimerEvent.TIMER, positionTimerHandler);
            positionTimer.start();
        }


        private function positionTimerHandler(event:TimerEvent):void {
            trace("positionTimerHandler: " + channel.position.toFixed(2));
        }

        private function completeHandler(event:Event):void {
            trace("completeHandler: " + event);
        }

        private function id3Handler(event:Event):void {
            trace("id3Handler: " + event);
        }

        private function ioErrorHandler(event:Event):void {
            trace("ioErrorHandler: " + event);
            positionTimer.stop();       
        }

        private function progressHandler(event:ProgressEvent):void {
            trace("progressHandler: " + event);
        }

        private function soundCompleteHandler(event:Event):void {
            trace("soundCompleteHandler: " + event);
            positionTimer.stop();
        }
    }
}
Soviut
Jacking the frame rate up to 60 takes care of the missing frames, but does not increase the granularity of the channel.position requests. This results in frame 55 being reported 3, or 4 times 56 being reported 3 or 4 times, and so on.
Robert
That's odd because ENTER_FRAME events should only fire once per frame.
Soviut
Right, and its not that it is firing more than once a frame, its that channel.position is being updated at a speed slower than the frame rate.
Robert
In your example, changing 50ms (which is right around 20FPS) to 33 (30FPS) still results in the 'doubling up' of positionTimerHandler every few frames - as the refresh rate for channel.position is faster than 50ms, but slower than 33ms
Robert
This isn't the most ideal answer, but it does provide information about the problem that might help other people.
Robert