aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/design/2026-07-10-audio-failure-taxonomy.org210
1 files changed, 206 insertions, 4 deletions
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/<uid>= 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 <app>=.
+- Flatpak app has no audio socket :: GUIDE — print =flatpak override --user --socket=pulseaudio <app>=.
+- Snap audio-record interface not connected :: GUIDE — print =sudo snap connect <snap>: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 <app>=.
+- 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 <app>=.
+- Flatpak silent on a PipeWire-only system :: GUIDE — print =flatpak override --user --filesystem=xdg-run/pipewire-0 <app>=.
+- Snap audio-playback interface unconnected :: GUIDE — print =snap connect <snap>: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.