How can you configure AVAudioSession to duck music and pause spoken audio? - ios

I'm working on a navigation app that uses AVSpeechSynthesizer to call out directions. If I'm playing a podcast I would like to be able to pause the audio while a direction is spoken and resume it afterwards. This is successfully achieved using:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers
error:nil];
However, if I play music using the above code the instruction gets mixed with the music without the volume of the music being reduced - making it difficult to hear the instruction. So I tried:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionDuckOthers
error:nil];
Now the background music reduces in volume, which is what I want, but when I play spoken audio (i.e. a podcast) it is no longer paused and instead gets mixed with the instruction - albeit at a reduced volume.
What I really want is something like: AVAudioSessionCategoryOptionInterruptSpokenAudioAndDuckWithOthers. However, that doesn't exist. I know that this combination is possible, because other apps (like Waze) have this behaviour.
I know that you can detect when audio is playing using:
BOOL isOtherAudioPlaying = [[AVAudioSession sharedInstance] isOtherAudioPlaying];
But, I couldn't find a way of determining if that Audio was spoken or not. If I could I could set things up according to what is playing at any given time. Can anyone help with this, preferably in Objective C?

This is how it's done:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionDuckOthers | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers
error:nil];
Works a charm!

Related

iOS AVAudioSession ducking is slow and synchronous

In iOS, I'm trying to duck the Music app's music when playing some sound effects. In case you don't know, "ducking" simply means that the music volume gets a bit down before playing my sound, then the sound plays, and then the music volume gets back its initial volume.
For ducking, I'm setting AVAudioSession category to AVAudioSessionCategoryAmbient with option AVAudioSessionCategoryOptionDuckOthers, and then activating/deactivating the session (and playing the sound in-between, obviously). It works well, but the volume changes seem to be done in the same thread as the call, and the app hangs while the volume is being modified.
If you want to replicate the behavior, I think the fastest route is to start a new SpriteKit project, which will give you the sample, ship rotating project. Then put the following code in the touchesBegan:withEvent method:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient withOptions: AVAudioSessionCategoryOptionDuckOthers error: nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[[AVAudioSession sharedInstance] setActive:NO error:nil];
Next run the app in an iOS device, put some music in the Music app and touch the screen to create ships and duck the music. You'll hear the ducking, but also see the ships freezing on the screen.
Is this normal? What would be the simplest way to avoid the app freezing while the ducking is made?
By the way, I'm using an iPhone 5S on iOS 8.1. Also, I'm using this in a Unity3D plugin. How can I duck the Music app from Unity itself?
You can try putting the AVAudioSession calls on a different thread. Then they won't be blocking the main (UI) thread. This is especially for setActive, which takes a noticeable amount of time to complete.
dispatch_queue_t myQueue = dispatch_queue_create("com.myname.myapp", nil);
dispatch_async(myQueue, ^{
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient withOptions: AVAudioSessionCategoryOptionDuckOthers error: nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
});
This question also seems relevant: iOS AudioSessionSetActive() blocking main thread?

iOS SDK: Play two different sounds at the same time. One should respect the mute button, the other should not

I want to play two sounds at the same time. One is a background sound and should respect the mute button but the other one is a recording that the user can optionally enable. If its enabled I want it to play no matter what the mute button is set to. Both sounds play at the same time.
I know how to force a sound to play with:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
and how to set it to respect the mute switch with:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
But both sounds use the same session and if I have use one setting for one sound I get the same setting for the other.
I use AVAudioPlayer to play the sounds.
Is it possible to have one sound forced and the other to respect the mute switch?

iOS: Keep playing music in background with Spotify SDK

I'm using the Spotify iOS SDK and have a question about the background music.
When a user locks the phone or presses the Home button I want my app to continue to play the music. How is this possible?
I've tested
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&setCategoryErr];
[[AVAudioSession sharedInstance] setActive:YES error:&activationErr];
Together with adding "Required background modes" to the apps plist.
Does this require that the AVAudioPlayer is used? Since I'm using the Spotify streamingPlayer its not possible.
Background playback should work just fine. You do need the audio item in your UIBackgroundModes Info.plist entry, though. Also, make sure you test on a device - I know remote control events don't work in the iOS Simulator, and that might be the case for background audio as well.
The iOS SDK will automatically set up the AVAudioSession for you, so you don't need to do that.
You might find this answer helpful, which is more in-depth: Background Audio with cocoalibspotify.

iOS MPMoviePlayerViewController stop playing when screen locks

I implement MPMoviePlayerViewController to play video and I enable airPlay and works great but the problem is when the screen on the ipad locks. My question is how can make sure the MPMoviePlayerViewController still in airPlay even when the screen locks. Any of you knows how can I make this work?
Where as Apple thinks that When user can't see the video what is the benefit to runs consistently? So there is no need to play Video when you can't see it. Apple pauses it when app goes to background or screen locks. If you still want to play it you can add notification to start and stop when you go to background and come to foreground.
Hope this helps.
In your Info.plist file, add the key "Required background modes" with the value "App plays audio or streams audio/video using AirPlay".
Also, when you receive the notification MPMoviePlayerIsAirPlayVideoActiveDidChangeNotification, add these lines:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
And add this line to your dealloc method:
[[AVAudioSession sharedInstance] setActive:NO error:nil];

Sound effect that quiets other audio iOS AVAudio

I want to have a sound effect in my iOS that, if other audio is playing (such as iTunes) quiets any other audio is playing and plays over top of it. I know
AVAudioSessionCategoryOptionMixWithOthers
Will mix the sounds together, but I want mine to be able to be heard clearly. I've seen this done in the RunKeeper app (when the lady describes your run to you it quiets your music) but I can not find it in Apple's Documentation.
Anyone have experience with this?
Apple's documentation says this only works with PlayAndRecord, but it works.
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionDuckOthers error: nil];

Resources