MIDI Parser - spessasus/spessasynth_core GitHub Wiki
This is the module responsible for parsing MIDI files.
Tip
If you encounter any errors in this documentation, please open an issue!
Tip
Using the npm package? Make sure you've read this
const parsedMIDI = new MIDI(arrayBuffer);
- arrayBuffer - an
arrayBuffer
anstance of the midi file.
The number of tracks in the file.
console.log(`this file has ${parsedMIDI.tracksAmount}`);
The track names of the MIDI file. If a track doesn't have a name, it will have an empty string.
console.log(`First track is called ${parsedMIDI.trackNames[0]}`);
The time division of the midi file. MIDI ticks per quarter note.
console.log(`this sequence's time division is ${parsedMIDI.timeDivision}`);
The sequence's name. The first track's Track Name
's event text.
console.log(`This sequence is named "${parsedMIDI.midiName}"`);
Tip
This property uses basic decoding. If the name is encoded in a different encoding, like shift_jis, it might be better to use rawMidiName.
The name of the MIDI file if provided during the initialization.
A boolean
indicating if the MIDI name is the same as file name, therefore there's no need to decode it.
The sequence's name, as a raw Uint8Array
. Useful for handling unusual encodings.
console.log(new TextDecoder("shift-jis").decode(parsedMIDI.rawMidiName)); // ใใกใชใใใ๏ผ๏ผ
The decoded copyright and description of the file. Individual messages are separated with a newline. Note: this not only includes the copyright events, but also others. Treat this like "additional information" about the file.
console.log(`Midi file description: ${parsedMIDI.copyright}`);
Ordered from last to first, all the tempo changes in the file. It will always contain at least one tempo change (the default 120BPM at zero ticks).
[
{
tempo: 140 /* tempo in BPM */,
ticks: 5437 /* absolute amount of MIDI Ticks from the start */
},
/*...*/
{
// the default tempo change
tempo: 120,
ticks: 0
}
];
The points of the loop detected in the MIDI file in ticks. If there's nothing detected, the loop will start from the first note on event and end will be the last note off. Current looping detection is: CC 2/4, 116/117 and "start," "loopStart" and "loopEnd" markers.
console.log(parsedMIDI.loop); // {start: 1294, end: 49573}
The MIDI file format. Usually 0 or 1, rarely 2.
console.log(parsedMIDI.format); // 1
The MIDI tick number of the first noteOn event in the sequence. It can be used to skip the initial silence.
console.log(parsedMIDI.firstNoteOn); // 1294
The MIDI tick number of the last voice event in the sequence. Treated as the last event in the sequence, even if the end of track is later.
console.log(parsedMIDI.lastVoiceEventTick); // 14518
The sequence's duration in seconds.
console.log(parsedMIDI.duration); // 125.64;
Important
The MIDI file's duration is the start of the file to midi.lastVoiceEventTick
.
To alter the end time,
add a controller change (preferably an unused CC, like CC#50) at the time you want the file to end,
then run midi.flush()
The detected midi ports for each track. Each port represents a batch of 16 channels.
console.log(parsedMIDI.midiPorts); // [0, 0, 0, 1, 1, 2, ...]
The channel offsets for each MIDI port, using the SpessaSynth method
console.log(parsedMIDI.midiPortChannelOffsets); // [16, 0, 48, 32, ...]
All the channels that each track refers to. An array of Set
s.
console.log(parsedMIDI.usedChannelsOnTrack) // [ Set[0, 1, 2, 3, 4] ] - this sequence has 1 track which plays on channels 0, 1, 2, 3 and 4
The key range of the sequence. The lowest pressed key and highest.
console.log(parsedMIDI.keyRange); // {min: 0, max: 127}
The detected lyrics, stored as binary text data, as MIDIs can use different encodings.
Stored as an array of Uint8Array
s, each is a single lyrics event.
Tip
See SF2 RMIDI Extension Specification for more info.
An ArrayBuffer
representation of the embedded soundfont in an RMID file. If no soundfont or not RMID, undefined.
This can be either SoundFont binary or DLS binary.
Warning
If the embedded soundfont is defined, Sequencer
will automatically pass it to the synthesizer.
If you want to avoid this behavior, make sure you set it to undefined before passing the rmid file.
A number
representing the bank offset of the file. Only applies to RMID, for normal MIDIs it's set to 0.
An Object
representing the INFO chunk of an RMID file. See this for more information.
The actual MIDI sequence data. Described below.
Updates internal values. Call this after editing the contents of midi.tracks
.
midi.flush();
Caution
Not calling flush
after making significant changes to the track may result in unexpected behavior.
Calculates time in seconds given the MIDI ticks.
midi.MIDIticksToSeconds(ticks);
- ticks -
number
- the time in MIDI ticks.
The returned value is the time in seconds from the start of the MIDI to the given tick.
Renders the sequence as a .mid-file. See Writing MIDI files for more info.
midi.writeMIDI();
The returned value is an Uint8Array
- a binary representation of the .mid-file.
This function writes out an RMIDI file (MIDI + SF2). See more info about this format
const rmidiBinary = midi.writeRMIDI(
soundfontBinary,
soundfont,
bankOffset = 0,
encoding = "Shift_JIS",
metadata = {},
correctBankOffset = true
);
- See Writing MIDI files for more info.
A function for modifying MIDI files. See Writing MIDI files for more info.
A function for applying a Synthesizer snapshot to a MIDI file. See Writing MIDI files for more info.
Goes through the MIDI file and returns all used program numbers and MIDI key:velocity combinations for them, for a given sound bank (used for capital tone fallback).
const used = midi.getUsedProgramsAndKeys(soundfont);
- soundfont -
BasicSoundBank
- an instance of the parsed soundbank to "play" the MIDI with.
The returned value is Object<string, Set<string>>
. That is:
- The key is a string representing a given preset that was used as
bank:program
, e.g.,8:16
(bank 8, program 16) - The value is a
Set
of all unique combinations played on this preset, formatted askey-velocity
, e.g.,60:120
(key 60, velocity 120)
Returns nicely formatted note data for easy sequence visualization.
const data = midi.getNoteTimes(minDrumLength = 0);
- minDrumLength - number, defaults to 0 - a number, in seconds, representing the minimum allowed time for a drum note, since they sometimes have a length of 0.
The returned value is an array of 16 arrays. Each of these represents one of the 16 MIDI channels.
Each channel is a list of notes, represented as objects with properties:
- midiNote - number - the MIDI key number
- velocity - number - the MIDI velocity
- start - number - the absolute note start time in seconds
- length - number - the note length in seconds
Example:
const data = [
[{midiNote: 60, velocity: 100, start: 0.5, length: 0.25}], // channel 1
// other 14 channels...
[{midiNote: 36, velocity: 96, start: 41.54, length: 0.1}] // channel 16
];
The file is stored as an array of tracks, accesible via parsedMIDI.tracks
.
Each track is an array of events.
Each event is a MIDIMessage
class, which is defined as follows;
class MIDIMessage
{
/**
* absolute amount of MIDI Ticks from the start of the track
* @type {number}
*/
ticks;
/**
* the status byte of the message as a number from 0 to 255
* @type {number}
*/
messageStatusByte;
/**
* @type {IndexedByteArray}
*/
messageData;
}
- ticks - absolute amount of MIDI Ticks from the start of the track.
- messageStatusByte - the status byte of the message as a number from 0 to 255. Learn more here and here.
Important
Note that for Meta Events, the status byte is the SECOND status byte, not the 0xFF!
- messageData - a
IndexedByteArray
(Pretty much exactly the same asUint8Array
) instance of the event's binary data.
Caution
Not calling flush
after making significant changes to the track data may result in unexpected behavior.