feat(audio): implement audio input supervisor and opus config management

add audio input supervisor with opus configuration support
update audio quality presets and configuration handling
restructure audio subprocess management with environment variables
This commit is contained in:
Alex P 2025-08-28 22:02:22 +00:00
parent 0d4176cf98
commit 9c0aff4489
9 changed files with 306 additions and 57 deletions

View File

@ -13,6 +13,7 @@ func main() {
versionJsonPtr := flag.Bool("version-json", false, "print version as json and exit")
audioServerPtr := flag.Bool("audio-output-server", false, "Run as audio server subprocess")
audioInputServerPtr := flag.Bool("audio-input-server", false, "Run as audio input server subprocess")
flag.Parse()
if *versionPtr || *versionJsonPtr {

View File

@ -8,8 +8,8 @@ import (
)
var (
// Global audio output supervisor instance
globalOutputSupervisor unsafe.Pointer // *AudioOutputSupervisor
globalInputSupervisor unsafe.Pointer // *AudioInputSupervisor
)
// isAudioServerProcess detects if we're running as the audio server subprocess
@ -70,3 +70,17 @@ func GetAudioOutputSupervisor() *AudioOutputSupervisor {
}
return (*AudioOutputSupervisor)(ptr)
}
// SetAudioInputSupervisor sets the global audio input supervisor
func SetAudioInputSupervisor(supervisor *AudioInputSupervisor) {
atomic.StorePointer(&globalInputSupervisor, unsafe.Pointer(supervisor))
}
// GetAudioInputSupervisor returns the global audio input supervisor
func GetAudioInputSupervisor() *AudioInputSupervisor {
ptr := atomic.LoadPointer(&globalInputSupervisor)
if ptr == nil {
return nil
}
return (*AudioInputSupervisor)(ptr)
}

View File

@ -167,7 +167,7 @@ func SetAudioQuality(quality AudioQuality) {
if config, exists := presets[quality]; exists {
currentConfig = config
// Update CGO OPUS encoder parameters based on quality
// Get OPUS encoder parameters based on quality
var complexity, vbr, signalType, bandwidth, dtx int
switch quality {
case AudioQualityLow:
@ -203,11 +203,27 @@ func SetAudioQuality(quality AudioQuality) {
dtx = GetConfig().AudioQualityMediumOpusDTX
}
// Dynamically update CGO OPUS encoder parameters
// Use current VBR constraint setting from config
vbrConstraint := GetConfig().CGOOpusVBRConstraint
if err := updateOpusEncoderParams(config.Bitrate*1000, complexity, vbr, vbrConstraint, signalType, bandwidth, dtx); err != nil {
logging.GetDefaultLogger().Error().Err(err).Msg("Failed to update OPUS encoder parameters")
// Restart audio output subprocess with new OPUS configuration
if supervisor := GetAudioOutputSupervisor(); supervisor != nil {
logger := logging.GetDefaultLogger().With().Str("component", "audio").Logger()
logger.Info().Int("quality", int(quality)).Msg("restarting audio output subprocess with new quality settings")
// Set new OPUS configuration
supervisor.SetOpusConfig(config.Bitrate*1000, complexity, vbr, signalType, bandwidth, dtx)
// Stop current subprocess
supervisor.Stop()
// Start subprocess with new configuration
if err := supervisor.Start(); err != nil {
logger.Error().Err(err).Msg("failed to restart audio output subprocess")
}
} else {
// Fallback to dynamic update if supervisor is not available
vbrConstraint := GetConfig().CGOOpusVBRConstraint
if err := updateOpusEncoderParams(config.Bitrate*1000, complexity, vbr, vbrConstraint, signalType, bandwidth, dtx); err != nil {
logging.GetDefaultLogger().Error().Err(err).Msg("Failed to update OPUS encoder parameters")
}
}
}
}
@ -230,6 +246,59 @@ func SetMicrophoneQuality(quality AudioQuality) {
presets := GetMicrophoneQualityPresets()
if config, exists := presets[quality]; exists {
currentMicrophoneConfig = config
// Get OPUS parameters for the selected quality
var complexity, vbr, signalType, bandwidth, dtx int
switch quality {
case AudioQualityLow:
complexity = GetConfig().AudioQualityLowOpusComplexity
vbr = GetConfig().AudioQualityLowOpusVBR
signalType = GetConfig().AudioQualityLowOpusSignalType
bandwidth = GetConfig().AudioQualityLowOpusBandwidth
dtx = GetConfig().AudioQualityLowOpusDTX
case AudioQualityMedium:
complexity = GetConfig().AudioQualityMediumOpusComplexity
vbr = GetConfig().AudioQualityMediumOpusVBR
signalType = GetConfig().AudioQualityMediumOpusSignalType
bandwidth = GetConfig().AudioQualityMediumOpusBandwidth
dtx = GetConfig().AudioQualityMediumOpusDTX
case AudioQualityHigh:
complexity = GetConfig().AudioQualityHighOpusComplexity
vbr = GetConfig().AudioQualityHighOpusVBR
signalType = GetConfig().AudioQualityHighOpusSignalType
bandwidth = GetConfig().AudioQualityHighOpusBandwidth
dtx = GetConfig().AudioQualityHighOpusDTX
case AudioQualityUltra:
complexity = GetConfig().AudioQualityUltraOpusComplexity
vbr = GetConfig().AudioQualityUltraOpusVBR
signalType = GetConfig().AudioQualityUltraOpusSignalType
bandwidth = GetConfig().AudioQualityUltraOpusBandwidth
dtx = GetConfig().AudioQualityUltraOpusDTX
default:
// Use medium quality as fallback
complexity = GetConfig().AudioQualityMediumOpusComplexity
vbr = GetConfig().AudioQualityMediumOpusVBR
signalType = GetConfig().AudioQualityMediumOpusSignalType
bandwidth = GetConfig().AudioQualityMediumOpusBandwidth
dtx = GetConfig().AudioQualityMediumOpusDTX
}
// Restart audio input subprocess with new OPUS configuration
if supervisor := GetAudioInputSupervisor(); supervisor != nil {
logger := logging.GetDefaultLogger().With().Str("component", "audio").Logger()
logger.Info().Int("quality", int(quality)).Msg("restarting audio input subprocess with new quality settings")
// Set new OPUS configuration
supervisor.SetOpusConfig(config.Bitrate*1000, complexity, vbr, signalType, bandwidth, dtx)
// Stop current subprocess
supervisor.Stop()
// Start subprocess with new configuration
if err := supervisor.Start(); err != nil {
logger.Error().Err(err).Msg("failed to restart audio input subprocess")
}
}
}
}

View File

@ -1555,10 +1555,10 @@ func DefaultAudioConfig() *AudioConfigConstants {
MaxPacketSize: 4000,
// Audio Quality Bitrates
AudioQualityLowOutputBitrate: 32,
AudioQualityLowInputBitrate: 16,
AudioQualityMediumOutputBitrate: 64,
AudioQualityMediumInputBitrate: 32,
AudioQualityLowOutputBitrate: 48,
AudioQualityLowInputBitrate: 24,
AudioQualityMediumOutputBitrate: 80,
AudioQualityMediumInputBitrate: 40,
// AudioQualityHighOutputBitrate defines bitrate for high-quality output.
// Used in: Professional applications requiring excellent audio fidelity
@ -1573,16 +1573,16 @@ func DefaultAudioConfig() *AudioConfigConstants {
AudioQualityHighInputBitrate: 64,
// AudioQualityUltraOutputBitrate defines bitrate for ultra-quality output.
// Used in: Audiophile-grade reproduction and high-bandwidth connections
// Impact: Maximum quality but requires significant bandwidth.
// Default 192kbps suitable for high-bandwidth, quality-critical scenarios.
AudioQualityUltraOutputBitrate: 192,
// Used in: High-quality reproduction with optimized resource usage
// Impact: Excellent quality while maintaining system stability.
// Default 160kbps provides excellent audio quality with reduced CPU load.
AudioQualityUltraOutputBitrate: 160,
// AudioQualityUltraInputBitrate defines bitrate for ultra-quality input.
// Used in: Professional microphone input requiring maximum quality
// Impact: Provides audiophile-grade voice quality with high bandwidth.
// Default 96kbps ensures maximum voice reproduction quality.
AudioQualityUltraInputBitrate: 96,
// Used in: Professional microphone input with balanced resource usage
// Impact: Provides excellent voice quality while maintaining stability.
// Default 80kbps ensures excellent voice reproduction with reduced CPU load.
AudioQualityUltraInputBitrate: 80,
// Audio Quality Sample Rates - Sampling frequencies for different quality levels
// Used in: Audio capture, processing, and format negotiation
@ -1590,15 +1590,15 @@ func DefaultAudioConfig() *AudioConfigConstants {
// AudioQualityLowSampleRate defines sampling frequency for low-quality audio.
// Used in: Bandwidth-constrained scenarios and basic audio requirements
// Impact: Captures frequencies up to 11kHz while minimizing processing load.
// Default 22.05kHz sufficient for speech and basic audio.
AudioQualityLowSampleRate: 22050,
// Impact: Captures frequencies up to 24kHz while maintaining efficiency.
// Default 48kHz provides better quality while maintaining compatibility.
AudioQualityLowSampleRate: 48000,
// AudioQualityMediumSampleRate defines sampling frequency for medium-quality audio.
// Used in: Standard audio scenarios requiring CD-quality reproduction
// Impact: Captures full audible range up to 22kHz with balanced processing.
// Default 44.1kHz provides CD-quality standard with excellent balance.
AudioQualityMediumSampleRate: 44100,
// Used in: Standard audio scenarios requiring high-quality reproduction
// Impact: Captures full audible range up to 24kHz with excellent processing.
// Default 48kHz provides professional standard with optimal balance.
AudioQualityMediumSampleRate: 48000,
// AudioQualityMicLowSampleRate defines sampling frequency for low-quality microphone.
// Used in: Voice/microphone input in bandwidth-constrained scenarios
@ -1612,9 +1612,9 @@ func DefaultAudioConfig() *AudioConfigConstants {
// AudioQualityLowFrameSize defines frame duration for low-quality audio.
// Used in: Bandwidth-constrained scenarios prioritizing efficiency
// Impact: Reduces processing overhead with acceptable latency increase.
// Default 40ms provides efficiency for constrained scenarios.
AudioQualityLowFrameSize: 40 * time.Millisecond,
// Impact: Balances processing overhead with acceptable latency.
// Default 20ms provides better responsiveness for low-quality scenarios.
AudioQualityLowFrameSize: 20 * time.Millisecond,
// AudioQualityMediumFrameSize defines frame duration for medium-quality audio.
// Used in: Standard real-time audio applications
@ -1629,14 +1629,14 @@ func DefaultAudioConfig() *AudioConfigConstants {
AudioQualityHighFrameSize: 20 * time.Millisecond,
// AudioQualityUltraFrameSize defines frame duration for ultra-quality audio.
// Used in: Applications requiring immediate audio feedback
// Impact: Minimizes latency for ultra-responsive audio processing.
// Default 10ms ensures minimal latency for immediate feedback.
AudioQualityUltraFrameSize: 10 * time.Millisecond,
// Used in: Applications requiring excellent quality with balanced performance
// Impact: Balances latency and processing efficiency for stable operation.
// Default 20ms provides excellent quality while reducing CPU load.
AudioQualityUltraFrameSize: 20 * time.Millisecond,
// Audio Quality Channels - Channel configuration for different quality levels
// Used in: Audio processing pipeline for channel handling and bandwidth control
AudioQualityLowChannels: 1,
AudioQualityLowChannels: 2,
AudioQualityMediumChannels: 2,
AudioQualityHighChannels: 2,
AudioQualityUltraChannels: 2,
@ -1645,32 +1645,32 @@ func DefaultAudioConfig() *AudioConfigConstants {
// Used in: Dynamic OPUS encoder configuration based on quality presets
// Impact: Controls encoding complexity, VBR, signal type, bandwidth, and DTX
// Low Quality OPUS Parameters - Optimized for bandwidth conservation
AudioQualityLowOpusComplexity: 1, // Low complexity for minimal CPU usage
AudioQualityLowOpusVBR: 0, // CBR for predictable bandwidth
AudioQualityLowOpusSignalType: 3001, // OPUS_SIGNAL_VOICE
AudioQualityLowOpusBandwidth: 1101, // OPUS_BANDWIDTH_NARROWBAND
AudioQualityLowOpusDTX: 1, // Enable DTX for silence suppression
// Low Quality OPUS Parameters - Optimized for bandwidth conservation with better quality
AudioQualityLowOpusComplexity: 3, // Balanced complexity for better quality
AudioQualityLowOpusVBR: 1, // VBR for better quality at same bitrate
AudioQualityLowOpusSignalType: 3002, // OPUS_SIGNAL_MUSIC for better general audio
AudioQualityLowOpusBandwidth: 1103, // OPUS_BANDWIDTH_WIDEBAND for better frequency range
AudioQualityLowOpusDTX: 0, // Disable DTX for consistent quality
// Medium Quality OPUS Parameters - Balanced performance and quality
AudioQualityMediumOpusComplexity: 5, // Medium complexity for balanced performance
AudioQualityMediumOpusVBR: 1, // VBR for better quality
// Medium Quality OPUS Parameters - Enhanced performance and quality
AudioQualityMediumOpusComplexity: 6, // Higher complexity for better quality
AudioQualityMediumOpusVBR: 1, // VBR for optimal quality
AudioQualityMediumOpusSignalType: 3002, // OPUS_SIGNAL_MUSIC
AudioQualityMediumOpusBandwidth: 1103, // OPUS_BANDWIDTH_WIDEBAND
AudioQualityMediumOpusBandwidth: 1104, // OPUS_BANDWIDTH_SUPERWIDEBAND for better range
AudioQualityMediumOpusDTX: 0, // Disable DTX for consistent quality
// High Quality OPUS Parameters - High quality with good performance
AudioQualityHighOpusComplexity: 8, // High complexity for better quality
// High Quality OPUS Parameters - Premium quality with optimized performance
AudioQualityHighOpusComplexity: 9, // Near-maximum complexity for excellent quality
AudioQualityHighOpusVBR: 1, // VBR for optimal quality
AudioQualityHighOpusSignalType: 3002, // OPUS_SIGNAL_MUSIC
AudioQualityHighOpusBandwidth: 1104, // OPUS_BANDWIDTH_SUPERWIDEBAND
AudioQualityHighOpusBandwidth: 1105, // OPUS_BANDWIDTH_FULLBAND for full frequency range
AudioQualityHighOpusDTX: 0, // Disable DTX for consistent quality
// Ultra Quality OPUS Parameters - Maximum quality settings
AudioQualityUltraOpusComplexity: 10, // Maximum complexity for best quality
// Ultra Quality OPUS Parameters - Optimized for high quality with reasonable resource usage
AudioQualityUltraOpusComplexity: 8, // Reduced complexity to prevent CPU overload
AudioQualityUltraOpusVBR: 1, // VBR for optimal quality
AudioQualityUltraOpusSignalType: 3002, // OPUS_SIGNAL_MUSIC
AudioQualityUltraOpusBandwidth: 1105, // OPUS_BANDWIDTH_FULLBAND
AudioQualityUltraOpusBandwidth: 1104, // OPUS_BANDWIDTH_SUPERWIDEBAND for better stability
AudioQualityUltraOpusDTX: 0, // Disable DTX for maximum quality
// CGO Audio Constants

View File

@ -4,18 +4,58 @@ import (
"context"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/jetkvm/kvm/internal/logging"
)
// getEnvInt reads an integer from environment variable with a default value
func getEnvIntInput(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
if intValue, err := strconv.Atoi(value); err == nil {
return intValue
}
}
return defaultValue
}
// parseOpusConfigInput reads OPUS configuration from environment variables
// with fallback to default config values for input server
func parseOpusConfigInput() (bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
// Read configuration from environment variables with config defaults
bitrate = getEnvIntInput("JETKVM_OPUS_BITRATE", GetConfig().CGOOpusBitrate)
complexity = getEnvIntInput("JETKVM_OPUS_COMPLEXITY", GetConfig().CGOOpusComplexity)
vbr = getEnvIntInput("JETKVM_OPUS_VBR", GetConfig().CGOOpusVBR)
signalType = getEnvIntInput("JETKVM_OPUS_SIGNAL_TYPE", GetConfig().CGOOpusSignalType)
bandwidth = getEnvIntInput("JETKVM_OPUS_BANDWIDTH", GetConfig().CGOOpusBandwidth)
dtx = getEnvIntInput("JETKVM_OPUS_DTX", GetConfig().CGOOpusDTX)
return bitrate, complexity, vbr, signalType, bandwidth, dtx
}
// applyOpusConfigInput applies OPUS configuration to the global config for input server
func applyOpusConfigInput(bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
config := GetConfig()
config.CGOOpusBitrate = bitrate
config.CGOOpusComplexity = complexity
config.CGOOpusVBR = vbr
config.CGOOpusSignalType = signalType
config.CGOOpusBandwidth = bandwidth
config.CGOOpusDTX = dtx
}
// RunAudioInputServer runs the audio input server subprocess
// This should be called from main() when the subprocess is detected
func RunAudioInputServer() error {
logger := logging.GetDefaultLogger().With().Str("component", "audio-input-server").Logger()
logger.Debug().Msg("audio input server subprocess starting")
// Parse OPUS configuration from environment variables
bitrate, complexity, vbr, signalType, bandwidth, dtx := parseOpusConfigInput()
applyOpusConfigInput(bitrate, complexity, vbr, signalType, bandwidth, dtx)
// Initialize validation cache for optimal performance
InitValidationCache()

View File

@ -4,6 +4,7 @@ import (
"fmt"
"os"
"os/exec"
"strconv"
"syscall"
"time"
)
@ -12,6 +13,9 @@ import (
type AudioInputSupervisor struct {
*BaseSupervisor
client *AudioInputClient
// Environment variables for OPUS configuration
opusEnv []string
}
// NewAudioInputSupervisor creates a new audio input supervisor
@ -22,6 +26,23 @@ func NewAudioInputSupervisor() *AudioInputSupervisor {
}
}
// SetOpusConfig sets OPUS configuration parameters as environment variables
// for the audio input subprocess
func (ais *AudioInputSupervisor) SetOpusConfig(bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
ais.mutex.Lock()
defer ais.mutex.Unlock()
// Store OPUS parameters as environment variables
ais.opusEnv = []string{
"JETKVM_OPUS_BITRATE=" + strconv.Itoa(bitrate),
"JETKVM_OPUS_COMPLEXITY=" + strconv.Itoa(complexity),
"JETKVM_OPUS_VBR=" + strconv.Itoa(vbr),
"JETKVM_OPUS_SIGNAL_TYPE=" + strconv.Itoa(signalType),
"JETKVM_OPUS_BANDWIDTH=" + strconv.Itoa(bandwidth),
"JETKVM_OPUS_DTX=" + strconv.Itoa(dtx),
}
}
// Start starts the audio input server subprocess
func (ais *AudioInputSupervisor) Start() error {
ais.mutex.Lock()
@ -40,11 +61,16 @@ func (ais *AudioInputSupervisor) Start() error {
return fmt.Errorf("failed to get executable path: %w", err)
}
// Build command arguments (only subprocess flag)
args := []string{"--audio-input-server"}
// Create command for audio input server subprocess
cmd := exec.CommandContext(ais.ctx, execPath, "--audio-input-server")
cmd.Env = append(os.Environ(),
"JETKVM_AUDIO_INPUT_IPC=true", // Enable IPC mode
)
cmd := exec.CommandContext(ais.ctx, execPath, args...)
// Set environment variables for IPC and OPUS configuration
env := append(os.Environ(), "JETKVM_AUDIO_INPUT_IPC=true") // Enable IPC mode
env = append(env, ais.opusEnv...) // Add OPUS configuration
cmd.Env = env
// Set process group to allow clean termination
cmd.SysProcAttr = &syscall.SysProcAttr{
@ -62,7 +88,7 @@ func (ais *AudioInputSupervisor) Start() error {
return fmt.Errorf("failed to start audio input server process: %w", err)
}
ais.logger.Info().Int("pid", cmd.Process.Pid).Msg("Audio input server subprocess started")
ais.logger.Info().Int("pid", cmd.Process.Pid).Strs("args", args).Strs("opus_env", ais.opusEnv).Msg("Audio input server subprocess started")
// Add process to monitoring
ais.processMonitor.AddProcess(cmd.Process.Pid, "audio-input-server")

View File

@ -4,18 +4,69 @@ import (
"context"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/jetkvm/kvm/internal/logging"
)
// getEnvInt reads an integer from environment variable with a default value
func getEnvInt(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
if intValue, err := strconv.Atoi(value); err == nil {
return intValue
}
}
return defaultValue
}
// parseOpusConfig reads OPUS configuration from environment variables
// with fallback to default config values
func parseOpusConfig() (bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
// Read configuration from environment variables with config defaults
bitrate = getEnvInt("JETKVM_OPUS_BITRATE", GetConfig().CGOOpusBitrate)
complexity = getEnvInt("JETKVM_OPUS_COMPLEXITY", GetConfig().CGOOpusComplexity)
vbr = getEnvInt("JETKVM_OPUS_VBR", GetConfig().CGOOpusVBR)
signalType = getEnvInt("JETKVM_OPUS_SIGNAL_TYPE", GetConfig().CGOOpusSignalType)
bandwidth = getEnvInt("JETKVM_OPUS_BANDWIDTH", GetConfig().CGOOpusBandwidth)
dtx = getEnvInt("JETKVM_OPUS_DTX", GetConfig().CGOOpusDTX)
return bitrate, complexity, vbr, signalType, bandwidth, dtx
}
// applyOpusConfig applies OPUS configuration to the global config
func applyOpusConfig(bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
logger := logging.GetDefaultLogger().With().Str("component", "audio-output-server").Logger()
config := GetConfig()
config.CGOOpusBitrate = bitrate
config.CGOOpusComplexity = complexity
config.CGOOpusVBR = vbr
config.CGOOpusSignalType = signalType
config.CGOOpusBandwidth = bandwidth
config.CGOOpusDTX = dtx
logger.Info().
Int("bitrate", bitrate).
Int("complexity", complexity).
Int("vbr", vbr).
Int("signal_type", signalType).
Int("bandwidth", bandwidth).
Int("dtx", dtx).
Msg("applied OPUS configuration")
}
// RunAudioOutputServer runs the audio output server subprocess
// This should be called from main() when the subprocess is detected
func RunAudioOutputServer() error {
logger := logging.GetDefaultLogger().With().Str("component", "audio-output-server").Logger()
logger.Debug().Msg("audio output server subprocess starting")
// Parse OPUS configuration from environment variables
bitrate, complexity, vbr, signalType, bandwidth, dtx := parseOpusConfig()
applyOpusConfig(bitrate, complexity, vbr, signalType, bandwidth, dtx)
// Initialize validation cache for optimal performance
InitValidationCache()

View File

@ -7,6 +7,7 @@ import (
"fmt"
"os"
"os/exec"
"strconv"
"sync/atomic"
"syscall"
"time"
@ -42,6 +43,9 @@ type AudioOutputSupervisor struct {
stopChanClosed bool // Track if stopChan is closed
processDoneClosed bool // Track if processDone is closed
// Environment variables for OPUS configuration
opusEnv []string
// Callbacks
onProcessStart func(pid int)
onProcessExit func(pid int, exitCode int, crashed bool)
@ -72,6 +76,23 @@ func (s *AudioOutputSupervisor) SetCallbacks(
s.onRestart = onRestart
}
// SetOpusConfig sets OPUS configuration parameters as environment variables
// for the audio output subprocess
func (s *AudioOutputSupervisor) SetOpusConfig(bitrate, complexity, vbr, signalType, bandwidth, dtx int) {
s.mutex.Lock()
defer s.mutex.Unlock()
// Store OPUS parameters as environment variables
s.opusEnv = []string{
"JETKVM_OPUS_BITRATE=" + strconv.Itoa(bitrate),
"JETKVM_OPUS_COMPLEXITY=" + strconv.Itoa(complexity),
"JETKVM_OPUS_VBR=" + strconv.Itoa(vbr),
"JETKVM_OPUS_SIGNAL_TYPE=" + strconv.Itoa(signalType),
"JETKVM_OPUS_BANDWIDTH=" + strconv.Itoa(bandwidth),
"JETKVM_OPUS_DTX=" + strconv.Itoa(dtx),
}
}
// Start begins supervising the audio output server process
func (s *AudioOutputSupervisor) Start() error {
if !atomic.CompareAndSwapInt32(&s.running, 0, 1) {
@ -223,18 +244,24 @@ func (s *AudioOutputSupervisor) startProcess() error {
s.mutex.Lock()
defer s.mutex.Unlock()
// Build command arguments (only subprocess flag)
args := []string{"--audio-output-server"}
// Create new command
s.cmd = exec.CommandContext(s.ctx, execPath, "--audio-output-server")
s.cmd = exec.CommandContext(s.ctx, execPath, args...)
s.cmd.Stdout = os.Stdout
s.cmd.Stderr = os.Stderr
// Set environment variables for OPUS configuration
s.cmd.Env = append(os.Environ(), s.opusEnv...)
// Start the process
if err := s.cmd.Start(); err != nil {
return fmt.Errorf("failed to start audio output server process: %w", err)
}
s.processPID = s.cmd.Process.Pid
s.logger.Info().Int("pid", s.processPID).Msg("audio server process started")
s.logger.Info().Int("pid", s.processPID).Strs("args", args).Strs("opus_env", s.opusEnv).Msg("audio server process started")
// Add process to monitoring
s.processMonitor.AddProcess(s.processPID, "audio-output-server")

21
main.go
View File

@ -44,6 +44,27 @@ func startAudioSubprocess() error {
// Set the global supervisor for access from audio package
audio.SetAudioOutputSupervisor(audioSupervisor)
// Create and register audio input supervisor
audioInputSupervisor := audio.NewAudioInputSupervisor()
audio.SetAudioInputSupervisor(audioInputSupervisor)
// Set default OPUS configuration for audio input supervisor (low quality for single-core RV1106)
config := audio.GetConfig()
audioInputSupervisor.SetOpusConfig(
config.AudioQualityLowInputBitrate*1000, // Convert kbps to bps
config.AudioQualityLowOpusComplexity,
config.AudioQualityLowOpusVBR,
config.AudioQualityLowOpusSignalType,
config.AudioQualityLowOpusBandwidth,
config.AudioQualityLowOpusDTX,
)
// Start audio input supervisor
if err := audioInputSupervisor.Start(); err != nil {
logger.Error().Err(err).Msg("failed to start audio input supervisor")
// Continue execution as audio input is not critical for basic functionality
}
// Set up callbacks for process lifecycle events
audioSupervisor.SetCallbacks(
// onProcessStart