-
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.
Important
Also see Creating MIDI Files From Scratch
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. The decimal part will be tuned via the RPN fine tune command if provided.
*/
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). See more info about this format
const rmidiBinary = writeRMIDI(
soundfontBinary,
midi,
soundfont,
bankOffset = 0,
encoding = "Shift_JIS",
metadata = {},
correctBankOffset = true
);
an Uint8Array
of the soundfont to embed, created by soundfont.write()
.
MIDI
to embed.
SoundFont2
- the soundfont that soundfontBinary
contains. Used for correcting bank and program changes.
number
, optional - The bank offset to apply to the file. A value of 0 is recommended. See this for more info
string
, optional - The encoding to add to the INFO chunk of an RMID file. Make sure to pick a value that is acceptable by the TextDecoder
Object
, optional - The metadata of the file. If left undefined, some basic metadata (like song's title) will be copied from the MIDI.
Important
All of the properties below are optional.
- name -
string
- the name of the song. - engineer -
string
- the engineer of the soundfont. - artist -
string
- the artist of the song. - album -
string
- the album's name. - genre -
string
- the song's genre. - comment -
string
- a comment about the song. - creationDate -
string
- the creation date of the file. If not provided, current day is used. - copyright -
string
- the copyright string. If not provided,midi.copyright
is copied. - picture -
ArrayBuffer
- the album cover of the song. Binary data of the image. - midiEncoding -
string
- The encoding of the inner MIDI file. Make sure to pick a value that is acceptable by theTextDecoder
Caution
Providing any of the metadata fields overrides the encoding with utf-8
.
This behavior is forced due to lack of support of other encodings by the TextEncoder
class.
boolean
, optional - if the function should correct all
program selects and bank selects in the MIDI file to reflect the embedded soundfont
(I.E. Make it Self-contained).
Recommended unless a specific use-case is required.
Defaults to true
.
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>
Note
This example uses soundfont3 compression. Make sure you've read this
const sfInput = document.getElementById("soundfont_upload");
const midiInput = document.getElementById("midi_upload");
document.getElementById("export").onchange = async () => {
// get the files
const soundfont = loadSoundFont(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,
compressionFunction: EncodeVorbisFunction // Remember to obtain your compression function
});
// get the rmidi
const rmidiBinary = writeRMIDI(soundfontBinary, midi, soundfont, 0, 'utf-8', {
name: "A cool song",
artist: "John",
creationDate: new Date().toDateString(),
album: "John's songs",
genre: "Rock",
comment: "My favorite!"
});
// 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!