diff options
Diffstat (limited to 'docs/design')
| -rw-r--r-- | docs/design/2026-07-10-audio-failure-taxonomy.org | 210 |
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. |
