0

I want to play music with an AVAudioPlayer using an NSTimer, but [player prepareToPlay] returns NO & doesn't play in the background.

Can somebody give me some idea?

Here's my code:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(playMusic:) userInfo:nil repeats:NO];
    [self.window makeKeyAndVisible];
    return YES;
}

- (void)playMusic:(NSTimer *)timer{
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    NSString *fileName = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Apologize.mp3"];
    NSURL *fileUrl = [NSURL fileURLWithPath:fileName];
    NSError *error;
    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:&error];
    if ([player prepareToPlay]) {
        [player play];
        NSLog(@"start!");
    }else{
        NSLog(@"error msg :%@",error);
    }
}

- (void)applicationDidEnterBackground:(UIApplication *)application {

    if ([[UIApplication sharedApplication] respondsToSelector:@selector(beginReceivingRemoteControlEvents)]){
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    }

    UIBackgroundTaskIdentifier backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
    //        /* just fail if this happens. */
        [[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
    }];
}
yuji
  • 16,695
  • 4
  • 63
  • 64
Garen
  • 3
  • 1
  • 7

3 Answers3

1

You need to do this init in the viewDidLoad :

NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"test"
                                                          ofType:@"mp3"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath];
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL
                                                          error:nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

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

This is for the audio initialization. Here, with a MP3 named test, imported in the project. Note that the beginReceivingRemoteControlEvents part is very important. Without it, you cannot start playing from background, just keep on listening to music already playing from foreground.

Now you need to listen to the event didEnterInBackGround (you could go with the applicationDidEnterBackground of your app delegate, but this way you can put the code in the class it belongs to):

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(didEnterBG:)
                                             name:UIApplicationDidEnterBackgroundNotification
                                           object:nil];

And now in that didEnterBG method :

self.backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:@selector(backgroundWork) userInfo:nil repeats:YES];
[self.backgroundTimer fire];

Here i make this sound repeat every 10s for personal reasons, but do whatever you need. Finally, the backgroundWork method :

- (void)backgroundWork{
    [self.audioPlayer prepareToPlay];
    [self.audioPlayer play];
}

Now your audio file is playing in background :-)

You should also note that your app need to have specific rights to keep doing jobs in background. You need to check the box under 'YourTarget' -> Capabilities -> Background Modes : Audio and AirPlay. Otherwise, the OS will terminate you app in a few minutes in real conditions (with no debugger or instrument).

Rémi Belzanti
  • 413
  • 5
  • 12
  • This is really helpful but what If I want to start the sound ONLY in background mode? because using [self.backgroundTimer fire] will call backgroundWork and the app will play the sound – LS_ Apr 28 '15 at 14:49
  • Well, as the background work function is called on `UIApplicationDidEnterBackgroundNotification`, the sound is only played when the app did actually enter in background mode. – Rémi Belzanti Apr 29 '15 at 08:53
0

before going to code. you need to tell the OS that you want permission to play music in background. you can do that by setting a Key in your app's info.plist

<key>UIBackgroundModes</key>
    <array>
            <string>audio</string>
    </array>

doing so, you will be able to play audio when app moves to background.

Saurabh Passolia
  • 8,099
  • 1
  • 26
  • 41
  • but when i start a music in app ,it can play in background... that drive me crazy for a long time...>_ – Garen Oct 20 '11 at 09:09
0

I have the same issue, but I found the solution from this post: http://developer.apple.com/library/ios/#qa/qa1668/_index.html

#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>

AVAudioSession *audioSession = [AVAudioSession sharedInstance];

NSError *setCategoryError = nil;
[audioSession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
if (setCategoryError) { /* handle the error condition */ }

NSError *activationError = nil;
[audioSession setActive:YES error:&activationError];
if (activationError) { /* handle the error condition */ }