@i-win/midi-processing-js
v1.0.0
Published
Converts MIDI files to a JSON-friendly object and back for you to modify and do what you need with it.
Downloads
106
Readme
midi-processing-js
NOTE: I made this library because it integrates well with what I do. This may not be rigorously tested, nor may I be able to constantly maintain it. If you need a library that is more robust please use @tonejs/midi.
Converts MIDI files to a JSON-friendly object and back for you to modify and do whatever you want with it.
Method Overview
Global Functions
fromMidiBytes(data: ArrayLike<number>): MidiRep
Convert any byte sequence into a MidiRep instance. May throw an exception with malformed data.
Sample usage:
const data = await readFile("./file.mid");
const midiRep = fromMidiBytes(data);toMidiBytes(midiRep: MidiRep): Uint8Array
Return the byte sequence from the MidiRep. May throw an exception with malformed data.
MNote
Represents an individual note.
| Property | Type | Description |
| --- | --- | --- |
| channel | number | Channel of the note. Each note has one. In FL Studio, if you enable map colors to channel notes, the color is the channel, but keep in mind colors here count from 0. int, 0-15 incl; cannot save if this is violated |
| pitch | number | C5 (middle C) == 60, C#5 == 61. int, 0-127 incl; cannot save if this is violated |
| vel | number | Velocity of this note when it starts playing. Usually 100 is the loudest volume. int, 0-127 incl; cannot save if this is violated |
| beat | number | Beat of the note where it starts playing. float >= 0; note dropped if this is violated, when saved. To avoid floating point issues, you may want to represent these values as a quotient of 96 if you want to do any comparisons (round(beat*96)/96). Or you can use approximate comparisons. |
| duration | number | Duration of the note. The note ends at beat + duration. float >= 0; set to 0 if violated when saved |
TempoChange
Represents a change in the MIDI's tempo. With no tempo changes, there will be one instance with beat = 0.
| Property | Type | Description |
| --- | --- | --- |
| beat | number | Beat where the tempo change occurs. float >= 0; instance dropped if this is violated, when saved |
| new_bpm | number | float >= 0; cannot save if this is violated |
Track
A track. Tracks store notes. While track ordering is guaranteed when you re-export the MIDI file, do not rely on the indices of each track since programs may offset them for some reason.
| Property | Type | Description |
| --- | --- | --- |
| notes | MNote[] | All notes of this track. Notes do not need to be sorted, and do not assume they are sorted without sorting them yourself. However, they are usually sorted after loading them with the MIDI loader bundled. |
| track_name | string \| null | Name of the track, or null if no name. |
| instrument | string \| null | Instrument name. This does not actually impact the instrument, and most of the time you may ignore this field. |
TimeSignature
| Property | Type | Description |
| --- | --- | --- |
| numerator | number | int, 0-15 inclusive; cannot save if violated or may be % 16 |
| denominator_log2 | number | int, 0-15 inclusive; cannot save if violated or may be % 16. A value of 2 means the time signature denominator is 4, because 2^2 == 4. A value of B means the time signature denominator is 2^B. |
| clocks_per_click | number | Do not change; int, 0-15 incl; cannot save if violated or may be % 16 |
| t32_notes_per_quarter | number | Do not change; int, 0-15 incl; cannot save if violated or may be % 16 |
| beat | number | float >= 0; instance dropped if this is violated, when saved |
KeySignature
Key signature changes. Unless explicit, this won't appear.
| Property | Type | Description |
| --- | --- | --- |
| key | number | int, -8 to 7; cannot save if violated |
| minor | boolean | Indicates if the key is minor. |
| beat | number | float >= 0; instance dropped if this is violated, when saved |
MidiRep
The primary container for MIDI data.
| Property | Type | Description |
| --- | --- | --- |
| tracks | Track[] | All tracks in this MIDI. In FL Studio, each MIDI out channel is its own track, and the name you give that thing will be its name. Order is preserved but tracks may be offset because some MIDI processors like adding stuff. |
| bpm_changes | TempoChange[] | All tempo changes. If there are no tempo changes, then there will be a TempoChange instance there at beat 0, which signals the song's BPM. This list may not and does not need to be sorted by beat, but on MIDI load, it is |
| time_signature_changes | TimeSignature[] | All time signature changes. MIDI files can still be exported even if time signature conventions are violated (e.g. 4/4, then it changes to 3/4 after 2 beats). This list may not and does not need to be sorted by beat, but on MIDI load, it is |
| key_signature_changes | KeySignature[] | Most of the time, this is empty. But if a MIDI has key signature changes specified for any reason, they will be here. This list may not and does not need to be sorted by beat, but on MIDI load, it is |
| channel_to_instrument_map | ChannelInstMapType | Array has length 16 (indices 0 to 15); Values must be between 0-127 inclusive. Indices correspond to MIDI channel; Values correspond to MIDI instrument number |
| ticks_per_beat | number | Do not modify. Typically, 96. Value must be between 0 and 2^16-1 |
Extra Functions
noChords(m: MidiRep): void
Removes all but one note from chords, keeping only the highest pitch. Only applies to notes on the same channel, track, and starting beat.
swingMidi(midiRepresentation: MidiRep, mult: number = 1): void
Adds a swing rhythm to the MIDI in place. Keep mult at 1
unswingMidi(midiRepresentation: MidiRep, mult: number = 1): void
Reverses any swing rhythm on the MIDI in place. Keep mult at 1
tempoIntegrator(m: MidiRep): void
Removes all tempo changes. Forces the tempo to 60 BPM (where 1 beat = 1 second). Adjusts note start times and durations to accommodate the removal of tempo changes.
Notes on Information Kept and Lost
All notes are saved.
Along with note data, we save and can rewrite:
- Track names
- Tempo, and tempo changes
- Key signatures and their changes, if declared
- Time signatures and their changes
Any other information is dropped.
If there exists two notes a and b
such that b starts after a starts, but before a ends, then
a's end time will be reduced to b's start time.
Tracks that are unnamed and contain no notes will be dropped. Tempo, time, and key signatures are stored inside tracks, but are stored separately here, so you can expect tracks exclusively containing them to be dropped.
On export, tracks may be offset due to additional tracks being put at the start for tempo, time signature, and key signature declarations.
Midi Specs
- Keys (pitches) must be between 0 and 127 inclusive
- Channels must be between 0 and 15 inclusive. There are always 16 channels.
- Velocity must be between 0 and 127 (but keep it 0 to 100)
- Instrument IDs must be between 0 and 127 inclusive
