jevstrudel.git / packages / core / zyklus.mjs

will move to https://github.com/felixroos/zyklus TODO: started flag

4function createClock(
5  getTime,
6  callback, // called slightly before each cycle
7  duration = 0.05, // duration of each cycle
8  interval = 0.1, // interval between callbacks
9  overlap = 0.1, // overlap between callbacks
10  setInterval = globalThis.setInterval,
11  clearInterval = globalThis.clearInterval,
12  round = true,
13) {
14  let tick = 0; // counts callbacks
15  let phase = 0; // next callback time
16  let precision = 10 ** 4; // used to round phase
17  let minLatency = 0.01;
18  const setDuration = (setter) => (duration = setter(duration));
19  overlap = overlap || interval / 2;
20  const onTick = () => {
21    const t = getTime();
22    const lookahead = t + interval + overlap; // the time window for this tick
23    if (phase === 0) {
24      phase = t + minLatency;
25    }
26    // callback as long as we're inside the lookahead
27    while (phase < lookahead) {
28      phase = round ? Math.round(phase * precision) / precision : phase;
29      callback(phase, duration, tick, t); // callback has to skip / handle phase < t!
30      phase += duration; // increment phase by duration
31      tick++;
32    }
33  };
34  let intervalID;
35  const start = () => {
36    clear(); // just in case start was called more than once
37    onTick();
38    intervalID = setInterval(onTick, interval * 1000);
39  };
40  const clear = () => {
41    intervalID !== undefined && clearInterval(intervalID);
42    intervalID = undefined;
43  };
44  const pause = () => clear();
45  const stop = () => {
46    tick = 0;
47    phase = 0;
48    clear();
49  };
50  const getPhase = () => phase;
51  // setCallback
52  return { setDuration, start, stop, pause, duration, interval, getPhase, minLatency };
53}
54export default createClock;