diff --git a/.devcontainer/install_audio_deps.sh b/.devcontainer/install_audio_deps.sh index a4a6d6d5..0a3d8043 100755 --- a/.devcontainer/install_audio_deps.sh +++ b/.devcontainer/install_audio_deps.sh @@ -48,7 +48,7 @@ if [ ! -f .built ]; then # Use minimal ALSA configuration to avoid FD_SETSIZE issues in devcontainer CFLAGS="$OPTIM_CFLAGS" ./configure --host $BUILDKIT_FLAVOR \ --enable-static=yes --enable-shared=no \ - --with-pcm-plugins=rate,linear \ + --with-pcm-plugins=plug,rate,linear,copy \ --disable-seq --disable-rawmidi --disable-ucm \ --disable-python --disable-old-symbols \ --disable-topology --disable-hwdep --disable-mixer \ diff --git a/audio.go b/audio.go index 15bb7a94..efbcc6dc 100644 --- a/audio.go +++ b/audio.go @@ -30,9 +30,9 @@ var ( func getAlsaDevice(source string) string { if source == "hdmi" { - return "hw:0,0" + return "plughw:0,0" } - return "hw:1,0" + return "plughw:1,0" } func initAudio() { diff --git a/internal/audio/c/audio.c b/internal/audio/c/audio.c index 70bf093c..8ea6c04c 100644 --- a/internal/audio/c/audio.c +++ b/internal/audio/c/audio.c @@ -3,16 +3,16 @@ * * Bidirectional audio processing optimized for ARM NEON SIMD: * - OUTPUT PATH: TC358743 HDMI or USB Gadget audio → Client speakers - * Pipeline: ALSA hw:0,0 or hw:1,0 capture → Opus encode (192kbps, FEC enabled) + * Pipeline: ALSA plughw:0,0 or plughw:1,0 capture → Opus encode (192kbps, FEC enabled) * * - INPUT PATH: Client microphone → Device speakers - * Pipeline: Opus decode (with FEC) → ALSA hw:1,0 playback + * Pipeline: Opus decode (with FEC) → ALSA plughw:1,0 playback * * Key features: * - ARM NEON SIMD optimization for all audio operations * - Opus in-band FEC for packet loss resilience * - S16_LE stereo, 20ms frames (sample rate configurable: 8k/12k/16k/24k/48kHz) - * - ALSA rate plugin resamples hardware output to match requested Opus-compatible rate + * - ALSA plughw layer provides automatic rate conversion from hardware to Opus rate */ #include @@ -140,19 +140,19 @@ void update_audio_decoder_constants(uint32_t sr, uint8_t ch, uint16_t fs, uint16 * Must be called before jetkvm_audio_capture_init or jetkvm_audio_playback_init * * Device mapping (set via ALSA_CAPTURE_DEVICE/ALSA_PLAYBACK_DEVICE): - * hw:0,0 = TC358743 HDMI audio input (for OUTPUT path capture) - * hw:1,0 = USB Audio Gadget (for OUTPUT path capture or INPUT path playback) + * plughw:0,0 = TC358743 HDMI audio with rate conversion (for OUTPUT path capture) + * plughw:1,0 = USB Audio Gadget with rate conversion (for OUTPUT path capture or INPUT path playback) */ static void init_alsa_devices_from_env(void) { // Always read from environment to support device switching alsa_capture_device = getenv("ALSA_CAPTURE_DEVICE"); if (alsa_capture_device == NULL || alsa_capture_device[0] == '\0') { - alsa_capture_device = "hw:1,0"; // Default: USB gadget audio for capture + alsa_capture_device = "plughw:1,0"; // Default: USB gadget audio for capture with rate conversion } alsa_playback_device = getenv("ALSA_PLAYBACK_DEVICE"); if (alsa_playback_device == NULL || alsa_playback_device[0] == '\0') { - alsa_playback_device = "hw:1,0"; // Default: USB gadget audio for playback + alsa_playback_device = "plughw:1,0"; // Default: USB gadget audio for playback with rate conversion } } @@ -430,7 +430,7 @@ static int configure_alsa_device(snd_pcm_t *handle, const char *device_name, uin /** * Initialize OUTPUT path (HDMI or USB Gadget audio capture → Opus encoder) - * Opens ALSA capture device from ALSA_CAPTURE_DEVICE env (default: hw:1,0, set to hw:0,0 for TC358743 HDMI) + * Opens ALSA capture device from ALSA_CAPTURE_DEVICE env (default: plughw:1,0, set to plughw:0,0 for HDMI) * and creates Opus encoder with optimized settings * @return 0 on success, -EBUSY if initializing, -1/-2/-3 on errors */ @@ -606,7 +606,7 @@ retry_read: /** * Initialize INPUT path (Opus decoder → device speakers) - * Opens ALSA playback device from ALSA_PLAYBACK_DEVICE env (default: hw:1,0) + * Opens ALSA playback device from ALSA_PLAYBACK_DEVICE env (default: plughw:1,0) * and creates Opus decoder. Returns immediately on device open failure (no fallback). * @return 0 on success, -EBUSY if initializing, -1/-2 on errors */ diff --git a/internal/audio/cgo_source.go b/internal/audio/cgo_source.go index 29a4a577..1f6e0905 100644 --- a/internal/audio/cgo_source.go +++ b/internal/audio/cgo_source.go @@ -83,10 +83,13 @@ func (c *CgoSource) Connect() error { func (c *CgoSource) connectOutput() error { os.Setenv("ALSA_CAPTURE_DEVICE", c.alsaDevice) - // USB Audio Gadget (hw:1,0) only supports 48kHz - // For HDMI (hw:0,0), use configured sample rate + // Using plughw: enables ALSA rate conversion plugin + // USB Gadget hardware is fixed at 48kHz (configfs hardcoded), so keep it at 48kHz + // HDMI can use configured rate - plughw resamples from hardware rate to Opus rate sampleRate := c.config.SampleRate - if c.alsaDevice == "hw:1,0" { + if c.alsaDevice == "plughw:1,0" { + sampleRate = 48000 + } else if sampleRate == 0 { sampleRate = 48000 } frameSize := uint16(sampleRate * 20 / 1000)