mirror of https://github.com/jetkvm/kvm.git
Compare commits
2 Commits
ef4b5572be
...
704d807fe9
| Author | SHA1 | Date |
|---|---|---|
|
|
704d807fe9 | |
|
|
7ce1520c2f |
|
|
@ -51,6 +51,11 @@ func (ais *AudioInputSupervisor) SetOpusConfig(bitrate, complexity, vbr, signalT
|
||||||
|
|
||||||
// Start begins supervising the audio input server process
|
// Start begins supervising the audio input server process
|
||||||
func (ais *AudioInputSupervisor) Start() error {
|
func (ais *AudioInputSupervisor) Start() error {
|
||||||
|
// Check if USB audio is enabled before starting
|
||||||
|
if !IsUsbAudioEnabled() {
|
||||||
|
return fmt.Errorf("USB audio device is disabled - cannot start audio input supervisor")
|
||||||
|
}
|
||||||
|
|
||||||
if !atomic.CompareAndSwapInt32(&ais.running, 0, 1) {
|
if !atomic.CompareAndSwapInt32(&ais.running, 0, 1) {
|
||||||
return fmt.Errorf("audio input supervisor is already running")
|
return fmt.Errorf("audio input supervisor is already running")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
var (
|
var (
|
||||||
globalOutputSupervisor unsafe.Pointer // *AudioOutputSupervisor
|
globalOutputSupervisor unsafe.Pointer // *AudioOutputSupervisor
|
||||||
globalInputSupervisor unsafe.Pointer // *AudioInputSupervisor
|
globalInputSupervisor unsafe.Pointer // *AudioInputSupervisor
|
||||||
|
usbAudioEnabledFunc func() bool // Callback to check USB audio status
|
||||||
)
|
)
|
||||||
|
|
||||||
// isAudioServerProcess detects if we're running as the audio server subprocess
|
// isAudioServerProcess detects if we're running as the audio server subprocess
|
||||||
|
|
@ -84,3 +85,16 @@ func GetAudioInputSupervisor() *AudioInputSupervisor {
|
||||||
}
|
}
|
||||||
return (*AudioInputSupervisor)(ptr)
|
return (*AudioInputSupervisor)(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetUsbAudioEnabledCallback sets the callback function to check USB audio status
|
||||||
|
func SetUsbAudioEnabledCallback(callback func() bool) {
|
||||||
|
usbAudioEnabledFunc = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUsbAudioEnabled checks if USB audio device is enabled via the callback
|
||||||
|
func IsUsbAudioEnabled() bool {
|
||||||
|
if usbAudioEnabledFunc == nil {
|
||||||
|
return false // Default to disabled if callback not set
|
||||||
|
}
|
||||||
|
return usbAudioEnabledFunc()
|
||||||
|
}
|
||||||
|
|
|
||||||
136
jsonrpc.go
136
jsonrpc.go
|
|
@ -914,11 +914,6 @@ func validateAudioConfiguration(enabled bool) error {
|
||||||
return nil // Disabling audio is always allowed
|
return nil // Disabling audio is always allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if audio supervisor is available
|
|
||||||
if audioSupervisor == nil {
|
|
||||||
return fmt.Errorf("audio supervisor not initialized - audio functionality not available")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if ALSA devices are available by attempting to list them
|
// Check if ALSA devices are available by attempting to list them
|
||||||
// This is a basic check to ensure the system has audio capabilities
|
// This is a basic check to ensure the system has audio capabilities
|
||||||
if _, err := os.Stat("/proc/asound/cards"); os.IsNotExist(err) {
|
if _, err := os.Stat("/proc/asound/cards"); os.IsNotExist(err) {
|
||||||
|
|
@ -965,13 +960,12 @@ func rpcSetUsbDevices(usbDevices usbgadget.Devices) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop global audio input supervisor if active
|
// Stop global audio input supervisor if active
|
||||||
audioInputSupervisor := audio.GetAudioInputSupervisor()
|
if AudioInputSupervisor != nil && AudioInputSupervisor.IsRunning() {
|
||||||
if audioInputSupervisor != nil && audioInputSupervisor.IsRunning() {
|
|
||||||
logger.Info().Msg("stopping global audio input supervisor")
|
logger.Info().Msg("stopping global audio input supervisor")
|
||||||
audioInputSupervisor.Stop()
|
AudioInputSupervisor.Stop()
|
||||||
// Wait for audio input supervisor to fully stop
|
// Wait for audio input supervisor to fully stop
|
||||||
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
||||||
if !audioInputSupervisor.IsRunning() {
|
if !AudioInputSupervisor.IsRunning() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
@ -980,12 +974,12 @@ func rpcSetUsbDevices(usbDevices usbgadget.Devices) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop audio output supervisor
|
// Stop audio output supervisor
|
||||||
if audioSupervisor != nil && audioSupervisor.IsRunning() {
|
if AudioOutputSupervisor != nil && AudioOutputSupervisor.IsRunning() {
|
||||||
logger.Info().Msg("stopping audio output supervisor")
|
logger.Info().Msg("stopping audio output supervisor")
|
||||||
audioSupervisor.Stop()
|
AudioOutputSupervisor.Stop()
|
||||||
// Wait for audio processes to fully stop before proceeding
|
// Wait for audio processes to fully stop before proceeding
|
||||||
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
||||||
if !audioSupervisor.IsRunning() {
|
if !AudioOutputSupervisor.IsRunning() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
@ -994,8 +988,8 @@ func rpcSetUsbDevices(usbDevices usbgadget.Devices) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info().Msg("audio processes stopped, proceeding with USB gadget reconfiguration")
|
logger.Info().Msg("audio processes stopped, proceeding with USB gadget reconfiguration")
|
||||||
} else if newAudioEnabled && audioSupervisor != nil && !audioSupervisor.IsRunning() {
|
} else if newAudioEnabled {
|
||||||
// Start audio processes when audio is enabled (after USB reconfiguration)
|
// Audio being enabled - supervisors will be created/started after USB reconfiguration
|
||||||
logger.Info().Msg("audio will be started after USB gadget reconfiguration")
|
logger.Info().Msg("audio will be started after USB gadget reconfiguration")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1010,17 +1004,42 @@ func rpcSetUsbDevices(usbDevices usbgadget.Devices) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start audio processes after successful USB reconfiguration if needed
|
// Start audio processes after successful USB reconfiguration if needed
|
||||||
if previousAudioEnabled != newAudioEnabled && newAudioEnabled && audioSupervisor != nil {
|
if previousAudioEnabled != newAudioEnabled && newAudioEnabled {
|
||||||
// Ensure supervisor is fully stopped before starting
|
// Create supervisors if they don't exist (happens when audio was previously disabled)
|
||||||
|
if AudioOutputSupervisor == nil {
|
||||||
|
logger.Info().Msg("creating audio output supervisor for USB audio enablement")
|
||||||
|
AudioOutputSupervisor = audio.NewAudioOutputSupervisor()
|
||||||
|
audio.SetAudioOutputSupervisor(AudioOutputSupervisor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create audio input supervisor if it doesn't exist
|
||||||
|
if AudioInputSupervisor == nil {
|
||||||
|
logger.Info().Msg("creating audio input supervisor for USB audio enablement")
|
||||||
|
AudioInputSupervisor = audio.NewAudioInputSupervisor()
|
||||||
|
audio.SetAudioInputSupervisor(AudioInputSupervisor)
|
||||||
|
|
||||||
|
// Set default OPUS configuration for audio input supervisor
|
||||||
|
audioConfig := audio.Config
|
||||||
|
AudioInputSupervisor.SetOpusConfig(
|
||||||
|
audioConfig.AudioQualityLowInputBitrate*1000, // Convert kbps to bps
|
||||||
|
audioConfig.AudioQualityLowOpusComplexity,
|
||||||
|
audioConfig.AudioQualityLowOpusVBR,
|
||||||
|
audioConfig.AudioQualityLowOpusSignalType,
|
||||||
|
audioConfig.AudioQualityLowOpusBandwidth,
|
||||||
|
audioConfig.AudioQualityLowOpusDTX,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure audio output supervisor is fully stopped before starting
|
||||||
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
||||||
if !audioSupervisor.IsRunning() {
|
if !AudioOutputSupervisor.IsRunning() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
logger.Info().Msg("starting audio processes after USB gadget reconfiguration")
|
logger.Info().Msg("starting audio processes after USB gadget reconfiguration")
|
||||||
if err := audioSupervisor.Start(); err != nil {
|
if err := AudioOutputSupervisor.Start(); err != nil {
|
||||||
logger.Error().Err(err).Msg("failed to start audio supervisor")
|
logger.Error().Err(err).Msg("failed to start audio output supervisor")
|
||||||
// Don't return error here as USB reconfiguration was successful
|
// Don't return error here as USB reconfiguration was successful
|
||||||
} else {
|
} else {
|
||||||
// Broadcast audio device change event to notify WebRTC session
|
// Broadcast audio device change event to notify WebRTC session
|
||||||
|
|
@ -1028,6 +1047,18 @@ func rpcSetUsbDevices(usbDevices usbgadget.Devices) error {
|
||||||
broadcaster.BroadcastAudioDeviceChanged(true, "usb_reconfiguration")
|
broadcaster.BroadcastAudioDeviceChanged(true, "usb_reconfiguration")
|
||||||
logger.Info().Msg("broadcasted audio device change event after USB reconfiguration")
|
logger.Info().Msg("broadcasted audio device change event after USB reconfiguration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start audio input supervisor if microphone restoration is enabled
|
||||||
|
// This makes audio input consistent with audio output - both auto-start when USB audio enabled
|
||||||
|
if AudioInputSupervisor != nil {
|
||||||
|
logger.Info().Msg("starting audio input supervisor after USB gadget reconfiguration")
|
||||||
|
if err := AudioInputSupervisor.Start(); err != nil {
|
||||||
|
logger.Warn().Err(err).Msg("failed to start audio input supervisor - will be available on-demand")
|
||||||
|
// This is not a critical failure - audio input can be started on-demand later
|
||||||
|
} else {
|
||||||
|
logger.Info().Msg("audio input supervisor started successfully after USB reconfiguration")
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if previousAudioEnabled != newAudioEnabled {
|
} else if previousAudioEnabled != newAudioEnabled {
|
||||||
// Broadcast audio device change event for disabling audio
|
// Broadcast audio device change event for disabling audio
|
||||||
broadcaster := audio.GetAudioEventBroadcaster()
|
broadcaster := audio.GetAudioEventBroadcaster()
|
||||||
|
|
@ -1074,13 +1105,12 @@ func rpcSetUsbDeviceState(device string, enabled bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop global audio input supervisor if active
|
// Stop global audio input supervisor if active
|
||||||
audioInputSupervisor := audio.GetAudioInputSupervisor()
|
if AudioInputSupervisor != nil && AudioInputSupervisor.IsRunning() {
|
||||||
if audioInputSupervisor != nil && audioInputSupervisor.IsRunning() {
|
|
||||||
logger.Info().Msg("stopping global audio input supervisor")
|
logger.Info().Msg("stopping global audio input supervisor")
|
||||||
audioInputSupervisor.Stop()
|
AudioInputSupervisor.Stop()
|
||||||
// Wait for audio input supervisor to fully stop
|
// Wait for audio input supervisor to fully stop
|
||||||
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
||||||
if !audioInputSupervisor.IsRunning() {
|
if !AudioInputSupervisor.IsRunning() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
@ -1089,41 +1119,77 @@ func rpcSetUsbDeviceState(device string, enabled bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop audio output supervisor
|
// Stop audio output supervisor
|
||||||
if audioSupervisor != nil && audioSupervisor.IsRunning() {
|
if AudioOutputSupervisor != nil && AudioOutputSupervisor.IsRunning() {
|
||||||
logger.Info().Msg("stopping audio output supervisor")
|
logger.Info().Msg("stopping audio output supervisor")
|
||||||
audioSupervisor.Stop()
|
AudioOutputSupervisor.Stop()
|
||||||
// Wait for audio processes to fully stop
|
// Wait for audio processes to fully stop
|
||||||
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
||||||
if !audioSupervisor.IsRunning() {
|
if !AudioOutputSupervisor.IsRunning() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
logger.Info().Msg("audio output supervisor stopped")
|
logger.Info().Msg("audio output supervisor stopped")
|
||||||
}
|
}
|
||||||
} else if enabled && audioSupervisor != nil {
|
} else if enabled {
|
||||||
// Ensure supervisor is fully stopped before starting
|
// Create supervisors if they don't exist (happens when audio was previously disabled)
|
||||||
|
if AudioOutputSupervisor == nil {
|
||||||
|
logger.Info().Msg("creating audio output supervisor for audio device enablement")
|
||||||
|
AudioOutputSupervisor = audio.NewAudioOutputSupervisor()
|
||||||
|
audio.SetAudioOutputSupervisor(AudioOutputSupervisor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create audio input supervisor if it doesn't exist
|
||||||
|
if AudioInputSupervisor == nil {
|
||||||
|
logger.Info().Msg("creating audio input supervisor for audio device enablement")
|
||||||
|
AudioInputSupervisor = audio.NewAudioInputSupervisor()
|
||||||
|
audio.SetAudioInputSupervisor(AudioInputSupervisor)
|
||||||
|
|
||||||
|
// Set default OPUS configuration for audio input supervisor
|
||||||
|
audioConfig := audio.Config
|
||||||
|
AudioInputSupervisor.SetOpusConfig(
|
||||||
|
audioConfig.AudioQualityLowInputBitrate*1000, // Convert kbps to bps
|
||||||
|
audioConfig.AudioQualityLowOpusComplexity,
|
||||||
|
audioConfig.AudioQualityLowOpusVBR,
|
||||||
|
audioConfig.AudioQualityLowOpusSignalType,
|
||||||
|
audioConfig.AudioQualityLowOpusBandwidth,
|
||||||
|
audioConfig.AudioQualityLowOpusDTX,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure audio output supervisor is fully stopped before starting
|
||||||
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
for i := 0; i < 50; i++ { // Wait up to 5 seconds
|
||||||
if !audioSupervisor.IsRunning() {
|
if !AudioOutputSupervisor.IsRunning() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
// Start audio processes when audio is enabled
|
// Start audio processes when audio is enabled
|
||||||
logger.Info().Msg("starting audio processes due to audio device being enabled")
|
logger.Info().Msg("starting audio processes due to audio device being enabled")
|
||||||
if err := audioSupervisor.Start(); err != nil {
|
if err := AudioOutputSupervisor.Start(); err != nil {
|
||||||
logger.Error().Err(err).Msg("failed to start audio supervisor")
|
logger.Error().Err(err).Msg("failed to start audio output supervisor")
|
||||||
} else {
|
} else {
|
||||||
// Broadcast audio device change event to notify WebRTC session
|
// Broadcast audio device change event to notify WebRTC session
|
||||||
broadcaster := audio.GetAudioEventBroadcaster()
|
broadcaster := audio.GetAudioEventBroadcaster()
|
||||||
broadcaster.BroadcastAudioDeviceChanged(true, "device_enabled")
|
broadcaster.BroadcastAudioDeviceChanged(true, "device_enabled")
|
||||||
logger.Info().Msg("broadcasted audio device change event after enabling audio device")
|
logger.Info().Msg("broadcasted audio device change event after enabling audio device")
|
||||||
}
|
}
|
||||||
// Always broadcast the audio device change event regardless of enable/disable
|
|
||||||
broadcaster := audio.GetAudioEventBroadcaster()
|
// Start audio input supervisor consistently with output
|
||||||
broadcaster.BroadcastAudioDeviceChanged(enabled, "device_state_changed")
|
if AudioInputSupervisor != nil {
|
||||||
logger.Info().Bool("enabled", enabled).Msg("broadcasted audio device state change event")
|
logger.Info().Msg("starting audio input supervisor due to audio device being enabled")
|
||||||
|
if err := AudioInputSupervisor.Start(); err != nil {
|
||||||
|
logger.Warn().Err(err).Msg("failed to start audio input supervisor - will be available on-demand")
|
||||||
|
} else {
|
||||||
|
logger.Info().Msg("audio input supervisor started successfully after enabling audio device")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always broadcast the audio device change event regardless of enable/disable
|
||||||
|
broadcaster := audio.GetAudioEventBroadcaster()
|
||||||
|
broadcaster.BroadcastAudioDeviceChanged(enabled, "device_state_changed")
|
||||||
|
logger.Info().Bool("enabled", enabled).Msg("broadcasted audio device state change event")
|
||||||
config.UsbDevices.Audio = enabled
|
config.UsbDevices.Audio = enabled
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid device: %s", device)
|
return fmt.Errorf("invalid device: %s", device)
|
||||||
|
|
|
||||||
34
main.go
34
main.go
|
|
@ -15,30 +15,36 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
appCtx context.Context
|
appCtx context.Context
|
||||||
isAudioServer bool
|
isAudioServer bool
|
||||||
audioProcessDone chan struct{}
|
audioProcessDone chan struct{}
|
||||||
audioSupervisor *audio.AudioOutputSupervisor
|
AudioOutputSupervisor *audio.AudioOutputSupervisor // Exported for jsonrpc access
|
||||||
|
AudioInputSupervisor *audio.AudioInputSupervisor // Exported for jsonrpc access
|
||||||
)
|
)
|
||||||
|
|
||||||
func startAudioSubprocess() error {
|
func startAudioSubprocess() error {
|
||||||
// Initialize validation cache for optimal performance
|
// Initialize validation cache for optimal performance
|
||||||
audio.InitValidationCache()
|
audio.InitValidationCache()
|
||||||
|
|
||||||
|
// Set up USB audio status callback for the audio package
|
||||||
|
audio.SetUsbAudioEnabledCallback(func() bool {
|
||||||
|
return config.UsbDevices != nil && config.UsbDevices.Audio
|
||||||
|
})
|
||||||
|
|
||||||
// Create audio server supervisor
|
// Create audio server supervisor
|
||||||
audioSupervisor = audio.NewAudioOutputSupervisor()
|
AudioOutputSupervisor = audio.NewAudioOutputSupervisor()
|
||||||
|
|
||||||
// Set the global supervisor for access from audio package
|
// Set the global supervisor for access from audio package
|
||||||
audio.SetAudioOutputSupervisor(audioSupervisor)
|
audio.SetAudioOutputSupervisor(AudioOutputSupervisor)
|
||||||
|
|
||||||
// Create and register audio input supervisor (but don't start it)
|
// Create and register audio input supervisor (but don't start it)
|
||||||
// Audio input will be started on-demand through the UI
|
// Audio input will be started on-demand through the UI
|
||||||
audioInputSupervisor := audio.NewAudioInputSupervisor()
|
AudioInputSupervisor = audio.NewAudioInputSupervisor()
|
||||||
audio.SetAudioInputSupervisor(audioInputSupervisor)
|
audio.SetAudioInputSupervisor(AudioInputSupervisor)
|
||||||
|
|
||||||
// Set default OPUS configuration for audio input supervisor (low quality for single-core RV1106)
|
// Set default OPUS configuration for audio input supervisor (low quality for single-core RV1106)
|
||||||
audioConfig := audio.Config
|
audioConfig := audio.Config
|
||||||
audioInputSupervisor.SetOpusConfig(
|
AudioInputSupervisor.SetOpusConfig(
|
||||||
audioConfig.AudioQualityLowInputBitrate*1000, // Convert kbps to bps
|
audioConfig.AudioQualityLowInputBitrate*1000, // Convert kbps to bps
|
||||||
audioConfig.AudioQualityLowOpusComplexity,
|
audioConfig.AudioQualityLowOpusComplexity,
|
||||||
audioConfig.AudioQualityLowOpusVBR,
|
audioConfig.AudioQualityLowOpusVBR,
|
||||||
|
|
@ -51,7 +57,7 @@ func startAudioSubprocess() error {
|
||||||
// when the user activates microphone input through the UI
|
// when the user activates microphone input through the UI
|
||||||
|
|
||||||
// Set up callbacks for process lifecycle events
|
// Set up callbacks for process lifecycle events
|
||||||
audioSupervisor.SetCallbacks(
|
AudioOutputSupervisor.SetCallbacks(
|
||||||
// onProcessStart
|
// onProcessStart
|
||||||
func(pid int) {
|
func(pid int) {
|
||||||
logger.Info().Int("pid", pid).Msg("audio server process started")
|
logger.Info().Int("pid", pid).Msg("audio server process started")
|
||||||
|
|
@ -107,7 +113,7 @@ func startAudioSubprocess() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the supervisor
|
// Start the supervisor
|
||||||
if err := audioSupervisor.Start(); err != nil {
|
if err := AudioOutputSupervisor.Start(); err != nil {
|
||||||
return fmt.Errorf("failed to start audio supervisor: %w", err)
|
return fmt.Errorf("failed to start audio supervisor: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,7 +122,7 @@ func startAudioSubprocess() error {
|
||||||
defer close(audioProcessDone)
|
defer close(audioProcessDone)
|
||||||
|
|
||||||
// Wait for supervisor to stop
|
// Wait for supervisor to stop
|
||||||
for audioSupervisor.IsRunning() {
|
for AudioOutputSupervisor.IsRunning() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,9 +294,9 @@ func Main(audioServer bool, audioInputServer bool) {
|
||||||
|
|
||||||
// Stop audio subprocess and wait for cleanup
|
// Stop audio subprocess and wait for cleanup
|
||||||
if !isAudioServer {
|
if !isAudioServer {
|
||||||
if audioSupervisor != nil {
|
if AudioOutputSupervisor != nil {
|
||||||
logger.Info().Msg("stopping audio supervisor")
|
logger.Info().Msg("stopping audio supervisor")
|
||||||
audioSupervisor.Stop()
|
AudioOutputSupervisor.Stop()
|
||||||
}
|
}
|
||||||
<-audioProcessDone
|
<-audioProcessDone
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import { useRTCStore, useSettingsStore } from "@/hooks/stores";
|
import { useRTCStore, useSettingsStore } from "@/hooks/stores";
|
||||||
import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc";
|
import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
|
import { useAudioEvents, AudioDeviceChangedData } from "@/hooks/useAudioEvents";
|
||||||
import { devLog, devInfo, devWarn, devError, devOnly } from "@/utils/debug";
|
import { devLog, devInfo, devWarn, devError, devOnly } from "@/utils/debug";
|
||||||
import { AUDIO_CONFIG } from "@/config/constants";
|
import { AUDIO_CONFIG } from "@/config/constants";
|
||||||
|
|
||||||
|
|
@ -589,6 +590,31 @@ export function useMicrophone() {
|
||||||
};
|
};
|
||||||
}, []); // No dependencies to prevent re-running
|
}, []); // No dependencies to prevent re-running
|
||||||
|
|
||||||
|
// Handle audio device changes (USB audio enable/disable)
|
||||||
|
const handleAudioDeviceChanged = useCallback((data: AudioDeviceChangedData) => {
|
||||||
|
// When USB audio is re-enabled and user previously had microphone enabled, restore it
|
||||||
|
if (data.enabled && data.reason === "usb_reconfiguration" && microphoneWasEnabled && !isMicrophoneActive && peerConnection) {
|
||||||
|
devInfo("USB audio re-enabled and microphone was previously enabled - attempting to restore");
|
||||||
|
startMicrophone().then((result) => {
|
||||||
|
if (result.success) {
|
||||||
|
devInfo("Microphone successfully restored after USB audio re-enable");
|
||||||
|
} else {
|
||||||
|
devWarn("Failed to restore microphone after USB audio re-enable:", result.error);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
devWarn("Error restoring microphone after USB audio re-enable:", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// When USB audio is disabled, clear the microphone enabled flag if it was set
|
||||||
|
else if (!data.enabled && data.reason === "usb_reconfiguration" && microphoneWasEnabled) {
|
||||||
|
devInfo("USB audio disabled - clearing microphone enabled flag");
|
||||||
|
setMicrophoneWasEnabled(false);
|
||||||
|
}
|
||||||
|
}, [microphoneWasEnabled, isMicrophoneActive, peerConnection, startMicrophone, setMicrophoneWasEnabled]);
|
||||||
|
|
||||||
|
// Subscribe to audio device change events
|
||||||
|
useAudioEvents(handleAudioDeviceChanged);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isMicrophoneActive,
|
isMicrophoneActive,
|
||||||
isMicrophoneMuted,
|
isMicrophoneMuted,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue