SoundManager 2: Seamless Looping MP3s in Flash (Demo)

Newer Older

Eureka! Here's a feature people have been asking about for years with SoundManager 2, progress has finally been made. ;) This is for AS3/Flash 9+ and also applies to regular Flash development, so read on if you have wanted seamless playback there too.

Maybe this isn't new to some, but I'd never been able to find anything previously that worked. The solution I've found involves encoding gapless MP3s and preloading the sound.

The Flash Sound Looping Problem
Historically, Flash has always seemed to have trouble looping dynamically-loaded MP3 sounds. Seamless looping of the sound appears to be impossible because of gaps introduced in the MP3 file, and perhaps from other unknown Flash weirdness. (Flash movies made in the Flash IDE with "embedded"/linked sounds don't suffer this problem, for whatever reason.)

Tip #1: Use AS3's native Sound.play(offset, loops)
Rather than try to handle onfinish() events and loop yourself, the trick is to specify the number of loops in the native Flash sound API play(offset, loops) method - and secondly (and most importantly,) creating a "gapless" MP3 file; that is, one which does not contain any empty data frames at the end. MP3 is encoded as frames, or blocks of sound, and it never rounds off quite right to make a seamless sound unless your sound has a perfectly-divisible number of samples. Thus, in most cases you'll have a gap of space presumably at the end of your sound.

Tip #2: Encode "gapless" MP3 by feeding the loop into itself
Using the free "LAME" MP3 encoding software, I made this loop seamless as such:
lame -b 128 -h --nogap loop.wav copy-of-loop.wav
(You will end up with two MP3 files in this case, the first should be the seamless one.)

LAME's "nogap" encoding feature is described as "gapless encoding for a set of contiguous files" - ie., making Pink Floyd's "Dark Side Of The Moon" play track 1 to track 2 without silence in-between. However for the looping case, what if I were to use the same track for both?

.. That is, creating the "no gap" version of the MP3 using the same file for sound #1 and sound #2. Put another way, the end is the beginning; any empty space in #1 gets filled with #2.. So, the end of sound #1 leads into the beginning of sound #2. That is the crucial difference which makes seamless looping - I think - work, even if it now means there is a small slice of duplicated samples.

Tip #3: Preload the MP3, play after onload()
Finally, I found that preloading the whole sound before playing it seems to make truly-seamless playback actually work. It seems to be "close, but not quite" if you simply start playing the sound without preloading.

Thus, the API call for SoundManager 2 boils down to something like this:

soundManager.createSound({
  id:'loopTest',
  url:'-nogap-test-loop.mp3',
  loops:3,
  autoLoad:true,
  onload:function() {
    this.play();
  }
});

This is quite nice as I've been wanting "native" seamless audio playback in Flash without having to write my own audio decoder etc. and subsequently re-writing the whole audio API, for years.

This was introduced on the v2.95b.20100323+DEV branch of SM2 on GitHub, and is included in current releases. See www.schillmania.com/projects/soundmanager2/ for the latest and greatest.

'ju:femaiz, lhl, imNicholas, and celsdevilla added this video to their favorites.

  1. -Jérôme- 38 months ago | reply

    Cool, but I do not quite get tip #2. You make a seemless transition between loop.wav and loop.wav having in fact twice loop.wav, but what happens when you loop the end of the "second"loop.wav to the beginning of the first one. Don't you have the same "divisible number of samples" issue with twice the length of the file as with just once the length of the file?

  2. Schill 38 months ago | reply

    @Jerome: I was thinking the same, but I suspect the repeated bit of sound #1 is small enough that it isn't noticed. (A guess, here.)

    That is to say, there is no dead "buffer" space at the end where the sound samples previously ended before the last block (for MP3, typically 1152 samples per frame or somesuch - quoting, "depends on MPEG version and layer.")

    What exactly goes in the empty space isn't clear to me (part of the start of the loop again, I'd assume), unless it's somehow buffered out and omitted when the sound actually starts over; frankly, I'm a bit lost after reading about the spec and samples per frame details - I could have it all wrong, for all I know, but it works. ;)

  3. 'ju:femaiz 38 months ago | reply

    Nice work sir!
    --
    Seen in my contacts' photos. (?)

  4. Ross 38 months ago | reply

    Wow, that is awesome.

  5. Dr. Nerdington 37 months ago | reply

    I'm surprised you don't have to "-nogap" three copies and throw away the first and third mp3s.

  6. Schill 37 months ago | reply

    This is a good point, actually. I should retry this using three copies, and see if the output any better. I thought maybe I did already, but I should verify for completeness' sake.

    Apparently Apple have some tips on encoding gapless H.264/AAC audio, for app developers; that is another format to take a look at.

  7. LUPike 5 months ago | reply

    This is perfect for adding background music for a site! I do have one question though... Is there a way to incorporate a "pause" button?

keyboard shortcuts: previous photo next photo L view in light box F favorite < scroll film strip left > scroll film strip right ? show all shortcuts