diff --git a/examples/audio/src/main/java/com/github/manevolent/ts3j/examples/audio/MusicPlayer.java b/examples/audio/src/main/java/com/github/manevolent/ts3j/examples/audio/MusicPlayer.java index 3a7bc7a..2c21829 100644 --- a/examples/audio/src/main/java/com/github/manevolent/ts3j/examples/audio/MusicPlayer.java +++ b/examples/audio/src/main/java/com/github/manevolent/ts3j/examples/audio/MusicPlayer.java @@ -93,6 +93,9 @@ public class MusicPlayer { long wake = System.nanoTime(); long delay = 150 * 1_000_000; // 50ms interval long sleep; + long start = System.nanoTime(); + double volume = 0.5D; + while (true) { int available = sink.availableInput(); @@ -101,6 +104,9 @@ public class MusicPlayer { if (frameQueue.peek() == null) { try { AudioFrame frame = audioSourceSubstream.next(); + for (int i = 0; i < frame.getLength(); i ++) + frame.getSamples()[i] *= volume; + Collection frameList = resampleFilter.apply(frame); frameQueue.addAll(frameList); } catch (EOFException ex) { @@ -130,6 +136,7 @@ public class MusicPlayer { wake += delay; sleep = (wake - System.nanoTime()) / 1_000_000; + System.err.println(((double)(System.nanoTime() - start) / 1_000_000_000D) + " " + (sink.getPacketsSent() / 50D) + " " + sink.getUnderflows()); if (sleep > 0) Thread.sleep(sleep); } 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 5ef448b..a69ce0d 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 @@ -24,7 +24,7 @@ import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHand import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandlerRetrievingData; import com.github.manevolent.ts3j.protocol.packet.statistics.PacketStatistics; import com.github.manevolent.ts3j.protocol.packet.transformation.InitPacketTransformation; -import com.github.manevolent.ts3j.util.HighPrecisionRecurrentTask; +import com.github.manevolent.ts3j.util.HighPrecisionTimer; import com.github.manevolent.ts3j.util.Ts3Debugging; import com.github.manevolent.ts3j.util.Pair; @@ -387,9 +387,9 @@ public class LocalTeamspeakClientSocket waitForState(ClientConnectionState.CONNECTED, timeout); - microphoneThread = new HighPrecisionRecurrentTask( + microphoneThread = new HighPrecisionTimer( 20, - 0.01F, + 5F, // up to 100ms of drift/catchup new MicrophoneTask() ); diff --git a/src/main/java/com/github/manevolent/ts3j/util/HighPrecisionRecurrentTask.java b/src/main/java/com/github/manevolent/ts3j/util/HighPrecisionTimer.java similarity index 63% rename from src/main/java/com/github/manevolent/ts3j/util/HighPrecisionRecurrentTask.java rename to src/main/java/com/github/manevolent/ts3j/util/HighPrecisionTimer.java index 6b3114c..8c04423 100644 --- a/src/main/java/com/github/manevolent/ts3j/util/HighPrecisionRecurrentTask.java +++ b/src/main/java/com/github/manevolent/ts3j/util/HighPrecisionTimer.java @@ -23,52 +23,60 @@ import java.util.concurrent.locks.LockSupport; * * Time of execution is held in account when calculating the time to sleep until the next execution. */ -public class HighPrecisionRecurrentTask extends Thread { - +public class HighPrecisionTimer extends Thread { private final int periodInNanos; - private final int spinningTimeNanos; + private final int maximumDriftInNanos; private final Runnable target; private volatile boolean stop; /** * @param periodInMilis The period in milliseconds of the task execution. - * @param percentageOfSleepSpinning The percentage of total sleeping that should be done spinning to increase accuracy. + * @param catchupTicks The number of ticks allowed to consecutively catch up to * @param target The task to run. */ - public HighPrecisionRecurrentTask(int periodInMilis, float percentageOfSleepSpinning, Runnable target) { + public HighPrecisionTimer(int periodInMilis, + float catchupTicks, + Runnable target) { super("TS3J AudioThread"); - if (percentageOfSleepSpinning < 0 || percentageOfSleepSpinning > 1) - throw new IllegalArgumentException("percentageOfSleepSpinning < 0 | percentageOfSleepSpinning > 1"); + this.periodInNanos = periodInMilis * 1_000_000; - this.spinningTimeNanos = (int) (periodInNanos * percentageOfSleepSpinning); + this.maximumDriftInNanos = (int) Math.floor((double)periodInNanos * (double)catchupTicks); this.target = target; } - public boolean isStop() { - return stop; - } - - public void setStop(boolean stop) { - this.stop = stop; + public void gracefulStop() { + if (!this.stop) { + this.stop = true; + //if (isAlive()) interrupt(); + } } @Override @SuppressWarnings(value = "empty-statement") public void run() { - long nextTarget = 0; + long now = System.nanoTime(); + long nextTarget = now; + while (!stop) { - long now = System.nanoTime(); target.run(); - long total = System.nanoTime() - now; - nextTarget = now + periodInNanos - total; - sleepFor(periodInNanos - total - spinningTimeNanos); - while (nextTarget > System.nanoTime()) { - ; //consume cycles + + nextTarget += periodInNanos; + + now = System.nanoTime(); + + // Clamp the next target to the maximum "drift" allowed + if (nextTarget - now > maximumDriftInNanos) + nextTarget = now - maximumDriftInNanos; + + if (sleepFor(nextTarget - now)) { + while (nextTarget > System.nanoTime()) { + ; //consume cycles + } } } } - private void sleepFor(long nanos) { + private boolean sleepFor(long nanos) { if (nanos > 0) { long elapsed = 0; while (elapsed < nanos) { @@ -76,7 +84,11 @@ public class HighPrecisionRecurrentTask extends Thread { LockSupport.parkNanos(nanos - elapsed); elapsed += System.nanoTime() - t0; } + + return true; } + + return false; } } \ No newline at end of file