standard_midi_file_format
Differences
This shows you the differences between two versions of the page.
| standard_midi_file_format [2011/10/12 00:08] – created javapimp | standard_midi_file_format [2023/08/18 18:15] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Standard MIDI File Format ====== | ||
| + | |||
| + | //Dustin Caldwell// | ||
| + | |||
| + | The standard MIDI file format is a very strange beast. When viewed as a | ||
| + | whole, it can be quite overwhelming. Of course, no matter how you look at it, | ||
| + | describing a piece of music in enough detail to be able to reproduce it | ||
| + | accurately is no small task. So, while complicated, | ||
| + | file format is fairly intuitive when understood. | ||
| + | |||
| + | I must insert a disclaimer here that I am by no means an expert with | ||
| + | midi nor midi files. I recently obtained a Gravis UltraSound board for my PC, | ||
| + | and upon hearing a few midi files (.MID) thought, "Gee, I'd like to be able to | ||
| + | make my own .MID files." | ||
| + | this was no trivial task. But, I couldn' | ||
| + | (besides, I once told my wife that computers aren't really that hard to use, | ||
| + | and I'd hate to be a hypocrite) So if any errors are found in this | ||
| + | information, | ||
| + | does not extend to EVERY type of midi command and EVERY possible file | ||
| + | configuration. It is a basic guide that should enable the reader (with a | ||
| + | moderate investment in time) to generate a quality midi file. | ||
| + | |||
| + | ===== 1. Overview ===== | ||
| + | |||
| + | A midi (.MID) file contains basically 2 things, Header chunks and Track | ||
| + | chunks. Section 2 explains the header chunks, and Section 3 explains the track | ||
| + | chunks. A midi file contains ONE header chunk describing the file format, | ||
| + | etc., and any number of track chunks. A track may be thought of in the same | ||
| + | way as a track on a multi-track tape deck. You may assign one track to each | ||
| + | voice, each staff, each instrument or whatever you want. | ||
| + | |||
| + | ===== 2. Header Chunk ===== | ||
| + | |||
| + | The header chunk appears at the beginning of the file, and describes the | ||
| + | file in three ways. The header chunk always looks like: | ||
| + | |||
| + | '' | ||
| + | |||
| + | The ascii equivalent of the first 4 bytes is MThd. After MThd comes the 4-byte | ||
| + | size of the header. This will always be 00 00 00 06, because the actual header | ||
| + | information will always be 6 bytes. | ||
| + | |||
| + | '' | ||
| + | |||
| + | 0 - single-track\\ | ||
| + | 1 - multiple tracks, synchronous\\ | ||
| + | 2 - multiple tracks, asynchronous\\ | ||
| + | |||
| + | Single track is fairly self-explanatory - one track only. Synchronous multiple | ||
| + | tracks means that the tracks will all be vertically synchronous, | ||
| + | words, they all start at the same time, and so can represent different parts | ||
| + | in one song. Asynchronous multiple tracks do not necessarily start at the same | ||
| + | time, and can be completely asynchronous. | ||
| + | |||
| + | '' | ||
| + | |||
| + | '' | ||
| + | later) | ||
| + | |||
| + | ===== 3. Track Chunks ===== | ||
| + | |||
| + | The remainder of the file after the header chunk consists of track chunks. | ||
| + | Each track has one header and may contain as many midi commands as you like. | ||
| + | The header for a track is very similar to the one for the file: | ||
| + | |||
| + | '' | ||
| + | |||
| + | As with the header, the first 4 bytes has an ascii equivalent. This one is | ||
| + | MTrk. The 4 bytes after MTrk give the length of the track (not including the | ||
| + | track header) in bytes. | ||
| + | |||
| + | Following the header are midi events. These events are identical to the | ||
| + | actual data sent and received by MIDI ports on a synth with one addition. A | ||
| + | midi event is preceded by a delta-time. A delta time is the number of ticks | ||
| + | after which the midi event is to be executed. The number of ticks per quarter | ||
| + | note was defined previously in the file header chunk. This delta-time is a | ||
| + | variable-length encoded value. This format, while confusing, allows large | ||
| + | numbers to use as many bytes as they need, without requiring small numbers to | ||
| + | waste bytes by filling with zeros. The number is converted into 7-bit bytes, | ||
| + | and the most-significant bit of each byte is 1 except for the last byte of the | ||
| + | number, which has a msb of 0. This allows the number to be read one byte at a | ||
| + | time, and when you see a msb of 0, you know that it was the last (least | ||
| + | significant) byte of the number. According to the MIDI spec, the entire delta- | ||
| + | time should be at most 4 bytes long. | ||
| + | |||
| + | Following the delta-time is a midi event. Each midi event (except a | ||
| + | running midi event) has a command byte which will always have a msb of 1 (the | ||
| + | value will be >= 128). A list of most of these commands is in appendix A. Each | ||
| + | command has different parameters and lengths, but the data that follows the | ||
| + | command will have a msb of 0 (less than 128). The exception to this is a meta- | ||
| + | event, which may contain data with a msb of 1. However, meta-events require a | ||
| + | length parameter which alleviates confusion. | ||
| + | |||
| + | One subtlety which can cause confusion is running mode. This is where | ||
| + | the actual midi command is omitted, and the last midi command issued is | ||
| + | assumed. This means that the midi event will consist of a delta-time and the | ||
| + | parameters that would go to the command if it were included. | ||
| + | |||
| + | ===== 4. Conclusion ===== | ||
| + | |||
| + | If this explanation has only served to confuse the issue more, the | ||
| + | appendices contain examples which may help clarify the issue. Also, 2 | ||
| + | utilities and a graphic file should have been included with this document: | ||
| + | |||
| + | DEC.EXE - This utility converts a binary file (like .MID) to a tab-delimited | ||
| + | text file containing the decimal equivalents of each byte. | ||
| + | |||
| + | REC.EXE - This utility converts a tab-delimited text file of decimal values | ||
| + | into a binary file in which each byte corresponds to one of the decimal | ||
| + | values. | ||
| + | |||
| + | MIDINOTE.PS - This is the postscript form of a page showing note numbers with | ||
| + | a keyboard and with the standard grand staff. | ||
| + | |||
| + | ====== Appendix A ====== | ||
| + | |||
| + | ===== 1. MIDI Event Commands ===== | ||
| + | |||
| + | Each command byte has 2 parts. The left nybble (4 bits) contains the actual | ||
| + | command, and the right nybble contains the midi channel number on which the | ||
| + | command will be executed. There are 16 midi channels, and 8 midi commands (the | ||
| + | command nybble must have a msb of 1). | ||
| + | |||
| + | In the following table, x indicates the midi channel number. Note that all | ||
| + | data bytes will be <128 (msb set to 0). | ||
| + | |||
| + | < | ||
| + | Hex Binary | ||
| + | 8x | ||
| + | nn=note number | ||
| + | vv=velocity | ||
| + | |||
| + | 9x | ||
| + | nn=note number | ||
| + | vv=velocity | ||
| + | |||
| + | Ax | ||
| + | nn=note number | ||
| + | vv=velocity | ||
| + | |||
| + | Bx | ||
| + | cc=controller number | ||
| + | vv=new value | ||
| + | |||
| + | Cx | ||
| + | pp=new program number | ||
| + | |||
| + | Dx | ||
| + | cc=channel number | ||
| + | |||
| + | Ex | ||
| + | change) | ||
| + | bb=bottom (least sig) 7 bits of value | ||
| + | tt=top (most sig) 7 bits of value | ||
| + | </ | ||
| + | | ||
| + | The following table lists meta-events which have no midi channel number. They | ||
| + | are of the format: | ||
| + | |||
| + | '' | ||
| + | |||
| + | All meta-events start with FF followed by the command (xx), the length, or | ||
| + | number of bytes that will contain data (nn), and the actual data (dd). | ||
| + | |||
| + | < | ||
| + | Hex Binary | ||
| + | 00 | ||
| + | nn=02 (length of 2-byte sequence number) | ||
| + | ssss=sequence number | ||
| + | |||
| + | 01 | ||
| + | nn=length in bytes of text | ||
| + | tt=text characters | ||
| + | |||
| + | 02 | ||
| + | copyright info. | ||
| + | nn tt=same as text event | ||
| + | |||
| + | 03 | ||
| + | nn tt=same as text event | ||
| + | |||
| + | 04 | ||
| + | nn tt=same as text event | ||
| + | |||
| + | 05 | ||
| + | nn tt=same as text event | ||
| + | |||
| + | 06 | ||
| + | nn tt=same as text event | ||
| + | |||
| + | 07 | ||
| + | nn tt=same as text event | ||
| + | |||
| + | 20 MIDI channel prefix assignment | ||
| + | |||
| + | 21 has been used, though unofficially, | ||
| + | |||
| + | |||
| + | 2F | ||
| + | track | ||
| + | |||
| + | 51 | ||
| + | tttttt=microseconds/ | ||
| + | |||
| + | 54 SMPTE offset | ||
| + | |||
| + | 58 | ||
| + | nn=numerator of time sig. | ||
| + | dd=denominator of time sig. 2=quarter | ||
| + | 3=eighth, etc. | ||
| + | cc=number of ticks in metronome click | ||
| + | bb=number of 32nd notes to the quarter | ||
| + | note | ||
| + | |||
| + | 59 | ||
| + | sf=sharps/ | ||
| + | 7=7 sharps) | ||
| + | mi=major/ | ||
| + | |||
| + | 7F | ||
| + | xx=number of bytes to be sent | ||
| + | dd=data | ||
| + | </ | ||
| + | |||
| + | The following table lists system messages which control the entire system. | ||
| + | These have no midi channel number. (these will generally only apply to | ||
| + | controlling a midi keyboard, etc.) | ||
| + | |||
| + | < | ||
| + | Hex Binary | ||
| + | F8 | ||
| + | required. | ||
| + | |||
| + | FA | ||
| + | |||
| + | FB | ||
| + | off | ||
| + | |||
| + | FC | ||
| + | </ | ||
| + | |||
| + | The following table lists the numbers corresponding to notes for use in note | ||
| + | on and note off commands. | ||
| + | |||
| + | |||
| + | < | ||
| + | Octave|| | ||
| + | # | ||
| + | || C | C# | D | D# | E | F | F# | G | G# | A | A# | B | ||
| + | ----------------------------------------------------------------------------- | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | 10 || 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | | ||
| + | </ | ||
| + | |||
| + | ====== | ||
| + | |||
| + | "MIDI Systems and Control" | ||
| + | |||
| + | "MIDI and Sound Book for the Atari ST" Bernd Enders and Wolfgang Klemme 1989 M&T Publishing, Inc. | ||
| + | |||
| + | MIDI file specs and general MIDI specs were also obtained by sending e-mail to LISTSERV@AUVM.AMERICAN.EDU with the phrase GET MIDISPEC PACKAGE in the message. | ||
| + | |||
| + | |||
| + | ====== DEC.CPP ====== | ||
| + | <code cpp> | ||
| + | /* file dec.cpp | ||
| + | |||
| + | by Dustin Caldwell | ||
| + | |||
| + | */ | ||
| + | |||
| + | |||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | void helpdoc(); | ||
| + | |||
| + | main() | ||
| + | { | ||
| + | FILE *fp; | ||
| + | |||
| + | unsigned char ch, c; | ||
| + | |||
| + | if((fp=fopen(_argv[1], | ||
| + | { | ||
| + | printf(" | ||
| + | helpdoc(); | ||
| + | exit(-1); | ||
| + | } | ||
| + | |||
| + | c=0; | ||
| + | ch=fgetc(fp); | ||
| + | |||
| + | while(!feof(fp)) | ||
| + | { | ||
| + | printf(" | ||
| + | c++; | ||
| + | if(c> | ||
| + | { | ||
| + | c=0; | ||
| + | printf(" | ||
| + | } | ||
| + | |||
| + | ch=fgetc(fp); | ||
| + | } | ||
| + | |||
| + | fclose(fp); | ||
| + | } | ||
| + | |||
| + | void helpdoc() | ||
| + | { | ||
| + | printf(" | ||
| + | |||
| + | printf(" | ||
| + | |||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ====== REC.CPP ====== | ||
| + | |||
| + | <code cpp> | ||
| + | /* File rec.cpp | ||
| + | by Dustin Caldwell | ||
| + | */ | ||
| + | |||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | void helpdoc(); | ||
| + | |||
| + | main() | ||
| + | { | ||
| + | FILE *rfp, *wfp; | ||
| + | |||
| + | unsigned char ch, c; | ||
| + | char s[20]; | ||
| + | |||
| + | if((rfp=fopen(_argv[1], | ||
| + | { | ||
| + | printf(" | ||
| + | helpdoc(); | ||
| + | exit(-1); | ||
| + | } | ||
| + | |||
| + | if((wfp=fopen(_argv[2], | ||
| + | { | ||
| + | printf(" | ||
| + | helpdoc(); | ||
| + | exit(-1); | ||
| + | } | ||
| + | |||
| + | c=0; | ||
| + | |||
| + | ch=fgetc(rfp); | ||
| + | |||
| + | while(!feof(rfp)) | ||
| + | { | ||
| + | |||
| + | if(isalnum(ch)) | ||
| + | { | ||
| + | c=0; | ||
| + | while(isdigit(ch)) | ||
| + | { | ||
| + | s[c]=ch; | ||
| + | c++; | ||
| + | ch=fgetc(rfp); | ||
| + | } | ||
| + | s[c]=NULL; | ||
| + | |||
| + | fputc(atoi(s), | ||
| + | |||
| + | } | ||
| + | |||
| + | ch=fgetc(rfp); | ||
| + | |||
| + | |||
| + | } | ||
| + | |||
| + | fclose(rfp); | ||
| + | fclose(wfp); | ||
| + | } | ||
| + | |||
| + | |||
| + | void helpdoc() | ||
| + | { | ||
| + | printf(" | ||
| + | |||
| + | printf(" | ||
| + | |||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | printf(" | ||
| + | } | ||
| + | </ | ||