Skip to content

Using BlueALSA with other ALSA plugins

borine edited this page Aug 16, 2022 · 9 revisions

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.

Plugins that cannot be used with BlueALSA

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

Plugin types that are not relevant to BlueALSA

  • null
    The null plugin type discards all playback samples, and generates a stream of null samples for capture.
  • mmap_emul
    The mmap_emul plugin coverts the access mode from RW to MMAP.

BlueALSA supports both MMAP and RW access modes, so mmap_emul is not necessary.

Plugins that are automatically invoked by plug

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

Useful plugins

plug

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"

empty

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"
}

asym

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.

file

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)"
	}
}

multi

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"
}

softvol

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 of softvol 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.