You are not logged in.
If nobody wants to maintain it, it's not needed.
If nobody can maintain it, it doesn't make sense.
Right, so the bluez-alsa packages in the official Devuan repository were built with --enable-systemd, which means they’ve picked up a systemd dependency — a bit of an oversight, really, given Devuan’s init-agnostic stance.
To address this, the packages were unpacked, the dependency manually adjusted, and then repackaged. They’ll install without complaint, though they might not function perfectly in the absence of systemd. It’s not ideal, but it’s a pragmatic fix.
The method used is a known workaround, documented here:
_https://dev1galaxy.org/viewtopic.php?id=7157
1. The package is not needed.
2. It is easy to compile.
The Devuan consumer community want to consume knowledge rather than packages. You should not keep them in ignorance.
Knowledge and only knowledge liberates, and absolute knowledge liberates absolutely.
Isaiah Berlin, Four Essays on Liberty (1969).
Download sourcedir:
mkdir BUILD3 && cd BUILD3 git clone https://github.com/arkq/bluez-alsa.git cd bluez-alsa && export DEBFULLNAME="John Frum" Install a template:
dh_make --createorig -p bluez-alsa_4.3.1-86 -c apache -e my@mail.org -s 1. debian/control
If you have libfreeaptx-dev installed, replace "libfreeaptx" with "libfreeaptx-dev" in Build-Depends. If you compiled libfreeaptx from git, leave it as is.
$ cat debian/control
Source: bluez-alsa
Section: libs
Priority: optional
Maintainer: John Frum <my@mail.org>
Rules-Requires-Root: no
Build-Depends:
debhelper-compat (= 13),
dh-exec,
libasound2-dev,
libbluetooth-dev (>= 5.0),
libdbus-1-dev (>= 1.6),
libfreeaptx,
libglib2.0-dev (>= 2.30),
libsbc-dev (>= 1.2),
liblc3-dev,
libldacbt-enc-dev (>= 2.0.0) [!hppa !m68k !powerpc !ppc64 !s390x !sparc64],
libldacbt-abr-dev (>= 2.0.0) [!hppa !m68k !powerpc !ppc64 !s390x !sparc64],
libmp3lame-dev,
libmpg123-dev,
libspandsp-dev,
libopus-dev,
libbsd-dev,
libreadline-dev,
check,
dbus-daemon <!nocheck>,
python3-docutils,
pkgconf
Standards-Version: 4.6.2
Homepage: https://github.com/Arkq/bluez-alsa
#Vcs-Browser: https://salsa.debian.org/debian/bluez-alsa
#Vcs-Git: https://salsa.debian.org/debian/bluez-alsa.git
Package: bluez-alsa
Architecture: linux-any
Multi-Arch: same
Section: libs
Depends:
${shlibs:Depends},
${misc:Depends},
Conflicts: bluez-alsa-utils, libasound2-plugin-bluez
Provides: bluez-alsa-utils (= 4.3.1-86-1), libasound2-plugin-bluez (= 4.3.1-86-1)
Replaces: bluez-alsa-utils, libasound2-plugin-bluez
Description: Bluetooth Audio ALSA Backend
Bluetooth Audio ALSA Backend allows bluetooth audio without PulseAudio and PipeWire.
This package provides sysVinit script for bluealsad daemon to run it without systemd.
.
This project is a rebirth of a direct integration between Bluez and ALSA.
Since Bluez >= 5, the build-in integration has been removed in favor of 3rd
party audio applications. From now on, Bluez acts as a middleware between an
audio application, which implements Bluetooth audio profile, and a Bluetooth
audio device.2. debian/rules
$ cat debian/rules
#!/usr/bin/make -f
# See debhelper(7) (uncomment to enable).
# Output every command that modifies files on the build system.
#export DH_VERBOSE = 1
# See FEATURE AREAS in dpkg-buildflags(1).
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
# See ENVIRONMENT in dpkg-buildflags(1).
# Package maintainers to append CFLAGS.
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
# Package maintainers to append LDFLAGS.
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
%:
dh $@ --with autoreconf
# dh_make generated override targets.
# This is an example for Cmake (see <https://bugs.debian.org/641051>).
#override_dh_auto_configure:
# dh_auto_configure -- \
# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
override_dh_auto_configure:
dh_auto_configure -- --prefix=/usr --sysconfdir=/etc --with-bluealsaduser=bluealsad --enable-ldac --with-libfreeaptx --enable-aptx --enable-aptx-hd --enable-faststream --enable-lc3-swb --enable-mp3lame --enable-mpg123 --enable-msbc --enable-ofono --enable-midi --enable-opus --enable-upower --enable-rfcomm --enable-a2dpconf --enable-hcitop --enable-test --enable-aac --enable-manpages --disable-systemd
override_dh_installinit:
dh_installinit --name=bluealsad
override_dh_shlibdeps:
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
3. debian/bluez-alsa.bluealsad.init
$ cat cat debian/bluez-alsa.bluealsad.init
cat: cat: No such file or directory
#!/bin/sh
### BEGIN INIT INFO
# Provides: bluealsa
# Required-Start: dbus $syslog $local_fs $remote_fs bluetooth
# Required-Stop: dbus $syslog $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: BlueALSA daemon
# Description: Bluetooth ALSA audio daemon
### END INIT INFO
. /lib/lsb/init-functions
NAME=bluealsad
DESC="BlueALSA daemon"
DAEMON=/usr/bin/$NAME
PIDFILE="/var/run/$NAME.pid"
STORAGE_DIR="/var/lib/bluealsa"
USER="bluealsad"
COMMON_OPTS="--quiet --oknodo --pidfile $PIDFILE"
# Working configuration without low-quality HSP codecs
BA_OPTS="-S -p a2dp-source -p a2dp-sink -p hfp-ag -p hfp-hf -c sbc -c aac -c mp3 -c aptx -c aptx-hd -c faststream -c opus -c msbc -c lc3-swb -c ldac --aac-afterburner --aac-bitrate=320000 --aac-vbr"
# Ensure bluealsad user exists and has correct group membership
if ! id $USER >/dev/null 2>&1; then
# Create user if it doesn't exist
useradd -r -s /bin/false $USER
usermod -a -G audio $USER
else
# User exists, check if in audio group
if ! id -nG $USER | grep -qw audio; then
usermod -a -G audio $USER
fi
fi
# Ensure storage directory exists with correct ownership
if [ ! -d "$STORAGE_DIR" ]; then
mkdir -p "$STORAGE_DIR"
chown $USER:$USER "$STORAGE_DIR"
chmod 0700 "$STORAGE_DIR"
else
# Directory exists, check ownership
CURRENT_OWNER=$(stat -c "%U:%G" "$STORAGE_DIR")
if [ "$CURRENT_OWNER" != "$USER:$USER" ]; then
chown $USER:$USER "$STORAGE_DIR"
chmod 0700 "$STORAGE_DIR"
fi
fi
case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
start-stop-daemon $COMMON_OPTS --start --background \
--make-pidfile --chuid $USER --exec $DAEMON -- $BA_OPTS
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
start-stop-daemon $COMMON_OPTS --stop --retry 5
log_end_msg $?
;;
restart|force-reload)
$0 stop
$0 start
;;
status)
status_of_proc $DAEMON "$NAME" || exit $?
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload|status}" >&2
exit 1
;;
esac
exit 0chmod +x debian/bluez-alsa.bluealsad.init Create Debian source package
dpkg-source -b . Build Debian package
dpkg-buildpackage -us -uc $ cd .. && ls -1 *.deb
bluez-alsa_4.3.1-86-1_amd64.deb
bluez-alsa-dbgsym_4.3.1-86-1_amd64.deb Install
sudo dpkg -i bluez-alsa_4.3.1-86-1_amd64.deb In bureaucratic terms, you have a complete Debian package upload set or package release bundle:
$ ls -1 bluez-alsa bluez-alsa_4.3.1-86-1_amd64.buildinfo bluez-alsa_4.3.1-86-1_amd64.changes bluez-alsa_4.3.1-86-1_amd64.deb bluez-alsa_4.3.1-86-1.debian.tar.xz bluez-alsa_4.3.1-86-1.dsc bluez-alsa_4.3.1-86.orig.tar.xz bluez-alsa-dbgsym_4.3.1-86-1_amd64.debThis includes:
Source files (.dsc, .orig.tar.xz, .debian.tar.xz)
Binary packages (.deb)
Debug symbols (.dbgsym.deb)
Build metadata (.buildinfo)
Changes file (.changes) — the key control file listing all others
The .changes file defines the full set, used by Debian archive tools (like dak) to process uploads into a repository.
Configure the bluealsad service to start automatically at system boot
sudo update-rc.d bluealsad defaults $ sudo service bluealsad restart
Stopping BlueALSA daemon: bluealsad.
Starting BlueALSA daemon: bluealsad. $ sudo service bluealsad status
bluealsad is running. See also:
_https://dev1galaxy.org/viewtopic.php?id=7671
Ah, the --with-bluealsaduser=bluealsad option — very sensible. Lets you run the daemon as a dedicated user, which is ever so slightly less daft than letting it run as root. Keeps things tidy, limits the carnage if something goes wrong. Think of it as giving your Bluetooth audio a sensible hat and a laminated badge — still can’t stop it from misbehaving entirely, but at least it’s not running the whole show.
If you don’t specify a user, it defaults to root, which works — until it doesn’t. And when used with older BlueZ versions (≤ 5.50), just make sure your chosen user’s in the "bluetooth" group, or it’ll sulk and refuse to connect. Nothing dramatic — just typical Linux passive aggression.
There is, one might say, a slight oversight in the Debian bluez-alsa package: it runs as root, which is a bit like giving the keys to the house to someone just to borrow the kettle. Should a flaw exist — and history suggests they often do — it could be exploited with elevated consequences.
The good news? This doesn’t affect Devuan at all. The package simply doesn’t work without systemd, so it sits there, harmless and inert — like a very quiet paperweight.
So while Debian’s setup is perhaps more trusting than strictly necessary, Devuan users are, by design, not involved in the drama.
What is the spec of your headset, is there something like this:
- Bluetooth codecs
- aptX™ Adaptive
aptX™ HD
aptX™
AAC
SBC
- Bluetooth profiles
- A2DP
AVRCP
HFP
Debian default is low quality SBC codec.
With aptX codec, sound quality is much better.
libfreeaptx is outdated in the oldstable
_https://tracker.debian.org/pkg/libfreeaptx
git version;
$ freeaptxdec --help
aptX decoder utility 0.2.2 (using libfreeaptx 0.2.2)
This utility decodes aptX or aptX HD audio stream
from stdin to a raw 24 bit signed stereo on stdout the Debian source of bluez-alsa is outdated
_https://tracker.debian.org/pkg/bluez-alsa
The git version is now v4.3.1-86-gf1e53d3. It means that 86 bugs were fixed. In any case, everything was compiled.
All codecs available for Linux were compiled and enabled.
$ bluealsactl status
Service: org.bluealsa
Version: v4.3.1-86-gf1e53d3
Adapters: hci0
Profiles:
A2DP-source : SBC MP3 AAC aptX aptX-HD FastStream LDAC Opus
A2DP-sink : SBC MP3 AAC aptX aptX-HD FastStream Opus
HFP-AG : CVSD mSBC LC3-SWB
HFP-HF : CVSD mSBC LC3-SWBhsp codecs are not loaded with init script. They are of low quality: 8kHz sample rate.
The question is: which codecs are supported by your BT headphones?
What is your spec?
Sennheiser HD 450BT does have aptX-HD, but it has aptX
Specifications:
Bluetooth profiles: AVRCP, A2DP, HFP, HSP
Bluetooth Audio Codecs: AptX, AptX Low Latency, AAC, SBC
$ bluealsa-aplay -L
bluealsa:DEV=80:C3:BA:8B:B6:CC,PROFILE=sco,SRV=org.bluealsa
HD 450BT, trusted audio-headphones, playback
SCO (mSBC): S16_LE 1 channel 16000 Hz
bluealsa:DEV=80:C3:BA:8B:B6:CC,PROFILE=sco,SRV=org.bluealsa
HD 450BT, trusted audio-headphones, capture
SCO (mSBC): S16_LE 1 channel 16000 Hz
bluealsa:DEV=80:C3:BA:8B:B6:CC,PROFILE=a2dp,SRV=org.bluealsa
HD 450BT, trusted audio-headphones, playback
A2DP (aptX): S16_LE 2 channels 48000 HzSennheiser HDB 630
_https://www.sennheiser-hearing.com/en-DE/p/hdb-630/
Specifications
Codec support
aptX™ Adaptive™, aptX™ HD, aptX™, AAC, SBC
Enjoy high resolution with support for aptX™ HD and aptX™ Adaptive, especially when paired with the included BTD 700 Bluetooth® USB C Dongle
For the purest signal, connect via USB C or analog input for lossless listening at up to 24 bit/96 kHz.
_https://www.sennheiser-hearing.com/en-DE/p/hdb-630/
Bowers & Wilkins Px7 S2e Bluetooth Wireless Headphones
_https://www.bowerswilkins.com/en-gb/product/over-ear-headphones/px7-s2e/FP44520P.html#specifications
Specifications
Bluetooth codecs
aptX™ Adaptive
aptX™ HD
aptX™
AAC
SBC
SBC is a basic low quality codec.
aptX Adaptive is not available for Linux.
AAC in BlueALSA uses the fdk-aac library from Fraunhofer IIS, which is not completely free software.
The fdk-aac library is under Fraunhofer license which:
Is free for non-commercial use
Requires licensing for commercial applications
Has patent encumbrances in some countries
You may try high quality options: --aac-afterburner --aac-bitrate=320000 --aac-true-bps
# without -c aptx -c aptx-hd
BA_OPTS="-S -p a2dp-source -p a2dp-sink -p hfp-ag -p hfp-hf -c sbc -c aac -c mp3 -c faststream -c opus -c msbc -c lc3-swb -c ldac --aac-afterburner --aac-bitrate=320000 --aac-true-bps"AAC codec supports 8 kHz to 96 kHz including 44.1kHz and 48kHz.
While BlueALSA's automatic selection is limited to 48kHz, the codecs themselves support higher rates. For example, AAC supports up to 96kHz.
You can manually select higher sample rates using:
bluealsactl codec -r96000 PCM_PATH aacbluealsactl list-pcmsSennheiser HD 450BT does not support 96kHz sample rate with AAC codec, but it has analog audio cable with 3.5mm jack plug. It is much better than any Bluetooth codec.
It was compiled with --enable-systemd (see: debian/rules )
I have already recompiled the Debian source package.
It is outdated, and the codecs are outdated.
The git version is much better.
It does not seem to contain any init script.
I have already enabled the stable repository:
$ apt source bluez-alsa-utils
Reading package lists... Done
Picking 'bluez-alsa' as source package instead of 'bluez-alsa-utils'
NOTICE: 'bluez-alsa' packaging is maintained in the 'Git' version control system at:
https://salsa.debian.org/bluetooth-team/bluez-alsa.git
Please use:
git clone https://salsa.debian.org/bluetooth-team/bluez-alsa.git
to retrieve the latest (possibly unreleased) updates to the package.
Need to get 257 kB of source archives.
Get:1 http://deb.devuan.org/merged stable/main bluez-alsa 4.3.1-3 (dsc) [2,528 B]
Get:2 http://deb.devuan.org/merged stable/main bluez-alsa 4.3.1-3 (tar) [249 kB]
Get:3 http://deb.devuan.org/merged stable/main bluez-alsa 4.3.1-3 (diff) [6,076 B]
Fetched 257 kB in 0s (764 kB/s)
dpkg-source: info: extracting bluez-alsa in bluez-alsa-4.3.1
dpkg-source: info: unpacking bluez-alsa_4.3.1.orig.tar.xz
dpkg-source: info: unpacking bluez-alsa_4.3.1-3.debian.tar.xzThe source package is from Debian repository, configured for systemd.
bluez-alsa-utils_4.3.1-3_amd64.debwas compiled for systemd, it does not have any init script inside.
_https://pkginfo.devuan.org/cgi-bin/package-query.html?c=package&q=bluez-alsa-utils=4.3.1-3.1
Package: bluez-alsa-utils
Version: 4.3.1-3.1
Installed-Size: 521
Maintainer: Debian Bluetooth Maintainers <team+pkg-bluetooth@tracker.debian.org>Content
/etc/alsa/conf.d/20-bluealsa.conf
/etc/default/bluez-alsa
/usr/bin/a2dpconf
/usr/bin/bluealsa-aplay
/usr/bin/bluealsa-cli
/usr/bin/bluealsa-rfcomm
/usr/bin/bluealsa
/usr/bin/hcitop
/usr/lib/systemd/system/bluealsa-aplay.service
/usr/lib/systemd/system/bluealsa.service
/usr/share/dbus-1/system.d/bluealsa.conf
/usr/share/doc/bluez-alsa-utils/changelog.Debian.gz
/usr/share/doc/bluez-alsa-utils/copyright
/usr/share/man/man1/bluealsa-aplay.1.gz
/usr/share/man/man1/bluealsa-cli.1.gz
/usr/share/man/man1/bluealsa-rfcomm.1.gz
/usr/share/man/man1/hcitop.1.gz
/usr/share/man/man8/bluealsa.8.gz
It should look like this:
/etc/init.d/bluealsaIn the git version, it is already renamed to "bluealsad"
https://pkginfo.devuan.org/cgi-bin/poli … .68.126.15
An error occurred while reading CGI reply (no response received)Can you post URL of the source package?
BlueALSA is a Bluetooth audio ALSA backend that allows the use of Bluetooth-connected audio devices without the use of PulseAudio or PipeWire.
_https://wiki.debian.org/Bluetooth/Alsa
It's not entirely beyond the wit of the average person to compile BlueALSA without systemd and manage a basic init script.
Step 1: Compile libfreeaptx codec from git.
Step 2: Compile bluez-alsa from git.Tested on Devuan 5 Daedalus:
$ inxi -Sxxx System: Host: devuan Kernel: 6.1.0-41-amd64 arch: x86_64 bits: 64 compiler: gcc v: 12.2.0 Desktop: MATE v: 1.26.0 info: mate-panel wm: marco v: 1.26.1 vt: 7 dm: LightDM v: 1.26.0 Distro: Devuan GNU/Linux 5 (daedalus)
Install compilers, packaging tools, and other useful utilities:
sudo apt-get install build-essential fakeroot git sed awk apt-file command-not-found1. Compile libfreeaptx
Remove the outdated version of libfreeaptx
apt --simulate remove freeaptx-utils libfreeaptx-dev libfreeaptx0sudo apt remove freeaptx-utils libfreeaptx-dev libfreeaptx0This will remove gstreamer1.0-plugins-bad and sayonara, if they are installed. You can reinstall them later if needed.
Create a build directory:
mkdir BUIL1 && cd BUILD1Clone the repository:
git clone https://github.com/regularhunter/libfreeaptx.gitCompile:
$ cd libfreeaptx && makeInstall to ../debdir, that is, BUILD1/debdir
$ make PREFIX=/usr DESTDIR="$(dirname $(pwd))"/debdir installGo back to the BUILD1folder
$ cd .. && ls -1
debdir
libfreeaptxMake a template to run dpkg-shlibdeps
mkdir debian && echo -e "Source: libfreeaptx\nPackage: libfreeaptx\nDepends: \${shlibs:Depends}" >> debian/control Verify:
$ cat debian/control
Source: libfreeaptx
Package: libfreeaptx
Depends: ${shlibs:Depends} Run dpkg-shlibdeps to calculate dependencies
dpkg-shlibdeps -v -xlibfreeaptx -ldebdir/usr/lib --ignore-missing-info -e $(find debdir/usr -type f 2>/dev/null) Check the dependencies calculated by dpkg-shlibdeps
$ cat debian/substvars
shlibs:Depends=libc6 (>= 2.34)This means the Depends field in the DEBIAN/control file you're about to create should be:
Depends: libc6 (>= 2.34)Now you can build the Debian package from debdir.
install -vm0755 -d debdir/DEBIAN $ install -vm0755 -d debdir/DEBIAN
install: creating directory 'debdir/DEBIAN'Create DEBIAN/control with a text editor
nano debdir/DEBIAN/control$ cat debdir/DEBIAN/control
Package: libfreeaptx
Version: 0.2.2
Architecture: amd64
Priority: optional
Section: libdevel
Source: libfreeaptx
Maintainer: Devuan
Installed-Size: 24.6 kB
Conflicts: freeaptx-utils, libfreeaptx-dev, libfreeaptx0
Provides: freeaptx-utils (= 0.2.2), libfreeaptx-dev (= 0.2.2), libfreeaptx0 (= 0.2.2)
Replaces: freeaptx-utils, libfreeaptx-dev, libfreeaptx0
Depends: libc6 (>= 2.34)
Homepage: https://github.com/regularhunter/libfreeaptx
Tag: devel::library, role::devel-lib
Download-Size: 4,596 B
Description: Free implementation of aptX codec.
libfreeaptx is based on version 0.2.0 of libopenaptx with the intent of
continuing under a free license without the additional license restriction
added to libopenaptx 0.2.1.
.
This package contains the binaries, libraries, and header files for libfreeaptx.Generate DEBIAN/md5sums
cd debdir && find . -type f -not -path "./DEBIAN/*" -exec md5sum {} + | sort -k 2 | sed 's/\.\/\(.*\)/\1/' > DEBIAN/md5sumscd .. && chmod 0644 -- debdir/DEBIAN/md5sums Build the Debian package
fakeroot -- dpkg-deb -b debdir libfreeaptx_0.2.2_amd64.deb$ fakeroot -- dpkg-deb -b debdir libfreeaptx_0.2.2_amd64.deb
dpkg-deb: building package 'libfreeaptx' in 'libfreeaptx_0.2.2_amd64.deb'.$ ls *.deb
libfreeaptx_0.2.2_amd64.debInstall
sudo dpkg -i libfreeaptx_0.2.2_amd64.deb Re-install gstreamer1.0-plugins-bad and sayonara if needed
sudo apt install gstreamer1.0-plugins-bad sayonara2. Compile bluez-alsa
Install Buid-Deps:
sudo apt install libfdk-aac-dev libasound2-dev libbluetooth-dev libdbus-1-dev libglib2.0-dev libsbc-dev liblc3-dev libldacbt-enc-dev libldacbt-abr-dev libmp3lame-dev libmpg123-dev libspandsp-dev libopus-dev libbsd-dev libreadline-dev check dbus-daemon python3-docutils pkgconf libudev-dev libell-devClone the source and run autoreconf
mkdir BUILD2 && cd BUILD2git clone https://github.com/arkq/bluez-alsa.gitcd bluez-alsa && autoreconf --installCreate a build directory
mkdir ../build && cd ../buildRun the configure script from within the build directory:
"$(dirname $(pwd))"/bluez-alsa/configure --prefix=/usr --sysconfdir=/etc --with-bluealsaduser=bluealsad --enable-ldac --with-libfreeaptx --enable-aptx --enable-aptx-hd --enable-faststream --enable-lc3-swb --enable-mp3lame --enable-mpg123 --enable-msbc --enable-ofono --enable-midi --enable-opus --enable-upower --enable-rfcomm --enable-a2dpconf --enable-hcitop --enable-test --enable-aac --enable-manpages --disable-systemd Compile
makeInstall to debdir
make DESTDIR="$(dirname $(pwd))"/debdir install$ cd .. && ls -1
bluez-alsa
build
debdirinstall -vDm0644 "$(pwd)"/bluez-alsa/LICENSE "$(pwd)"/debdir/usr/share/licenses/bluez-alsa/LICENSETo run bluealsad daemon on Devuan, you need a simple init script
mkdir -vp debdir/etc/init.dnano debdir/etc/init.d/bluealsad$ cat debdir/etc/init.d/bluealsad
#!/bin/sh
### BEGIN INIT INFO
# Provides: bluealsa
# Required-Start: dbus $syslog $local_fs $remote_fs bluetooth
# Required-Stop: dbus $syslog $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: BlueALSA daemon
# Description: Bluetooth ALSA audio daemon
### END INIT INFO
. /lib/lsb/init-functions
NAME=bluealsad
DESC="BlueALSA daemon"
DAEMON=/usr/bin/$NAME
PIDFILE="/var/run/$NAME.pid"
STORAGE_DIR="/var/lib/bluealsa"
USER="bluealsad"
COMMON_OPTS="--quiet --oknodo --pidfile $PIDFILE"
# Working configuration without low-quality HSP codecs
BA_OPTS="-S -p a2dp-source -p a2dp-sink -p hfp-ag -p hfp-hf -c sbc -c aac -c mp3 -c aptx -c aptx-hd -c faststream -c opus -c msbc -c lc3-swb -c ldac --aac-afterburner --aac-bitrate=320000 --aac-vbr"
# Ensure bluealsad user exists and has correct group membership
if ! id $USER >/dev/null 2>&1; then
# Create user if it doesn't exist
useradd -r -s /bin/false $USER
usermod -a -G audio $USER
else
# User exists, check if in audio group
if ! id -nG $USER | grep -qw audio; then
usermod -a -G audio $USER
fi
fi
# Ensure storage directory exists with correct ownership
if [ ! -d "$STORAGE_DIR" ]; then
mkdir -p "$STORAGE_DIR"
chown $USER:$USER "$STORAGE_DIR"
chmod 0700 "$STORAGE_DIR"
else
# Directory exists, check ownership
CURRENT_OWNER=$(stat -c "%U:%G" "$STORAGE_DIR")
if [ "$CURRENT_OWNER" != "$USER:$USER" ]; then
chown $USER:$USER "$STORAGE_DIR"
chmod 0700 "$STORAGE_DIR"
fi
fi
case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
start-stop-daemon $COMMON_OPTS --start --background \
--make-pidfile --chuid $USER --exec $DAEMON -- $BA_OPTS
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
start-stop-daemon $COMMON_OPTS --stop --retry 5
log_end_msg $?
;;
restart|force-reload)
$0 stop
$0 start
;;
status)
status_of_proc $DAEMON "$NAME" || exit $?
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload|status}" >&2
exit 1
;;
esac
exit 0Make it executable
chmod +x debdir/etc/init.d/bluealsadMake a template
mkdir debian && echo -e "Source: bluez-alsa\nPackage: bluez-alsa\nDepends: \${shlibs:Depends}" >> debian/control $ cat debian/control
Source: bluez-alsa
Package: bluez-alsa
Depends: ${shlibs:Depends}Run dpkg-shlibdeps to calculate dependencies
dpkg-shlibdeps -v -xbluez-alsa -ldebdir/usr/lib --ignore-missing-info -e $(find debdir/usr -type f 2>/dev/null) $ cat debian/substvars
shlibs:Depends=libasound2 (>= 1.1.7), libbluetooth3 (>= 4.91), libbsd0 (>= 0.0), libc6 (>= 2.34), libdbus-1-3 (>= 1.9.14), libfdk-aac2 (>= 2.0.1), libglib2.0-0 (>= 2.67.3), liblc3-0 (>= 1.0.1), libldacbt-abr2 (>= 2.0.2), libldacbt-enc2 (>= 2.0.2), libmp3lame0 (>= 3.100), libmpg123-0 (>= 1.28.0), libncurses6 (>= 6), libopus0 (>= 1.1), libreadline8 (>= 6.0), libsbc1 (>= 2.0), libspandsp2 (>= 0.0.6~pre18), libtinfo6 (>= 6)install -vm0755 -d debdir/DEBIAN Create DEBIAN/control with a text editor
$ cat debdir/DEBIAN/control
Package: bluez-alsa
Version: 4.3.1-86
Architecture: amd64
Priority: optional
Section: utils
Source: bluez-alsa
Maintainer: Devuan
Installed-Size: 541 kB
Conflicts: bluez-alsa-utils, libasound2-plugin-bluez
Provides: bluez-alsa-utils (= 4.3.1-86), libasound2-plugin-bluez (= 4.3.1-86)
Replaces: bluez-alsa-utils, libasound2-plugin-bluez
Depends: libasound2 (>= 1.1.7), libbluetooth3 (>= 4.91), libbsd0 (>= 0.0), libc6 (>= 2.34), libdbus-1-3 (>= 1.9.14), libfdk-aac2 (>= 2.0.1), libglib2.0-0 (>= 2.67.3), liblc3-0 (>= 1.0.1), libldacbt-abr2 (>= 2.0.2), libldacbt-enc2 (>= 2.0.2), libmp3lame0 (>= 3.100), libmpg123-0 (>= 1.28.0), libncurses6 (>= 6), libopus0 (>= 1.1), libreadline8 (>= 6.0), libsbc1 (>= 2.0), libspandsp2 (>= 0.0.6~pre18), libtinfo6 (>= 6)
Homepage: https://github.com/Arkq/bluez-alsa
Description: Bluetooth Audio ALSA Backend (utils)
Bluetooth Audio ALSA Backend allows bluetooth audio without PulseAudio and PipeWire.
This package provides sysVinit script for bluealsad daemon to run it without systemd.
.
This project is a rebirth of a direct integration between Bluez and ALSA.
Since Bluez >= 5, the build-in integration has been removed in favor of 3rd
party audio applications. From now on, Bluez acts as a middleware between an
audio application, which implements Bluetooth audio profile, and a Bluetooth
audio device.Generate DEBIAN/md5sums
cd debdir && find . -type f -not -path "./DEBIAN/*" -exec md5sum {} + | sort -k 2 | sed 's/\.\/\(.*\)/\1/' > DEBIAN/md5sumscd .. && chmod 0644 -- debdir/DEBIAN/md5sums Make a Debian package
fakeroot -- dpkg-deb -b debdir bluez-alsa_4.3.1-86_amd64.deb$ fakeroot -- dpkg-deb -b debdir bluez-alsa_4.3.1-86_amd64.deb
dpkg-deb: building package 'bluez-alsa' in 'bluez-alsa_4.3.1-86_amd64.deb'.$ ls *.deb
bluez-alsa_4.3.1-86_amd64.debInstall
sudo dpkg -i bluez-alsa_4.3.1-86_amd64.debConfigure the bluealsad service to start automatically at system boot
sudo update-rc.d bluealsad defaultsStart the bluealsad service and check its status
sudo service bluealsad start
sudo service bluealsad statusIf, for some strange reason, bluealsad fails to start, restart services in the correct order:
sudo service dbus restart
sudo service dbus status
sudo service bluetooth restart
sudo service bluetooth status
sudo service bluealsad restart
sudo service bluealsad statusOr simply reboot the system to ensure all services start in the proper sequence.
3. Pair and connect Bluetooth headphones using the Blueman Manager
$ apt-cache show blueman | grep Recommends:
Recommends: pulseaudio-module-bluetoothIt will install pulseaudio and/or pipewire. if they are not pinned.
The magic command apt-mark hold can fix the problem
sudo apt-mark hold pulseaudio* pipewire*sudo apt install bluemanPut Headphones in Pairing Mode:
Turn on your headphones and press/hold the power or Bluetooth button until an indicator light flashes (usually blue/red), showing they are discoverable.
Launch Blueman Manager:
blueman-managerSearch for Devices:
In the Blueman Manager window, click the "Search" button (or magnifying glass icon) to scan for devices.
Pair the Headphones:
When your headphones appear, right-click the device name.
Select Pair. Confirm the pairing request if a prompt appears (often within 20 seconds).
Right-click again and select Trust so they connect automatically next time.
Connect and Set Audio Output:
Right-click the device in Blueman and choose Connect
You can do the same with bluetoothctl
bluetoothctl scan on
bluetoothctl pair XX:XX:XX:XX:XX:XX
bluetoothctl trust XX:XX:XX:XX:XX:XX
bluetoothctl connect XX:XX:XX:XX:XX:XX TEST:
alsamixer -D bluealsaaplay -D bluealsa /usr/share/sounds/alsa/Front_Center.wav$ bluealsad --help | grep "Available BT profiles" -A20
Available BT profiles:
- a2dp-source Advanced Audio Source (v1.4)
- a2dp-sink Advanced Audio Sink (v1.4)
- hfp-ofono Hands-Free AG/HF handled by oFono
- hfp-ag Hands-Free Audio Gateway (v1.9)
- hfp-hf Hands-Free (v1.9)
- hsp-ag Headset Audio Gateway (v1.2)
- hsp-hs Headset (v1.2)
- midi Bluetooth LE MIDI (v1.0)
Available BT audio codecs:
a2dp-source: SBC, MP3, AAC, aptX, aptX-HD, FastStream, LDAC, Opus
a2dp-sink: SBC, MP3, AAC, aptX, aptX-HD, FastStream, Opus
hfp-*: CVSD, mSBC, LC3-SWB$ bluealsactl status
Service: org.bluealsa
Version: v4.3.1-86-gf1e53d3
Adapters: hci0
Profiles:
A2DP-source : SBC MP3 AAC aptX aptX-HD FastStream LDAC Opus
A2DP-sink : SBC MP3 AAC aptX aptX-HD FastStream Opus
HFP-AG : CVSD mSBC LC3-SWB
HFP-HF : CVSD mSBC LC3-SWBRight, so not all Bluetooth codecs are supported by your headphones — it’s a bit of a mismatch, really. With the Sennheiser HD 450BT on Linux, you’re limited to SBC, AAC, and aptX, even if the device supports more in other ecosystems.
The bluealsactl output confirms this:
$ bluealsactl codec $(bluealsactl list-pcms | grep a2dpsrc | head -1)
Available codecs: SBC AAC aptX
Selected codec: aptX You can switch to AAC manually:
bluealsactl codec $(bluealsactl list-pcms | grep a2dpsrc | head -1) aac And verify:
$ bluealsactl codec $(bluealsactl list-pcms | grep a2dpsrc | head -1)
Available codecs: SBC AAC aptX
Selected codec: AAC Both aptX and aptX-HD codecs support 16kHz, 32kHz, 44.1kHz, and 48kHz sample rates with BlueALSA.
The aptX-HD implementation in src/a2dp-aptx-hd.c includes:
Sample rate support for 16kHz, 32kHz, 44.1kHz, and 48kHz
24-bit audio support
Encoder and decoder initialization functions
Tested with:
ASUS USB-BT540 Bluetooth 5.4 Dongle Adapter Compatible with Linux
Sennheiser HD 450BT
Specifications:
Bluetooth profiles: AVRCP, A2DP, HFP, HSP
Bluetooth Audio Codecs: AptX, AptX Low Latency, AAC, SBC
$ bluealsa-aplay -L
bluealsa:DEV=80:C3:BA:8B:B6:CC,PROFILE=sco,SRV=org.bluealsa
HD 450BT, trusted audio-headphones, playback
SCO (mSBC): S16_LE 1 channel 16000 Hz
bluealsa:DEV=80:C3:BA:8B:B6:CC,PROFILE=sco,SRV=org.bluealsa
HD 450BT, trusted audio-headphones, capture
SCO (mSBC): S16_LE 1 channel 16000 Hz
bluealsa:DEV=80:C3:BA:8B:B6:CC,PROFILE=a2dp,SRV=org.bluealsa
HD 450BT, trusted audio-headphones, playback
A2DP (aptX): S16_LE 2 channels 48000 Hz$ bluealsa-aplay -l
**** List of PLAYBACK Bluetooth Devices ****
hci0: 80:C3:BA:8B:B6:CC [HD 450BT], trusted audio-headphones
SCO (mSBC): S16_LE 1 channel 16000 Hz
A2DP (aptX): S16_LE 2 channels 48000 Hz
**** List of CAPTURE Bluetooth Devices ****
hci0: 80:C3:BA:8B:B6:CC [HD 450BT], trusted audio-headphones
SCO (mSBC): S16_LE 1 channel 16000 HzALSA config for BlueALSA virtual PCM device
$ cat ~/.asoundrc
defaults.pcm.rate_converter "fftrate"
pcm.!default "bluealsa"MPV Player config:
$ cat ~/.config/mpv/mpv.conf
ao=alsa
alsa-resample=yes
alsa-buffer-time=160000
alsa-periods=4Celluloid:
Open the Preferences menu.
Go to the Miscellaneous tab.
Enter mpv command-line options into the "Extra MPV Options" text box:
ao=alsa alsa-resample=yes alsa-buffer-time=160000 alsa-periods=4Audacious:
go to Preferences → Audio → Buffer size
set 2000 ms
Firefox
about:config
media.cubeb.backend alsa
media.resampling.enabled false
media.cubeb_latency_playback_ms 160TEST:
$ mpv *.dsf
(+) Audio --aid=1 (dsd_lsbf_planar 2ch 1411200Hz)
Input: 1411200 Hz, 2 ch, 's16_le' (0x2): dummy = 0, period = 56448
Output: 48000 Hz, 2 ch, 's16_le' (0x2): dummy = 0, period = 1920
Rates: 1411200 --> 48000 (J: 0.00%, T: FFT, W: Vorbis)
Ok.
AO: [alsa] 1411200Hz stereo 2ch float
(Paused) AV: 00:00:30 / 00:04:35 (11%)$ audacious 2>&1 *.dsf
Input: 1411200 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 352800
Output: 48000 Hz, 2 ch, 's16_le' (0x2): dummy = 0, period = 12000
Rates: 1411200 --> 48000 (J: 0.00%, T: FFT, W: Vorbis)
Ok.$ MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL firefox 2>&1 https://rutube.ru/video/4202a6f411ad55ea7f55e38f860e26bc/ | grep --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware|Input|Output|Rates" | grep -vE "hasVideo=0|hasAudio=0"
[Child 21800: Main Thread]: D/MediaDecoder MediaDecoder[7f5208797000] MetadataLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1
[Child 21800: Main Thread]: D/MediaDecoder MediaDecoder[7f5208797000] FirstFrameLoaded, channels=2 rate=44100 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1
[Child 21800: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 44100
[Child 21800: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 44100$ mpv 2>&1 rudra*
(+) Audio --aid=1 (flac 2ch 44100Hz)
AO: [alsa] 44100Hz stereo 2ch s16
(Paused) A: 00:00:41 / 00:18:33 (4%)It means that sample rate 44100Hz is indeed supported by aptX codec. That is why you do not see logs of fftrate, which is now the default ALSA resampler.
Buffer settings for aplayand arecord
aplay -F 40000 -B 160000 <.wav>
arecord -F 40000 -B 160000 <.wav>$ aplay -F 40000 -B 160000 1_08*
Playing WAVE '1_08-Faust - Funeral March Of A Marionette - DSD256.dsf.wav' : Signed 32 bit Little Endian, Rate 1411200 Hz, Stereo
Input: 1411200 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 56448
Output: 48000 Hz, 2 ch, 's16_le' (0x2): dummy = 0, period = 1920
Rates: 1411200 --> 48000 (J: 0.00%, T: FFT, W: Vorbis)
Ok.Full duplex with mSBC codec
$ arecord -F 40000 -B 160000 -D bluealsa:PROFILE=sco -V mono -f CD | aplay -F 40000 -B 160000 -D bluealsa:PROFILE=sco
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Input: 16000 Hz, 1 ch, 's16_le' (0x2): dummy = 0, period = 640
Output: 44100 Hz, 1 ch, 's16_le' (0x2): dummy = 0, period = 1764
Rates: 16000 --> 44100 (J: 0.00%, T: FFT, W: Vorbis)
Ok.
Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Input: 44100 Hz, 1 ch, 's16_le' (0x2): dummy = 0, period = 1764
Output: 16000 Hz, 1 ch, 's16_le' (0x2): dummy = 0, period = 640
Rates: 44100 --> 16000 (J: 0.00%, T: FFT, W: Vorbis)
Ok.
################################# +| MAXmSBC codec Playback/Capture devices:
**** PLAYBACK Bluetooth Device ****
hci0: 80:C3:BA:8B:B6:CC [HD 450BT], trusted audio-headphones
SCO (mSBC): S16_LE 1 channel 16000 Hz
**** CAPTURE Bluetooth Device ****
hci0: 80:C3:BA:8B:B6:CC [HD 450BT], trusted audio-headphones
SCO (mSBC): S16_LE 1 channel 16000 Hzronn is a Markdown-like format for writing man pages. It's readable and editable in any text editor.
To enable syntax highlighting for .ronn files in VSCodium:
Open the .ronn file.
Click on "Plain Text" in the bottom-right corner of the status bar.
Select "Markdown" from the language list.
This applies Markdown highlighting, which works well for .ronn files.
Alternatively, use the command palette:
Ctrl+Shift+P → "Change Language Mode" → "Markdown".
To convert .ronn files to man format, use the ronn utility available in Devuan repositories:
$ apt-cache search ronn$
ronn - tool to build manuals from Markdown
ruby-ronn - library to build manuals from MarkdownInstall ronn:
sudo apt install ronnConvert .ronn file to man format:
ronn --roff pcm_conv.1.ronnThis generates pcm_conv.1.
View the man page:
man ./pcm_conv.1Example .ronn file:
cat pcm_conv.1.ronnPCM_CONV(1) User Commands PCM_CONV(1)
## NAME
pcm_conv - high-quality FFT/DCT-based audio resampler
## SYNOPSIS
`pcm_conv` [<input_file>] [<output_file>] [ `-c` <number> ] [ `-m` <mask> ] [ `-n` <on>`|`<off> ] [ `-I` <input_order> ] [ `-O` <output_order> ] [ `-b` <bits> ] [ `-f` <frequency> ] [ `-T` <trans> ] [ `-W` <win> ] [ `-v` ] [ `-h | --help | --help-all` ]
## DESCRIPTION
Converts WAVE files with high-quality sample rate conversion using FFT or DCT transforms.
## OPTIONS
`-c`
: Number of output channels: 1 (mono), 2 (stereo), up to 6 (3/2+SW).
`-m`
: Channel mask (may conflict with -c).
`-n`
: Normalize channel matrix (default: on).
`-I` <order>, `-O` <order>
: Input/output channel order: MS, ALSA, or OSS layout.
`-b` <bits>
: Output bit depth: 8, 16, 24, 32, 32f, or 64f.
`-f` <value of sample rate in Hz>, e.g.: `-f` 192000
: Output sample rate. The range of 6000–192000 Hz is 100% reliable. Rates above 192 kHz (e.g., 1411200 Hz) appear reliable based on testing, but require further validation with `pcm_mse`.
`-T` <dct>`|`<fft>
: Transform type (default: dct).
`-W` <vorbis>`|`<hanning>`|`<rect>
: Windowing function (default: vorbis).
`-v`
: Verbose output.
`--help-all`
: Show full help.
## FILES
/etc/fftrate.conf
: System-wide configuration.
~/.asoundrc
: User ALSA configuration (used by fftrate plugin).
## SEE ALSO
arateconf(1), fftrate(7)
## EXAMPLES
Convert a 32-bit WAV file to 192kHz, 32-bit fixed-point using FFT resampling:
$ pcm_conv -f 192000 -b 32 -T fft -v *32bit.wav Faust_192kHz_32bit_downfft.wav
Input file : 08-Faust - Funeral March Of A Marionette - 32bit.wav
Samples rate = 352800 Hz
Channels = 2
Bits per sample = 32 (actual: 32, float)
Output file: Faust_192kHz_32bit_downfft.wav
Samples rate = 192000 Hz
Channels = 2
Bits per sample = 32 (actual: 32, fixed)
FL FR
FL 1.000 -----
FR ----- 1.000
Windows : "Vorbis" (37632 => 20480)
Delay : 107 ms
Sync. : ON
Trans. : "FFT"
[|||||||||||||||||||||||||||||||||||||||||||||||||||] 99.0 %
Ok.
File duration : 275.906667 sec
Processing time: 13.305000 sec ( 4.82% of real time)
## VERSION
1.6.3
## DATE
November 2025Manual:
man ronn-format_https://rtomayko.github.io/ronn/ronn-format.7.html
So after the fox, after the fox
Off to the hunt with chains and locks
So after the fox, after the fox
Someone is always chasing after the foxAfter the Fox (1966) _https://www.youtube.com/watch?v=MrQl0VsQYtw
For the truly dedicated — those who believe their audio deserves better.
🔧 Step 0: Lock It Down
Before we begin, disable the PulseAudio backend - yes, even the rusty one:
Open Firefox and type about:config in the address bar
Search: media.cubeb.backend
Click +, set type to string, value to alsa
Now Firefox talks directly to ALSA - no middlemen.
✅ You’ve just sidestepped a surveillance-level audio framework.
🕵️ Step 1: The Smoking Gun
Firefox uses Speex Resampler under the hood - yes, that Speex.
It’s in the code: AudioConverter.cpp includes <speex/speex_resampler.h>.
_https://searchfox.org/firefox-main/source/dom/media/AudioConverter.cpp
#include "AudioConverter.h" #include <speex/speex_resampler.h>
But here’s the twist:
On macOS, cubeb logs:
Input and output sample-rate match, target rate of 48000Hz
On Linux? Silence.
Nothing. Nada. Zilch.
🚨 Suspicious?
Or is Mozilla simply not telling Linux users what it’s up to?
macOS Firefox logs:
➤ MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL /Applications/Firefox\ Developer\ Edition.app/Contents/MacOS/firefox 2>&1 https://youtu.be/uO6jfQ5tQHM | ggrep --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware" | ggrep -v "hasVideo=0"
[Child 4501: Main Thread]: D/MediaDecoder MediaDecoder[11631e700] MetadataLoaded, channels=2 rate=48000 hasAudio=1 hasVideo=1
[Child 4501: Main Thread]: D/MediaDecoder MediaDecoder[116f37300] MetadataLoaded, channels=2 rate=48000 hasAudio=1 hasVideo=1
[Child 4501: Main Thread]: D/MediaDecoder MediaDecoder[116f37300] FirstFrameLoaded, channels=2 rate=48000 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1
[Child 4501: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 48000
[Parent 4486: AudioIPC Server RPC]: E/cubeb mod.rs:3995: (0x183e31c00) Output hardware description: AudioStreamBasicDescription { mSampleRate: 96000.0, mFormatID: 1819304813, mFormatFlags: 9, mBytesPerPacket: 8, mFramesPerPacket: 1, mBytesPerFrame: 8, mChannelsPerFrame: 2, mBitsPerChannel: 32, mReserved: 0 }
[Parent 4486: AudioIPC Server RPC]: E/cubeb cubeb_resampler_internal.h:553:Input and output sample-rate match, target rate of 48000HzOn macOS, Firefox appears to feign transparency and user-friendliness. But macOS isn’t quite Linux: applications ought to behave in a civilised manner.
Right. So Firefox has its own resampler, and cubeb has its own as well - meaning the audio could be quietly upsampled and downsampled like some sort of secret internal game of pass-the-parcel, and we’d never even see it in the logs.
Typical, really. Everything’s transparent and user-friendly - just not actually.
What's Actually Happening
Resampling: Cubeb uses its Speex-based resampler (cubeb_resampler_internal.h:199–207).
Stream initialisation: Once the target rate is determined, Cubeb sets up the stream at 48kHz (cubeb_resampler_internal.h:561–577).
Passthrough mode: When input and output rates match, Cubeb uses a passthrough resampler that simply forwards buffers without conversion (cubeb_resampler_internal.h:548–556).
The resampler selection logic in cubeb_resampler_create_internal() confirms this:
// Check if rates match - if so, use passthrough
if (((input_params && input_params->rate == target_rate) &&
(output_params && output_params->rate == target_rate)) ||
(input_params && !output_params && (input_params->rate == target_rate)) ||
(output_params && !input_params &&
(output_params->rate == target_rate))) {
LOG("Input and output sample-rate match, target rate of %dHz", target_rate);
return new passthrough_resampler<T>(...);
}When rates don’t match, it creates a cubeb_resampler_speex_one_way for conversion (cubeb_resampler_internal.h:187–203).
🔍 Step 2: The Bit Depth Conspiracy
Firefox converts everything to 32-bit float - internally.
But let’s be honest:
20 years ago, fftrate used 64-bit float.
Today, serious audio tools use 128-bit float or more.
And what does Firefox do?
sample spec: float32le 2ch 48000Hz🧠 32-bit float - the Stone Age of precision.
It works, but is it good enough?
This isn’t about whether it functions — it’s whether it respects the medium. Firefox settles for the lowest common denominator, even when the hardware and OS are ready to do better.
🧩 Step 3: Who’s Really Resampling?
You’ve set:
media.resampling.enabled falseSo Firefox should not resample.
Logs show:
MetadataLoaded, channels=2 rate=48000 hasAudio=1 hasVideo=1
FirstFrameLoaded, channels=2 rate=48000 hasAudio=1 hasVideo=1
CubebStreamInit output stream rate 48000🛠️ Final Command: Firefox logging together with fftrate logging
➤ MOZ_LOG="MediaDecoder:4,cubeb:5" stdbuf -oL firefox 2>&1 https://youtu.be/uO6jfQ5tQHM | grep --line-buffered -E "MetadataLoaded.*rate=|FirstFrameLoaded.*rate=|CubebStreamInit output stream rate|target rate|Output hardware|Input|Output|Rates" | grep -v "hasVideo=0"
[Child 13197: Main Thread]: D/MediaDecoder MediaDecoder[7f6c62c17900] MetadataLoaded, channels=2 rate=48000 hasAudio=1 hasVideo=1
Input: 44100 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 1764
Output: 48000 Hz, 2 ch, 's16_le' (0x2): dummy = 0, period = 1920
Rates: 44100 --> 48000 (J: 0.00%, T: FFT, W: Vorbis)
[Child 13197: Main Thread]: D/MediaDecoder MediaDecoder[7f6c62c17900] FirstFrameLoaded, channels=2 rate=48000 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1
[Child 13197: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 48000
Input: 48000 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 1920
Output: 48000 Hz, 2 ch, 's16_le' (0x2): dummy = 0, period = 1920
Rates: 48000 --> 48000 (J: 0.00%, T: None, W: Planar)This reflects only the observable behaviour. The full pipeline? Still hidden. As usual.
The Mystery Audio Stream
Input: 44100 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 1764The Short and Curious Tale
Right then, here's what's happening: Firefox, being a proper gentleman, politely taps your audio system on the shoulder to ask, "I say, what sample rate do you prefer?" This happens through Cubeb's alsa_get_preferred_sample_rate() function.
Firefox tries 44.1kHz first (a rather common rate, don't you know), but your dmix plugin, being a bit of a stickler for rules, insists on 48kHz. So ALSA’s resampler steps in like a proper butler, converting the stream—even though Firefox requested SND_PCM_NO_AUTO_RESAMPLE.
The "dummy = 0" in your logs? That's just ALSA's way of saying "This is a real device, gov'nor, not some test dummy."
Why This Isn't Your YouTube Video
This probing happens before any actual playback — it's merely Firefox checking what your audio system can handle. The actual YouTube stream will use whatever rate this probe determines (likely 48kHz in your case).
The Bottom Line
Perfectly normal behaviour, old chap! Just Firefox being thorough and your dmix plugin being particular. The resampling you see is ALSA doing its thing, completely separate from Cubeb's internal workings. Your hardware supporting 44.1kHz doesn't stop dmix from having its own ideas about proper audio rates.
Notes
This probe stream mechanism is used across all Cubeb backends for capability detection.
The format 's32_le' is 32-bit signed integer.
The "T: FFT" indicates ALSA's FFT-based resampling algorithm.
"J: 0.00%" shows minimal jitter — the conversion is working rather well indeed.
🧙♂️ Conclusion
We cannot prove Firefox is resampling — because it doesn’t log it.
But we know:
It uses Speex resampler.
It hides resampling logs on Linux.
So is it resampling?
🔎 We may never know.
But we’ll keep watching.
Citations
File: src/cubeb_alsa.c (L1228-1272)
static int
alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
(void)ctx;
int r, dir;
snd_pcm_t * pcm;
snd_pcm_hw_params_t * hw_params;
snd_pcm_hw_params_alloca(&hw_params);
/* get a pcm, disabling resampling, so we get a rate the
* hardware/dmix/pulse/etc. supports. */
r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
SND_PCM_NO_AUTO_RESAMPLE);
if (r < 0) {
return CUBEB_ERROR;
}
r = WRAP(snd_pcm_hw_params_any)(pcm, hw_params);
if (r < 0) {
WRAP(snd_pcm_close)(pcm);
return CUBEB_ERROR;
}
r = WRAP(snd_pcm_hw_params_get_rate)(hw_params, rate, &dir);
if (r >= 0) {
/* There is a default rate: use it. */
WRAP(snd_pcm_close)(pcm);
return CUBEB_OK;
}
/* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */
*rate = 44100;
r = WRAP(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, NULL);
if (r < 0) {
WRAP(snd_pcm_close)(pcm);
return CUBEB_ERROR;
}
WRAP(snd_pcm_close)(pcm);
return CUBEB_OK;
}You may test GTK3 with WhiteSur GTK Dark Theme:
_https://dev1galaxy.org/viewtopic.php?id=7224
Yes. You can find examples here:
_https://dev1galaxy.org/viewtopic.php?pid=59689#p59689
A sort of rather simple command like this:
$ MOZ_LOG="MediaDecoder:4,cubeb:3" firefox 2>&1 https://youtu.be/uO6jfQ5tQHM | awk '!/ATT/ && !/libva/ && !/s=1/ && !/s=0/ && (!/MediaDecoder/ || /rate/)'
[Child 17205: Main Thread]: D/MediaDecoder MediaDecoder[7ff8efdfc600] MetadataLoaded, channels=2 rate=48000 hasAudio=1 hasVideo=1
[Child 17205: MediaSupervisor #2]: I/cubeb media.cubeb.sandbox: true
[Child 17205: Main Thread]: D/MediaDecoder MediaDecoder[7ff8efdfc600] FirstFrameLoaded, channels=2 rate=48000 hasAudio=1 hasVideo=1 mPlayState=PLAY_STATE_LOADING transportSeekable=1
[Child 17205: MediaDecoderStateMachine #1]: I/cubeb CubebStreamInit output stream rate 48000
Input: 48000 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 1920
Output: 44100 Hz, 2 ch, 's32_le' (0xa): dummy = 0, period = 1764
Rates: 48000 --> 44100 (J: 0.00%, T: FFT, W: Vorbis)
Ok.fftrate is a project of Sergei Petrov
_https://github.com/PetrovSE/fftrate
I created man pages to help you with packaging
Info from old pcm-conv:
Short name: FFT-DCT4-MDCT
Long name : FFT, DCT4 and MDCT by Cooley-Tukey, Good-Thomas and Winograd algorithms
Version : 2.0.0.2
Build : Feb 28 2012, 19:47:28, linux-x86_64It seems that the fftrate resampler was created in 2007.
Thank you very much for packaging fftrate.
You can test it with LibreWolf browser.
_https://librewolf.net/installation/debian/
We have a repository for Debian-based distributions (Debian, Ubuntu, Mint, etc.), with which you can easily install and update LibreWolf. To add it to your system and install LibreWolf, run the following commands one by one:sudo apt update && sudo apt install extrepo -ysudo extrepo enable librewolfsudo apt update && sudo apt install librewolf -y
Disable pulse-rust backend
librewolf about:configType into "Search preference name"
media.cubeb.backendSelect "string"
Press "+"
Type "alsa" then Enter
As a result, you have a new entry in Firefox's "hidden preferences"
media.cubeb.backend alsaRe-start LibreWolf.
Zoom Zoom Extension for Browsers → Firefox Add-ons
_https://zoom.us/download
File: pcm_mse.1
.TH PCM_MSE 1 "November 2025" "1.6.3" "User Commands"
.SH NAME
pcm_mse \- compute Mean Squared Error (MSE) between two PCM WAV files
.SH SYNOPSIS
.B pcm_mse
.I <file1.wav> <file2.wav> [resid.wav]
.RB [ " -b " samples ]
.RB [ " -e " samples ]
.BR [ -h | --help | --help-all ]
.SH DESCRIPTION
Computes the MSE between two WAV files, optionally saving the residual (difference) signal.
Ideal for evaluating audio resampling quality.
.SH OPTIONS
.TP
.I file1.wav
First input WAV file.
.TP
.I file2.wav
Second input WAV file.
.TP
.I resid.wav
Optional output file for residual signal.
.TP
.BI \-b " samples"
Start comparison at sample number (default: 0).
.TP
.BI \-e " samples"
End comparison at sample number (default: end of file).
.TP
.B \-\-help-all
Show full help.
.SH EXAMPLE
.B pcm_mse 1.wav 2.wav -b 10 -e 10000000000
.PP
Compares samples 10 to 10,000,000,000.
.SH SEE ALSO
pcm_conv(1), pcm_info(1) File: pcm_info.1
.TH PCM_INFO 1 "November 2025" "1.6.3" "User Commands"
.SH NAME
pcm_info \- display audio format and metadata of a WAV file
.SH SYNOPSIS
.B pcm_info
.I <file.wav>
.SH DESCRIPTION
Displays detailed format information of a WAV file:
.IP
- Sample rate (Hz)
.IP
- Number of channels
.IP
- Bit depth (and actual storage)
.IP
- Channel mask and layout
.IP
- Total samples and duration
.SH OUTPUT
Prints to stdout:
.PP
Parameters:
.br
Samples rate = 192000 Hz
.br
Channels = 2
.br
Bits per sample = 24 (actual: 24, fixed)
.br
Extensible format: ON
.br
Channel mask: 000003 hex, 3 dec
.br
Channels : FL, FR
.br
Samples : 42.080.131
.br
Duration: 3 min 39.167349 sec
.SH EXAMPLE
.B pcm_info HiRes.wav
.SH SEE ALSO
pcm_mse(1), pcm_conv(1) Install Man Pages
sudo cp -v pcm_conv.1 /usr/share/man/man1/
sudo cp -v fftrate.7 /usr/share/man/man7/
sudo cp -v arateconf.1 /usr/share/man/man1/
sudo cp -v pcm_mse.1 /usr/share/man/man1/
sudo cp -v pcm_info.1 /usr/share/man/man1/sudo mandb # Rebuild man database Now you can view them:
man pcm_conv
man fftrate
man arateconf
man pcm_mse
man pcm_info A first draft
File: fftrate.7
.TH FFTRATE 7 "November 2025" "1.6.3" "Miscellaneous"
.SH NAME
fftrate \- FFT-based high-quality ALSA sample rate converter
.SH DESCRIPTION
An ALSA plugin providing high-fidelity sample rate conversion via FFT resampling.
Enable via:
.IP
defaults.pcm.rate_converter "fftrate"
.PP
in ~/.asoundrc or use
.B arateconf
to generate it interactively.
.SH CONFIGURATION
See /etc/fftrate.conf for advanced settings.
.SH FILES
.TP
/etc/fftrate.conf
Global configuration.
.TP
~/.asoundrc
User-specific ALSA config.
.SH AUTHOR
Petrov S.E., https://github.com/PetrovSE/fftrate File: arateconf.1
.TH ARATECONF 1 "November 2025" "1.6.3" "User Commands"
.SH NAME
arateconf \- interactive ALSA configuration tool for fftrate
.SH DESCRIPTION
Generates ~/.asoundrc to use fftrate as the default sample rate converter.
Supports device selection, format setup, and hardware testing.
.SH OPTIONS
No command-line options. Runs interactively.
.SH INTERACTIVE COMMANDS
.TP
0, 1, ...
Select sound card.
.TP
F
Change sample format (bit depth, rate, channels).
.TP
T
Run hardware compatibility test.
.TP
S
Save configuration to ~/.asoundrc.
.TP
ESC
Exit without saving.
.SH FILES
~/.asoundrc \- generated ALSA configuration.
.SH SEE ALSO
fftrate(7), pcm_conv(1), asoundrc(5) File: pcm_conv.1
.TH PCM_CONV 1 "November 2025" "1.6.3" "User Commands"
.SH NAME
pcm_conv \- high-quality FFT/DCT-based audio resampler
.SH SYNOPSIS
.B pcm_conv
.I [input file] [output file]
.RB [ " -c " number ]
.RB [ " -m " mask ]
.RB [ " -n " on|off ]
.RB [ " -I " input_order ]
.RB [ " -O " output_order ]
.RB [ " -b " bits ]
.RB [ " -f " frequency ]
.RB [ " -T " trans ]
.RB [ " -W " win ]
.BR [ -v ]
.BR [ -h | --help | --help-all ]
.SH DESCRIPTION
Converts WAVE files with high-quality sample rate conversion using FFT or DCT transforms.
.SH OPTIONS
.TP
.B \-c
Number of output channels: 1 (mono), 2 (stereo), up to 6 (3/2+SW).
.TP
.B \-m
Channel mask (may conflict with \-c).
.TP
.B \-n
Normalize channel matrix (default: on).
.TP
.BI \-I " order", \-O " order"
Input/output channel order: MS, ALSA, or OSS layout.
.TP
.BI \-b " bits"
Output bit depth: 8, 16, 24, 32, 32f, or 64f.
.TP
.BI \-f " Hz"
Output sample rate (6000–192000 Hz).
.TP
.BI \-T " dct|fft"
Transform type (default: dct).
.TP
.BI \-W " vorbis|hanning|rect"
Windowing function (default: vorbis).
.TP
.B \-v
Verbose output.
.TP
.B \-\-help-all
Show full help.
.SH FILES
.TP
/etc/fftrate.conf
System-wide configuration.
.TP
~/.asoundrc
User ALSA configuration (used by fftrate plugin).
.SH SEE ALSO
arateconf(1), fftrate(7) Install Man Pages
sudo cp -v pcm_conv.1 /usr/share/man/man1/
sudo cp -v fftrate.7 /usr/share/man/man7/
sudo cp -v arateconf.1 /usr/share/man/man1/
sudo mandb # Rebuild man database Now you can view them:
man pcm_conv
man fftrate
man arateconfDescription: Wave PCM utils
This package contains simple utilities for the wave PCM processing.
.
The following utilities are included:
- pcm_info: information of the data format in 'wav' file
- pcm_conv: standalone PCM converter
- pcm_mse: computing of the Mean Squared Error (MSE) between two 'wav' files in PCM format
This package contains utilities for the ALSA configuration.
.
The following utilities are included:
- arateconf: ALSA configurator
Description: ALSA library additional plugin
This package contains plugin for the ALSA library that are
not included in the main libasound2 package.
.
The following plugins are included:
- fftrate: FFT based rate converter
.
Well done on getting fftrate packaged — this is a meaningful step toward better, more independent audio on Linux.
I’d like to gently suggest a small improvement to ensure long-term compatibility: in debian/rules, consider adding a sed pre-build step to handle the min(/max( macro clashes, like so:
override_dh_auto_build:
find . -type f \( -name "*.cpp" -o -name "*.h" \) -exec sed -i -e 's/min(/MIN(/g' -e 's/max(/MAX(/g' {} +
dh_auto_buildThis is a proven, lightweight fix used in other ALSA modules and avoids C++ keyword conflicts without requiring source forks.
Also, if you haven’t already, do take a look at the packaging work already done in /debian/packets/ — the control, *.install, and *.postinst files there are excellent references. It’s great to see collaborative effort turning into real infrastructure.
Thank you again for making high-quality ALSA audio accessible to all.
To @tux_99
You're absolutely right — ALSA's documentation is notoriously opaque, and that confusion has fueled decades of misconceptions. The irony? The default configuration has quietly handled full-duplex and software mixing for years. Tools like fftrate are excellent enhancements — they improve resampling quality (defaults.pcm.rate_converter "fftrate") — but they don’t fix broken audio; they refine an already working system. So while fftrate is a smart tweak for audiophiles, the real issue isn’t the tech — it’s that the documentation never told anyone it already worked.
_https://dev1galaxy.org/viewtopic.php?id=7142
arateconf
_https://dev1galaxy.org/viewtopic.php?id=6644
modified dmix + dsnoop
_https://dev1galaxy.org/viewtopic.php?id=7587
To@Altoid
I know recompiling packages isn’t everyone’s idea of fun, but this one’s worth it. Rebuilding libasound2-plugins without PulseAudio isn’t just a technical tweak — it’s a small act of digital self-determination. No more phantom plugins trying to phone home to a server that isn’t there. No more audio routing chaos. Just clean, direct ALSA control.
And once it’s done, it stays done. No files sneaking back on update. It’s stable, silent, and free.
The process? A few commands, one .deb, and you’re set for life. Freedom isn’t always easy — but it is simple.
_https://dev1galaxy.org/viewtopic.php?id=7523
$ ls -1 /usr/share/alsa/alsa.conf.d
10-rate-lav.conf
10-samplerate.conf
10-speexrate.conf
50-arcam-av-ctl.conf
50-jack.conf
50-oss.conf
60-a52-encoder.conf
60-speex.conf
60-upmix.conf
60-vdownmix.conf
98-usb-stream.conf$ ls -l -1 /etc/alsa/conf.d
total 0
lrwxrwxrwx 1 root root 44 Jul 1 2024 10-rate-lav.conf -> /usr/share/alsa/alsa.conf.d/10-rate-lav.conf
lrwxrwxrwx 1 root root 46 Jul 1 2024 10-samplerate.conf -> /usr/share/alsa/alsa.conf.d/10-samplerate.conf
lrwxrwxrwx 1 root root 45 Jul 1 2024 10-speexrate.conf -> /usr/share/alsa/alsa.conf.d/10-speexrate.conf
lrwxrwxrwx 1 root root 48 Jul 1 2024 50-arcam-av-ctl.conf -> /usr/share/alsa/alsa.conf.d/50-arcam-av-ctl.conf
lrwxrwxrwx 1 root root 40 Jul 1 2024 50-jack.conf -> /usr/share/alsa/alsa.conf.d/50-jack.conf
lrwxrwxrwx 1 root root 39 Jul 1 2024 50-oss.conf -> /usr/share/alsa/alsa.conf.d/50-oss.conf
lrwxrwxrwx 1 root root 47 Jul 1 2024 60-a52-encoder.conf -> /usr/share/alsa/alsa.conf.d/60-a52-encoder.conf
lrwxrwxrwx 1 root root 41 Jul 1 2024 60-speex.conf -> /usr/share/alsa/alsa.conf.d/60-speex.conf
lrwxrwxrwx 1 root root 41 Jul 1 2024 60-upmix.conf -> /usr/share/alsa/alsa.conf.d/60-upmix.conf
lrwxrwxrwx 1 root root 44 Jul 1 2024 60-vdownmix.conf -> /usr/share/alsa/alsa.conf.d/60-vdownmix.conf
lrwxrwxrwx 1 root root 46 Jul 1 2024 98-usb-stream.conf -> /usr/share/alsa/alsa.conf.d/98-usb-stream.conf$ apt-file find 99-pulseaudio-default.conf.example
libasound2-plugins: /etc/alsa/conf.d/99-pulseaudio-default.conf.exampleThe situation possesses all the hallmarks of a particularly British sitcom with a distinct whiff of the absurd: a comedy of errors where everything technically works, yet nothing functions as expected, all because no one is quite looking at the same page — though, ironically, the instructions were printed in bold type for over a decade.
1. The Setup
For years, ALSA has quietly shipped with a fully functional software mixer — powered by dmix, dsnoop, and asym — enabled by default in Debian, Devuan, and their derivatives. Full-duplex audio (simultaneous playback and recording) works out of the box. You can test it with a simple terminal command:
arecord -f cd -V mono | aplay If you see a VU meter dancing merrily across your screen, congratulations — your system has been quietly and competently handling bidirectional audio without any fuss. No configuration required. It just works.
2. The Misunderstanding
Enter the developers of cubeb, the audio backend used by Firefox and other web-centric applications. In their implementation (cubeb_alsa.c:1407–1455), they appear to have treated ALSA not as a mature, self-sufficient audio architecture, but as a bare-bones fallback — a last resort when PulseAudio isn’t available. Rather than querying ALSA’s actual device topology using snd_device_name_hint(), they sidestepped proper enumeration entirely. The result? A single, fictional “default” device is reported — not discovered, but invented.
3. The Irony
Here’s the punchline: cubeb does successfully open and use ALSA’s default PCM device (cubeb_alsa.c:76), which in turn leverages the very same asym plugin that seamlessly routes playback through dmix and capture through dsnoop. The system works — it’s using the full power of ALSA’s default configuration.
Yet, when asked to describe what it’s using, cubeb confidently declares:
- “This device supports 10,000 channels.”
- “Latency? Oh, zero — perfectly instantaneous.”
- “Sample rate? A firm 48,000 Hz, and no questions asked.”
It’s like a waiter who serves you a perfectly cooked meal from a five-star kitchen but insists the dish was microwaved and made for a hundred people.
4. The Cascade
This small fiction snowballs into a full-blown mythos:
- Community wikis (Gentoo, Arch) propagate elaborate ~/.asoundrc configurations, as if the default setup were broken or incomplete — when in fact, most of these configs downgrade functionality, disabling dmix, dsnoop, or full-duplex support.
- Official documentation (e.g., Debian Wiki) points users to /etc/alsa/conf.d/, a directory that often doesn’t exist or isn’t used, while the real defaults live in /usr/share/alsa/.
- The ALSA Wiki itself offers examples that encourage users to reinvent the wheel — often poorly — reinforcing the idea that ALSA needs fixing.
- A widespread belief persists that ALSA cannot function without PulseAudio — despite decades of evidence to the contrary.
5. The Punchline
And so, the final irony:
- YouTube videos play without issue — simple playback, no problem.
- Zoom’s standalone app works flawlessly — it speaks ALSA’s language directly, bypassing the fiction.
- But Zoom’s web client fails — not because the audio system is broken, but because cubeb lies about what it sees. It doesn’t expose the real capabilities of the default device, leaving the web application blind.
It’s a classic farce: the hardware works, the kernel works, ALSA works, and even cubeb sort of works — yet the whole edifice collapses under the weight of misinformation.
The true absurdity lies in the fact that everyone is correct — in their own little world.
- ALSA functions perfectly — proven by aplay and arecord running in tandem.
- cubeb opens the audio device successfully — it’s not failing, it’s just lying about it.
- Zoom’s native app works because it doesn’t rely on this charade — it talks to ALSA like a grown-up.
- Users follow the documentation — only to be led down rabbit holes of unnecessary configuration.
It’s a Monty Python sketch in code form: a perfectly functional system rendered unusable not by bugs or limitations, but by a cascade of assumptions, omissions, and polite fictions. Everyone’s following the rules. The rules are wrong.
The “British sitcom with a touch of absurdity” comparison is apt. This isn’t a tragedy — it’s a comedy of errors. Well-meaning developers, decades of misleading documentation, and a “secret” that was never really hidden — just overlooked — have combined to create the illusion of brokenness.
The solution was there all along: the default ALSA configuration is sufficient. It enables full-duplex, software mixing, and robust device routing. No extra tools, no PulseAudio, no custom configs — just arecord | aplay and a quiet sense of satisfaction.
The real joke? We spent fifteen years fixing something that wasn’t broken — because no one thought to just ask what was already working.
It’s not so much that Firefox can’t handle ALSA, but rather that its cubeb backend takes a somewhat… minimalist approach to device enumeration.
The issue lies in alsa_enumerate_devices(), which, rather than listing actual hardware, returns a single, rather fictional "default" device (cubeb_alsa.c:1408–1456). This isn’t so much discovery as polite suggestion.
The Present Situation
Currently, the backend reports a device with:
- A maximum of 10,000 channels (ambitious, if not grounded in reality),
- Zero latency (one can only dream),
- And a fixed sample rate of 48,000 Hz (cubeb_alsa.c:1430–1451).
Unsurprisingly, Zoom’s web client sees “internal speakers” and “internal mic” — but they’re rather like ghosts: visible, yet impossible to engage with (e.g., Zoom’s web client with Mozilla LibreWolf 145.0.1-2, cubeb's ALSA backend).
What snd_device_name_hint Actually Does
This is ALSA’s proper device discovery function. It returns real devices — hardware, plugins, virtuals — with actual capabilities. It’s what one might reasonably expect to be used.
Why a Complete Rewrite Is in Order
To fix this, the cubeb ALSA backend would need to:
1. Call snd_device_name_hint() — a modest step, but one currently overlooked.
2. Query real hardware parameters — using standard ALSA calls to learn actual channel counts, rates, and latencies (cubeb_alsa.c:1213–1221).
3. Distinguish input, output, and duplex devices — a basic courtesy to the user.
4. Expose the actual ALSA topology — including asymmetric setups using dmix and dsnoop, which many rely on.
Why This Hasn’t Happened
The developers appear to have treated ALSA as a fallback — a bare-bones option for when PulseAudio isn’t available. This overlooks that ALSA, with its default software mixing, is quite capable. The current implementation suffices for playing the odd YouTube video, but falters when full-duplex communication — such as a Zoom call — is required.
For Comparison
The OSS backend in cubeb does properly enumerate devices by probing /dev/sndstat (cubeb_oss.c:344–443). A similar effort for ALSA would be only reasonable.
In Summary
The fix amounts to implementing what the standalone Zoom app already does: using ALSA as it’s meant to be used. The current stub is so far from proper enumeration that a full rewrite — replacing fiction with fact — is the only sensible path forward.
Citations
File: src/cubeb_alsa.c (L73-78)
#define CUBEB_STREAM_MAX 16
#define CUBEB_WATCHDOG_MS 10000
#define CUBEB_ALSA_PCM_NAME "default"
#define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin"File: src/cubeb_alsa.c (L1213-1221)
r = WRAP(snd_pcm_hw_params_any)(stm->pcm, hw_params);
if (r < 0) {
return CUBEB_ERROR;
}
r = WRAP(snd_pcm_hw_params_get_channels_max)(hw_params, max_channels);
if (r < 0) {
return CUBEB_ERROR;
}
File: src/cubeb_alsa.c (L1408-1456)
static int
alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
{
cubeb_device_info * device = NULL;
if (!context)
return CUBEB_ERROR;
uint32_t rate, max_channels;
int r;
r = alsa_get_preferred_sample_rate(context, &rate);
if (r != CUBEB_OK) {
return CUBEB_ERROR;
}
r = alsa_get_max_channel_count(context, &max_channels);
if (r != CUBEB_OK) {
return CUBEB_ERROR;
}
char const * a_name = "default";
device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
assert(device);
if (!device)
return CUBEB_ERROR;
device->device_id = a_name;
device->devid = (cubeb_devid)device->device_id;
device->friendly_name = a_name;
device->group_id = a_name;
device->vendor_name = a_name;
device->type = type;
device->state = CUBEB_DEVICE_STATE_ENABLED;
device->preferred = CUBEB_DEVICE_PREF_ALL;
device->format = CUBEB_DEVICE_FMT_S16NE;
device->default_format = CUBEB_DEVICE_FMT_S16NE;
device->max_channels = max_channels;
device->min_rate = rate;
device->max_rate = rate;
device->default_rate = rate;
device->latency_lo = 0;
device->latency_hi = 0;
collection->device = device;
collection->count = 1;
return CUBEB_OK;File: src/cubeb_oss.c (L344-443)
static int
oss_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
{
cubeb_device_info * devinfop = NULL;
char * line = NULL;
size_t linecap = 0;
FILE * sndstatfp = NULL;
int collection_cnt = 0;
int is_ud = 0;
int skipall = 0;
devinfop = calloc(1, sizeof(cubeb_device_info));
if (devinfop == NULL)
goto fail;
sndstatfp = fopen("/dev/sndstat", "r");
if (sndstatfp == NULL)
goto fail;
while (getline(&line, &linecap, sndstatfp) > 0) {
const char * devid = NULL;
struct sndstat_info sinfo;
oss_audioinfo ai;
if (!strncmp(line, SNDSTAT_FV_BEGIN_STR, strlen(SNDSTAT_FV_BEGIN_STR))) {
skipall = 1;
continue;
}
if (!strncmp(line, SNDSTAT_BEGIN_STR, strlen(SNDSTAT_BEGIN_STR))) {
is_ud = 0;
skipall = 0;
continue;
}
if (!strncmp(line, SNDSTAT_USER_BEGIN_STR,
strlen(SNDSTAT_USER_BEGIN_STR))) {
is_ud = 1;
skipall = 0;
continue;
}
if (skipall || isblank(line[0]))
continue;
if (oss_sndstat_line_parse(line, is_ud, &sinfo))
continue;
devinfop[collection_cnt].type = 0;
switch (sinfo.type) {
case CUBEB_DEVICE_TYPE_INPUT:
if (type & CUBEB_DEVICE_TYPE_OUTPUT)
continue;
break;
case CUBEB_DEVICE_TYPE_OUTPUT:
if (type & CUBEB_DEVICE_TYPE_INPUT)
continue;
break;
case 0:
continue;
}
if (oss_probe_open(sinfo.devname, type, NULL, &ai))
continue;
devid = oss_cubeb_devid_intern(context, sinfo.devname);
if (devid == NULL)
continue;
devinfop[collection_cnt].device_id = strdup(sinfo.devname);
asprintf((char **)&devinfop[collection_cnt].friendly_name, "%s: %s",
sinfo.devname, sinfo.desc);
devinfop[collection_cnt].group_id = strdup(sinfo.devname);
devinfop[collection_cnt].vendor_name = NULL;
if (devinfop[collection_cnt].device_id == NULL ||
devinfop[collection_cnt].friendly_name == NULL ||
devinfop[collection_cnt].group_id == NULL) {
oss_free_cubeb_device_info_strings(&devinfop[collection_cnt]);
continue;
}
devinfop[collection_cnt].type = type;
devinfop[collection_cnt].devid = devid;
devinfop[collection_cnt].state = CUBEB_DEVICE_STATE_ENABLED;
devinfop[collection_cnt].preferred =
(sinfo.preferred) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
devinfop[collection_cnt].format = CUBEB_DEVICE_FMT_S16NE;
devinfop[collection_cnt].default_format = CUBEB_DEVICE_FMT_S16NE;
devinfop[collection_cnt].max_channels = ai.max_channels;
devinfop[collection_cnt].default_rate = OSS_PREFER_RATE;
devinfop[collection_cnt].max_rate = ai.max_rate;
devinfop[collection_cnt].min_rate = ai.min_rate;
devinfop[collection_cnt].latency_lo = 0;
devinfop[collection_cnt].latency_hi = 0;
collection_cnt++;
void * newp =
reallocarray(devinfop, collection_cnt + 1, sizeof(cubeb_device_info));
if (newp == NULL)
goto fail;
devinfop = newp;
}Zoom works with ALSA out of the box because it directly interfaces with ALSA’s native audio API on Linux, bypassing the need for PulseAudio or compatibility layers like apulse.
The Zoom Workplace client is built to use ALSA as a first-class audio backend. As long as your system’s ALSA configuration supports full-duplex audio (simultaneous playback and recording), Zoom can access the microphone and speakers without additional tools.
Debian, Devuan, and many other Linux distributions enable full-duplex by default in their ALSA setup — meaning no user configuration is required. Tools like arecord and aplay can test this:
arecord -f cd -V mono | aplayIf you see a VU meter, full-duplex is active — and Zoom will work immediately.
Zoom does not rely on dmix or dsnoop plugins unless custom configurations demand them. It accesses the default PCM devices directly, which is why it functions reliably on minimal or pure ALSA systems.
Zoom does not enable an internal full-duplex mode if your ALSA configuration lacks it.
Zoom relies entirely on the underlying ALSA setup to provide full-duplex audio (simultaneous playback and recording). If your sound card or ALSA configuration does not support full-duplex — either through hardware capabilities or proper software configuration (e.g., using dmix and dsnoop, or a full-duplex PCM device) — Zoom cannot create that functionality on its own
However, Zoom can work around certain limitations when:
- The default PCM device is correctly configured (e.g., using plughw or a plug-type device).
- Full-duplex is achievable at the driver or configuration level.
But if your sound system cannot perform full-duplex audio (as tested via arecord | aplay), Zoom will not function properly, and you may experience one-way audio or dropped streams.
In short: Full-duplex must be enabled at the ALSA level — Zoom won’t add it if it’s missing.
Zoom Workplace for Linux works seamlessly with ALSA (Advanced Linux Sound Architecture) without requiring additional sound servers such as PulseAudio. In most cases, no special configuration is needed — even with custom ALSA setups.
You can download Zoom Workplace from:
_https://zoom.us/download
Compatibility Overview
✅ Works out of the box with default ALSA configurations.
✅ Compatible with modified ALSA setups (e.g., those using fftrate resampling).
✅ Functions correctly with ALSA configurations generated by tools like arateconf.
🔑 Full-duplex audio support must be enabled in your ALSA configuration for optimal performance.
How to Use Zoom with Default ALSA Settings
Even if you're new to Linux audio configuration, rest assured — everything should work straight away. Follow these steps to confirm your setup:
1. Ensure Default ALSA Configuration Is Active
If you've previously created a custom configuration file, remove it temporarily:
rm ~/.asoundrc
This ensures you’re using the standard system-wide ALSA settings (as provided by Debian, Devuan, or your distribution).
2. Identify Your Sound Card
List available sound cards:
cat /proc/asound/cards
Or check card IDs directly:
cat /sys/class/sound/card*/id
Note the name (e.g., PCH, system) — you’ll need this if your system has multiple audio devices.
3. Set the Default Sound Card (Optional – For Multi-Card Systems)
If you have more than one sound card, create a user-level configuration file:
nano ~/.asoundrc
Add the following lines, replacing PCH with your actual card name:
defaults.pcm.!card PCH
defaults.ctl.!card PCH
4. Connect a Microphone
Plug in an external microphone or ensure the built-in mic is available.
5. Adjust Microphone Volume
Open the ALSA mixer:
alsamixer
- Press F4 to switch to capture (recording) controls.
- Use the arrow keys to increase the microphone level.
- Press Esc to exit.
6. Test Full-Duplex Audio
Verify that your system can play and record simultaneously:
arecord -f cd -V mono | aplay
You should see a real-time VU meter indicating activity:
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
###############+ | 29%
This confirms bidirectional audio is functioning.
7. Exit the Test
Press Ctrl+C to stop the recording and playback.
💡 Since this test uses the default audio device, successful completion strongly suggests Zoom will work without issues.
Installing and Testing Zoom
1. Install Zoom Workplace for Linux from the official website. _https://zoom.us/download
2. Launch it from the terminal:
zoom
3. Go to Settings > Audio.
4. Use the Test Microphone button to verify input and output.
Both microphone and speakers should function correctly with no further configuration.
📝 Note: Tools like apulse (a PulseAudio compatibility layer) are *not required*. Zoom interacts directly with ALSA and performs well without emulation layers.
With this setup, Zoom Workplace delivers reliable audio performance on pure ALSA systems—ideal for lightweight or minimal Linux installations.
librewolf? Try
MOZ_LOG="cubeb:5" librewolf 2>&1 about:supportIt seems that Zoom does not like Firefox Nightly
Zoom may not be supported on your browser
Copy the link below and paste it in Safari, Chrome, Firefox, Edge, or Opera
Installed Zoom Workplace for Linux. It works with ALSA out of the box.
Zoom app > Settings > Audio > Test microphone
apulse is not needed
Zoom Workplace for Linux
_https://zoom.us/download
Zoom Workplace app also works with ALSA on Linux Mint.
hot-plug? Do you mean jack sensing?
Jack sensing is a feature in audio devices that automatically detects when a plug, such as headphones or an auxiliary cable, is inserted or removed from the audio jack. This detection triggers an event, like muting the internal speakers or prompting the user to select an audio output. It is primarily implemented using either mechanical switches or impedance-based measurements.
Run
alsamixerand set everything right.
about:support
Name Firefox Nightly
Version 146.0a1
Build ID 20251108203729
...
Audio Backend alsa
Max Channels 10000
Preferred Sample Rate 48000
Roundtrip latency (standard deviation) NaNms (NaN)$ MOZ_LOG="cubeb:5" firefox-nightly 2>&1 about:support
[Parent 14786: Main Thread]: V/cubeb media.cubeb.force_null_context: false
[Parent 14786: Main Thread]: V/cubeb media.cubeb.sandbox: true
ATTENTION: default value of option mesa_glthread overridden by environment.
ATTENTION: default value of option mesa_glthread overridden by environment.
ATTENTION: default value of option mesa_glthread overridden by environment.
[Child 14912: Main Thread]: V/cubeb media.cubeb.force_null_context: false
[Child 14912: Main Thread]: V/cubeb media.cubeb.sandbox: true
[Child 14912: Main Thread]: W/cubeb could not set real-time limit in CubebUtils::InitLibrary
[Parent 14786: Main Thread]: D/cubeb Starting cubeb server...
[Parent 14786: Main Thread]: D/cubeb media.audioipc.shm_area_size: 0
JavaScript warning: resource://gre/modules/Troubleshoot.sys.mjs, line 723: WebGL context was lost.
JavaScript warning: resource://gre/modules/Troubleshoot.sys.mjs, line 723: WebGL context was lost.
[Parent 14786: Main Thread]: I/cubeb media.cubeb.sandbox: true
[Parent 14786: Main Thread]: D/cubeb media.audioipc.shm_area_size: 0
[Parent 14786: Main Thread]: D/cubeb media.audioipc.stack_size: 262144
[Parent 14786: AudioIPC Server RPC]: E/cubeb cubeb.c:727:DeviceID: "default" (PREFERRED)
Name: "default"
Group: "default"
Vendor: "default"
Type: output
State: enabled
Maximum channels: 10000
Format: S16LE (0x10) (default: S16LE)
Rate: [48000, 48000] (default: 48000)
Latency: lo 0 frames, hi 0 frames
[Parent 14786: Main Thread]: E/cubeb cubeb.c:727:DeviceID: "default" (PREFERRED)
Name: "default"
Group: "default"
Vendor: "default"
Type: output
State: enabled
Maximum channels: 10000
Format: S16LE (0x10) (default: S16LE)
Rate: [48000, 48000] (default: 48000)
Latency: lo 0 frames, hi 0 frames
[Parent 14786: AudioIPC Server RPC]: E/cubeb cubeb.c:727:DeviceID: "default" (PREFERRED)
Name: "default"
Group: "default"
Vendor: "default"
Type: input
State: enabled
Maximum channels: 10000
Format: S16LE (0x10) (default: S16LE)
Rate: [48000, 48000] (default: 48000)
Latency: lo 0 frames, hi 0 frames
[Parent 14786: Main Thread]: E/cubeb cubeb.c:727:DeviceID: "default" (PREFERRED)
Name: "default"
Group: "default"
Vendor: "default"
Type: input
State: enabled
Maximum channels: 10000
Format: S16LE (0x10) (default: S16LE)
Rate: [48000, 48000] (default: 48000)
Latency: lo 0 frames, hi 0 frames
[Child 14981: Main Thread]: V/cubeb media.cubeb.force_null_context: false
[Child 14981: Main Thread]: V/cubeb media.cubeb.sandbox: true
[Child 14981: Main Thread]: W/cubeb could not set real-time limit in CubebUtils::InitLibrary
[Parent 14786: Main Thread]: D/cubeb media.audioipc.shm_area_size: 0
[Child 15006: Main Thread]: V/cubeb media.cubeb.force_null_context: false
[Child 15006: Main Thread]: V/cubeb media.cubeb.sandbox: true
[Child 15006: Main Thread]: W/cubeb could not set real-time limit in CubebUtils::InitLibrary
[Parent 14786: Main Thread]: D/cubeb media.audioipc.shm_area_size: 0
...
[Parent 14786: BackgroundThreadPool #1]: D/cubeb Default devices latency in seconds -nan (stddev: -nan)Firefox 58 (Nightly) tightened its sandbox a bit more. Now ioctl() calls are forbidden too, but are used by ALSA libraries. That causes sandbox violation with subsequent process termination. Exception can be added by setting parameter security.sandbox.content.syscall_whitelist in about:config. That field accepts a comma separated list of system call numbers. Add there 16 for x86-64, or 54 for x86 or ARM.
Firefox 60 tighened its content sandbox more, but at the same time moved audio accesses from content processes to the main process. From Firefox 60 onwards no changes to the sandbox settings are necessary.
_https://github.com/i-rinat/apulse
What Logs Show
Firefox Nightly logs confirm the Firefox 60+ architecture change is working correctly:
[Parent 14786: Main Thread]: D/cubeb Starting cubeb server...
[Parent 14786: AudioIPC Server RPC]: E/cubeb cubeb.c:727:DeviceID: "default"The key evidence:
1. Audio runs in the parent process: Notice [Parent 14786: Main Thread] and [Parent 14786: AudioIPC Server RPC] - audio device enumeration happens in the parent process, not child processes
2. AudioIPC architecture: The log shows Starting cubeb server... and AudioIPC Server RPC, which is Firefox's audio IPC (Inter-Process Communication) system that isolates audio in a separate server process
3. Sandbox is enabled but doesn't block audio: media.cubeb.sandbox: true in both parent and child processes, yet audio devices are successfully enumerated (both input and output show as State: enabled)
4. No sandbox override needed: Unlike your ESR logs where Firefox forced the sandbox setting, Nightly respects the sandbox while still allowing audio access through the AudioIPC architecture
Why This Works in Nightly but Not ESR
The difference between Firefox 140.5.0 ESR and Nightly:
- ESR (logs): Shows [Child 12906: Main Thread]: E/cubeb media.cubeb.sandbox: false, but content sandbox enabled - forcing true, indicating audio was being accessed from child processes where the sandbox blocked it
- Nightly (current logs): Audio access happens in [Parent 14786: AudioIPC Server RPC], completely bypassing the content process sandbox issue
This confirms that Firefox ESR 140.5.0 may have regressed or not fully implemented the Firefox 60+ audio architecture, while Nightly has it working correctly.
What This Means for Cubeb
From Cubeb's perspective, nothing changed - the ALSA backend code is identical in both cases :
- It still opens devices with snd_pcm_open() [link to Repo mozilla/cubeb: src/cubeb_alsa.c:1050-1051]
- It still enumerates devices the same way [link to Repo mozilla/cubeb: src/cubeb_alsa.c:1430-1451]
- The latency reporting still returns 0 due to ALSA's dmix/dsnoop limitations [link to Repo mozilla/cubeb: src/cubeb_alsa.c:1450-1451]
The difference is where Firefox calls this code - in the parent process (Nightly) vs. sandboxed child processes (your ESR).
Notes
Nightly logs prove that the microphone issue in ESR is indeed a Firefox sandbox architecture problem, not a Cubeb bug. The Cubeb ALSA backend works perfectly when Firefox's AudioIPC architecture is properly implemented.