diff --git a/README.md b/README.md index 62dd268..cd19ff6 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,20 @@ If you modify the config file while Barnard is running, your changes may be over You can set username and defaultserver in your config file, and they will be used if none is specified when launching barnard. (Note that the default username (an empty string) and the default server name (localhost:64738) have been the defaults for barnard up to this point, and have been left that way for compatibility.) +## Audio Packet Duration + +Barnard sends 10 ms audio packets by default. On a slow or unstable connection, +using larger packets can reduce packet overhead and make short dropouts less +noticeable, at the cost of additional voice latency. Start Barnard with one of +the supported durations: + +```sh +barnard --audio-interval 20 +``` + +Supported values are `10`, `20`, `40`, and `60` milliseconds. Try `20` ms +first; use `40` ms only if the connection remains unreliable. + ## Audio Devices You can set the default input and output devices in the config file as well. diff --git a/client_notification_test.go b/client_notification_test.go index 7e223b9..9798819 100644 --- a/client_notification_test.go +++ b/client_notification_test.go @@ -70,6 +70,22 @@ func TestNotificationExpansionIsSinglePassAndNotifyDoesNotBlock(t *testing.T) { } } +func TestAudioIntervalDuration(t *testing.T) { + for _, milliseconds := range []int{10, 20, 40, 60} { + got, err := audioIntervalDuration(milliseconds) + if err != nil { + t.Errorf("audioIntervalDuration(%d): %v", milliseconds, err) + continue + } + if got != time.Duration(milliseconds)*time.Millisecond { + t.Errorf("audioIntervalDuration(%d) = %v", milliseconds, got) + } + } + if _, err := audioIntervalDuration(30); err == nil { + t.Fatal("audioIntervalDuration accepted unsupported duration") + } +} + func TestServerAddressDefaultsPortWithoutBreakingIPv6(t *testing.T) { for input, want := range map[string]string{ "server": "server:64738", diff --git a/main.go b/main.go index a9cbe96..e7d949d 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "net/http" "os" "strings" + "time" barnlog "git.stormux.org/storm/barnard/log" @@ -84,6 +85,7 @@ func main() { configSet := false certificateSet := 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)") profile := flag.Bool("profile", false, "add http server to serve profiles") noiseSuppressionEnabled := flag.Bool("noise-suppression", false, "enable noise suppression for microphone input") autoTransmit := flag.Bool("auto-transmit", false, "start transmitting immediately on connect") @@ -94,6 +96,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) + } // Set up logging var level barnlog.Level @@ -192,6 +198,7 @@ func main() { NoiseSuppressor: noise.NewSuppressor(), } b.Config.Buffers = *buffers + b.Config.AudioInterval = selectedAudioInterval b.Config.DisableUDP = *tcpOnly b.Hotkeys = b.UserConfig.GetHotkeys() @@ -238,6 +245,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) + } +} + // serverAddress adds Mumble's default port without corrupting an IPv6 literal. func serverAddress(address string) string { if _, port, err := net.SplitHostPort(address); err == nil && port != "" {