-
Notifications
You must be signed in to change notification settings - Fork 12
Writing MIDI Files
Below is a basic guide to writing .mid and .rmi files
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.
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.
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
.
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();
Writes out an RMIDI file (midi + SF2).
const rmidiBinary = writeRMIDI(soundfontBinary, midi, soundfont);
- soundfontBinary - an
Uint8Array
of the soundfont to embed, created bysoundfont.write()
. - midi -
MIDI
to embed. - soundfont -
SoundFont2
- the soundfont thatsoundfontBinary
contains. Used for correcting bank and program changes.
Tip
use trimSoundfont to drastically reduce the file size. consider also using compression (like shown in example) to save even more space. (using these both methods I managed to cram a 1GB soundfont into a 5MB RMIDI!)
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
trimSoundfont(soundfont, midi);
// write out with compression to save space (0.5 is medium quality)
const soundfontBinary = soundfont.write({compress: true, compressionQuality: 0.5});
// 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();
}
Tip
If you encounter any errors in this documentation, please open an issue!
Warning
Make sure you always update worklet_processor.min.js
along with the npm package!