A simple Bluetooth Speaker Daemon designed for the Raspberry Pi 3.
BT-Speaker aims to just work on a vanilla installation using pure ALSA.
Quick Installation for Raspberry Pi OS:
sudo -i
bash <(curl -s https://raw.githubusercontent.com/lukasjapan/bt-speaker/master/install.sh)
For details refer to the comments in the install script.
Depending on your application, you might also want to send all audio to the headphone jack.
This can be done by raspi-config
:
Advanced Options
-> Audio
-> Force 3.5mm ('headphone') jack
Note: Bt-speaker has been made with the default Raspberry Pi OS audio configuration in mind. If you are using external sound cards or have installed a sound daemon (like PulseAudio or Jack) you might need to adjust the config file accordingly.
Having a sound daemon installed might even break things. So please keep in mind that BT-Speaker was made to avoid a heavy audio stack and will not officially support such setup.
The BT-Speaker daemon does not behave like a typical bluetooth device. Once a client disconnects, the speaker will immediately allow other clients to connect. This means that the quickest device may claim the speaker and no real bluetooth pairing occurs. The bright side of this logic is that no button for unpairing is needed.
The speakers name will default to the hostname of your Raspberry Pi. BT-Speaker does not manage this value. You are advised to change the hostname according to your needs.
The default settings of BT-Speaker will be copied and can be overridden in /etc/bt_speaker/config.ini
.
Section | Key | Default Value | Description |
---|---|---|---|
bt_speaker | play_command | aplay -f cd - | The raw audio in CD Format (16bit little endian, 44100Hz, stereo) is piped to this command. |
bt_speaker | connect_command | /etc/bt_speaker/hooks/connect | Command that is called when an audio device connects to BT-Speaker |
bt_speaker | disconnect_command | /etc/bt_speaker/hooks/disconnect | Command that is called when an audio device disconnects from BT-Speaker |
bluez | device_path | /org/bluez/hci0 | The DBUS path where BT-Speaker can find the bluetooth device |
bluez | discoverable | yes | Specifies if the raspberry pi should advertise itself if no client is connected. |
bluez | pin_code | 0000 | The pin code if btmgmt ssp off |
alsa | enabled | yes | Enables volume control via alsamixer |
alsa | mixer | The volume of this mixer will be set from AVRCP messages (Remote volume control) Use HDMI or Headphone . If not set, the first mixer available is used. |
|
alsa | id | 0 | The alsa id of the mixer control |
alsa | cardindex | 0 | The alsa cardindex of the soundcard |
The settings in the alsa section specify on which alsa mixer (more info here) volume changes are applied. You need to adjust these settings if you are using an external sound card.
The BT-Speaker daemon has been written in Python and works with Bluez5. It talks to the Bluez daemon via the Bluez DBUS interface.
BT-Speaker will register itself as an A2DP capable device and route the received audio fully decoded to ALSAs aplay
command.
Changes in volume are detected via messages from the AVRCP profile and are applied directly to the ALSA master volume.
Some devices may filter out BT-Speaker and require the bluetooth device class to be expicitly set. Although BT-Speaker does not support to change the device class itself, you can change it manually after launching BT-Speaker.
pi@raspberrypi:~ $ sudo hciconfig hci0 class 0x240408
More about Bluetooth device classes can be found (here)
The great BT-Manager library does (currently) only work with Bluez4. Changes in the Bluez DBUS API from version 4 to 5 were huge and fully porting BT-Manager would have been a too heavy task. So instead, I extracted all relevant parts and ported them to Bluez5 as good as I could. Documentation and probably lots of other parts there have yet to be adjusted, so refer to that code with caution.
The following describes some internals of the audio stream that is transferred via bluetooth.
The Light of Dawn blog describes the format very accurate:
As it turns out, the audio data is compressed with SBC codec. But I can't just use "sbcdec" tool from SBC package to decode it, as the audio data is encapsulated in A2DP packets, not naked SBC-compressed audio data. A2DP packets are RTP packets (referenced by A2DP specification, and detailed in this IETF draft) containing A2DP Media Payload. We need to extract the SBC audio data, pass it through SBC decompressor, and only then we get raw audio data that can be sent to ALSA.
Unfortunately there is no media player (or at least I didn't find any) that could handle this 'SBC in RTP' format natively. However, BT-Manager already provided a C library that takes care of the decoding process. The decoded output is raw audio data in CD format (16 bit little endian, 44100Hz, stereo) and can be piped to ALSA as mentioned in the blog.
The C library for the decoding process is located in the codecs folder. Its functions are called via Python CFFI. BT-Speaker provides the binary for ARM already, so there is no need to compile the codec manually.
However, if you need to do so for some reason, please be aware that the Makefile has been adjusted by the following:
- The default
PLATFORM
setting has been changed toarmv6
- The
-O3
flag has been added