AVAudioEngine changes other app's output route - ios

I'm seeing an unexpected effect with AVAudioEngine that I'm trying to get my head around. The route of another app's audio playback changes from the speaker to the earpiece when my app's engine starts. I want the other app to keep playing out of the speaker.
Here's how to reproduce.
Make an empty app and add the UnexpectedAudio class below. In your ViewController class add and initialize an unexpected property (let unexpected = UnexpectedAudio()), and then add a button to your storyboard that calls unexpected.start().
class UnexpectedAudio {
let engine: AVAudioEngine
init() {
self.engine = AVAudioEngine()
_ = engine.inputNode // lazily creates node
}
func start() {
try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord,
mode:AVAudioSessionModeDefault,
options: [.mixWithOthers])
try! engine.start()
}
}
Run the app, but don't press the button yet. Then with no headphones plugged in, jump over to Apple Music and play a song. It should come out the speaker. Now, switch back to your app and press the button. For me, this makes the Apple Music audio switch back to the earpiece.
Any idea why this might be happening? And how I can prevent the route change? Thanks!

Related

App with playback audio category stops other apps sound

I added radio functionality to my application recently.
I wanted it to play music even when it is in the background.
So I implemented this piece of code
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: .default, options: [.duckOthers, .allowBluetooth])
try AVAudioSession.sharedInstance().setActive(true)
} catch let error as NSError {
print(error)
}
I got the app to play music when in the background now but the problem is that the app stops all other app's sounds when launched.
For example when SoundCloud is playing music and when I launch my app Soundcloud stops the music.
What I want to achieve is to make my app play music when it is in the background and also allow other apps to play music when my app is not playing music.
How can I fix that issue?
Thanks
do {
try AVAudioSession.sharedInstance().setCategory(.ambient)
try AVAudioSession.sharedInstance().setActive(true)
} catch { print(error) }
Above code add in AppDelegate.swift files didFinishLaunchingwithOptions method. This will solve your problem
To partially answer your question, you can allow your app to play audio while other apps are playing audio with the following configuration:
do {
try AVAudioSession.sharedInstance().setCategory(.playback, options: .mixWithOthers)
} catch(let error) {
print(error.localizedDescription)
}
.playback means the audio playing is central to the use of your app. So if you're playing music, background sounds like white noise, etc. you probably are using playback mode. .mixWithOthers is 'An option that indicates whether audio from this session mixes with audio from active sessions in other audio apps.' - in other words allows your primary audio to mix with audio coming from other apps.
The one thing I haven't figured out - is why on the first launch of my app other background audio pauses. Subsequent launches seem to work fine.

How do I make AVAudioSession work in silent mode and with .mixWithOthers

I'm writing an iOS app in Swift (Xcode 11.5) using AVAudioEngine
It's all working fine, but I can't get it to play along with other audio apps when the iPhone/iPad has the mute button off.
With this:
try AVAudioSession.sharedInstance().setCategory(.playback)
my audio works with the mute button on or off, but if I'm playing something from Apple Music, when I hit play on my audio, Apple Music pauses.
With this:
try AVAudioSession.sharedInstance().setCategory(.ambient, options: .mixWithOthers)
my audio works with Apple Music, but doesn't play if the mute button is off.
With this:
try AVAudioSession.sharedInstance().setCategory(.playback, options: [.mixWithOthers])
having .mixWithOthers makes no difference.
I'm sure there is a combination of options that will make it work with the mute button on or off and not stop other audio playing, but I can't find it!
TIA for any suggestions.
Ian
I've figured this out and, as pointed out by matt, I was doing something wrong! I can't post my code here because it is pretty big. Basically, I've written and audio class that handles all the audio files, declares the engine, AVAudioEngine(); nodes, AVAudioPlayerNode(); and does all the playing and so on.
My mistake was to put this sort of stuff in that class, and it was obviously the wrong place:
do {
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setActive(false)
if resetOptions.muteOtherApps {
try audioSession.setCategory(.playback, options: [])
} else {
try audioSession.setCategory(.playback, options: [.mixWithOthers])
}
} catch {
print("Audio error")
}
Since I'm letting the user choose between these options, it was convenient for me to put it in my main ViewController class. However, if it's set fixed for the app, from what I've read a good place to put it is in AppDelegate.Swift in the didFinishLaunchingWithOptions function.
The main point, at least what worked for me, is to keep these things separate to the class that is actually managing the audio.
So, thanks Matt.
Ian

