Audio recording

Bluetooth audio profiles based on Bluetooth Low Energy (BLE) Audio allow bidirectional streaming of high quality audio (for example, stereo audio with a 32 kHz sampling rate). This is possible thanks to the creation of the LE Isochronous Channel (ISO). ISO is similar to the Synchronous Connection-Oriented (SCO) Link because it also uses reserved wireless bandwidth, but the bandwidth reservation is no longer capped at 64 Kbps and can be dynamically adjusted.

Bluetooth audio input can use the latest AudioManager API for nearly all use cases, excluding phone calls. This guide covers how to record stereo audio from BLE Audio hearables.

Configure your application

First, configure your application to target the correct SDK in build.gradle:

targetSdkVersion 31

Register audio callback

Create an AudioDeviceCallback that lets your application know of any changes to connected or disconnected AudioDevices.

final AudioDeviceCallback audioDeviceCallback = new AudioDeviceCallback() {
  @Override
  public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
    };
  @Override
  public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
    // Handle device removal
  };
};

audioManager.registerAudioDeviceCallback(audioDeviceCallback);

Find BLE Audio Device

Get a list of all connected audio devices with input supported, then use getType() to see if the device is a headset using AudioDeviceInfo.TYPE_BLE_HEADSET.

Kotlin

val allDeviceInfo = audioManager.getDevices(GET_DEVICES_INPUTS)
var bleInputDevice: AudioDeviceInfo? = null
  for (device in allDeviceInfo) {
    if (device.type == AudioDeviceInfo.TYPE_BLE_HEADSET) {
      bleInputDevice = device
      break
    }
  }

Java

AudioDeviceInfo[] allDeviceInfo = audioManager.getDevices(GET_DEVICES_INPUTS);
AudioDeviceInfo bleInputDevice = null;
for (AudioDeviceInfo device : allDeviceInfo) {
  if (device.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET) {
    bleInputDevice = device;
    break;
  }
}

Stereo support

To check if stereo microphones are supported on the selected device, see if the device has two or more channels. If the device only has one channel, set the channel mask to mono.

Kotlin

var channelMask: Int = AudioFormat.CHANNEL_IN_MONO
if (audioDevice.channelCounts.size >= 2) {
  channelMask = AudioFormat.CHANNEL_IN_STEREO
}

Java

if (bleInputDevice.getChannelCounts() >= 2) {
  channelMask = AudioFormat.CHANNEL_IN_STEREO;
};

Set up the audio recorder

Audio recorders can be set up using the standard AudioRecord builder. Use the channel mask to select stereo or mono configuration.

Kotlin

val recorder = AudioRecord.Builder()
  .setAudioSource(MediaRecorder.AudioSource.MIC)
  .setAudioFormat(AudioFormat.Builder()
    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
    .setSampleRate(32000)
    .setChannelMask(channelMask)
    .build())
  .setBufferSizeInBytes(2 * minBuffSizeBytes)
  .build()

Java

AudioRecord recorder = new AudioRecord.Builder()
  .setAudioSource(MediaRecorder.AudioSource.MIC)
  .setAudioFormat(new AudioFormat.Builder()
    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
    .setSampleRate(32000)
    .setChannelMask(channelMask)
    .build())
  .setBufferSizeInBytes(2*minBuffSizeBytes)
  .build();

Set preferred device

Setting a preferred device informs the audio recorder which audio device you wish to record with.

Kotlin

recorder.preferredDevice = audioDevice

Java

recorder.setPreferredDevice(bleInputDevice);

Now, you can record audio as outlined in the MediaRecorder guide.