Clamp high precision timer to specific tick drift
This commit is contained in:
+7
@@ -93,6 +93,9 @@ public class MusicPlayer {
|
|||||||
long wake = System.nanoTime();
|
long wake = System.nanoTime();
|
||||||
long delay = 150 * 1_000_000; // 50ms interval
|
long delay = 150 * 1_000_000; // 50ms interval
|
||||||
long sleep;
|
long sleep;
|
||||||
|
long start = System.nanoTime();
|
||||||
|
double volume = 0.5D;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int available = sink.availableInput();
|
int available = sink.availableInput();
|
||||||
|
|
||||||
@@ -101,6 +104,9 @@ public class MusicPlayer {
|
|||||||
if (frameQueue.peek() == null) {
|
if (frameQueue.peek() == null) {
|
||||||
try {
|
try {
|
||||||
AudioFrame frame = audioSourceSubstream.next();
|
AudioFrame frame = audioSourceSubstream.next();
|
||||||
|
for (int i = 0; i < frame.getLength(); i ++)
|
||||||
|
frame.getSamples()[i] *= volume;
|
||||||
|
|
||||||
Collection<AudioFrame> frameList = resampleFilter.apply(frame);
|
Collection<AudioFrame> frameList = resampleFilter.apply(frame);
|
||||||
frameQueue.addAll(frameList);
|
frameQueue.addAll(frameList);
|
||||||
} catch (EOFException ex) {
|
} catch (EOFException ex) {
|
||||||
@@ -130,6 +136,7 @@ public class MusicPlayer {
|
|||||||
|
|
||||||
wake += delay;
|
wake += delay;
|
||||||
sleep = (wake - System.nanoTime()) / 1_000_000;
|
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);
|
if (sleep > 0) Thread.sleep(sleep);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -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.handler.client.LocalClientHandlerRetrievingData;
|
||||||
import com.github.manevolent.ts3j.protocol.packet.statistics.PacketStatistics;
|
import com.github.manevolent.ts3j.protocol.packet.statistics.PacketStatistics;
|
||||||
import com.github.manevolent.ts3j.protocol.packet.transformation.InitPacketTransformation;
|
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.Ts3Debugging;
|
||||||
import com.github.manevolent.ts3j.util.Pair;
|
import com.github.manevolent.ts3j.util.Pair;
|
||||||
|
|
||||||
@@ -387,9 +387,9 @@ public class LocalTeamspeakClientSocket
|
|||||||
|
|
||||||
waitForState(ClientConnectionState.CONNECTED, timeout);
|
waitForState(ClientConnectionState.CONNECTED, timeout);
|
||||||
|
|
||||||
microphoneThread = new HighPrecisionRecurrentTask(
|
microphoneThread = new HighPrecisionTimer(
|
||||||
20,
|
20,
|
||||||
0.01F,
|
5F, // up to 100ms of drift/catchup
|
||||||
new MicrophoneTask()
|
new MicrophoneTask()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
+34
-22
@@ -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.
|
* 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 periodInNanos;
|
||||||
private final int spinningTimeNanos;
|
private final int maximumDriftInNanos;
|
||||||
private final Runnable target;
|
private final Runnable target;
|
||||||
private volatile boolean stop;
|
private volatile boolean stop;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param periodInMilis The period in milliseconds of the task execution.
|
* @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.
|
* @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");
|
super("TS3J AudioThread");
|
||||||
if (percentageOfSleepSpinning < 0 || percentageOfSleepSpinning > 1)
|
|
||||||
throw new IllegalArgumentException("percentageOfSleepSpinning < 0 | percentageOfSleepSpinning > 1");
|
|
||||||
this.periodInNanos = periodInMilis * 1_000_000;
|
this.periodInNanos = periodInMilis * 1_000_000;
|
||||||
this.spinningTimeNanos = (int) (periodInNanos * percentageOfSleepSpinning);
|
this.maximumDriftInNanos = (int) Math.floor((double)periodInNanos * (double)catchupTicks);
|
||||||
this.target = target;
|
this.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStop() {
|
public void gracefulStop() {
|
||||||
return stop;
|
if (!this.stop) {
|
||||||
}
|
this.stop = true;
|
||||||
|
//if (isAlive()) interrupt();
|
||||||
public void setStop(boolean stop) {
|
}
|
||||||
this.stop = stop;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings(value = "empty-statement")
|
@SuppressWarnings(value = "empty-statement")
|
||||||
public void run() {
|
public void run() {
|
||||||
long nextTarget = 0;
|
long now = System.nanoTime();
|
||||||
|
long nextTarget = now;
|
||||||
|
|
||||||
while (!stop) {
|
while (!stop) {
|
||||||
long now = System.nanoTime();
|
|
||||||
target.run();
|
target.run();
|
||||||
long total = System.nanoTime() - now;
|
|
||||||
nextTarget = now + periodInNanos - total;
|
nextTarget += periodInNanos;
|
||||||
sleepFor(periodInNanos - total - spinningTimeNanos);
|
|
||||||
while (nextTarget > System.nanoTime()) {
|
now = System.nanoTime();
|
||||||
; //consume cycles
|
|
||||||
|
// 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) {
|
if (nanos > 0) {
|
||||||
long elapsed = 0;
|
long elapsed = 0;
|
||||||
while (elapsed < nanos) {
|
while (elapsed < nanos) {
|
||||||
@@ -76,7 +84,11 @@ public class HighPrecisionRecurrentTask extends Thread {
|
|||||||
LockSupport.parkNanos(nanos - elapsed);
|
LockSupport.parkNanos(nanos - elapsed);
|
||||||
elapsed += System.nanoTime() - t0;
|
elapsed += System.nanoTime() - t0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user