From 3ea32e0eaa363d0596391c968b41f0eab012f5c3 Mon Sep 17 00:00:00 2001 From: NeilClack Date: Thu, 14 May 2026 09:57:13 -0500 Subject: [PATCH] Add volume changing logic --- internal/application/application.go | 1 + internal/db/schema.go | 1 + internal/mixer/mixer.go | 10 +++++++++ internal/mixer/mixer_test.go | 34 +++++++++++++++++++++++++++++ internal/page/audioDevices.go | 31 +++++++++++++++++++++++++- internal/page/audioDevices_test.go | 28 ++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 internal/mixer/mixer_test.go create mode 100644 internal/page/audioDevices_test.go diff --git a/internal/application/application.go b/internal/application/application.go index e77e332..b00ec7a 100644 --- a/internal/application/application.go +++ b/internal/application/application.go @@ -121,6 +121,7 @@ func Run() { StandardFontSize: 32, LinePattern: "fibonacci", AudioEnabled: true, + SavedVolume: 1.0, } if err := DB.Create(&initialConfig).Error; err != nil { logger.Error("failed to insert default config into database: %v", err) diff --git a/internal/db/schema.go b/internal/db/schema.go index 235bea9..89775d1 100644 --- a/internal/db/schema.go +++ b/internal/db/schema.go @@ -16,6 +16,7 @@ type AppConfig struct { StandardFontSize int // The font size used for most text rendering LinePattern string // "fibonacci" AudioEnabled bool + SavedVolume float64 // Last saved volume as a percentage } type Log struct { diff --git a/internal/mixer/mixer.go b/internal/mixer/mixer.go index f143680..995dc0c 100644 --- a/internal/mixer/mixer.go +++ b/internal/mixer/mixer.go @@ -247,6 +247,16 @@ func SetAllSinkVolumes(level float64) error { return nil } +// ConvertVolume converts a desired volume percentage (0%, 25%, 33%, 90%, etc) to +// the float64 value needed to pass to SetAllSinkVolumes 0..1.0 +// Percentages should be passed as integers. 25% -> 25 +func ConvertVolume(volPerc int) float64 { + if volPerc == 0 { + return 0.0 + } + return float64(volPerc) / 100.0 +} + // parseSinks parses the output of `wpctl status` and extracts audio sinks. func parseSinks(data []byte) ([]Sink, error) { var sinks []Sink diff --git a/internal/mixer/mixer_test.go b/internal/mixer/mixer_test.go new file mode 100644 index 0000000..f45d591 --- /dev/null +++ b/internal/mixer/mixer_test.go @@ -0,0 +1,34 @@ +package mixer_test + +import ( + "math" + "testing" + + "rhemawavetech.com/rhemawave/internal/mixer" +) + +func TestConvertVolume(t *testing.T) { + tests := []struct { + name string + input int + expected float64 + }{ + {"0 percent", 0, 0.0}, + {"25 percent", 25, 0.25}, + {"33 percent", 33, 0.33}, + {"50 percent", 50, 0.50}, + {"90 percent", 90, 0.90}, + {"100 percent", 100, 1.0}, + } + + const epsilon = 1e-9 + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := mixer.ConvertVolume(tt.input) + if math.Abs(got-tt.expected) > epsilon { + t.Errorf("ConvertVolume(%d) = %v, want %v", tt.input, got, tt.expected) + } + }) + } +} diff --git a/internal/page/audioDevices.go b/internal/page/audioDevices.go index 1fa117e..b10d7b3 100644 --- a/internal/page/audioDevices.go +++ b/internal/page/audioDevices.go @@ -9,6 +9,7 @@ import ( "rhemawavetech.com/rhemawave/internal/db" "rhemawavetech.com/rhemawave/internal/fontManager" "rhemawavetech.com/rhemawave/internal/logger" + "rhemawavetech.com/rhemawave/internal/mixer" "rhemawavetech.com/rhemawave/internal/shaderManager" ) @@ -128,6 +129,31 @@ func (p *AudioDevices) ToggleAudio() { p.CreateAudioStatusItem() } +// UpdateVolume increments or decrements system volume by level (percent change) +func (p *AudioDevices) UpdateVolume(level int) { + // Calculate the new volume + newVol := mixer.ConvertVolume(level) + p.Context.CfgPtr.SavedVolume + // Check that it's within bounds + if newVol < 0.0 || newVol > 1.0 { + return + } + // Force 1.0 rather than 0.99999999999999989 + if newVol > .99 { + newVol = 1.0 + } + // Update the config + p.Context.CfgPtr.SavedVolume = newVol + fmt.Printf("New Volume: %f", newVol) + // Update the database + p.Context.DbPtr.Save(&p.Context.CfgPtr) + // Set the new volume + err := mixer.SetAllSinkVolumes(newVol) + if err != nil { + fmt.Printf("Error setting allSinkVolumes: %v", err) + } + +} + func (p *AudioDevices) HandleEvent(event *sdl.Event) error { e, ok := (*event).(*sdl.KeyboardEvent) if !ok || e.Type != sdl.KEYDOWN { @@ -135,9 +161,12 @@ func (p *AudioDevices) HandleEvent(event *sdl.Event) error { } switch e.Keysym.Sym { - case sdl.K_RETURN: p.ToggleAudio() + case sdl.K_RIGHT: // Right increase + p.UpdateVolume(10) + case sdl.K_LEFT: // Left decrease + p.UpdateVolume(-10) case sdl.K_BACKSPACE: p.Context.NavigateTo(SETTINGS) } diff --git a/internal/page/audioDevices_test.go b/internal/page/audioDevices_test.go new file mode 100644 index 0000000..7c7c2cd --- /dev/null +++ b/internal/page/audioDevices_test.go @@ -0,0 +1,28 @@ +package page_test + +import ( + "testing" + + "rhemawavetech.com/rhemawave/internal/page" +) + +func TestUpdateVolume(t *testing.T) { + tests := []struct { + name string + input int + }{ + {"0 percent", 0}, + {"25 percent", 25}, + {"50 percent", 50}, + {"100 percent", 100}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + devices := &page.AudioDevices{} + // Currently only verifies no panic — expand assertions when + // UpdateVolume has observable side effects or a return value. + devices.UpdateVolume(tt.input) + }) + } +}