mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 05:38:35 +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 { ccv } = registerControl('ccv');
|
||||
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 { midibend } = registerControl('midibend');
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
//Send SysEx while playing notes
|
||||
note("c3 e3 g3")
|
||||
.sysex("<[0xF0,0x7E,0x7F,0x09,0x01,0xF7] [0xF0,0x7E,0x7F,0x09,0x02,0xF7]>")
|
||||
.midi()
|
||||
let data = "0x79:0x09:0x11:0x0A:0x00:0x00"; // Set NSX-39 voice to say "Aa"
|
||||
|
||||
note("c d e f e d c").sysex(id, data).midi();
|
||||
```
|
||||
|
||||
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';
|
||||
|
||||
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 { noteToMidi } from '@strudel/core';
|
||||
import { Note } from 'webmidi';
|
||||
|
||||
// if you use WebMidi from outside of this package, make sure to import that instance:
|
||||
export const { WebMidi } = _WebMidi;
|
||||
|
||||
@ -43,13 +44,16 @@ export function enableWebMidi(options = {}) {
|
||||
resolve(WebMidi);
|
||||
return;
|
||||
}
|
||||
WebMidi.enable((err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
onReady?.(WebMidi);
|
||||
resolve(WebMidi);
|
||||
});
|
||||
WebMidi.enable(
|
||||
(err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
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
|
||||
const timeOffsetString = `+${getEventOffsetMs(targetTime, currentTime) + latencyMs}`;
|
||||
// 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;
|
||||
|
||||
@ -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`);
|
||||
}
|
||||
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 });
|
||||
}
|
||||
// Handle sysex
|
||||
if (sysex !== undefined) {
|
||||
if (!Array.isArray(sysex)) {
|
||||
if (sysexid !== undefined && sysexdata !== undefined) {
|
||||
//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)');
|
||||
}
|
||||
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');
|
||||
}
|
||||
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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user