/miditones

Convert MIDI files to a simplified time-ordered sequence of note commands

Primary LanguageCMIT LicenseMIT

/*********************************************************************************************
*
*  MIDITONES: Convert a MIDI file into a simple bytestream of notes
*
*
*  MIDITONES converts a MIDI music file into a much simplified stream of commands, so that
*  the music can easily be played on a small microcontroller-based synthesizer that has
*  only simple tone generators. This is on github at www.github.com/LenShustek/miditones.
*
*  Volume ("velocity") and instrument information in the MIDI file can either be
*  discarded or kept. All the tracks are processed and merged into a single time-ordered
*  stream of "note on", "note off", "change instrument" and "delay" commands.
*
*  This was written for the "Playtune" series of Arduino and Teensy microcontroller
*  synthesizers. See the separate documentation for the various Playtune.players at
*    www.github.com/LenShustek/arduino-playtune
*    www.github.com/LenShustek/ATtiny-playtune
*    www.github.com/LenShustek/Playtune_poll
*    www.github.com/LenShustek/Playtune_samp
*  MIDITONES may also prove useful for other simple music synthesizers..
*
*  The output can be either a C-language source code fragment that initializes an
*  array with the command bytestream, or a binary file with the bytestream itself.
*
*  MIDITONES is written in standard ANSI C and is meant to be executed from the
*  command line.  There is no GUI interface.
*
*  The MIDI file format is complicated, and this has not been tested on all of its
*  variations.  In particular we have tested only format type "1", which seems
*  to be what most of them are.  Let me know if you find MIDI files that it
*  won't digest and I'll see if I can fix it.
*
*  There is a companion program in the same repository called Miditones_scroll
*  that can convert the bytestream generated by MIDITONES into a piano-player
*  like listing for debugging or annotation. See the documentation in the
*  beginning of its source code.
*
*
*  *****  The MIDITONES command line  *****
*
*  To convert a MIDI file called "chopin.mid" into a command bytestream, execute
*
*     miditones chopin
*
*  It will create a file in the same directory called "chopin.c" which contains
*  the C-language statement to intiialize an array called "score" with the bytestream.
*
*
*  The general form for command line execution is this:
*
*     miditones  <options>  <basefilename>
*
*  The <basefilename> is the base name, without an extension, for the input and
*  output files. It can contain directory path information, or not.
*
*  The input file is <basefilename>.mid  The output filename(s)
*  are the base file name with .c, .bin, and/or .log extensions.
*
*
*  The following commonly-used command-line options can be specified:
*
*  -v   Add velocity (volume) information to the output bytestream
*
*  -i   Add instrument change commands to the output bytestream
*
*  -pt  Translate notes in the MIDI percussion track to note numbers 128..255
*       and assign them to a tone generator as usual.
*
*  -d   Generate a self-describing file header that says which optional bytestream
*       fields are present. This is highly recommended if you are using later
*       Playtune players that can check the header to know what data to expect.
*
*  -b   Generate a binary file with the name <basefilename>.bin, instead of a
*       C-language source file with the name <basefilename>.c.
*
*  -tn  Generate the bytestream so that at most "n" tone generators are used.
*       The default is 6 tone generators, and the maximum is 16. The program
*       will report how many notes had to be discarded because there weren't
*       enough tone generators.
*
*
* The best combination of options to use with the later Playtune music players is:
*        -v -i -pt -d
*
*
* The following are lesser-used command-line options:
*
*  -p   Only parse the MIDI file, and don't generate an output file.
*       Tracks are processed sequentially instead of being merged into chronological order.
*       This is mostly useful for debugging MIDI file parsing problems.
*
*  -lp  Log input file parsing information to the <basefilename>.log file
*
*  -lg  Log output bytestream generation information to the <basefilename>.log file
*
*  -nx  Put about "x" items on each line of the C file output
*
*  -sn  Use bytestream generation strategy "n".
*       Two strategies are currently implemented:
*          1:favor track 1 notes instead of all tracks equally
*          2:try to keep each track to its own tone generator
*
*  -cn  Only process the channel numbers whose bits are on in the number "n".
*       For example, -c3 means "only process channels 0 and 1". In addition to decimal,
*       "n" can be also specified in hex using a 0x prefix or octal with a 0 prefix.
*
*  -kn  Change the musical key of the output by n chromatic notes.
*       -k-12 goes one octave down, -k12 goes one octave up, etc.
*
*  -pi  Ignore notes in the MIDI percussion track 9 (also called 10 by some)
*
*  -dp  Generate IDE-dependent C code to define PROGMEM
*
*  -r   Terminate the output file with a "restart" command instead of a "stop" command.
*
*  -h   Give command-line help.
*
*
*  *****  The score bytestream  *****
*
*  The generated bytestream is a series of commands that turn notes on and off,
*  maybe change instruments, and begin delays until the next note change.
*  Here are the details, with numbers shown in hexadecimal.
*
*  If the high-order bit of the byte is 1, then it is one of the following commands:
*
*    9t nn [vv]
*           Start playing note nn on tone generator t, replacing any previous note.  
*           Generators are numbered starting with 0. The note numbers are the MIDI 
*           numbers for the chromatic scale, with decimal 69 being Middle A (440 Hz).
*           If the -v option was given, a second byte is added to indicate note volume.
*
*    8t     Stop playing the note on tone generator t.
*
*    Ct ii  Change tone generator t to play instrument ii from now on. This will only
*           be generated if the -i option was given.
*
*    F0     End of score; stop playing.
*
*    E0     End of score; start playing again from the beginning.
*
*  If the high-order bit of the byte is 0, it is a command to delay for a while until
*  the next note change.  The other 7 bits and the 8 bits of the following byte are
*  interpreted as a 15-bit big-endian integer that is the number of milliseconds to
*  wait before processing the next command.  For example,
*
*     07 D0
*
*  would cause a delay of 0x07d0 = 2000 decimal millisconds, or 2 seconds.  Any tones
*  that were playing before the delay command will continue to play.
*
*  If the -d option is specified, the bytestream begins with a little header that tells
*  what optional information will be in the data. This makes the file more self-describing,
*  and allows music players to adapt to different kinds of files. The later Playtune
*  players do that. The header looks like this:
*
*    'Pt'   Two ascii characters that signal the presence of the header
*     nn    The length (in one byte) of the entire header, 6..255
*     ff1   A byte of flag bits, three of which are currently defined:
*               80 velocity information is present
*               40 instrument change information is present
*               20 translated percussion notes are present
*     ff2    Another byte of flags, currently undefined
*     tt     The number (in one byte) of tone generators actually used in this music.
*
*     Any subsequent header bytes covered by the count, if present, are currently undefined
*     and should be ignored by players.
*
*  Len Shustek, 4 Feb 2011 and later
*