Skip to content

PulseAudio integration

borine edited this page Oct 29, 2021 · 34 revisions

Using Bluealsa and PulseAudio Together

PulseAudio (https://www.freedesktop.org/wiki/Software/PulseAudio/) is the de-facto Linux standard audio server, particularly for desktop installations. Many linux distributions' desktops require PulseAudio in order to provide full functionality.

PulseAudio has its own bluetooth audio implementation which is sufficient in many common usage scenarios. If you need to use PulseAudio for any reason then it is recommended to at least try its internal bluetooth audio implementation before attempting this integration. Conversely, if you do not need PulseAudio, then it is recommended to use Bluealsa for bluetooth audio and not to run PulseAudio at all.

PulseAudio does not support the range of bluetooth codecs and other features that are offered by Bluealsa; therefore it is sometimes desirable to have installed both Bluealsa (for all bluetooth audio capabilities) and PulseAudio (for desktop integration, audio stream routing, etc, etc). This wiki article describes one method of using the two services in an integrated way. This approach should be considered as "experimental" and as such suffers many compromises and limitations.

Using these two services in the chain of audio processing results in a high latency. Not so high that it is an issue for music player applications, but high enough that video applications will find it difficult to synchronize sound to video. Therefore this is not a universal solution, but may be useful in some circumstances.

Bluez allows only one service to register as provider of a bluetooth profile, so it is necessary to disable the PulseAudio bluetooth modules in order to use Pulseaudio in combination with Bluealsa.

Preparing PulseAudio for use with Bluealsa

Disable bluetooth modules

There are three ways to disable the PulseAudio bluetooth modules.

  1. Uninstall the module packages

    Many distributions deliver the PulseAudio bluetooth modules as separate packages, and so for these the simplest solution is to uninstall those packages. For example, on Ubuntu

    sudo apt purge pulseaudio-module-bluetooth
    
  2. Remove the modules from the PulseAudio configuration

    If the modules cannot be uninstalled, then they can still be disabled in the PulseAudio global configuration. This is achieved by editing the file /etc/pulse/default.pa to comment out the bluetooth module entries as shown here:

    ### Automatically load driver modules for Bluetooth hardware
    #.ifexists module-bluetooth-policy.so
    #load-module module-bluetooth-policy
    #.endif
    
    #.ifexists module-bluetooth-discover.so
    #load-module module-bluetooth-discover
    #.endif
    

    Restart the PulseAudio service to read the new configuration. On most desktop systems this service is configured to automatically restart, so it is sufficient to type:

    pulseaudio --kill
    
  3. Unload the PulseAudio bluetooth modules at runtime

    It is possible to temporarily remove the modules at runtime with the commands:

    pactl unload-module module-bluetooth-policy
    pactl unload-module module-bluetooth-discover
    

    Note that these commands will have to be re-run each time PulseAudio is restarted.

Configure PulseAudio to suspend on idle

This step is only necessary if you wish to use a device that offers both A2DP and HFP/HSP sinks (or both A2DP and HFP/HSP sources).

Unless configured otherwise, PulseAudio will immediately open and hold each bluealsa PCM device that is loaded. This can cause problems for devices that support both A2DP and SCO PCMs. The only way to keep a PCM loaded but not open is to "suspend" it. However, most PulseAudio GUI tools (including pavucontrol) do not allow the user to manually suspend and unsuspend sinks and sources. The workaround for this is to enable the "suspend-on-idle" feature, with a sufficiently low timeout. Some linux distributions enable this feature, others do not. To check if your PulseAudio instance has this module loaded, type:

pactl list modules

and look for the module-suspend-on-idle entry:

Module #20
	Name: module-suspend-on-idle
	Argument: timeout=5
	Usage counter: n/a
	Properties:
		module.author = "Lennart Poettering"
		module.description = "When a sink/source is idle for too long, suspend it"
		module.version = "11.1"

The Module number, and other parameter values, may differ.

If the module is loaded, check the "Argument:" parameter. We need a low timeout value because there will be silence of that amount of time when switching from A2DP to SCO. If the Argument is blank, the default timeout is 5 seconds.

If the module is not loaded or the timeout needs to be changed, we must edit the file /etc/pulse/default.pa. Add an entry (or edit an existing entry) as:

### Automatically suspend sinks/sources that become idle for too long
load-module module-suspend-on-idle timeout=2

Important Note:

pavucontrol appears to disable this auto suspend feature, even though the module is loaded. This means that it is impossible to switch a stream from A2DP to SCO if pavucontrol is used. The only workaround appears to be not to use pavucontrol

Adding Bluealsa Devices as PulseAudio Sinks / Sources

1. Define alsa pcm for bluealsa with no conversions

It is recommended to allow PulseAudio to perform any necessary audio format conversions internally, and not to use the alsa-lib "plug" or other alsa plugins. This is especially important if you have libasound version 1.1.2 or 1.1.3 which will otherwise cause the PulseAudio daemon to deadlock. A simple way to achieve this is to create a file 21-bluealsa-raw.conf in the appropriate alsa config directory

  • /usr/share/alsa/alsa.conf.d/21-bluealsa-raw.conf ( alsa-lib version <= 1.1.6 )
  • /etc/alsa/conf.d/21-bluealsa-raw.conf ( alsa-lib version >= 1.1.7 )

Put the following into that file:

pcm.bluealsa_raw {
	@args [ DEV PROFILE DELAY SRV ]
	@args.DEV {
		type string
		default {
			@func refer
			name defaults.bluealsa.device
		}
	}
	@args.PROFILE {
		type string
		default {
			@func refer
			name defaults.bluealsa.profile
		}
	}
	@args.DELAY {
		type integer
		default {
			@func refer
			name defaults.bluealsa.delay
		}
	}
	@args.SRV {
		type string
		default {
			@func refer
			name defaults.bluealsa.service
		}
	}
	type bluealsa
	device $DEV
	profile $PROFILE
	delay $DELAY
	service $SRV
}

2. Create sink and / or source objects

Each bluetooth device that you wish to use with PulseAudio needs to be added individually as a sink, source, or both as appropriate. If you wish to use both A2DP and SCO profiles for a single device, then they too will need to be added individually.

The way to add a device is to load the module-alsa-sink or module-alsa-source modules with appropriate parameters. In order to later remove a device, it is helpful to make a note of the module index allocated to it. The utility pactl returns that index on successful completion. The complete list of parameters for these modules can be seen here: https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Modules/

It is necessary to explicitly set the "fragments" and "fragment_size" module parameters because the defaults used by module-alsa-sink and module-alsa-source can cause PulseAudio to become unresponsive for many seconds at a time. I recommend to try fragments=1 and fragment_size=960 as a first guess, and experiment from there.

It is helpful to give the device a user-friendly description for use in the description displayed in graphical tools such as pavucontrol or other desktop sound control applications/widgets, by using the sink_properties or source_properties parameter as appropriate.

It is also possible to set the display icon in the source_properties parameter - it will default to "audio-card" but you may prefer to use "audio-speakers-bluetooth", "audio-headphones-bluetooth", "audio-headset-bluetooth", "phone-bluetooth", or maybe just "bluetooth". The icons actually available will depend on your desktop distribution.

The device must first be connected before adding to PulseAudio.

For example, to load a playback (sink) device, set its internal name (used by command line tools such as pactl and pacmd), set its user-friendly description and icon, and record its allocated module index, type:

MODULE=$(pactl load-module module-alsa-sink device='bluealsa_raw:DEV=XX:XX:XX:XX:XX:XX,PROFILE=a2dp' fragments='1' fragment_size='960' sink_name=MyFriendlyName sink_properties="device.description='My\ Friendly\ Description'device.icon_name=audio-speakers-bluetooth")

(Note the use of "bluealsa_raw" as defined above in the device field, and the need to escape space characters with a backslash in the description field. Do not leave any space between property settings)

To remove the device from PulseAudio, type

pactl unload-module $MODULE

The procedure for a capture (source) device is the same, except to use 'source' in place of 'sink' in the commands:

MODULE=$(pactl load-module module-alsa-source device='bluealsa_raw:DEV=XX:XX:XX:XX:XX:XX,PROFILE=a2dp' fragments='1' fragment_size='960' source_name=MyFriendlyName source_properties="device.description='My\ Friendly\ Description'device.icon_name=phone-bluetooth")

Automating The Addition Of Bluealsa Devices to PulseAudio

1. Create D-Bus service

It is possible to automate the above procedure by running a service that listens for D-Bus PCMAdded and PCMRemoved events from bluealsa and invokes the above commands in response.

As a simple example to demonstrate how that might be done, we provide a bash script for download:

bluepulse.bash

The script should be run as a normal user, not as root.

The script depends on GNU Awk (gawk) to parse the dbus messages.

Note that when switching a stream from an A2DP sink to a SCO sink on the same device, there will be several seconds of silence before the stream resumes, because SCO playback will only commence when the A2DP device is suspended by PulseAudio.

2. Run the D-Bus service whenever PulseAudio is running

If you are running PulseAudio within your desktop session and using systemd, then the above script (or your own improved service) can be launched whenever you log in to the desktop by enabling it as a systemd user service.

For example, if you install the above script as /usr/local/bin/bluepulse.bash, save the following systemd service file as:

/usr/local/lib/systemd/user/bluepulse.service

[Unit]
Description=Bluealsa PulseAudio Integration
BindsTo=pulseaudio.service
After=pulseaudio.service

[Service]
Type=simple
ExecStart=/usr/local/bin/bluepulse.bash
RestartSec=2
Restart=on-failure

[Install]
WantedBy=pulseaudio.service

Each user that wishes to use this service can then enable it to start at login with:

systemctl --user enable bluepulse.service

Systemd will now start the bluepulse service for that user whenever pulseaudio is started, and stop it when pulseaudio stops.