Clamp high precision timer to specific tick drift

This commit is contained in:
Manevolent
2019-01-22 11:50:03 -05:00
parent 3e8991929e
commit 0fed4c68ce
3 changed files with 44 additions and 25 deletions
@@ -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<AudioFrame> 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);
}
@@ -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()
);
@@ -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 gracefulStop() {
if (!this.stop) {
this.stop = true;
//if (isAlive()) interrupt();
}
public void setStop(boolean stop) {
this.stop = stop;
}
@Override
@SuppressWarnings(value = "empty-statement")
public void run() {
long nextTarget = 0;
while (!stop) {
long now = System.nanoTime();
long nextTarget = now;
while (!stop) {
target.run();
long total = System.nanoTime() - now;
nextTarget = now + periodInNanos - total;
sleepFor(periodInNanos - total - spinningTimeNanos);
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;
}
}