├── LICENSE.txt ├── Makefile ├── README.txt ├── SAMPLE_MoneyMoney_scroll.txt ├── WINDOWS_EXE.txt ├── midi2tones.c ├── midi2tones_32bit.exe ├── midi2tones_64bit.exe ├── miditones_scroll.c ├── miditones_scroll_32bit.exe └── miditones_scroll_64bit.exe /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Original work Copyright (c) 2016, Len Shustek 4 | Modified work Copyright (c) 2016, Scott Allen 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | miditones: miditones.c 2 | gcc -O2 -Wall -o midi2tones midi2tones.c 3 | 4 | clean: 5 | rm -f midi2tones 6 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * MIDI2TONES: Convert a MIDI file into a simple bytestream of notes 4 | * 5 | * This is a fork of MIDITONES as it stood on September 27, 2016 6 | * Copyright (c) 2011,2013,2015,2016, Len Shustek 7 | * https://github.com/LenShustek/miditones 8 | * 9 | * The purpose of the fork was to add an alternate output format. 10 | * 11 | * MIDI2TONES converts a MIDI music file into a much simplified stream of commands, so that 12 | * the music can easily be played on a small microcontroller-based synthesizer that has 13 | * only simple tone generators. This is on GitHub at 14 | * https://github.com/MLXXXp/midi2tones 15 | * 16 | * This was written for the "Playtune" series of Arduino and Teensy microcontroller 17 | * synthesizers. See the separate documentation for the various Playtune players at 18 | * https://github.com/LenShustek/arduino-playtune 19 | * https://github.com/LenShustek/ATtiny-playtune 20 | * https://github.com/LenShustek/Playtune_poll 21 | * https://github.com/LenShustek/Playtune_samp 22 | * and also the ArduboyPlaytune library derived from arduino-playtune 23 | * https://github.com/Arduboy/ArduboyPlaytune 24 | * 25 | * MIDI2TONES may also prove useful for other simple music synthesizers. 26 | * 27 | * Volume ("velocity") and instrument information in the MIDI file can either be 28 | * discarded or kept. All the tracks are processed and merged into a single time-ordered 29 | * stream of "note on", "note off", "change instrument" and "delay" commands. 30 | * 31 | * An alternate output format can be specified, which consists of a single monotonic 32 | * stream of frequency/duration pairs of 16 bit values. The specified frequency can 33 | * also include a flag to indicate that the note is to be played at a higher volume, 34 | * if the velocity of the MIDI note is above a certain value. 35 | * This format is suitable for use with the ArduboyTones library, which is on GitHub at 36 | * https://github.com/MLXXXp/ArduboyTones 37 | * 38 | * The output can be either a C-language source code fragment that initializes an 39 | * array with the command bytestream, or a binary file with the bytestream itself. 40 | * 41 | * MIDI2TONES is written in standard ANSI C and is meant to be executed from the 42 | * command line. There is no GUI interface. 43 | * 44 | * The MIDI file format is complicated, and this has not been tested on all of its 45 | * variations. In particular we have tested only format type "1", which seems 46 | * to be what most of them are. Let me know if you find MIDI files that it 47 | * won't digest and I'll see if I can fix it. 48 | * 49 | * There is a companion program in the same repository called Miditones_scroll that 50 | * can convert the Playtune bytestream generated by MIDI2TONES into a piano-player 51 | * like listing for debugging or annotation. See the documentation in the 52 | * beginning of its source code. 53 | * 54 | * 55 | * ***** The MIDI2TONES command line ***** 56 | * 57 | * To convert a MIDI file called "chopin.mid" into a command bytestream, execute 58 | * 59 | * midi2tones chopin 60 | * 61 | * It will create a file in the same directory called "chopin.c" which contains 62 | * the C-language statement to intiialize an array called "score" with the bytestream. 63 | * 64 | * 65 | * The general form for command line execution is this: 66 | * 67 | * midi2tones 68 | * 69 | * Options must be specified individually, each with its own "-" lead-in, and separated 70 | * with spaces. A forward slash "/" can be used instead of a dash "-" for option lead-ins. 71 | * 72 | * The is the base name, without an extension, for the input and 73 | * output files. It can contain directory path information, or not. 74 | * 75 | * The input file is .mid The output filename(s) 76 | * are the base file name with .c, .bin, and/or .log extensions. 77 | * 78 | * 79 | * The following commonly-used command-line options can be specified: 80 | * 81 | * -on Generate output format "n". 82 | * Two formats are available: 83 | * 1: The Playtune format (which is the default if this option isn't given). 84 | * 2: The frequency/duration pair format, as used by ArduboyTones. 85 | * 86 | * -v Add velocity (volume) information to the output bytestream. 87 | * 88 | * -vn For the alternate format, "n" specifies the minimum velocity value that will 89 | * produce a high volume tone. Without this option all tones will be 90 | * normal volume. 91 | * 92 | * -i Add instrument change commands to the output bytestream. 93 | * 94 | * -pt Translate notes in the MIDI percussion track to note numbers 128..255 95 | * and assign them to a tone generator as usual. 96 | * 97 | * -d Generate a self-describing file header that says which optional bytestream 98 | * fields are present. This is highly recommended if you are using later 99 | * Playtune players that can check the header to know what data to expect. 100 | * 101 | * -b Generate a binary file with the name .bin, instead of a 102 | * C-language source file with the name .c. 103 | * 104 | * -tn Generate the bytestream so that at most "n" tone generators are used. 105 | * The default is 6 tone generators, and the maximum is 16. The program 106 | * will report how many notes had to be discarded because there weren't 107 | * enough tone generators. 108 | * 109 | * 110 | * The following are lesser-used command-line options: 111 | * 112 | * -p Only parse the MIDI file, and don't generate an output file. 113 | * Tracks are processed sequentially instead of being merged into chronological order. 114 | * This is mostly useful for debugging MIDI file parsing problems. 115 | * 116 | * -lp Log input file parsing information to the .log file. 117 | * 118 | * -lg Log output bytestream generation information to the .log file. 119 | * 120 | * -nx Put about "x" items on each line of the C file output. 121 | * 122 | * -sn Use bytestream generation strategy "n". 123 | * Two strategies are currently implemented: 124 | * 1: Favor track 1 notes instead of all tracks equally. 125 | * 2: Try to keep each track to its own tone generator. 126 | * 127 | * -cn Only process the channel numbers whose bits are on in the number "n". 128 | * For example, -c3 means "only process channels 0 and 1". In addition to decimal, 129 | * "n" can be also specified in hex using a 0x prefix or octal with a 0 prefix. 130 | * For the alternate output format, only the lowest bit will be used to specify 131 | * the single channel to be processed, and without this option channel 0 will 132 | * be used. 133 | * 134 | * -kn Change the musical key of the output by n chromatic notes. 135 | * -k-12 goes one octave down, -k12 goes one octave up, etc. 136 | * 137 | * -pi Ignore notes in the MIDI percussion track 9 (also called 10 by some). 138 | * 139 | * -dp Generate IDE-dependent C code to define PROGMEM. 140 | * 141 | * -fx For the alternate output format, instead of using defined note names, 142 | * output actual frequency values in decimal format depending on "x": 143 | * -fa: For high volume notes use format "+TONE_HIGH_VOLUME". 144 | * -fb: For high volume notes just add 0x8000 to the frequency value. 145 | * 146 | * -r Terminate the output file with a "restart" command instead of a "stop" command. 147 | * 148 | * -h Give command-line help. 149 | * 150 | * 151 | * ***** The score bytestream ***** 152 | * 153 | * The generated bytestream is a series of commands that turn notes on and off, 154 | * maybe change instruments, and begin delays until the next note change. 155 | * Here are the details, with numbers shown in hexadecimal. 156 | * 157 | * If the high-order bit of the byte is 1, then it is one of the following commands: 158 | * 159 | * 9t nn [vv] 160 | * Start playing note nn on tone generator t, replacing any previous note. 161 | * Generators are numbered starting with 0. The note numbers are the MIDI 162 | * numbers for the chromatic scale, with decimal 69 being Middle A (440 Hz). 163 | * If the -v option was given, a second byte is added to indicate note volume. 164 | * 165 | * 8t Stop playing the note on tone generator t. 166 | * 167 | * Ct ii Change tone generator t to play instrument ii from now on. This will only 168 | * be generated if the -i option was given. 169 | * 170 | * F0 End of score; stop playing. 171 | * 172 | * E0 End of score; start playing again from the beginning. Will be generated if 173 | * the -r option was given. 174 | * 175 | * If the high-order bit of the byte is 0, it is a command to delay for a while until 176 | * the next note change. The other 7 bits and the 8 bits of the following byte are 177 | * interpreted as a 15-bit big-endian integer that is the number of milliseconds to 178 | * wait before processing the next command. For example, 179 | * 180 | * 07 D0 181 | * 182 | * would cause a delay of 0x07d0 = 2000 decimal millisconds, or 2 seconds. Any tones 183 | * that were playing before the delay command will continue to play. 184 | * 185 | * If the -d option is specified, the bytestream begins with a little header that tells 186 | * what optional information will be in the data. This makes the file more self-describing, 187 | * and allows music players to adapt to different kinds of files. The later Playtune 188 | * players do that. The header looks like this: 189 | * 190 | * 'Pt' Two ascii characters that signal the presence of the header 191 | * nn The length (in one byte) of the entire header, 6..255 192 | * ff1 A byte of flag bits, three of which are currently defined: 193 | * 80 velocity information is present 194 | * 40 instrument change information is present 195 | * 20 translated percussion notes are present 196 | * ff2 Another byte of flags, currently undefined 197 | * tt The number (in one byte) of tone generators actually used in this music. 198 | * 199 | * Any subsequent header bytes covered by the count, if present, are currently undefined 200 | * and should be ignored by players. 201 | * 202 | * ***** The alternate frequency/duration pair output format ***** 203 | * 204 | * The generated stream is a series of frequency/duration value pairs. The frequency 205 | * is in Hz and the duration is in milliseconds. Each value is 16 bits. For a binary 206 | * file the values are stored high byte first. The ArduboyTones player supports 207 | * frequencies from 16 Hz to 32767 Hz but MIDI2TONES converts MIDI note numbers in the 208 | * range from note 12 (16.352 Hz rounded to 16 Hz) to note 127 (12543.9 Hz rounded 209 | * to 12544 Hz). 210 | * 211 | * Periods of silence are represented by a frequency/duration pair with a frequency 212 | * value of 0. 213 | * 214 | * Since the output is monotonic, only one MIDI channel is processed. The lowest bit 215 | * set in the -cn option's mask will indicate the channel to be used. If the -cn option 216 | * isn't given, channel 0 will be used. 217 | * 218 | * Tones can be specified to play at either normal or high volume. High volume is 219 | * indicated by setting the high bit of the frequency value (i.e. adding 0x8000 to the 220 | * desired frequency). A note will be set to high volume if the -vn option is used and 221 | * the MIDI velocity of the note is equal to or greater than the option value. 222 | * 223 | * For the C output format, frequencies will be output as note names, as defined in the 224 | * ArduboyTones library's ArduboyTonesPitches.h file. If the -f option is given, 225 | * the actual frequency, in decimal, will be used instead. Durations will be output 226 | * in decimal. 227 | * 228 | * Output files are terminated with a single 16 bit value of 0x8000 to indicate 229 | * end of score - stop playing. A file can instead be terminated with 0x8001 to indicate 230 | * end of score - start playing again from the beginning, which is specified using the 231 | * -r option. 232 | * 233 | * Len Shustek, 4 Feb 2011 and later. 234 | * Frequency/duration pair output format and other changes: 235 | * Scott Allen, 27 Sept 2016 and later. 236 | *********************************************************************************************/ 237 | -------------------------------------------------------------------------------- /SAMPLE_MoneyMoney_scroll.txt: -------------------------------------------------------------------------------- 1 | MIDITONES_SCROLL V1.5, (C) 2011,2016 Len Shustek 2 | Processing MoneyMoney.bin, 30291 bytes. 3 | Found Pt self-describing file header with flags E0 00, # tone gens = 16 4 | 5 | duration time gen0 gen1 gen2 gen3 gen4 gen5 bytestream code 6 | 7 | 2000 0.000 0000: 50 74 06 E0 00 10 07 D0 8 | 15 2.000 HHatC v65 0008: 90 AA 41 00 0F 9 | 484 2.015 000D: 80 01 E4 10 | 15 2.499 HHatC v65 0010: 90 AA 41 00 0F 11 | 484 2.514 0015: 80 01 E4 12 | 15 2.998 HHatC v65 0018: 90 AA 41 00 0F 13 | 484 3.013 001D: 80 01 E4 14 | 15 3.497 HHatC v65 0020: 90 AA 41 00 0F 15 | 484 3.512 0025: 80 01 E4 16 | BPiano BPiano BPiano BPiano Xyloph 17 | 62 3.996 4E v104 4A v108 5C v104 5E v108 6E v88 0028: C0 01 90 40 68 C1 01 91 45 6C C2 01 92 48 68 C3 01 93 4C 6C C4 0D 94 58 58 00 3E 18 | 62 4.058 6E v88 0043: 83 82 81 80 00 3E 19 | 125 4.120 0049: 84 00 7D 20 | 62 4.245 4E v96 4A v100 5C v84 5E v78 6E v72 004C: 90 40 60 91 45 64 92 48 54 93 4C 4E 94 58 48 00 3E 21 | 62 4.307 6E v72 005D: 83 82 81 80 00 3E 22 | 125 4.369 0063: 84 00 7D 23 | 62 4.494 4E v84 4A v88 5C v78 5E v92 6E v84 0066: 90 40 54 91 45 58 92 48 4E 93 4C 5C 94 58 54 00 3E 24 | 62 4.556 6E v84 0077: 83 82 81 80 00 3E 25 | 125 4.618 007D: 84 00 7D 26 | 62 4.743 4E v100 4A v100 5C v82 5E v88 6E v88 0080: 90 40 64 91 45 64 92 48 52 93 4C 58 94 58 58 00 3E 27 | 62 4.805 6E v88 0091: 83 82 81 80 00 3E 28 | 125 4.867 0097: 84 00 7D 29 | 62 4.992 4E v96 4A v100 5C v88 5E v104 6E v88 009A: 90 40 60 91 45 64 92 48 58 93 4C 68 94 58 58 00 3E 30 | 62 5.054 6E v88 00AB: 83 82 81 80 00 3E 31 | 125 5.116 00B1: 84 00 7D 32 | 125 5.241 4E v88 4A v92 5C v78 5E v104 6E v88 00B4: 90 40 58 91 45 5C 92 48 4E 93 4C 68 94 58 58 00 7D 33 | 98 5.366 4E v88 4A v92 5C v78 5E v104 00C5: 84 00 62 34 | 26 5.464 00C8: 83 82 81 80 00 1A 35 | 125 5.490 4E v108 5E v66 00CE: 90 40 6C 94 4C 42 00 7D 36 | 125 5.615 4A v108 5A v62 00D6: 80 90 45 6C 84 94 51 3E 00 7D 37 | 125 5.740 5C v96 6C v62 00E0: 80 90 48 60 84 94 54 3E 00 7D 38 | 125 5.865 5E v104 6E v80 00EA: 80 90 4C 68 84 94 58 50 00 7D 39 | 62 5.990 4F v104 4A v86 5C v84 5D# v65 6D# v68 00F4: 80 90 41 68 91 45 56 92 48 54 93 4B 41 84 94 57 44 00 3E 40 | 62 6.052 6D# v68 0107: 83 82 81 80 00 3E 41 | 125 6.114 010D: 84 00 7D 42 | 62 6.239 4F v59 4A v88 5C v80 5D# v82 6D# v88 0110: 90 41 3B 91 45 58 92 48 50 93 4B 52 94 57 58 00 3E 43 | 62 6.301 6D# v88 0121: 83 82 81 80 00 3E 44 | 125 6.363 0127: 84 00 7D 45 | 62 6.488 4F v96 4A v100 5C v84 5D# v82 6D# v88 012A: 90 41 60 91 45 64 92 48 54 93 4B 52 94 57 58 00 3E 46 | 62 6.550 6D# v88 013B: 83 82 81 80 00 3E 47 | 125 6.612 0141: 84 00 7D 48 | 62 6.737 4F v92 4A v100 5C v80 5D# v82 6D# v76 0144: 90 41 5C 91 45 64 92 48 50 93 4B 52 94 57 4C 00 3E 49 | 62 6.799 6D# v76 0155: 83 82 81 80 00 3E 50 | 125 6.861 015B: 84 00 7D 51 | 62 6.986 4F v100 4A v108 5C v84 5D# v80 6D# v64 015E: 90 41 64 91 45 6C 92 48 54 93 4B 50 94 57 40 00 3E 52 | 62 7.048 6D# v64 016F: 83 82 81 80 00 3E 53 | 125 7.110 0175: 84 00 7D 54 | 125 7.235 4F v78 4A v100 5C v78 5D# v74 6D# v80 0178: 90 41 4E 91 45 64 92 48 4E 93 4B 4A 94 57 50 00 7D 55 | 98 7.360 4F v78 4A v100 5C v78 5D# v74 0189: 84 00 62 56 | 26 7.458 018C: 83 82 81 80 00 1A 57 | 125 7.484 4F v104 5F v64 0192: 90 41 68 94 4D 40 00 7D 58 | 125 7.609 4A v82 5A v64 019A: 80 90 45 52 84 94 51 40 00 7D 59 | 125 7.734 5C v74 6C v62 01A4: 80 90 48 4A 84 94 54 3E 00 7D 60 | 125 7.859 5D# v92 6D# v76 01AE: 80 90 4B 5C 84 94 57 4C 00 7D 61 | 125 7.984 3D v86 4F v92 4A v92 5D v88 6D v88 01B8: 80 90 32 56 91 41 5C 92 45 5C 93 4A 58 84 94 56 58 00 7D 62 | 333 8.109 3D v86 4F v92 4A v92 5D v88 01CB: 84 01 4D 63 | 20 8.442 3D v86 4F v92 5D v88 01CE: 82 00 14 64 | 20 8.462 3D v86 4F v92 01D1: 83 00 14 65 | 114 8.482 3D v86 4F v92 5C v104 6C v84 01D4: 92 48 68 94 54 54 00 72 66 | 10 8.596 3D v86 5C v104 6C v84 01DC: 81 00 0A 67 | 88 8.606 3D v86 5C v104 01DF: 84 00 58 68 | 36 8.694 3D v86 01E2: 82 00 24 69 | 62 8.730 3D v86 4A v72 5A v62 01E5: 91 45 48 94 51 3E 00 3E 70 | 62 8.792 3D v86 5A v62 01ED: 81 00 3E 71 | 93 8.854 3D v86 01F0: 84 00 5D 72 | 31 8.947 01F3: 80 00 1F 73 | 78 8.978 3E v74 4D v82 4G# v104 5C v104 6C v84 01F6: 90 34 4A 91 3E 52 92 44 68 93 48 68 94 54 54 00 4E 74 | 15 9.056 3E v74 4D v82 6C v84 0207: 82 83 00 0F 75 | 31 9.071 3E v74 6C v84 020B: 81 00 1F 76 | 83 9.102 3E v74 020E: 84 00 53 77 | 41 9.185 0211: 80 00 29 78 | 125 9.226 3E v58 4D v86 4G# v112 5C v112 6C v84 0214: 90 34 3A 91 3E 56 92 44 70 93 48 70 94 54 54 00 7D 79 | 250 9.351 3E v58 4D v86 4G# v112 5C v112 0225: 84 00 FA 80 | 83 9.601 3E v58 0228: 83 82 81 00 53 81 | 41 9.684 022D: 80 00 29 82 | 125 9.725 4C v112 4E v96 4A v100 5A v80 0230: 90 3C 70 91 40 60 92 45 64 94 51 50 00 7D 83 | 229 9.850 4C v112 4E v96 4A v100 023E: 84 00 E5 84 | 26 10.079 4C v112 4E v96 0241: 82 00 1A 85 | 31 10.105 4C v112 0244: 81 00 1F 86 | 338 10.136 0247: 80 01 52 87 | 62 10.474 2A v96 024A: 90 2D 60 00 3E 88 | 62 10.536 2A v96 3E v100 024F: 91 34 64 00 3E 89 | 31 10.598 2A v96 3E v100 3A v82 0254: 92 39 52 00 1F 90 | 10 10.629 3E v100 3A v82 0259: 80 00 0A 91 | 20 10.639 3A v82 025C: 81 00 14 92 | 15 10.659 3C v88 3A v82 025F: 90 30 58 00 0F 93 | 46 10.674 3C v88 0264: 82 00 2E 94 | 20 10.720 3C v88 3E v86 0267: 91 34 56 00 14 95 | 41 10.740 3E v86 026C: 80 00 29 96 | 15 10.781 3A v76 3E v86 026F: 90 39 4C 00 0F 97 | 31 10.796 3A v76 0274: 81 00 1F 98 | 15 10.827 0277: 80 00 0F 99 | 62 10.842 4C v100 027A: 90 3C 64 00 3E 100 | 62 10.904 4C v100 4E v88 027F: 91 40 58 00 3E 101 | 10 10.966 4C v100 4E v88 4A v112 6A v92 0284: 92 45 70 94 5D 5C 00 0A 102 | 20 10.976 4E v88 4A v112 6A v92 028C: 80 00 14 103 | 31 10.996 4A v112 6A v92 028F: 81 00 1F 104 | 62 11.027 6A v92 0292: 82 00 3E 105 | 875 11.089 0295: 84 03 6B 106 | FiBass Callip 107 | 15 11.964 1A v56 3A v74 5A v55 BassD v61 0298: C0 21 90 21 38 91 39 4A 95 A3 3D C2 52 92 51 37 00 0F 108 | 109 11.979 1A v56 3A v74 5A v55 02AA: 85 00 6D 109 | 125 12.088 1A v56 3B v72 5B v53 02AD: 81 91 3B 48 82 92 53 35 00 7D 110 | 93 12.213 2A v58 4C v66 6C v52 02B7: 80 90 2D 3A 81 91 3C 42 82 92 54 34 00 5D 111 | 31 12.306 4C v66 6C v52 02C5: 80 00 1F 112 | 125 12.337 4E v96 6E v51 02C8: 81 91 40 60 82 92 58 33 00 7D 113 | 15 12.462 1A v56 3A v84 5A v50 BassD v61 02D2: 90 21 38 81 91 39 54 95 A3 3D 82 92 51 32 00 0F 114 | 109 12.477 1A v56 3A v84 5A v50 02E2: 85 00 6D 115 | 125 12.586 1A v56 3B v88 5B v48 02E5: 81 91 3B 58 82 92 53 30 00 7D 116 | 83 12.711 2A v54 4C v82 6C v47 02EF: 80 90 2D 36 81 91 3C 52 82 92 54 2F 00 53 117 | 41 12.794 4C v82 6C v47 02FD: 80 00 29 118 | 125 12.835 4E v112 6E v46 0300: 81 91 40 70 82 92 58 2E 00 7D 119 | 15 12.960 1A v60 3A v108 5A v44 BassD v62 030A: 90 21 3C 81 91 39 6C 95 A3 3E 82 92 51 2C 00 0F 120 | 109 12.975 1A v60 3A v108 5A v44 031A: 85 00 6D 121 | 125 13.084 1A v60 3B v104 5B v43 031D: 81 91 3B 68 82 92 53 2B 00 7D 122 | 119 13.209 2A v54 4C v100 6C v42 0327: 80 90 2D 36 81 91 3C 64 82 92 54 2A 00 77 123 | 5 13.328 4C v100 6C v42 0335: 80 00 05 124 | 125 13.333 4E v108 6E v40 0338: 81 91 40 6C 82 92 58 28 00 7D 125 | 15 13.458 1A v62 3A v100 5A v39 BassD v63 0342: 90 21 3E 81 91 39 64 95 A3 3F 82 92 51 27 00 0F 126 | 109 13.473 1A v62 3A v100 5A v39 0352: 85 00 6D 127 | 125 13.582 1A v62 3B v112 5B v38 0355: 81 91 3B 70 82 92 53 26 00 7D 128 | 125 13.707 2A v58 4C v104 6C v37 035F: 80 90 2D 3A 81 91 3C 68 82 92 54 25 00 7D 129 | 10 13.832 2A v58 4E v117 6E v35 036D: 81 91 40 75 82 92 58 23 00 0A 130 | 114 13.842 4E v117 6E v35 0377: 80 00 72 131 | 15 13.956 1A v60 3A v100 5A v34 BassD v63 037A: 90 21 3C 81 91 39 64 95 A3 3F 82 92 51 22 00 0F 132 | 109 13.971 1A v60 3A v100 5A v34 038A: 85 00 6D 133 | 125 14.080 1A v60 3B v100 5B v33 038D: 81 91 3B 64 82 92 53 21 00 7D 134 | 72 14.205 2A v54 4C v86 6C v31 0397: 80 90 2D 36 81 91 3C 56 82 92 54 1F 00 48 135 | 52 14.277 4C v86 6C v31 03A5: 80 00 34 136 | 125 14.329 4E v92 6E v30 03A8: 81 91 40 5C 82 92 58 1E 00 7D 137 | 15 14.454 1A v60 3A v84 5A v29 BassD v64 03B2: 90 21 3C 81 91 39 54 95 A3 40 82 92 51 1D 00 0F 138 | 109 14.469 1A v60 3A v84 5A v29 03C2: 85 00 6D 139 | 125 14.578 1A v60 3B v74 5B v27 03C5: 81 91 3B 4A 82 92 53 1B 00 7D 140 | 57 14.703 2A v50 4C v70 6C v26 03CF: 80 90 2D 32 81 91 3C 46 82 92 54 1A 00 39 141 | 67 14.760 4C v70 6C v26 03DD: 80 00 43 142 | 125 14.827 4E v86 6E v25 03E0: 81 91 40 56 82 92 58 19 00 7D 143 | 15 14.952 1A v62 3A v74 5A v24 BassD v65 03EA: 90 21 3E 81 91 39 4A 95 A3 41 82 92 51 18 00 0F 144 | 109 14.967 1A v62 3A v74 5A v24 03FA: 85 00 6D 145 | 125 15.076 1A v62 3B v74 5B v22 03FD: 81 91 3B 4A 82 92 53 16 00 7D 146 | 88 15.201 2A v54 4C v60 6C v21 0407: 80 90 2D 36 81 91 3C 3C 82 92 54 15 00 58 147 | 36 15.289 4C v60 6C v21 0415: 80 00 24 148 | 125 15.325 4E v72 6E v20 0418: 81 91 40 48 82 92 58 14 00 7D 149 | 15 15.450 1A v58 3A v56 5A v18 BassD v65 0422: 90 21 3A 81 91 39 38 95 A3 41 82 92 51 12 00 0F 150 | 109 15.465 1A v58 3A v56 5A v18 0432: 85 00 6D 151 | 125 15.574 1A v58 3B v64 5B v17 0435: 81 91 3B 40 82 92 53 11 00 7D 152 | <...> 153 | <...> 7652: F0 154 | 155 | At most 16 tone generators were used. 156 | 2349 notes were not displayed because we were told to show only 6 generators. 157 | Done. 158 | -------------------------------------------------------------------------------- /WINDOWS_EXE.txt: -------------------------------------------------------------------------------- 1 | Windows executable files were created using Mingw-w64 2 | https://mingw-w64.org/ 3 | 4 | The following commands were used to create 64 bit and 32 bit executables: 5 | 6 | x86_64-w64-mingw32-gcc -O2 -Wall -o midi2tones_64bit.exe midi2tones.c 7 | 8 | i686-w64-mingw32-gcc -O2 -Wall -o midi2tones_32bit.exe midi2tones.c 9 | 10 | x86_64-w64-mingw32-gcc -O2 -Wall -o miditones_scroll_64bit.exe miditones_scroll.c 11 | 12 | i686-w64-mingw32-gcc -O2 -Wall -o miditones_scroll_32bit.exe miditones_scroll.c 13 | 14 | -------------------------------------------------------------------------------- /midi2tones.c: -------------------------------------------------------------------------------- 1 | /********************************************************************************************* 2 | * 3 | * MIDI2TONES: Convert a MIDI file into a simple bytestream of notes 4 | * 5 | * This is a fork of MIDITONES as it stood on September 27, 2016 6 | * Copyright (c) 2011,2013,2015,2016, Len Shustek 7 | * https://github.com/LenShustek/miditones 8 | * 9 | * The purpose of the fork was to add an alternate output format. 10 | * 11 | * MIDI2TONES converts a MIDI music file into a much simplified stream of commands, so that 12 | * the music can easily be played on a small microcontroller-based synthesizer that has 13 | * only simple tone generators. This is on GitHub at 14 | * https://github.com/MLXXXp/midi2tones 15 | * 16 | * This was written for the "Playtune" series of Arduino and Teensy microcontroller 17 | * synthesizers. See the separate documentation for the various Playtune players at 18 | * https://github.com/LenShustek/arduino-playtune 19 | * https://github.com/LenShustek/ATtiny-playtune 20 | * https://github.com/LenShustek/Playtune_poll 21 | * https://github.com/LenShustek/Playtune_samp 22 | * and also the ArduboyPlaytune library derived from arduino-playtune 23 | * https://github.com/Arduboy/ArduboyPlaytune 24 | * 25 | * MIDI2TONES may also prove useful for other simple music synthesizers. 26 | * 27 | * Volume ("velocity") and instrument information in the MIDI file can either be 28 | * discarded or kept. All the tracks are processed and merged into a single time-ordered 29 | * stream of "note on", "note off", "change instrument" and "delay" commands. 30 | * 31 | * An alternate output format can be specified, which consists of a single monotonic 32 | * stream of frequency/duration pairs of 16 bit values. The specified frequency can 33 | * also include a flag to indicate that the note is to be played at a higher volume, 34 | * if the velocity of the MIDI note is above a certain value. 35 | * This format is suitable for use with the ArduboyTones library, which is on GitHub at 36 | * https://github.com/MLXXXp/ArduboyTones 37 | * 38 | * The output can be either a C-language source code fragment that initializes an 39 | * array with the command bytestream, or a binary file with the bytestream itself. 40 | * 41 | * MIDI2TONES is written in standard ANSI C and is meant to be executed from the 42 | * command line. There is no GUI interface. 43 | * 44 | * The MIDI file format is complicated, and this has not been tested on all of its 45 | * variations. In particular we have tested only format type "1", which seems 46 | * to be what most of them are. Let me know if you find MIDI files that it 47 | * won't digest and I'll see if I can fix it. 48 | * 49 | * There is a companion program in the same repository called Miditones_scroll that 50 | * can convert the Playtune bytestream generated by MIDI2TONES into a piano-player 51 | * like listing for debugging or annotation. See the documentation in the 52 | * beginning of its source code. 53 | * 54 | * 55 | * ***** The MIDI2TONES command line ***** 56 | * 57 | * To convert a MIDI file called "chopin.mid" into a command bytestream, execute 58 | * 59 | * midi2tones chopin 60 | * 61 | * It will create a file in the same directory called "chopin.c" which contains 62 | * the C-language statement to intiialize an array called "score" with the bytestream. 63 | * 64 | * 65 | * The general form for command line execution is this: 66 | * 67 | * midi2tones 68 | * 69 | * Options must be specified individually, each with its own "-" lead-in, and separated 70 | * with spaces. A forward slash "/" can be used instead of a dash "-" for option lead-ins. 71 | * 72 | * The is the base name, without an extension, for the input and 73 | * output files. It can contain directory path information, or not. 74 | * 75 | * The input file is .mid The output filename(s) 76 | * are the base file name with .c, .bin, and/or .log extensions. 77 | * 78 | * 79 | * The following commonly-used command-line options can be specified: 80 | * 81 | * -on Generate output format "n". 82 | * Two formats are available: 83 | * 1: The Playtune format (which is the default if this option isn't given). 84 | * 2: The frequency/duration pair format, as used by ArduboyTones. 85 | * 86 | * -v Add velocity (volume) information to the output bytestream. 87 | * 88 | * -vn For the alternate format, "n" specifies the minimum velocity value that will 89 | * produce a high volume tone. Without this option all tones will be 90 | * normal volume. 91 | * 92 | * -i Add instrument change commands to the output bytestream. 93 | * 94 | * -pt Translate notes in the MIDI percussion track to note numbers 128..255 95 | * and assign them to a tone generator as usual. 96 | * 97 | * -d Generate a self-describing file header that says which optional bytestream 98 | * fields are present. This is highly recommended if you are using later 99 | * Playtune players that can check the header to know what data to expect. 100 | * 101 | * -b Generate a binary file with the name .bin, instead of a 102 | * C-language source file with the name .c. 103 | * 104 | * -tn Generate the bytestream so that at most "n" tone generators are used. 105 | * The default is 6 tone generators, and the maximum is 16. The program 106 | * will report how many notes had to be discarded because there weren't 107 | * enough tone generators. 108 | * 109 | * 110 | * The following are lesser-used command-line options: 111 | * 112 | * -p Only parse the MIDI file, and don't generate an output file. 113 | * Tracks are processed sequentially instead of being merged into chronological order. 114 | * This is mostly useful for debugging MIDI file parsing problems. 115 | * 116 | * -lp Log input file parsing information to the .log file. 117 | * 118 | * -lg Log output bytestream generation information to the .log file. 119 | * 120 | * -nx Put about "x" items on each line of the C file output. 121 | * 122 | * -sn Use bytestream generation strategy "n". 123 | * Two strategies are currently implemented: 124 | * 1: Favor track 1 notes instead of all tracks equally. 125 | * 2: Try to keep each track to its own tone generator. 126 | * 127 | * -cn Only process the channel numbers whose bits are on in the number "n". 128 | * For example, -c3 means "only process channels 0 and 1". In addition to decimal, 129 | * "n" can be also specified in hex using a 0x prefix or octal with a 0 prefix. 130 | * For the alternate output format, only the lowest bit will be used to specify 131 | * the single channel to be processed, and without this option channel 0 will 132 | * be used. 133 | * 134 | * -kn Change the musical key of the output by n chromatic notes. 135 | * -k-12 goes one octave down, -k12 goes one octave up, etc. 136 | * 137 | * -pi Ignore notes in the MIDI percussion track 9 (also called 10 by some). 138 | * 139 | * -dp Generate IDE-dependent C code to define PROGMEM. 140 | * 141 | * -fx For the alternate output format, instead of using defined note names, 142 | * output actual frequency values in decimal format depending on "x": 143 | * -fa: For high volume notes use format "+TONE_HIGH_VOLUME". 144 | * -fb: For high volume notes just add 0x8000 to the frequency value. 145 | * 146 | * -r Terminate the output file with a "restart" command instead of a "stop" command. 147 | * 148 | * -h Give command-line help. 149 | * 150 | * 151 | * ***** The score bytestream ***** 152 | * 153 | * The generated bytestream is a series of commands that turn notes on and off, 154 | * maybe change instruments, and begin delays until the next note change. 155 | * Here are the details, with numbers shown in hexadecimal. 156 | * 157 | * If the high-order bit of the byte is 1, then it is one of the following commands: 158 | * 159 | * 9t nn [vv] 160 | * Start playing note nn on tone generator t, replacing any previous note. 161 | * Generators are numbered starting with 0. The note numbers are the MIDI 162 | * numbers for the chromatic scale, with decimal 69 being Middle A (440 Hz). 163 | * If the -v option was given, a second byte is added to indicate note volume. 164 | * 165 | * 8t Stop playing the note on tone generator t. 166 | * 167 | * Ct ii Change tone generator t to play instrument ii from now on. This will only 168 | * be generated if the -i option was given. 169 | * 170 | * F0 End of score; stop playing. 171 | * 172 | * E0 End of score; start playing again from the beginning. Will be generated if 173 | * the -r option was given. 174 | * 175 | * If the high-order bit of the byte is 0, it is a command to delay for a while until 176 | * the next note change. The other 7 bits and the 8 bits of the following byte are 177 | * interpreted as a 15-bit big-endian integer that is the number of milliseconds to 178 | * wait before processing the next command. For example, 179 | * 180 | * 07 D0 181 | * 182 | * would cause a delay of 0x07d0 = 2000 decimal millisconds, or 2 seconds. Any tones 183 | * that were playing before the delay command will continue to play. 184 | * 185 | * If the -d option is specified, the bytestream begins with a little header that tells 186 | * what optional information will be in the data. This makes the file more self-describing, 187 | * and allows music players to adapt to different kinds of files. The later Playtune 188 | * players do that. The header looks like this: 189 | * 190 | * 'Pt' Two ascii characters that signal the presence of the header 191 | * nn The length (in one byte) of the entire header, 6..255 192 | * ff1 A byte of flag bits, three of which are currently defined: 193 | * 80 velocity information is present 194 | * 40 instrument change information is present 195 | * 20 translated percussion notes are present 196 | * ff2 Another byte of flags, currently undefined 197 | * tt The number (in one byte) of tone generators actually used in this music. 198 | * 199 | * Any subsequent header bytes covered by the count, if present, are currently undefined 200 | * and should be ignored by players. 201 | * 202 | * ***** The alternate frequency/duration pair output format ***** 203 | * 204 | * The generated stream is a series of frequency/duration value pairs. The frequency 205 | * is in Hz and the duration is in milliseconds. Each value is 16 bits. For a binary 206 | * file the values are stored high byte first. The ArduboyTones player supports 207 | * frequencies from 16 Hz to 32767 Hz but MIDI2TONES converts MIDI note numbers in the 208 | * range from note 12 (16.352 Hz rounded to 16 Hz) to note 127 (12543.9 Hz rounded 209 | * to 12544 Hz). 210 | * 211 | * Periods of silence are represented by a frequency/duration pair with a frequency 212 | * value of 0. 213 | * 214 | * Since the output is monotonic, only one MIDI channel is processed. The lowest bit 215 | * set in the -cn option's mask will indicate the channel to be used. If the -cn option 216 | * isn't given, channel 0 will be used. 217 | * 218 | * Tones can be specified to play at either normal or high volume. High volume is 219 | * indicated by setting the high bit of the frequency value (i.e. adding 0x8000 to the 220 | * desired frequency). A note will be set to high volume if the -vn option is used and 221 | * the MIDI velocity of the note is equal to or greater than the option value. 222 | * 223 | * For the C output format, frequencies will be output as note names, as defined in the 224 | * ArduboyTones library's ArduboyTonesPitches.h file. If the -f option is given, 225 | * the actual frequency, in decimal, will be used instead. Durations will be output 226 | * in decimal. 227 | * 228 | * Output files are terminated with a single 16 bit value of 0x8000 to indicate 229 | * end of score - stop playing. A file can instead be terminated with 0x8001 to indicate 230 | * end of score - start playing again from the beginning, which is specified using the 231 | * -r option. 232 | * 233 | * Len Shustek, 4 Feb 2011 and later. 234 | * Frequency/duration pair output format and other changes: 235 | * Scott Allen, 27 Sept 2016 and later. 236 | *********************************************************************************************/ 237 | 238 | /********************************************************************************************* 239 | * The MIT License (MIT) 240 | * Original work Copyright (c) 2011,2013,2015,2016, Len Shustek 241 | * Modified work Copyright (c) 2016, Scott Allen 242 | * 243 | * Permission is hereby granted, free of charge, to any person obtaining a copy 244 | * of this software and associated documentation files (the "Software"), to deal 245 | * in the Software without restriction, including without limitation the rights 246 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 247 | * copies of the Software, and to permit persons to whom the Software is 248 | * furnished to do so, subject to the following conditions: 249 | * 250 | * The above copyright notice and this permission notice shall be included in all 251 | * copies or substantial portions of the Software. 252 | * 253 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 254 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 255 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 256 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 257 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR 258 | * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 259 | *********************************************************************************************/ 260 | 261 | /* 262 | * Change log 263 | * 19 January 2011, L.Shustek, V1.0 264 | * -Initial release. 265 | * 26 February 2011, L. Shustek, V1.1 266 | * -Expand the documentation generated in the output file. 267 | * -End the binary output file with an "end of score" command. 268 | * -Fix bug: Some "stop note" commands were generated too early. 269 | * 04 March 2011, L. Shustek, V1.2 270 | * -Minor error message rewording. 271 | * 13 June 2011, L. Shustek, V1.3 272 | * -Add -s2 strategy to try to keep each track on its own tone generator 273 | * for when there are separate speakers. This obviously works only when 274 | * each track is monophonic. (Suggested by Michal Pustejovsky) 275 | * 20 November 2011, L. Shustek, V1.4 276 | * -Add -cn option to mask which channels (tracks) to process 277 | * -Add -kn option to change key 278 | * Both of these are in support of music-playing on my Tesla Coil. 279 | * 05 December 2011, L. Shustek, V1.5 280 | * -Fix command line parsing error for option -s1 281 | * -Display the commandline in the C file output 282 | * -Change to decimal instead of hex for note numbers in the C file output 283 | * 06 August 2013, L. Shustek, V1.6 284 | * -Changed to allow compilation and execution in 64-bit environments 285 | * by using C99 standard intN_t and uintN_t types for MIDI structures, 286 | * and formatting specifications like "PRId32" instead of "ld". 287 | * 04 April 2015, L. Shustek, V1.7 288 | * -Made friendlier to other compilers: import source of strlcpy and strlcat, 289 | * fixed various type mismatches that the LCC compiler didn't fret about. 290 | * Generate "const" for data initialization for compatibility with Arduino IDE v1.6.x. 291 | * 23 January 2016, D. Blackketter, V1.8 292 | * -Fix warnings and errors building on Mac OS X via "gcc miditones.c" 293 | * 25 January 2016, D. Blackketter, Paul Stoffregen, V1.9 294 | * -Merge in velocity output option from Arduino/Teensy Audio Library 295 | * 26 June 2016, L. Shustek, V1.10 296 | * -Fix overflow problem in calculating long delays. (Thanks go to Tiago Rocha.) 297 | * In the process I discover and work around an LCC 32-bit compiler bug. 298 | * 14 August 2016: L. Shustek, V1.11 299 | * -Fix our interpretation of MIDI "running status": it applies only to MIDI events 300 | * (8x through Ex), not, as we thought, also to Sysex (Fx) or Meta (FF) events. 301 | * -Improve parsing of text events for the log. 302 | * -Change log file note and patch numbers, etc., to decimal. 303 | * -Document a summary of the MIDI file format so I don't have to keep looking it up. 304 | * -Add -pi and -pt options to ignore or translate the MIDI percussion track 9. 305 | * -Remove string.h for more portability; add strlength(). 306 | * -Add -i option for recording instrument types in the bytestream. 307 | * -Add -d option for generating a file description header. 308 | * -Add -dp option to make generating the PROGMEM definition optional 309 | * -Add -n option to specify number of items per output line 310 | * -Do better error checking on options 311 | * -Reformat option help 312 | * 26 September 2016, Scott Allen, V1.12 313 | * - Fix spelling and minor formatting errors 314 | * - Fix -p option parsing and handling, which broke when -pi and -pt were added 315 | * - Fix handling of the -nx option to count more accurately 316 | * - Give a proper error message for missing base name 317 | * - Include the header and terminator in the score byte count 318 | * 319 | * **** MIDITONES forked and renamed MIDI2TONES **** 320 | * 321 | * 27 September 2016, Scott Allen, V1.0.0 322 | * -Add alternate frequency/duration pair output format and options to support it 323 | * -Add -r option to terminate output with "restart" instead of "stop" 324 | * -Allow hex and octal entry, in addition to decimal, for -cn option 325 | * -Prevent unnecessary "note off" commands from being generated by delaying 326 | * them until we see if a "note on" is generated before the next wait. 327 | * Ported from MIDITONES V1.14 328 | */ 329 | #define VERSION "1.0.0" 330 | 331 | /*-------------------------------------------------------------------------------------------- 332 | 333 | A CONCISE SUMMARY OF MIDI FILE FORMAT 334 | 335 | L. Shustek, 16 July 2016. 336 | Gleaned from http://www.music.mcgill.ca/~ich/classes/mumt306/StandardMIDIfileformat.html 337 | 338 | Notation: 339 | is 1-4 bytes of 7-bit data, concatenated into one 7- to 28-bit number. The high bit of the last byte is 0. 340 | lower case letters are hex digits. If preceeded by 0, only low 7 bits are used. 341 | "xx" are ascii text characters 342 | {xxx}... means indefinite repeat of xxx 343 | 344 | A MIDI file is: header_chunk {track_chunk}... 345 | 346 | header_chunk 347 | "MThd" 00000006 ffff nnnn dddd 348 | 349 | track_chunk 350 | 351 | "MTrk" llllllll { track_event}... 352 | 353 | "running status" track_event 354 | 355 | 0x to 7x: assume a missing 8n to En event code which is the same as the last MIDI-event track_event 356 | 357 | MIDI-event track_event 358 | 359 | 8n 0kk 0vv note off, channel n, note kk, velocity vv 360 | 9n 0kk 0vv note on, channel n, note kk, velocity vv 361 | An 0kk 0vv key pressure, channel n, note kk, pressure vv 362 | Bn 0cc 0vv control value change, channel n, controller cc, new value vv 363 | Cn 0pp program patch (instrument) change, channel n, new program pp 364 | Dn 0vv channel pressure, channel n, pressure vv 365 | En 0ll 0mm pitch wheel change, value llmm 366 | 367 | Note that channel 9 (called 10 by some programs) is used for percussion, particularly notes 35 to 81. 368 | 369 | Sysex event track_event 370 | 371 | F0 0ii {0dd}... F7 system-dependent data for manufacture ii. See www.gweep.net/~prefect/eng/reference/protocol/midispec.html 372 | F2 0ll 0mm song position pointer 373 | F3 0ss song select 374 | F6 tune request 375 | F7 end of system-dependent data 376 | F8 timing clock sync 377 | FA start playing 378 | FB continue playing 379 | FC stop playing 380 | FE active sensing (hearbeat) 381 | 382 | Meta event track_event 383 | 384 | FF 00 02 ssss specify sequence number 385 | FF 01 "xx"... arbitrary text 386 | FF 02 "xx"... copyright notice 387 | FF 03 "xx"... sequence or track name 388 | FF 04 "xx"... instrument name 389 | FF 05 "xx"... lyric to be sung 390 | FF 06 "xx"... name of marked point in the score 391 | FF 07 "xx"... description of cue point in the score 392 | FF 20 01 0c default channel for subsequent events without a channel is c 393 | FF 2F 00 end of track 394 | FF 51 03 tttttt set tempo in microseconds per quarter-note 395 | FF 54 05 hhmmssfrff set SMPTE time to start the track 396 | FF 58 04 nnddccbb set time signature 397 | FF 59 02 sfmi set key signature 398 | FF 7F data sequencer-specific data 399 | 400 | --------------------------------------------------------------------------------------------*/ 401 | 402 | #include 403 | #include 404 | #include 405 | #include 406 | #include 407 | #include 408 | 409 | 410 | /*********** MIDI file header formats *****************/ 411 | 412 | struct midi_header { 413 | int8_t MThd[4]; 414 | uint32_t header_size; 415 | uint16_t format_type; 416 | uint16_t number_of_tracks; 417 | uint16_t time_division; 418 | }; 419 | 420 | struct track_header { 421 | int8_t MTrk[4]; 422 | uint32_t track_size; 423 | }; 424 | 425 | 426 | /*********** Global variables ******************/ 427 | 428 | #define MAX_TONEGENS 16 /* max tone generators: tones we can play simultaneously */ 429 | #define DEFAULT_TONEGENS 6 /* default number of tone generators */ 430 | #define MAX_TRACKS 24 /* max number of MIDI tracks we will process */ 431 | #define PERCUSSION_TRACK 9 /* the track MIDI uses for percussion sounds */ 432 | 433 | /* for alternate output format: */ 434 | #define TONE_HIGH_VOLUME 0x8000 /* add to frequency for high volume */ 435 | #define TONES_END 0x8000 /* end marker for stop playing */ 436 | #define TONES_REPEAT 0x8001 /* end marker for restart playing */ 437 | 438 | bool loggen, logparse, parseonly, strategy1, strategy2, binaryoutput, define_progmem, 439 | velocityoutput, instrumentoutput, percussion_ignore, percussion_translate, do_header, 440 | alt_out, gen_restart, freq_style_a, freq_style_b, option_n; 441 | FILE *infile, *outfile, *logfile; 442 | uint8_t *buffer, *hdrptr; 443 | unsigned long buflen; 444 | int num_tracks; 445 | int tracks_done = 0; 446 | int outfile_maxitems = 26; 447 | int outfile_itemcount = 0; 448 | int num_tonegens = DEFAULT_TONEGENS; 449 | int num_tonegens_used = 0; 450 | int instrument_changes = 0; 451 | int note_on_commands = 0; 452 | unsigned channel_mask = 0xffff; // bit mask of channels to process 453 | int keyshift = 0; // optional chromatic note shift for output file 454 | long int outfile_bytecount = 0; 455 | unsigned int ticks_per_beat = 240; 456 | unsigned long timenow = 0; 457 | unsigned long tempo; /* current tempo in usec/qnote */ 458 | int velocity_threshold = 128; /* alt out format: minimum velocity for high volume */ 459 | int pending_note; /* alt out format: note number awaiting output */ 460 | int pending_velocity; /* alt out format: velocity of note awaiting output */ 461 | int alt_out_channel; /* alt out format: MIDI channel used */ 462 | 463 | struct tonegen_status { /* current status of a tone generator */ 464 | bool playing; /* is it playing? */ 465 | bool stopnote_pending; /* do we need to stop this generator before the next wait? */ 466 | int track; /* if so, which track is the note from? */ 467 | int note; /* what note is playing? */ 468 | int instrument; /* what instrument? */ 469 | } tonegen[MAX_TONEGENS] = { 470 | { 471 | 0} 472 | }; 473 | 474 | struct track_status { /* current processing point of a MIDI track */ 475 | uint8_t *trkptr; /* ptr to the next note change */ 476 | uint8_t *trkend; /* ptr past the end of the track */ 477 | unsigned long time; /* what time we're at in the score */ 478 | unsigned long tempo; /* the tempo last set, in usec per qnote */ 479 | unsigned int preferred_tonegen; /* for strategy2, try to use this generator */ 480 | unsigned char cmd; /* CMD_xxxx next to do */ 481 | unsigned char note; /* for which note */ 482 | unsigned char chan; /* from which channel it was */ 483 | unsigned char velocity; /* the current volume */ 484 | unsigned char last_event; /* the last event, for MIDI's "running status" */ 485 | bool tonegens[MAX_TONEGENS]; /* which tone generators our notes are playing on */ 486 | } track[MAX_TRACKS] = { 487 | { 488 | 0} 489 | }; 490 | 491 | int midi_chan_instrument[16] = { 492 | 0 493 | }; /* which instrument is currently being played on each channel */ 494 | 495 | /* output bytestream commands, which are also stored in track_status.cmd */ 496 | 497 | #define CMD_PLAYNOTE 0x90 /* play a note: low nibble is generator #, note is next byte */ 498 | #define CMD_STOPNOTE 0x80 /* stop a note: low nibble is generator # */ 499 | #define CMD_INSTRUMENT 0xc0 /* change instrument; low nibble is generator #, instrument is next byte */ 500 | #define CMD_RESTART 0xe0 /* restart the score from the beginning */ 501 | #define CMD_STOP 0xf0 /* stop playing */ 502 | /* if CMD < 0x80, then the other 7 bits and the next byte are a 15-bit number of msec to delay */ 503 | 504 | /* these other commands stored in the track_status.com */ 505 | #define CMD_TEMPO 0xFE /* tempo in usec per quarter note ("beat") */ 506 | #define CMD_TRACKDONE 0xFF /* no more data left in this track */ 507 | 508 | 509 | struct file_hdr_t { /* what the optional file header looks like */ 510 | char id1; // 'P' 511 | char id2; // 't' 512 | unsigned char hdr_length; // length of whole file header 513 | unsigned char f1; // flag byte 1 514 | unsigned char f2; // flag byte 2 515 | unsigned char num_tgens; // how many tone generators are used by this score 516 | } file_header = { 517 | 'P', 't', sizeof (struct file_hdr_t), 0, 0, MAX_TONEGENS}; 518 | 519 | #define HDR_F1_VOLUME_PRESENT 0x80 520 | #define HDR_F1_INSTRUMENTS_PRESENT 0x40 521 | #define HDR_F1_PERCUSSION_PRESENT 0x20 522 | 523 | 524 | long int file_header_num_tgens_position; 525 | 526 | const char note_name[][3] = { 527 | "C", "CS", "D", "DS", "E", "F", "FS", "G", "GS", "A", "AS", "B" }; 528 | 529 | /* MIDI note frequencies (notes below number 12 not supported) */ 530 | const uint16_t note_freq[] = { 531 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 11 */ 532 | 16, 17, 18, 19, 21, 22, 23, 25, 26, 28, 29, 31, /* 12 - 23 */ 533 | 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, /* 24 - 35 */ 534 | 65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 117, 123, /* 36 - 47 */ 535 | 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, /* 48 - 59 */ 536 | 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, /* 60 - 71 */ 537 | 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, /* 72 - 83 */ 538 | 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, /* 84 - 95 */ 539 | 2093, 2218, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, /* 96 - 107 */ 540 | 4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902, /* 108 - 119 */ 541 | 8372, 8870, 9397, 9956,10548,11175,11840,12544 /* 120 - 127 */ 542 | }; 543 | 544 | /************** command-line processing *******************/ 545 | 546 | void SayUsage (char *programName) { 547 | static char *usage[] = { 548 | "Convert MIDI files to an Arduino PLAYTUNE bytestream,", 549 | "or a frequency/duration pairs stream suitable for ArduboyTones", 550 | "", 551 | "Use: midi2tones ", 552 | " input file will be .mid", 553 | " output file will be .bin or .c", 554 | " log file will be .log", 555 | "", 556 | "Commonly-used options:", 557 | " -o1 generate Playtune output format (the default)", 558 | " -o2 generate the frequency/duration pair output format", 559 | " -v include velocity data", 560 | " -vn for alternate format: n is the minimum velocity for high volume notes", 561 | " -i include instrument change commands", 562 | " -pt translate notes in the percussion track to notes 129 to 255", 563 | " -d include a self-describing file header", 564 | " -b generate binary file output instead of C source text", 565 | " -tn use at most n tone generators (default is 6, max is 16)", 566 | "", 567 | " The best options for later Playtune music players are: -v -i -pt -d", 568 | "", 569 | "Lesser-used command-line options:", 570 | " -p parse only, don't generate bytestream", 571 | " -lp log input parsing", 572 | " -lg log output generation", 573 | " -nx put about x items on each line of the C file output", 574 | " -s1 strategy 1: favor track 1", 575 | " -s2 strategy 2: try to assign tracks to specific tone generators", 576 | " -cn mask for which tracks to process, e.g. -c3 for only 0 and 1", 577 | " -kn key shift in chromatic notes, positive or negative", 578 | " -pi ignore notes in the percussion track (9)", 579 | " -fa for alternate format: high volume notes as \"+TONE_HIGH_VOLUME\"", 580 | " -fb for alternate format: high volume notes as a single decimal value", 581 | " -r terminate output file with \"restart\" instead of \"stop\" command", 582 | " -dp define PROGMEM in output C code", 583 | NULL 584 | }; 585 | int i = 0; 586 | while (usage[i] != NULL) 587 | fprintf (stderr, "%s\n", usage[i++]); 588 | } 589 | 590 | 591 | int HandleOptions (int argc, char *argv[]) { 592 | /* returns the index of the first argument that is not an option; i.e. 593 | does not start with a dash or a slash*/ 594 | 595 | int i, nch, firstnonoption = 0; 596 | 597 | /* --- The following skeleton comes from C:\lcc\lib\wizard\textmode.tpl. */ 598 | for (i = 1; i < argc; i++) { 599 | if (argv[i][0] == '/' || argv[i][0] == '-') { 600 | switch (toupper (argv[i][1])) { 601 | case 'H': 602 | case '?': 603 | SayUsage (argv[0]); 604 | exit (1); 605 | case 'O': 606 | if (argv[i][2] == '1') 607 | alt_out = false; 608 | else if (argv[i][2] == '2') { 609 | alt_out = true; 610 | printf ("Generating frequency/duration pair output format.\n"); 611 | } 612 | else 613 | goto opterror; 614 | if (argv[i][3] != '\0') 615 | goto opterror; 616 | break; 617 | case 'L': 618 | if (toupper (argv[i][2]) == 'G') 619 | loggen = true; 620 | else if (toupper (argv[i][2]) == 'P') 621 | logparse = true; 622 | else 623 | goto opterror; 624 | if (argv[i][3] != '\0') 625 | goto opterror; 626 | break; 627 | case 'P': 628 | if (argv[i][2] == '\0') { 629 | parseonly = true; 630 | break; 631 | } 632 | else if (toupper (argv[i][2]) == 'I') 633 | percussion_ignore = true; 634 | else if (toupper (argv[i][2]) == 'T') 635 | percussion_translate = true; 636 | else 637 | goto opterror; 638 | if (argv[i][3] != '\0') 639 | goto opterror; 640 | break; 641 | case 'B': 642 | binaryoutput = true; 643 | if (argv[i][2] != '\0') 644 | goto opterror; 645 | break; 646 | case 'V': 647 | velocityoutput = true; 648 | if (argv[i][2] == '\0') 649 | break; 650 | if (sscanf (&argv[i][2], "%d%n", &velocity_threshold, &nch) != 1 651 | || velocity_threshold < 0 || velocity_threshold > 127) 652 | goto opterror; 653 | printf ("Using velocity >= %d for high volume.\n", velocity_threshold); 654 | if (argv[i][2 + nch] != '\0') 655 | goto opterror; 656 | break; 657 | case 'I': 658 | instrumentoutput = true; 659 | if (argv[i][2] != '\0') 660 | goto opterror; 661 | break; 662 | case 'S': 663 | if (argv[i][2] == '1') 664 | strategy1 = true; 665 | else if (argv[i][2] == '2') 666 | strategy2 = true; 667 | else 668 | goto opterror; 669 | if (argv[i][3] != '\0') 670 | goto opterror; 671 | break; 672 | case 'T': 673 | if (sscanf (&argv[i][2], "%d%n", &num_tonegens, &nch) != 1 674 | || num_tonegens < 1 || num_tonegens > MAX_TONEGENS) 675 | goto opterror; 676 | printf ("Using %d tone generators.\n", num_tonegens); 677 | if (argv[i][2 + nch] != '\0') 678 | goto opterror; 679 | break; 680 | case 'N': 681 | if (sscanf (&argv[i][2], "%d%n", &outfile_maxitems, &nch) != 1 || outfile_maxitems < 1) 682 | goto opterror; 683 | if (argv[i][2 + nch] != '\0') 684 | goto opterror; 685 | option_n = true; 686 | break; 687 | case 'C': 688 | if (sscanf (&argv[i][2], "%i%n", &channel_mask, &nch) != 1 689 | || channel_mask == 0 || channel_mask > 0xffff) 690 | goto opterror; 691 | printf ("Channel (track) mask is %04X.\n", channel_mask); 692 | if (argv[i][2 + nch] != '\0') 693 | goto opterror; 694 | break; 695 | case 'K': 696 | if (sscanf (&argv[i][2], "%d%n", &keyshift, &nch) != 1 || keyshift < -100 697 | || keyshift > 100) 698 | goto opterror; 699 | printf ("Using keyshift %d.\n", keyshift); 700 | if (argv[i][2 + nch] != '\0') 701 | goto opterror; 702 | break; 703 | case 'D': 704 | if (argv[i][2] == '\0') { 705 | do_header = true; 706 | break; 707 | } 708 | if (toupper (argv[i][2]) == 'P') 709 | define_progmem = true; 710 | else 711 | goto opterror; 712 | if (argv[i][3] != '\0') 713 | goto opterror; 714 | break; 715 | case 'F': 716 | if (toupper (argv[i][2]) == 'A') 717 | freq_style_a = true; 718 | else if (toupper (argv[i][2]) == 'B') 719 | freq_style_b = true; 720 | else 721 | goto opterror; 722 | if (argv[i][3] != '\0') 723 | goto opterror; 724 | break; 725 | case 'R': 726 | gen_restart = true; 727 | if (argv[i][2] != '\0') 728 | goto opterror; 729 | break; 730 | /* add more option switches here */ 731 | opterror: 732 | default: 733 | fprintf (stderr, "\n*** unknown option: %s\n\n", argv[i]); 734 | SayUsage (argv[0]); 735 | exit (4); 736 | } 737 | } else { 738 | firstnonoption = i; 739 | break; 740 | } 741 | } 742 | return firstnonoption; 743 | } 744 | 745 | void print_command_line (int argc, char *argv[]) { 746 | int i; 747 | fprintf (outfile, "// command line: "); 748 | for (i = 0; i < argc; i++) 749 | fprintf (outfile, "%s ", argv[i]); 750 | fprintf (outfile, "\n"); 751 | } 752 | 753 | 754 | /**************** utility routines **********************/ 755 | 756 | /* portable string length */ 757 | int strlength (const char *str) { 758 | int i; 759 | for (i = 0; str[i] != '\0'; ++i); 760 | return i; 761 | } 762 | 763 | /* safe string copy */ 764 | size_t miditones_strlcpy (char *dst, const char *src, size_t siz) { 765 | char *d = dst; 766 | const char *s = src; 767 | size_t n = siz; 768 | /* Copy as many bytes as will fit */ 769 | if (n != 0) { 770 | while (--n != 0) { 771 | if ((*d++ = *s++) == '\0') 772 | break; 773 | } 774 | } 775 | /* Not enough room in dst, add NUL and traverse rest of src */ 776 | if (n == 0) { 777 | if (siz != 0) 778 | *d = '\0'; /* NUL-terminate dst */ 779 | while (*s++); 780 | } 781 | return (s - src - 1); /* count does not include NUL */ 782 | } 783 | 784 | /* safe string concatenation */ 785 | 786 | size_t miditones_strlcat (char *dst, const char *src, size_t siz) { 787 | char *d = dst; 788 | const char *s = src; 789 | size_t n = siz; 790 | size_t dlen; 791 | /* Find the end of dst and adjust bytes left but don't go past end */ 792 | while (n-- != 0 && *d != '\0') 793 | d++; 794 | dlen = d - dst; 795 | n = siz - dlen; 796 | if (n == 0) 797 | return (dlen + strlength (s)); 798 | while (*s != '\0') { 799 | if (n != 1) { 800 | *d++ = *s; 801 | n--; 802 | } 803 | s++; 804 | } 805 | *d = '\0'; 806 | return (dlen + (s - src)); /* count does not include NUL */ 807 | } 808 | 809 | /* match a constant character sequence */ 810 | 811 | int charcmp (const char *buf, const char *match) { 812 | int len, i; 813 | len = strlength (match); 814 | for (i = 0; i < len; ++i) 815 | if (buf[i] != match[i]) 816 | return 0; 817 | return 1; 818 | } 819 | 820 | /* announce a fatal MIDI file format error */ 821 | 822 | void midi_error (char *msg, unsigned char *bufptr) { 823 | unsigned char *ptr; 824 | fprintf (stderr, "---> MIDI file error at position %04X (%d): %s\n", 825 | (uint16_t) (bufptr - buffer), (uint16_t) (bufptr - buffer), msg); 826 | /* print some bytes surrounding the error */ 827 | ptr = bufptr - 16; 828 | if (ptr < buffer) 829 | ptr = buffer; 830 | for (; ptr <= bufptr + 16 && ptr < buffer + buflen; ++ptr) 831 | fprintf (stderr, ptr == bufptr ? " [%02X] " : "%02X ", *ptr); 832 | fprintf (stderr, "\n"); 833 | exit (8); 834 | } 835 | 836 | /* check that we have a specified number of bytes left in the buffer */ 837 | 838 | void chk_bufdata (unsigned char *ptr, unsigned long int len) { 839 | if ((unsigned) (ptr + len - buffer) > buflen) 840 | midi_error ("data missing", ptr); 841 | } 842 | 843 | /* fetch big-endian numbers */ 844 | 845 | uint16_t rev_short (uint16_t val) { 846 | return ((val & 0xff) << 8) | ((val >> 8) & 0xff); 847 | } 848 | 849 | uint32_t rev_long (uint32_t val) { 850 | return (((rev_short ((uint16_t) val) & 0xffff) << 16) | 851 | (rev_short ((uint16_t) (val >> 16)) & 0xffff)); 852 | } 853 | 854 | /* write a big-endian 16 bit number to the output file */ 855 | void output_word (uint16_t val) { 856 | putc ((unsigned char) (val >> 8), outfile); 857 | putc ((unsigned char) (val & 0xff), outfile); 858 | outfile_bytecount += 2; 859 | } 860 | 861 | /* account for new items in the non-binary output file 862 | and generate a newline every so often. */ 863 | 864 | void outfile_items (int n) { 865 | if (!alt_out) 866 | outfile_bytecount += n; 867 | else 868 | outfile_bytecount += (n * 2); 869 | outfile_itemcount += n; 870 | if (!binaryoutput && outfile_itemcount >= outfile_maxitems) { 871 | fprintf (outfile, "\n"); 872 | outfile_itemcount = 0; 873 | } 874 | } 875 | 876 | /************** process the MIDI file header *****************/ 877 | 878 | void process_header (void) { 879 | struct midi_header *hdr; 880 | unsigned int time_division; 881 | 882 | chk_bufdata (hdrptr, sizeof (struct midi_header)); 883 | hdr = (struct midi_header *) hdrptr; 884 | if (!charcmp ((char *) hdr->MThd, "MThd")) 885 | midi_error ("Missing 'MThd'", hdrptr); 886 | num_tracks = rev_short (hdr->number_of_tracks); 887 | time_division = rev_short (hdr->time_division); 888 | if (time_division < 0x8000) 889 | ticks_per_beat = time_division; 890 | else 891 | ticks_per_beat = ((time_division >> 8) & 0x7f) /* SMTE frames/sec */ *(time_division & 0xff); /* ticks/SMTE frame */ 892 | if (logparse) { 893 | fprintf (logfile, "Header size %" PRId32 "\n", rev_long (hdr->header_size)); 894 | fprintf (logfile, "Format type %d\n", rev_short (hdr->format_type)); 895 | fprintf (logfile, "Number of tracks %d\n", num_tracks); 896 | fprintf (logfile, "Time division %04X\n", time_division); 897 | fprintf (logfile, "Ticks/beat = %d\n", ticks_per_beat); 898 | } 899 | hdrptr += rev_long (hdr->header_size) + 8; /* point past header to track header, presumably. */ 900 | return; 901 | } 902 | 903 | 904 | /**************** Process a MIDI track header *******************/ 905 | 906 | void start_track (int tracknum) { 907 | struct track_header *hdr; 908 | unsigned long tracklen; 909 | 910 | chk_bufdata (hdrptr, sizeof (struct track_header)); 911 | hdr = (struct track_header *) hdrptr; 912 | if (!charcmp ((char *) (hdr->MTrk), "MTrk")) 913 | midi_error ("Missing 'MTrk'", hdrptr); 914 | tracklen = rev_long (hdr->track_size); 915 | if (logparse) 916 | fprintf (logfile, "\nTrack %d length %ld\n", tracknum, tracklen); 917 | hdrptr += sizeof (struct track_header); /* point past header */ 918 | chk_bufdata (hdrptr, tracklen); 919 | track[tracknum].trkptr = hdrptr; 920 | hdrptr += tracklen; /* point to the start of the next track */ 921 | track[tracknum].trkend = hdrptr; /* the point past the end of the track */ 922 | } 923 | 924 | 925 | /* Get a MIDI-style variable-length integer */ 926 | 927 | unsigned long get_varlen (uint8_t ** ptr) { 928 | /* Get a 1-4 byte variable-length value and adjust the pointer past it. 929 | These are a succession of 7-bit values with a MSB bit of zero marking the end */ 930 | 931 | unsigned long val; 932 | int i, byte; 933 | 934 | val = 0; 935 | for (i = 0; i < 4; ++i) { 936 | byte = *(*ptr)++; 937 | val = (val << 7) | (byte & 0x7f); 938 | if (!(byte & 0x80)) 939 | return val; 940 | } 941 | return val; 942 | } 943 | 944 | 945 | /*************** Process the MIDI track data ***************************/ 946 | 947 | /* Skip in the track for the next "note on", "note off" or "set tempo" command, 948 | then record that information in the track status block and return. */ 949 | 950 | void find_note (int tracknum) { 951 | unsigned long int delta_time; 952 | int event, chan; 953 | int i; 954 | int note, velocity, controller, pressure, pitchbend, instrument; 955 | int meta_cmd, meta_length; 956 | unsigned long int sysex_length; 957 | struct track_status *t; 958 | char *tag; 959 | 960 | /* process events */ 961 | 962 | t = &track[tracknum]; /* our track status structure */ 963 | while (t->trkptr < t->trkend) { 964 | 965 | delta_time = get_varlen (&t->trkptr); 966 | if (logparse) { 967 | fprintf (logfile, "trk %d ", tracknum); 968 | if (delta_time) { 969 | fprintf (logfile, "delta time %4ld, ", delta_time); 970 | } else { 971 | fprintf (logfile, " "); 972 | } 973 | } 974 | t->time += delta_time; 975 | if (*t->trkptr < 0x80) 976 | event = t->last_event; /* using "running status": same event as before */ 977 | else { /* otherwise get new "status" (event type) */ 978 | event = *t->trkptr++; 979 | } 980 | if (event == 0xff) { /* meta-event */ 981 | meta_cmd = *t->trkptr++; 982 | meta_length = *t->trkptr++; 983 | switch (meta_cmd) { 984 | case 0x00: 985 | if (logparse) 986 | fprintf (logfile, "sequence number %d\n", rev_short (*(unsigned short *) t->trkptr)); 987 | break; 988 | case 0x01: 989 | tag = "description"; 990 | goto show_text; 991 | case 0x02: 992 | tag = "copyright"; 993 | goto show_text; 994 | case 0x03: 995 | tag = "track name"; 996 | if (tracknum == 0 && !parseonly && !binaryoutput) { 997 | /* Incredibly, MIDI has no standard for recording the name of the piece! 998 | Track 0's "trackname" is often used for that so we output it to the C file as documentation. */ 999 | fprintf (outfile, "// "); 1000 | for (i = 0; i < meta_length; ++i) { 1001 | int ch = t->trkptr[i]; 1002 | fprintf (outfile, "%c", isprint (ch) ? ch : '?'); 1003 | } 1004 | fprintf (outfile, "\n"); 1005 | } 1006 | goto show_text; 1007 | case 0x04: 1008 | tag = "instrument name"; 1009 | goto show_text; 1010 | case 0x05: 1011 | tag = "lyric"; 1012 | goto show_text; 1013 | case 0x06: 1014 | tag = "marked point"; 1015 | goto show_text; 1016 | case 0x07: 1017 | tag = "cue point"; 1018 | show_text: 1019 | if (logparse) { 1020 | fprintf (logfile, "meta cmd %02X, length %d, %s: \"", meta_cmd, meta_length, tag); 1021 | for (i = 0; i < meta_length; ++i) { 1022 | int ch = t->trkptr[i]; 1023 | fprintf (logfile, "%c", isprint (ch) ? ch : '?'); 1024 | } 1025 | fprintf (logfile, "\"\n"); 1026 | } 1027 | break; 1028 | case 0x20: 1029 | if (logparse) 1030 | fprintf (logfile, "channel prefix %d\n", *t->trkptr); 1031 | break; 1032 | case 0x2f: 1033 | if (logparse) 1034 | fprintf (logfile, "end of track\n"); 1035 | break; 1036 | case 0x51: /* tempo: 3 byte big-endian integer! */ 1037 | t->cmd = CMD_TEMPO; 1038 | t->tempo = rev_long (*(unsigned long *) (t->trkptr - 1)) & 0xffffffL; 1039 | if (logparse) 1040 | fprintf (logfile, "set tempo %ld usec/qnote\n", t->tempo); 1041 | t->trkptr += meta_length; 1042 | return; 1043 | case 0x54: 1044 | if (logparse) 1045 | fprintf (logfile, "SMPTE offset %08" PRIx32 "\n", 1046 | rev_long (*(unsigned long *) t->trkptr)); 1047 | break; 1048 | case 0x58: 1049 | if (logparse) 1050 | fprintf (logfile, "time signature %08" PRIx32 "\n", 1051 | rev_long (*(unsigned long *) t->trkptr)); 1052 | break; 1053 | case 0x59: 1054 | if (logparse) 1055 | fprintf (logfile, "key signature %04X\n", rev_short (*(unsigned short *) t->trkptr)); 1056 | break; 1057 | case 0x7f: 1058 | tag = "sequencer data"; 1059 | goto show_hex; 1060 | default: /* unknown meta command */ 1061 | tag = "???"; 1062 | show_hex: 1063 | if (logparse) { 1064 | fprintf (logfile, "meta cmd %02X, length %d, %s: ", meta_cmd, meta_length, tag); 1065 | for (i = 0; i < meta_length; ++i) 1066 | fprintf (logfile, "%02X ", t->trkptr[i]); 1067 | fprintf (logfile, "\n"); 1068 | } 1069 | 1070 | break; 1071 | } 1072 | t->trkptr += meta_length; 1073 | } 1074 | 1075 | else if (event < 0x80) 1076 | midi_error ("Unknown MIDI event type", t->trkptr); 1077 | 1078 | else { 1079 | if (event < 0xf0) 1080 | t->last_event = event; // remember "running status" if not meta or sysex event 1081 | chan = event & 0xf; 1082 | t->chan = chan; 1083 | switch (event >> 4) { 1084 | case 0x8: 1085 | t->note = *t->trkptr++; 1086 | velocity = *t->trkptr++; 1087 | note_off: 1088 | if (logparse) 1089 | fprintf (logfile, "note %d off, chan %d, velocity %d\n", t->note, chan, velocity); 1090 | if ((1 << chan) & channel_mask) { /* if we're processing this channel */ 1091 | t->cmd = CMD_STOPNOTE; 1092 | return; /* stop processing and return */ 1093 | } 1094 | break; // else keep looking 1095 | case 0x9: 1096 | t->note = *t->trkptr++; 1097 | velocity = *t->trkptr++; 1098 | if (velocity == 0) /* some scores use note-on with zero velocity for off! */ 1099 | goto note_off; 1100 | t->velocity = velocity; 1101 | if (logparse) 1102 | fprintf (logfile, "note %d on, chan %d, velocity %d\n", t->note, chan, velocity); 1103 | if ((1 << chan) & channel_mask) { /* if we're processing this channel */ 1104 | t->cmd = CMD_PLAYNOTE; 1105 | return; /* stop processing and return */ 1106 | } 1107 | break; // else keep looking 1108 | case 0xa: 1109 | note = *t->trkptr++; 1110 | velocity = *t->trkptr++; 1111 | if (logparse) 1112 | fprintf (logfile, "after-touch %d, %d\n", note, velocity); 1113 | break; 1114 | case 0xb: 1115 | controller = *t->trkptr++; 1116 | velocity = *t->trkptr++; 1117 | if (logparse) 1118 | fprintf (logfile, "control change %d, %d\n", controller, velocity); 1119 | break; 1120 | case 0xc: 1121 | instrument = *t->trkptr++; 1122 | midi_chan_instrument[chan] = instrument; // record new instrument for this channel 1123 | if (logparse) 1124 | fprintf (logfile, "program patch %d\n", instrument); 1125 | break; 1126 | case 0xd: 1127 | pressure = *t->trkptr++; 1128 | if (logparse) 1129 | fprintf (logfile, "channel after-touch %d\n", pressure); 1130 | break; 1131 | case 0xe: 1132 | pitchbend = *t->trkptr++ | (*t->trkptr++ << 7); 1133 | if (logparse) 1134 | fprintf (logfile, "pitch wheel change %d\n", pitchbend); 1135 | break; 1136 | case 0xf: 1137 | sysex_length = get_varlen (&t->trkptr); 1138 | if (logparse) 1139 | fprintf (logfile, "SysEx event %d, %ld bytes\n", event, sysex_length); 1140 | t->trkptr += sysex_length; 1141 | break; 1142 | default: 1143 | midi_error ("Unknown MIDI command", t->trkptr); 1144 | } 1145 | } 1146 | } 1147 | t->cmd = CMD_TRACKDONE; /* no more notes to process */ 1148 | ++tracks_done; 1149 | } 1150 | 1151 | 1152 | /* generate "stop note" commands for any channels that have them pending */ 1153 | 1154 | void gen_stopnotes(void) { 1155 | struct tonegen_status *tg; 1156 | for (int tgnum = 0; tgnum < num_tonegens; ++tgnum) { 1157 | tg = &tonegen[tgnum]; 1158 | if (tg->stopnote_pending) { 1159 | if (binaryoutput) { 1160 | putc (CMD_STOPNOTE | tgnum, outfile); 1161 | outfile_bytecount += 1; 1162 | } else { 1163 | fprintf (outfile, " 0x%02X,", CMD_STOPNOTE | tgnum); 1164 | outfile_items (1); 1165 | } 1166 | tg->stopnote_pending = false; 1167 | } 1168 | } 1169 | } 1170 | 1171 | 1172 | /********************* main ****************************/ 1173 | 1174 | int main (int argc, char *argv[]) { 1175 | int argno; 1176 | char *filebasename; 1177 | #define MAXPATH 120 1178 | char filename[MAXPATH]; 1179 | int tracknum; 1180 | int earliest_tracknum; 1181 | unsigned long earliest_time; 1182 | int notes_skipped = 0; 1183 | char *text; 1184 | 1185 | printf ("MIDI2TONES V%s\n(C) 2011-2016 Len Shustek\n(C) 2016 Scott Allen\n", VERSION); 1186 | if (argc == 1) { /* no arguments */ 1187 | SayUsage (argv[0]); 1188 | return 1; 1189 | } 1190 | 1191 | /* process options */ 1192 | 1193 | argno = HandleOptions (argc, argv); 1194 | if (argno == 0) { 1195 | fprintf (stderr, "\n*** No basefilename given\n\n"); 1196 | SayUsage (argv[0]); 1197 | exit (4); 1198 | } 1199 | filebasename = argv[argno]; 1200 | 1201 | /* if alternate output format, overide options with required values */ 1202 | if (alt_out) { 1203 | num_tonegens = 1; 1204 | do_header = false; 1205 | instrumentoutput = false; 1206 | percussion_translate = false; 1207 | velocityoutput = true; 1208 | if (!option_n) 1209 | outfile_maxitems = 16; 1210 | 1211 | /* use only lowest channel from mask */ 1212 | for (int i = 0; i < 16; i++) { 1213 | if (channel_mask >> i & 1) { 1214 | channel_mask = 1 << i; 1215 | alt_out_channel = i; 1216 | printf ("Using only MIDI channel %d\n", i); 1217 | break; 1218 | } 1219 | } 1220 | } 1221 | 1222 | /* Open the log file */ 1223 | 1224 | if (logparse || loggen) { 1225 | miditones_strlcpy (filename, filebasename, MAXPATH); 1226 | miditones_strlcat (filename, ".log", MAXPATH); 1227 | logfile = fopen (filename, "w"); 1228 | if (!logfile) { 1229 | fprintf (stderr, "Unable to open log file %s\n", filename); 1230 | return 1; 1231 | } 1232 | fprintf (logfile, "MIDI2TONES V%s log file\n", VERSION); 1233 | } 1234 | 1235 | /* Open the input file */ 1236 | 1237 | miditones_strlcpy (filename, filebasename, MAXPATH); 1238 | miditones_strlcat (filename, ".mid", MAXPATH); 1239 | infile = fopen (filename, "rb"); 1240 | if (!infile) { 1241 | fprintf (stderr, "Unable to open input file %s\n", filename); 1242 | return 1; 1243 | } 1244 | 1245 | /* Read the whole input file into memory */ 1246 | 1247 | fseek (infile, 0, SEEK_END); /* find file size */ 1248 | buflen = ftell (infile); 1249 | fseek (infile, 0, SEEK_SET); 1250 | buffer = (unsigned char *) malloc (buflen + 1); 1251 | if (!buffer) { 1252 | fprintf (stderr, "Unable to allocate %ld bytes for the file\n", buflen); 1253 | return 1; 1254 | } 1255 | fread (buffer, buflen, 1, infile); 1256 | fclose (infile); 1257 | if (logparse) 1258 | fprintf (logfile, "Processing %s, %ld bytes\n", filename, buflen); 1259 | 1260 | /* Create the output file */ 1261 | 1262 | if (!parseonly) { 1263 | miditones_strlcpy (filename, filebasename, MAXPATH); 1264 | if (binaryoutput) { 1265 | miditones_strlcat (filename, ".bin", MAXPATH); 1266 | outfile = fopen (filename, "wb"); 1267 | } else { 1268 | miditones_strlcat (filename, ".c", MAXPATH); 1269 | outfile = fopen (filename, "w"); 1270 | } 1271 | if (!outfile) { 1272 | fprintf (stderr, "Unable to open output file %s\n", filename); 1273 | return 1; 1274 | } 1275 | file_header.f1 = (velocityoutput ? HDR_F1_VOLUME_PRESENT : 0) 1276 | | (instrumentoutput ? HDR_F1_INSTRUMENTS_PRESENT : 0) 1277 | | (percussion_translate ? HDR_F1_PERCUSSION_PRESENT : 0); 1278 | file_header.num_tgens = num_tonegens; 1279 | if (!binaryoutput) { /* create header of C file that initializes score data */ 1280 | if (!alt_out) 1281 | text = "Playtune bytestream"; 1282 | else 1283 | text = "ArduboyTones stream"; 1284 | time_t rawtime; 1285 | time (&rawtime); 1286 | fprintf (outfile, "// %s for file \"%s.mid\" ", text, filebasename); 1287 | fprintf (outfile, "created by MIDI2TONES V%s on %s", VERSION, 1288 | asctime (localtime (&rawtime))); 1289 | print_command_line (argc, argv); 1290 | if (!alt_out) { 1291 | if (channel_mask != 0xffff) 1292 | fprintf (outfile, "// Only the masked channels were processed: %04X\n", channel_mask); 1293 | } else { 1294 | fprintf (outfile, "// From channel %d\n", alt_out_channel); 1295 | } 1296 | if (keyshift != 0) 1297 | fprintf (outfile, "// Keyshift was %d chromatic notes\n", keyshift); 1298 | if (define_progmem) { 1299 | fprintf (outfile, "#ifdef __AVR__\n"); 1300 | fprintf (outfile, "#include \n"); 1301 | fprintf (outfile, "#else\n"); 1302 | fprintf (outfile, "#define PROGMEM\n"); 1303 | fprintf (outfile, "#endif\n"); 1304 | } 1305 | if (!alt_out) 1306 | fprintf (outfile, "const byte score[] PROGMEM = {\n"); 1307 | else 1308 | fprintf (outfile, "const uint16_t score[] PROGMEM = {\n"); 1309 | if (do_header) { // write the C initialization for the file header 1310 | fprintf (outfile, "'P','t', 6, 0x%02X, 0x%02X, ", file_header.f1, file_header.f2); 1311 | fflush (outfile); 1312 | file_header_num_tgens_position = ftell (outfile); // remember where the number of tone generators is 1313 | fprintf (outfile, "%2d, // (Playtune file header)\n", file_header.num_tgens); 1314 | outfile_bytecount += 6; 1315 | } 1316 | } else if (do_header) { // write the binary file header 1317 | for (int i = 0; i < sizeof (file_header); ++i) 1318 | putc (((unsigned char *) &file_header)[i], outfile); 1319 | file_header_num_tgens_position = (char *) &file_header.num_tgens - (char *) &file_header; 1320 | outfile_bytecount += sizeof (file_header); 1321 | } 1322 | } 1323 | 1324 | /* process the MIDI file header */ 1325 | 1326 | hdrptr = buffer; /* pointer to file and track headers */ 1327 | process_header (); 1328 | printf (" Processing %d tracks.\n", num_tracks); 1329 | if (num_tracks > MAX_TRACKS) 1330 | midi_error ("Too many tracks", buffer); 1331 | 1332 | /* initialize processing of all the tracks */ 1333 | 1334 | for (tracknum = 0; tracknum < num_tracks; ++tracknum) { 1335 | start_track (tracknum); /* process the track header */ 1336 | find_note (tracknum); /* position to the first note on/off */ 1337 | /* if we are in "parse only" mode, do the whole track, 1338 | so we do them one at a time instead of time-synchronized. */ 1339 | if (parseonly) 1340 | while (track[tracknum].cmd != CMD_TRACKDONE) 1341 | find_note (tracknum); 1342 | } 1343 | 1344 | /* Continue processing all tracks, in an order based on the simulated time. 1345 | This is not unlike multiway merging used for tape sorting algoritms in the 50's! */ 1346 | 1347 | tracknum = 0; 1348 | if (!parseonly) { 1349 | do { /* while there are still track notes to process */ 1350 | struct track_status *trk; 1351 | struct tonegen_status *tg; 1352 | int tgnum; 1353 | int count_tracks; 1354 | unsigned long delta_time, delta_msec; 1355 | 1356 | /* Find the track with the earliest event time, 1357 | and output a delay command if time has advanced. 1358 | 1359 | A potential improvement: If there are multiple tracks with the same time, 1360 | first do the ones with STOPNOTE as the next command, if any. That would 1361 | help avoid running out of tone generators. In practice, though, most MIDI 1362 | files do all the STOPNOTEs first anyway, so it won't have much effect. 1363 | */ 1364 | 1365 | earliest_time = 0x7fffffff; 1366 | 1367 | /* Usually we start with the track after the one we did last time (tracknum), 1368 | so that if we run out of tone generators, we have been fair to all the tracks. 1369 | The alternate "strategy1" says we always start with track 0, which means 1370 | that we favor early tracks over later ones when there aren't enough tone generators. 1371 | */ 1372 | 1373 | count_tracks = num_tracks; 1374 | if (strategy1) 1375 | tracknum = num_tracks; /* beyond the end, so we start with track 0 */ 1376 | do { 1377 | if (++tracknum >= num_tracks) 1378 | tracknum = 0; 1379 | trk = &track[tracknum]; 1380 | if (trk->cmd != CMD_TRACKDONE && trk->time < earliest_time) { 1381 | earliest_time = trk->time; 1382 | earliest_tracknum = tracknum; 1383 | } 1384 | } 1385 | while (--count_tracks); 1386 | 1387 | tracknum = earliest_tracknum; /* the track we picked */ 1388 | trk = &track[tracknum]; 1389 | if (loggen) 1390 | fprintf (logfile, "Earliest time is trk %d, time %ld\n", tracknum, earliest_time); 1391 | if (earliest_time < timenow) 1392 | midi_error ("INTERNAL: time went backwards", trk->trkptr); 1393 | 1394 | /* If time has advanced, output a "delay" command or frequency/duration pair */ 1395 | 1396 | delta_time = earliest_time - timenow; 1397 | if (delta_time) { 1398 | if (!alt_out) 1399 | gen_stopnotes(); /* first check if any tone generators have "stop note" commands pending */ 1400 | /* Convert ticks to milliseconds based on the current tempo */ 1401 | unsigned long long temp; 1402 | temp = ((unsigned long long) delta_time * tempo) / ticks_per_beat; 1403 | delta_msec = temp / 1000; // get around LCC compiler bug 1404 | if (loggen) 1405 | fprintf (logfile, "->Delay %ld msec (%ld ticks)\n", delta_msec, delta_time); 1406 | if (!alt_out) { 1407 | if (delta_msec > 0x7fff) 1408 | midi_error ("INTERNAL: time delta too big", trk->trkptr); 1409 | /* output a 15-bit delay in big-endian format */ 1410 | if (binaryoutput) { 1411 | output_word (delta_msec); 1412 | } else { 1413 | fprintf (outfile, " %ld,%ld,", delta_msec >> 8, delta_msec & 0xff); 1414 | outfile_items (2); 1415 | } 1416 | } else { 1417 | uint16_t freq; 1418 | bool high_vol; 1419 | if (delta_msec > 0xffff) 1420 | midi_error ("INTERNAL: time delta too big", trk->trkptr); 1421 | /* output a frequency/duration pair */ 1422 | freq = note_freq[pending_note]; 1423 | high_vol = (pending_velocity >= velocity_threshold) && (freq != 0); 1424 | if (binaryoutput) { 1425 | if (high_vol) 1426 | freq += TONE_HIGH_VOLUME; 1427 | output_word (freq); 1428 | output_word (delta_msec); 1429 | } else { 1430 | if (freq_style_a) { 1431 | fprintf (outfile, " %d", freq); 1432 | if (high_vol) 1433 | fprintf (outfile, "+TONE_HIGH_VOLUME"); 1434 | } else if (freq_style_b) { 1435 | if (high_vol) 1436 | freq += TONE_HIGH_VOLUME; 1437 | fprintf (outfile, " %d", freq); 1438 | } else { 1439 | if (freq != 0) { 1440 | fprintf (outfile, " NOTE_%s%d", note_name[pending_note % 12], pending_note / 12); 1441 | if (high_vol) 1442 | putc ('H', outfile); 1443 | } else { 1444 | fprintf (outfile, " NOTE_REST"); 1445 | } 1446 | } 1447 | fprintf (outfile, ",%ld,", delta_msec); 1448 | outfile_items (2); 1449 | } 1450 | } 1451 | } 1452 | timenow = earliest_time; 1453 | 1454 | /* If this track event is "set tempo", just change the global tempo. 1455 | That affects how we generate "delay" commands. */ 1456 | 1457 | if (trk->cmd == CMD_TEMPO) { 1458 | tempo = trk->tempo; 1459 | if (loggen) 1460 | fprintf (logfile, "Tempo changed to %ld usec/qnote\n", tempo); 1461 | find_note (tracknum); 1462 | } 1463 | 1464 | /* If this track event is "stop note", process it and all subsequent "stop notes" for this track 1465 | that are happening at the same time. Doing so frees up as many tone generators as possible. */ 1466 | 1467 | else if (trk->cmd == CMD_STOPNOTE) 1468 | do { 1469 | // stop a note 1470 | if (!percussion_ignore || trk->chan != PERCUSSION_TRACK) /* if we didn't ignore it as percussion */ 1471 | for (tgnum = 0; tgnum < num_tonegens; ++tgnum) { /* find which generator is playing it */ 1472 | tg = &tonegen[tgnum]; 1473 | if (tg->playing && tg->track == tracknum && tg->note == trk->note) { 1474 | if (loggen) 1475 | fprintf (logfile, 1476 | "->Stop note %d, generator %d, track %d\n", 1477 | tg->note, tgnum, tracknum); 1478 | tg->stopnote_pending = true; /* must stop the current note if another doesn't start first */ 1479 | tg->playing = false; 1480 | trk->tonegens[tgnum] = false; 1481 | if (alt_out) 1482 | pending_note = pending_velocity = 0; 1483 | } 1484 | } 1485 | find_note (tracknum); // use up the note 1486 | } 1487 | while (trk->cmd == CMD_STOPNOTE && trk->time == timenow); 1488 | 1489 | /* If this track event is "start note", process only it. 1490 | Don't do more than one, so we allow other tracks their chance at grabbing tone generators. */ 1491 | 1492 | else if (trk->cmd == CMD_PLAYNOTE) { 1493 | if (!percussion_ignore || trk->chan != PERCUSSION_TRACK) { /* ignore percussion track notes if asked to */ 1494 | bool foundgen = false; 1495 | /* maybe try to use the same tone generator that this track used last time */ 1496 | if (strategy2) { 1497 | tg = &tonegen[trk->preferred_tonegen]; 1498 | if (!tg->playing) { 1499 | tgnum = trk->preferred_tonegen; 1500 | foundgen = true; 1501 | } 1502 | } 1503 | /* if not, then try for a free tone generator that had been playing the same instrument we need */ 1504 | if (!foundgen) 1505 | for (tgnum = 0; tgnum < num_tonegens; ++tgnum) { 1506 | tg = &tonegen[tgnum]; 1507 | if (!tg->playing && tg->instrument == midi_chan_instrument[trk->chan]) { 1508 | foundgen = true; 1509 | break; 1510 | } 1511 | } 1512 | /* if not, then try for any free tone generator */ 1513 | if (!foundgen) 1514 | for (tgnum = 0; tgnum < num_tonegens; ++tgnum) { 1515 | tg = &tonegen[tgnum]; 1516 | if (!tg->playing) { 1517 | foundgen = true; 1518 | break; 1519 | } 1520 | } 1521 | if (foundgen) { 1522 | int shifted_note; 1523 | if (tgnum + 1 > num_tonegens_used) 1524 | num_tonegens_used = tgnum + 1; 1525 | tg->playing = true; 1526 | tg->track = tracknum; 1527 | tg->note = trk->note; 1528 | tg->stopnote_pending = false; 1529 | trk->tonegens[tgnum] = true; 1530 | trk->preferred_tonegen = tgnum; 1531 | ++note_on_commands; 1532 | if (tg->instrument != midi_chan_instrument[trk->chan]) { /* new instrument for this generator */ 1533 | tg->instrument = midi_chan_instrument[trk->chan]; 1534 | ++instrument_changes; 1535 | if (loggen) 1536 | fprintf (logfile, 1537 | "gen %d changed to instrument %d\n", tgnum, tg->instrument); 1538 | if (instrumentoutput) { /* output a "change instrument" command */ 1539 | if (binaryoutput) { 1540 | putc (CMD_INSTRUMENT | tgnum, outfile); 1541 | putc (tg->instrument, outfile); 1542 | } else { 1543 | fprintf (outfile, " 0x%02X,%d,", CMD_INSTRUMENT | tgnum, tg->instrument); 1544 | outfile_items (2); 1545 | } 1546 | } 1547 | } 1548 | if (loggen) 1549 | fprintf (logfile, 1550 | "->Start note %d, velocity %d, generator %d, instrument %d, track %d\n", 1551 | trk->note, trk->velocity, tgnum, tg->instrument, tracknum); 1552 | if (percussion_translate && trk->chan == PERCUSSION_TRACK) { /* if requested, */ 1553 | shifted_note = trk->note + 128; // shift percussion notes up to 128..255 1554 | } else { /* shift notes as requested */ 1555 | shifted_note = trk->note + keyshift; 1556 | } 1557 | if (!alt_out) { 1558 | if (shifted_note < 0) 1559 | shifted_note = 0; 1560 | if (shifted_note > 127) 1561 | shifted_note = 127; 1562 | if (binaryoutput) { 1563 | putc (CMD_PLAYNOTE | tgnum, outfile); 1564 | putc (shifted_note, outfile); 1565 | outfile_bytecount += 2; 1566 | if (velocityoutput) { 1567 | putc (trk->velocity, outfile); 1568 | outfile_bytecount++; 1569 | } 1570 | } else { 1571 | if (!velocityoutput) { 1572 | fprintf (outfile, " 0x%02X,%d,", CMD_PLAYNOTE | tgnum, shifted_note); 1573 | outfile_items (2); 1574 | } else { 1575 | fprintf (outfile, " 0x%02X,%d,%d,", 1576 | CMD_PLAYNOTE | tgnum, shifted_note, trk->velocity); 1577 | outfile_items (3); 1578 | } 1579 | } 1580 | } else { 1581 | if ((shifted_note < 12) || (shifted_note > 127)) { 1582 | pending_note = pending_velocity = 0; 1583 | } else { 1584 | pending_note = shifted_note; 1585 | pending_velocity = trk->velocity; 1586 | } 1587 | } 1588 | } else { 1589 | if (loggen) 1590 | fprintf (logfile, 1591 | "----> No free generator, skipping note %d, track %d\n", 1592 | trk->note, tracknum); 1593 | ++notes_skipped; 1594 | } 1595 | } 1596 | find_note (tracknum); // use up the note 1597 | } 1598 | 1599 | } /* !parseonly do */ 1600 | while (tracks_done < num_tracks); 1601 | 1602 | if (!alt_out) 1603 | gen_stopnotes(); /* flush out any pending "stop note" commands */ 1604 | 1605 | // generate the end-of-score command and some commentary 1606 | if (binaryoutput) { 1607 | if (!alt_out) { 1608 | putc (gen_restart ? CMD_RESTART : CMD_STOP, outfile); 1609 | outfile_bytecount++; 1610 | } else { 1611 | output_word (gen_restart ? TONES_REPEAT : TONES_END); 1612 | } 1613 | } else { 1614 | if (outfile_itemcount != 0) 1615 | putc ('\n', outfile); 1616 | if (!alt_out) { 1617 | fprintf (outfile, " 0x%02x", gen_restart ? CMD_RESTART : CMD_STOP); 1618 | outfile_bytecount++; 1619 | } else { 1620 | if (freq_style_b) 1621 | fprintf (outfile, " 0x%04x", gen_restart ? TONES_REPEAT : TONES_END); 1622 | else 1623 | fprintf (outfile, " %s", gen_restart ? "TONES_REPEAT" : "TONES_END"); 1624 | outfile_bytecount += 2; 1625 | } 1626 | fprintf (outfile, "\n};\n// This score contains %ld bytes", outfile_bytecount); 1627 | if (!alt_out) 1628 | fprintf (outfile, ", and %d tone generator%s used.\n",num_tonegens_used, 1629 | num_tonegens_used == 1 ? " is" : "s are"); 1630 | else 1631 | fprintf (outfile, ".\n"); 1632 | if (notes_skipped) 1633 | fprintf (outfile, "// %d notes had to be skipped.\n", notes_skipped); 1634 | } 1635 | if (!alt_out) 1636 | printf (" %s %d tone generators were used.\n", 1637 | num_tonegens_used < num_tonegens ? "Only" : "All", num_tonegens_used); 1638 | if (notes_skipped) 1639 | printf (" %d notes were skipped because there %s.\n", notes_skipped, 1640 | alt_out ? "is only 1 tone generator" 1641 | : "weren't enough tone generators"); 1642 | printf (" %ld bytes of score data were generated.\n", outfile_bytecount); 1643 | if (loggen) 1644 | fprintf (logfile, "%d note-on commands, %d instrument changes.\n", 1645 | note_on_commands, instrument_changes); 1646 | 1647 | if (do_header) { // rewrite the file header with the actual number of tone generators used 1648 | if (fseek (outfile, file_header_num_tgens_position, SEEK_SET) != 0) 1649 | fprintf (stderr, "Can't seek to number of tone generators in the header\n"); 1650 | else { 1651 | if (binaryoutput) 1652 | putc (num_tonegens_used, outfile); 1653 | else 1654 | fprintf (outfile, "%2d", num_tonegens_used); 1655 | } 1656 | } 1657 | fclose (outfile); 1658 | } /* if (!parseonly) */ 1659 | 1660 | if (loggen || logparse) 1661 | fclose (logfile); 1662 | printf (" Done.\n"); 1663 | return 0; 1664 | } 1665 | -------------------------------------------------------------------------------- /midi2tones_32bit.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLXXXp/midi2tones/510e928e22f38f27579a01c5919f2e6971c0fff2/midi2tones_32bit.exe -------------------------------------------------------------------------------- /midi2tones_64bit.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLXXXp/midi2tones/510e928e22f38f27579a01c5919f2e6971c0fff2/midi2tones_64bit.exe -------------------------------------------------------------------------------- /miditones_scroll.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************************** 2 | * 3 | * MIDITONES_SCROLL 4 | * 5 | * Decode a PLAYTUNE bytestream of notes as a time-ordered scroll, sort of like a 6 | * piano roll with non-uniform time. This is a command-line program with no GUI. 7 | * 8 | * 9 | * There are two primary uses: 10 | * 11 | * (1) To debug errors that cause some MIDI scripts to sound strange. 12 | * 13 | * (2) To create a C-program array initialized with the bytestream, but annotated 14 | * with the original notes. This is semantically the same as the normal output 15 | * of MIDITONES, but is easier to edit manually. The downside is that the C 16 | * source code file is much larger. 17 | * 18 | * In both cases it reads a xxx.bin file that was created from a MIDI file by 19 | * MIDITONES using the -b option. 20 | * 21 | * 22 | * For use case (1), just invoke the program with the base filename. The output is 23 | * to the console, which can be directed to a file using the usual >file redirection. 24 | * For example, starting with the original midi file "song.mid", say this: 25 | * 26 | * miditones -b song 27 | * miditones_scroll song >song.txt 28 | * 29 | * and then the file "song.txt" will contain the piano roll. 30 | * 31 | * 32 | * For use case (2), use the -c option to create a .c file. 33 | * For example, starting with the original midi file "song.mid", say this: 34 | * 35 | * miditones -b song 36 | * miditones_scroll -c song 37 | * 38 | * and then the file "song.c" will contain the PLAYTUNE bytestream C code. 39 | * 40 | * 41 | * Other command-line options: 42 | * 43 | * -tn says that up to n tone generators should be displayed. The default 44 | * is 6 and the maximum is 16. 45 | * 46 | * -v says that we expect the binary file to contain encoded note volume 47 | * information as generated from Miditones with the -v option. That 48 | * volume information is displayed next to each note. 49 | * 50 | * -vi says that we expect volume information to be in the file, but we 51 | * should ignore it when creating the display. 52 | * 53 | * -ii says we should not display instrument information we find 54 | * 55 | * 56 | * For source code to this and related programs, see 57 | * www.github.com/LenShustek/miditones 58 | * www.github.com/LenShustek/arduino-playtune 59 | * 60 | *---------------------------------------------------------------------------------------- 61 | * The MIT License (MIT) 62 | * Copyright (c) 2011,2013,2015,2016, Len Shustek 63 | * 64 | * Permission is hereby granted, free of charge, to any person obtaining a copy 65 | * of this software and associated documentation files (the "Software"), to deal 66 | * in the Software without restriction, including without limitation the rights 67 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 68 | * copies of the Software, and to permit persons to whom the Software is 69 | * furnished to do so, subject to the following conditions: 70 | * 71 | * The above copyright notice and this permission notice shall be included in all 72 | * copies or substantial portions of the Software. 73 | * 74 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 75 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 76 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 77 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 78 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR 79 | * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 80 | ******************************************************************************************/ 81 | // formatted with: indent miditones_scroll.c -br -brf -brs -ce -npsl -nut -i3 -l100 -lc100 82 | 83 | /* 84 | * Change log 85 | * 86 | * 26 February 2011, L.Shustek, V1.0 87 | * - Initial release 88 | * 29 December 2013, L.Shustek, V1.1 89 | * - Add a "-c" option to create C code output. 90 | * Thanks go to mats.engstrom for the idea. 91 | * 04 April 2015, L. Shustek, V1.3 92 | * - Made friendlier to other compilers,import source of strlcpy and strlcat, 93 | * and fix various type mismatches that the LCC compiler didn't fret about. 94 | * Generate "const" data initialization for compatibility with Arduino IDE v1.6.x. 95 | * 5 August 2016, L. Shustek, V1.4 96 | * - Add -v and -vi options to handle optional volume codes that Miditones can 97 | * now generate. 98 | * - Handle notes>127 as percussion, which Miditones can now generate 99 | * - Make the number of generators be 16 maximum, but the number actually displayed 100 | * can be specified by the -tn command-line option. 101 | * - Remove string.h because OS X has macros for strlcpy; thus had to add strlen(). 102 | * It's tough to write even non-GUI code for many environments! 103 | * - Add casts for where address subtraction is a long, like OS X. 104 | * 6 August 2016, L. Shustek, V1.5 105 | * - Handle optional instrument change information. 106 | * - Look for the optional self-describing file header. 107 | * 30 September 2016, L. Shustek, V1.6 108 | * - Count the number of unnecessary "stop note" commands in the bytestream 109 | */ 110 | 111 | #define VERSION "1.6" 112 | 113 | #include 114 | #include 115 | #include 116 | #include 117 | #include 118 | #include 119 | 120 | 121 | /*********** Global variables ******************/ 122 | 123 | #define MAX_TONEGENS 16 /* max tone generators we could display */ 124 | 125 | #define SILENT -1 /* "silent note" code */ 126 | 127 | int gen_note[MAX_TONEGENS]; // the note we're playing, or SILENT 128 | int gen_volume[MAX_TONEGENS]; // the volume we're playing 129 | int gen_instrument[MAX_TONEGENS]; // the instrument we're playing 130 | bool gen_instrument_changed[MAX_TONEGENS]; 131 | bool gen_did_stopnote[MAX_TONEGENS]; // did we just do a stopnote? 132 | 133 | FILE *infile, *outfile; 134 | unsigned char *buffer, *bufptr; 135 | unsigned long buflen; 136 | unsigned int num_tonegens = 6; // default number of generators 137 | unsigned int max_tonegen_found = 0; 138 | unsigned int notes_skipped = 0; 139 | unsigned int stopnotes_before_startnote = 0; 140 | 141 | unsigned long timenow = 0; 142 | unsigned char cmd, gen; 143 | unsigned char *lastbufptr; 144 | unsigned delay; 145 | bool codeoutput = false; 146 | bool expect_volume = false; 147 | bool ignore_volume = false; 148 | bool ignore_instruments = false; 149 | 150 | struct file_hdr_t { /* what the optional file header looks like */ 151 | char id1; // 'P' 152 | char id2; // 't' 153 | unsigned char hdr_length; // length of whole file header 154 | unsigned char f1; // flag byte 1 155 | unsigned char f2; // flag byte 2 156 | unsigned char num_tgens; // how many tone generators are used by this score 157 | }; 158 | #define HDR_F1_VOLUME_PRESENT 0x80 159 | #define HDR_F1_INSTRUMENTS_PRESENT 0x40 160 | #define HDR_F1_PERCUSSION_PRESENT 0x20 161 | 162 | 163 | static char *notename[256] = { /* maximum 5 characters */ 164 | 165 | // map from MIDI note number to octave and note name, 166 | 167 | "-1C ", "-1C#", "-1D ", "-1D#", "-1E ", "-1F ", "-1F#", "-1G ", "-1G#", "-1A ", "-1A#", "-1B ", 168 | " 0C ", " 0C#", " 0D ", " 0D#", " 0E ", " 0F ", " 0F#", " 0G ", " 0G#", " 0A ", " 0A#", " 0B ", 169 | " 1C ", " 1C#", " 1D ", " 1D#", " 1E ", " 1F ", " 1F#", " 1G ", " 1G#", " 1A ", " 1A#", " 1B ", 170 | " 2C ", " 2C#", " 2D ", " 2D#", " 2E ", " 2F ", " 2F#", " 2G ", " 2G#", " 2A ", " 2A#", " 2B ", 171 | " 3C ", " 3C#", " 3D ", " 3D#", " 3E ", " 3F ", " 3F#", " 3G ", " 3G#", " 3A ", " 3A#", " 3B ", 172 | " 4C ", " 4C#", " 4D ", " 4D#", " 4E ", " 4F ", " 4F#", " 4G ", " 4G#", " 4A ", " 4A#", " 4B ", 173 | " 5C ", " 5C#", " 5D ", " 5D#", " 5E ", " 5F ", " 5F#", " 5G ", " 5G#", " 5A ", " 5A#", " 5B ", 174 | " 6C ", " 6C#", " 6D ", " 6D#", " 6E ", " 6F ", " 6F#", " 6G ", " 6G#", " 6A ", " 6A#", " 6B ", 175 | " 7C ", " 7C#", " 7D ", " 7D#", " 7E ", " 7F ", " 7F#", " 7G ", " 7G#", " 7A ", " 7A#", " 7B ", 176 | " 8C ", " 8C#", " 8D ", " 8D#", " 8E ", " 8F ", " 8F#", " 8G ", " 8G#", " 8A ", " 8A#", " 8B ", 177 | " 9C ", " 9C#", " 9D ", " 9D#", " 9E ", " 9F ", " 9F#", " 9G ", 178 | 179 | // or to channel 9 percussion notes as relocated by Miditones to notes 128..255 180 | 181 | "P000 ", "P001 ", "P002 ", "P003 ", "P004 ", "P005 ", "P006 ", "P007 ", 182 | "P008 ", "P009 ", "P010 ", "P011 ", "P012 ", "P013 ", "P014 ", "P015 ", 183 | "P016 ", "P017 ", "P018 ", "P019 ", "P020 ", "P021 ", "P022 ", "P023 ", 184 | "P024 ", "P025 ", "P026 ", "Laser", "Whip ", "ScrPu", "ScrPl", "Stick", 185 | "MetCk", "P033 ", "MetBl", "BassD", "KickD", "SnaSt", "SnaD ", "Clap ", 186 | "ESnaD", "FTom2", "HHatC", "FTom1", "HHatF", "LTom ", "HHatO", "LMTom", 187 | "HMTom", "CrCym", "HTom ", "RiCym", "ChCym", "RiBel", "Tamb ", "SpCym", 188 | "CowBl", "CrCym", "VSlap", "RiCym", "HBong", "LBong", "CongD", "Conga", 189 | "Tumba", "HTimb", "LTimb", "HAgog", "LAgog", "Cabas", "Marac", "SWhis", 190 | "LWhis", "SGuir", "LGuir", "Clave", "HWood", "LWood", "HCuic", "LCuic", 191 | "MTria", "OTria", "Shakr", "Sleig", "BelTr", "Casta", "SirdD", "Sirdu", 192 | "P088 ", "P089 ", "P090 ", "SnDmR", "OcDrm", "SmDrB", "P094 ", "P095 ", 193 | "P096 ", "P097 ", "P098 ", "P099 ", "P100 ", "P101 ", "P102 ", "P103 ", 194 | "P104 ", "P105 ", "P106 ", "P107 ", "P108 ", "P109 ", "P110 ", "P111 ", 195 | "P112 ", "P113 ", "P114 ", "P115 ", "P116 ", "P117 ", "P118 ", "P119 ", 196 | "P120 ", "P121 ", "P122 ", "P123 ", "P124 ", "P125 ", "P126 ", "P127" 197 | }; 198 | 199 | static char *instrumentname[128] = { /* maximum 6 characters */ 200 | "APiano", "BPiano", "EPiano", "HPiano", "E1Pian", "E2Pian", "Harpsi", "Clavic", 201 | "Celest", "Glockn", "MusBox", "Vibrap", "Marimb", "Xyloph", "TubBel", "Dulcim", 202 | "DOrgan", "POrgan", "ROrgan", "COrgan", "dOrgan", "Accord", "Harmon", "TAccor", 203 | "NyGuit", "StGuit", "JzGuit", "ClGuit", "MuGuit", "OvGuit", "DsGuit", "HaGuit", 204 | "AcBass", "FiBass", "PiBass", "FrBass", "S1Bass", "S2Bass", "y1Bass", "y2Bass", 205 | "Violin", "Viola ", "Cello ", "CnBass", "TrStng", "PzStng", "OrHarp", "Timpan", 206 | "S1Ensb", "S1Ensb", "y1Strg", "y2Strg", "ChAhhs", "VcOohs", "SyVoic", "OrcHit", 207 | "Trumpt", "Trombn", "Tuba ", "MuTrum", "FrHorn", "Brass ", "y1Bras", "y2Bras", 208 | "SopSax", "AltSax", "TenSax", "BarSax", "Oboe ", "EnHorn", "Basson", "Clarin", 209 | "Piccol", "Flute ", "Record", "PFlute", "BlBotl", "Shakuh", "Whistl", "Ocarin", 210 | "Square", "Sawtoo", "Callip", "Chiff ", "Charag", "Voice ", "Fifths", "BassLd", 211 | "Pad1 ", "Pad2 ", "Pad3 ", "Pad4 ", "Pad5 ", "Pad6 ", "Pad7 ", "Pad 8 ", 212 | "FX1 ", "FX2 ", "FX3 ", "FX4 ", "FX5 ", "FX6 ", "FX7 ", "FX8 ", 213 | "Sitar ", "Banjo ", "Shamis", "Koto ", "Kalimb", "Bagpip", "Fiddle", "Shanai", 214 | "TnkBel", "Agogo ", "StDrum", "WdBlok", "TaiDrm", "MelTom", "SynDrm", "RevCym", 215 | "GuitFr", "Breath", "Seashr", "BirdTw", "Phone ", "Copter", "Claps ", "Guns " 216 | }; 217 | 218 | 219 | /************** command-line processing *******************/ 220 | 221 | void SayUsage (char *programName) { 222 | static char *usage[] = { 223 | "Display a MIDITONES bytestream", 224 | "Usage: miditones_scroll ", 225 | " reads .bin", 226 | " -tn displays up to n tone generators", 227 | " -v expects and displays volume information", 228 | " -vi expects and ignores volume information", 229 | " -c option creates an annotated C source file as .c", 230 | "" 231 | }; 232 | int i = 0; 233 | while (usage[i][0] != '\0') 234 | fprintf (stderr, "%s\n", usage[i++]); 235 | } 236 | 237 | int HandleOptions (int argc, char *argv[]) { 238 | /* returns the index of the first argument that is not an option; i.e. 239 | does not start with a dash or a slash */ 240 | 241 | int i, firstnonoption = 0; 242 | 243 | /* --- The following skeleton comes from C:\lcc\lib\wizard\textmode.tpl. */ 244 | for (i = 1; i < argc; i++) { 245 | if (argv[i][0] == '/' || argv[i][0] == '-') { 246 | switch (toupper (argv[i][1])) { 247 | case 'H': 248 | case '?': 249 | SayUsage (argv[0]); 250 | exit (1); 251 | case 'C': 252 | codeoutput = true; 253 | break; 254 | case 'T': 255 | if (sscanf (&argv[i][2], "%d", &num_tonegens) != 1 || num_tonegens < 1 256 | || num_tonegens > MAX_TONEGENS) 257 | goto opterror; 258 | printf ("Displaying %d tone generators.\n", num_tonegens); 259 | break; 260 | case 'V': 261 | expect_volume = true; 262 | if (argv[i][2] == '\0') 263 | break; 264 | if (toupper (argv[i][2]) == 'I') 265 | ignore_volume = true; 266 | else 267 | goto opterror; 268 | break; 269 | 270 | /* add more option switches here */ 271 | opterror: 272 | default: 273 | fprintf (stderr, "unknown option: %s\n", argv[i]); 274 | SayUsage (argv[0]); 275 | exit (4); 276 | } 277 | } else { 278 | firstnonoption = i; 279 | break; 280 | } 281 | } 282 | return firstnonoption; 283 | } 284 | 285 | /*************** portable string length *****************/ 286 | 287 | int strlength (const char *str) { 288 | int i; 289 | for (i = 0; str[i] != '\0'; ++i); 290 | return i; 291 | } 292 | 293 | /*************** safe string copy *****************/ 294 | 295 | unsigned int strlcpy (char *dst, const char *src, unsigned int siz) { 296 | char *d = dst; 297 | const char *s = src; 298 | unsigned int n = siz; 299 | /* Copy as many bytes as will fit */ 300 | if (n != 0) { 301 | while (--n != 0) { 302 | if ((*d++ = *s++) == '\0') 303 | break; 304 | } 305 | } 306 | /* Not enough room in dst, add NUL and traverse rest of src */ 307 | if (n == 0) { 308 | if (siz != 0) 309 | *d = '\0'; /* NUL-terminate dst */ 310 | while (*s++); 311 | } 312 | return (s - src - 1); /* count does not include NUL */ 313 | } 314 | 315 | /*************** safe string concatenation *****************/ 316 | 317 | unsigned int strlcat (char *dst, const char *src, unsigned int siz) { 318 | char *d = dst; 319 | const char *s = src; 320 | unsigned int n = siz; 321 | unsigned int dlen; 322 | /* Find the end of dst and adjust bytes left but don't go past end */ 323 | while (n-- != 0 && *d != '\0') 324 | d++; 325 | dlen = d - dst; 326 | n = siz - dlen; 327 | if (n == 0) 328 | return (dlen + strlength (s)); 329 | while (*s != '\0') { 330 | if (n != 1) { 331 | *d++ = *s; 332 | n--; 333 | } 334 | s++; 335 | } 336 | *d = '\0'; 337 | return (dlen + (s - src)); /* count does not include NUL */ 338 | } 339 | 340 | 341 | /*************** Found a fatal input file format error ************************/ 342 | 343 | void file_error (char *msg, unsigned char *bufptr) { 344 | unsigned char *ptr; 345 | fprintf (stderr, "\n---> file format error at position %04X (%d): %s\n", 346 | (unsigned int) (bufptr - buffer), (unsigned int) (bufptr - buffer), msg); 347 | /* print some bytes surrounding the error */ 348 | ptr = bufptr - 16; 349 | if (ptr < buffer) 350 | ptr = buffer; 351 | for (; ptr <= bufptr + 16 && ptr < buffer + buflen; ++ptr) 352 | fprintf (stderr, ptr == bufptr ? " [%02X] " : "%02X ", *ptr); 353 | fprintf (stderr, "\n"); 354 | exit (8); 355 | } 356 | 357 | /************** Output a line for the current status as we start a delay **************/ 358 | 359 | // show the current time, status of all the tone generators, and the bytestream data that got us here 360 | 361 | void print_status (void) { 362 | int gen; 363 | bool any_instr_changed = false; 364 | for (gen = 0; gen < num_tonegens; ++gen) 365 | any_instr_changed |= gen_instrument_changed[gen]; 366 | if (any_instr_changed) { 367 | if (codeoutput) 368 | fprintf (outfile, "//"); 369 | fprintf (outfile, "%21s", ""); 370 | for (gen = 0; gen < num_tonegens; ++gen) { 371 | if (gen_instrument_changed[gen]) { 372 | gen_instrument_changed[gen] = false; 373 | fprintf (outfile, "%6s", instrumentname[gen_instrument[gen]]); 374 | } else 375 | fprintf (outfile, "%6s", ""); 376 | if (expect_volume && !ignore_volume) 377 | fprintf (outfile, "%5s", ""); 378 | } 379 | fprintf (outfile, "\n"); 380 | } 381 | 382 | if (codeoutput) 383 | fprintf (outfile, "/*"); // start comment 384 | // print the current timestamp 385 | fprintf (outfile, "%5d %7d.%03d ", delay, timenow / 1000, timenow % 1000); 386 | // print the current status of all tone generators 387 | for (gen = 0; gen < num_tonegens; ++gen) { 388 | fprintf (outfile, "%6s", gen_note[gen] == SILENT ? " " : notename[gen_note[gen]]); 389 | if (expect_volume && !ignore_volume) 390 | if (gen_note[gen] == SILENT) 391 | fprintf (outfile, " "); 392 | else 393 | fprintf (outfile, " v%-3d", gen_volume[gen]); 394 | } 395 | // display the hex commands that created these changes 396 | fprintf (outfile, " %04X: ", (unsigned int) (lastbufptr - buffer)); // offset 397 | if (codeoutput) 398 | fprintf (outfile, "*/ "); // end comment 399 | for (; lastbufptr <= bufptr; ++lastbufptr) 400 | fprintf (outfile, codeoutput ? "0x%02X," : "%02X ", *lastbufptr); 401 | fprintf (outfile, "\n"); 402 | lastbufptr = bufptr + 1; 403 | 404 | } 405 | 406 | int countbits (unsigned int bitmap) { 407 | int count; 408 | for (count = 0; bitmap; bitmap >>= 1) 409 | count += bitmap & 1; 410 | return count; 411 | } 412 | 413 | 414 | /********************* main loop ****************************/ 415 | 416 | int main (int argc, char *argv[]) { 417 | int argno, i; 418 | char *filebasename; 419 | #define MAXPATH 80 420 | char filename[MAXPATH]; 421 | unsigned int tonegens_used; // bitmap of tone generators used 422 | unsigned int num_tonegens_used; // count of tone generators used 423 | 424 | printf ("MIDITONES_SCROLL V%s, (C) 2011,2016 Len Shustek\n", VERSION); 425 | if (argc == 1) { /* no arguments */ 426 | SayUsage (argv[0]); 427 | return 1; 428 | } 429 | 430 | /* process options */ 431 | 432 | argno = HandleOptions (argc, argv); 433 | filebasename = argv[argno]; 434 | 435 | /* Open the input file */ 436 | 437 | strlcpy (filename, filebasename, MAXPATH); 438 | strlcat (filename, ".bin", MAXPATH); 439 | infile = fopen (filename, "rb"); 440 | if (!infile) { 441 | fprintf (stderr, "Unable to open input file %s", filename); 442 | return 1; 443 | } 444 | 445 | /* Create the output file */ 446 | 447 | if (codeoutput) { 448 | strlcpy (filename, filebasename, MAXPATH); 449 | strlcat (filename, ".c", MAXPATH); 450 | outfile = fopen (filename, "w"); 451 | if (!infile) { 452 | fprintf (stderr, "Unable to open output file %s", filename); 453 | return 1; 454 | } 455 | } else 456 | outfile = stdout; 457 | 458 | /* Read the whole input file into memory */ 459 | 460 | fseek (infile, 0, SEEK_END); /* find file size */ 461 | buflen = ftell (infile); 462 | fseek (infile, 0, SEEK_SET); 463 | buffer = (unsigned char *) malloc (buflen + 1); 464 | if (!buffer) { 465 | fprintf (stderr, "Unable to allocate %ld bytes for the file", buflen); 466 | return 1; 467 | } 468 | fread (buffer, buflen, 1, infile); 469 | fclose (infile); 470 | printf ("Processing %s.bin, %ld bytes.\n", filebasename, buflen); 471 | if (codeoutput) { 472 | time_t rawtime; 473 | time (&rawtime); 474 | fprintf (outfile, "// Playtune bytestream for file \"%s.bin\"", filebasename); 475 | fprintf (outfile, " created by MIDITONES_SCROLL V%s on %s\n", VERSION, 476 | asctime (localtime (&rawtime))); 477 | fprintf (outfile, "const byte PROGMEM score [] = {\n"); 478 | } 479 | 480 | /* Check for the optional self-describing file header */ 481 | 482 | bufptr = buffer; 483 | { 484 | struct file_hdr_t *hdrptr = (struct file_hdr_t *) buffer; 485 | if (buflen > sizeof (struct file_hdr_t) && hdrptr->id1 == 'P' && hdrptr->id2 == 't') { 486 | printf ("Found Pt self-describing file header with flags %02X %02X, # tone gens = %d\n", 487 | hdrptr->f1, hdrptr->f2, hdrptr->num_tgens); 488 | expect_volume = hdrptr->f1 & HDR_F1_VOLUME_PRESENT; 489 | bufptr += hdrptr->hdr_length; 490 | if (codeoutput) { 491 | fprintf (outfile, "'P','t', 6, 0x%02X, 0x%02X, %2d, // (Playtune file header)\n", 492 | hdrptr->f1, hdrptr->f2, hdrptr->num_tgens); 493 | } 494 | } 495 | } 496 | 497 | /* Do the titles */ 498 | 499 | fprintf (outfile, "\n"); 500 | if (codeoutput) 501 | fprintf (outfile, "//"); 502 | fprintf (outfile, "duration time "); 503 | for (i = 0; i < num_tonegens; ++i) 504 | fprintf (outfile, expect_volume && !ignore_volume ? " gen%-5d" : " gen%-2d", i); 505 | fprintf (outfile, " bytestream code\n\n"); 506 | for (gen = 0; gen < num_tonegens; ++gen) 507 | gen_note[gen] = SILENT; 508 | tonegens_used = 0; 509 | lastbufptr = buffer; 510 | 511 | /* Process the commmands sequentially */ 512 | 513 | for (; bufptr < buffer + buflen; ++bufptr) { 514 | cmd = *bufptr; 515 | if (cmd < 0x80) { /* delay */ 516 | delay = ((unsigned int) cmd << 8) + *++bufptr; 517 | print_status (); // tone generator status now 518 | timenow += delay; // advance time 519 | for (gen = 0; gen < MAX_TONEGENS; ++gen) 520 | gen_did_stopnote[gen] = false; 521 | } else if (cmd != 0xf0) { /* a command */ 522 | gen = cmd & 0x0f; 523 | if (gen > max_tonegen_found) 524 | max_tonegen_found = gen; 525 | cmd = cmd & 0xf0; 526 | if (cmd == 0x90) { /* note on */ 527 | gen_note[gen] = *++bufptr; // note number 528 | tonegens_used |= 1 << gen; // record that we used this generator at least once 529 | if (gen_did_stopnote[gen]) { 530 | ++stopnotes_before_startnote; 531 | // printf("unnecessary stopnote on gen %d\n", gen); 532 | } 533 | if (expect_volume) 534 | gen_volume[gen] = *++bufptr; // volume 535 | if (gen >= num_tonegens) 536 | ++notes_skipped; // won't be displaying this note 537 | } else if (cmd == 0x80) { /* note off */ 538 | if (gen_note[gen] == SILENT) 539 | file_error ("tone generator not on", bufptr); 540 | gen_note[gen] = SILENT; 541 | gen_did_stopnote[gen] = true; 542 | } else if (cmd == 0xc0) { /* change instrument */ 543 | gen_instrument[gen] = *++bufptr & 0x7f; 544 | gen_instrument_changed[gen] = true; 545 | } else 546 | file_error ("unknown command", bufptr); 547 | } 548 | } 549 | 550 | 551 | /* Do the final cleanup */ 552 | 553 | delay = 0; 554 | --bufptr; 555 | if (codeoutput) 556 | --bufptr; //don't do 0xf0 for code, because we don't want the trailing comma 557 | print_status (); // print final status 558 | if (codeoutput) { 559 | fprintf (outfile, " 0xf0};\n"); 560 | num_tonegens_used = countbits (tonegens_used); 561 | fprintf (outfile, "// This score contains %ld bytes, and %d tone generator%s used.\n", 562 | buflen, num_tonegens_used, num_tonegens_used == 1 ? " is" : "s are"); 563 | } else 564 | fprintf (outfile, "\n"); 565 | printf ("At most %u tone generators were used.\n", max_tonegen_found + 1); 566 | if (notes_skipped) 567 | printf ("%u notes were not displayed because we were told to show only %u generators.\n", 568 | notes_skipped, num_tonegens); 569 | printf("%u stopnote commands were unnecessary.\n", stopnotes_before_startnote); 570 | printf ("Done.\n"); 571 | } 572 | -------------------------------------------------------------------------------- /miditones_scroll_32bit.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLXXXp/midi2tones/510e928e22f38f27579a01c5919f2e6971c0fff2/miditones_scroll_32bit.exe -------------------------------------------------------------------------------- /miditones_scroll_64bit.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MLXXXp/midi2tones/510e928e22f38f27579a01c5919f2e6971c0fff2/miditones_scroll_64bit.exe --------------------------------------------------------------------------------