diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs
index 9b522f05..7ffca4a5 100644
--- a/packages/core/cyclist.mjs
+++ b/packages/core/cyclist.mjs
@@ -10,28 +10,35 @@ import { logger } from './logger.mjs';
export class Cyclist {
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) {
this.started = false;
- this.cps = 1; // TODO
- this.phase = 0;
- this.getTime = getTime;
+ this.cps = 1;
+ this.lastTick = 0; // absolute time when last tick (clock callback) happened
+ this.lastBegin = 0; // query begin of last tick
+ this.lastEnd = 0; // query end of last tick
+ this.getTime = getTime; // get absolute time
this.onToggle = onToggle;
- this.latency = latency;
+ this.latency = latency; // fixed trigger time offset
const round = (x) => Math.round(x * 1000) / 1000;
this.clock = createClock(
getTime,
+ // called slightly before each cycle
(phase, duration, tick) => {
if (tick === 0) {
this.origin = phase;
}
- const begin = round(phase - this.origin);
- this.phase = begin - latency;
- const end = round(begin + duration);
- const time = getTime();
try {
- const haps = this.pattern.queryArc(begin, end); // get Haps
+ const time = getTime();
+ const begin = this.lastEnd;
+ this.lastBegin = begin;
+ const end = round(begin + duration * this.cps);
+ this.lastEnd = end;
+ const haps = this.pattern.queryArc(begin, end);
+ const tickdeadline = phase - time; // time left till phase begins
+ this.lastTick = time + tickdeadline;
+
haps.forEach((hap) => {
if (hap.part.begin.equals(hap.whole.begin)) {
- const deadline = hap.whole.begin + this.origin - time + latency;
- const duration = hap.duration * 1;
+ const deadline = (hap.whole.begin - begin) / this.cps + tickdeadline + latency;
+ const duration = hap.duration / this.cps;
onTrigger?.(hap, deadline, duration);
}
});
@@ -39,15 +46,13 @@ export class Cyclist {
logger(`[cyclist] error: ${e.message}`);
onError?.(e);
}
- }, // called slightly before each cycle
+ },
interval, // duration of each cycle
);
}
- getPhase() {
- return this.getTime() - this.origin - this.latency;
- }
now() {
- return this.getTime() - this.origin + this.clock.minLatency;
+ const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration;
+ return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency;
}
setStarted(v) {
this.started = v;
diff --git a/packages/core/zyklus.mjs b/packages/core/zyklus.mjs
index e66d8e2e..3d25b054 100644
--- a/packages/core/zyklus.mjs
+++ b/packages/core/zyklus.mjs
@@ -44,6 +44,6 @@ function createClock(
};
const getPhase = () => phase;
// setCallback
- return { setDuration, start, stop, pause, duration, getPhase, minLatency };
+ return { setDuration, start, stop, pause, duration, interval, getPhase, minLatency };
}
export default createClock;
diff --git a/packages/react/src/hooks/useHighlighting.mjs b/packages/react/src/hooks/useHighlighting.mjs
index 6e336586..4ce2a936 100644
--- a/packages/react/src/hooks/useHighlighting.mjs
+++ b/packages/react/src/hooks/useHighlighting.mjs
@@ -1,5 +1,6 @@
import { useEffect, useRef } from 'react';
import { setHighlights } from '../components/CodeMirror6';
+const round = (x) => Math.round(x * 1000) / 1000;
function useHighlighting({ view, pattern, active, getTime }) {
const highlights = useRef([]);
@@ -14,7 +15,7 @@ function useHighlighting({ view, pattern, active, getTime }) {
// force min framerate of 10 fps => fixes crash on tab refocus, where lastEnd could be far away
// see https://github.com/tidalcycles/strudel/issues/108
const begin = Math.max(lastEnd.current ?? audioTime, audioTime - 1 / 10, -0.01); // negative time seems buggy
- const span = [begin, audioTime + 1 / 60];
+ const span = [round(begin), round(audioTime + 1 / 60)];
lastEnd.current = span[1];
highlights.current = highlights.current.filter((hap) => hap.whole.end > audioTime); // keep only highlights that are still active
const haps = pattern.queryArc(...span).filter((hap) => hap.hasOnset());
diff --git a/website/src/repl/Footer.jsx b/website/src/repl/Footer.jsx
index 07bdd386..6a9ea876 100644
--- a/website/src/repl/Footer.jsx
+++ b/website/src/repl/Footer.jsx
@@ -92,7 +92,7 @@ export function Footer({ context }) {
{activeFooter === 'console' &&