Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing and Troubleshooting Background Audio in iOS

There are a lot of questions relating to background music playback in iOS on StackOverflow. None fully explore all edge cases, the aim of this question is to be the final word in background audio question on iOS

Definitions & Assumptions

All the code, questions and examples refer to ios5.

"background" — The state an app is put into when the user presses the home button or the power button (so the devices displays the lock screen). The app can also be put into background using the multitasking switcher or the multitasking gestures on iPad.

"audio" — Audio played back using AudioQueue (including AVAudioPlayer)

Prerequisites

As I understand it there are 2 requirements to get an app to play audio in the background.

  1. Set UIBackgroundModes to audio in the Info.plist
  2. [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

Requirements

My use-case is playing relatively long audio in the background (music). There are potentially hundreds of tracks and the app will play them sequentially. It can be considered that the audio will play indefinitely.

The app will handle interruptions by pausing the playback.

Questions

I've had mixed success with:

[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:...];

Allowing audio to play in the background. But I'm confused as to if its required and how it differs to:

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

Edge Cases

  1. Interruptions. If you register to be notified of audio interruptions (phone calls etc), by becoming the delegate of AVAudioPlayer. For example, if you then pause or stop your audio when an interruption starts and resume when it ends is your app suspended if the interruption exceeds 10 minutes (max time allowed for background tasks to complete)?

  2. The Simulator will stop the audio if Lock or Home are invoked, while using:

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

However this works on a device. Is this a known issue?

like image 574
Richard Stelling Avatar asked Aug 15 '12 10:08

Richard Stelling


People also ask

How do you put Background Sounds on iOS?

Go to Settings > Accessibility > Audio/Visual > Background Sounds, then turn on Background Sounds. Set any of the following: Sound: Choose a sound; the audio file downloads to your iPhone. Volume: Drag the slider.

What is background sound on iPhone?

Background Sounds offers six different ambient noises that play on a loop: rain, stream and ocean, as well as bright, balanced and dark noise, which are different pitches of white noise. You can play these by themselves or under any podcast, music or video streaming app.

Why don't I have Background Sounds iOS 15?

Apple added new Background Sounds (rain, bright noise, ocean, and more) to the iPhone with iOS 15. To activate, head to Settings > Accessibility > Audio/Visual > Background Sounds > Toggle to on.

Can you add more Background Sounds iOS 15?

With iOS 15 and iPadOS 15 or later, you can play ambient soundscapes in the background with your iPhone, iPad, or iPod touch. Listen to ocean, rain, bright noise sounds, and more.


2 Answers

I have some experience with GPS background mode, and background audio. This is not exactly the same as your situation (you want to play a long audio file, and I play short messages) but here's what I can tell you:

  • beginBackgroundTaskWithExpirationHandler This selector has one purpose when being invoked when in background: avoid the application to return to the suspended state in which no code can be invoked anymore (you're "frozen"). So as long as you invoked beginBackgroundTaskWithExpirationHandler and before you terminated your long running task with beginBackgroundTaskWithExpirationHandler, you use the CPU, and consume battery.
    I really doubt that playing a file in the background should use the battery of the iPhone as if it was running an app so I doubt that beginBackgroundTaskWithExpirationHandler is really involved in your flow.

  • Simulator: don't rely on the simulator: it does not fully implement background modes. Actually, when you click on the home button, your app goes in background, but at this stage, you may still be able to execute code in your app. After a while, then, your app will be suspended (=frozen), and your code execution will be suspended in order to save the battery. This suspended state will never occur on the simulator.

  • Interruptions. It's not up to you to pause/resume the playback when a phone call comes in. the platform is in charge of this, and you can just react to this with your AVAudioSessionDelegate . However, you can influence the way your session is going to interact with other audio sounds by setting property on your audio session (see kAudioSessionProperty_OverrideCategoryMixWithOthers for instance). So the flow is more: your describe the way your audio session should interact with the rest of the system, the system will mix the sounds accordingly to that, and if your session gets interrupted, you'll be notified with the AVAudioSessionDelegate.

Hope this helps.

like image 83
yonel Avatar answered Sep 30 '22 21:09

yonel


I have used below code to Device Control -

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];

Used to get register for listening the remote control. Once done remove it -

[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];

make the App canBecomeFirstResponder-

- (BOOL)canBecomeFirstResponder {
    return YES;
}

Used delegate method to handle iPhone control, like play and pause while doble tap on the home button

- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
    //if it is a remote control event handle it correctly
    if (event.type == UIEventTypeRemoteControl) {
        if (event.subtype == UIEventSubtypeRemoteControlPlay) {
           [audioPlayer play];
            NSLog(@"play");
        } else if (event.subtype == UIEventSubtypeRemoteControlPause) {
           [audioPlayer stop];
             NSLog(@"pause");
        } else if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause) {
            NSLog(@"toggle");
        }
    }
}
like image 31
Sanoj Kashyap Avatar answered Sep 30 '22 21:09

Sanoj Kashyap