mirror of https://github.com/jetkvm/kvm.git
refactor(audio): centralize config constants and update usage
Move hardcoded values to config constants and update all references to use centralized configuration. Includes: - Audio processing timeouts and intervals - CGO sleep durations - Batch processing parameters - Event formatting and timeouts - Process monitor calculations
This commit is contained in:
parent
9e343b3cc7
commit
c5216920b3
|
@ -274,7 +274,8 @@ func GetBatchAudioProcessor() *BatchAudioProcessor {
|
|||
|
||||
// Initialize on first use
|
||||
if atomic.CompareAndSwapInt32(&batchProcessorInitialized, 0, 1) {
|
||||
processor := NewBatchAudioProcessor(4, 5*time.Millisecond) // 4 frames per batch, 5ms timeout
|
||||
config := GetConfig()
|
||||
processor := NewBatchAudioProcessor(config.BatchProcessorFramesPerBatch, config.BatchProcessorTimeout)
|
||||
atomic.StorePointer(&globalBatchProcessor, unsafe.Pointer(processor))
|
||||
return processor
|
||||
}
|
||||
|
@ -286,7 +287,8 @@ func GetBatchAudioProcessor() *BatchAudioProcessor {
|
|||
}
|
||||
|
||||
// Fallback: create a new processor (should rarely happen)
|
||||
return NewBatchAudioProcessor(4, 5*time.Millisecond)
|
||||
config := GetConfig()
|
||||
return NewBatchAudioProcessor(config.BatchProcessorFramesPerBatch, config.BatchProcessorTimeout)
|
||||
}
|
||||
|
||||
// EnableBatchAudioProcessing enables the global batch processor
|
||||
|
|
|
@ -34,7 +34,7 @@ static int sample_rate = 48000; // Will be set from GetConfig().CGOSampl
|
|||
static int channels = 2; // Will be set from GetConfig().CGOChannels
|
||||
static int frame_size = 960; // Will be set from GetConfig().CGOFrameSize
|
||||
static int max_packet_size = 1500; // Will be set from GetConfig().CGOMaxPacketSize
|
||||
static int sleep_microseconds = 50000; // Will be set from GetConfig().CGOSleepMicroseconds
|
||||
static int sleep_microseconds = 1000; // Will be set from GetConfig().CGOUsleepMicroseconds
|
||||
|
||||
// Function to update constants from Go configuration
|
||||
void update_audio_constants(int bitrate, int complexity, int vbr, int vbr_constraint,
|
||||
|
@ -244,7 +244,7 @@ int jetkvm_audio_read_encode(void *opus_buf) {
|
|||
} else if (pcm_rc == -ESTRPIPE) {
|
||||
// Device suspended, try to resume
|
||||
while ((err = snd_pcm_resume(pcm_handle)) == -EAGAIN) {
|
||||
usleep(1000); // 1ms
|
||||
usleep(sleep_microseconds); // Use centralized constant
|
||||
}
|
||||
if (err < 0) {
|
||||
err = snd_pcm_prepare(pcm_handle);
|
||||
|
@ -358,7 +358,7 @@ int jetkvm_audio_decode_write(void *opus_buf, int opus_size) {
|
|||
} else if (pcm_rc == -ESTRPIPE) {
|
||||
// Device suspended, try to resume
|
||||
while ((err = snd_pcm_resume(pcm_playback_handle)) == -EAGAIN) {
|
||||
usleep(1000); // 1ms
|
||||
usleep(sleep_microseconds); // Use centralized constant
|
||||
}
|
||||
if (err < 0) {
|
||||
err = snd_pcm_prepare(pcm_playback_handle);
|
||||
|
@ -376,7 +376,7 @@ int jetkvm_audio_decode_write(void *opus_buf, int opus_size) {
|
|||
void jetkvm_audio_playback_close() {
|
||||
// Wait for any ongoing operations to complete
|
||||
while (playback_initializing) {
|
||||
usleep(1000); // 1ms
|
||||
usleep(sleep_microseconds); // Use centralized constant
|
||||
}
|
||||
|
||||
// Atomic check and set to prevent double cleanup
|
||||
|
@ -399,7 +399,7 @@ void jetkvm_audio_playback_close() {
|
|||
void jetkvm_audio_close() {
|
||||
// Wait for any ongoing operations to complete
|
||||
while (capture_initializing) {
|
||||
usleep(1000); // 1ms
|
||||
usleep(sleep_microseconds); // Use centralized constant
|
||||
}
|
||||
|
||||
capture_initialized = 0;
|
||||
|
@ -448,7 +448,7 @@ func cgoAudioInit() error {
|
|||
C.int(config.CGOChannels),
|
||||
C.int(config.CGOFrameSize),
|
||||
C.int(config.CGOMaxPacketSize),
|
||||
C.int(config.CGOSleepMicroseconds),
|
||||
C.int(config.CGOUsleepMicroseconds),
|
||||
)
|
||||
|
||||
result := C.jetkvm_audio_init()
|
||||
|
|
|
@ -830,6 +830,56 @@ type AudioConfigConstants struct {
|
|||
PoolGrowthMultiplier int // 2x growth multiplier for pool sizes
|
||||
LatencyScalingFactor float64 // 2.0 for latency ratio scaling
|
||||
OptimizerAggressiveness float64 // 0.7 for optimizer aggressiveness
|
||||
|
||||
// CGO Audio Processing Constants
|
||||
CGOUsleepMicroseconds int // 1000 microseconds (1ms) for CGO usleep calls
|
||||
CGOPCMBufferSize int // 1920 samples for PCM buffer (max 2ch*960)
|
||||
CGONanosecondsPerSecond float64 // 1000000000.0 for nanosecond conversions
|
||||
|
||||
// Frontend Constants
|
||||
FrontendOperationDebounceMS int // 1000ms debounce for frontend operations
|
||||
FrontendSyncDebounceMS int // 1000ms debounce for sync operations
|
||||
FrontendSampleRate int // 48000Hz sample rate for frontend audio
|
||||
FrontendRetryDelayMS int // 500ms retry delay
|
||||
FrontendShortDelayMS int // 200ms short delay
|
||||
FrontendLongDelayMS int // 300ms long delay
|
||||
FrontendSyncDelayMS int // 500ms sync delay
|
||||
FrontendMaxRetryAttempts int // 3 maximum retry attempts
|
||||
FrontendAudioLevelUpdateMS int // 100ms audio level update interval
|
||||
FrontendFFTSize int // 256 FFT size for audio analysis
|
||||
FrontendAudioLevelMax int // 100 maximum audio level
|
||||
FrontendReconnectIntervalMS int // 3000ms reconnect interval
|
||||
FrontendSubscriptionDelayMS int // 100ms subscription delay
|
||||
FrontendDebugIntervalMS int // 5000ms debug interval
|
||||
|
||||
// Process Monitor Constants
|
||||
ProcessMonitorDefaultMemoryGB int // 4GB default memory for fallback
|
||||
ProcessMonitorKBToBytes int // 1024 conversion factor
|
||||
ProcessMonitorDefaultClockHz float64 // 250.0 Hz default for ARM systems
|
||||
ProcessMonitorFallbackClockHz float64 // 1000.0 Hz fallback clock
|
||||
ProcessMonitorTraditionalHz float64 // 100.0 Hz traditional clock
|
||||
|
||||
// Batch Processing Constants
|
||||
BatchProcessorFramesPerBatch int // 4 frames per batch
|
||||
BatchProcessorTimeout time.Duration // 5ms timeout
|
||||
|
||||
// Output Streaming Constants
|
||||
OutputStreamingFrameIntervalMS int // 20ms frame interval (50 FPS)
|
||||
|
||||
// IPC Constants
|
||||
IPCInitialBufferFrames int // 500 frames for initial buffer
|
||||
|
||||
// Event Constants
|
||||
EventTimeoutSeconds int // 2 seconds for event timeout
|
||||
EventTimeFormatString string // "2006-01-02T15:04:05.000Z" time format
|
||||
EventSubscriptionDelayMS int // 100ms subscription delay
|
||||
|
||||
// Input Processing Constants
|
||||
InputProcessingTimeoutMS int // 10ms processing timeout threshold
|
||||
|
||||
// Adaptive Buffer Constants
|
||||
AdaptiveBufferCPUMultiplier int // 100 multiplier for CPU percentage
|
||||
AdaptiveBufferMemoryMultiplier int // 100 multiplier for memory percentage
|
||||
}
|
||||
|
||||
// DefaultAudioConfig returns the default configuration constants
|
||||
|
@ -1247,6 +1297,56 @@ func DefaultAudioConfig() *AudioConfigConstants {
|
|||
PoolGrowthMultiplier: 2, // Pool growth multiplier
|
||||
LatencyScalingFactor: 2.0, // Latency ratio scaling factor
|
||||
OptimizerAggressiveness: 0.7, // Optimizer aggressiveness factor
|
||||
|
||||
// CGO Audio Processing Constants
|
||||
CGOUsleepMicroseconds: 1000, // 1000 microseconds (1ms) for CGO usleep calls
|
||||
CGOPCMBufferSize: 1920, // 1920 samples for PCM buffer (max 2ch*960)
|
||||
CGONanosecondsPerSecond: 1000000000.0, // 1000000000.0 for nanosecond conversions
|
||||
|
||||
// Frontend Constants
|
||||
FrontendOperationDebounceMS: 1000, // 1000ms debounce for frontend operations
|
||||
FrontendSyncDebounceMS: 1000, // 1000ms debounce for sync operations
|
||||
FrontendSampleRate: 48000, // 48000Hz sample rate for frontend audio
|
||||
FrontendRetryDelayMS: 500, // 500ms retry delay
|
||||
FrontendShortDelayMS: 200, // 200ms short delay
|
||||
FrontendLongDelayMS: 300, // 300ms long delay
|
||||
FrontendSyncDelayMS: 500, // 500ms sync delay
|
||||
FrontendMaxRetryAttempts: 3, // 3 maximum retry attempts
|
||||
FrontendAudioLevelUpdateMS: 100, // 100ms audio level update interval
|
||||
FrontendFFTSize: 256, // 256 FFT size for audio analysis
|
||||
FrontendAudioLevelMax: 100, // 100 maximum audio level
|
||||
FrontendReconnectIntervalMS: 3000, // 3000ms reconnect interval
|
||||
FrontendSubscriptionDelayMS: 100, // 100ms subscription delay
|
||||
FrontendDebugIntervalMS: 5000, // 5000ms debug interval
|
||||
|
||||
// Process Monitor Constants
|
||||
ProcessMonitorDefaultMemoryGB: 4, // 4GB default memory for fallback
|
||||
ProcessMonitorKBToBytes: 1024, // 1024 conversion factor
|
||||
ProcessMonitorDefaultClockHz: 250.0, // 250.0 Hz default for ARM systems
|
||||
ProcessMonitorFallbackClockHz: 1000.0, // 1000.0 Hz fallback clock
|
||||
ProcessMonitorTraditionalHz: 100.0, // 100.0 Hz traditional clock
|
||||
|
||||
// Batch Processing Constants
|
||||
BatchProcessorFramesPerBatch: 4, // 4 frames per batch
|
||||
BatchProcessorTimeout: 5 * time.Millisecond, // 5ms timeout
|
||||
|
||||
// Output Streaming Constants
|
||||
OutputStreamingFrameIntervalMS: 20, // 20ms frame interval (50 FPS)
|
||||
|
||||
// IPC Constants
|
||||
IPCInitialBufferFrames: 500, // 500 frames for initial buffer
|
||||
|
||||
// Event Constants
|
||||
EventTimeoutSeconds: 2, // 2 seconds for event timeout
|
||||
EventTimeFormatString: "2006-01-02T15:04:05.000Z", // "2006-01-02T15:04:05.000Z" time format
|
||||
EventSubscriptionDelayMS: 100, // 100ms subscription delay
|
||||
|
||||
// Input Processing Constants
|
||||
InputProcessingTimeoutMS: 10, // 10ms processing timeout threshold
|
||||
|
||||
// Adaptive Buffer Constants
|
||||
AdaptiveBufferCPUMultiplier: 100, // 100 multiplier for CPU percentage
|
||||
AdaptiveBufferMemoryMultiplier: 100, // 100 multiplier for memory percentage
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -226,7 +226,7 @@ func convertAudioMetricsToEventDataWithLatencyMs(metrics AudioMetrics) AudioMetr
|
|||
FramesReceived: metrics.FramesReceived,
|
||||
FramesDropped: metrics.FramesDropped,
|
||||
BytesProcessed: metrics.BytesProcessed,
|
||||
LastFrameTime: metrics.LastFrameTime.Format("2006-01-02T15:04:05.000Z"),
|
||||
LastFrameTime: metrics.LastFrameTime.Format(GetConfig().EventTimeFormatString),
|
||||
ConnectionDrops: metrics.ConnectionDrops,
|
||||
AverageLatency: fmt.Sprintf("%.1fms", float64(metrics.AverageLatency.Nanoseconds())/1e6),
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ func convertAudioInputMetricsToEventDataWithLatencyMs(metrics AudioInputMetrics)
|
|||
FramesSent: metrics.FramesSent,
|
||||
FramesDropped: metrics.FramesDropped,
|
||||
BytesProcessed: metrics.BytesProcessed,
|
||||
LastFrameTime: metrics.LastFrameTime.Format("2006-01-02T15:04:05.000Z"),
|
||||
LastFrameTime: metrics.LastFrameTime.Format(GetConfig().EventTimeFormatString),
|
||||
ConnectionDrops: metrics.ConnectionDrops,
|
||||
AverageLatency: fmt.Sprintf("%.1fms", float64(metrics.AverageLatency.Nanoseconds())/1e6),
|
||||
}
|
||||
|
@ -463,7 +463,7 @@ func (aeb *AudioEventBroadcaster) sendToSubscriber(subscriber *AudioEventSubscri
|
|||
return false
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(subscriber.ctx, 2*time.Second)
|
||||
ctx, cancel := context.WithTimeout(subscriber.ctx, time.Duration(GetConfig().EventTimeoutSeconds)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err := wsjson.Write(ctx, subscriber.conn, event)
|
||||
|
|
|
@ -80,7 +80,7 @@ func (aim *AudioInputManager) WriteOpusFrame(frame []byte) error {
|
|||
processingTime := time.Since(startTime)
|
||||
|
||||
// Log high latency warnings
|
||||
if processingTime > 10*time.Millisecond {
|
||||
if processingTime > time.Duration(GetConfig().InputProcessingTimeoutMS)*time.Millisecond {
|
||||
aim.logger.Warn().
|
||||
Dur("latency_ms", processingTime).
|
||||
Msg("High audio processing latency detected")
|
||||
|
@ -116,7 +116,7 @@ func (aim *AudioInputManager) WriteOpusFrameZeroCopy(frame *ZeroCopyAudioFrame)
|
|||
processingTime := time.Since(startTime)
|
||||
|
||||
// Log high latency warnings
|
||||
if processingTime > 10*time.Millisecond {
|
||||
if processingTime > time.Duration(GetConfig().InputProcessingTimeoutMS)*time.Millisecond {
|
||||
aim.logger.Warn().
|
||||
Dur("latency_ms", processingTime).
|
||||
Msg("High audio processing latency detected")
|
||||
|
|
|
@ -122,7 +122,7 @@ func (s *OutputStreamer) streamLoop() {
|
|||
defer runtime.UnlockOSThread()
|
||||
|
||||
// Adaptive timing for frame reading
|
||||
frameInterval := time.Duration(20) * time.Millisecond // 50 FPS base rate
|
||||
frameInterval := time.Duration(GetConfig().OutputStreamingFrameIntervalMS) * time.Millisecond // 50 FPS base rate
|
||||
ticker := time.NewTicker(frameInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
|
|
|
@ -316,7 +316,7 @@ func (pm *ProcessMonitor) getClockTicks() float64 {
|
|||
if len(fields) >= 2 {
|
||||
if period, err := strconv.ParseInt(fields[1], 10, 64); err == nil && period > 0 {
|
||||
// Convert nanoseconds to Hz
|
||||
hz := 1000000000.0 / float64(period)
|
||||
hz := GetConfig().CGONanosecondsPerSecond / float64(period)
|
||||
if hz >= minValidClockTicks && hz <= maxValidClockTicks {
|
||||
pm.clockTicks = hz
|
||||
return
|
||||
|
@ -344,7 +344,7 @@ func (pm *ProcessMonitor) getTotalMemory() int64 {
|
|||
pm.memoryOnce.Do(func() {
|
||||
file, err := os.Open("/proc/meminfo")
|
||||
if err != nil {
|
||||
pm.totalMemory = int64(defaultMemoryGB) * 1024 * 1024 * 1024
|
||||
pm.totalMemory = int64(defaultMemoryGB) * int64(GetConfig().ProcessMonitorKBToBytes) * int64(GetConfig().ProcessMonitorKBToBytes) * int64(GetConfig().ProcessMonitorKBToBytes)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
@ -356,14 +356,14 @@ func (pm *ProcessMonitor) getTotalMemory() int64 {
|
|||
fields := strings.Fields(line)
|
||||
if len(fields) >= 2 {
|
||||
if kb, err := strconv.ParseInt(fields[1], 10, 64); err == nil {
|
||||
pm.totalMemory = kb * 1024
|
||||
pm.totalMemory = kb * int64(GetConfig().ProcessMonitorKBToBytes)
|
||||
return
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
pm.totalMemory = int64(defaultMemoryGB) * 1024 * 1024 * 1024 // Fallback
|
||||
pm.totalMemory = int64(defaultMemoryGB) * int64(GetConfig().ProcessMonitorKBToBytes) * int64(GetConfig().ProcessMonitorKBToBytes) * int64(GetConfig().ProcessMonitorKBToBytes) // Fallback
|
||||
})
|
||||
return pm.totalMemory
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ type ZeroCopyAudioFrame struct {
|
|||
// ZeroCopyFramePool manages reusable zero-copy audio frames
|
||||
type ZeroCopyFramePool struct {
|
||||
// Atomic fields MUST be first for ARM32 alignment (int64 fields need 8-byte alignment)
|
||||
counter int64 // Frame counter (atomic)
|
||||
hitCount int64 // Pool hit counter (atomic)
|
||||
missCount int64 // Pool miss counter (atomic)
|
||||
counter int64 // Frame counter (atomic)
|
||||
hitCount int64 // Pool hit counter (atomic)
|
||||
missCount int64 // Pool miss counter (atomic)
|
||||
allocationCount int64 // Total allocations counter (atomic)
|
||||
|
||||
// Other fields
|
||||
|
@ -94,7 +94,7 @@ func (p *ZeroCopyFramePool) Get() *ZeroCopyAudioFrame {
|
|||
frame.mutex.Unlock()
|
||||
return frame
|
||||
}
|
||||
|
||||
|
||||
// First try pre-allocated frames for fastest access
|
||||
p.mutex.Lock()
|
||||
if len(p.preallocated) > 0 {
|
||||
|
|
Loading…
Reference in New Issue