A utility to manipulate and parse MIDI data.
npm install --save @uttori/audio-midi
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);
- AudioMIDI ⇐
DataBuffer
AudioMIDI - MIDI Utility MIDI File Format Parser & Generator
- WritableNote :
object
- WritableTrack :
object
- NoteData :
object
- SysExData :
object
- EventData :
string
|number
|Uint8Array
|NoteData
|SysExData
- MidiTrackEvent :
object
- Header :
object
- Track :
object
- UsedNote :
object
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>
- .format :
- 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
- .decodeHeader(chunk) ⇒
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);
The MIDI format: 0, 1, or 2
Kind: instance property of AudioMIDI
The internal track count.
Kind: instance property of AudioMIDI
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
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.
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.
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. |
Writes the MIDI data to a binary file.
Kind: instance method of AudioMIDI
Returns: DataBuffer
- The binary data buffer.
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. |
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
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 |
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. |
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. |
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. |
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;
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') === 0
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'
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. |
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. |
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. |
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. |
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. |
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. |
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. |
Kind: global typedef
Properties
Name | Type | Description |
---|---|---|
noteNumber | number |
The numeric value of the note. |
noteString | string |
The human-readable note string. |
To run the test suite, first install the dependencies, then run npm test
:
npm install
npm test
DEBUG=Uttori* npm test
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