Bluetooth A2DP as output

Hello,

Is it possible to route iPhone audio to the bluetooth speaker? I want to process audio from microphone and send result to bluetooth A2DP speaker. I use following code:


AudioComponentDescription componentDescription = {
        .componentType = kAudioUnitType_Output,
        .componentSubType = kAudioUnitSubType_RemoteIO,
        .componentManufacturer = kAudioUnitManufacturer_Apple,
        .componentFlags = 0,
        .componentFlagsMask = 0
    };
    AudioComponent component = AudioComponentFindNext(NULL, &componentDescription);
    checkResult(AudioComponentInstanceNew(component, &_audioUnit), "AudioComponentInstanceNew");
   
    UInt32 oneFlag = 1;
    AudioUnitElement bus0 = 0;
    checkResult(AudioUnitSetProperty(_audioUnit,
                                     kAudioOutputUnitProperty_EnableIO,
                                     kAudioUnitScope_Output,
                                     bus0,
                                     &oneFlag,
                                     sizeof(oneFlag)), "AudioUnitSetProperty");
   
    AudioUnitElement bus1 = 1;
    checkResult(AudioUnitSetProperty(_audioUnit,
                                     kAudioOutputUnitProperty_EnableIO,
                                     kAudioUnitScope_Input,
                                     bus1,
                                     &oneFlag,
                                     sizeof(oneFlag)), "AudioUnitSetProperty");
   
    AudioStreamBasicDescription streamDescription = {
        .mSampleRate = 44100,
        .mFormatID = kAudioFormatLinearPCM,
        .mFormatFlags = kAudioFormatFlagIsSignedInteger,
        .mChannelsPerFrame = 2,
        .mBytesPerPacket = 2 * sizeof(SInt16),
        .mFramesPerPacket = 1,
        .mBytesPerFrame = 2 * sizeof(SInt16),
        .mBitsPerChannel = 8 * sizeof(SInt16)
    };
    checkResult(AudioUnitSetProperty(_audioUnit,
                                     kAudioUnitProperty_StreamFormat,
                                     kAudioUnitScope_Input,
                                     bus0,
                                     &streamDescription,
                                     sizeof(streamDescription)), "AudioUnitSetProperty");
   
    checkResult(AudioUnitSetProperty(_audioUnit,
                                     kAudioUnitProperty_StreamFormat,
                                     kAudioUnitScope_Output,
                                     bus1,
                                     &streamDescription,
                                     sizeof(streamDescription)), "AudioUnitSetProperty");
   
    AURenderCallbackStruct renderCallback {
        .inputProc = callback,
        .inputProcRefCon = this
    };
    checkResult(AudioUnitSetProperty(_audioUnit,
                                     kAudioUnitProperty_SetRenderCallback,
                                     kAudioUnitScope_Global,
                                     bus0,
                                     &renderCallback,
                                     sizeof(renderCallback)), "AudioUnitSetProperty");
   
    checkResult(AudioUnitInitialize(_audioUnit), "AudioUnitInitialize");


But this send audio to headphones or default speaker.

Accepted Reply

Just wanted to update this thread from awhile back and mention it is in fact possible (as of iOS 10) to use the iPhone Microphone as input and Bluetooth A2DP headset as audio output at the same time within a single App. In order to do this the app has to set the AVAudioSessionCategoryOptionAllowBluetoothA2DP category option on its AVAudioSession. Audio sessions using the AVAudioSessionCategoryMultiRoute or AVAudioSessionCategoryRecord categories implicitly clear this option. See <https://developer.apple.com/documentation/avfoundation/avaudiosessioncategoryoptions/avaudiosessioncategoryoptionallowbluetootha2dp>


Note that if both AVAudioSessionCategoryOptionAllowBluetooth and AVAudioSessionCategoryOptionAllowBluetoothA2DP are set, and the Bluetooth device supports both profiles (e.g., AirPods), Core Audio will prefer the HFP route. Also note that when HFP is in use, it’s always bidirectional -- meaning Core Audio will use the Bluetooth device’s microphone and speaker.

Replies

This is a routing issue, the A2DP output route will currently not be available to you in AVAudioSessionCategoryPlayAndRecord.

See < http://forums.developer.apple.com/message/8370 > for more details. To confirm this you can either use MPVolumeView which will display available outputs or add a few lines of code to your app.

For example, using AVAudioSessionCategoryPlayback and asking the AVAudioSession for the currentRoute.outputs will give you something like this - in this case, it shows output via A2DP to a Jambox.


(
    "<AVAudioSessionPortDescription: 0x17000b610, type = BluetoothA2DPOutput; name = JAMBOX by Jawbone; UID = 00:21:3C:65:41:CE-tacl; selectedDataSource = (null)>"
)


