Audio to bluetooth headset on iOS

I got a bugreport (through a bad app store review, sigh) that bluetooth headsets / headphones do not playback audio on the iOS plattform. I researched a bit and it seems the fix should be as simple as adding the following lines to juce_ios_Audio.cpp around line 105:

UInt32 audioCategory = audioInputIsAvailable ? kAudioSessionCategory_PlayAndRecord
                                                     : kAudioSessionCategory_MediaPlayback;

AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (audioCategory), &audioCategory);
		
UInt32 allowBluetoothInput = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, sizeof(allowBluetoothInput), &allowBluetoothInput);
		
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this);

kAudioSessionProperty_OverrideCategoryEnableBluetoothInput is available in in iOS 3.1 and later. While the name seems to imply that it only applies to audio input it affects audio output as well. This behavior is documented in the Audio Session Services Reference (http://developer.apple.com/library/ios/#documentation/AudioToolbox/Reference/AudioSessionServicesReference/Reference/reference.html):

“This property affects the kAudioSessionCategory_PlayAndRecord category as follows: If the audio input to the device is coming from a Bluetooth headset, setting this property to TRUE results in audio output also going to the Bluetooth headset.”

I can’t test right now, my already ordered bluetooth headset is still on its way from Amazon. If someone could verify if this fixes bluetooth audio that would be great.

Patrick

Thanks for tracking that one down… I also don’t have a bluetooth headset, anyone tried it?

With the change I hear audio but with the wrong samplerate. The problem is when iOS enables the headset, the device samplerate changes. I dont see were the changed device samplerate is propagated to my code.

Patrick

When a device sample rate changed you should get called with prepareToPlay().

Alright, I hear audio with a correct samplerate now, its very low though only a samplerate of 8000.

I am sure the bluetooth headset supports a higher samplerate. I hear good quality audio when using the default music app on the iPhone with the same headset or if I pair the headset with my macbook. On the macbook I see two additional audio output devices when I connect the bluetooth headset. MM100 and MM100 Stereo. MM100 sounds just as bad as audio through my app on the iPhone, MM100 Stereo sounds great.

Maybe something is missing during audio device setup? I haven’t seen anything obvious though.

Patrick

It seems I finally found the solution. To make the bluetooth headset work correctly the audio category must be set to kAudioSessionCategory_MediaPlayback. If it is set to kAudioSessionCategory_PlayAndRecord I was not able to get better samplerates than 8000.

To get best audio quality with kAudioSessionCategory_MediaPlayback and at least some audio with kAudioSessionCategory_PlayAndRecord setup of the audio unit should be changed like this:

AudioSessionSetActive (true);

UInt32 audioCategory = (inputChannels > 0 && audioInputIsAvailable) ? kAudioSessionCategory_PlayAndRecord : kAudioSessionCategory_MediaPlayback;

if(audioCategory == kAudioSessionCategory_PlayAndRecord)
{
	UInt32 allowBluetoothInput = 1;
	AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, sizeof(allowBluetoothInput), &allowBluetoothInput);
}

AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (audioCategory), &audioCategory);

Then for audio playback only initialize the device manager like this:

Besides bluetooth audio this also makes the MPVolumeView work correctly. Using the MPVolumeView control the user can change the active audio output device including Airplay playback devices! I successfully tested these changes with two bluetooth headsets and an software Airplay receiver. Here is what the MPVolumeView looks like with these devices connected:

[attachment=0]IMG_0758.PNG[/attachment]

Patrick

Excellent stuff, thanks Patrick, I’ll get that sorted out!

Very annoying about the 8kHz thing, I guess it must be pushing bluetooth’s bandwidth limit.

My guess is, iOS sets the output samplerate to the same value as the maximum input samplerate because output and input samplerates where both 8000.

If you have code changes that need testing on bluethooth headsets / airplay receivers please let me know. I’ll be happy to help.

Patrick

Thanks! I checked in some changes earlier today if you want to try them.

My tests were successful. I tested with a bluetooth headset and an airplay software receiver and both worked just fine.

Patrick

I have been testing this with an MM200 bluetooth headset. It works in a similar manner to the one that Patrick describes. On OSX it shows up in the DeviceManagerSelector component as 2 separate devices, both a regular and a stereo version. When using the bluetooth device for both input and output, it limits to 8 kHz.

On the iPhone, the bluetooth device works just fine on the output when the number of input channels is set to zero (as does airplay). However, when I want to work using the input from the headset also, the device will appear in the MPVolumeView, but whenever I select it, the selection jumps to iPhone audio. I go back to the MPVolumeView and see that the selection has jumped.

Any ideas folks?

Hi there,

Bluetooth seems to still be unstable on iOs devices. Here is my issue:
My iOs app uses input but I also would like to be able to have a normal sample rate while Bluetooth headset are connected.

I guess the best compromise would be to force AVAudioSessionCategoryPlayback when Bluetooth headset is connected and AVAudioSessionCategoryPlayAndRecord when it is not.

Or, is there any way to input sound from iPad and output through bluetooth headset using AVAudioSessionCategoryPlayback ?

Would you guys recommend a workaround to do this?

Thanks,
Bastien

Found out here: JUCE IOS Bluetooth headphones issue and solution

For JUCE 5.4.7, in juce_ios_Audio.cpp I had to comment out the option AVAudioSessionCategoryOptionAllowBluetooth in setAudioSessionCategory() to get bluetooth audio to work with headphones (even though it would play over an old bluetooth speaker).