From 42354a1f07ee3c3e9b3fc69b8b011b31380f0be9 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Fri, 10 Jul 2026 23:07:14 -0500 Subject: docs: triage the audio failure catalogue into remedy classes I sorted both directions into nine symptom clusters each, every entry tagged Auto, Privileged, Reboot-tail, or Guide under the privilege model. The clusters mirror the doctor's probe ladder, so the boundary between what it fixes and what it can only guide falls along the tiers it already probes. Two findings are worth keeping. The buildable core reduces to five primitives on both sides (set-default, set-card-profile, unmute, restart-services, config-drop-in), so the breadth is in the diagnosis, not the remedies. And the clusters imply three new read-only probes: dmesg-pattern hints, an unmute-doesn't-stick signature, and a re-probe-after-idle. --- docs/design/2026-07-10-audio-failure-taxonomy.org | 210 +++++++++++++++++++++- 1 file changed, 206 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/design/2026-07-10-audio-failure-taxonomy.org b/docs/design/2026-07-10-audio-failure-taxonomy.org index ffaee42..c504f81 100644 --- a/docs/design/2026-07-10-audio-failure-taxonomy.org +++ b/docs/design/2026-07-10-audio-failure-taxonomy.org @@ -94,6 +94,111 @@ Built 2026-07-10 from a parallel research sweep across eight layers (kernel/driv - XDG_RUNTIME_DIR unset in ssh/cron/non-login session (no). Recording works in the desktop terminal but fails from ssh or cron. Non-login sessions skip pam_systemd, so the socket in =/run/user/= isn't found. Fix: run as the seat user with =XDG_RUNTIME_DIR= set. [[https://bbs.archlinux.org/viewtopic.php?id=286176][arch 286176]] - Auto-gain (AGC) pulling the mic level toward zero (no). The mic gets progressively quieter; something keeps lowering the input volume. An app or WirePlumber is allowed to auto-adjust the source gain and only ratchets down. Fix: a WirePlumber access rule blocking the client from changing gain. [[https://www.lumeh.org/wiki/audio/stop-adjusting-my-microphone/][lumeh]] +* Input triage + +The 58 input failure modes sorted into nine symptom clusters, each owned by a probe the doctor runs (or would run). Every entry carries a remedy class: + +- *AUTO* — user-scope, reversible, runs anywhere and on any machine. The existing Auto/Confirm tiers apply. +- *PRIV* — needs sudo. Runs silently where passwordless sudo exists (every archsetup install), prompts on a CLI with a tty, degrades to Guide only in a GUI with neither. Defaults to Confirm/Arm, never silent Auto. +- *REBOOT-TAIL* — the applicable part runs (often PRIV), then the doctor instructs the reboot it cannot complete or verify past. +- *GUIDE* — nothing to run: a physical action, a BIOS setting, an app-permission the doctor can't grant, or a wait-for-upstream fix. Diagnose and print the instruction. + +Rough distribution: ~26 AUTO, ~6 PRIV, ~6 REBOOT-TAIL, ~20 GUIDE (a handful are AUTO now plus PRIV to persist). The buildable share (AUTO plus the runnable part of PRIV/REBOOT-TAIL) is a little over half. Triage is a proposal — correct the class on any line. + +** Cluster 1 — no capture device at all +Probe: kernel =/proc/asound= plus the unit tier. New work: dmesg-pattern hints would let the doctor name the specific cause (firmware-load-fail, NHLT-missing) rather than a generic "no hardware". + +- Missing SOF firmware :: REBOOT-TAIL — install =sof-firmware= (PRIV), reboot to finish, verify after. +- Stale dmic_detect / dsp_driver modprobe override :: REBOOT-TAIL — remove the override, rebuild initramfs (PRIV), reboot. +- Missing codec model / ADC quirk :: REBOOT-TAIL — the =model=...= modprobe workaround (PRIV) plus reboot; GUIDE the real kernel-quirk fix. +- Kernel regression broke the mic :: GUIDE — boot the previous kernel or install linux-lts; the doctor can't choose your boot kernel. +- AMD ACP6x DMIC DMI quirk :: GUIDE — needs a newer kernel carrying the board quirk; nothing to run. +- Missing NHLT ACPI table :: GUIDE — flash firmware that supplies NHLT; out of reach. +- New AMD ACP7.0 unsupported :: GUIDE — wait for the kernel plus UCM support. +- Focusrite Scarlett needs a quirk :: REBOOT-TAIL — =device_setup= modprobe (PRIV) plus reboot on old kernels; GUIDE "run kernel >= 6.7" otherwise. + +** Cluster 2 — device present but records silence +Probe: semantic tier (source mute, volume, pin, default). This is the input mirror of the output side's silenced and stale-default rules. + +- Wrong card profile, no input route :: AUTO — =set-card-profile= to a duplex/input profile. +- Capture / Internal Mic switch muted :: AUTO — unmute via wpctl/pactl (and the ALSA capture switch via amixer). +- Input Source enum on the wrong pin :: AUTO — set Input Source to the connected pin. +- alsactl restores a muted capture at boot :: AUTO + PRIV — unmute now (AUTO); persist with =sudo alsactl store= (PRIV). +- Auto-Mute Mode disabling capture :: AUTO — set Auto-Mute Disabled. +- Wrong default capture in .asoundrc :: AUTO — set the right default source (the PipeWire-era fix, not the legacy file). +- Default source is a monitor / wrong device :: AUTO — =set-default= to the real mic, pin it. +- Capture source muted on startup (stale WP state) :: AUTO — unmute; clear the stale WirePlumber state. +- Filter / virtual source shadows the real mic :: AUTO — =set-default= to the real source, or fix node.target. + +** Cluster 3 — works but too quiet +Probe: semantic volume/gain. + +- Internal Mic Boost at zero :: AUTO — raise Mic Boost. +- Auto-gain (AGC) pulling the level to zero :: AUTO — a WirePlumber rule blocking the client from changing gain. + +** Cluster 4 — works then dies or drops out +Probe: node state over time / suspend. New work: a re-probe-after-idle check to catch the suspend cases. + +- Node suspended, never resumes :: AUTO — =session.suspend-timeout-seconds = 0= drop-in plus restart. +- Forced or mismatched sample rate :: AUTO — =allowed-rates= config plus restart. +- USB autosuspend powers the mic down :: PRIV — a udev =power/control=on= rule (or =usbcore.autosuspend=-1=, REBOOT-TAIL). +- Intermittent USB disconnect (URB resets) :: GUIDE — swap the cable or use a direct port; physical. +- USB bus/hub can't supply power :: GUIDE — a powered hub or the interface PSU; physical. +- Mic dead after boot until replug :: GUIDE — enable ErP in BIOS so the port power-cycles; physical/BIOS. + +** Cluster 5 — the sound server lost a device the hardware has +Probe: kernel-vs-graph (the mic-unrecognized rule) plus WirePlumber liveness. This is where the spec's coarse set-emptiness rule lives. + +- ACP vs UCM detection mismatch :: AUTO — set =use-acp=/=use-ucm= plus restart wireplumber. +- Missing UCM capture profile :: AUTO — fall back to ACP; GUIDE the UCM patch/install. +- WirePlumber fails to start on old config :: AUTO — remove the user drop-in plus restart (PRIV if the file is under =/etc=). +- pipewire-pulse dead or wrong socket :: AUTO — restart the user services; check XDG_RUNTIME_DIR. +- Broken ~/.config/wireplumber override :: AUTO — remove/repair the drop-in plus restart. +- WirePlumber cached a no-input profile (USB) :: AUTO — =set-card-profile input:...=; clear stale state if it won't stick. + +** Cluster 6 — Bluetooth mic +Probe: bluez card profile plus codec. Needs the graph-first gate from the review so a BT mic never reads as no-mic-hardware. + +- Headset stuck in A2DP, no mic :: AUTO — =set-card-profile headset-head-unit=. +- oFono installed but unconfigured :: PRIV — remove oFono (or set the native backend in config, AUTO). +- mSBC codec not enabled :: AUTO — codecs config plus restart wireplumber. +- Auto-switch to headset profile never triggers :: AUTO — set-card-profile / autoswitch config. +- Codec negotiation drops to baseline :: AUTO — pin codec order. +- Adapter rejects hsp_ag plus hfp_ag together :: AUTO — restrict roles in config. +- BT mic invisible to arecord :: GUIDE — not a fault; it's a PipeWire node. The graph-first gate prevents a false no-mic-hardware here. +- Switching to the mic profile tanks playback :: GUIDE — expected A2DP/HFP tradeoff. + +** Cluster 7 — stack healthy, an app can't hear me +Probe: none in-stack. This is the tail on a healthy verdict: name the likely app/portal cause and print the fix. The Chrome incident lives here. + +- Wayland browser returns an empty mic list :: GUIDE — the WebRTC/PipeWire launch flags or browser prefs. +- Flatpak app lacks device permission :: GUIDE — print =flatpak override --user --device=all =. +- Flatpak app has no audio socket :: GUIDE — print =flatpak override --user --socket=pulseaudio =. +- Snap audio-record interface not connected :: GUIDE — print =sudo snap connect :audio-record=. +- xdg-desktop-portal not started under Hyprland :: GUIDE — fix the Hyprland startup env / session target. +- Permission-store cached a deny :: GUIDE — print =flatpak permission-reset =. +- Browser per-site mic block :: GUIDE — the browser's site settings. +- An app grabs the mic exclusively :: GUIDE — change the app to shared capture. + +** Cluster 8 — hardware / physical +Probe: kernel tier plus a signature test (issue an unmute, re-probe; a mute that doesn't clear is a hardware switch). + +- Hardware mic-mute / privacy kill-switch :: GUIDE — flip the physical switch; the doctor detects the unmute-doesn't-stick signature. +- Laptop mic-mute key not honored (F20) :: GUIDE — bind the key to a pactl toggle (a config change). +- Combo jack keeps the internal mic selected :: AUTO + PRIV — select Headset Mic (AUTO); the =model== option to persist (PRIV, reboot). +- Jack retasked to the wrong function :: REBOOT-TAIL — hdajackretask override plus install plus reboot. +- TRRS wiring mismatch (CTIA/OMTP) :: GUIDE — a wiring adapter; physical. +- Broken / loose 3.5mm jack :: GUIDE — physical/hardware. +- Webcam's built-in mic is a separate device :: AUTO + PRIV — =set-default= away from the webcam (AUTO); a udev blacklist to persist (PRIV). +- BIOS/UEFI disables the internal mic :: GUIDE — re-enable it in BIOS; physical/firmware. + +** Cluster 9 — environment / config +Probe: process plus environment checks (a leftover pulseaudio process, XDG_RUNTIME_DIR, group membership). + +- PulseAudio and pipewire-pulse both installed :: PRIV — remove pulseaudio, set =autospawn = no=. +- User lacks realtime privileges / RTKit :: PRIV — =limits.d= plus the realtime group (relogin/reboot). +- XDG_RUNTIME_DIR unset in ssh/cron :: GUIDE — set it in the non-login context; environment-specific. + * Output (speakers / headphones) failure modes Built 2026-07-10 from a parallel sweep across eight output-specific layers (kernel/driver/firmware, ALSA mixer/pin, PipeWire routing, Bluetooth, HDMI/DisplayPort, USB DAC + hardware/BIOS, app/browser, and audio quality: crackle/xrun/latency). 64 raw entries deduped to 59 distinct root causes. Same fields and =sudo/reboot= triage signal as the input section. @@ -181,10 +286,107 @@ Built 2026-07-10 from a parallel sweep across eight output-specific layers (kern - HDA codec power-save clicks and BT-latency hits (yes). Loud click/pop on first play after idle on internal HDA; also wrecks Bluetooth latency. =snd_hda_intel power_save= cuts codec power and clicks on wake. Fix: =options snd_hda_intel power_save=0 power_save_controller=N= in =/etc/modprobe.d/=. [[https://forums.linuxmint.com/viewtopic.php?t=186531][mint 186531]] - Insufficient ALSA headroom on an emulated/quirky device (no). Constant fine crackle from a sink whose hardware pointer PipeWire can't track (VM audio, cheap onboard). The default small =api.alsa.headroom= leaves no slack. Fix: a WirePlumber ALSA rule adding =api.alsa.headroom = 2048=. [[https://pipewire.pages.freedesktop.org/wireplumber/daemon/configuration/alsa.html][wp alsa config]] -* Sources - -Every entry carries its source inline as an org link. Reports span the Arch/Manjaro/EndeavourOS/Fedora/Ubuntu/Mint/openSUSE forums, askubuntu, the PipeWire and WirePlumber docs, ALSA and Flatpak issue trackers, kernel bug trackers, and vendor forums (NVIDIA, Dell). Collected 2026-07-10. +* Output triage + +The 59 output failure modes sorted into nine symptom clusters, same remedy classes as the input triage (AUTO / PRIV / REBOOT-TAIL / GUIDE — see that section's legend). The clusters shift for output: no mic-specific groups, but HDMI/DisplayPort and degraded-audio (crackle/xrun) each earn a dedicated cluster, and speaker-vs-headphone jack routing replaces the input pin cluster. + +Rough distribution: ~24 AUTO, ~8 PRIV, ~11 REBOOT-TAIL, ~16 GUIDE. Output leans more REBOOT-TAIL than input, because its hardware tail (smart-amp firmware, HDA model quirks, hdajackretask, GPU modeset) is heavier. Triage is a proposal — correct the class on any line. + +** Cluster 1 — no sound at all / dummy output +Probe: units + kernel =/proc/asound= + =dsp_driver=. The output mirror of input cluster 1. + +- SOF DSP firmware not installed :: REBOOT-TAIL — install =sof-firmware= (PRIV), reboot. +- Cirrus CS35L41 smart amp, missing _DSD :: REBOOT-TAIL — =firmware-cirrus= + the _DSD override (PRIV), reboot; GUIDE the BIOS-update path. +- TI TAS2781 smart amp absent :: GUIDE — needs a kernel built with the driver + TI firmware; nothing to run on a stock stack. +- Wrong HDA model autodetection :: REBOOT-TAIL — =model== modprobe (PRIV) + reboot. +- snd_soc_skl grabs the card, only Dummy Output :: REBOOT-TAIL — =dmic_detect=0= + blacklist =snd_soc_skl= (PRIV) + reboot. +- AMD Ryzen HDA codec probe timeout :: REBOOT-TAIL — the =probe_mask=/=dmic_detect= workaround (PRIV) + reboot; GUIDE a newer kernel. +- Kernel regression killed HDA output :: GUIDE — boot the previous kernel / linux-lts. +- Kernel USB-audio regression, DAC "Off" profile :: GUIDE — boot linux-lts / wait for the fix. +- BIOS-disabled onboard, or a GPU HDA controller steals ordering :: GUIDE + PRIV — re-enable in BIOS (GUIDE); =enable=1,0= modprobe to demote the GPU's audio (PRIV, reboot). + +** Cluster 2 — sound plays but from the wrong device +Probe: default sink + priority. The output side of routing. + +- Default sink lands on HDMI instead of speakers :: AUTO — =set-default= to the speakers. +- Default keeps reverting to the wrong sink :: AUTO — a =priority.session= rule. +- WirePlumber fails to start / stale config jams routing :: AUTO — remove the drop-in + restart (PRIV if under =/etc=). +- Corrupted WirePlumber restore state :: AUTO — =rm= the state + restart. +- pipewire-pulse dead, Pulse apps silent :: AUTO — restart the user services. +- USB DAC not auto-selected as default :: AUTO — a =priority.session= rule. + +** Cluster 3 — right default but silent (muted or zero) +Probe: semantic sink mute/volume. The output mirror of input cluster 2. + +- Master / PCM channel muted :: AUTO — unmute. +- Auto-Mute Mode silences speakers :: AUTO — disable Auto-Mute. +- alsactl restores a muted master at boot :: AUTO + PRIV — unmute now (AUTO); =sudo alsactl store= to persist (PRIV). +- Front/Surround/Center/LFE channel muted :: AUTO — unmute the channels. +- Sink present but silent until a mute/unmute toggle :: AUTO — =set-mute 0= / =set-volume=. + +** Cluster 4 — speakers work but headphones don't (or the reverse) +Probe: jack-sense + pin config. Mostly hdajackretask/model-quirk territory, so this cluster leans REBOOT-TAIL. + +- Dead headphone pin needs hdajackretask :: REBOOT-TAIL — hdajackretask override + install + reboot. +- Phantom jack-sense keeps speakers muted :: REBOOT-TAIL — hdajackretask reconfigure; GUIDE if it needs a codec quirk. +- Wrong codec model quirk, no headphone or speaker :: REBOOT-TAIL — =model== modprobe + reboot. +- Headphone path maps volume to the wrong control :: PRIV — edit the alsa-card-profile mixer path (a system file), restart audio. +- Headphone jack not detected, speakers keep playing :: REBOOT-TAIL — =model== modprobe + reboot. +- Front-panel headphone jack not wired by BIOS :: REBOOT-TAIL — hdajackretask override + install + reboot. + +** Cluster 5 — works then dies or pops +Probe: node state / suspend + re-probe-after-idle. + +- Sink suspended on idle, pops on start/stop :: AUTO — =session.suspend-timeout-seconds = 0= drop-in + restart. +- USB autosuspend powers the DAC down :: PRIV — a udev =power/control=on= rule (or =usbcore.autosuspend=-1=, REBOOT-TAIL). +- DAC needs an implicit-feedback quirk :: REBOOT-TAIL — =implicit_fb= modprobe (PRIV) + reboot. + +** Cluster 6 — Bluetooth output +Probe: bluez profile + codec. + +- A2DP Sink profile deselects / unavailable :: PRIV — clear =/var/lib/bluetooth= + re-pair (or a config fix, AUTO). +- LDAC/aptX never negotiates, falls to SBC :: PRIV — install the codec packages; AUTO for the codecs config. +- Playback drops to HSP/HFP mid-song :: AUTO — autoswitch off; force =a2dp-sink=. +- BT sink not made default on connect :: AUTO — =set-default=; a switch-on-connect rule. +- Silent sink after suspend/resume :: AUTO — a seat-monitoring drop-in; restart as the workaround. +- Volume slider does nothing (absolute volume) :: AUTO — =enable-hw-volume = false=. +- Adapter never exposes a sink (firmware) :: REBOOT-TAIL — update linux-firmware, reload/reboot. + +** Cluster 7 — HDMI / DisplayPort +Probe: GPU audio codec + ELD. Output-only cluster. + +- GPU HDMI codec never initializes :: REBOOT-TAIL — =dsp_driver= modprobe + initramfs + reboot. +- NVIDIA GPU audio removed by a PM udev rule :: PRIV — edit the udev rule (reload/reboot). +- NVIDIA HDMI audio needs DRM modesetting :: REBOOT-TAIL — =modeset=1= modprobe + initramfs + reboot. +- HDMI audio dies after suspend/resume :: PRIV — =sudo alsa force-reload= (or alsactl store/restore). +- Empty ELD after resume (kernel regression) :: GUIDE — boot linux-lts; replug to force an ELD re-read. +- Wrong monitor is the audio sink :: AUTO — =set-card-profile= to the right hdmi-extra port. +- HDMI sink present but silent until daemon restart :: AUTO — restart the user services. +- HDMI sample-rate mismatch, display rejects :: AUTO — pin the rate. + +** Cluster 8 — degraded audio (crackle, xrun, latency) +Probe: xrun counters (pw-top) + config. Output-only cluster; sound plays but is degraded. + +- Quantum drops too low under load :: AUTO — a =min-quantum= drop-in + restart. +- Realtime scheduling denied (RTKit) :: PRIV — =limits.d= + the realtime group (relogin). +- Timer-based scheduling on a fussy USB DAC :: AUTO — a =disable-tsched= rule. +- Forced global sample rate causes resample artifacts :: AUTO — =allowed-rates= config. +- Small quantum too tight for browser/games :: AUTO — per-app =PIPEWIRE_LATENCY= / a stream rule. +- HDA power-save clicks (and BT-latency hits) :: PRIV — =power_save=0= modprobe. +- Insufficient ALSA headroom :: AUTO — a =headroom= rule. + +** Cluster 9 — stack healthy, an app is silent +Probe: none in-stack; the tail on a healthy verdict. The output mirror of input cluster 7. + +- Flatpak app has no PulseAudio socket :: GUIDE — print =flatpak override --user --socket=pulseaudio =. +- Flatpak silent on a PipeWire-only system :: GUIDE — print =flatpak override --user --filesystem=xdg-run/pipewire-0 =. +- Snap audio-playback interface unconnected :: GUIDE — print =snap connect :audio-playback=. +- Browser per-tab mute zeroes the stream :: GUIDE — unmute the tab, or the app's stream. +- Wine/Proton game silent, wrong audio driver :: GUIDE — winecfg output device; install the 32-bit audio libs. +- App hard-coded to raw hw:0, device busy :: GUIDE — change the app's device to =default= / =plughw=. +- Chromium doesn't follow the default-sink change :: GUIDE — reassign the stream in pavucontrol. +- Electron app routed to the wrong sink :: GUIDE — move the stream in pavucontrol. * Sources -(collected per entry) +Every entry carries its source inline as an org link. Reports span the Arch/Manjaro/EndeavourOS/Fedora/Ubuntu/Mint/openSUSE forums, askubuntu, the PipeWire and WirePlumber docs, ALSA and Flatpak issue trackers, kernel bug trackers, and vendor forums (NVIDIA, Dell). Collected 2026-07-10. -- cgit v1.2.3