-
Notifications
You must be signed in to change notification settings - Fork 189
Using BlueALSA with other ALSA plugins
The purpose of this article is to illustrate how the BlueALSA PCM plugin type can be used with the various plugin types built-in to alsa-lib when writing ALSA configurations. Some of the ALSA plugin types are designed specifically for use with sound cards and will not work with any other I/O device type such as BlueALSA; some others are not relevant because they do not perform any useful function in combination with BlueALSA. However many of them can be used to good effect with BlueALSA devices.
It is recommended to read the BlueALSA plugins manual page and the ALSA PCM plugins documentation before proceeding with the examples here. If you are unfamiliar with the ALSA configuration syntax, it is defined here: ALSA Configuration files.
The following plugin types are designed specifically for use with sound cards and cannot be used with any I/O device type except hw
.
- dmix
- dshare
- dsnoop
-
null
Thenull
plugin type discards all playback samples, and generates a stream of null samples for capture. -
mmap_emul
Themmap_emul
plugin coverts the access mode from RW to MMAP.
BlueALSA supports both MMAP and RW access modes, so mmap_emul
is not necessary.
The following are format conversion or channel mapping plugins, and they are invoked as needed automatically by the plug
plugin, so it is rare to need to use any of them explicitly with BlueALSA.
- adpcm
- alaw
- copy
- iec958
- linear
- lfloat
- mulaw
- rate
- route
The plug
plugin type performs automatic conversion of audio parameters where required if the "slave" device is not capable of handling the stream parameters used by the client. It does so by inserting one or more of the above conversion plugins into the audio processing chain. The BlueALSA pre-defined PCM, bluealsa
, includes plug
within its definition, but if you define your own PCM of type bluealsa
then the plug
must be added explictly if required. For example, to define a PCM called bt-headset
that automatically converts an audio stream to the correct parameters then plays it to the Bluetooth A2DP device at address 11:22:33:44:55:66, we can use either of the following equivalent forms:
pcm.bt-headset {
type plug
slave.pcm {
type bluealsa
device "11:22:33:44:55:66"
profile "a2dp"
}
}
or
pcm.bt-headset "bluealsa:DEV=11:22:33:44:55:66,PROFILE=a2dp"
The empty
plugin type merely passes all API calls through to its slave. Its main use is to permit binding of arguments, and creation of a new hint description. So for example, to create a BlueALSA PCM specifically for one particular Bluetooth device, say 00:11:22:33:44:55:66, we could use:
pcm.speaker1 {
type empty
slave.pcm "bluealsa:DEV=00:11:22:33:44:55:66"
hint.description "Bluetooth Speaker Number One"
}
The asym
plugin type allows to use different devices for capture and playback, accessed by the same PCM id. BlueALSA can be used as capture slave, playback slave, or both. In the following example, playback streams are directed to the default BlueALSA device, and capture streams are taken from the built-in system default source (for example an on-board microphone).
pcm.asym_demo {
type asym
playback.pcm "bluealsa"
capture.pcm "sysdefault"
hint.description "Playback to Bluetooth, capture from local microphone"
}
Please note that the asym plugin requires both slaves to be available; if no BlueALSA device is connected then it is not possible to use this example PCM to capture even though sysdefault
is still available.
The file
plugin type copies the stream to a file, fifo, or command pipeline. BlueALSA can be used as slave. For example, to save a stream as a wav file while playing or capturing from the default BlueALSA device:
pcm.file_demo {
type file
slave.pcm "bluealsa"
file "file-plugin-demo-%r:%c:%f.wav"
format "wav"
hint.description "Copy bluetooth audio stream to file"
}
The audio parameters of the audio recorded by the file
plugin (i.e. rate, channels, sample format) are as passed from/to the client. The above example has the file
instance called directly by the application, so the recording will have the audio parameters of the application. If we wish to record with the parameters used by the Bluetooth stream we must place the plug
plugin as client of the file
plugin, and remove plug
from the slave of the file
plugin (remember that the bluealsa
pre-defined PCM has plug
included in its definition). So we cannot use the pre-defined bluealsa
PCM, and have to define our own slave PCM of type "bluealsa". To specify the "default" BlueALSA device we use the special address 00:00:00:00:00:00
.
pcm.file_demo2 {
type plug
slave.pcm {
type file
file "file-plugin-demo-%r:%c:%f.wav"
format "wav"
slave.pcm {
type bluealsa
device "00:00:00:00:00:00"
profile "a2dp"
}
hint.description "Copy Bluetooth audio stream to file (Bluetooth audio parameters)"
}
}
The multi
plugin type permits channels from a single client to be split between multiple slaves. So for example it can be used to play from a single application to two Bluetooth headsets at the same time, or to a Bluetooth speaker and wired speakers at the same time.
When using a BlueALSA device in conjunction with a hardware device, the BlueALSA device must be the first slave (or "master slave") because otherwise the BlueALSA event handling code will not be called correctly, causing failure of the multi
. This means that other plugins (in particular dmix
) which also require to be "master slave" cannot be used with BlueALSA within a multi
plugin device. Note that with many sound cards, the ALSA definition of default
and sysdefault
PCMs includes dmix
, making them also incompatible with BlueALSA in a multi
.
Because a BlueALSA PCM cannot be used by more than one process at a time, this means that the same restriction applies to any multi
that includes a BlueALSA PCM. So the lack of compatibility with dmix
is not as restrictive as it may at first appear.
Note that multi
requires all its "slave" devices to be available, so if the BlueALSA device disconnects then the whole of the multi
will fail.
An example of using the multi
plugin to play audio to two Bluetooth headsets simultaneously:
pcm.bluealsa-multi {
type plug
slave.pcm {
type multi
slaves {
a {
pcm "bluealsa:11:22:33:44:55:66"
channels 2
}
b {
pcm "bluealsa:AA:BB:CC:DD:EE:FF"
channels 2
}
}
bindings [
{ slave a channel 0 }
{ slave a channel 1 }
{ slave b channel 0 }
{ slave b channel 1 }
]
}
ttable {
0.0 1.0
1.1 1.0
0.2 1.0
1.3 1.0
}
hint.description "Two Bluetooth Devices Together|IOIDOutput"
}
When sending to a BlueALSA device in combination with a sound card device we need to use BlueALSA as the first "slave". There is likely to be an uncomfortable "echo" effect due to the large latency in Bluetooth audio compared to a local sound card, but we can compensate for this by applying extra delay to the sound card PCM. For this we use the ALSA upmix
plugin, which is included in the alsa-plugins
package by most distributions. upmix
adds a delay to channels 2 and 3 (ie rear left and rear right) when upmixing from a stereo source to a 4-channel sink, so we need to re-purpose those channels of the multi
for the hardware device, and use channels 0 and 1 for the BlueALSA device. So for example to apply a delay of 200ms:
# Example PCM plays to both BlueALSA device 11:22:33:44:55:66 and the
# first sound card, with a delay of 200 milliseconds to the sound card.
pcm.bt+hw {
type plug
slave {
# tell "plug" to convert any mono source to stereo before passing to
# upmix. This guarantees that upmix will apply the delay
channels 2
pcm {
type upmix
channels 4
delay 200
slave.pcm {
type multi
slaves {
bt {
pcm "bluealsa:11:22:33:44:55:66"
channels 2
}
hw {
# This device must not use "dmix"
pcm "plughw:0,0"
channels 2
}
}
bindings [
{ slave bt channel 0 }
{ slave bt channel 1 }
{ slave hw channel 0 }
{ slave hw channel 1 }
]
}
}
}
hint.description "Bluetooth and Wired Speakers Together|IOIDOutput"
}
The softvol
plugin type creates a user volume control for its "slave" PCM; the control is associated with a sound card and appears in the mixer for that sound card. BlueALSA creates its own mixer device with controls for all BlueALSA PCMs, so it is not necessary to use the ALSA softvol in this way. However, there may be some circumstances in which it is desirable to have a BlueALSA control appear in the same mixer as the sound card controls, in which case these examples may be helpful. Note that once created, the control will persist, it will not be removed when the BlueALSA device disconnects.
The ALSA softvol control is distinct from, and not connected to, the BlueALSA volume controls. The volume scaling is applied by alsa-lib within the application before the stream is sent to BlueALSA. Any volume scaling then applied by BlueALSA will be in addition to the scaling already applied by ALSA softvol.
To be useful with BlueALSA the control name must not already exist on the card, otherwise it will control the card and not the BlueALSA PCM. This example creates a volume scale control called "BlueALSA" that controls the default BlueALSA device. The control appears in the mixer for card 0.
Warning user controls are created in kernel memory and are not removed from memory if the
softvol
definition is removed from the ALSA configuration. Depending on your distribution they will most likely persist even across reboots. If you are using an ALSA release that is older than 1.2.5 you should consult your distribution's documentation regarding removal ofsoftvol
controls before trying these examples.
pcm.bluealsa-vol {
type softvol
slave.pcm "bluealsa"
control {
name "BlueALSA Playback Volume"
card 0
}
hint.description "Bluetooth Playback with Softvol|IOIDOutput"
}
To create a "simple" control that has both volume scale and mute switch it is necessary to add them individually to the PCM, one chained as the slave of the other. For example:
pcm.bluealsa-vol {
type softvol
resolution 2
control {
name "BlueALSA Playback Switch"
card 0
}
slave.pcm {
type softvol
slave.pcm "bluealsa"
control {
name "BlueALSA Playback Volume"
card 0
}
}
hint.description "Bluetooth Playback with Softvol|IOIDOutput"
}
To remove user controls, it is not sufficient to delete the definitions from the ALSA configuration as they will still persist in kernel memory.
With alsa-utils
release 1.2.5 and later you can use the ALSA alsactl
utility:
alsactl clean 0 "name='BlueALSA Playback Volume'" "name='BlueALSA Playback Switch'"
For earlier releases you should consult your distribution's documentation.
Many ALSA card configurations include a user control called "PCM" in their definition of the default
device (for example cards using the Intel HDA codec; some other cards, notably USB cards, do not have this control defined). This control can also be used for bluetooth devices, so that the same volume control is used for both default
and BlueALSA devices.
As a simple example, the following creates a PCM called a2dp
which sends audio to the most recently connected bluetooth A2DP playback device and has its volume scaled by a control called PCM
on card 0. This same control also scales the volume of the default
device on card 0 with many ALSA card configurations:
pcm.a2dp {
type softvol
slave.pcm "bluealsa:PROFILE=a2dp"
control.name "PCM Playback Volume"
control.card 0
hint.description "Bluetooth A2DP with PCM volume control"
}
alsa-lib also includes 2 CTL plugin types that may be used with BlueALSA.
The empty
CTL plugin type just redirects the control device to another plugin. As with the "empty" PCM plugin type, its main uses are for binding arguments and adding hint description. Example:
ctl.speaker1 {
type empty
child {
type bluealsa
device "00:11:22:33:44:55:66"
}
hint.description "Bluetooth Speaker Number One"
}
This plugin can create new names for control elements.
So for example to create a mixer for the default BlueALSA PCM in which the "A2DP" playback control is called "Master", we could use:
ctl.bluealsa-default {
type remap
child {
type bluealsa
device "00:00:00:00:00:00"
}
remap {
'name="A2DP Playback Volume"' 'name="Master Playback Volume"'
'name="A2DP Playback Switch"' 'name="Master Playback Switch"'
}
}
Note that the names of the low-level ("primitive") controls must be used, so that to rename the "A2DP" control as "Master" we must rename both the volume component and the switch (mute) component.