BLUEALSA-PLUGINS(7)

πŸ“¦
version 4.3.1 Β· 4 versions
, in alpine 3.24, debian trixie, ubuntu questing

πŸ’– BLUEALSA-PLUGINS(7) β€” the manual page, but make it a group chat πŸ’–

Miscellaneous ✨ okay so this is BLUEALSA-PLUGINS(7), and bestie, it's giving "your Bluetooth devices finally act like they belong here" πŸŽ€


🌈 NAME 🌈

bluealsa-plugins β€” Bluetooth Audio ALSA Plugins πŸ¦‹


✨ DESCRIPTION ✨

Okay so here's the whole vibe: BlueALSA lets applications get at Bluetooth audio devices through the ALSA alsa-lib API. That's it, that's the main character move πŸ’…

And because of that, users of those applications can use Bluetooth speakers, headphones, headsets and hands-free devices pretty much like they were local devices. No cap. Local-device energy, Bluetooth-device reality 🌸

How does this magic happen tho? Two ALSA plugins, periodt:

  • one for PCM audio streams 🎧
  • one for CTL volume controls 🎚️

Two plugins, one iconic integration, and your headset just quietly slays like it was plugged in all along πŸ‘‘


bestie there's more coming, don't go anywhere 🫢

πŸ’– PCM PLUGIN πŸ’–

okay so bestie, the BlueALSA ALSA PCM plugin? it talks to the bluealsa(8) service. that's the whole vibe. you can use it to define ALSA PCMs in your very own config file (like ~/.asoundrc, slay), or you can just use the predefined bluealsa PCM that's already sitting there waiting for you like the icon it is. 🫢

🌸 The Predefined bluealsa PCM 🌸

