commit 0e5db1afa327fa25d057474b3232294f392525e5
parent 6f8b8c69d4f0a6a651b8a755b690151c91582c3b
Author: Matthew Gantenbein <ganten1998@gmail.com>
Date: Mon, 1 Jun 2026 15:08:05 -0500
docs: diagnostics/README — layer-by-layer audio debugging method
Captures the methodology from the one-ear-in-Discord investigation: measure
each layer (tap → device output → device properties → browser capture → SDP →
Discord DSP → receiver) in order instead of guessing at the device. Notes the
key mistakes: identical-L/R test tones can't detect mono collapse (use distinct
L/R), and the listener's own playback is the easiest variable to forget.
Issue resolved (stereo works); can't attribute to a single change among the
44.1k driver default, a headphone swap, and a voice rejoin. Hydra measured
correct throughout.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat:
1 file changed, 46 insertions(+), 0 deletions(-)
diff --git a/diagnostics/README.md b/diagnostics/README.md
@@ -0,0 +1,46 @@
+# Hydra diagnostics
+
+Tools for diagnosing audio-path problems — built while chasing a "one ear in Discord" bug
+that turned out to be **downstream of Hydra entirely**. The lesson they encode: when audio
+"sounds wrong," measure each layer in order rather than guessing at the device.
+
+## The layers, and how to measure each
+
+1. **Tap capture** (does Hydra capture the app at all, balanced?) — route an app via the TUI
+ and watch the route's peak in `hydra query`. A balanced source should give equal L/R.
+2. **Hydra device output** (does the virtual device emit true stereo?) — record Hydra's
+ *input* with ffmpeg and check per-channel frequency content:
+ ```sh
+ ffmpeg -f avfoundation -i ":0" -t 2 -y /tmp/out.wav # ":0" = Hydra (check -list_devices)
+ # feed a hard-panned source (300Hz L / 900Hz R) and confirm L≠R in the recording
+ ```
+ IMPORTANT: test with a **distinct-L/R** source, not an identical-L/R tone — an identical
+ tone plays in both ears even if a channel is being dropped, so it can't detect mono
+ collapse. (This was a real mistake during the original investigation.)
+3. **Device properties** (`devdiff.m`) — dump format / channel-layout / labels for every
+ input device, to A/B Hydra against a known-good device (e.g. Loopback):
+ ```sh
+ clang -framework CoreAudio -framework CoreFoundation devdiff.m -o /tmp/devdiff && /tmp/devdiff
+ ```
+ This is what found Hydra was defaulting to 48000 Hz while the host ran at 44100 — the one
+ device-level difference from the working Loopback mic.
+4. **Browser capture** (`hydra_mic_probe.js`) — paste into Vesktop DevTools console; reports
+ what Chromium's WebRTC capture actually sees (track settings + per-channel peaks). Confirms
+ whether the browser receives stereo from the device.
+5. **SDP / Opus** — read StereoMic's own console logs (no pasting needed):
+ `[StereoMic] munged setLocalDescription` AND `munged setRemoteDescription` both firing =
+ Opus is advertising stereo on offer + answer. If only local fires, the answer-munge didn't
+ apply (a StereoMic-side issue, not Hydra).
+6. **Discord audio processing** — Echo Cancellation / Noise Suppression(Krisp) / Auto Gain
+ are mono-only; any one enabled collapses capture to mono regardless of device. Turn all
+ off in Vesktop → Voice & Video.
+7. **The receiver** — the easiest variable to forget. Verify the *listener's* playback is
+ actually stereo (headphones on a second device joined to the same channel) before
+ concluding the transmit path is broken. Unknown/mono speakers on the far end mimic every
+ send-side bug.
+
+## Hard-won rule
+
+Hydra measured correct at every layer (44.1k, clean [L,R] labels, true stereo to the
+browser, both channels captured). Most "Hydra is broken" symptoms live in layers 5–7. Build
+the measurement before changing the device.