├── testcases ├── theme.imf ├── theme.mid ├── c7-019.mid ├── c7-019.wlf ├── mdadoda.imf ├── mdadoda.mid ├── salute.mid ├── salute.wlf ├── stunts.mid ├── stunts.raw ├── bachbash.imf ├── bachbash.mid ├── hesbacka.imf ├── hesbacka.mid ├── kilo-song_4.dro ├── kilo-song_4.mid ├── kilo-song_5.dro ├── kilo-song_5.mid ├── full128gm_sbplay.dro ├── full128gm_sbplay.mid └── sango_fighter_title_drov2.dro ├── Makefile ├── make ├── freq.hpp ├── misc.txt ├── .github └── workflows │ └── build_win.yml ├── verify.sh ├── drum.txt ├── gen_test_midi.cpp ├── freq.cpp ├── droshrink.c ├── patch.txt ├── README ├── midiio.hpp ├── inst.txt ├── midiio.cpp └── dro2midi.cpp /testcases/theme.imf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/theme.imf -------------------------------------------------------------------------------- /testcases/theme.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/theme.mid -------------------------------------------------------------------------------- /testcases/c7-019.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/c7-019.mid -------------------------------------------------------------------------------- /testcases/c7-019.wlf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/c7-019.wlf -------------------------------------------------------------------------------- /testcases/mdadoda.imf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/mdadoda.imf -------------------------------------------------------------------------------- /testcases/mdadoda.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/mdadoda.mid -------------------------------------------------------------------------------- /testcases/salute.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/salute.mid -------------------------------------------------------------------------------- /testcases/salute.wlf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/salute.wlf -------------------------------------------------------------------------------- /testcases/stunts.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/stunts.mid -------------------------------------------------------------------------------- /testcases/stunts.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/stunts.raw -------------------------------------------------------------------------------- /testcases/bachbash.imf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/bachbash.imf -------------------------------------------------------------------------------- /testcases/bachbash.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/bachbash.mid -------------------------------------------------------------------------------- /testcases/hesbacka.imf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/hesbacka.imf -------------------------------------------------------------------------------- /testcases/hesbacka.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/hesbacka.mid -------------------------------------------------------------------------------- /testcases/kilo-song_4.dro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/kilo-song_4.dro -------------------------------------------------------------------------------- /testcases/kilo-song_4.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/kilo-song_4.mid -------------------------------------------------------------------------------- /testcases/kilo-song_5.dro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/kilo-song_5.dro -------------------------------------------------------------------------------- /testcases/kilo-song_5.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/kilo-song_5.mid -------------------------------------------------------------------------------- /testcases/full128gm_sbplay.dro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/full128gm_sbplay.dro -------------------------------------------------------------------------------- /testcases/full128gm_sbplay.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/full128gm_sbplay.mid -------------------------------------------------------------------------------- /testcases/sango_fighter_title_drov2.dro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malvineous/dro2midi/HEAD/testcases/sango_fighter_title_drov2.dro -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OBJS = dro2midi.o midiio.o 2 | PROGS = dro2midi droshrink 3 | 4 | -include config.mak 5 | 6 | all: $(PROGS) 7 | 8 | dro2midi: $(OBJS) 9 | $(CXX) -o $@ $^ $(LDFLAGS) 10 | 11 | droshrink: droshrink.c 12 | $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) 13 | 14 | clean: 15 | rm -f $(PROGS) $(OBJS) 16 | 17 | .PHONY: all clean 18 | -------------------------------------------------------------------------------- /make: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Usage: 4 | # 5 | # ./make 6 | # 7 | # or if you have a cross compiler for i586-pc-mingw32 installed 8 | # 9 | # ./make i586-pc-mingw32 .exe 10 | # 11 | 12 | if [ "$1" != "" ]; then 13 | PLATFORM="$1"- 14 | TARGET="dro2midi$2" 15 | else 16 | PLATFORM="" 17 | TARGET="dro2midi" 18 | fi 19 | 20 | ${PLATFORM}g++ -o ${TARGET} dro2midi.cpp midiio.cpp && 21 | ${PLATFORM}strip ${TARGET} 22 | -------------------------------------------------------------------------------- /freq.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef _MSC_VER 4 | // Keep MS VC++ happy 5 | inline double log2(double a) 6 | { 7 | return log(a) / log(2.0); 8 | } 9 | #endif 10 | 11 | double freq2key(int freq, int octave); 12 | /* 13 | 14 | extern int freqlist[]; 15 | 16 | int freq2key(int freq, int octave); 17 | int key2freq(int key, int &freq, int &octave); 18 | int nearestfreq(int freq); 19 | int relfreq(int freq, int halfnotes); 20 | */ 21 | -------------------------------------------------------------------------------- /misc.txt: -------------------------------------------------------------------------------- 1 | Some notes 2 | ---------- 3 | 4 | To convert all the i*.reg instrument files into the new format, do this (under 5 | Linux) 6 | 7 | for I in i*.reg; do echo `fromdos < $I` | awk '{print $4 "-" $5 "/" $7 "-" $8 "/" $10 "-" $11 "/" $13 "-" $14 "/" $16 "/" $18 "-" $19 ": patch=" $2+1}'; done >> inst.txt 8 | 9 | I already did this for the default files, so you'll only need to do it if you've 10 | amassed a large collection of .reg files. Alternatively if you don't use Linux 11 | feel free to e-mail the .reg files to me and I'll convert them for you (and add 12 | them into the distribution for future releases.) 13 | -------------------------------------------------------------------------------- /.github/workflows/build_win.yml: -------------------------------------------------------------------------------- 1 | name: Dro2midi Windows build 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build-sln: 9 | runs-on: windows-2019 10 | 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: ilammy/msvc-dev-cmd@v1 14 | 15 | 16 | - name: Setup MSBuild.exe 17 | uses: microsoft/setup-msbuild@v1.1 18 | with: 19 | msbuild-architecture: x64 20 | vs-version: 16.1 21 | 22 | - name: Build solution 23 | run: | 24 | SET CL=-Wno-c++11-narrowing 25 | cmd /c build.bat 26 | 27 | - name: Upload artifact 28 | uses: actions/upload-artifact@v2 29 | with: 30 | name: dro2midi-win-x64 31 | path: dro2midi.exe 32 | 33 | -------------------------------------------------------------------------------- /verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | tmp_midi="/tmp/verify.mid.$$" 4 | tmp_logf="/tmp/verify.log.$$" 5 | 6 | trap 'rm -f "${tmp_midi}" "${tmp_logf}"' 0 7 | 8 | for midi_file in ./testcases/*.mid 9 | do 10 | for format in raw dro imf wlf 11 | do 12 | bfname="$(basename "${midi_file}" ".mid")" 13 | ifname="./testcases/${bfname}.${format}" 14 | 15 | [ -r "${ifname}" ] || continue 16 | 17 | printf "Verifying:\t%-48s" "${ifname}" 18 | 19 | wine ./dro2midi.exe "${ifname}" "${tmp_midi}" > "${tmp_logf}" 2>&1 20 | 21 | if [ -r "${tmp_midi}" ] && cmp -s "${tmp_midi}" "${midi_file}" 22 | then 23 | echo " [OK]" 24 | else 25 | echo " [FAILED]" 26 | echo 27 | cat "${tmp_logf}" 28 | echo 29 | echo "Converting ${ifname} into ${format} format failed." 30 | exit 31 | fi 32 | done 33 | done 34 | -------------------------------------------------------------------------------- /drum.txt: -------------------------------------------------------------------------------- 1 | 35=Acoustic Bass Drum 2 | 36=Bass Drum 1 3 | 37=Side Stick 4 | 38=Acoustic Snare 5 | 39=Hand Clap 6 | 40=Electric Snare 7 | 41=Low Floor Tom 8 | 42=Closed Hi Hat 9 | 43=High Floor Tom 10 | 44=Pedal Hi-Hat 11 | 45=Low Tom 12 | 46=Open Hi-Hat 13 | 47=Low-Mid Tom 14 | 48=Hi-Mid Tom 15 | 49=Crash Cymbal 1 16 | 50=High Tom 17 | 51=Ride Cymbal 1 18 | 52=Chinese Cymbal 19 | 53=Ride Bell 20 | 54=Tambourine 21 | 55=Splash Cymbal 22 | 56=Cowbell 23 | 57=Crash Cymbal 2 24 | 58=Vibraslap 25 | 59=Ride Cymbal 2 26 | 60=Hi Bongo 27 | 61=Low Bongo 28 | 62=Mute Hi Conga 29 | 63=Open Hi Conga 30 | 64=Low Conga 31 | 65=High Timbale 32 | 66=Low Timbale 33 | 67=High Agogo 34 | 68=Low Agogo 35 | 69=Cabasa 36 | 70=Maracas 37 | 71=Short Whistle 38 | 72=Long Whistle 39 | 73=Short Guiro 40 | 74=Long Guiro 41 | 75=Claves 42 | 76=Hi Wood Block 43 | 77=Low Wood Block 44 | 78=Mute Cuica 45 | 79=Open Cuica 46 | 80=Mute Triangle 47 | 81=Open Triangle 48 | -------------------------------------------------------------------------------- /gen_test_midi.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // gen_test_midi.cpp - by malvineous@shikadi.net 3 | // 4 | // Generate a MIDI file that plays one note from all 128 GM instruments (in 5 | // order), followed by all the percussion notes. 6 | // 7 | // When this file is played through an OPL synthesiser capable of MIDI 8 | // playback, then captured through DOSBox, it will provide a complete set of 9 | // OPL patches that can easily be associated back to MIDI instruments. 10 | // 11 | 12 | #include "midiio.hpp" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define WRITE_BINARY "wb" 21 | #define READ_TEXT "r" 22 | 23 | int main(int argc, char**argv) 24 | { 25 | if (argc != 2) { 26 | fprintf(stderr, "no output filename given\n"); 27 | return 1; 28 | } 29 | 30 | MidiWrite *write = new MidiWrite(argv[1]); 31 | if (!write) { 32 | fprintf(stderr, "out of memory\n"); 33 | return 1; 34 | } 35 | if (!write->getf()) { 36 | perror(argv[1]); 37 | return 1; 38 | } 39 | 40 | float tempo = 120.0; 41 | int resolution = 280; 42 | write->head(/* version */ 0, /* track count updated later */0, resolution); 43 | 44 | write->track(); 45 | write->tempo((long)(60000000.0 / tempo)); 46 | write->tact(4,4,24,8); 47 | 48 | write->volume(1, 127); 49 | write->volume(9, 127); 50 | for (int i = 0; i < 128; i++) { 51 | write->program(1, i); 52 | write->noteon(1, 60, 127); 53 | write->time(50); 54 | write->noteoff(1, 60); 55 | write->time(50); 56 | } 57 | /* 58 | // Need to play a normal note for percussion to get DOSBox to start capturing 59 | // OPL data! (Why hasn't my patch to fix that been incorporated yet?!) 60 | write->noteon(1, 60, 127); 61 | write->time(50); 62 | write->noteoff(1, 60); 63 | write->time(200); 64 | for (int i = 35; i < 82; i++) { 65 | write->noteon(9, i, 127); 66 | write->time(50); 67 | write->noteoff(9, i); 68 | write->time(50); 69 | }*/ 70 | 71 | delete write; 72 | 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /freq.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "freq.hpp" 4 | 5 | double freq2key(int freq, int octave) 6 | { 7 | /* octave *= 12; 8 | if (freq == 0) 9 | return 0; 10 | if (searchfreq(freq)) 11 | return octave + besti; 12 | else 13 | return 0;*/ 14 | int iFNum = freq; 15 | int iBlock = octave; 16 | double dbOriginalFreq = 49716.0 * (double)iFNum * pow(2, (double)(iBlock - 20)); 17 | return 69.0 + 12.0 * log2(dbOriginalFreq / 440.0); 18 | } 19 | /* 20 | int freqlist[] = 21 | { 22 | 0x159, // C 24 23 | 0x16B, // C# 25 24 | 0x181, // D 26 25 | 0x198, // D# 27 26 | 0x1B0, // E 28 27 | 0x1CA, // F 29 28 | 0x1E5, // F# 30 29 | 0x202, // G 31 30 | 0x220, // G# 32 31 | 0x241, // A 33 32 | 0x263, // A# 34 33 | 0x287, // B 35 34 | 0x2AE, // C' 36 35 | 0x2DB, // C#' 37 36 | 0x306, // D' 38 37 | 0x334, // D#' 39 38 | 0x365, // E' 40 39 | 0x399, // F' 41 40 | 0x3CF, // F#' 42 41 | 0x3FE, // G' 43 42 | 0 43 | }; 44 | 45 | #define DIST(a, b) ((a > b) ? (a - b) : (b - a)) 46 | 47 | int besti = -1; 48 | 49 | int searchfreq(int freq) 50 | { 51 | besti = -1; 52 | int bestdist = 0; 53 | for (int i = 0; freqlist[i]; i++) 54 | { 55 | int dist = DIST(freq, freqlist[i]); 56 | if (besti < 0 || dist < bestdist) 57 | { 58 | besti = i; 59 | bestdist = dist; 60 | } 61 | } 62 | return besti >= 0; 63 | } 64 | 65 | int nearestfreq(int freq) 66 | { 67 | if (freq == 0) 68 | return -1; 69 | 70 | if (searchfreq(freq)) 71 | return freqlist[besti]; 72 | else 73 | return -1; 74 | } 75 | 76 | int relfreq(int freq, int halfnotes) 77 | { 78 | if (!searchfreq(freq)) 79 | return -1; 80 | int dir = (halfnotes > 0) ? +1 : -1; 81 | int i; 82 | for (i = besti; i >= 0 && freqlist[i] && halfnotes != 0; halfnotes -= dir, i += dir) 83 | ; 84 | if (halfnotes == 0) 85 | return freqlist[i]; 86 | return -1; 87 | } 88 | 89 | int key2freq(int key, int &freq, int &octave) 90 | { 91 | octave = key / 12 - 3; 92 | freq = freqlist[key % 12]; 93 | 94 | return 1; 95 | } 96 | */ 97 | -------------------------------------------------------------------------------- /droshrink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static uint32_t readUINT32LE(FILE *f) 9 | { 10 | unsigned char c[4]; 11 | fread(&c, 1, 4, f); 12 | return c[0] | (c[1] << 8L) | (c[2] << 16L) | (c[3] << 24L); 13 | } 14 | 15 | static int usage() { 16 | printf( 17 | "usage: droshrink N in.dro out.dro\n\n" 18 | "N: number of command pairs to be removed from end of file.\n" 19 | ); 20 | return 1; 21 | } 22 | 23 | int main(int argc, char**argv) { 24 | if(argc != 4) return usage(); 25 | 26 | unsigned n = atoi(argv[1]); 27 | 28 | FILE *in = fopen(argv[2], "r"); 29 | FILE *out = fopen(argv[3], "w"); 30 | 31 | if(!in || !out) { 32 | perror("fopen"); 33 | return 1; 34 | } 35 | 36 | unsigned char cSig[9]; 37 | cSig[8] = 0; 38 | fread(cSig, 1, 8, in); 39 | assert(!strcmp(cSig, "DBRAWOPL")); 40 | assert(readUINT32LE(in) == 2); 41 | 42 | fwrite(cSig, 1, 8, out); 43 | fwrite("\2\0\0\0", 1, 4, out); 44 | 45 | struct dro2hdr { 46 | uint32_t iLengthPairs; 47 | uint32_t iLengthMS; 48 | uint8_t iHardwareType; 49 | uint8_t iFormat; 50 | uint8_t iCompression; 51 | uint8_t iShortDelayCode; 52 | uint8_t iLongDelayCode; 53 | uint8_t iCodemapLength; 54 | uint8_t iCodemap[128]; 55 | } dro2hdr = {0}, dro2hdr_out; 56 | dro2hdr.iLengthPairs = readUINT32LE(in); 57 | dro2hdr.iLengthMS = readUINT32LE(in); 58 | fread(&dro2hdr.iHardwareType, 1, 6, in); 59 | if(dro2hdr.iCodemapLength >= 128) { 60 | fprintf(stderr, "invalid setting %u for iCodemapLength!\n", (unsigned) dro2hdr.iCodemapLength); 61 | return 2; 62 | } 63 | fread(dro2hdr.iCodemap, 1, dro2hdr.iCodemapLength, in); 64 | dro2hdr_out = dro2hdr; 65 | dro2hdr_out.iLengthPairs -= n; 66 | /* FIXME: not big-endian safe */ 67 | fwrite(&dro2hdr_out, 1, offsetof(struct dro2hdr, iCodemap), out); 68 | fwrite(dro2hdr_out.iCodemap, 1, dro2hdr.iCodemapLength, out); 69 | unsigned i; 70 | for(i=0; idro2midi file.dro file.mid 39 | 40 | For a list of the command line options run dro2midi with no parameters or 41 | see below for more details. For best results, all the .txt data files should 42 | be in the current directory. 43 | 44 | Instrument mappings between Adlib registers and MIDI instruments are stored in 45 | inst.txt. This file contains a number of existing mappings, but additional 46 | mappings can easily be added. During conversion, if an exact match cannot 47 | be found the mapping with the closest Adlib parameters will be used instead. 48 | A message will be printed when this happens, along with a line that can be 49 | copied into inst.txt to provide an exact match. This approximation can have 50 | the unfortunate side effect of providing some odd conversions, such as 51 | converting a bass-line into a monotonic drum. 52 | 53 | To get a perfect conversion you may wish to delete all but the first 54 | "all-zero" instrument in inst.txt, which will cause all instruments to be 55 | converted as a piano. You can then copy the definitions printed during 56 | conversion one by one into inst.txt, to assign the best-sounding instrument 57 | without worring about any default mappings taking over. Alternatively the -i 58 | option can be used which will disable the closest-match algorithm, and only 59 | exact matches will be used (again, anything that can't be exactly matched will 60 | be mapped as a piano.) 61 | 62 | // Command-line options 63 | ///////////////////////// 64 | 65 | -p disables generation of MIDI pitchbends. This results in a single note-on 66 | when the instrument sounds, but no further pitch change results until the 67 | note is switched off again. This can also be used to prevent the large number 68 | of small pitchbends generated when the conversion constant is slightly off (but 69 | see the -c option, which now provides a better way around this.) 70 | 71 | -a will, if pitchbends are disabled with -p, approximate any pitchbend by 72 | playing the nearest note to the new pitch at the time. This results in a 73 | "hammering" of notes during a pitchbend, which while humourous, is probably 74 | of limited use. 75 | 76 | -r disables the conversion of OPL rhythm mode instruments. As OPL rhythm mode 77 | conversion is now quite flexible, this option should rarely be needed. 78 | 79 | -i will disable the approximation algorithm which selects similar instruments 80 | when an exact match cannot be found in the mapping file (insts.txt). This is 81 | useful when trying to create a perfect map for a single song, as it makes it 82 | easier to pick out which instruments are being mapped. This option should not 83 | be used for the final conversion however, as any instruments that haven't been 84 | precisely mapped will come out (by default) as a grand piano. (This default 85 | mapping is simply the first entry in insts.txt.) 86 | 87 | -c changes the conversion constant used when converting OPL notes into their 88 | MIDI equivalents. This is the heart of what DRO2MIDI does. Unfortunately 89 | depending on which set of documents are available, those writing OPL *players* 90 | are told the conversion constant is either 49716, or 50000. Thus half the 91 | games out there use one, and half use the other. The result of using the wrong 92 | constant in the DRO2MIDI conversion is a tiny difference in pitch when the song 93 | is played (about 1/17th of a cent.) Most people will be unable to hear the 94 | difference between these two values, however if the wrong constant is used by 95 | DRO2MIDI during the conversion into MIDI, the resulting file will contain 96 | thousands of small pitchbend events, as it tries to approximate the exact OPL 97 | note played. Using -c to try a different constant should solve this problem. 98 | The default constant is 49716, and "-c alt" will change the constant to 50000. 99 | It is possible to specify an arbitrary constant like "-c 49999" however this 100 | should be unnecessary unless the same nonstandard constant was used wherever 101 | the song was originally played. Note that no error checking is performed here, 102 | so out of range values could easily crash the program (not that that's a major 103 | problem though...) After conversion the number of pitchbend events is 104 | displayed at the end of the output, so it will be obvious when the correct 105 | constant is in use as this number will be significantly smaller than with 106 | any other constant. 107 | 108 | -v disables the volume detection. Normally DRO2MIDI will take the OPL 109 | carrier's "Level" amount and translate it to a MIDI note velocity. This will 110 | result in the output MIDI file more accurately matching the loud and quiet 111 | parts of the OPL song. Some songs (e.g. Stunts) somehow manage to work with 112 | these volume levels set to zero, which results in very quiet MIDI files (there 113 | is an internal limit as to how quiet a note can sound to prevent it being lost 114 | entirely.) If your output MIDI file is much too quiet, this option will cause 115 | all notes to be played at maximum velocity. 116 | 117 | -s instructs dro2midi to write all detected instruments to a .sbi file. This 118 | is a 52 byte binary instrument format for OPL chips created by Creative Labs. 119 | It is supported by applications written to work with the OPL, such as Ad Lib 120 | Tracker 2. 121 | 122 | // inst.txt 123 | ///////////// 124 | 125 | The instrument mappings are stored in inst.txt, in a format like this: 126 | 127 | NO 07-12/4F-00/F2-F2/60-72/08/00-00: patch=15 # Tubular bells 128 | 129 | These lines are printed automatically when an unknown instrument is encounted. 130 | All you will need to do is copy and paste the line into insts.txt and choose 131 | a MIDI instrument for it. The file itself is read in from the current 132 | directory during conversion, so if you run DRO2MIDI in another folder remember 133 | to copy the file across too or your mappings won't be used. 134 | 135 | The first two characters indicate what type of instrument it is. The 136 | hexadecimal numbers that follow are the Adlib register values for that 137 | instrument. "patch=15" assigns MIDI instrument #15 for this Adlib instrument. 138 | For percussion, "drum=35" could be used instead. Anything after a # symbol is 139 | treated as a comment. See the comments at the top of the file for more 140 | detailed information. 141 | 142 | The instrument names (and values to supply to the patch= parameter) are stored 143 | in patch.txt, and the drum names (and numbers) in drum.txt. You may find 144 | these files helpful to reference when selecting instruments for conversion. 145 | (These two files are read in from the current directory during conversion to 146 | allow the display of instrument names in status messages instead of just 147 | numbers.) 148 | 149 | Note that the parser for insts.txt file is quick and dirty, so it's easy to 150 | get a syntax error - for example, an otherwise blank line with a single space 151 | on it will cause an error (so if you get an error about a blank line, make 152 | sure it really is blank!) 153 | 154 | // License 155 | //////////// 156 | 157 | DRO2MIDI was based on IMF2MIDI by Guenter Nagler. DRO2MIDI is released under 158 | the GPL license, except where it is incompatible with IMF2MIDI's original 159 | license, in which case IMF2MIDI's license takes precedence. 160 | 161 | ---- Begin IMF2MIDI license ---- 162 | 163 | IMF2MIDI (c) 1996 was created by Guenter Nagler. 164 | 165 | IMF2MIDI is free and may be used as you wish with this one exception: 166 | 167 | You may NOT charge any fee or derive any profit for distribution 168 | of IMF2MIDI. Thus, you may NOT sell or bundle IMF2MIDI with any 169 | product in a retail environment (shareware disk distribution, CD-ROM, 170 | etc.) without permission of the author. 171 | 172 | You may give IMF2MIDI to your friends, upload it to a BBS, or ftp it to 173 | another internet site, as long as you don't charge anything for it. 174 | 175 | ---- End IMF2MIDI license ---- 176 | 177 | // Contact 178 | //////////// 179 | 180 | Source code is available at http://www.shikadi.net/utils/ 181 | 182 | You can e-mail me at malvineous@shikadi.net 183 | -------------------------------------------------------------------------------- /midiio.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __MIDIIO__ 2 | #define __MIDIIO__ 3 | 4 | #include 5 | 6 | #ifndef MIDI_BUFSIZE 7 | #define MIDI_BUFSIZE 1024 8 | #endif 9 | 10 | // opening modes of a midi file 11 | #define WRITE_BINARY "wb" 12 | #define READ_BINARY "rb" 13 | 14 | const unsigned long MThd = 0x4D546864ul; 15 | const unsigned long MTrk = 0x4D54726Bul; 16 | 17 | // different standard midi formats 18 | #define VERSION_MULTICHANNEL 0 19 | #define VERSION_SINGLECHANNEL 1 20 | #define VERSION_MULTISONG 2 21 | 22 | #define OPTION_NOCONTROLS 1 // no control details but general information 23 | #define OPTION_NOEVENTS 2 // no track events at all 24 | #define OPTION_NOMETAEVENTS 4 // no meta details but general information 25 | #define OPTION_NOSYSEVENTS 8 // no sysex details but general information 26 | #define OPTION_NONOTEEVENTS 16 // no note events (8x or 9x) 27 | #define OPTION_NOPOLYEVENTS 32 // no poly aftertouch events (Ax) 28 | #define OPTION_NOCONTROLEVENTS 64 // no control events at all (Bx) 29 | #define OPTION_NOPROGRAMEVENTS 128 // no program change events (Cx) 30 | #define OPTION_NOAFTERTOUCHEVENTS 256 // no aftertouch events (Dx) 31 | #define OPTION_NOPITCHBENDEVENTS 512 // no pitchbend events (Ex) 32 | #define OPTION_NOREALTIMEEVENTS 1024 // no realtime events (Fx) 33 | 34 | // getchannel delivers a valid channel or: 35 | #define NOCHANNEL (-1) 36 | #define MULTICHANNEL (-2) 37 | #define VALIDCHANNEL(ch) ((ch) >= 0 && (ch) <= 15) 38 | #define gm_drumchannel 9 39 | #define SAYCHANNEL(ch) ((ch) + 1) // 0..15 in midi format but spoken 1..16! 40 | 41 | // for use of param what in function text() 42 | #define meta_text 1 43 | #define meta_copyright 2 44 | #define meta_trackname 3 45 | #define meta_instrument 4 46 | #define meta_lyric 5 47 | #define meta_marker 6 48 | #define meta_cuepoint 7 49 | #define meta_endtrack 0x2f 50 | 51 | #define ctrl_highbank 0 52 | #define ctrl_wheel 1 53 | #define ctrl_breath 2 54 | #define ctrl_foot 4 55 | #define ctrl_portamentotime 5 56 | #define ctrl_data 6 57 | #define ctrl_volume 7 58 | #define ctrl_balance 10 59 | #define ctrl_expression 11 60 | #define ctrl_lowbank 32 61 | #define ctrl_hold 64 62 | #define ctrl_reverb 91 63 | #define ctrl_chorus 93 64 | #define ctrl_datainc 96 65 | #define ctrl_datadec 97 66 | #define ctrl_lowrpn 100 67 | #define ctrl_highrpn 101 68 | #define ctrl_resetctrlrs 121 69 | #define ctrl_allnotesoff 123 70 | 71 | #define balance_left 0 72 | #define balance_center 64 73 | #define balance_right 127 74 | 75 | #define volume_mute 0 76 | #define volume_full 127 77 | 78 | #define bpm(ticks) (60000000.0 / (ticks)) 79 | #define ticks(beats) ((unsigned long)(60000000.0 / (beats))) 80 | 81 | #define tempo_60bpm (1000000L) 82 | #define tempo_120bpm (500000L) 83 | #define tempo_240bpm (250000L) 84 | 85 | 86 | class MidiRead 87 | { 88 | public: 89 | static const char* copyright(); 90 | 91 | MidiRead(const char* filename, FILE* f = 0); 92 | virtual ~MidiRead(); 93 | 94 | FILE* getf(); 95 | 96 | int run(); 97 | int runhead(); 98 | int runtrack(int trackno); 99 | int runevent(long trackend); 100 | 101 | long getpos() { return pos_; } 102 | long geteventpos() { return pos_; } 103 | long getcurpos() { return curpos_; } 104 | unsigned long getcurtime() { return curtime_; } // in midi units 105 | 106 | virtual void head(unsigned version, unsigned tracks, unsigned clicksperquarter); 107 | virtual void track(int trackno, long length, int channel); 108 | virtual void endtrack(int trackno); // closing track 109 | 110 | virtual void time(unsigned long ticks); // delay between events 111 | 112 | virtual void event(int what, int len = 0, unsigned char* data = 0); 113 | 114 | // these are event categories: 115 | virtual void meta(int what, int len, const unsigned char* data); 116 | // these are special meta events: 117 | virtual void text(int what, int len, const char* whattext, const unsigned char* txt); 118 | virtual void end(); // end of track command 119 | virtual void prefixchannel(unsigned char channel); 120 | virtual void prefixport(unsigned char port); 121 | virtual void seqnumber(unsigned int seqno); 122 | virtual void smpteofs(int hour, int min, int sec, int frame, int fracframe); 123 | virtual void tact(int nom, int denom, int clicksperbeat, int notes32perbeat); 124 | virtual void tempo(unsigned long microsecperbeat); 125 | virtual void key(int signature, int isminor); // C=0, -7=7flats +7=7sharps 126 | virtual void program(int channel, int prg); 127 | virtual void control(int channel, int what, int val); // general controls 128 | // special controls: 129 | virtual void highbank(int channel, int val); 130 | virtual void wheel(int channel, int val); 131 | virtual void breath(int channel, int val); 132 | virtual void foot(int channel, int val); 133 | virtual void portamentotime(int channel, int val); 134 | virtual void data(int channel, int val); 135 | virtual void volume(int channel, int val); 136 | virtual void balance(int channel, int val); 137 | virtual void expression(int channel, int val); 138 | virtual void lowbank(int channel, int val); 139 | virtual void hold(int channel, int val); 140 | virtual void reverb(int channel, int val); 141 | virtual void chorus(int channel, int val); 142 | virtual void datainc(int channel, int val); 143 | virtual void datadec(int channel, int val); 144 | virtual void lowrpn(int channel, int val); 145 | virtual void highrpn(int channel, int val); 146 | virtual void resetctrlrs(int channel, int val); 147 | virtual void allnotesoff(int channel, int val); 148 | virtual void pitchbendrange(int channel, int val); 149 | virtual void noteon(int channel, int note, int vel); 150 | virtual void noteoff(int channel, int note, int vel); 151 | virtual void pitchbend(int channel, int val); 152 | virtual void polyaftertouch(int channel, int note, int val); 153 | virtual void aftertouch(int channel, int val); 154 | virtual void songpos(unsigned pos); 155 | virtual void songselect(unsigned char song); 156 | virtual void tunerequest(); 157 | virtual void timingclock(); 158 | virtual void start(); 159 | virtual void cont(); 160 | virtual void stop(); 161 | virtual void activesense(); 162 | virtual void sysex(int syslen, const unsigned char* sysdata); 163 | // these are special sysex events: 164 | virtual void xgreset(); 165 | virtual void gmreset(); 166 | virtual void gsreset(); 167 | virtual void gsexit(); 168 | 169 | virtual void endmidi(); // after last track 170 | virtual void error(const char* msg); 171 | virtual void warning(const char* msg); 172 | 173 | virtual void percent(int perc); 174 | 175 | int getchannel() { return curchannel_; } 176 | void setchannel(int channel); 177 | 178 | static const char* progname(int n, int channel); 179 | static const char* notename(unsigned char note); 180 | 181 | int options_; 182 | 183 | int version_, tracks_, clicks_, trackno_; 184 | 185 | void seek(long pos); 186 | int getbyte(); 187 | unsigned getword(); 188 | unsigned long gettri(); 189 | unsigned long getlong(); 190 | unsigned long getdelta(); 191 | unsigned char* get(int len); 192 | unsigned char* need(int nbytes); 193 | 194 | unsigned long microsec(unsigned long units, unsigned long msperbeats); 195 | long units(unsigned long microsec, unsigned long msperbeats); 196 | 197 | // use scanchannel only at start of track! 198 | int scanchannel(unsigned long maxlen); // channel 0-15 or -1=no channel or -2=multichannels 199 | 200 | // use sysevent only directly after reading F0 or F7 201 | int scansysevent(unsigned long maxlen); 202 | 203 | protected: 204 | const char *midiname_; 205 | FILE* f_; 206 | unsigned char shouldclose_; // 0=no, otherwise=yes 207 | long filesize_; 208 | unsigned char buf_[MIDI_BUFSIZE]; 209 | int buflen_, bufpos_; 210 | int curchannel_; 211 | unsigned long curtime_; 212 | int percent_; 213 | int lastcode_; 214 | unsigned long tracklen_; 215 | 216 | long pos_, curpos_; 217 | unsigned char curdeltalen_; // number of bytes read by recent getdelta() call 218 | }; 219 | 220 | class MidiWrite 221 | { 222 | public: 223 | static const char* copyright(); 224 | 225 | MidiWrite(const char* filename); 226 | virtual ~MidiWrite(); 227 | 228 | FILE* getf(); 229 | 230 | long getcurpos() { return curpos_; } 231 | long getcurtime() { return curtime_; } 232 | void cleardelta(); 233 | 234 | void head(int version = 1, int tracks = 0, unsigned clicksperquarter = 192); 235 | void track(); 236 | void endtrack(); 237 | 238 | void event(int what, int len, const unsigned char* data); 239 | 240 | void text(int what, int len, const unsigned char* txt); 241 | void meta(int what, int len, const unsigned char* data); // 0xff .... 242 | virtual void prefixchannel(unsigned char channel); 243 | virtual void prefixport(unsigned char port); 244 | virtual void seqnumber(unsigned int seqno); 245 | virtual void smpteofs(int hour, int min, int sec, int frame, int fracframe); 246 | virtual void key(int signature, int isminor); 247 | void tact(int nom, int denom, int v1, int v2); 248 | void tempo(unsigned long microsecperbeat); 249 | void end(); 250 | void program(int channel, int prg); 251 | void control(int channel, int what, int val); 252 | // these are special controls 253 | void highbank(int channel, int val); 254 | void wheel(int channel, int val); 255 | void breath(int channel, int val); 256 | void foot(int channel, int val); 257 | void portamentotime(int channel, int val); 258 | void data(int channel, int val); 259 | void volume(int channel, int val); 260 | void balance(int channel, int val); 261 | void expression(int channel, int val); 262 | void lowbank(int channel, int val); 263 | void hold(int channel, int val); 264 | void reverb(int channel, int val); 265 | void chorus(int channel, int val); 266 | void datainc(int channel, int val); 267 | void datadec(int channel, int val); 268 | void lowrpn(int channel, int val); 269 | void highrpn(int channel, int val); 270 | void resetctrlrs(int channel, int val); 271 | void allnotesoff(int channel, int val); 272 | void pitchbendrange(int channel, int halfnotes); 273 | 274 | void noteon(int channel, int note, int vel); 275 | void noteoff(int channel, int note, int vel = 0); 276 | void time(unsigned long ticks); 277 | void pitchbend(int channel, int val); 278 | void polyaftertouch(int channel, int note, int val); 279 | void aftertouch(int channel, int val); 280 | void songpos(unsigned pos); 281 | void songselect(unsigned char song); 282 | void tunerequest(); 283 | void timingclock(); 284 | void start(); 285 | void cont(); 286 | void stop(); 287 | void activesense(); 288 | void sysex(int syslen, const unsigned char* sysdata); 289 | void xgreset(); 290 | void gmreset(); 291 | void gsreset(); 292 | void gsexit(); 293 | 294 | void putbyte(unsigned char val); 295 | void putcode(unsigned char code); 296 | void putword(unsigned val); 297 | void puttri(unsigned long val); 298 | void putlong(unsigned long val); 299 | void putdelta(unsigned long val); 300 | void puttime(); 301 | void put(int len, const unsigned char* c); 302 | void seek(long pos); 303 | 304 | virtual void error(const char* msg); 305 | virtual void warning(const char* msg); 306 | 307 | int unitsperquarter(); 308 | 309 | protected: 310 | const char *midiname_; 311 | FILE* f_; 312 | long trackpos_, curpos_, filesize_; 313 | int trackchannel_, trackcount_, lastcode_, endtrack_; 314 | 315 | unsigned char buf_[MIDI_BUFSIZE]; 316 | int bufpos_, buflen_; 317 | 318 | unsigned long curdelta_; 319 | unsigned long curtime_; 320 | 321 | int clicks_; 322 | 323 | void flush(); 324 | }; 325 | 326 | 327 | class MidiCopy : public MidiRead 328 | { 329 | public: 330 | MidiCopy(const char* filename, FILE* f = 0); 331 | 332 | void setoutput(MidiWrite* dest); 333 | void stopoutput(); 334 | MidiWrite* getoutput(); 335 | 336 | void mapchannel(int channel, int newchannel); 337 | void ignorechannel(int channel); 338 | 339 | virtual void head(unsigned version, unsigned tracks, unsigned clicksperquarter); 340 | virtual void track(int trackno, long length, int channel); 341 | virtual void endtrack(int trackno); 342 | 343 | virtual void time(unsigned long ticks); 344 | 345 | virtual void event(int what, int len = 0, unsigned char* data = 0); 346 | 347 | virtual void meta(int what, int len, unsigned char* data); 348 | virtual void text(int what, int len, char* whattext, unsigned char* txt); 349 | virtual void end(); 350 | virtual void prefixchannel(unsigned char channel); 351 | virtual void prefixport(unsigned char port); 352 | virtual void seqnumber(unsigned int seqno); 353 | virtual void smpteofs(int hour, int min, int sec, int frame, int fracframe); 354 | virtual void tact(int nom, int denom, int clicksperbeat, int notes32perbeat); 355 | virtual void tempo(unsigned long microsecperbeat); 356 | virtual void key(int signature, int isminor); // C=0, -7=7flats +7=7sharps 357 | virtual void program(int channel, int prg); 358 | virtual void control(int channel, int what, int val); 359 | virtual void highbank(int channel, int val); 360 | virtual void wheel(int channel, int val); 361 | virtual void breath(int channel, int val); 362 | virtual void foot(int channel, int val); 363 | virtual void portamentotime(int channel, int val); 364 | virtual void data(int channel, int val); 365 | virtual void volume(int channel, int val); 366 | virtual void balance(int channel, int val); 367 | virtual void expression(int channel, int val); 368 | virtual void lowbank(int channel, int val); 369 | virtual void hold(int channel, int val); 370 | virtual void reverb(int channel, int val); 371 | virtual void chorus(int channel, int val); 372 | virtual void datainc(int channel, int val); 373 | virtual void datadec(int channel, int val); 374 | virtual void lowrpn(int channel, int val); 375 | virtual void highrpn(int channel, int val); 376 | virtual void resetctrlrs(int channel, int val); 377 | virtual void allnotesoff(int channel, int val); 378 | virtual void noteon(int channel, int note, int vel); 379 | virtual void noteoff(int channel, int note, int vel); 380 | virtual void pitchbend(int channel, int val); 381 | virtual void polyaftertouch(int channel, int note, int val); 382 | virtual void aftertouch(int channel, int val); 383 | virtual void songpos(unsigned pos); 384 | virtual void songselect(unsigned char song); 385 | virtual void tunerequest(); 386 | virtual void timingclock(); 387 | virtual void start(); 388 | virtual void cont(); 389 | virtual void stop(); 390 | virtual void activesense(); 391 | virtual void sysex(int syslen, unsigned char* sysdata); 392 | virtual void gmreset(); 393 | virtual void gsreset(); 394 | virtual void gsexit(); 395 | virtual void xgreset(); 396 | 397 | protected: 398 | MidiWrite* dest_; 399 | int mapchannel_[16]; // channel 0-15 or events are ignored for invalid channel 400 | }; 401 | 402 | #endif 403 | -------------------------------------------------------------------------------- /inst.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Patch mapping file for DRO2MIDI v1.4 3 | # 4 | # If you add a number of new patches to this file, please e-mail them to me 5 | # at malvineous@shikadi.net so that I can include them in the next version. 6 | # 7 | # The lines to put in this file are printed automatically when an unknown 8 | # instrument is encountered, but should you need to tinker further, the basic 9 | # format is: 10 | # 11 | # : 12 | # 13 | # The instrument type indicates what the register settings refer to: 14 | # 15 | # NO - normal instrument 16 | # BD - rhythm-mode bass drum 17 | # SD - rhythm mode snare drum 18 | # TT - rhythm mode tom tom 19 | # TC - rhythm mode top cymbal 20 | # HH - rhythm mode hi-hat 21 | # 22 | # To understand the register settings please see the code and the Adlib 23 | # docs. 24 | # 25 | # The options (probably the only bit you'll ever need to change) are 26 | # as follows: 27 | # 28 | # patch=123 - set this instrument to MIDI patch 123. MIDI patches 29 | # start at 1. See patch.txt for a list. 30 | # drum=123 - set this instrument to MIDI percussion on GM channel 10 with 31 | # note number 123. See drum.txt for a list. 32 | # transpose=12 - transpose this instrument's notes by the given number 33 | # of semitones. 12 is up one whole octave, -12 is down 34 | # one whole octave. Default is zero. 35 | # mute - temporarily mute this instrument. 36 | # 37 | # At the minimum you must specify either "patch" or "drum", but never both on 38 | # the same line. 39 | # 40 | 41 | NO 00-00/00-00/00-00/00-00/00/00-00: patch=1 # Default patch: Grand Piano 42 | 43 | # Rhythm-mode base/defaults 44 | BD 00-00/0B-0C/A8-D6/4C-4F/00/00-00: patch=117 # Default bass drum: Taiko 45 | SD 0C/0F/F8/B6/02: drum=52 # Default snare: Splash Cymbal 46 | HH 01/00/F7/B5/00/00: drum=42 # Default hi-hat: Closed Hi-Hat 47 | TC 01/00/2F/FF/00: patch=120 transpose=24 # Default top-cymbal: Reverse cymbal - transposed to make it attack faster :-/ 48 | TT 04/00/F7/B5/A4/00: patch=118 # Default tom-tom: Melodic Tom 49 | SD 0C/00/F8/B5/00: drum=38 # Alternate snare - a snare! 50 | BD 00-00/0B-00/A8-D6/4C-4F/00/00-00: patch=117 # Another Taiko for a different (quieter) Bass Drum 51 | 52 | NO 01-21/18-84/D4-C4/F2-8A/0A/00-00: patch=30 # Overdriven guitar 53 | NO 11-11/0A-13/FE-F2/04-BD/08/00-00: patch=31 # Distortion guitar 54 | NO 11-54/43-44/F1-F0/FF-FF/08/00-03: patch=31 # Distortion guitar 55 | 56 | # Doofus 57 | NO 00-00/00-02/F4-F5/23-43/01/03-00: patch=88 # Lead 8 (bass + lead) 58 | NO F1-31/82-43/F3-F4/22-22/01/01-03: patch=87 # Lead 7 (fifths) 59 | NO D2-10/82-02/F3-F1/11-21/01/02-03: patch=80 60 | 61 | # Wolf3d 62 | NO 06-00/00-2E/F0-F7/F0-F7/0E/00-00: drum=38 # Acoustic snare 63 | NO 71-22/C3-1D/8E-8B/17-0E/02/00-00: patch=59 # Tuba 64 | NO 00-00/0D-16/E8-A5/EF-FF/06/00-00: patch=33 # Acoustic bass 65 | NO 21-21/9A-9B/53-A0/56-16/0E/00-00: patch=58 # Trombone 66 | NO 32-11/44-0C/F8-F5/FF-7F/0E/00-00: patch=128 # Gunshot (marching sound) 67 | NO 13-E1/4D-1C/FA-F1/11-F1/08/00-00: patch=69 # Oboe (pregnant.wlf) 68 | NO 06-00/00-1F/55-F8/F0-F5/0E/00-00: drum=42 # Closed hi-hat 69 | NO 02-01/29-9B/F0-F4/75-33/00/00-00: patch=10 # Glockenspiel 70 | NO A2-E2/1D-9F/95-60/24-2A/02/00-00: patch=101 71 | NO 32-61/9A-A3/51-60/19-39/0C/00-00: drum=35 # Synth drum 72 | NO 00-00/40-10/09-F7/53-94/00/00-00: patch=117 # Taiko 73 | NO A2-E2/1D-9B/95-60/24-2A/02/00-00: patch=24 # Tango Accordion 74 | NO 71-61/56-16/51-54/03-17/0E/00-00: patch=23 # Harmonica 75 | NO 70-22/8D-25/6E-6B/17-0E/02/00-00: patch=22 # Accordion 76 | NO 32-11/44-11/F8-F5/FF-7F/0E/00-00: drum=41 # Low floor tom 77 | NO 31-61/1B-21/61-D2/06-36/0C/00-00: patch=58 # Trombone 78 | NO 31-61/1F-16/31-50/06-36/0C/00-00: patch=57 # Trumpet 79 | NO 70-22/8D-16/6E-6B/17-0E/02/00-00: patch=69 # Oboe 80 | 81 | NO 00-00/0B-01/A8-D6/4C-4F/00/00-00: drum=36 82 | #NO 32-11/44-3F/F8-F5/FF-7F/0E/00-00: patch=70 83 | NO 32-61/9A-93/51-60/19-39/0C/00-00: patch=71 84 | NO 31-22/C3-1F/87-8B/17-0E/02/00-00: patch=70 85 | NO 31-22/C3-11/87-8B/17-0E/02/00-00: patch=70 86 | #wonderin.wlf 87 | NO A2-E2/1D-91/95-60/24-2A/02/00-00: patch=36 transpose=12 #72 88 | NO 05-00/00-1E/F0-FA/77-E5/0E/00-00: drum=42 89 | NO 31-21/16-0C/73-80/8E-9E/0E/00-00: patch=88 90 | NO 21-21/9A-8E/53-A0/56-16/0E/00-00: patch=71 91 | 92 | NO 02-01/29-8D/F0-F4/75-33/00/00-00: patch=117 93 | NO 02-01/29-96/F5-F2/75-F3/00/00-00: patch=71 94 | 95 | # Cosmo 96 | NO 11-01/44-06/F8-F7/FF-45/00/00-00: drum=36 97 | NO 05-00/00-10/F0-FA/77-E5/0E/00-00: drum=42 # Closed hi-hat 98 | NO 05-00/00-09/F0-F8/FF-B9/0E/01-00: drum=42 99 | NO 05-00/00-12/F0-F6/FF-FF/0E/00-00: drum=44 100 | NO 24-21/D0-16/F0-E0/01-86/02/00-00: patch=76 # Pan flute 101 | NO 2C-A1/D4-1F/F9-C0/FF-FF/00/00-00: patch=13 # Marimba 102 | NO 71-61/56-1F/51-54/03-17/0E/00-00: patch=80 # Ocarina 103 | NO E2-E1/CA-1F/F8-C0/E5-0E/08/00-00: patch=80 # Ocarina 104 | 105 | NO 11-54/43-56/F1-F0/FF-FF/08/00-03: patch=30 # Overdriven guitar 106 | NO 11-54/43-4A/F1-F0/FF-FF/08/00-03: patch=31 # Distortion guitar 107 | NO 21-21/15-96/D3-C3/2C-2C/0A/00-00: patch=33 108 | NO 01-11/4F-1C/F1-F2/53-74/06/00-00: patch=13 109 | NO 00-00/00-11/F0-F6/FF-FF/04/00-00: patch=106 110 | NO 30-23/16-0C/71-88/EE-1E/0E/00-00: patch=69 111 | #NO 31-22/C3-0C/87-8B/17-0E/02/00-00: patch=1 112 | NO 61-21/19-0C/73-A0/57-17/0C/00-00: patch=71 113 | NO 01-21/18-91/D4-C4/F2-8A/0A/00-00: patch=36 114 | 115 | NO 32-11/44-0B/F8-F5/FF-7F/0E/00-00: drum=49 # Crash cymbal 1 116 | NO 32-11/44-0D/F8-F5/FF-7F/0E/00-00: drum=57 # Crash cymbal 2 117 | #mrockit.imf 118 | NO 2C-A1/D4-1B/F9-C0/FF-FF/00/00-00: patch=14 119 | NO 05-00/00-0D/F0-FA/77-E5/0E/00-00: drum=42 120 | NO 71-21/1C-0C/51-54/03-17/0E/00-00: patch=31 #82 121 | NO 00-00/0B-0D/A8-D6/4C-4F/00/00-00: drum=38 122 | NO 07-61/51-0C/F5-F0/33-25/06/00-00: patch=12 123 | 124 | # Vinyl Goddess from Mars 125 | NO 21-06/40-80/F1-F4/31-44/00/00-00: patch=81 # Square 126 | NO F1-21/01-0B/97-F1/17-18/04/00-00: patch=82 # Sawtooth 127 | NO 32-11/44-00/F8-F5/FF-7F/0E/00-00: patch=13 # Marimbas 128 | 129 | # Jill 130 | NO 00-01/2A-00/F2-F5/1F-88/0C/00-00: patch=19 # Rock organ 131 | NO 05-01/4E-00/DA-F9/25-15/0A/00-00: patch=13 # Marimbas 132 | NO CA-CC/84-00/F0-59/F0-62/0C/00-00: patch=30 # Overdriven guitar 133 | NO 00-51/1C-80/13-52/13-B2/0C/00-00: patch=13 # Marimbas 134 | NO 21-51/1C-80/13-52/12-B2/0C/00-00: patch=33 # Acoustic bass 135 | NO A1-A1/27-80/74-65/8F-2A/02/00-00: patch=12 # Vibraphone 136 | NO 18-25/88-80/FB-F4/22-12/08/00-00: patch=113 # Tinkle bell #15 # Tubular bells 137 | NO 00-01/00-00/FA-F3/13-7A/00/00-00: patch=33 138 | NO 70-71/C8-80/D5-26/11-11/0A/00-00: patch=93 139 | NO 20-21/8E-00/A5-36/8F-3D/06/00-00: patch=89 140 | NO 50-01/00-00/FA-F3/71-7A/00/00-00: patch=95 141 | NO 07-12/4F-00/F2-F2/60-72/08/00-00: patch=15 # Tubular bells 142 | 143 | NO 21-01/2A-00/F2-F5/1F-88/0C/00-00: patch=36 144 | HH 01/00/F7/B5/01/00: drum=46 # Open Hi-Hat 145 | HH 01/10/F7/B5/01/00: drum=42 146 | #BD 00-00/0B-02/A8-D6/4C-4F/00/00-00: patch=1 147 | #BD 00-00/0B-04/A8-D6/4C-4F/00/00-00: patch=1 148 | SD 0C/04/F7/A7/00: drum=38 149 | HH 01/00/DA/18/F8/00: drum=42 150 | 151 | 152 | # Kiloblaster 153 | NO 00-08/40-40/F1-F1/53-53/08/00-00: patch=31 #82 154 | NO 47-05/40-00/FF-FF/06-06/00/00-00: patch=108 # Koto 155 | NO 50-00/00-00/FB-F3/71-B9/00/00-00: patch=33 # Acoustic bass 156 | NO 00-00/00-00/FB-F3/71-B9/00/00-00: patch=40 # Synth bass 2 157 | NO 00-01/00-00/75-F5/93-82/00/00-00: patch=37 # Slap bass 1 158 | NO 02-01/29-80/F5-F2/75-F3/00/00-00: patch=108 # Koto 159 | 160 | # Major Stryker 161 | NO 01-21/18-89/D4-C4/F2-8A/0A/00-00: patch=117 162 | #NO 07-12/0A-1F/F2-F2/60-52/00/03-03: patch=53 # Choir Aahs 163 | NO 31-21/16-0B/73-80/8E-9E/0E/00-00: patch=34 164 | NO 30-21/16-0B/73-80/7E-9E/0E/00-00: patch=40 165 | NO 31-21/16-1B/73-80/8E-9E/0E/00-00: patch=92 166 | NO 01-01/1B-1A/F0-F0/FF-FF/04/00-00: patch=17 transpose=24 167 | NO 05-00/00-1A/F0-F6/FF-FF/0E/00-00: drum=35 168 | NO 06-00/00-0B/55-F8/F0-F5/0E/00-00: drum=38 169 | NO 31-21/16-0F/73-80/8E-9E/0E/00-00: patch=33 170 | 171 | # Body Count 172 | NO 01-01/8C-8A/F2-A0/94-5B/00/02-03: patch=82 # Sawtooth 173 | NO 20-24/CD-89/D5-61/29-1B/00/00-01: patch=31 # Dist guit 174 | NO 00-00/0B-00/A8-D6/4C-4F/00/00-00: drum=36 175 | NO 26-01/0A-00/FD-F8/0F-4A/0C/00-00: drum=44 176 | 177 | # Monster Bash 178 | NO 00-11/02-82/F0-F0/FF-FF/06/00-00: patch=82 # Sawtooth 179 | NO 01-01/04-02/F1-F1/01-04/06/00-00: patch=82 # Sawtooth 180 | NO 21-31/4F-0A/F1-F2/11-11/06/00-00: patch=13 # Marimbas (demons.imf starting note) 181 | NO 13-91/97-8B/9A-9B/12-11/0E/00-00: patch=71 # Bassoon #69 # Oboe 182 | NO 21-A2/87-91/74-65/17-17/07/00-00: patch=61 # French horn 183 | NO 22-21/16-0B/63-71/FD-FE/0E/00-00: patch=72 # Clarinet 184 | NO 21-A2/8A-93/74-65/17-17/07/00-00: patch=70 # English horn 185 | NO 0C-00/0D-0D/F8-D6/B5-4F/01/00-00: patch=11 186 | NO 0C-00/15-15/F8-D6/B5-4F/01/00-00: patch=14 187 | NO B4-D7/87-92/A4-45/02-42/06/00-00: patch=49 188 | NO E0-B1/9A-08/F7-F9/0F-0B/08/00-00: patch=43 # Cello 189 | NO B2-B1/D1-85/91-91/2A-2A/09/02-01: patch=66 190 | NO 06-C4/00-01/FF-FB/F0-FF/0E/00-00: drum=69 # Casaba 191 | NO 00-C4/40-12/80-C0/7A-7E/08/00-00: patch=72 192 | NO 21-28/CD-CD/FF-FF/0F-0F/01/00-00: patch=20 # Pipe organ 193 | NO 40-00/41-0C/73-8F/71-01/0A/00-00: patch=82 transpose=-12 # Sawtooth wave 194 | NO 06-C4/00-07/FF-FB/F0-FF/0E/00-00: patch=116 195 | NO D0-9E/80-04/F4-A2/E4-06/08/00-00: drum=81 # Open triangle 196 | NO D0-9E/80-02/F4-A2/E4-06/08/00-00: patch=113 # Tinkle bell 197 | NO 20-11/90-10/F5-F4/9D-78/0C/02-03: patch=17 198 | 199 | NO B1-61/1C-8B/41-92/1F-3B/0E/00-00: patch=49 200 | NO 31-0A/00-3F/F1-F2/F3-32/04/00-00: patch=43 201 | #NO 07-14/8F-BF/82-82/7D-7D/0C/00-00: patch=1 202 | NO 47-83/40-0F/B8-E5/84-8F/0E/00-00: patch=117 203 | NO 06-C4/00-02/FF-FB/F0-FF/0E/00-00: patch=116 204 | NO 06-C4/00-05/FF-FB/F0-FF/0E/00-00: patch=114 205 | NO 21-23/23-93/A0-90/09-0A/0E/00-00: patch=46 206 | 207 | # Monster Bash (theme.imf) 208 | NO 31-0A/00-11/F1-F2/F3-32/04/00-00: patch=85 209 | BD 00-00/0B-17/A8-D6/4C-4F/00/00-00: patch=74 # Alt bass drum: Flute 210 | NO 13-91/97-8D/9A-9B/12-11/0E/00-00: patch=21 211 | 212 | # Bio Menace 213 | NO 32-11/44-06/F8-F5/FF-7F/0E/00-00: patch=76 # Pan flute 214 | NO 01-01/4F-1D/F1-D3/50-7C/06/00-00: patch=21 # Reed organ 215 | NO 01-21/18-9A/D4-C4/F2-8A/0A/00-00: patch=36 # Fretless bass 216 | 217 | #NO 00-00/0D-10/E8-A5/EF-FF/06/00-00: drum=36 218 | #NO 00-00/0D-1F/E8-A5/EF-FF/06/00-00: patch=1 #these seem too similar to be a drum and piano 219 | NO 30-35/35-11/F5-F0/F0-9B/02/00-00: patch=85 220 | 221 | NO 11-01/86-93/F2-A0/A8-A8/08/00-00: patch=85 222 | NO 00-00/0B-11/A8-D6/4C-4F/00/00-00: drum=36 223 | NO 06-00/00-0C/F0-F7/F0-F7/0E/00-00: drum=38 224 | 225 | # Duke II 226 | NO 07-12/0A-1E/F2-F2/60-52/00/03-03: patch=33 # Acoustic bass 227 | NO 07-12/0A-20/F2-F2/60-52/00/03-03: patch=33 228 | NO 00-00/0B-07/A8-D6/4C-4F/00/00-00: drum=35 229 | NO 07-12/0A-1B/F2-F2/60-52/00/03-03: patch=117 230 | NO 07-12/0A-1F/F2-F2/60-52/00/03-03: patch=117 231 | NO 11-54/43-49/F1-F0/FF-FF/08/00-03: patch=30 # Overdriven guitar 232 | NO 00-00/0B-0A/A8-D6/4C-4F/00/00-00: patch=19 233 | NO 05-00/00-1F/F0-FA/77-E5/0E/00-00: patch=37 234 | NO 05-00/00-20/F0-FA/77-E5/0E/00-00: drum=42 # background quick hihat 235 | NO 05-00/00-18/F0-FA/77-E5/0E/00-00: drum=42 236 | NO 06-00/00-0A/F0-F7/F0-F7/0E/00-00: drum=45 237 | NO 06-00/00-1A/F0-F7/F0-F7/0E/00-00: drum=38 # end of riff snare 1 238 | NO 06-00/00-12/F0-F7/F0-F7/0E/00-00: drum=40 # end of riff snare 2 239 | NO 11-54/43-40/F1-F0/FF-FF/08/00-03: patch=31 # Distortion guitar 240 | 241 | NO 11-11/0A-05/FE-F2/04-BD/08/00-00: patch=37 242 | NO 41-01/11-0C/F0-F0/FF-FF/0A/00-00: patch=30 243 | 244 | NO 21-21/15-86/D3-C3/2C-2C/0A/00-00: patch=36 245 | 246 | # Commander Keen - track18/Omegamatic 247 | NO 11-11/0C-1F/F2-F2/01-B6/08/00-00: patch=19 transpose=12 248 | NO 00-00/0B-1A/A8-D6/4C-4F/00/00-00: patch=13 249 | NO 01-21/18-8E/D4-C4/F2-8A/0A/00-00: patch=36 250 | NO 2B-21/CA-15/F8-C0/E5-FF/00/00-00: patch=60 251 | NO 06-00/00-16/F0-F7/F0-F7/0E/00-00: drum=38 252 | NO 20-21/1B-16/63-63/0A-0B/0C/00-00: patch=57 253 | NO 00-00/0B-22/A8-D6/4C-4F/00/00-00: patch=46 254 | NO 31-61/1B-16/61-D2/06-36/0C/00-00: patch=1 255 | 256 | NO 01-01/11-10/F0-F0/FF-F8/0A/00-00: patch=110 257 | NO 01-01/11-1B/F0-F0/FF-F8/0A/00-00: patch=110 258 | NO 01-01/11-1F/F0-F0/FF-F8/0A/00-00: patch=110 259 | NO 07-00/00-1B/F0-5C/F0-DC/0E/00-00: drum=70 260 | NO 00-02/40-12/09-F7/53-94/0E/00-00: patch=48 transpose=12 # Timpani 261 | 262 | NO 00-00/0D-23/E8-A5/EF-FF/06/00-00: patch=19 263 | NO 00-00/0D-1D/E8-A5/EF-FF/06/00-00: patch=19 264 | 265 | NO 21-21/15-8C/D3-C3/2C-2C/0A/00-00: patch=33 266 | NO 32-11/44-15/F8-F5/FF-7F/0E/00-00: patch=118 267 | 268 | # God of Thunder - song33.imf 269 | NO 32-11/44-10/F8-F5/FF-7F/0E/00-00: patch=117 #drum=42 # Closed Hi-Hat 270 | NO F1-21/01-10/77-81/17-18/02/00-00: patch=69 # Oboe (main tune) 271 | NO 21-21/15-0C/B4-94/4C-AC/0A/00-00: patch=21 # (bassy 1/4) 272 | NO 11-01/86-90/F2-A0/A8-A8/08/00-00: patch=19 # (bassy 2/4) 273 | NO 61-21/19-10/73-A0/57-17/0C/00-00: patch=21 # (bassy 3/4) 274 | NO 06-00/00-10/F4-F6/A0-46/0E/00-00: patch=59 transpose=-12 # (bassy 4/4, quiet few notes) 275 | NO 61-21/19-0E/53-A0/58-18/0C/00-00: patch=67 # English Horn (second tune) 276 | NO 64-61/C9-10/B0-F0/01-86/02/00-00: patch=81 # Square wave (third tune) 277 | # song01.imf 278 | NO 61-21/19-0E/73-A0/57-17/0C/00-00: patch=57 # Trumpet 279 | NO 00-00/0B-0B/A8-D6/4C-4F/00/00-00: patch=49 # Tuba 280 | NO 32-11/44-13/F8-F5/FF-7F/0E/00-00: drum=42 # Closed Hi-Hat 281 | # song02.imf 282 | NO 11-11/0A-0D/FE-F2/04-BD/08/00-00: patch=33 283 | NO 2B-21/CA-0E/F8-C0/E5-FF/00/00-00: patch=13 284 | # song04.imf 285 | NO 11-11/0A-10/FE-F2/04-BD/08/00-00: patch=33 # Acoustic Bass 286 | NO 03-01/8A-90/F0-F4/7B-7B/08/00-00: patch=25 # Acoustic Guitar 287 | NO A4-E2/12-90/F4-60/30-2A/02/00-00: patch=76 # Pan Flute 288 | NO 28-21/CF-10/F8-C0/E5-FF/00/00-00: patch=14 # Xylophone 289 | 290 | # Stunts 291 | NO 0F-00/3F-3F/F0-F6/00-06/0E/00-00: drum=36 # Closed Hi-Hat 292 | NO C0-00/3F-3F/F8-F1/F8-27/00/02-00: drum=42 # Acoustic Bass Drum 293 | NO 0F-00/3F-3F/F0-F0/F2-04/0C/00-00: drum=49 # Crash cymbal (one note at start) 294 | NO 02-26/3F-3F/F3-B5/F3-15/0E/00-00: patch=34 transpose=12 # Main bass line 33,19,34 295 | NO 0F-00/3F-3F/F0-FA/00-09/0E/00-00: drum=42 # Closed Hi-Hat 296 | NO E4-A2/3F-3F/74-F5/05-05/0D/00-03: patch=30 transpose=24 # Main tune 1 297 | NO 22-22/3F-3F/66-66/16-16/0B/02-02: patch=31 transpose=24 # Main tune 2 298 | NO 0F-00/3F-3F/F0-F7/00-27/0E/00-00: drum=40 # Snare, comes in after intro 299 | NO A2-E2/3F-3F/CF-B0/07-0C/07/02-03: patch=31 # Main tune backup 300 | NO C0-00/3F-3F/F0-F6/00-06/0C/02-00: patch=118 # Tomtom right at the end 301 | 302 | # Bubble Bobble (story.raw) 303 | NO 31-16/87-96/A1-7D/11-43/08/00-00: patch=6 304 | NO 01-11/4F-0F/F1-D2/53-74/06/00-00: patch=5 305 | NO 21-22/0D-80/E9-65/3A-6C/0A/00-00: patch=36 306 | NO 01-01/11-16/F2-F5/1F-88/0A/00-00: patch=25 307 | NO 32-61/9A-91/51-A2/1B-3B/0C/00-00: patch=2 308 | NO 01-08/40-4F/F1-F1/53-53/00/00-00: patch=62 309 | 310 | NO 21-21/9F-89/53-AA/5A-1A/0C/00-00: patch=113 311 | NO 21-22/0D-89/E9-65/3A-6C/0A/00-00: patch=36 312 | 313 | # Zone 66 314 | NO 00-00/34-00/F3-FF/60-04/0E/00-00: patch=92 315 | NO 61-C0/D2-00/F0-C2/00-F1/00/03-00: patch=80 316 | NO 00-00/9A-82/51-60/19-39/0C/01-01: patch=75 317 | NO 06-00/00-4A/F4-F7/A0-46/0E/00-00: drum=42 318 | NO 31-00/0D-00/E8-A5/EF-FF/06/00-00: drum=36 319 | NO 12-00/80-00/C8-A7/12-57/0E/02-00: drum=38 320 | 321 | # Old patches taken over from the .reg files 322 | NO 01-01/11-1D/F0-F0/FF-F8/0A/00-00: patch=28 323 | NO 00-00/3F-00/39-FF/F3-05/00/00-00: patch=34 324 | NO 31-15/85-86/A1-73/10-33/08/00-00: patch=13 325 | NO 02-01/29-94/F5-F2/75-F3/00/00-00: patch=11 326 | NO 11-54/43-45/F1-F0/FF-FF/08/00-03: patch=82 327 | NO A1-E2/13-95/D6-60/AF-2A/02/00-00: patch=38 328 | NO 11-54/43-45/F1-F0/FF-FF/08/00-03: patch=81 329 | NO A1-E2/13-95/D6-60/AF-2A/02/00-00: patch=84 330 | NO 00-00/0B-17/A8-D6/4C-4F/00/00-00: patch=117 331 | NO 21-21/9A-8C/53-A0/56-16/0E/00-00: patch=58 332 | NO 21-21/15-8B/D3-C3/2C-2C/0A/00-00: patch=88 333 | NO 61-21/19-07/53-A0/58-18/0C/00-00: patch=57 334 | NO 21-21/9A-8C/53-A0/56-16/0E/00-00: patch=59 335 | NO B3-90/4A-1E/B6-D1/32-31/0E/00-00: patch=12 336 | NO 05-00/00-08/F0-F8/FF-B9/0E/01-00: patch=116 337 | NO 00-00/00-02/F4-F5/23-43/01/03-00: patch=22 338 | NO F1-31/82-43/F3-F4/22-22/01/01-03: patch=91 339 | NO D0-12/87-47/F6-F0/21-21/00/01-00: patch=36 340 | NO D0-11/02-02/F4-F5/23-43/01/03-00: patch=1 341 | NO D2-10/82-02/F3-F1/11-21/01/02-03: patch=23 342 | NO 01-21/18-86/D4-C4/F2-8A/0A/00-00: patch=30 343 | NO D1-11/0B-0C/90-72/23-23/01/03-00: patch=24 344 | NO 61-E1/A7-95/72-50/8E-1A/02/00-00: patch=64 345 | NO 21-21/15-11/B4-94/4C-AC/0A/00-00: patch=34 346 | NO 61-E1/A7-95/72-50/8E-1A/02/00-00: patch=70 347 | #NO A2-E2/1D-91/95-60/24-2A/02/00-00: patch=63 348 | NO 11-11/0A-0B/FE-F2/04-BD/08/00-00: patch=36 349 | NO 01-11/4F-3F/F1-F2/53-74/03/00-00: patch=10 # Glockenspiel 350 | NO 00-00/3F-0C/39-FF/F3-05/00/00-00: patch=25 351 | 352 | 353 | # General MIDI assignments used by Creative Labs' MIDI player (PLAY.EXE) 354 | # These are last so that they can be overridden above 355 | 356 | NO 00-00/4F-00/F1-D2/51-43/06/00-00: patch=1 357 | NO 02-12/4F-00/F1-D2/51-43/02/00-00: patch=2 358 | NO 00-11/4A-00/F1-D2/53-74/06/00-00: patch=3 359 | NO 03-11/4F-00/F1-D2/53-74/06/01-01: patch=4 360 | NO 01-11/66-00/F1-D2/51-C3/06/00-00: patch=5 361 | NO C0-D2/52-00/F1-D2/53-94/06/00-00: patch=6 362 | NO 12-18/86-00/F3-FC/00-33/08/00-00: patch=7 363 | NO D0-12/4E-00/A8-92/32-A7/00/03-02: patch=8 364 | NO C8-D1/4F-00/F2-F3/64-77/08/00-00: patch=9 365 | NO 33-34/0E-00/01-7D/11-34/08/00-00: patch=10 366 | NO 17-16/50-00/D1-D3/52-92/04/00-01: patch=11 367 | NO E7-E1/21-00/F5-F6/77-14/08/00-00: patch=12 368 | NO 95-81/4E-00/DA-F9/25-15/0A/00-00: patch=13 369 | NO 27-21/1F-00/F5-F5/96-57/08/00-00: patch=14 370 | NO 87-F1/4E-80/B1-E6/33-42/00/00-00: patch=15 371 | NO 31-11/87-80/A1-7D/11-43/08/00-00: patch=16 372 | NO 32-B1/8C-00/91-A1/07-19/05/02-00: patch=17 373 | NO 31-B4/54-80/F1-F5/07-19/07/00-00: patch=18 374 | NO 24-21/40-49/FF-FF/0F-0F/01/00-00: patch=19 375 | NO D2-F1/44-80/91-A1/57-09/03/01-01: patch=20 376 | NO 01-02/52-80/F0-F0/1F-1F/0A/01-00: patch=21 377 | NO 21-32/4F-01/F2-52/0B-0B/0A/00-01: patch=22 378 | NO F0-F2/93-00/D8-B3/0B-0B/0A/02-01: patch=23 379 | NO 20-31/5D-00/F2-52/0B-0B/00/03-02: patch=24 380 | NO 01-01/1B-00/F4-F3/25-46/00/02-00: patch=25 381 | NO 11-01/0F-00/F4-F3/25-46/00/01-00: patch=26 382 | NO 01-01/27-00/F1-F4/1F-88/0A/02-00: patch=27 383 | NO 12-13/44-00/EA-D2/32-E7/00/01-01: patch=28 384 | NO 30-31/45-00/A4-F5/32-E7/00/03-00: patch=29 385 | NO 21-21/0F-00/F5-F1/17-78/04/02-01: patch=30 386 | NO 01-20/41-00/D1-C1/34-A5/04/03-03: patch=31 387 | NO 10-12/43-00/A7-E3/97-E7/00/03-02: patch=32 388 | NO 20-21/28-00/C5-D2/15-A4/0C/00-00: patch=33 389 | NO 30-21/16-00/F2-F3/9F-78/0C/00-00: patch=34 390 | NO 30-21/11-00/82-F3/9F-78/0A/00-00: patch=35 391 | NO 21-21/23-00/73-93/1A-87/0C/00-00: patch=36 392 | NO 30-21/0E-00/62-F3/55-68/0A/02-00: patch=37 393 | NO 30-22/0C-00/62-D5/B5-98/08/01-00: patch=38 394 | NO 70-72/93-40/64-A1/43-43/0A/00-00: patch=39 395 | NO 30-32/8D-80/44-92/43-43/0A/02-00: patch=40 396 | NO E1-E2/4E-00/65-61/43-44/00/02-02: patch=41 397 | NO A1-A2/8E-00/65-63/43-45/00/02-02: patch=42 398 | NO B0-61/87-40/D1-62/11-15/06/02-01: patch=43 399 | NO F0-20/8A-80/B1-A0/11-15/06/02-01: patch=44 400 | NO F1-E2/89-40/73-43/01-05/06/02-00: patch=45 401 | NO 31-21/57-80/F8-F7/F9-E6/0E/03-02: patch=46 402 | NO 32-01/24-80/F1-F5/35-35/00/00-00: patch=47 403 | NO 00-00/04-00/AA-D2/C8-B3/0A/00-00: patch=48 404 | NO E0-F1/4F-00/D4-55/0B-0B/0A/02-02: patch=49 405 | NO E0-F0/52-00/96-35/05-01/0A/02-02: patch=50 406 | NO E1-F1/4F-00/36-45/05-02/0A/02-02: patch=51 407 | NO E2-E1/48-80/21-41/43-45/00/02-01: patch=52 408 | NO E0-F1/16-00/41-20/52-72/00/02-02: patch=53 409 | NO E0-F1/11-00/01-D0/52-72/00/02-02: patch=54 410 | NO E0-F1/1A-00/61-30/52-73/00/00-02: patch=55 411 | NO 50-50/0B-00/84-A4/4B-99/0A/00-00: patch=56 412 | NO 31-61/1C-80/41-92/0B-3B/0E/00-00: patch=57 413 | NO B1-61/1C-00/41-92/1F-3B/0E/00-00: patch=58 414 | NO 20-21/18-00/52-A2/15-24/0C/00-00: patch=59 415 | NO C1-C1/94-80/74-A3/EA-F5/0E/02-01: patch=60 416 | NO 21-21/28-00/41-81/B4-98/0E/00-00: patch=61 417 | NO 21-21/1D-00/51-E1/AE-3E/0E/02-01: patch=62 418 | NO E0-E0/93-80/51-81/A6-97/0E/02-01: patch=63 419 | NO E0-E1/93-80/51-E1/A6-97/0E/02-01: patch=64 420 | NO E0-F2/4B-01/D8-B3/0B-0B/08/02-01: patch=65 421 | NO E0-F1/49-01/B8-B3/0B-0B/08/02-01: patch=66 422 | NO E0-F0/4E-01/98-C3/0B-0B/08/01-02: patch=67 423 | NO E0-F1/4C-01/88-D3/0B-0B/08/01-01: patch=68 424 | NO F1-E4/C5-00/7E-8C/17-0E/08/00-00: patch=69 425 | NO 60-72/4F-00/D8-B3/0B-0B/0A/00-01: patch=70 426 | NO 31-72/D1-80/D5-91/19-1B/0C/00-00: patch=71 427 | NO 32-71/C8-80/D5-73/19-1B/0C/00-00: patch=72 428 | NO E2-62/6A-00/9E-55/8F-2A/0E/00-00: patch=73 429 | NO E0-61/EC-00/7E-65/8F-2A/0E/00-00: patch=74 430 | NO 62-A2/88-83/84-75/27-17/09/00-00: patch=75 431 | NO 62-A2/84-83/84-75/27-17/09/00-00: patch=76 432 | NO E3-62/6D-00/57-57/04-77/0E/00-00: patch=77 433 | NO F1-E1/28-00/57-67/34-5D/0E/03-00: patch=78 434 | NO D1-72/C7-00/31-42/0F-09/0B/00-00: patch=79 435 | NO F2-72/C7-00/51-42/05-69/0B/00-00: patch=80 436 | NO 23-31/4F-00/51-60/5B-25/00/01-01: patch=81 437 | NO 22-31/48-00/31-C0/9B-65/00/02-01: patch=82 438 | NO F1-E1/28-00/57-67/34-0D/0E/03-00: patch=83 439 | NO E1-E1/23-00/57-67/04-4D/0E/03-00: patch=84 440 | NO E2-31/42-08/78-F3/0B-0B/08/01-01: patch=85 441 | NO E2-E2/21-00/11-40/52-73/08/01-01: patch=86 442 | NO 23-A4/C0-00/51-35/07-79/0D/01-02: patch=87 443 | NO 24-A0/C0-00/51-75/07-09/09/01-02: patch=88 444 | NO E0-F0/16-00/B1-E0/51-75/00/02-02: patch=89 445 | NO 03-A4/C0-00/52-F4/03-55/09/00-00: patch=90 446 | NO E1-E1/93-80/31-A1/A6-97/0A/01-01: patch=91 447 | NO F0-71/C4-80/10-11/01-C1/01/02-02: patch=92 448 | NO C1-E0/4F-00/B1-12/53-74/06/02-02: patch=93 449 | NO C0-41/6D-00/F9-F2/21-B3/0E/01-00: patch=94 450 | NO E3-E2/4C-00/21-A1/43-45/00/01-01: patch=95 451 | NO E3-E2/0C-00/11-80/52-73/08/01-01: patch=96 452 | NO 26-88/C0-00/55-F8/47-19/0B/00-00: patch=97 453 | NO 23-E4/D4-00/E5-35/03-65/07/00-00: patch=98 454 | NO 27-32/C0-00/32-A4/62-33/00/00-00: patch=99 455 | NO D0-31/4E-00/98-A2/32-47/00/01-02: patch=100 456 | NO F0-71/C0-00/93-43/03-02/0F/01-00: patch=101 457 | NO E0-F1/1A-80/13-33/52-13/00/01-02: patch=102 458 | NO E0-F1/1A-00/45-32/BA-91/00/00-02: patch=103 459 | NO 11-15/18-03/58-A2/02-72/0A/01-00: patch=104 460 | NO 10-18/80-40/F1-F1/53-53/00/00-00: patch=105 461 | NO 31-17/86-80/A1-7D/11-23/08/00-00: patch=106 462 | NO 10-18/80-40/F1-F6/53-54/00/00-00: patch=107 463 | NO 31-34/21-00/F5-93/56-E8/08/01-00: patch=108 464 | NO 03-15/4F-00/F1-D6/39-74/06/03-00: patch=109 465 | NO 31-22/43-00/6E-8B/17-0C/02/01-02: patch=110 466 | NO 31-22/1C-80/61-52/03-67/0E/00-00: patch=111 467 | NO 60-F0/0C-80/81-61/03-0C/08/00-01: patch=112 468 | NO 27-05/55-00/31-A7/62-75/00/00-00: patch=113 469 | NO 95-16/81-00/E7-96/01-67/04/00-00: patch=114 470 | NO 0C-01/87-80/F0-F2/05-05/04/01-01: patch=115 471 | NO 35-11/44-00/F8-F5/FF-75/0E/00-00: patch=116 472 | NO 10-10/0B-00/A7-D5/EC-F5/00/00-00: patch=117 473 | NO 20-01/0B-00/A8-D6/C8-B7/00/00-00: patch=118 474 | NO 00-01/0B-00/88-D5/C4-B7/00/00-00: patch=119 475 | NO 0C-10/8F-80/41-33/31-2B/08/00-03: patch=120 476 | NO 17-F7/00-00/3B-EA/DF-97/0B/03-00: patch=121 477 | NO 12-18/06-00/73-3C/02-74/0E/00-00: patch=122 478 | NO 02-08/00-00/3E-14/01-F3/0E/02-02: patch=123 479 | NO F5-F6/D4-00/EB-45/03-68/07/00-00: patch=124 480 | NO F0-CA/00-C0/DA-B0/71-17/08/01-01: patch=125 481 | NO F0-E2/00-C0/1E-11/11-11/08/01-01: patch=126 482 | NO E7-E8/00-04/34-10/00-B2/0E/02-02: patch=127 483 | NO 0C-04/00-00/F0-F6/F0-E6/0E/02-00: patch=128 484 | -------------------------------------------------------------------------------- /midiio.cpp: -------------------------------------------------------------------------------- 1 | // midiio.cpp written by Günter Nagler 1995 (gnagler@ihm.tu-graz.ac.at) 2 | #include "midiio.hpp" 3 | #include 4 | #ifdef __MSDOS__ 5 | #include 6 | #endif 7 | #include 8 | #include 9 | 10 | static const char* copyright = "midiio v1.4 (c) 1995 by Günter Nagler"; 11 | 12 | int compress = 1; 13 | 14 | #define NOTREALISTIC_PAUSE 0x1000000UL 15 | 16 | // common sysex events 17 | unsigned char sysex_gmreset[] = { 0xF0, 0x05, 0x7E, 0x7F, 0x09, 0x01, 0xF7 }; 18 | unsigned char sysex_gsreset[] = { 0xF0, 0x0A, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7 }; 19 | unsigned char sysex_gsexit[] = { 0xF0, 0x0A, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x7F, 0x42, 0xF7 }; 20 | unsigned char sysex_xgreset[] = { 0xF0, 0x08, 0x43, 0x10, 0x4C, 0x00, 0x00, 0x7E, 0x00, 0xF7 }; 21 | 22 | static int issysex(const unsigned char* sysex, const unsigned char* sysdata, int syslen) 23 | { 24 | if (*sysex == 0xF0) 25 | sysex++; 26 | while (syslen > 0) 27 | { 28 | if (*sysex != *sysdata) 29 | return 0; 30 | syslen--; 31 | if (*sysex == 0xF7) 32 | break; 33 | sysex++; 34 | sysdata++; 35 | } 36 | return (syslen != 0) ? 0 : 1; 37 | } 38 | 39 | static int sysexlen(unsigned const char* sysex) 40 | { 41 | int len = 0; 42 | 43 | len = 0; 44 | while (*sysex != 0xF7) 45 | { 46 | sysex++; 47 | len++; 48 | } 49 | return len+1; // incl. F7 50 | } 51 | 52 | // class MidiRead 53 | 54 | const char* MidiRead::copyright() 55 | { 56 | return (const char*)::copyright; 57 | } 58 | 59 | MidiRead::MidiRead(const char* filename, FILE* f) 60 | { 61 | midiname_ = filename; 62 | if (f) 63 | { 64 | f_ = f; 65 | shouldclose_ = 0; 66 | } 67 | else 68 | { 69 | shouldclose_ = 1; 70 | if (!filename) 71 | f_ = 0; 72 | else 73 | f_ = fopen(filename, READ_BINARY); 74 | } 75 | 76 | buflen_ = 0; 77 | bufpos_ = 0; 78 | curpos_ = 0; 79 | pos_ = 0; 80 | curchannel_ = NOCHANNEL; 81 | curtime_ = 0; 82 | options_ = 0; 83 | if (f_) 84 | { 85 | fseek(f_, 0L, SEEK_END); 86 | filesize_ = ftell(f_); 87 | fseek(f_, 0L, SEEK_SET); 88 | } 89 | else 90 | filesize_ = 0; 91 | 92 | version_ = tracks_ = clicks_ = trackno_ = 0; 93 | tracklen_ = 0; 94 | } 95 | 96 | 97 | MidiRead::~MidiRead() 98 | { 99 | if (f_ && shouldclose_) 100 | fclose(f_); 101 | } 102 | 103 | int MidiRead::runhead() 104 | { 105 | if (!f_) 106 | { 107 | error("file not open"); 108 | return 0; 109 | } 110 | seek(0); 111 | if (getlong() != MThd) 112 | { 113 | error("missing midi header MThd"); 114 | return 0; 115 | } 116 | 117 | if (getlong() == 6) 118 | { 119 | version_ = getword(); 120 | tracks_ = getword(); 121 | clicks_ = getword(); 122 | head(version_, tracks_, clicks_); 123 | } 124 | else 125 | { 126 | error("illegal midi header"); 127 | return 0; 128 | } 129 | return 1; 130 | } 131 | 132 | int MidiRead::run() 133 | { 134 | pos_ = curpos_; 135 | if (!runhead()) 136 | return 0; 137 | pos_ = curpos_; 138 | for (trackno_ = 1; trackno_ <= tracks_; trackno_++) 139 | if (!runtrack(trackno_)) 140 | return 0; 141 | if (curpos_ >= filesize_) 142 | percent(percent_ = 100); 143 | endmidi(); 144 | return 1; 145 | } 146 | 147 | int MidiRead::runevent(long trackend) 148 | { 149 | int midicode; 150 | 151 | pos_ = curpos_; 152 | 153 | unsigned char *c = need(1); 154 | if (!c || c[0] >= 0x80 || lastcode_ < 0) 155 | midicode = getbyte(); 156 | else 157 | midicode = lastcode_; 158 | 159 | if (midicode < 0) 160 | return 0; 161 | 162 | switch(midicode) 163 | { 164 | case 0xf0: // sysex 165 | { 166 | int syslen = scansysevent(trackend - curpos_); 167 | if (!syslen) 168 | { 169 | error("end of sysex not found or sysex too large"); 170 | return 0; 171 | } 172 | unsigned char* sysdata = get(syslen); 173 | if ((options_ & OPTION_NOSYSEVENTS) == 0) 174 | { 175 | if (issysex(sysex_gmreset, sysdata, syslen)) 176 | gmreset(); 177 | else if (issysex(sysex_gsreset, sysdata, syslen)) 178 | gsreset(); 179 | else if (issysex(sysex_gsexit, sysdata, syslen)) 180 | gsexit(); 181 | else if (issysex(sysex_xgreset, sysdata, syslen)) 182 | xgreset(); 183 | else 184 | sysex(syslen, sysdata); 185 | } 186 | else if ((options_ & OPTION_NOREALTIMEEVENTS) == 0) 187 | sysex(syslen, sysdata); 188 | else 189 | event(0xf0, syslen, sysdata); 190 | } 191 | break; 192 | case 0xf2: 193 | { 194 | c = get(2); if (!c) return 0; 195 | if ((options_ & OPTION_NOREALTIMEEVENTS) == 0) 196 | songpos((unsigned(c[1]) << 7) + unsigned(c[0])); 197 | else 198 | event(0xf2, 2, c); 199 | break; 200 | } 201 | case 0xf3: 202 | c = get(1); if (!c) return 0; 203 | if ((options_ & OPTION_NOREALTIMEEVENTS) == 0) 204 | songselect(*c); 205 | else 206 | event(0xf3, 1, c); 207 | break; 208 | case 0xf6: 209 | if ((options_ & OPTION_NOREALTIMEEVENTS) == 0) 210 | tunerequest(); 211 | else 212 | event(0xf6); 213 | break; 214 | case 0xf8: 215 | if ((options_ & OPTION_NOREALTIMEEVENTS) == 0) 216 | timingclock(); 217 | else 218 | event(0xf8); 219 | break; 220 | case 0xfa: 221 | if ((options_ & OPTION_NOREALTIMEEVENTS) == 0) 222 | start(); 223 | else 224 | event(0xfa); 225 | break; 226 | case 0xfb: 227 | if ((options_ & OPTION_NOREALTIMEEVENTS) == 0) 228 | cont(); 229 | else 230 | event(0xfb); 231 | break; 232 | case 0xfc: 233 | if ((options_ & OPTION_NOREALTIMEEVENTS) == 0) 234 | stop(); 235 | else 236 | event(0xfc); 237 | break; 238 | case 0xfe: 239 | if ((options_ & OPTION_NOREALTIMEEVENTS) == 0) 240 | activesense(); 241 | else 242 | event(0xfe); 243 | break; 244 | case 0xff: 245 | { 246 | int c; 247 | unsigned long endpos; 248 | int len; 249 | 250 | c = getbyte(); 251 | len = (int)getdelta(); 252 | endpos = curpos_ + len; 253 | if (options_ & OPTION_NOREALTIMEEVENTS) 254 | { 255 | len += curdeltalen_ + 1; 256 | seek(curpos_ - curdeltalen_ - 1); 257 | event(0xff, len, get(len)); 258 | } 259 | else if (options_ & OPTION_NOMETAEVENTS) 260 | meta(c, len, get(len)); 261 | else 262 | switch(c) 263 | { 264 | case 0: 265 | if (len == 2) 266 | seqnumber(getword()); 267 | else 268 | meta(c, len, get(len)); 269 | break; 270 | case meta_text: 271 | text(c, len, "text", get(len)); break; 272 | case meta_copyright: 273 | text(c, len, "copyright", get(len)); break; 274 | case meta_trackname: 275 | text(c, len, "trackname", get(len)); break; 276 | case meta_instrument: 277 | text(c, len, "instrument", get(len)); break; 278 | case meta_lyric: 279 | text(c, len, "lyric", get(len)); break; 280 | case meta_marker: 281 | text(c, len, "marker", get(len)); break; 282 | case meta_cuepoint: 283 | text(c, len, "cuepoint", get(len)); break; 284 | case 8: 285 | case 9: 286 | case 10: 287 | case 11: 288 | case 12: 289 | case 13: 290 | case 14: 291 | case 15: 292 | text(c, len, 0, get(len)); break; 293 | case 0x20: 294 | if (len == 1) 295 | prefixchannel(getbyte()); 296 | else 297 | meta(c, len, get(len)); 298 | break; 299 | case 0x21: 300 | if (len == 1) 301 | prefixport(getbyte()); 302 | else 303 | meta(c, len, get(len)); 304 | break; 305 | case 0x2F: 306 | end(); 307 | break; 308 | case 0x51: 309 | tempo(gettri()); 310 | break; 311 | case 0x54: 312 | if (len == 5) 313 | { 314 | unsigned char *s = get(len); 315 | 316 | smpteofs(s[0], s[1], s[2], s[3], s[4]); 317 | } 318 | else 319 | meta(c, len, get(len)); 320 | break; 321 | case 0x58: 322 | if (len == 4) 323 | { 324 | int nom, clicksperbeat, notes32perbeat, log2denom; 325 | 326 | nom = getbyte(); 327 | log2denom = getbyte(); 328 | 329 | clicksperbeat = getbyte(); 330 | notes32perbeat = getbyte(); 331 | tact(nom, 1 << log2denom, clicksperbeat, notes32perbeat); 332 | } 333 | else 334 | meta(c, len, get(len)); 335 | break; 336 | case 0x59: 337 | if (len == 2) 338 | { 339 | signed char s[2]; 340 | 341 | s[0] = (signed char)getbyte(); // sf 342 | s[1] = (signed char)getbyte(); // mi 343 | if (s[0] >= -7 && s[0] <= +7 && s[1] >= 0 && s[1] <= 1) 344 | key(s[0], s[1]); 345 | else 346 | meta(c, len, (unsigned char*)s); 347 | } 348 | else 349 | meta(c, len, get(len)); 350 | break; 351 | default: 352 | meta(c, len, get(len)); 353 | break; 354 | } 355 | seek(endpos); 356 | } 357 | break; 358 | default: 359 | { 360 | if (midicode < 0) 361 | return 0; 362 | if (midicode < 0x80) 363 | { 364 | char msg[30]; 365 | 366 | sprintf(msg, "illegal midi command %02X", midicode); 367 | error(msg); 368 | return 0; 369 | } 370 | int channel = midicode & 0x0F; 371 | int cmd = midicode & 0xF0; 372 | 373 | switch(cmd) 374 | { 375 | case 0x80: 376 | case 0x90: 377 | { 378 | unsigned char* c; 379 | 380 | lastcode_ = midicode; 381 | c = get(2); if (!c) return 0; 382 | if (options_ & OPTION_NONOTEEVENTS) 383 | event(midicode, 2, c); 384 | else 385 | { 386 | if (cmd == 0x80 || c[1] == 0) 387 | noteoff(channel, c[0], c[1]); 388 | else 389 | noteon(channel, c[0], c[1]); 390 | } 391 | } 392 | break; 393 | case 0xA0: 394 | { 395 | unsigned char* p = get(2); if (!p) return 0; 396 | 397 | lastcode_ = midicode; 398 | 399 | if (options_ & OPTION_NOPOLYEVENTS) 400 | event(midicode, 2, p); 401 | else 402 | { 403 | polyaftertouch(channel, p[0], p[1]); 404 | } 405 | } 406 | break; 407 | case 0xB0: 408 | { 409 | unsigned char* p = get(2); if (!p) return 0; 410 | 411 | lastcode_ = midicode; 412 | if (options_ & OPTION_NOCONTROLEVENTS) 413 | event(midicode, 2, p); 414 | else if (options_ & OPTION_NOCONTROLS) 415 | control(channel, p[0], p[1]); 416 | else 417 | switch(p[0]) 418 | { 419 | case ctrl_highbank: highbank(channel, p[1]); break; 420 | case ctrl_wheel: wheel(channel, p[1]); break; 421 | case ctrl_breath: breath(channel, p[1]); break; 422 | case ctrl_foot: foot(channel, p[1]); break; 423 | case ctrl_portamentotime: portamentotime(channel, p[1]); break; 424 | case ctrl_data: data(channel, p[1]); break; 425 | case ctrl_volume: volume(channel, p[1]); break; 426 | case ctrl_balance: balance(channel, p[1]); break; 427 | case ctrl_expression: expression(channel, p[1]); break; 428 | case ctrl_lowbank: lowbank(channel, p[1]); break; 429 | case ctrl_hold: hold(channel, p[1]); break; 430 | case ctrl_reverb: reverb(channel, p[1]); break; 431 | case ctrl_chorus: chorus(channel, p[1]); break; 432 | case ctrl_datainc: datainc(channel, p[1]); break; 433 | case ctrl_datadec: datadec(channel, p[1]); break; 434 | case ctrl_lowrpn: lowrpn(channel, p[1]); break; 435 | case ctrl_highrpn: 436 | case ctrl_resetctrlrs: resetctrlrs(channel, p[1]); break; 437 | case ctrl_allnotesoff: allnotesoff(channel, p[1]); break; 438 | { 439 | unsigned char *c = need(8); 440 | 441 | if (c && 442 | c[0] == 0 && c[1] == midicode && c[2] == ctrl_lowrpn && c[3] == 0 && 443 | c[4] == 0 && c[5] == midicode && c[6] == ctrl_data) 444 | { 445 | c = get(8); 446 | pitchbendrange(channel, c[7]); 447 | } 448 | else 449 | highrpn(channel, p[1]); 450 | break; 451 | } 452 | default: 453 | control(channel, p[0], p[1]); 454 | break; 455 | } 456 | break; 457 | } 458 | 459 | case 0xC0: 460 | { 461 | unsigned char* p = get(1); if (!p) return 0; 462 | 463 | lastcode_ = midicode; 464 | if (options_ & OPTION_NOPROGRAMEVENTS) 465 | event(midicode, 1, p); 466 | else 467 | program(channel, p[0]); 468 | } 469 | break; 470 | case 0xD0: 471 | { 472 | unsigned char* p = get(1); if (!p) return 0; 473 | 474 | lastcode_ = midicode; 475 | if (options_ & OPTION_NOAFTERTOUCHEVENTS) 476 | event(midicode, 1, p); 477 | else 478 | aftertouch(channel, p[0]); 479 | } 480 | break; 481 | case 0xE0: 482 | { 483 | unsigned char* p = get(2); if (!p) return 0; 484 | unsigned val = unsigned(p[0]) + (unsigned(p[1]) << 7); 485 | 486 | lastcode_ = midicode; 487 | if (options_ & OPTION_NOPITCHBENDEVENTS) 488 | event(midicode, 2, p); 489 | else 490 | pitchbend(channel, val); 491 | } 492 | break; 493 | default: 494 | { 495 | char msg[30]; 496 | 497 | sprintf(msg, "unexpected command byte %02X", midicode); 498 | error(msg); 499 | return 0; 500 | } 501 | } 502 | } 503 | break; 504 | } 505 | return (int)(curpos_ - pos_); 506 | } 507 | 508 | int MidiRead::runtrack(int trackno) 509 | { 510 | unsigned long trackpos = curpos_, trackend; 511 | 512 | curtime_ = 0; 513 | lastcode_ = -1; 514 | pos_ = curpos_; 515 | if (!f_) 516 | { 517 | error("file not open"); 518 | return 0; 519 | } 520 | 521 | if (getlong() != MTrk) 522 | { 523 | error("missing midi track MTrk"); 524 | return 0; 525 | } 526 | tracklen_ = getlong(); 527 | track(trackno, tracklen_, curchannel_ = scanchannel(tracklen_)); 528 | trackpos = curpos_; 529 | trackend = trackpos + tracklen_; 530 | lastcode_ = -1; 531 | if ((options_ & OPTION_NOEVENTS) == 0) 532 | while (curpos_ < trackpos + tracklen_) 533 | { 534 | int newpercent = (int)((curpos_ * 100) / filesize_); 535 | if (newpercent != percent_) 536 | percent(percent_ = newpercent); 537 | 538 | unsigned long delta = getdelta(); 539 | if ( delta >= NOTREALISTIC_PAUSE ) 540 | warning("Unrealistic large pause found"); 541 | time(delta); 542 | curtime_ += delta; 543 | if (runevent(trackend) <= 0) 544 | return 0; 545 | } 546 | seek(trackend); 547 | pos_ = curpos_; 548 | endtrack(trackno); 549 | return 1; 550 | } 551 | 552 | void MidiRead::setchannel(int channel) 553 | { 554 | assert(channel >= -1 && channel <= 15); 555 | curchannel_ = channel; 556 | } 557 | 558 | int MidiRead::scansysevent(unsigned long maxlen) 559 | { 560 | int n = sizeof(buf_); 561 | unsigned char* c, *p; 562 | long savepos = curpos_; 563 | int len; 564 | 565 | if (maxlen < n) 566 | n = (int)maxlen; 567 | c = need(n); 568 | if (!c) 569 | { 570 | n = buflen_; 571 | c = need(n); 572 | if (!c) 573 | return 0; 574 | } 575 | 576 | if (c[0] < 0x80) 577 | { 578 | // short sysex 0..127 bytes 579 | len = c[0]; 580 | if (n >= len+1) 581 | { 582 | if (c[len] == 0xF7) 583 | return len+1; 584 | } 585 | } 586 | else if (n >= 2 && c[1] < 0x80) 587 | { 588 | // 128..16383 bytes 589 | int len = (int(c[0] & 0x7f) << 7) + c[1]; 590 | if (n >= len + 2) 591 | { 592 | if (c[len+1] == 0xF7) 593 | return len+2; 594 | } 595 | } 596 | // sysex events without length information? 597 | p = (unsigned char*)memchr(c, 0xF7, n); 598 | if (p) 599 | return (int)(p - c + 1); 600 | seek(savepos); 601 | return 0; 602 | } 603 | 604 | int MidiRead::scanchannel(unsigned long maxlen) 605 | { 606 | int n = 512; 607 | unsigned char* c; 608 | int firstchannel = NOCHANNEL; 609 | int channel, code; 610 | long savepos = curpos_, endpos; 611 | int lastcode = -1; 612 | 613 | if (maxlen < n) 614 | n = (int)maxlen; 615 | c = need(n); 616 | if (!c) 617 | return -1; 618 | 619 | endpos = curpos_ + n; 620 | while (curpos_ < endpos) 621 | { 622 | channel = NOCHANNEL; 623 | getdelta(); 624 | c = need(1); 625 | if (!c || *c >= 0x80 || lastcode < 0) 626 | code = getbyte(); 627 | else 628 | code = lastcode; 629 | switch(code & 0xF0) 630 | { 631 | case 0xF0: 632 | switch(code) 633 | { 634 | case 0xFF: 635 | getbyte(); 636 | get((int)getdelta()); 637 | break; 638 | case 0xf0: // sysex 639 | { 640 | int len = scansysevent(endpos-curpos_); 641 | if (!get(len)) 642 | goto endscan; 643 | } 644 | break; 645 | case 0xf2: get(2); break; 646 | case 0xf3: getbyte(); break; 647 | case 0xf6: 648 | case 0xf8: 649 | case 0xfa: 650 | case 0xfb: 651 | case 0xfc: 652 | case 0xfe: 653 | break; 654 | default: 655 | goto endscan; 656 | } 657 | break; 658 | case 0x80: 659 | case 0x90: 660 | case 0xA0: 661 | case 0xB0: 662 | case 0xE0: 663 | channel = code & 15; 664 | get(2); 665 | break; 666 | case 0xC0: 667 | case 0xD0: 668 | channel = code & 15; 669 | getbyte(); 670 | break; 671 | default: 672 | goto endscan; 673 | } 674 | if (code < 0xf0) 675 | lastcode = code; 676 | if (channel >= 0) 677 | { 678 | if (firstchannel < 0) 679 | firstchannel = channel; 680 | else if (channel != firstchannel) 681 | { 682 | firstchannel = MULTICHANNEL; 683 | break; 684 | } 685 | } 686 | } 687 | endscan: 688 | seek(savepos); 689 | return firstchannel; 690 | } 691 | 692 | int MidiRead::getbyte() 693 | { 694 | unsigned char* c = get(1); 695 | 696 | if (c) 697 | return *c; 698 | return -1; 699 | } 700 | 701 | unsigned MidiRead::getword() 702 | { 703 | unsigned char* c = get(2); 704 | unsigned n = 0; 705 | 706 | if (c) 707 | { 708 | n = *c++; 709 | n = (n << 8) + *c++; 710 | } 711 | return n; 712 | } 713 | 714 | unsigned long MidiRead::gettri() 715 | { 716 | unsigned char* c = get(3); 717 | unsigned long n = 0; 718 | 719 | if (c) 720 | { 721 | n = *c++; 722 | n = (n << 8) + *c++; 723 | n = (n << 8) + *c++; 724 | } 725 | return n; 726 | } 727 | 728 | unsigned long MidiRead::getlong() 729 | { 730 | unsigned char* c = get(4); 731 | unsigned long n = 0; 732 | 733 | if (c) 734 | { 735 | n = *c++; 736 | n = (n << 8) + *c++; 737 | n = (n << 8) + *c++; 738 | n = (n << 8) + *c++; 739 | } 740 | return n; 741 | } 742 | 743 | unsigned long MidiRead::getdelta() 744 | { 745 | unsigned long n = 0; 746 | int i = 0, c; 747 | 748 | curdeltalen_ = 0; 749 | for (i = 0; i < 4; i++) 750 | { 751 | c = getbyte(); 752 | if (c < 0) 753 | { 754 | error("unexpected end of file"); 755 | return 0; 756 | } 757 | curdeltalen_++; 758 | n = (n << 7) + (c & 0x7f); 759 | if ((c & 0x80) == 0) 760 | break; 761 | } 762 | return n; 763 | } 764 | 765 | unsigned char* MidiRead::need(int n) 766 | { 767 | assert(n >= 0); 768 | if (n == 0) 769 | return 0; 770 | if (n > buflen_) 771 | { 772 | if (!f_) 773 | return 0; 774 | if (n > sizeof(buf_)) 775 | return 0; 776 | if (n > sizeof(buf_) - bufpos_) 777 | { 778 | // move to beginning of buf 779 | memmove(buf_, buf_ + bufpos_, buflen_); 780 | bufpos_ = 0; 781 | } 782 | // add new data at end 783 | if (sizeof(buf_) - bufpos_ - buflen_ > 0) 784 | { 785 | fseek(f_, curpos_+buflen_, SEEK_SET); 786 | int l = fread(buf_+bufpos_+buflen_, 1, sizeof(buf_) - bufpos_ - buflen_, f_); 787 | if (l > 0) 788 | buflen_ += l; 789 | else if (l == 0) 790 | ; 791 | } 792 | } 793 | if (n <= buflen_) 794 | return buf_ + bufpos_; 795 | return 0; 796 | } 797 | 798 | unsigned char* MidiRead::get(int n) 799 | { 800 | unsigned char* s; 801 | 802 | s = need(n); 803 | if (s) 804 | { 805 | buflen_ -= n; 806 | bufpos_ += n; 807 | curpos_ += n; 808 | } 809 | else if (n > sizeof(buf_)) 810 | warning("midi event larger than internal bufsize ignored"); 811 | else if (n > 0) 812 | { 813 | error("unexpected end of file"); 814 | exit(1); 815 | } 816 | return s; 817 | } 818 | 819 | void MidiRead::seek(long pos) 820 | { 821 | if (pos == curpos_ || pos < 0) 822 | return; 823 | if (pos >= curpos_ - bufpos_ && pos < curpos_ - bufpos_ + buflen_) 824 | { 825 | int n = (int)(pos - curpos_ + bufpos_); 826 | 827 | if (n < bufpos_) 828 | { 829 | buflen_ += bufpos_ - n; 830 | bufpos_ -= bufpos_ - n; 831 | } 832 | else 833 | { 834 | buflen_ -= n - bufpos_; 835 | bufpos_ += n - bufpos_; 836 | } 837 | curpos_ = pos; 838 | } 839 | else 840 | { 841 | fseek(f_, curpos_ = pos, SEEK_SET); 842 | bufpos_ = buflen_ = 0; 843 | } 844 | } 845 | 846 | unsigned long MidiRead::microsec(unsigned long units, unsigned long msperbeat) 847 | { 848 | assert(clicks_ != 0); // call runhead() or run() first! 849 | 850 | if (units > msperbeat) 851 | return (units / clicks_) * msperbeat; 852 | else 853 | return units * (msperbeat / clicks_); 854 | } 855 | 856 | 857 | long MidiRead::units(unsigned long microsec, unsigned long msperbeat) 858 | { 859 | assert(clicks_ != 0); // call runhead() or run() first! 860 | assert(msperbeat > 0); // invalid tempo! 861 | int clicks = clicks_; 862 | while ((msperbeat & 1) == 0) 863 | { 864 | if ((clicks & 1) == 0) 865 | clicks >>= 1; 866 | else if ((microsec & 1) == 0) 867 | microsec >>= 1; 868 | else 869 | break; 870 | msperbeat >>= 1; 871 | } 872 | if (microsec >= 0x10000L) 873 | return (microsec / msperbeat) * clicks; 874 | else 875 | return (microsec * clicks) / msperbeat; 876 | } 877 | 878 | 879 | static const char* GMProg[128] = 880 | { 881 | "Piano", "BritePiano", "HammerPiano", "HonkeyTonk", "NewTines", "DigiPiano", "Harpsicord", "Clav", 882 | "Celesta", "Glocken", "MusicBox", "Vibes", "Marimba", "Xylophon", "Tubular", "Santur", 883 | "FullOrgan", "PercOrgan", "BX-3Organ", "ChurchPipe", "Positive", "Musette", "Harmonica", "Tango", 884 | "ClassicGtr", "A.Guitar", "JazzGuitar", "CleanGtr", "MuteGuitar", "OverDrive", "DistGuitar", "RockMonics", 885 | "JazzBass", "DeepBass", "PickBass", "FretLess", "SlapBass1", "SlapBass2", "SynthBass1", "SynthBass2", 886 | "Violin", "Viola", "Cello", "ContraBass", "TremoloStr", "Pizzicato", "Harp", "Timpani", 887 | "Marcato", "SlowString", "AnalogPad", "StringPad", "Choir", "DooVoice", "Voices", "OrchHit", 888 | "Trumpet", "Trombone", "Tuba", "MutedTrumpet", "FrenchHorn", "Brass", "SynBrass1", "SynBrass2", 889 | "SopranoSax", "AltoSax", "TenorSax", "BariSax", "SweetOboe", "EnglishHorn", "BasoonOboe", "Clarinet", 890 | "Piccolo", "Flute", "Recorder", "PanFlute", "Bottle", "Shakuhachi","Whistle", "Ocarina", 891 | "SquareWave", "SawWave", "SynCalinope", "SynChiff", "Charang", "AirChorus", "Rezzo4ths", "Bass&Lead", 892 | "Fantasia", "WarmPad", "PolyPad", "GhostPad", "BowedGlas", "MetalPad", "HaloPad", "Sweep", 893 | "IceRain", "SoundTrack", "Crystal", "Atmosphere", "Brightness", "Goblin", "EchoDrop", "StarTheme", 894 | "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba","Scotland","Fiddle", "Shanai", 895 | "MetalBell", "Agogo", "SteelDrums", "Woodblock", "Taiko", "Tom", "SynthTom", "RevCymbal", 896 | "FretNoise", "NoiseChiff", "Seashore", "Birds", "Telephone", "Helicopter", "Stadium!!", "GunShot" 897 | }; 898 | 899 | const char* MidiRead::notename(unsigned char note) 900 | { 901 | static char name[5]; 902 | char* s = name; 903 | 904 | switch(note % 12) 905 | { 906 | case 0: *s++ = 'c'; break; 907 | case 1: *s++ = 'c'; *s++ = '#'; break; 908 | case 2: *s++ = 'd'; break; 909 | case 3: *s++ = 'd'; *s++ = '#'; break; 910 | case 4: *s++ = 'e'; break; 911 | case 5: *s++ = 'f'; break; 912 | case 6: *s++ = 'f'; *s++ = '#'; break; 913 | case 7: *s++ = 'g'; break; 914 | case 8: *s++ = 'g'; *s++ = '#'; break; 915 | case 9: *s++ = 'a'; break; 916 | case 10: *s++ = 'a'; *s++ = '#'; break; 917 | case 11: *s++ = 'b'; break; // former 'h': German language only 918 | } 919 | 920 | sprintf(s, "%d", (note / 12)-1); // octave (assuming Piano C4 is 60) 921 | return (const char*) name; 922 | } 923 | 924 | 925 | const char* MidiRead::progname(int n, int channel) 926 | { 927 | static char defname[10] = ""; 928 | 929 | if (channel == 9) // drum programs 930 | { 931 | switch(n) 932 | { 933 | case 0: return "Dr1"; 934 | case 0x10: return "Dr2"; 935 | case 0x19: return "Dr3"; 936 | case 0x20: return "Dr4"; 937 | case 0x28: return "Dr5"; 938 | case 0x40: return "Dr6"; 939 | case 0x18: return "Dr7"; 940 | case 0x30: return "Dr8"; 941 | } 942 | } 943 | else if (n >= 0 && n <= 127) 944 | return GMProg[n]; 945 | def: 946 | sprintf(defname, "%d", n); 947 | return (const char*)defname; 948 | } 949 | 950 | void MidiRead::head(unsigned version, unsigned tracks, unsigned clicksperquarter) 951 | { 952 | } 953 | 954 | void MidiRead::track(int trackno, long length, int channel) 955 | { 956 | } 957 | 958 | void MidiRead::endtrack(int trackno) 959 | { 960 | } 961 | 962 | void MidiRead::event(int what, int len, unsigned char* data) 963 | { 964 | } 965 | 966 | void MidiRead::seqnumber(unsigned int seqno) 967 | { 968 | } 969 | 970 | void MidiRead::smpteofs(int hour, int min, int sec, int frame, int fracframe) 971 | { 972 | } 973 | 974 | void MidiRead::key(int signature, int isminor) 975 | { 976 | } 977 | 978 | void MidiRead::prefixchannel(unsigned char channel) 979 | { 980 | } 981 | 982 | void MidiRead::prefixport(unsigned char port) 983 | { 984 | } 985 | 986 | void MidiRead::text(int what, int len, const char* whattext, const unsigned char* txt) 987 | { 988 | } 989 | 990 | void MidiRead::meta(int what, int len, const unsigned char* data) 991 | { 992 | } 993 | 994 | void MidiRead::end() 995 | { 996 | } 997 | 998 | void MidiRead::tact(int nom, int denom, int v1, int v2) 999 | { 1000 | } 1001 | 1002 | void MidiRead::tempo(unsigned long ticks) 1003 | { 1004 | } 1005 | 1006 | void MidiRead::program(int channel, int program) 1007 | { 1008 | } 1009 | 1010 | void MidiRead::control(int channel, int what, int value) 1011 | { 1012 | } 1013 | 1014 | void MidiRead::highbank(int channel, int val) 1015 | { 1016 | control(channel, ctrl_highbank, val); 1017 | } 1018 | 1019 | void MidiRead::wheel(int channel, int val) 1020 | { 1021 | control(channel, ctrl_wheel, val); 1022 | } 1023 | 1024 | void MidiRead::breath(int channel, int val) 1025 | { 1026 | control(channel, ctrl_breath, val); 1027 | } 1028 | 1029 | void MidiRead::foot(int channel, int val) 1030 | { 1031 | control(channel, ctrl_foot, val); 1032 | } 1033 | 1034 | void MidiRead::portamentotime(int channel, int val) 1035 | { 1036 | control(channel, ctrl_portamentotime, val); 1037 | } 1038 | 1039 | void MidiRead::data(int channel, int val) 1040 | { 1041 | control(channel, ctrl_data, val); 1042 | } 1043 | 1044 | void MidiRead::volume(int channel, int val) 1045 | { 1046 | control(channel, ctrl_volume, val); 1047 | } 1048 | 1049 | void MidiRead::balance(int channel, int val) 1050 | { 1051 | control(channel, ctrl_balance, val); 1052 | } 1053 | 1054 | void MidiRead::expression(int channel, int val) 1055 | { 1056 | control(channel, ctrl_expression, val); 1057 | } 1058 | 1059 | void MidiRead::lowbank(int channel, int val) 1060 | { 1061 | control(channel, ctrl_lowbank, val); 1062 | } 1063 | 1064 | void MidiRead::hold(int channel, int val) 1065 | { 1066 | control(channel, ctrl_hold, val); 1067 | } 1068 | 1069 | void MidiRead::reverb(int channel, int val) 1070 | { 1071 | control(channel, ctrl_reverb, val); 1072 | } 1073 | 1074 | void MidiRead::chorus(int channel, int val) 1075 | { 1076 | control(channel, ctrl_chorus, val); 1077 | } 1078 | 1079 | void MidiRead::datainc(int channel, int val) 1080 | { 1081 | control(channel, ctrl_datainc, val); 1082 | } 1083 | 1084 | void MidiRead::datadec(int channel, int val) 1085 | { 1086 | control(channel, ctrl_datadec, val); 1087 | } 1088 | 1089 | void MidiRead::lowrpn(int channel, int val) 1090 | { 1091 | control(channel, ctrl_lowrpn, val); 1092 | } 1093 | 1094 | void MidiRead::highrpn(int channel, int val) 1095 | { 1096 | control(channel, ctrl_highrpn, val); 1097 | } 1098 | 1099 | void MidiRead::resetctrlrs(int channel, int val) 1100 | { 1101 | control(channel, ctrl_resetctrlrs, val); 1102 | } 1103 | 1104 | void MidiRead::allnotesoff(int channel, int val) 1105 | { 1106 | control(channel, ctrl_allnotesoff, val); 1107 | } 1108 | 1109 | void MidiRead::pitchbendrange(int channel, int halfnotes) 1110 | { 1111 | highrpn(channel, 0); 1112 | time(0); 1113 | lowrpn(channel, 0); 1114 | time(0); 1115 | data(channel, halfnotes); 1116 | } 1117 | 1118 | void MidiRead::noteon(int channel, int note, int vel) 1119 | { 1120 | } 1121 | 1122 | void MidiRead::noteoff(int channel, int note, int vel) 1123 | { 1124 | } 1125 | 1126 | void MidiRead::time(unsigned long ticks) 1127 | { 1128 | } 1129 | 1130 | void MidiRead::pitchbend(int channel, int val) 1131 | { 1132 | } 1133 | 1134 | void MidiRead::polyaftertouch(int channel, int note, int val) 1135 | { 1136 | } 1137 | 1138 | void MidiRead::aftertouch(int channel, int val) 1139 | { 1140 | } 1141 | 1142 | void MidiRead::songpos(unsigned pos) 1143 | { 1144 | } 1145 | 1146 | void MidiRead::songselect(unsigned char song) 1147 | { 1148 | } 1149 | 1150 | void MidiRead::tunerequest() 1151 | { 1152 | } 1153 | 1154 | void MidiRead::timingclock() 1155 | { 1156 | } 1157 | 1158 | void MidiRead::start() 1159 | { 1160 | } 1161 | 1162 | void MidiRead::cont() 1163 | { 1164 | } 1165 | 1166 | void MidiRead::stop() 1167 | { 1168 | } 1169 | 1170 | void MidiRead::activesense() 1171 | { 1172 | } 1173 | 1174 | void MidiRead::sysex(int syslen, const unsigned char* sysdata) 1175 | { 1176 | } 1177 | 1178 | void MidiRead::gmreset() 1179 | { 1180 | } 1181 | 1182 | void MidiRead::gsreset() 1183 | { 1184 | } 1185 | 1186 | void MidiRead::gsexit() 1187 | { 1188 | } 1189 | 1190 | void MidiRead::xgreset() 1191 | { 1192 | } 1193 | 1194 | void MidiRead::endmidi() 1195 | { 1196 | } 1197 | 1198 | void MidiRead::percent(int perc) 1199 | { 1200 | } 1201 | 1202 | void MidiRead::error(const char* msg) 1203 | { 1204 | fprintf(stderr, "error: %s\n", msg); 1205 | } 1206 | 1207 | void MidiRead::warning(const char* msg) 1208 | { 1209 | fprintf(stderr, "warning: %s\n", msg); 1210 | } 1211 | 1212 | FILE* MidiRead::getf() 1213 | { 1214 | return f_; 1215 | } 1216 | 1217 | // class MidiWrite 1218 | 1219 | const char* MidiWrite::copyright() 1220 | { 1221 | return (const char*)::copyright; 1222 | } 1223 | 1224 | MidiWrite::MidiWrite(const char* filename) 1225 | { 1226 | midiname_ = filename; 1227 | if (midiname_) 1228 | f_ = fopen(midiname_, WRITE_BINARY); 1229 | else 1230 | f_ = 0; 1231 | trackpos_ = -1; 1232 | curpos_ = 0; 1233 | trackchannel_ = -1; 1234 | 1235 | bufpos_ = 0; 1236 | buflen_ = 0; 1237 | filesize_ = 0; 1238 | trackcount_ = 0; 1239 | curtime_ = curdelta_ = 0; 1240 | lastcode_ = -1; 1241 | clicks_ = 0; 1242 | } 1243 | 1244 | MidiWrite::~MidiWrite() 1245 | { 1246 | if (trackcount_ > 0) 1247 | { 1248 | seek(10); 1249 | putword(trackcount_); 1250 | } 1251 | if (trackpos_ > 0) 1252 | endtrack(); 1253 | flush(); 1254 | if (f_) 1255 | fclose(f_); 1256 | } 1257 | 1258 | void MidiWrite::head(int version, int tracks, unsigned clicksperquarter) 1259 | { 1260 | seek(0); 1261 | putlong(MThd); 1262 | putlong(6); 1263 | putword(version); 1264 | putword(tracks); // unknown 1265 | putword(clicks_ = clicksperquarter); 1266 | } 1267 | 1268 | void MidiWrite::track() 1269 | { 1270 | if (trackpos_ > 0) 1271 | endtrack(); 1272 | endtrack_ = 0; 1273 | lastcode_ = -1; 1274 | curtime_ = curdelta_ = 0; 1275 | seek(trackpos_ = filesize_); 1276 | putlong(MTrk); 1277 | putlong(0); // unknown yet 1278 | trackcount_++; 1279 | } 1280 | 1281 | void MidiWrite::endtrack() 1282 | { 1283 | seek(filesize_); 1284 | if (!endtrack_) 1285 | end(); 1286 | if (trackpos_ <= 0) 1287 | return; 1288 | seek(trackpos_+4); 1289 | putlong(filesize_ - trackpos_ - 8); 1290 | trackpos_ = 0; 1291 | } 1292 | 1293 | void MidiWrite::event(int what, int len, const unsigned char* data) 1294 | { 1295 | puttime(); 1296 | putcode(what); 1297 | put(len, data); 1298 | } 1299 | 1300 | void MidiWrite::prefixchannel(unsigned char channel) 1301 | { 1302 | meta(0x20, 1, &channel); 1303 | } 1304 | 1305 | void MidiWrite::prefixport(unsigned char port) 1306 | { 1307 | meta(0x21, 1, &port); 1308 | } 1309 | 1310 | void MidiWrite::text(int what, int len, const unsigned char* txt) 1311 | { 1312 | if (len < 0) 1313 | len = strlen((char *)txt); 1314 | meta(what, len, txt); 1315 | } 1316 | 1317 | void MidiWrite::meta(int what, int len, const unsigned char* data) 1318 | { 1319 | puttime(); 1320 | putcode(0xff); 1321 | putbyte(what); 1322 | putdelta(len); 1323 | put(len, data); 1324 | } 1325 | 1326 | void MidiWrite::end() 1327 | { 1328 | if (endtrack_) // don't need end of track event twice 1329 | return; 1330 | endtrack_ = 1; 1331 | meta(0x2f, 0, 0); 1332 | } 1333 | 1334 | void MidiWrite::tact(int nom, int denom, int v1, int v2) 1335 | { 1336 | int log2denom; 1337 | 1338 | switch(denom) 1339 | { 1340 | case 1: log2denom = 0; break; 1341 | case 2: log2denom = 1; break; 1342 | case 4: log2denom = 2; break; 1343 | case 8: log2denom = 3; break; 1344 | case 16: log2denom = 4; break; 1345 | case 32: log2denom = 5; break; 1346 | case 64: log2denom = 6; break; 1347 | case 128: log2denom = 7; break; 1348 | case 256: log2denom = 8; break; 1349 | default: 1350 | log2denom = 2; 1351 | assert((1 << log2denom) == denom); 1352 | } 1353 | puttime(); 1354 | putcode(0xff); 1355 | putbyte(0x58); 1356 | putbyte(4); 1357 | putbyte(nom); 1358 | putbyte(log2denom); 1359 | putbyte(v1); 1360 | putbyte(v2); 1361 | } 1362 | 1363 | void MidiWrite::seqnumber(unsigned int seqno) 1364 | { 1365 | puttime(); 1366 | putcode(0xff); 1367 | putbyte(0x00); 1368 | putbyte(2); 1369 | putword(seqno); 1370 | } 1371 | 1372 | void MidiWrite::smpteofs(int hour, int min, int sec, int frame, int fracframe) 1373 | { 1374 | puttime(); 1375 | putcode(0xff); 1376 | putbyte(0x54); 1377 | putbyte(5); 1378 | putbyte(hour); 1379 | putbyte(min); 1380 | putbyte(sec); 1381 | putbyte(frame); 1382 | putbyte(fracframe); 1383 | } 1384 | 1385 | void MidiWrite::key(int signature, int isminor) 1386 | { 1387 | puttime(); 1388 | putcode(0xff); 1389 | putbyte(0x59); 1390 | putbyte(2); 1391 | putbyte((signed char)signature); 1392 | putbyte((signed char)isminor); 1393 | } 1394 | 1395 | void MidiWrite::tempo(unsigned long ticks) 1396 | { 1397 | puttime(); 1398 | putcode(0xff); 1399 | putbyte(0x51); 1400 | putbyte(3); 1401 | puttri(ticks); 1402 | } 1403 | 1404 | void MidiWrite::program(int channel, int prg) 1405 | { 1406 | assert(channel >= 0 && channel < 16); 1407 | puttime(); 1408 | putcode(0xC0 + channel); 1409 | putbyte(prg); 1410 | } 1411 | 1412 | void MidiWrite::control(int channel, int what, int val) 1413 | { 1414 | assert(channel >= 0 && channel < 16); 1415 | puttime(); 1416 | putcode(0xB0 + channel); 1417 | putbyte(what); 1418 | putbyte(val); 1419 | } 1420 | 1421 | void MidiWrite::highbank(int channel, int val) 1422 | { 1423 | control(channel, ctrl_highbank, val); 1424 | } 1425 | 1426 | void MidiWrite::wheel(int channel, int val) 1427 | { 1428 | control(channel, ctrl_wheel, val); 1429 | } 1430 | 1431 | void MidiWrite::breath(int channel, int val) 1432 | { 1433 | control(channel, ctrl_breath, val); 1434 | } 1435 | 1436 | void MidiWrite::foot(int channel, int val) 1437 | { 1438 | control(channel, ctrl_foot, val); 1439 | } 1440 | 1441 | void MidiWrite::portamentotime(int channel, int val) 1442 | { 1443 | control(channel, ctrl_portamentotime, val); 1444 | } 1445 | 1446 | void MidiWrite::data(int channel, int val) 1447 | { 1448 | control(channel, ctrl_data, val); 1449 | } 1450 | 1451 | void MidiWrite::volume(int channel, int val) 1452 | { 1453 | control(channel, ctrl_volume, val); 1454 | } 1455 | 1456 | void MidiWrite::balance(int channel, int val) 1457 | { 1458 | control(channel, ctrl_balance, val); 1459 | } 1460 | 1461 | void MidiWrite::expression(int channel, int val) 1462 | { 1463 | control(channel, ctrl_expression, val); 1464 | } 1465 | 1466 | void MidiWrite::lowbank(int channel, int val) 1467 | { 1468 | control(channel, ctrl_lowbank, val); 1469 | } 1470 | 1471 | void MidiWrite::hold(int channel, int val) 1472 | { 1473 | control(channel, ctrl_hold, val); 1474 | } 1475 | 1476 | void MidiWrite::reverb(int channel, int val) 1477 | { 1478 | control(channel, ctrl_reverb, val); 1479 | } 1480 | 1481 | void MidiWrite::chorus(int channel, int val) 1482 | { 1483 | control(channel, ctrl_chorus, val); 1484 | } 1485 | 1486 | void MidiWrite::datainc(int channel, int val) 1487 | { 1488 | control(channel, ctrl_datainc, val); 1489 | } 1490 | 1491 | void MidiWrite::datadec(int channel, int val) 1492 | { 1493 | control(channel, ctrl_datadec, val); 1494 | } 1495 | 1496 | void MidiWrite::lowrpn(int channel, int val) 1497 | { 1498 | control(channel, ctrl_lowrpn, val); 1499 | } 1500 | 1501 | void MidiWrite::highrpn(int channel, int val) 1502 | { 1503 | control(channel, ctrl_highrpn, val); 1504 | } 1505 | 1506 | void MidiWrite::resetctrlrs(int channel, int val) 1507 | { 1508 | control(channel, ctrl_resetctrlrs, val); 1509 | } 1510 | 1511 | void MidiWrite::allnotesoff(int channel, int val) 1512 | { 1513 | control(channel, ctrl_allnotesoff, val); 1514 | } 1515 | 1516 | void MidiWrite::noteon(int channel, int note, int vel) 1517 | { 1518 | assert(channel >= 0 && channel < 16); 1519 | puttime(); 1520 | putcode(0x90+channel); 1521 | putbyte(note); 1522 | putbyte(vel); 1523 | } 1524 | 1525 | void MidiWrite::noteoff(int channel, int note, int vel) 1526 | { 1527 | assert(channel >= 0 && channel < 16); 1528 | puttime(); 1529 | if (vel != 0 || lastcode_ < 0 || (lastcode_ & 0xF0) != 0x90) 1530 | putcode(0x80+channel); 1531 | else 1532 | putcode(0x90+channel); // vel == 0! 1533 | putbyte(note); 1534 | putbyte(vel); 1535 | } 1536 | 1537 | void MidiWrite::time(unsigned long ticks) 1538 | { 1539 | if ( ticks >= NOTREALISTIC_PAUSE ) 1540 | warning("generating unrealistic large pause"); 1541 | curdelta_ += ticks; 1542 | curtime_ += ticks; 1543 | } 1544 | 1545 | void MidiWrite::cleardelta() 1546 | { 1547 | curtime_ -= curdelta_; 1548 | curdelta_ = 0; 1549 | } 1550 | 1551 | void MidiWrite::pitchbend(int channel, int val) 1552 | { 1553 | assert(channel >= 0 && channel < 16); 1554 | puttime(); 1555 | putcode(0xE0 + channel); 1556 | putbyte(val & 0x7F); 1557 | putbyte((val >> 7) & 0x7F); 1558 | } 1559 | 1560 | void MidiWrite::polyaftertouch(int channel, int note, int val) 1561 | { 1562 | assert(channel >= 0 && channel < 16); 1563 | puttime(); 1564 | putcode(0xA0 + channel); 1565 | putbyte(note); 1566 | putbyte(val); 1567 | } 1568 | 1569 | void MidiWrite::aftertouch(int channel, int val) 1570 | { 1571 | assert(channel >= 0 && channel < 16); 1572 | puttime(); 1573 | putcode(0xD0 + channel); 1574 | putbyte(val); 1575 | } 1576 | 1577 | void MidiWrite::songpos(unsigned pos) 1578 | { 1579 | puttime(); 1580 | putcode(0xF2); 1581 | putbyte(pos & 0x7F); 1582 | putbyte((pos >> 7) & 0x7F); 1583 | } 1584 | 1585 | void MidiWrite::songselect(unsigned char song) 1586 | { 1587 | puttime(); 1588 | putcode(0xF3); 1589 | putbyte(song); 1590 | } 1591 | 1592 | void MidiWrite::tunerequest() 1593 | { 1594 | puttime(); 1595 | putcode(0xf6); 1596 | } 1597 | 1598 | void MidiWrite::timingclock() 1599 | { 1600 | puttime(); 1601 | putcode(0xf8); 1602 | } 1603 | 1604 | void MidiWrite::start() 1605 | { 1606 | puttime(); 1607 | putcode(0xfa); 1608 | } 1609 | 1610 | void MidiWrite::cont() 1611 | { 1612 | puttime(); 1613 | putcode(0xfb); 1614 | } 1615 | 1616 | void MidiWrite::stop() 1617 | { 1618 | puttime(); 1619 | putcode(0xfc); 1620 | } 1621 | 1622 | void MidiWrite::activesense() 1623 | { 1624 | puttime(); 1625 | putcode(0xfe); 1626 | } 1627 | 1628 | void MidiWrite::sysex(int syslen, const unsigned char* sysdata) 1629 | { 1630 | puttime(); 1631 | putcode(0xf0); 1632 | if (*sysdata == 0xF0) 1633 | { 1634 | syslen--; 1635 | sysdata++; 1636 | } 1637 | put(syslen, sysdata); 1638 | } 1639 | 1640 | void MidiWrite::gmreset() 1641 | { 1642 | sysex(sysexlen(sysex_gmreset), sysex_gmreset); 1643 | } 1644 | 1645 | void MidiWrite::xgreset() 1646 | { 1647 | sysex(sysexlen(sysex_xgreset), sysex_xgreset); 1648 | } 1649 | 1650 | void MidiWrite::gsreset() 1651 | { 1652 | sysex(sysexlen(sysex_gsreset), sysex_gsreset); 1653 | } 1654 | 1655 | void MidiWrite::gsexit() 1656 | { 1657 | sysex(sysexlen(sysex_gsexit), sysex_gsexit); 1658 | } 1659 | 1660 | void MidiWrite::pitchbendrange(int channel, int halfnotes) 1661 | { 1662 | highrpn(channel, 0); 1663 | lowrpn(channel, 0); 1664 | data(channel, halfnotes); 1665 | } 1666 | 1667 | void MidiWrite::putbyte(unsigned char val) 1668 | { 1669 | put(1, &val); 1670 | } 1671 | 1672 | void MidiWrite::putcode(unsigned char code) 1673 | { 1674 | int put; 1675 | 1676 | assert(code >= 0x80); 1677 | 1678 | if (compress) 1679 | put = !(code == lastcode_ && code <= 0x9f); 1680 | else 1681 | put = 1; 1682 | if (put) 1683 | putbyte(code); 1684 | lastcode_ = code; 1685 | } 1686 | 1687 | static unsigned char c[4]; 1688 | 1689 | void MidiWrite::putword(unsigned val) 1690 | { 1691 | c[1] = (unsigned char)(val & 0xff); val >>= 8; 1692 | c[0] = (unsigned char)(val & 0xff); 1693 | put(2, c); 1694 | } 1695 | 1696 | void MidiWrite::puttri(unsigned long val) 1697 | { 1698 | c[2] = (unsigned char)(val & 0xff); val >>= 8; 1699 | c[1] = (unsigned char)(val & 0xff); val >>= 8; 1700 | c[0] = (unsigned char)(val & 0xff); 1701 | put(3, c); 1702 | } 1703 | 1704 | void MidiWrite::putlong(unsigned long val) 1705 | { 1706 | c[3] = (unsigned char)(val & 0xff); val >>= 8; 1707 | c[2] = (unsigned char)(val & 0xff); val >>= 8; 1708 | c[1] = (unsigned char)(val & 0xff); val >>= 8; 1709 | c[0] = (unsigned char)(val & 0xff); 1710 | put(4, c); 1711 | } 1712 | 1713 | void MidiWrite::putdelta(unsigned long val) 1714 | { 1715 | int i = 0, j = 3; 1716 | while (i < 4) 1717 | { 1718 | c[j] = val & 0x7F; 1719 | if (j < 3) 1720 | c[j] |= 0x80; 1721 | val >>= 7; 1722 | i++; 1723 | if (!val) 1724 | break; 1725 | j--; 1726 | } 1727 | put(i, c+j); 1728 | } 1729 | 1730 | void MidiWrite::puttime() 1731 | { 1732 | putdelta(curdelta_); 1733 | curdelta_ = 0; 1734 | } 1735 | 1736 | void MidiWrite::flush() 1737 | { 1738 | if (buflen_ > 0) 1739 | { 1740 | fseek(f_, curpos_ - bufpos_, SEEK_SET); 1741 | if (fwrite(buf_, buflen_, 1, f_) != 1) 1742 | error("write error (maybe disk full)"); 1743 | assert(ftell(f_) == curpos_ - bufpos_ + buflen_); 1744 | bufpos_ = buflen_ = 0; 1745 | } 1746 | } 1747 | 1748 | void MidiWrite::put(int len, const unsigned char* c) 1749 | { 1750 | if (len <= 0) 1751 | return; 1752 | if (c == 0 || len > sizeof(buf_)) 1753 | return; 1754 | if (sizeof(buf_) - bufpos_ < len) 1755 | flush(); 1756 | memcpy(buf_+bufpos_, c, len); 1757 | bufpos_+=len; 1758 | if (bufpos_ > buflen_) 1759 | buflen_ = bufpos_; 1760 | curpos_+= len; 1761 | if (curpos_ > filesize_) 1762 | filesize_ = curpos_; 1763 | } 1764 | 1765 | void MidiWrite::seek(long pos) 1766 | { 1767 | assert(pos >= 0 && pos <= filesize_); 1768 | if (curpos_ == pos) 1769 | return; 1770 | if (pos >= curpos_-bufpos_ && pos <= curpos_-bufpos_+buflen_) 1771 | { 1772 | bufpos_ = (int)(pos - (curpos_-bufpos_)); 1773 | curpos_ = pos; 1774 | return; 1775 | } 1776 | flush(); 1777 | curpos_ = pos; 1778 | } 1779 | 1780 | FILE* MidiWrite::getf() 1781 | { 1782 | return f_; 1783 | } 1784 | 1785 | void MidiWrite::error(const char* msg) 1786 | { 1787 | fprintf(stderr, "midi write error: %s\n", msg); 1788 | } 1789 | 1790 | void MidiWrite::warning(const char* msg) 1791 | { 1792 | fprintf(stderr, "midi write warning: %s\n", msg); 1793 | } 1794 | 1795 | int MidiWrite::unitsperquarter() 1796 | { 1797 | return clicks_; 1798 | } 1799 | 1800 | MidiCopy::MidiCopy(const char* filename, FILE* f) : MidiRead(filename, f) 1801 | { 1802 | dest_ = NULL; 1803 | for (int c = 0; c < 16; c++) 1804 | mapchannel_[c] = c; // no change 1805 | } 1806 | 1807 | void MidiCopy::head(unsigned version, unsigned tracks, unsigned clicksperquarter) 1808 | { 1809 | if (dest_) 1810 | dest_->head(version, 0, clicksperquarter); 1811 | } 1812 | 1813 | void MidiCopy::track(int trackno, long length, int channel) 1814 | { 1815 | if (dest_) 1816 | dest_->track(); 1817 | } 1818 | 1819 | void MidiCopy::endtrack(int trackno) 1820 | { 1821 | if (dest_) 1822 | dest_->endtrack(); 1823 | } 1824 | 1825 | void MidiCopy::event(int what, int len, unsigned char* data) 1826 | { 1827 | if (dest_) 1828 | dest_->event(what, len, data); 1829 | } 1830 | 1831 | 1832 | void MidiCopy::seqnumber(unsigned int seqno) 1833 | { 1834 | if (dest_) 1835 | dest_->seqnumber(seqno); 1836 | } 1837 | 1838 | void MidiCopy::smpteofs(int hour, int min, int sec, int frame, int fracframe) 1839 | { 1840 | if (dest_) 1841 | dest_->smpteofs(hour, min, sec, frame, fracframe); 1842 | } 1843 | 1844 | void MidiCopy::key(int signature, int isminor) 1845 | { 1846 | if (dest_) 1847 | dest_->key(signature, isminor); 1848 | } 1849 | 1850 | void MidiCopy::prefixchannel(unsigned char channel) 1851 | { 1852 | if (dest_ && mapchannel_[channel] >= 0) 1853 | dest_->prefixchannel(mapchannel_[channel]); 1854 | } 1855 | 1856 | void MidiCopy::prefixport(unsigned char port) 1857 | { 1858 | if (dest_) 1859 | dest_->prefixport(port); 1860 | } 1861 | 1862 | void MidiCopy::text(int what, int len, char* whattext, unsigned char* txt) 1863 | { 1864 | if (dest_) 1865 | dest_->text(what, len, txt); 1866 | } 1867 | 1868 | void MidiCopy::meta(int what, int len, unsigned char* data) 1869 | { 1870 | if (dest_) 1871 | dest_->meta(what, len, data); 1872 | } 1873 | 1874 | void MidiCopy::end() 1875 | { 1876 | if (dest_) 1877 | dest_->end(); 1878 | } 1879 | 1880 | void MidiCopy::tact(int nom, int denom, int v1, int v2) 1881 | { 1882 | if (dest_) 1883 | dest_->tact(nom, denom, v1,v2); 1884 | } 1885 | 1886 | void MidiCopy::tempo(unsigned long ticks) 1887 | { 1888 | if (dest_) 1889 | dest_->tempo(ticks); 1890 | } 1891 | 1892 | void MidiCopy::program(int channel, int program) 1893 | { 1894 | if (dest_ && mapchannel_[channel] >= 0) 1895 | dest_->program(mapchannel_[channel], program); 1896 | } 1897 | 1898 | void MidiCopy::control(int channel, int what, int value) 1899 | { 1900 | if (dest_ && mapchannel_[channel] >= 0) 1901 | dest_->control(mapchannel_[channel], what, value); 1902 | } 1903 | 1904 | void MidiCopy::highbank(int channel, int val) 1905 | { 1906 | if (dest_ && mapchannel_[channel] >= 0) 1907 | dest_->highbank(mapchannel_[channel], val); 1908 | } 1909 | 1910 | void MidiCopy::wheel(int channel, int val) 1911 | { 1912 | if (dest_ && mapchannel_[channel] >= 0) 1913 | dest_->wheel(mapchannel_[channel], val); 1914 | } 1915 | 1916 | void MidiCopy::breath(int channel, int val) 1917 | { 1918 | if (dest_ && mapchannel_[channel] >= 0) 1919 | dest_->wheel(mapchannel_[channel], val); 1920 | } 1921 | 1922 | void MidiCopy::foot(int channel, int val) 1923 | { 1924 | if (dest_ && mapchannel_[channel] >= 0) 1925 | dest_->foot(mapchannel_[channel], val); 1926 | } 1927 | 1928 | void MidiCopy::portamentotime(int channel, int val) 1929 | { 1930 | if (dest_ && mapchannel_[channel] >= 0) 1931 | dest_->portamentotime(mapchannel_[channel], val); 1932 | } 1933 | 1934 | void MidiCopy::data(int channel, int val) 1935 | { 1936 | if (dest_ && mapchannel_[channel] >= 0) 1937 | dest_->data(mapchannel_[channel], val); 1938 | } 1939 | 1940 | void MidiCopy::volume(int channel, int val) 1941 | { 1942 | if (dest_ && mapchannel_[channel] >= 0) 1943 | dest_->volume(mapchannel_[channel], val); 1944 | } 1945 | 1946 | void MidiCopy::balance(int channel, int val) 1947 | { 1948 | if (dest_ && mapchannel_[channel] >= 0) 1949 | dest_->balance(mapchannel_[channel], val); 1950 | } 1951 | 1952 | void MidiCopy::expression(int channel, int val) 1953 | { 1954 | if (dest_ && mapchannel_[channel] >= 0) 1955 | dest_->expression(mapchannel_[channel], val); 1956 | } 1957 | 1958 | void MidiCopy::lowbank(int channel, int val) 1959 | { 1960 | if (dest_ && mapchannel_[channel] >= 0) 1961 | dest_->lowbank(mapchannel_[channel], val); 1962 | } 1963 | 1964 | void MidiCopy::hold(int channel, int val) 1965 | { 1966 | if (dest_ && mapchannel_[channel] >= 0) 1967 | dest_->hold(mapchannel_[channel], val); 1968 | } 1969 | 1970 | void MidiCopy::reverb(int channel, int val) 1971 | { 1972 | if (dest_ && mapchannel_[channel] >= 0) 1973 | dest_->reverb(mapchannel_[channel], val); 1974 | } 1975 | 1976 | void MidiCopy::chorus(int channel, int val) 1977 | { 1978 | if (dest_ && mapchannel_[channel] >= 0) 1979 | dest_->chorus(mapchannel_[channel], val); 1980 | } 1981 | 1982 | void MidiCopy::datainc(int channel, int val) 1983 | { 1984 | if (dest_ && mapchannel_[channel] >= 0) 1985 | dest_->datainc(mapchannel_[channel], val); 1986 | } 1987 | 1988 | void MidiCopy::datadec(int channel, int val) 1989 | { 1990 | if (dest_ && mapchannel_[channel] >= 0) 1991 | dest_->datadec(mapchannel_[channel], val); 1992 | } 1993 | 1994 | void MidiCopy::lowrpn(int channel, int val) 1995 | { 1996 | if (dest_ && mapchannel_[channel] >= 0) 1997 | dest_->lowrpn(mapchannel_[channel], val); 1998 | } 1999 | 2000 | void MidiCopy::highrpn(int channel, int val) 2001 | { 2002 | if (dest_ && mapchannel_[channel] >= 0) 2003 | dest_->highrpn(mapchannel_[channel], val); 2004 | } 2005 | 2006 | void MidiCopy::resetctrlrs(int channel, int val) 2007 | { 2008 | if (dest_ && mapchannel_[channel] >= 0) 2009 | dest_->resetctrlrs(mapchannel_[channel], val); 2010 | } 2011 | 2012 | void MidiCopy::allnotesoff(int channel, int val) 2013 | { 2014 | if (dest_ && mapchannel_[channel] >= 0) 2015 | dest_->allnotesoff(mapchannel_[channel], val); 2016 | } 2017 | 2018 | 2019 | void MidiCopy::noteon(int channel, int note, int vel) 2020 | { 2021 | if (dest_ && mapchannel_[channel] >= 0) 2022 | dest_->noteon(mapchannel_[channel], note, vel); 2023 | } 2024 | 2025 | void MidiCopy::noteoff(int channel, int note, int vel) 2026 | { 2027 | if (dest_ && mapchannel_[channel] >= 0) 2028 | dest_->noteoff(mapchannel_[channel], note, vel); 2029 | } 2030 | 2031 | void MidiCopy::time(unsigned long ticks) 2032 | { 2033 | if (dest_) 2034 | dest_->time(ticks); 2035 | } 2036 | 2037 | void MidiCopy::pitchbend(int channel, int val) 2038 | { 2039 | if (dest_ && mapchannel_[channel] >= 0) 2040 | dest_->pitchbend(mapchannel_[channel],val); 2041 | } 2042 | 2043 | void MidiCopy::polyaftertouch(int channel, int note, int val) 2044 | { 2045 | if (dest_ && mapchannel_[channel] >= 0) 2046 | dest_->polyaftertouch(mapchannel_[channel], note, val); 2047 | } 2048 | 2049 | void MidiCopy::aftertouch(int channel, int val) 2050 | { 2051 | if (dest_ && mapchannel_[channel] >= 0) 2052 | dest_->aftertouch(mapchannel_[channel], val); 2053 | } 2054 | 2055 | void MidiCopy::songpos(unsigned pos) 2056 | { 2057 | if (dest_) 2058 | dest_->songpos(pos); 2059 | } 2060 | 2061 | void MidiCopy::songselect(unsigned char song) 2062 | { 2063 | if (dest_) 2064 | dest_->songselect(song); 2065 | } 2066 | 2067 | void MidiCopy::tunerequest() 2068 | { 2069 | if (dest_) 2070 | dest_->tunerequest(); 2071 | 2072 | } 2073 | 2074 | void MidiCopy::timingclock() 2075 | { 2076 | if (dest_) 2077 | dest_->timingclock(); 2078 | } 2079 | 2080 | void MidiCopy::start() 2081 | { 2082 | if (dest_) 2083 | dest_->start(); 2084 | } 2085 | 2086 | void MidiCopy::cont() 2087 | { 2088 | if (dest_) 2089 | dest_->cont(); 2090 | } 2091 | 2092 | void MidiCopy::stop() 2093 | { 2094 | if (dest_) 2095 | dest_->stop(); 2096 | } 2097 | 2098 | void MidiCopy::activesense() 2099 | { 2100 | if (dest_) 2101 | dest_->activesense(); 2102 | } 2103 | 2104 | void MidiCopy::sysex(int syslen, unsigned char* sysdata) 2105 | { 2106 | if (dest_) 2107 | dest_->sysex(syslen, sysdata); 2108 | } 2109 | 2110 | void MidiCopy::xgreset() 2111 | { 2112 | if (dest_) 2113 | dest_->xgreset(); 2114 | } 2115 | 2116 | void MidiCopy::gmreset() 2117 | { 2118 | if (dest_) 2119 | dest_->gmreset(); 2120 | } 2121 | 2122 | void MidiCopy::gsreset() 2123 | { 2124 | if (dest_) 2125 | dest_->gsreset(); 2126 | } 2127 | 2128 | void MidiCopy::gsexit() 2129 | { 2130 | if (dest_) 2131 | dest_->gsexit(); 2132 | } 2133 | 2134 | void MidiCopy::setoutput(MidiWrite* dest) 2135 | { 2136 | assert(dest != NULL && dest->getf() != NULL); // need a valid MidiWrite instance 2137 | dest_ = dest; 2138 | } 2139 | 2140 | void MidiCopy::stopoutput() 2141 | { 2142 | dest_ = NULL; // owner is responsible for deleting the MidiWrite object 2143 | } 2144 | 2145 | MidiWrite* MidiCopy::getoutput() 2146 | { 2147 | return dest_; 2148 | } 2149 | 2150 | void MidiCopy::mapchannel(int channel, int newchannel) 2151 | { 2152 | assert(channel >= 0 && channel < 16); 2153 | assert(newchannel >= 0 && newchannel < 16); 2154 | mapchannel_[channel] = newchannel; 2155 | } 2156 | 2157 | void MidiCopy::ignorechannel(int channel) 2158 | { 2159 | assert(channel >= 0 && channel < 16); 2160 | mapchannel_[channel] = -1; 2161 | } 2162 | -------------------------------------------------------------------------------- /dro2midi.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // DRO2MIDI - Convert DOSBox raw OPL captures (.dro), Rdos (.raw) and id 3 | // Software (.imf, .wlf) into MIDI files (.mid) 4 | // 5 | // Created by malvineous@shikadi.net in June 2007. See README for license. 6 | // Based on imf2midi v1.0 written by Guenter Nagler in 1996 (gnagler@ihm.tu-graz.ac.at) 7 | // 8 | // v1.0 / 2007-06-16 / malvineous@shikadi.net: Original release 9 | // - imf2midi with .imf reader hacked to read .dro files instead 10 | // 11 | // v1.1 / 2007-07-28 / malvineous@shikadi.net: More file formats 12 | // - Added .imf and .raw support. 13 | // - Replaced Guenter's OPL -> MIDI frequency conversion algorithm (from a 14 | // lookup table into a much more accurate formula), consequently was able 15 | // to simplify pitchbend code (now conversions with pitchbends enabled 16 | // sound quite good!) 17 | // 18 | // v1.2 / 2007-07-28 / malvineous@shikadi.net: Bugfix release 19 | // - Fixed some file length calculations causing some files to be converted 20 | // without any notes. 21 | // - Added portamento-to-note for large (>2 semitone) pitchbends, but it 22 | // doesn't seem to work when using Timidity. 23 | // 24 | // v1.3 / 2007-09-02 / malvineous@shikadi.net: New features 25 | // - Fixed "tom tom" incorrectly called "bass drum" in output messages. 26 | // - Fixed multi-note pitchbends by removing portamento-to-note and 27 | // adjusting standard pitchbend range instead, thanks to a suggestion 28 | // by Xky (xkyrauh2001@hotmail.com) 29 | // - Implemented a better method for reading Adlib register -> MIDI patch 30 | // mapping information (all stored in inst.txt now instead of having a 31 | // seperate file for each instrument.) Also improved method for mapping 32 | // instruments to percussion on MIDI channel 10. 33 | // - Fixed OPL rhythm instrument conversion issue (a MIDI noteon was being 34 | // generated too often - if the OPL instrument is on and we receive 35 | // another keyon, it *shouldn't* generate a fresh MIDI keyon.) 36 | // - Fixed IMF type-1 conversion issue where unsigned numbers were being 37 | // read as signed, and the conversion was cutting off half way through. 38 | // 39 | // v1.4 / 2009-03-28 / malvineous@shikadi.net 40 | // - Some code cleanup, fixed all the warnings in midiio.cpp. 41 | // - Fixed a bunch of char/unsigned char issues, hopefully Win32 42 | // conversions will now be as reliable as under Linux. 43 | // - Added line numbers to instrument names and mapping file error messages. 44 | // - Added new instrument mapping entries for rhythm mode instruments 45 | // (which previously were hard coded.) 46 | // - Added transpose and mute options to instrument mapping file. 47 | // - Added -c option to change OPL constant during conversion, thanks to a 48 | // suggestion from Wraithverge (liam82067@yahoo.com) 49 | // - Added -v option to disable note volume (helps with Stunts which 50 | // otherwise comes out with no notes because they're all silent.) 51 | // - Corrected OPL volume -> MIDI note velocity algorithm, and added hard 52 | // limit to prevent notes from having a zero velocity (which stops them 53 | // from being converted, like with Stunts.) 54 | // - New instrument mappings (to be copied into insts.txt) are printed to 55 | // stderr, so "dro2midi 2>> insts.txt" will conveniently append them all 56 | // to the mapping file. Thanks to Wraithverge for the idea. 57 | // - Replaced getop() and getchannel() lookup functions with GET_OP() and 58 | // GET_CHANNEL() macro algorithms to calculate the values as required. 59 | // - Included 128 standard GM instrument mappings extracted from Creative 60 | // Labs' MIDI player (see gen_test_midi.cpp.) 61 | // 62 | // v1.5 / 2010-03-28 / Wraithverge (liam82067 at yahoo dot com): Changes 63 | // - Added code for several MIDI Controller Events for output MID-files, 64 | // and they are the following: 65 | // Reset Controllers [121] -- At the head of the Event list. 66 | // Balance (or Pan) [10] -- At the head of the Event list. 67 | // Expression [11] -- At the head of the Event list. 68 | // All Notes Off [123] -- At the foot of the Event list. 69 | // Hopefully, other musicians will find these to be needed, as well. 70 | // - Added detectors for the (at this time) two DRO formats, and a hint 71 | // to let us know that the DRO v2.0 format is not supported. 72 | // 73 | // v1.6 / 2013-11-14 / bsa (http://bsutherland.github.io/JuceOPLVSTi) 74 | // - Added -s switch to save detected instruments in Creative Sound 75 | // Blaster Instrument format (.sbi) 76 | // 77 | // v1.7 / 2021-07-14 / Snooping One (https://github.com/Snoopin1) 78 | // - Improved instrument detection and SBI export. Already-detected 79 | // instruments no longer get detected as new instruments when their 80 | // volume changes within a song. Exported SBIs no longer have numbered 81 | // internal names, so that duplicate SBIs can be identified with a 82 | // checksum calculator. 83 | // 84 | 85 | #define VERSION "1.7" 86 | #define MAPPING_FILE "inst.txt" 87 | 88 | #define PATCH_NAME_FILE "patch.txt" 89 | #define PERC_NAME_FILE "drum.txt" 90 | #define NUM_MIDI_PATCHES 128 // 128 MIDI instruments 91 | #define NUM_MIDI_PERC 128 // 46 MIDI percussive notes (channel 10), but 128 possible notes 92 | #define INSTR_NAMELEN 32 // Maximum length of an instrument name 93 | 94 | //#define PITCHBEND_RANGE 12.0 // 12 == pitchbends can go up to a full octave 95 | #define PITCHBEND_RANGE 24.0 // 24 == pitchbends can go up two full octaves 96 | #define PITCHBEND_ONESEMITONE (8192.0 / PITCHBEND_RANGE) 97 | const double pitchbend_center = 8192.0; 98 | 99 | #include "midiio.hpp" 100 | #include 101 | #include 102 | #include 103 | #include 104 | #include 105 | #include 106 | #include 107 | 108 | #define WRITE_BINARY "wb" 109 | #define READ_TEXT "r" 110 | 111 | #ifdef _MSC_VER 112 | // Keep MS VC++ happy 113 | #define strncasecmp _strnicmp 114 | #define strcasecmp _stricmp 115 | #define snprintf _snprintf 116 | #define log2(x) (log(x) / log(2.)) 117 | inline double round( double d ) 118 | { 119 | return floor( d + 0.5 ); 120 | } 121 | #endif 122 | 123 | bool bRhythm = true; // convert rhythm mode instruments (-r) 124 | bool bUsePitchBends = true; // use pitch bends to better match MIDI note frequency with the OPL frequency (-p) 125 | bool bApproximatePitchbends = false; // if pitchbends are disabled, should we approximate them by playing the nearest note when the pitch changes? 126 | bool bPerfectMatchesOnly = false; // if true, only match perfect instruments 127 | bool bEnableVolume = true; // enable note velocity based on OPL instrument volume 128 | bool bWriteSbiInstruments = false; // write detected instruments to .SBI files 129 | 130 | // Rhythm instruments 131 | enum RHYTHM_INSTRUMENT { 132 | NormalInstrument, 133 | BassDrum, 134 | SnareDrum, 135 | TomTom, 136 | TopCymbal, 137 | HiHat 138 | }; 139 | 140 | // MIDI channels to use for these instruments when they are mapped as normal 141 | // notes. Probably best not to use 1-10 as these are used by the rest of the 142 | // OPL mapping code. Note these are zero-based, so the GM drum channel is 143 | // channel 9 in this context. 144 | #define CHAN_BASSDRUM 10 // Bass drum sits on OPL channel 7 145 | #define CHAN_SNAREDRUM 11 // OPL channel 8 carrier 146 | #define CHAN_TOMTOM 12 // OPL channel 9 modulator 147 | #define CHAN_TOPCYMBAL 13 // OPL channel 9 carrier 148 | #define CHAN_HIHAT 14 // OPL channel 8 modulator 149 | 150 | char cPatchName[NUM_MIDI_PATCHES][INSTR_NAMELEN]; 151 | char cPercName[NUM_MIDI_PERC][INSTR_NAMELEN]; 152 | 153 | char* input = 0; 154 | char* output = 0; 155 | 156 | FILE* f = 0; // input file 157 | MidiWrite* write = 0; 158 | 159 | //int resolution = 384; // 560Hz IMF 160 | int resolution = 500; // 1000Hz DRO 161 | int program = 0; 162 | float tempo = 120.0; 163 | 164 | // Arrays of [9] refer to OPL channels, arrays of [16] also refer to OPL 165 | // channels but use fake channels (11-15) for rhythm mode instruments (but 166 | // only for elements like keyon and instrument mapping that can happen 167 | // independently of the OPL channel in use. Things like OPL channel pitch 168 | // which affect both rhythm mode instruments using that channel are not 169 | // stored separately, i.e. they're in the [9] array.) 170 | int mapchannel[16]; 171 | int curfreq[9]; 172 | bool keyAlreadyOn[16]; 173 | int lastkey[16]; // last MIDI key pressed on this channel 174 | int pitchbent[16]; 175 | int transpose[16]; // used for instruments with mapped transpose values 176 | int drumnote[16]; // note to play on MIDI channel 10 if Adlib channel has a 177 | // percussive instrument assigned to it 178 | int lastprog[16]; // last program/patch set on the MIDI channel 179 | bool mute[16]; // true if the instrument on this channel is currently muted 180 | 181 | // Statistics 182 | int iNotesActive = 0; 183 | int iPitchbendCount = 0; 184 | int iTotalNotes = 0; 185 | 186 | typedef struct 187 | { 188 | /*unsigned char reg20[2]; 189 | unsigned char reg40[2]; 190 | unsigned char reg60[2]; 191 | unsigned char reg80[2]; 192 | unsigned char regC0; 193 | unsigned char regE0[2];*/ 194 | 195 | unsigned int reg20[2]; 196 | unsigned int reg40[2]; 197 | unsigned int reg60[2]; 198 | unsigned int reg80[2]; 199 | unsigned int regC0; 200 | unsigned int regE0[2]; 201 | 202 | // Is this a normal instrument or a mapping for a rhythm instrument? 203 | RHYTHM_INSTRUMENT eRhythmInstrument; 204 | 205 | int prog; // MIDI patch (or -1 if a drum) 206 | int isdrum; 207 | int note; // note to play if drum 208 | bool muted; // true if this instrument is muted 209 | char name[128]; 210 | 211 | signed int iTranspose; // number of semitones to transpose this instrument 212 | 213 | // The instrument can be redirected to another. This is used when a new 214 | // instrument is encountered - its info is printed to the screen then it's 215 | // recorded (pointing to its best match) to prevent it appearing as a new 216 | // instrument hundreds of times and cluttering the output. 217 | int redirect; // if >= 0, use ::instr[redirect] instead of this one 218 | 219 | // When using this struct to cache current channel parms we need to 220 | // remember the octave, so that when registers 0xA0-0xA8 are changed 221 | // (to change note frequency) we can still pass the octave to the note 222 | // conversion function (as the octave is otherwise only available when 223 | // setting registers 0xB0-0xB8.) 224 | int iOctave; 225 | } INSTRUMENT; 226 | 227 | INSTRUMENT reg[9]; // current registers of channel 228 | 229 | #define MAXINSTR 2048 230 | int instrcnt = 0; 231 | INSTRUMENT instr[MAXINSTR]; 232 | 233 | int iFormat = 0; // input format 234 | #define FORMAT_IMF 1 235 | #define FORMAT_DRO 2 236 | #define FORMAT_RAW 3 237 | #define FORMAT_DRO2 4 238 | 239 | int iSpeed = 0; // clock speed (in Hz) 240 | int iInitialSpeed = 0; // first iSpeed value written to MIDI header 241 | 242 | double dbConversionVal; 243 | // Convert the given OPL F-num and octave values into a fractional MIDI note 244 | // number (for the use of pitchbends.) 245 | double freq2key(int freq, int octave) 246 | { 247 | int iFNum = freq; 248 | int iBlock = octave; 249 | double dbOriginalFreq = dbConversionVal * (double)iFNum * pow(2, (double)(iBlock - 20)); 250 | return 69.0 + 12.0 * log2(dbOriginalFreq / 440.0); 251 | } 252 | 253 | void version() 254 | { 255 | printf("DRO2MIDI v" VERSION " - Convert raw Adlib captures to General MIDI\n" 256 | "Written by malvineous@shikadi.net in 2007\n" 257 | "Heavily based upon IMF2MIDI written by Guenter Nagler in 1996\n" 258 | "With contributions by Wraithverge (C) 2010, bsa in 2013\n" 259 | "http://www.shikadi.net/utils/\n" 260 | "\n" 261 | ); 262 | return; 263 | } 264 | 265 | void usage() 266 | { 267 | version(); 268 | fprintf(stderr, 269 | "Usage: dro2midi [-p [-a]] [-r] [-i] [-c alt|] [-v] input.dro output.mid\n" 270 | "\n" 271 | "Where:\n" 272 | " -p Disable use of MIDI pitch bends\n" 273 | " -a If pitchbends are disabled, approximate by playing the nearest note\n" 274 | " -r Don't convert OPL rhythm-mode instruments\n" 275 | " -i Only use instruments that match perfectly (default is 'close enough is\n" 276 | " good enough.') Useful when guessing new patches. Instruments that can't\n" 277 | " be matched use the first entry in " MAPPING_FILE " (piano by default)\n" 278 | " -c Change the value used to convert OPL notes into MIDI notes from the\n" 279 | " default 49716. Any non-zero value can be specified. The special word\n" 280 | " \"alt\" means 50000, the other commonly used value. It is unlikely any\n" 281 | " other values will need to be used, unless you get an excessive amount\n" 282 | " of artificial pitchbends in the output MIDI.\n" 283 | " -v Disable note velocity and play all notes as loudly as possible,\n" 284 | " instead of trying to match the volume of the OPL note.\n" 285 | " -s Write detected instruments to .sbi files\n" 286 | " (Creative Sound Blaster Instrument).\n" 287 | "\n" 288 | "Supported input formats:\n" 289 | " .raw Rdos RAW OPL capture\n" 290 | " .dro DOSBox RAW OPL capture\n" 291 | " .imf id Software Music Format (type-0 and type-1 at 560Hz)\n" 292 | " .wlf id Software Music Format (type-0 and type-1 at 700Hz)\n" 293 | "\n" 294 | "Instrument definitions are read in from " MAPPING_FILE ". Instrument names\n" 295 | "are read in from " PATCH_NAME_FILE " and " PERC_NAME_FILE ". See the README for more details.\n" 296 | ); 297 | exit(1); 298 | } 299 | 300 | /* Channel 1 2 3 4 5 6 7 8 9 301 | * Operator 1 00 01 02 08 09 0A 10 11 12 } cells 302 | * Operator 2 03 04 05 0B 0C 0D 13 14 15 } 303 | */ 304 | // Converts a cell into a channel 305 | #define GET_CHANNEL(i) ((((i) / 8) * 3) + (((i) % 8) % 3)) 306 | 307 | // Converts a cell into 0 (for operator 1) or 1 (for operator 2) 308 | #define GET_OP(i) (((i) % 8) / 3) 309 | 310 | // Helper functions to read data from files in a non-optimised but platform 311 | // independent (little/big endian) way. 312 | inline unsigned char readByte(FILE *f) 313 | { 314 | unsigned char c; 315 | fread(&c, 1, 1, f); 316 | return c; 317 | } 318 | inline unsigned short readUINT16LE(FILE *f) 319 | { 320 | unsigned char c[2]; 321 | fread(&c, 1, 2, f); 322 | return c[0] | (c[1] << 8L); 323 | } 324 | inline unsigned long readUINT32LE(FILE *f) 325 | { 326 | unsigned char c[4]; 327 | fread(&c, 1, 4, f); 328 | return c[0] | (c[1] << 8L) | (c[2] << 16L) | (c[3] << 24L); 329 | } 330 | 331 | 332 | #define EPRINTF(FMT, ...) fprintf(stderr, "%s: " FMT, fname, __VA_ARGS__) 333 | bool loadInstruments(void) 334 | { 335 | for (int i = 0; i < NUM_MIDI_PATCHES; i++) sprintf(cPatchName[i], "Patch #%d", i+1); 336 | for (int i = 0; i < NUM_MIDI_PERC; i++) sprintf(cPercName[i], "Note #%d", i); 337 | 338 | char line[256]; 339 | const char *fname; 340 | 341 | fname = PATCH_NAME_FILE; 342 | FILE* p = fopen(PATCH_NAME_FILE, "r"); 343 | if (!p) { 344 | fprintf(stderr, "Warning: Unable to open file listing patch names (" 345 | PATCH_NAME_FILE ")\nInstrument names will not be available.\n"); 346 | } else { 347 | while (fgets(line, sizeof(line)-1, p)) { 348 | int iValue, iLen; 349 | char *p = strpbrk(line, "\n\r"); 350 | if (p) *p = '\0'; // terminate the string at the newline 351 | if (sscanf(line, "%d=%n", &iValue, &iLen) == 1) { 352 | assert(iValue <= NUM_MIDI_PATCHES); 353 | snprintf(cPatchName[iValue-1], INSTR_NAMELEN, "%s [%d]", &line[iLen], iValue); 354 | } else if ((line[0] != '#') && (line[0] != '\n')) { 355 | EPRINTF("Invalid line: %s\n", line); 356 | } 357 | } 358 | fclose(p); 359 | } 360 | 361 | fname = PERC_NAME_FILE; 362 | p = fopen(PERC_NAME_FILE, "r"); 363 | if (!p) { 364 | fprintf(stderr, "Warning: Unable to open file listing percussion note " 365 | "names (" PERC_NAME_FILE ")\nPercussion names will not be available.\n"); 366 | } else { 367 | while (fgets(line, sizeof(line)-1, p)) { 368 | int iValue, iLen; 369 | char *p = strpbrk(line, "\n\r"); 370 | if (p) *p = '\0'; // terminate the string at the newline 371 | if (sscanf(line, "%d=%n", &iValue, &iLen) == 1) { 372 | assert(iValue <= NUM_MIDI_PERC); 373 | snprintf(cPercName[iValue], INSTR_NAMELEN, "%s [%d]", &line[iLen], iValue); 374 | } else if ((line[0] != '#') && (line[0] != '\n')) { 375 | EPRINTF("Invalid line: %s\n", line); 376 | } 377 | } 378 | fclose(p); 379 | } 380 | 381 | fname = MAPPING_FILE; 382 | FILE* f = fopen(MAPPING_FILE, "r"); 383 | if (!f) { 384 | fprintf(stderr, "Warning: Unable to open instrument mapping file " 385 | MAPPING_FILE ", defaulting to a Grand Piano\nfor all instruments.\n"); 386 | return true; 387 | } 388 | INSTRUMENT in; 389 | memset(&in, 0, sizeof(in)); 390 | in.redirect = -1; // none of these should redirect (but later automatic 391 | // instruments will redirect to these ones) 392 | 393 | // Loop until we run out of lines in the data file or we hit the maximum 394 | // number of instruments 395 | char value[256]; 396 | int iLineNum = 0; 397 | for (::instrcnt = 0; fgets(line, sizeof(line)-1, f) && (instrcnt < MAXINSTR);) { 398 | iLineNum++; 399 | 400 | // Ignore blank lines and comments 401 | if ((line[0] == '#') || (line[0] == '\r') || (line[0] == '\n')) continue; 402 | 403 | // Figure out what type of rhythm mode instrument (if any) the first two 404 | // chars are referring to. 405 | char cInstType[3]; 406 | int iNumFields = sscanf(line, "%2s ", cInstType); 407 | if ((cInstType[0] == 'N') && (cInstType[1] == 'O')) in.eRhythmInstrument = NormalInstrument; 408 | else if ((cInstType[0] == 'B') && (cInstType[1] == 'D')) in.eRhythmInstrument = BassDrum; 409 | else if ((cInstType[0] == 'S') && (cInstType[1] == 'D')) in.eRhythmInstrument = SnareDrum; 410 | else if ((cInstType[0] == 'T') && (cInstType[1] == 'T')) in.eRhythmInstrument = TomTom; 411 | else if ((cInstType[0] == 'T') && (cInstType[1] == 'C')) in.eRhythmInstrument = TopCymbal; 412 | else if ((cInstType[0] == 'H') && (cInstType[1] == 'H')) in.eRhythmInstrument = HiHat; 413 | else { 414 | EPRINTF("Invalid instrument type \"%s\" on line %d:\n\n %s\n", 415 | cInstType, iLineNum, line); 416 | return false; 417 | } 418 | 419 | switch (in.eRhythmInstrument) { 420 | case NormalInstrument: 421 | case BassDrum: 422 | // Normal instrument or rhythm Bass Drum, read both 423 | // operators + connection byte 424 | iNumFields = sscanf(&line[3], "%02X-%02X/%02X-%02X/%02X-%02X/%02X-%02X" 425 | "/%02X/%02X-%02X: %s\n", 426 | &in.reg20[0], &in.reg20[1], 427 | &in.reg40[0], &in.reg40[1], 428 | &in.reg60[0], &in.reg60[1], 429 | &in.reg80[0], &in.reg80[1], 430 | &in.regC0, 431 | &in.regE0[0], &in.regE0[1], value); 432 | if (iNumFields != 12) { 433 | EPRINTF("Unable to parse line %d: (expected 12 " 434 | "fields, got %d)\n\n%s\n", iLineNum, iNumFields, line); 435 | return false; 436 | } 437 | break; 438 | 439 | case TomTom: 440 | case HiHat: 441 | // This instrument is one operator only, but it does use the connection 442 | // byte (probably) 443 | iNumFields = sscanf(&line[3], "%02X/%02X/%02X/%02X/%02X/%02X: %s\n", 444 | &in.reg20[0], 445 | &in.reg40[0], 446 | &in.reg60[0], 447 | &in.reg80[0], 448 | &in.regC0, 449 | &in.regE0[0], value); 450 | if (iNumFields != 7) { 451 | EPRINTF("Unable to parse line %d: (expected 7 " 452 | "fields, got %d)\n\n%s\n", iLineNum, iNumFields, line); 453 | return false; 454 | } 455 | break; 456 | 457 | case SnareDrum: 458 | case TopCymbal: 459 | // This instrument does not uses the connection byte, so read in one byte 460 | // less. Also read the values into the other operator. 461 | iNumFields = sscanf(&line[3], "%02X/%02X/%02X/%02X/%02X: %s\n", 462 | &in.reg20[1], 463 | &in.reg40[1], 464 | &in.reg60[1], 465 | &in.reg80[1], 466 | &in.regE0[1], value); 467 | if (iNumFields != 6) { 468 | EPRINTF("Unable to parse line %d: (expected 6 " 469 | "fields, got %d)\n\n%s\n", iLineNum, iNumFields, line); 470 | return false; 471 | } 472 | break; 473 | } 474 | 475 | // Default options 476 | in.isdrum = 0; 477 | in.prog = 1; 478 | in.iTranspose = 0; 479 | in.muted = false; 480 | 481 | // If we got this far it's a valid instrument 482 | int iValue; 483 | char nextopt[256]; 484 | // We need to manually terminate the %255c in the sscanf() above, so do 485 | // this by terminating it at the end-of-line char. Hopefully reading past 486 | // the end of the input string (which causes this) won't crash things... 487 | for (int i = 0; i < 256; i++) { 488 | if (value[i] == '\n') value[i] = 0; 489 | else if (value[i] == '\0') break; 490 | } 491 | value[255] = 0; 492 | 493 | const char *cList = value; 494 | int iLen; 495 | while (sscanf(cList, "%s%n", nextopt, &iLen) >= 1) { 496 | cList += iLen; 497 | if (nextopt[0] == '#') break; // reached an end of line comment 498 | if (nextopt[0] == ' ') continue; // skip double spaces 499 | if (sscanf(nextopt, "patch=%d", &iValue) == 1) { 500 | // MIDI patch 501 | in.isdrum = 0; 502 | in.prog = iValue - 1; 503 | if ((in.prog < 0) || (in.prog > 127)) { 504 | EPRINTF("ERROR: Instrument #%d (line %d) was set to " 505 | "patch=%d, but this value must be between 1 and 128 inclusive.\n", 506 | instrcnt, iLineNum, in.prog + 1); 507 | return false; 508 | } 509 | } else if (sscanf(nextopt, "drum=%d", &iValue) == 1) { 510 | // MIDI drum 511 | in.isdrum = 1; 512 | in.prog = -1; 513 | in.note = iValue; 514 | if ((in.note < 0) || (in.note > 127)) { 515 | EPRINTF("ERROR: Drum instrument #%d (line %d) was set to " 516 | "drum=%d, but this value must be between 1 and 128 inclusive.\n", 517 | instrcnt, iLineNum, in.note); 518 | return false; 519 | } 520 | } else if (sscanf(nextopt, "transpose=%d", &iValue) == 1) { 521 | // MIDI drum 522 | in.iTranspose = iValue; 523 | } else if (strcmp(nextopt, "mute") == 0) { 524 | // Mute this instrument 525 | in.muted = true; 526 | } else { 527 | EPRINTF("Unknown instrument option on line %d: %s\n", 528 | iLineNum, nextopt); 529 | return false; 530 | } 531 | } 532 | 533 | char cInstTypeText[256]; 534 | switch (in.eRhythmInstrument) { 535 | case NormalInstrument: cInstTypeText[0] = '\0'; break; 536 | case BassDrum: strcpy(cInstTypeText, "(OPL BD) "); break; 537 | case TomTom: strcpy(cInstTypeText, "(OPL TT) "); break; 538 | case HiHat: strcpy(cInstTypeText, "(OPL HH) "); break; 539 | case SnareDrum: strcpy(cInstTypeText, "(OPL SD) "); break; 540 | case TopCymbal: strcpy(cInstTypeText, "(OPL TC) "); break; 541 | } 542 | sprintf(in.name, "Inst#%03d %s@ line %3d%s: %s", instrcnt, 543 | cInstTypeText, 544 | iLineNum, 545 | (in.isdrum) ? " (perc)" : "", 546 | (in.isdrum) ? cPercName[in.note] : cPatchName[in.prog] 547 | ); 548 | if (in.iTranspose) { 549 | char cTranspose[256]; 550 | sprintf(cTranspose, " @ %+d semitones", in.iTranspose); 551 | strcat(in.name, cTranspose); 552 | } 553 | if (in.muted) strcat(in.name, " {muted}"); 554 | //printf("%s\n", in.name); 555 | 556 | // Add instrument 557 | ::instr[instrcnt++] = in; 558 | } 559 | fclose(f); 560 | return true; 561 | } 562 | #undef EPRINTF 563 | 564 | long difference(int a, int b, int importance = 1) 565 | { 566 | long diff = a - b; 567 | if (diff < 0) diff = -diff; 568 | return diff * importance; 569 | } 570 | 571 | long compareinstr(INSTRUMENT& a, INSTRUMENT& b, RHYTHM_INSTRUMENT ri) 572 | { 573 | // Note that we're not using b.eRhythmInstrument below as "b" refers to the 574 | // OPL channel, and this never has an instrument type set. The instrument 575 | // type we want is passed in as "ri", so we compare against that instead. 576 | //fprintf(stderr, "%d ", ri); 577 | switch (ri) { 578 | case NormalInstrument: 579 | case BassDrum: 580 | // Compare the full register set (both operators plus the connection 581 | // byte) for normal instruments (which occupy a whole channel) and the 582 | // one rhythm mode instrument which also occupies a whole channel. 583 | return 584 | difference(a.eRhythmInstrument, ri, 4) + 585 | difference(a.reg20[0], b.reg20[0], 2) + 586 | difference(a.reg20[1], b.reg20[1], 2) + 587 | difference(a.reg40[0], b.reg40[0], 1) + 588 | difference(a.reg60[0], b.reg60[0], 2) + 589 | difference(a.reg60[1], b.reg60[1], 2) + 590 | difference(a.reg80[0], b.reg80[0], 2) + 591 | difference(a.reg80[1], b.reg80[1], 2) + 592 | difference(a.regC0, b.regC0, 3) + 593 | difference(a.regE0[0], b.regE0[0], 1) + 594 | difference(a.regE0[1], b.regE0[1], 1); 595 | case TomTom: 596 | case HiHat: 597 | // These two rhythm instruments only use one operator's settings, the 598 | // settings of the other operator (should) be ignored. There is also 599 | // only one Connection byte, but there is no documentation to say whether 600 | // this applies to these modulator-only rhythm instruments or to the 601 | // other carrier-only ones (below.) I'm guessing and putting it here. 602 | return 603 | difference(a.eRhythmInstrument, ri, 4) + 604 | difference(a.reg20[0], b.reg20[0], 2) + 605 | difference(a.reg40[0], b.reg40[0], 1) + 606 | difference(a.reg60[0], b.reg60[0], 2) + 607 | difference(a.reg80[0], b.reg80[0], 2) + 608 | difference(a.regC0, b.regC0, 3) + 609 | difference(a.regE0[0], b.regE0[0], 1); 610 | case SnareDrum: 611 | case TopCymbal: 612 | // These instruments only use one operator - but the other one compared 613 | // to the previous set above. They also don't use the single Connection 614 | // byte (I think.) 615 | return 616 | difference(a.eRhythmInstrument, ri, 4) + 617 | difference(a.reg20[1], b.reg20[1], 2) + 618 | difference(a.reg60[1], b.reg60[1], 2) + 619 | difference(a.reg80[1], b.reg80[1], 2) + 620 | difference(a.regE0[1], b.regE0[1], 1); 621 | } 622 | } 623 | 624 | void writesbi(const char* filename, int instrno, int chanOPL) { 625 | char fname[100]; 626 | char title[32]; 627 | snprintf(fname, 100, "%s_%03d.sbi", filename, instrno); 628 | FILE* f_sbi = fopen(fname, WRITE_BINARY); 629 | if (!f_sbi) { 630 | fprintf(stderr, "Could not open instrument file %s for writing.\n", filename); 631 | } else { 632 | fwrite("SBI\x1a", sizeof(char), 4, f_sbi); 633 | memset(title, 0, 32); 634 | snprintf(title, 32, "dro2midi"); 635 | fwrite(title, sizeof(char), 32, f_sbi); 636 | unsigned char instr[16]; 637 | memset(instr, 0, 16); 638 | instr[0] = (unsigned char)reg[chanOPL].reg20[0]; 639 | instr[1] = (unsigned char)reg[chanOPL].reg20[1]; 640 | instr[2] = (unsigned char)reg[chanOPL].reg40[0]; 641 | instr[3] = (unsigned char)reg[chanOPL].reg40[1]; 642 | instr[4] = (unsigned char)reg[chanOPL].reg60[0]; 643 | instr[5] = (unsigned char)reg[chanOPL].reg60[1]; 644 | instr[6] = (unsigned char)reg[chanOPL].reg80[0]; 645 | instr[7] = (unsigned char)reg[chanOPL].reg80[1]; 646 | instr[8] = (unsigned char)reg[chanOPL].regE0[0]; 647 | instr[9] = (unsigned char)reg[chanOPL].regE0[1]; 648 | instr[10] = (unsigned char)reg[chanOPL].regC0; 649 | fwrite(instr, sizeof(char), 16, f_sbi); 650 | fclose(f_sbi); 651 | } 652 | } 653 | 654 | int findinstr(int chanMIDI) 655 | { 656 | assert((chanMIDI < 9) || ((chanMIDI >= CHAN_BASSDRUM) && (chanMIDI <= CHAN_HIHAT))); 657 | 658 | RHYTHM_INSTRUMENT ri; 659 | int chanOPL; 660 | switch (chanMIDI) { 661 | case CHAN_BASSDRUM: ri = BassDrum; chanOPL = 6; break; 662 | case CHAN_SNAREDRUM: ri = SnareDrum; chanOPL = 7; break; 663 | case CHAN_TOMTOM: ri = TomTom; chanOPL = 8; break; 664 | case CHAN_TOPCYMBAL: ri = TopCymbal; chanOPL = 8; break; 665 | case CHAN_HIHAT: ri = HiHat; chanOPL = 7; break; 666 | default: ri = NormalInstrument; chanOPL = chanMIDI; break; 667 | } 668 | 669 | int besti = -1; 670 | long bestdiff = -1; 671 | for (int i = 0; i < instrcnt; i++) { 672 | long diff = compareinstr(instr[i], reg[chanOPL], ri); 673 | if (besti < 0 || diff < bestdiff) { 674 | bestdiff = diff; 675 | besti = i; 676 | if (bestdiff == 0) break; 677 | } 678 | } 679 | 680 | if (besti >= 0) { // could be -1 if no instruments are loaded 681 | while (instr[besti].redirect >= 0) { // Could have multiple redirects 682 | // This instrument was an automatically generated one to avoid printing 683 | // the instrument definition multiple times, so instead of using the auto 684 | // one, use the one it originally matched against. 685 | besti = instr[besti].redirect; 686 | } 687 | } 688 | 689 | if (bestdiff != 0) { 690 | if (::bPerfectMatchesOnly) { 691 | // User doesn't want "close enough is good enough" instrument guessing 692 | besti = 0; // use first instrument 693 | } 694 | // Couldn't find an exact match, print the details 695 | switch (ri) { 696 | case NormalInstrument: 697 | case BassDrum: 698 | // Normal instrument or rhythm Bass Drum use both 699 | // operators + connection byte 700 | printf("** New instrument in use on channel %d\n** Copy this into " 701 | MAPPING_FILE " to assign it a MIDI patch:\n", chanOPL); 702 | fprintf(stderr, "%s %02X-%02X/%02X-%02X/%02X-%02X/%02X-%02X/%02X/" 703 | "%02X-%02X: patch=?\n", 704 | ((ri == BassDrum) ? "BD" : "NO"), 705 | reg[chanOPL].reg20[0], reg[chanOPL].reg20[1], 706 | reg[chanOPL].reg40[0], reg[chanOPL].reg40[1], 707 | reg[chanOPL].reg60[0], reg[chanOPL].reg60[1], 708 | reg[chanOPL].reg80[0], reg[chanOPL].reg80[1], 709 | reg[chanOPL].regC0, 710 | reg[chanOPL].regE0[0], reg[chanOPL].regE0[1] 711 | ); 712 | if (::bWriteSbiInstruments) { 713 | writesbi(output, instrcnt, chanOPL); 714 | } 715 | break; 716 | case TomTom: 717 | case HiHat: 718 | // This instrument is one operator only, but it does use the connection 719 | // byte (probably) 720 | printf("** New rhythm instrument in use on OPL channel %d modulator\n" 721 | "** Copy this into " MAPPING_FILE " to assign it a MIDI patch:\n", 722 | chanOPL); 723 | fprintf(stderr, "%s %02X/%02X/%02X/%02X/%02X/%02X: " 724 | "patch=?\n", 725 | ((ri == TomTom) ? "TT" : "HH"), 726 | reg[chanOPL].reg20[0], 727 | reg[chanOPL].reg40[0], 728 | reg[chanOPL].reg60[0], 729 | reg[chanOPL].reg80[0], 730 | reg[chanOPL].regC0, 731 | reg[chanOPL].regE0[0] 732 | ); 733 | break; 734 | case SnareDrum: 735 | case TopCymbal: 736 | // This instrument does not uses the connection byte, so read in one 737 | // byte less. Also read the values into the other operator. 738 | // This instrument is one operator only, but it does use the connection 739 | // byte (probably) 740 | printf("** New rhythm instrument in use on OPL channel %d carrier\n" 741 | "** Copy this into " MAPPING_FILE " to assign it a MIDI patch:\n", 742 | chanOPL); 743 | fprintf(stderr, "%s %02X/%02X/%02X/%02X/%02X: " 744 | "patch=?\n", 745 | ((ri == SnareDrum) ? "SD" : "TC"), 746 | reg[chanOPL].reg20[1], 747 | reg[chanOPL].reg40[1], 748 | reg[chanOPL].reg60[1], 749 | reg[chanOPL].reg80[1], 750 | reg[chanOPL].regE0[1] 751 | ); 752 | break; 753 | } 754 | 755 | printf(">> Using similar match: %s\n", instr[besti].name); 756 | // Save this unknown instrument as a known one, so the same registers don't get printed again 757 | // reg[channel].prog = instr[besti].prog; // but keep the same patch that we've already assigned to the instrument, so it doesn't drop back to a piano for the rest of the song 758 | // Maybe ^ isn't necessary if we're redirecting? 759 | instr[instrcnt] = reg[chanOPL]; 760 | instr[instrcnt].eRhythmInstrument = ri; 761 | if (besti >= 0) { 762 | instr[instrcnt].redirect = besti; // Next time this instrument is matched, use the original one instead 763 | } else { 764 | instr[instrcnt].redirect = -1; // Will only happen when no instruments are loaded 765 | } 766 | instrcnt++; 767 | } 768 | return besti; 769 | } 770 | 771 | // Function for processing OPL note on and off events, and generating MIDI 772 | // events in response. This function is also called when the pitch changes 773 | // while a note is currently being played, causing it to generate MIDI 774 | // pitchbends (if enabled) instead. 775 | // Normally chanOPL and chanMIDI will be the same, except for rhythm mode 776 | // instruments, which uses chanOPL for pitch and instrument patches, but 777 | // chanMIDI for instrument mapping and note on/off events (since there will be 778 | // two instrument maps and notes for a single OPL channel.) 779 | void doNoteOnOff(bool bKeyOn, int chanOPL, int chanMIDI) 780 | { 781 | double keyFrac = freq2key(curfreq[chanOPL], reg[chanOPL].iOctave); 782 | int key = (int)round(keyFrac); 783 | if ((key > 0) && (bKeyOn)) { 784 | // This is set to true to forcibly stop a MIDI keyon being generated for 785 | // this note. This is done when a pitchbend is deemed as having done the 786 | // job properly. 787 | bool bKeyonAgain = true; 788 | 789 | if (keyAlreadyOn[chanMIDI]) { 790 | // There's already a note playing on this channel, just worry about the pitch of that 791 | 792 | if (mapchannel[chanMIDI] != gm_drumchannel) { 793 | // We're using a normal instrument here 794 | 795 | if (::bUsePitchBends) { 796 | // It's the same note, but the pitch is off just slightly, use a pitchbend 797 | //double dbDiff = fabs(keyFrac - key); // should be between -0.9999 and 0.9999 798 | double dbDiff = keyFrac - (double)(lastkey[chanMIDI] - transpose[chanMIDI]); // hopefully between -PITCHBEND_RANGE and PITCHBEND_RANGE 799 | 800 | if (dbDiff > PITCHBEND_RANGE) { 801 | fprintf(stderr, "Warning: This song wanted to pitchbend by %.2f notes, but the maximum is %.1f\n", dbDiff, PITCHBEND_RANGE); 802 | 803 | // Turn this note off 804 | write->noteoff(mapchannel[chanMIDI], lastkey[chanMIDI]); 805 | ::iNotesActive--; 806 | lastkey[chanMIDI] = -1; 807 | keyAlreadyOn[chanMIDI] = false; 808 | // leave bKeyonAgain as true, so that a noteon will be played instead 809 | } else { 810 | int iNewBend = (int)(pitchbend_center + (PITCHBEND_ONESEMITONE * dbDiff)); 811 | if (iNewBend != pitchbent[chanMIDI]) { 812 | //printf("pitchbend to %d/%.2lf (center + %d) (%.2lf " 813 | // "semitones)\n", iNewBend, (double)pitchbend_center*2, 814 | // (int)(iNewBend - pitchbend_center), (double)dbDiff); 815 | write->pitchbend(mapchannel[chanMIDI], iNewBend); 816 | // ::iPitchbendCount++; 817 | pitchbent[chanMIDI] = iNewBend; 818 | } 819 | // This pitchbend has done the job, don't play a noteon 820 | bKeyonAgain = false; 821 | } 822 | } else { 823 | // We're not using pitchbends, so just switch off the note if it's different (the next one will play below) 824 | if ((::bApproximatePitchbends) && (key != (lastkey[chanMIDI] - transpose[chanMIDI]))) { 825 | write->noteoff(mapchannel[chanMIDI], lastkey[chanMIDI]); 826 | ::iNotesActive--; 827 | lastkey[chanMIDI] = -1; 828 | keyAlreadyOn[chanMIDI] = false; 829 | //bKeyonAgain = true; 830 | } else { 831 | // Same note, different pitch, just pretend like it's not there 832 | bKeyonAgain = false; 833 | } 834 | } 835 | } else { 836 | // This has mapped to a percussive MIDI note, so no pitchbends (or 837 | // we'll bend all the percussive notes on the MIDI percussion channel.) 838 | // But we don't want to play the note again, 'cos it's already on, so 839 | // just ignore the keyon event. 840 | // We'll also end up here if the instrument parameters are changed 841 | // while the instrument is sounding, e.g. to change the characteristics 842 | // of a hihat without sounding a new note. This won't be converted. 843 | bKeyonAgain = false; 844 | } 845 | } // else this is a percussive instrument 846 | 847 | //} else { 848 | //if ((!bDontKeyonAgain) && ((!keyAlreadyOn[channel]) || (::bUsePitchBends))) { // If *now* there's no note playing... (or we're using pitchbends, i.e. a portamento has been set up) 849 | if (bKeyonAgain) { // If *now* there's no note playing... (or we're using pitchbends, i.e. a portamento has been set up) 850 | // See if we need to update anything 851 | 852 | // See if the instrument needs to change 853 | int i = findinstr(chanMIDI); 854 | if ( 855 | (i >= 0) && ( 856 | (instr[i].prog != lastprog[chanMIDI]) || 857 | ( 858 | (instr[i].isdrum) && 859 | (drumnote[chanMIDI] != instr[i].note) 860 | ) || ( 861 | // Same instrument mapping, but different mute setting? 862 | (instr[i].muted != mute[chanMIDI]) 863 | ) 864 | ) 865 | ) { 866 | printf("// Ch%02d <- %s\n", chanMIDI, instr[i].name); 867 | if (!instr[i].isdrum) { 868 | // Normal instrument (not MIDI percussion) 869 | assert(instr[i].prog >= 0); 870 | 871 | if (mapchannel[chanMIDI] == gm_drumchannel) { 872 | // This was playing drums, now we're back to normal notes 873 | 874 | // make sure this sets things back to what they were in the init 875 | // section in main() 876 | mapchannel[chanMIDI] = chanMIDI; 877 | drumnote[chanMIDI] = -1; // NOTE: This drumnote won't be reset if the drum instrument was muted! (As it then wouldn't have been assigned to gm_drumchannel) 878 | } 879 | 880 | transpose[chanMIDI] = instr[i].iTranspose; 881 | write->program(mapchannel[chanMIDI], lastprog[chanMIDI] = instr[i].prog); 882 | } else { 883 | // This new instrument is a drum 884 | assert(instr[i].prog == -1); 885 | 886 | /*if (instr[i].muted) { 887 | // This instrument is muted, which means whichever channel we 888 | // assign it to will become muted. We can't therefore assign it to 889 | // the drum channel as we normally would, otherwise all the MIDI 890 | // percussion will become muted. So we assign it to its normal 891 | // channel instead, like we would with a non-percussion instrument. 892 | mapchannel[chanMIDI] = chanMIDI; 893 | } else { 894 | mapchannel[chanMIDI] = gm_drumchannel; 895 | }*/ 896 | mapchannel[chanMIDI] = gm_drumchannel; 897 | drumnote[chanMIDI] = instr[i].note; 898 | lastprog[chanMIDI] = instr[i].prog; 899 | // drums don't use transpose values 900 | } 901 | mute[chanMIDI] = instr[i].muted; 902 | } 903 | 904 | // Play the note 905 | //if ((::bUsePitchBends) && (!keyAlreadyOn[channel])) { 906 | if ((::bUsePitchBends) && (mapchannel[chanMIDI] != gm_drumchannel)) { // If pitchbends are enabled and this isn't a percussion instrument 907 | double dbDiff = keyFrac - key; // should be between -0.9999 and 0.9999 908 | assert(dbDiff < PITCHBEND_RANGE); // not really necessary... 909 | 910 | int iNewBend = (int)(pitchbend_center + (PITCHBEND_ONESEMITONE * dbDiff)); 911 | if (iNewBend != pitchbent[chanMIDI]) { 912 | //printf("new note at pitchbend %d\n", iNewBend); 913 | write->pitchbend(mapchannel[chanMIDI], iNewBend); // pitchbends are between 0x0000L and 0x2000L 914 | // ::iPitchbendCount++; 915 | pitchbent[chanMIDI] = iNewBend; 916 | } 917 | } 918 | 919 | int level; 920 | if (!mute[chanMIDI]) { 921 | if (::bEnableVolume) { 922 | level = reg[chanOPL].reg40[1] & 0x3f; 923 | if (level > 0x30) level = 0x30; // don't allow fully silent notes 924 | } else level = 0; // 0 == loudest 925 | } else { 926 | level = 0x3f; // silent 927 | } 928 | 929 | if (mapchannel[chanMIDI] != gm_drumchannel) { 930 | // Normal note 931 | lastkey[chanMIDI] = key + transpose[chanMIDI]; 932 | } else { 933 | // Percussion 934 | //write->noteon(gm_drumchannel, drumnote[chanMIDI], (0x3f - level) << 1); 935 | lastkey[chanMIDI] = drumnote[chanMIDI]; 936 | } 937 | 938 | write->noteon(mapchannel[chanMIDI], lastkey[chanMIDI], (0x3f - level) << 1); 939 | //printf("note on chan %d, mute is %s\n", chanMIDI, mute[chanMIDI] ? "true" : "false"); 940 | ::iNotesActive++; 941 | ::iTotalNotes++; 942 | 943 | // If this note went on with a pitchbend active on the channel, count it 944 | if (pitchbent[chanMIDI] != pitchbend_center) ::iPitchbendCount++; 945 | 946 | keyAlreadyOn[chanMIDI] = true; 947 | 948 | } // if (not muted) 949 | 950 | } else { 951 | // There's no note currently playing on this channel, so if we've still got 952 | // one switch it off. 953 | if (lastkey[chanMIDI] != -1) { 954 | write->noteoff(mapchannel[chanMIDI], lastkey[chanMIDI]); 955 | ::iNotesActive--; 956 | lastkey[chanMIDI] = -1; 957 | keyAlreadyOn[chanMIDI] = false; 958 | } 959 | } 960 | 961 | return; 962 | } 963 | 964 | static const char* dro2hwtypestr(unsigned hwtype) { 965 | switch(hwtype) { 966 | case 0: return "OPL2"; 967 | case 1: return "OPL2 dual"; 968 | case 2: return "OPL3"; 969 | default: return "UNKNOWN"; 970 | } 971 | } 972 | 973 | int main(int argc, char**argv) 974 | { 975 | int c; 976 | 977 | // Defaults 978 | ::dbConversionVal = 49716.0; 979 | 980 | argc--; argv++; 981 | while (argc > 0 && **argv == '-') 982 | { 983 | if (strncasecmp(*argv, "-r", 2) == 0) { 984 | ::bRhythm = false; 985 | printf("Rhythm-mode instruments disabled.\n"); 986 | } else if (strncasecmp(*argv, "-p", 2) == 0) { 987 | ::bUsePitchBends = false; 988 | printf("Pitchbends disabled.\n"); 989 | } else if (strncasecmp(*argv, "-a", 2) == 0) { 990 | ::bApproximatePitchbends = true; 991 | } else if (strncasecmp(*argv, "-i", 2) == 0) { 992 | ::bPerfectMatchesOnly = true; 993 | printf("Only using exact instrument matches - approximations disabled!\n"); 994 | } else if (strncasecmp(*argv, "-v", 2) == 0) { 995 | ::bEnableVolume = false; 996 | printf("Note velocity disabled, all notes will be played as loud as possible.\n"); 997 | } else if (strncasecmp(*argv, "-s", 2) == 0) { 998 | ::bWriteSbiInstruments = true; 999 | } else if (strncasecmp(*argv, "-c", 2) == 0) { 1000 | argc--; argv++; 1001 | if (argc == 0) { 1002 | fprintf(stderr, "-c requires a parameter\n"); 1003 | usage(); 1004 | } 1005 | if (strncasecmp(*argv, "alt", 3) == 0) { 1006 | ::dbConversionVal = 50000.0; 1007 | } else { 1008 | // Use the given value 1009 | ::dbConversionVal = strtod(*argv, NULL); 1010 | if (::dbConversionVal == 0) { 1011 | fprintf(stderr, "-c requires a non-zero parameter\n"); 1012 | usage(); 1013 | } 1014 | } 1015 | } else if (strncasecmp(*argv, "--version", 9) == 0) { 1016 | version(); 1017 | return 0; 1018 | } else { 1019 | fprintf(stderr, "invalid option %s\n", *argv); 1020 | usage(); 1021 | } 1022 | argc--; argv++; 1023 | } 1024 | if (argc < 2) usage(); 1025 | 1026 | if ((::bUsePitchBends) && (::bApproximatePitchbends)) { 1027 | fprintf(stderr, "ERROR: Pitchbends can only be approximated (-a) if " 1028 | "proper MIDI pitchbends are disabled (-p)\n"); 1029 | return 1; 1030 | } 1031 | 1032 | input = argv[0]; 1033 | output = argv[1]; 1034 | if (strcmp(input, output) == 0) 1035 | { 1036 | fprintf(stderr, "cannot convert to same file\n"); 1037 | return 1; 1038 | } 1039 | 1040 | if (!loadInstruments()) return 1; 1041 | 1042 | 1043 | f = fopen(input, READ_BINARY); 1044 | if (!f) 1045 | { 1046 | perror(input); 1047 | return 1; 1048 | } 1049 | unsigned long imflen = 0; 1050 | struct dro2hdr { 1051 | uint32_t iLengthPairs; 1052 | uint32_t iLengthMS; 1053 | uint8_t iHardwareType; 1054 | uint8_t iFormat; 1055 | uint8_t iCompression; 1056 | uint8_t iShortDelayCode; 1057 | uint8_t iLongDelayCode; 1058 | uint8_t iCodemapLength; 1059 | uint8_t iCodemap[128]; 1060 | } dro2hdr; 1061 | 1062 | unsigned char cSig[9]; 1063 | fseek(f, 0, SEEK_SET); 1064 | fread(cSig, 1, 8, f); 1065 | cSig[8] = 0; 1066 | iSpeed = 0; 1067 | 1068 | if (strcmp((char *)cSig, "DBRAWOPL") == 0) { 1069 | ::iFormat = FORMAT_DRO; 1070 | unsigned long version = readUINT32LE(f); 1071 | if (version == 0x10000) { 1072 | printf("Input file is in DOSBox DRO v1.0 format.\n"); 1073 | } else if (version == 0x2) { 1074 | printf("Input file is in DOSBox DRO v2.0 format.\n"); 1075 | ::iFormat = FORMAT_DRO2; 1076 | } else { 1077 | printf("Input file is in DOSBox DRO format, but an unknown version!\n"); 1078 | return 3; 1079 | } 1080 | ::iInitialSpeed = 1000; 1081 | // filepos at this point is 12, pointing at main DRO header. 1082 | 1083 | if(::iFormat == FORMAT_DRO) { 1084 | fseek(f, 16, SEEK_SET); // seek to "length in bytes" field 1085 | imflen = readUINT32LE(f); 1086 | } else { 1087 | dro2hdr.iLengthPairs = readUINT32LE(f); 1088 | dro2hdr.iLengthMS = readUINT32LE(f); 1089 | fread(&dro2hdr.iHardwareType, 1, 6, f); 1090 | if(dro2hdr.iCodemapLength >= 128) { 1091 | fprintf(stderr, "invalid setting %u for iCodemapLength!\n", (unsigned) dro2hdr.iCodemapLength); 1092 | return 2; 1093 | } 1094 | fread(dro2hdr.iCodemap, 1, dro2hdr.iCodemapLength, f); 1095 | imflen = dro2hdr.iLengthPairs * 2; 1096 | printf(">>> === DROv2 header info === <<<\n"); 1097 | printf(">>> iLengthPairs\t%u\n", (unsigned) dro2hdr.iLengthPairs); 1098 | printf(">>> iLengthMS\t\t%u\n", (unsigned) dro2hdr.iLengthMS); 1099 | printf(">>> iHardwareType\t%u (%s)\n", (unsigned) dro2hdr.iHardwareType, dro2hwtypestr(dro2hdr.iHardwareType)); 1100 | printf(">>> iFormat\t\t%u\n", (unsigned) dro2hdr.iFormat); 1101 | printf(">>> iCompression\t%u\n", (unsigned) dro2hdr.iCompression); 1102 | printf(">>> iShortDelayCode\t%u\n", (unsigned) dro2hdr.iShortDelayCode); 1103 | printf(">>> iLongDelayCode\t%u\n", (unsigned) dro2hdr.iLongDelayCode); 1104 | printf(">>> iCodemapLength\t%u\n", (unsigned) dro2hdr.iCodemapLength); 1105 | if(dro2hdr.iCompression) { 1106 | printf("unsupported DRO2 compression type.\n"); 1107 | return 2; 1108 | } 1109 | } 1110 | 1111 | } else if (strcmp((char *)cSig, "RAWADATA") == 0) { 1112 | ::iFormat = FORMAT_RAW; 1113 | printf("Input file is in Rdos RAW format.\n"); 1114 | 1115 | // Read until EOF (0xFFFF is really the end but we'll check that during conversion) 1116 | fseek(f, 0, SEEK_END); 1117 | imflen = ftell(f); 1118 | 1119 | fseek(f, 8, SEEK_SET); // seek to "initial clock speed" field 1120 | ::iInitialSpeed = 1000; 1121 | int iClockSpeed = readUINT16LE(f); 1122 | if ((iClockSpeed == 0) || (iClockSpeed == 0xFFFF)) { 1123 | ::iSpeed = (int)18.2; // default to 18.2Hz...well, 18Hz thanks to rounding 1124 | } else { 1125 | ::iSpeed = (int)(1193180.0 / iClockSpeed); 1126 | } 1127 | } else { 1128 | ::iFormat = FORMAT_IMF; 1129 | if ((cSig[0] == 0) && (cSig[1] == 0)) { 1130 | printf("Input file appears to be in IMF type-0 format.\n"); 1131 | fseek(f, 0, SEEK_END); 1132 | imflen = ftell(f); 1133 | fseek(f, 0, SEEK_SET); 1134 | } else { 1135 | printf("Input file appears to be in IMF type-1 format.\n"); 1136 | imflen = cSig[0] + (cSig[1] << 8); 1137 | fseek(f, 2, SEEK_SET); // seek to start of actual OPL data 1138 | } 1139 | if (strcasecmp(&input[strlen(input)-3], "imf") == 0) { 1140 | printf("File extension is .imf - using 560Hz speed (rename to .wlf if " 1141 | "this is too slow)\n"); 1142 | ::iInitialSpeed = 560; 1143 | } else if (strcasecmp(&input[strlen(input)-3], "wlf") == 0) { 1144 | printf("File extension is .wlf - using 700Hz speed (rename to .imf if " 1145 | "this is too fast)\n"); 1146 | ::iInitialSpeed = 700; 1147 | } else { 1148 | printf("Unknown file extension - must be .imf or .wlf\n"); 1149 | return 3; 1150 | } 1151 | } 1152 | printf("Using conversion constant of %.1lf\n", ::dbConversionVal); 1153 | 1154 | write = new MidiWrite(output); 1155 | if (!write) { 1156 | fprintf(stderr, "out of memory\n"); 1157 | return 1; 1158 | } 1159 | if (!write->getf()) { 1160 | perror(output); 1161 | return 1; 1162 | } 1163 | if (iSpeed == 0) { 1164 | iSpeed = iInitialSpeed; 1165 | } 1166 | resolution = iInitialSpeed / 2; 1167 | write->head(/* version */ 0, /* track count updated later */0, resolution); 1168 | 1169 | write->track(); 1170 | write->tempo((long)(60000000.0 / tempo)); 1171 | write->tact(4,4,24,8); 1172 | 1173 | for (c = 0; c < 10; c++) { 1174 | mapchannel[c] = c; 1175 | write->resetctrlrs(mapchannel[c], 0); // Reset All Controllers (Ensures default settings upon every playback). 1176 | write->volume(mapchannel[c], 127); 1177 | write->balance(mapchannel[c], 64); // Usually called 'Pan'. 1178 | write->expression(mapchannel[c], 127); // Similar to 'Volume', but this is primarily used for volume damping. 1179 | } 1180 | 1181 | for (c = 0; c <= 8; c++) { 1182 | lastprog[c] = -1; 1183 | reg[c].iOctave = 0; 1184 | } 1185 | 1186 | int delay = 0; 1187 | int channel; 1188 | int code, param; 1189 | 1190 | for (c = 0; c < 9; c++) { 1191 | curfreq[c] = 0; 1192 | mapchannel[c] = c; // This can get reset when playing a drum and then a normal instrument on a channel - see instrument-change code below 1193 | keyAlreadyOn[c] = false; 1194 | lastkey[c] = -1; // last MIDI key pressed on this channel 1195 | pitchbent[c] = (int)pitchbend_center; 1196 | transpose[c] = 0; 1197 | drumnote[c] = 0; // probably not necessary... 1198 | mute[c] = false; 1199 | 1200 | if (::bUsePitchBends) { 1201 | write->control(mapchannel[c], 100, 0); // RPN LSB for "Pitch Bend Sensitivity" 1202 | write->control(mapchannel[c], 101, 0); // RPN MSB for "Pitch Bend Sensitivity" 1203 | write->control(mapchannel[c], 6, (int)PITCHBEND_RANGE); // Data for Pitch Bend Sensitivity (in semitones) - controller 38 can be used for cents in addition 1204 | write->control(mapchannel[c], 100, 0x7F); // RPN LSB for "Finished" 1205 | write->control(mapchannel[c], 101, 0x7F); // RPN MSB for "Finished" 1206 | } 1207 | // write->pitchbend(mapchannel[c], pitchbend_center); 1208 | } 1209 | // Rhythm-mode only channels 1210 | for (c = 10; c < 15; c++) { 1211 | keyAlreadyOn[c] = false; 1212 | mapchannel[c] = c; // as above, this will be changed later but must be 1213 | // eventually set back to this value here (or the instrument will jump 1214 | // channels unexpectedly.) 1215 | pitchbent[c] = (int)pitchbend_center; 1216 | lastkey[c] = -1; // last MIDI key pressed on this channel 1217 | transpose[c] = 0; 1218 | drumnote[c] = 0; // probably not necessary... 1219 | mute[c] = false; 1220 | } 1221 | 1222 | int iMinLen = 2; // Minimum length for valid notes to still be present 1223 | if(::iFormat == FORMAT_IMF) iMinLen = 4; 1224 | 1225 | unsigned long iSize = imflen; // sometimes the counter wraps around, need this to stop it from happening 1226 | while ((imflen >= (unsigned long)iMinLen) && (imflen <= iSize)) { 1227 | 1228 | // Get the next OPL register and value from the input file 1229 | 1230 | switch (::iFormat) { 1231 | case FORMAT_IMF: 1232 | // Write the last iteration's delay (since the delay needs to come *after* the note) 1233 | write->time(delay); 1234 | 1235 | code = readByte(f); 1236 | param = readByte(f); 1237 | delay = readUINT16LE(f); 1238 | imflen -= 4; 1239 | break; 1240 | 1241 | case FORMAT_DRO2: 1242 | code = readByte(f); 1243 | param = readByte(f); 1244 | imflen -= 2; 1245 | if(code == dro2hdr.iShortDelayCode) { 1246 | delay = param + 1; 1247 | } else if (code == dro2hdr.iLongDelayCode) { 1248 | delay = (param + 1) << 8; 1249 | } 1250 | if(delay) { 1251 | // Write any delay (as this needs to come *before* the next note) 1252 | write->time(delay); 1253 | delay = 0; 1254 | continue; 1255 | } 1256 | if((code & 0x7f) >= dro2hdr.iCodemapLength) { 1257 | fprintf(stderr, "error: corrupt data encountered!\n"); 1258 | return 2; 1259 | } 1260 | code = (code & 0x80) | dro2hdr.iCodemap[code & 0x7f]; 1261 | break; 1262 | 1263 | case FORMAT_DRO: 1264 | code = readByte(f); 1265 | imflen--; 1266 | switch (code) { 1267 | case 0x00: // delay (byte) 1268 | delay += 1 + readByte(f); 1269 | imflen--; 1270 | continue; 1271 | case 0x01: // delay (int) 1272 | delay += 1 + readUINT16LE(f); 1273 | imflen -= 2; 1274 | continue; 1275 | case 0x02: // use first OPL chip 1276 | case 0x03: // use second OPL chip 1277 | fprintf(stderr, "Warning: This song uses multiple OPL chips - this isn't yet supported!\n"); 1278 | continue; 1279 | case 0x04: // escape 1280 | code = readByte(f); 1281 | imflen--; 1282 | break; 1283 | } 1284 | param = readByte(f); 1285 | imflen--; 1286 | 1287 | // Write any delay (as this needs to come *before* the next note) 1288 | write->time(delay); 1289 | delay = 0; 1290 | break; 1291 | case FORMAT_RAW: 1292 | param = readByte(f); 1293 | code = readByte(f); 1294 | imflen -= 2; 1295 | switch (code) { 1296 | case 0x00: // delay 1297 | delay += param; 1298 | continue; 1299 | case 0x02: // control data 1300 | switch (param) { 1301 | case 0x00: { 1302 | if (delay != 0) { 1303 | // See below - we need to write out any delay at the old clock speed before we change it 1304 | write->time((delay * iInitialSpeed / ::iSpeed)); 1305 | delay = 0; 1306 | } 1307 | int iClockSpeed = readUINT16LE(f); 1308 | if ((iClockSpeed == 0) || (iClockSpeed == 0xFFFF)) { 1309 | printf("Speed set to invalid value, ignoring speed change.\n"); 1310 | } else { 1311 | ::iSpeed = (int)round(1193180.0 / iClockSpeed); 1312 | printf("Speed changed to %dHz\n", iSpeed); 1313 | } 1314 | imflen -= 2; 1315 | break; 1316 | } 1317 | case 0x01: 1318 | case 0x02: 1319 | printf("Switching OPL ports is not yet implemented!\n"); 1320 | break; 1321 | } 1322 | continue; 1323 | case 0xFF: 1324 | if (param == 0xFF) { 1325 | // End of song 1326 | imflen = 0; 1327 | continue; 1328 | } 1329 | break; 1330 | } 1331 | 1332 | // Write any delay (as this needs to come *before* the next note) 1333 | // Since our global clock speed is 1000Hz, we have to multiply this 1334 | // delay accordingly as the delay units are in the current clock speed. 1335 | // This calculation converts them into 1000Hz delay units regardless of 1336 | // the current clock speed. 1337 | if (delay != 0) write->time((delay * iInitialSpeed / ::iSpeed)); 1338 | //printf("delay is %d (ticks %d)\n", (delay * iInitialSpeed / ::iSpeed), delay); 1339 | delay = 0; 1340 | break; 1341 | 1342 | default: // should never happen 1343 | break; 1344 | 1345 | } // switch (::iFormat) 1346 | 1347 | // Convert the OPL register and value into a MIDI event 1348 | 1349 | if (code >= 0xa0 && code <= 0xa8) { // set freq bits 0-7 1350 | channel = code-0xa0; 1351 | curfreq[channel] = (curfreq[channel] & 0xF00) + (param & 0xff); 1352 | if (keyAlreadyOn[channel]) { 1353 | param = 0x20; // bare noteon for code below 1354 | doNoteOnOff(true, channel, channel); 1355 | } 1356 | continue; 1357 | } else if (code >= 0xB0 && code <= 0xB8) { // set freq bits 8-9 and octave and on/off 1358 | channel = code - 0xb0; 1359 | curfreq[channel] = (curfreq[channel] & 0x0FF) + ((param & 0x03)<<8); 1360 | // save octave so we know what it is if we run 0xA0-0xA8 regs change code 1361 | // next (which doesn't have the octave) 1362 | reg[channel].iOctave = (param >> 2) & 7; 1363 | 1364 | int keyon = (param >> 5) & 1; 1365 | doNoteOnOff(keyon, channel, channel); 1366 | } else if ((code == 0xBD) && (::bRhythm)) { 1367 | if ((param >> 5) & 1) { 1368 | // Bass Drum 1369 | doNoteOnOff((param >> 4) & 1, channel, CHAN_BASSDRUM); 1370 | doNoteOnOff((param >> 3) & 1, channel, CHAN_SNAREDRUM); 1371 | doNoteOnOff((param >> 2) & 1, channel, CHAN_TOMTOM); 1372 | doNoteOnOff((param >> 1) & 1, channel, CHAN_TOPCYMBAL); 1373 | doNoteOnOff( param & 1, channel, CHAN_HIHAT); 1374 | } 1375 | } else if (code >= 0x20 && code <= 0x35) { 1376 | channel = GET_CHANNEL(code-0x20); 1377 | reg[channel].reg20[GET_OP(code-0x20)] = param; 1378 | } else if (code >= 0x40 && code <= 0x55) { 1379 | channel = GET_CHANNEL(code-0x40); 1380 | reg[channel].reg40[GET_OP(code-0x40)] = param; 1381 | } else if (code >= 0x60 && code <= 0x75) { 1382 | channel = GET_CHANNEL(code-0x60); 1383 | reg[channel].reg60[GET_OP(code-0x60)] = param; 1384 | } else if (code >= 0x80 && code <= 0x95) { 1385 | channel = GET_CHANNEL(code-0x80); 1386 | reg[channel].reg80[GET_OP(code-0x80)] = param; 1387 | } else if (code >= 0xc0 && code <= 0xc8) { 1388 | channel = code-0xc0; 1389 | reg[channel].regC0 = param; 1390 | } else if (code >= 0xe0 && code <= 0xF5) { 1391 | channel = GET_CHANNEL(code-0xe0); 1392 | reg[channel].regE0[GET_OP(code-0xe0)] = param; 1393 | } 1394 | } // while(readinput...) 1395 | 1396 | for (c = 0; c < 10; c++) { 1397 | mapchannel[c] = c; 1398 | write->allnotesoff(mapchannel[c], 0); // All Notes Off (Ensures that even incomplete Notes will be switched-off per each MIDI channel at the end-of-playback). 1399 | } 1400 | 1401 | delete write; 1402 | fclose(f); 1403 | 1404 | // Display completion message and some stats 1405 | printf("\nConversion complete. Wrote %s\n\n Total pitchbent notes: %d\n" 1406 | " Total notes: %d\n Notes still active at end of song: %d\n\n", 1407 | output, ::iPitchbendCount, ::iTotalNotes, ::iNotesActive); 1408 | 1409 | return 0; 1410 | } 1411 | --------------------------------------------------------------------------------