iPhone SDK: Making Your AudioSession Routes Play Nicely with the Vibrate Switch

So, you’re writing an awesome multimedia application for the iPhone. Everything’s going swimmingly, and you even have some nice animation effects for when the iPhone is plugged into an external accessory, just like the iPod app:

- (void)toggleVolumeDisplay: (BOOL)show
{
	BOOL isShowing = (volumeSlider.alpha > 0.0);
	if( isShowing && !show )
	{
		// Animate volume slider down and out, while animating play controls to center of view
	}
	else if( !isShowing && show )
	{
		// Animate volume slider in, while animating play controls up to the top of the view
	}
}

Or something like that. That’s purely to illustrate what you might be doing in response to a run-of-the-mill hardware route change (headphones plugged in, connected to speaker dock, etc.), which of course you’re detecting like this:

	// When the view is loaded
	AudioSessionAddPropertyListener( kAudioSessionProperty_AudioRouteChange, RouteChangeListener, (void *)self);

	// Called later as part of your listener callback
	CFStringRef state = nil;
	UInt32 propertySize = sizeof(CFStringRef);
	OSStatus result = AudioSessionGetProperty( kAudioSessionProperty_AudioRoute, &propertySize, &state);
	if( result == kAudioSessionNoError)
	{
		if( CFStringGetLength(state) > 0)
		{
			if ([(NSString *)state compare:@"LineOut"] == NSOrderedSame) // only special case we care about
				shouldShowVolumeControl = NO;
		}
		else
			; // vibrate switch engaged
	}
	else
	{
		if (result == kAudioSessionUnsupportedPropertyError)
			shouldShowVolumeControl = NO; // probably simulator
		else
			; // error encountered
	}

This has been documented in plenty of places.

Then, you flip the Ring/Vibrate switch and everything grinds to a halt. Your route change callback doesn’t get called, and now your MPVolumeView declares to the world that the volume isn’t available. Of course it’s not available. That’s why we’re going to all this trouble – to hide the volume slider when we aren’t permitted to change it!

After a long time pondering this problem (it’s been on the backburner for about a week), I eventually realized that the route change callback might be dependent on the so-called “category” of the AudioSession. For example, if your app just plays alert sounds occasionally, you (probably) don’t want them sounding when the phone is set to vibrate. On the other hand, it’s critical for audio players like the iPod app to be able to play even when in vibrate mode. So, how do we go about changing our session category and getting those mysteriously absent route change callbacks? Like so:

	UInt32 audioCategory = kAudioSessionCategory_MediaPlayback;
	AudioSessionSetProperty( kAudioSessionProperty_AudioCategory, sizeof(UInt32), &audioCategory);

This tells the AudioSession that we want to be regarded as a “media playback” app, rather than, for example, an “ambient sound” app. Now, regardless of the position of the vibrate switch, we get notifications when our iPhone is docked and undocked from external accessories, and we can animate our volume controls as we desired.

Category: Programming, iPhone View Comments

blog comments powered by Disqus

Back to top