diff --git a/src/main/java/com/github/manevolent/ts3j/audio/Microphone.java b/src/main/java/com/github/manevolent/ts3j/audio/Microphone.java index fe4e0e3..2d4c6fb 100644 --- a/src/main/java/com/github/manevolent/ts3j/audio/Microphone.java +++ b/src/main/java/com/github/manevolent/ts3j/audio/Microphone.java @@ -22,6 +22,15 @@ public interface Microphone { return 0; } + /** + * Whether {@link #provide} hands out frames captured on a clock of their own, such as a sound + * card's. That clock drifts against the send timer, so each tick then sends every frame that is + * ready instead of one, keeping the drift from piling up as latency or overflowing a queue. + */ + default boolean isLiveCapture() { + return false; + } + /** How many copies follow each voice packet, {@link #getRedundancyDelay} apart. */ default int getRedundancyCopies() { return 1; diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java index 7911e9e..00656c7 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java @@ -596,6 +596,7 @@ public class LocalTeamspeakClientSocket new MicrophoneTask() ); microphoneThread.setDaemon(true); + microphoneThread.setPriority(Thread.MAX_PRIORITY); if (getState() != ClientConnectionState.DISCONNECTED) microphoneThread.start(); @@ -693,26 +694,30 @@ public class LocalTeamspeakClientSocket // TODO // Set a state and send a muted event } else if (microphone.isReady()) { - byte[] data = microphone.provide(); - if (data.length <= 0) - return; + boolean live = microphone.isLiveCapture(); + byte[] data; + do { + data = microphone.provide(); + if (data.length <= 0) break; - PacketBody0Voice voice = new PacketBody0Voice(getRole().getOut()); + PacketBody0Voice voice = new PacketBody0Voice(getRole().getOut()); - voice.setCodecType(microphone.getCodec()); - voice.setCodecData(data); - if (flaggedPackets > 0) voice.setServerFlag0(sessionId); + voice.setCodecType(microphone.getCodec()); + voice.setCodecData(data); + if (flaggedPackets > 0) voice.setServerFlag0(sessionId); - try { - writePacket(voice); + try { + writePacket(voice); - flaggedPackets--; - wasSendingAudio = true; - } catch (Exception e) { - // All we are really concerned about here is a disconnection event, in which case the loop - // will exit. - getExceptionHandler().accept(e); - } + flaggedPackets--; + wasSendingAudio = true; + } catch (Exception e) { + // All we are really concerned about here is a disconnection event, in which case the loop + // will exit. + getExceptionHandler().accept(e); + return; + } + } while (live && microphone.isReady()); } else if (wasSendingAudio) { PacketBody0Voice voice = new PacketBody0Voice(getRole().getOut()); diff --git a/src/main/java/com/github/manevolent/ts3j/util/HighPrecisionTimer.java b/src/main/java/com/github/manevolent/ts3j/util/HighPrecisionTimer.java index 8c04423..af374e8 100644 --- a/src/main/java/com/github/manevolent/ts3j/util/HighPrecisionTimer.java +++ b/src/main/java/com/github/manevolent/ts3j/util/HighPrecisionTimer.java @@ -64,8 +64,8 @@ public class HighPrecisionTimer extends Thread { now = System.nanoTime(); - // Clamp the next target to the maximum "drift" allowed - if (nextTarget - now > maximumDriftInNanos) + // After a stall, catch up at most the allowed number of ticks rather than all of them + if (now - nextTarget > maximumDriftInNanos) nextTarget = now - maximumDriftInNanos; if (sleepFor(nextTarget - now)) {