Skip to content

Commit

Permalink
Merge pull request tidalcycles#699 from tidalcycles/midi-in
Browse files Browse the repository at this point in the history
Midi in
  • Loading branch information
felixroos authored Sep 28, 2023
2 parents 527b895 + 0dcc55e commit 8a74195
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 17 deletions.
1 change: 0 additions & 1 deletion packages/core/controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,6 @@ const generic_params = [
*/
['waveloss'],
// TODO: midi effects?
['midicmd'],
['dur'],
// ['modwheel'],
['expression'],
Expand Down
6 changes: 6 additions & 0 deletions packages/core/pattern.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2343,3 +2343,9 @@ export const fit = register('fit', (pat) =>
export const { loopAtCps, loopatcps } = register(['loopAtCps', 'loopatcps'], function (factor, cps, pat) {
return _loopAt(factor, pat, cps);
});

/** exposes a custom value at query time. basically allows mutating state without evaluation */
export const ref = (accessor) =>
pure(1)
.withValue(() => reify(accessor()))
.innerJoin();
58 changes: 42 additions & 16 deletions packages/midi/midi.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/

import * as _WebMidi from 'webmidi';
import { Pattern, isPattern, logger } from '@strudel.cycles/core';
import { Pattern, isPattern, logger, ref } from '@strudel.cycles/core';
import { noteToMidi } from '@strudel.cycles/core';
import { Note } from 'webmidi';
// if you use WebMidi from outside of this package, make sure to import that instance:
Expand All @@ -15,8 +15,8 @@ function supportsMidi() {
return typeof navigator.requestMIDIAccess === 'function';
}

function getMidiDeviceNamesString(outputs) {
return outputs.map((o) => `'${o.name}'`).join(' | ');
function getMidiDeviceNamesString(devices) {
return devices.map((o) => `'${o.name}'`).join(' | ');
}

export function enableWebMidi(options = {}) {
Expand Down Expand Up @@ -52,30 +52,28 @@ export function enableWebMidi(options = {}) {
});
});
}
// const outputByName = (name: string) => WebMidi.getOutputByName(name);
const outputByName = (name) => WebMidi.getOutputByName(name);

// output?: string | number, outputs: typeof WebMidi.outputs
function getDevice(output, outputs) {
if (!outputs.length) {
function getDevice(indexOrName, devices) {
if (!devices.length) {
throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`);
}
if (typeof output === 'number') {
return outputs[output];
if (typeof indexOrName === 'number') {
return devices[indexOrName];
}
if (typeof output === 'string') {
return outputByName(output);
const byName = (name) => devices.find((output) => output.name.includes(name));
if (typeof indexOrName === 'string') {
return byName(indexOrName);
}
// attempt to default to first IAC device if none is specified
const IACOutput = outputs.find((output) => output.name.includes('IAC'));
const device = IACOutput ?? outputs[0];
const IACOutput = byName('IAC');
const device = IACOutput ?? devices[0];
if (!device) {
throw new Error(
`🔌 MIDI device '${output ? output : ''}' not found. Use one of ${getMidiDeviceNamesString(WebMidi.outputs)}`,
`🔌 MIDI device '${device ? device : ''}' not found. Use one of ${getMidiDeviceNamesString(devices)}`,
);
}

return IACOutput ?? outputs[0];
return IACOutput ?? devices[0];
}

// send start/stop messages to outputs when repl starts/stops
Expand Down Expand Up @@ -164,3 +162,31 @@ Pattern.prototype.midi = function (output) {
}
});
};

let listeners = {};
const refs = {};

export async function midin(input) {
const initial = await enableWebMidi(); // only returns on first init
const device = getDevice(input, WebMidi.inputs);

if (initial) {
const otherInputs = WebMidi.inputs.filter((o) => o.name !== device.name);
logger(
`Midi enabled! Using "${device.name}". ${
otherInputs?.length ? `Also available: ${getMidiDeviceNamesString(otherInputs)}` : ''
}`,
);
refs[input] = {};
}
const cc = (cc) => ref(() => refs[input][cc] || 0);

listeners[input] && device.removeListener('midimessage', listeners[input]);
listeners[input] = (e) => {
const cc = e.dataBytes[0];
const v = e.dataBytes[1];
refs[input] && (refs[input][cc] = v / 127);
};
device.addListener('midimessage', listeners[input]);
return cc;
}

0 comments on commit 8a74195

Please sign in to comment.