From fbb6a148ff64aa1b9fe8bad77d5b4f76667be7eb Mon Sep 17 00:00:00 2001 From: Brandon McGinty Date: Thu, 20 Aug 2026 14:31:59 -0400 Subject: [PATCH] restore the saved microphone volume on connect Apply the persisted microphone volume when the stream is created. The value was written to the configuration on every adjustment but never read back, so the microphone returned to full gain on each start. Read a saved volume of zero as zero rather than as a missing value. A user who muted their microphone and quit came back unmuted. Save the configuration after a volume change and report a failure. The setter only updated the in-memory value, so the new level was lost unless something else happened to save afterwards. Co-Authored-By: Claude Opus 5 --- client.go | 1 + config/user_config.go | 9 +++++++++ config/user_config_test.go | 12 ++++++++++++ ui.go | 6 ++++++ 4 files changed, 28 insertions(+) diff --git a/client.go b/client.go index 9a03da1..0b43ac0 100644 --- a/client.go +++ b/client.go @@ -51,6 +51,7 @@ func (b *Barnard) connect(reconnect bool) bool { return false } b.Stream = stream + b.Stream.SetMicVolume(b.UserConfig.GetMicVolume(), false) b.Stream.AttachStream(b.Client) b.Stream.SetNoiseProcessor(b.NoiseSuppressor) b.Stream.SetAGCEnabled(b.UserConfig.GetAGCEnabled()) diff --git a/config/user_config.go b/config/user_config.go index dd21265..529dea2 100644 --- a/config/user_config.go +++ b/config/user_config.go @@ -316,6 +316,15 @@ func (c *Config) SetMicVolume(v float32) { c.config.MicVolume = &t } +func (c *Config) GetMicVolume() float32 { + c.mu.Lock() + defer c.mu.Unlock() + if c.config.MicVolume == nil { + return 1.0 + } + return *c.config.MicVolume +} + func (c *Config) GetHotkeys() *Hotkeys { return c.config.Hotkeys } diff --git a/config/user_config_test.go b/config/user_config_test.go index f37b3cf..0be9623 100644 --- a/config/user_config_test.go +++ b/config/user_config_test.go @@ -115,6 +115,18 @@ func TestMakeHostPortHandlesIPv6AndMalformedAddress(t *testing.T) { t.Fatalf("got %q:%d", host, port) } } + +// Regression: a stored zero mic volume was treated as an uninitialized value, +// so a persisted mute became full volume after reconnecting. +func TestMicVolumeAllowsPersistedMute(t *testing.T) { + path := filepath.Join(t.TempDir(), "barnard.toml") + cfg := NewConfig(&path) + cfg.SetMicVolume(0) + if got := cfg.GetMicVolume(); got != 0 { + t.Fatalf("got %v, want mute", got) + } +} + func TestConfigUsesHomeEnvironmentForDefaultPath(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) diff --git a/ui.go b/ui.go index 21f9ae3..5afe6ca 100644 --- a/ui.go +++ b/ui.go @@ -338,11 +338,17 @@ func (b *Barnard) setTransmit(ui *uiterm.Ui, val int) { func (b *Barnard) OnMicVolumeDown(ui *uiterm.Ui, key uiterm.Key) { b.Stream.SetMicVolume(-0.1, true) b.UserConfig.SetMicVolume(b.Stream.GetMicVolume()) + if err := b.UserConfig.SaveConfig(); err != nil { + b.AddOutputLine("Microphone: could not save volume: " + err.Error()) + } } func (b *Barnard) OnMicVolumeUp(ui *uiterm.Ui, key uiterm.Key) { b.Stream.SetMicVolume(0.1, true) b.UserConfig.SetMicVolume(b.Stream.GetMicVolume()) + if err := b.UserConfig.SaveConfig(); err != nil { + b.AddOutputLine("Microphone: could not save volume: " + err.Error()) + } } func (b *Barnard) OnQuitPress(ui *uiterm.Ui, key uiterm.Key) {