Skip to content

Writing MIDI Files

spessasus edited this page Jul 24, 2024 · 14 revisions

Writing MIDI Files

Below is a basic guide to writing .mid and .rmi files

Writing a .mid file

writeMIDIFile

Renders the sequence as a .mid file.

writeMIDIFile(midi);
  • midi - the MIDI instance to export.

The returned value is an Uint8Array - a binary representation of the .mid file.

modifyMIDI

Allows to easily modify the sequence's programs and controllers.

modifyMIDI(midi, desiredProgramChanges, desiredControllerChanges, desiredChannelsToClear, desiredChannelsToTranspose);
  • midi - the MIDI instance to modify.
  • desiredProgramChanges - an array of objects, defined as follows:
/**
 * @typedef desiredProgramChange {Object}
 * @property {number} channel - the channel to modify. Note that this allows going over 16 if the MIDI is a multi port file
 * @property {number} program - the MIDI program to use.
 * @property {number} bank - the bank to use.
 * @property {boolean} isDrum - if the channel is a drum channel. Will add GS Use Drums System exclusive and GS on if needed
 */
  • desiredControllerChanges - an array of objects, defined as follows:
/**
 * @typedef desiredControllerChange {Object}
 * @property {number} channel - same as above.
 * @property {number} controllerNumber - the MIDI CC number to use.
 * @property {number} controllerValue - the desired value of the controller.
 * 
 */
  • desiredChannelsToClear - an array of numbers, indicating the channel number to effectively mute.
  • desiredChannelsToTranspose - an array of objects, defined as follows:
/**
 * @typedef desiredTranspose {Object}
 * @property {number} channel - same as above.
 * @property {number} keyShift - the amount to shift the notes on this channel by. Can be negative, but must be integer.
 */

Warning

Clearing the channel removes the messages rather than setting volume to 0! This operation is irreversible if the original midi file is lost.

applySnapshot

Applies a SynthesizerSnapshot to the sequence in place. This means changing the programs and controllers if they are locked.

applySnapshotToMIDI(midi, snapshot);
  • midi - the MIDI instance to modify.
  • snapshot - the SynthesizerSnapshot to use.

For example if channel 1 has locked preset on Drawbar Organ, this will remove all program changes for channel 1 and add one at the start to change the program to Drawbar organ.

Example

Below is a basic example of writing a modified MIDI file

// create your midi and synthesizer
const midi = new MIDI(yourBufferGoesHere);
const synth = new Synthetizer(yourContext, yourSoundfontBuffer);

// ...

// get the snapshot and apply it
const snapshot = await synth.getSynthesizerSnapshot();
applySnapshotToMIDI(midi, snapshot);

// write midi 
const midiBinary = writeMIDIFile(midi);

// save the file
const blob = new Blob([midiBinary.buffer], {type: "audio/midi"});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = midi.midiName + ".mid";
a.click();

Writing an .rmi file

writeRMIDI

Writes out an RMIDI file (midi + SF2).

const rmidiBinary = writeRMIDI(soundfontBinary, midi, soundfont);
  • soundfontBinary - an Uint8Array of the soundfont to embed, created by soundfont.write().
  • midi - MIDI to embed.
  • soundfont - SoundFont2 - the soundfont that soundfontBinary contains. Used for correcting bank and program changes.

Tip

use getTrimmedSoundfont to drastically reduce the file size.

Example

Below is a simple example for exporting an RMIDI file

<label for='soundfont_upload'>Upload soundfont</label>
<input type='file' id='soundfont_upload'>
<label for='midi_upload'>Upload MIDI</label>
<input type='file' id='midi_upload'>
<button id='export'>Export</button>
const sfInput = document.getElementById("soundfont_upload");
const midiInput = document.getElementById("midi_upload");
document.getElementById("export").onchange = async () => {
    // get the files
    const soundfont = new SoundFont2(await sfInput.files[0].arrayBuffer());
    const midi = new MIDI(await midiInput.files[0].arrayBuffer());
    
    // trim the soundfont
    const soundfontBinary = getTrimmedSoundfont(soundfont, midi);
    // get the rmidi
    const rmidiBinary = writeRMIDI(soundfontBinary, midi, soundfont);
    
    // save the file
    const blob = new Blob([rmidiBinary.buffer], {type: "audio/rmid"});
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = midi.midiName + ".rmi";
    a.click();
}