From 265be7308802418cc9a822885427447e8803c424 Mon Sep 17 00:00:00 2001 From: bruce Date: Thu, 14 Nov 2013 21:47:47 +0800 Subject: [PATCH] Add support for writing .sbi instruments to dro2midi. --- dro2midi/README | 177 ++++++++++++++++++++++++++++++++++++++++++ dro2midi/dro2midi.cpp | 46 ++++++++++- 2 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 dro2midi/README diff --git a/dro2midi/README b/dro2midi/README new file mode 100644 index 0000000..2763938 --- /dev/null +++ b/dro2midi/README @@ -0,0 +1,177 @@ +--------------------------------------------------------------- + DRO2MIDI - version 1.4 (2009-03-29) + Written by malvineous@shikadi.net + Heavily based upon IMF2MIDI written by Guenter Nagler in 1996 + http://www.shikadi.net/utils/ +--------------------------------------------------------------- + +// What is it? +//////////////// + +DRO2MIDI converts Adlib music (in .dro, .imf or .raw format) into standard +MIDI files, so that it can be played or edited in most audio applications, or +so the notes can be extracted to be played on a real instrument. + +// Features +///////////// + + * Converts DOSBox .dro captures, id Software .imf songs and Rdos .raw captures + + * Converts OPL frequency changes into MIDI pitchbends + + * Tries to map Adlib instruments to MIDI instruments as best it can, but + these mappings can be easily added to and changed + + * Instruments can also be mapped to MIDI percussion + + * OPL rhythm-mode percussion is converted (v1.4 adds support for user-defined + mapping via new syntax in the mapping file) + + * The OPL conversion constant can be changed (see -c option) to more + accurately convert notes without excessive pitchbend events + +// Usage +////////// + + Linux: $ dro2midi file.dro file.mid + + Windows: C:\>dro2midi file.dro file.mid + +For a list of the command line options run dro2midi with no parameters or +see below for more details. For best results, all the .txt data files should +be in the current directory. + +Instrument mappings between Adlib registers and MIDI instruments are stored in +inst.txt. This file contains a number of existing mappings, but additional +mappings can easily be added. During conversion, if an exact match cannot +be found the mapping with the closest Adlib parameters will be used instead. +A message will be printed when this happens, along with a line that can be +copied into inst.txt to provide an exact match. This approximation can have +the unfortunate side effect of providing some odd conversions, such as +converting a bass-line into a monotonic drum. + +To get a perfect conversion you may wish to delete all but the first +"all-zero" instrument in inst.txt, which will cause all instruments to be +converted as a piano. You can then copy the definitions printed during +conversion one by one into inst.txt, to assign the best-sounding instrument +without worring about any default mappings taking over. Alternatively the -i +option can be used which will disable the closest-match algorithm, and only +exact matches will be used (again, anything that can't be exactly matched will +be mapped as a piano.) + +// Command-line options +///////////////////////// + +-p disables generation of MIDI pitchbends. This results in a single note-on +when the instrument sounds, but no further pitch change results until the +note is switched off again. This can also be used to prevent the large number +of small pitchbends generated when the conversion constant is slightly off (but +see the -c option, which now provides a better way around this.) + +-a will, if pitchbends are disabled with -p, approximate any pitchbend by +playing the nearest note to the new pitch at the time. This results in a +"hammering" of notes during a pitchbend, which while humourous, is probably +of limited use. + +-r disables the conversion of OPL rhythm mode instruments. As OPL rhythm mode +conversion is now quite flexible, this option should rarely be needed. + +-i will disable the approximation algorithm which selects similar instruments +when an exact match cannot be found in the mapping file (insts.txt). This is +useful when trying to create a perfect map for a single song, as it makes it +easier to pick out which instruments are being mapped. This option should not +be used for the final conversion however, as any instruments that haven't been +precisely mapped will come out (by default) as a grand piano. (This default +mapping is simply the first entry in insts.txt.) + +-c changes the conversion constant used when converting OPL notes into their +MIDI equivalents. This is the heart of what DRO2MIDI does. Unfortunately +depending on which set of documents are available, those writing OPL *players* +are told the conversion constant is either 49716, or 50000. Thus half the +games out there use one, and half use the other. The result of using the wrong +constant in the DRO2MIDI conversion is a tiny difference in pitch when the song +is played (about 1/17th of a cent.) Most people will be unable to hear the +difference between these two values, however if the wrong constant is used by +DRO2MIDI during the conversion into MIDI, the resulting file will contain +thousands of small pitchbend events, as it tries to approximate the exact OPL +note played. Using -c to try a different constant should solve this problem. +The default constant is 49716, and "-c alt" will change the constant to 50000. +It is possible to specify an arbitrary constant like "-c 49999" however this +should be unnecessary unless the same nonstandard constant was used wherever +the song was originally played. Note that no error checking is performed here, +so out of range values could easily crash the program (not that that's a major +problem though...) After conversion the number of pitchbend events is +displayed at the end of the output, so it will be obvious when the correct +constant is in use as this number will be significantly smaller than with +any other constant. + +-v disables the volume detection. Normally DRO2MIDI will take the OPL +carrier's "Level" amount and translate it to a MIDI note velocity. This will +result in the output MIDI file more accurately matching the loud and quiet +parts of the OPL song. Some songs (e.g. Stunts) somehow manage to work with +these volume levels set to zero, which results in very quiet MIDI files (there +is an internal limit as to how quiet a note can sound to prevent it being lost +entirely.) If your output MIDI file is much too quiet, this option will cause +all notes to be played at maximum velocity. + +// inst.txt +///////////// + +The instrument mappings are stored in inst.txt, in a format like this: + + NO 07-12/4F-00/F2-F2/60-72/08/00-00: patch=15 # Tubular bells + +These lines are printed automatically when an unknown instrument is encounted. +All you will need to do is copy and paste the line into insts.txt and choose +a MIDI instrument for it. The file itself is read in from the current +directory during conversion, so if you run DRO2MIDI in another folder remember +to copy the file across too or your mappings won't be used. + +The first two characters indicate what type of instrument it is. The +hexadecimal numbers that follow are the Adlib register values for that +instrument. "patch=15" assigns MIDI instrument #15 for this Adlib instrument. +For percussion, "drum=35" could be used instead. Anything after a # symbol is +treated as a comment. See the comments at the top of the file for more +detailed information. + +The instrument names (and values to supply to the patch= parameter) are stored +in patch.txt, and the drum names (and numbers) in drum.txt. You may find +these files helpful to reference when selecting instruments for conversion. +(These two files are read in from the current directory during conversion to +allow the display of instrument names in status messages instead of just +numbers.) + +Note that the parser for insts.txt file is quick and dirty, so it's easy to +get a syntax error - for example, an otherwise blank line with a single space +on it will cause an error (so if you get an error about a blank line, make +sure it really is blank!) + +// License +//////////// + +DRO2MIDI was based on IMF2MIDI by Guenter Nagler. DRO2MIDI is released under +the GPL license, except where it is incompatible with IMF2MIDI's original +license, in which case IMF2MIDI's license takes precedence. + +---- Begin IMF2MIDI license ---- + +IMF2MIDI (c) 1996 was created by Guenter Nagler. + +IMF2MIDI is free and may be used as you wish with this one exception: + + You may NOT charge any fee or derive any profit for distribution + of IMF2MIDI. Thus, you may NOT sell or bundle IMF2MIDI with any + product in a retail environment (shareware disk distribution, CD-ROM, + etc.) without permission of the author. + +You may give IMF2MIDI to your friends, upload it to a BBS, or ftp it to +another internet site, as long as you don't charge anything for it. + +---- End IMF2MIDI license ---- + +// Contact +//////////// + +Source code is available at http://www.shikadi.net/utils/ + +You can e-mail me at malvineous@shikadi.net diff --git a/dro2midi/dro2midi.cpp b/dro2midi/dro2midi.cpp index 95c025d..aa44e3d 100644 --- a/dro2midi/dro2midi.cpp +++ b/dro2midi/dro2midi.cpp @@ -70,8 +70,12 @@ // - Added detectors for the (at this time) two DRO formats, and a hint // to let us know that the DRO v2.0 format is not supported. // +// v1.6 / 2013-11-14 / bsa (http://bsutherland.github.io/JuceOPLVSTi) +// - Added -s switch to save detected instruments in Creative Sound +// Blaster Instrument format (.sbi) +// -#define VERSION "1.5" +#define VERSION "1.6" #define MAPPING_FILE "inst.txt" #define PATCH_NAME_FILE "patch.txt" @@ -113,6 +117,7 @@ bool bUsePitchBends = true; // use pitch bends to better match MIDI note frequen bool bApproximatePitchbends = false; // if pitchbends are disabled, should we approximate them by playing the nearest note when the pitch changes? bool bPerfectMatchesOnly = false; // if true, only match perfect instruments bool bEnableVolume = true; // enable note velocity based on OPL instrument volume +bool bWriteSbiInstruments = false; // write detected instruments to .SBI files // Rhythm instruments enum RHYTHM_INSTRUMENT { @@ -240,7 +245,7 @@ void version() printf("DRO2MIDI v" VERSION " - Convert raw Adlib captures to General MIDI\n" "Written by malvineous@shikadi.net in 2007\n" "Heavily based upon IMF2MIDI written by Guenter Nagler in 1996\n" - "With contributions by Wraithverge (C) 2010.\n" + "With contributions by Wraithverge (C) 2010, bsa in 2013\n" "http://www.shikadi.net/utils/\n" "\n" ); @@ -267,6 +272,8 @@ void usage() " of artificial pitchbends in the output MIDI.\n" " -v Disable note velocity and play all notes as loudly as possible,\n" " instead of trying to match the volume of the OPL note.\n" + " -s Write detected instruments to .sbi files\n" + " (Creative Sound Blaster Instrument).\n" "\n" "Supported input formats:\n" " .raw Rdos RAW OPL capture\n" @@ -599,6 +606,36 @@ long compareinstr(INSTRUMENT& a, INSTRUMENT& b, RHYTHM_INSTRUMENT ri) } } +void writesbi(const char* filename, int instrno, int chanOPL) { + char fname[100]; + char title[32]; + snprintf(fname, 100, "%s_%03d.sbi", filename, instrno); + FILE* f_sbi = fopen(fname, WRITE_BINARY); + if (!f_sbi) { + fprintf(stderr, "Could not open instrument file %s for writing.\n", filename); + } else { + fwrite("SBI\x1a", sizeof(char), 4, f_sbi); + memset(title, 0, 32); + snprintf(title, 32, "dro2midi_%03d", instrno); + fwrite(title, sizeof(char), 32, f_sbi); + unsigned char instr[16]; + memset(instr, 0, 16); + instr[0] = (unsigned char)reg[chanOPL].reg20[0]; + instr[1] = (unsigned char)reg[chanOPL].reg20[1]; + instr[2] = (unsigned char)reg[chanOPL].reg40[0]; + instr[3] = (unsigned char)reg[chanOPL].reg40[1]; + instr[4] = (unsigned char)reg[chanOPL].reg60[0]; + instr[5] = (unsigned char)reg[chanOPL].reg60[1]; + instr[6] = (unsigned char)reg[chanOPL].reg80[0]; + instr[7] = (unsigned char)reg[chanOPL].reg80[1]; + instr[8] = (unsigned char)reg[chanOPL].regE0[0]; + instr[9] = (unsigned char)reg[chanOPL].regE0[1]; + instr[10] = (unsigned char)reg[chanOPL].regC0; + fwrite(instr, sizeof(char), 16, f_sbi); + fclose(f_sbi); + } +} + int findinstr(int chanMIDI) { assert((chanMIDI < 9) || ((chanMIDI >= CHAN_BASSDRUM) && (chanMIDI <= CHAN_HIHAT))); @@ -657,6 +694,9 @@ int findinstr(int chanMIDI) reg[chanOPL].regC0, reg[chanOPL].regE0[0], reg[chanOPL].regE0[1] ); + if (::bWriteSbiInstruments) { + writesbi(output, instrcnt, chanOPL); + } break; case TomTom: case HiHat: @@ -930,6 +970,8 @@ int main(int argc, char**argv) } else if (strncasecmp(*argv, "-v", 2) == 0) { ::bEnableVolume = false; printf("Note velocity disabled, all notes will be played as loud as possible.\n"); + } else if (strncasecmp(*argv, "-s", 2) == 0) { + ::bWriteSbiInstruments = true; } else if (strncasecmp(*argv, "-c", 2) == 0) { argc--; argv++; if (argc == 0) {