MPMusicPlayerController applicationMusicPlayer - Resume when application launches - ios

The MPMusicPlayerController applicationMusicPlayer runs independent of the iPod (or Music) app, which is what I want. It stops playing when the application goes into the background which is again what I want.
However, I would like it to resume playing when the application resumes. Currently I do this by calling the play method again, however this causes the audio to start from the beginning. Is there a way I can actually resume playing from where the audio left off? I can't seem to find anything about it.

Inside the App Delegate's applicationWillResignActive: method, read the musicPlayer.currentPlaybackTime property (declared in the MPMediaPlayback protocol) and store that value somewhere. You could set up a property such as:
#property (assign, nonatomic) NSTimeInterval playbackTime;
and assign it with:
- (void)applicationWillResignActive:(UIApplication *)application
{
MPMusicPlayerController *myPlayer =
[MPMusicPlayerController applicationMusicPlayer];
self.playbackTime = myPlayer.currentPlaybackTime;
[myPlayer pause];
}
When the app is about to become active again, the App Delegate's applicationDidBecomeActive: method will be called. Inside that method, set the currentPlaybackTime property.
- (void)applicationDidBecomeActive:(UIApplication *)application
{
MPMusicPlayerController *myPlayer =
[MPMusicPlayerController applicationMusicPlayer];
myPlayer.currentPlaybackTime = self.playbackTime;
[myPlayer play];
}

Related

AudioQueueStart returns 561015905 (AVAudioSessionErrorCodeCannotStartPlaying)

