├── .gitignore ├── LICENSE ├── README.md ├── examples ├── input.js ├── list_ports.js ├── monitor_all_inputs.js ├── mtc.js └── output.js ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json └── tests └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tom Dinchak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-easymidi 2 | node-easymidi is a simple event-based MIDI messaging wrapper for [node-midi](https://github.com/justinlatimer/node-midi). 3 | 4 | # Installation 5 | Install with NPM: 6 | 7 | ``` 8 | npm install easymidi 9 | ``` 10 | 11 | # Usage Overview 12 | The module can interface with existing MIDI inputs/outputs or create virtual inputs/outputs. Here's a simple example to listen for note on events from an existing MIDI input: 13 | 14 | ```javascript 15 | const easymidi = require('easymidi'); 16 | const input = new easymidi.Input('MIDI Input Name'); 17 | input.on('noteon', function (msg) { 18 | // do something with msg 19 | }); 20 | ``` 21 | 22 | Here's an example of sending a note on message to an existing MIDI output: 23 | 24 | ```javascript 25 | const easymidi = require('easymidi'); 26 | const output = new easymidi.Output('MIDI Output Name'); 27 | output.send('noteon', { 28 | note: 64, 29 | velocity: 127, 30 | channel: 3 31 | }); 32 | ``` 33 | 34 | The Input and Output objects are [EventEmitters](http://nodejs.org/api/events.html#events_class_events_eventemitter) and you can use the EventEmitter functions such as `once()`, `removeListener()`, and `removeAllListeners()` as well. 35 | 36 | # Virtual Devices 37 | Virtual devices can be created by passing a `true` argument to the Input or Output constructors: 38 | 39 | ```javascript 40 | const virtualInput = new easymidi.Input('Virtual input name', true); 41 | const virtualOutput = new easymidi.Output('Virtual output name', true); 42 | ``` 43 | 44 | # Device Lists 45 | You can get an array of existing MIDI input or output names using the `getInputs()` and `getOutputs` functions: 46 | 47 | ```javascript 48 | const inputs = easymidi.getInputs(); 49 | const outputs = easymidi.getOutputs(); 50 | ``` 51 | 52 | # Closing Devices 53 | When you're finished with a MIDI device you can `close()` it: 54 | 55 | ```javascript 56 | const input = new easymidi.Input('My input', true); 57 | input.close(); 58 | 59 | const output = new easymidi.Output('My output', true); 60 | output.close(); 61 | ``` 62 | 63 | # Message Reference 64 | The following table describes the MIDI message types that are supported and the parameters of each: 65 | 66 | | Type | Parameter | Parameter | Parameter | 67 | |--------------------|--------------------|------------------|----------------| 68 | | noteon | note [0-127] | velocity [0-127] | channel [0-15] | 69 | | noteoff | note [0-127] | velocity [0-127] | channel [0-15] | 70 | | poly aftertouch | note [0-127] | velocity [0-127] | channel [0-15] | 71 | | cc | controller [0-127] | value [0-127] | channel [0-15] | 72 | | program | number [0-127] | | channel [0-15] | 73 | | channel aftertouch | pressure [0-127] | | channel [0-15] | 74 | | pitch | value [0-16384] | | channel [0-15] | 75 | | position | value [0-16384] | | | 76 | | mtc | type [0-7] | value [0-15] | | 77 | | select | song [0-127] | | | 78 | | clock | | | | 79 | | start | | | | 80 | | continue | | | | 81 | | stop | | | | 82 | | activesense | | | | 83 | | reset | | | | 84 | | sysex | bytes (variable length array) | | | 85 | 86 | # Examples 87 | 88 | Receive a noteon message: 89 | 90 | ```javascript 91 | input.on('noteon', function (params) { 92 | // params = {note: ..., velocity: ..., channel: ...} 93 | }); 94 | ``` 95 | 96 | Send a control change message: 97 | 98 | ```javascript 99 | output.send('cc', { 100 | controller: 37, 101 | value: 80, 102 | channel: 0 103 | }) 104 | ``` 105 | 106 | Listen for midi clock messages: 107 | 108 | ```javascript 109 | input.on('clock', function () { 110 | // do something on every clock tick 111 | }); 112 | ``` 113 | 114 | Send a sysex message. 115 | Throws an error if array does not start with 0xf0 (240) and end with 0xf7 (247). 116 | ```javascript 117 | output.send('sysex',[240, 126, 1, 6, 1, 247]); 118 | ``` 119 | 120 | See the [example programs](https://github.com/dinchak/node-easymidi/tree/master/examples) for more examples. 121 | -------------------------------------------------------------------------------- /examples/input.js: -------------------------------------------------------------------------------- 1 | const easymidi = require('../index.js'); 2 | 3 | const INPUT_NAME = 'YOUR_INPUT_NAME'; 4 | 5 | const input = new easymidi.Input(INPUT_NAME); 6 | 7 | input.on('noteoff', msg => console.log('noteoff', msg.note, msg.velocity, msg.channel)); 8 | 9 | input.on('noteon', msg => console.log('noteon', msg.note, msg.velocity, msg.channel)); 10 | 11 | input.on('poly aftertouch', msg => console.log('poly aftertouch', msg.note, msg.pressure, msg.channel)); 12 | 13 | input.on('cc', msg => console.log('cc', msg.controller, msg.value, msg.channel)); 14 | 15 | input.on('program', msg => console.log('program', msg.number, msg.channel)); 16 | 17 | input.on('channel aftertouch', msg => console.log('channel aftertouch', msg.pressure, msg.channel)); 18 | 19 | input.on('pitch', msg => console.log('pitch', msg.value, msg.channel)); 20 | 21 | input.on('position', msg => console.log('position', msg.value)); 22 | 23 | input.on('select', msg => console.log('select', msg.song)); 24 | 25 | input.on('clock', () => console.log('clock')); 26 | 27 | input.on('start', () => console.log('start')) 28 | 29 | input.on('continue', () => console.log('continue')); 30 | 31 | input.on('stop', () => console.log('stop')); 32 | 33 | input.on('reset', () => console.log('reset')); 34 | -------------------------------------------------------------------------------- /examples/list_ports.js: -------------------------------------------------------------------------------- 1 | const easymidi = require('../index.js'); 2 | 3 | console.log('MIDI inputs:'); 4 | console.log(easymidi.getInputs()); 5 | 6 | console.log('MIDI outputs:'); 7 | console.log(easymidi.getOutputs()); 8 | -------------------------------------------------------------------------------- /examples/monitor_all_inputs.js: -------------------------------------------------------------------------------- 1 | const easymidi = require('../index.js'); 2 | 3 | // Monitor all MIDI inputs with a single "message" listener 4 | easymidi.getInputs().forEach((inputName) => { 5 | const input = new easymidi.Input(inputName); 6 | input.on('message', (msg) => { 7 | const vals = Object.keys(msg).map((key) => `${key}: ${msg[key]}`); 8 | console.log(`${inputName}: ${vals.join(', ')}`); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/mtc.js: -------------------------------------------------------------------------------- 1 | const easymidi = require('../index.js'); 2 | 3 | // Connect to first available midi port 4 | const inputs = easymidi.getInputs(); 5 | if (inputs.length <= 0) { 6 | console.log('No midi device found'); 7 | process.exit(1); 8 | } 9 | console.log('Connecting to: ' + inputs[0]); 10 | const input = new easymidi.Input(inputs[0]); 11 | 12 | // SMPTE Types 13 | // 0 = 24 fps 14 | // 1 = 25 fps 15 | // 2 = 30 fps (Drop-Frame) 16 | // 3 = 30 fps 17 | input.on('smpte', (smpte) => { 18 | console.log(`smpte: ${smpte.smpte} – smpteType: ${smpte.smpteType}`); 19 | }); 20 | -------------------------------------------------------------------------------- /examples/output.js: -------------------------------------------------------------------------------- 1 | const easymidi = require('../index'); 2 | 3 | const OUTPUT_NAME = 'YOUR_OUTPUT_NAME'; 4 | 5 | const output = new easymidi.Output(OUTPUT_NAME); 6 | 7 | output.send('noteoff', { 8 | note: 64, 9 | velocity: 0, 10 | channel: 0 11 | }); 12 | 13 | output.send('noteon', { 14 | note: 64, 15 | velocity: 127, 16 | channel: 0 17 | }); 18 | 19 | output.send('cc', { 20 | controller: 64, 21 | value: 127, 22 | channel: 0 23 | }); 24 | 25 | output.send('poly aftertouch', { 26 | note: 64, 27 | pressure: 127, 28 | channel: 0 29 | }); 30 | 31 | output.send('channel aftertouch', { 32 | pressure: 127, 33 | channel: 0 34 | }); 35 | 36 | output.send('program', { 37 | number: 2, 38 | channel: 0 39 | }); 40 | 41 | output.send('pitch', { 42 | value: 12345, 43 | channel: 0 44 | }); 45 | 46 | output.send('position', { 47 | value: 12345 48 | }); 49 | 50 | output.send('select', { 51 | song: 10 52 | }); 53 | 54 | output.send('clock'); 55 | 56 | output.send('start'); 57 | 58 | output.send('continue'); 59 | 60 | output.send('stop'); 61 | 62 | output.send('reset'); 63 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "events"; 2 | 3 | export type Channel = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15; 4 | 5 | export interface Note { 6 | note: number; 7 | velocity: number; 8 | channel: Channel; 9 | } 10 | 11 | export interface PolyAfterTouch { 12 | note: number; 13 | pressure: number; 14 | channel: Channel; 15 | } 16 | 17 | export interface ControlChange { 18 | controller: number; 19 | value: number; 20 | channel: Channel; 21 | } 22 | 23 | export interface Program { 24 | number: number; 25 | channel: Channel; 26 | } 27 | 28 | export interface ChannelAfterTouch { 29 | pressure: number; 30 | channel: Channel; 31 | } 32 | 33 | export interface Pitch { 34 | value: number; 35 | channel: Channel; 36 | } 37 | 38 | export interface Position { 39 | value: number; 40 | } 41 | 42 | export interface Mtc { 43 | type: number; 44 | value: number; 45 | } 46 | 47 | export interface Select { 48 | song: number; 49 | } 50 | 51 | export interface Sysex { 52 | bytes: number[]; 53 | } 54 | 55 | export declare class Input extends EventEmitter { 56 | constructor(name: string, virtual?: boolean); 57 | name: string; 58 | on(evt: "noteon" | "noteoff", handler: (param: Note) => void): this; 59 | on(evt: "poly aftertouch", handler: (param: PolyAfterTouch) => void): this; 60 | on(evt: "cc", handler: (param: ControlChange) => void): this; 61 | on(evt: "program", handler: (param: Program) => void): this; 62 | on(evt: "channel aftertouch", handler: (param: ChannelAfterTouch) => void): this; 63 | on(evt: "pitch", handler: (param: Pitch) => void): this; 64 | on(evt: "position", handler: (param: Position) => void): this; 65 | on(evt: "mtc", handler: (param: Mtc) => void): this; 66 | on(evt: "select", handler: (param: Select) => void): this; 67 | on(evt: "clock", handler: () => void): this; 68 | on(evt: "start", handler: () => void): this; 69 | on(evt: "continue", handler: () => void): this; 70 | on(evt: "stop", handler: () => void): this; 71 | on(evt: "activesense", handler: () => void): this; 72 | on(evt: "reset", handler: () => void): this; 73 | on(evt: "sysex", handler: (param: Sysex) => void): this; 74 | close(): void; 75 | isPortOpen(): boolean; 76 | } 77 | 78 | export declare class Output { 79 | constructor(name: string, virtual?: boolean); 80 | name: string; 81 | send(evt: "noteon", param: Note): void; 82 | send(evt: "noteoff", param: Note): void; 83 | send(evt: "poly aftertouch", param: PolyAfterTouch): void; 84 | send(evt: "cc", param: ControlChange): void; 85 | send(evt: "program", param: Program): void; 86 | send(evt: "channel aftertouch", param: ChannelAfterTouch): void; 87 | send(evt: "pitch", param: Pitch): void; 88 | send(evt: "position", param: Position): void; 89 | send(evt: "mtc", param: Mtc): void; 90 | send(evt: "select", param: Select): void; 91 | send(evt: "clock"): void; 92 | send(evt: "start"): void; 93 | send(evt: "continue"): void; 94 | send(evt: "stop"): void; 95 | send(evt: "activesense"): void; 96 | send(evt: "reset"): void; 97 | send(evt: "sysex", param: Array): void; 98 | close(): void; 99 | isPortOpen(): boolean; 100 | } 101 | 102 | export declare function getInputs(): string[]; 103 | export declare function getOutputs(): string[]; 104 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const midi = require('@julusian/midi'); 2 | const EventEmitter = require('events').EventEmitter; 3 | 4 | const swap = (obj) => Object.entries(obj).reduce((acc, [key, value]) => { 5 | acc[value] = key; 6 | return acc; 7 | }, {}); 8 | 9 | const INPUT_TYPES = { 10 | 0x08: 'noteoff', 11 | 0x09: 'noteon', 12 | 0x0A: 'poly aftertouch', 13 | 0x0B: 'cc', 14 | 0x0C: 'program', 15 | 0x0D: 'channel aftertouch', 16 | 0x0E: 'pitch', 17 | }; 18 | const INPUT_EXTENDED_TYPES = { 19 | 0xF0: 'sysex', 20 | 0xF1: 'mtc', 21 | 0xF2: 'position', 22 | 0xF3: 'select', 23 | 0xF6: 'tune', 24 | 0xF7: 'sysex end', 25 | 0xF8: 'clock', 26 | 0xFA: 'start', 27 | 0xFB: 'continue', 28 | 0xFC: 'stop', 29 | 0xFE: 'activesense', 30 | 0xFF: 'reset' 31 | }; 32 | const OUTPUT_TYPES = swap(INPUT_TYPES); 33 | const OUTPUT_EXTENDED_TYPES = swap(INPUT_EXTENDED_TYPES); 34 | 35 | class Input extends EventEmitter { 36 | constructor(name, virtual) { 37 | super(); 38 | this._input = new midi.input(); 39 | this._input.ignoreTypes(false, false, false); 40 | this._pendingSysex = false; 41 | this._sysex = []; 42 | this.name = name; 43 | this.inputPortNumberedNames = getInputs(); 44 | 45 | if (virtual) { 46 | this._input.openVirtualPort(name); 47 | } else { 48 | const numInputs = this._input.getPortCount(); 49 | let found = false; 50 | for (let i = 0; i < numInputs; i++) { 51 | if (name === this.inputPortNumberedNames[i]) { 52 | found = true; 53 | this._input.openPort(i); 54 | } 55 | } 56 | if (!found) { 57 | throw new Error('No MIDI input found with name: ' + name); 58 | } 59 | } 60 | 61 | this._input.on('message', (deltaTime, bytes) => { 62 | // a long sysex can be sent in multiple chunks, depending on the RtMidi buffer size 63 | let proceed = true; 64 | if (this._pendingSysex && (bytes.length > 0)) { 65 | if (bytes[0] < 0x80) { 66 | this._sysex = this._sysex.concat(bytes); 67 | if (bytes[bytes.length - 1] === 0xf7) { 68 | const msg = { _type: 'sysex', bytes: this._sysex }; 69 | this.emit('sysex', msg); 70 | this.emit('message', msg); 71 | sysex = []; 72 | this._pendingSysex = false; 73 | } 74 | proceed = false; 75 | } 76 | else { 77 | // ignore invalid sysex messages 78 | this._sysex = []; 79 | this._pendingSysex = false; 80 | } 81 | } 82 | if (proceed) { 83 | const data = this.parseMessage(bytes); 84 | if ((data.type === 'sysex') && (bytes[bytes.length - 1] !== 0xf7)) { 85 | this._sysex = [...bytes]; 86 | this._pendingSysex = true; 87 | } 88 | else { 89 | data.msg._type = data.type; // easy access to message type 90 | this.emit(data.type, data.msg); 91 | // also emit "message" event, to allow easy monitoring of all messages 92 | this.emit('message', data.msg); 93 | if (data.type === 'mtc') { 94 | this.parseMtc(data.msg); 95 | } 96 | } 97 | } 98 | }); 99 | } 100 | 101 | close() { 102 | this._input.closePort(); 103 | } 104 | 105 | isPortOpen() { 106 | return this._input.isPortOpen(); 107 | } 108 | 109 | parseMtc(data) { 110 | const byteNumber = data.type; 111 | const smpte = []; 112 | let value = data.value; 113 | let smpteMessageCounter = 0; 114 | let smpteType; 115 | 116 | if (byteNumber === 7) { 117 | const bits = []; 118 | for (let i = 3; i >= 0; i--) { 119 | const bit = value & (1 << i) ? 1 : 0; 120 | bits.push(bit); 121 | } 122 | value = bits[3]; 123 | smpteType = (bits[1] * 2) + bits[2]; 124 | } 125 | smpte[byteNumber] = value; 126 | if (smpteMessageCounter !== 7) { 127 | smpteMessageCounter++; 128 | return; 129 | } 130 | if (byteNumber === 7) { 131 | const smpteFormatted = 132 | (smpte[7] * 16 + smpte[6]).toString().padStart(2, '0') 133 | + ':' 134 | + (smpte[5] * 16 + smpte[4]).toString().padStart(2, '0') 135 | + ':' 136 | + (smpte[3] * 16 + smpte[2]).toString().padStart(2, '0') 137 | + ':' 138 | + (smpte[1] * 16 + smpte[0]).toString().padStart(2, '0'); 139 | 140 | this.emit('smpte', { 141 | smpte: smpteFormatted, 142 | smpteType, 143 | }); 144 | } 145 | } 146 | 147 | parseMessage(bytes) { 148 | const msg = {}; 149 | let type = 'unknown'; 150 | 151 | if (bytes[0] >= 0xF0) { 152 | type = INPUT_EXTENDED_TYPES[bytes[0]]; 153 | } else { 154 | type = INPUT_TYPES[bytes[0] >> 4]; 155 | msg.channel = bytes[0] & 0xF; 156 | } 157 | if (type === 'noteoff' || type === 'noteon') { 158 | msg.note = bytes[1]; 159 | msg.velocity = bytes[2]; 160 | } 161 | if (type === 'cc') { 162 | msg.controller = bytes[1]; 163 | msg.value = bytes[2]; 164 | } 165 | if (type === 'poly aftertouch') { 166 | msg.note = bytes[1]; 167 | msg.pressure = bytes[2]; 168 | } 169 | if (type === 'channel aftertouch') { 170 | msg.pressure = bytes[1]; 171 | } 172 | if (type === 'program') { 173 | msg.number = bytes[1]; 174 | } 175 | if (type === 'pitch' || type === 'position') { 176 | msg.value = bytes[1] + (bytes[2] * 128); 177 | } 178 | if (type === 'sysex') { 179 | msg.bytes = bytes; 180 | } 181 | if (type === 'select') { 182 | msg.song = bytes[1]; 183 | } 184 | if (type === 'mtc') { 185 | msg.type = (bytes[1] >> 4) & 0x07; 186 | msg.value = bytes[1] & 0x0F; 187 | } 188 | return { 189 | type, 190 | msg, 191 | }; 192 | } 193 | } 194 | 195 | 196 | class Output { 197 | constructor(name, virtual) { 198 | this._output = new midi.output(); 199 | this.name = name; 200 | this.outputPortNumberedNames = getOutputs(); 201 | 202 | if (virtual) { 203 | this._output.openVirtualPort(name); 204 | } else { 205 | const numOutputs = this._output.getPortCount(); 206 | let found = false; 207 | for (let i = 0; i < numOutputs; i++) { 208 | if (name === this.outputPortNumberedNames[i]) { 209 | found = true; 210 | this._output.openPort(i); 211 | } 212 | } 213 | if (!found) { 214 | throw new Error('No MIDI output found with name: ' + name); 215 | } 216 | } 217 | } 218 | 219 | close() { 220 | this._output.closePort(); 221 | } 222 | 223 | isPortOpen() { 224 | return this._output.isPortOpen(); 225 | } 226 | 227 | send(type, args) { 228 | this._output.sendMessage(this.parseMessage(type, args)); 229 | } 230 | 231 | parseMessage(type, args) { 232 | const bytes = []; 233 | 234 | if (OUTPUT_TYPES[type]) { 235 | args.channel = args.channel || 0; 236 | bytes.push((OUTPUT_TYPES[type] << 4) + args.channel); 237 | } else if (OUTPUT_EXTENDED_TYPES[type]) { 238 | bytes.push(OUTPUT_EXTENDED_TYPES[type]); 239 | } else { 240 | throw new Error('Unknown midi message type: ' + type); 241 | } 242 | 243 | if (type === 'noteoff' || type === 'noteon') { 244 | bytes.push(args.note); 245 | bytes.push(args.velocity); 246 | } 247 | if (type === 'cc') { 248 | bytes.push(args.controller); 249 | bytes.push(args.value); 250 | } 251 | if (type === 'poly aftertouch') { 252 | bytes.push(args.note); 253 | bytes.push(args.pressure); 254 | } 255 | if (type === 'channel aftertouch') { 256 | bytes.push(args.pressure); 257 | } 258 | if (type === 'program') { 259 | bytes.push(args.number); 260 | } 261 | if (type === 'pitch' || type === 'position') { 262 | bytes.push(args.value & 0x7F); // lsb 263 | bytes.push((args.value & 0x3F80) >> 7); // msb 264 | } 265 | if (type === 'mtc') { 266 | bytes.push((args.type << 4) + args.value); 267 | } 268 | if (type === 'select') { 269 | bytes.push(args.song); 270 | } 271 | if (type === 'sysex') { 272 | // sysex commands should start with 0xf0 and end with 0xf7. Throw an error if it doesn't. 273 | if (args.length <= 3 || args[0] !== 0xf0 || args[args.length - 1] !== 0xf7) { // 274 | throw new Error("sysex commands should be an array that starts with 0xf0 and end with 0xf7"); 275 | } 276 | args.slice(1).forEach((arg) => bytes.push(arg)); // 0xf0 was already added at the beginning of parseMessage. 277 | } 278 | return bytes; 279 | } 280 | }; 281 | 282 | // utilities 283 | const getInputs = () => { 284 | const input = new midi.input(); 285 | const inputs = []; 286 | for (let i = 0; i < input.getPortCount(); i++) { 287 | var counter = 0; 288 | const portName = input.getPortName(i); 289 | var numberedPortName = portName; 290 | while(inputs.includes(numberedPortName)) { 291 | counter++; 292 | numberedPortName = portName + counter; 293 | } 294 | inputs.push(numberedPortName); 295 | } 296 | input.closePort(); 297 | return inputs; 298 | } 299 | 300 | const getOutputs = () => { 301 | const output = new midi.output(); 302 | const outputs = []; 303 | for (let i = 0; i < output.getPortCount(); i++) { 304 | var counter = 0; 305 | const portName = output.getPortName(i); 306 | var numberedPortName = portName; 307 | while(outputs.includes(numberedPortName)) { 308 | counter++; 309 | numberedPortName = portName + counter; 310 | } 311 | outputs.push(numberedPortName); 312 | } 313 | output.closePort(); 314 | return outputs; 315 | } 316 | 317 | module.exports = { 318 | Input, 319 | getInputs, 320 | Output, 321 | getOutputs, 322 | }; 323 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "easymidi", 3 | "version": "3.1.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "easymidi", 9 | "version": "3.1.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@julusian/midi": "^3.0.0-3" 13 | }, 14 | "devDependencies": { 15 | "chai": "^4.3.6", 16 | "mocha": "^10.1.0" 17 | }, 18 | "engines": { 19 | "node": ">=14.15" 20 | } 21 | }, 22 | "node_modules/@julusian/midi": { 23 | "version": "3.0.1", 24 | "resolved": "https://registry.npmjs.org/@julusian/midi/-/midi-3.0.1.tgz", 25 | "integrity": "sha512-djrDIi/brNwo5ZU6PptxOT59C0TKEAU0veOXdUWUp6MGo9QJkrsL7iKlWNhXUhlcvEY+Sp0+lBphz2+f0uwhFA==", 26 | "hasInstallScript": true, 27 | "dependencies": { 28 | "node-addon-api": "^5.0.0", 29 | "pkg-prebuilds": "^0.1.0" 30 | }, 31 | "engines": { 32 | "node": ">=14.15" 33 | } 34 | }, 35 | "node_modules/ansi-colors": { 36 | "version": "4.1.1", 37 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 38 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 39 | "dev": true, 40 | "engines": { 41 | "node": ">=6" 42 | } 43 | }, 44 | "node_modules/ansi-regex": { 45 | "version": "5.0.1", 46 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 47 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 48 | "engines": { 49 | "node": ">=8" 50 | } 51 | }, 52 | "node_modules/ansi-styles": { 53 | "version": "4.3.0", 54 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 55 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 56 | "dependencies": { 57 | "color-convert": "^2.0.1" 58 | }, 59 | "engines": { 60 | "node": ">=8" 61 | }, 62 | "funding": { 63 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 64 | } 65 | }, 66 | "node_modules/anymatch": { 67 | "version": "3.1.3", 68 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 69 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 70 | "dev": true, 71 | "dependencies": { 72 | "normalize-path": "^3.0.0", 73 | "picomatch": "^2.0.4" 74 | }, 75 | "engines": { 76 | "node": ">= 8" 77 | } 78 | }, 79 | "node_modules/argparse": { 80 | "version": "2.0.1", 81 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 82 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 83 | "dev": true 84 | }, 85 | "node_modules/assertion-error": { 86 | "version": "1.1.0", 87 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 88 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 89 | "dev": true, 90 | "engines": { 91 | "node": "*" 92 | } 93 | }, 94 | "node_modules/balanced-match": { 95 | "version": "1.0.2", 96 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 97 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 98 | "dev": true 99 | }, 100 | "node_modules/binary-extensions": { 101 | "version": "2.2.0", 102 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 103 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 104 | "dev": true, 105 | "engines": { 106 | "node": ">=8" 107 | } 108 | }, 109 | "node_modules/brace-expansion": { 110 | "version": "2.0.1", 111 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 112 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 113 | "dev": true, 114 | "dependencies": { 115 | "balanced-match": "^1.0.0" 116 | } 117 | }, 118 | "node_modules/braces": { 119 | "version": "3.0.2", 120 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 121 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 122 | "dev": true, 123 | "dependencies": { 124 | "fill-range": "^7.0.1" 125 | }, 126 | "engines": { 127 | "node": ">=8" 128 | } 129 | }, 130 | "node_modules/browser-stdout": { 131 | "version": "1.3.1", 132 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 133 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 134 | "dev": true 135 | }, 136 | "node_modules/camelcase": { 137 | "version": "6.3.0", 138 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 139 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 140 | "dev": true, 141 | "engines": { 142 | "node": ">=10" 143 | }, 144 | "funding": { 145 | "url": "https://github.com/sponsors/sindresorhus" 146 | } 147 | }, 148 | "node_modules/chai": { 149 | "version": "4.3.7", 150 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", 151 | "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", 152 | "dev": true, 153 | "dependencies": { 154 | "assertion-error": "^1.1.0", 155 | "check-error": "^1.0.2", 156 | "deep-eql": "^4.1.2", 157 | "get-func-name": "^2.0.0", 158 | "loupe": "^2.3.1", 159 | "pathval": "^1.1.1", 160 | "type-detect": "^4.0.5" 161 | }, 162 | "engines": { 163 | "node": ">=4" 164 | } 165 | }, 166 | "node_modules/chalk": { 167 | "version": "4.1.2", 168 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 169 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 170 | "dev": true, 171 | "dependencies": { 172 | "ansi-styles": "^4.1.0", 173 | "supports-color": "^7.1.0" 174 | }, 175 | "engines": { 176 | "node": ">=10" 177 | }, 178 | "funding": { 179 | "url": "https://github.com/chalk/chalk?sponsor=1" 180 | } 181 | }, 182 | "node_modules/chalk/node_modules/supports-color": { 183 | "version": "7.2.0", 184 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 185 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 186 | "dev": true, 187 | "dependencies": { 188 | "has-flag": "^4.0.0" 189 | }, 190 | "engines": { 191 | "node": ">=8" 192 | } 193 | }, 194 | "node_modules/check-error": { 195 | "version": "1.0.2", 196 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 197 | "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", 198 | "dev": true, 199 | "engines": { 200 | "node": "*" 201 | } 202 | }, 203 | "node_modules/chokidar": { 204 | "version": "3.5.3", 205 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 206 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 207 | "dev": true, 208 | "funding": [ 209 | { 210 | "type": "individual", 211 | "url": "https://paulmillr.com/funding/" 212 | } 213 | ], 214 | "dependencies": { 215 | "anymatch": "~3.1.2", 216 | "braces": "~3.0.2", 217 | "glob-parent": "~5.1.2", 218 | "is-binary-path": "~2.1.0", 219 | "is-glob": "~4.0.1", 220 | "normalize-path": "~3.0.0", 221 | "readdirp": "~3.6.0" 222 | }, 223 | "engines": { 224 | "node": ">= 8.10.0" 225 | }, 226 | "optionalDependencies": { 227 | "fsevents": "~2.3.2" 228 | } 229 | }, 230 | "node_modules/cliui": { 231 | "version": "7.0.4", 232 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 233 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 234 | "dev": true, 235 | "dependencies": { 236 | "string-width": "^4.2.0", 237 | "strip-ansi": "^6.0.0", 238 | "wrap-ansi": "^7.0.0" 239 | } 240 | }, 241 | "node_modules/color-convert": { 242 | "version": "2.0.1", 243 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 244 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 245 | "dependencies": { 246 | "color-name": "~1.1.4" 247 | }, 248 | "engines": { 249 | "node": ">=7.0.0" 250 | } 251 | }, 252 | "node_modules/color-name": { 253 | "version": "1.1.4", 254 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 255 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 256 | }, 257 | "node_modules/concat-map": { 258 | "version": "0.0.1", 259 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 260 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 261 | "dev": true 262 | }, 263 | "node_modules/debug": { 264 | "version": "4.3.4", 265 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 266 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 267 | "dev": true, 268 | "dependencies": { 269 | "ms": "2.1.2" 270 | }, 271 | "engines": { 272 | "node": ">=6.0" 273 | }, 274 | "peerDependenciesMeta": { 275 | "supports-color": { 276 | "optional": true 277 | } 278 | } 279 | }, 280 | "node_modules/debug/node_modules/ms": { 281 | "version": "2.1.2", 282 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 283 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 284 | "dev": true 285 | }, 286 | "node_modules/decamelize": { 287 | "version": "4.0.0", 288 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 289 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 290 | "dev": true, 291 | "engines": { 292 | "node": ">=10" 293 | }, 294 | "funding": { 295 | "url": "https://github.com/sponsors/sindresorhus" 296 | } 297 | }, 298 | "node_modules/deep-eql": { 299 | "version": "4.1.3", 300 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", 301 | "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", 302 | "dev": true, 303 | "dependencies": { 304 | "type-detect": "^4.0.0" 305 | }, 306 | "engines": { 307 | "node": ">=6" 308 | } 309 | }, 310 | "node_modules/diff": { 311 | "version": "5.0.0", 312 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 313 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 314 | "dev": true, 315 | "engines": { 316 | "node": ">=0.3.1" 317 | } 318 | }, 319 | "node_modules/emoji-regex": { 320 | "version": "8.0.0", 321 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 322 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 323 | }, 324 | "node_modules/escalade": { 325 | "version": "3.1.1", 326 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 327 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 328 | "engines": { 329 | "node": ">=6" 330 | } 331 | }, 332 | "node_modules/escape-string-regexp": { 333 | "version": "4.0.0", 334 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 335 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 336 | "dev": true, 337 | "engines": { 338 | "node": ">=10" 339 | }, 340 | "funding": { 341 | "url": "https://github.com/sponsors/sindresorhus" 342 | } 343 | }, 344 | "node_modules/fill-range": { 345 | "version": "7.0.1", 346 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 347 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 348 | "dev": true, 349 | "dependencies": { 350 | "to-regex-range": "^5.0.1" 351 | }, 352 | "engines": { 353 | "node": ">=8" 354 | } 355 | }, 356 | "node_modules/find-up": { 357 | "version": "5.0.0", 358 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 359 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 360 | "dev": true, 361 | "dependencies": { 362 | "locate-path": "^6.0.0", 363 | "path-exists": "^4.0.0" 364 | }, 365 | "engines": { 366 | "node": ">=10" 367 | }, 368 | "funding": { 369 | "url": "https://github.com/sponsors/sindresorhus" 370 | } 371 | }, 372 | "node_modules/flat": { 373 | "version": "5.0.2", 374 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 375 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 376 | "dev": true, 377 | "bin": { 378 | "flat": "cli.js" 379 | } 380 | }, 381 | "node_modules/fs.realpath": { 382 | "version": "1.0.0", 383 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 384 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 385 | "dev": true 386 | }, 387 | "node_modules/fsevents": { 388 | "version": "2.3.2", 389 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 390 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 391 | "dev": true, 392 | "hasInstallScript": true, 393 | "optional": true, 394 | "os": [ 395 | "darwin" 396 | ], 397 | "engines": { 398 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 399 | } 400 | }, 401 | "node_modules/get-caller-file": { 402 | "version": "2.0.5", 403 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 404 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 405 | "engines": { 406 | "node": "6.* || 8.* || >= 10.*" 407 | } 408 | }, 409 | "node_modules/get-func-name": { 410 | "version": "2.0.0", 411 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 412 | "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", 413 | "dev": true, 414 | "engines": { 415 | "node": "*" 416 | } 417 | }, 418 | "node_modules/glob": { 419 | "version": "7.2.0", 420 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 421 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 422 | "dev": true, 423 | "dependencies": { 424 | "fs.realpath": "^1.0.0", 425 | "inflight": "^1.0.4", 426 | "inherits": "2", 427 | "minimatch": "^3.0.4", 428 | "once": "^1.3.0", 429 | "path-is-absolute": "^1.0.0" 430 | }, 431 | "engines": { 432 | "node": "*" 433 | }, 434 | "funding": { 435 | "url": "https://github.com/sponsors/isaacs" 436 | } 437 | }, 438 | "node_modules/glob-parent": { 439 | "version": "5.1.2", 440 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 441 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 442 | "dev": true, 443 | "dependencies": { 444 | "is-glob": "^4.0.1" 445 | }, 446 | "engines": { 447 | "node": ">= 6" 448 | } 449 | }, 450 | "node_modules/glob/node_modules/brace-expansion": { 451 | "version": "1.1.11", 452 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 453 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 454 | "dev": true, 455 | "dependencies": { 456 | "balanced-match": "^1.0.0", 457 | "concat-map": "0.0.1" 458 | } 459 | }, 460 | "node_modules/glob/node_modules/minimatch": { 461 | "version": "3.1.2", 462 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 463 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 464 | "dev": true, 465 | "dependencies": { 466 | "brace-expansion": "^1.1.7" 467 | }, 468 | "engines": { 469 | "node": "*" 470 | } 471 | }, 472 | "node_modules/has-flag": { 473 | "version": "4.0.0", 474 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 475 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 476 | "dev": true, 477 | "engines": { 478 | "node": ">=8" 479 | } 480 | }, 481 | "node_modules/he": { 482 | "version": "1.2.0", 483 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 484 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 485 | "dev": true, 486 | "bin": { 487 | "he": "bin/he" 488 | } 489 | }, 490 | "node_modules/inflight": { 491 | "version": "1.0.6", 492 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 493 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 494 | "dev": true, 495 | "dependencies": { 496 | "once": "^1.3.0", 497 | "wrappy": "1" 498 | } 499 | }, 500 | "node_modules/inherits": { 501 | "version": "2.0.4", 502 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 503 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 504 | "dev": true 505 | }, 506 | "node_modules/is-binary-path": { 507 | "version": "2.1.0", 508 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 509 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 510 | "dev": true, 511 | "dependencies": { 512 | "binary-extensions": "^2.0.0" 513 | }, 514 | "engines": { 515 | "node": ">=8" 516 | } 517 | }, 518 | "node_modules/is-extglob": { 519 | "version": "2.1.1", 520 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 521 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 522 | "dev": true, 523 | "engines": { 524 | "node": ">=0.10.0" 525 | } 526 | }, 527 | "node_modules/is-fullwidth-code-point": { 528 | "version": "3.0.0", 529 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 530 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 531 | "engines": { 532 | "node": ">=8" 533 | } 534 | }, 535 | "node_modules/is-glob": { 536 | "version": "4.0.3", 537 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 538 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 539 | "dev": true, 540 | "dependencies": { 541 | "is-extglob": "^2.1.1" 542 | }, 543 | "engines": { 544 | "node": ">=0.10.0" 545 | } 546 | }, 547 | "node_modules/is-number": { 548 | "version": "7.0.0", 549 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 550 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 551 | "dev": true, 552 | "engines": { 553 | "node": ">=0.12.0" 554 | } 555 | }, 556 | "node_modules/is-plain-obj": { 557 | "version": "2.1.0", 558 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 559 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 560 | "dev": true, 561 | "engines": { 562 | "node": ">=8" 563 | } 564 | }, 565 | "node_modules/is-unicode-supported": { 566 | "version": "0.1.0", 567 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 568 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 569 | "dev": true, 570 | "engines": { 571 | "node": ">=10" 572 | }, 573 | "funding": { 574 | "url": "https://github.com/sponsors/sindresorhus" 575 | } 576 | }, 577 | "node_modules/js-yaml": { 578 | "version": "4.1.0", 579 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 580 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 581 | "dev": true, 582 | "dependencies": { 583 | "argparse": "^2.0.1" 584 | }, 585 | "bin": { 586 | "js-yaml": "bin/js-yaml.js" 587 | } 588 | }, 589 | "node_modules/locate-path": { 590 | "version": "6.0.0", 591 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 592 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 593 | "dev": true, 594 | "dependencies": { 595 | "p-locate": "^5.0.0" 596 | }, 597 | "engines": { 598 | "node": ">=10" 599 | }, 600 | "funding": { 601 | "url": "https://github.com/sponsors/sindresorhus" 602 | } 603 | }, 604 | "node_modules/log-symbols": { 605 | "version": "4.1.0", 606 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 607 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 608 | "dev": true, 609 | "dependencies": { 610 | "chalk": "^4.1.0", 611 | "is-unicode-supported": "^0.1.0" 612 | }, 613 | "engines": { 614 | "node": ">=10" 615 | }, 616 | "funding": { 617 | "url": "https://github.com/sponsors/sindresorhus" 618 | } 619 | }, 620 | "node_modules/loupe": { 621 | "version": "2.3.6", 622 | "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", 623 | "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", 624 | "dev": true, 625 | "dependencies": { 626 | "get-func-name": "^2.0.0" 627 | } 628 | }, 629 | "node_modules/minimatch": { 630 | "version": "5.0.1", 631 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", 632 | "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", 633 | "dev": true, 634 | "dependencies": { 635 | "brace-expansion": "^2.0.1" 636 | }, 637 | "engines": { 638 | "node": ">=10" 639 | } 640 | }, 641 | "node_modules/mocha": { 642 | "version": "10.2.0", 643 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", 644 | "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", 645 | "dev": true, 646 | "dependencies": { 647 | "ansi-colors": "4.1.1", 648 | "browser-stdout": "1.3.1", 649 | "chokidar": "3.5.3", 650 | "debug": "4.3.4", 651 | "diff": "5.0.0", 652 | "escape-string-regexp": "4.0.0", 653 | "find-up": "5.0.0", 654 | "glob": "7.2.0", 655 | "he": "1.2.0", 656 | "js-yaml": "4.1.0", 657 | "log-symbols": "4.1.0", 658 | "minimatch": "5.0.1", 659 | "ms": "2.1.3", 660 | "nanoid": "3.3.3", 661 | "serialize-javascript": "6.0.0", 662 | "strip-json-comments": "3.1.1", 663 | "supports-color": "8.1.1", 664 | "workerpool": "6.2.1", 665 | "yargs": "16.2.0", 666 | "yargs-parser": "20.2.4", 667 | "yargs-unparser": "2.0.0" 668 | }, 669 | "bin": { 670 | "_mocha": "bin/_mocha", 671 | "mocha": "bin/mocha.js" 672 | }, 673 | "engines": { 674 | "node": ">= 14.0.0" 675 | }, 676 | "funding": { 677 | "type": "opencollective", 678 | "url": "https://opencollective.com/mochajs" 679 | } 680 | }, 681 | "node_modules/ms": { 682 | "version": "2.1.3", 683 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 684 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 685 | "dev": true 686 | }, 687 | "node_modules/nanoid": { 688 | "version": "3.3.3", 689 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", 690 | "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", 691 | "dev": true, 692 | "bin": { 693 | "nanoid": "bin/nanoid.cjs" 694 | }, 695 | "engines": { 696 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 697 | } 698 | }, 699 | "node_modules/node-addon-api": { 700 | "version": "5.1.0", 701 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", 702 | "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" 703 | }, 704 | "node_modules/normalize-path": { 705 | "version": "3.0.0", 706 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 707 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 708 | "dev": true, 709 | "engines": { 710 | "node": ">=0.10.0" 711 | } 712 | }, 713 | "node_modules/once": { 714 | "version": "1.4.0", 715 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 716 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 717 | "dev": true, 718 | "dependencies": { 719 | "wrappy": "1" 720 | } 721 | }, 722 | "node_modules/p-limit": { 723 | "version": "3.1.0", 724 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 725 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 726 | "dev": true, 727 | "dependencies": { 728 | "yocto-queue": "^0.1.0" 729 | }, 730 | "engines": { 731 | "node": ">=10" 732 | }, 733 | "funding": { 734 | "url": "https://github.com/sponsors/sindresorhus" 735 | } 736 | }, 737 | "node_modules/p-locate": { 738 | "version": "5.0.0", 739 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 740 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 741 | "dev": true, 742 | "dependencies": { 743 | "p-limit": "^3.0.2" 744 | }, 745 | "engines": { 746 | "node": ">=10" 747 | }, 748 | "funding": { 749 | "url": "https://github.com/sponsors/sindresorhus" 750 | } 751 | }, 752 | "node_modules/path-exists": { 753 | "version": "4.0.0", 754 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 755 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 756 | "dev": true, 757 | "engines": { 758 | "node": ">=8" 759 | } 760 | }, 761 | "node_modules/path-is-absolute": { 762 | "version": "1.0.1", 763 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 764 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 765 | "dev": true, 766 | "engines": { 767 | "node": ">=0.10.0" 768 | } 769 | }, 770 | "node_modules/pathval": { 771 | "version": "1.1.1", 772 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 773 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 774 | "dev": true, 775 | "engines": { 776 | "node": "*" 777 | } 778 | }, 779 | "node_modules/picomatch": { 780 | "version": "2.3.1", 781 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 782 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 783 | "dev": true, 784 | "engines": { 785 | "node": ">=8.6" 786 | }, 787 | "funding": { 788 | "url": "https://github.com/sponsors/jonschlinkert" 789 | } 790 | }, 791 | "node_modules/pkg-prebuilds": { 792 | "version": "0.1.0", 793 | "resolved": "https://registry.npmjs.org/pkg-prebuilds/-/pkg-prebuilds-0.1.0.tgz", 794 | "integrity": "sha512-ALsGSiwO6EDvjrrFRiv7Q6HZPrqCgTpNxQMFs3P4Ic25cP94DmLy0iGvZDlJmQBbq2IS8xkZrifwkoOHIetY9Q==", 795 | "dependencies": { 796 | "yargs": "^17.5.1" 797 | }, 798 | "bin": { 799 | "pkg-prebuilds-copy": "bin/copy.mjs", 800 | "pkg-prebuilds-verify": "bin/verify.mjs" 801 | }, 802 | "engines": { 803 | "node": ">= 14.15.0" 804 | } 805 | }, 806 | "node_modules/pkg-prebuilds/node_modules/cliui": { 807 | "version": "8.0.1", 808 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 809 | "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 810 | "dependencies": { 811 | "string-width": "^4.2.0", 812 | "strip-ansi": "^6.0.1", 813 | "wrap-ansi": "^7.0.0" 814 | }, 815 | "engines": { 816 | "node": ">=12" 817 | } 818 | }, 819 | "node_modules/pkg-prebuilds/node_modules/yargs": { 820 | "version": "17.7.2", 821 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 822 | "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 823 | "dependencies": { 824 | "cliui": "^8.0.1", 825 | "escalade": "^3.1.1", 826 | "get-caller-file": "^2.0.5", 827 | "require-directory": "^2.1.1", 828 | "string-width": "^4.2.3", 829 | "y18n": "^5.0.5", 830 | "yargs-parser": "^21.1.1" 831 | }, 832 | "engines": { 833 | "node": ">=12" 834 | } 835 | }, 836 | "node_modules/pkg-prebuilds/node_modules/yargs-parser": { 837 | "version": "21.1.1", 838 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 839 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 840 | "engines": { 841 | "node": ">=12" 842 | } 843 | }, 844 | "node_modules/randombytes": { 845 | "version": "2.1.0", 846 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 847 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 848 | "dev": true, 849 | "dependencies": { 850 | "safe-buffer": "^5.1.0" 851 | } 852 | }, 853 | "node_modules/readdirp": { 854 | "version": "3.6.0", 855 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 856 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 857 | "dev": true, 858 | "dependencies": { 859 | "picomatch": "^2.2.1" 860 | }, 861 | "engines": { 862 | "node": ">=8.10.0" 863 | } 864 | }, 865 | "node_modules/require-directory": { 866 | "version": "2.1.1", 867 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 868 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 869 | "engines": { 870 | "node": ">=0.10.0" 871 | } 872 | }, 873 | "node_modules/safe-buffer": { 874 | "version": "5.2.1", 875 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 876 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 877 | "dev": true, 878 | "funding": [ 879 | { 880 | "type": "github", 881 | "url": "https://github.com/sponsors/feross" 882 | }, 883 | { 884 | "type": "patreon", 885 | "url": "https://www.patreon.com/feross" 886 | }, 887 | { 888 | "type": "consulting", 889 | "url": "https://feross.org/support" 890 | } 891 | ] 892 | }, 893 | "node_modules/serialize-javascript": { 894 | "version": "6.0.0", 895 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 896 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 897 | "dev": true, 898 | "dependencies": { 899 | "randombytes": "^2.1.0" 900 | } 901 | }, 902 | "node_modules/string-width": { 903 | "version": "4.2.3", 904 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 905 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 906 | "dependencies": { 907 | "emoji-regex": "^8.0.0", 908 | "is-fullwidth-code-point": "^3.0.0", 909 | "strip-ansi": "^6.0.1" 910 | }, 911 | "engines": { 912 | "node": ">=8" 913 | } 914 | }, 915 | "node_modules/strip-ansi": { 916 | "version": "6.0.1", 917 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 918 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 919 | "dependencies": { 920 | "ansi-regex": "^5.0.1" 921 | }, 922 | "engines": { 923 | "node": ">=8" 924 | } 925 | }, 926 | "node_modules/strip-json-comments": { 927 | "version": "3.1.1", 928 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 929 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 930 | "dev": true, 931 | "engines": { 932 | "node": ">=8" 933 | }, 934 | "funding": { 935 | "url": "https://github.com/sponsors/sindresorhus" 936 | } 937 | }, 938 | "node_modules/supports-color": { 939 | "version": "8.1.1", 940 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 941 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 942 | "dev": true, 943 | "dependencies": { 944 | "has-flag": "^4.0.0" 945 | }, 946 | "engines": { 947 | "node": ">=10" 948 | }, 949 | "funding": { 950 | "url": "https://github.com/chalk/supports-color?sponsor=1" 951 | } 952 | }, 953 | "node_modules/to-regex-range": { 954 | "version": "5.0.1", 955 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 956 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 957 | "dev": true, 958 | "dependencies": { 959 | "is-number": "^7.0.0" 960 | }, 961 | "engines": { 962 | "node": ">=8.0" 963 | } 964 | }, 965 | "node_modules/type-detect": { 966 | "version": "4.0.8", 967 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 968 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 969 | "dev": true, 970 | "engines": { 971 | "node": ">=4" 972 | } 973 | }, 974 | "node_modules/workerpool": { 975 | "version": "6.2.1", 976 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", 977 | "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", 978 | "dev": true 979 | }, 980 | "node_modules/wrap-ansi": { 981 | "version": "7.0.0", 982 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 983 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 984 | "dependencies": { 985 | "ansi-styles": "^4.0.0", 986 | "string-width": "^4.1.0", 987 | "strip-ansi": "^6.0.0" 988 | }, 989 | "engines": { 990 | "node": ">=10" 991 | }, 992 | "funding": { 993 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 994 | } 995 | }, 996 | "node_modules/wrappy": { 997 | "version": "1.0.2", 998 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 999 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1000 | "dev": true 1001 | }, 1002 | "node_modules/y18n": { 1003 | "version": "5.0.8", 1004 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1005 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1006 | "engines": { 1007 | "node": ">=10" 1008 | } 1009 | }, 1010 | "node_modules/yargs": { 1011 | "version": "16.2.0", 1012 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1013 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1014 | "dev": true, 1015 | "dependencies": { 1016 | "cliui": "^7.0.2", 1017 | "escalade": "^3.1.1", 1018 | "get-caller-file": "^2.0.5", 1019 | "require-directory": "^2.1.1", 1020 | "string-width": "^4.2.0", 1021 | "y18n": "^5.0.5", 1022 | "yargs-parser": "^20.2.2" 1023 | }, 1024 | "engines": { 1025 | "node": ">=10" 1026 | } 1027 | }, 1028 | "node_modules/yargs-parser": { 1029 | "version": "20.2.4", 1030 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 1031 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 1032 | "dev": true, 1033 | "engines": { 1034 | "node": ">=10" 1035 | } 1036 | }, 1037 | "node_modules/yargs-unparser": { 1038 | "version": "2.0.0", 1039 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1040 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1041 | "dev": true, 1042 | "dependencies": { 1043 | "camelcase": "^6.0.0", 1044 | "decamelize": "^4.0.0", 1045 | "flat": "^5.0.2", 1046 | "is-plain-obj": "^2.1.0" 1047 | }, 1048 | "engines": { 1049 | "node": ">=10" 1050 | } 1051 | }, 1052 | "node_modules/yocto-queue": { 1053 | "version": "0.1.0", 1054 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1055 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1056 | "dev": true, 1057 | "engines": { 1058 | "node": ">=10" 1059 | }, 1060 | "funding": { 1061 | "url": "https://github.com/sponsors/sindresorhus" 1062 | } 1063 | } 1064 | } 1065 | } 1066 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "easymidi", 3 | "version": "3.1.0", 4 | "description": "Simple event-based MIDI messaging", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha tests/test.js" 8 | }, 9 | "engines": { 10 | "node": ">=14.15" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/dinchak/node-easymidi.git" 15 | }, 16 | "keywords": [ 17 | "midi" 18 | ], 19 | "author": { 20 | "name": "Tom Dinchak", 21 | "email": "dinchak@gmail.com" 22 | }, 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/dinchak/node-easymidi/issues" 26 | }, 27 | "homepage": "https://github.com/dinchak/node-easymidi", 28 | "devDependencies": { 29 | "chai": "^4.3.6", 30 | "mocha": "^10.1.0" 31 | }, 32 | "dependencies": { 33 | "@julusian/midi": "^3.0.0-3" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const easymidi = require('../index.js'); 3 | 4 | const input = new easymidi.Input('test input', true); 5 | const output = new easymidi.Output('test output', true); 6 | 7 | // route send to input 8 | output.send = (type, args) => { 9 | input._input.emit('message', -1, output.parseMessage(type, args)); 10 | }; 11 | 12 | it('receives a noteon message', (done) => { 13 | input.once('noteon', (data) => { 14 | expect(data.note).to.equal(30); 15 | expect(data.velocity).to.equal(11); 16 | expect(data.channel).to.equal(10); 17 | done(); 18 | }); 19 | output.send('noteon', { 20 | note: 30, 21 | velocity: 11, 22 | channel: 10 23 | }); 24 | }); 25 | 26 | it('receives a noteoff message', (done) => { 27 | input.once('noteoff', (data) => { 28 | expect(data.note).to.equal(10); 29 | expect(data.velocity).to.equal(0); 30 | expect(data.channel).to.equal(0); 31 | done(); 32 | }); 33 | output.send('noteoff', { 34 | note: 10, 35 | velocity: 0, 36 | channel: 0 37 | }); 38 | }); 39 | 40 | it('receives a cc message', (done) => { 41 | input.once('cc', (data) => { 42 | expect(data.controller).to.equal(100); 43 | expect(data.value).to.equal(127); 44 | expect(data.channel).to.equal(10); 45 | done(); 46 | }); 47 | output.send('cc', { 48 | controller: 100, 49 | value: 127, 50 | channel: 10 51 | }); 52 | }); 53 | 54 | it('receives a program message', (done) => { 55 | input.once('program', (data) => { 56 | expect(data.number).to.equal(100); 57 | expect(data.channel).to.equal(10); 58 | done(); 59 | }); 60 | output.send('program', { 61 | number: 100, 62 | channel: 10 63 | }); 64 | }); 65 | 66 | it('receives a channel aftertouch message', (done) => { 67 | input.once('channel aftertouch', (data) => { 68 | expect(data.pressure).to.equal(100); 69 | expect(data.channel).to.equal(10); 70 | done(); 71 | }); 72 | output.send('channel aftertouch', { 73 | pressure: 100, 74 | channel: 10 75 | }); 76 | }); 77 | 78 | it('receives a pitch message', (done) => { 79 | input.once('pitch', (data) => { 80 | expect(data.value).to.equal(66); 81 | expect(data.channel).to.equal(5); 82 | done(); 83 | }); 84 | output.send('pitch', { 85 | value: 66, 86 | channel: 5 87 | }); 88 | }); 89 | 90 | it('receives a position message', (done) => { 91 | input.once('position', (data) => { 92 | expect(data.value).to.equal(255); 93 | done(); 94 | }); 95 | output.send('position', { 96 | value: 255 97 | }); 98 | }); 99 | 100 | it('receives a mtc message', (done) => { 101 | input.once('mtc', (data) => { 102 | expect(data.type).to.equal(7); 103 | expect(data.value).to.equal(15); 104 | done(); 105 | }); 106 | output.send('mtc', { 107 | type: 7, 108 | value: 15 109 | }); 110 | }); 111 | 112 | it('receives a select message', (done) => { 113 | input.once('select', (data) => { 114 | expect(data.song).to.equal(127); 115 | done(); 116 | }); 117 | output.send('select', { 118 | song: 127 119 | }); 120 | }); 121 | 122 | it('receives a clock message', (done) => { 123 | input.once('clock', () => { 124 | done(); 125 | }); 126 | output.send('clock'); 127 | }); 128 | 129 | it('receives a start message', (done) => { 130 | input.once('start', () => { 131 | done(); 132 | }); 133 | output.send('start'); 134 | }); 135 | 136 | it('receives a continue message', (done) => { 137 | input.once('continue', () => { 138 | done(); 139 | }); 140 | output.send('continue'); 141 | }); 142 | 143 | it('receives a stop message', (done) => { 144 | input.once('stop', () => { 145 | done(); 146 | }); 147 | output.send('stop'); 148 | }); 149 | 150 | it('receives a reset message', (done) => { 151 | input.once('reset', () => { 152 | done(); 153 | }); 154 | output.send('reset'); 155 | }); 156 | 157 | input.close(); 158 | output.close(); 159 | --------------------------------------------------------------------------------