Add audio device switching UI
This commit is contained in:
+31
-20
@@ -31,10 +31,15 @@ var (
|
||||
defaultAllow = sdl.AUDIO_ALLOW_ANY_CHANGE
|
||||
|
||||
refreshCacheCallback func()
|
||||
|
||||
persistConfigCallback func(*db.AppConfig) error
|
||||
)
|
||||
|
||||
// Init initializes SDL audio + SDL_mixer. Safe to call multiple times.
|
||||
func Init(cfg *db.AppConfig, onUpdate func()) error {
|
||||
// persistConfig persists the given config (used to save the selected audio
|
||||
// device across restarts); it may be nil, in which case device selections are
|
||||
// not saved.
|
||||
func Init(cfg *db.AppConfig, onUpdate func(), persistConfig func(*db.AppConfig) error) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if inited {
|
||||
@@ -42,6 +47,7 @@ func Init(cfg *db.AppConfig, onUpdate func()) error {
|
||||
}
|
||||
|
||||
refreshCacheCallback = onUpdate
|
||||
persistConfigCallback = persistConfig
|
||||
|
||||
if err := sdl.InitSubSystem(sdl.INIT_AUDIO); err != nil {
|
||||
return fmt.Errorf("SDL audio init failed: %w", err)
|
||||
@@ -52,9 +58,20 @@ func Init(cfg *db.AppConfig, onUpdate func()) error {
|
||||
logger.Error("Failed to initialize MP3/OGG support: %v", err)
|
||||
}
|
||||
|
||||
// A stored "default" (the first-run seed) is the system default.
|
||||
deviceName := cfg.SelectedAudioDevice
|
||||
if deviceName == "default" {
|
||||
if deviceName == "" || deviceName == "default" {
|
||||
deviceName = ""
|
||||
} else {
|
||||
// Honor a persisted device selection by resolving it against the
|
||||
// currently enumerated devices, falling back to the system default if
|
||||
// it is no longer present.
|
||||
if resolved, ok := resolvePreferredDevice(deviceName); ok {
|
||||
deviceName = resolved
|
||||
} else {
|
||||
logger.Warning("Selected audio device %q not found; using system default", cfg.SelectedAudioDevice)
|
||||
deviceName = ""
|
||||
}
|
||||
}
|
||||
if err := openDeviceLocked(deviceName); err != nil {
|
||||
sdl.QuitSubSystem(sdl.INIT_AUDIO)
|
||||
@@ -178,7 +195,10 @@ func IsPlaying() bool {
|
||||
return mix.PlayingMusic()
|
||||
}
|
||||
|
||||
// SetVolume sets the SDL_mixer music volume to level (0.0–1.0).
|
||||
// SetVolume sets the SDL_mixer music volume to level (0.0–1.0), mapped onto
|
||||
// SDL_mixer's 0–128 scale (mix.MAX_VOLUME). This is relative to the OS audio
|
||||
// sink, which the kiosk shell pins to 100%, so in-app volume is 0–100% of the
|
||||
// sink output.
|
||||
func SetVolume(level float64) error {
|
||||
if level < 0 || level > 1 {
|
||||
return fmt.Errorf("volume must be between 0.0 and 1.0, got %f", level)
|
||||
@@ -204,23 +224,6 @@ func ConvertVolume(volPerc int) float64 {
|
||||
return float64(volPerc) / 100.0
|
||||
}
|
||||
|
||||
// ListDevices returns the list of available output device names (from core SDL).
|
||||
func ListDevices() []string {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
n := sdl.GetNumAudioDevices(false)
|
||||
if n <= 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, n)
|
||||
for i := 0; i < n; i++ {
|
||||
if name := sdl.GetAudioDeviceName(i, false); name != "" {
|
||||
out = append(out, name)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// CurrentDevice returns the last requested/opened device name ("" means default).
|
||||
func CurrentDevice() string {
|
||||
return deviceName
|
||||
@@ -265,6 +268,14 @@ func SwitchDevice(newName string, cfg *db.AppConfig) error {
|
||||
}
|
||||
|
||||
if cfg != nil {
|
||||
// Persist whatever device is actually open (openDeviceLocked falls
|
||||
// back to the system default if the requested name cannot be opened).
|
||||
cfg.SelectedAudioDevice = CurrentDevice()
|
||||
if persistConfigCallback != nil {
|
||||
if err := persistConfigCallback(cfg); err != nil {
|
||||
logger.Error("Failed to persist audio device selection: %v", err)
|
||||
}
|
||||
}
|
||||
currentVolume = cfg.SavedVolume
|
||||
v := int(math.Round(cfg.SavedVolume * float64(mix.MAX_VOLUME)))
|
||||
mix.VolumeMusic(v)
|
||||
|
||||
Reference in New Issue
Block a user