much simpler clock poc

This commit is contained in:
Felix Roos
2024-04-09 23:59:00 +02:00
parent e8ed57f588
commit 36ee4cfc98
2 changed files with 90 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
export const laserclock = ({ onTick, audioContext }) => {
const ready = new Promise((resolve) => {
document.addEventListener('click', async function init() {
document.removeEventListener('click', init);
audioContext = audioContext || new AudioContext();
audioContext.resume();
const workletCode = `class LaserClock extends AudioWorkletProcessor {
constructor() {
super();
this.block = 0;
this.tick = 0;
this.started = true;
this.blocksPerCallback = 20;
this.port.onmessage = (e) => {
switch (e.data) {
case "start":
console.log('start!')
this.started = true;
break;
case "stop":
console.log('stop!')
this.started = false;
this.block = 0;
this.tick = 0;
break;
}
};
}
process(inputs, outputs, parameters) {
if(!this.started) {
return true;
}
if(this.block % this.blocksPerCallback === 0) {
this.port.postMessage({tick: this.tick, currentTime});
this.tick++;
}
this.block++;
return true;
}
}
registerProcessor("laserclock-processor", LaserClock);`;
const dataURL = `data:text/javascript;base64,${btoa(workletCode)}`;
await audioContext.audioWorklet.addModule(dataURL);
const clock = new AudioWorkletNode(audioContext, 'laserclock-processor');
clock.port.onmessage = (e) => onTick(e.data);
clock.connect(audioContext.destination);
resolve({ audioContext, clock, start, stop });
});
});
const start = () => ready.then(({ clock }) => clock.port.postMessage('start'));
const stop = () => ready.then(({ clock }) => clock.port.postMessage('stop'));
return { ready, start, stop };
};
+37
View File
@@ -0,0 +1,37 @@
<body>
<button id="playButton">play</button>
<button id="stopButton">stop</button>
<script>
import { laserclock } from '../laserclock.mjs';
import { evaluate } from '@strudel/transpiler';
import { evalScope } from '@strudel/core';
import { superdough, setDefaultAudioContext, registerSynthSounds } from '@strudel/webaudio';
const init = async () => {
await evalScope(import('@strudel/core'), import('@strudel/mini'), import('@strudel/tonal'));
};
init().then(async () => {
let { pattern } = await evaluate('chord("<Dm7 G7 C^7 A7b9>(3,8)").jux(rev).voicing()');
let last;
let minLatency = 0.1;
const { start, stop, ready } = laserclock({
onTick: async (data) => {
await ready;
const { currentTime } = data;
if (last) {
const haps = pattern.queryArc(last, currentTime).filter((h) => h.hasOnset());
haps.forEach((hap) => {
const start = hap.whole.begin + minLatency;
superdough(hap.value, `=${start}`, hap.duration);
});
}
last = currentTime;
},
} as any);
ready.then(({ audioContext }) => setDefaultAudioContext(audioContext));
registerSynthSounds();
document.getElementById('playButton')?.addEventListener('click', () => start());
document.getElementById('stopButton')?.addEventListener('click', () => stop());
});
</script>
</body>