Is there a way to route the audio coming from the wired line-in input directly to AirPods?
Currently, I'm creating a .playAndRecord
audio session. Paired the AirPods. Later on, with AVAudioEngine
I connect the input device directly to the output device.
engine.connect(
engine.inputNode,
to: engine.outputNode,
format: engine.inputNode.inputFormat(forBus: 0)
)
Works nicely, I can hear the live (!) sound from the AirPods microphone directly in my ears. 🎉 But then I connect the line-in cable, unfortunately, it overrides the output as well to the line-in (I can read the metering levels nicely, though). If I connect the AirPods, then again, both the input and the output change to AirPods.
Is there a way to reroute the output device to AirPods, but keep the input device unchanged?
UPDATE: I tried set the input port (to line-in) manually using
setPreferredInput
, but as soon as the input is selected, the output is also get adjusted (to the wired device).
UPDATE: I tried routing both with
AVRoutePickerView
andMPVolumeView
, but all yielded the same result. If I pick a new output, the input gets modified as well.
UPDATE: AudioBus app does this, I can set wired headphones as input, and AirPods as output, works like a charm. So it is definitely possible, I just have no idea what API to use. Actually, neither AudioBus can share the audio to two sets of AirPods.
Yes! 🎉 However, it only works if the audio session is created with the allowBluetoothA2DP
option (according to @RobNapier above). It is an output-only profile, therefore it does not route input from the Bluetooth devices at all.
A2DP is a stereo, output-only profile intended for higher bandwidth audio use cases, such as music playback. The system automatically routes to A2DP ports if you configure an app’s audio session to use the
ambient
,soloAmbient
, orplayback
categories.
Starting with iOS 10.0, apps using theplayAndRecord
category may also allow routing output to paired Bluetooth A2DP devices. To enable this behavior, pass this category option when setting your audio session’s category.
You can make it work with a bare-bones sound manager something like below.
class Sound {
...
private var session: AVAudioSession {
AVAudioSession.sharedInstance()
}
private let engine = AVAudioEngine()
init() {
setupAudioSession()
}
func setupAudioSession() {
do {
try session.setCategory(
.playAndRecord,
options: [
.allowBluetoothA2DP, // 🔑
.allowAirPlay
]
)
try session.setActive(true)
} catch {
print("Could not configure and activate session. \(error)")
}
}
func start() {
engine.connect(
engine.inputNode,
to: engine.outputNode,
format: engine.inputNode.inputFormat(forBus: 0)
)
do {
try engine.start()
} catch {
print("Could not start engine. \(error)")
}
}
...
}
If you put a MPVolumeView
somewhere on the UI, then you can explicitly select the route to bluetooth devices (and more). But I guess even without that you can change the route by simply physically connect/disconnect the devices themselves.