diff --git a/internal/audio/cgo_audio.go b/internal/audio/cgo_audio.go index 98267400..e6a48529 100644 --- a/internal/audio/cgo_audio.go +++ b/internal/audio/cgo_audio.go @@ -709,6 +709,9 @@ func cgoAudioReadEncode(buf []byte) (int, error) { return 0, newBufferTooSmallError(len(buf), minRequired) } + // Skip initialization check for now to avoid CGO compilation issues + // TODO: Add proper initialization state checking + n := C.jetkvm_audio_read_encode(unsafe.Pointer(&buf[0])) if n < 0 { return 0, newAudioReadEncodeError(int(n)) diff --git a/internal/audio/input_supervisor.go b/internal/audio/input_supervisor.go index fa52d4ba..be9042cd 100644 --- a/internal/audio/input_supervisor.go +++ b/internal/audio/input_supervisor.go @@ -141,7 +141,8 @@ func (ais *AudioInputSupervisor) Stop() { // Try graceful termination first if ais.cmd != nil && ais.cmd.Process != nil { - ais.logger.Info().Int("pid", ais.cmd.Process.Pid).Msg("Stopping audio input server subprocess") + pid := ais.cmd.Process.Pid + ais.logger.Info().Int("pid", pid).Msg("Stopping audio input server subprocess") // Send SIGTERM err := ais.cmd.Process.Signal(syscall.SIGTERM) @@ -161,9 +162,17 @@ func (ais *AudioInputSupervisor) Stop() { case <-time.After(GetConfig().InputSupervisorTimeout): // Force kill if graceful shutdown failed ais.logger.Warn().Msg("Audio input server subprocess did not stop gracefully, force killing") - err := ais.cmd.Process.Kill() - if err != nil { - ais.logger.Error().Err(err).Msg("Failed to kill audio input server subprocess") + // Check if process is still alive before attempting to kill + if ais.cmd != nil && ais.cmd.Process != nil { + // Check process state to avoid "process already finished" error + if ais.cmd.ProcessState == nil { + err := ais.cmd.Process.Kill() + if err != nil { + ais.logger.Error().Err(err).Msg("Failed to kill audio input server subprocess") + } + } else { + ais.logger.Info().Msg("Audio input server subprocess already finished") + } } } } diff --git a/internal/audio/output_streaming.go b/internal/audio/output_streaming.go index 7c5b1d1a..ed876538 100644 --- a/internal/audio/output_streaming.go +++ b/internal/audio/output_streaming.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "runtime" + "strings" "sync" "sync/atomic" "time" @@ -311,10 +312,18 @@ func StartAudioOutputStreaming(send func([]byte)) error { return ErrAudioAlreadyRunning } - // Initialize CGO audio capture - if err := CGOAudioInit(); err != nil { + // Initialize CGO audio capture with retry logic + var initErr error + for attempt := 0; attempt < 3; attempt++ { + if initErr = CGOAudioInit(); initErr == nil { + break + } + getOutputStreamingLogger().Warn().Err(initErr).Int("attempt", attempt+1).Msg("Audio initialization failed, retrying") + time.Sleep(time.Duration(attempt+1) * 100 * time.Millisecond) + } + if initErr != nil { atomic.StoreInt32(&outputStreamingRunning, 0) - return err + return fmt.Errorf("failed to initialize audio after 3 attempts: %w", initErr) } ctx, cancel := context.WithCancel(context.Background()) @@ -341,7 +350,7 @@ func StartAudioOutputStreaming(send func([]byte)) error { case <-ctx.Done(): return default: - // Capture audio frame with enhanced error handling + // Capture audio frame with enhanced error handling and initialization checking n, err := CGOAudioReadEncode(buffer) if err != nil { consecutiveErrors++ @@ -350,6 +359,13 @@ func StartAudioOutputStreaming(send func([]byte)) error { Int("consecutive_errors", consecutiveErrors). Msg("Failed to read/encode audio") + // Check if this is an initialization error (C error code -1) + if strings.Contains(err.Error(), "C error code -1") { + getOutputStreamingLogger().Error().Msg("Audio system not initialized properly, forcing reinitialization") + // Force immediate reinitialization for init errors + consecutiveErrors = maxConsecutiveErrors + } + // Implement progressive backoff for consecutive errors if consecutiveErrors >= maxConsecutiveErrors { getOutputStreamingLogger().Error().