Merge pull request #710 from tidalcycles/midi-clock
add midi clock support
This commit is contained in:
commit
527b895e3c
5 changed files with 36 additions and 7 deletions
|
|
@ -1066,6 +1066,7 @@ const generic_params = [
|
||||||
*/
|
*/
|
||||||
['waveloss'],
|
['waveloss'],
|
||||||
// TODO: midi effects?
|
// TODO: midi effects?
|
||||||
|
['midicmd'],
|
||||||
['dur'],
|
['dur'],
|
||||||
// ['modwheel'],
|
// ['modwheel'],
|
||||||
['expression'],
|
['expression'],
|
||||||
|
|
|
||||||
|
|
@ -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 });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue