kvm/internal/audio/metrics_registry.go

152 lines
4.5 KiB
Go

//go:build cgo
package audio
import (
"sync"
"sync/atomic"
"time"
)
// MetricsRegistry provides a centralized source of truth for all audio metrics
// This eliminates duplication between session-specific and global managers
type MetricsRegistry struct {
mu sync.RWMutex
audioMetrics AudioMetrics
audioInputMetrics AudioInputMetrics
audioConfig AudioConfig
microphoneConfig AudioConfig
lastUpdate int64 // Unix timestamp
}
var (
globalMetricsRegistry *MetricsRegistry
registryOnce sync.Once
)
// GetMetricsRegistry returns the global metrics registry instance
func GetMetricsRegistry() *MetricsRegistry {
registryOnce.Do(func() {
globalMetricsRegistry = &MetricsRegistry{
lastUpdate: time.Now().Unix(),
}
})
return globalMetricsRegistry
}
// UpdateAudioMetrics updates the centralized audio output metrics
func (mr *MetricsRegistry) UpdateAudioMetrics(metrics AudioMetrics) {
mr.mu.Lock()
mr.audioMetrics = metrics
mr.lastUpdate = time.Now().Unix()
mr.mu.Unlock()
// Update Prometheus metrics directly to avoid circular dependency
UpdateAudioMetrics(convertAudioMetricsToUnified(metrics))
}
// UpdateAudioInputMetrics updates the centralized audio input metrics
func (mr *MetricsRegistry) UpdateAudioInputMetrics(metrics AudioInputMetrics) {
mr.mu.Lock()
mr.audioInputMetrics = metrics
mr.lastUpdate = time.Now().Unix()
mr.mu.Unlock()
// Update Prometheus metrics directly to avoid circular dependency
UpdateMicrophoneMetrics(convertAudioInputMetricsToUnified(metrics))
}
// UpdateAudioConfig updates the centralized audio configuration
func (mr *MetricsRegistry) UpdateAudioConfig(config AudioConfig) {
mr.mu.Lock()
mr.audioConfig = config
mr.lastUpdate = time.Now().Unix()
mr.mu.Unlock()
// Update Prometheus metrics directly
UpdateAudioConfigMetrics(config)
}
// UpdateMicrophoneConfig updates the centralized microphone configuration
func (mr *MetricsRegistry) UpdateMicrophoneConfig(config AudioConfig) {
mr.mu.Lock()
mr.microphoneConfig = config
mr.lastUpdate = time.Now().Unix()
mr.mu.Unlock()
// Update Prometheus metrics directly
UpdateMicrophoneConfigMetrics(config)
}
// GetAudioMetrics returns the current audio output metrics
func (mr *MetricsRegistry) GetAudioMetrics() AudioMetrics {
mr.mu.RLock()
defer mr.mu.RUnlock()
return mr.audioMetrics
}
// GetAudioInputMetrics returns the current audio input metrics
func (mr *MetricsRegistry) GetAudioInputMetrics() AudioInputMetrics {
mr.mu.RLock()
defer mr.mu.RUnlock()
return mr.audioInputMetrics
}
// GetAudioConfig returns the current audio configuration
func (mr *MetricsRegistry) GetAudioConfig() AudioConfig {
mr.mu.RLock()
defer mr.mu.RUnlock()
return mr.audioConfig
}
// GetMicrophoneConfig returns the current microphone configuration
func (mr *MetricsRegistry) GetMicrophoneConfig() AudioConfig {
mr.mu.RLock()
defer mr.mu.RUnlock()
return mr.microphoneConfig
}
// GetLastUpdate returns the timestamp of the last metrics update
func (mr *MetricsRegistry) GetLastUpdate() time.Time {
timestamp := atomic.LoadInt64(&mr.lastUpdate)
return time.Unix(timestamp, 0)
}
// StartMetricsCollector starts a background goroutine to collect metrics
func (mr *MetricsRegistry) StartMetricsCollector() {
go func() {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for range ticker.C {
// Collect from session-specific manager if available
if sessionProvider := GetSessionProvider(); sessionProvider != nil && sessionProvider.IsSessionActive() {
if inputManager := sessionProvider.GetAudioInputManager(); inputManager != nil {
metrics := inputManager.GetMetrics()
mr.UpdateAudioInputMetrics(metrics)
}
} else {
// Fallback to global manager if no session is active
globalManager := getAudioInputManager()
metrics := globalManager.GetMetrics()
mr.UpdateAudioInputMetrics(metrics)
}
// Collect audio output metrics directly from global metrics variable to avoid circular dependency
audioMetrics := AudioMetrics{
FramesReceived: atomic.LoadInt64(&metrics.FramesReceived),
FramesDropped: atomic.LoadInt64(&metrics.FramesDropped),
BytesProcessed: atomic.LoadInt64(&metrics.BytesProcessed),
ConnectionDrops: atomic.LoadInt64(&metrics.ConnectionDrops),
LastFrameTime: metrics.LastFrameTime,
AverageLatency: metrics.AverageLatency,
}
mr.UpdateAudioMetrics(audioMetrics)
// Collect configuration directly from global variables to avoid circular dependency
mr.UpdateAudioConfig(currentConfig)
mr.UpdateMicrophoneConfig(currentMicrophoneConfig)
}
}()
}