From d42024b02468eaf088cde61e546c50deb01f7fcd Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 25 Nov 2025 06:51:04 +0200 Subject: [PATCH] Fix audio quality and cleanup issues in audio implementation - Fix OPUS_BANDWIDTH constant from 1104 to 1105 (fullband 20kHz vs superwideband 12kHz) - Fix PacketLossPerc default from 0 to 20 to match C layer default - Add early return in FEC recovery when pcm_frames <= 0 to avoid zero-frame ALSA writes - Remove unused InputRelay fields (source, ctx, cancel) and unused NewInputRelay parameter - Add async cleanup on timeout in SetAudioOutputEnabled/SetAudioInputEnabled --- audio.go | 4 +++- internal/audio/c/audio.c | 11 ++++++++--- internal/audio/relay.go | 10 +--------- internal/audio/source.go | 2 +- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/audio.go b/audio.go index 1f6c4163..28ac26a4 100644 --- a/audio.go +++ b/audio.go @@ -160,7 +160,7 @@ func startInputAudioUnderMutex(alsaPlaybackDevice string) error { } newSource := audio.NewCgoInputSource(alsaPlaybackDevice, getAudioConfig()) - newRelay := audio.NewInputRelay(&newSource) + newRelay := audio.NewInputRelay() if err := newRelay.Start(); err != nil { audioLogger.Error().Err(err).Str("alsaPlaybackDevice", alsaPlaybackDevice).Msg("Failed to start input relay") @@ -275,6 +275,7 @@ func SetAudioOutputEnabled(enabled bool) error { case <-time.After(5 * time.Second): audioLogger.Error().Msg("Audio output start timed out after 5 seconds") audioOutputEnabled.Store(false) // Revert state on timeout + go stopOutputAudio() // Clean up any partial initialization asynchronously return fmt.Errorf("audio output start timed out after 5 seconds") } } @@ -307,6 +308,7 @@ func SetAudioInputEnabled(enabled bool) error { case <-time.After(5 * time.Second): audioLogger.Error().Msg("Audio input start timed out after 5 seconds") audioInputEnabled.Store(false) // Revert state on timeout + go stopInputAudio() // Clean up any partial initialization asynchronously return fmt.Errorf("audio input start timed out after 5 seconds") } } diff --git a/internal/audio/c/audio.c b/internal/audio/c/audio.c index 8ea3f8da..2704340e 100644 --- a/internal/audio/c/audio.c +++ b/internal/audio/c/audio.c @@ -88,7 +88,7 @@ static uint16_t max_packet_size = 1500; #define OPUS_VBR 1 // Variable bitrate mode enabled #define OPUS_VBR_CONSTRAINT 1 // Constrained VBR maintains bitrate ceiling #define OPUS_SIGNAL_TYPE 3002 // OPUS_SIGNAL_MUSIC (optimized for music/audio content) -#define OPUS_BANDWIDTH 1104 // OPUS_BANDWIDTH_FULLBAND (0-20kHz frequency range) +#define OPUS_BANDWIDTH 1105 // OPUS_BANDWIDTH_FULLBAND (0-20kHz frequency range) #define OPUS_LSB_DEPTH 16 // 16-bit PCM sample depth (S16_LE format) static uint8_t opus_dtx_enabled = 1; @@ -1068,11 +1068,16 @@ __attribute__((hot)) int jetkvm_audio_decode_write(void * __restrict__ opus_buf, fprintf(stdout, "INFO: playback: FEC recovered %d frames\n", pcm_frames); fflush(stdout); } else { - fprintf(stderr, "WARN: playback: FEC returned 0 frames (silence)\n"); - fflush(stderr); + pthread_mutex_unlock(&playback_mutex); + return 0; // FEC returned no frames, nothing to write } } + if (__builtin_expect(pcm_frames <= 0, 0)) { + pthread_mutex_unlock(&playback_mutex); + return 0; // Nothing to write + } + retry_write: if (__builtin_expect(atomic_load(&playback_stop_requested), 0)) { pthread_mutex_unlock(&playback_mutex); diff --git a/internal/audio/relay.go b/internal/audio/relay.go index 8b42de62..5f3e8acf 100644 --- a/internal/audio/relay.go +++ b/internal/audio/relay.go @@ -142,21 +142,14 @@ func (r *OutputRelay) relayLoop() { } type InputRelay struct { - source *AudioSource - ctx context.Context - cancel context.CancelFunc logger zerolog.Logger running atomic.Bool } -func NewInputRelay(source *AudioSource) *InputRelay { - ctx, cancel := context.WithCancel(context.Background()) +func NewInputRelay() *InputRelay { logger := logging.GetDefaultLogger().With().Str("component", "audio-input-relay").Logger() return &InputRelay{ - source: source, - ctx: ctx, - cancel: cancel, logger: logger, } } @@ -175,6 +168,5 @@ func (r *InputRelay) Stop() { return } - r.cancel() r.logger.Debug().Msg("input relay stopped") } diff --git a/internal/audio/source.go b/internal/audio/source.go index fcc19e62..f3683106 100644 --- a/internal/audio/source.go +++ b/internal/audio/source.go @@ -20,7 +20,7 @@ func DefaultAudioConfig() AudioConfig { BufferPeriods: 12, DTXEnabled: true, FECEnabled: true, - PacketLossPerc: 0, + PacketLossPerc: 20, } }