Simplify audio management

Moved all start/stop of sources into audio (out of jsonrpc)
Clean up duplicated code, made direction a bool, more logging, made all source/relay atomics.
Eliminate SetConfig since we always set it during start.
Eliminate the extra initialized flag.
Properly detect when USB audio was previously active.
Relay has the pointer to the source, not a copy.
CgoSource (and stub) expose the AudioSource interface.
This commit is contained in:
Marc Brooks 2025-11-17 19:57:41 -06:00
parent 8c7764a663
commit 1ec9941103
No known key found for this signature in database
GPG Key ID: 583A6AF2D6AE1DC6
6 changed files with 144 additions and 201 deletions

180
audio.go
View File

@ -15,10 +15,10 @@ var (
audioMutex sync.Mutex audioMutex sync.Mutex
setAudioTrackMutex sync.Mutex // Prevents concurrent setAudioTrack() calls setAudioTrackMutex sync.Mutex // Prevents concurrent setAudioTrack() calls
inputSourceMutex sync.Mutex // Serializes Connect() and WriteMessage() calls to input source inputSourceMutex sync.Mutex // Serializes Connect() and WriteMessage() calls to input source
outputSource audio.AudioSource outputSource atomic.Pointer[audio.AudioSource]
inputSource atomic.Pointer[audio.AudioSource] inputSource atomic.Pointer[audio.AudioSource]
outputRelay *audio.OutputRelay outputRelay atomic.Pointer[audio.OutputRelay]
inputRelay *audio.InputRelay inputRelay atomic.Pointer[audio.InputRelay]
audioInitialized bool audioInitialized bool
activeConnections atomic.Int32 activeConnections atomic.Int32
audioLogger zerolog.Logger audioLogger zerolog.Logger
@ -79,58 +79,81 @@ func startAudio() error {
return nil return nil
} }
if outputSource == nil && audioOutputEnabled.Load() && currentAudioTrack != nil { if activeConnections.Load() <= 0 {
ensureConfigLoaded() audioLogger.Debug().Msg("No active connections, skipping audio start")
alsaDevice := getAlsaDevice(config.AudioOutputSource) return nil
source := audio.NewCgoOutputSource(alsaDevice)
source.SetConfig(getAudioConfig())
outputSource = source
outputRelay = audio.NewOutputRelay(outputSource, currentAudioTrack)
if err := outputRelay.Start(); err != nil {
audioLogger.Error().Err(err).Msg("Failed to start audio output relay")
}
} }
ensureConfigLoaded() ensureConfigLoaded()
if inputSource.Load() == nil && audioInputEnabled.Load() && config.UsbDevices != nil && config.UsbDevices.Audio {
alsaPlaybackDevice := getAlsaDevice("usb")
source := audio.NewCgoInputSource(alsaPlaybackDevice)
source.SetConfig(getAudioConfig())
var audioSource audio.AudioSource = source
inputSource.Store(&audioSource)
inputRelay = audio.NewInputRelay(audioSource) if audioOutputEnabled.Load() && currentAudioTrack != nil {
if err := inputRelay.Start(); err != nil { startOutputAudioUnderMutex(getAlsaDevice(config.AudioOutputSource))
audioLogger.Error().Err(err).Msg("Failed to start input relay") }
}
if audioInputEnabled.Load() && config.UsbDevices != nil && config.UsbDevices.Audio {
startInputAudioUnderMutex(getAlsaDevice("usb"))
} }
return nil return nil
} }
func startOutputAudioUnderMutex(alsaOutputDevice string) {
newSource := audio.NewCgoOutputSource(alsaOutputDevice, getAudioConfig())
oldSource := outputSource.Swap(&newSource)
newRelay := audio.NewOutputRelay(&newSource, currentAudioTrack)
oldRelay := outputRelay.Swap(newRelay)
if oldRelay != nil {
oldRelay.Stop()
}
if oldSource != nil {
(*oldSource).Disconnect()
}
if err := newRelay.Start(); err != nil {
audioLogger.Error().Err(err).Str("alsaOutputDevice", alsaOutputDevice).Msg("Failed to start audio output relay")
}
}
func startInputAudioUnderMutex(alsaPlaybackDevice string) {
newSource := audio.NewCgoInputSource(alsaPlaybackDevice, getAudioConfig())
oldSource := outputSource.Swap(&newSource)
newRelay := audio.NewInputRelay(&newSource)
oldRelay := inputRelay.Swap(newRelay)
if oldRelay != nil {
oldRelay.Stop()
}
if oldSource != nil {
(*oldSource).Disconnect()
}
if err := newRelay.Start(); err != nil {
audioLogger.Error().Err(err).Str("alsaPlaybackDevice", alsaPlaybackDevice).Msg("Failed to start input relay")
}
}
func stopOutputAudio() { func stopOutputAudio() {
audioMutex.Lock() audioMutex.Lock()
outRelay := outputRelay outRelay := outputRelay.Swap(nil)
outSource := outputSource outSource := outputSource.Swap(nil)
outputRelay = nil
outputSource = nil
audioMutex.Unlock() audioMutex.Unlock()
if outRelay != nil { if outRelay != nil {
outRelay.Stop() outRelay.Stop()
} }
if outSource != nil { if outSource != nil {
outSource.Disconnect() (*outSource).Disconnect()
} }
} }
func stopInputAudio() { func stopInputAudio() {
audioMutex.Lock() audioMutex.Lock()
inRelay := inputRelay inRelay := inputRelay.Swap(nil)
inputRelay = nil
audioMutex.Unlock()
inSource := inputSource.Swap(nil) inSource := inputSource.Swap(nil)
audioMutex.Unlock()
if inRelay != nil { if inRelay != nil {
inRelay.Stop() inRelay.Stop()
@ -156,7 +179,7 @@ func onWebRTCConnect() {
func onWebRTCDisconnect() { func onWebRTCDisconnect() {
count := activeConnections.Add(-1) count := activeConnections.Add(-1)
if count == 0 { if count <= 0 {
// Stop audio immediately to release HDMI audio device which shares hardware with video device // Stop audio immediately to release HDMI audio device which shares hardware with video device
stopAudio() stopAudio()
} }
@ -166,39 +189,12 @@ func setAudioTrack(audioTrack *webrtc.TrackLocalStaticSample) {
setAudioTrackMutex.Lock() setAudioTrackMutex.Lock()
defer setAudioTrackMutex.Unlock() defer setAudioTrackMutex.Unlock()
// Capture old resources and update state in single critical section stopOutputAudio()
audioMutex.Lock()
currentAudioTrack = audioTrack currentAudioTrack = audioTrack
oldRelay := outputRelay
oldSource := outputSource
outputRelay = nil
outputSource = nil
var newRelay *audio.OutputRelay if err := startAudio(); err != nil {
var newSource audio.AudioSource audioLogger.Error().Err(err).Msg("Failed to start with new audio track")
if currentAudioTrack != nil && audioOutputEnabled.Load() {
ensureConfigLoaded()
alsaDevice := getAlsaDevice(config.AudioOutputSource)
newSource := audio.NewCgoOutputSource(alsaDevice)
newSource.SetConfig(getAudioConfig())
newRelay = audio.NewOutputRelay(newSource, currentAudioTrack)
outputSource = newSource
outputRelay = newRelay
}
audioMutex.Unlock()
// Stop/start resources outside mutex to avoid blocking on CGO calls
if oldRelay != nil {
oldRelay.Stop()
}
if oldSource != nil {
oldSource.Disconnect()
}
if newRelay != nil {
if err := newRelay.Start(); err != nil {
audioLogger.Error().Err(err).Msg("Failed to start output relay")
}
} }
} }
@ -250,72 +246,44 @@ func SetAudioOutputSource(source string) error {
return nil return nil
} }
stopOutputAudio()
config.AudioOutputSource = source config.AudioOutputSource = source
stopOutputAudio() if err := startAudio(); err != nil {
audioLogger.Error().Err(err).Str("source", source).Msg("Failed to start audio output after source change")
if audioOutputEnabled.Load() && activeConnections.Load() > 0 && currentAudioTrack != nil {
alsaDevice := getAlsaDevice(source)
newSource := audio.NewCgoOutputSource(alsaDevice)
newSource.SetConfig(getAudioConfig())
newRelay := audio.NewOutputRelay(newSource, currentAudioTrack)
audioMutex.Lock()
outputSource = newSource
outputRelay = newRelay
audioMutex.Unlock()
if err := newRelay.Start(); err != nil {
audioLogger.Error().Err(err).Str("source", source).Msg("Failed to start audio relay with new source")
}
} }
return SaveConfig() return SaveConfig()
} }
func RestartAudioOutput() { func RestartAudioOutput() error {
audioMutex.Lock() audioMutex.Lock()
hasActiveOutput := outputSource != nil && currentAudioTrack != nil && audioOutputEnabled.Load() hasActiveOutput := audioOutputEnabled.Load() && currentAudioTrack != nil && outputSource.Load() != nil
audioMutex.Unlock() audioMutex.Unlock()
if !hasActiveOutput { if !hasActiveOutput {
return return nil
} }
audioLogger.Info().Msg("Restarting audio output") audioLogger.Info().Msg("Restarting audio output")
stopOutputAudio() stopOutputAudio()
return startAudio()
ensureConfigLoaded()
alsaDevice := getAlsaDevice(config.AudioOutputSource)
newSource := audio.NewCgoOutputSource(alsaDevice)
newSource.SetConfig(getAudioConfig())
newRelay := audio.NewOutputRelay(newSource, currentAudioTrack)
audioMutex.Lock()
outputSource = newSource
outputRelay = newRelay
audioMutex.Unlock()
if err := newRelay.Start(); err != nil {
audioLogger.Error().Err(err).Msg("Failed to restart audio output")
}
} }
func handleInputTrackForSession(track *webrtc.TrackRemote) { func handleInputTrackForSession(track *webrtc.TrackRemote) {
myTrackID := track.ID() myTrackID := track.ID()
audioLogger.Debug(). trackLogger := audioLogger.With().
Str("codec", track.Codec().MimeType). Str("codec", track.Codec().MimeType).
Str("track_id", myTrackID). Str("track_id", myTrackID).
Msg("starting input track handler") Logger()
trackLogger.Debug().Msg("starting input track handler")
for { for {
currentTrackID := currentInputTrack.Load() currentTrackID := currentInputTrack.Load()
if currentTrackID != nil && *currentTrackID != myTrackID { if currentTrackID != nil && *currentTrackID != myTrackID {
audioLogger.Debug(). trackLogger.Debug().
Str("my_track_id", myTrackID).
Str("current_track_id", *currentTrackID). Str("current_track_id", *currentTrackID).
Msg("input track handler exiting - superseded") Msg("input track handler exiting - superseded")
return return
@ -324,10 +292,10 @@ func handleInputTrackForSession(track *webrtc.TrackRemote) {
rtpPacket, _, err := track.ReadRTP() rtpPacket, _, err := track.ReadRTP()
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
audioLogger.Debug().Str("track_id", myTrackID).Msg("input track ended") trackLogger.Debug().Msg("input track ended")
return return
} }
audioLogger.Warn().Err(err).Str("track_id", myTrackID).Msg("failed to read RTP packet") trackLogger.Warn().Err(err).Msg("failed to read RTP packet")
continue continue
} }

View File

@ -25,46 +25,47 @@ const (
) )
type CgoSource struct { type CgoSource struct {
direction string outputDevice bool
alsaDevice string alsaDevice string
initialized bool connected bool
connected bool mu sync.Mutex
mu sync.Mutex logger zerolog.Logger
logger zerolog.Logger opusBuf []byte
opusBuf []byte config AudioConfig
config AudioConfig
} }
func NewCgoOutputSource(alsaDevice string) *CgoSource { var _ AudioSource = (*CgoSource)(nil)
logger := logging.GetDefaultLogger().With().Str("component", "audio-output-cgo").Logger()
func NewCgoOutputSource(alsaDevice string, cfg AudioConfig) AudioSource {
logger := logging.GetDefaultLogger().With().
Str("component", "audio-output-cgo").
Str("alsa_device", alsaDevice).
Logger()
return &CgoSource{ return &CgoSource{
direction: "output", outputDevice: true,
alsaDevice: alsaDevice, alsaDevice: alsaDevice,
logger: logger, logger: logger,
opusBuf: make([]byte, ipcMaxFrameSize), opusBuf: make([]byte, ipcMaxFrameSize),
config: DefaultAudioConfig(), config: cfg,
} }
} }
func NewCgoInputSource(alsaDevice string) *CgoSource { func NewCgoInputSource(alsaDevice string, cfg AudioConfig) AudioSource {
logger := logging.GetDefaultLogger().With().Str("component", "audio-input-cgo").Logger() logger := logging.GetDefaultLogger().With().
Str("component", "audio-input-cgo").
Str("alsa_device", alsaDevice).
Logger()
return &CgoSource{ return &CgoSource{
direction: "input", outputDevice: false,
alsaDevice: alsaDevice, alsaDevice: alsaDevice,
logger: logger, logger: logger,
opusBuf: make([]byte, ipcMaxFrameSize), opusBuf: make([]byte, ipcMaxFrameSize),
config: DefaultAudioConfig(), config: cfg,
} }
} }
func (c *CgoSource) SetConfig(cfg AudioConfig) {
c.mu.Lock()
defer c.mu.Unlock()
c.config = cfg
}
func (c *CgoSource) Connect() error { func (c *CgoSource) Connect() error {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
@ -73,7 +74,7 @@ func (c *CgoSource) Connect() error {
return nil return nil
} }
if c.direction == "output" { if c.outputDevice {
os.Setenv("ALSA_CAPTURE_DEVICE", c.alsaDevice) os.Setenv("ALSA_CAPTURE_DEVICE", c.alsaDevice)
dtx := C.uchar(0) dtx := C.uchar(0)
@ -93,7 +94,6 @@ func (c *CgoSource) Connect() error {
Uint8("buffer_periods", c.config.BufferPeriods). Uint8("buffer_periods", c.config.BufferPeriods).
Uint32("sample_rate", c.config.SampleRate). Uint32("sample_rate", c.config.SampleRate).
Uint8("packet_loss_perc", c.config.PacketLossPerc). Uint8("packet_loss_perc", c.config.PacketLossPerc).
Str("alsa_device", c.alsaDevice).
Msg("Initializing audio capture") Msg("Initializing audio capture")
C.update_audio_constants( C.update_audio_constants(
@ -139,7 +139,6 @@ func (c *CgoSource) Connect() error {
} }
c.connected = true c.connected = true
c.initialized = true
return nil return nil
} }
@ -151,10 +150,12 @@ func (c *CgoSource) Disconnect() {
return return
} }
if c.direction == "output" { if c.outputDevice {
C.jetkvm_audio_capture_close() C.jetkvm_audio_capture_close()
os.Unsetenv("ALSA_CAPTURE_DEVICE")
} else { } else {
C.jetkvm_audio_playback_close() C.jetkvm_audio_playback_close()
os.Unsetenv("ALSA_PLAYBACK_DEVICE")
} }
c.connected = false c.connected = false
@ -173,7 +174,7 @@ func (c *CgoSource) ReadMessage() (uint8, []byte, error) {
return 0, nil, fmt.Errorf("not connected") return 0, nil, fmt.Errorf("not connected")
} }
if c.direction != "output" { if !c.outputDevice {
c.mu.Unlock() c.mu.Unlock()
return 0, nil, fmt.Errorf("ReadMessage only supported for output direction") return 0, nil, fmt.Errorf("ReadMessage only supported for output direction")
} }
@ -203,7 +204,7 @@ func (c *CgoSource) WriteMessage(msgType uint8, payload []byte) error {
return fmt.Errorf("not connected") return fmt.Errorf("not connected")
} }
if c.direction != "input" { if c.outputDevice {
c.mu.Unlock() c.mu.Unlock()
return fmt.Errorf("WriteMessage only supported for input direction") return fmt.Errorf("WriteMessage only supported for input direction")
} }

View File

@ -6,11 +6,13 @@ package audio
type CgoSource struct{} type CgoSource struct{}
func NewCgoOutputSource(alsaDevice string) *CgoSource { var _ AudioSource = (*CgoSource)(nil)
func NewCgoOutputSource(alsaDevice string, audioConfig AudioConfig) AudioSource {
panic("audio CGO source not supported on this platform") panic("audio CGO source not supported on this platform")
} }
func NewCgoInputSource(alsaDevice string) *CgoSource { func NewCgoInputSource(alsaDevice string, audioConfig AudioConfig) AudioSource {
panic("audio CGO source not supported on this platform") panic("audio CGO source not supported on this platform")
} }
@ -33,7 +35,3 @@ func (c *CgoSource) ReadMessage() (uint8, []byte, error) {
func (c *CgoSource) WriteMessage(msgType uint8, payload []byte) error { func (c *CgoSource) WriteMessage(msgType uint8, payload []byte) error {
panic("audio CGO source not supported on this platform") panic("audio CGO source not supported on this platform")
} }
func (c *CgoSource) SetConfig(cfg AudioConfig) {
panic("audio CGO source not supported on this platform")
}

View File

@ -13,7 +13,7 @@ import (
) )
type OutputRelay struct { type OutputRelay struct {
source AudioSource source *AudioSource
audioTrack *webrtc.TrackLocalStaticSample audioTrack *webrtc.TrackLocalStaticSample
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
@ -26,7 +26,7 @@ type OutputRelay struct {
framesDropped atomic.Uint32 framesDropped atomic.Uint32
} }
func NewOutputRelay(source AudioSource, audioTrack *webrtc.TrackLocalStaticSample) *OutputRelay { func NewOutputRelay(source *AudioSource, audioTrack *webrtc.TrackLocalStaticSample) *OutputRelay {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
logger := logging.GetDefaultLogger().With().Str("component", "audio-output-relay").Logger() logger := logging.GetDefaultLogger().With().Str("component", "audio-output-relay").Logger()
@ -73,19 +73,19 @@ func (r *OutputRelay) relayLoop() {
const reconnectDelay = 1 * time.Second const reconnectDelay = 1 * time.Second
for r.running.Load() { for r.running.Load() {
if !r.source.IsConnected() { if !(*r.source).IsConnected() {
if err := r.source.Connect(); err != nil { if err := (*r.source).Connect(); err != nil {
r.logger.Debug().Err(err).Msg("failed to connect, will retry") r.logger.Debug().Err(err).Msg("failed to connect, will retry")
time.Sleep(reconnectDelay) time.Sleep(reconnectDelay)
continue continue
} }
} }
msgType, payload, err := r.source.ReadMessage() msgType, payload, err := (*r.source).ReadMessage()
if err != nil { if err != nil {
if r.running.Load() { if r.running.Load() {
r.logger.Warn().Err(err).Msg("read error, reconnecting") r.logger.Warn().Err(err).Msg("read error, reconnecting")
r.source.Disconnect() (*r.source).Disconnect()
time.Sleep(reconnectDelay) time.Sleep(reconnectDelay)
} }
continue continue
@ -104,14 +104,14 @@ func (r *OutputRelay) relayLoop() {
} }
type InputRelay struct { type InputRelay struct {
source AudioSource source *AudioSource
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
logger zerolog.Logger logger zerolog.Logger
running atomic.Bool running atomic.Bool
} }
func NewInputRelay(source AudioSource) *InputRelay { func NewInputRelay(source *AudioSource) *InputRelay {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
logger := logging.GetDefaultLogger().With().Str("component", "audio-input-relay").Logger() logger := logging.GetDefaultLogger().With().Str("component", "audio-input-relay").Logger()

View File

@ -32,5 +32,4 @@ type AudioSource interface {
IsConnected() bool IsConnected() bool
Connect() error Connect() error
Disconnect() Disconnect()
SetConfig(cfg AudioConfig)
} }

View File

@ -18,7 +18,6 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go.bug.st/serial" "go.bug.st/serial"
"github.com/jetkvm/kvm/internal/audio"
"github.com/jetkvm/kvm/internal/hidrpc" "github.com/jetkvm/kvm/internal/hidrpc"
"github.com/jetkvm/kvm/internal/usbgadget" "github.com/jetkvm/kvm/internal/usbgadget"
"github.com/jetkvm/kvm/internal/utils" "github.com/jetkvm/kvm/internal/utils"
@ -688,10 +687,12 @@ func rpcGetUsbConfig() (usbgadget.Config, error) {
func rpcSetUsbConfig(usbConfig usbgadget.Config) error { func rpcSetUsbConfig(usbConfig usbgadget.Config) error {
LoadConfig() LoadConfig()
wasUsbAudioEnabled := config.UsbDevices != nil && config.UsbDevices.Audio
config.UsbConfig = &usbConfig config.UsbConfig = &usbConfig
gadget.SetGadgetConfig(config.UsbConfig) gadget.SetGadgetConfig(config.UsbConfig)
wasAudioEnabled := config.UsbDevices != nil && config.UsbDevices.Audio
return updateUsbRelatedConfig(wasAudioEnabled) return updateUsbRelatedConfig(wasUsbAudioEnabled)
} }
func rpcGetWakeOnLanDevices() ([]WakeOnLanDevice, error) { func rpcGetWakeOnLanDevices() ([]WakeOnLanDevice, error) {
@ -903,43 +904,23 @@ func rpcGetUsbDevices() (usbgadget.Devices, error) {
return *config.UsbDevices, nil return *config.UsbDevices, nil
} }
func updateUsbRelatedConfig(wasAudioEnabled bool) error { func updateUsbRelatedConfig(wasUsbAudioEnabled bool) error {
ensureConfigLoaded() ensureConfigLoaded()
nowHasUsbAudio := config.UsbDevices != nil && config.UsbDevices.Audio
outputSourceIsUsb := config.AudioOutputSource == "usb"
audioMutex.Lock() // must stop input audio before reconfiguring
inRelay := inputRelay stopInputAudio()
inputRelay = nil
audioMutex.Unlock()
inSource := inputSource.Swap(nil) // if we're currently sourcing audio from USB, stop the output audio before reconfiguring
if outputSourceIsUsb {
if inRelay != nil {
inRelay.Stop()
}
if inSource != nil {
(*inSource).Disconnect()
}
// Auto-switch to HDMI audio output when USB audio is disabled
audioNowEnabled := config.UsbDevices != nil && config.UsbDevices.Audio
if wasAudioEnabled && !audioNowEnabled && config.AudioOutputSource == "usb" {
config.AudioOutputSource = "hdmi"
stopOutputAudio() stopOutputAudio()
if audioOutputEnabled.Load() && activeConnections.Load() > 0 && currentAudioTrack != nil { }
alsaDevice := getAlsaDevice("hdmi")
newSource := audio.NewCgoOutputSource(alsaDevice)
newSource.SetConfig(getAudioConfig())
newRelay := audio.NewOutputRelay(newSource, currentAudioTrack)
audioMutex.Lock() // Auto-switch to HDMI audio output when USB audio was selected and is now disabled
outputSource = newSource if wasUsbAudioEnabled && !nowHasUsbAudio && config.AudioOutputSource == "usb" {
outputRelay = newRelay logger.Info().Msg("USB audio just disabled, automatic switch audio output source to HDMI")
audioMutex.Unlock() config.AudioOutputSource = "hdmi"
if err := newRelay.Start(); err != nil {
logger.Warn().Err(err).Msg("Failed to start HDMI audio after USB audio disabled")
}
}
} }
if err := gadget.UpdateGadgetConfig(); err != nil { if err := gadget.UpdateGadgetConfig(); err != nil {
@ -950,18 +931,15 @@ func updateUsbRelatedConfig(wasAudioEnabled bool) error {
return fmt.Errorf("failed to save config: %w", err) return fmt.Errorf("failed to save config: %w", err)
} }
// Restart audio if USB audio is enabled with active connections if err := startAudio(); err != nil {
if activeConnections.Load() > 0 && config.UsbDevices != nil && config.UsbDevices.Audio { logger.Warn().Err(err).Msg("Failed to restart audio after USB reconfiguration")
if err := startAudio(); err != nil {
logger.Warn().Err(err).Msg("Failed to restart audio after USB reconfiguration")
}
} }
return nil return nil
} }
func rpcSetUsbDevices(usbDevices usbgadget.Devices) error { func rpcSetUsbDevices(usbDevices usbgadget.Devices) error {
wasAudioEnabled := config.UsbDevices != nil && config.UsbDevices.Audio wasUsbAudioEnabled := config.UsbDevices != nil && config.UsbDevices.Audio
currentDevices := gadget.GetGadgetDevices() currentDevices := gadget.GetGadgetDevices()
// Skip reconfiguration if devices haven't changed to avoid HID disruption // Skip reconfiguration if devices haven't changed to avoid HID disruption
@ -973,11 +951,11 @@ func rpcSetUsbDevices(usbDevices usbgadget.Devices) error {
config.UsbDevices = &usbDevices config.UsbDevices = &usbDevices
gadget.SetGadgetDevices(config.UsbDevices) gadget.SetGadgetDevices(config.UsbDevices)
return updateUsbRelatedConfig(wasAudioEnabled) return updateUsbRelatedConfig(wasUsbAudioEnabled)
} }
func rpcSetUsbDeviceState(device string, enabled bool) error { func rpcSetUsbDeviceState(device string, enabled bool) error {
wasAudioEnabled := config.UsbDevices != nil && config.UsbDevices.Audio wasUsbAudioEnabled := config.UsbDevices != nil && config.UsbDevices.Audio
currentDevices := gadget.GetGadgetDevices() currentDevices := gadget.GetGadgetDevices()
switch device { switch device {
@ -1002,7 +980,7 @@ func rpcSetUsbDeviceState(device string, enabled bool) error {
} }
gadget.SetGadgetDevices(config.UsbDevices) gadget.SetGadgetDevices(config.UsbDevices)
return updateUsbRelatedConfig(wasAudioEnabled) return updateUsbRelatedConfig(wasUsbAudioEnabled)
} }
func rpcGetAudioOutputEnabled() (bool, error) { func rpcGetAudioOutputEnabled() (bool, error) {
@ -1105,8 +1083,7 @@ func rpcSetAudioConfig(bitrate int, complexity int, dtxEnabled bool, fecEnabled
} }
func rpcRestartAudioOutput() error { func rpcRestartAudioOutput() error {
RestartAudioOutput() return RestartAudioOutput()
return nil
} }
func rpcGetAudioInputAutoEnable() (bool, error) { func rpcGetAudioInputAutoEnable() (bool, error) {