diff --git a/internal/audio/core_metrics.go b/internal/audio/core_metrics.go index 923bd1da..3f1932cd 100644 --- a/internal/audio/core_metrics.go +++ b/internal/audio/core_metrics.go @@ -260,14 +260,14 @@ var ( lastMetricsUpdate int64 // Counter value tracking (since prometheus counters don't have Get() method) - audioFramesReceivedValue int64 - audioFramesDroppedValue int64 - audioBytesProcessedValue int64 - audioConnectionDropsValue int64 - micFramesSentValue int64 - micFramesDroppedValue int64 - micBytesProcessedValue int64 - micConnectionDropsValue int64 + audioFramesReceivedValue uint64 + audioFramesDroppedValue uint64 + audioBytesProcessedValue uint64 + audioConnectionDropsValue uint64 + micFramesSentValue uint64 + micFramesDroppedValue uint64 + micBytesProcessedValue uint64 + micConnectionDropsValue uint64 // Atomic counters for device health metrics - functionality removed, no longer used @@ -277,11 +277,11 @@ var ( // UnifiedAudioMetrics provides a common structure for both input and output audio streams type UnifiedAudioMetrics struct { - FramesReceived int64 `json:"frames_received"` - FramesDropped int64 `json:"frames_dropped"` - FramesSent int64 `json:"frames_sent,omitempty"` - BytesProcessed int64 `json:"bytes_processed"` - ConnectionDrops int64 `json:"connection_drops"` + FramesReceived uint64 `json:"frames_received"` + FramesDropped uint64 `json:"frames_dropped"` + FramesSent uint64 `json:"frames_sent,omitempty"` + BytesProcessed uint64 `json:"bytes_processed"` + ConnectionDrops uint64 `json:"connection_drops"` LastFrameTime time.Time `json:"last_frame_time"` AverageLatency time.Duration `json:"average_latency"` } @@ -303,10 +303,10 @@ func convertAudioMetricsToUnified(metrics AudioMetrics) UnifiedAudioMetrics { func convertAudioInputMetricsToUnified(metrics AudioInputMetrics) UnifiedAudioMetrics { return UnifiedAudioMetrics{ FramesReceived: 0, // AudioInputMetrics doesn't have FramesReceived - FramesDropped: metrics.FramesDropped, - FramesSent: metrics.FramesSent, - BytesProcessed: metrics.BytesProcessed, - ConnectionDrops: metrics.ConnectionDrops, + FramesDropped: uint64(metrics.FramesDropped), + FramesSent: uint64(metrics.FramesSent), + BytesProcessed: uint64(metrics.BytesProcessed), + ConnectionDrops: uint64(metrics.ConnectionDrops), LastFrameTime: metrics.LastFrameTime, AverageLatency: metrics.AverageLatency, } @@ -314,22 +314,22 @@ func convertAudioInputMetricsToUnified(metrics AudioInputMetrics) UnifiedAudioMe // UpdateAudioMetrics updates Prometheus metrics with current audio data func UpdateAudioMetrics(metrics UnifiedAudioMetrics) { - oldReceived := atomic.SwapInt64(&audioFramesReceivedValue, metrics.FramesReceived) + oldReceived := atomic.SwapUint64(&audioFramesReceivedValue, metrics.FramesReceived) if metrics.FramesReceived > oldReceived { audioFramesReceivedTotal.Add(float64(metrics.FramesReceived - oldReceived)) } - oldDropped := atomic.SwapInt64(&audioFramesDroppedValue, metrics.FramesDropped) + oldDropped := atomic.SwapUint64(&audioFramesDroppedValue, metrics.FramesDropped) if metrics.FramesDropped > oldDropped { audioFramesDroppedTotal.Add(float64(metrics.FramesDropped - oldDropped)) } - oldBytes := atomic.SwapInt64(&audioBytesProcessedValue, metrics.BytesProcessed) + oldBytes := atomic.SwapUint64(&audioBytesProcessedValue, metrics.BytesProcessed) if metrics.BytesProcessed > oldBytes { audioBytesProcessedTotal.Add(float64(metrics.BytesProcessed - oldBytes)) } - oldDrops := atomic.SwapInt64(&audioConnectionDropsValue, metrics.ConnectionDrops) + oldDrops := atomic.SwapUint64(&audioConnectionDropsValue, metrics.ConnectionDrops) if metrics.ConnectionDrops > oldDrops { audioConnectionDropsTotal.Add(float64(metrics.ConnectionDrops - oldDrops)) } @@ -345,22 +345,22 @@ func UpdateAudioMetrics(metrics UnifiedAudioMetrics) { // UpdateMicrophoneMetrics updates Prometheus metrics with current microphone data func UpdateMicrophoneMetrics(metrics UnifiedAudioMetrics) { - oldSent := atomic.SwapInt64(&micFramesSentValue, metrics.FramesSent) + oldSent := atomic.SwapUint64(&micFramesSentValue, metrics.FramesSent) if metrics.FramesSent > oldSent { microphoneFramesSentTotal.Add(float64(metrics.FramesSent - oldSent)) } - oldDropped := atomic.SwapInt64(&micFramesDroppedValue, metrics.FramesDropped) + oldDropped := atomic.SwapUint64(&micFramesDroppedValue, metrics.FramesDropped) if metrics.FramesDropped > oldDropped { microphoneFramesDroppedTotal.Add(float64(metrics.FramesDropped - oldDropped)) } - oldBytes := atomic.SwapInt64(&micBytesProcessedValue, metrics.BytesProcessed) + oldBytes := atomic.SwapUint64(&micBytesProcessedValue, metrics.BytesProcessed) if metrics.BytesProcessed > oldBytes { microphoneBytesProcessedTotal.Add(float64(metrics.BytesProcessed - oldBytes)) } - oldDrops := atomic.SwapInt64(&micConnectionDropsValue, metrics.ConnectionDrops) + oldDrops := atomic.SwapUint64(&micConnectionDropsValue, metrics.ConnectionDrops) if metrics.ConnectionDrops > oldDrops { microphoneConnectionDropsTotal.Add(float64(metrics.ConnectionDrops - oldDrops)) } diff --git a/internal/audio/ipc_input.go b/internal/audio/ipc_input.go index bbee28df..70e8c8b4 100644 --- a/internal/audio/ipc_input.go +++ b/internal/audio/ipc_input.go @@ -532,19 +532,36 @@ func (ais *AudioInputServer) processOpusConfig(data []byte) error { logger.Info().Interface("config", config).Msg("applying dynamic Opus encoder configuration") - // Apply the Opus encoder configuration dynamically - err := CGOUpdateOpusEncoderParams( - config.Bitrate, - config.Complexity, - config.VBR, - 0, // VBR constraint - using default - config.SignalType, - config.Bandwidth, - config.DTX, - ) + // Ensure capture is initialized before updating encoder parameters + // The C function requires both encoder and capture_initialized to be true + if err := CGOAudioInit(); err != nil { + logger.Debug().Err(err).Msg("Audio capture already initialized or initialization failed") + // Continue anyway - capture may already be initialized + } + + // Apply the Opus encoder configuration dynamically with retry logic + var err error + for attempt := 0; attempt < 3; attempt++ { + err = CGOUpdateOpusEncoderParams( + config.Bitrate, + config.Complexity, + config.VBR, + 0, // VBR constraint - using default + config.SignalType, + config.Bandwidth, + config.DTX, + ) + if err == nil { + break + } + logger.Warn().Err(err).Int("attempt", attempt+1).Msg("Failed to update Opus encoder parameters, retrying") + if attempt < 2 { + time.Sleep(time.Duration(attempt+1) * 50 * time.Millisecond) + } + } if err != nil { - logger.Error().Err(err).Msg("failed to apply Opus encoder configuration") + logger.Error().Err(err).Msg("failed to apply Opus encoder configuration after retries") return fmt.Errorf("failed to apply Opus configuration: %w", err) } diff --git a/internal/audio/quality_presets.go b/internal/audio/quality_presets.go index 60d8a994..65bfce33 100644 --- a/internal/audio/quality_presets.go +++ b/internal/audio/quality_presets.go @@ -63,10 +63,10 @@ type AudioConfig struct { // AudioMetrics tracks audio performance metrics type AudioMetrics struct { - FramesReceived int64 - FramesDropped int64 - BytesProcessed int64 - ConnectionDrops int64 + FramesReceived uint64 + FramesDropped uint64 + BytesProcessed uint64 + ConnectionDrops uint64 LastFrameTime time.Time AverageLatency time.Duration } @@ -398,16 +398,16 @@ func flushBatchedMetrics() { // Update main metrics if we have any batched data if framesReceived > 0 { - atomic.AddInt64(&metrics.FramesReceived, framesReceived) + atomic.AddUint64(&metrics.FramesReceived, uint64(framesReceived)) } if bytesProcessed > 0 { - atomic.AddInt64(&metrics.BytesProcessed, bytesProcessed) + atomic.AddUint64(&metrics.BytesProcessed, uint64(bytesProcessed)) } if framesDropped > 0 { - atomic.AddInt64(&metrics.FramesDropped, framesDropped) + atomic.AddUint64(&metrics.FramesDropped, uint64(framesDropped)) } if connectionDrops > 0 { - atomic.AddInt64(&metrics.ConnectionDrops, connectionDrops) + atomic.AddUint64(&metrics.ConnectionDrops, uint64(connectionDrops)) } // Update last flush time