make the outgoing packet interval configurable

Add -audio-interval to choose a 10, 20, 40, or 60 ms packet duration.
Longer packets cut per-packet overhead on slow or lossy links at the
cost of latency. Anything else is rejected at startup rather than
silently truncated to 10 ms frames.

Step the audio sequence by the frame duration.
Sequence numbers are Mumble timestamps in 10 ms units, so a 60 ms
packet advances the counter by six. Incrementing by one made the
receiver see every packet as arriving far too early.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Brandon McGinty
2026-08-20 14:42:52 -04:00
co-authored by Claude Opus 5
parent 6dcaa7f7a6
commit 7e1faba06b
2 changed files with 23 additions and 1 deletions
+5 -1
View File
@@ -263,11 +263,15 @@ func (c *Client) AudioOutgoing() chan<- AudioBuffer {
ch := make(chan AudioBuffer)
go func() {
var seq int64
frameStep := int64(c.Config.AudioFrameSize() / AudioDefaultFrameSize)
if frameStep < 1 {
frameStep = 1
}
previous := <-ch
for p := range ch {
previous.writeAudio(c, seq, false)
previous = p
seq = (seq + 1) % math.MaxInt32
seq = (seq + frameStep) % math.MaxInt32
}
if previous != nil {
previous.writeAudio(c, seq, true)
+18
View File
@@ -115,6 +115,7 @@ func main() {
serverSet := false
usernameSet := false
buffers := flag.Int("buffers", 16, "number of audio buffers to use")
audioInterval := flag.Int("audio-interval", 10, "outgoing audio packet duration in ms (10, 20, 40, or 60)")
jitterBuffer := flag.Int("jitter-buffer", 40, "incoming per-user audio buffer in ms (0, 20, 40, or 60)")
profile := flag.Bool("profile", false, "add http server to serve profiles")
noiseSuppressionEnabled := flag.Bool("noise-suppression", false, "enable noise suppression for microphone input")
@@ -123,6 +124,10 @@ func main() {
logFile := flag.String("logfile", "", "write logs to this file (logging is disabled when omitted)")
flag.Parse()
selectedAudioInterval, err := audioIntervalDuration(*audioInterval)
if err != nil {
handle_raw_error(err)
}
selectedJitterBuffer, err := jitterBufferDuration(*jitterBuffer)
if err != nil {
handle_raw_error(err)
@@ -219,6 +224,7 @@ func main() {
NoiseSuppressor: noise.NewSuppressor(),
}
b.Config.Buffers = *buffers
b.Config.AudioInterval = selectedAudioInterval
b.Config.DisableUDP = *tcpOnly
b.Config.IncomingAudioBuffer = selectedJitterBuffer
@@ -260,6 +266,18 @@ func main() {
handle_error(&b)
}
// audioIntervalDuration converts the packet duration requested at startup to
// one of the Opus durations supported by Mumble.
func audioIntervalDuration(milliseconds int) (time.Duration, error) {
interval := time.Duration(milliseconds) * time.Millisecond
switch interval {
case 10 * time.Millisecond, 20 * time.Millisecond, 40 * time.Millisecond, 60 * time.Millisecond:
return interval, nil
default:
return 0, fmt.Errorf("audio interval must be 10, 20, 40, or 60 ms, got %d", milliseconds)
}
}
// jitterBufferDuration converts the requested incoming playout delay to a
// supported duration. Zero starts playback without an initial safety buffer.
func jitterBufferDuration(milliseconds int) (time.Duration, error) {