Commit Graph
17 Commits
Author SHA1 Message Date
Brandon McGintyandClaude Opus 5 da1c6bdc26 release jitter buffer entries as they are consumed
Packets are removed from the jitter buffer by resliceing past them, which
leaves the popped entries in the backing array. Each one pins a decoded
audio frame until the array is next reallocated.

Clear the slot before resliceing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 12:31:25 -04:00
Brandon McGintyandClaude Opus 5 7647fdb233 release per-user audio resources before tearing down the renderer
Destroy detached the audio listener, which closes each per-user stream
channel and ends the goroutines OnAudioStream started, and then immediately
destroyed the OpenAL context and closed the renderer without waiting for
them. Those goroutines delete their OpenAL source and buffers through
s.render, which returns false once the renderer is closed, so the deletes
were silently skipped and the resources were left to the device.

Track the goroutines and let them finish first, bounded so a wedged renderer
cannot hang a reconnect. Both device close calls now report failure as well:
alcCloseDevice frees nothing and returns false while a device still has
contexts, buffers or sources outstanding, and dropping the handle after that
leaks the device and everything it owns somewhere the Go collector cannot
see it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 12:31:16 -04:00
Brandon McGintyandClaude Opus 5 6dcaa7f7a6 handle dropped packets and network delays
Add a per-user jitter buffer with restart resync.
Incoming audio was rendered straight to OpenAL as packets arrived, so
normal network jitter produced gaps and the renderer starved between
frames. Buffer each user's decoded frames for a configurable playout
delay (-jitter-buffer, default 40ms) and let the render thread pull
from that buffer instead.

Drop late packets, and use packet duration to set the expected frame
number.
A packet below the expected frame number is dropped rather than
stalling delivery, and the expected frame is stepped by the packet's
real duration instead of a fixed 10ms.

After detecting a large backward jump in frame numbering, along with a
sustained run of late packets, resync to the new frame numbering.
When a sender switches audio devices, Mumble destroys and recreates
their audio stream. This resets their frame number to zero without
sending a terminator packet, which causes a potentially endless hang
while we wait for a frame number that will not arrive for hours.

Own the OpenAL context on one dedicated render thread.
Contexts are current to an OS thread, so creating sources and buffers
from whichever goroutine happened to be running could operate on no
context at all. Every source and buffer is now created, filled, and
deleted on that thread, and file playback renders through it too.

Report device and capture failures with the device name.
Startup errors said only that a device could not be opened, and a
capture stall was reported as a microphone failure even though it is
normal scheduler timing. Fatal microphone errors now exit instead of
leaving a client that cannot transmit.

Release queued buffers and deactivate the context on shutdown.
Streams were torn down while buffers were still queued on a source,
and the context was destroyed while still current.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 14:42:46 -04:00
Brandon McGintyandClaude Opus 5 5ec82eb1fd fix recorded audio truncation and rate mismatch
Accumulate incoming frames per source and consume fixed-size chunks.
Each speaker's frames were queued whole and one frame was taken per
tick, so a frame that did not match the recorder's frame size was
truncated or padded and the recording drifted out of time with the
audio. Frames of any size now append to a per-source buffer that is
drained in exact chunks.

Tell the recorder whether a frame is stereo instead of guessing from
its length.
Mono microphone frames were being interpreted as stereo, which halved
their duration and produced static in the output.

Let the recorder worker close ffmpeg's stdin.
Stop closed it from the caller while the worker was still writing,
turning a normal stop into a broken pipe and losing the tail of the
recording.

Reserve the output file exclusively and hand ffmpeg the descriptor.
The path was generated, then reopened by name, so another process
could take the name in between. The file is opened once with O_EXCL
and passed to ffmpeg as an inherited descriptor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 14:42:41 -04:00
Brandon McGintyandClaude Opus 5 60470ad091 make per-user audio state thread-safe
Move volume, boost, mute, and OpenAL source behind accessors guarded
by a per-user mutex.
The audio goroutine reads these fields on every decoded packet while
the UI writes them from key handlers, which is a data race on the
gain a stream is currently rendering with.

Also add the per-user sequence fields the decoder needs to notice
gaps in a user's audio stream.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 14:22:33 -04:00
Storm Dragon 9bc514e74f Remove voice effects 2026-07-14 18:41:50 -04:00
Storm Dragon 5ac6703489 Use RNNoise for noise suppression 2026-06-28 23:15:09 -04:00
Storm Dragon 69674a0dab Add standards-aware recording 2026-05-14 00:42:30 -04:00
Storm Dragon e3b6eac2a0 Support for stereo mic. 2026-02-09 22:33:17 -05:00
Storm Dragon fae372bb78 Added /file and /stop commands. 2025-11-30 20:31:06 -05:00
Storm DragonandClaude f96cb1f79b Add real-time voice effects for outgoing audio
Implements 7 voice effects that can be cycled through with F12:
- None (default)
- Echo: Single repeating delay with feedback (250ms)
- Reverb: Multiple short delays without feedback
- High Pitch: Chipmunk voice using cubic interpolation
- Low Pitch: Deep voice effect
- Robot: Ring modulation for robotic sound
- Chorus: Layered voices with pitch variations

The effects are applied after noise suppression and AGC in the audio
pipeline. Selected effect is persisted to config file. Includes
comprehensive documentation in README.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 16:27:08 -04:00
Storm DragonandClaude 82b308000d Add automatic gain control for outgoing microphone audio.
This addresses low microphone volume issues by automatically normalizing
outgoing audio levels with dynamic range compression and soft limiting.
The AGC is always enabled and applies voice-optimized parameters to
ensure consistent audio levels are sent to other users while preserving
manual volume control for incoming audio.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 15:33:18 -04:00
Storm DragonandClaude b966106727 Fix audio distortion and improve noise suppression.
- Add saturation protection to prevent crackling at high volume boost levels
- Implement keyboard click detection using energy spike analysis
- Reduce aggressive noise suppression parameters to prevent audio artifacts
- Apply stronger suppression specifically for detected keyboard clicks while preserving voice quality

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-20 03:50:14 -04:00
Storm Dragon df7159bad1 Initial noise suppression added. 2025-08-16 21:29:52 -04:00
Storm Dragon 2e337db3c5 Updated everything for dependencies. All sub packages are now part of the project. This was a massive update, hopefully won't have to be reverted. 2025-01-16 17:03:01 -05:00
Storm Dragon a5c0e7a71c Added mute option. It can work per user or per channel, so covers deafen functionality too. 2025-01-16 13:36:46 -05:00
Storm Dragon 3f0246a4f8 Initial commit, lots of cleanup and stuff to do, it may not work. 2025-01-15 23:43:44 -05:00