mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-23 03:28:33 +00:00
Refactor: Consolidate configuration variables into midiConfig object
- add options argument to .midi - add midiConfig object with properties
This commit is contained in:
parent
3c2692bdda
commit
2ccb95aec0
@ -257,35 +257,71 @@ function sendAftertouch(miditouch, device, midichan, timeOffsetString) {
|
|||||||
device.sendChannelAftertouch(miditouch, midichan, { time: timeOffsetString });
|
device.sendChannelAftertouch(miditouch, midichan, { time: timeOffsetString });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sends a note message to the given device on the given channel
|
||||||
|
function sendNote(note, velocity, duration, device, midichan, timeOffsetString) {
|
||||||
|
if (note == null || note === '') {
|
||||||
|
throw new Error('note cannot be null or empty');
|
||||||
|
}
|
||||||
|
if (velocity != null && (typeof velocity !== 'number' || velocity < 0 || velocity > 1)) {
|
||||||
|
throw new Error('velocity must be a number between 0 and 1');
|
||||||
|
}
|
||||||
|
if (duration != null && (typeof duration !== 'number' || duration < 0)) {
|
||||||
|
throw new Error('duration must be a positive number');
|
||||||
|
}
|
||||||
|
|
||||||
|
const midiNumber = typeof note === 'number' ? note : noteToMidi(note);
|
||||||
|
const midiNote = new Note(midiNumber, { attack: velocity, duration });
|
||||||
|
device.playNote(midiNote, midichan, {
|
||||||
|
time: timeOffsetString,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MIDI output: Opens a MIDI output port.
|
* MIDI output: Opens a MIDI output port.
|
||||||
* @param {string | number} output MIDI device name or index defaulting to 0
|
* @param {string | number} midiport MIDI device name or index defaulting to 0
|
||||||
|
* @param {object} options Additional MIDI configuration options
|
||||||
* @example
|
* @example
|
||||||
* note("c4").midichan(1).midi("IAC Driver Bus 1")
|
* note("c4").midichan(1).midi('IAC Driver Bus 1')
|
||||||
|
* @example
|
||||||
|
* note("c4").midichan(1).midi('IAC Driver Bus 1', { controller: true, latency: 50 })
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.midi = function (output) {
|
|
||||||
if (isPattern(output)) {
|
Pattern.prototype.midi = function (midiport, options = {}) {
|
||||||
|
if (isPattern(midiport)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${
|
`.midi does not accept Pattern input for midiport. Make sure to pass device name with single quotes. Example: .midi('${
|
||||||
WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1'
|
WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1'
|
||||||
}')`,
|
}')`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let portName = output;
|
|
||||||
let isController = false;
|
|
||||||
let mapping = {};
|
|
||||||
|
|
||||||
//TODO: MIDI mapping related
|
// For backward compatibility
|
||||||
if (typeof output === 'object') {
|
if (typeof midiport === 'object') {
|
||||||
const { port, controller = false, ...remainingProps } = output;
|
const { port, isController = false, ...configOptions } = midiport;
|
||||||
portName = port;
|
options = {
|
||||||
isController = controller;
|
isController,
|
||||||
mapping = remainingProps;
|
...configOptions,
|
||||||
|
...options, // Keep any options passed separately
|
||||||
|
};
|
||||||
|
midiport = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let midiConfig = {
|
||||||
|
// Default configuration values
|
||||||
|
isController: false, // Disable sending notes for midi controllers
|
||||||
|
latencyMs: 34, // Default latency to get audio engine to line up in ms
|
||||||
|
noteOffsetMs: 10, // Default note-off offset to prevent glitching in ms
|
||||||
|
midichannel: 1, // Default MIDI channel
|
||||||
|
velocity: 0.9, // Default velocity
|
||||||
|
gain: 1, // Default gain
|
||||||
|
midimap: 'default', // Default MIDI map
|
||||||
|
midiport: midiport, // Store the port in the config
|
||||||
|
...options, // Override defaults with provided options
|
||||||
|
};
|
||||||
|
|
||||||
enableWebMidi({
|
enableWebMidi({
|
||||||
onEnabled: ({ outputs }) => {
|
onEnabled: ({ outputs }) => {
|
||||||
const device = getDevice(portName, outputs);
|
const device = getDevice(midiConfig.midiport, outputs);
|
||||||
const otherOutputs = outputs.filter((o) => o.name !== device.name);
|
const otherOutputs = outputs.filter((o) => o.name !== device.name);
|
||||||
logger(
|
logger(
|
||||||
`Midi enabled! Using "${device.name}". ${
|
`Midi enabled! Using "${device.name}". ${
|
||||||
@ -303,30 +339,31 @@ Pattern.prototype.midi = function (output) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hap.ensureObjectValue();
|
hap.ensureObjectValue();
|
||||||
|
|
||||||
//magic number to get audio engine to line up, can probably be calculated somehow
|
//magic number to get audio engine to line up, can probably be calculated somehow
|
||||||
const latencyMs = 34;
|
const latencyMs = midiConfig.latencyMs;
|
||||||
// passing a string with a +num into the webmidi api adds an offset to the current time https://webmidijs.org/api/classes/Output
|
// passing a string with a +num into the webmidi api adds an offset to the current time https://webmidijs.org/api/classes/Output
|
||||||
const timeOffsetString = `+${getEventOffsetMs(targetTime, currentTime) + latencyMs}`;
|
const timeOffsetString = `+${getEventOffsetMs(targetTime, currentTime) + latencyMs}`;
|
||||||
|
|
||||||
// destructure value
|
// midi event values from hap with configurable defaults
|
||||||
let {
|
let {
|
||||||
note,
|
note,
|
||||||
nrpnn,
|
nrpnn,
|
||||||
nrpv,
|
nrpv,
|
||||||
ccn,
|
ccn,
|
||||||
ccv,
|
ccv,
|
||||||
midichan = 1,
|
midichan = midiConfig.midichannel,
|
||||||
midicmd,
|
midicmd,
|
||||||
midibend,
|
midibend,
|
||||||
miditouch,
|
miditouch,
|
||||||
polyTouch, //??
|
polyTouch,
|
||||||
gain = 1,
|
gain = midiConfig.gain,
|
||||||
velocity = 0.9,
|
velocity = midiConfig.velocity,
|
||||||
progNum,
|
progNum,
|
||||||
sysexid,
|
sysexid,
|
||||||
sysexdata,
|
sysexdata,
|
||||||
midimap = 'default',
|
midimap = midiConfig.midimap,
|
||||||
midiport = output,
|
midiport = midiConfig.midiport,
|
||||||
} = hap.value;
|
} = hap.value;
|
||||||
|
|
||||||
const device = getDevice(midiport, WebMidi.outputs);
|
const device = getDevice(midiport, WebMidi.outputs);
|
||||||
@ -338,20 +375,24 @@ Pattern.prototype.midi = function (output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
velocity = gain * velocity;
|
velocity = gain * velocity;
|
||||||
|
|
||||||
|
// Handle midimap
|
||||||
// if midimap is set, send a cc messages from defined controls
|
// if midimap is set, send a cc messages from defined controls
|
||||||
if (midicontrolMap.has(midimap)) {
|
if (midicontrolMap.has(midimap)) {
|
||||||
const ccs = mapCC(midicontrolMap.get(midimap), hap.value);
|
const ccs = mapCC(midicontrolMap.get(midimap), hap.value);
|
||||||
ccs.forEach(({ ccn, ccv }) => sendCC(ccn, ccv, device, midichan, timeOffsetString));
|
ccs.forEach(({ ccn, ccv }) => sendCC(ccn, ccv, device, midichan, timeOffsetString));
|
||||||
|
} else if (midimap !== 'default') {
|
||||||
|
// Add warning when a non-existent midimap is specified
|
||||||
|
logger(`[midi] midimap "${midimap}" not found! Available maps: ${[...midicontrolMap.keys()].join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// note off messages will often a few ms arrive late, try to prevent glitching by subtracting from the duration length
|
// Handle note
|
||||||
const duration = (hap.duration.valueOf() / cps) * 1000 - 10;
|
if (note !== undefined && !midiConfig.isController) {
|
||||||
if (note != null && !isController) {
|
// note off messages will often a few ms arrive late,
|
||||||
const midiNumber = typeof note === 'number' ? note : noteToMidi(note);
|
// try to prevent glitching by subtracting noteOffsetMs from the duration length
|
||||||
const midiNote = new Note(midiNumber, { attack: velocity, duration });
|
const duration = (hap.duration.valueOf() / cps) * 1000 - midiConfig.noteOffsetMs;
|
||||||
device.playNote(midiNote, midichan, {
|
|
||||||
time: timeOffsetString,
|
sendNote(note, velocity, duration, device, midichan, timeOffsetString);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle program change
|
// Handle program change
|
||||||
@ -427,7 +468,7 @@ const refs = {};
|
|||||||
* @param {string | number} input MIDI device name or index defaulting to 0
|
* @param {string | number} input MIDI device name or index defaulting to 0
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
* @example
|
* @example
|
||||||
* let cc = await midin("IAC Driver Bus 1")
|
* let cc = await midin('IAC Driver Bus 1')
|
||||||
* note("c a f e").lpf(cc(0).range(0, 1000)).lpq(cc(1).range(0, 10)).sound("sawtooth")
|
* note("c a f e").lpf(cc(0).range(0, 1000)).lpq(cc(1).range(0, 10)).sound("sawtooth")
|
||||||
*/
|
*/
|
||||||
export async function midin(input) {
|
export async function midin(input) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user