It's like "Google Maps" navigate in the background.
I have added "App plays audio" to "Required background modes" already, but it doesn't work.
What's wrong with it?
Here is the sample source code:
-(void)applicationDidEnterBackground:(UIApplication *)application
{
UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:#selector(isMultitaskingSupported)])
{
backgroundSupported = device.multitaskingSupported;
}
if (backgroundSupported && _bgTask==UIBackgroundTaskInvalid )
{
UIApplication* app = [UIApplication sharedApplication];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (app.applicationState==UIApplicationStateBackground && _bgTask!=UIBackgroundTaskInvalid)
{
[NSThread sleepForTimeInterval:3];
[self playAudio];
}
[app endBackgroundTask:_bgTask];
_bgTask = UIBackgroundTaskInvalid;
});
}
}
-(void)playAudio {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
NSURL *audioFileLocationURL = [[NSBundle mainBundle] URLForResource:#"sound" withExtension:#"caf"];
NSError *error;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileLocationURL error:&error];
[self.audioPlayer setNumberOfLoops:-1];
self.audioPlayer.delegate = self;
[self.audioPlayer prepareToPlay];
[self.audioPlayer play];
}
The url of audio file maybe is from web, and will play multiple audio file in a queue.
From Apple's doc on the subject:
Because it's likely any background tasks you start in
applicationDidEnterBackground: will not run until after that method
exits, you should request additional background execution time before
starting those tasks. In other words, first call
beginBackgroundTaskWithExpirationHandler: and then run the task on a
dispatch queue or secondary thread.
From my own experience of playing background audio, if you don't request extra time you need to set things up beforehand so that [self.audioPlayer play] is literally the first line of code in applicationDidEnterBackground, otherwise the system suspends the app before the audio has a chance to kick in and keep it awake.
Related
I have an app mostly based around Core Bluetooth.
When something specific happens, the app is woken up using Core Bluetooth background modes and it fires off an alarm, however I can't get the alarm working when the app is not in the foreground.
I have an Alarm Singleton class which initialises AVAudioPlayer like this:
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:soundName
ofType:#"caf"]];
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[self.player prepareToPlay];
self.player.numberOfLoops = -1;
[self.player setVolume:1.0];
NSLog(#"%#", self.player);
This is the method that is called when my alarm code is called:
-(void)startAlert
{
NSLog(#"%s", __FUNCTION__);
playing = YES;
[self.player play];
NSLog(#"%i", self.player.playing);
if (vibrate) {
[self vibratePattern];
}
}
Now when the app is in the foreground, self.player.playing returns 1 however when the app is in the background self.player.playing returns 0. Why would this be?
All the code is being called, so the app is awake and functioning.
The vibrate works perfectly which uses AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
Any idea why this sound won't play?
Thanks
Apple has a nice Technical Q&A article about this in its documentation (see also Playing and Recording Background Audio).
I think one big thing missing is that you haven't activated the Audio Background Mode in the Xcode settings:
Maybe also adding [self.player prepareToPlay] in your alert method is helpful.
I have an App than also needs background audio but my App works with the App background mode "Voice over IP" as it needs to record sometimes. I play background audio telling the App singleton I need to play audio in background:
UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;
if([thePlayer play]){
newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
}
EDIT: You must call [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL]; before your app goes to background. In my app, it is at the same time you start playing, in yours, if the player might be started in background, you should do:
- (void)applicationDidEnterBackground:(UIApplication *)application{
// You should retain newTaskId to check for background tasks and finish them
newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
}
Apple docs
Either enable audio support from the Background modes section of the Capabilities tab
Or enable this support by including the UIBackgroundModes key with the audio value in your app’s Info.plist file
I take it you have the audio background mode specified for the app. Even so, I'm not sure you can set an audio session to be active while in the background. You need to have activated it before going into the background. You may also need to play some silent audio to keep this active, but this is seems like bad practice (it may drain the battery). Looking at the docs for notifications there seems to be a way to have a local notification play an audio sample that's included in your bundle, which seems to be what you want to do, so maybe that's the way to go.
Try this :
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
link
Try this http://www.sagorin.org/ios-playing-audio-in-background-audio/
You need to enable your app to handle audiosession interruptions (and ended interruptions) while in the background. Apps handle audio interruptions through notification center:
First, register your app with the notification center:
- (void) registerForMediaPlayerNotifications {
[notificationCenter addObserver : self
selector: #selector (handle_iPodLibraryChanged:)
name: MPMediaLibraryDidChangeNotification
object: musicPlayer];
[[MPMediaLibrary defaultMediaLibrary] beginGeneratingLibraryChangeNotifications];
}
Now save player state when interruption begins:
- (void) audioPlayerBeginInterruption: player {
NSLog (#"Interrupted. The system has paused audio playback.");
if (playing) {
playing = NO;
interruptedOnPlayback = YES;
}
}
And reactivate audio session and resume playback when interruption ends:
-(void) audioPlayerEndInterruption: player {
NSLog (#"Interruption ended. Resuming audio playback.");
[[AVAudioSession sharedInstance] setActive: YES error: nil];
if (interruptedOnPlayback) {
[appSoundPlayer prepareToPlay];
[appSoundPlayer play];
playing = YES;
interruptedOnPlayback = NO;
}
}
Here's Apple's sample code with full implementation of what you're trying to achieve:
https://developer.apple.com/library/ios/samplecode/AddMusic/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008845
I was also facing the same problem, but i was only facing it only during initial time when i was trying to play a sound while app was in background, once the app comes in foreground and i play the sound once than it works in background also.
So as soon as app is launched/ login is successful in my case, i was running this code:
[self startRingcall];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self stopRingcall];
});
- (void)startRingcall
{
if( self.audioPlayer )
[self.audioPlayer stop];
NSURL* musicFile = [NSURL fileURLWithPath:[[[UILayer sharedInstance] getResourceBundle] pathForResource:#"meetcalling" ofType:#"caf"]];
NSError *error;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:musicFile error:&error];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
if (error != nil) {
NSLog(#"meetringer sound error -> %#",error.localizedDescription);
}
self.audioPlayer.volume = 0;
[self.audioPlayer play];
self.audioPlayer.numberOfLoops = 1;
}
- (void)stopRingcall
{
if( self.audioPlayer )
[self.audioPlayer stop];
self.audioPlayer = nil;
}
In Info.plist I added:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>location</string>
</array>
...
I load the sound like this:
NSURL *urlFail = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/criticalSound.wav", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:urlFail error:&error];
[audioPlayer setDelegate:self];
audioPlayer.numberOfLoops = 0;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
[audioPlayer setVolume:1.0];
if (audioPlayer == nil){
}else{
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
musicPlayer = [MPMusicPlayerController iPodMusicPlayer];
if (musicPlayer.playbackState == MPMusicPlaybackStatePlaying)
{
self.wasMusicAppOn = YES;
[musicPlayer pause];
}else{
self.wasMusicAppOn = NO;
}
[audioPlayer play];
}
This works fine when app is active.
But I have a location update when application is in background and from time to time (like when user pass 500m) I should play this sound.
So I put this code there.
Anyway problem is that sound plays when app is in foreground but doesn't work when it is in background.
What am I doing wrong here?
As far as I've been able to tell, you must actually be playing sound while going into the background to be able to continue playing sound in the background.
Also, the description for AVAudioSessionCategoryAmbient states that "Your audio is silenced by screen locking and by the Silent switch (called the Ring/Silent switch on iPhone)".
You need to make couple of changes in plist file.
1) Set Required background mode to App plays audio
2) set Application does not run in background to YES.
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&setCategoryErr];
[[AVAudioSession sharedInstance] setActive:YES error:&activationErr];
Then, you need to write these much code in AppDelegate
Now, you can easily run audio while phone screen locks or in background.
It works fine for me. :)
for more please refer to Playing Audio in background mode and Audio Session Programming Guide
I'm building a running app that notifies the user by audio that they need to start running or start walking every couple of minutes. I was able to get the sound to run in the background, even with the lock screen on using AVAudioPlayer.
Here's snippets of what I have:
ViewController.h
#property (nonatomic, strong) AVAudioPlayer *audioWalk;
ViewController.m
- (void)viewDidLoad{
[super viewDidLoad];
// Walk Audio File
NSString *soundFile2 = [[NSBundle mainBundle] pathForResource:#"Walk" ofType:#"wav"];
audioWalk = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:soundFile2] error:nil];
// Load the audio into memory
[audioWalk prepareToPlay];
// Permit the timer to run in the background
bgTask = 0;
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
// Prevent the application from going to sleep while it is running
[UIApplication sharedApplication].idleTimerDisabled = YES;
// Starts recieving remote control events and is the first responder
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
// Plays audio
[audioWarmup play];
}
I'm trying to figure out how to play my audio without interrupting the music that is playing in the background. Also, the sounds need to play in the background and when the screen is locked. Any help would be much appreciated!
I was able to fix it. Here's what I did below. This solutions allows an audio file to run in the background, even with the lock screen, while not interrupting or pausing audio from other applications (particularly music).
- (void)viewDidLoad{
[super viewDidLoad];
// Play audio even if lock screen is on, the with options allows audio from other applications to play without interruption
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error: nil];
[[AVAudioSession sharedInstance] setActive: YES error:nil];
// Walk Audio File
NSString *soundFile2 = [[NSBundle mainBundle] pathForResource:#"Walk" ofType:#"wav"];
audioWalk = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:soundFile2] error:nil];
// Load the audio into memory
[audioWalk prepareToPlay];
// Permit the timer to run in the background
bgTask = 0;
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
// Prevent the application from going to sleep while it is running
[UIApplication sharedApplication].idleTimerDisabled = YES;
// Starts receiving remote control events and is the first responder
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
// Plays audio
[audioWarmup play];
}
The following is the code that works just find to get the audio to stream:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:stringURL]];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
connectionPlay = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSError *playerError;
player = [[AVAudioPlayer alloc] initWithData:streamData error:&playerError];
player.numberOfLoops = 0;
player.volume = 1.0f;
[player prepareToPlay];
if (playerError) {
NSLog(#"audio player error: %#", [playerError localizedDescription]);
}
if (player == nil)
NSLog(#"%#", [playerError description]);
else
[player play];
When I attempt to take the app into background mode, i seizes to play. I have gone into the .plist and have entered Required background modes, item 0, App plays Audio. This did not fix my problem.
I began placing this:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
error:nil];
[[AVAudioSession sharedInstance] setActive:YES
error:nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
Inside of
- (void)applicationWillResignActive:(UIApplication *)application
- (void)applicationDidEnterBackground:(UIApplication *)application
and
inside of the viewDidLoad in the main ViewController. As well as right after [player prepareToPlay].
I am not sure if the issue is with me not setting the proper background settings or with the app cutting out the connection. Basically, I am not sure what I am missing.
I have been looking at:
http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVAudioSession_ClassReference/Reference/Reference.html
and
https://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/Introduction/Introduction.html
EDIT: anyone?
I have no idea what it was, but I just transferred all of code to a new project, added this code
- (void)applicationWillResignActive:(UIApplication *)application
{
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
error:nil];
[[AVAudioSession sharedInstance] setActive:YES
error:nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
and the plist settings, and it seems to work on the phone, but not the simulator..
I am doing sample application that play a song and that song will still playing even the application goes background.
In iOS 5 xcode simulator, it still play the song when app goes to background but when I run on iOS 5.0.1 iPad2 device, app stop playing sound when it goes to background.
The following are the code to play a song and do background task. Do you face this kind of issue in iOS 5.0.1? or am I missing something in code? But I am wondering how it is working in simulator?
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url=[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"01-Track" ofType:#"mp3"]];
NSError *error;
audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
if(error)
{
NSLog(#"Error in audio palyer: %#", [error localizedDescription]);
}
else
{
audioPlayer.delegate =self;
[audioPlayer prepareToPlay];
trackControl.maximumValue=[audioPlayer duration];
trackControl.value=0.0;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
}
}
-(IBAction)playAudio:(id)sender
{
UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;
if([audioPlayer play]){
newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
}
Thanks!
For audio to continue working in background you should add a key called Required Background
Modes in your info.plist with value App plays audio.But it's use is not very much promoted by apple unless it's absolutely necessary.