Duck and Resume: a little love from the iPhone music player

I just got a couple of nice features working in one of my projects at Raizlabs. These are both easy to implement, but difficult to find a good explanation of how to implement. Here I’ll show you how to:

Duck (lower the music volume underneath application sounds)
Resume playback after an interruption (a phone call or timer)

Duck!

Our client has music playing from the user’s iPod playlist, and has verbal cues as well. The cues are sometimes hard to hear over the music, so he wanted the music to duck: to fade to a lower volume before the cue starts and to fade back up to the original volume after the cue stops.

It’s quite clear from the Apple documentation that this is possible. As is their wont, they have pages and pages of documentation saying what is possible, but not a single piece of code. And in this case, there is one crucial piece of info missing from their documentation.

Audio Session
To make this work, you have to set up an audio session.

AudioSessionInitialize (NULL, NULL, NULL, NULL);

Audio Category
Choose your category. For ducking to work, you need to choose one of the categories that mixes audio from other applications, so choose Ambient, Playback, or PlayAndRecord. (For more info on the categories, see Apple’s session category doc and the files linked on that page.)

Set your category as a property on the session thus:

UInt32 sessionCategory = kAudioSessionCategory_AmbientSound;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (sessionCategory), &sessionCategory);

If you have chosen either Playback or PlayAnd Record, you’ll also need to set the mixing property (Ambient mixes by default so you don’t have to include this code if you use Ambient):

OSStatus propertySetError = 0;
UInt32 allowMixing = true;
propertySetError = AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof (allowMixing), &allowMixing);

You can see an explanation of the above in Apple’s Audio Session Cookbook.

Duck Property (if it looks like a duck and sounds like a duck…)
Then, to enable ducking, you set the duck property:

OSStatus propertySetError = 0;
UInt32 allowMixing = true;
propertySetError |= AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck, sizeof(allowMixing), &allowMixing);

Finally, you have to activate your session:

AudioSessionSetActive(YES);

All this is somewhat clear from the dozens of pages of Apple docs.

At this point you can create an AVAudioPlayer (or whichever sound player/recorder you want), an MPMusicPlayerController (or whichever media player you want) and play them both at the same time.

The Problem
The only problem is that if you put all of this code in your app delegate, a view controller, or any other central place, your music won’t duck. Well, it will duck, but it will start ducking when your app opens and stay down until your app closes. What the d*ck?

The Solution
What Apple doesn’t tell you is that their magical duck property takes effect when the session is activated. So you have to take the non-intuitive step of activating your session just before you tell your player to play, and then deactivate as soon as the sound has finished playing.

-(void) playSomething {
AudioSessionSetActive(YES);
[player play];
}
-(void) finishedPlayingSomething {
AudioSessionSetActive(NO);
}

(Thank you Google Translate and the people of Japan for your help in this — there are not a lot of posts if you search for kAudioSessionProperty_OtherMixableAudioShouldDuck.)

Resuming Playback

Resuming playback after an interruption is easy. In fact, it’s so easy I’m not sure it’s actually happening. Let me know if I missed something.

I spent an hour reading through all the various Apple docs on handling interruptions, working with iPod music, session categories that mix audio, the interruption lifecycle, etc etc. It’s not clear from the docs that you can resume iPod playback if you have a session category that mixes audio types. They do very clearly state that if you are using an AVAudio player, after an interruption starts you need to *check whether resumption of audio process is supported, *save state and context, and *update the user interface; and that after the interruption ends, you have to *restore the state and context, *reactivate the audio session, and *update the user interface (of course, with no code snippets at all). So I was prepared for the long haul.

There are two protocol methods related to the AVAudioPlayerDelegate (or AVAudioRecorderDelegate) that you need to “implement”: audioPlayerBeginInterruption and audioPlayerEndInterruption. The weird part is that you don’t have to do anything in these methods, just place them in your implementation file. Here is my code:

-(void) audioPlayerBeginInterruption:(AVAudioPlayer *)player {
// magic is happening
}
-(void) audioPlayerEndInterruption:(AVAudioPlayer *)player {
// thanks Apple for coding so I don’t have to!
}

You really don’t have to do anything at all…and it works perfectly. Or at least it appears to work well, I’m still waiting to wake up and find that I dreamed it all.

One thing to note is that these methods happen in your sound player, but affect the iPod player. We have one class that owns an AVAudioPlayer, and a different class that owns an MPMusicPlayerController. These two delegate protocol methods need to be in your AVAudioPlayer delegate, and then yes, they will also resume iPod music.

All in all, both of these are quite easy to implement, but difficult to find out exactly how to implement them. If you have noticed that I didn’t add any links to Apple docs in this section, that’s because they weren’t helpful. We love you Apple, but sometimes you just don’t explain things very well.

Good luck with your iPhone apps!

Share and Enjoy:
  • Facebook
  • Twitter
  • Digg
  • del.icio.us
  • StumbleUpon
  • Mixx
  • Google Bookmarks
  • Blogplay
  • email
  • LinkedIn
  • MySpace
  • Reddit
  • Technorati
  • Yahoo! Buzz
  • PDF

This entry was posted on Thursday, January 21st, 2010 at 6:06 pm and is filed under Programming. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

3 Responses to “Duck and Resume: a little love from the iPhone music player”

  1. Jeremy Says:

    Just what I was looking for – thanks for the code snippets!

  2. Trackbacks Says:

Leave a Reply

You must be logged in to post a comment.