From 9c72db913b272560fd6cdae4e147040650fe3013 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 7 Oct 2025 01:38:42 +0300 Subject: [PATCH] feat: Optimize audio quality and default to USB audio Audio quality improvements: - Enable constrained VBR to prevent bitrate starvation at low volumes - Increase Opus complexity from 2 to 5 for better quality - Enable DTX for bandwidth optimization - Enable FEC (Forward Error Correction) - Add DTX and FEC signaling in SDP (usedtx=1;useinbandfec=1) Default configuration changes: - Change default audio output source from HDMI to USB - Enable USB Audio device by default - USB audio works on current stable image (HDMI requires newer device tree) These changes fix crackling issues at low volumes and provide better overall audio quality for both USB and HDMI audio paths. --- config.go | 3 ++- internal/audio/c/audio.c | 12 ++++++------ ui/src/components/popovers/AudioPopover.tsx | 2 +- ui/src/routes/devices.$id.tsx | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/config.go b/config.go index 2ae9d8bb..b6273836 100644 --- a/config.go +++ b/config.go @@ -160,10 +160,11 @@ var defaultConfig = &Config{ RelativeMouse: true, Keyboard: true, MassStorage: true, + Audio: true, }, NetworkConfig: &network.NetworkConfig{}, DefaultLogLevel: "INFO", - AudioOutputSource: "hdmi", + AudioOutputSource: "usb", } var ( diff --git a/internal/audio/c/audio.c b/internal/audio/c/audio.c index 0f38f596..803737dc 100644 --- a/internal/audio/c/audio.c +++ b/internal/audio/c/audio.c @@ -55,20 +55,20 @@ static uint8_t channels = 2; static uint16_t frame_size = 960; // 20ms frames at 48kHz static uint32_t opus_bitrate = 128000; -static uint8_t opus_complexity = 5; // Higher complexity for better quality on RV1106 +static uint8_t opus_complexity = 5; // Higher complexity for better quality static uint16_t max_packet_size = 1500; // Opus encoder constants (hardcoded for production) #define OPUS_VBR 1 // VBR enabled -#define OPUS_VBR_CONSTRAINT 0 // Unconstrained VBR (better for low-volume signals) +#define OPUS_VBR_CONSTRAINT 1 // Constrained VBR (prevents bitrate starvation at low volumes) #define OPUS_SIGNAL_TYPE 3002 // OPUS_SIGNAL_MUSIC (better transient handling) -#define OPUS_BANDWIDTH 1104 // OPUS_BANDWIDTH_SUPERWIDEBAND (16kHz, better quality at 128kbps) -#define OPUS_DTX 0 // DTX disabled (prevents audio drops) +#define OPUS_BANDWIDTH 1104 // OPUS_BANDWIDTH_SUPERWIDEBAND (16kHz) +#define OPUS_DTX 1 // DTX enabled (bandwidth optimization) #define OPUS_LSB_DEPTH 16 // 16-bit depth // ALSA retry configuration static uint32_t sleep_microseconds = 1000; -static uint32_t sleep_milliseconds = 1; // Precomputed: sleep_microseconds / 1000 +static uint32_t sleep_milliseconds = 1; static uint8_t max_attempts_global = 5; static uint32_t max_backoff_us_global = 500000; @@ -373,7 +373,7 @@ int jetkvm_audio_capture_init() { opus_encoder_ctl(encoder, OPUS_SET_DTX(OPUS_DTX)); opus_encoder_ctl(encoder, OPUS_SET_LSB_DEPTH(OPUS_LSB_DEPTH)); - opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(0)); + opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1)); opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(20)); capture_initialized = 1; diff --git a/ui/src/components/popovers/AudioPopover.tsx b/ui/src/components/popovers/AudioPopover.tsx index 55261ef5..a3c114eb 100644 --- a/ui/src/components/popovers/AudioPopover.tsx +++ b/ui/src/components/popovers/AudioPopover.tsx @@ -10,7 +10,7 @@ import notifications from "@/notifications"; export default function AudioPopover() { const { send } = useJsonRpc(); - const [audioOutputSource, setAudioOutputSource] = useState("hdmi"); + const [audioOutputSource, setAudioOutputSource] = useState("usb"); const [audioOutputEnabled, setAudioOutputEnabled] = useState(true); const [audioInputEnabled, setAudioInputEnabled] = useState(true); const [usbAudioEnabled, setUsbAudioEnabled] = useState(false); diff --git a/ui/src/routes/devices.$id.tsx b/ui/src/routes/devices.$id.tsx index 51491a42..ef9e0eb3 100644 --- a/ui/src/routes/devices.$id.tsx +++ b/ui/src/routes/devices.$id.tsx @@ -191,7 +191,7 @@ export default function KvmIdRoute() { console.warn("[SDP] Opus 48kHz stereo not found in answer - stereo may not work"); } else { const pt = opusMatch[1]; - const stereoParams = 'stereo=1;sprop-stereo=1;maxaveragebitrate=128000'; + const stereoParams = 'stereo=1;sprop-stereo=1;maxaveragebitrate=128000;usedtx=1;useinbandfec=1'; const fmtpRegex = new RegExp(`a=fmtp:${pt}\\s+(.+)`, 'i'); const fmtpMatch = remoteDescription.sdp.match(fmtpRegex); @@ -463,7 +463,7 @@ export default function KvmIdRoute() { console.warn("[SDP] Opus 48kHz stereo not found in offer - stereo may not work"); } else { const pt = opusMatch[1]; - const stereoParams = 'stereo=1;sprop-stereo=1;maxaveragebitrate=128000'; + const stereoParams = 'stereo=1;sprop-stereo=1;maxaveragebitrate=128000;usedtx=1;useinbandfec=1'; const fmtpRegex = new RegExp(`a=fmtp:${pt}\\s+(.+)`, 'i'); const fmtpMatch = offer.sdp.match(fmtpRegex);