parent
fd994d37cb
commit
ee76ad2d1f
@ -1,171 +1,241 @@ |
||||
/********************************************************************************************* |
||||
* |
||||
* 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 |
||||
* www.github.com/LenShustek/Playtune_synth |
||||
* 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 |
||||
* |
||||
|
||||
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 |
||||
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. |
||||
|
||||
MIDITONES was written for the "Playtune" series of Arduino and Teensy |
||||
microcontroller software synthesizers: |
||||
|
||||
www.github.com/LenShustek/arduino-playtune |
||||
This original version of Playtune, first released in 2011, uses a separate hardware timer |
||||
for each note to generate a square wave on an output pin. All the pins are then combined |
||||
with a simple resistor network connected to a speaker and/or amplifier. It can only play |
||||
as many simutaneous notes as there are timers. There is no volume modulation. |
||||
|
||||
www.github.com/LenShustek/Playtune_poll |
||||
This second vesion uses only one hardware timer that interrupts periodically at a fast |
||||
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 |
||||
simultaneous notes is limited only by the number of output pins and the speed of the processor. |
||||
|
||||
www.github.com/LenShustek/Playtune_samp |
||||
The third version also uses only one hardware timer interrupting frequently, but |
||||
uses the hardware digital-to-analog converter on high-performance microntrollers like |
||||
the Teensy to generate an analog wave that is the sum of stored samples of sounds for |
||||
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 |
||||
quality is much better, although not in league with real synthesizers. It currently |
||||
only supports Teensy boards. |
||||
|
||||
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 |
||||
It allows up to 16 simultaneous sound generators that are internally mixed, at |
||||
the appropriate volume, to produce one monophonic audio stream. |
||||
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 |
||||
(from MIDI channel 10) are generated from longer sampled waveforms of a complete |
||||
instrument strike. Each generator's volume is independently adjusted according to |
||||
the MIDI velocity of the note being played before all channels are mixed. |
||||
|
||||
www.github.com/LenShustek/ATtiny-playtune |
||||
This is a much simplified version that fits, with a small song, into an ATtiny |
||||
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 |
||||
for the Evil Mad Scientist menorah kit: |
||||
https://www.evilmadscientist.com/2009/new-led-hanukkah-menorah-kit/ |
||||
https://forum.evilmadscientist.com/discussion/263/making-the-menorah-play-music |
||||
(Imagine what you can do with the $1 8-pin ATtiny85 with a whopping 8K!) |
||||
|
||||
MIDITONES may also prove useful for other simple music synthesizers. There are |
||||
various forks of the code, and the Playtune players, on Githib. |
||||
|
||||
*** THE PROGRAM |
||||
|
||||
MIDITONES is written in standard ANSI C and is meant to be executed from the |
||||
command line. There is no GUI interface. |
||||
|
||||
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. |
||||
|
||||
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 near the |
||||
top of its source code. |
||||
|
||||
|
||||
*** THE 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, and the output filename(s) |
||||
are the base file name with .c, .h, .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. |
||||
|
||||
-t=n 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: |
||||
|
||||
-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, |
||||
"n" can be also specified in hex using a 0x prefix. |
||||
|
||||
-dp Generate Arduino IDE-dependent C code that uses PROGMEM for the bytestream. |
||||
|
||||
-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. |
||||
|
||||
-lp Log input file parsing information to the <basefilename>.log file |
||||
|
||||
-lg Log output bytestream generation information to the <basefilename>.log file |
||||
|
||||
-n=x Put about "x" items on each line of the C file output |
||||
|
||||
-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. |
||||
|
||||
-pi Ignore notes in the MIDI percussion track 9 (also called 10 by some) |
||||
|
||||
-r Terminate the output file with a "restart" command instead of a "stop" command. |
||||
|
||||
-sn Use bytestream generation strategy "n". Two are currently implemented: |
||||
1:favor track 1 notes instead of all tracks equally |
||||
2:try to keep each track to its own tone generator |
||||
|
||||
-h Give command-line help. |
||||
|
||||
-delaymin=x Don't generate delays less than x milliseconds long, to reduce the number |
||||
of "delay" commands and thus make the bytestream smaller, at the expense of |
||||
moving notes slightly. The deficits are accumulated and eventually used, |
||||
so that there is no loss of synchronization in the long term. |
||||
The default is 0, which means timing is exact to the millisecond. |
||||
|
||||
-releasetime=x Stop each note x milliseconds before it is supposed to end. This results |
||||
in better sound separation between notes. It might also allow more notes to |
||||
be played with fewer tone generators, since there could be fewer |
||||
simultaneous notes playing. |
||||
|
||||
-notemin=x The releasetime notwithstanding, don't let the resulting note be reduced |
||||
to smaller than x milliseconds. Making releasetime very large and notemin |
||||
small results in staccato sounds. |
||||
|
||||
-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…
Reference in new issue