From ed526897dc5368ddeb5baedb4729f9d8e2401a15 Mon Sep 17 00:00:00 2001 From: ericek111 Date: Fri, 25 Sep 2026 18:30:23 +0000 Subject: [PATCH] Switch audio devices without reopening the connection A new input device used to wait for the next connect, and the shared mixer kept its line on the old output until it went idle for good. The microphone now reopens on the new device as the settings are applied, and the mixer moves its line over when the output device changes. Co-Authored-By: Claude Opus 5.5 --- .../java/com/ts3client/audio/CaptureVoiceInput.java | 13 ++++++++++++- .../main/java/com/ts3client/audio/MixedPlayout.java | 8 +++++++- .../com/ts3client/audio/StreamingVoiceOutput.java | 4 ++-- .../main/java/com/ts3client/audio/VoiceInput.java | 4 ++++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/ts3-client/core/src/main/java/com/ts3client/audio/CaptureVoiceInput.java b/ts3-client/core/src/main/java/com/ts3client/audio/CaptureVoiceInput.java index df08660..584b981 100644 --- a/ts3-client/core/src/main/java/com/ts3client/audio/CaptureVoiceInput.java +++ b/ts3-client/core/src/main/java/com/ts3client/audio/CaptureVoiceInput.java @@ -76,7 +76,7 @@ public final class CaptureVoiceInput implements VoiceInput { private final AudioIo io; private final OpusCodec opus; - private final String deviceName; + private volatile String deviceName; private final Object encoderLock = new Object(); private volatile OpusParameters params; @@ -117,6 +117,17 @@ public final class CaptureVoiceInput implements VoiceInput { // ---- live configuration (safe to call from the UI thread) ---- + @Override + public synchronized void setInputDevice(String deviceId) { + String device = deviceId == null ? "" : deviceId; + if (device.equals(deviceName)) return; + deviceName = device; + if (running.get()) { + stop(); + start(); + } + } + public void setMode(Settings.InputMode mode) { this.mode = mode; } diff --git a/ts3-client/core/src/main/java/com/ts3client/audio/MixedPlayout.java b/ts3-client/core/src/main/java/com/ts3client/audio/MixedPlayout.java index fa2ebdb..81ef5c2 100644 --- a/ts3-client/core/src/main/java/com/ts3client/audio/MixedPlayout.java +++ b/ts3-client/core/src/main/java/com/ts3client/audio/MixedPlayout.java @@ -66,9 +66,15 @@ final class MixedPlayout implements Playout { float[] mix = null; float[] scratch = null; byte[] out = null; + String device = null; while (awaitSpeech()) { + if (line != null && !device.equals(outputDevice.get())) { + line.close(); + line = null; + } if (line == null) { - line = io.openPlayback(outputDevice.get(), VoiceFormat.MAX_CHANNELS); + device = outputDevice.get(); + line = io.openPlayback(device, VoiceFormat.MAX_CHANNELS); line.start(); channels = line.channels(); mix = new float[FRAME * channels]; diff --git a/ts3-client/core/src/main/java/com/ts3client/audio/StreamingVoiceOutput.java b/ts3-client/core/src/main/java/com/ts3client/audio/StreamingVoiceOutput.java index bf69fe9..ff97e80 100644 --- a/ts3-client/core/src/main/java/com/ts3client/audio/StreamingVoiceOutput.java +++ b/ts3-client/core/src/main/java/com/ts3client/audio/StreamingVoiceOutput.java @@ -60,7 +60,7 @@ public final class StreamingVoiceOutput implements VoiceOutput { public StreamingVoiceOutput(AudioIo io, OpusCodec opus, String outputDevice, Lines lines) { this.opus = opus; - this.outputDevice = outputDevice; + this.outputDevice = outputDevice == null ? "" : outputDevice; this.playout = switch (lines) { case PER_SPEAKER -> new PerSpeakerPlayout(io, () -> this.outputDevice, this::gainFor, clock); case SHARED -> new MixedPlayout(io, () -> this.outputDevice, this::gainFor, streams.values()); @@ -137,7 +137,7 @@ public final class StreamingVoiceOutput implements VoiceOutput { @Override public void setOutputDevice(String device) { - this.outputDevice = device; + this.outputDevice = device == null ? "" : device; } /** Entry point wired into {@code client.setVoiceHandler(...)}. */ diff --git a/ts3-client/core/src/main/java/com/ts3client/audio/VoiceInput.java b/ts3-client/core/src/main/java/com/ts3client/audio/VoiceInput.java index 48b3fcd..83c963f 100644 --- a/ts3-client/core/src/main/java/com/ts3client/audio/VoiceInput.java +++ b/ts3-client/core/src/main/java/com/ts3client/audio/VoiceInput.java @@ -27,6 +27,9 @@ public interface VoiceInput extends Microphone { boolean isLocalMuted(); + /** Moves capture to another device, reopening at once if capturing; empty is the default. */ + void setInputDevice(String deviceId); + void setMode(Settings.InputMode mode); void setVadMode(Settings.VadMode mode); @@ -80,6 +83,7 @@ public interface VoiceInput extends Microphone { /** Takes on the voice settings, as they stand now, while capturing. */ default void apply(Settings settings) { + setInputDevice(settings.inputDevice); setMode(settings.inputMode); setVadMode(settings.vadMode); setThresholdDb(settings.vadThresholdDb);