I'm trying to use new ios8 notification actions to let users play audio from their lock screen.
I manage to download the data in the background, but the avaudioplayer does not seem to play.
Any help would be greatly appreciated !
Here is the code :
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler
{
//handle the actions
if ([identifier isEqualToString:#"ACCEPT_IDENTIFIER"]){
Message *newMessage = [Message rawMessageToInstance:[userInfo valueForKey:#"message"]];
AVAudioSession* session = [AVAudioSession sharedInstance];
BOOL success; NSError* error;
success = [session setCategory:AVAudioSessionCategoryPlayback
error:&error];
if (!success)
NSLog(#"AVAudioSession error setting category:%#",error);
[session setActive:YES error:nil];
NSLog(#"%lu",newMessage.identifier);
[ApiUtils downloadAudioFileAtURL:[newMessage getMessageURL] success:^void(NSData *data) {
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithData:data error:nil];
[player setVolume:2];
[player play];
[self performSelector:#selector(completeNotif:) withObject:completionHandler afterDelay:[player duration]];
} failure:^(){
completionHandler();
}];
}
}
I had the same problem ... first I came up, it must have to do something with background and sound playing options - so I added the background audio flag to the info.plist.
With standard AVAudio stuf it works on the simulator, but not on the device. The special hook is, that you want to start the audio while you are in background - so add the code from anger: How to use kAudioSessionProperty_OverrideCategoryMixWithOthers before starting with the AVAudioPlayer and it will also work on the device.
Hope that helps, for me it works.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I am able to duck my background audio while playing new sounds. However I am unable to resume the background audio level to maximum again. When my delegate tries to "unduck" it just keeps being ducked. The normal fix for this is AudiosessionSetProperty, but that's deprecated in iOS 7 and Apple doesn't give any hints in the deprecation warnings or documentation.
I call this method on load of view.
- (void) configureAVAudioSession
{
//get your app's audioSession singleton object
AVAudioSession* session = [AVAudioSession sharedInstance];
//error handling
BOOL success;
NSError* error;
success=[session setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
if (!success)
{
NSLog(#"AVAudioSession error :%#",error);
}
else
{
}
success = [session setActive:YES error:&error];
if (!success) {
NSLog(#"Error setting active %#",error);
}
else
{
NSLog(#"succes settings active");
}
}
This is when I play audio
-(void)playTimeOnGo
{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"just-like-magic"
ofType:#"mp3"]];
self.audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:nil];
self.audioPlayer.delegate=(id<AVAudioPlayerDelegate>)self;
//get your app's audioSession singleton object
AVAudioSession* session = [AVAudioSession sharedInstance];
//error handling
BOOL success;
NSError* error;
success=[session setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionDuckOthers error:&error];
[self.audioPlayer prepareToPlay];
[self.audioPlayer play];
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
This is my delegate when audio is done to resume background audio and undock audio
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)data successfully:(BOOL)flag{
[self configureAVAudioSession];
NSLog(#"playback ended");
}
So how do I unduck the background music again without deprecated APIs? calling [self configureAVAudioSession]; apparently doesn't work....
Ladies an gentlemen, I provide you with a non-deprecated working example of how to properly use ducking in iOS 7.
In your whatever "view load method" call this method, where the bool is set to YES. this will mix the background audio and prepare it for ducking
- (void) configureAVAudioSession:(bool) active
{
//get your app's audioSession singleton object
AVAudioSession* session = [AVAudioSession sharedInstance];
//error handling
BOOL success;
NSError* error;
success=[session setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
if (!success)
{
NSLog(#"AVAudioSession error :%#",error);
}
else
{
}
success = [session setActive:active error:&error];
if (!success) {
NSLog(#"Error setting active %#",error);
}
else
{
//NSLog(#"success settings active");
}
}
Play you audio file with this method. Watch how ducking happens.. remember to set the delegate as i have in this example EVERY time you play a new audio alert. dont set it once in view load methods.
-(void)playTimeOnGo
{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
//alertName is the name of your audio file without extention. extenstions is, doh extenstion like "mp"
pathForResource:_dataManager.optionsSettings.setStarts.alertName
ofType:_dataManager.optionsSettings.setStarts.alertExtension]];
self.audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:nil];
self.audioPlayer.delegate=(id<AVAudioPlayerDelegate>)self;
//get your app's audioSession singleton object
AVAudioSession* session = [AVAudioSession sharedInstance];
//error handling
BOOL success;
NSError* error;
success=[session setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionDuckOthers error:&error];
[self.audioPlayer prepareToPlay];
[self.audioPlayer play];
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
the delegate will call this method after playback and turn up the volume of background music :) Please dont get confused that i am using a thread for this. u can just call the method if you wish but this stalls the main thread about a second maybe even two seconds. So it thats okay with you, dont use a thread and just call [self configureAVAudioSession:NO];
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)data successfully:(BOOL)flag
{
_playBackFinishedDelegateThead = [[NSThread alloc] initWithTarget:self selector:#selector(configureAVAudioSession:) object:NO];
[_playBackFinishedDelegateThead start];
}
This example IS 100% tested and working in my app.
My app Used Required background modes "App plays audio"
I can record on background mode. but if phone call?
I used the delegate
-(void)loadView
{
NSError *error=nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
if (error) {//Do something error}
error = nil;
[[AVAudioSession sharedInstance] setActive:YES error:&error];
if (error) {//Do something error}
}
-(void)audioRecorderBeginInterruption
{
[AVAudioRecorder pause];
}
-(void)audioRecorderEndInterruption
{
[AVAudioRecorder record];
}
These two methods are executed normally
But AVAudioRecorder is not Run.
I want keep recording when the phone call end.
try this delegate method,
- (void)applicationWillResignActive:(UIApplication *)application
{
//post your Notification
}
it should be call back at your working class which include your method,
-(void)audioRecorderEndInterruption
{
// add Notification
[AVAudioRecorder record];
}
I have a very simple app that I have built to test out AVAudioPlayer. I have a 16s aif file that plays just fine on my Mac. Put it in a simple app and ran on my iPhone but no sound. Sending 'play' to the object returns success and audioPlayerDidFinishPlaying comes back with success after 16 seconds. Still no sound. Have checked the sound volume on the iPhone, just fine. Have checked to make sure my AVAudioPlayer object has volume set at 1.0 - it does. Can anybody help?
Here's my code
NSString *soundPath = [[NSBundle mainBundle] pathForResource:#"Soothing" ofType:#"aif"];
NSURL *playURL = [NSURL fileURLWithPath:soundPath];
NSError *error;
self.myAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:playURL error:&error];
NSLog(#"About to play sound");
self.myAudioPlayer.delegate = self;
[self.myAudioPlayer prepareToPlay];
bool success = [self.myAudioPlayer play];
if (!success) {
NSLog(#"Failed to play file");
} else NSLog(#"Looks like it succeded");
OK - got it figured out. I had not configured the AVAudioSession for my app. Did something very simple as follows (copied from Apple's example code):
[[AVAudioSession sharedInstance] setDelegate: self];
NSError *setCategoryError = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &setCategoryError];
if (setCategoryError)
NSLog(#"Error setting category! %#", setCategoryError);
Sound now plays - plus learned a lot about how to control playback through screen locks, etc., which will be valuable.
Did you check if you get anything from the error?
if(error) {
NSLog( #"%#", [error localizedDescription] );
}
I'm trying to play audio file at particular time which i have set.for that I have added audio in "UIBackgroundModes" in info.plist and backgroundtaskidentifier in didEnterBackground.Its working in simulator but not in device.Can anyone help to solve this.
Thanks in Advance...
You can check out this reference app with streaming and backgrounding of audio.
EDIT:
Example of a local notification w/ attached sound. More detail here.
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Application entered background state.");
// bgTask is instance variable
NSAssert(self->bgTask == UIInvalidBackgroundTask, nil);
bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
});
}];
dispatch_async(dispatch_get_main_queue(), ^{
while ([application backgroundTimeRemaining] > 1.0) {
NSString *friend = [self checkForIncomingChat];
if (friend) {
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif) {
localNotif.alertBody = [NSString stringWithFormat:
NSLocalizedString(#"%# has a message for you.", nil), friend];
localNotif.alertAction = NSLocalizedString(#"Read Message", nil);
localNotif.soundName = #"alarmsound.caf";
localNotif.applicationIconBadgeNumber = 1;
[application presentLocalNotificationNow:localNotif];
[localNotif release];
friend = nil;
break;
}
}
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
});
}
If I understand you correctly, you want to play a sound at a particular time, no matter if your app is active and foregrounded at that time? Your only (app review-friendly) option is to schedule a local notification, as in slf's answer. UIBackgroundModes won't help you: the audio background mode is for being allowed to execute code while backgrounded only when you start playing audio when you are foregrounded. Example usages are music players and radio apps.
If you explain your use case more thoroughly, I might have a better answer.
I have a series of audio files which I want to play in sequence. I setup audioPlayerDidFinishPlaying delegate method to start the next file in the sequence. This works with no problem if I turn off screen lock or if I run it on the emulator or on an iOS4 device. However, when I run it on a disconnected device, iOS5, screen locked, it stops after the first track. Is there some way to start a new sound track after the screen lock has come on?
Here is my basic code
// Registers this class as the delegate of the audio session.
[[AVAudioSession sharedInstance] setDelegate: self];
// Use this code instead to allow the app sound to continue to play when the screen is locked.
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
UInt32 doSetProperty = 0;
AudioSessionSetProperty ( kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof (doSetProperty),
&doSetProperty
);
// Registers the audio route change listener callback function
AudioSessionAddPropertyListener (
kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback,
self
);
// Activates the audio session.
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive: YES error: &activationError];
NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:#"m4a"];
float vol = [appSoundPlayer volume];
AVAudioPlayer * newAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
self.appSoundPlayer = newAudio; // automatically retain audio and dealloc old file if new file is loaded
[newAudio release]; // release the audio safely
appSoundPlayer.delegate = self;
[appSoundPlayer prepareToPlay];
[appSoundPlayer setNumberOfLoops:0];
[appSoundPlayer play];
[appSoundPlayer setVolume:vol];
[progTime setProgress:0];
}
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) lappSoundPlayer successfully: (BOOL) flag {
//... code here to select nex sound track
[lappSoundPlayer play];
}
I have already made it so the app continues to play the current track even when screen lock comes on, I just can't get it to start up the next track.
Note: I have verified that this was working in iOS4.
Have you set up an audio session category, specifically AVAudioSessionCategoryPlayback, as described here?
[[AVAudioSession sharedInstance] setDelegate: self];
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
If that doesn't work, another thing to try, according to user ascanio in this message in the Apple dev forums, is to set your app up to listen for remote control events:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];