My app uses Audio Queue Services to play sounds. On app start, I set audio session category to solo ambient:
`[[AVAudioSession sharedInstance] setCategory:AVAudioSEssionCategorySoloAmbient error:&error];`
When my app delegate gets applicationWillResignActive notification, I call AudioQueuePause(queue); for all playing sounds. And when the app delegate gets applicationDidBecomeActive, I call
OSStatus status = AudioQueueStart(queue, 0);
Sometimes (and it's hard to reproduce) status equals to 561015905. This value does not belong to Audio Queue Result Codes. It is AVAudioSessionErrorCodeCannotStartPlaying, and docs say
"The app is not allowed to start recording and/or playing, usually because of a lack of audio key in its Info.plist. This could also
happen if the app has this key but uses a category that can't record
and/or play in the background (AVAudioSessionCategoryAmbient,
AVAudioSessionCategorySoloAmbient, etc.)."
I definitely do not want to play the sound in background (that's why I am pausing audio queues when app resigns active). So, what am I doing wrong?
I had a similar problem, but I made a little in a different way...There was musical app in which background music was necessary, but under certain conditions she has to howled to play in background mode.
For me, i use this code:
#import "ViewController.h"
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVAudioPlayer.h>
#import <AVFoundation/AVAudioSession.h>
#interface ViewController ()
{
AVAudioPlayer *audioPlayer;
}
#property (assign) SystemSoundID pewPewSound;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL * pewPewURL = [[NSBundle mainBundle]URLForResource:#"Calypso" withExtension:#"caf"];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pewPewURL error:nil];
if (audioPlayer)
{
[audioPlayer setNumberOfLoops:100];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[audioPlayer prepareToPlay];
[audioPlayer play];
}
}
#end
If u don't touch any Xcode setting, this code play looping melody, only in foreground, and if u go to background, player is stopped.
if u do this:
melody will continue to play in background, and in my case i can stop and play melody in background and foreground when i want. And I add system sound to call then u stop player, system sound stopped then U continue play music.
-(void)stopPlaying
{
[audioPlayer pause];
NSURL * pewPewURL;
pewPewURL = [[NSBundle mainBundle]URLForResource:#"Calypso" withExtension:#"caf"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)pewPewURL, &_pewPewSound);
AudioServicesPlaySystemSound(self.pewPewSound);
}
-(void)continueToPlay
{
if ([audioPlayer prepareToPlay])
{
[audioPlayer play];
AudioServicesRemoveSystemSoundCompletion(self.pewPewSound);
AudioServicesDisposeSystemSoundID(self.pewPewSound);
}
}
I hope it will help to solve yours problems, If there are questions, set I will answer everything.

iOS add audio playing via AVPlayer to OS default player?

So the idea is - when we play audio in Music app in iPhone and press home button then swipe from bottom of the screen, we can see the audio playing in default OS player with play/pause controls and audio continue play.
Now what i need to do, i play an audio by using AVAudioPlayer in my app and if user press home button, the audio need to play continue as in case of music app.
But i have no idea how to add audio in OS default player? Any help or suggestion would be highly appreciated.
Edit: I need to play audio using streaming so i guess i need to use AVPlayer instead of AVAudioPlayer
You could use an MPMovieplayerController since you'll be more than likely be using an M3U8 playlist.
Here's the documentation http://developer.apple.com/library/ios/documentation/MediaPlayer/Reference/mpmovieplayercontroller_class/index.html
But it basically goes like this:
Initialize the MPMoviePlayerController, assign a file type (stream), put the source url, and present it in the view stack.
For the background audio, you'd have to use AudioSession like in this answer iOS MPMoviePlayerController playing audio in background
You can do this as a background audio task so that the audio keeps playing even after the user presses the home button and your app goes into the background. First you create an AVAudioSession. Then you set up an array of AVPlayerObjects and a AVQueuePlayer in your viewDidLoad method. There's a great tutorial by Ray Wenderlich that discusses all of this in detail http://www.raywenderlich.com/29948/backgrounding-for-ios. You can set up a callback method (observer method) so that the app is sent additional audio data as it streams in - (void)observeValueForKeyPath.
Here's how the code looks (from Ray Wenderlich's tutorial):
in viewDidLoad:
// Set AVAudioSession
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
// Change the default output audio route
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,
sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
NSArray *queue = #[
[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"IronBacon" withExtension:#"mp3"]],
[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"FeelinGood" withExtension:#"mp3"]],
[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"WhatYouWant" withExtension:#"mp3"]]];
self.player = [[AVQueuePlayer alloc] initWithItems:queue];
self.player.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
[self.player addObserver:self
forKeyPath:#"currentItem"
options:NSKeyValueObservingOptionNew
context:nil];
in a callback method:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"currentItem"])
{
AVPlayerItem *item = ((AVPlayer *)object).currentItem;
self.lblMusicName.text = ((AVURLAsset*)item.asset).URL.pathComponents.lastObject;
NSLog(#"New music name: %#", self.lblMusicName.text);
}
}
Don't forget to add the member variables in your view controller's private API in the implementation file:
#interface TBFirstViewController ()
#property (nonatomic, strong) AVQueuePlayer *player;
#property (nonatomic, strong) id timeObserver;
#end

start playing audio from a background task via AVAudioPlayer in Xcode

I am trying to start playing a sound from a background task via an AVAudioPlayer that is instantiated then, so here's what I've got.
For readability I cut out all user selected values.
- (void)restartHandler {
bg = 0;
bg = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bg];
}];
tim = [NSTimer timerWithTimeInterval:.1 target:self selector:#selector(upd) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:tim forMode:NSRunLoopCommonModes];
}
- (void)upd {
if ([[NSDate date] timeIntervalSinceDate:startDate] >= difference) {
[self playSoundFile];
[tim invalidate];
[[UIApplication sharedApplication] endBackgroundTask:bg];
}
}
- (void)playSoundFile {
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&sessionError];
// new player object
_player = [[AVQueuePlayer alloc] init];
[_player insertItem:[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"Manamana" withExtension:#"m4a"]] afterItem:nil];
[_player setVolume:.7];
[_player play];
NSLog(#"Testing");
}
Explanation: bg, tim, startDate, difference and _player are globally declared variables. I call - (void)restartHandler from a user-fired method and inside it start a 10Hz repeating timer for - (void)upd. When a pre-set difference is reached, - (void)playSoundFile gets called and initiates and starts the player. The testing NSLog at the end of the method gets called.
Now the strange thing is if I call - (void)playSoundFile when the app is active, everything works out just fine, but if I start it from the background task it just won't play any sound.
Edit
So I tried using different threads at runtime and as it seems, if the Player is not instantiated on the Main Thread this same problem will appear.
I also tried using AVPlayer and AVAudioPlayer where the AVAudioPlayer's .playing property did return YES and still no sound was playing.
From what I've learned after writing an player App, it seems that you can not start playing audio when your App is already in background for longer than X seconds, even if you have configured everything right.
To fix it, you have to use background task wisely.
The most important thing is that you must NOT call endBackgroundTask immediately after playSoundFile. Delay it for about 5-10 seconds.
Here is how I managed doing it for iOS6 and iOS7.
1, Add audio UIBackgroundModes in plist.
2, Set audio session category:
3, Create an AVQueuePlayer in the main thread and enqueue an audio asset before the App enter background.
*For continues playing in background (like playing a playlist)*
4, Make sure the audio queue in AVQueuePlayer never become empty, otherwise your App will be suspended when it finishes playing the last asset.
5, If 5 seconds is not enough for you to get the next asset you can employ background task to delay the suspend. When the asset is ready you should call endBackgroundTask after 10 seconds.
include your playSoundFile call in the below, this should always run it in main thread
dispatch_async(dispatch_get_main_queue(), ^{
});
add this if to your - (void) playSoundFile to check if player is created, if not, then create it
if(!_player) {
_player = [[AVQueuePlayer alloc] init];
}
Seems to me as if you do not have the appropriate background mode set?
Also, looks like another user had the same issue and fixed it with an Apple Docs post. Link to Post
You may need to just do the step to set the audio session as active:
[audioSession setActive:YES error:&activationError];
Hope it helps!
If you need to start playing a sound in the background (without a trick such as keeping on playing at volume zero until you need the sound):
Set Background mode Audio in your p.list;
Set AudioSession category to AVAudioSessionCategoryPlayback
Set AudioSession to active
When your backgrounded app becomes running, start a background task before playing the sound (apparently when the remaining background time is less than 10 seconds, the sound is not played)
This now works for me.
What fixed it for me was to start playing some audio just as application quits, so I added 1sec blank audio in applicationWillResignActive in the AppDelegate
func applicationWillResignActive(_ application: UIApplication) {
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.endBackgroundUpdateTask(true)
})
let secSilence = URL(fileURLWithPath: Bundle.main.path(forResource: "1secSilence", ofType: "mp3")!)
do {
audioPlayer = try AVAudioPlayer(contentsOf: secSilence)
}
catch {
print("Error in loading sound file")
}
audioPlayer.prepareToPlay()
audioPlayer.play()
}

