Original format was created by Microsoft and later expanded by the MIDI Manufacturers Association.
Original format expansion idea by Zoltán Bacskó of Falcosoft, later expanded by spessasus. Specification written by spessasus with the help of Zoltán.
Revision 1.22
MIDI files have long-faced a significant challenge: different sounds on different devices. SF2 + MIDI combinations address this issue partially by ensuring that playing both files through an SF2-compliant synth results in the same sound being produced. The RMIDI format is not new; it was originally developed by Microsoft as a RIFF wrapper for MIDI files and later expanded by the MIDI Manufacturers Association to support embedding DLS sound banks. However, DLS is not widely used today, whereas the SoundFont2 (SF2) format serves a similar purpose and remains quite popular. The SF2 RMIDI format integrates MIDI and SF2 files into a single file, augmented with additional metadata. This document serves as a specification for this format extension. This version of RMIDI was created by Zoltán Bacskó of Falcosoft and implemented in Falcosoft SoundFont Midi Player 6. I am in contact with Zoltán, who granted permission for me to write this specification. If you find any part of this specification unclear, please reach out via this thread or file a GitHub issue in this repository. Also feel free to report any issues such as typos or expansions!
This extension has been designed with the following goals in mind:
This specification assumes familiarity with the SoundFont2 format and the Standard MIDI File (SMF) format. Additional terminology used in this specification includes:
Bank Select MSB and the bank number of a SoundFont preset. It’s a 7-bit value, except for the SoundFont’s drum presets, which use bank number 128.The file extension is .rmi.
The file type should be referred to as MIDI with embedded SF2, Embedded MIDI or SF2 RMIDI.
The RMIDI format uses RIFF chunks to structure the data.
The RIFF format is unchanged from the original RMIDI format. Described here for completeness.
Each RIFF chunk in an RMIDI file follows this format:
RIFF)sfbk)52 49 46 46 05 00 00 00 48 65 6C 6C 6F 00
52 49 46 46 - ASCII string “RIFF”05 00 00 00 - 32-bit chunk length: 548 65 6C 6C 6F - the chunk’s data: ASCII string “Hello”00 - a pad byte of 0 to make the total byte count even.An RMIDI file consists of:
RIFF chunk (main chunk)
RMID ASCII stringdata chunk containing the complete MIDI file (MThd, MTrk, etc.)LIST chunk: Metadata for the file, similar to SF2’s chunk
INFO ASCII stringRIFF chunk: Complete soundfont binary.
It is optional.
The first four bytes of this chunk should be sfbk, indicating a soundfont2 binary.
SoundFont3 format is allowed.
Note that for legacy files, it may instead be a DLS file.RIFF chunk
RMID ASCII stringdata - The MIDI file data: MThd, MTrk etc…LIST
INFO ASCII stringINAM chunk
Never Gonna Give You Up UTF-8 stringIART chunk
Rick Astley UTF-8 stringICRD chunk
1987 UTF-8 stringIENC chunk
utf-8 ASCII stringDBNK chunk
RIFF chunk - the SoundFont binary file.The following file structure shows that:
UTF-8 encoding.When the file structure deviates from the above:
Software that supports DLS should use the contained DLS
and assume a bank offset of 1 or try to detect the bank offset
since the older format does not specify the DBNK chunk.
The last two rules ensure backwards compatibility with the older RMIDI format.
The INFO chunk describes file metadata and the soundfont’s bank offset.
The INFO chunk may contain the following optional chunks:
DBNK chunk: Soundfont’s bank offset. See DBNK Chunk for details.IENC chunk: Encoding used for the metadata chunks: name of the encoding stored as string.
Not case-sensitive, but lowercase is preferred (e.g., utf-8).
Software capable of reading the IENC chunk must support the following encodings.
Note that this field must use basic ASCII encoding.MENC chunk: Encoding hint for the text evens within the MIDI file. The same string format as IENC.Below are the defined chunks containing additional information about the song:
INAM chunk: Song name/title. String of any length.ICOP chunk: Copyright. String of any length.IART chunk: Artist (MIDI creator). String of any length.ICRD chunk: Creation date. String of any length. The software must write the date as an ISO 8601 date or date time string. For example 2025-08-17 or 2025-08-17T19:15:25Z. This allows software to parse the date and display it in a different way or use it for management purposes.IPRD or IALB chunk: Album name. String of any length. It can be used interchangeably. If both exist in the file, the software should use IALB.IPIC chunk: Attached picture (e.g., album cover). Binary picture data. PNG or JPEG recommended.IGNR chunk: Song genre. String of any length.ICMT chunk: Comment/description. String of any length.IENG chunk: Engineer (soundfont creator). String of any length.ISFT chunk: Software used to create the file. String of any length.ISBJ chunk: Subject of the file. String of any length.The following rules apply to the INFO chunk:
IENC chunk is not specified, the software can use any encoding, but assuming utf-8 is recommended.MENC chunk is not specified, the software decides MIDI’s encoding.For Level 3 compatibility, software must support the following encodings (both lowercase and uppercase):
utf-8shift-jis or Shift_JIS (equivalent encodings)windows-1250 (Central Europe)windows-1251 (Cyrillic)windows-1252 (Western)windows-1253 (Greek)windows-1254 (Turkish)windows-1255 (Hebrew)windows-1256 (Arabic)windows-1257 (Baltic)windows-1258 (Vietnamese)Software may decode other encodings but is not required to.
For Level 4 compatibility, software must support the following image formats:
Other formats (e.g., gif, webp, ico) may also be supported but are not required.
The DBNK chunk is an optional RIFF chunk within the RMIDI INFO List.
It describes the bank offset for the embedded sound bank.
It always has a length of two bytes, with these bytes forming a 16-bit, unsigned, little-endian number. If the chunk’s length is not two bytes or the number is out of range, the file should be rejected.
Current boundaries are: minimum: 0 and maximum: 127. The other byte is reserved for future use.
If no DBNK is specified, an offset of 1 is assumed by default. If the file does not contain any Sound bank (SF2 or DLS), the offset shall default to 0.
For general use, a bank offset of 0 is recommended as it allows bundling the soundfont and the MIDI without modification.
The RMI file may come with an embedded SF2 or DLS SoundFont, usually after the INFO chunk. This sound bank provides the exclusive sounds used within the MIDI sequence, temporarily replacing given MIDI program and bank numbers with the presets contained within the sound bank.
The bank offset adjusts every bank in the embedded sound bank
except for bank 128 by adding itself to every patch’s wBank field.
For files without an embedded sound bank, the bank offset is ignored and assumed to be 0, regardless of the DBNK chunk if present.
For example:
If a preset named Acoustic Piano 2 with program 0 and bank 1 exists within an RMIDI file which uses bank offset of 1,
it should effectively be interpreted as program 0 and bank 2.
If a preset named Standard Drum Kit exists within the same RMIDI file with program 0 and bank 128,
the bank will remain 128.
If the resulting bank number exceeds 127 (except for drum kits) or is smaller than 0, then it should be turned into 0.
Below is a simple JavaScript-like code for a Level 1 RMIDI-compatible player.
Note: this code does not perform any checks and assumes that the file is valid and contains all three chunks, for the sake of simplicity.
const file = open("song.rmi");
// read RIFF
const chunk = readRIFF(file);
// skip 'RMID' string
chunk.data.seek(chunk.data.position + 4);
// read 'data' chunk
const midiChunk = readRIFF(chunk.data);
const midiFile = midiChunk.data;
// read the 'LIST' INFO chunk
const info = readRIFF(chunk.data);
// skip the 'INFO' string
const infoString = info.data.seek(info.data.position + 4);
const infoList = readLIST(info.data);
// bank offset is 1 by default
let bankOffset = 1;
// if DBNK exists
if(infoList.find(infoChunk => infoChunk.header === "DBNK")) {
// DBNK is 2 bytes signed int 16
bankOffset = infoList["DBNK"].toSignedInt16();
}
// clamp the bank offset
bankOffset = Math.min(Math.max(0, bankOffset), 127);
// read the sound bank (not as a riff chunk but copy the binary content)
const soundFont = chunk.slice(chunk.data.position, chunk.data.length - chunk.data.position);
// initialize the synthesizer
const player = new Player(soundFont);
// adjust bank offset
for(const preset of player.soundFont.presets)
{
preset.bankNumber += bankOffset;
}
// play the song
player.play(midiFile);
Not all chunks in the file must be read for the file to play correctly. Software compatibility with the RMIDI format is categorized into levels:
Minimum requirements for the software to be compliant. The software must:
RMID ASCII string as the file indicator.data chunk containing the MIDI data.DBNK chunk within the INFO chunk and correctly offset the soundfont (or a bank select messages in the MIDI) based on this value.RIFF chunk with the soundfont data.This level ensures the correct playback and is recommended for software that does not need to support metadata.
This level requires basic interpretation of the INFO chunk. The software must:
INAM, IPRD, ICRD, ICOP, etc.) as ASCII or utf-8.This level requires support for the IENC chunk. The software must:
IENC chunk and support the required encodings.As of 2024-08-07, Falcosoft Midi Player meets this level of compatibility.
This level requires support for the IPIC chunk. The software must:
IPIC chunk and support the required image formats.As of 2024-08-06, SpessaSynth meets this level of compatibility.
As of 2024-08-20, foo_midi meets this level of compatibility.
There are currently two distinct types of RMIDI files that vary in their use cases.
Note that these have identical file structure; these vary only in the way they provide sounds for the sequence.
A self-contained file is defined as a SF2 RMIDI file which only refers to its own SoundFont bank,
and the said bank contains all and only the necessary presets to play the file.
It is recommended to use DBNK of 0 for writing such files, but it is not required.
Writing self-contained RMIDI files is recommended, but not required.
An external file is defined as a SF2 RMIDI file which relies on a complete sound bank loaded as a fallback with the embedded sound bank only containing special sound effects, specific to the file.
The software not capable of loading two sound banks at once (the main one and the embedded one) may reject the file.
This type of file usually uses bank 1 or greater, but it may use bank 0.
The following recommendations are not required for file validity but are advised:
utf-8 encoding for the metadata chunks if possible.The directory examples contains RMIDI Files for testing:
Field of Hopes and Dreams - complete, level 4, self-contained file with IPIC chunk and metadata. Offset 0. Uses SF3 compression.GRABBAG_EmbeddedSF2 - self-contained file with no DBNK chunk. Offset 1.offset_5 - self-contained file with offset of 5.Rock_test - external file with no DBNK chunk. Offset 1, expects a full GM sound bank loaded at bank 0.bachsb - an RMIDI file without an embedded sound bank.AWEBLOWN - a DLS RMIDI file no DBNK chunk.
Offset 1, expects a full GM sound bank loaded at bank 0.
Software not capable of reading DLS should reject this file.Below is SpessaSynth implementation of the format in JavaScript, which may be useful for developers:
This document is in no way endorsed or otherwise affiliated with the MIDI Manufacturers Association, Microsoft, Creative Technology Ltd. or E-mu Systems, Inc., or any other organization mentioned in this document.
SoundFont® is a registered trademark of Creative Technology Ltd.
All other trademarks are the property of their respective owners.