the easiest way in, fr fr, is the predefined ALSA PCM device called bluealsa. its definition is of type plug, which means audio format conversion (if it's even needed) happens automatically by the PCM. no stress, no manual labour. πŸ’…

it has these parameters: DEV, PROFILE, CODEC, VOL, SOFTVOL, DELAY, and SRV. and guess what β€” all of them have defaults. all of them. every single one. ✨

you specify parameter values in an ALSA PCM name using this syntax:

bluealsa:DEV=01:23:45:67:89:AB,PROFILE=a2dp,CODEC=aac,VOL=60,SOFTVOL=no,DELAY=0,SRV=org.bluealsa

πŸŽ€ PCM Parameters πŸŽ€

  • DEV β€” the device Bluetooth address, written as XX:XX:XX:XX:XX:XX. heads up: device names or aliases are not valid here, only the raw address, no cap. the default is 00:00:00:00:00:00, and that magic value selects the most recently connected device of whichever profile you picked. so it's like "idk just gimme the last one" energy. 😌
  • PROFILE β€” either a2dp or sco. if you say sco, it selects either the Hands-Free (HFP) or Headset (HSP) profile, whichever one is actually connected on the selected device. it doesn't care which, it just takes what's there. the default is a2dp. 🎧
  • CODEC β€” specifies the codec used by the profile. here's the tea: when a connection gets established between a device and a host, BlueALSA negotiates the best available codec with the device. this parameter lets the ALSA configuration override that selection, main character behaviour honestly. the default value is unchanged, which causes the PCM to use its existing codec setting. codec names are case insensitive β€” so aptX, aptx, and APTX are all accepted, no need to be precious about it. and if the codec you asked for isn't available? the plugin issues a warning and uses the default value instead. polite. 🫠
lowkey important: BlueALSA does not support changing the HFP codec from a HFP-HF node. only the HFP-AG node can change the HFP codec. that's just the rules bestie.
also: oFono does not permit the audio agent to select the codec, so this parameter has no effect when BlueALSA is used with oFono for HFP support. πŸ™ˆ

for the A2DP profile you can also specify a "configuration" for the codec, by appending the configuration as a hex string separated from the codec name by a colon. the bits responsible for the number of channels and the sampling frequency are set by the plugin with respect to options provided by the user (the channel mode and sampling frequency bits act as a mask). like so:

CODEC=SBC:FC450240

this SBC configuration limits the channel mode options to mono and dual channel. so, in the case of a 2 channel audio stream, the plugin will negotiate the dual channel mode instead of the default (if supported) joint stereo mode. she adapts. πŸ’…

  • VOL β€” specifies the initial volume for the PCM when it's opened. default value is unchanged, which makes the PCM use its existing volume setting. the value is an integer percentage of the maximum volume, [0-100]. you can also set the mute status by appending - to mute the sound or + to unmute it. warning: the volume is not restored to its original value when the PCM is closed. it does not go back. it simply does not. 😭

for example, to set the initial volume to 80% and ensure that mute is disabled for this PCM:

VOL=80+
  • SOFTVOL β€” enables or disables BlueALSA's software volume feature for this PCM. see the bluealsa(8) manual page for more info on software volume, iykyk. this is a boolean option (values on or off), but it also accepts the special value unchanged, which causes the PCM to use its existing softvol value. the default value is unchanged. 🌈
  • DELAY β€” an integer number which is added to the reported delay (latency) value, so you can manually adjust the audio synchronization. it's not normally required and defaults to 0. see the EXT parameter of the CTL plugin in the CTL Parameters section below for a more flexible and convenient method of manually adjusting the reported delay by using a mixer control. fancy. ✨
  • SRV β€” the D-Bus service name of the BlueALSA daemon. defaults to org.bluealsa. see bluealsa(8) for more information. not normally required, so don't stress about it. πŸ’œ

πŸ¦‹ Setting Different Defaults πŸ¦‹

you can override the defaults by defining the ones you want to change in your own configuration (e.g. in ~/.asoundrc.conf), like this:

defaults.bluealsa.device "00:11:22:33:44:55"
defaults.bluealsa.profile "sco"
defaults.bluealsa.codec "cvsd"
defaults.bluealsa.volume "50+"
defaults.bluealsa.softvol off
defaults.bluealsa.delay 5000
defaults.bluealsa.service "org.bluealsa.source"

note that volume takes a string value, so the default must be enclosed in quotation marks. she's a string girlie, respect it. πŸŽ€


πŸ’… Positional Parameters πŸ’…

ALSA permits arguments to be given as positional parameters as an alternative to explicitly naming them. the catch? when you use positional parameters it is important that the values are given in the correct sequence:

  1. DEV
  2. PROFILE
  3. CODEC
  4. VOL
  5. SOFTVOL
  6. DELAY
  7. SRV

for example:

bluealsa:01:23:45:67:89:AB,a2dp,unchanged,unchanged,unchanged,0,org.bluealsa

when using positional parameters, defaults can only be implied at the end of the id string. so this:

bluealsa:01:23:45:67:89:AB

is equivalent to the full form above. but this:

bluealsa:01:23:45:67:89:AB,a2dp,,80+

is not permitted. nope. no gaps in the middle, bestie, that's just how it is. 😀


πŸ‘‘ Defining BlueALSA PCMs πŸ‘‘

you can define your own ALSA PCM in the ALSA configuration. to do this, create an ALSA configuration node defining a PCM with type bluealsa. the configuration node has the following fields:

pcm.name {
  type bluealsa     # Bluetooth PCM
  device STR        # Device address in format XX:XX:XX:XX:XX:XX
  profile STR       # Profile type (a2dp or sco)
  [codec STR]       # Preferred codec
  [volume STR]      # Initial volume for this PCM
  [softvol BOOLEAN] # Enable/disable BlueALSA's software volume
  [delay INT]       # Extra delay (frames) to be reported (default 0)
  [service STR]     # DBus name of service (default org.bluealsa)
}

the device and profile fields must be specified, so the plugin can select the correct Bluetooth transport. the other fields are optional. 🫢

real talk: the default values for the optional fields are not overridden automatically by the configuration defaults.bluealsa.* in a PCM defined this way. however, the configuration defaults can be referenced by use of @func refer (see the ALSA configuration file syntax documentation for more information). so you can still get there, you just have to ask nicely. ✨

when choosing a name for your PCM definition β€” the name pcm.bluealsa is predefined by the bluez-alsa installation (see section The Predefined bluealsa PCM above), so it should not be used as a name for your own PCM devices, because doing so will most likely have unexpected or undesirable results. don't do it. she's taken. πŸ’€

note that the volume field is of type string, so the value must be enclosed in double-quotes. see the PCM Parameters section above for more information on each field.

and do not confuse the PCM type bluealsa with the PCM named bluealsa. they are two different girlies:

  • the type does not perform any audio conversions. you will have to wrap your own defined PCMs with type plug to achieve that.
  • whereas the predefined PCM pcm.bluealsa is of type plug.

periodt. πŸ’…


⭐ Name Hints ⭐

applications that follow ALSA guidelines will obtain the list of defined PCMs by using the alsa-lib namehints API. to make BlueALSA PCMs visible via that API it is necessary to add a "hint" section to the ALSA configuration. if you have defined a new PCM, then the hint goes into the PCM configuration entry, like this:

pcm.bt-headphones {
    type plug
    slave.pcm {
        type bluealsa
        device "00:11:22:33:44:55"
        profile "a2dp"
    }
    hint {
        show on
        description "My Bluetooth headphones"
    }
}

now using aplay -L will include the following in its output:

# aplay -L
bt-headphones
    My Bluetooth headphones
#

if you're using the predefined bluealsa PCM, then you can create a "namehint" entry in your ~/.asoundrc file like this:

namehint.pcm {
    mybluealsadevice "bluealsa:DEV=00:11:22:33:44:55,PROFILE=a2dp|My Bluetooth headphones"
}

then aplay -L shows:

# aplay -L
bluealsa:DEV=00:11:22:33:44:55,PROFILE=a2dp
    My Bluetooth headphones
πŸ› bug alert for the old heads: for alsa-lib versions before v1.2.3.2, a bug in the namehint parser means that a namehint.pcm entry has to be written as
namehint.pcm {
    mybluealsadevice "bluealsa:DEV=00:11:22:33:44:55,PROFILE=a2dp|DESCMy Bluetooth headphones"
}

(note the keyword DESC after the pipe symbol and before the description text.)

with that hint in place, the PCM will be listed as both a Capture and Playback device. so arecord -L will also list it. that's generally OK for HFP/HSP devices, but an A2DP device most often offers only Capture (e.g. a mobile phone) or only Playback (e.g. a Bluetooth speaker). it is possible to use the hint description to limit the listing to only one direction using an undocumented syntax of ALSA configuration files. πŸ‘€

if the hint.description value ends with |IOIDInput the PCM will only show in listings of Capture devices; if it ends with |IOIDOutput the PCM will only show in listings of Playback devices.

so we can modify our example above to:

pcm.bt-headphones {
    type plug
    slave.pcm {
        type bluealsa
        device "00:11:22:33:44:55"
        profile "a2dp"
    }
    hint {
        show on
        description "My Bluetooth headphones|IOIDOutput"
    }
}

or:

namehint.pcm {
    mybluealsadevice "bluealsa:DEV=00:11:22:33:44:55,PROFILE=a2dp|My Bluetooth headphones|IOIDOutput"
}

now the aplay -L output will be exactly the same as before, but arecord -L will not include bt-headphones in its output. she knows her lane. πŸ’…

when using the namehint.pcm method, the key (mybluealsadevice in the above example) must be unique but otherwise is not used. the first part of the value string, before the pipe | symbol, is the string that is to be passed to ALSA applications to identify the PCM (e.g. with aplay -D ...). the next section, after the pipe symbol, is the description that will be presented to the user. the optional |IOID section is not included in the description given to the application. iykyk. ✨

πŸ’… CTL PLUGIN πŸ’…

okay bestie so the BlueALSA ALSA CTL plugin lets you define your own ALSA CTLs (those are mixer devices, iykyk) right in your own config file, like ~/.asoundrc, OR you can just use the predefined config that ships with the bluez-alsa project. easy.

important lil thing: a BlueALSA CTL device has no associated soundcard, which means alsamixer will NOT list it in its F6 menu. rude, I know. so you gotta pick it one of two ways:

  • start alsamixer like this:
alsamixer -D bluealsa
  • OR go to the F6 menu, select "enter device name ..", then type bluealsa into the "Device Name" box. ✨

The CTL has two operating modes: Default mode and Single Device mode. let's get into it πŸ’–


πŸŽ€ Default Mode πŸŽ€

In this mode, when a device connects, the mixer creates new controls for it, and when a device disconnects, the mixer removes its controls. alsamixer(1) will show these changes dynamically. it's giving live updates, we love that for us.

Control names are constructed by combining the device Bluetooth alias with either the profile type ('A2DP' or 'SCO') of the controlled PCM, or the word "Battery" for battery level indicators. If two or more connected devices have the same alias, then an index number gets added to the name to make it unique. πŸ’…

The Bluetooth "alias" of a device is by default the same as its "name". The name is a string defined by the device manufacturer and embedded in its firmware. Typically two identical devices will have identical names. The "alias" is created by BlueZ and stored locally on the host computer. So the alias can be changed using a tool such as bluetoothctl(1) to make it unique if desired. As manufacturers tend to use long names for their devices, the alias can also be useful to give a short "nickname" to a device. cute!

Although this default mode works well with alsamixer, there are some limitations that may make it unsuitable for some applications. In particular:

  • If device aliases are not unique, then the index number associated with each is not easily predictable in advance; so it can be difficult to programmatically associate a PCM with its volume control. 😭
  • A consequence of the alsa-lib implementation of controls is that when one Bluetooth device connects or disconnects, it is necessary to remove all controls from all devices in the mixer and create a new set. This invalidates pointers held by applications and can cause application crashes. (Hardware sound cards do not have randomly appearing and disappearing controls, so many, or even most, applications are not programmed correctly to deal with it.) πŸ’€

πŸ¦‹ Single Device Mode πŸ¦‹

The BlueALSA CTL also implements an alternative mode that presents controls only for one specified device. In this case the control names are simply the profile type of the controlled PCM ('A2DP' or 'SCO') or the word "Battery". There is never any need for index suffixes or device alias. Immediately this overcomes the two main issues of the default mode. slay. πŸ‘‘

Single device mode is achieved by including the device Bluetooth address as an argument to the ALSA device id, for example:

alsamixer -D bluealsa:00:11:22:33:44:55

A notable difference between single-device mode and the default mode is in the cases of the device not being connected when the mixer is opened, and when the device disconnects while the mixer is open. πŸ‘€

For the default mode, the mixer will still open, even if no devices are connected, but will display no controls. In single device mode, the open request will fail with an error message. periodt.

Similarly, in default mode when a device disconnects, the mixer remains open but removes the set of controls and creates a new control set without the disconnected device. That new set will be empty if no devices remain. If the device then re-connects, the mixer will again create a new set of controls with the newly connected device included. πŸ”„

In single device mode, when its device disconnects, then the mixer will close. The alsamixer application will continue running with no associated device or controls, but will not automatically re-open the mixer if the device re-connects. The user can use F6 to open a new device. 🫢

As a special case, a single device mixer can be opened with the address 00:00:00:00:00:00. This will create a mixer with controls for the most recently connected device at the time the mixer is opened. Once created, that mixer behaves the same as if it had been opened with the actual address of the device: it does not change to a new device if another is subsequently connected. no cap. 🌈


πŸ‘‘ The Predefined bluealsa CTL πŸ‘‘

The bluealsa CTL has parameters DEV, EXT, BTT, DYN, and SRV. All the parameters have defaults. yes, all of them. πŸŽ€

✨ CTL Parameters ✨

  • DEV β€” The device Bluetooth address in the form XX:XX:XX:XX:XX:XX. Device names or aliases are not valid here. The default value is FF:FF:FF:FF:FF:FF, which selects controls from all connected devices (see Default Mode above). Also accepts the special address 00:00:00:00:00:00, which selects the most recently connected device. πŸ“
  • EXT β€” Causes the plugin to include extra controls. These are the controls for Bluetooth codec selection, volume mode selection, delay adjustment (sync) and/or battery level indicator. If the value is yes, then all of these additional controls are included; if the value is no, then none of them are included. The default is no.

This parameter can also select individual controls by using a colon (':') separated list of control names. The control names are codec, mode, sync and battery. For example:

EXT=codec
EXT=mode:battery

See Codec switching in the NOTES section below for more information on the codec selection control. πŸ”₯

The volume mode controls take values "software" and "pass-through"; the playback control has index 0 and capture control has index 1. See the Volume control section in the bluealsa(8) for more information on the software volume setting.

The delay adjustment controls are called "Sync". They can be used to apply a fixed adjustment to the delay reported by the associated PCM to the application, and may be useful with applications that need to synchronize the bluetooth audio stream with some other stream, such as a video. The values are in milliseconds from -3275 ms to +3275 ms in steps of 25 ms. The playback control has index 0 and the capture control has index 1. Each codec supported by a PCM has its own delay adjustment value. Note that this control changes only the delay value reported to the application by ALSA β€” it does not affect the actual delay (latency) of the PCM stream. Values set by this control type are saved in the BlueALSA persistent state files, and so are remembered and automatically applied each time the PCM is used. πŸ’Ύβœ¨

The read-only battery level indicator will be shown only if the device supports battery level reporting. πŸ”‹

  • BTT β€” Appends Bluetooth transport type (e.g. "-SNK" or "-HFP-AG") to the control element names. When using with the Default Mode, this will reduce the number of available characters for the Bluetooth device name, so the default value is no.

In some rare circumstances, when more than one A2DP or HFP/HSP profile is connected with a single Bluetooth device, it might happen that the control element names for such device will not be unique. This might be problematic for control applications which use the ALSA High Level Control Interface, e.g. amixer or alsamixer. Such applications will report error or simply crash. πŸ’€ This can be avoided by setting the BTT parameter to yes.

  • DYN β€” Enables "dynamic" operation. The plugin will add and remove controls as profiles are connected or disconnected. This is the normal behavior, so the default value is "yes". This argument is ignored in default mode; in that mode operation is always dynamic. There are some applications that are not programmed to handle dynamic addition or removal of controls, and can fail when such events occur. Setting this argument to no in single device mode with such applications can protect them from such failures. When dynamic operation is disabled, the plugin never adds or removes any controls. If a single profile is disconnected, then its associated volume control is put into an inactive state, i.e.: read-only with its value and playback/capture switch set to 0. πŸ™ˆ
  • SRV β€” The D-Bus service name of the BlueALSA daemon. Defaults to org.bluealsa. See bluealsa(8) for more information. πŸ¦‹

The default values can be overridden in the ALSA configuration, for example:

defaults.bluealsa.ctl.device "00:11:22:33:44:55"
defaults.bluealsa.ctl.bttransport "no"
defaults.bluealsa.ctl.dynamic "yes"
defaults.bluealsa.ctl.extended "no"

🌸 Defining BlueALSA CTLs 🌸

You can define your own ALSA CTL in the ALSA configuration. To do this, create an ALSA configuration node defining a CTL with type bluealsa. The configuration node has the following fields:

ctl.name {
  type bluealsa     # Bluetooth PCM
  [device STR]      # Device address (default "FF:FF:FF:FF:FF:FF")
  [extended STR]    # Include additional controls (default no)
  [bttransport STR] # Append BT transport to element names (yes/no, default no)
  [dynamic STR]     # Enable dynamic operation (yes/no, default yes)
  [service STR]     # D-Bus name of service (default "org.bluealsa")
}

All the fields (except type) are optional. See the CTL Parameters section above for more information on each field. As for PCM definitions above, the default values for the optional fields are hard-coded into the plugin; they are not overridden by the configuration defaults.bluealsa. settings. πŸ’–

✨ NOTES ✨

okay bestie, before you go plugging things in, there's some tea you need to know β˜•πŸ’…

🎧 Codec selection

When you're using this on a HFP gateway node, there might be a lil delay with HFP PCMs after connection until the codec gets selected. Like, it's typically less than two seconds. And during that tiny window? You literally can't open the PCM plugin β€” it'll fail with "Resource temporarily unavailable" (EAGAIN). So just chill for a sec, okay? 😭

🎧 Codec switching

Changing the codec on a BlueALSA transport? That makes the PCM(s) running on that transport terminate. So using a Codec control can have some undesirable consequences, bestie. And here's the messy part: the alsamixer(1) UI doesn't have a separate pick-list for enumerated types, so just browsing the list of codecs with this control actually sends a Codec change request every time a different codec is displayed. Not ideal. So using this control type with alsamixer(1)? Not recommended. But it works great with other mixer apps like amixer(1). ✨

Also, heads up: BlueALSA does not support changing the HFP codec from a HFP-HF node. Only the HFP-AG node can change the HFP codec. Periodt. πŸ’…

🎧 Transport acquisition

The audio connection of a profile doesn't just appear the second a device connects. The A2DP source device, or HFP/HSP gateway device, has to first "acquire" the profile transport.

When the BlueALSA PCM plugin is used on a source A2DP or gateway HFP/HSP node, then bluealsa(8) will automatically acquire the transport and start the audio transfer when the plugin starts the PCM. Slay. 🫢

But when it's used on an A2DP sink or HFP/HSP HF/HS node, then bluealsa(8) has to wait for the remote device to acquire the transport. During this waiting time, the PCM plugin acts like the device "clock" is stopped β€” it doesn't generate any poll() events, and your app will be blocked when writing or reading to/from the PCM. For apps playing audio from a file or recording to a file, this is usually fine. But when streaming between some other device and a BlueALSA device? This can lead to very large latency (delay) or trigger underruns or overruns in the other device. Big yikes. 😬

🎧 PCM drain and non-blocking operation

The BlueALSA PCM plugin does not support draining of capture PCMs. For a capture PCM, snd_pcm_drain() has the same effect as snd_pcm_drop(). This is just a limitation of the ALSA ioplug external plugin API. Not us, it's them. πŸ’€

For playback PCMs, BlueALSA does support the drain operation in both blocking and non-blocking modes. In blocking mode, the drain operation will wait until the BlueALSA server has played out the final audio frame. In non-blocking mode, the plugin will tell the application that drain is complete as soon as the ALSA ring buffer has been flushed β€” which means some audio frames at the end of the stream might get lost in non-blocking mode, because the PCM may stop before the server has had time to encode and play out all the frames. So, like, don't be surprised if the last lil bit goes missing. πŸ™ˆ

πŸ“ FILES

  • /etc/alsa/conf.d/20-bluealsa.conf β€” BlueALSA device configuration file. It's ALSA additional configuration, and it defines the bluealsa PCM and CTL devices. πŸŽ€

Copyright (c) 2016-2023 Arkadiusz Bokowy.

The bluez-alsa project is licensed under the terms of the MIT license. πŸ’–

πŸ‘€ SEE ALSO


And that's the whole vibe! This page is from BlueALSA v4.3.1, dated July 2023, and it's the BLUEALSA-PLUGINS(7) manual page. Stay iconic, bestie. πŸ‘‘πŸ’–

πŸ“¦ where this page comes from