
Sound
For the game I have just finished developing I have a few very short and simple sound effects. Right away as I added the first one it was clear I would have to add some way of controlling the volume because the sound was inappropriately loud at whatever setting I had my phone on at the time.
I didn’t want to make the user go to the settings page and pull a slider up or down just to change the volume when there’s a perfectly good hardware volume rocker on the side of the iPhone, but when I pressed the volume rocker in my game, I was changing the volume of the phone ringer. So how have other people done it I wondered? Surely there had to be a way to hook into the volume rocker press event using Cocoa Touch.
After a few minutes searching the SDK documentation it was clear there is not a standard Cocoa Touch API for this and in fact, Apple confirmed as much to me when I used one of my then soon to expire free support incidents to ask them out it. So again, now had other people done it? And not just a few – virtually every game behaves in the way I wanted to make mine behave and couldn’t figure out how. Then by pure chance I pressed the volume rocker at the exact moment when my 0.2 second click sound was playing, and eureka! it changed the volume of my app. By trial and error, I learned the exact behaviour I desired was enabled by default on the phone without my having to do anything at all, just as long as I touched the rocker button whilst a sound was playing.
That was great – it even persists the volume between runs of the application without my having to write any additional code – it was everything I wanted, except for one tiny problem; all my sounds were incredibly short. Even if they weren’t, this behaviour is unexpected and likely to confuse the user. I wanted the game to respond to the volume rockers all the time, even when no sound was playing. The popular Flight Control game has the exact behaviour I was looking for.
So I went and asked my old pal Google how those geniuses at Firemint had done it. Well, it turns out there’s a hack, and a really dirty one at that, but it works. I was using the AVAudioPlayer Objective-C class to play my sounds. Once initialized with the URL path of a bundled audio file, the AVAudioPlayer instance can be made to buffer it’s sound in readiness to play the sound by calling the “prepareToPlay” method. The idea is that you call “play” soon after. Well it turns out, if you initialize an instance of AVAudioPlayer with a valid audio file and call “prepareToPlay” on it but never call “play”, that’s enough to put the application into a permanent state of responsiveness to the volume rocker.
I felt dirty after implementing this hack and I’ve submitted a feature request for a better way of doing it at bugreport.apple.com, and I encourage anyone with similar requirements to do the same, but it’s quite effective. The only small problem I found is that a few seconds after calling prepareToPlay on my otherwise unused AVAudioPlayer instance I see a small leak coming from the instance even though I have kept a reference to the object as class member variable. There could be a way around this that I haven’t discovered yet but since it only leaks a tiny amount of memory and it only happens once, I haven’t gone to any great effort to resolve it.
If anyone knows a better way of doing this please do comment and explain how. I feel somewhat dubious that this is how every developer has achieved interaction with the volume rocker button, especially on such popular titled as Flight Control but I can’t find another way. Unfortunately I can’t seem to track down the forum post that lead me to this discovery but if I find it I will post a link and credit the wise man (or woman) who discovered the hack.
