fix: resolve 4 critical audio mixer bugs (v0.5)

- SwitchDevice volume silently zeroed: Init uses cfg.SavedVolume,
  SwitchDevice restores saved volume after reopening device
- SwitchDevice not persisted to database: caller saves SelectedAudioDevice
- Device restore passes invalid 'system default' to SDL: now passes ''
- Stop() no-op when audio toggled off: removed AudioEnabled guard

Regression tests added for all 4 bugs. CI now runs go test before build.
This commit is contained in:
NeilClack
2026-07-11 11:39:58 -05:00
parent 72910e7006
commit bb132ceeaf
8 changed files with 150 additions and 34 deletions
+8 -12
View File
@@ -69,7 +69,7 @@ func Init(cfg *db.AppConfig, onUpdate func()) error {
return err
}
SetAllSinkVolumes(0.20)
SetAllSinkVolumes(cfg.SavedVolume)
inited = true
return nil
@@ -163,10 +163,6 @@ func Play(path string, loopForever bool, cfg *db.AppConfig) error {
// Stop stops playback. If fadeMs > 0, fades out asynchronously; otherwise halts immediately.
// It also frees the currently loaded music (so a new Play will reload).
func Stop(fadeMs int, cfg *db.AppConfig) {
if !cfg.AudioEnabled {
return
}
mu.Lock()
defer mu.Unlock()
if !inited {
@@ -390,7 +386,7 @@ func CurrentDevice() string {
}
// SwitchDevice switches output devices, preserving volume and resuming playback of the current track/loop state if any.
func SwitchDevice(newName string) error {
func SwitchDevice(newName string, cfg *db.AppConfig) error {
mu.Lock()
defer mu.Unlock()
@@ -401,10 +397,7 @@ func SwitchDevice(newName string) error {
// Log device switch attempt
// Automatically defaults to "system default"
oldDevice := CurrentDevice() // Which device is to be closed
if oldDevice == "" {
oldDevice = "system default"
}
if newName == "" { // Which device is being switched to?
if newName == "" { // Which device is being switched to?
newName = "system default"
}
@@ -431,6 +424,7 @@ func SwitchDevice(newName string) error {
// Attempt to open the newName device
if err := openDeviceLocked(newName); err != nil {
// If error switching to new device, switch back to the old device
// oldDevice is already "" for default (which openDeviceLocked expects)
if restoreErr := openDeviceLocked(oldDevice); restoreErr != nil {
// We should not hit an error when switching back, but if we do...
logger.Error("Failed to switch to device: %s", oldDevice)
@@ -439,8 +433,10 @@ func SwitchDevice(newName string) error {
return err
}
// Restore volume to MAX after reopening device (matches Play() behavior)
mix.VolumeMusic(mix.MAX_VOLUME)
// Restore saved volume after reopening device
if cfg != nil {
SetAllSinkVolumes(cfg.SavedVolume)
}
// Optionally resume previous track if there was one
if wasPlaying && savedPath != "" {