When I have airpods connected to my iphone and I try to override the audio to speaker, the audio defaults back to the airpods. I do not get this problem with any other bluetooth device or other audio options. How can I make the speaker output stick when airpods are connected?
Here is how I set up the audio session:
var err: Error? = nil
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSession.Category.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .allowBluetoothA2DP, .mixWithOthers])
} catch {
NSLog("Unable to change audio category because : \(String(describing: err?.localizedDescription))")
err = nil
}
try? session.setMode(AVAudioSession.Mode.voiceChat)
if err != nil {
NSLog("Unable to change audio mode because : \(String(describing: err?.localizedDescription))")
err = nil
}
let sampleRate: Double = 44100.0
try? session.setPreferredSampleRate(sampleRate)
if err != nil {
NSLog("Unable to change preferred sample rate because : \(String(describing: err?.localizedDescription))")
err = nil
}
try? session.setPreferredIOBufferDuration(0.005)
if err != nil {
NSLog("Unable to change preferred sample rate because : \(String(describing: err?.localizedDescription))")
err = nil
}
Speaker row on the action sheet:
let speakerOutput = UIAlertAction(title: "Speaker", style: .default, handler: {
(alert: UIAlertAction!) -> Void in
self.overrideSpeaker(override: true)
})
for description in currentRoute.outputs {
if convertFromAVAudioSessionPort(description.portType) == convertFromAVAudioSessionPort(AVAudioSession.Port.builtInSpeaker){
speakerOutput.setValue(true, forKey: "checked")
break
}
}
speakerOutput.setValue(UIImage(named: "ActionSpeaker.png")?.withRenderingMode(.alwaysOriginal), forKey: "image")
optionMenu.addAction(speakerOutput)
I am changing to speaker here and the bool does come in as true:
func overrideSpeaker(override : Bool) {
do {
let port: AVAudioSession.PortOverride = override ? .speaker : .none
try session.overrideOutputAudioPort(port)
} catch {
NSLog("audioSession error toggling speaker: \(error.localizedDescription)")
}
}
Here is my route change delegate, I get override first and then newDeviceAvailable:
@objc func handleRouteChange(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue:reasonValue) else {
return
}
switch reason {
case .newDeviceAvailable,
.categoryChange:
var audioShown = false
for output in session.currentRoute.outputs where output.portType != AVAudioSession.Port.builtInReceiver && output.portType != AVAudioSession.Port.builtInSpeaker {
self.showAudio()
audioShown = true
break
}
if !audioShown {
self.showSpeaker()
}
break
case .routeConfigurationChange:
break
case .override:
break
default: ()
}
}
err
variable, but you then never use it... how should the methods you are calling know that they should write errors intoerr
? I recommend you usetry
-catch
. – return true