mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-27 21:48:27 +00:00
Get sysex working
split sysex message into sysexid and sysexdata sysexid is a device identification number or array sysexdata is an array of data to be sent to the device
This commit is contained in:
parent
e085819fe2
commit
13a4512601
@ -1514,7 +1514,8 @@ export const { control } = registerControl('control');
|
|||||||
export const { ccn } = registerControl('ccn');
|
export const { ccn } = registerControl('ccn');
|
||||||
export const { ccv } = registerControl('ccv');
|
export const { ccv } = registerControl('ccv');
|
||||||
export const { pc } = registerControl('pc');
|
export const { pc } = registerControl('pc');
|
||||||
export const { sysex } = registerControl('sysex');
|
export const { sysexid } = registerControl('sysexid');
|
||||||
|
export const { sysexdata } = registerControl('sysexdata');
|
||||||
export const { polyTouch } = registerControl('polyTouch');
|
export const { polyTouch } = registerControl('polyTouch');
|
||||||
export const { midibend } = registerControl('midibend');
|
export const { midibend } = registerControl('midibend');
|
||||||
export const { miditouch } = registerControl('miditouch');
|
export const { miditouch } = registerControl('miditouch');
|
||||||
|
|||||||
@ -76,19 +76,17 @@ The exact sound that each program number maps to depends on your MIDI device's c
|
|||||||
## sysex (System Exclusive Message)
|
## sysex (System Exclusive Message)
|
||||||
|
|
||||||
The `sysex` control sends MIDI System Exclusive (SysEx) messages to your MIDI device.
|
The `sysex` control sends MIDI System Exclusive (SysEx) messages to your MIDI device.
|
||||||
ysEx messages are device-specific commands that allow deeper control over synthesizer parameters.
|
sysEx messages are device-specific commands that allow deeper control over synthesizer parameters.
|
||||||
The value should be an array of numbers between 0-255 representing the SysEx data bytes.
|
The value should be an array of numbers between 0-255 representing the SysEx data bytes.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Send a simple SysEx message
|
// Send a simple SysEx message
|
||||||
sysex([0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7]).midi()
|
let id = 0x43; //Yamaha
|
||||||
```
|
//let id = "0x00:0x20:0x32"; //Behringer ID can be an array of numbers
|
||||||
|
|
||||||
```javascript
|
let data = "0x79:0x09:0x11:0x0A:0x00:0x00"; // Set NSX-39 voice to say "Aa"
|
||||||
//Send SysEx while playing notes
|
|
||||||
note("c3 e3 g3")
|
note("c d e f e d c").sysex(id, data).midi();
|
||||||
.sysex("<[0xF0,0x7E,0x7F,0x09,0x01,0xF7] [0xF0,0x7E,0x7F,0x09,0x02,0xF7]>")
|
|
||||||
.midi()
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The exact format of SysEx messages depends on your MIDI device's specification.
|
The exact format of SysEx messages depends on your MIDI device's specification.
|
||||||
|
|||||||
130
packages/midi/gm.mjs
Normal file
130
packages/midi/gm.mjs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
export const gm = {
|
||||||
|
piano: 0,
|
||||||
|
bright_acoustic_piano: 1,
|
||||||
|
electric_grand_piano: 2,
|
||||||
|
honky_tonk_piano: 3,
|
||||||
|
epiano1: 4,
|
||||||
|
epiano2: 5,
|
||||||
|
harpsichord: 6,
|
||||||
|
clavinet: 7,
|
||||||
|
celesta: 8,
|
||||||
|
glockenspiel: 9,
|
||||||
|
music_box: 10,
|
||||||
|
vibraphone: 11,
|
||||||
|
marimba: 12,
|
||||||
|
xylophone: 13,
|
||||||
|
tubular_bells: 14,
|
||||||
|
dulcimer: 15,
|
||||||
|
drawbar_organ: 16,
|
||||||
|
percussive_organ: 17,
|
||||||
|
rock_organ: 18,
|
||||||
|
church_organ: 19,
|
||||||
|
reed_organ: 20,
|
||||||
|
accordion: 21,
|
||||||
|
harmonica: 22,
|
||||||
|
bandoneon: 23,
|
||||||
|
acoustic_guitar_nylon: 24,
|
||||||
|
acoustic_guitar_steel: 25,
|
||||||
|
electric_guitar_jazz: 26,
|
||||||
|
electric_guitar_clean: 27,
|
||||||
|
electric_guitar_muted: 28,
|
||||||
|
overdriven_guitar: 29,
|
||||||
|
distortion_guitar: 30,
|
||||||
|
guitar_harmonics: 31,
|
||||||
|
acoustic_bass: 32,
|
||||||
|
electric_bass_finger: 33,
|
||||||
|
electric_bass_pick: 34,
|
||||||
|
fretless_bass: 35,
|
||||||
|
slap_bass_1: 36,
|
||||||
|
slap_bass_2: 37,
|
||||||
|
synth_bass_1: 38,
|
||||||
|
synth_bass_2: 39,
|
||||||
|
violin: 40,
|
||||||
|
viola: 41,
|
||||||
|
cello: 42,
|
||||||
|
contrabass: 43,
|
||||||
|
tremolo_strings: 44,
|
||||||
|
pizzicato_strings: 45,
|
||||||
|
orchestral_harp: 46,
|
||||||
|
timpani: 47,
|
||||||
|
string_ensemble_1: 48,
|
||||||
|
string_ensemble_2: 49,
|
||||||
|
synth_strings_1: 50,
|
||||||
|
synth_strings_2: 51,
|
||||||
|
choir_aahs: 52,
|
||||||
|
voice_oohs: 53,
|
||||||
|
synth_choir: 54,
|
||||||
|
orchestra_hit: 55,
|
||||||
|
trumpet: 56,
|
||||||
|
trombone: 57,
|
||||||
|
tuba: 58,
|
||||||
|
muted_trumpet: 59,
|
||||||
|
french_horn: 60,
|
||||||
|
brass_section: 61,
|
||||||
|
synth_brass_1: 62,
|
||||||
|
synth_brass_2: 63,
|
||||||
|
soprano_sax: 64,
|
||||||
|
alto_sax: 65,
|
||||||
|
tenor_sax: 66,
|
||||||
|
baritone_sax: 67,
|
||||||
|
oboe: 68,
|
||||||
|
english_horn: 69,
|
||||||
|
bassoon: 70,
|
||||||
|
clarinet: 71,
|
||||||
|
piccolo: 72,
|
||||||
|
flute: 73,
|
||||||
|
recorder: 74,
|
||||||
|
pan_flute: 75,
|
||||||
|
blown_bottle: 76,
|
||||||
|
shakuhachi: 77,
|
||||||
|
whistle: 78,
|
||||||
|
ocarina: 79,
|
||||||
|
lead_1_square: 80,
|
||||||
|
lead_2_sawtooth: 81,
|
||||||
|
lead_3_calliope: 82,
|
||||||
|
lead_4_chiff: 83,
|
||||||
|
lead_5_charang: 84,
|
||||||
|
lead_6_voice: 85,
|
||||||
|
lead_7_fifths: 86,
|
||||||
|
lead_8_bass_lead: 87,
|
||||||
|
pad_1_new_age: 88,
|
||||||
|
pad_2_warm: 89,
|
||||||
|
pad_3_polysynth: 90,
|
||||||
|
pad_4_choir: 91,
|
||||||
|
pad_5_bowed: 92,
|
||||||
|
pad_6_metallic: 93,
|
||||||
|
pad_7_halo: 94,
|
||||||
|
pad_8_sweep: 95,
|
||||||
|
fx_1_rain: 96,
|
||||||
|
fx_2_soundtrack: 97,
|
||||||
|
fx_3_crystal: 98,
|
||||||
|
fx_4_atmosphere: 99,
|
||||||
|
fx_5_brightness: 100,
|
||||||
|
fx_6_goblins: 101,
|
||||||
|
fx_7_echoes: 102,
|
||||||
|
fx_8_sci_fi: 103,
|
||||||
|
sitar: 104,
|
||||||
|
banjo: 105,
|
||||||
|
shamisen: 106,
|
||||||
|
koto: 107,
|
||||||
|
kalimba: 108,
|
||||||
|
bagpipe: 109,
|
||||||
|
fiddle: 110,
|
||||||
|
shanai: 111,
|
||||||
|
tinkle_bell: 112,
|
||||||
|
agogo: 113,
|
||||||
|
steel_drums: 114,
|
||||||
|
woodblock: 115,
|
||||||
|
taiko_drum: 116,
|
||||||
|
melodic_tom: 117,
|
||||||
|
synth_drum: 118,
|
||||||
|
reverse_cymbal: 119,
|
||||||
|
guitar_fret_noise: 120,
|
||||||
|
breath_noise: 121,
|
||||||
|
seashore: 122,
|
||||||
|
bird_tweet: 123,
|
||||||
|
telephone: 124,
|
||||||
|
helicopter: 125,
|
||||||
|
applause: 126,
|
||||||
|
gunshot: 127,
|
||||||
|
};
|
||||||
@ -1,3 +1,4 @@
|
|||||||
import './midi.mjs';
|
import './midi.mjs';
|
||||||
|
|
||||||
export * from './midi.mjs';
|
export * from './midi.mjs';
|
||||||
|
export * from './gm.mjs';
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import * as _WebMidi from 'webmidi';
|
|||||||
import { Pattern, getEventOffsetMs, isPattern, logger, ref } from '@strudel/core';
|
import { Pattern, getEventOffsetMs, isPattern, logger, ref } from '@strudel/core';
|
||||||
import { noteToMidi } from '@strudel/core';
|
import { noteToMidi } from '@strudel/core';
|
||||||
import { Note } from 'webmidi';
|
import { Note } from 'webmidi';
|
||||||
|
|
||||||
// if you use WebMidi from outside of this package, make sure to import that instance:
|
// if you use WebMidi from outside of this package, make sure to import that instance:
|
||||||
export const { WebMidi } = _WebMidi;
|
export const { WebMidi } = _WebMidi;
|
||||||
|
|
||||||
@ -43,13 +44,16 @@ export function enableWebMidi(options = {}) {
|
|||||||
resolve(WebMidi);
|
resolve(WebMidi);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WebMidi.enable((err) => {
|
WebMidi.enable(
|
||||||
if (err) {
|
(err) => {
|
||||||
reject(err);
|
if (err) {
|
||||||
}
|
reject(err);
|
||||||
onReady?.(WebMidi);
|
}
|
||||||
resolve(WebMidi);
|
onReady?.(WebMidi);
|
||||||
});
|
resolve(WebMidi);
|
||||||
|
},
|
||||||
|
{ sysex: true },
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +138,20 @@ Pattern.prototype.midi = function (output) {
|
|||||||
// 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
|
// destructure value
|
||||||
let { note, nrpnn, nrpv, ccn, ccv, midichan = 1, midicmd, gain = 1, velocity = 0.9, pc, sysex } = hap.value;
|
let {
|
||||||
|
note,
|
||||||
|
nrpnn,
|
||||||
|
nrpv,
|
||||||
|
ccn,
|
||||||
|
ccv,
|
||||||
|
midichan = 1,
|
||||||
|
midicmd,
|
||||||
|
gain = 1,
|
||||||
|
velocity = 0.9,
|
||||||
|
pc,
|
||||||
|
sysexid,
|
||||||
|
sysexdata,
|
||||||
|
} = hap.value;
|
||||||
|
|
||||||
velocity = gain * velocity;
|
velocity = gain * velocity;
|
||||||
|
|
||||||
@ -173,15 +190,18 @@ Pattern.prototype.midi = function (output) {
|
|||||||
throw new Error(`Expected ${name} to be a number between 0 and 127 for program change`);
|
throw new Error(`Expected ${name} to be a number between 0 and 127 for program change`);
|
||||||
}
|
}
|
||||||
device.sendProgramChange(value, paramSpec.channel || midichan, { time: timeOffsetString });
|
device.sendProgramChange(value, paramSpec.channel || midichan, { time: timeOffsetString });
|
||||||
} else if (paramSpec.sysex) {
|
|
||||||
if (!Array.isArray(value)) {
|
|
||||||
throw new Error(`Expected ${name} to be an array of numbers (0-255) for sysex`);
|
|
||||||
}
|
|
||||||
if (!value.every((byte) => Number.isInteger(byte) && byte >= 0 && byte <= 255)) {
|
|
||||||
throw new Error(`All sysex bytes in ${name} must be integers between 0 and 255`);
|
|
||||||
}
|
|
||||||
device.sendSysex(undefined, value, { time: timeOffsetString });
|
|
||||||
}
|
}
|
||||||
|
// ToDo: support sysex for mapped parameters
|
||||||
|
// } else if (paramSpec.sysex) {
|
||||||
|
// if (!Array.isArray(value)) {
|
||||||
|
// throw new Error(`Expected ${name} to be an array of numbers (0-255) for sysex`);
|
||||||
|
// }
|
||||||
|
// if (!value.every((byte) => Number.isInteger(byte) && byte >= 0 && byte <= 255)) {
|
||||||
|
// throw new Error(`All sysex bytes in ${name} must be integers between 0 and 255`);
|
||||||
|
// }
|
||||||
|
// device.sendSysex(0x43, value, { time: timeOffsetString });
|
||||||
|
// //device.sendSysex(0x43, [0x79, 0x09, 0x11, 0x0A, 0x00,0x02], { time: timeOffsetString });
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -193,14 +213,25 @@ Pattern.prototype.midi = function (output) {
|
|||||||
device.sendProgramChange(pc, midichan, { time: timeOffsetString });
|
device.sendProgramChange(pc, midichan, { time: timeOffsetString });
|
||||||
}
|
}
|
||||||
// Handle sysex
|
// Handle sysex
|
||||||
if (sysex !== undefined) {
|
if (sysexid !== undefined && sysexdata !== undefined) {
|
||||||
if (!Array.isArray(sysex)) {
|
//console.log('sysex', sysexid, sysexdata);
|
||||||
|
if (Array.isArray(sysexid)) {
|
||||||
|
if (!sysexid.every((byte) => Number.isInteger(byte) && byte >= 0 && byte <= 255)) {
|
||||||
|
throw new Error('all sysexid bytes must be integers between 0 and 255');
|
||||||
|
}
|
||||||
|
} else if (!Number.isInteger(sysexid) || sysexid < 0 || sysexid > 255) {
|
||||||
|
throw new Error('A:sysexid must be an number between 0 and 255 or an array of such integers');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(sysexdata)) {
|
||||||
throw new Error('expected sysex to be an array of numbers (0-255)');
|
throw new Error('expected sysex to be an array of numbers (0-255)');
|
||||||
}
|
}
|
||||||
if (!sysex.every((byte) => Number.isInteger(byte) && byte >= 0 && byte <= 255)) {
|
if (!sysexdata.every((byte) => Number.isInteger(byte) && byte >= 0 && byte <= 255)) {
|
||||||
throw new Error('all sysex bytes must be integers between 0 and 255');
|
throw new Error('all sysex bytes must be integers between 0 and 255');
|
||||||
}
|
}
|
||||||
device.sendSysex(undefined, sysex, { time: timeOffsetString });
|
|
||||||
|
device.sendSysex(sysexid, sysexdata, { time: timeOffsetString });
|
||||||
|
//device.sendSysex(0x43, [0x79, 0x09, 0x11, 0x0A, 0x00,0x1e], { time: timeOffsetString });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle control change
|
// Handle control change
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user