views:

1008

answers:

1

.We have an AS3 player that requires Flash 10 (to overcome old issues with 9 and playback offsets) to handle clipping of audio. However when I select start and end points to playback from, many times what is played back doesn't match the same clip as what is played in Audacity. Here are some details on my test mp3 (results from ffmpeg)

  libavutil     49.10. 0 / 49.10. 0
  libavcodec    52. 0. 0 / 52. 0. 0
  libavformat   52.22. 1 / 52.22. 1
  libavdevice   52. 1. 0 / 52. 1. 0
  libswscale     0. 6. 1 /  0. 6. 1
  libpostproc   51. 2. 0 / 51. 2. 0
  built on Sep 24 2008 15:49:57, gcc: 4.2.4 (TDM-1 for MinGW)
Input #0, mp3, from 'reagan_challenger.mp3':
  Duration: 00:00:14.62, start: 0.000000, bitrate: 127 kb/s
    Stream #0.0: Audio: mp3, 44100 Hz, stereo, s16, 128 kb/s

Original url: http://ericmalone.net/test_media/reagan_challenger.mp3

Clipped url: http://ericmalone.net/test_media/reagan_clipped.wav

Clip was created on the backend using ffmpeg to get the clip from 4017ms - 7400ms:

ffmpeg -i reagan_challenger.mp3 -ss 4.017 -t 3.383 reagan_clipped.wav

I've also clipped the file using Audacity to the slightly less accurate positions 4.017676 seconds - 7.401333 seconds at http://ericmalone.net/test_media/reagan_challenger_from_audacity.wav

Here's the clipped portion playing back via the code below http://ericmalone.net/test_media/SoundExample.swf

Notice that the word challenger completes in the flash playback, but is truncated in both the Audacity and ffmpeg based clippings.

The method by which we stop audio playback in flash is fairly standard. I've extracted the sound bits to a test class. Here's the relevant code

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

    public class SoundExample extends Sprite {
        private var url:String = "http://www.ericmalone.net/test_media/reagan_challenger.mp3";
        private var soundFactory:Sound;
        private var song:SoundChannel;

     private var clipStart:int = 4017;
     private var clipEnd:int = 7400;

     private var timer:Timer;
     private var timerInterval:uint = 20;

     private var textField:TextField;

        public function SoundExample() {
            var request:URLRequest = new URLRequest(url);
            soundFactory = new Sound();
            soundFactory.addEventListener(Event.COMPLETE, completeHandler);
            soundFactory.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            soundFactory.load(request);

            textField = new TextField();
            textField.wordWrap = true;
            textField.width = 500;
            addChild(textField);
        }

        private function completeHandler(event:Event):void {
            output("file completely loaded and playing");
            timer = new Timer(timerInterval);
            timer.addEventListener(TimerEvent.TIMER, onTimer);
            song = soundFactory.play(clipStart);
            timer.start();
        }

        private function ioErrorHandler(event:Event):void {
            output("ioErrorHandler: " + event);
        }

        private function onTimer(event:TimerEvent):void
        {
            // stop playback if position is greater than clip end 
            // or if there's less duration left than difference of the timer and current position
            if((song.position >= clipEnd || clipEnd - song.position < timerInterval))
            {
             timer.stop();
             song.stop();
             output("Ending playback at " + song.position + " to be as close to " + clipEnd + " as possible");

            }
        }

        private function output(str):void
        {
            textField.text = str;
            trace(str); 
        }
    }
}

So the question is, is there a better way to get more accurate playback from within flash? Does it depend on codec/sampling rate/etc?

+2  A: 

Not directly regarding the proper mp3 encoding but could help...

You must be aware the Timer class has a certain precision margin.

The test in your example sounded good for me (no click in the middle) and I came 0,2% close to the desired result which is quite acceptable IMO.

Regarding the code there's a detail I wanted to pick up on, given the following condition from your example :

clipEnd - song.position < timerInterval

Imagine the following scenario :

  • The timer runs at 20ms interval, from 0 to 7400
  • The next to last tick lands on 7381 advancing by 1ms
  • The sound gets stopped at 99.74%

If the condition hadn't canceled it I guess it would have stopped at 7401 (100.01%)

Maybe you should try something like (beware, blind coded):

if(song.position >= clipEnd || (clipEnd - song.position < (song.position + timerInterval) - clipEnd))
{
    // Stop Timer
}

The delay will depend on the code you execute, the computer etc. but I suggest having a play with different interval values to keep to approximate the best 'evenness'.

Also, make sure you don't have other methods around delaying the execution of the timer Event. To do so, you should probably isolate this Timer portion in order to benchmark it (removing the sound as well).

Theo.T
The timer isn't actually much of a problem. If you listen to the swf example, you'll hear that Reagan completes the word 'challenger'. Playback usually ends around 7407 ms. However if you clip the mp3 with ffmpeg or Audacity up to 7400 (link above) a significant portion of the word is cut off.
er1234