How to use kAudioSessionProperty_OverrideCategoryMixWithOthers - ios

I would like to make my virtual instrument app able to being used (via a MIDI keyboard) when the app is in the background. This works fine by setting UIBackgroundModes to "audio" and setting the AudioSessions's category to AVAudioSessionCategoryPlayback. Furthermore, to allow mixing with the Music player app, I set the property kAudioSessionProperty_OverrideCategoryMixWithOthers.
To save battery energy, it is recommended to let the user switch off the background operation if it is not needed. So there is a switch in my app to toggle between the category AVAudioSessionCategoryAmbient and the category AVAudioSessionCategoryPlayback (with kAudioSessionProperty_OverrideCategoryMixWithOthers). The code is like this (error handling omitted):
NSString *category = supportsBackgroundOperation ? AVAudioSessionCategoryPlayback : AVAudioSessionCategoryAmbient;
[session setCategory:category error:nil];
if (category == AVAudioSessionCategoryPlayback) {
UInt32 allowMixing = true;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(allowMixing), &allowMixing);
}
Now the problem: Suppose the Music application is playing in the background. The user is playing the virtual instrument in the foreground (via a MIDI keyboard) to accompany the Music player and decides to start yet another app, say a slide show to enjoy why playing the instrument. So he/she goes to the settings view of my app and activates background operation. Then the above code is executed. Boom, the Music application gets silent. As I understand it, the kAudioSessionProperty_OverrideCategoryMixWithOthers property can only be set after setting the category, but when I set the category to AVAudioSessionCategoryPlayback, the Music player is silenced by the system before I have a chance to set kAudioSessionProperty_OverrideCategoryMixWithOthers.
Is this was happens? Can there anything be done? I tried to deactivate/activate the session before/after changing the category, but that lead to other trouble.
Markus

Since iOS 6.0 you can use
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
Swift 5:
try? AVAudioSession.sharedInstance().setCategory(.playback, options: .mixWithOthers)

You can achieve this. The principle is to deactivate your audio session first, setup all the properties of your audio session, and then active audio session. In this way, the music playbacked in other app will not be silenced.
// Initialize audio session
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
// Active your audio session
[audioSession setActive: NO error: nil];
// Set audio session category
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
// Modifying Playback Mixing Behavior, allow playing music in other apps
OSStatus propertySetError = 0;
UInt32 allowMixing = true;
propertySetError = AudioSessionSetProperty (
kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof (allowMixing),
&allowMixing);
// Active your audio session
[audioSession setActive: YES error: nil];
This solution works for me. But from your description, You mentioned that you did try activate/deactivate the session before/after changing the category but lead to other problems. Did you do the deactivate/activate in the exact same way as above? If so, provide more information about other troubles and maybe I can help.

I have to disagree with M0rph3v5.
The only valid value for the withOptions parameter is AVAudioSessionInterruptionOptionShouldResume

Related

Preventing no-audio AVPlayer from pausing background audio

