Add support for writing .sbi instruments to dro2midi.
This commit is contained in:
parent
ebf75b5dd6
commit
265be73088
2 changed files with 221 additions and 2 deletions
177
dro2midi/README
Normal file
177
dro2midi/README
Normal file
|
@ -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
|
|
@ -70,8 +70,12 @@
|
||||||
// - Added detectors for the (at this time) two DRO formats, and a hint
|
// - 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.
|
// 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 MAPPING_FILE "inst.txt"
|
||||||
|
|
||||||
#define PATCH_NAME_FILE "patch.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 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 bPerfectMatchesOnly = false; // if true, only match perfect instruments
|
||||||
bool bEnableVolume = true; // enable note velocity based on OPL instrument volume
|
bool bEnableVolume = true; // enable note velocity based on OPL instrument volume
|
||||||
|
bool bWriteSbiInstruments = false; // write detected instruments to .SBI files
|
||||||
|
|
||||||
// Rhythm instruments
|
// Rhythm instruments
|
||||||
enum RHYTHM_INSTRUMENT {
|
enum RHYTHM_INSTRUMENT {
|
||||||
|
@ -240,7 +245,7 @@ void version()
|
||||||
printf("DRO2MIDI v" VERSION " - Convert raw Adlib captures to General MIDI\n"
|
printf("DRO2MIDI v" VERSION " - Convert raw Adlib captures to General MIDI\n"
|
||||||
"Written by malvineous@shikadi.net in 2007\n"
|
"Written by malvineous@shikadi.net in 2007\n"
|
||||||
"Heavily based upon IMF2MIDI written by Guenter Nagler in 1996\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"
|
"http://www.shikadi.net/utils/\n"
|
||||||
"\n"
|
"\n"
|
||||||
);
|
);
|
||||||
|
@ -267,6 +272,8 @@ void usage()
|
||||||
" of artificial pitchbends in the output MIDI.\n"
|
" of artificial pitchbends in the output MIDI.\n"
|
||||||
" -v Disable note velocity and play all notes as loudly as possible,\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"
|
" 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"
|
"\n"
|
||||||
"Supported input formats:\n"
|
"Supported input formats:\n"
|
||||||
" .raw Rdos RAW OPL capture\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)
|
int findinstr(int chanMIDI)
|
||||||
{
|
{
|
||||||
assert((chanMIDI < 9) || ((chanMIDI >= CHAN_BASSDRUM) && (chanMIDI <= CHAN_HIHAT)));
|
assert((chanMIDI < 9) || ((chanMIDI >= CHAN_BASSDRUM) && (chanMIDI <= CHAN_HIHAT)));
|
||||||
|
@ -657,6 +694,9 @@ int findinstr(int chanMIDI)
|
||||||
reg[chanOPL].regC0,
|
reg[chanOPL].regC0,
|
||||||
reg[chanOPL].regE0[0], reg[chanOPL].regE0[1]
|
reg[chanOPL].regE0[0], reg[chanOPL].regE0[1]
|
||||||
);
|
);
|
||||||
|
if (::bWriteSbiInstruments) {
|
||||||
|
writesbi(output, instrcnt, chanOPL);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TomTom:
|
case TomTom:
|
||||||
case HiHat:
|
case HiHat:
|
||||||
|
@ -930,6 +970,8 @@ int main(int argc, char**argv)
|
||||||
} else if (strncasecmp(*argv, "-v", 2) == 0) {
|
} else if (strncasecmp(*argv, "-v", 2) == 0) {
|
||||||
::bEnableVolume = false;
|
::bEnableVolume = false;
|
||||||
printf("Note velocity disabled, all notes will be played as loud as possible.\n");
|
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) {
|
} else if (strncasecmp(*argv, "-c", 2) == 0) {
|
||||||
argc--; argv++;
|
argc--; argv++;
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
|
|
Loading…
Reference in a new issue