Merge pull request #710 from tidalcycles/midi-clock

add midi clock support
This commit is contained in:
Felix Roos 2023-09-27 22:53:49 +02:00 committed by GitHub
commit 527b895e3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 36 additions and 7 deletions

View file

@ -1066,6 +1066,7 @@ const generic_params = [
*/ */
['waveloss'], ['waveloss'],
// TODO: midi effects? // TODO: midi effects?
['midicmd'],
['dur'], ['dur'],
// ['modwheel'], // ['modwheel'],
['expression'], ['expression'],

View file

@ -78,6 +78,19 @@ function getDevice(output, outputs) {
return IACOutput ?? outputs[0]; return IACOutput ?? outputs[0];
} }
// send start/stop messages to outputs when repl starts/stops
if (typeof window !== 'undefined') {
window.addEventListener('message', (e) => {
if (!WebMidi?.enabled) {
return;
}
if (e.data === 'strudel-stop') {
WebMidi.outputs.forEach((output) => output.sendStop());
}
// cannot start here, since we have no timing info, see sendStart below
});
}
Pattern.prototype.midi = function (output) { Pattern.prototype.midi = function (output) {
if (isPattern(output)) { if (isPattern(output)) {
throw new Error( throw new Error(
@ -103,6 +116,7 @@ Pattern.prototype.midi = function (output) {
return this.onTrigger((time, hap, currentTime, cps) => { return this.onTrigger((time, hap, currentTime, cps) => {
if (!WebMidi.enabled) { if (!WebMidi.enabled) {
console.log('not enabled');
return; return;
} }
const device = getDevice(output, WebMidi.outputs); const device = getDevice(output, WebMidi.outputs);
@ -113,7 +127,7 @@ Pattern.prototype.midi = function (output) {
const timeOffsetString = `+${offset}`; const timeOffsetString = `+${offset}`;
// destructure value // destructure value
const { note, nrpnn, nrpv, ccn, ccv, midichan = 1 } = hap.value; const { note, nrpnn, nrpv, ccn, ccv, midichan = 1, midicmd } = hap.value;
const velocity = hap.context?.velocity ?? 0.9; // TODO: refactor velocity const velocity = hap.context?.velocity ?? 0.9; // TODO: refactor velocity
// note off messages will often a few ms arrive late, try to prevent glitching by subtracting from the duration length // note off messages will often a few ms arrive late, try to prevent glitching by subtracting from the duration length
@ -125,7 +139,7 @@ Pattern.prototype.midi = function (output) {
time: timeOffsetString, time: timeOffsetString,
}); });
} }
if (ccv && ccn) { if (ccv !== undefined && ccn !== undefined) {
if (typeof ccv !== 'number' || ccv < 0 || ccv > 1) { if (typeof ccv !== 'number' || ccv < 0 || ccv > 1) {
throw new Error('expected ccv to be a number between 0 and 1'); throw new Error('expected ccv to be a number between 0 and 1');
} }
@ -135,5 +149,18 @@ Pattern.prototype.midi = function (output) {
const scaled = Math.round(ccv * 127); const scaled = Math.round(ccv * 127);
device.sendControlChange(ccn, scaled, midichan, { time: timeOffsetString }); device.sendControlChange(ccn, scaled, midichan, { time: timeOffsetString });
} }
if (hap.whole.begin + 0 === 0) {
// we need to start here because we have the timing info
device.sendStart({ time: timeOffsetString });
}
if (['clock', 'midiClock'].includes(midicmd)) {
device.sendClock({ time: timeOffsetString });
} else if (['start'].includes(midicmd)) {
device.sendStart({ time: timeOffsetString });
} else if (['stop'].includes(midicmd)) {
device.sendStop({ time: timeOffsetString });
} else if (['continue'].includes(midicmd)) {
device.sendContinue({ time: timeOffsetString });
}
}); });
}; };

View file

@ -14,7 +14,6 @@ class MyProcessor extends AudioWorkletProcessor {
if(e.data==='stop') { if(e.data==='stop') {
this.stopped = true; this.stopped = true;
} else if(e.data?.dough) { } else if(e.data?.dough) {
const deadline = e.data.time-currentTime;
__q.push(e.data) __q.push(e.data)
} else { } else {
msg?.(e.data) msg?.(e.data)

View file

@ -1,6 +1,6 @@
{ {
"name": "superdough", "name": "superdough",
"version": "0.9.8", "version": "0.9.9",
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.", "description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
"main": "index.mjs", "main": "index.mjs",
"type": "module", "type": "module",

View file

@ -33,7 +33,7 @@ const supabase = createClient(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
); );
const modules = [ let modules = [
import('@strudel.cycles/core'), import('@strudel.cycles/core'),
import('@strudel.cycles/tonal'), import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'), import('@strudel.cycles/mini'),
@ -45,13 +45,13 @@ const modules = [
import('@strudel.cycles/csound'), import('@strudel.cycles/csound'),
]; ];
if (isTauri()) { if (isTauri()) {
modules.concat([ modules = modules.concat([
import('@strudel/desktopbridge/loggerbridge.mjs'), import('@strudel/desktopbridge/loggerbridge.mjs'),
import('@strudel/desktopbridge/midibridge.mjs'), import('@strudel/desktopbridge/midibridge.mjs'),
import('@strudel/desktopbridge/oscbridge.mjs'), import('@strudel/desktopbridge/oscbridge.mjs'),
]); ]);
} else { } else {
modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]); modules = modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]);
} }
const modulesLoading = evalScope( const modulesLoading = evalScope(
@ -153,6 +153,8 @@ export function Repl({ embedded = false }) {
if (!play) { if (!play) {
cleanupDraw(false); cleanupDraw(false);
window.postMessage('strudel-stop'); window.postMessage('strudel-stop');
} else {
window.postMessage('strudel-start');
} }
}, },
drawContext, drawContext,