Mixing sounds with haXe, the Memory API, and looping MP3s
I started working on my own Sound manager, called the SampleTrackManager, for mixing and playing back music and sound effects in flash. I know there are quite a few Sound manager’s out there, but I was inspired by Joe Berkovitz’s standingwave project and the Alchemy work done on standingwave3 to synchronize playback of sounds. I played around with the Flash 10 SampleDataEvent when it first came out, so I figured this would be a good opportunity to make something useful with it. Plus I was kind of annoyed that I couldn’t play 32 Sounds in a for loop and have them synchronize because there’s some latency. 🙂
The goal of the Sound manager was to make it easy to play samples on different tracks so I could operate on them separately while still being able to choreograph them. The first version was in AS3 and while it worked the performance was terrible. Playing a single looping sample was 3 times slowing than playing a looping Sound instance, and while increasing the number of samples written to the SampleDataEvent helped performance it introduced latency. I suspected the all the ByteArray function calls for reading and writing floats in the inner mixing loop was slowing things, down since functions calls in Flash are very slow.
So I created a haXe version and utilized the haxe.Memory API which uses the new Alchemy opcodes. To my surprise I can now mix 64 samples using about the same amount of CPU as playing back 32 Sound instances! Wow!
Once I organize things, I’ll put the SampleTrackManager up on github. I have a hierarchical state machine I want to release as well, but that’s another post.
One issue I ran into while working on my test demo, was the infamous gap issue with mp3 playback. I did a little searching and found a solution posted by Scott Schiller for SoundManager2. It turns out that using the lame mp3 encoder’s –nogap options is the key to creating looping mp3s. I created a little bash script that works great!
# Creates a loopable mp3 from a looping wav.
# Example: loopMp3 input.wav output.mp3
cp $1 loop_gapless/start.wav
cp $1 loop_gapless/mid.wav
cp $1 loop_gapless/end.wav
lame -b 128 -h --nogap start.wav mid.wav end.wav
cp mid.mp3 ../$2
rm -rf loop_gapless
— UPDATE: I’ve posted the code, the mp3 script, and a sample here: