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
This commit is contained in:
Alex P 2025-11-25 06:51:04 +02:00
parent 3d1c2a13b9
commit d42024b024
4 changed files with 13 additions and 14 deletions

View File

@ -160,7 +160,7 @@ func startInputAudioUnderMutex(alsaPlaybackDevice string) error {
} }
newSource := audio.NewCgoInputSource(alsaPlaybackDevice, getAudioConfig()) newSource := audio.NewCgoInputSource(alsaPlaybackDevice, getAudioConfig())
newRelay := audio.NewInputRelay(&newSource) newRelay := audio.NewInputRelay()
if err := newRelay.Start(); err != nil { if err := newRelay.Start(); err != nil {
audioLogger.Error().Err(err).Str("alsaPlaybackDevice", alsaPlaybackDevice).Msg("Failed to start input relay") 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): case <-time.After(5 * time.Second):
audioLogger.Error().Msg("Audio output start timed out after 5 seconds") audioLogger.Error().Msg("Audio output start timed out after 5 seconds")
audioOutputEnabled.Store(false) // Revert state on timeout 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") 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): case <-time.After(5 * time.Second):
audioLogger.Error().Msg("Audio input start timed out after 5 seconds") audioLogger.Error().Msg("Audio input start timed out after 5 seconds")
audioInputEnabled.Store(false) // Revert state on timeout 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") return fmt.Errorf("audio input start timed out after 5 seconds")
} }
} }

View File

@ -88,7 +88,7 @@ static uint16_t max_packet_size = 1500;
#define OPUS_VBR 1 // Variable bitrate mode enabled #define OPUS_VBR 1 // Variable bitrate mode enabled
#define OPUS_VBR_CONSTRAINT 1 // Constrained VBR maintains bitrate ceiling #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_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) #define OPUS_LSB_DEPTH 16 // 16-bit PCM sample depth (S16_LE format)
static uint8_t opus_dtx_enabled = 1; 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); fprintf(stdout, "INFO: playback: FEC recovered %d frames\n", pcm_frames);
fflush(stdout); fflush(stdout);
} else { } else {
fprintf(stderr, "WARN: playback: FEC returned 0 frames (silence)\n"); pthread_mutex_unlock(&playback_mutex);
fflush(stderr); 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: retry_write:
if (__builtin_expect(atomic_load(&playback_stop_requested), 0)) { if (__builtin_expect(atomic_load(&playback_stop_requested), 0)) {
pthread_mutex_unlock(&playback_mutex); pthread_mutex_unlock(&playback_mutex);

View File

@ -142,21 +142,14 @@ func (r *OutputRelay) relayLoop() {
} }
type InputRelay struct { type InputRelay struct {
source *AudioSource
ctx context.Context
cancel context.CancelFunc
logger zerolog.Logger logger zerolog.Logger
running atomic.Bool running atomic.Bool
} }
func NewInputRelay(source *AudioSource) *InputRelay { func NewInputRelay() *InputRelay {
ctx, cancel := context.WithCancel(context.Background())
logger := logging.GetDefaultLogger().With().Str("component", "audio-input-relay").Logger() logger := logging.GetDefaultLogger().With().Str("component", "audio-input-relay").Logger()
return &InputRelay{ return &InputRelay{
source: source,
ctx: ctx,
cancel: cancel,
logger: logger, logger: logger,
} }
} }
@ -175,6 +168,5 @@ func (r *InputRelay) Stop() {
return return
} }
r.cancel()
r.logger.Debug().Msg("input relay stopped") r.logger.Debug().Msg("input relay stopped")
} }

View File

@ -20,7 +20,7 @@ func DefaultAudioConfig() AudioConfig {
BufferPeriods: 12, BufferPeriods: 12,
DTXEnabled: true, DTXEnabled: true,
FECEnabled: true, FECEnabled: true,
PacketLossPerc: 0, PacketLossPerc: 20,
} }
} }