Merge the newest logging, jitter recovery, AGC hotkey, and channel tree refresh work from bmmcginty/barnard-stormdragon-ai-fork dev.
Co-authored-by: Brandon McGinty <git@bmcginty.us>
Co-authored-by: Tyler Spivey <tspivey@pcdesk.net>
Tree items render a display string snapshotted at build time, so
changing a user's volume updated the gain but left the stale
percentage on screen. Rebuild the tree and refresh after volume
change and reset, matching what the mute hotkeys already do.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Automatic gain control was always on with no way to turn it off. Toggle
it with F12, the /agc command, or the agc FIFO command, and persist the
choice in the configuration file the same way noise suppression does.
AgcEnabled defaults to true so existing setups keep their current
behavior, and the saved value is applied to the stream on connect. The
enabled flag becomes an atomic.Bool because the capture goroutine reads
it while the UI goroutine writes it, and the lazily created right
channel AGC now inherits the left channel's state.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mumble destroys and recreates AudioInput when the sender switches audio
devices. The destructor sends no terminator, and the replacement resets
iFrameCounter to zero, so a sender who never unkeys silently restarts its
frame numbering mid-burst. The jitter buffer kept expecting the old
sequence and discarded every packet as late until the next unkey.
Resync on a sustained run of late packets combined with a backwards jump
too large to be network reordering. Both conditions are needed: the run
length alone would let a clump of reordered packets drag the expected
sequence backwards, and small jumps need no intervention because the
restarted stream climbs past the stale expectation on its own.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merge the full history from bmmcginty/barnard-stormdragon-ai-fork dev, including UDP audio transport, jitter handling, concurrency fixes, and regression coverage.
Co-authored-by: Brandon McGinty <git@bmcginty.us>
The recording pipeline had two bugs that combined to produce
buzzing/static in recorded audio:
1. NormalizeStereoFrame truncated incoming audio frames to
frameSize*AudioChannels samples. When the Opus decoder produced
20ms frames (1920 stereo samples) but the recorder used a 10ms
frameSize (960 samples), half the audio from every packet was
silently dropped.
2. The recorder's run() loop dequeued one fixed-length frame per
source per tick. After truncation, the remaining 10ms of each
20ms packet was gone, so every other tick produced silence.
This 50 Hz on/off pattern sounded like static.
Fixes:
- NormalizeStereoFrame no longer truncates; it only converts mono
to stereo and preserves all audio data
- RecordAudioFrame now accepts an explicit stereo flag from callers
instead of guessing from sample count (which failed for even-length
mono data like 480-sample mic frames)
- run() accumulates variable-length frames per source and consumes
them in fixed-size chunks, preserving any leftover for the next tick
- popNext() now computes the actual frame step from PCM sample count
instead of always advancing by 1. Mumble 1.5 frame numbers are 10ms
timestamps, so 20ms stereo frames advance by 2, eliminating the
'seq gap' skip that fired on every single packet.
- Drain loop now discards late/duplicate packets (sequence < expected)
instead of letting them permanently block the buffer. Previously a
single late packet at jitterBuf[0] would cause popNext to always
return nil without the gap check catching it (only handled >), so
all subsequent drains would break immediately.
- Reduced reclaim log verbosity: log every 50th or on state change,
not every call with processed==0.
- Log OpenAL vendor/version/renderer at startup
- Log buffer creation count and user volume on new audio stream
- Enhanced reclaim(): log source state, processed/queued/empty counts,
and check for OpenAL errors; log every 50th cycle or on unusual state
- Log jitter buffer drain events (first 3 + periodic) with buffer counts
- Log sequence gap skips in jitter buffer
- WARN when processAudioPacket has no empty buffers (audio dropped)
- Check OpenAL errors after Buffer.SetData, QueueBuffer, and Play()
- Log source state transitions when calling Play() on non-playing source