views:

957

answers:

2

The documentation is rather poorly written when talking about playing audio in the background. It gives the impression that all you have to do to continue playing the audio that you are currently playing is to just add a key/value pair to the info.plist file and wallah, it's magic.

However, this is not the case. For instance, if I play an mp3 that is 2 minutes long, obviously the audio is long enough to be able to play after I hit the home button to send my app to the background. The thing is, even though I have that key/value pair in my info.plist file, it pauses the audio and then resumes playing once I switch back to the app.

Apple states that all frameworks for audio support background and the sound should continue playing up until the point that it ends and then Apple will suspend your app.

So, my question is: What are they doing that I am missing? Do I also have to use their new delegates somehow or call the audio on the applicationDidEnterBackground? To me, that wouldn't make sense since I am not saving state (they do for me with fast app switching) or really handling anything in the background other than audio which they say is supposed to be handled automagically.

+8  A: 

The one part missing from the documentation is you need to set your audio session.

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

Add that and you'll be good to go.

Joshua Weinberg
That should be the answer.
Kendall Helmstetter Gelner
At least someone realizes it! :)
Joshua Weinberg
Josh, you were getting the checkmark, don't worry :) It worked like a charm. stackoverflow was screwing up on my slow network so i had to close the browser to correct it
iWasRobbed
Just playing around :) You should file a radar about the documentation bug if you feel it wasn't clear that it is dependent on the audio session.
Joshua Weinberg
Good point, I'll be sure to so we can save others from headaches. Thanks again
iWasRobbed
+2  A: 

You also need to make calls to beginBackgroundTaskWithExpirationHandler: before you start a song (do it all the time whether in foreground or not) and endBackgroundTask when it ends. Something like this:

// ivar, initialized to UIBackgroundTaskInvalid in awakeFromNib
UIBackgroundTaskIdentifier bgTaskId; 

When starting an audio player we save the bg task id:

if ([audioPlayer play]) {
  bgTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
}

Now the audioPlayerDidFinishPlaying: callback will still occur because we have started a "background task" and in the callback we can start the next audio file.

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)success
{
    UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;

    if (self.haveMoreAudioToPlay) {
        newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
        [self playNextAudioFile];
    }

    if (bgTaskId != UIBackgroundTaskInvalid) {
        [[UIApplication sharedApplication] endBackgroundTask: bgTaskId];
    }

    bgTaskId = newTaskId;
}      
progrmr
Why would you start a background task just to play audio. This is not necessary at all.
Joshua Weinberg
Do you think this would keep it alive only for the time Apple allots when you request `beginBackgroundTaskWithExpirationHandler` or will it keep playing as long as you are using `audioDidFinishPlaying` to loop a sound or play a new sound?
iWasRobbed
+1 to progrmr for going above and beyond since i will indeed need that as well for what i am doing (not sure why someone downvoted you, but i appreciate your answer)
iWasRobbed
Do you really need to go through all the trouble with task completion to start background audio? This is contrary to the material in the WWDC 2010 video on the topic ('Session 109 - Adopting Multitasking on iPhone OS, Part 2') at around minute 24, they show their avTouch app being converted for background audio, and they key was to prevent further GPU usage when you're in the background because that's what will kill the app. This was done by registering for a UIApplicationDidEnterBackgroundNotification and preventing GPU usage while in the background.
Joost Schuur
@IWasRobbed: you need this if you want to switch audio tracks in the background (which wasn't exactly the original question) otherwise your app suspends when the audio finishes playing. See [this discussion](https://devforums.apple.com/message/239381).
progrmr
@Joshua: If you started the audio in the foreground and want to suspend when that audio file ends, then you don't need to do this. But you can't play the next audio track because you will never get the audioPlayerDidFinishPlaying callback if you're didn't tell the system that you are doing background processing by using beginBackgroundTaskWithExpirationHandler.
progrmr