I understand that based on AVAudioSession category AVPlayer will pause background audio currently played by other app when starting its playback. I wonder if this is the expected behavior regardless of the type item/content being played?
I'm asking as in my case showing video with no sound. The video files contain no audio track. AVPlayer itself is also set to muted. It just a little weird to me that I still have to change to AVAudioSession category even though I do not produce audio. So I want to know if that is by design or if there is something that I miss. For example, could I set up AVPlayer with no audio session so that it knows not to worry about audio or something like that.
No. Just set the audio category to "Ambient" so that it doesn't step on any other audio:
AVAudioSession *session = [AVAudioSession sharedInstance];
if (session) {
NSError *setCategoryError = nil;
BOOL success = [session setCategory:AVAudioSessionCategoryAmbient error:&setCategoryError];
if (success) {
NSError *activationError = nil;
[session setActive:YES error:&activationError];
} else {
NSLog(#"Failed to set music category to ambient. May interrupt other audio.");
}
}

In iOS created application to force audio output via speaker, but doesn't work when application is in background

I want to implement, when i plugged in headset/headphone in iPhone and play any music, music should play from speaker instead of headphone.
So using this below code that working fine when my application is in foreground. But when my application goes in background that time this code is not working.
I want to implement this functionality in 3 case
When application is in foreground.
When application is in background.
When application remove from backgound.
Please give me solution for that.
I have used this below code,
+ (void) initAudioSessionRouting{
// Called once to route all audio through speakers, even if something's plugged into the headphone jack
static BOOL audioSessionSetup = NO;
if (audioSessionSetup == NO) {
// set category to accept properties assigned below
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error: &sessionError];
// Doubly force audio to come out of speaker
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);
// fix issue with audio interrupting video recording - allow audio to mix on top of other media
UInt32 doSetProperty = 1;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doSetProperty), &doSetProperty);
// set active
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
// add listener for audio input changes
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, onAudioRouteChange, nil );
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioInputAvailable, onAudioRouteChange, nil );
}
// Force audio to come out of speaker
[[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
// set flag
audioSessionSetup = YES;}
Thanks in advance

iOS: How to play buffering audio in bluetooth headset in my music app?

I am currently doing one music app. in that i need to play buffering
audio song in blue tooth headset. i searched for code past 1 day. but
i can't. please give solution how to add this in my music app.
One more thing i need to play that audio in bluetooth headset while iphone is in backeground mode and screen lock mode.
AVAudioSession* audioSession = [AVAudioSession sharedInstance];
//[audioSession setDelegate:self];
NSError *error;
[audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:&error];
[audioSession setActive: YES error: nil];
// check the audio route
UInt32 size = sizeof(CFStringRef);
CFStringRef route;
OSStatus result = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &size, &route);
NSLog(#"route = %#", route);
// if bluetooth headset connected, should be "HeadsetBT"
// if not connected, will be "ReceiverAndMicrophone"
// now, play a quick sound we put in the bundle (bomb.wav)
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
SystemSoundID soundFileObject;
soundFileURLRef = CFBundleCopyResourceURL (mainBundle,CFSTR ("sample"),CFSTR ("m4a"),NULL);
AudioServicesCreateSystemSoundID (soundFileURLRef,&soundFileObject);
AudioServicesPlaySystemSound (soundFileObject); // should play into headset
I have added this code in my music app. but this code is for Bundle audio file. but i need buffering audio. if it is not possible please tell is my code is correcte to play local audio file through blue tooth headset.?
From what i read in the documentation , setting the category for the audio session should be
AVAudioSessionCategoryPlayAndRecord
or
AVAudioSessionCategoryRecord
if setting the option to
AVAudioSessionCategoryOptionAllowBluetooth
so you should change that, another thing when i look at the enum: AVAudioSessionCategoryOptions the option to bluetooth is __TVOS_PROHIBITED but i don't know the alternative...

Audio feed back in foreground and background

I have to give audio feedback when my app is in foreground and also in background. But when the app enters background audio feedback is not heard. In info.plist I have set background mode to App plays audio or streams audio/video using AirPlay and used the following but the audioPlayerDidFinishPlaying delegate is not called when app enters background and audio is not heard.
AVAudioPlayer *sound = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&err];
sound.delegate = self;
///Fixed the issue No audible feedback when main audio is silent
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
// This is necessary if you want to play a sequence of songs, otherwise your app will be
// killed after the first one finishes.
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[soundQueue addObject:sound];
You need to make couple of changes in plist file.
1) Set Required background mode to App plays audio
2) set Application does not run in background to YES.
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&setCategoryErr];
[[AVAudioSession sharedInstance] setActive:YES error:&activationErr];
Then, you need to write these much code in AppDelegate
Now, you can easily run audio while phone screen locks or in background.
It works fine for me. :)
for more please refer to Playing Audio in background mode and Audio Session Programming Guide

Return volume control to iOS device ringer after playback ends

I have an audio app that uses the Media Playback audio session category to allow background audio. Once my audio session is initialized, the hardware volume buttons on the iOS device control the volume of my audio session.
Once audio playback stops, I'd like to return control of the phone ringer back to the hardware volume buttons but my attempt to do this by deactivating the audio session doesn't do the trick.
Here is how I'm initializing and activating my audio session:
AudioSessionInitialize(NULL, NULL, interruptionListenerCallback, self);
AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback,
self);
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
sizeof(sessionCategory),
&sessionCategory);
Here is how I'm attempting to deactivate the audio session and return control of the iOS device's ringer back to the hardware volume controls:
AudioSessionSetActive(false);
There is at least one app that I know of that behaves this way (Audible.com's iOS app). Does anyone have any idea what I may be doing wrong?
In apples documentation I think you are going to have to actually remove the listener.
Look up:
AudioSessionRemovePropertyListenerWithUserData
http://developer.apple.com/library/ios/#documentation/AudioToolbox/Reference/AudioSessionServicesReference/Reference/reference.html
I just ran into this problem, but I'm using AVAudioPlayer. If I tried to deactivate my session right after calling play, it didn't work. But waiting for audioPlayerDidFinishPlaying:successfully: and then doing this worked:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error;
BOOL wasSuccessful = [audioSession setActive:NO error:&error];
NSLog(#"wasSuccessful: %#", wasSuccessful ? #"Yes" : #"No");
}
I'm using the default audio session, BTW.

Resources