iOS Audio Routing - Bluetooth Output + Built-in Microphone Input

Hello!

I'm experiencing an issue with iOS's audio routing system when trying to use Bluetooth headphones for audio output while also recording environmental audio from the built-in microphone.

Desired behavior:

  • Play audio through Bluetooth headset (AirPods)
  • Record unprocessed environmental audio from the iPhone's built-in microphone

Actual behavior:

  • When explicitly selecting the built-in microphone, iOS reports it's using it (in currentRoute.inputs)
  • However, the actual audio data received is clearly still coming from the AirPods microphone
  • The audio is heavily processed with voice isolation/noise cancellation, removing environmental sounds

Environment Details

  • Device: iPhone 12 Pro Max
  • iOS Version: 18.4.1
  • Hardware: AirPods
  • Audio Framework: AVAudioEngine (also tried AudioQueue)

Code Attempted

I've tried multiple approaches to force the correct routing:

func configureAudioSession() {
    let session = AVAudioSession.sharedInstance()

    // Configure to allow Bluetooth output but use built-in mic
    try? session.setCategory(.playAndRecord, 
                           options: [.allowBluetoothA2DP, .defaultToSpeaker])
    try? session.setActive(true)

    // Explicitly select built-in microphone
    if let inputs = session.availableInputs,
       let builtInMic = inputs.first(where: { $0.portType == .builtInMic }) {
        try? session.setPreferredInput(builtInMic)
        print("Selected input: \(builtInMic.portName)")
    }

    // Log the current route
    let route = session.currentRoute
    print("Current input: \(route.inputs.first?.portName ?? "None")")

    // Configure audio engine with native format
    let inputNode = audioEngine.inputNode
    let nativeFormat = inputNode.inputFormat(forBus: 0)

    inputNode.installTap(onBus: 0, bufferSize: 1024, format: nativeFormat) { buffer, time in
        // Process audio buffer
        // Despite showing "Built-in Microphone" in route, audio appears to be 
        // coming from AirPods with voice isolation applied - welp!
    }

    try? audioEngine.start()
}

I've also tried various combinations of:

  • Different audio session modes (.default, .measurement, .voiceChat)
  • Different option combinations (with/without .allowBluetooth, .allowBluetoothA2DP)
  • Setting session.setPreferredInput() both before and after activation

Diagnostic Observations

When AirPods are connected:

  • AVAudioSession.currentRoute.inputs correctly shows "Built-in Microphone" after setPreferredInput()
  • The actual audio data received shows clear signs of AirPods' voice isolation processing
  • Background/environmental sounds are actively filtered out...
  • When recording a test audio played near the phone (not through the app), the recording is nearly silent. Only headset voice goes through.

Questions

  • Is there a workaround to force iOS to actually use the built-in microphone while maintaining Bluetooth output?
  • Are there any lower-level configurations that might resolve this issue?

Any insights, workarounds, or suggestions would be greatly appreciated. This is blocking a critical feature in my application that requires environmental audio recording while providing audio feedback through headphones 😅

iOS Audio Routing - Bluetooth Output + Built-in Microphone Input
 
 
Q