eslint-disable-next-line no-undef TODO: swap below line with above one when firefox supports esm imports in service workers see https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker?retiredLocale=de#browser_compatibility import createClock from './zyklus.mjs';
{id: {started: boolean}}
21const sendMessage = (type, payload) => { 22 channel.postMessage({ type, payload }); 23}; 24 25const sendTick = (phase, duration, tick, time) => { 26 const num_seconds_since_cps_change = num_ticks_since_cps_change * duration; 27 const tickdeadline = phase - time; 28 const lastTick = time + tickdeadline; 29 const num_cycles_since_cps_change = num_seconds_since_cps_change * cps; 30 const begin = num_cycles_at_cps_change + num_cycles_since_cps_change; 31 const secondsSinceLastTick = time - lastTick - duration; 32 const eventLength = duration * cps; 33 const end = begin + eventLength; 34 const cycle = begin + secondsSinceLastTick * cps; 35 36 sendMessage('tick', { 37 begin, 38 end, 39 cps, 40 time, 41 cycle, 42 }); 43 num_ticks_since_cps_change++; 44};
create clock method from zyklus
50const startClock = (id) => { 51 clients.set(id, { started: true }); 52 if (started) { 53 return; 54 } 55 clock.start(); 56 started = true; 57}; 58const stopClock = async (id) => { 59 clients.set(id, { started: false }); 60 61 const otherClientStarted = Array.from(clients.values()).some((c) => c.started); 62 //dont stop the clock if other instances are running... 63 if (!started || otherClientStarted) { 64 return; 65 } 66 67 clock.stop(); 68 setCycle(0); 69 started = false; 70}; 71 72const setCycle = (cycle) => { 73 num_ticks_since_cps_change = 0; 74 num_cycles_at_cps_change = cycle; 75}; 76 77const processMessage = (message) => { 78 const { type, payload } = message; 79 80 switch (type) { 81 case 'cpschange': { 82 if (payload.cps !== cps) { 83 const num_seconds_since_cps_change = num_ticks_since_cps_change * duration; 84 num_cycles_at_cps_change = num_cycles_at_cps_change + num_seconds_since_cps_change * cps; 85 num_seconds_at_cps_change = num_seconds_at_cps_change + num_seconds_since_cps_change; 86 cps = payload.cps; 87 num_ticks_since_cps_change = 0; 88 } 89 break; 90 } 91 case 'setcycle': { 92 setCycle(payload.cycle); 93 break; 94 } 95 case 'toggle': { 96 if (payload.started) { 97 startClock(message.id); 98 } else { 99 stopClock(message.id); 100 } 101 break; 102 } 103 } 104}; 105 106self.onconnect = function (e) { 107 // the incoming port 108 const port = e.ports[0]; 109 110 port.addEventListener('message', function (e) { 111 processMessage(e.data); 112 }); 113 port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter. 114};
used to consistently schedule events, for use in a service worker - see https://codeberg.org/uzu/strudel/src/branch/main/packages/core/clockworker.mjs
117function createClock( 118 getTime, 119 callback, // called slightly before each cycle 120 duration = 0.05, // duration of each cycle 121 interval = 0.1, // interval between callbacks 122 overlap = 0.1, // overlap between callbacks 123) { 124 let tick = 0; // counts callbacks 125 let phase = 0; // next callback time 126 let precision = 10 ** 4; // used to round phase 127 let minLatency = 0.01; 128 const setDuration = (setter) => (duration = setter(duration)); 129 overlap = overlap || interval / 2; 130 const onTick = () => { 131 const t = getTime(); 132 const lookahead = t + interval + overlap; // the time window for this tick 133 if (phase === 0) { 134 phase = t + minLatency; 135 } 136 // callback as long as we're inside the lookahead 137 while (phase < lookahead) { 138 phase = Math.round(phase * precision) / precision; 139 phase >= t && callback(phase, duration, tick, t); 140 phase < t && console.log('TOO LATE', phase); // what if latency is added from outside? 141 phase += duration; // increment phase by duration 142 tick++; 143 } 144 }; 145 let intervalID; 146 const start = () => { 147 clear(); // just in case start was called more than once 148 onTick(); 149 intervalID = setInterval(onTick, interval * 1000); 150 }; 151 const clear = () => intervalID !== undefined && clearInterval(intervalID); 152 const pause = () => clear(); 153 const stop = () => { 154 tick = 0; 155 phase = 0; 156 clear(); 157 }; 158 const getPhase = () => phase; 159 // setCallback 160 return { setDuration, start, stop, pause, duration, interval, getPhase, minLatency }; 161}