From 0e76023c393d8a66e51bab58761bf703e17874e2 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 11 Sep 2025 13:27:10 +0300 Subject: [PATCH] Improvement (Maintainability): Make all C code more manageable by moving it to its own dedicated file --- internal/audio/c/audio.c | 607 +++++++++++++++++++++++++++++++++++ internal/audio/cgo_audio.go | 608 +----------------------------------- 2 files changed, 608 insertions(+), 607 deletions(-) create mode 100644 internal/audio/c/audio.c diff --git a/internal/audio/c/audio.c b/internal/audio/c/audio.c new file mode 100644 index 00000000..eebaacb7 --- /dev/null +++ b/internal/audio/c/audio.c @@ -0,0 +1,607 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// C state for ALSA/Opus with safety flags +static snd_pcm_t *pcm_handle = NULL; +static snd_pcm_t *pcm_playback_handle = NULL; +static OpusEncoder *encoder = NULL; +static OpusDecoder *decoder = NULL; +// Opus encoder settings - initialized from Go configuration +static int opus_bitrate = 96000; // Will be set from Config.CGOOpusBitrate +static int opus_complexity = 3; // Will be set from Config.CGOOpusComplexity +static int opus_vbr = 1; // Will be set from Config.CGOOpusVBR +static int opus_vbr_constraint = 1; // Will be set from Config.CGOOpusVBRConstraint +static int opus_signal_type = 3; // Will be set from Config.CGOOpusSignalType +static int opus_bandwidth = 1105; // OPUS_BANDWIDTH_WIDEBAND for compatibility (was 1101) +static int opus_dtx = 0; // Will be set from Config.CGOOpusDTX +static int opus_lsb_depth = 16; // LSB depth for improved bit allocation on constrained hardware +static int sample_rate = 48000; // Will be set from Config.CGOSampleRate +static int channels = 2; // Will be set from Config.CGOChannels +static int frame_size = 960; // Will be set from Config.CGOFrameSize +static int max_packet_size = 1500; // Will be set from Config.CGOMaxPacketSize +static int sleep_microseconds = 1000; // Will be set from Config.CGOUsleepMicroseconds +static int max_attempts_global = 5; // Will be set from Config.CGOMaxAttempts +static int max_backoff_us_global = 500000; // Will be set from Config.CGOMaxBackoffMicroseconds +// Hardware optimization flags for constrained environments +static int use_mmap_access = 0; // Disable MMAP for compatibility (was 1) +static int optimized_buffer_size = 0; // Disable optimized buffer sizing for stability (was 1) + +// C function declarations (implementations are below) +int jetkvm_audio_init(); +void jetkvm_audio_close(); +int jetkvm_audio_read_encode(void *opus_buf); +int jetkvm_audio_decode_write(void *opus_buf, int opus_size); +int jetkvm_audio_playback_init(); +void jetkvm_audio_playback_close(); + +// Function to update constants from Go configuration +void update_audio_constants(int bitrate, int complexity, int vbr, int vbr_constraint, + int signal_type, int bandwidth, int dtx, int lsb_depth, int sr, int ch, + int fs, int max_pkt, int sleep_us, int max_attempts, int max_backoff) { + opus_bitrate = bitrate; + opus_complexity = complexity; + opus_vbr = vbr; + opus_vbr_constraint = vbr_constraint; + opus_signal_type = signal_type; + opus_bandwidth = bandwidth; + opus_dtx = dtx; + opus_lsb_depth = lsb_depth; + sample_rate = sr; + channels = ch; + frame_size = fs; + max_packet_size = max_pkt; + sleep_microseconds = sleep_us; + max_attempts_global = max_attempts; + max_backoff_us_global = max_backoff; +} + +// State tracking to prevent race conditions during rapid start/stop +static volatile int capture_initializing = 0; +static volatile int capture_initialized = 0; +static volatile int playback_initializing = 0; +static volatile int playback_initialized = 0; + +// Function to dynamically update Opus encoder parameters +int update_opus_encoder_params(int bitrate, int complexity, int vbr, int vbr_constraint, + int signal_type, int bandwidth, int dtx) { + // This function updates encoder parameters for audio input (capture) + // Only capture uses the encoder; playback uses a separate decoder + if (!encoder || !capture_initialized) { + return -1; // Audio encoder not initialized + } + + // Update the static variables + opus_bitrate = bitrate; + opus_complexity = complexity; + opus_vbr = vbr; + opus_vbr_constraint = vbr_constraint; + opus_signal_type = signal_type; + opus_bandwidth = bandwidth; + opus_dtx = dtx; + + // Apply the new settings to the encoder + int result = 0; + result |= opus_encoder_ctl(encoder, OPUS_SET_BITRATE(opus_bitrate)); + result |= opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(opus_complexity)); + result |= opus_encoder_ctl(encoder, OPUS_SET_VBR(opus_vbr)); + result |= opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(opus_vbr_constraint)); + result |= opus_encoder_ctl(encoder, OPUS_SET_SIGNAL(opus_signal_type)); + result |= opus_encoder_ctl(encoder, OPUS_SET_BANDWIDTH(opus_bandwidth)); + result |= opus_encoder_ctl(encoder, OPUS_SET_DTX(opus_dtx)); + + return result; // 0 on success, non-zero on error +} + +// Enhanced ALSA device opening with exponential backoff retry logic +static int safe_alsa_open(snd_pcm_t **handle, const char *device, snd_pcm_stream_t stream) { + int attempt = 0; + int err; + int backoff_us = sleep_microseconds; // Start with base sleep time + + while (attempt < max_attempts_global) { + err = snd_pcm_open(handle, device, stream, SND_PCM_NONBLOCK); + if (err >= 0) { + // Switch to blocking mode after successful open + snd_pcm_nonblock(*handle, 0); + return 0; + } + + attempt++; + if (attempt >= max_attempts_global) break; + + // Enhanced error handling with specific retry strategies + if (err == -EBUSY || err == -EAGAIN) { + // Device busy or temporarily unavailable - retry with backoff + usleep(backoff_us); + backoff_us = (backoff_us * 2 < max_backoff_us_global) ? backoff_us * 2 : max_backoff_us_global; + } else if (err == -ENODEV || err == -ENOENT) { + // Device not found - longer wait as device might be initializing + usleep(backoff_us * 2); + backoff_us = (backoff_us * 2 < max_backoff_us_global) ? backoff_us * 2 : max_backoff_us_global; + } else if (err == -EPERM || err == -EACCES) { + // Permission denied - shorter wait, likely persistent issue + usleep(backoff_us / 2); + } else { + // Other errors - standard backoff + usleep(backoff_us); + backoff_us = (backoff_us * 2 < max_backoff_us_global) ? backoff_us * 2 : max_backoff_us_global; + } + } + return err; +} + +// Optimized ALSA configuration with stack allocation and performance tuning +static int configure_alsa_device(snd_pcm_t *handle, const char *device_name) { + snd_pcm_hw_params_t *params; + snd_pcm_sw_params_t *sw_params; + int err; + + if (!handle) return -1; + + // Use stack allocation for better performance + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_sw_params_alloca(&sw_params); + + // Hardware parameters + err = snd_pcm_hw_params_any(handle, params); + if (err < 0) return err; + + // Use MMAP access for direct hardware memory access if enabled + if (use_mmap_access) { + err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_MMAP_INTERLEAVED); + if (err < 0) { + // Fallback to RW access if MMAP fails + err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); + } + } else { + err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); + } + if (err < 0) return err; + + err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); + if (err < 0) return err; + + err = snd_pcm_hw_params_set_channels(handle, params, channels); + if (err < 0) return err; + + // Set exact rate for better performance + err = snd_pcm_hw_params_set_rate(handle, params, sample_rate, 0); + if (err < 0) { + // Fallback to near rate if exact fails + unsigned int rate = sample_rate; + err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0); + if (err < 0) return err; + } + + // Optimize buffer sizes for constrained hardware + snd_pcm_uframes_t period_size = frame_size; + if (optimized_buffer_size) { + // Use smaller periods for lower latency on constrained hardware + period_size = frame_size / 2; + if (period_size < 64) period_size = 64; // Minimum safe period size + } + err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, 0); + if (err < 0) return err; + + // Optimize buffer size based on hardware constraints + snd_pcm_uframes_t buffer_size; + if (optimized_buffer_size) { + // Use 2 periods for ultra-low latency on constrained hardware + buffer_size = period_size * 2; + } else { + // Standard 4 periods for good latency/stability balance + buffer_size = period_size * 4; + } + err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size); + if (err < 0) return err; + + err = snd_pcm_hw_params(handle, params); + if (err < 0) return err; + + // Software parameters for optimal performance + err = snd_pcm_sw_params_current(handle, sw_params); + if (err < 0) return err; + + // Start playback/capture when buffer is period_size frames + err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, period_size); + if (err < 0) return err; + + // Allow transfers when at least period_size frames are available + err = snd_pcm_sw_params_set_avail_min(handle, sw_params, period_size); + if (err < 0) return err; + + err = snd_pcm_sw_params(handle, sw_params); + if (err < 0) return err; + + return snd_pcm_prepare(handle); +} + +// Initialize ALSA and Opus encoder with improved safety +int jetkvm_audio_init() { + int err; + + // Prevent concurrent initialization + if (__sync_bool_compare_and_swap(&capture_initializing, 0, 1) == 0) { + return -EBUSY; // Already initializing + } + + // Check if already initialized + if (capture_initialized) { + capture_initializing = 0; + return 0; + } + + // Clean up any existing resources first + if (encoder) { + opus_encoder_destroy(encoder); + encoder = NULL; + } + if (pcm_handle) { + snd_pcm_close(pcm_handle); + pcm_handle = NULL; + } + + // Try to open ALSA capture device + err = safe_alsa_open(&pcm_handle, "hw:1,0", SND_PCM_STREAM_CAPTURE); + if (err < 0) { + capture_initializing = 0; + return -1; + } + + // Configure the device + err = configure_alsa_device(pcm_handle, "capture"); + if (err < 0) { + snd_pcm_close(pcm_handle); + pcm_handle = NULL; + capture_initializing = 0; + return -1; + } + + // Initialize Opus encoder with optimized settings + int opus_err = 0; + encoder = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_AUDIO, &opus_err); + if (!encoder || opus_err != OPUS_OK) { + if (pcm_handle) { snd_pcm_close(pcm_handle); pcm_handle = NULL; } + capture_initializing = 0; + return -2; + } + + // Apply optimized Opus encoder settings for constrained hardware + opus_encoder_ctl(encoder, OPUS_SET_BITRATE(opus_bitrate)); + opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(opus_complexity)); + opus_encoder_ctl(encoder, OPUS_SET_VBR(opus_vbr)); + opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(opus_vbr_constraint)); + opus_encoder_ctl(encoder, OPUS_SET_SIGNAL(opus_signal_type)); + opus_encoder_ctl(encoder, OPUS_SET_BANDWIDTH(opus_bandwidth)); // WIDEBAND for compatibility + opus_encoder_ctl(encoder, OPUS_SET_DTX(opus_dtx)); + // Set LSB depth for improved bit allocation on constrained hardware (disabled for compatibility) + // opus_encoder_ctl(encoder, OPUS_SET_LSB_DEPTH(opus_lsb_depth)); + // Enable packet loss concealment for better resilience + opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(5)); + // Set prediction disabled for lower latency + opus_encoder_ctl(encoder, OPUS_SET_PREDICTION_DISABLED(1)); + + capture_initialized = 1; + capture_initializing = 0; + return 0; +} + +// jetkvm_audio_read_encode captures audio from ALSA, encodes with Opus, and handles errors. +// Implements robust error recovery for buffer underruns and device suspension. +// Returns: >0 (bytes written), -1 (init error), -2 (unrecoverable error) +int jetkvm_audio_read_encode(void *opus_buf) { + short pcm_buffer[1920]; // max 2ch*960 + unsigned char *out = (unsigned char*)opus_buf; + int err = 0; + int recovery_attempts = 0; + const int max_recovery_attempts = 3; + + // Safety checks + if (!capture_initialized || !pcm_handle || !encoder || !opus_buf) { + return -1; + } + +retry_read: + ; + int pcm_rc = snd_pcm_readi(pcm_handle, pcm_buffer, frame_size); + + // Handle ALSA errors with robust recovery strategies + if (pcm_rc < 0) { + if (pcm_rc == -EPIPE) { + // Buffer underrun - implement progressive recovery + recovery_attempts++; + if (recovery_attempts > max_recovery_attempts) { + return -1; // Give up after max attempts + } + + // Try to recover with prepare + err = snd_pcm_prepare(pcm_handle); + if (err < 0) { + // If prepare fails, try drop and prepare + snd_pcm_drop(pcm_handle); + err = snd_pcm_prepare(pcm_handle); + if (err < 0) return -1; + } + + // Wait before retry to allow device to stabilize + usleep(sleep_microseconds * recovery_attempts); + goto retry_read; + } else if (pcm_rc == -EAGAIN) { + // No data available - return 0 to indicate no frame + return 0; + } else if (pcm_rc == -ESTRPIPE) { + // Device suspended, implement robust resume logic + recovery_attempts++; + if (recovery_attempts > max_recovery_attempts) { + return -1; + } + + // Try to resume with timeout + int resume_attempts = 0; + while ((err = snd_pcm_resume(pcm_handle)) == -EAGAIN && resume_attempts < 10) { + usleep(sleep_microseconds); + resume_attempts++; + } + if (err < 0) { + // Resume failed, try prepare as fallback + err = snd_pcm_prepare(pcm_handle); + if (err < 0) return -1; + } + // Wait before retry to allow device to stabilize + usleep(sleep_microseconds * recovery_attempts); + return 0; // Skip this frame but don't fail + } else if (pcm_rc == -ENODEV) { + // Device disconnected - critical error + return -1; + } else if (pcm_rc == -EIO) { + // I/O error - try recovery once + recovery_attempts++; + if (recovery_attempts <= max_recovery_attempts) { + snd_pcm_drop(pcm_handle); + err = snd_pcm_prepare(pcm_handle); + if (err >= 0) { + usleep(sleep_microseconds); + goto retry_read; + } + } + return -1; + } else { + // Other errors - limited retry for transient issues + recovery_attempts++; + if (recovery_attempts <= 1 && (pcm_rc == -EINTR || pcm_rc == -EBUSY)) { + usleep(sleep_microseconds / 2); + goto retry_read; + } + return -1; + } + } + + // If we got fewer frames than expected, pad with silence + if (pcm_rc < frame_size) { + memset(&pcm_buffer[pcm_rc * channels], 0, (frame_size - pcm_rc) * channels * sizeof(short)); + } + + int nb_bytes = opus_encode(encoder, pcm_buffer, frame_size, out, max_packet_size); + return nb_bytes; +} + +// Initialize ALSA playback with improved safety +int jetkvm_audio_playback_init() { + int err; + + // Prevent concurrent initialization + if (__sync_bool_compare_and_swap(&playback_initializing, 0, 1) == 0) { + return -EBUSY; // Already initializing + } + + // Check if already initialized + if (playback_initialized) { + playback_initializing = 0; + return 0; + } + + // Clean up any existing resources first + if (decoder) { + opus_decoder_destroy(decoder); + decoder = NULL; + } + if (pcm_playback_handle) { + snd_pcm_close(pcm_playback_handle); + pcm_playback_handle = NULL; + } + + // Try to open the USB gadget audio device for playback + err = safe_alsa_open(&pcm_playback_handle, "hw:1,0", SND_PCM_STREAM_PLAYBACK); + if (err < 0) { + // Fallback to default device + err = safe_alsa_open(&pcm_playback_handle, "default", SND_PCM_STREAM_PLAYBACK); + if (err < 0) { + playback_initializing = 0; + return -1; + } + } + + // Configure the device + err = configure_alsa_device(pcm_playback_handle, "playback"); + if (err < 0) { + snd_pcm_close(pcm_playback_handle); + pcm_playback_handle = NULL; + playback_initializing = 0; + return -1; + } + + // Initialize Opus decoder + int opus_err = 0; + decoder = opus_decoder_create(sample_rate, channels, &opus_err); + if (!decoder || opus_err != OPUS_OK) { + snd_pcm_close(pcm_playback_handle); + pcm_playback_handle = NULL; + playback_initializing = 0; + return -2; + } + + playback_initialized = 1; + playback_initializing = 0; + return 0; +} + +// jetkvm_audio_decode_write decodes Opus data and writes PCM to ALSA playback device +// with error recovery and packet loss concealment +int jetkvm_audio_decode_write(void *opus_buf, int opus_size) { + short pcm_buffer[1920]; // max 2ch*960 + unsigned char *in = (unsigned char*)opus_buf; + int err = 0; + int recovery_attempts = 0; + const int max_recovery_attempts = 3; + + // Safety checks + if (!playback_initialized || !pcm_playback_handle || !decoder || !opus_buf || opus_size <= 0) { + return -1; + } + + // Additional bounds checking + if (opus_size > max_packet_size) { + return -1; + } + + // Decode Opus to PCM with error handling + int pcm_frames = opus_decode(decoder, in, opus_size, pcm_buffer, frame_size, 0); + if (pcm_frames < 0) { + // Try packet loss concealment on decode error + pcm_frames = opus_decode(decoder, NULL, 0, pcm_buffer, frame_size, 0); + if (pcm_frames < 0) return -1; + } + +retry_write: + ; + // Write PCM to playback device with robust recovery + int pcm_rc = snd_pcm_writei(pcm_playback_handle, pcm_buffer, pcm_frames); + if (pcm_rc < 0) { + if (pcm_rc == -EPIPE) { + // Buffer underrun - implement progressive recovery + recovery_attempts++; + if (recovery_attempts > max_recovery_attempts) { + return -2; + } + + // Try to recover with prepare + err = snd_pcm_prepare(pcm_playback_handle); + if (err < 0) { + // If prepare fails, try drop and prepare + snd_pcm_drop(pcm_playback_handle); + err = snd_pcm_prepare(pcm_playback_handle); + if (err < 0) return -2; + } + + // Wait before retry to allow device to stabilize + usleep(sleep_microseconds * recovery_attempts); + goto retry_write; + } else if (pcm_rc == -ESTRPIPE) { + // Device suspended, implement robust resume logic + recovery_attempts++; + if (recovery_attempts > max_recovery_attempts) { + return -2; + } + + // Try to resume with timeout + int resume_attempts = 0; + while ((err = snd_pcm_resume(pcm_playback_handle)) == -EAGAIN && resume_attempts < 10) { + usleep(sleep_microseconds); + resume_attempts++; + } + if (err < 0) { + // Resume failed, try prepare as fallback + err = snd_pcm_prepare(pcm_playback_handle); + if (err < 0) return -2; + } + // Wait before retry to allow device to stabilize + usleep(sleep_microseconds * recovery_attempts); + return 0; // Skip this frame but don't fail + } else if (pcm_rc == -ENODEV) { + // Device disconnected - critical error + return -2; + } else if (pcm_rc == -EIO) { + // I/O error - try recovery once + recovery_attempts++; + if (recovery_attempts <= max_recovery_attempts) { + snd_pcm_drop(pcm_playback_handle); + err = snd_pcm_prepare(pcm_playback_handle); + if (err >= 0) { + usleep(sleep_microseconds); + goto retry_write; + } + } + return -2; + } else if (pcm_rc == -EAGAIN) { + // Device not ready - brief wait and retry + recovery_attempts++; + if (recovery_attempts <= max_recovery_attempts) { + usleep(sleep_microseconds / 4); + goto retry_write; + } + return -2; + } else { + // Other errors - limited retry for transient issues + recovery_attempts++; + if (recovery_attempts <= 1 && (pcm_rc == -EINTR || pcm_rc == -EBUSY)) { + usleep(sleep_microseconds / 2); + goto retry_write; + } + return -2; + } + } + + return pcm_frames; +} + +// Safe playback cleanup with double-close protection +void jetkvm_audio_playback_close() { + // Wait for any ongoing operations to complete + while (playback_initializing) { + usleep(sleep_microseconds); // Use centralized constant + } + + // Atomic check and set to prevent double cleanup + if (__sync_bool_compare_and_swap(&playback_initialized, 1, 0) == 0) { + return; // Already cleaned up + } + + if (decoder) { + opus_decoder_destroy(decoder); + decoder = NULL; + } + if (pcm_playback_handle) { + snd_pcm_drain(pcm_playback_handle); + snd_pcm_close(pcm_playback_handle); + pcm_playback_handle = NULL; + } +} + +// Safe capture cleanup +void jetkvm_audio_close() { + // Wait for any ongoing operations to complete + while (capture_initializing) { + usleep(sleep_microseconds); + } + + // Atomic check and set to prevent double cleanup + if (__sync_bool_compare_and_swap(&capture_initialized, 1, 0) == 0) { + return; // Already cleaned up + } + + if (encoder) { + opus_encoder_destroy(encoder); + encoder = NULL; + } + if (pcm_handle) { + snd_pcm_drain(pcm_handle); + snd_pcm_close(pcm_handle); + pcm_handle = NULL; + } +} \ No newline at end of file diff --git a/internal/audio/cgo_audio.go b/internal/audio/cgo_audio.go index af4ef35f..00b346bd 100644 --- a/internal/audio/cgo_audio.go +++ b/internal/audio/cgo_audio.go @@ -15,613 +15,7 @@ import ( #cgo CFLAGS: -I$HOME/.jetkvm/audio-libs/alsa-lib-$ALSA_VERSION/include -I$HOME/.jetkvm/audio-libs/opus-$OPUS_VERSION/include -I$HOME/.jetkvm/audio-libs/opus-$OPUS_VERSION/celt #cgo LDFLAGS: -L$HOME/.jetkvm/audio-libs/alsa-lib-$ALSA_VERSION/src/.libs -lasound -L$HOME/.jetkvm/audio-libs/opus-$OPUS_VERSION/.libs -lopus -lm -ldl -static -#include -#include -#include -#include -#include -#include -#include -#include - -// C state for ALSA/Opus with safety flags -static snd_pcm_t *pcm_handle = NULL; -static snd_pcm_t *pcm_playback_handle = NULL; -static OpusEncoder *encoder = NULL; -static OpusDecoder *decoder = NULL; -// Opus encoder settings - initialized from Go configuration -static int opus_bitrate = 96000; // Will be set from Config.CGOOpusBitrate -static int opus_complexity = 3; // Will be set from Config.CGOOpusComplexity -static int opus_vbr = 1; // Will be set from Config.CGOOpusVBR -static int opus_vbr_constraint = 1; // Will be set from Config.CGOOpusVBRConstraint -static int opus_signal_type = 3; // Will be set from Config.CGOOpusSignalType -static int opus_bandwidth = 1105; // OPUS_BANDWIDTH_WIDEBAND for compatibility (was 1101) -static int opus_dtx = 0; // Will be set from Config.CGOOpusDTX -static int opus_lsb_depth = 16; // LSB depth for improved bit allocation on constrained hardware -static int sample_rate = 48000; // Will be set from Config.CGOSampleRate -static int channels = 2; // Will be set from Config.CGOChannels -static int frame_size = 960; // Will be set from Config.CGOFrameSize -static int max_packet_size = 1500; // Will be set from Config.CGOMaxPacketSize -static int sleep_microseconds = 1000; // Will be set from Config.CGOUsleepMicroseconds -static int max_attempts_global = 5; // Will be set from Config.CGOMaxAttempts -static int max_backoff_us_global = 500000; // Will be set from Config.CGOMaxBackoffMicroseconds -// Hardware optimization flags for constrained environments -static int use_mmap_access = 0; // Disable MMAP for compatibility (was 1) -static int optimized_buffer_size = 0; // Disable optimized buffer sizing for stability (was 1) - -// C function declarations (implementations are below) -int jetkvm_audio_init(); -void jetkvm_audio_close(); -int jetkvm_audio_read_encode(void *opus_buf); -int jetkvm_audio_decode_write(void *opus_buf, int opus_size); -int jetkvm_audio_playback_init(); -void jetkvm_audio_playback_close(); - -// Function to update constants from Go configuration -void update_audio_constants(int bitrate, int complexity, int vbr, int vbr_constraint, - int signal_type, int bandwidth, int dtx, int lsb_depth, int sr, int ch, - int fs, int max_pkt, int sleep_us, int max_attempts, int max_backoff) { - opus_bitrate = bitrate; - opus_complexity = complexity; - opus_vbr = vbr; - opus_vbr_constraint = vbr_constraint; - opus_signal_type = signal_type; - opus_bandwidth = bandwidth; - opus_dtx = dtx; - opus_lsb_depth = lsb_depth; - sample_rate = sr; - channels = ch; - frame_size = fs; - max_packet_size = max_pkt; - sleep_microseconds = sleep_us; - max_attempts_global = max_attempts; - max_backoff_us_global = max_backoff; -} - -// State tracking to prevent race conditions during rapid start/stop -static volatile int capture_initializing = 0; -static volatile int capture_initialized = 0; -static volatile int playback_initializing = 0; -static volatile int playback_initialized = 0; - -// Function to dynamically update Opus encoder parameters -int update_opus_encoder_params(int bitrate, int complexity, int vbr, int vbr_constraint, - int signal_type, int bandwidth, int dtx) { - // This function updates encoder parameters for audio input (capture) - // Only capture uses the encoder; playback uses a separate decoder - if (!encoder || !capture_initialized) { - return -1; // Audio encoder not initialized - } - - // Update the static variables - opus_bitrate = bitrate; - opus_complexity = complexity; - opus_vbr = vbr; - opus_vbr_constraint = vbr_constraint; - opus_signal_type = signal_type; - opus_bandwidth = bandwidth; - opus_dtx = dtx; - - // Apply the new settings to the encoder - int result = 0; - result |= opus_encoder_ctl(encoder, OPUS_SET_BITRATE(opus_bitrate)); - result |= opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(opus_complexity)); - result |= opus_encoder_ctl(encoder, OPUS_SET_VBR(opus_vbr)); - result |= opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(opus_vbr_constraint)); - result |= opus_encoder_ctl(encoder, OPUS_SET_SIGNAL(opus_signal_type)); - result |= opus_encoder_ctl(encoder, OPUS_SET_BANDWIDTH(opus_bandwidth)); - result |= opus_encoder_ctl(encoder, OPUS_SET_DTX(opus_dtx)); - - return result; // 0 on success, non-zero on error -} - -// Enhanced ALSA device opening with exponential backoff retry logic -static int safe_alsa_open(snd_pcm_t **handle, const char *device, snd_pcm_stream_t stream) { - int attempt = 0; - int err; - int backoff_us = sleep_microseconds; // Start with base sleep time - - while (attempt < max_attempts_global) { - err = snd_pcm_open(handle, device, stream, SND_PCM_NONBLOCK); - if (err >= 0) { - // Switch to blocking mode after successful open - snd_pcm_nonblock(*handle, 0); - return 0; - } - - attempt++; - if (attempt >= max_attempts_global) break; - - // Enhanced error handling with specific retry strategies - if (err == -EBUSY || err == -EAGAIN) { - // Device busy or temporarily unavailable - retry with backoff - usleep(backoff_us); - backoff_us = (backoff_us * 2 < max_backoff_us_global) ? backoff_us * 2 : max_backoff_us_global; - } else if (err == -ENODEV || err == -ENOENT) { - // Device not found - longer wait as device might be initializing - usleep(backoff_us * 2); - backoff_us = (backoff_us * 2 < max_backoff_us_global) ? backoff_us * 2 : max_backoff_us_global; - } else if (err == -EPERM || err == -EACCES) { - // Permission denied - shorter wait, likely persistent issue - usleep(backoff_us / 2); - } else { - // Other errors - standard backoff - usleep(backoff_us); - backoff_us = (backoff_us * 2 < max_backoff_us_global) ? backoff_us * 2 : max_backoff_us_global; - } - } - return err; -} - -// Optimized ALSA configuration with stack allocation and performance tuning -static int configure_alsa_device(snd_pcm_t *handle, const char *device_name) { - snd_pcm_hw_params_t *params; - snd_pcm_sw_params_t *sw_params; - int err; - - if (!handle) return -1; - - // Use stack allocation for better performance - snd_pcm_hw_params_alloca(¶ms); - snd_pcm_sw_params_alloca(&sw_params); - - // Hardware parameters - err = snd_pcm_hw_params_any(handle, params); - if (err < 0) return err; - - // Use MMAP access for direct hardware memory access if enabled - if (use_mmap_access) { - err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_MMAP_INTERLEAVED); - if (err < 0) { - // Fallback to RW access if MMAP fails - err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); - } - } else { - err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); - } - if (err < 0) return err; - - err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); - if (err < 0) return err; - - err = snd_pcm_hw_params_set_channels(handle, params, channels); - if (err < 0) return err; - - // Set exact rate for better performance - err = snd_pcm_hw_params_set_rate(handle, params, sample_rate, 0); - if (err < 0) { - // Fallback to near rate if exact fails - unsigned int rate = sample_rate; - err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0); - if (err < 0) return err; - } - - // Optimize buffer sizes for constrained hardware - snd_pcm_uframes_t period_size = frame_size; - if (optimized_buffer_size) { - // Use smaller periods for lower latency on constrained hardware - period_size = frame_size / 2; - if (period_size < 64) period_size = 64; // Minimum safe period size - } - err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, 0); - if (err < 0) return err; - - // Optimize buffer size based on hardware constraints - snd_pcm_uframes_t buffer_size; - if (optimized_buffer_size) { - // Use 2 periods for ultra-low latency on constrained hardware - buffer_size = period_size * 2; - } else { - // Standard 4 periods for good latency/stability balance - buffer_size = period_size * 4; - } - err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size); - if (err < 0) return err; - - err = snd_pcm_hw_params(handle, params); - if (err < 0) return err; - - // Software parameters for optimal performance - err = snd_pcm_sw_params_current(handle, sw_params); - if (err < 0) return err; - - // Start playback/capture when buffer is period_size frames - err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, period_size); - if (err < 0) return err; - - // Allow transfers when at least period_size frames are available - err = snd_pcm_sw_params_set_avail_min(handle, sw_params, period_size); - if (err < 0) return err; - - err = snd_pcm_sw_params(handle, sw_params); - if (err < 0) return err; - - return snd_pcm_prepare(handle); -} - -// Initialize ALSA and Opus encoder with improved safety -int jetkvm_audio_init() { - int err; - - // Prevent concurrent initialization - if (__sync_bool_compare_and_swap(&capture_initializing, 0, 1) == 0) { - return -EBUSY; // Already initializing - } - - // Check if already initialized - if (capture_initialized) { - capture_initializing = 0; - return 0; - } - - // Clean up any existing resources first - if (encoder) { - opus_encoder_destroy(encoder); - encoder = NULL; - } - if (pcm_handle) { - snd_pcm_close(pcm_handle); - pcm_handle = NULL; - } - - // Try to open ALSA capture device - err = safe_alsa_open(&pcm_handle, "hw:1,0", SND_PCM_STREAM_CAPTURE); - if (err < 0) { - capture_initializing = 0; - return -1; - } - - // Configure the device - err = configure_alsa_device(pcm_handle, "capture"); - if (err < 0) { - snd_pcm_close(pcm_handle); - pcm_handle = NULL; - capture_initializing = 0; - return -1; - } - - // Initialize Opus encoder with optimized settings - int opus_err = 0; - encoder = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_AUDIO, &opus_err); - if (!encoder || opus_err != OPUS_OK) { - if (pcm_handle) { snd_pcm_close(pcm_handle); pcm_handle = NULL; } - capture_initializing = 0; - return -2; - } - - // Apply optimized Opus encoder settings for constrained hardware - opus_encoder_ctl(encoder, OPUS_SET_BITRATE(opus_bitrate)); - opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(opus_complexity)); - opus_encoder_ctl(encoder, OPUS_SET_VBR(opus_vbr)); - opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(opus_vbr_constraint)); - opus_encoder_ctl(encoder, OPUS_SET_SIGNAL(opus_signal_type)); - opus_encoder_ctl(encoder, OPUS_SET_BANDWIDTH(opus_bandwidth)); // WIDEBAND for compatibility - opus_encoder_ctl(encoder, OPUS_SET_DTX(opus_dtx)); - // Set LSB depth for improved bit allocation on constrained hardware (disabled for compatibility) - // opus_encoder_ctl(encoder, OPUS_SET_LSB_DEPTH(opus_lsb_depth)); - // Enable packet loss concealment for better resilience - opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(5)); - // Set prediction disabled for lower latency - opus_encoder_ctl(encoder, OPUS_SET_PREDICTION_DISABLED(1)); - - capture_initialized = 1; - capture_initializing = 0; - return 0; -} - -// jetkvm_audio_read_encode captures audio from ALSA, encodes with Opus, and handles errors. -// Implements robust error recovery for buffer underruns and device suspension. -// Returns: >0 (bytes written), -1 (init error), -2 (unrecoverable error) -int jetkvm_audio_read_encode(void *opus_buf) { - short pcm_buffer[1920]; // max 2ch*960 - unsigned char *out = (unsigned char*)opus_buf; - int err = 0; - int recovery_attempts = 0; - const int max_recovery_attempts = 3; - - // Safety checks - if (!capture_initialized || !pcm_handle || !encoder || !opus_buf) { - return -1; - } - -retry_read: - ; - int pcm_rc = snd_pcm_readi(pcm_handle, pcm_buffer, frame_size); - - // Handle ALSA errors with robust recovery strategies - if (pcm_rc < 0) { - if (pcm_rc == -EPIPE) { - // Buffer underrun - implement progressive recovery - recovery_attempts++; - if (recovery_attempts > max_recovery_attempts) { - return -1; // Give up after max attempts - } - - // Try to recover with prepare - err = snd_pcm_prepare(pcm_handle); - if (err < 0) { - // If prepare fails, try drop and prepare - snd_pcm_drop(pcm_handle); - err = snd_pcm_prepare(pcm_handle); - if (err < 0) return -1; - } - - // Wait before retry to allow device to stabilize - usleep(sleep_microseconds * recovery_attempts); - goto retry_read; - } else if (pcm_rc == -EAGAIN) { - // No data available - return 0 to indicate no frame - return 0; - } else if (pcm_rc == -ESTRPIPE) { - // Device suspended, implement robust resume logic - recovery_attempts++; - if (recovery_attempts > max_recovery_attempts) { - return -1; - } - - // Try to resume with timeout - int resume_attempts = 0; - while ((err = snd_pcm_resume(pcm_handle)) == -EAGAIN && resume_attempts < 10) { - usleep(sleep_microseconds); - resume_attempts++; - } - if (err < 0) { - // Resume failed, try prepare as fallback - err = snd_pcm_prepare(pcm_handle); - if (err < 0) return -1; - } - // Wait before retry to allow device to stabilize - usleep(sleep_microseconds * recovery_attempts); - return 0; // Skip this frame but don't fail - } else if (pcm_rc == -ENODEV) { - // Device disconnected - critical error - return -1; - } else if (pcm_rc == -EIO) { - // I/O error - try recovery once - recovery_attempts++; - if (recovery_attempts <= max_recovery_attempts) { - snd_pcm_drop(pcm_handle); - err = snd_pcm_prepare(pcm_handle); - if (err >= 0) { - usleep(sleep_microseconds); - goto retry_read; - } - } - return -1; - } else { - // Other errors - limited retry for transient issues - recovery_attempts++; - if (recovery_attempts <= 1 && (pcm_rc == -EINTR || pcm_rc == -EBUSY)) { - usleep(sleep_microseconds / 2); - goto retry_read; - } - return -1; - } - } - - // If we got fewer frames than expected, pad with silence - if (pcm_rc < frame_size) { - memset(&pcm_buffer[pcm_rc * channels], 0, (frame_size - pcm_rc) * channels * sizeof(short)); - } - - int nb_bytes = opus_encode(encoder, pcm_buffer, frame_size, out, max_packet_size); - return nb_bytes; -} - -// Initialize ALSA playback with improved safety -int jetkvm_audio_playback_init() { - int err; - - // Prevent concurrent initialization - if (__sync_bool_compare_and_swap(&playback_initializing, 0, 1) == 0) { - return -EBUSY; // Already initializing - } - - // Check if already initialized - if (playback_initialized) { - playback_initializing = 0; - return 0; - } - - // Clean up any existing resources first - if (decoder) { - opus_decoder_destroy(decoder); - decoder = NULL; - } - if (pcm_playback_handle) { - snd_pcm_close(pcm_playback_handle); - pcm_playback_handle = NULL; - } - - // Try to open the USB gadget audio device for playback - err = safe_alsa_open(&pcm_playback_handle, "hw:1,0", SND_PCM_STREAM_PLAYBACK); - if (err < 0) { - // Fallback to default device - err = safe_alsa_open(&pcm_playback_handle, "default", SND_PCM_STREAM_PLAYBACK); - if (err < 0) { - playback_initializing = 0; - return -1; - } - } - - // Configure the device - err = configure_alsa_device(pcm_playback_handle, "playback"); - if (err < 0) { - snd_pcm_close(pcm_playback_handle); - pcm_playback_handle = NULL; - playback_initializing = 0; - return -1; - } - - // Initialize Opus decoder - int opus_err = 0; - decoder = opus_decoder_create(sample_rate, channels, &opus_err); - if (!decoder || opus_err != OPUS_OK) { - snd_pcm_close(pcm_playback_handle); - pcm_playback_handle = NULL; - playback_initializing = 0; - return -2; - } - - playback_initialized = 1; - playback_initializing = 0; - return 0; -} - -// jetkvm_audio_decode_write decodes Opus data and writes PCM to ALSA playback device -// with error recovery and packet loss concealment -int jetkvm_audio_decode_write(void *opus_buf, int opus_size) { - short pcm_buffer[1920]; // max 2ch*960 - unsigned char *in = (unsigned char*)opus_buf; - int err = 0; - int recovery_attempts = 0; - const int max_recovery_attempts = 3; - - // Safety checks - if (!playback_initialized || !pcm_playback_handle || !decoder || !opus_buf || opus_size <= 0) { - return -1; - } - - // Additional bounds checking - if (opus_size > max_packet_size) { - return -1; - } - - // Decode Opus to PCM with error handling - int pcm_frames = opus_decode(decoder, in, opus_size, pcm_buffer, frame_size, 0); - if (pcm_frames < 0) { - // Try packet loss concealment on decode error - pcm_frames = opus_decode(decoder, NULL, 0, pcm_buffer, frame_size, 0); - if (pcm_frames < 0) return -1; - } - -retry_write: - ; - // Write PCM to playback device with robust recovery - int pcm_rc = snd_pcm_writei(pcm_playback_handle, pcm_buffer, pcm_frames); - if (pcm_rc < 0) { - if (pcm_rc == -EPIPE) { - // Buffer underrun - implement progressive recovery - recovery_attempts++; - if (recovery_attempts > max_recovery_attempts) { - return -2; - } - - // Try to recover with prepare - err = snd_pcm_prepare(pcm_playback_handle); - if (err < 0) { - // If prepare fails, try drop and prepare - snd_pcm_drop(pcm_playback_handle); - err = snd_pcm_prepare(pcm_playback_handle); - if (err < 0) return -2; - } - - // Wait before retry to allow device to stabilize - usleep(sleep_microseconds * recovery_attempts); - goto retry_write; - } else if (pcm_rc == -ESTRPIPE) { - // Device suspended, implement robust resume logic - recovery_attempts++; - if (recovery_attempts > max_recovery_attempts) { - return -2; - } - - // Try to resume with timeout - int resume_attempts = 0; - while ((err = snd_pcm_resume(pcm_playback_handle)) == -EAGAIN && resume_attempts < 10) { - usleep(sleep_microseconds); - resume_attempts++; - } - if (err < 0) { - // Resume failed, try prepare as fallback - err = snd_pcm_prepare(pcm_playback_handle); - if (err < 0) return -2; - } - // Wait before retry to allow device to stabilize - usleep(sleep_microseconds * recovery_attempts); - return 0; // Skip this frame but don't fail - } else if (pcm_rc == -ENODEV) { - // Device disconnected - critical error - return -2; - } else if (pcm_rc == -EIO) { - // I/O error - try recovery once - recovery_attempts++; - if (recovery_attempts <= max_recovery_attempts) { - snd_pcm_drop(pcm_playback_handle); - err = snd_pcm_prepare(pcm_playback_handle); - if (err >= 0) { - usleep(sleep_microseconds); - goto retry_write; - } - } - return -2; - } else if (pcm_rc == -EAGAIN) { - // Device not ready - brief wait and retry - recovery_attempts++; - if (recovery_attempts <= max_recovery_attempts) { - usleep(sleep_microseconds / 4); - goto retry_write; - } - return -2; - } else { - // Other errors - limited retry for transient issues - recovery_attempts++; - if (recovery_attempts <= 1 && (pcm_rc == -EINTR || pcm_rc == -EBUSY)) { - usleep(sleep_microseconds / 2); - goto retry_write; - } - return -2; - } - } - - return pcm_frames; -} - -// Safe playback cleanup with double-close protection -void jetkvm_audio_playback_close() { - // Wait for any ongoing operations to complete - while (playback_initializing) { - usleep(sleep_microseconds); // Use centralized constant - } - - // Atomic check and set to prevent double cleanup - if (__sync_bool_compare_and_swap(&playback_initialized, 1, 0) == 0) { - return; // Already cleaned up - } - - if (decoder) { - opus_decoder_destroy(decoder); - decoder = NULL; - } - if (pcm_playback_handle) { - snd_pcm_drain(pcm_playback_handle); - snd_pcm_close(pcm_playback_handle); - pcm_playback_handle = NULL; - } -} - -// Safe capture cleanup -void jetkvm_audio_close() { - // Wait for any ongoing operations to complete - while (capture_initializing) { - usleep(sleep_microseconds); // Use centralized constant - } - - capture_initialized = 0; - - if (encoder) { - opus_encoder_destroy(encoder); - encoder = NULL; - } - if (pcm_handle) { - snd_pcm_drop(pcm_handle); // Drop pending samples - snd_pcm_close(pcm_handle); - pcm_handle = NULL; - } - - // Also clean up playback - jetkvm_audio_playback_close(); -} +#include "c/audio.c" */ import "C"