Monitor: live mic-level meter — prove audio is really flowing#803
Merged
Conversation
The host's first worry during an event is "is the recording actually coming in?" recording_health tells them chunks are landing; this adds the finer signal underneath it: the participant's live mic input level. Capture path (safe, verified): the recorder taps its own mic MediaStream with a passive AnalyserNode on a separate AudioContext, never connected to output, torn down on stop/unmount — so it cannot disturb MediaRecorder. Validated on real Chrome (oscillator → MediaStreamDestination): chunks kept flowing while the analyser read the same stream, the level tracked the signal, and it dropped to zero on silence. The real-mic getUserMedia path still wants a physical-device pass (iOS Safari / Android Chrome). Pipeline: recorder.getAudioLevel() (0..1 RMS) → conversation ping `audio_level` (clamped/rounded server-side, NaN/inf ignored) → liveness telemetry → monitor payload → a small 5-bar signal meter on receiving rows. All-quiet reads as a soft "very quiet — check the mic isn't muted" hint, not an alarm. Tests: audio_level round-trips into the payload (null when unreported); ping clamps/rounds and drops NaN/inf. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a live mic-level meter to the Monitor so the host can see that audio is really flowing — not just that chunks are landing.
recording_health(from #800) already answers "are chunks arriving?" This adds the finer signal underneath it: the participant's actual mic input level, rendered as a tiny 5-bar signal meter on rows that are actively receiving audio.How it flows
useChunkedAudioRecorder.getAudioLevel()(0..1 RMS) → conversation pingaudio_level→ liveness telemetry → monitor payload →AudioLevelMeterinLiveMonitorSection.MediaStreamwith a passiveAnalyserNodeon a separateAudioContext, never connected to output, torn down on stop/unmount. It's a read-only branch that cannot disturbMediaRecorder.audio_levelis clamped to [0,1] and rounded 2dp; NaN/inf from a misbehaving client are dropped.Automated verification (the graph technique)
Ran the exact Web Audio graph on real headless Chrome (oscillator →
MediaStreamDestination, so no mic permission needed):MediaRecorderproduced 7 real Opus chunks (26.7 KB) while theAnalyserNoderead the same stream.meterDropsOnSilence: true).Still needs a physical-device pass
The macOS real-mic
getUserMediapath can't be exercised in CI (OS-gated), so before this rides to prod it wants a quick check on iOS Safari + Android Chrome: start a recording, confirm the meter tracks speech and that recording/chunk upload + background/lock still behave.Tests
audio_levelround-trips into the monitor payload (null when unreported)Checks: ruff ✓, mypy ✓, pytest ✓ (31 passed, +2 new), tsc ✓, biome ✓, catalogs recompiled.
🤖 Generated with Claude Code