From 0f1f5754725b2856075b66a0d11fc4c56750d11a Mon Sep 17 00:00:00 2001 From: Steven Goodwin Date: Tue, 2 Jul 2024 10:12:10 +0100 Subject: [PATCH 1/3] Add MIDI constants to remove the magic numbers from sendMessage calls --- gm.js | 132 +++++++++++++++++++++++++++++ gmdrums.js | 51 +++++++++++ midi.js | 12 +++ msg.js | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++ notes.js | 50 +++++++++++ 5 files changed, 489 insertions(+) create mode 100644 gm.js create mode 100644 gmdrums.js create mode 100644 msg.js create mode 100644 notes.js diff --git a/gm.js b/gm.js new file mode 100644 index 0000000..dfe5818 --- /dev/null +++ b/gm.js @@ -0,0 +1,132 @@ +// General MIDI patches +module.exports = { + + ACOUSTIC_GRAND_PIANO : 0, + BRIGHT_ACOUSTIC_PIANO : 1, + ELECTRIC_GRAND_PIANO : 2, + HONKY_TONK_PIANO : 3, + ELECTRIC_PIANO_1 : 4, + ELECTRIC_PIANO_2 : 5, + HARPSICHORD : 6, + CLAVI : 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, + TANGO_ACCORDION : 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, + SYNTHSTRINGS_1 : 50, + SYNTHSTRINGS_2 : 51, + CHOIR_AAHS : 52, + VOICE_OOHS : 53, + SYNTH_VOICE : 54, + ORCHESTRA_HIT : 55, + TRUMPET : 56, + TROMBONE : 57, + TUBA : 58, + MUTED_TRUMPET : 59, + FRENCH_HORN : 60, + BRASS_SECTION : 61, + SYNTHBRASS_1 : 62, + SYNTHBRASS_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_AND_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_SCIFI : 103, + SITAR : 104, + BANJO : 105, + SHAMISEN : 106, + KOTO : 107, + KALIMBA : 108, + BAG_PIPE : 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_RING : 124, + HELICOPTER : 125, + APPLAUSE : 126, + GUNSHOT : 127, +}; diff --git a/gmdrums.js b/gmdrums.js new file mode 100644 index 0000000..bcad87c --- /dev/null +++ b/gmdrums.js @@ -0,0 +1,51 @@ +// General MIDI drum patches, track 10 +module.exports = { + + ACOUSTIC_BASS_DRUM : 35, + BASS_DRUM : 36, + SIDE_STICK : 37, + ACOUSTIC_SNARE : 38, + HAND_CLAP : 39, + ELECTRIC_SNARE : 40, + LOW_FLOOR_TOM : 41, + CLOSED_HI_HAT : 42, + HIGH_FLOOR_TOM : 43, + PEDAL_HI_HAT : 44, + LOW_TOM : 45, + OPEN_HI_HAT : 46, + LOW_MID_TOM : 47, + HI_MID_TOM : 48, + CRASH_CYMBAL_1 : 49, + HIGH_TOM : 50, + RIDE_CYMBAL_1 : 51, + CHINESE_CYMBAL : 52, + RIDE_BELL : 53, + TAMBOURINE : 54, + SPLASH_CYMBAL : 55, + COWBELL : 56, + CRASH_CYMBAL_2 : 57, + VIBRA_SLAP : 58, + RIDE_CYMBAL_2 : 59, + HI_BONGO : 60, + LOW_BONGO : 61, + MUTE_HI_CONGA : 62, + OPEN_HI_CONGA : 63, + LOW_CONGA : 64, + HIGH_TIMBALE : 65, + LOW_TIMBALE : 66, + HIGH_AGOGO : 67, + LOW_AGOGO : 68, + CABASA : 69, + MARACAS : 70, + SHORT_WHISTLE : 71, + LONG_WHISTLE : 72, + SHORT_GUIRO : 73, + LONG_GUIRO : 74, + CLAVES : 75, + HI_WOOD_BLOCK : 76, + LOW_WOOD_BLOCK : 77, + MUTE_CUICA : 78, + OPEN_CUICA : 79, + MUTE_TRIANGLE : 80, + OPEN_TRIANGLE : 81, +}; diff --git a/midi.js b/midi.js index f8d1089..584a6d6 100644 --- a/midi.js +++ b/midi.js @@ -7,6 +7,13 @@ const Stream = require('stream'); // MIDI input inherits from EventEmitter const { EventEmitter } = require('events'); +// Bring in the set of constants to reflect MIDI messages and their +// parameters, to eliminate the need for magic numbers. +const gm = require('./gm'); +const gmdrums = require('./gmdrums'); +const notes = require('./notes'); +const msg = require('./msg'); + class Input extends EventEmitter { constructor() { super() @@ -151,6 +158,11 @@ module.exports = { Input, Output, + gm, + gmdrums, + notes, + msg, + createReadStream, createWriteStream, diff --git a/msg.js b/msg.js new file mode 100644 index 0000000..600f99c --- /dev/null +++ b/msg.js @@ -0,0 +1,244 @@ +// MIDI messages +module.exports = { + + // All basic messages are held in the root, because of how commonly they're + // used. Luckily, there's no name clashes with sub-names + NOTE_OFF: 0x80, /* [ pitch, volume ] */ + NOTE_ON: 0x90, /* [ pitch, volume ] */ + NOTE_KEY_PRESSURE: 0xa0, /* [ pitch, pressure (after touch) ] */ + SET_PARAMETER: 0xb0, /* [ param number (CC), setting ] */ + SET_PROGRAM: 0xc0, /* [ program ] */ + CHANGE_PRESSURE: 0xd0, /* [ pressure (after touch) ] */ + SET_PITCHWHEEL: 0xe0, /* [ LSB, MSG (two 7 bit values) ] */ + + METAEVENT: 0xff, + SYS_EX1: 0xf0, + SYS_EX2: 0xf7, + + /* Alternative names */ + PATCH_CHANGE: 0xc0, /* Equivalent to SET_PROGRAM */ + CONTROL_CHANGE: 0xb0, /* Equivalent to SET_PARAMETER */ + + SYSMASK: 0xf0, + + + // Control changes + cc: { + /* 0-31, where defined, all indicate the MSB */ + BANK_SELECT: 0, + MODULATION: 1, + BREATH_CONTROL: 2, + UNDEFINED_3: 3, + FOOT_CONTROL: 4, + PORTAMENTO_TIME: 5, + DATE_ENTRY: 6, + VOLUME: 7, + BALANCE: 8, + UNDEFINED_9: 9, + PAN: 10, + EXPRESSION: 11, + EFFECT_CONTROL_1: 12, + EFFECT_CONTROL_2: 13, + UNDEFINED_14: 14, + UNDEFINED_15: 15, + GENERAL_PURPOSE_1: 16, + GENERAL_PURPOSE_2: 17, + GENERAL_PURPOSE_3: 18, + GENERAL_PURPOSE_4: 19, + + /* 20-31 are undefined */ + UNDEFINED_20: 20, + UNDEFINED_21: 21, + UNDEFINED_22: 22, + UNDEFINED_23: 23, + UNDEFINED_24: 24, + UNDEFINED_25: 25, + UNDEFINED_26: 26, + UNDEFINED_27: 27, + UNDEFINED_28: 28, + UNDEFINED_29: 29, + UNDEFINED_30: 30, + UNDEFINED_31: 31, + + /* LSB for control changes 0-31 32-63 */ + BANK_SELECT_LSB: 32, + MODULATION_LSB: 33, + BREATH_CONTROL_LSB: 34, + UNDEFINED_35: 35, + FOOT_CONTROL_LSB: 36, + PORTAMENTO_TIME_LSB: 37, + DATA_ENTRY_LSB: 38, + VOLUME_LSB: 39, + BALANCE_LSB: 40, + UNDEFINED_41: 41, + PAN_LSB: 42, + EXPRESSION_LSB: 43, + EFFECT_CONTROL_1_LSB: 44, + EFFECT_CONTROL_2_LSB: 45, + + UNDEFINED_46: 46, + UNDEFINED_47: 47, + GENERAL_PURPOSE_1_LSB: 48, + GENERAL_PURPOSE_2_LSB: 49, + GENERAL_PURPOSE_3_LSB: 50, + GENERAL_PURPOSE_4_LSB: 51, + + /* 52-63 are undefined */ + UNDEFINED_52: 52, + UNDEFINED_53: 53, + UNDEFINED_54: 54, + UNDEFINED_55: 55, + UNDEFINED_56: 56, + UNDEFINED_57: 57, + UNDEFINED_58: 58, + UNDEFINED_59: 59, + UNDEFINED_60: 60, + UNDEFINED_61: 61, + UNDEFINED_62: 62, + UNDEFINED_63: 63, + + SUSTAIN_PEDAL: 64, + PORTAMENTO: 65, + PEDAL_SUSTENUTO: 66, + PEDAL_SOFT: 67, + LEGATO_FOOT_SWITCH: 68, + HOLD_2: 69, + SOUND_VARIATION: 70, + TIMBRE: 71, + RELEASE_TIME: 72, + ATTACKTIME: 73, + BRIGHTNESS: 74, + REVERB: 75, + DELAY: 76, + PITCH_TRANSPOSE: 77, + FLANGE: 78, + SPECIAL_FX: 79, + + GENERAL_PURPOSE_5_LSB: 80, + GENERAL_PURPOSE_6_LSB: 81, + GENERAL_PURPOSE_7_LSB: 82, + GENERAL_PURPOSE_8_LSB: 83, + + PORTAMENTO_CONTROL: 84, + + /* 85-90 are undefined */ + UNDEFINED_85: 85, + UNDEFINED_86: 86, + UNDEFINED_87: 87, + UNDEFINED_88: 88, + UNDEFINED_89: 89, + UNDEFINED_90: 90, + + /* Effects depth */ + FX_DEPTH: 91, + TREMELO_DEPTH: 92, + CHORUS_DEPTH: 93, + CELESTA_DEPTH: 94, + PHASER_DEPTH: 95, + DATA_INC: 96, + DATA_DEC: 97, + NON_REG_PARAM_LSB: 98, + NON_REG_PARAM_MSB: 99, + REG_PARAM_LSB: 100, + REG_PARAM_MSB: 101, + + /* 102-119 are undefined */ + UNDEFINED_102: 102, + UNDEFINED_103: 103, + UNDEFINED_104: 104, + UNDEFINED_105: 105, + UNDEFINED_106: 106, + UNDEFINED_107: 107, + UNDEFINED_108: 108, + UNDEFINED_109: 109, + UNDEFINED_110: 110, + UNDEFINED_111: 111, + UNDEFINED_112: 112, + UNDEFINED_113: 113, + UNDEFINED_114: 114, + UNDEFINED_115: 115, + UNDEFINED_116: 116, + UNDEFINED_117: 117, + UNDEFINED_118: 118, + UNDEFINED_119: 119, + + // Modes + ALL_SOUND_OFF: 120, + RESET_ALL_CONTROLLERS: 121, + LOCAL_CONTROL: 122, + ALL_NOTES_OFF: 123, + OMNI_MODE_OFF: 124, + OMNI_MODE_ON: 125, + MONO_MODE_ON: 126, + POLY_MODE_ON: 127, + + /* Alternative names */ + MOD_WHEEL: 1, + + /* All sound controllers have only LSB */ + HARM_CONTENT: 71, + SOUND_CONTROLLER_1: 70, + SOUND_CONTROLLER_2: 71, + SOUND_CONTROLLER_3: 72, + SOUND_CONTROLLER_4: 73, + SOUND_CONTROLLER_5: 74, + SOUND_CONTROLLER_6: 75, + SOUND_CONTROLLER_7: 76, + SOUND_CONTROLLER_8: 77, + SOUND_CONTROLLER_9: 78, + SOUND_CONTROLLER_10: 79, + + EFFECT_1_DEPTH: 91, + EFFECT_2_DEPTH: 92, + EFFECT_3_DEPTH: 93, + EFFECT_4_DEPTH: 94, + EFFECT_5_DEPTH: 95, + + DETUNE_DEPTH: 94, + }, + + + /* + ** System Common (Status byte: 1111 0ttt) + */ + syscommon: { + UNDEFINED_1: 0xf1, + SONG_POSITION: 0xf2, /* [LSB, MSB] */ + SONG_SELECT: 0xf3, + UNDEFINED_4: 0xf4, + UNDEFINED_5: 0xf5, + TUNE_REQUEST: 0xf6, + EOX: 0xf7, /* End of Exclusive */ + }, + + + /* + ** System Real Time (Status byte: 1111 1ttt) + */ + realtime: { + TIMING_CLOCK: 0xf8, + UNDEFINED_F9: 0xf9, + START: 0xfa, + CONTINUE: 0xfb, + STOP: 0xfc, + UNDEFINED_FD: 0xfd, + ACTIVE_SENSING: 0xfe, + SYSTEM_RESET: 0xff, + }, + + + /* + ** System Exclusive (Status byte: 1111 0000) + ** + ** The first byte of a sysex must be the identification number + ** (7 bits, MSB=0). This is followed by an arbitary number of + ** data bytes (all MSB=0), and ending in the sexEOX msg. + ** Note: Any other status byte (where MSB=1) will also terminate + ** a sysex message, with the exception of the System Real Time + ** events above. + */ + sysex: { + EOX: 0xf7, /* End of Exclusive */ + }, + +}; diff --git a/notes.js b/notes.js new file mode 100644 index 0000000..cd5345d --- /dev/null +++ b/notes.js @@ -0,0 +1,50 @@ +// MIDI notes +module.exports = { + + /* + ** The MIDI spec only indicates middle C to be + ** 60. It doesn't indicate which octave this is. + ** Some may consider 4, if they label octaves + ** from -1, instead of 0. I have adopted on octave + ** number C here. + */ + MIDDLE_C : 60, + + // Relative offsets + C : 0, + C_SHARP : 1, + C_FLAT : -11, + D : 2, + D_SHARP : 3, + D_FLAT : 1, + E : 4, + E_SHARP : 5, + E_FLAT : 3, + F : 5, + F_SHARP : 6, + F_FLAT : 5, + G : 7, + G_SHARP : 8, + G_FLAT : 6, + A : 9, + A_SHARP : 10, + A_FLAT : 8, + B : 11, + B_SHARP : 12, + B_FLAT : 10, + + // For the octaves + C0 : 0, + C1 : 12, + C2 : 24, + C3 : 36, + C4 : 48, + C5 : 60, + C6 : 72, + C7 : 84, + C8 : 96, + C9 : 108, + C10 : 120, + +}; + From 0627a74b2c1e545cc90854de451110b49ef9424e Mon Sep 17 00:00:00 2001 From: Steven Goodwin Date: Wed, 3 Jul 2024 14:36:21 +0100 Subject: [PATCH 2/3] Change the naming of the constants --- gmdrums.js => drums.js | 0 gm.js => instruments.js | 0 msg.js => messages.js | 2 +- midi.js | 20 +++++++++++--------- 4 files changed, 12 insertions(+), 10 deletions(-) rename gmdrums.js => drums.js (100%) rename gm.js => instruments.js (100%) rename msg.js => messages.js (99%) diff --git a/gmdrums.js b/drums.js similarity index 100% rename from gmdrums.js rename to drums.js diff --git a/gm.js b/instruments.js similarity index 100% rename from gm.js rename to instruments.js diff --git a/msg.js b/messages.js similarity index 99% rename from msg.js rename to messages.js index 600f99c..363c7f0 100644 --- a/msg.js +++ b/messages.js @@ -9,7 +9,7 @@ module.exports = { SET_PARAMETER: 0xb0, /* [ param number (CC), setting ] */ SET_PROGRAM: 0xc0, /* [ program ] */ CHANGE_PRESSURE: 0xd0, /* [ pressure (after touch) ] */ - SET_PITCHWHEEL: 0xe0, /* [ LSB, MSG (two 7 bit values) ] */ + SET_PITCHWHEEL: 0xe0, /* [ LSB, MSB (two 7 bit values) ] */ METAEVENT: 0xff, SYS_EX1: 0xf0, diff --git a/midi.js b/midi.js index 584a6d6..9cf137b 100644 --- a/midi.js +++ b/midi.js @@ -9,10 +9,10 @@ const { EventEmitter } = require('events'); // Bring in the set of constants to reflect MIDI messages and their // parameters, to eliminate the need for magic numbers. -const gm = require('./gm'); -const gmdrums = require('./gmdrums'); -const notes = require('./notes'); -const msg = require('./msg'); +const Instruments = require('./instruments'); +const Drums = require('./drums'); +const Notes = require('./notes'); +const Messages = require('./messages'); class Input extends EventEmitter { constructor() { @@ -158,13 +158,15 @@ module.exports = { Input, Output, - gm, - gmdrums, - notes, - msg, - createReadStream, createWriteStream, + + Constants: { + Instruments, + Drums, + Notes, + Messages, + }, // Backwards compatibility. input: Input, From c74717dcac8a6d0e7c40f53b38f6adfe70b11804 Mon Sep 17 00:00:00 2001 From: Steven Goodwin Date: Wed, 3 Jul 2024 14:45:24 +0100 Subject: [PATCH 3/3] Add JSdoc comments to constants --- midi.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/midi.js b/midi.js index 9cf137b..de9464f 100644 --- a/midi.js +++ b/midi.js @@ -9,9 +9,13 @@ const { EventEmitter } = require('events'); // Bring in the set of constants to reflect MIDI messages and their // parameters, to eliminate the need for magic numbers. +/** An instrument list, only valid in the General MIDI standard */ const Instruments = require('./instruments'); +/** A drum map, only valid in the General MIDI standard */ const Drums = require('./drums'); +/** Note descriptions, with Middle C = C5 = MIDI note 60 */ const Notes = require('./notes'); +/** Message names, including CCs */ const Messages = require('./messages'); class Input extends EventEmitter {