2

Is there a way to route the audio coming from the wired line-in input directly to AirPods?

enter image description here

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 and MPVolumeView, 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.

Geri Borbás
  • 15,810
  • 18
  • 109
  • 172
  • May I create an app to get the input, and another to put the output? Therefore it could be two separate audio sessions I guess. – Geri Borbás Feb 07 '22 at 19:49
  • 1
    I haven't tested with AirPods (which may have some tricks to make this not work), but try setting you AVAudioSession options to `allowBluetoothA2DP`. The reason this may work (and does work in some other situations) is that A2DP is unidirectional, output only. So it can only send audio to the earbuds; it can't get microphone input on the same channel. So it has to get input from some other microphone, and should select your line-in. (This absolutely works with non-AirPod earbuds and the built-in microphone. In fact, it's annoying. Not as certain w/ AirPods+LineIn.) – Rob Napier Feb 07 '22 at 19:56
  • @RobNapier Rob, it actually works! Thanks for the pointer, I don't think I would arrive to this any time soon. – Geri Borbás Feb 07 '22 at 21:26
  • @RobNapier It was somewhat suspicious that AudioBus had no AirPods listed amongst the system inputs, perhaps it had similar considerations. Now the big question, whether I can use the audio sharing (two AirPods), but that is for another day. – Geri Borbás Feb 07 '22 at 21:29
  • @RobNapier Unfortunately it goes silent when I connect the 2nd AirPods (that would be the use-case for the app). If I switch back to VLC for example, I can hear music in both AirPods. But back in the session, it does not work. Could there be perhaps any specific session configuration to enable Shared audio? Or could `.playAndRecord` category or `.allowBluetoothA2DP` option be somewhat incompatible with audio sharing feature? – Geri Borbás Feb 08 '22 at 09:50
  • 1
    I don't have any experience with shared audio. I don't know beyond setting preferred input, but if there are two identical routes, I'm betting it's going to move the output, too (as it did in your original tests). – Rob Napier Feb 08 '22 at 13:56
  • 1
    Thanks for getting back again, I'll do some testing (categories, playback only, etc.), try to isolate where it stops being shared. Then probably create new question with that. This question is solved, nevertheless. ✨ – Geri Borbás Feb 08 '22 at 14:25

1 Answers1

1

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, or playback categories.

Starting with iOS 10.0, apps using the playAndRecord 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.

Geri Borbás
  • 15,810
  • 18
  • 109
  • 172