By deans ~ May 11th, 2009. Filed under: Resources.
I’ve been working on a new App for much longer than we’d budgeted. The sad part is that the real coding has been complete for several days. Instead of submitting this app and getting on with the next one, I’ve spent the time trying to iron out sound issues. This isn’t a complicated app, but it does, on occasion, play multiple short (<2 sec) sounds simultaneously. The sounds are not complex, and I was initially hoping to skate by with the AudioServices system sounds. I know that that’s not the recommended practice, but it was the easiest to code, so I started there.
The problem with that approach quickly became apparent. There was a noticeable distortion when more than 2-3 sounds were playing at the same time. The unpleasant effect was kind of a stuttering static, sort of like one might hear if one over-drives speakers. This, unfortunately, pretty much ruined the effect of the app, so Paul suggested lowering the volume of the sounds. Sadly, one can’t do that for an AudioServices system sound.
At that point, I switched the app over to using AVAudioPlayer. This seemed like it was going to be a good solution, so I wrote a utility app to help me test the individual sound volume settings and spent quite a bit of time experimenting to find the “right” level for each possible combination of sounds. I really thought that I was in the home stretch until I fired up the app and found that the motion went from butter smooth to unacceptably jerky. Since actual playback of AVAudioPlayer is asynchronous with regards to the main thread, the last thing that I looked at (after wasting more time than I should have on optimization) was the actual call to [myAVAudioPlayer play]. Big mistake.
After eliminating everything else, I bracketed the [myAVAudioPlayer play] calls with a timer and was amazed to discover that the calls were taking 0.04 – 0.05 seconds, on the calling thread, just to get started. After that, the player went along asynchronously, as expected. Since my simulation loop was running in increments of 0.0167 seconds, having each call to [myAVAudioPlayer play] block for 3x that amount made for a very unhappy user experience. My next thought was to bounce right to OpenAL, but I decided that the (apparently) no longer sanctioned (at least it’s not available from Apple anymore) SoundEngine might give me most of what I needed, without my having to worry about managing buffers and sources.
Unfortunately, SoundEngine lacked some OpenAL functionality that I really needed (being able to find out if a given effect is currently playing and also to find out how far along the effect is). I ended up implementing extensions to SoundEngine to get at the OpenAL stuff, and, so far, everything is working. The most recent discovery was that the SoundEngine/OpenAL combination does not initialize and properly configure an AudioSession. This means that our app stopped when the device went into Auto-Lock. Not necessarily a good thing for a “lifestyle” app. Once I took care of that, I’m back to calibrating the sound levels and doing more testing on the functionality. I constantly have my fingers crossed that I don’t encounter any more problems that will force me to abandon the extended SoundEngine.
I may do a longer post on the perils of using compressed audio formats in the future, but let’s wrap this bit up with a warning. While compression can really help with your download times, you may want to be careful about choosing when to employ compressed formats. There are a couple of reasons for this. First, some of the iPhone SDK playback options support only a limited subset of the compressed formats. Second, the device won’t play the highly compressed formats simultaneously. If you plan to play multiple sounds at the same time, highly compressed formats might not be an option.
Most of the points covered here are well documented in the Getting Started with Audio & Video document. If I’d had the discipline to read the whole thing before I started down this path, the trip would have been much less frustrating.