How can I prevent video recording with AVFoundation from interrupting any global audio that is currently playing (Swift)?

I have successfully implemented valid and working AVFoundation video recording functionality into my application. That said, any playing audio e.g. Apple Music or a YouTube video playing in the background is interrupted / immediately paused whenever the video recording begins.
My question is: How could I allow the globally-playing audio to continue to play without interruption when recording video?
I was able to do this by adapting the answer here to Swift.
I was doing this to prevent a silent tutorial video in my app from interrupting any currently playing audio. Setting the AVAudioSession category to "ambient" using the setCategory(_:mode:options:) method seems to be the key here.
Apple seems to suggest using this method within the application didFinishLaunchingWithOptions method, but I think their example is targeted towards audio apps. So depending on your needs, you may want to put it there.
Here's the adapted code, I placed it within the view controller that is showing the video:
import AVFoundation
...
override func viewDidLoad() {
super.viewDidLoad()
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.ambient, mode: .default, options: .mixWithOthers)
} catch {
print(error.localizedDescription)
}
}

iOS Volume only adjustable on Playback

Developing an iOS app which plays back audio messages in segments (not continuous playback). When the app is opened, I initialize the audio session with the following options.
func _initAudioSesh() {
let audioSession = AVAudioSession.sharedInstance()
do {
if #available(iOS 10.0, *) {
try audioSession.setCategory(
AVAudioSessionCategoryPlayAndRecord,
mode: AVAudioSessionModeVoiceChat,
options: [
AVAudioSessionCategoryOptions.defaultToSpeaker,
AVAudioSessionCategoryOptions.allowBluetooth,
AVAudioSessionCategoryOptions.allowBluetoothA2DP
]
)
} else {
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
}
} catch {
print("Setting category to AVAudioSessionCategoryPlayback failed.")
}
}
Then, when ready to playback audio, I grab focus using setActive(true) and release focus using setActive(false)
The issue I'm encountering is that in the app, the hardware volume buttons only work when audio is playing, otherwise, the buttons do nothing. I was able to hack around this by ALWAYS holding setActive(true), but that hack is ugly and causes other issues. Has anyone else experienced volume buttons not working/adjusting in-app, and only working when audio is actively being played?
As soon as I leave the app, audio adjustment works, as soon as I bring it back into focus, it stops working unless I begin to play audio.
I've tried messing with how & when I create the audio session, with no success.
This ended up being a result of a specific library we're using (react-native-system-settings), which has now been patched. If other users encounter issues, the fix seems to be around allowing UI to show in VolumeView. Otherwise, it doesn't allow the hardware buttons to affect volume.

Remote controls of audio player are not visible in IOS 11 lock screen

I have a custom class which plays an audio with AVPlayer. In the method of this class have added play, pause, previous and next track commands to MPRemoteCommandCenter instance like so:
let mediaPlayerCommandCenter = MPRemoteCommandCenter.shared()
mediaPlayerCommandCenter.playCommand.addTarget(self, action: #selector(playRemoteHandler(_:))) // .. and so on
I have added also MPNowPlayingInfoCenter details.
"Background mode" is turned on in the "Capabilities" section of project settings and app plays audio in background normally.
In the application(application:didFinishLaunchingWithOptions:) method AVAudioSession category selected and activated also:
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print(error)
}
application.beginReceivingRemoteControlEvents() // in the documentation noticed that it is not necessary if we are using MPRemoteCommandCenter but anyways I tried it also
After all these codes, remote controls are visible neither in lock screen nor contol center. But audio plays normally in the background.

Resources