I would like my app to be notified when microphone interruption has ended before i start recording.
say for example my application is trying to start recording from background and during this time if any other applications like skype is accessing microphone and i shouldn't do. so i want to start register/observe for interruption so that when skype call ended, my app should be notified.
I can able to achieve only when app is recording and interruption began . i will be notified when interruption ended not on while about to start recording .
please help .
I tried to register using
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleAudioSessionInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
// observe the interruption begin / end
- (void)handleAudioSessionInterruption:(NSNotification*)notification
{
AVAudioSessionInterruptionType interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
AVAudioSessionInterruptionOptions interruptionOption = [notification.userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
NSLog(#"interruptionType:%lu",(unsigned long)interruptionType);
switch (interruptionType) {
case AVAudioSessionInterruptionTypeBegan:{
NSLog(#"interruption began");
break;
}
case AVAudioSessionInterruptionTypeEnded:{
NSLog(#"interruption ended");
break;
}
default:
break;
}
}
Related
I added the Spotify player to my app which also plays music using the MPMusicPlayerController. When music is playing from Spotify and the screen is locked, the remote control events are not received for play/pause and FFW/RWD when the user presses these buttons on the locked screen.
If music is playing from the MPMusicPlayerController, I am able to receive the remote control events based on the following code:
-(void) ViewDidLoad {
...
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
...
}
and
- (BOOL) canBecomeFirstResponder
{
return YES;
}
- (void) remoteControlReceivedWithEvent: (UIEvent*) event
{
// see [event subtype] for details
if (event.type == UIEventTypeRemoteControl) {
// We may be receiving an event from the lockscreen
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
case UIEventSubtypeRemoteControlPlay:
case UIEventSubtypeRemoteControlPause:
// User pressed play or pause from lockscreen
[self playOrPauseMusic:nil];
break;
case UIEventSubtypeRemoteControlNextTrack:
// User pressed FFW from lockscreen
[self fastForwardMusic:nil];
break;
case UIEventSubtypeRemoteControlPreviousTrack:
// User pressed rewind from lockscreen
[self rewindMusic:nil];
break;
default:
break;
}
}
}
While the iPod controls are visible when the app enters the background, they do not respond when I press pause. Instead, the iPod controls disappear when I press pause. What addition is needed to enable detection of play/pause and FFW/RWD when streaming audio such as Spotify is playing in the background from lock screen?
I believe I ran into this in the past. If I remember correctly I added in the
-(void)remoteControlReceivedWithEvent:(UIEvent *) event { ... }
as well as
- (BOOL) canBecomeFirstResponder { return YES; }
to the app delegate (This is also where my audio controller lived). I was having having the issue where the UIViewControllers were not alive during the time I wanted to catch the UIEventTypeRemoteControl notifications.
Give that a try and see if that helps.
After further investigation, I have found that if include the following code when my app enters the background and when the remote control events are received, the iPod controls do not disappear.
// Set up info center to display album artwork within ipod controls (needed for spotify)
MPMediaItemArtwork *ipodControlArtwork = [[MPMediaItemArtwork alloc]initWithImage:artworkImage];
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = [NSDictionary dictionaryWithObjectsAndKeys:nowPlayingTitle, MPMediaItemPropertyTitle,
nowPlayingArtist, MPMediaItemPropertyArtist, ipodControlArtwork, MPMediaItemPropertyArtwork, [NSNumber numberWithDouble:0.0], MPNowPlayingInfoPropertyPlaybackRate, nil];
I am not sure what code to add, so let me know what you need to see. I am using MPMoviePlayer in conjunction with Widevine. I am having an issue where the movie stops playing. I check the MoviePlaybackStates and rarely, if ever does it catch. Most of the time it just stops. I want to believe it has something to do with buffering. I am streaming the video, and widevine callbacks gives me no errors. Any ideas how I can track this down or what the issue is?
You should follow loadState instead of playbackState.
The way to do it is to observe MPMoviePlayerLoadStateDidChangeNotification notification, to see how's that buffering going.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(loadStateChanged:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
somewhere before initializing your player.
and
-(void)loadStateChanged:(NSNotification *)notif
{
NSString *loadState=#"";
switch (self.player.loadState) {
case MPMovieLoadStateUnknown: {
loadState=#"MPMovieLoadStateUnknown";
break;
}
case MPMovieLoadStatePlayable: {
loadState=#"MPMovieLoadStatePlayable";
break;
}
case MPMovieLoadStatePlaythroughOK: {
loadState=#"MPMovieLoadStatePlaythroughOK";
break;
}
case MPMovieLoadStateStalled: {
loadState=#"MPMovieLoadStateStalled";
break;
}
}
NSLog(#"%#", loadState);
}
I wonder if it's possible to detect when the the music in my "iPod" on the phone is being paused, not if it's paused or currently playing, just the moment when the music is being paused. I guess i should be using AVAudio?
Thanks!
MPMusicPlayerController has a method:
+(MPMusicPlayerController *)iPodMusicPlayer;
The iPod music player employs the iPod app on your behalf. On
instantiation, it takes on the current iPod app state and controls
that state as your app runs. Specifically, the shared state includes
the following:
Repeat mode (see “Repeat Modes”) Shuffle mode (see “Shuffle Modes”
Now-playing item (see nowPlayingItem) Playback state (see
playbackState) Other aspects of iPod state, such as the on-the-go
playlist, are not shared. Music that is playing continues to play when
your app moves to the background.
You can check its playbackState which may be:
enum {
MPMusicPlaybackStateStopped,
MPMusicPlaybackStatePlaying,
MPMusicPlaybackStatePaused,
MPMusicPlaybackStateInterrupted,
MPMusicPlaybackStateSeekingForward,
MPMusicPlaybackStateSeekingBackward
};
typedef NSInteger MPMusicPlaybackState;
You can also get notified if its playbackState change with the MPMusicPlayerControllerPlaybackStateDidChangeNotification.
#property (nonatomic, strong) MPMusicPlayerController *musicPlayer;
-(void)iPodMusicPlayer
{
musicPlayer = [MPMusicPlayerController iPodMusicPlayer];
switch ([musicPlayer playbackState])
{
case: MPMusicPlaybackStateStopped:
NSLog(#"iPod player is stopped)";
//Do something
break;
case: MPMusicPlaybackStatePaused:
NSLog(#"iPod player is paused");
//Do something
break;
case: MPMusicPlaybackStatePlaying:
NSLog(#"iPod player is playing");
//Do something
break;
//Etc.
default:
break;
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(musicPlayerPlayBackStatusChanged:)
name:MPMusicPlayerControllerPlaybackStateDidChangeNotification
object:nil];
[musicPlayer beginGeneratingPlaybackNotifications];
}
-(void)musicPlayerPlayBackStatusChanged:(NSNotification *)notification
{
switch ([musicPlayer playbackState])
{
case: MPMusicPlaybackStateStopped:
NSLog(#"iPod player is stopped)";
//Do something
break;
case: MPMusicPlaybackStatePaused:
NSLog(#"iPod player is paused");
//Do something
break;
case: MPMusicPlaybackStatePlaying:
NSLog(#"iPod player is playing");
//Do something
break;
//Etc.
default:
break;
}
}
I have iOS app that handles audiosession interrupts with:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(AudioInterrupt:)
name:AVAudioSessionInterruptionNotification
object: NULL];
and in AudioInterrupt:
- (void)AudioInterrupt:(NSNotification*)notification
{
NSDictionary *interuptionDict = notification.userInfo;
// get the AVAudioSessionInterruptionTypeKey enum from the dictionary
NSInteger interuptionType = [[interuptionDict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];
NSNumber* seccondReason = [[notification userInfo] objectForKey:#"AVAudioSessionInterruptionOptionKey"] ;
// decide what to do based on interruption type here...
switch (interuptionType) {
case AVAudioSessionInterruptionTypeBegan:
[[[self pureAudioHandler] audioController] setActive: NO];
NSLog(#"Interruption started");
break;
case AVAudioSessionInterruptionTypeEnded:
NSLog(#"Interruption ended");
break;
}
switch ([seccondReason integerValue]) {
case AVAudioSessionInterruptionOptionShouldResume:
NSLog(#"Resume Audio");
[[[self pureAudioHandler] audioController] configurePlaybackWithSampleRate:44100 numberChannels:2 inputEnabled:NO mixingEnabled:YES];
[[[self pureAudioHandler] audioController] setActive: YES];
break;
default:
break;
}
}
This works fine with alarms and Siri. However if i have no internet connection and i press home button i get "Siri not available...". AVAudioSessionInterruptionTypeBegan is triggered. Press homebutton twice to get back into app and nor AVAudioSessionInterruptionTypeEnded or AVAudioSessionInterruptionOptionShouldResume is fired. Any workarounds?
iPad mini retina with 7.0.3
Lot of experimenting showed that interrupts aren't fired all times as they should. Sometimes missing InterruptionTypeBegan when entering Siri, and all this happens quite randomly on current testcase (Unity3D & Kalimba(libpd)).
Instead used applicationWillResignActive for killing audio and applicationDidBecomeActive to start it again, as these are working 100% of the time.
Interesting fact is that when entering back from Siri (no wifi, so "Siri not available..." appears), it changes samplerate back to half of machines native samplerate (24000 on ipad mini) after some time.
I'm trying to figure out if this is possible - my app activates an audio session that is initialized as:
[[[AVAudioSession alloc] init] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
I would like to be able to understand when an additional audio session that originated from another app or the OS is playing.
I know about the ability to implement the delegate methods beginInterruption: and endInterruption but these won't get invoked because of the AVAudioSessionCategoryOptionMixWithOthers option I'm using.
Is there a way to achieve this without using private API?
Thanks in advance.
The way you manage your application's Audio Session has had some significant changes since iOS 6.0, and deserves a brief mention first. Before iOS 6.0 you would make use of AVAudioSession and AudioSessionServices classes, incorporating delegation and property listening respectively. From iOS 6.0 onwards use AVAudioSession class and incorporate notifications.
The following is for iOS 6.0 onwards.
To tell if other audio outside your applications sandbox is playing use -
// query if other audio is playing
BOOL isPlayingWithOthers = [[AVAudioSession sharedInstance] isOtherAudioPlaying];
// test it with...
(isPlayingWithOthers) ? NSLog(#"other audio is playing") : NSLog(#"no other audio is playing");
As for interruption handling you'll need to observe AVAudioSessionInterruptionNotification and AVAudioSessionRouteChangeNotification. So in the class that manages your audio session you could put something like the following - this should be called once at the start of the application lifecycle and don't forget to remove observer in the dealloc method of the same class.
// ensure we already have a singleton object
[AVAudioSession sharedInstance];
// register for notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(interruption:)
name:AVAudioSessionInterruptionNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(routeChange:)
name:AVAudioSessionRouteChangeNotification
object:nil];
And finally add the following selectors interruption: and routeChange: - these will receive a NSNotification object that has a property called userInfo of type NSDictionary that you read to assist any conditionals your application has.
- (void)interruption:(NSNotification*)notification {
// get the user info dictionary
NSDictionary *interuptionDict = notification.userInfo;
// get the AVAudioSessionInterruptionTypeKey enum from the dictionary
NSInteger interuptionType = [[interuptionDict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];
// decide what to do based on interruption type here...
switch (interuptionType) {
case AVAudioSessionInterruptionTypeBegan:
NSLog(#"Audio Session Interruption case started.");
// fork to handling method here...
// EG:[self handleInterruptionStarted];
break;
case AVAudioSessionInterruptionTypeEnded:
NSLog(#"Audio Session Interruption case ended.");
// fork to handling method here...
// EG:[self handleInterruptionEnded];
break;
default:
NSLog(#"Audio Session Interruption Notification case default.");
break;
} }
And similarly...
- (void)routeChange:(NSNotification*)notification {
NSDictionary *interuptionDict = notification.userInfo;
NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (routeChangeReason) {
case AVAudioSessionRouteChangeReasonUnknown:
NSLog(#"routeChangeReason : AVAudioSessionRouteChangeReasonUnknown");
break;
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
// a headset was added or removed
NSLog(#"routeChangeReason : AVAudioSessionRouteChangeReasonNewDeviceAvailable");
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
// a headset was added or removed
NSLog(#"routeChangeReason : AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
break;
case AVAudioSessionRouteChangeReasonCategoryChange:
// called at start - also when other audio wants to play
NSLog(#"routeChangeReason : AVAudioSessionRouteChangeReasonCategoryChange");//AVAudioSessionRouteChangeReasonCategoryChange
break;
case AVAudioSessionRouteChangeReasonOverride:
NSLog(#"routeChangeReason : AVAudioSessionRouteChangeReasonOverride");
break;
case AVAudioSessionRouteChangeReasonWakeFromSleep:
NSLog(#"routeChangeReason : AVAudioSessionRouteChangeReasonWakeFromSleep");
break;
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
NSLog(#"routeChangeReason : AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory");
break;
default:
break;
} }
There is no need to poll anything as long as you check the state of your applications audio session say for example in the viewDidLoad of your root view controller, at the start of you apps lifecycle. Any changes from there onwards to your applications audio session will be known via these two main notifications. Replace the NSLog statements with what ever your code needs to do based on the cases contained in the switch.
You can find more information about AVAudioSessionInterruptionTypeKey and AVAudioSessionRouteChangeReasonKey in the AVAudioSession class reference documentation.
My apologies for the long answer but I think Audio Session management in iOS is rather fiddly and Apple's Audio Session Programming Guide, at the time of writing this, does not include code examples using notifications for interruption handling.
You can check if other audio is playing like this:
UInt32 otherAudioIsPlaying;
UInt32 propertySize = sizeof (otherAudioIsPlaying);
AudioSessionGetProperty (kAudioSessionProperty_OtherAudioIsPlaying, &propertySize, &otherAudioIsPlaying );
[self handleIfAudioIsPlaying: otherAudioIsPlaying];
Then you can add a loop and check every X second if something changed.