/midi2tones

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

Primary LanguageCOtherNOASSERTION

/*********************************************************************************************
*
*  MIDI2TONES: Convert a MIDI file into a simple bytestream of notes
*
*  This is a fork of MIDITONES as it stood on September 27, 2016
*  Copyright (c) 2011,2013,2015,2016, Len Shustek
*    https://github.com/LenShustek/miditones
*
*  The purpose of the fork was to add an alternate output format.
*
*  MIDI2TONES 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
*    https://github.com/MLXXXp/midi2tones
*
*  This was written for the "Playtune" series of Arduino and Teensy microcontroller
*  synthesizers. See the separate documentation for the various Playtune players at
*    https://github.com/LenShustek/arduino-playtune
*    https://github.com/LenShustek/ATtiny-playtune
*    https://github.com/LenShustek/Playtune_poll
*    https://github.com/LenShustek/Playtune_samp
*  and also the ArduboyPlaytune library derived from arduino-playtune
*    https://github.com/Arduboy/ArduboyPlaytune
*
*  MIDI2TONES may also prove useful for other simple music synthesizers.
*
*  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.
*
*  An alternate output format can be specified, which consists of a single monotonic
*  stream of frequency/duration pairs of 16 bit values. The specified frequency can
*  also include a flag to indicate that the note is to be played at a higher volume,
*  if the velocity of the MIDI note is above a certain value.
*  This format is suitable for use with the ArduboyTones library, which is on GitHub at
*    https://github.com/MLXXXp/ArduboyTones
*
*  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.
*
*  MIDI2TONES 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 Playtune bytestream generated by MIDI2TONES into a piano-player
*  like listing for debugging or annotation. See the documentation in the
*  beginning of its source code.
*
*
*  *****  The MIDI2TONES command line  *****
*
*  To convert a MIDI file called "chopin.mid" into a command bytestream, execute
*
*     midi2tones 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:
*
*     midi2tones <options> <basefilename>
*
*  Options must be specified individually, each with its own "-" lead-in, and separated
*  with spaces. A forward slash "/" can be used instead of a dash "-" for option lead-ins. 
*
*  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:
*
*  -on  Generate output format "n".
*       Two formats are available:
*          1: The Playtune format (which is the default if this option isn't given).
*          2: The frequency/duration pair format, as used by ArduboyTones.
*
*  -v   Add velocity (volume) information to the output bytestream.
*
*  -vn  For the alternate format, "n" specifies the minimum velocity value that will
*       produce a high volume tone. Without this option all tones will be
*       normal volume.
*
*  -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 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.
*       For the alternate output format, only the lowest bit will be used to specify
*       the single channel to be processed, and without this option channel 0 will
*       be used.
*
*  -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.
*
*  -fx  For the alternate output format, instead of using defined note names,
*       output actual frequency values in decimal format depending on "x":
*          -fa: For high volume notes use format "<freq>+TONE_HIGH_VOLUME".
*          -fb: For high volume notes just add 0x8000 to the frequency value.
*
*  -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. Will be generated if
*           the -r option was given.
*
*  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.
*
*  *****  The alternate frequency/duration pair output format  *****
*
*  The generated stream is a series of frequency/duration value pairs. The frequency
*  is in Hz and the duration is in milliseconds. Each value is 16 bits. For a binary
*  file the values are stored high byte first. The ArduboyTones player supports
*  frequencies from 16 Hz to 32767 Hz but MIDI2TONES converts MIDI note numbers in the
*  range from note 12 (16.352 Hz rounded to 16 Hz) to note 127 (12543.9 Hz rounded
*  to 12544 Hz).
*
*  Periods of silence are represented by a frequency/duration pair with a frequency
*  value of 0.
*
*  Since the output is monotonic, only one MIDI channel is processed. The lowest bit
*  set in the -cn option's mask will indicate the channel to be used. If the -cn option
*  isn't given, channel 0 will be used.
*
*  Tones can be specified to play at either normal or high volume. High volume is
*  indicated by setting the high bit of the frequency value (i.e. adding 0x8000 to the
*  desired frequency). A note will be set to high volume if the -vn option is used and
*  the MIDI velocity of the note is equal to or greater than the option value.
*
*  For the C output format, frequencies will be output as note names, as defined in the
*  ArduboyTones library's ArduboyTonesPitches.h file. If the -f option is given,
*  the actual frequency, in decimal, will be used instead. Durations will be output
*  in decimal.
*
*  Output files are terminated with a single 16 bit value of 0x8000 to indicate
*  end of score - stop playing. A file can instead be terminated with 0x8001 to indicate
*  end of score - start playing again from the beginning, which is specified using the
*  -r option.
*
*  Len Shustek, 4 Feb 2011 and later.
*  Frequency/duration pair output format and other changes:
*  Scott Allen, 27 Sept 2016 and later.
*********************************************************************************************/