From ec470aa2c6f1400a4f1a14ec68be413850388c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Forment?= Date: Sat, 9 Nov 2024 01:41:59 +0100 Subject: [PATCH] Feat: midi() command support external instrument parameter mapping This commit adds a second argument to the midi() command: mapping. This argument should be an object containing a key-value map of MIDI controls used by an external synthesizer. If any control is used that matches the mapping, a CC message is sent. --- packages/midi/midi.mjs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index 32e66f6c..92cf8290 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -89,11 +89,10 @@ if (typeof window !== 'undefined') { }); } -Pattern.prototype.midi = function (output) { +Pattern.prototype.midi = function (output, mapping) { if (isPattern(output)) { throw new Error( - `.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${ - WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1' + `.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1' }')`, ); } @@ -103,8 +102,7 @@ Pattern.prototype.midi = function (output) { const device = getDevice(output, outputs); const otherOutputs = outputs.filter((o) => o.name !== device.name); logger( - `Midi enabled! Using "${device.name}". ${ - otherOutputs?.length ? `Also available: ${getMidiDeviceNamesString(otherOutputs)}` : '' + `Midi enabled! Using "${device.name}". ${otherOutputs?.length ? `Also available: ${getMidiDeviceNamesString(otherOutputs)}` : '' }`, ); }, @@ -137,6 +135,23 @@ Pattern.prototype.midi = function (output) { time: timeOffsetString, }); } + + // Handle mapped parameters if mapping exists + if (mapping && mapping.parameters) { + Object.entries(hap.value).forEach(([param, value]) => { + const mappedParam = mapping.parameters[param]; + if (mappedParam && typeof value === 'number') { + const scaled = Math.round(value * 127); + device.sendControlChange( + mappedParam.cc, + scaled, + mappedParam.channel || midichan, + { time: timeOffsetString } + ); + } + }); + } + if (ccv !== undefined && ccn !== undefined) { if (typeof ccv !== 'number' || ccv < 0 || ccv > 1) { throw new Error('expected ccv to be a number between 0 and 1'); @@ -169,8 +184,7 @@ const refs = {}; export async function midin(input) { if (isPattern(input)) { throw new Error( - `midin: does not accept Pattern as input. Make sure to pass device name with single quotes. Example: midin('${ - WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1' + `midin: does not accept Pattern as input. Make sure to pass device name with single quotes. Example: midin('${WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1' }')`, ); } @@ -184,8 +198,7 @@ export async function midin(input) { if (initial) { const otherInputs = WebMidi.inputs.filter((o) => o.name !== device.name); logger( - `Midi enabled! Using "${device.name}". ${ - otherInputs?.length ? `Also available: ${getMidiDeviceNamesString(otherInputs)}` : '' + `Midi enabled! Using "${device.name}". ${otherInputs?.length ? `Also available: ${getMidiDeviceNamesString(otherInputs)}` : '' }`, ); refs[input] = {};