Is MPMusicPlayerController working properly on iPod 5g?

I am developing an application for iOS which works in two different modes, one of them plays music from the iPod library, and the other one synthesizes sounds (eg: pure tones).
In order to synthesize sounds I use an audio unit, so through its callback I can pass the desired signal. As far as sounds must be played at an specific volume, I set the device volume using iPodMusicPlayer.
The issue I see is that the first time the application is run on an iPod Touch 5g after turning it on, once the iPodMusicPlayer is used to set the volume of the device, applicationMusicPlayer will not respond to stop.
I came up with the following code. Runing it on an iPod Touch 5g after turning it on, music will not stop after touching stop.
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
MPMusicPlayerController *mp = [MPMusicPlayerController iPodMusicPlayer];
mp.volume = 0.3;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)playAction:(id)sender {
MPMusicPlayerController *mp = [MPMusicPlayerController applicationMusicPlayer];
MPMediaQuery *query = [MPMediaQuery songsQuery];
[mp setQueueWithQuery:query];
[mp play];
}
- (IBAction)stopAction:(id)sender {
MPMusicPlayerController *mp = [MPMusicPlayerController applicationMusicPlayer];
[mp stop];
}
#end
PS: It is also possible to reproduce the issue by entering the music app, the quitting it (double press on home button and quit music from the multitasking bar), and finally run the code above.
In the other devices I have tested so far (iPad 2nd and 3rd generation, iPod Touch 4 generation) the application works well.
Thanks in advance.

How can we handle enable/disable background audio abilities at runtime on iOS devices?

I know how to set up my iOS application to make it playing audio in background (= when another application is used on the iOS device) using the plist.
Some applications like:
- BLOOM
- some radio players
provides a basic UISwitch to enable or disable this behavior.
Any ideas about how to do that ?
Have an iVar of type UIBackgroundTaskIdentifier in the main class that handles audio playing code, initialize this with beginBackgroundTaskWithExpirationHandler.... method before starting the audio player and use endBackgroundTask when audio player completes.
Code:
#inerface AudioPlayerController : NSObject
{
UIBackgroundTaskIdentifier bgTaskID;
}
#end
#implementation AudioPlayerController
- (void) startPlayer
{
bgTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
// Write code to start the audio player
}
// Call this when your program receives message of audio playing complete
- (void) audioPlayComplete
{
// Do the audio playing finish
if (bgTaskID != UIBackgroundTaskInvalid)
[[UIApplication sharedApplication] endBackgroundTask:bgTaskID];
}
#end
In your AppDeletate:
- (void)applicationDidEnterBackground:(UIApplication *)application {
[[YouPlayerClass sharedInstance] stop];
}
Thus The audio stops at app entering background.

Resources