Using BlueALSA with the JACK Audio Connection Kit - arkq/bluez-alsa GitHub Wiki
Introduction
A BlueALSA PCM can be used with JACK in the same way as any other ALSA PCM, so long as the constraints imposed by Bluetooth Audio are taken into consideration:
-
Bluetooth Audio is high-latency. There is nothing that BlueALSA or JACK can do to change that. So you need to resist the instinct of JACK users and general JACK advice to use very small period sizes and buffers. You will not succeed. At best you will waste CPU cycles and at worst experience constant underruns/overruns. Similarly, there is generally no need to run the jackd service with real-time priority unless that is also required by the jack client (for example zita-j2a and zita-a2j require real-time priority), it will not reduce latency.
-
BlueALSA PCMs are transient. They can connect and disconnect at random intervals. JACK clients and servers always assume that PCMs are permanent; there is no provision in the JACK model for removal of a sound device while in use.
jackd
,alsa_out
and other clients will fail if playing to a BlueALSA PCM that disconnects. -
A2DP transports may become "idle". The source may keep the transport open but not send any audio frames. Jack source clients such as
zita-a2j
andalsa_in
will generate errors and possibly fail completely when this happens, so it is important to ensure the audio stream is running before starting the Jack source client, and that the stream continues to run as long as the Jack source is running. This issue applies only to BlueALSA capture devices.
There have been a number of bluez-alsa issues raised in respect of using BlueALSA with JACK. Athough those issues, and the recommendations and workarounds mentioned in them, were valid when written, there have been many improvements to both BlueALSA and JACK recently. So much so that by using the latest bluez-alsa release and recent JACK releases it now quite straightforward to use them together for A2DP playback (within the constraints described above). A2DP capture is also possible, but needs a little more care.
BlueALSA can be used without any need to create ~/.asoundrc
entries, using
either zita-j2a
or alsa_out
, and can even be used as the backend device
of a jackd
service although that is not recommended.
Playback
For audio output on a BlueALSA host running with the a2dp-source
profile,
start the jack client when the Bluetooth device has connected. Here are some
examples:
zita-j2a
Possibly the simplest example is to create a jack sink using the bluealsa
default PCM:
zita-j2a -j bluealsa -d bluealsa -p 1024 -n 3 -c 2 -L
The command line arguments are as follows:
-j bluealsa
Use the name "bluealsa" for the sink. This is the name that jack
clients use to send audio to the sink.
-d bluealsa
Use the alsa device "bluealsa". This device name is predefined by
the bluez-alsa installation, and used in this way will select the most
recently connected A2DP playback device.
-p 1024
Use an ALSA period size of 1024 frames.
-n 3
Use an ALSA buffer of 3 periods. A smaller buffer will almost certainly
result in constant underruns.
-c 2
Use 2 channels. `zita-j2a` will create 2 jack ports, called
`bluealsa:playback_1` and `bluealsa:playback_2`.
-L
Use S16_LE samples. Without this option `zita-j2a` may convert to
32-bit samples only for the ALSA `plug` plugin to convert them back
to 16-bit.
To use a specific bluetooth device rather than the default, specify its MAC address in the device name, and give it a unique jack name:
zita-j2a -j bt_headphones -d bluealsa:XX:XX:XX:XX:XX:XX -p 4096 -n 3 -c 2 -L
We can now play to the headphones from any jack client. For example:
mpg123 -o jack -a bt_headphones:playback_1,bt_headphones:playback_2 MyFavouriteMusic.mp3
or
JACK_PLAY_CONNECT_TO=bt_headphones:playback_%d jack-play test_sounds.wav
alsa_out
The command line arguments for alsa_out
are the same as for zita-j2a
except
that -L
is not supported. For example:
alsa_out -j bt_headphones -d bluealsa:XX:XX:XX:XX:XX:XX -c 2 -p 4096 -n 3
There is still a bug in alsa_out
that causes a buffer overflow if the device
name is too long. It appears the longest device name allowed is 29 characters.
Fortunately the form used here is only 26 characters.
jackd
BlueALSA can also be used as a backend for a jackd service
jackd -r -d alsa -P bluealsa -n 3 -S -o 2
-r
Do not request real-time scheduling (optional - will also work without
this)
-d alsa
Use the ALSA backend
-P bluealsa
Use the most recently connected BlueALSA playback device. You can also
choose a specific device with `-P bluealsa:XX:XX:XX:XX:XX:XX`
-n 3
Use an ALSA buffer of 3 periods. A smaller buffer is sure to produce
underruns.
-o 2
Create 2 output channels
-S
Use S16_LE sample format
Clients can then send to the BlueALSA device with
JACK_PLAY_CONNECT_TO=system:playback_%d jack-play test_sounds.wav
Capture
When using BlueALSA with the profile a2dp-sink
to input audio, for example
from a mobile phone, it is important that the jack client must not be started
until after the audio stream has been started. The client should be stopped
when the remote device stops the audio stream (it will most likely fail anyway
if not explicitly stopped). With the latest BlueALSA sources, the bluealsa
plugin can be used with hw compatibility mode "silence" to artificially keep
the application stream running even when the remote device is not sending audio
so that the jack client can start as soon as the remote device connects; or
alternatively a service such as bluealsa-agent
from the
bluealsa-autoconfig
project can be used to automatically start and stop the jack client when the
remote device starts or stops the stream.
Some examples using the hwcompat
method:
zita-a2j
zita-a2j -j bluealsa -d bluealsa:HWCOMPAT=silence -p 1024 -n 3 -c 2 -L
alsa-in
alsa_in -j bt_headphones -d bluealsa:HWCOMPAT=silence -c 2 -p 4096 -n 3
.asoundrc
It is possible to remove the ALSA plug
plugin from the audio processing
chain, and instead rely on JACK's own audio processing features. To do this we
have to define our PCMs in our ~/.asoundrc file. For example:
pcm.bt-headphones {
type bluealsa
device XX:XX:XX:XX:XX:XX
profile a2dp
}
ctl.bt-headphones {
type bluealsa
}
Remember here the limit of 29 characters for the PCM name when using
alsa_out
With ALSA plug
removed, we no longer need to force a sample format, so the
-L
or -S
argument is no longer needed.
Then we can use this with JACK as:
zita-j2a -j bt-headphones -d bt-headphones -p 4096 -n 3 -c 2
or
jackd -r -d alsa -P bt-headphones -n 3 -o 2
Optimizing latency
Note that on some devices the hardware interrupts from the Bluetooth controller can interfere with the timeliness of the handlers for interrupts from the soundcard. This can make it impossible to run the jackd server with very small period sizes. The simplest solution is to increase the jackd period size, for example on a Raspberry Pi Zero W (original model, single core Arm V6 at 1GHz), I can use
zita-a2j -j bluealsa -d bluealsa -c 2 -r 44100 -p 1024 -n 3
with
jackd -d alsa -P hw -o2 -n3 -S -p512
and that give very good results. However, using the same zita-a2j
command
with
jackd -d alsa -P hw -o2 -n3 -S -p64
gives broken sound with a stream of errors from jackd
.
Alternatively, if using a modern multi-core processor, it is possible to direct
the interrupts to different cores such that the handlers can run concurrently.
For example set the Bluetooth SMP IRQ affinity to one core, and use taskset
to limit jackd
and zita-a2j
to the other cores. Consult your distribution
documentation for advice on how to achieve this.