diff --git a/Media/icon.png b/Media/icon.png
new file mode 100644
index 0000000..a1c3d43
Binary files /dev/null and b/Media/icon.png differ
diff --git a/JuceOPLVSTi.jucer b/OPL.jucer
similarity index 50%
rename from JuceOPLVSTi.jucer
rename to OPL.jucer
index 6de47df..d09deb5 100644
--- a/JuceOPLVSTi.jucer
+++ b/OPL.jucer
@@ -1,147 +1,145 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cmd/release_win.cmd b/cmd/release_win.cmd
deleted file mode 100644
index 18a2d92..0000000
--- a/cmd/release_win.cmd
+++ /dev/null
@@ -1,23 +0,0 @@
-@ECHO OFF
-IF [%1]==[] GOTO usage
-
-ECHO Building Windows Release
-SET RELEASE_VERSION=%1
-
-MSBUILD Builds\VisualStudio2015\JuceOPLVSTi.vcxproj /p:Configuration="Release - 32-bit" /p:Platform="x86"
-MSBUILD Builds\VisualStudio2015\JuceOPLVSTi.vcxproj /p:Configuration="Release - 64-bit" /p:Platform="x64"
-
-MKDIR %RELEASE_TEMP%
-COPY "Builds\VisualStudio2015\Release - 32-bit\JuceOPLVSTi.dll" .
-COPY "Builds\VisualStudio2015\x64\Release - 64-bit\JuceOPLVSTi x64.dll" .
-
-REM We can zip using jar from the JDK!
-REM http://stackoverflow.com/a/29879102/1480560
-DEL JuceOPLVSTi_%RELEASE_VERSION%.zip
-jar -cMf JuceOPLVSTi_%RELEASE_VERSION%.zip "JuceOPLVSTi x64.dll" JuceOPLVSTi.dll Instruments
-DEL "JuceOPLVSTi x64.dll" JuceOPLVSTi.dll
-GOTO :eof
-
-:usage
-ECHO Specify version on command line (eg 0-14-1)
-
diff --git a/dro2midi/README b/dro2midi/README
deleted file mode 100644
index b388fc1..0000000
--- a/dro2midi/README
+++ /dev/null
@@ -1,182 +0,0 @@
----------------------------------------------------------------
- DRO2MIDI - version 1.6 (2013-11-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.
-
--s instructs dro2midi to write all detected instruments to a .sbi file. This
-is a 52 byte binary instrument format for OPL chips created by Creative Labs.
-It is supported by applications written to work with the OPL, such as Ad Lib
-Tracker 2.
-
-// 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/build.bat b/dro2midi/build.bat
deleted file mode 100644
index 2864811..0000000
--- a/dro2midi/build.bat
+++ /dev/null
@@ -1 +0,0 @@
-cl midiio.cpp dro2midi.cpp /link /OUT:dro2midi.exe
diff --git a/dro2midi/dro2midi.cpp b/dro2midi/dro2midi.cpp
deleted file mode 100644
index aa44e3d..0000000
--- a/dro2midi/dro2midi.cpp
+++ /dev/null
@@ -1,1328 +0,0 @@
-//
-// DRO2MIDI - Convert DOSBox raw OPL captures (.dro), Rdos (.raw) and id
-// Software (.imf, .wlf) into MIDI files (.mid)
-//
-// Created by malvineous@shikadi.net in June 2007. See README for license.
-// Based on imf2midi v1.0 written by Guenter Nagler in 1996 (gnagler@ihm.tu-graz.ac.at)
-//
-// v1.0 / 2007-06-16 / malvineous@shikadi.net: Original release
-// - imf2midi with .imf reader hacked to read .dro files instead
-//
-// v1.1 / 2007-07-28 / malvineous@shikadi.net: More file formats
-// - Added .imf and .raw support.
-// - Replaced Guenter's OPL -> MIDI frequency conversion algorithm (from a
-// lookup table into a much more accurate formula), consequently was able
-// to simplify pitchbend code (now conversions with pitchbends enabled
-// sound quite good!)
-//
-// v1.2 / 2007-07-28 / malvineous@shikadi.net: Bugfix release
-// - Fixed some file length calculations causing some files to be converted
-// without any notes.
-// - Added portamento-to-note for large (>2 semitone) pitchbends, but it
-// doesn't seem to work when using Timidity.
-//
-// v1.3 / 2007-09-02 / malvineous@shikadi.net: New features
-// - Fixed "tom tom" incorrectly called "bass drum" in output messages.
-// - Fixed multi-note pitchbends by removing portamento-to-note and
-// adjusting standard pitchbend range instead, thanks to a suggestion
-// by Xky (xkyrauh2001@hotmail.com)
-// - Implemented a better method for reading Adlib register -> MIDI patch
-// mapping information (all stored in inst.txt now instead of having a
-// seperate file for each instrument.) Also improved method for mapping
-// instruments to percussion on MIDI channel 10.
-// - Fixed OPL rhythm instrument conversion issue (a MIDI noteon was being
-// generated too often - if the OPL instrument is on and we receive
-// another keyon, it *shouldn't* generate a fresh MIDI keyon.)
-// - Fixed IMF type-1 conversion issue where unsigned numbers were being
-// read as signed, and the conversion was cutting off half way through.
-//
-// v1.4 / 2009-03-28 / malvineous@shikadi.net
-// - Some code cleanup, fixed all the warnings in midiio.cpp.
-// - Fixed a bunch of char/unsigned char issues, hopefully Win32
-// conversions will now be as reliable as under Linux.
-// - Added line numbers to instrument names and mapping file error messages.
-// - Added new instrument mapping entries for rhythm mode instruments
-// (which previously were hard coded.)
-// - Added transpose and mute options to instrument mapping file.
-// - Added -c option to change OPL constant during conversion, thanks to a
-// suggestion from Wraithverge (liam82067@yahoo.com)
-// - Added -v option to disable note volume (helps with Stunts which
-// otherwise comes out with no notes because they're all silent.)
-// - Corrected OPL volume -> MIDI note velocity algorithm, and added hard
-// limit to prevent notes from having a zero velocity (which stops them
-// from being converted, like with Stunts.)
-// - New instrument mappings (to be copied into insts.txt) are printed to
-// stderr, so "dro2midi 2>> insts.txt" will conveniently append them all
-// to the mapping file. Thanks to Wraithverge for the idea.
-// - Replaced getop() and getchannel() lookup functions with GET_OP() and
-// GET_CHANNEL() macro algorithms to calculate the values as required.
-// - Included 128 standard GM instrument mappings extracted from Creative
-// Labs' MIDI player (see gen_test_midi.cpp.)
-//
-// v1.5 / 2010-03-28 / Wraithverge (liam82067 at yahoo dot com): Changes
-// - Added code for several MIDI Controller Events for output MID-files,
-// and they are the following:
-// Reset Controllers [121] -- At the head of the Event list.
-// Balance (or Pan) [10] -- At the head of the Event list.
-// Expression [11] -- At the head of the Event list.
-// All Notes Off [123] -- At the foot of the Event list.
-// Hopefully, other musicians will find these to be needed, as well.
-// - 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.6"
-#define MAPPING_FILE "inst.txt"
-
-#define PATCH_NAME_FILE "patch.txt"
-#define PERC_NAME_FILE "drum.txt"
-#define NUM_MIDI_PATCHES 128 // 128 MIDI instruments
-#define NUM_MIDI_PERC 128 // 46 MIDI percussive notes (channel 10), but 128 possible notes
-#define INSTR_NAMELEN 32 // Maximum length of an instrument name
-
-//#define PITCHBEND_RANGE 12.0 // 12 == pitchbends can go up to a full octave
-#define PITCHBEND_RANGE 24.0 // 24 == pitchbends can go up two full octaves
-#define PITCHBEND_ONESEMITONE (8192.0 / PITCHBEND_RANGE)
-const double pitchbend_center = 8192.0;
-
-#include "midiio.hpp"
-#include
-#include
-#include
-#include
-#include
-#include
-
-#define WRITE_BINARY "wb"
-#define READ_TEXT "r"
-
-#ifdef _MSC_VER
-// Keep MS VC++ happy
-#define strncasecmp _strnicmp
-#define strcasecmp _stricmp
-#define snprintf _snprintf
-#define log2(x) (log(x) / log(2.))
-inline double round( double d )
-{
-return floor( d + 0.5 );
-}
-#endif
-
-bool bRhythm = true; // convert rhythm mode instruments (-r)
-bool bUsePitchBends = true; // use pitch bends to better match MIDI note frequency with the OPL frequency (-p)
-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 {
- NormalInstrument,
- BassDrum,
- SnareDrum,
- TomTom,
- TopCymbal,
- HiHat
-};
-
-// MIDI channels to use for these instruments when they are mapped as normal
-// notes. Probably best not to use 1-10 as these are used by the rest of the
-// OPL mapping code. Note these are zero-based, so the GM drum channel is
-// channel 9 in this context.
-#define CHAN_BASSDRUM 10 // Bass drum sits on OPL channel 7
-#define CHAN_SNAREDRUM 11 // OPL channel 8 carrier
-#define CHAN_TOMTOM 12 // OPL channel 9 modulator
-#define CHAN_TOPCYMBAL 13 // OPL channel 9 carrier
-#define CHAN_HIHAT 14 // OPL channel 8 modulator
-
-char cPatchName[NUM_MIDI_PATCHES][INSTR_NAMELEN];
-char cPercName[NUM_MIDI_PERC][INSTR_NAMELEN];
-
-char* input = 0;
-char* output = 0;
-
-FILE* f = 0; // input file
-MidiWrite* write = 0;
-
-//int resolution = 384; // 560Hz IMF
-int resolution = 500; // 1000Hz DRO
-int program = 0;
-float tempo = 120.0;
-
-// Arrays of [9] refer to OPL channels, arrays of [16] also refer to OPL
-// channels but use fake channels (11-15) for rhythm mode instruments (but
-// only for elements like keyon and instrument mapping that can happen
-// independently of the OPL channel in use. Things like OPL channel pitch
-// which affect both rhythm mode instruments using that channel are not
-// stored separately, i.e. they're in the [9] array.)
-int mapchannel[16];
-int curfreq[9];
-bool keyAlreadyOn[16];
-int lastkey[16]; // last MIDI key pressed on this channel
-int pitchbent[16];
-int transpose[16]; // used for instruments with mapped transpose values
-int drumnote[16]; // note to play on MIDI channel 10 if Adlib channel has a
- // percussive instrument assigned to it
-int lastprog[16]; // last program/patch set on the MIDI channel
-bool mute[16]; // true if the instrument on this channel is currently muted
-
-// Statistics
-int iNotesActive = 0;
-int iPitchbendCount = 0;
-int iTotalNotes = 0;
-
-typedef struct
-{
- /*unsigned char reg20[2];
- unsigned char reg40[2];
- unsigned char reg60[2];
- unsigned char reg80[2];
- unsigned char regC0;
- unsigned char regE0[2];*/
-
- unsigned int reg20[2];
- unsigned int reg40[2];
- unsigned int reg60[2];
- unsigned int reg80[2];
- unsigned int regC0;
- unsigned int regE0[2];
-
- // Is this a normal instrument or a mapping for a rhythm instrument?
- RHYTHM_INSTRUMENT eRhythmInstrument;
-
- int prog; // MIDI patch (or -1 if a drum)
- int isdrum;
- int note; // note to play if drum
- bool muted; // true if this instrument is muted
- char name[128];
-
- signed int iTranspose; // number of semitones to transpose this instrument
-
- // The instrument can be redirected to another. This is used when a new
- // instrument is encountered - its info is printed to the screen then it's
- // recorded (pointing to its best match) to prevent it appearing as a new
- // instrument hundreds of times and cluttering the output.
- int redirect; // if >= 0, use ::instr[redirect] instead of this one
-
- // When using this struct to cache current channel parms we need to
- // remember the octave, so that when registers 0xA0-0xA8 are changed
- // (to change note frequency) we can still pass the octave to the note
- // conversion function (as the octave is otherwise only available when
- // setting registers 0xB0-0xB8.)
- int iOctave;
-} INSTRUMENT;
-
-INSTRUMENT reg[9]; // current registers of channel
-
-#define MAXINSTR 2048
-int instrcnt = 0;
-INSTRUMENT instr[MAXINSTR];
-
-int iFormat = 0; // input format
-#define FORMAT_IMF 1
-#define FORMAT_DRO 2
-#define FORMAT_RAW 3
-int iSpeed = 0; // clock speed (in Hz)
-int iInitialSpeed = 0; // first iSpeed value written to MIDI header
-
-double dbConversionVal;
-// Convert the given OPL F-num and octave values into a fractional MIDI note
-// number (for the use of pitchbends.)
-double freq2key(int freq, int octave)
-{
- int iFNum = freq;
- int iBlock = octave;
- double dbOriginalFreq = dbConversionVal * (double)iFNum * pow(2, (double)(iBlock - 20));
- return 69.0 + 12.0 * log2(dbOriginalFreq / 440.0);
-}
-
-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, bsa in 2013\n"
- "http://www.shikadi.net/utils/\n"
- "\n"
- );
- return;
-}
-
-void usage()
-{
- version();
- fprintf(stderr,
- "Usage: dro2midi [-p [-a]] [-r] [-i] [-c alt|] [-v] input.dro output.mid\n"
- "\n"
- "Where:\n"
- " -p Disable use of MIDI pitch bends\n"
- " -a If pitchbends are disabled, approximate by playing the nearest note\n"
- " -r Don't convert OPL rhythm-mode instruments\n"
- " -i Only use instruments that match perfectly (default is 'close enough is\n"
- " good enough.') Useful when guessing new patches. Instruments that can't\n"
- " be matched use the first entry in " MAPPING_FILE " (piano by default)\n"
- " -c Change the value used to convert OPL notes into MIDI notes from the\n"
- " default 49716. Any non-zero value can be specified. The special word\n"
- " \"alt\" means 50000, the other commonly used value. It is unlikely any\n"
- " other values will need to be used, unless you get an excessive amount\n"
- " 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"
- " .dro DOSBox RAW OPL capture\n"
- " .imf id Software Music Format (type-0 and type-1 at 560Hz)\n"
- " .wlf id Software Music Format (type-0 and type-1 at 700Hz)\n"
- "\n"
- "Instrument definitions are read in from " MAPPING_FILE ". Instrument names\n"
- "are read in from " PATCH_NAME_FILE " and " PERC_NAME_FILE ". See the README for more details.\n"
- );
- exit(1);
-}
-
-/* Channel 1 2 3 4 5 6 7 8 9
- * Operator 1 00 01 02 08 09 0A 10 11 12 } cells
- * Operator 2 03 04 05 0B 0C 0D 13 14 15 }
- */
-// Converts a cell into a channel
-#define GET_CHANNEL(i) ((((i) / 8) * 3) + (((i) % 8) % 3))
-
-// Converts a cell into 0 (for operator 1) or 1 (for operator 2)
-#define GET_OP(i) (((i) % 8) / 3)
-
-// Helper functions to read data from files in a non-optimised but platform
-// independent (little/big endian) way.
-inline unsigned char readByte(FILE *f)
-{
- unsigned char c;
- fread(&c, 1, 1, f);
- return c;
-}
-inline unsigned short readUINT16LE(FILE *f)
-{
- unsigned char c[2];
- fread(&c, 1, 2, f);
- return c[0] | (c[1] << 8L);
-}
-inline unsigned long readUINT32LE(FILE *f)
-{
- unsigned char c[4];
- fread(&c, 1, 4, f);
- return c[0] | (c[1] << 8L) | (c[2] << 16L) | (c[3] << 24L);
-}
-
-bool loadInstruments(void)
-{
- for (int i = 0; i < NUM_MIDI_PATCHES; i++) sprintf(cPatchName[i], "Patch #%d", i+1);
- for (int i = 0; i < NUM_MIDI_PERC; i++) sprintf(cPercName[i], "Note #%d", i);
-
- char line[256];
-
- FILE* p = fopen(PATCH_NAME_FILE, "r");
- if (!p) {
- fprintf(stderr, "Warning: Unable to open file listing patch names ("
- PATCH_NAME_FILE ")\nInstrument names will not be available.\n");
- } else {
- while (fgets(line, sizeof(line)-1, p)) {
- int iValue, iLen;
- char *p = strpbrk(line, "\n\r");
- if (p) *p = '\0'; // terminate the string at the newline
- if (sscanf(line, "%d=%n", &iValue, &iLen) == 1) {
- assert(iValue <= NUM_MIDI_PATCHES);
- snprintf(cPatchName[iValue-1], INSTR_NAMELEN, "%s [%d]", &line[iLen], iValue);
- } else if ((line[0] != '#') && (line[0] != '\n')) {
- fprintf(stderr, "Invalid line in " PATCH_NAME_FILE ": %s\n", line);
- }
- }
- fclose(p);
- }
-
- p = fopen(PERC_NAME_FILE, "r");
- if (!p) {
- fprintf(stderr, "Warning: Unable to open file listing percussion note "
- "names (" PERC_NAME_FILE ")\nPercussion names will not be available.\n");
- } else {
- while (fgets(line, sizeof(line)-1, p)) {
- int iValue, iLen;
- char *p = strpbrk(line, "\n\r");
- if (p) *p = '\0'; // terminate the string at the newline
- if (sscanf(line, "%d=%n", &iValue, &iLen) == 1) {
- assert(iValue <= NUM_MIDI_PERC);
- snprintf(cPercName[iValue], INSTR_NAMELEN, "%s [%d]", &line[iLen], iValue);
- } else if ((line[0] != '#') && (line[0] != '\n')) {
- fprintf(stderr, "Invalid line in " PERC_NAME_FILE ": %s\n", line);
- }
- }
- fclose(p);
- }
-
- FILE* f = fopen(MAPPING_FILE, "r");
- if (!f) {
- fprintf(stderr, "Warning: Unable to open instrument mapping file "
- MAPPING_FILE ", defaulting to a Grand Piano\nfor all instruments.\n");
- return true;
- }
- INSTRUMENT in;
- memset(&in, 0, sizeof(in));
- in.redirect = -1; // none of these should redirect (but later automatic
- // instruments will redirect to these ones)
-
- // Loop until we run out of lines in the data file or we hit the maximum
- // number of instruments
- char value[256];
- int iLineNum = 0;
- for (::instrcnt = 0; fgets(line, sizeof(line)-1, f) && (instrcnt < MAXINSTR);) {
- iLineNum++;
-
- // Ignore blank lines and comments
- if ((line[0] == '#') || (line[0] == '\r') || (line[0] == '\n')) continue;
-
- // Figure out what type of rhythm mode instrument (if any) the first two
- // chars are referring to.
- char cInstType[3];
- int iNumFields = sscanf(line, "%2s ", cInstType);
- if ((cInstType[0] == 'N') && (cInstType[1] == 'O')) in.eRhythmInstrument = NormalInstrument;
- else if ((cInstType[0] == 'B') && (cInstType[1] == 'D')) in.eRhythmInstrument = BassDrum;
- else if ((cInstType[0] == 'S') && (cInstType[1] == 'D')) in.eRhythmInstrument = SnareDrum;
- else if ((cInstType[0] == 'T') && (cInstType[1] == 'T')) in.eRhythmInstrument = TomTom;
- else if ((cInstType[0] == 'T') && (cInstType[1] == 'C')) in.eRhythmInstrument = TopCymbal;
- else if ((cInstType[0] == 'H') && (cInstType[1] == 'H')) in.eRhythmInstrument = HiHat;
- else {
- fprintf(stderr, "Invalid instrument type \"%s\" on line %d:\n\n %s\n",
- cInstType, iLineNum, line);
- return false;
- }
-
- switch (in.eRhythmInstrument) {
- case NormalInstrument:
- case BassDrum:
- // Normal instrument or rhythm Bass Drum, read both
- // operators + connection byte
- iNumFields = sscanf(&line[3], "%02X-%02X/%02X-%02X/%02X-%02X/%02X-%02X"
- "/%02X/%02X-%02X: %255c\n",
- &in.reg20[0], &in.reg20[1],
- &in.reg40[0], &in.reg40[1],
- &in.reg60[0], &in.reg60[1],
- &in.reg80[0], &in.reg80[1],
- &in.regC0,
- &in.regE0[0], &in.regE0[1], value);
- if (iNumFields != 12) {
- fprintf(stderr, "Unable to parse line %d: (expected 12 instrument "
- "fields, got %d)\n\n%s\n", iLineNum, iNumFields, line);
- return false;
- }
- break;
-
- case TomTom:
- case HiHat:
- // This instrument is one operator only, but it does use the connection
- // byte (probably)
- iNumFields = sscanf(&line[3], "%02X/%02X/%02X/%02X/%02X/%02X: %255c\n",
- &in.reg20[0],
- &in.reg40[0],
- &in.reg60[0],
- &in.reg80[0],
- &in.regC0,
- &in.regE0[0], value);
- if (iNumFields != 7) {
- fprintf(stderr, "Unable to parse line %d: (expected 7 instrument "
- "fields, got %d)\n\n%s\n", iLineNum, iNumFields, line);
- return false;
- }
- break;
-
- case SnareDrum:
- case TopCymbal:
- // This instrument does not uses the connection byte, so read in one byte
- // less. Also read the values into the other operator.
- iNumFields = sscanf(&line[3], "%02X/%02X/%02X/%02X/%02X: %255c\n",
- &in.reg20[1],
- &in.reg40[1],
- &in.reg60[1],
- &in.reg80[1],
- &in.regE0[1], value);
- if (iNumFields != 6) {
- fprintf(stderr, "Unable to parse line %d: (expected 6 instrument "
- "fields, got %d)\n\n%s\n", iLineNum, iNumFields, line);
- return false;
- }
- break;
- }
-
- // Default options
- in.isdrum = 0;
- in.prog = 1;
- in.iTranspose = 0;
- in.muted = false;
-
- // If we got this far it's a valid instrument
- int iValue;
- char nextopt[256];
- // We need to manually terminate the %255c in the sscanf() above, so do
- // this by terminating it at the end-of-line char. Hopefully reading past
- // the end of the input string (which causes this) won't crash things...
- for (int i = 0; i < 256; i++) {
- if (value[i] == '\n') value[i] = 0;
- else if (value[i] == '\0') break;
- }
- value[255] = 0;
-
- const char *cList = value;
- int iLen;
- while (sscanf(cList, "%s%n", nextopt, &iLen) >= 1) {
- cList += iLen;
- if (nextopt[0] == '#') break; // reached an end of line comment
- if (nextopt[0] == ' ') continue; // skip double spaces
- if (sscanf(nextopt, "patch=%d", &iValue) == 1) {
- // MIDI patch
- in.isdrum = 0;
- in.prog = iValue - 1;
- if ((in.prog < 0) || (in.prog > 127)) {
- fprintf(stderr, "ERROR: Instrument #%d (line %d) was set to "
- "patch=%d, but this value must be between 1 and 128 inclusive.\n",
- instrcnt, iLineNum, in.prog + 1);
- return false;
- }
- } else if (sscanf(nextopt, "drum=%d", &iValue) == 1) {
- // MIDI drum
- in.isdrum = 1;
- in.prog = -1;
- in.note = iValue;
- if ((in.note < 0) || (in.note > 127)) {
- fprintf(stderr, "ERROR: Drum instrument #%d (line %d) was set to "
- "drum=%d, but this value must be between 1 and 128 inclusive.\n",
- instrcnt, iLineNum, in.note);
- return false;
- }
- } else if (sscanf(nextopt, "transpose=%d", &iValue) == 1) {
- // MIDI drum
- in.iTranspose = iValue;
- } else if (strcmp(nextopt, "mute") == 0) {
- // Mute this instrument
- in.muted = true;
- } else {
- fprintf(stderr, "Unknown instrument option on line %d: %s\n",
- iLineNum, nextopt);
- return false;
- }
- }
-
- char cInstTypeText[256];
- switch (in.eRhythmInstrument) {
- case NormalInstrument: cInstTypeText[0] = '\0'; break;
- case BassDrum: strcpy(cInstTypeText, "(OPL BD) "); break;
- case TomTom: strcpy(cInstTypeText, "(OPL TT) "); break;
- case HiHat: strcpy(cInstTypeText, "(OPL HH) "); break;
- case SnareDrum: strcpy(cInstTypeText, "(OPL SD) "); break;
- case TopCymbal: strcpy(cInstTypeText, "(OPL TC) "); break;
- }
- sprintf(in.name, "Inst#%03d %s@ line %3d%s: %s", instrcnt,
- cInstTypeText,
- iLineNum,
- (in.isdrum) ? " (perc)" : "",
- (in.isdrum) ? cPercName[in.note] : cPatchName[in.prog]
- );
- if (in.iTranspose) {
- char cTranspose[256];
- sprintf(cTranspose, " @ %+d semitones", in.iTranspose);
- strcat(in.name, cTranspose);
- }
- if (in.muted) strcat(in.name, " {muted}");
- //printf("%s\n", in.name);
-
- // Add instrument
- ::instr[instrcnt++] = in;
- }
- fclose(f);
- return true;
-}
-
-long difference(int a, int b, int importance = 1)
-{
- long diff = a - b;
- if (diff < 0) diff = -diff;
- return diff * importance;
-}
-
-long compareinstr(INSTRUMENT& a, INSTRUMENT& b, RHYTHM_INSTRUMENT ri)
-{
- // Note that we're not using b.eRhythmInstrument below as "b" refers to the
- // OPL channel, and this never has an instrument type set. The instrument
- // type we want is passed in as "ri", so we compare against that instead.
- //fprintf(stderr, "%d ", ri);
- switch (ri) {
- case NormalInstrument:
- case BassDrum:
- // Compare the full register set (both operators plus the connection
- // byte) for normal instruments (which occupy a whole channel) and the
- // one rhythm mode instrument which also occupies a whole channel.
- return
- difference(a.eRhythmInstrument, ri, 4) +
- difference(a.reg20[0], b.reg20[0], 2) +
- difference(a.reg20[1], b.reg20[1], 2) +
- difference(a.reg40[0], b.reg40[0], 1) +
- difference(a.reg40[1], b.reg40[1], 1) +
- difference(a.reg60[0], b.reg60[0], 2) +
- difference(a.reg60[1], b.reg60[1], 2) +
- difference(a.reg80[0], b.reg80[0], 2) +
- difference(a.reg80[1], b.reg80[1], 2) +
- difference(a.regC0, b.regC0, 3) +
- difference(a.regE0[0], b.regE0[0], 1) +
- difference(a.regE0[1], b.regE0[1], 1);
- case TomTom:
- case HiHat:
- // These two rhythm instruments only use one operator's settings, the
- // settings of the other operator (should) be ignored. There is also
- // only one Connection byte, but there is no documentation to say whether
- // this applies to these modulator-only rhythm instruments or to the
- // other carrier-only ones (below.) I'm guessing and putting it here.
- return
- difference(a.eRhythmInstrument, ri, 4) +
- difference(a.reg20[0], b.reg20[0], 2) +
- difference(a.reg40[0], b.reg40[0], 1) +
- difference(a.reg60[0], b.reg60[0], 2) +
- difference(a.reg80[0], b.reg80[0], 2) +
- difference(a.regC0, b.regC0, 3) +
- difference(a.regE0[0], b.regE0[0], 1);
- case SnareDrum:
- case TopCymbal:
- // These instruments only use one operator - but the other one compared
- // to the previous set above. They also don't use the single Connection
- // byte (I think.)
- return
- difference(a.eRhythmInstrument, ri, 4) +
- difference(a.reg20[1], b.reg20[1], 2) +
- difference(a.reg40[1], b.reg40[1], 1) +
- difference(a.reg60[1], b.reg60[1], 2) +
- difference(a.reg80[1], b.reg80[1], 2) +
- difference(a.regE0[1], b.regE0[1], 1);
- }
-}
-
-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)));
-
- RHYTHM_INSTRUMENT ri;
- int chanOPL;
- switch (chanMIDI) {
- case CHAN_BASSDRUM: ri = BassDrum; chanOPL = 6; break;
- case CHAN_SNAREDRUM: ri = SnareDrum; chanOPL = 7; break;
- case CHAN_TOMTOM: ri = TomTom; chanOPL = 8; break;
- case CHAN_TOPCYMBAL: ri = TopCymbal; chanOPL = 8; break;
- case CHAN_HIHAT: ri = HiHat; chanOPL = 7; break;
- default: ri = NormalInstrument; chanOPL = chanMIDI; break;
- }
-
- int besti = -1;
- long bestdiff = -1;
- for (int i = 0; i < instrcnt; i++) {
- long diff = compareinstr(instr[i], reg[chanOPL], ri);
- if (besti < 0 || diff < bestdiff) {
- bestdiff = diff;
- besti = i;
- if (bestdiff == 0) break;
- }
- }
-
- if (besti >= 0) { // could be -1 if no instruments are loaded
- while (instr[besti].redirect >= 0) { // Could have multiple redirects
- // This instrument was an automatically generated one to avoid printing
- // the instrument definition multiple times, so instead of using the auto
- // one, use the one it originally matched against.
- besti = instr[besti].redirect;
- }
- }
-
- if (bestdiff != 0) {
- if (::bPerfectMatchesOnly) {
- // User doesn't want "close enough is good enough" instrument guessing
- besti = 0; // use first instrument
- }
- // Couldn't find an exact match, print the details
- switch (ri) {
- case NormalInstrument:
- case BassDrum:
- // Normal instrument or rhythm Bass Drum use both
- // operators + connection byte
- printf("** New instrument in use on channel %d\n** Copy this into "
- MAPPING_FILE " to assign it a MIDI patch:\n", chanOPL);
- fprintf(stderr, "%s %02X-%02X/%02X-%02X/%02X-%02X/%02X-%02X/%02X/"
- "%02X-%02X: patch=?\n",
- ((ri == BassDrum) ? "BD" : "NO"),
- reg[chanOPL].reg20[0], reg[chanOPL].reg20[1],
- reg[chanOPL].reg40[0], reg[chanOPL].reg40[1],
- reg[chanOPL].reg60[0], reg[chanOPL].reg60[1],
- reg[chanOPL].reg80[0], reg[chanOPL].reg80[1],
- reg[chanOPL].regC0,
- reg[chanOPL].regE0[0], reg[chanOPL].regE0[1]
- );
- if (::bWriteSbiInstruments) {
- writesbi(output, instrcnt, chanOPL);
- }
- break;
- case TomTom:
- case HiHat:
- // This instrument is one operator only, but it does use the connection
- // byte (probably)
- printf("** New rhythm instrument in use on OPL channel %d modulator\n"
- "** Copy this into " MAPPING_FILE " to assign it a MIDI patch:\n",
- chanOPL);
- fprintf(stderr, "%s %02X/%02X/%02X/%02X/%02X/%02X: "
- "patch=?\n",
- ((ri == TomTom) ? "TT" : "HH"),
- reg[chanOPL].reg20[0],
- reg[chanOPL].reg40[0],
- reg[chanOPL].reg60[0],
- reg[chanOPL].reg80[0],
- reg[chanOPL].regC0,
- reg[chanOPL].regE0[0]
- );
- break;
- case SnareDrum:
- case TopCymbal:
- // This instrument does not uses the connection byte, so read in one
- // byte less. Also read the values into the other operator.
- // This instrument is one operator only, but it does use the connection
- // byte (probably)
- printf("** New rhythm instrument in use on OPL channel %d carrier\n"
- "** Copy this into " MAPPING_FILE " to assign it a MIDI patch:\n",
- chanOPL);
- fprintf(stderr, "%s %02X/%02X/%02X/%02X/%02X: "
- "patch=?\n",
- ((ri == SnareDrum) ? "SD" : "TC"),
- reg[chanOPL].reg20[1],
- reg[chanOPL].reg40[1],
- reg[chanOPL].reg60[1],
- reg[chanOPL].reg80[1],
- reg[chanOPL].regE0[1]
- );
- break;
- }
-
- printf(">> Using similar match: %s\n", instr[besti].name);
- // Save this unknown instrument as a known one, so the same registers don't get printed again
-// 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
- // Maybe ^ isn't necessary if we're redirecting?
- instr[instrcnt] = reg[chanOPL];
- instr[instrcnt].eRhythmInstrument = ri;
- if (besti >= 0) {
- instr[instrcnt].redirect = besti; // Next time this instrument is matched, use the original one instead
- } else {
- instr[instrcnt].redirect = -1; // Will only happen when no instruments are loaded
- }
- instrcnt++;
- }
- return besti;
-}
-
-// Function for processing OPL note on and off events, and generating MIDI
-// events in response. This function is also called when the pitch changes
-// while a note is currently being played, causing it to generate MIDI
-// pitchbends (if enabled) instead.
-// Normally chanOPL and chanMIDI will be the same, except for rhythm mode
-// instruments, which uses chanOPL for pitch and instrument patches, but
-// chanMIDI for instrument mapping and note on/off events (since there will be
-// two instrument maps and notes for a single OPL channel.)
-void doNoteOnOff(bool bKeyOn, int chanOPL, int chanMIDI)
-{
- double keyFrac = freq2key(curfreq[chanOPL], reg[chanOPL].iOctave);
- int key = (int)round(keyFrac);
- if ((key > 0) && (bKeyOn)) {
- // This is set to true to forcibly stop a MIDI keyon being generated for
- // this note. This is done when a pitchbend is deemed as having done the
- // job properly.
- bool bKeyonAgain = true;
-
- if (keyAlreadyOn[chanMIDI]) {
- // There's already a note playing on this channel, just worry about the pitch of that
-
- if (mapchannel[chanMIDI] != gm_drumchannel) {
- // We're using a normal instrument here
-
- if (::bUsePitchBends) {
- // It's the same note, but the pitch is off just slightly, use a pitchbend
- //double dbDiff = fabs(keyFrac - key); // should be between -0.9999 and 0.9999
- double dbDiff = keyFrac - (double)(lastkey[chanMIDI] - transpose[chanMIDI]); // hopefully between -PITCHBEND_RANGE and PITCHBEND_RANGE
-
- if (dbDiff > PITCHBEND_RANGE) {
- fprintf(stderr, "Warning: This song wanted to pitchbend by %.2f notes, but the maximum is %.1f\n", dbDiff, PITCHBEND_RANGE);
-
- // Turn this note off
- write->noteoff(mapchannel[chanMIDI], lastkey[chanMIDI]);
- ::iNotesActive--;
- lastkey[chanMIDI] = -1;
- keyAlreadyOn[chanMIDI] = false;
- // leave bKeyonAgain as true, so that a noteon will be played instead
- } else {
- int iNewBend = (int)(pitchbend_center + (PITCHBEND_ONESEMITONE * dbDiff));
- if (iNewBend != pitchbent[chanMIDI]) {
- //printf("pitchbend to %d/%.2lf (center + %d) (%.2lf "
- // "semitones)\n", iNewBend, (double)pitchbend_center*2,
- // (int)(iNewBend - pitchbend_center), (double)dbDiff);
- write->pitchbend(mapchannel[chanMIDI], iNewBend);
-// ::iPitchbendCount++;
- pitchbent[chanMIDI] = iNewBend;
- }
- // This pitchbend has done the job, don't play a noteon
- bKeyonAgain = false;
- }
- } else {
- // We're not using pitchbends, so just switch off the note if it's different (the next one will play below)
- if ((::bApproximatePitchbends) && (key != (lastkey[chanMIDI] - transpose[chanMIDI]))) {
- write->noteoff(mapchannel[chanMIDI], lastkey[chanMIDI]);
- ::iNotesActive--;
- lastkey[chanMIDI] = -1;
- keyAlreadyOn[chanMIDI] = false;
- //bKeyonAgain = true;
- } else {
- // Same note, different pitch, just pretend like it's not there
- bKeyonAgain = false;
- }
- }
- } else {
- // This has mapped to a percussive MIDI note, so no pitchbends (or
- // we'll bend all the percussive notes on the MIDI percussion channel.)
- // But we don't want to play the note again, 'cos it's already on, so
- // just ignore the keyon event.
- // We'll also end up here if the instrument parameters are changed
- // while the instrument is sounding, e.g. to change the characteristics
- // of a hihat without sounding a new note. This won't be converted.
- bKeyonAgain = false;
- }
- } // else this is a percussive instrument
-
- //} else {
- //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)
- if (bKeyonAgain) { // If *now* there's no note playing... (or we're using pitchbends, i.e. a portamento has been set up)
- // See if we need to update anything
-
- // See if the instrument needs to change
- int i = findinstr(chanMIDI);
- if (
- (i >= 0) && (
- (instr[i].prog != lastprog[chanMIDI]) ||
- (
- (instr[i].isdrum) &&
- (drumnote[chanMIDI] != instr[i].note)
- ) || (
- // Same instrument mapping, but different mute setting?
- (instr[i].muted != mute[chanMIDI])
- )
- )
- ) {
- printf("// Ch%02d <- %s\n", chanMIDI, instr[i].name);
- if (!instr[i].isdrum) {
- // Normal instrument (not MIDI percussion)
- assert(instr[i].prog >= 0);
-
- if (mapchannel[chanMIDI] == gm_drumchannel) {
- // This was playing drums, now we're back to normal notes
-
- // make sure this sets things back to what they were in the init
- // section in main()
- mapchannel[chanMIDI] = chanMIDI;
- 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)
- }
-
- transpose[chanMIDI] = instr[i].iTranspose;
- write->program(mapchannel[chanMIDI], lastprog[chanMIDI] = instr[i].prog);
- } else {
- // This new instrument is a drum
- assert(instr[i].prog == -1);
-
- /*if (instr[i].muted) {
- // This instrument is muted, which means whichever channel we
- // assign it to will become muted. We can't therefore assign it to
- // the drum channel as we normally would, otherwise all the MIDI
- // percussion will become muted. So we assign it to its normal
- // channel instead, like we would with a non-percussion instrument.
- mapchannel[chanMIDI] = chanMIDI;
- } else {
- mapchannel[chanMIDI] = gm_drumchannel;
- }*/
- mapchannel[chanMIDI] = gm_drumchannel;
- drumnote[chanMIDI] = instr[i].note;
- lastprog[chanMIDI] = instr[i].prog;
- // drums don't use transpose values
- }
- mute[chanMIDI] = instr[i].muted;
- }
-
- // Play the note
- //if ((::bUsePitchBends) && (!keyAlreadyOn[channel])) {
- if ((::bUsePitchBends) && (mapchannel[chanMIDI] != gm_drumchannel)) { // If pitchbends are enabled and this isn't a percussion instrument
- double dbDiff = keyFrac - key; // should be between -0.9999 and 0.9999
- assert(dbDiff < PITCHBEND_RANGE); // not really necessary...
-
- int iNewBend = (int)(pitchbend_center + (PITCHBEND_ONESEMITONE * dbDiff));
- if (iNewBend != pitchbent[chanMIDI]) {
- //printf("new note at pitchbend %d\n", iNewBend);
- write->pitchbend(mapchannel[chanMIDI], iNewBend); // pitchbends are between 0x0000L and 0x2000L
-// ::iPitchbendCount++;
- pitchbent[chanMIDI] = iNewBend;
- }
- }
-
- int level;
- if (!mute[chanMIDI]) {
- if (::bEnableVolume) {
- level = reg[chanOPL].reg40[1] & 0x3f;
- if (level > 0x30) level = 0x30; // don't allow fully silent notes
- } else level = 0; // 0 == loudest
- } else {
- level = 0x3f; // silent
- }
-
- if (mapchannel[chanMIDI] != gm_drumchannel) {
- // Normal note
- lastkey[chanMIDI] = key + transpose[chanMIDI];
- } else {
- // Percussion
- //write->noteon(gm_drumchannel, drumnote[chanMIDI], (0x3f - level) << 1);
- lastkey[chanMIDI] = drumnote[chanMIDI];
- }
-
- write->noteon(mapchannel[chanMIDI], lastkey[chanMIDI], (0x3f - level) << 1);
- //printf("note on chan %d, mute is %s\n", chanMIDI, mute[chanMIDI] ? "true" : "false");
- ::iNotesActive++;
- ::iTotalNotes++;
-
- // If this note went on with a pitchbend active on the channel, count it
- if (pitchbent[chanMIDI] != pitchbend_center) ::iPitchbendCount++;
-
- keyAlreadyOn[chanMIDI] = true;
-
- } // if (not muted)
-
- } else {
- // There's no note currently playing on this channel, so if we've still got
- // one switch it off.
- if (lastkey[chanMIDI] != -1) {
- write->noteoff(mapchannel[chanMIDI], lastkey[chanMIDI]);
- ::iNotesActive--;
- lastkey[chanMIDI] = -1;
- keyAlreadyOn[chanMIDI] = false;
- }
- }
-
- return;
-}
-
-int main(int argc, char**argv)
-{
- int c;
-
- // Defaults
- ::dbConversionVal = 49716.0;
-
- argc--; argv++;
- while (argc > 0 && **argv == '-')
- {
- if (strncasecmp(*argv, "-r", 2) == 0) {
- ::bRhythm = false;
- printf("Rhythm-mode instruments disabled.\n");
- } else if (strncasecmp(*argv, "-p", 2) == 0) {
- ::bUsePitchBends = false;
- printf("Pitchbends disabled.\n");
- } else if (strncasecmp(*argv, "-a", 2) == 0) {
- ::bApproximatePitchbends = true;
- } else if (strncasecmp(*argv, "-i", 2) == 0) {
- ::bPerfectMatchesOnly = true;
- printf("Only using exact instrument matches - approximations disabled!\n");
- } 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) {
- fprintf(stderr, "-c requires a parameter\n");
- usage();
- }
- if (strncasecmp(*argv, "alt", 3) == 0) {
- ::dbConversionVal = 50000.0;
- } else {
- // Use the given value
- ::dbConversionVal = strtod(*argv, NULL);
- if (::dbConversionVal == 0) {
- fprintf(stderr, "-c requires a non-zero parameter\n");
- usage();
- }
- }
- } else if (strncasecmp(*argv, "--version", 9) == 0) {
- version();
- return 0;
- } else {
- fprintf(stderr, "invalid option %s\n", *argv);
- usage();
- }
- argc--; argv++;
- }
- if (argc < 2) usage();
-
- if ((::bUsePitchBends) && (::bApproximatePitchbends)) {
- fprintf(stderr, "ERROR: Pitchbends can only be approximated (-a) if "
- "proper MIDI pitchbends are disabled (-p)\n");
- return 1;
- }
-
- input = argv[0];
- output = argv[1];
- if (strcmp(input, output) == 0)
- {
- fprintf(stderr, "cannot convert to same file\n");
- return 1;
- }
-
- if (!loadInstruments()) return 1;
-
-
- f = fopen(input, READ_BINARY);
- if (!f)
- {
- perror(input);
- return 1;
- }
- unsigned long imflen = 0;
-
- unsigned char cSig[9];
- fseek(f, 0, SEEK_SET);
- //fgets(cSig, 9, f);
- fread(cSig, 1, 9, f);
- iSpeed = 0;
- if (strncmp((char *)cSig, "DBRAWOPL", 8) == 0) {
- ::iFormat = FORMAT_DRO;
- fseek(f, 8, SEEK_SET); // Seek to "version" fields.
- unsigned long version = readUINT32LE(f);
- if (version == 0x10000) {
- printf("Input file is in DOSBox DRO v1.0 format.\n");
- } else if (version == 0x2) {
- printf("Input file is in DOSBox DRO v2.0 format, which is not supported.\n");
- return 2;
- } else {
- printf("Input file is in DOSBox DRO format, but an unknown version!\n");
- return 3;
- }
- ::iInitialSpeed = 1000;
-
- fseek(f, 16, SEEK_SET); // seek to "length in bytes" field
- imflen = readUINT32LE(f);
- } else if (strncmp((char *)cSig, "RAWADATA", 8) == 0) {
- ::iFormat = FORMAT_RAW;
- printf("Input file is in Rdos RAW format.\n");
-
- // Read until EOF (0xFFFF is really the end but we'll check that during conversion)
- fseek(f, 0, SEEK_END);
- imflen = ftell(f);
-
- fseek(f, 8, SEEK_SET); // seek to "initial clock speed" field
- ::iInitialSpeed = 1000;
- int iClockSpeed = readUINT16LE(f);
- if ((iClockSpeed == 0) || (iClockSpeed == 0xFFFF)) {
- ::iSpeed = (int)18.2; // default to 18.2Hz...well, 18Hz thanks to rounding
- } else {
- ::iSpeed = (int)(1193180.0 / iClockSpeed);
- }
- } else {
- ::iFormat = FORMAT_IMF;
- if ((cSig[0] == 0) && (cSig[1] == 0)) {
- printf("Input file appears to be in IMF type-0 format.\n");
- fseek(f, 0, SEEK_END);
- imflen = ftell(f);
- fseek(f, 0, SEEK_SET);
- } else {
- printf("Input file appears to be in IMF type-1 format.\n");
- imflen = cSig[0] + (cSig[1] << 8);
- fseek(f, 2, SEEK_SET); // seek to start of actual OPL data
- }
- if (strcasecmp(&input[strlen(input)-3], "imf") == 0) {
- printf("File extension is .imf - using 560Hz speed (rename to .wlf if "
- "this is too slow)\n");
- ::iInitialSpeed = 560;
- } else if (strcasecmp(&input[strlen(input)-3], "wlf") == 0) {
- printf("File extension is .wlf - using 700Hz speed (rename to .imf if "
- "this is too fast)\n");
- ::iInitialSpeed = 700;
- } else {
- printf("Unknown file extension - must be .imf or .wlf\n");
- return 3;
- }
- }
- printf("Using conversion constant of %.1lf\n", ::dbConversionVal);
-
- write = new MidiWrite(output);
- if (!write) {
- fprintf(stderr, "out of memory\n");
- return 1;
- }
- if (!write->getf()) {
- perror(output);
- return 1;
- }
- if (iSpeed == 0) {
- iSpeed = iInitialSpeed;
- }
- resolution = iInitialSpeed / 2;
- write->head(/* version */ 0, /* track count updated later */0, resolution);
-
- write->track();
- write->tempo((long)(60000000.0 / tempo));
- write->tact(4,4,24,8);
-
- for (c = 0; c < 10; c++) {
- mapchannel[c] = c;
- write->resetctrlrs(mapchannel[c], 0); // Reset All Controllers (Ensures default settings upon every playback).
- write->volume(mapchannel[c], 127);
- write->balance(mapchannel[c], 64); // Usually called 'Pan'.
- write->expression(mapchannel[c], 127); // Similar to 'Volume', but this is primarily used for volume damping.
- }
-
- for (c = 0; c <= 8; c++) {
- lastprog[c] = -1;
- reg[c].iOctave = 0;
- }
-
- int delay = 0;
- int channel;
- int code, param;
-
- for (c = 0; c < 9; c++) {
- curfreq[c] = 0;
- mapchannel[c] = c; // This can get reset when playing a drum and then a normal instrument on a channel - see instrument-change code below
- keyAlreadyOn[c] = false;
- lastkey[c] = -1; // last MIDI key pressed on this channel
- pitchbent[c] = (int)pitchbend_center;
- transpose[c] = 0;
- drumnote[c] = 0; // probably not necessary...
- mute[c] = false;
-
- if (::bUsePitchBends) {
- write->control(mapchannel[c], 100, 0); // RPN LSB for "Pitch Bend Sensitivity"
- write->control(mapchannel[c], 101, 0); // RPN MSB for "Pitch Bend Sensitivity"
- write->control(mapchannel[c], 6, (int)PITCHBEND_RANGE); // Data for Pitch Bend Sensitivity (in semitones) - controller 38 can be used for cents in addition
- write->control(mapchannel[c], 100, 0x7F); // RPN LSB for "Finished"
- write->control(mapchannel[c], 101, 0x7F); // RPN MSB for "Finished"
- }
-// write->pitchbend(mapchannel[c], pitchbend_center);
- }
- // Rhythm-mode only channels
- for (c = 10; c < 15; c++) {
- keyAlreadyOn[c] = false;
- mapchannel[c] = c; // as above, this will be changed later but must be
- // eventually set back to this value here (or the instrument will jump
- // channels unexpectedly.)
- pitchbent[c] = (int)pitchbend_center;
- lastkey[c] = -1; // last MIDI key pressed on this channel
- transpose[c] = 0;
- drumnote[c] = 0; // probably not necessary...
- mute[c] = false;
- }
-
- int iMinLen; // Minimum length for valid notes to still be present
- switch (::iFormat) {
- case FORMAT_IMF: iMinLen = 4; break;
- case FORMAT_DRO: iMinLen = 2; break;
- case FORMAT_RAW: iMinLen = 2; break;
- }
-
- unsigned long iSize = imflen; // sometimes the counter wraps around, need this to stop it from happening
- while ((imflen >= (unsigned long)iMinLen) && (imflen <= iSize)) {
-
- // Get the next OPL register and value from the input file
-
- switch (::iFormat) {
- case FORMAT_IMF:
- // Write the last iteration's delay (since the delay needs to come *after* the note)
- write->time(delay);
-
- code = readByte(f);
- param = readByte(f);
- delay = readUINT16LE(f);
- imflen -= 4;
- break;
- case FORMAT_DRO:
- code = readByte(f);
- imflen--;
- switch (code) {
- case 0x00: // delay (byte)
- delay += 1 + readByte(f);
- imflen--;
- continue;
- case 0x01: // delay (int)
- delay += 1 + readUINT16LE(f);
- imflen -= 2;
- continue;
- case 0x02: // use first OPL chip
- case 0x03: // use second OPL chip
- fprintf(stderr, "Warning: This song uses multiple OPL chips - this isn't yet supported!\n");
- continue;
- case 0x04: // escape
- code = readByte(f);
- imflen--;
- break;
- }
- param = readByte(f);
- imflen--;
-
- // Write any delay (as this needs to come *before* the next note)
- write->time(delay);
- delay = 0;
- break;
- case FORMAT_RAW:
- param = readByte(f);
- code = readByte(f);
- imflen -= 2;
- switch (code) {
- case 0x00: // delay
- delay += param;
- continue;
- case 0x02: // control data
- switch (param) {
- case 0x00: {
- if (delay != 0) {
- // See below - we need to write out any delay at the old clock speed before we change it
- write->time((delay * iInitialSpeed / ::iSpeed));
- delay = 0;
- }
- int iClockSpeed = readUINT16LE(f);
- if ((iClockSpeed == 0) || (iClockSpeed == 0xFFFF)) {
- printf("Speed set to invalid value, ignoring speed change.\n");
- } else {
- ::iSpeed = (int)round(1193180.0 / iClockSpeed);
- printf("Speed changed to %dHz\n", iSpeed);
- }
- imflen -= 2;
- break;
- }
- case 0x01:
- case 0x02:
- printf("Switching OPL ports is not yet implemented!\n");
- break;
- }
- continue;
- case 0xFF:
- if (param == 0xFF) {
- // End of song
- imflen = 0;
- continue;
- }
- break;
- }
-
- // Write any delay (as this needs to come *before* the next note)
- // Since our global clock speed is 1000Hz, we have to multiply this
- // delay accordingly as the delay units are in the current clock speed.
- // This calculation converts them into 1000Hz delay units regardless of
- // the current clock speed.
- if (delay != 0) write->time((delay * iInitialSpeed / ::iSpeed));
- //printf("delay is %d (ticks %d)\n", (delay * iInitialSpeed / ::iSpeed), delay);
- delay = 0;
- break;
-
- default: // should never happen
- break;
-
- } // switch (::iFormat)
-
- // Convert the OPL register and value into a MIDI event
-
- if (code >= 0xa0 && code <= 0xa8) { // set freq bits 0-7
- channel = code-0xa0;
- curfreq[channel] = (curfreq[channel] & 0xF00) + (param & 0xff);
- if (keyAlreadyOn[channel]) {
- param = 0x20; // bare noteon for code below
- doNoteOnOff(true, channel, channel);
- }
- continue;
- } else if (code >= 0xB0 && code <= 0xB8) { // set freq bits 8-9 and octave and on/off
- channel = code - 0xb0;
- curfreq[channel] = (curfreq[channel] & 0x0FF) + ((param & 0x03)<<8);
- // save octave so we know what it is if we run 0xA0-0xA8 regs change code
- // next (which doesn't have the octave)
- reg[channel].iOctave = (param >> 2) & 7;
- int keyon = (param >> 5) & 1;
- doNoteOnOff(keyon, channel, channel);
- } else if ((code == 0xBD) && (::bRhythm)) {
- if ((param >> 5) & 1) {
- // Bass Drum
- doNoteOnOff((param >> 4) & 1, channel, CHAN_BASSDRUM);
- doNoteOnOff((param >> 3) & 1, channel, CHAN_SNAREDRUM);
- doNoteOnOff((param >> 2) & 1, channel, CHAN_TOMTOM);
- doNoteOnOff((param >> 1) & 1, channel, CHAN_TOPCYMBAL);
- doNoteOnOff( param & 1, channel, CHAN_HIHAT);
- }
- } else if (code >= 0x20 && code <= 0x35) {
- channel = GET_CHANNEL(code-0x20);
- reg[channel].reg20[GET_OP(code-0x20)] = param;
- } else if (code >= 0x40 && code <= 0x55) {
- channel = GET_CHANNEL(code-0x40);
- reg[channel].reg40[GET_OP(code-0x40)] = param;
- } else if (code >= 0x60 && code <= 0x75) {
- channel = GET_CHANNEL(code-0x60);
- reg[channel].reg60[GET_OP(code-0x60)] = param;
- } else if (code >= 0x80 && code <= 0x95) {
- channel = GET_CHANNEL(code-0x80);
- reg[channel].reg80[GET_OP(code-0x80)] = param;
- } else if (code >= 0xc0 && code <= 0xc8) {
- channel = code-0xc0;
- reg[channel].regC0 = param;
- } else if (code >= 0xe0 && code <= 0xF5) {
- channel = GET_CHANNEL(code-0xe0);
- reg[channel].regE0[GET_OP(code-0xe0)] = param;
- }
- }
-
- for (c = 0; c < 10; c++) {
- mapchannel[c] = c;
- 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).
- }
-
- delete write;
- fclose(f);
-
- // Display completion message and some stats
- printf("\nConversion complete. Wrote %s\n\n Total pitchbent notes: %d\n"
- " Total notes: %d\n Notes still active at end of song: %d\n\n",
- output, ::iPitchbendCount, ::iTotalNotes, ::iNotesActive);
-
- return 0;
-}
diff --git a/dro2midi/gen_test_midi.cpp b/dro2midi/gen_test_midi.cpp
deleted file mode 100644
index 99eb97d..0000000
--- a/dro2midi/gen_test_midi.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-// gen_test_midi.cpp - by malvineous@shikadi.net
-//
-// Generate a MIDI file that plays one note from all 128 GM instruments (in
-// order), followed by all the percussion notes.
-//
-// When this file is played through an OPL synthesiser capable of MIDI
-// playback, then captured through DOSBox, it will provide a complete set of
-// OPL patches that can easily be associated back to MIDI instruments.
-//
-
-#include "midiio.hpp"
-#include
-#include
-#include
-#include
-#include
-#include
-
-#define WRITE_BINARY "wb"
-#define READ_TEXT "r"
-
-int main(int argc, char**argv)
-{
- if (argc != 2) {
- fprintf(stderr, "no output filename given\n");
- return 1;
- }
-
- MidiWrite *write = new MidiWrite(argv[1]);
- if (!write) {
- fprintf(stderr, "out of memory\n");
- return 1;
- }
- if (!write->getf()) {
- perror(argv[1]);
- return 1;
- }
-
- float tempo = 120.0;
- int resolution = 280;
- write->head(/* version */ 0, /* track count updated later */0, resolution);
-
- write->track();
- write->tempo((long)(60000000.0 / tempo));
- write->tact(4,4,24,8);
-
- write->volume(1, 127);
- write->volume(9, 127);
- for (int i = 0; i < 128; i++) {
- write->program(1, i);
- write->noteon(1, 60, 127);
- write->time(50);
- write->noteoff(1, 60);
- write->time(50);
- }
-/*
- // Need to play a normal note for percussion to get DOSBox to start capturing
- // OPL data! (Why hasn't my patch to fix that been incorporated yet?!)
- write->noteon(1, 60, 127);
- write->time(50);
- write->noteoff(1, 60);
- write->time(200);
- for (int i = 35; i < 82; i++) {
- write->noteon(9, i, 127);
- write->time(50);
- write->noteoff(9, i);
- write->time(50);
- }*/
-
- delete write;
-
- return 0;
-}
diff --git a/dro2midi/make b/dro2midi/make
deleted file mode 100644
index a452b9f..0000000
--- a/dro2midi/make
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-
-# Usage:
-#
-# ./make
-#
-# or if you have a cross compiler for i586-pc-mingw32 installed
-#
-# ./make i586-pc-mingw32 .exe
-#
-
-if [ "$1" != "" ]; then
- PLATFORM="$1"-
- TARGET="dro2midi$2"
-else
- PLATFORM=""
- TARGET="dro2midi"
-fi
-
-${PLATFORM}g++ -o ${TARGET} dro2midi.cpp midiio.cpp &&
- ${PLATFORM}strip ${TARGET}
diff --git a/dro2midi/midiio.cpp b/dro2midi/midiio.cpp
deleted file mode 100644
index 618e8ce..0000000
--- a/dro2midi/midiio.cpp
+++ /dev/null
@@ -1,2161 +0,0 @@
-// midiio.cpp written by Günter Nagler 1995 (gnagler@ihm.tu-graz.ac.at)
-#include "midiio.hpp"
-#include
-#ifdef __MSDOS__
-#include
-#endif
-#include
-#include
-
-static const char* copyright = "midiio v1.4 (c) 1995 by Günter Nagler (" __DATE__ ")";
-
-int compress = 1;
-
-#define NOTREALISTIC_PAUSE 0x1000000UL
-
-// common sysex events
-unsigned char sysex_gmreset[] = { 0xF0, 0x05, 0x7E, 0x7F, 0x09, 0x01, 0xF7 };
-unsigned char sysex_gsreset[] = { 0xF0, 0x0A, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7 };
-unsigned char sysex_gsexit[] = { 0xF0, 0x0A, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x7F, 0x42, 0xF7 };
-unsigned char sysex_xgreset[] = { 0xF0, 0x08, 0x43, 0x10, 0x4C, 0x00, 0x00, 0x7E, 0x00, 0xF7 };
-
-static int issysex(const unsigned char* sysex, const unsigned char* sysdata, int syslen)
-{
- if (*sysex == 0xF0)
- sysex++;
- while (syslen > 0)
- {
- if (*sysex != *sysdata)
- return 0;
- syslen--;
- if (*sysex == 0xF7)
- break;
- sysex++;
- sysdata++;
- }
- return (syslen != 0) ? 0 : 1;
-}
-
-static int sysexlen(unsigned const char* sysex)
-{
-int len = 0;
-
- len = 0;
- while (*sysex != 0xF7)
- {
- sysex++;
- len++;
- }
- return len+1; // incl. F7
-}
-
-// class MidiRead
-
-const char* MidiRead::copyright()
-{
- return (const char*)::copyright;
-}
-
-MidiRead::MidiRead(const char* filename, FILE* f)
-{
- midiname_ = filename;
- if (f)
- {
- f_ = f;
- shouldclose_ = 0;
- }
- else
- {
- shouldclose_ = 1;
- if (!filename)
- f_ = 0;
- else
- f_ = fopen(filename, READ_BINARY);
- }
-
- buflen_ = 0;
- bufpos_ = 0;
- curpos_ = 0;
- pos_ = 0;
- curchannel_ = NOCHANNEL;
- curtime_ = 0;
- options_ = 0;
- if (f_)
- {
- fseek(f_, 0L, SEEK_END);
- filesize_ = ftell(f_);
- fseek(f_, 0L, SEEK_SET);
- }
- else
- filesize_ = 0;
-
- version_ = tracks_ = clicks_ = trackno_ = 0;
- tracklen_ = 0;
-}
-
-
-MidiRead::~MidiRead()
-{
- if (f_ && shouldclose_)
- fclose(f_);
-}
-
-int MidiRead::runhead()
-{
- if (!f_)
- {
- error("file not open");
- return 0;
- }
- seek(0);
- if (getlong() != MThd)
- {
- error("missing midi header MThd");
- return 0;
- }
-
- if (getlong() == 6)
- {
- version_ = getword();
- tracks_ = getword();
- clicks_ = getword();
- head(version_, tracks_, clicks_);
- }
- else
- {
- error("illegal midi header");
- return 0;
- }
- return 1;
-}
-
-int MidiRead::run()
-{
- pos_ = curpos_;
- if (!runhead())
- return 0;
- pos_ = curpos_;
- for (trackno_ = 1; trackno_ <= tracks_; trackno_++)
- if (!runtrack(trackno_))
- return 0;
- if (curpos_ >= filesize_)
- percent(percent_ = 100);
- endmidi();
- return 1;
-}
-
-int MidiRead::runevent(long trackend)
-{
-int midicode;
-
- pos_ = curpos_;
-
- unsigned char *c = need(1);
- if (!c || c[0] >= 0x80 || lastcode_ < 0)
- midicode = getbyte();
- else
- midicode = lastcode_;
-
- if (midicode < 0)
- return 0;
-
- switch(midicode)
- {
- case 0xf0: // sysex
- {
- int syslen = scansysevent(trackend - curpos_);
- if (!syslen)
- {
- error("end of sysex not found or sysex too large");
- return 0;
- }
- unsigned char* sysdata = get(syslen);
- if ((options_ & OPTION_NOSYSEVENTS) == 0)
- {
- if (issysex(sysex_gmreset, sysdata, syslen))
- gmreset();
- else if (issysex(sysex_gsreset, sysdata, syslen))
- gsreset();
- else if (issysex(sysex_gsexit, sysdata, syslen))
- gsexit();
- else if (issysex(sysex_xgreset, sysdata, syslen))
- xgreset();
- else
- sysex(syslen, sysdata);
- }
- else if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
- sysex(syslen, sysdata);
- else
- event(0xf0, syslen, sysdata);
- }
- break;
- case 0xf2:
- {
- c = get(2); if (!c) return 0;
- if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
- songpos((unsigned(c[1]) << 7) + unsigned(c[0]));
- else
- event(0xf2, 2, c);
- break;
- }
- case 0xf3:
- c = get(1); if (!c) return 0;
- if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
- songselect(*c);
- else
- event(0xf3, 1, c);
- break;
- case 0xf6:
- if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
- tunerequest();
- else
- event(0xf6);
- break;
- case 0xf8:
- if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
- timingclock();
- else
- event(0xf8);
- break;
- case 0xfa:
- if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
- start();
- else
- event(0xfa);
- break;
- case 0xfb:
- if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
- cont();
- else
- event(0xfb);
- break;
- case 0xfc:
- if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
- stop();
- else
- event(0xfc);
- break;
- case 0xfe:
- if ((options_ & OPTION_NOREALTIMEEVENTS) == 0)
- activesense();
- else
- event(0xfe);
- break;
- case 0xff:
- {
- int c;
- unsigned long endpos;
- int len;
-
- c = getbyte();
- len = (int)getdelta();
- endpos = curpos_ + len;
- if (options_ & OPTION_NOREALTIMEEVENTS)
- {
- len += curdeltalen_ + 1;
- seek(curpos_ - curdeltalen_ - 1);
- event(0xff, len, get(len));
- }
- else if (options_ & OPTION_NOMETAEVENTS)
- meta(c, len, get(len));
- else
- switch(c)
- {
- case 0:
- if (len == 2)
- seqnumber(getword());
- else
- meta(c, len, get(len));
- break;
- case meta_text:
- text(c, len, "text", get(len)); break;
- case meta_copyright:
- text(c, len, "copyright", get(len)); break;
- case meta_trackname:
- text(c, len, "trackname", get(len)); break;
- case meta_instrument:
- text(c, len, "instrument", get(len)); break;
- case meta_lyric:
- text(c, len, "lyric", get(len)); break;
- case meta_marker:
- text(c, len, "marker", get(len)); break;
- case meta_cuepoint:
- text(c, len, "cuepoint", get(len)); break;
- case 8:
- case 9:
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- text(c, len, 0, get(len)); break;
- case 0x20:
- if (len == 1)
- prefixchannel(getbyte());
- else
- meta(c, len, get(len));
- break;
- case 0x21:
- if (len == 1)
- prefixport(getbyte());
- else
- meta(c, len, get(len));
- break;
- case 0x2F:
- end();
- break;
- case 0x51:
- tempo(gettri());
- break;
- case 0x54:
- if (len == 5)
- {
- unsigned char *s = get(len);
-
- smpteofs(s[0], s[1], s[2], s[3], s[4]);
- }
- else
- meta(c, len, get(len));
- break;
- case 0x58:
- if (len == 4)
- {
- int nom, clicksperbeat, notes32perbeat, log2denom;
-
- nom = getbyte();
- log2denom = getbyte();
-
- clicksperbeat = getbyte();
- notes32perbeat = getbyte();
- tact(nom, 1 << log2denom, clicksperbeat, notes32perbeat);
- }
- else
- meta(c, len, get(len));
- break;
- case 0x59:
- if (len == 2)
- {
- signed char s[2];
-
- s[0] = (signed char)getbyte(); // sf
- s[1] = (signed char)getbyte(); // mi
- if (s[0] >= -7 && s[0] <= +7 && s[1] >= 0 && s[1] <= 1)
- key(s[0], s[1]);
- else
- meta(c, len, (unsigned char*)s);
- }
- else
- meta(c, len, get(len));
- break;
- default:
- meta(c, len, get(len));
- break;
- }
- seek(endpos);
- }
- break;
- default:
- {
- if (midicode < 0)
- return 0;
- if (midicode < 0x80)
- {
- char msg[30];
-
- sprintf(msg, "illegal midi command %02X", midicode);
- error(msg);
- return 0;
- }
- int channel = midicode & 0x0F;
- int cmd = midicode & 0xF0;
-
- switch(cmd)
- {
- case 0x80:
- case 0x90:
- {
- unsigned char* c;
-
- lastcode_ = midicode;
- c = get(2); if (!c) return 0;
- if (options_ & OPTION_NONOTEEVENTS)
- event(midicode, 2, c);
- else
- {
- if (cmd == 0x80 || c[1] == 0)
- noteoff(channel, c[0], c[1]);
- else
- noteon(channel, c[0], c[1]);
- }
- }
- break;
- case 0xA0:
- {
- unsigned char* p = get(2); if (!p) return 0;
-
- lastcode_ = midicode;
-
- if (options_ & OPTION_NOPOLYEVENTS)
- event(midicode, 2, p);
- else
- {
- polyaftertouch(channel, p[0], p[1]);
- }
- }
- break;
- case 0xB0:
- {
- unsigned char* p = get(2); if (!p) return 0;
-
- lastcode_ = midicode;
- if (options_ & OPTION_NOCONTROLEVENTS)
- event(midicode, 2, p);
- else if (options_ & OPTION_NOCONTROLS)
- control(channel, p[0], p[1]);
- else
- switch(p[0])
- {
- case ctrl_highbank: highbank(channel, p[1]); break;
- case ctrl_wheel: wheel(channel, p[1]); break;
- case ctrl_breath: breath(channel, p[1]); break;
- case ctrl_foot: foot(channel, p[1]); break;
- case ctrl_portamentotime: portamentotime(channel, p[1]); break;
- case ctrl_data: data(channel, p[1]); break;
- case ctrl_volume: volume(channel, p[1]); break;
- case ctrl_balance: balance(channel, p[1]); break;
- case ctrl_expression: expression(channel, p[1]); break;
- case ctrl_lowbank: lowbank(channel, p[1]); break;
- case ctrl_hold: hold(channel, p[1]); break;
- case ctrl_reverb: reverb(channel, p[1]); break;
- case ctrl_chorus: chorus(channel, p[1]); break;
- case ctrl_datainc: datainc(channel, p[1]); break;
- case ctrl_datadec: datadec(channel, p[1]); break;
- case ctrl_lowrpn: lowrpn(channel, p[1]); break;
- case ctrl_highrpn:
- case ctrl_resetctrlrs: resetctrlrs(channel, p[1]); break;
- case ctrl_allnotesoff: allnotesoff(channel, p[1]); break;
- {
- unsigned char *c = need(8);
-
- if (c &&
- c[0] == 0 && c[1] == midicode && c[2] == ctrl_lowrpn && c[3] == 0 &&
- c[4] == 0 && c[5] == midicode && c[6] == ctrl_data)
- {
- c = get(8);
- pitchbendrange(channel, c[7]);
- }
- else
- highrpn(channel, p[1]);
- break;
- }
- default:
- control(channel, p[0], p[1]);
- break;
- }
- break;
- }
-
- case 0xC0:
- {
- unsigned char* p = get(1); if (!p) return 0;
-
- lastcode_ = midicode;
- if (options_ & OPTION_NOPROGRAMEVENTS)
- event(midicode, 1, p);
- else
- program(channel, p[0]);
- }
- break;
- case 0xD0:
- {
- unsigned char* p = get(1); if (!p) return 0;
-
- lastcode_ = midicode;
- if (options_ & OPTION_NOAFTERTOUCHEVENTS)
- event(midicode, 1, p);
- else
- aftertouch(channel, p[0]);
- }
- break;
- case 0xE0:
- {
- unsigned char* p = get(2); if (!p) return 0;
- unsigned val = unsigned(p[0]) + (unsigned(p[1]) << 7);
-
- lastcode_ = midicode;
- if (options_ & OPTION_NOPITCHBENDEVENTS)
- event(midicode, 2, p);
- else
- pitchbend(channel, val);
- }
- break;
- default:
- {
- char msg[30];
-
- sprintf(msg, "unexpected command byte %02X", midicode);
- error(msg);
- return 0;
- }
- }
- }
- break;
- }
- return (int)(curpos_ - pos_);
-}
-
-int MidiRead::runtrack(int trackno)
-{
-unsigned long trackpos = curpos_, trackend;
-
- curtime_ = 0;
- lastcode_ = -1;
- pos_ = curpos_;
- if (!f_)
- {
- error("file not open");
- return 0;
- }
-
- if (getlong() != MTrk)
- {
- error("missing midi track MTrk");
- return 0;
- }
- tracklen_ = getlong();
- track(trackno, tracklen_, curchannel_ = scanchannel(tracklen_));
- trackpos = curpos_;
- trackend = trackpos + tracklen_;
- lastcode_ = -1;
- if ((options_ & OPTION_NOEVENTS) == 0)
- while (curpos_ < trackpos + tracklen_)
- {
- int newpercent = (int)((curpos_ * 100) / filesize_);
- if (newpercent != percent_)
- percent(percent_ = newpercent);
-
- unsigned long delta = getdelta();
- if ( delta >= NOTREALISTIC_PAUSE )
- warning("Unrealistic large pause found");
- time(delta);
- curtime_ += delta;
- if (runevent(trackend) <= 0)
- return 0;
- }
- seek(trackend);
- pos_ = curpos_;
- endtrack(trackno);
- return 1;
-}
-
-void MidiRead::setchannel(int channel)
-{
- assert(channel >= -1 && channel <= 15);
- curchannel_ = channel;
-}
-
-int MidiRead::scansysevent(unsigned long maxlen)
-{
-int n = sizeof(buf_);
-unsigned char* c, *p;
-long savepos = curpos_;
-int len;
-
- if (maxlen < n)
- n = (int)maxlen;
- c = need(n);
- if (!c)
- {
- n = buflen_;
- c = need(n);
- if (!c)
- return 0;
- }
-
- if (c[0] < 0x80)
- {
- // short sysex 0..127 bytes
- len = c[0];
- if (n >= len+1)
- {
- if (c[len] == 0xF7)
- return len+1;
- }
- }
- else if (n >= 2 && c[1] < 0x80)
- {
- // 128..16383 bytes
- int len = (int(c[0] & 0x7f) << 7) + c[1];
- if (n >= len + 2)
- {
- if (c[len+1] == 0xF7)
- return len+2;
- }
- }
- // sysex events without length information?
- p = (unsigned char*)memchr(c, 0xF7, n);
- if (p)
- return (int)(p - c + 1);
- seek(savepos);
- return 0;
-}
-
-int MidiRead::scanchannel(unsigned long maxlen)
-{
-int n = 512;
-unsigned char* c;
-int firstchannel = NOCHANNEL;
-int channel, code;
-long savepos = curpos_, endpos;
-int lastcode = -1;
-
- if (maxlen < n)
- n = (int)maxlen;
- c = need(n);
- if (!c)
- return -1;
-
- endpos = curpos_ + n;
- while (curpos_ < endpos)
- {
- channel = NOCHANNEL;
- getdelta();
- c = need(1);
- if (!c || *c >= 0x80 || lastcode < 0)
- code = getbyte();
- else
- code = lastcode;
- switch(code & 0xF0)
- {
- case 0xF0:
- switch(code)
- {
- case 0xFF:
- getbyte();
- get((int)getdelta());
- break;
- case 0xf0: // sysex
- {
- int len = scansysevent(endpos-curpos_);
- if (!get(len))
- goto endscan;
- }
- break;
- case 0xf2: get(2); break;
- case 0xf3: getbyte(); break;
- case 0xf6:
- case 0xf8:
- case 0xfa:
- case 0xfb:
- case 0xfc:
- case 0xfe:
- break;
- default:
- goto endscan;
- }
- break;
- case 0x80:
- case 0x90:
- case 0xA0:
- case 0xB0:
- case 0xE0:
- channel = code & 15;
- get(2);
- break;
- case 0xC0:
- case 0xD0:
- channel = code & 15;
- getbyte();
- break;
- default:
- goto endscan;
- }
- if (code < 0xf0)
- lastcode = code;
- if (channel >= 0)
- {
- if (firstchannel < 0)
- firstchannel = channel;
- else if (channel != firstchannel)
- {
- firstchannel = MULTICHANNEL;
- break;
- }
- }
- }
-endscan:
- seek(savepos);
- return firstchannel;
-}
-
-int MidiRead::getbyte()
-{
-unsigned char* c = get(1);
-
- if (c)
- return *c;
- return -1;
-}
-
-unsigned MidiRead::getword()
-{
-unsigned char* c = get(2);
-unsigned n = 0;
-
- if (c)
- {
- n = *c++;
- n = (n << 8) + *c++;
- }
- return n;
-}
-
-unsigned long MidiRead::gettri()
-{
-unsigned char* c = get(3);
-unsigned long n = 0;
-
- if (c)
- {
- n = *c++;
- n = (n << 8) + *c++;
- n = (n << 8) + *c++;
- }
- return n;
-}
-
-unsigned long MidiRead::getlong()
-{
-unsigned char* c = get(4);
-unsigned long n = 0;
-
- if (c)
- {
- n = *c++;
- n = (n << 8) + *c++;
- n = (n << 8) + *c++;
- n = (n << 8) + *c++;
- }
- return n;
-}
-
-unsigned long MidiRead::getdelta()
-{
-unsigned long n = 0;
-int i = 0, c;
-
- curdeltalen_ = 0;
- for (i = 0; i < 4; i++)
- {
- c = getbyte();
- if (c < 0)
- {
- error("unexpected end of file");
- return 0;
- }
- curdeltalen_++;
- n = (n << 7) + (c & 0x7f);
- if ((c & 0x80) == 0)
- break;
- }
- return n;
-}
-
-unsigned char* MidiRead::need(int n)
-{
- assert(n >= 0);
- if (n == 0)
- return 0;
- if (n > buflen_)
- {
- if (!f_)
- return 0;
- if (n > sizeof(buf_))
- return 0;
- if (n > sizeof(buf_) - bufpos_)
- {
- // move to beginning of buf
- memmove(buf_, buf_ + bufpos_, buflen_);
- bufpos_ = 0;
- }
- // add new data at end
- if (sizeof(buf_) - bufpos_ - buflen_ > 0)
- {
- fseek(f_, curpos_+buflen_, SEEK_SET);
- int l = fread(buf_+bufpos_+buflen_, 1, sizeof(buf_) - bufpos_ - buflen_, f_);
- if (l > 0)
- buflen_ += l;
- else if (l == 0)
- ;
- }
- }
- if (n <= buflen_)
- return buf_ + bufpos_;
- return 0;
-}
-
-unsigned char* MidiRead::get(int n)
-{
-unsigned char* s;
-
- s = need(n);
- if (s)
- {
- buflen_ -= n;
- bufpos_ += n;
- curpos_ += n;
- }
- else if (n > sizeof(buf_))
- warning("midi event larger than internal bufsize ignored");
- else if (n > 0)
- {
- error("unexpected end of file");
- exit(1);
- }
- return s;
-}
-
-void MidiRead::seek(long pos)
-{
- if (pos == curpos_ || pos < 0)
- return;
- if (pos >= curpos_ - bufpos_ && pos < curpos_ - bufpos_ + buflen_)
- {
- int n = (int)(pos - curpos_ + bufpos_);
-
- if (n < bufpos_)
- {
- buflen_ += bufpos_ - n;
- bufpos_ -= bufpos_ - n;
- }
- else
- {
- buflen_ -= n - bufpos_;
- bufpos_ += n - bufpos_;
- }
- curpos_ = pos;
- }
- else
- {
- fseek(f_, curpos_ = pos, SEEK_SET);
- bufpos_ = buflen_ = 0;
- }
-}
-
-unsigned long MidiRead::microsec(unsigned long units, unsigned long msperbeat)
-{
- assert(clicks_ != 0); // call runhead() or run() first!
-
- if (units > msperbeat)
- return (units / clicks_) * msperbeat;
- else
- return units * (msperbeat / clicks_);
-}
-
-
-long MidiRead::units(unsigned long microsec, unsigned long msperbeat)
-{
- assert(clicks_ != 0); // call runhead() or run() first!
- assert(msperbeat > 0); // invalid tempo!
- int clicks = clicks_;
- while ((msperbeat & 1) == 0)
- {
- if ((clicks & 1) == 0)
- clicks >>= 1;
- else if ((microsec & 1) == 0)
- microsec >>= 1;
- else
- break;
- msperbeat >>= 1;
- }
- if (microsec >= 0x10000L)
- return (microsec / msperbeat) * clicks;
- else
- return (microsec * clicks) / msperbeat;
-}
-
-
-static const char* GMProg[128] =
-{
- "Piano", "BritePiano", "HammerPiano", "HonkeyTonk", "NewTines", "DigiPiano", "Harpsicord", "Clav",
- "Celesta", "Glocken", "MusicBox", "Vibes", "Marimba", "Xylophon", "Tubular", "Santur",
- "FullOrgan", "PercOrgan", "BX-3Organ", "ChurchPipe", "Positive", "Musette", "Harmonica", "Tango",
- "ClassicGtr", "A.Guitar", "JazzGuitar", "CleanGtr", "MuteGuitar", "OverDrive", "DistGuitar", "RockMonics",
- "JazzBass", "DeepBass", "PickBass", "FretLess", "SlapBass1", "SlapBass2", "SynthBass1", "SynthBass2",
- "Violin", "Viola", "Cello", "ContraBass", "TremoloStr", "Pizzicato", "Harp", "Timpani",
- "Marcato", "SlowString", "AnalogPad", "StringPad", "Choir", "DooVoice", "Voices", "OrchHit",
- "Trumpet", "Trombone", "Tuba", "MutedTrumpet", "FrenchHorn", "Brass", "SynBrass1", "SynBrass2",
- "SopranoSax", "AltoSax", "TenorSax", "BariSax", "SweetOboe", "EnglishHorn", "BasoonOboe", "Clarinet",
- "Piccolo", "Flute", "Recorder", "PanFlute", "Bottle", "Shakuhachi","Whistle", "Ocarina",
- "SquareWave", "SawWave", "SynCalinope", "SynChiff", "Charang", "AirChorus", "Rezzo4ths", "Bass&Lead",
- "Fantasia", "WarmPad", "PolyPad", "GhostPad", "BowedGlas", "MetalPad", "HaloPad", "Sweep",
- "IceRain", "SoundTrack", "Crystal", "Atmosphere", "Brightness", "Goblin", "EchoDrop", "StarTheme",
- "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba","Scotland","Fiddle", "Shanai",
- "MetalBell", "Agogo", "SteelDrums", "Woodblock", "Taiko", "Tom", "SynthTom", "RevCymbal",
- "FretNoise", "NoiseChiff", "Seashore", "Birds", "Telephone", "Helicopter", "Stadium!!", "GunShot"
-};
-
-const char* MidiRead::notename(unsigned char note)
-{
-static char name[5];
-char* s = name;
-
- switch(note % 12)
- {
- case 0: *s++ = 'c'; break;
- case 1: *s++ = 'c'; *s++ = '#'; break;
- case 2: *s++ = 'd'; break;
- case 3: *s++ = 'd'; *s++ = '#'; break;
- case 4: *s++ = 'e'; break;
- case 5: *s++ = 'f'; break;
- case 6: *s++ = 'f'; *s++ = '#'; break;
- case 7: *s++ = 'g'; break;
- case 8: *s++ = 'g'; *s++ = '#'; break;
- case 9: *s++ = 'a'; break;
- case 10: *s++ = 'a'; *s++ = '#'; break;
- case 11: *s++ = 'b'; break; // former 'h': German language only
- }
-
- sprintf(s, "%d", (note / 12)-1); // octave (assuming Piano C4 is 60)
- return (const char*) name;
-}
-
-
-const char* MidiRead::progname(int n, int channel)
-{
-static char defname[10] = "";
-
- if (channel == 9) // drum programs
- {
- switch(n)
- {
- case 0: return "Dr1";
- case 0x10: return "Dr2";
- case 0x19: return "Dr3";
- case 0x20: return "Dr4";
- case 0x28: return "Dr5";
- case 0x40: return "Dr6";
- case 0x18: return "Dr7";
- case 0x30: return "Dr8";
- }
- }
- else if (n >= 0 && n <= 127)
- return GMProg[n];
-def:
- sprintf(defname, "%d", n);
- return (const char*)defname;
-}
-
-void MidiRead::head(unsigned version, unsigned tracks, unsigned clicksperquarter)
-{
-}
-
-void MidiRead::track(int trackno, long length, int channel)
-{
-}
-
-void MidiRead::endtrack(int trackno)
-{
-}
-
-void MidiRead::event(int what, int len, unsigned char* data)
-{
-}
-
-void MidiRead::seqnumber(unsigned int seqno)
-{
-}
-
-void MidiRead::smpteofs(int hour, int min, int sec, int frame, int fracframe)
-{
-}
-
-void MidiRead::key(int signature, int isminor)
-{
-}
-
-void MidiRead::prefixchannel(unsigned char channel)
-{
-}
-
-void MidiRead::prefixport(unsigned char port)
-{
-}
-
-void MidiRead::text(int what, int len, const char* whattext, const unsigned char* txt)
-{
-}
-
-void MidiRead::meta(int what, int len, const unsigned char* data)
-{
-}
-
-void MidiRead::end()
-{
-}
-
-void MidiRead::tact(int nom, int denom, int v1, int v2)
-{
-}
-
-void MidiRead::tempo(unsigned long ticks)
-{
-}
-
-void MidiRead::program(int channel, int program)
-{
-}
-
-void MidiRead::control(int channel, int what, int value)
-{
-}
-
-void MidiRead::highbank(int channel, int val)
-{
- control(channel, ctrl_highbank, val);
-}
-
-void MidiRead::wheel(int channel, int val)
-{
- control(channel, ctrl_wheel, val);
-}
-
-void MidiRead::breath(int channel, int val)
-{
- control(channel, ctrl_breath, val);
-}
-
-void MidiRead::foot(int channel, int val)
-{
- control(channel, ctrl_foot, val);
-}
-
-void MidiRead::portamentotime(int channel, int val)
-{
- control(channel, ctrl_portamentotime, val);
-}
-
-void MidiRead::data(int channel, int val)
-{
- control(channel, ctrl_data, val);
-}
-
-void MidiRead::volume(int channel, int val)
-{
- control(channel, ctrl_volume, val);
-}
-
-void MidiRead::balance(int channel, int val)
-{
- control(channel, ctrl_balance, val);
-}
-
-void MidiRead::expression(int channel, int val)
-{
- control(channel, ctrl_expression, val);
-}
-
-void MidiRead::lowbank(int channel, int val)
-{
- control(channel, ctrl_lowbank, val);
-}
-
-void MidiRead::hold(int channel, int val)
-{
- control(channel, ctrl_hold, val);
-}
-
-void MidiRead::reverb(int channel, int val)
-{
- control(channel, ctrl_reverb, val);
-}
-
-void MidiRead::chorus(int channel, int val)
-{
- control(channel, ctrl_chorus, val);
-}
-
-void MidiRead::datainc(int channel, int val)
-{
- control(channel, ctrl_datainc, val);
-}
-
-void MidiRead::datadec(int channel, int val)
-{
- control(channel, ctrl_datadec, val);
-}
-
-void MidiRead::lowrpn(int channel, int val)
-{
- control(channel, ctrl_lowrpn, val);
-}
-
-void MidiRead::highrpn(int channel, int val)
-{
- control(channel, ctrl_highrpn, val);
-}
-
-void MidiRead::resetctrlrs(int channel, int val)
-{
- control(channel, ctrl_resetctrlrs, val);
-}
-
-void MidiRead::allnotesoff(int channel, int val)
-{
- control(channel, ctrl_allnotesoff, val);
-}
-
-void MidiRead::pitchbendrange(int channel, int halfnotes)
-{
- highrpn(channel, 0);
- time(0);
- lowrpn(channel, 0);
- time(0);
- data(channel, halfnotes);
-}
-
-void MidiRead::noteon(int channel, int note, int vel)
-{
-}
-
-void MidiRead::noteoff(int channel, int note, int vel)
-{
-}
-
-void MidiRead::time(unsigned long ticks)
-{
-}
-
-void MidiRead::pitchbend(int channel, int val)
-{
-}
-
-void MidiRead::polyaftertouch(int channel, int note, int val)
-{
-}
-
-void MidiRead::aftertouch(int channel, int val)
-{
-}
-
-void MidiRead::songpos(unsigned pos)
-{
-}
-
-void MidiRead::songselect(unsigned char song)
-{
-}
-
-void MidiRead::tunerequest()
-{
-}
-
-void MidiRead::timingclock()
-{
-}
-
-void MidiRead::start()
-{
-}
-
-void MidiRead::cont()
-{
-}
-
-void MidiRead::stop()
-{
-}
-
-void MidiRead::activesense()
-{
-}
-
-void MidiRead::sysex(int syslen, const unsigned char* sysdata)
-{
-}
-
-void MidiRead::gmreset()
-{
-}
-
-void MidiRead::gsreset()
-{
-}
-
-void MidiRead::gsexit()
-{
-}
-
-void MidiRead::xgreset()
-{
-}
-
-void MidiRead::endmidi()
-{
-}
-
-void MidiRead::percent(int perc)
-{
-}
-
-void MidiRead::error(const char* msg)
-{
- fprintf(stderr, "error: %s\n", msg);
-}
-
-void MidiRead::warning(const char* msg)
-{
- fprintf(stderr, "warning: %s\n", msg);
-}
-
-FILE* MidiRead::getf()
-{
- return f_;
-}
-
-// class MidiWrite
-
-const char* MidiWrite::copyright()
-{
- return (const char*)::copyright;
-}
-
-MidiWrite::MidiWrite(const char* filename)
-{
- midiname_ = filename;
- if (midiname_)
- f_ = fopen(midiname_, WRITE_BINARY);
- else
- f_ = 0;
- trackpos_ = -1;
- curpos_ = 0;
- trackchannel_ = -1;
-
- bufpos_ = 0;
- buflen_ = 0;
- filesize_ = 0;
- trackcount_ = 0;
- curtime_ = curdelta_ = 0;
- lastcode_ = -1;
- clicks_ = 0;
-}
-
-MidiWrite::~MidiWrite()
-{
- if (trackcount_ > 0)
- {
- seek(10);
- putword(trackcount_);
- }
- if (trackpos_ > 0)
- endtrack();
- flush();
- if (f_)
- fclose(f_);
-}
-
-void MidiWrite::head(int version, int tracks, unsigned clicksperquarter)
-{
- seek(0);
- putlong(MThd);
- putlong(6);
- putword(version);
- putword(tracks); // unknown
- putword(clicks_ = clicksperquarter);
-}
-
-void MidiWrite::track()
-{
- if (trackpos_ > 0)
- endtrack();
- endtrack_ = 0;
- lastcode_ = -1;
- curtime_ = curdelta_ = 0;
- seek(trackpos_ = filesize_);
- putlong(MTrk);
- putlong(0); // unknown yet
- trackcount_++;
-}
-
-void MidiWrite::endtrack()
-{
- seek(filesize_);
- if (!endtrack_)
- end();
- if (trackpos_ <= 0)
- return;
- seek(trackpos_+4);
- putlong(filesize_ - trackpos_ - 8);
- trackpos_ = 0;
-}
-
-void MidiWrite::event(int what, int len, const unsigned char* data)
-{
- puttime();
- putcode(what);
- put(len, data);
-}
-
-void MidiWrite::prefixchannel(unsigned char channel)
-{
- meta(0x20, 1, &channel);
-}
-
-void MidiWrite::prefixport(unsigned char port)
-{
- meta(0x21, 1, &port);
-}
-
-void MidiWrite::text(int what, int len, const unsigned char* txt)
-{
- if (len < 0)
- len = strlen((char *)txt);
- meta(what, len, txt);
-}
-
-void MidiWrite::meta(int what, int len, const unsigned char* data)
-{
- puttime();
- putcode(0xff);
- putbyte(what);
- putdelta(len);
- put(len, data);
-}
-
-void MidiWrite::end()
-{
- if (endtrack_) // don't need end of track event twice
- return;
- endtrack_ = 1;
- meta(0x2f, 0, 0);
-}
-
-void MidiWrite::tact(int nom, int denom, int v1, int v2)
-{
-int log2denom;
-
- switch(denom)
- {
- case 1: log2denom = 0; break;
- case 2: log2denom = 1; break;
- case 4: log2denom = 2; break;
- case 8: log2denom = 3; break;
- case 16: log2denom = 4; break;
- case 32: log2denom = 5; break;
- case 64: log2denom = 6; break;
- case 128: log2denom = 7; break;
- case 256: log2denom = 8; break;
- default:
- log2denom = 2;
- assert((1 << log2denom) == denom);
- }
- puttime();
- putcode(0xff);
- putbyte(0x58);
- putbyte(4);
- putbyte(nom);
- putbyte(log2denom);
- putbyte(v1);
- putbyte(v2);
-}
-
-void MidiWrite::seqnumber(unsigned int seqno)
-{
- puttime();
- putcode(0xff);
- putbyte(0x00);
- putbyte(2);
- putword(seqno);
-}
-
-void MidiWrite::smpteofs(int hour, int min, int sec, int frame, int fracframe)
-{
- puttime();
- putcode(0xff);
- putbyte(0x54);
- putbyte(5);
- putbyte(hour);
- putbyte(min);
- putbyte(sec);
- putbyte(frame);
- putbyte(fracframe);
-}
-
-void MidiWrite::key(int signature, int isminor)
-{
- puttime();
- putcode(0xff);
- putbyte(0x59);
- putbyte(2);
- putbyte((signed char)signature);
- putbyte((signed char)isminor);
-}
-
-void MidiWrite::tempo(unsigned long ticks)
-{
- puttime();
- putcode(0xff);
- putbyte(0x51);
- putbyte(3);
- puttri(ticks);
-}
-
-void MidiWrite::program(int channel, int prg)
-{
- assert(channel >= 0 && channel < 16);
- puttime();
- putcode(0xC0 + channel);
- putbyte(prg);
-}
-
-void MidiWrite::control(int channel, int what, int val)
-{
- assert(channel >= 0 && channel < 16);
- puttime();
- putcode(0xB0 + channel);
- putbyte(what);
- putbyte(val);
-}
-
-void MidiWrite::highbank(int channel, int val)
-{
- control(channel, ctrl_highbank, val);
-}
-
-void MidiWrite::wheel(int channel, int val)
-{
- control(channel, ctrl_wheel, val);
-}
-
-void MidiWrite::breath(int channel, int val)
-{
- control(channel, ctrl_breath, val);
-}
-
-void MidiWrite::foot(int channel, int val)
-{
- control(channel, ctrl_foot, val);
-}
-
-void MidiWrite::portamentotime(int channel, int val)
-{
- control(channel, ctrl_portamentotime, val);
-}
-
-void MidiWrite::data(int channel, int val)
-{
- control(channel, ctrl_data, val);
-}
-
-void MidiWrite::volume(int channel, int val)
-{
- control(channel, ctrl_volume, val);
-}
-
-void MidiWrite::balance(int channel, int val)
-{
- control(channel, ctrl_balance, val);
-}
-
-void MidiWrite::expression(int channel, int val)
-{
- control(channel, ctrl_expression, val);
-}
-
-void MidiWrite::lowbank(int channel, int val)
-{
- control(channel, ctrl_lowbank, val);
-}
-
-void MidiWrite::hold(int channel, int val)
-{
- control(channel, ctrl_hold, val);
-}
-
-void MidiWrite::reverb(int channel, int val)
-{
- control(channel, ctrl_reverb, val);
-}
-
-void MidiWrite::chorus(int channel, int val)
-{
- control(channel, ctrl_chorus, val);
-}
-
-void MidiWrite::datainc(int channel, int val)
-{
- control(channel, ctrl_datainc, val);
-}
-
-void MidiWrite::datadec(int channel, int val)
-{
- control(channel, ctrl_datadec, val);
-}
-
-void MidiWrite::lowrpn(int channel, int val)
-{
- control(channel, ctrl_lowrpn, val);
-}
-
-void MidiWrite::highrpn(int channel, int val)
-{
- control(channel, ctrl_highrpn, val);
-}
-
-void MidiWrite::resetctrlrs(int channel, int val)
-{
- control(channel, ctrl_resetctrlrs, val);
-}
-
-void MidiWrite::allnotesoff(int channel, int val)
-{
- control(channel, ctrl_allnotesoff, val);
-}
-
-void MidiWrite::noteon(int channel, int note, int vel)
-{
- assert(channel >= 0 && channel < 16);
- puttime();
- putcode(0x90+channel);
- putbyte(note);
- putbyte(vel);
-}
-
-void MidiWrite::noteoff(int channel, int note, int vel)
-{
- assert(channel >= 0 && channel < 16);
- puttime();
- if (vel != 0 || lastcode_ < 0 || (lastcode_ & 0xF0) != 0x90)
- putcode(0x80+channel);
- else
- putcode(0x90+channel); // vel == 0!
- putbyte(note);
- putbyte(vel);
-}
-
-void MidiWrite::time(unsigned long ticks)
-{
- if ( ticks >= NOTREALISTIC_PAUSE )
- warning("generating unrealistic large pause");
- curdelta_ += ticks;
- curtime_ += ticks;
-}
-
-void MidiWrite::cleardelta()
-{
- curtime_ -= curdelta_;
- curdelta_ = 0;
-}
-
-void MidiWrite::pitchbend(int channel, int val)
-{
- assert(channel >= 0 && channel < 16);
- puttime();
- putcode(0xE0 + channel);
- putbyte(val & 0x7F);
- putbyte((val >> 7) & 0x7F);
-}
-
-void MidiWrite::polyaftertouch(int channel, int note, int val)
-{
- assert(channel >= 0 && channel < 16);
- puttime();
- putcode(0xA0 + channel);
- putbyte(note);
- putbyte(val);
-}
-
-void MidiWrite::aftertouch(int channel, int val)
-{
- assert(channel >= 0 && channel < 16);
- puttime();
- putcode(0xD0 + channel);
- putbyte(val);
-}
-
-void MidiWrite::songpos(unsigned pos)
-{
- puttime();
- putcode(0xF2);
- putbyte(pos & 0x7F);
- putbyte((pos >> 7) & 0x7F);
-}
-
-void MidiWrite::songselect(unsigned char song)
-{
- puttime();
- putcode(0xF3);
- putbyte(song);
-}
-
-void MidiWrite::tunerequest()
-{
- puttime();
- putcode(0xf6);
-}
-
-void MidiWrite::timingclock()
-{
- puttime();
- putcode(0xf8);
-}
-
-void MidiWrite::start()
-{
- puttime();
- putcode(0xfa);
-}
-
-void MidiWrite::cont()
-{
- puttime();
- putcode(0xfb);
-}
-
-void MidiWrite::stop()
-{
- puttime();
- putcode(0xfc);
-}
-
-void MidiWrite::activesense()
-{
- puttime();
- putcode(0xfe);
-}
-
-void MidiWrite::sysex(int syslen, const unsigned char* sysdata)
-{
- puttime();
- putcode(0xf0);
- if (*sysdata == 0xF0)
- {
- syslen--;
- sysdata++;
- }
- put(syslen, sysdata);
-}
-
-void MidiWrite::gmreset()
-{
- sysex(sysexlen(sysex_gmreset), sysex_gmreset);
-}
-
-void MidiWrite::xgreset()
-{
- sysex(sysexlen(sysex_xgreset), sysex_xgreset);
-}
-
-void MidiWrite::gsreset()
-{
- sysex(sysexlen(sysex_gsreset), sysex_gsreset);
-}
-
-void MidiWrite::gsexit()
-{
- sysex(sysexlen(sysex_gsexit), sysex_gsexit);
-}
-
-void MidiWrite::pitchbendrange(int channel, int halfnotes)
-{
- highrpn(channel, 0);
- lowrpn(channel, 0);
- data(channel, halfnotes);
-}
-
-void MidiWrite::putbyte(unsigned char val)
-{
- put(1, &val);
-}
-
-void MidiWrite::putcode(unsigned char code)
-{
-int put;
-
- assert(code >= 0x80);
-
- if (compress)
- put = !(code == lastcode_ && code <= 0x9f);
- else
- put = 1;
- if (put)
- putbyte(code);
- lastcode_ = code;
-}
-
-static unsigned char c[4];
-
-void MidiWrite::putword(unsigned val)
-{
- c[1] = (unsigned char)(val & 0xff); val >>= 8;
- c[0] = (unsigned char)(val & 0xff);
- put(2, c);
-}
-
-void MidiWrite::puttri(unsigned long val)
-{
- c[2] = (unsigned char)(val & 0xff); val >>= 8;
- c[1] = (unsigned char)(val & 0xff); val >>= 8;
- c[0] = (unsigned char)(val & 0xff);
- put(3, c);
-}
-
-void MidiWrite::putlong(unsigned long val)
-{
- c[3] = (unsigned char)(val & 0xff); val >>= 8;
- c[2] = (unsigned char)(val & 0xff); val >>= 8;
- c[1] = (unsigned char)(val & 0xff); val >>= 8;
- c[0] = (unsigned char)(val & 0xff);
- put(4, c);
-}
-
-void MidiWrite::putdelta(unsigned long val)
-{
- int i = 0, j = 3;
- while (i < 4)
- {
- c[j] = val & 0x7F;
- if (j < 3)
- c[j] |= 0x80;
- val >>= 7;
- i++;
- if (!val)
- break;
- j--;
- }
- put(i, c+j);
-}
-
-void MidiWrite::puttime()
-{
- putdelta(curdelta_);
- curdelta_ = 0;
-}
-
-void MidiWrite::flush()
-{
- if (buflen_ > 0)
- {
- fseek(f_, curpos_ - bufpos_, SEEK_SET);
- if (fwrite(buf_, buflen_, 1, f_) != 1)
- error("write error (maybe disk full)");
- assert(ftell(f_) == curpos_ - bufpos_ + buflen_);
- bufpos_ = buflen_ = 0;
- }
-}
-
-void MidiWrite::put(int len, const unsigned char* c)
-{
- if (len <= 0)
- return;
- if (c == 0 || len > sizeof(buf_))
- return;
- if (sizeof(buf_) - bufpos_ < len)
- flush();
- memcpy(buf_+bufpos_, c, len);
- bufpos_+=len;
- if (bufpos_ > buflen_)
- buflen_ = bufpos_;
- curpos_+= len;
- if (curpos_ > filesize_)
- filesize_ = curpos_;
-}
-
-void MidiWrite::seek(long pos)
-{
- assert(pos >= 0 && pos <= filesize_);
- if (curpos_ == pos)
- return;
- if (pos >= curpos_-bufpos_ && pos <= curpos_-bufpos_+buflen_)
- {
- bufpos_ = (int)(pos - (curpos_-bufpos_));
- curpos_ = pos;
- return;
- }
- flush();
- curpos_ = pos;
-}
-
-FILE* MidiWrite::getf()
-{
- return f_;
-}
-
-void MidiWrite::error(const char* msg)
-{
- fprintf(stderr, "midi write error: %s\n", msg);
-}
-
-void MidiWrite::warning(const char* msg)
-{
- fprintf(stderr, "midi write warning: %s\n", msg);
-}
-
-int MidiWrite::unitsperquarter()
-{
- return clicks_;
-}
-
-MidiCopy::MidiCopy(const char* filename, FILE* f) : MidiRead(filename, f)
-{
- dest_ = NULL;
- for (int c = 0; c < 16; c++)
- mapchannel_[c] = c; // no change
-}
-
-void MidiCopy::head(unsigned version, unsigned tracks, unsigned clicksperquarter)
-{
- if (dest_)
- dest_->head(version, 0, clicksperquarter);
-}
-
-void MidiCopy::track(int trackno, long length, int channel)
-{
- if (dest_)
- dest_->track();
-}
-
-void MidiCopy::endtrack(int trackno)
-{
- if (dest_)
- dest_->endtrack();
-}
-
-void MidiCopy::event(int what, int len, unsigned char* data)
-{
- if (dest_)
- dest_->event(what, len, data);
-}
-
-
-void MidiCopy::seqnumber(unsigned int seqno)
-{
- if (dest_)
- dest_->seqnumber(seqno);
-}
-
-void MidiCopy::smpteofs(int hour, int min, int sec, int frame, int fracframe)
-{
- if (dest_)
- dest_->smpteofs(hour, min, sec, frame, fracframe);
-}
-
-void MidiCopy::key(int signature, int isminor)
-{
- if (dest_)
- dest_->key(signature, isminor);
-}
-
-void MidiCopy::prefixchannel(unsigned char channel)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->prefixchannel(mapchannel_[channel]);
-}
-
-void MidiCopy::prefixport(unsigned char port)
-{
- if (dest_)
- dest_->prefixport(port);
-}
-
-void MidiCopy::text(int what, int len, char* whattext, unsigned char* txt)
-{
- if (dest_)
- dest_->text(what, len, txt);
-}
-
-void MidiCopy::meta(int what, int len, unsigned char* data)
-{
- if (dest_)
- dest_->meta(what, len, data);
-}
-
-void MidiCopy::end()
-{
- if (dest_)
- dest_->end();
-}
-
-void MidiCopy::tact(int nom, int denom, int v1, int v2)
-{
- if (dest_)
- dest_->tact(nom, denom, v1,v2);
-}
-
-void MidiCopy::tempo(unsigned long ticks)
-{
- if (dest_)
- dest_->tempo(ticks);
-}
-
-void MidiCopy::program(int channel, int program)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->program(mapchannel_[channel], program);
-}
-
-void MidiCopy::control(int channel, int what, int value)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->control(mapchannel_[channel], what, value);
-}
-
-void MidiCopy::highbank(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->highbank(mapchannel_[channel], val);
-}
-
-void MidiCopy::wheel(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->wheel(mapchannel_[channel], val);
-}
-
-void MidiCopy::breath(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->wheel(mapchannel_[channel], val);
-}
-
-void MidiCopy::foot(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->foot(mapchannel_[channel], val);
-}
-
-void MidiCopy::portamentotime(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->portamentotime(mapchannel_[channel], val);
-}
-
-void MidiCopy::data(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->data(mapchannel_[channel], val);
-}
-
-void MidiCopy::volume(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->volume(mapchannel_[channel], val);
-}
-
-void MidiCopy::balance(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->balance(mapchannel_[channel], val);
-}
-
-void MidiCopy::expression(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->expression(mapchannel_[channel], val);
-}
-
-void MidiCopy::lowbank(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->lowbank(mapchannel_[channel], val);
-}
-
-void MidiCopy::hold(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->hold(mapchannel_[channel], val);
-}
-
-void MidiCopy::reverb(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->reverb(mapchannel_[channel], val);
-}
-
-void MidiCopy::chorus(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->chorus(mapchannel_[channel], val);
-}
-
-void MidiCopy::datainc(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->datainc(mapchannel_[channel], val);
-}
-
-void MidiCopy::datadec(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->datadec(mapchannel_[channel], val);
-}
-
-void MidiCopy::lowrpn(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->lowrpn(mapchannel_[channel], val);
-}
-
-void MidiCopy::highrpn(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->highrpn(mapchannel_[channel], val);
-}
-
-void MidiCopy::resetctrlrs(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->resetctrlrs(mapchannel_[channel], val);
-}
-
-void MidiCopy::allnotesoff(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->allnotesoff(mapchannel_[channel], val);
-}
-
-
-void MidiCopy::noteon(int channel, int note, int vel)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->noteon(mapchannel_[channel], note, vel);
-}
-
-void MidiCopy::noteoff(int channel, int note, int vel)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->noteoff(mapchannel_[channel], note, vel);
-}
-
-void MidiCopy::time(unsigned long ticks)
-{
- if (dest_)
- dest_->time(ticks);
-}
-
-void MidiCopy::pitchbend(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->pitchbend(mapchannel_[channel],val);
-}
-
-void MidiCopy::polyaftertouch(int channel, int note, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->polyaftertouch(mapchannel_[channel], note, val);
-}
-
-void MidiCopy::aftertouch(int channel, int val)
-{
- if (dest_ && mapchannel_[channel] >= 0)
- dest_->aftertouch(mapchannel_[channel], val);
-}
-
-void MidiCopy::songpos(unsigned pos)
-{
- if (dest_)
- dest_->songpos(pos);
-}
-
-void MidiCopy::songselect(unsigned char song)
-{
- if (dest_)
- dest_->songselect(song);
-}
-
-void MidiCopy::tunerequest()
-{
- if (dest_)
- dest_->tunerequest();
-
-}
-
-void MidiCopy::timingclock()
-{
- if (dest_)
- dest_->timingclock();
-}
-
-void MidiCopy::start()
-{
- if (dest_)
- dest_->start();
-}
-
-void MidiCopy::cont()
-{
- if (dest_)
- dest_->cont();
-}
-
-void MidiCopy::stop()
-{
- if (dest_)
- dest_->stop();
-}
-
-void MidiCopy::activesense()
-{
- if (dest_)
- dest_->activesense();
-}
-
-void MidiCopy::sysex(int syslen, unsigned char* sysdata)
-{
- if (dest_)
- dest_->sysex(syslen, sysdata);
-}
-
-void MidiCopy::xgreset()
-{
- if (dest_)
- dest_->xgreset();
-}
-
-void MidiCopy::gmreset()
-{
- if (dest_)
- dest_->gmreset();
-}
-
-void MidiCopy::gsreset()
-{
- if (dest_)
- dest_->gsreset();
-}
-
-void MidiCopy::gsexit()
-{
- if (dest_)
- dest_->gsexit();
-}
-
-void MidiCopy::setoutput(MidiWrite* dest)
-{
- assert(dest != NULL && dest->getf() != NULL); // need a valid MidiWrite instance
- dest_ = dest;
-}
-
-void MidiCopy::stopoutput()
-{
- dest_ = NULL; // owner is responsible for deleting the MidiWrite object
-}
-
-MidiWrite* MidiCopy::getoutput()
-{
- return dest_;
-}
-
-void MidiCopy::mapchannel(int channel, int newchannel)
-{
- assert(channel >= 0 && channel < 16);
- assert(newchannel >= 0 && newchannel < 16);
- mapchannel_[channel] = newchannel;
-}
-
-void MidiCopy::ignorechannel(int channel)
-{
- assert(channel >= 0 && channel < 16);
- mapchannel_[channel] = -1;
-}
diff --git a/dro2midi/midiio.hpp b/dro2midi/midiio.hpp
deleted file mode 100644
index bc88e10..0000000
--- a/dro2midi/midiio.hpp
+++ /dev/null
@@ -1,402 +0,0 @@
-#ifndef __MIDIIO__
-#define __MIDIIO__
-
-#include
-
-#ifndef MIDI_BUFSIZE
-#define MIDI_BUFSIZE 1024
-#endif
-
-// opening modes of a midi file
-#define WRITE_BINARY "wb"
-#define READ_BINARY "rb"
-
-const unsigned long MThd = 0x4D546864ul;
-const unsigned long MTrk = 0x4D54726Bul;
-
-// different standard midi formats
-#define VERSION_MULTICHANNEL 0
-#define VERSION_SINGLECHANNEL 1
-#define VERSION_MULTISONG 2
-
-#define OPTION_NOCONTROLS 1 // no control details but general information
-#define OPTION_NOEVENTS 2 // no track events at all
-#define OPTION_NOMETAEVENTS 4 // no meta details but general information
-#define OPTION_NOSYSEVENTS 8 // no sysex details but general information
-#define OPTION_NONOTEEVENTS 16 // no note events (8x or 9x)
-#define OPTION_NOPOLYEVENTS 32 // no poly aftertouch events (Ax)
-#define OPTION_NOCONTROLEVENTS 64 // no control events at all (Bx)
-#define OPTION_NOPROGRAMEVENTS 128 // no program change events (Cx)
-#define OPTION_NOAFTERTOUCHEVENTS 256 // no aftertouch events (Dx)
-#define OPTION_NOPITCHBENDEVENTS 512 // no pitchbend events (Ex)
-#define OPTION_NOREALTIMEEVENTS 1024 // no realtime events (Fx)
-
-// getchannel delivers a valid channel or:
-#define NOCHANNEL (-1)
-#define MULTICHANNEL (-2)
-#define VALIDCHANNEL(ch) ((ch) >= 0 && (ch) <= 15)
-#define gm_drumchannel 9
-#define SAYCHANNEL(ch) ((ch) + 1) // 0..15 in midi format but spoken 1..16!
-
-// for use of param what in function text()
-#define meta_text 1
-#define meta_copyright 2
-#define meta_trackname 3
-#define meta_instrument 4
-#define meta_lyric 5
-#define meta_marker 6
-#define meta_cuepoint 7
-#define meta_endtrack 0x2f
-
-#define ctrl_highbank 0
-#define ctrl_wheel 1
-#define ctrl_breath 2
-#define ctrl_foot 4
-#define ctrl_portamentotime 5
-#define ctrl_data 6
-#define ctrl_volume 7
-#define ctrl_balance 10
-#define ctrl_expression 11
-#define ctrl_lowbank 32
-#define ctrl_hold 64
-#define ctrl_reverb 91
-#define ctrl_chorus 93
-#define ctrl_datainc 96
-#define ctrl_datadec 97
-#define ctrl_lowrpn 100
-#define ctrl_highrpn 101
-#define ctrl_resetctrlrs 121
-#define ctrl_allnotesoff 123
-
-#define balance_left 0
-#define balance_center 64
-#define balance_right 127
-
-#define volume_mute 0
-#define volume_full 127
-
-#define bpm(ticks) (60000000.0 / (ticks))
-#define ticks(beats) ((unsigned long)(60000000.0 / (beats)))
-
-#define tempo_60bpm (1000000L)
-#define tempo_120bpm (500000L)
-#define tempo_240bpm (250000L)
-
-
-class MidiRead
-{
-public:
- static const char* copyright();
-
- MidiRead(const char* filename, FILE* f = 0);
- virtual ~MidiRead();
-
- FILE* getf();
-
- int run();
- int runhead();
- int runtrack(int trackno);
- int runevent(long trackend);
-
- long getpos() { return pos_; }
- long geteventpos() { return pos_; }
- long getcurpos() { return curpos_; }
- unsigned long getcurtime() { return curtime_; } // in midi units
-
- virtual void head(unsigned version, unsigned tracks, unsigned clicksperquarter);
- virtual void track(int trackno, long length, int channel);
- virtual void endtrack(int trackno); // closing track
-
- virtual void time(unsigned long ticks); // delay between events
-
- virtual void event(int what, int len = 0, unsigned char* data = 0);
-
- // these are event categories:
- virtual void meta(int what, int len, const unsigned char* data);
- // these are special meta events:
- virtual void text(int what, int len, const char* whattext, const unsigned char* txt);
- virtual void end(); // end of track command
- virtual void prefixchannel(unsigned char channel);
- virtual void prefixport(unsigned char port);
- virtual void seqnumber(unsigned int seqno);
- virtual void smpteofs(int hour, int min, int sec, int frame, int fracframe);
- virtual void tact(int nom, int denom, int clicksperbeat, int notes32perbeat);
- virtual void tempo(unsigned long microsecperbeat);
- virtual void key(int signature, int isminor); // C=0, -7=7flats +7=7sharps
- virtual void program(int channel, int prg);
- virtual void control(int channel, int what, int val); // general controls
- // special controls:
- virtual void highbank(int channel, int val);
- virtual void wheel(int channel, int val);
- virtual void breath(int channel, int val);
- virtual void foot(int channel, int val);
- virtual void portamentotime(int channel, int val);
- virtual void data(int channel, int val);
- virtual void volume(int channel, int val);
- virtual void balance(int channel, int val);
- virtual void expression(int channel, int val);
- virtual void lowbank(int channel, int val);
- virtual void hold(int channel, int val);
- virtual void reverb(int channel, int val);
- virtual void chorus(int channel, int val);
- virtual void datainc(int channel, int val);
- virtual void datadec(int channel, int val);
- virtual void lowrpn(int channel, int val);
- virtual void highrpn(int channel, int val);
- virtual void resetctrlrs(int channel, int val);
- virtual void allnotesoff(int channel, int val);
- virtual void pitchbendrange(int channel, int val);
- virtual void noteon(int channel, int note, int vel);
- virtual void noteoff(int channel, int note, int vel);
- virtual void pitchbend(int channel, int val);
- virtual void polyaftertouch(int channel, int note, int val);
- virtual void aftertouch(int channel, int val);
- virtual void songpos(unsigned pos);
- virtual void songselect(unsigned char song);
- virtual void tunerequest();
- virtual void timingclock();
- virtual void start();
- virtual void cont();
- virtual void stop();
- virtual void activesense();
- virtual void sysex(int syslen, const unsigned char* sysdata);
- // these are special sysex events:
- virtual void xgreset();
- virtual void gmreset();
- virtual void gsreset();
- virtual void gsexit();
-
- virtual void endmidi(); // after last track
- virtual void error(const char* msg);
- virtual void warning(const char* msg);
-
- virtual void percent(int perc);
-
- int getchannel() { return curchannel_; }
- void setchannel(int channel);
-
- static const char* progname(int n, int channel);
- static const char* notename(unsigned char note);
-
- int options_;
-
- int version_, tracks_, clicks_, trackno_;
-
- void seek(long pos);
- int getbyte();
- unsigned getword();
- unsigned long gettri();
- unsigned long getlong();
- unsigned long getdelta();
- unsigned char* get(int len);
- unsigned char* need(int nbytes);
-
- unsigned long microsec(unsigned long units, unsigned long msperbeats);
- long units(unsigned long microsec, unsigned long msperbeats);
-
- // use scanchannel only at start of track!
- int scanchannel(unsigned long maxlen); // channel 0-15 or -1=no channel or -2=multichannels
-
- // use sysevent only directly after reading F0 or F7
- int scansysevent(unsigned long maxlen);
-
-protected:
- const char *midiname_;
- FILE* f_;
- unsigned char shouldclose_; // 0=no, otherwise=yes
- long filesize_;
- unsigned char buf_[MIDI_BUFSIZE];
- int buflen_, bufpos_;
- int curchannel_;
- unsigned long curtime_;
- int percent_;
- int lastcode_;
- unsigned long tracklen_;
-
- long pos_, curpos_;
- unsigned char curdeltalen_; // number of bytes read by recent getdelta() call
-};
-
-class MidiWrite
-{
-public:
- static const char* copyright();
-
- MidiWrite(const char* filename);
- virtual ~MidiWrite();
-
- FILE* getf();
-
- long getcurpos() { return curpos_; }
- long getcurtime() { return curtime_; }
- void cleardelta();
-
- void head(int version = 1, int tracks = 0, unsigned clicksperquarter = 192);
- void track();
- void endtrack();
-
- void event(int what, int len, const unsigned char* data);
-
- void text(int what, int len, const unsigned char* txt);
- void meta(int what, int len, const unsigned char* data); // 0xff ....
- virtual void prefixchannel(unsigned char channel);
- virtual void prefixport(unsigned char port);
- virtual void seqnumber(unsigned int seqno);
- virtual void smpteofs(int hour, int min, int sec, int frame, int fracframe);
- virtual void key(int signature, int isminor);
- void tact(int nom, int denom, int v1, int v2);
- void tempo(unsigned long microsecperbeat);
- void end();
- void program(int channel, int prg);
- void control(int channel, int what, int val);
- // these are special controls
- void highbank(int channel, int val);
- void wheel(int channel, int val);
- void breath(int channel, int val);
- void foot(int channel, int val);
- void portamentotime(int channel, int val);
- void data(int channel, int val);
- void volume(int channel, int val);
- void balance(int channel, int val);
- void expression(int channel, int val);
- void lowbank(int channel, int val);
- void hold(int channel, int val);
- void reverb(int channel, int val);
- void chorus(int channel, int val);
- void datainc(int channel, int val);
- void datadec(int channel, int val);
- void lowrpn(int channel, int val);
- void highrpn(int channel, int val);
- void resetctrlrs(int channel, int val);
- void allnotesoff(int channel, int val);
- void pitchbendrange(int channel, int halfnotes);
-
- void noteon(int channel, int note, int vel);
- void noteoff(int channel, int note, int vel = 0);
- void time(unsigned long ticks);
- void pitchbend(int channel, int val);
- void polyaftertouch(int channel, int note, int val);
- void aftertouch(int channel, int val);
- void songpos(unsigned pos);
- void songselect(unsigned char song);
- void tunerequest();
- void timingclock();
- void start();
- void cont();
- void stop();
- void activesense();
- void sysex(int syslen, const unsigned char* sysdata);
- void xgreset();
- void gmreset();
- void gsreset();
- void gsexit();
-
- void putbyte(unsigned char val);
- void putcode(unsigned char code);
- void putword(unsigned val);
- void puttri(unsigned long val);
- void putlong(unsigned long val);
- void putdelta(unsigned long val);
- void puttime();
- void put(int len, const unsigned char* c);
- void seek(long pos);
-
- virtual void error(const char* msg);
- virtual void warning(const char* msg);
-
- int unitsperquarter();
-
-protected:
- const char *midiname_;
- FILE* f_;
- long trackpos_, curpos_, filesize_;
- int trackchannel_, trackcount_, lastcode_, endtrack_;
-
- unsigned char buf_[MIDI_BUFSIZE];
- int bufpos_, buflen_;
-
- unsigned long curdelta_;
- unsigned long curtime_;
-
- int clicks_;
-
- void flush();
-};
-
-
-class MidiCopy : public MidiRead
-{
-public:
- MidiCopy(const char* filename, FILE* f = 0);
-
- void setoutput(MidiWrite* dest);
- void stopoutput();
- MidiWrite* getoutput();
-
- void mapchannel(int channel, int newchannel);
- void ignorechannel(int channel);
-
- virtual void head(unsigned version, unsigned tracks, unsigned clicksperquarter);
- virtual void track(int trackno, long length, int channel);
- virtual void endtrack(int trackno);
-
- virtual void time(unsigned long ticks);
-
- virtual void event(int what, int len = 0, unsigned char* data = 0);
-
- virtual void meta(int what, int len, unsigned char* data);
- virtual void text(int what, int len, char* whattext, unsigned char* txt);
- virtual void end();
- virtual void prefixchannel(unsigned char channel);
- virtual void prefixport(unsigned char port);
- virtual void seqnumber(unsigned int seqno);
- virtual void smpteofs(int hour, int min, int sec, int frame, int fracframe);
- virtual void tact(int nom, int denom, int clicksperbeat, int notes32perbeat);
- virtual void tempo(unsigned long microsecperbeat);
- virtual void key(int signature, int isminor); // C=0, -7=7flats +7=7sharps
- virtual void program(int channel, int prg);
- virtual void control(int channel, int what, int val);
- virtual void highbank(int channel, int val);
- virtual void wheel(int channel, int val);
- virtual void breath(int channel, int val);
- virtual void foot(int channel, int val);
- virtual void portamentotime(int channel, int val);
- virtual void data(int channel, int val);
- virtual void volume(int channel, int val);
- virtual void balance(int channel, int val);
- virtual void expression(int channel, int val);
- virtual void lowbank(int channel, int val);
- virtual void hold(int channel, int val);
- virtual void reverb(int channel, int val);
- virtual void chorus(int channel, int val);
- virtual void datainc(int channel, int val);
- virtual void datadec(int channel, int val);
- virtual void lowrpn(int channel, int val);
- virtual void highrpn(int channel, int val);
- virtual void resetctrlrs(int channel, int val);
- virtual void allnotesoff(int channel, int val);
- virtual void noteon(int channel, int note, int vel);
- virtual void noteoff(int channel, int note, int vel);
- virtual void pitchbend(int channel, int val);
- virtual void polyaftertouch(int channel, int note, int val);
- virtual void aftertouch(int channel, int val);
- virtual void songpos(unsigned pos);
- virtual void songselect(unsigned char song);
- virtual void tunerequest();
- virtual void timingclock();
- virtual void start();
- virtual void cont();
- virtual void stop();
- virtual void activesense();
- virtual void sysex(int syslen, unsigned char* sysdata);
- virtual void gmreset();
- virtual void gsreset();
- virtual void gsexit();
- virtual void xgreset();
-
-protected:
- MidiWrite* dest_;
- int mapchannel_[16]; // channel 0-15 or events are ignored for invalid channel
-};
-
-#endif
diff --git a/img/Two-OP AM.png b/img/Two-OP AM.png
old mode 100644
new mode 100755
diff --git a/img/Two-OP FM.png b/img/Two-OP FM.png
old mode 100644
new mode 100755
diff --git a/img/abs_sine.png b/img/abs_sine.png
old mode 100644
new mode 100755
diff --git a/img/adlib.png b/img/adlib.png
old mode 100644
new mode 100755
diff --git a/img/algo_switch_off.png b/img/algo_switch_off.png
old mode 100644
new mode 100755
diff --git a/img/algo_switch_on.png b/img/algo_switch_on.png
old mode 100644
new mode 100755
diff --git a/img/algo_switch_on2.png b/img/algo_switch_on2.png
old mode 100644
new mode 100755
diff --git a/img/algo_switch_on3.png b/img/algo_switch_on3.png
old mode 100644
new mode 100755
diff --git a/img/alternating_sine.png b/img/alternating_sine.png
old mode 100644
new mode 100755
diff --git a/img/bassdrum.png b/img/bassdrum.png
old mode 100644
new mode 100755
diff --git a/img/camel_sine.png b/img/camel_sine.png
old mode 100644
new mode 100755
diff --git a/img/channeloff.png b/img/channeloff.png
old mode 100644
new mode 100755
diff --git a/img/channelon.png b/img/channelon.png
old mode 100644
new mode 100755
diff --git a/img/cymbal.png b/img/cymbal.png
old mode 100644
new mode 100755
diff --git a/img/disabled.png b/img/disabled.png
old mode 100644
new mode 100755
diff --git a/img/full_sine.png b/img/full_sine.png
old mode 100644
new mode 100755
diff --git a/img/half_sine.png b/img/half_sine.png
old mode 100644
new mode 100755
diff --git a/img/hihat.png b/img/hihat.png
old mode 100644
new mode 100755
diff --git a/img/line_border_horiz.png b/img/line_border_horiz.png
old mode 100644
new mode 100755
diff --git a/img/line_border_vert.png b/img/line_border_vert.png
old mode 100644
new mode 100755
diff --git a/img/logarithmic_saw.png b/img/logarithmic_saw.png
old mode 100644
new mode 100755
diff --git a/img/quarter_sine.png b/img/quarter_sine.png
old mode 100644
new mode 100755
diff --git a/img/snare.png b/img/snare.png
old mode 100644
new mode 100755
diff --git a/img/square.png b/img/square.png
old mode 100644
new mode 100755
diff --git a/img/toggle_off_sq.png b/img/toggle_off_sq.png
old mode 100644
new mode 100755
diff --git a/img/toggle_on_sq.png b/img/toggle_on_sq.png
old mode 100644
new mode 100755
diff --git a/img/tom.png b/img/tom.png
old mode 100644
new mode 100755
diff --git a/img/yamaha.png b/img/yamaha.png
old mode 100644
new mode 100755
diff --git a/py/atten_levels.py b/py/atten_levels.py
deleted file mode 100644
index e0ba7a9..0000000
--- a/py/atten_levels.py
+++ /dev/null
@@ -1,13 +0,0 @@
-lvls = [.75, 1.5, 3, 6, 12, 24]
-
-def get_db(i):
- db = 0.0
- for d in lvls:
- if i & 0x1:
- db += d
- i >>= 1
- return db
-
-for i in range(2**6):
- db = get_db(i)
- print '"%.2f dB",' % db,
diff --git a/py/oplparse.py b/py/oplparse.py
deleted file mode 100644
index 33b9d56..0000000
--- a/py/oplparse.py
+++ /dev/null
@@ -1,340 +0,0 @@
-import sys
-import json
-
-REG_SCOPE = {
- 0x00: 'Chip',
- 0x20: '0x20',
- 0x30: '0x20',
- 0x40: '0x40',
- 0x50: '0x40',
- 0x60: 'Attack/decay',
- 0x70: 'Attack/decay',
- 0x80: 'Sustain/release',
- 0x90: 'Sustain/release',
- 0xA0: 'Frequency (L)',
- 0xB0: 'Key on/off/Frequency(H)',
- 0xE0: 'Wave select',
- 0xF0: 'Wave select',
-}
-
-# Map register ranges to channel + operator
-OPERATOR_MAP = {
- 0x00: (1, 1),
- 0x01: (2, 1),
- 0x02: (3, 1),
- 0x03: (1, 2),
- 0x04: (2, 2),
- 0x05: (3, 2),
- 0x08: (4, 1),
- 0x09: (5, 1),
- 0x0a: (6, 1),
- 0x0b: (4, 2),
- 0x0c: (4, 2),
- 0x0d: (6, 2),
- 0x10: (7, 1),
- 0x11: (8, 1),
- 0x12: (9, 1),
- 0x13: (7, 2),
- 0x14: (8, 2),
- 0x15: (9, 2),
-}
-OSC_MAP = {1: 'mod', 2: 'car'}
-
-def reg_to_chan_and_op(reg):
- # from REGOP in opl driver
- #return ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f )
- chan, op = OPERATOR_MAP.get(reg % 32, (-1, 1))
- print 'ch%02d.%s: ' % (chan, OSC_MAP[op]),
- return chan, op
-
-def interpret_00(reg, val, ev):
- regs = {1:'TEST', 2:'Timer 1', 3:'Timer 2', 4:'Timer Ctrl', 8:'CSM/keysplit'}
- print '%02X (%s)' % (reg, regs.get(reg, '??')),
-
-def interpret_20(reg, val, ev):
- ch, op = reg_to_chan_and_op(reg)
- ev[2]['ch'] = ch
- bitfields = {7: 'tre', 6: 'vib', 5: 'sus', 4:'ksr'}
- bf = val & 0xf0
- opts = []
- for b, opt in bitfields.iteritems():
- optset = 'X' if (bf & (0x1 << b)) else '-'
- opts.append('%s:%s' % (opt, optset))
- ev[op-1][opt]=optset
- fm_mult = val & 0x0f
- mults = {0:0.5, 11:10, 13:12, 14:15}
- fm_mult = mults.get(fm_mult, fm_mult)
- opts.append('fm_mult:x%d' % (fm_mult))
- ev[op-1]['fm_mult'] = fm_mult
- print '; '.join(opts) ,
-
-def _db_atten_to_factor(atten_db):
- return (10 ** -(atten_db / 10))
-
-def interpret_40(reg, val, ev):
- ch, op = reg_to_chan_and_op(reg)
- ev[2]['ch'] = ch
- bit_db = {5:24, 4:12, 3:6, 2:3, 1:1.5, 0:0.75}
- oct_db = {0:0, 0x40:1.5, 0x80:3, 0xC0:6}
- lvl = 0.0
- for b, db in bit_db.iteritems():
- if (val & (0x1 << b)):
- lvl += db
- db_oct = oct_db[val & 0xC0]
- oct_scale = '-%d dB/8ve' % db_oct
- factor = _db_atten_to_factor(lvl)
- ev[op-1]['db'] = lvl
- ev[op-1]['db_oct'] = db_oct
- ev[op-1]['scale'] = factor
- print '%.2f dB; (x%.4f) %s' % (lvl, factor, oct_scale) ,
-
-def interpret_60(reg, val, ev):
- ch, op = reg_to_chan_and_op(reg)
- ev[2]['ch'] = ch
- a = val >> 4
- d = val & 0x0f
- ev[op-1]['a'] = a
- ev[op-1]['d'] = d
- print 'Att: 0x%1x; Dec: 0x%1x' % (a, d) ,
-
-def interpret_80(reg, val, ev):
- ch, op = reg_to_chan_and_op(reg)
- ev[2]['ch'] = ch
- s = val >> 4
- r = val & 0x0f
- ev[op-1]['s'] = s
- ev[op-1]['r'] = r
- print 'Sus: 0x%1x; Rel: 0x%1x' % (s, r) ,
-
-def interpret_A0(reg, val, ev):
- global freq_lsb
- freq_lsb = val
- chan = 1 + reg % 0xA0
- print 'ch%02d: Freq-LSB=%02X' % (chan, val) ,
-
-def interpret_B0(reg, val, ev):
- global freq_lsb
- try:
- freq_lsb
- except NameError:
- freq_lsb = 0
- oct = (val >> 2) & 0x7
- fnum = ((val & 0x3) << 8) + freq_lsb
- chan = 1 + reg % 0xB0
- key = 'ON' if (1 << 5) & val else 'off'
- frq = _fnum_to_hz(fnum, oct)
- ev[1]['frq'] = frq
- ev[2]['key'] = key
- ev[2]['ch'] = chan
- #ev[0]['gate'] = key
- print 'ch%02d: Key-%s; f0x%03X oct:%d (%.4f Hz)' % (chan, key, fnum, oct, frq) ,
-
-'''
-A0-A8: Frequency Number:
-
- Determines the pitch of the note. Highest bits of F-Number are stored
- in the register below.
-
-
-B0-B8: Key On / Block Number / F-Number(hi bits):
-
- bit 5: KEY-ON. When 1, channel output is enabled.
- bits 2-4: Block Number. Roughly determines the octave.
- bits 0-1: Frequency Number. 2 highest bits of the above register.
-
- The following formula is used to determine F-Number and Block:
-
- F-Number = Music Frequency * 2^(20-Block) / 49716 Hz
-'''
-
-def _fnum_to_hz(fnum, octave):
- return (49716.0 * fnum) / (2 ** (21 - octave))
-
-def interpret_BD(reg, val, ev):
- print '* BD' ,
-
-def interpret_C0(reg, val, ev):
- alg = 'ADD' if val & 0x1 else 'MODULATE'
- feedback = (val >> 1) & 0xc
- ev[2]['alg'] = alg
- ev[2]['feedback'] = feedback
- print 'feedback: %d algo: %s' % (feedback, alg) ,
-
-def interpret_E0(reg, val, ev):
- ch, op = reg_to_chan_and_op(reg)
- ev[2]['ch'] = ch
- waves = {0: 'SIN', 1: 'HALFSIN', 2: 'ABSSIN', 3: 'QUARTSIN'}
- wav = waves[val & 0x3]
- ev[op-1]['wav'] = wav
- print wav ,
-
-def interpret_write(ts, reg, val, ev):
- print '%10d : ' % ts,
- if reg >= 0x01 and reg <= 0x08:
- interpret_00(reg, val, ev)
- elif reg >= 0x20 and reg <= 0x35:
- interpret_20(reg, val, ev)
- elif reg >= 0x40 and reg <= 0x55:
- interpret_40(reg, val, ev)
- elif reg >= 0x60 and reg <= 0x75:
- interpret_60(reg, val, ev)
- elif reg >= 0x80 and reg <= 0x95:
- interpret_80(reg, val, ev)
- elif reg >= 0xA0 and reg <= 0xA8:
- interpret_A0(reg, val, ev)
- elif reg >= 0xB0 and reg <= 0xB8:
- interpret_B0(reg, val, ev)
- elif reg == 0xBD:
- interpret_BD(reg, val, ev)
- elif reg >= 0xC0 and reg <= 0xC8:
- interpret_C0(reg, val, ev)
- elif reg >= 0xE0 and reg <= 0xF5:
- interpret_E0(reg, val, ev)
- else:
- print '?????',
- print
-
- #scope = REG_SCOPE.get(reg & 0xF0, '??')
- #chan, operator = reg_to_chan_and_op(reg)
- #aprint '%10f : %02d.%1d : %02x : %20s' % (float(ts)/1000, chan, operator, val, scope)
-
-def parse_opldump(stream):
- events = dict()
- t0 = tp = False
- line = stream.readline()
- while len(line) >= 1:
- ts, opl2, op = line.split(':')
- if 'OPL2' == opl2:
- ts = int(ts)
- if not t0:
- t0 = ts
- ts -= t0
- reg, val = op.split('=')
- reg = int(reg, 16)
- val = int(val, 16)
- try:
- ev = events[ts]
- except KeyError:
- ev = [dict(), dict(), dict()]
- events[ts] = ev
- interpret_write(ts, reg, val, ev)
- line = stream.readline()
- return events
-
-def get_javascript_for(event):
- mod = event[0]
- car = event[1]
- anc = event[2]
- indent = 8*' '
- lines = []
- if 'ch' in anc:
- lines.append('var ch = opl2.channels[%d];\n' % (anc['ch'] - 1))
- if 'fm_mult' in car:
- lines.append('ch.setModulatorMultiplier(%d); ch.setCarrierMultiplier(%d);\n' % (mod['fm_mult'], car['fm_mult']))
- if 'scale' in car:
- lines.append('ch.setModulatorAttenuation(%f); ch.setCarrierAttenuation(%f);\n' % (mod['scale'], car['scale']))
- if 'a' in mod:
- lines.append('ch.setEnvParams(ch.modEnv, 1/%d, 1/%d, 1/%d, 1/%d);\n' % (mod['a'], mod['d'], mod['s']+1, mod['r']))
- if 'a' in car:
- lines.append('ch.setEnvParams(ch.carEnv, 1/%d, 1/%d, 1/%d, 1/%d);\n' % (car['a'], car['d'], car['s']+1, car['r']))
- if 'wav' in car:
- lines.append('ch.setCarrierWaveform(ch.%s); ch.setModulatorWaveform(ch.%s);\n' % (car['wav'], mod['wav']))
- if 'key' in anc:
- if 'ON' == anc['key'].upper():
- lines.append('ch.noteOn(%f);\n' % car['frq'])
- elif 'OFF' == anc['key'].upper():
- lines.append('ch.noteOff();\n')
- return indent + indent.join(lines)
-
-
-def find_unique_instruments(events):
- instruments = dict()
- ilist = list()
- timestamps = events.keys()
- timestamps.sort()
- for t in timestamps:
- ev = events[t]
- try: # get rid of fields which don't define uniqueness
- del ev[2]['key']
- del ev[2]['ch']
- del ev[1]['frq']
- except KeyError:
- pass
- instr_json = json.dumps(ev, indent=2)
- if instr_json not in instruments:
- instruments[instr_json] = t
- ilist.append(instr_json)
- return ilist, instruments
-
-def print_instrument(json_i, ts):
- print
- print '@ %10d:' % ts
- try:
- d = json.loads(json_i)
- print 'Waveforms: %10s%10s' % (d[0]['wav'], d[1]['wav'])
- print 'Freq mult: %9dx%9dx' % (d[0]['fm_mult'], d[1]['fm_mult'])
- print 'Levels: %7d dB%7d dB' % (-d[0]['db'], -d[1]['db'])
- envs = (d[0]['a'], d[0]['d'], d[0]['s'], d[0]['r'], d[1]['a'], d[1]['d'], d[1]['s'], d[1]['r'])
- print 'Envelopes: %1x %1x %1x %1x %1x %1x %1x %1x' % envs
- except KeyError:
- print 'incomplete instrument?'
-
-# Conversions mapping to VST floating point values
-W2F = {'SIN':0, 'HALFSIN':.33, 'ABSSIN':.66, 'QUARTSIN':1}
-def m2f(mult):
- if mult < 1: # half frq
- return 0
- else:
- return float(mult)/15.0
-
-def a2f(att_db):
- a = float(att_db)/0.75
- return a/63.0
-
-def b2f(bool_param):
- if 'X' == bool_param:
- return 1.0
- else:
- return 0.0
-D2F = {0:0.0,1.5:0.33,3.0:0.66,6.0:1.0}
-
-def e2f(env_val):
- return float(env_val)/15.0
-
-def output_instrument_vst_program(json_i, ts):
- try:
- d = json.loads(json_i)
- m=d[0]; c=d[1]
- lines = [
- ' const float i_params_%d[] = {' % ts,
- ' %.6ff, %.6ff, // waveforms' % (W2F[c['wav']], W2F[m['wav']]),
- ' %.6ff, %.6ff, // frq multipliers' % (m2f(c['fm_mult']), m2f(m['fm_mult'])),
- ' %.6ff, %.6ff, // attenuation' % (a2f(c['db']), a2f(m['db'])),
- ' %.1ff, %.1ff, %.1ff, %.1ff, // tre / vib / sus / ks' % tuple([b2f(c[x]) for x in ['tre', 'vib', 'sus', 'ksr']]),
- ' %.1ff, %.1ff, %.1ff, %.1ff, // tre / vib / sus / ks' % tuple([b2f(m[x]) for x in ['tre', 'vib', 'sus', 'ksr']]),
- ' %.6ff, %.6ff, // KSR/8ve' % (D2F[c['db_oct']], D2F[m['db_oct']]),
- ' %.6ff, // algorithm' % (1.0 if 'ADD'==d[2]['alg'] else 0.0),
- ' %.6ff, // feedback' % (float(d[2]['feedback'])/7.0),
- ' %.1ff, %.1ff, %.1ff, %.1ff, // adsr' % tuple([e2f(c[x]) for x in ['a', 'd', 's', 'r']]),
- ' %.1ff, %.1ff, %.1ff, %.1ff, // adsr' % tuple([e2f(m[x]) for x in ['a', 'd', 's', 'r']]),
- ' };',
- ' std::vector v_i_params_%d (i_params_%d, i_params_%d + sizeof(i_params_%d) / sizeof(float));' % (ts,ts,ts,ts),
- ' programs["Instr %d"] = std::vector(v_i_params_%d);' % (ts, ts),
- ]
- print
- print '\n'.join(lines)
- except KeyError:
- pass
- #print
- #print '// incomplete instrument..'
-
-def main(argv):
- events = parse_opldump(sys.stdin)
- ilist, instruments = find_unique_instruments(events)
- for i in ilist:
- output_instrument_vst_program(i, instruments[i])
- #print i, instruments[i]
- #print_instrument(i, instruments[i])
-
-if '__main__' == __name__:
- main(sys.argv)
diff --git a/py/sbi_filter.py b/py/sbi_filter.py
deleted file mode 100644
index 2a1f28d..0000000
--- a/py/sbi_filter.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import glob
-import os
-
-class SBI:
- def __init__(self, sbi_data):
- if sbi_data[0:3] != b'SBI':
- raise ValueError('Invalid file header.')
- if len(sbi_data) != 52:
- raise ValueError('Invalid file size.')
- self.register_values = sbi_data[36:]
-
- def __eq__(self, other):
- return isinstance(other, SBI) and self.register_values == other.register_values
-
- def __hash__(self):
- return hash(self.register_values)
-
-
-# Requires Python 3.5+
-def find_distinct_sbis(root_dir):
- sbis = {}
- bad = []
- for filename in glob.iglob(os.path.join(root_dir, '**', '*.sbi'), recursive=True):
- with open(filename, 'rb') as f:
- try:
- sbi = SBI(f.read())
- try:
- dup_filename = sbis[sbi]
- print('Duplicate SBI file: %s (duplicate of %s)' % (filename, dup_filename))
- bad.append(filename)
- except KeyError:
- sbis[sbi] = filename
- except ValueError as e:
- print('Invalid SBI file: %s (%s)' % (filename, e))
- bad.append(filename)
- return sbis, bad
-
-
-if __name__ == '__main__':
- distinct_sbis, to_delete = find_distinct_sbis('.')
- print('Found %d distinct SBI files' % len(distinct_sbis))
- print('Writing batch file..')
- with open('delete_bad_sbis.cmd', 'w') as batch_file:
- for delete in to_delete:
- print('DEL %s' % delete, file=batch_file)