@uttori/audio-midi
v0.9.1
Published
Utility to parse and manipulate MIDI files.
Downloads
37
Maintainers
Readme
Uttori Audio MIDI
A utility to manipulate and parse MIDI data.
Install
npm install --save @uttori/audio-midiExample
import fs from 'fs';
import AudioMIDI from '@uttori/audio-midi';
const data = fs.readFileSync('./song.mid');
const midi = new AudioPadInfo(data);
console.log('MIDI:', midi);API Reference
Classes
Typedefs
AudioMIDI ⇐ DataBuffer
AudioMIDI - MIDI Utility MIDI File Format Parser & Generator
Kind: global class
Extends: DataBuffer
- AudioMIDI ⇐ DataBuffer
- new AudioMIDI([input], [options])
- instance
- .format : number
- .trackCount : number
- .timeDivision : number
- .chunks : Array.<Track>
- .readVariableLengthValues ⇒ number
- .parse()
- .addTrack() ⇒ Track
- .addEvent(track, event)
- .saveToDataBuffer() ⇒ DataBuffer
- .writeChunk(dataBuffer, chunk)
- .writeEvent(dataBuffer, event)
- .getUsedNotes() ⇒ Array.<UsedNote>
- .validate() ⇒ Array.<string>
- static
- .decodeHeader(chunk) ⇒ Header
- .getControllerLabel(controller) ⇒ string
- .getManufacturerLabel(manufacturerId) ⇒ string
- .writeVariableLengthValue(dataBuffer, value)
- .writeEventData(dataBuffer, data)
- .generateTempoEvent(bpm) ⇒ MidiTrackEvent
- .generateMetaStringEvent(metaType, data) ⇒ MidiTrackEvent
- .generateEndOfTrackEvent() ⇒ MidiTrackEvent
- .convertToMidi(options) ⇒ AudioMIDI
- .noteToMidi(noteString, [octaveOffset], [noteMap]) ⇒ number
- .midiToNote(midiValue, [octaveOffset], [noteNames]) ⇒ string
new AudioMIDI([input], [options])
Creates a new AudioMIDI.
| Param | Type | Description | | --- | --- | --- | | [input] | Array.<number> | ArrayBuffer | Buffer | DataBuffer | Int8Array | Int16Array | Int32Array | number | string | Uint8Array | Uint16Array | Uint32Array | undefined | The data to process. | | [options] | object | Options for this AudioMIDI instance. | | [options.format] | number | The MIDI format: 0, 1, or 2, default is 0. | | [options.timeDivision] | number | The indication of how MIDI ticks should be translated into time, default is 128. |
Example (AudioMIDI)
const data = fs.readFileSync('./song.mid');
const file = new AudioMIDI(data);
file.parse();
console.log('Chunks:', file.chunks);audioMIDI.format : number
The MIDI format: 0, 1, or 2
Kind: instance property of AudioMIDI
audioMIDI.trackCount : number
The internal track count.
Kind: instance property of AudioMIDI
audioMIDI.timeDivision : number
The indication of how MIDI ticks should be translated into time.
Kind: instance property of AudioMIDI
audioMIDI.chunks : Array.<Track>
Kind: instance property of AudioMIDI
audioMIDI.readVariableLengthValues ⇒ number
Several different values in events are expressed as variable length quantities (e.g. delta time values). A variable length value uses a minimum number of bytes to hold the value, and in most circumstances this leads to some degree of data compresssion.
A variable length value uses the low order 7 bits of a byte to represent the value or part of the value. The high order bit is an "escape" or "continuation" bit. All but the last byte of a variable length value have the high order bit set. The last byte has the high order bit cleared. The bytes always appear most significant byte first.
Kind: instance property of AudioMIDI
Returns: number - The length of the next chunk.
audioMIDI.parse()
Parse a MIDI file from a Uint8Array.
Kind: instance method of AudioMIDI
See
- Expanded MIDI 1.0 Messages List (Status Bytes)
- MIDI 1.0 Universal System Exclusive Messages
- DLS Proprietary Chunk IDs
audioMIDI.addTrack() ⇒ Track
Adds a new track to the MIDI file.
Kind: instance method of AudioMIDI
Returns: Track - The new track.
audioMIDI.addEvent(track, event)
Adds an event to a track.
Kind: instance method of AudioMIDI
| Param | Type | Description | | --- | --- | --- | | track | Track | The track to add the event to. | | event | Event | Array.<Event> | The event to add. |
audioMIDI.saveToDataBuffer() ⇒ DataBuffer
Writes the MIDI data to a binary file.
Kind: instance method of AudioMIDI
Returns: DataBuffer - The binary data buffer.
audioMIDI.writeChunk(dataBuffer, chunk)
Write a track chunk to the data buffer.
Kind: instance method of AudioMIDI
| Param | Type | Description | | --- | --- | --- | | dataBuffer | DataBuffer | The data buffer to write to. | | chunk | Track | The track chunk to write. |
audioMIDI.writeEvent(dataBuffer, event)
Helper function to write an event to the data buffer.
Kind: instance method of AudioMIDI
| Param | Type | Description | | --- | --- | --- | | dataBuffer | DataBuffer | The data buffer to write to. | | event | MidiTrackEvent | The event to write. |
audioMIDI.getUsedNotes() ⇒ Array.<UsedNote>
Returns a sorted list of all unique note numbers used in "Note On" events, along with their note names (e.g. "C3", "D#4").
Kind: instance method of AudioMIDI
Returns: Array.<UsedNote> - Array of note data
audioMIDI.validate() ⇒ Array.<string>
Validate a MIDI instance for common issues.
Matching Note Ons / Offs: A velocity > 0 "Note On" increments activeNotes[note]. A "Note Off" or "Note On" with velocity == 0 decrements. If the count is already 0, that is invalid. At the end of the track, if any notes still have a positive count, that is also invalid.
Meta Events: We do a small switch on event.metaType to check if the declared metaEventLength is correct for well-known meta events (End of Track, Set Tempo, Time Signature, etc.).
Chunk Length: Since the parser already stored each chunk's chunkLength, we do minimal checks: if chunkLength > 0 but there are zero events, or vice versa, that is unusual.
Kind: instance method of AudioMIDI
Returns: Array.<string> - Array of warning / error messages discovered, an empty array if no issues are found.
AudioMIDI.decodeHeader(chunk) ⇒ Header
Decodes and validates MIDI Header.
Checks for MThd header, reads the chunk length, format, track count, and PPQN (pulses per quarter note) / PPQ (pulses per quarter) / PQN (per quarter note) / TPQN (ticks per quarter note) / TPB (ticks per beat).
Signature (Decimal): [77, 84, 104, 100, ...] Signature (Hexadecimal): [4D, 54, 68, 64, ...] Signature (ASCII): [M, T, h, d, ...]
Kind: static method of AudioMIDI
Returns: Header - The decoded values.
Throws:
- Error Invalid WAV header
| Param | Type | Description | | --- | --- | --- | | chunk | Buffer | string | Uint8Array | Data Blob |
AudioMIDI.getControllerLabel(controller) ⇒ string
Return the human readable controller name from the ID.
Kind: static method of AudioMIDI
Returns: string - The human-readable controller name.
See
| Param | Type | Description | | --- | --- | --- | | controller | number | The controller ID. |
AudioMIDI.getManufacturerLabel(manufacturerId) ⇒ string
Return the human readable manufacturer name from the ID.
Kind: static method of AudioMIDI
Returns: string - The human-readable manufacturer name.
See: MidiKit Help MIDI Manufacturers List
| Param | Type | Description | | --- | --- | --- | | manufacturerId | number | The manufacturer ID. |
AudioMIDI.writeVariableLengthValue(dataBuffer, value)
Write a variable-length value.
Kind: static method of AudioMIDI
| Param | Type | Description | | --- | --- | --- | | dataBuffer | DataBuffer | The data buffer to write to. | | value | number | The value to write as a variable-length quantity. |
AudioMIDI.writeEventData(dataBuffer, data)
Write event data.
Kind: static method of AudioMIDI
| Param | Type | Description | | --- | --- | --- | | dataBuffer | DataBuffer | The data buffer to write to. | | data | Uint8Array | Array.<number> | The event data to write. |
AudioMIDI.generateTempoEvent(bpm) ⇒ MidiTrackEvent
Generate a Set Tempo event with a provided BPM.
Kind: static method of AudioMIDI
Returns: MidiTrackEvent - The tempo event with the correct byte values.
| Param | Type | Description | | --- | --- | --- | | bpm | number | The desired tempo in Beats Per Minute. |
AudioMIDI.generateMetaStringEvent(metaType, data) ⇒ MidiTrackEvent
Generate a Meta String event:
- 0x01: 'Text Event'
- 0x02: 'Copyright Notice'
- 0x03: 'Sequence / Track Name'
- 0x04: 'Instrument Name'
- 0x05: 'Lyrics'
- 0x06: 'Marker'
- 0x07: 'Cue Point'
- 0x08: 'Program Name'
- 0x09: 'Device (Port) Name'
Kind: static method of AudioMIDI
Returns: MidiTrackEvent - The meta string event with the encoded string data.
| Param | Type | Description | | --- | --- | --- | | metaType | number | The meta event type. (e.g., 0x03 for Track Name). | | data | string | The string value for the event (e.g., the name of the track). |
AudioMIDI.generateEndOfTrackEvent() ⇒ MidiTrackEvent
Generate an end of track event.
Kind: static method of AudioMIDI
Returns: MidiTrackEvent - The end of track event.
AudioMIDI.convertToMidi(options) ⇒ AudioMIDI
Convert a collection of tracks and notes into a new AudioMIDI instance.
Kind: static method of AudioMIDI
Returns: AudioMIDI - The newly constured MIDI
| Param | Type | Description | | --- | --- | --- | | options | object | The options | | [options.ppq] | number | The pulses per quarter note, default is 480. | | [options.bpm] | number | The BPM of the track, when blank no tempo event will be added. | | [options.tracks] | Array.<WritableTrack> | The MIDI tracks to write. | | [options.skipNotes] | Array.<number> | The MIDI notes to ship, if any. |
Example
const midi = AudioMIDI.convertToMidi({
bpm,
ppq,
tracks: [
{
notes: myCustomNotes.map((note) => {
return {
note: note.midiNote,
velocity: note.velocity,
length: note.length,
}
}),
metaStringEvents: {
0x03: `Custom MIDI`,
},
}
],
skipNotes: [128],
});
return midi;AudioMIDI.noteToMidi(noteString, [octaveOffset], [noteMap]) ⇒ number
Convert a note string like C1 or D#2 to the MIDI value.
Kind: static method of AudioMIDI
Returns: number - The MIDI value for the provided note.
| Param | Type | Default | Description | | --- | --- | --- | --- | | noteString | string | | The notation string. | | [octaveOffset] | number | 2 | The default octave offset for C1, where a value of 2 means C1 = 36; default is 2. | | [noteMap] | Record.<string, number> | | The note map to use for the conversion. |
Example
AudioMIDI.noteToMidi('C4') === 72
AudioMIDI.noteToMidi('C3') === 60
AudioMIDI.noteToMidi('C2') === 48
AudioMIDI.noteToMidi('C1') === 36
AudioMIDI.noteToMidi('C-1') === 12
AudioMIDI.noteToMidi('C-2') === 0AudioMIDI.midiToNote(midiValue, [octaveOffset], [noteNames]) ⇒ string
Convert a MIDI value back to a note string like C1 or D#2.
Kind: static method of AudioMIDI
Returns: string - The note label corresponding to the MIDI value.
| Param | Type | Default | Description | | --- | --- | --- | --- | | midiValue | number | | The MIDI value (0-127). | | [octaveOffset] | number | 2 | The default octave offset for C1, where a value of 2 means C1 = 36; default is 2. | | [noteNames] | Array.<string> | | The note names to use for the conversion. |
Example
AudioMIDI.midiToNote(72) === 'C4'
AudioMIDI.midiToNote(60) === 'C3'
AudioMIDI.midiToNote(48) === 'C2'
AudioMIDI.midiToNote(36) === 'C1'
AudioMIDI.midiToNote(12) === 'C-1'
AudioMIDI.midiToNote(0) === 'C-2'WritableNote : object
Kind: global typedef
Properties
| Name | Type | Description | | --- | --- | --- | | ticks | number | The delay in ticks until the next track. | | midiNote | number | The MIDI note value. | | velocity | number | The velocity of the note (0-127). | | length | number | The length of the note in ticks. |
WritableTrack : object
Kind: global typedef
Properties
| Name | Type | Description | | --- | --- | --- | | [bpm] | number | The BPM of the track, when blank no tempo event will be added. | | [metaStringEvents] | Record.<number, string> | A key value collection of meta events to add where they key is the event type and the value is the data to add. | | [notes] | Array.<WritableNote> | A collection of notes to write on the track. |
NoteData : object
Kind: global typedef
Properties
| Name | Type | Description | | --- | --- | --- | | note | string | A note value. | | velocity | number | The velocity of the note (0-127). | | length | number | The length of the note in ticks. |
SysExData : object
Kind: global typedef
Properties
| Name | Type | Description | | --- | --- | --- | | manufacturerId | number | The manufacturer's ID code. | | manufacturerLabel | string | The manufacturer's label based on the ID. | | data | Array.<number> | The SysEx data bytes. |
EventData : string | number | Uint8Array | NoteData | SysExData
Kind: global typedef
MidiTrackEvent : object
Kind: global typedef
Properties
| Name | Type | Description | | --- | --- | --- | | deltaTime | number | The delta time of the MIDI event. | | type | number | The type of the event (e.g., meta event, regular event). | | label | string | A human-readable label describing the event. | | data | EventData | The data associated with the event. | | [metaType] | number | The subtype of the meta event. | | [metaEventLength] | number | The length of the meta event data. | | [channel] | number | The MIDI channel the event is for. | | [tag] | number | The tag for the M-Live Tag event. |
Header : object
Kind: global typedef
Properties
| Name | Type | Description | | --- | --- | --- | | type | string | The type of the chunk (e.g., MThd, MTrk). | | format | number | The format of the MIDI file (header only). | | trackCount | number | The number of tracks in the MIDI file (header only). | | timeDivision | number | The time division of the MIDI file (header only). | | chunkLength | number | The length of the chunk data. |
Track : object
Kind: global typedef
Properties
| Name | Type | Description | | --- | --- | --- | | type | string | The type of the chunk (e.g., MThd, MTrk). | | chunkLength | number | The length of the chunk data. | | events | Array.<MidiTrackEvent> | The collection of events in the track. |
UsedNote : object
Kind: global typedef
Properties
| Name | Type | Description | | --- | --- | --- | | noteNumber | number | The numeric value of the note. | | noteString | string | The human-readable note string. |
Tests
To run the test suite, first install the dependencies, then run npm test:
npm install
npm test
DEBUG=Uttori* npm testContributors
References
I found these links really helpful for understanding the MIDI format.
- https://midi.org/midi-1-0-control-change-messages
- https://midi.org/community/midi-specifications/yamaha-meta-events-in-midi-files
- https://www.mixagesoftware.com/en/midikit/help/HTML/meta_events.html
- https://web.archive.org/web/20140325195418/http://www.ta7.de/txt/musik/musi0006.htm#expand
- https://www.lim.di.unimi.it/IEEE/MIDI/META.HTM
- https://www.un4seen.com/forum/?topic=20355.msg142507#msg142507
- https://www.music.mcgill.ca/~ich/classes/mumt306/StandardMIDIfileformat.html
- http://www.jososoft.dk/yamaha/docs_specs.htm
