Major rewrite: queue out-of-order events to allow release/sustain note timing

pull/18/head
Len Shustek 6 years ago
parent fd994d37cb
commit ee76ad2d1f
  1. 412
      README.txt
  2. 2045
      miditones.c
  3. BIN
      miditones.exe

@ -1,171 +1,241 @@
/*********************************************************************************************
* MIDITONES: Convert a MIDI file into a simple bytestream of notes
* MIDITONES: Convert a MIDI file into a simple bytestream of notes
*
* MIDITONES compiles a MIDI music file into a much simplified compact time-ordered stream of
* MIDITONES converts a MIDI music file into a much simplified stream of commands, so that commands, so that the music can easily be played on a small microcontroller-based synthesizer
* the music can easily be played on a small microcontroller-based synthesizer that has that has only simple tone generators. This is on github at www.github.com/LenShustek/miditones.
* 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
* 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
* 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.
* stream of "note on", "note off", "change instrument" and "delay" commands.
* MIDITONES was written for the "Playtune" series of Arduino and Teensy
* This was written for the "Playtune" series of Arduino and Teensy microcontroller microcontroller software synthesizers:
* synthesizers. See the separate documentation for the various Playtune.players at
* www.github.com/LenShustek/arduino-playtune www.github.com/LenShustek/arduino-playtune
* www.github.com/LenShustek/ATtiny-playtune This original version of Playtune, first released in 2011, uses a separate hardware timer
* www.github.com/LenShustek/Playtune_poll for each note to generate a square wave on an output pin. All the pins are then combined
* www.github.com/LenShustek/Playtune_samp with a simple resistor network connected to a speaker and/or amplifier. It can only play
* www.github.com/LenShustek/Playtune_synth as many simutaneous notes as there are timers. There is no volume modulation.
* MIDITONES may also prove useful for other simple music synthesizers..
* www.github.com/LenShustek/Playtune_poll
* The output can be either a C-language source code fragment that initializes an This second vesion uses only one hardware timer that interrupts periodically at a fast
* array with the command bytestream, or a binary file with the bytestream itself. rate, and toggles square waves onto any number of digital output pins. It also implements
* primitive volume modulation by changing the duty cycle of the square wave. The number of
* MIDITONES is written in standard ANSI C and is meant to be executed from the simultaneous notes is limited only by the number of output pins and the speed of the processor.
* command line. There is no GUI interface.
* www.github.com/LenShustek/Playtune_samp
* The MIDI file format is complicated, and this has not been tested on all of its The third version also uses only one hardware timer interrupting frequently, but
* variations. In particular we have tested only format type "1", which seems uses the hardware digital-to-analog converter on high-performance microntrollers like
* to be what most of them are. Let me know if you find MIDI files that it the Teensy to generate an analog wave that is the sum of stored samples of sounds for
* won't digest and I'll see if I can fix it. many different instruments. The samples are scaled to the right frequency and volume,
* and any number of instrument samples can be used and mapped to MIDI patches. The sound
* There is a companion program in the same repository called Miditones_scroll quality is much better, although not in league with real synthesizers. It currently
* that can convert the bytestream generated by MIDITONES into a piano-player only supports Teensy boards.
* like listing for debugging or annotation. See the documentation in the
* beginning of its source code. www.github.com/LenShustek/Playtune_synth
* The fourth version is an audio object for the PJRC Audio Library.
* https://www.pjrc.com/teensy/td_libs_Audio.html
* ***** The MIDITONES command line ***** It allows up to 16 simultaneous sound generators that are internally mixed, at
* the appropriate volume, to produce one monophonic audio stream.
* To convert a MIDI file called "chopin.mid" into a command bytestream, execute Sounds are created from sampled one-cycle waveforms for any number of instruments,
* each with its own attack-hold-decay-sustain-release envelope. Percussion sounds
* miditones chopin (from MIDI channel 10) are generated from longer sampled waveforms of a complete
* instrument strike. Each generator's volume is independently adjusted according to
* It will create a file in the same directory called "chopin.c" which contains the MIDI velocity of the note being played before all channels are mixed.
* the C-language statement to intiialize an array called "score" with the bytestream.
* www.github.com/LenShustek/ATtiny-playtune
* This is a much simplified version that fits, with a small song, into an ATtiny
* The general form for command line execution is this: processor with only 4K of flash memory. It also using polling with only one timer,
* and avoids multiplication or division at runtime for speed. It was written
* miditones <options> <basefilename> for the Evil Mad Scientist menorah kit:
* https://www.evilmadscientist.com/2009/new-led-hanukkah-menorah-kit/
* The <basefilename> is the base name, without an extension, for the input and https://forum.evilmadscientist.com/discussion/263/making-the-menorah-play-music
* output files. It can contain directory path information, or not. (Imagine what you can do with the $1 8-pin ATtiny85 with a whopping 8K!)
*
* The input file is <basefilename>.mid The output filename(s) MIDITONES may also prove useful for other simple music synthesizers. There are
* are the base file name with .c, .bin, and/or .log extensions. various forks of the code, and the Playtune players, on Githib.
*
* *** THE PROGRAM
* The following commonly-used command-line options can be specified:
* MIDITONES is written in standard ANSI C and is meant to be executed from the
* -v Add velocity (volume) information to the output bytestream command line. There is no GUI interface.
*
* -i Add instrument change commands to the output bytestream 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.
* -pt Translate notes in the MIDI percussion track to note numbers 128..255
* and assign them to a tone generator as usual. 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
* -d Generate a self-describing file header that says which optional bytestream to be what most of them are. Let me know if you find MIDI files that it
* fields are present. This is highly recommended if you are using later won't digest and I'll see if I can fix it.
* Playtune players that can check the header to know what data to expect.
* There is a companion program in the same repository called Miditones_scroll
* -b Generate a binary file with the name <basefilename>.bin, instead of a that can convert the bytestream generated by MIDITONES into a piano-player
* C-language source file with the name <basefilename>.c. like listing for debugging or annotation. See the documentation near the
* top of its source code.
* -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 *** THE COMMAND LINE
* enough tone generators.
* To convert a MIDI file called "chopin.mid" into a command bytestream, execute
*
* The best combination of options to use with the later Playtune music players is: miditones chopin
* -v -i -pt -d
* 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 following are lesser-used command-line options:
*
* -p Only parse the MIDI file, and don't generate an output file. The general form for command line execution is this:
* Tracks are processed sequentially instead of being merged into chronological order.
* This is mostly useful for debugging MIDI file parsing problems. miditones <options> <basefilename>
*
* -lp Log input file parsing information to the <basefilename>.log file The <basefilename> is the base name, without an extension, for the input and
* output files. It can contain directory path information, or not.
* -lg Log output bytestream generation information to the <basefilename>.log file
* The input file is <basefilename>.mid, and the output filename(s)
* -nx Put about "x" items on each line of the C file output are the base file name with .c, .h, .bin, and/or .log extensions.
*
* -sn Use bytestream generation strategy "n".
* Two strategies are currently implemented: The following commonly-used command-line options can be specified:
* 1:favor track 1 notes instead of all tracks equally
* 2:try to keep each track to its own tone generator -v Add velocity (volume) information to the output bytestream
*
* -cn Only process the channel numbers whose bits are on in the number "n". -i Add instrument change commands to the output bytestream
* 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. -pt Translate notes in the MIDI percussion track to note numbers 128..255
* and assign them to a tone generator as usual.
* -kn Change the musical key of the output by n chromatic notes.
* -k-12 goes one octave down, -k12 goes one octave up, etc. -d Generate a self-describing file header that says which optional bytestream
* fields are present. This is highly recommended if you are using later
* -pi Ignore notes in the MIDI percussion track 9 (also called 10 by some) Playtune players that can check the header to know what data to expect.
*
* -dp Generate IDE-dependent C code to define PROGMEM -b Generate a binary file with the name <basefilename>.bin, instead of a
* C-language source file with the name <basefilename>.c.
* -r Terminate the output file with a "restart" command instead of a "stop" command.
* -t=n Generate the bytestream so that at most "n" tone generators are used.
* -h Give command-line help. 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 score bytestream *****
* The best combination of options to use with the later Playtune music players is:
* The generated bytestream is a series of commands that turn notes on and off, -v -i -pt -d
* maybe change instruments, and begin delays until the next note change.
* Here are the details, with numbers shown in hexadecimal. The following are lesser-used command-line options:
*
* If the high-order bit of the byte is 1, then it is one of the following commands: -c=n 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,
* 9t nn [vv] "n" can be also specified in hex using a 0x prefix.
* Start playing note nn on tone generator t, replacing any previous note.
* Generators are numbered starting with 0. The note numbers are the MIDI -dp Generate Arduino IDE-dependent C code that uses PROGMEM for the bytestream.
* 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. -k=n Change the musical key of the output by n chromatic notes.
* -k=-12 goes one octave down, -k=12 goes one octave up, etc.
* 8t Stop playing the note on tone generator t.
* -lp Log input file parsing information to the <basefilename>.log file
* Ct ii Change tone generator t to play instrument ii from now on. This will only
* be generated if the -i option was given. -lg Log output bytestream generation information to the <basefilename>.log file
*
* F0 End of score; stop playing. -n=x Put about "x" items on each line of the C file output
*
* E0 End of score; start playing again from the beginning. -p Only parse the MIDI file, and don't generate an output file.
* Tracks are processed sequentially instead of being merged into chronological order.
* If the high-order bit of the byte is 0, it is a command to delay for a while until This is mostly useful for debugging MIDI file parsing problems.
* 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 -pi Ignore notes in the MIDI percussion track 9 (also called 10 by some)
* wait before processing the next command. For example,
* -r Terminate the output file with a "restart" command instead of a "stop" command.
* 07 D0
* -sn Use bytestream generation strategy "n". Two are currently implemented:
* would cause a delay of 0x07d0 = 2000 decimal millisconds, or 2 seconds. Any tones 1:favor track 1 notes instead of all tracks equally
* that were playing before the delay command will continue to play. 2:try to keep each track to its own tone generator
*
* If the -d option is specified, the bytestream begins with a little header that tells -h Give command-line help.
* 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 -delaymin=x Don't generate delays less than x milliseconds long, to reduce the number
* players do that. The header looks like this: of "delay" commands and thus make the bytestream smaller, at the expense of
* moving notes slightly. The deficits are accumulated and eventually used,
* 'Pt' Two ascii characters that signal the presence of the header so that there is no loss of synchronization in the long term.
* nn The length (in one byte) of the entire header, 6..255 The default is 0, which means timing is exact to the millisecond.
* ff1 A byte of flag bits, three of which are currently defined:
* 80 velocity information is present -releasetime=x Stop each note x milliseconds before it is supposed to end. This results
* 40 instrument change information is present in better sound separation between notes. It might also allow more notes to
* 20 translated percussion notes are present be played with fewer tone generators, since there could be fewer
* ff2 Another byte of flags, currently undefined simultaneous notes playing.
* tt The number (in one byte) of tone generators actually used in this music.
* -notemin=x The releasetime notwithstanding, don't let the resulting note be reduced
* Any subsequent header bytes covered by the count, if present, are currently undefined to smaller than x milliseconds. Making releasetime very large and notemin
* and should be ignored by players. small results in staccato sounds.
*
* Len Shustek, 4 Feb 2011 and later -attacktime=x The high-volume attack phase of a note lasts x milliseconds, after which
* the lower-volume sustain phase begins, unless the release time makes it
too short. (Only valid with -v.)
-attacknotemax=x Notes larger than x milliseconds won't have the attack/sustain profile
applied. That allows sustained organ-like pedaling.
-sustainlevel=p The volume level during the sustain phase is p percent of the starting
note volume. (Only valid with -v.)
-scorename Use <basefilename> as the name of the score in the generated C code
instead of "score", and name the file <basefilename>.h instead of
<basefilenam>.c. That allows multiple scores to be directly #included
into an Arduino .ino file without modification.
Note that for backwards compatibility and easier batch-file processing, the equal sign
for specifying an option's numeric value may be omitted. Also, numeric values may be
specified in hex as 0xhhhh.
*** THE SCORE BYTESTREAM
The generated bytestream is a series of commands to turn notes on and off,
change instruments, and request a delay until the next event time.
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,
where the two characters are a hex representation of one byte:
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, the third byte specifies the 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, but start playing again from the beginning. This is
generated by the -r option.
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 volume 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, 2011 to 2019; see the change log.

File diff suppressed because it is too large Load Diff

Binary file not shown.
Loading…
Cancel
Save