### Install components Source: https://github.com/arkq/bluez-alsa/blob/master/INSTALL.md Install the built components onto the local system. ```sh sudo make install ``` -------------------------------- ### bluetoothctl session example Source: https://github.com/arkq/bluez-alsa/wiki/Bluetooth-Pairing-And-Connecting An example session using the bluetoothctl command-line tool for Bluetooth pairing and connection management. Lines starting with '[bluetooth]# ' are user prompts. ```bash # Start bluetoothctl [bluetooth]# power on [bluetooth]# agent on [bluetooth]# default-agent # Pair with Bluetooth Headphones [bluetooth]# scan on Discovery started [CHG] Controller C0:25:06:81:23:45 Discovering: yes [NEW] Device C4:67:B5:37:02:02 Excellent Headset [bluetooth]# pair C4:67:B5:37:02:02 Attempting to pair with C4:67:B5:37:02:02 [CHG] Device C4:67:B5:37:02:02 Paired: yes Pairing C4:67:B5:37:02:02: succeeded # Trust and connect to Headphones [bluetooth]# trust C4:67:B5:37:02:02 [CHG] Device C4:67:B5:37:02:02 Trusted: yes Changing C4:67:B5:37:02:02: trusted: yes [bluetooth]# connect C4:67:B5:37:02:02 Attempting to connect to C4:67:B5:37:02:02 Connection successful [CHG] Device C4:67:B5:37:02:02 ServicesResolved: yes # Pair with Mobile Phone (as responder) # First, make the BlueALSA host discoverable [bluetooth]# discoverable on [bluetooth]# pairable on # On the Mobile Phone, initiate pairing with the BlueALSA host (04:42:1A:55:01:01) # After the phone prompts for confirmation, accept it. # Once paired, trust and connect to the phone [bluetooth]# trust B8:27:EB:B5:03:03 [CHG] Device B8:27:EB:B5:03:03 Trusted: yes Changing B8:27:EB:B5:03:03: trusted: yes [bluetooth]# connect B8:27:EB:B5:03:03 Attempting to connect to B8:27:EB:B5:03:03 Connection successful [CHG] Device B8:27:EB:B5:03:03 ServicesResolved: yes # Exit bluetoothctl [bluetooth]# exit ``` -------------------------------- ### Fedora Dependencies Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Installs the required tools and essential development libraries for Fedora. ```sh sudo dnf install git automake libtool pkgconfig gcc python3-docutils sudo dnf install alsa-lib-devel bluez-libs-devel dbus-glib-devel sbc-devel ``` -------------------------------- ### Example command to run the script Source: https://github.com/arkq/bluez-alsa/wiki/Using-BlueALSA-with-HFP-and-HSP-Devices This example shows how to set environment variables for ALSA devices and latency, and then run the bluealsa-audio-agent script. ```shell BA_HF_SPEAKER="plughw:0,0" BA_HF_MICROPHONE="plughw:0,0" BA_HF_LATENCY=60 bluealsa-audio-agent ``` -------------------------------- ### Debian/Raspberry Pi OS/Ubuntu Dependencies Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Installs the required tools and essential development libraries for Debian-based systems. ```sh sudo apt-get install git automake build-essential libtool pkg-config python3-docutils sudo apt-get install libasound2-dev libbluetooth-dev libdbus-1-dev libglib2.0-dev libsbc-dev ``` -------------------------------- ### Install to a packageable directory Source: https://github.com/arkq/bluez-alsa/blob/master/INSTALL.md Install components into a specific directory for packaging and copying to other hosts. ```sh sudo make DESTDIR=$(pwd)/BLUEALSA install ``` -------------------------------- ### Install LC3plus library and header Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Commands to manually copy the built LC3plus library and header file to system locations. ```shell sudo cp libLC3plus.so /usr/local/lib sudo cp lc3plus.h /usr/local/include ``` -------------------------------- ### D-Bus Policy Example Source: https://github.com/arkq/bluez-alsa/blob/master/INSTALL.md Example D-Bus policy file required to enable the bluealsad daemon to register with D-Bus as a service. This policy grants permissions for a specific user to own the service and for members of the 'audio' group to use BlueALSA PCMs and mixer. ```xml ``` -------------------------------- ### Uninstall and Install Source: https://github.com/arkq/bluez-alsa/wiki/Migrating-from-release-4.3.1-or-earlier Instructions for uninstalling the old version and installing the new version of bluez-alsa from source. ```shell cd # Uninstall the old version git checkout v4.3.1 # or earlier used version make && sudo make uninstall # Install the new version git checkout master make && sudo make install ``` -------------------------------- ### PipeWire integration script example Source: https://github.com/arkq/bluez-alsa/wiki/PulseAudio-integration A bash script example for automating the addition of BlueALSA devices to PipeWire, with configuration variables. ```bash #!/bin/bash # Some variables which can be used to customise this script. # Select which device types to exclude from loading into pipewire. # A space separated list of types, where valid types are: # a2dp_source a2dp_sink sco_source sco_sink # outputs (speakers, headphones) are "sinks", # inputs (microphones) are "sources". # By default no devices are excluded # For example, to exclude all sources (recommended when using with # bluealsa-aplay): # EXCLUDE="a2dp_source sco_source" EXCLUDE="a2dp_source sco_source sco_sink" # Change this if you wish to use a different icon for clients such as # pavucontrol. ICON_NAME="bluetooth" # For BlueALSA playback devices only, set this to "yes" to use the most recently # connected BlueALSA PCM as the PipeWire or PulseAudio default device. SET_DEFAULT_SINK=yes ``` -------------------------------- ### Arch Dependencies Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Installs the required tools and essential development libraries for Arch Linux. ```sh sudo pacman -S git base-devel python-docutils sudo pacman -S alsa-lib bluez-libs dbus glib2 sbc ``` -------------------------------- ### Status command example output Source: https://github.com/arkq/bluez-alsa/blob/master/doc/bluealsactl.1.rst Example output for the 'status' command, showing service information, adapters, and profiles. ```text Service: org.bluealsa Version: v4.1.1 Adapters: hci0 hci1 Profiles: A2DP-source : SBC AAC HFP-AG : CVSD mSBC HSP-AG : CVSD ``` -------------------------------- ### Install BlueALSA to a specific directory Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Command to install BlueALSA into a directory tree for transfer to another machine. ```console make DESTDIR=$(pwd)/BLUEALSA install ``` -------------------------------- ### Install required packages Source: https://github.com/arkq/bluez-alsa/wiki/Setting-up-a-USB-audio-device-as-a-Bluetooth-speaker-under-Ubuntu Installs the necessary bluez and bluez-alsa-utils packages on Ubuntu 22.04. ```shell sudo apt install bluez bluez-alsa-utils ``` -------------------------------- ### Info command example output for Volume and Mute Source: https://github.com/arkq/bluez-alsa/blob/master/doc/bluealsactl.1.rst Example output for the 'info' command showing ChannelMap, Volume, and Mute properties. ```text ChannelMap: FC FL FR RL RR LFE Volume: 127 127 127 127 127 127 Mute: off off off off off off ``` -------------------------------- ### asym plugin example Source: https://github.com/arkq/bluez-alsa/wiki/Using-BlueALSA-with-other-ALSA-plugins This example demonstrates how to use the 'asym' plugin to direct playback streams to a BlueALSA device and capture streams from the system's default microphone. ```alsa pcm.asym_demo { type asym playback.pcm "bluealsa" capture.pcm "sysdefault" hint.description "Playback to Bluetooth, capture from local microphone" } ``` -------------------------------- ### file PCM device example with placeholders Source: https://github.com/arkq/bluez-alsa/wiki/ALSA-devices-with-bluealsa‐aplay Example of using placeholders in the filename for the file PCM device. ```console bluealsa-aplay --pcm=file:/tmp/recording-%f-%r-%c.pcm ``` -------------------------------- ### Info command example output for Available and Selected codecs (verbose) Source: https://github.com/arkq/bluez-alsa/blob/master/doc/bluealsactl.1.rst Example output for the 'info --verbose' command, showing available and selected codecs with their capabilities/configurations. ```text Available codecs: SBC:ffff02fa AAC:c0ffff035b60 Selected codec: AAC:400084035b60 ``` -------------------------------- ### Example of changing codec and sample rate Source: https://github.com/arkq/bluez-alsa/blob/master/doc/bluealsactl.1.rst This example demonstrates how to change the channel count and sample rate for a specific PCM path using the 'aac' codec. ```bash bluealsactl codec -c6 -r96000 PCM_PATH aac ``` -------------------------------- ### alsaequal plugin examples Source: https://github.com/arkq/bluez-alsa/wiki/Using-BlueALSA-with-other-ALSA-plugins Examples of using the alsaequal plugin to pass the PCM stream through a graphic equalizer. ```shell ctl.bt-headset-equal { type equal controls ".alsaequal-bt-headset.bin" } pcm.bt-headset-equal { type plug slave.pcm { type equal slave.pcm { type plug slave.pcm { type bluealsa device "11:22:33:44:55:66" profile "a2dp" } } controls ".alsaequal-bt-headset.bin" } } ``` -------------------------------- ### Example aplay -L output Source: https://github.com/arkq/bluez-alsa/wiki/Using-BlueALSA-with-dmix Example output of 'aplay -L' showing a configured Bluetooth audio playback device. ```text Jabra MOVE v2.3.0 A2DP Jabra MOVE v2.3.0 (A2DP) Bluetooth Audio Playback ``` -------------------------------- ### Example OPTIONS environment variable definition Source: https://github.com/arkq/bluez-alsa/wiki/|-ARCHIVE:-Systemd-integration-(outdated-and-deprecated) Example of how to define the OPTIONS environment variable in /etc/default/bluealsa to pass command-line options to bluealsa. ```shell OPTIONS="-p a2dp-source -p a2dp-sink" ``` -------------------------------- ### Starting jackd with BlueALSA backend Source: https://github.com/arkq/bluez-alsa/wiki/Using-BlueALSA-with-the-JACK-Audio-Connection-Kit This command starts the jackd server using the ALSA backend and specifying BlueALSA as the playback device. ```console jackd -r -d alsa -P bluealsa -n 3 -S -o 2 ``` -------------------------------- ### Example systemd unit file for bluealsa Source: https://github.com/arkq/bluez-alsa/wiki/|-ARCHIVE:-Systemd-integration-(outdated-and-deprecated) This is an example unit file to manage bluealsa with systemd, to be saved as /usr/local/lib/systemd/system/bluealsa.service. ```systemd [Unit] Description=Bluealsa daemon Documentation=https://github.com/Arkq/bluez-alsa/ After=dbus-org.bluez.service Requires=dbus-org.bluez.service [Service] Type=dbus BusName=org.bluealsa ;by default enable A2DP source profile only - can be overridden in the EnvironmentFile Environment="OPTIONS=--profile=a2dp-source" EnvironmentFile=-/etc/default/bluealsa ExecStart=/usr/bin/bluealsa $OPTIONS Restart=on-failure ProtectSystem=strict ProtectHome=true PrivateTmp=true PrivateDevices=true RemoveIPC=true RestrictAddressFamilies=AF_UNIX AF_BLUETOOTH ; Also non-privileged can user be used ; this example assumes a user and group called 'bluealsa' exist ;User=bluealsa ;Group=bluealsa ;NoNewPrivileges=true [Install] WantedBy=bluetooth.target ``` -------------------------------- ### file plugin example Source: https://github.com/arkq/bluez-alsa/wiki/Using-BlueALSA-with-other-ALSA-plugins This example shows how to use the 'file' plugin to save an audio stream to a WAV file while playing or capturing from the default BlueALSA device. ```alsa 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" } ``` -------------------------------- ### Example override file for enabling aptX codec Source: https://github.com/arkq/bluez-alsa/wiki/Systemd-integration An example of a drop-in override file content to enable the aptX codec for the bluealsa service. ```ini [Service] ExecStart= ExecStart=/usr/bin/bluealsad -S -p a2dp-source -p a2dp-sink -c aptx ``` -------------------------------- ### Example Usage Source: https://github.com/arkq/bluez-alsa/blob/master/doc/bluealsa-rfcomm.1.rst This example shows how to connect to a Bluetooth device using bluealsa-rfcomm and interact with it via RFCOMM terminal commands. ```bash bluealsa-rfcomm /org/bluealsa/hci0/dev_1C_48_F9_9D_81_5C 1C:48:F9:9D:81:5C> RING > AT+IPHONEACCEV=2,1,6,2,0 > AT+CKPD=200 disconnected ``` -------------------------------- ### Event-driven Example Source: https://github.com/arkq/bluez-alsa/wiki/Using-the-BlueALSA-D‐Bus-API This C code demonstrates how to use the BlueALSA ObjectManager to respond to signals from BlueALSA PCMs and RFCOMMs, reporting connection, disconnection, and property change events. ```c /* * bluealsa-monitor.c * * Print BlueALSA PCM and RFCOMM connection, disconnection and property change * events. */ #include #include "org.bluealsa.h" /* Handler called when an object managed by the BlueALSA object manager is * created. */ static void on_object_added( GDBusObjectManager* self, GDBusObject* object, gpointer user_data) { BluealsaPCM1 *pcm; BluealsaRFCOMM1 *rfcomm; gchar *s; GVariant *v; /* The object may be either a PCM or a RFCOMM, so we distinguish * them by first trying to obtain the PCM1 interface using * bluealsa_object_get_pcm1(), and if that fails then try to get the * RFCOMM1 interface using bluealsa_object_get_rfcomm1() */ if ((pcm = bluealsa_object_get_pcm1(BLUEALSA_OBJECT(object)))) { g_print("PCM added:\n"); g_print("\tobject path: %s\n", g_dbus_object_get_object_path(object)); g_print("\tdevice: %s\n", bluealsa_pcm1_get_device(pcm)); g_print("\ttransport: %s\n", bluealsa_pcm1_get_transport(pcm)); g_print("\tmode: %s\n", bluealsa_pcm1_get_mode(pcm)); g_print("\tsequence number: %u\n", bluealsa_pcm1_get_sequence(pcm)); g_print("\tcodec: %s\n", bluealsa_pcm1_get_codec(pcm)); v = bluealsa_pcm1_get_codec_configuration(pcm); if (v) { gsize n; const guint8 *config = g_variant_get_fixed_array(v, &n, sizeof(guint8)); g_print("\tcodec configuration: "); for (gsize i = 0; i < n; i++) g_print("%02hhx", config[i]); g_print("\n"); } g_print("\taudio format: %#4x\n", bluealsa_pcm1_get_format(pcm)); g_print("\taudio channels: %u\n", bluealsa_pcm1_get_channels(pcm)); const gchar *const *channel_ptr = bluealsa_pcm1_get_channel_map(pcm); g_print("\taudio channel map: [ "); while (channel_ptr && *channel_ptr) g_print("%s ", *channel_ptr++); g_print("]\n"); g_print("\taudio rate: %u\n", bluealsa_pcm1_get_rate(pcm)); v = bluealsa_pcm1_get_volume(pcm); if (v) { s = g_variant_print(v, FALSE); g_print("\tvolume: %s\n", s); g_free(s); } g_print("\tsoft volume: %s\n", bluealsa_pcm1_get_soft_volume(pcm) ? "yes" : "no"); g_print("\trunning: %s\n", bluealsa_pcm1_get_running(pcm) ? "yes" : "no"); g_print("\tdelay: %u\n", bluealsa_pcm1_get_delay(pcm)); g_print("\tclient delay: %d\n", bluealsa_pcm1_get_client_delay(pcm)); /* The pcm object remains valid until the profile connection is closed. * So it is safe to save the pcm pointer for use outside this function, * until we are notified that it has been removed by the object_removed * signal. When we are finished with the pcm we free it by * dereferencing it. In this example we do not use the pcm outside of * this function, so we dereference it here. */ g_object_unref(pcm); return; } if ((rfcomm = bluealsa_object_get_rfcomm1(BLUEALSA_OBJECT(object)))) { g_print("RFCOMM added:\n"); g_print("\tobject path: %s\n", g_dbus_object_get_object_path(object)); g_print("\ttransport: %s\n", bluealsa_rfcomm1_get_transport(rfcomm)); g_print("\tbattery charge: %hhd\n", bluealsa_rfcomm1_get_battery(rfcomm)); /* Note: the "Features" property is always empty when the object is * first added, so we do not report it here. */ /* Just the same as with a pcm object, we dereference the rfcomm * object when we are finished with it. */ g_object_unref(rfcomm); return; } } /* Handler called when an object managed by the BlueALSA object manager is * removed. */ static void on_object_removed( GDBusObjectManager* self, GDBusObject* object, gpointer user_data) { BluealsaPCM1 *pcm; BluealsaRFCOMM1 *rfcomm; if ((pcm = bluealsa_object_peek_pcm1(BLUEALSA_OBJECT(object)))) { g_print("PCM removed:\n"); g_print("\tobject path: %s\n", g_dbus_object_get_object_path(object)); } else if ((rfcomm = bluealsa_object_peek_rfcomm1(BLUEALSA_OBJECT(object)))) { g_print("RFCOMM removed:\n"); g_print("\tobject path: %s\n", g_dbus_object_get_object_path(object)); } /* Note: if the pcm or rfcomm object was previously saved then it must * be dereferenced in order to free it. In this example that is not * necessary. */ } /* Handler called when a Property of an object managed by the BlueALSA object * manager changes its value. */ static void on_interface_proxy_properties_changed( GDBusObjectManagerClient *manager, GDBusObjectProxy *object_proxy, GDBusProxy *interface_proxy, GVariant *changed_properties, const gchar *const *invalidated_properties, gpointer user_data) { GVariantIter iter; gchar *key; GVariant *value; gchar *s; /* Notes: ``` -------------------------------- ### Asynchronous GetCodecs Method Call Example Source: https://github.com/arkq/bluez-alsa/wiki/Using-the-BlueALSA-D‐Bus-API This C code example demonstrates how to invoke the PCM1 GetCodecs method asynchronously using the BlueALSA D-Bus API. It includes the necessary headers, callback function, and main function to set up the D-Bus proxy and initiate the asynchronous call. ```c #include #include "org.bluealsa.h" static GMainLoop *main_loop = NULL; static void print_channels(GVariant *channels) { const guint8 *val; gsize count; gsize i; val = g_variant_get_fixed_array(channels, &count, sizeof(*val)); g_print(" channels:"); for (i = 0; i < count; i++) g_print(" %hhu", val[i]); g_print("\n"); } static void print_rates(GVariant *rates) { const guint32 *val; gsize count; gsize i; val = g_variant_get_fixed_array(rates, &count, sizeof(*val)); g_print(" rates:"); for (i = 0; i < count; i++) g_print(" %u", val[i]); g_print("\n"); } static void iterate_caps(GVariantIter *capabilities) { GVariant *value; gchar *key; while (g_variant_iter_loop(capabilities, "{sv}", &key, &value)) { if (g_strcmp0(key, "Channels") == 0) print_channels(value); else if (g_strcmp0(key, "Rates") == 0) print_rates(value); } } /** * This function is the call-back invoked by D-Bus when the response to the * GetCodecs request arrives. */ static void get_codecs_result(GObject* source_object, GAsyncResult* res, gpointer data) { BluealsaPCM1 *pcm = BLUEALSA_PCM1(source_object); GVariant *out_codecs; /* type="a{sa{sv}}" */ GVariantIter iter; GVariantIter *caps; GError *error = NULL; gchar *codec; /* Call the appropriate "*_finish()" function to retrieve the result data * from the result message. */ if (bluealsa_pcm1_call_get_codecs_finish(pcm, &out_codecs, res, &error)) { g_variant_iter_init(&iter, out_codecs); while (g_variant_iter_loop(&iter, "{sa{sv}}", &codec, &caps)) { g_print("%s\n", codec); iterate_caps(caps); } } else { g_printerr("GetCodecs failed: %s\n", error->message); g_error_free(error); } /* In this simple example we quit the main loop after we have processed * the response message. */ g_main_loop_quit(main_loop); } int main(int argc, char *argv[]) { BluealsaPCM1 *pcm = NULL; const gchar *object_path; GError *error = NULL; gpointer user_data = NULL; if (argc < 2) { g_print("Usage: %s PCM_PATH\n", argv[0]); exit(EXIT_FAILURE); } object_path = argv[1]; if (!g_variant_is_object_path(object_path)) { g_print("Invalid object path [%s]\n", object_path); return(EXIT_FAILURE); } pcm = bluealsa_pcm1_proxy_new_for_bus_sync( G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, "org.bluealsa", object_path, NULL, &error); if (!pcm) { if (error) g_print("Failed to create D-Bus proxy PCM (%s)\n", error->message); return(EXIT_FAILURE); } /* Call the GetCodecs method asynchronously */ bluealsa_pcm1_call_get_codecs( pcm, G_DBUS_CALL_FLAGS_NONE, -1, NULL, get_codecs_result, user_data); /* Run the glib main loop to collect the method response */ main_loop = g_main_loop_new(NULL, TRUE); g_main_loop_run(main_loop); g_object_unref(pcm); return EXIT_SUCCESS; } ``` -------------------------------- ### Event-Driven Example Source: https://github.com/arkq/bluez-alsa/wiki/Using-the-BlueALSA-D‐Bus-API This C code demonstrates an event-driven approach to interacting with the BlueALSA D-Bus API. It connects to the system bus, sets up signal handlers for object additions, removals, and property changes, and then enters a main loop to process events. ```c static void on_interface_proxy_properties_changed(GDBusProxy *proxy, const gchar *interface_name, GVariant *changed_properties, const gchar *const *invalidated_properties, gpointer user_data) { GVariantIter iter; GVariant *value; const gchar *key; char *s; GDBusObjectProxy *object_proxy = G_DBUS_OBJECT_PROXY(g_dbus_proxy_get_object(proxy)); /* * 1. The property value has already been changed in the associated PCM1 or * RFCOMM1 object before this handler is called. * * 2. If we need to know which object interface this property relates * to, we can do something like: * * if (BLUEALSA_IS_PCM1(interface_proxy)) { * BluealsaPCM1 *pcm = BLUEALSA_PCM1(interface_proxy); * ... actions for PCM1 interface * } * else if (BLUEALSA_IS_RFCOMM1(interface_proxy)) { * BluealsaRFCOMM1 *rfcomm = BLUEALSA_RFCOMM1(interface_proxy); * ... actions for RFCOMM1 interface * } */ g_print("Properties Changed on %s:\n", g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy))); g_variant_iter_init(&iter, changed_properties); while (g_variant_iter_next(&iter, "{&sv}", &key, &value)){ s = g_variant_print(value, FALSE); g_print("\t%s: %s\n", key, s); g_free(s); g_variant_unref(value); } } int main() { GDBusObjectManager *manager; GError *error = NULL; manager = bluealsa_object_manager_client_new_for_bus_sync( G_BUS_TYPE_SYSTEM, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, "org.bluealsa", "/org/bluealsa", NULL, &error); if (error) { g_printerr("Failed to create object manager (%s)\n", error->message); exit(EXIT_FAILURE); } g_signal_connect(manager, "object-added", G_CALLBACK(on_object_added), NULL); g_signal_connect(manager, "object-removed", G_CALLBACK(on_object_removed), NULL); g_signal_connect(manager, "interface_proxy_properties_changed", G_CALLBACK(on_interface_proxy_properties_changed), NULL); GMainLoop *main_loop; main_loop = g_main_loop_new(NULL, TRUE); g_main_loop_run(main_loop); g_object_unref(manager); return EXIT_SUCCESS; } ``` -------------------------------- ### Display configure help Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Command to display all available configure script options and environment variables. ```shell ../configure --help ``` -------------------------------- ### List Connected PCMs Source: https://github.com/arkq/bluez-alsa/wiki/Using-the-BlueALSA-D‐Bus-API A C example demonstrating how to use the generated ObjectManager code to retrieve and print a list of connected PCM object paths, similar to the functionality of 'bluealsactl list-pcms'. ```c /* * bluealsa-list.c * * Print a list of connected BlueALSA PCM object paths */ #include #include "org.bluealsa.h" int main() { GDBusObjectManager *manager; GError *error = NULL; GList *bluealsa_objects = NULL; manager = bluealsa_object_manager_client_new_for_bus_sync( G_BUS_TYPE_SYSTEM, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, "org.bluealsa", "/org/bluealsa", NULL, &error); if (error) { g_printerr("Failed to create object manager client (%s)\n", error->message); exit(EXIT_FAILURE); } bluealsa_objects = g_dbus_object_manager_get_objects(manager); for (GList *item = g_list_first(bluealsa_objects); item; item = item->next) { GDBusObject *object = item->data; if (bluealsa_object_peek_pcm1(BLUEALSA_OBJECT(object))) g_print("%s\n", g_dbus_object_get_object_path(object)); g_object_unref(object); } g_list_free(bluealsa_objects); g_object_unref(manager); return EXIT_SUCCESS; } ``` -------------------------------- ### Print the Properties of a connected BlueALSA PCM Source: https://github.com/arkq/bluez-alsa/wiki/Using-the-BlueALSA-D‐Bus-API This C example demonstrates how to access a specific BlueALSA PCM using its D-Bus object path and print its properties, including device, transport, mode, codec, format, channels, rate, volume, and supported codecs. ```c /* * bluealsa-info.c * * Print the Properties of a connected BlueALSA PCM */ #include #include "org.bluealsa.h" /* Macros to extract loudness and mute component from encoded Volume property */ #define BA_LOUDNESS(x) (x & 0x7F) #define BA_MUTED(x) (x & 0x80) /* Convert encoded Format property to name of equivalent ALSA PCM format */ static const gchar *alsa_format_name(guint16 format) { switch (format) { case 0x0108: return "U8"; case 0x8210: return "S16_LE"; case 0x8318: return "S24_3LE"; case 0x8418: return "S24_LE"; case 0x8420: return "S32_LE"; default: return "UNKNOWN"; } } int main(int argc, char *argv[]) { BluealsaPCM1 *pcm = NULL; const gchar *object_path; const gchar *device; GVariant *v; GError *error = NULL; if (argc < 2) { g_printerr("Usage: %s PCM_PATH\n", argv[0]); exit(EXIT_FAILURE); } object_path = argv[1]; if (!g_variant_is_object_path(object_path)) { g_printerr("Invalid object path [%s]\n", object_path); exit(EXIT_FAILURE); } pcm = bluealsa_pcm1_proxy_new_for_bus_sync( G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, "org.bluealsa", object_path, NULL, &error); if (!pcm) { if (error) g_printerr("Failed to create D-Bus proxy PCM (%s)\n", error->message); exit(EXIT_FAILURE); } g_print("object path: %s\n", object_path); device = bluealsa_pcm1_get_device(pcm); if (!device) { g_printerr("Object path does not represent a connected PCM\n"); exit(EXIT_FAILURE); } g_print("device: %s\n", device); g_print("transport: %s\n", bluealsa_pcm1_get_transport(pcm)); g_print("mode: %s\n", bluealsa_pcm1_get_mode(pcm)); g_print("sequence number: %u\n", bluealsa_pcm1_get_sequence(pcm)); g_print("codec: %s\n", bluealsa_pcm1_get_codec(pcm)); /* The CodecConfiguration property is returned as a GVariant containing an * array of bytes. Here we print the value as a single hexadecimal string * (as used by the Bluealsa utility a2dpconf). */ v = bluealsa_pcm1_get_codec_configuration(pcm); if (v) { gsize n; const guint8 *config = g_variant_get_fixed_array(v, &n, sizeof(guint8)); g_print("codec configuration: "); for (gsize i = 0; i < n; i++) g_print("%02hhx", config[i]); g_print("\n"); } g_print("audio format: %s\n", alsa_format_name(bluealsa_pcm1_get_format(pcm))); g_print("audio channels: %u\n", bluealsa_pcm1_get_channels(pcm)); const gchar *const *channel_ptr = bluealsa_pcm1_get_channel_map(pcm); g_print("audio channel map: [ "); while (channel_ptr && *channel_ptr) g_print("%s ", *channel_ptr++); g_print("]\n"); g_print("audio rate: %u\n", bluealsa_pcm1_get_rate(pcm)); /* The Volume property is returned as a GVariant containing an array of * bytes representing encoded loudness and mute values, one value for each * channel. */ v = bluealsa_pcm1_get_volume(pcm); if (v) { gsize len; const guint8 *volume = g_variant_get_fixed_array(v, &len, sizeof(guint8)); for (gsize i = 0; i < len; i++) g_print("volume channel %zu: %hhu%s\n", i, BA_LOUDNESS(volume[i]), BA_MUTED(volume[i]) ? " (muted)" : ""); } g_print("soft volume: %s\n", bluealsa_pcm1_get_soft_volume(pcm) ? "yes" : "no"); g_print("running: %s\n", bluealsa_pcm1_get_running(pcm) ? "yes" : "no"); g_print("delay: %u\n", bluealsa_pcm1_get_delay(pcm)); g_print("client delay: %d\n", bluealsa_pcm1_get_client_delay(pcm)); /* Obtain the set of supported codecs by invoking the GetCodecs method. */ if (bluealsa_pcm1_call_get_codecs_sync( pcm, G_DBUS_CALL_FLAGS_NONE, -1, &v, NULL, &error)) { GVariantIter iter; GVariant *child; gchar *key; g_print("supported codecs: ["); g_variant_iter_init(&iter, v); while (g_variant_iter_next(&iter, "{sa{sv}}", &key, &child)) g_print(" %s", key); g_print(" ]\n"); } if (error) g_error_free(error); g_object_unref(pcm); return EXIT_SUCCESS; } ``` -------------------------------- ### Makefile for BlueALSA Compilation Source: https://github.com/arkq/bluez-alsa/wiki/Using-the-BlueALSA-D‐Bus-API This Makefile compiles the BlueALSA interface XML file and example source files into executable programs. It includes definitions for compiler flags, libraries, and code generation arguments. ```make IFACE_XML := org.bluealsa.xml GLIB_CFLAGS := -I/usr/include/gio-unix-2.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/sysprof-6 -pthread GLIB_LIBS := -lgio-2.0 -lgobject-2.0 -lglib-2.0 CODEGEN_ARGS := \ --interface-prefix org.bluealsa \ --c-generate-object-manager \ --c-generate-autocleanup all \ --c-namespace Bluealsa \ --glib-min-required 2.64 \ --annotate "org.bluealsa.PCM1:CodecConfiguration" org.gtk.GDBus.C.ForceGVariant yes \ --annotate "org.bluealsa.PCM1:Volume" org.gtk.GDBus.C.ForceGVariant yes PROGRAMS := bluealsa-list bluealsa-info bluealsa-monitor bluealsa-open bluealsa-async all: $(PROGRAMS) bluealsa-list: bluealsa-list.o org.bluealsa.o $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(GLIB_LIBS) bluealsa-info: bluealsa-info.o org.bluealsa.o $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(GLIB_LIBS) bluealsa-monitor: bluealsa-monitor.o org.bluealsa.o $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(GLIB_LIBS) bluealsa-open: bluealsa-open.o org.bluealsa.o $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(GLIB_LIBS) bluealsa-async: bluealsa-async.o org.bluealsa.o $(CC) $(CFLAGS) -o $@ $^ $(GLIB_LIBS) org.bluealsa.c: $(IFACE_XML) gdbus-codegen --body \ $(CODEGEN_ARGS) \ --output $@ $< org.bluealsa.h: $(IFACE_XML) gdbus-codegen --header \ $(CODEGEN_ARGS) \ --output $@ $< org.bluealsa.o: org.bluealsa.c org.bluealsa.h $(CC) $(CFLAGS) $(GLIB_CFLAGS) -c -o $@ $< bluealsa-list.o: bluealsa-list.c org.bluealsa.h $(CC) $(CFLAGS) $(GLIB_CFLAGS) -c -o $@ $< bluealsa-info.o: bluealsa-info.c org.bluealsa.h $(CC) $(CFLAGS) $(GLIB_CFLAGS) -c -o $@ $< bluealsa-monitor.o: bluealsa-monitor.c org.bluealsa.h $(CC) $(CFLAGS) $(GLIB_CFLAGS) -c -o $@ $< bluealsa-open.o: bluealsa-open.c org.bluealsa.h $(CC) $(CFLAGS) $(GLIB_CFLAGS) -c -o $@ $< bluealsa-async.o: bluealsa-async.c org.bluealsa.h $(CC) $(CFLAGS) $(GLIB_CFLAGS) -c -o $@ $< clean: $(RM) $(PROGRAMS) org.bluealsa.[ch] *.o ``` -------------------------------- ### Sending OK after Starting Audio Stream Source: https://github.com/arkq/bluez-alsa/wiki/Using-BlueALSA-with-HFP-and-HSP-Devices Example of sending the OK response after the 'real' audio stream has been started, in the context of implementing a gateway device. ```console user@host:~ bluealsa-rfcomm /org/bluealsa/hci0/dev_01_23_45_67_89_AB > AT+CKPD=200 01:23:45:67:89:AB> OK ``` -------------------------------- ### Sending and receiving PCM audio streams Source: https://github.com/arkq/bluez-alsa/wiki/Using-the-BlueALSA-D‐Bus-API This C example demonstrates how to use the PCM Open() method to read and write audio streams, transferring audio between the BlueALSA PCM and standard input or output. This functionality is similar to the `bluealsactl open` command. ```c /* * bluealsa-open.c * * Transfer raw PCM audio data via stdin or stdout */ #include #include #include #include "org.bluealsa.h" int main(int argc, char *argv[]) { BluealsaPCM1 *pcm = NULL; const gchar *object_path; gint fd_pcm = -1; gint fd_ctrl = -1; gboolean needs_drain = FALSE; gboolean drained = FALSE; gssize count; gchar ctrl_response[3] = {0}; GUnixFDList *fd_list = NULL; GInputStream *input_stream = NULL; GOutputStream *output_stream = NULL; GInputStream *input_ctrl = NULL; GOutputStream *output_ctrl = NULL; GError *error = NULL; if (argc < 2) { g_printerr("Usage: %s PCM_PATH\n", argv[0]); exit(EXIT_FAILURE); } object_path = argv[1]; if (!g_variant_is_object_path(object_path)) { g_printerr("Invalid object path [%s]\n", object_path); goto fail; } pcm = bluealsa_pcm1_proxy_new_for_bus_sync( G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, "org.bluealsa", object_path, NULL, &error); if (!pcm) { if (error) g_printerr("Failed to create D-Bus proxy PCM (%s)\n", error->message); goto fail; } if (!(bluealsa_pcm1_call_open_sync( pcm, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL, &fd_list, NULL, &error))) { if (error) g_printerr("Unable to open PCM (%s)\n", error->message); goto fail; } fd_pcm = g_unix_fd_list_get(fd_list, 0, &error); fd_ctrl = g_unix_fd_list_get(fd_list, 1, &error); g_object_unref(fd_list); if (strcmp(bluealsa_pcm1_get_mode(pcm), "source") == 0) { input_stream = g_unix_input_stream_new(fd_pcm, TRUE); output_stream = g_unix_output_stream_new(STDOUT_FILENO, FALSE); } else { input_stream = g_unix_input_stream_new(STDIN_FILENO, FALSE); output_stream = g_unix_output_stream_new(fd_pcm, TRUE); needs_drain = TRUE; } input_ctrl = g_unix_input_stream_new(fd_ctrl, TRUE); output_ctrl = g_unix_output_stream_new(fd_ctrl, TRUE); count = 0; do { count = g_output_stream_splice( output_stream, input_stream, G_OUTPUT_STREAM_SPLICE_NONE, NULL, &error); } while (count > 0); if (count == 0 && needs_drain) { if (g_output_stream_write(output_ctrl, "Drain", 5, NULL, &error) == 5) { if (g_input_stream_read(input_ctrl, ctrl_response, 2, NULL, &error) == 2 && * "Sending and receiving PCM audio streams" ``` -------------------------------- ### Optimize for minimum size Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Compile BlueALSA with optimization flags for minimum size. ```console make CFLAGS="-Os -s" ``` -------------------------------- ### Configure with options Source: https://github.com/arkq/bluez-alsa/blob/master/INSTALL.md After choosing desired options, create a build directory and run configure. ```sh mkdir build && cd build ../configure [ OPTION ... ] ``` -------------------------------- ### Emulate Bluetooth headset with A2DP and HSP support Source: https://github.com/arkq/bluez-alsa/blob/master/doc/bluealsad.8.rst This example shows how to start the bluealsad daemon to emulate a Bluetooth headset with A2DP and HSP support. ```bash bluealsad -p a2dp-sink -p hsp-hs ``` -------------------------------- ### View all configuration options Source: https://github.com/arkq/bluez-alsa/blob/master/INSTALL.md Run this command to see a complete list of all available configuration options. ```sh ./configure --help ``` -------------------------------- ### Optimize for speed and size compromise Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Compile BlueALSA with optimization flags for a balance between speed and size. ```console make CFLAGS="-O2 -s" ``` -------------------------------- ### Defining a new PCM with bluealsa slave and hints Source: https://github.com/arkq/bluez-alsa/wiki/|-ARCHIVE:-Using-the-BlueALSA-ALSA-pcm-plugin Example of defining a new PCM entry with a bluealsa slave and a 'show on' hint. ```shell 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" } } ``` -------------------------------- ### Optimize for maximum speed Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Compile BlueALSA with optimization flags for maximum speed. ```console make CFLAGS="-Ofast -s" ``` -------------------------------- ### Create Build Directory Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Creates a new directory for the build products, keeping them separate from the source code. ```sh mkdir build cd build ``` -------------------------------- ### sysdefault PCM device example Source: https://github.com/arkq/bluez-alsa/wiki/ALSA-devices-with-bluealsa‐aplay Example of how to use the sysdefault PCM device with a specific card. ```console bluealsa-aplay --pcm=sysdefault:CARD=1 ``` -------------------------------- ### Example PropertyChanged Event Source: https://github.com/arkq/bluez-alsa/blob/master/doc/bluealsactl.1.rst An example of how the PropertyChanged event is displayed for a stereo A2DP PCM with the right channel muted. ```text PropertyChanged PCM_PATH Volume 127 127[M] ``` -------------------------------- ### Build LC3plus shared library Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Command to build the LC3plus shared library from source. ```console make libLC3plus.so ``` -------------------------------- ### Release build with AAC, aptX, oFono, and mSBC Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Generates a release build with support for AAC, aptX, oFono, and mSBC. ```shell ../configure --enable-aac --enable-aptx --with-libopenaptx --enable-ofono --enable-msbc ``` -------------------------------- ### vdownmix plugin examples Source: https://github.com/arkq/bluez-alsa/wiki/Using-BlueALSA-with-other-ALSA-plugins Examples of using the vdownmix plugin to convert multi-channel streams to stereo. ```shell pcm.!bluealsa_surround40 { type plug slave { channels 4 pcm { type vdownmix slave.pcm bluealsa } } } pcm.!bluealsa_surround51 { type plug slave { channels 6 pcm { type vdownmix slave.pcm bluealsa } } } ``` -------------------------------- ### Loopback device with format conversion using plug plugin Source: https://github.com/arkq/bluez-alsa/wiki/Using-BlueALSA-with-dmix This example demonstrates using the Loopback device with format conversion. The capture side is set to a specific format, and the playback side uses the 'plug' plugin for conversion. ```shell arecord -f float_le -c 2 -r 8000 -D hw:Loopback,1 capture.wav & aplay -D plughw:Loopback,0 /usr/share/sounds/alsa/Front_Center.wav ``` -------------------------------- ### Example ALSA pcm configuration Source: https://github.com/arkq/bluez-alsa/wiki/Using-BlueALSA-with-dmix Example of how an ALSA pcm configuration appears for a connected Bluetooth device. ```text Simple mixer control 'Jabra MOVE v2.3.0 A2DP',0 ``` -------------------------------- ### Optimize for debugging Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Compile BlueALSA with optimization flags for debugging. ```console make CFLAGS="-Og -g" ``` -------------------------------- ### Clone BlueALSA Source Code Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Clones the BlueALSA source code from Github and navigates into the directory. ```sh git clone https://github.com/arkq/bluez-alsa.git cd bluez-alsa ``` -------------------------------- ### Stream format identifier example 2 Source: https://github.com/arkq/bluez-alsa/blob/master/doc/org.bluealsa.PCM1.7.rst Example of a stream format identifier: signed 24-bit 4 bytes little-endian. ```text 0x8418 - signed 24-bit 4 bytes little-endian ``` -------------------------------- ### Stream format identifier example 1 Source: https://github.com/arkq/bluez-alsa/blob/master/doc/org.bluealsa.PCM1.7.rst Example of a stream format identifier: unsigned 16-bit 2 bytes big-endian. ```text 0x4210 - unsigned 16-bit 2 bytes big-endian ``` -------------------------------- ### Start BlueALSA manually from the command line Source: https://github.com/arkq/bluez-alsa/wiki/|-ARCHIVE:-Systemd-integration-(outdated-and-deprecated) Commands to start and stop the bluealsa.service using systemctl. ```bash sudo systemctl start bluealsa.service ``` ```bash sudo systemctl stop bluealsa.service ``` -------------------------------- ### Simple debug build Source: https://github.com/arkq/bluez-alsa/wiki/Installation-from-source Generates a simple debug build without additional options. ```shell ../configure --enable-debug ``` -------------------------------- ### Compile the project Source: https://github.com/arkq/bluez-alsa/blob/master/INSTALL.md Run this command in the build directory to compile the project. ```sh make ```