Doing the same when in the AVAudioSessionCategoryPlayAndRecord category and asking for availableInputs from the audio session and inputs/outputs from the currentRoute produces the following. A2DP is gone as the current output and will not be avalable as an output choice with MPVolumeView.


Available Inputs:
(
    "<AVAudioSessionPortDescription: 0x1740190b0, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>"
)

Current Route Input:
(
    "<AVAudioSessionPortDescription: 0x17401fa80, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>"
)

Current Route output:
(
    "<AVAudioSessionPortDescription: 0x17401fb10, type = Receiver; name = Receiver; UID = Built-In Receiver; selectedDataSource = (null)>"
)


Having a headset plugged in yields the following, now you have more than one available input.


Available Inputs:
(
    "<AVAudioSessionPortDescription: 0x170009110, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Bottom>",
    "<AVAudioSessionPortDescription: 0x170009180, type = MicrophoneWired; name = Headset Microphone; UID = Wired Microphone; selectedDataSource = (null)>"
)

Current Route Input:
(
    "<AVAudioSessionPortDescription: 0x170008fb0, type = MicrophoneWired; name = Headset Microphone; UID = Wired Microphone; selectedDataSource = (null)>"
)

Current Route Output:
(
    "<AVAudioSessionPortDescription: 0x170017140, type = Headphones; name = Headphones; UID = Wired Headphones; selectedDataSource = (null)>"
)


By querying the AVAudioSession object and looking at AVAudioSessionPortDescriptions and AVAudioSessionDataSourceDescriptions one can figure out which inputs and outputs are available and from there may be able to select specific data sources for each. Note that if you set a mode it may affect routing and also remember that the current preferred method for choosing outputs is through the use of MPVolumeView.


Recommended reading & viewing:

Input selection is discussed in "QA1799 AVAudioSession - Microphone Selection" < http://developer.apple.com/library/ios/qa/qa1799/_index.html >

Audio Session Management for iOS < https://developer.apple.com/videos/wwdc/2011/#413 >

Audio Session and Multiroute Audio in iOS< http://developer.apple.com/videos/wwdc/2012/#505 >

Here are a few examples along with the console output from the route change notifications when in the AVAudioSessionCategoryPlayback category using MPVolumeView to select outputs:


Going from Headphones to A2DP:


Route Changed - Previous Route Description:


<AVAudioSessionRouteDescription: 0x17401da70,
inputs = (null);
outputs = (
    "<AVAudioSessionPortDescription: 0x17401d8c0, type = Headphones; name = Headphones; UID = Wired Headphones; selectedDataSource = (null)>"
)>


Current Route:


<AVAudioSessionRouteDescription: 0x17401db70,
inputs = (null);
outputs = (
    "<AVAudioSessionPortDescription: 0x17401db50, type = BluetoothA2DPOutput; name = JAMBOX by Jawbone; UID = 00:21:3C:65:41:CE-tacl; selectedDataSource = (null)>"
)>

Going from A2DP back to Headphones.

Route Changed - Previous Route Description:


<AVAudioSessionRouteDescription: 0x17001ad90,
inputs = (null);
outputs = (
    "<AVAudioSessionPortDescription: 0x17001c1b0, type = BluetoothA2DPOutput; name = JAMBOX by Jawbone; UID = 00:21:3C:65:41:CE-tacl; selectedDataSource = (null)>"
)>


Current Route:


<AVAudioSessionRouteDescription: 0x17001ad40,
inputs = (null);
outputs = (
    "<AVAudioSessionPortDescription: 0x17001c1a0, type = Headphones; name = Headphones; UID = Wired Headphones; selectedDataSource = (null)>"
)>

Hi, is it possible to achieve builtinmic as input and Bluetooth AD2P headphones as output simultaneously using the multiroute category? Or does this limitation occur in all modes? Thanks so much for your help.

Just wanted to update this thread from awhile back and mention it is in fact possible (as of iOS 10) to use the iPhone Microphone as input and Bluetooth A2DP headset as audio output at the same time within a single App. In order to do this the app has to set the AVAudioSessionCategoryOptionAllowBluetoothA2DP category option on its AVAudioSession. Audio sessions using the AVAudioSessionCategoryMultiRoute or AVAudioSessionCategoryRecord categories implicitly clear this option. See <https://developer.apple.com/documentation/avfoundation/avaudiosessioncategoryoptions/avaudiosessioncategoryoptionallowbluetootha2dp>


Note that if both AVAudioSessionCategoryOptionAllowBluetooth and AVAudioSessionCategoryOptionAllowBluetoothA2DP are set, and the Bluetooth device supports both profiles (e.g., AirPods), Core Audio will prefer the HFP route. Also note that when HFP is in use, it’s always bidirectional -- meaning Core Audio will use the Bluetooth device’s microphone and speaker.

Is this possible in the reverse with iOS 10? ie. take in audio data via bluetooth a2dp and playback in realtime on the built-in speakers?