Changing the default audio device on Windows is a tricky task, especially for Bluetooth audio devices. The APIs involved are undocumented and vary across Windows versions. The same problem applies to reconnecting to an already paired Bluetooth audio device.
The audio features described here are built on top of the Bluetooth Classic audio profiles — HFP, HSP, A2DP, and AVRCP. For a general explanation of what each of those profiles does, see the Bluetooth Classic profiles overview.
The Bluetooth Framework solves both. It provides a consistent way to enumerate audio devices, switch the system default, reconnect to a paired device without using the Windows Add Device dialog, and control volume. Four classes cover the full workflow:
wclAudioSwitcher— enumerate available audio devices, select the default one, reconnect to and disconnect from Bluetooth audio devices.wclBluetoothAudioReceiver— turn a PC into a Bluetooth audio receiver.wclAudioMeter— measure the current audio level and receive change events.wclAudioVolume— read and change audio volume and mute state on a selected endpoint.
Audio switcher
The wclAudioSwitcher class enumerates the available audio devices and allows selecting a default one for playback and
recording. The system default device is the one Windows uses for output and input. Input and output devices are selected separately for
each role.
An audio device is described by a set of properties:
- Id — the system ID assigned to the device.
- Friendly name — the device's friendly name.
- Description — a description of the device.
- Flow — how the device is used: Render for output, Capture for input.
- Roles — the roles associated with the device: Console (games), Multimedia, Communications.
- State — Active, Disabled, Not Present, or Unplugged.
- MAC — the Bluetooth device MAC address, if the audio device is Bluetooth.
- Service — the connected Bluetooth service UUID, if applicable.
Change the default audio device
When a new audio device is connected, Windows makes it the default one. To change the system default, select a device, choose a role, and
apply the change. The AudioSwitcher sample application demonstrates this.
Reconnect to a paired device
If a Bluetooth audio device is not paired yet, the wclBluetoothManager class is used to pair with it and install the required
services. Windows then connects to the device automatically and makes it the default audio device.
If the device is already paired, there is no easy way to reconnect it through the Windows UI without opening the Add Device dialog. The
Bluetooth Framework provides a direct way to reconnect to an already paired Bluetooth audio device
without using the dialog or re-pairing. The AudioSwitcher sample demonstrates the flow: enable the Unplugged checkbox,
enumerate devices, find the target by MAC or name, and click Connect. The system connects to the selected device and sets it as
the default.
Disconnect
There are two common ways to disconnect a Bluetooth audio device: turn the device off, or use the Windows Add Device dialog. A third option is to unpair the device, but that removes it entirely and requires a new pairing.
Both options are inconvenient when the device is a two-in-one headset (microphone and speaker) and only one part needs to be disconnected,
for example the microphone. The Bluetooth Framework provides a way to disconnect any connected Bluetooth
device, or a specific part of it, without unpairing. The AudioSwitcher sample shows how to disconnect a single service of a
Bluetooth audio device while the other service stays connected.
Using wclAudioSwitcher
The snippet below opens the switcher, enumerates the active audio devices, and sets a new default device for the multimedia role.
var
Switcher: TwclAudioSwitcher;
begin
Switcher := TwclAudioSwitcher.Create(nil);
try
Switcher.Open;
var Devices: TwclAudioDevices;
Switcher.Enum([asActive], Devices);
for var Device in Devices do
Log(Device.FriendlyName);
Switcher.SetDefault(drMultimedia, Devices[0].Id);
finally
Switcher.Close;
Switcher.Free;
end;
end;
TwclAudioSwitcher* Switcher = new TwclAudioSwitcher(NULL);
try
{
Switcher->Open();
TwclAudioDevices Devices;
Switcher->Enum(TwclAudioDeviceStates() << asActive,
Devices);
for (int i = 0; i < Devices.Length; i++)
Log(Devices[i].FriendlyName);
Switcher->SetDefault(drMultimedia, Devices[0].Id);
}
__finally
{
Switcher->Close();
delete Switcher;
}
wclAudioSwitcher Switcher = new wclAudioSwitcher();
try
{
Switcher.Open();
wclAudioDevice[] Devices;
Switcher.Enum(wclAudioDeviceState.asActive,
out Devices);
foreach (wclAudioDevice Device in Devices)
Log(Device.FriendlyName);
Switcher.SetDefault(
wclAudioDeviceRole.drMultimedia, Devices[0].Id);
}
finally
{
Switcher.Close();
}
Dim Switcher As New wclAudioSwitcher()
Try
Switcher.Open()
Dim Devices As wclAudioDevice() = Nothing
Switcher.Enum(wclAudioDeviceState.asActive, Devices)
For Each Device As wclAudioDevice In Devices
Log(Device.FriendlyName)
Next
Switcher.SetDefault(
wclAudioDeviceRole.drMultimedia, Devices(0).Id)
Finally
Switcher.Close()
End Try
CwclAudioSwitcher Switcher;
Switcher.Open();
wclAudioDevices Devices;
Switcher.Enum(wclAudioDeviceStates{ asActive }, Devices);
for (auto& Device : Devices)
Log(Device.FriendlyName);
Switcher.SetDefault(drMultimedia, Devices[0].Id);
Switcher.Close();
Audio receiver
The audio receiver feature turns a PC into an audio playback device. This is useful when an audio stream from a phone or another device needs to be captured on the PC side.
To use it, pair with the source device first. The BluetoothManager sample application shows how to pair with a
Bluetooth-enabled device. Once the device is paired, enumerate its services, select the A2DP service, and install it.
Then, in the AudioReceiver sample application, find the paired device and start the receiver. The receiver waits for
an incoming audio connection. The OnConnected event fires when the remote device starts an audio connection. The
OnDisconnected event fires when the remote device terminates it. A connection can also be forced from the PC side by
calling Connect.
Current limitations:
- Only the A2DP profile is supported.
- To disconnect a remote device from the PC side, close the receiver and start it again.
- If the remote device terminates the connection from its side, initiating a new connection from the PC side requires restarting the receiver.
Using wclBluetoothAudioReceiver
The example below starts the audio watcher, handles a discovered device, and makes the PC listen for or connect to an A2DP source.
var
Watcher: TwclBluetoothAudioWatcher;
Receiver: TwclBluetoothAudioReceiver;
begin
Watcher := TwclBluetoothAudioWatcher.Create(nil);
Receiver := TwclBluetoothAudioReceiver.Create(nil);
Watcher.OnDeviceAdded := WatcherDeviceAdded;
Watcher.Start;
// In the DeviceAdded handler, store the device ID.
// Make the PC discoverable as an A2DP receiver:
Receiver.Listen(SelectedDeviceId);
// Or force a connection to a specific paired device:
Receiver.Connect;
Receiver.Close;
Watcher.Stop;
Receiver.Free;
Watcher.Free;
end;
TwclBluetoothAudioWatcher* Watcher =
new TwclBluetoothAudioWatcher(NULL);
TwclBluetoothAudioReceiver* Receiver =
new TwclBluetoothAudioReceiver(NULL);
Watcher->OnDeviceAdded = WatcherDeviceAdded;
Watcher->Start();
// DeviceAdded event handler:
void __fastcall TForm1::WatcherDeviceAdded(TObject* Sender,
const String Id, const String Name)
{
SelectedDeviceId = Id;
}
// Listen for incoming connections:
Receiver->Listen(SelectedDeviceId);
// Or force a connection:
Receiver->Connect();
Receiver->Close();
Watcher->Stop();
delete Receiver;
delete Watcher;
wclBluetoothAudioWatcher Watcher =
new wclBluetoothAudioWatcher();
wclBluetoothAudioReceiver Receiver =
new wclBluetoothAudioReceiver();
Watcher.OnDeviceAdded += (sender, args) =>
{
SelectedDeviceId = args.Id;
};
Watcher.Start();
// Start listening as an A2DP receiver:
Receiver.Listen(SelectedDeviceId);
// Or initiate a connection from the PC side:
Receiver.Connect();
Receiver.Close();
Watcher.Stop();
Dim Watcher As New wclBluetoothAudioWatcher()
Dim Receiver As New wclBluetoothAudioReceiver()
AddHandler Watcher.OnDeviceAdded, Sub(sender, args)
SelectedDeviceId = args.Id
End Sub
Watcher.Start()
' Start listening:
Receiver.Listen(SelectedDeviceId)
' Or force a connection:
Receiver.Connect()
Receiver.Close()
Watcher.Stop()
CwclBluetoothAudioWatcher Watcher;
CwclBluetoothAudioReceiver Receiver;
Watcher.OnDeviceAdded = [](void* Sender,
const tstring& Id, const tstring& Name)
{
SelectedDeviceId = Id;
};
Watcher.Start();
// Listen for incoming A2DP connections:
Receiver.Listen(SelectedDeviceId);
// Or initiate a connection:
Receiver.Connect();
Receiver.Close();
Watcher.Stop();
Volume control
Two classes handle volume. The wclAudioMeter class monitors audio levels on a selected endpoint, so an application can
display the current level. The wclAudioVolume class reads and changes the input and output stream volume and mute state
on a selected endpoint.
Using wclAudioMeter and wclAudioVolume
The example below opens an audio endpoint, reads its peak level, and adjusts the master volume and mute state.
var
Meter: TwclAudioMeter;
Volume: TwclAudioVolume;
Peak: Single;
begin
Meter := TwclAudioMeter.Create(nil);
Volume := TwclAudioVolume.Create(nil);
Meter.Open(EndpointId);
Volume.Open(EndpointId);
// Get peak audio level:
Meter.GetPeak(Peak);
ShowMessage('Current peak: ' + FloatToStr(Peak));
// Adjust master volume (0.0 to 1.0):
Volume.SetVolume(0.75);
// Mute / unmute:
Volume.SetMute(True);
Volume.Close;
Meter.Close;
Volume.Free;
Meter.Free;
end;
TwclAudioMeter* Meter = new TwclAudioMeter(NULL);
TwclAudioVolume* Volume = new TwclAudioVolume(NULL);
Meter->Open(EndpointId);
Volume->Open(EndpointId);
float Peak;
Meter->GetPeak(Peak);
ShowMessage("Current peak: " + FloatToStr(Peak));
Volume->SetVolume(0.75);
Volume->SetMute(true);
Volume->Close();
Meter->Close();
delete Volume;
delete Meter;
wclAudioMeter Meter = new wclAudioMeter();
wclAudioVolume Volume = new wclAudioVolume();
Meter.Open(EndpointId);
Volume.Open(EndpointId);
float Peak;
Meter.GetPeak(out Peak);
MessageBox.Show("Current peak: " + Peak.ToString());
Volume.SetVolume(0.75f);
Volume.SetMute(true);
Volume.Close();
Meter.Close();
Dim Meter As New wclAudioMeter()
Dim Volume As New wclAudioVolume()
Meter.Open(EndpointId)
Volume.Open(EndpointId)
Dim Peak As Single
Meter.GetPeak(Peak)
MessageBox.Show("Current peak: " & Peak.ToString())
Volume.SetVolume(0.75F)
Volume.SetMute(True)
Volume.Close()
Meter.Close()
CwclAudioMeter Meter;
CwclAudioVolume Volume;
Meter.Open(EndpointId);
Volume.Open(EndpointId);
float Peak;
Meter.GetPeak(Peak);
AfxMessageBox(CString("Current peak: ")
+ FloatToStr(Peak));
Volume.SetVolume(0.75f);
Volume.SetMute(true);
Volume.Close();
Meter.Close();
Frequently asked questions
- How to switch the default Bluetooth audio device programmatically?
- Use the
wclAudioSwitcherclass from the Bluetooth Framework to enumerate audio devices and set the default for the multimedia, communications, or gaming role. - How to reconnect to an already paired Bluetooth audio device?
- The Bluetooth Framework provides a unique feature to reconnect to a paired Bluetooth
audio device without using the Windows Add Device dialog or re-pairing. The
AudioSwitchersample demonstrates the flow. - Can the Bluetooth Framework turn a PC into a Bluetooth speaker?
- Yes. The
wclBluetoothAudioReceiverclass allows a PC to act as an A2DP audio receiver, accepting incoming audio streams from paired devices.