I have added an observer for the interrupt notification when recording audio.
This works fine when performing an outgoing-call, getting an incoming call and not answering, Siri, etc..
Now my app is running in the background with the red bar at the top of the screen, and continuing the recording in the states described above is not a problem.
But when I actually answer an incoming-call. I get another AVAudioSessionInterruptionTypeBegan notification and then when I stop the call, I never get a notification AVAudioSessionInterruptionTypeEnded type.
I have tried using the CTCallCenter to detect when a call has started, but I am unable to restart the recording from that callback.
Does anyone know how to get the interrupt mechanism to work with an incoming call that is actually getting answered?
This is (part of) the code I am using;
CFNotificationCenterAddObserver(
CFNotificationCenterGetLocalCenter(),
this,
&handle_interrupt,
(__bridge CFStringRef) AVAudioSessionInterruptionNotification,
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately );
...
static void handle_interrupt( CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo )
{
au_recorder *recorder = static_cast<au_recorder*>( observer );
NSNumber* interruptionType = [( ( __bridge NSDictionary* ) userInfo ) objectForKey:AVAudioSessionInterruptionTypeKey];
switch ( [interruptionType unsignedIntegerValue] )
{
case AVAudioSessionInterruptionTypeBegan:
{
// pause recorder without stopping recording (already done by OS)
recorder->pause();
break;
}
case AVAudioSessionInterruptionTypeEnded:
{
NSNumber* interruptionOption = [( ( __bridge NSDictionary* ) userInfo ) objectForKey:AVAudioSessionInterruptionOptionKey];
if ( interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume )
{
recorder->resume();
}
break;
}
}
}
I have tried binding the notification to either the AppDelegate, a UIViewController and a separate class, but that doesn't appear to help.
Edit
This is what I tried using the CTCallCenter, but this is very flaky. When recorder->resume() is called from the callback, it either works, crashes violently or doesn't do anything at all until the app is put back in the foreground again manually.
callCenter = [[CTCallCenter alloc] init];
callCenter.callEventHandler = ^(CTCall *call)
{
if ([call.callState isEqualToString:CTCallStateDisconnected])
{
recorder->resume();
}
};
UPDATE
If you hackily wait for a second or so, you can restart the recording from your callEventHandler (although you haven't described your violent crashes, it works fine for me):
if ([call.callState isEqualToString:CTCallStateDisconnected])
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
self.recorder->resume();
});
}
This is all without a background task or changing to an exclusive audio session. The delay works around the fact that the call ending notification comes in before Phone.app deactivates its audio session & 1 second seems to be small enough to fall into some kind of background descheduling grace period. I've lost count of how many implementation details are assumed in this, so maybe you'd like to read on to the
Solution that seems to be backed by documentation:
N.B While there's a link to "suggestive" documentation for this solution, it was mostly guided by these two errors popping up and their accompanying comments in the header files:
AVAudioSessionErrorCodeCannotInterruptOthers
The app's audio session is non-mixable and trying to go active while in the background.
This is allowed only when the app is the NowPlaying app.
AVAudioSessionErrorInsufficientPriority
The app was not allowed to set the audio category because another app (Phone, etc.) is controlling it.
You haven't shown how your AVAudioSession is configured, but the fact that you're not getting an AVAudioSessionInterruptionTypeEnded notification suggests you're not using an exclusive audio session (i.e. you're setting "mix with others"):
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
The obsoleted CTCallCenter and newer CXCallObserver callbacks seem to happen too early, before the interruption ends, and maybe with some further work you could find a way to run a background task that restarts the recording a little while after the call ends (my initial attempts at this failed).
So right now, the only way I know to restart the recording after receiving a phone call is:
Use an exclusive audio session (this gives you an end interruption):
if (!([session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]
&& [session setActive:YES error:&error])) {
NSLog(#"Audio session error: %#", error);
}
and integrate with "Now Playing" (not joking, this is a structural part of the solution):
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.playCommand addTargetWithHandler:^(MPRemoteCommandEvent *event) {
NSLog(#"PLAY");
return MPRemoteCommandHandlerStatusSuccess;
}];
[commandCenter.pauseCommand addTargetWithHandler:^(MPRemoteCommandEvent *event) {
NSLog(#"PAUSE");
return MPRemoteCommandHandlerStatusSuccess;
}];
// isn't this mutually exclusive with mwo? or maybe that's remote control keys
// not seeing anything. maybe it needs actual playback?
NSDictionary* nowPlaying = #{
MPMediaItemPropertyTitle: #"foo",
MPMediaItemPropertyArtist: #"Me",
MPMediaItemPropertyAlbumTitle: #"brown album",
};
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlaying;
Pieces of this solution were cherry picked from the Audio Guidelines for User-Controlled Playback and Recording Apps documentation.
p.s. maybe the lock screen integration or non-exclusive audio session are deal breakers for you, but there are other situations where you'll never get an end interruption, e.g. launching another exclusive app, like Music (although this may not be an issue with mixWithOthers, so maybe you should go with the code-smell delay solution.
Related
I am trying to get device motion updates on an iPhone or iPad in the background using CMMotionManager. I have reviewed all previous posts on this topic and thought that I had code that would work. My app also uses background audio, and this works properly in the foreground and background. In Info.plist, I have background audio and background location updates enabled.
For testing purposes, I have declared "var motionManager = CMMotionManager()" in AppDelegate, and included the following code in didFinishLaunchingWithOptions:
motionManager.deviceMotionUpdateInterval = 0.10
let queue = NSOperationQueue()
motionManager.startDeviceMotionUpdatesToQueue(queue, withHandler: {
data, error in
let accelerationVector = sqrt(pow(data!.userAcceleration.x, 2) + pow(data!.userAcceleration.y, 2) + pow(data!.userAcceleration.z, 2))
print("\(accelerationVector)")
})
When I run the app on my device, the code executes in the foreground as expected, but when I press the home button, I get about 10 more readings before it stops. When I tap on the app icon, the readings start again. I have also placed breakpoints on the code in the handler, and get similar results.
What am I missing?
Do NOT trust the NSLog or other similar log output
I used to meet this problem. I want collect the motion data in background and I made a demo about this. I found I can get all the data and log when my app in active, but the xCode log console output nothing when the application in background.
I used to think the problem is the CoreMotion data can only be collected in foreground, but I am wrong. All the callbacks still working when application enter background, Just the NSLog stop telling me the data.
If you don't believe, just collect all the data into a NSMutableArray or other collections, then check the data collected when app in background.
e.g.
#property (nonatomic, strong) NSMutableArray *arrAltimeters;
...
[self.altimeter startRelativeAltitudeUpdatesToQueue:self.operationQueue withHandler:^(CMAltitudeData * _Nullable altitudeData, NSError * _Nullable error) {
[self.arrAltimeters addObject:altitudeData];
NSLog(#"Altimate data count = %ld", self.arrAltimeters.count);
}];
I have declared the motion manager in ViewDidlLoad
CMMotionManager *motionManager;
/*--Initialising Motion Manager--*/
motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 1.0;
As whenever the app goes in background the motionManager stop providing callbacks in this case , You need to restart the motionmanager when the app goes in background or in foreground.To do this we need to follow the process below:
1) we first need to Register for the app transition notification :
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
2)Notification callback functions, in which you will restart the motion manager
-(void)appDidEnterBackground{
[self restartMotionUpdates];
}
-(void)appDidBecomeActive{
[self restartMotionUpdates];
}
3)This function which will restart the motion manager
-(void)restartMotionUpdates{
[motionManager stopDeviceMotionUpdates];
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
NSLog(#"x=%f y=%f z=%f",fabs(motion.userAcceleration.x),fabs(motion.userAcceleration.y),fabs(motion.userAcceleration.z));
}];
}
After doing more research, I figured out the problem. I was using mpmusicplayercontroller to play background audio. This will play the audio in the background, but this does not keep my app running in the background. When I switched to using avplayer to play audio, the app runs in the background including the device motion updates.
I am working on iOS app which will be compatible with iOS 6/7 and stream audio .mp3 files from a website.
I have already gotten this to work using the following code:
-(NSString*)documentsFolder
{
NSString* dataPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:NULL];
return dataPath;
}
-(NSString*)createURLFile:(NSString*)songURL
{
NSString* M3U_FILE = #"song.m3u";
NSString* path = [NSString stringWithFormat:#"%#",[[self documentsFolder] stringByAppendingPathComponent:M3U_FILE]];
if([[NSFileManager defaultManager] createFileAtPath:path contents:nil attributes:nil])
{
NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:path];
if(outFile != nil)
{
NSData* buffer = [songURL dataUsingEncoding:NSUTF8StringEncoding];
[outFile writeData:buffer];
return path;
}
}
return nil;
}
- (void)createStreamer
{
// Remove any previous references.
[[NSNotificationCenter defaultCenter] removeObserver:self];
// Create a new player.
NSString* fileURL = [self createURLFile:self.aSong.songpath];
self.songPlayer = [[AVPlayer alloc]initWithURL:[NSURL fileURLWithPath:fileURL]];
NSAssert(self.songPlayer != nil, #"NIL AVPlayer Created!!!");
// Observer for when the song ends...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[self.songPlayer currentItem]];
[[UIApplication sharedApplication] setIdleTimerDisabled: YES];
}
I store the url for the .mp3 file in a local .m3u file and use that to load up the AVPlayer. In earlier versions of iOS, I was told that the AVPlayer would load the song first and then play it, not stream it immediately. While this does not appear to be true in iOS 6/7 (the song starts streaming almost immediately), the .m3u file was being created in case there were any problems created by not having it done this way.
With this, a loop is monitoring the status of the AVPlayer and after a few seconds, the audio starts to play out the phone without a problem.
For testing purposes, I set up an MPVolumeView on the page which plays songs:
MPVolumeView *volumeView = [[[MPVolumeView alloc] initWithFrame:CGRectMake(0, 0, 310, 20)] autorelease];
volumeView.center = CGPointMake(160,62);
[volumeView sizeToFit];
[self.view addSubview:volumeView];
The reason for this is that the volume slider will also show an indicator if the bluetooth is connected as an audio output source and allow me to change the audio route between the phone and the bluetooth device. So far, so good.
I connected my phone to my Jawbox Jambone via bluetooth, start the AVPlayer on a song, and the song comes out of the Jawbox as expected. The volume control has the small "rectangle with arrow" indicating that I can switch the audio output and indeed, while the song is playing, I can switch between the phone and the Jawbox. Happiness.
The problem occurs when I try to connect it to a car. I have two experiences with this:
The car is already paired with the phone for making/receiving calls. The phone even indicates it is paired when I get into the car. But when I use the same code to play the same audio files, they only come out of the phone. The volume slider does not show the "bluetooth route" indicator at all (like it does not recognize the car as a audio output route).
In another car, the audio was streaming from another app (some radio streaming app). The other app was stopped and this one started. The audio started playing for the same song tested above, but stopped after a second or two. Again, there was no indicator on the volume slider that the bluetooth was connected at this point.
Can somebody explain to me why the audio could stream fine out to one bluetooth device but not to another?
Have I missed something (an entitlement?) in the profile for my app that will allow it to stream audio via bluetooth to a car?
There is this project at GIT.
Play iOS project is a streaming client for Play that runs on your iPhone/iPad. It supports background audio as well as the media keys when backgrounded.
It supports:
Streams shoutcast stream
Displays currently playing track
Background audio
Lock screen album art & play controls
AirPlay (along with Bluetooth) streaming. Supports sending metadata
and album art
You can download the project here.
I have not tested this on CAR bluetooth audio player though. Hope it may be of any help to you.
In the first example, your car may simply be a remote player. You would need to register for remote events like this (consider using an AVAudioPlayer instead of an AVPlayer also)
Setup the AudioSession to recognize a bluetooth audio route:
- (BOOL)prepareAudioSession {
// deactivate existing session
NSError *setCategoryError = nil;
NSError *activationError = nil;
BOOL success = [[AVAudioSession sharedInstance] setActive:NO error: nil];
if (!success) {
NSLog(#"deactivationError");
}
// set audio session category AVAudioSessionCategoryPlayAndRecord options AVAudioSessionCategoryOptionAllowBluetooth
success = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:&setCategoryError];
if (!success)
{
NSLog(#"setCategoryError %#",setCategoryError);
}
// activate audio session
success = [[AVAudioSession sharedInstance] setActive:YES error: &activationError];
if (!success) {
NSLog(#"activationError");
}
return success;
}
You can check the routes:
AVAudioSessionRouteDescription *mAVASRD = audioSession.currentRoute;
NSLog(#"the array is %#",mAVASRD.outputs);
for (int ctr = 0; ctr < [mAVASRD.outputs count]; ctr++)
{
AVAudioSessionPortDescription *myPortDescription = [mAVASRD.outputs objectAtIndex:ctr];
NSLog(#"the type is %#",myPortDescription.portType);
NSLog(#"the name is %#",myPortDescription.portName);
NSLog(#"the UID is %#",myPortDescription.UID);
NSLog(#"the data sources are %#",myPortDescription.dataSources);
}
Then initialize your AVAudioPlayer and turn on RemoteControlEvents (you can use the console in your car to send play/pause/etc)
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
then implement something like the delegate method for AVAudioPlayer in this stack overflow question to capture the received events and react accordingly in your code:
AVAudioPlayer on Lock Screen
In the scenario 2, when you moved one app to the background (the radio streaming app) and started your app, the likely culprit for the issue is the same cause - your app has to recognize the bluetooth route for audio.
By the way for phone calls and Siri, the iOS uses a different Bluetooth channel that the default for remote control (which is the one I am describing for your car).
When you setup this route and remote control events, you also get a bonus byproduct - your app will be controllable from the lock screen. Check out this technical note from Apple to configure your app to play in the background as well if that is also something you need to do when the screen locks: Technical QA document QA1668
Finally, for added integration via your bluetooth route, look at MPNowPlayingInfoCenter - put the title artist artwork and other good stuff on the lock screen and on most bluetooth screens in the car that are displaying that information.
I'm pretty sure MPVolumeView can only address Bluetooth devices which conform to the newer low power consumption Bluetooth spec... (Bluetooth Low Energy or BLE)...
I know the phone app doesn't use MPVolumeView, probably this other audio player doesn't either.. You may need to look into CoreBluetooth and implement your own :( good luck. There may be a solution on github
A bluetooth speaker designed as a speaker will be no problem.
However, a car will usually be a "phone" bluetooth speaker and will only accept a "phone" type of communications.
My guess would be that you would have to trick it by setting up a "phone audio" connection and having the incoming audio transfer into the void, and the outgoing music stream as a phone signal.
Mind you, signal quality might degrade and there probaly won't be a fix for that.
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.
We are recording audio with AVAudioRecorder but when ever we receive any incoming call Recording gets stopped. If we try to Resume recording again then it will start from the beginning. I have checked the Voice Memos(iOS Apple Application) & find the same problem. I have also checked the Audio Queue services Sample Code (SpeakHere) But same problem is there.
So Please help how to resume audio recording after incoming call?
Note:- I Know there are many question related to audio recording But unable to find solution.So please Help, Your help is greatly appreciated. Thanks in advance.
It is possible in iOS,to resume a audio recording after incoming call.Basic concept behind this is the use of AVAudioSession and AudioQueue ,when you receive a incoming call on your device it would give a kAudioSessionBeginInterruption and on disconnecting the call you got a kAudioSessionEndInterruption......on receiving a incoming call ,just pause the audio queue and then [[AVAudioSession sharedInstance]setActive:NO error:nil];
......Then on the kAudioSessionEndInterruption ,when the call ends,just resume the paused queue and [[AVAudioSession sharedInstance]setActive:YES error:nil];
This will work......I have successfully run the program and able to continue the recording after receiving an incoming call......
Hope this will help you.......
Apple explain AVAudioSessionInterruption observer as a new method to handle interruptions. Apple explains it in Swift at this page but for someones that search for objective-c code this github issue must be useful.
You need to handle audio interrupts like
AVAudioSession *session = [AVAudioSession sharedInstance];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(audioSessionInterruptionNotification:)
name:AVAudioSessionInterruptionNotification
object:session];
where your audio session interruption notification AVAudioSessionInterruptionNotification is something like
-(void)audioSessionInterruptionNotification:(NSNotification*)notification {
NSString* seccReason = #"";
//Check the type of notification, especially if you are sending multiple AVAudioSession events here
NSLog(#"Interruption notification name %#", notification.name);
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
seccReason = #"Interruption notification received";
//Check to see if it was a Begin interruption
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
seccReason = #"Interruption began";
} else if([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]){
seccReason = #"Interruption ended!";
//Resume your audio
}
}
}
You need to write your handling code then for interruption begin and end.
Use the AVFoundation Delegate Methods,
-(void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder
{
[audiorecorder pause];
}
-(void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder
{
[audiorecorder record];
}
I have a piano application. It's working fine, with a little error. If I play several keys at the same time very fast, the sounds disappears for a couple of seconds, and receive the following message in the console
AudioQueueStart posting message to kill mediaserverd
Here is the relevant code:
-(IBAction)playNoteFromKeyTouch:(id) sender{
[NSThread detachNewThreadSelector:#selector(playNote:) toTarget:self withObject:[NSString stringWithFormat:#"Piano.mf.%#",[sender currentTitle]]];
}
-(void)playNote:(NSString *) note{
NSError *err;
NSString *path = [[NSBundle mainBundle] pathForResource:note ofType:#"aiff"];
AVAudioPlayer *p = [[AVAudioPlayer alloc ] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&err];
p.delegate = self;
if (err) {
NSLog(#"%#", err);
}else{
[p prepareToPlay];
[p play];
}
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[player release];
}
I have tested with Instruments and I don't have any memory leak. If somebody could have an idea to avoid this error it would be appreciated.
I suffer from a similar issue.
I spent ages trying to solve the issue, and I think my particular issue takes place when:
I'm in an AudioCategory that doesn't allow sound to play while the mute switch is on.
I start to play a sound (I actually don't do this in the app, but this is how I can reproduce reliably).
With the sound still playing, I switch to another AudioCategory that doesn't allow sound to play while the mute switch is on.
From this point onwards, I get 'posting message to kill mediaserverd' from what looks like various points in calls in the AudioSession API. The app hangs, the device hangs, and I struggle to get the device back to a normal running state.
According to this message it's the device's mute switch.
It turns out that having the iPad muted via the device's physical switch was
causing the problem with my app. As long as the button is not
switched on the problem does not occur.
Sheesh. How to programmatically override?
I "solved" the issue using SoundBankPlayer instead of AVAudioPlayer. SoundBanker info.