aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2016-12-06 20:13:02 +0300
committerWohlstand <admin@wohlnet.ru>2016-12-06 20:13:02 +0300
commitabee80062051164aa4931527a54c07046dbc1394 (patch)
tree18d59e00318781a166590d4c17b49ca138ddfc2a /src
parent20aff6650e77940237f696e33728a85b1f566fa9 (diff)
downloadlibADLMIDI-abee80062051164aa4931527a54c07046dbc1394.tar.gz
libADLMIDI-abee80062051164aa4931527a54c07046dbc1394.tar.bz2
libADLMIDI-abee80062051164aa4931527a54c07046dbc1394.zip
Added Nuked OPL3 emulator (which is more accurate than DosBox's)
Also: - Fixed warnings in the CLang code model plugin for Qt Creator - Fixed bend coefficient which caused incorrect hi-hats in DMX banks
Diffstat (limited to 'src')
-rw-r--r--src/adlmidi.cpp3786
-rw-r--r--src/adlmidi.h44
-rw-r--r--src/dbopl.cpp3254
-rw-r--r--src/nukedopl3.c1429
-rw-r--r--src/nukedopl3.h151
5 files changed, 5542 insertions, 3122 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp
index 70532b8..0f4a91c 100644
--- a/src/adlmidi.cpp
+++ b/src/adlmidi.cpp
@@ -23,13 +23,13 @@
// Setup compiler defines useful for exporting required public API symbols in gme.cpp
#ifndef ADLMIDI_EXPORT
- #if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL)
- #define ADLMIDI_EXPORT __declspec(dllexport)
- #elif defined (LIBADLMIDI_VISIBILITY)
- #define ADLMIDI_EXPORT __attribute__((visibility ("default")))
- #else
- #define ADLMIDI_EXPORT
- #endif
+#if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL)
+#define ADLMIDI_EXPORT __declspec(dllexport)
+#elif defined (LIBADLMIDI_VISIBILITY)
+#define ADLMIDI_EXPORT __attribute__((visibility ("default")))
+#else
+#define ADLMIDI_EXPORT
+#endif
#endif
#ifdef _WIN32
@@ -53,7 +53,11 @@
#include <algorithm>
#include "fraction.h"
+#ifdef ADLMIDI_USE_DOSBOX_OPL
#include "dbopl.h"
+#else
+#include "nukedopl3.h"
+#endif
#include "adldata.hh"
#include "adlmidi.h"
@@ -68,32 +72,36 @@ class MIDIplay;
static const unsigned MaxCards = 100;
static std::string ADLMIDI_ErrorString;
-static const unsigned short Operators[23*2] =
- {// Channels 0-2
- 0x000,0x003,0x001,0x004,0x002,0x005, // operators 0, 3, 1, 4, 2, 5
- // Channels 3-5
- 0x008,0x00B,0x009,0x00C,0x00A,0x00D, // operators 6, 9, 7,10, 8,11
- // Channels 6-8
- 0x010,0x013,0x011,0x014,0x012,0x015, // operators 12,15, 13,16, 14,17
- // Same for second card
- 0x100,0x103,0x101,0x104,0x102,0x105, // operators 18,21, 19,22, 20,23
- 0x108,0x10B,0x109,0x10C,0x10A,0x10D, // operators 24,27, 25,28, 26,29
- 0x110,0x113,0x111,0x114,0x112,0x115, // operators 30,33, 31,34, 32,35
- // Channel 18
- 0x010,0x013, // operators 12,15
- // Channel 19
- 0x014,0xFFF, // operator 16
- // Channel 19
- 0x012,0xFFF, // operator 14
- // Channel 19
- 0x015,0xFFF, // operator 17
- // Channel 19
- 0x011,0xFFF }; // operator 13
+static const unsigned short Operators[23 * 2] =
+{
+ // Channels 0-2
+ 0x000, 0x003, 0x001, 0x004, 0x002, 0x005, // operators 0, 3, 1, 4, 2, 5
+ // Channels 3-5
+ 0x008, 0x00B, 0x009, 0x00C, 0x00A, 0x00D, // operators 6, 9, 7,10, 8,11
+ // Channels 6-8
+ 0x010, 0x013, 0x011, 0x014, 0x012, 0x015, // operators 12,15, 13,16, 14,17
+ // Same for second card
+ 0x100, 0x103, 0x101, 0x104, 0x102, 0x105, // operators 18,21, 19,22, 20,23
+ 0x108, 0x10B, 0x109, 0x10C, 0x10A, 0x10D, // operators 24,27, 25,28, 26,29
+ 0x110, 0x113, 0x111, 0x114, 0x112, 0x115, // operators 30,33, 31,34, 32,35
+ // Channel 18
+ 0x010, 0x013, // operators 12,15
+ // Channel 19
+ 0x014, 0xFFF, // operator 16
+ // Channel 19
+ 0x012, 0xFFF, // operator 14
+ // Channel 19
+ 0x015, 0xFFF, // operator 17
+ // Channel 19
+ 0x011, 0xFFF
+}; // operator 13
static const unsigned short Channels[23] =
- {0x000,0x001,0x002, 0x003,0x004,0x005, 0x006,0x007,0x008, // 0..8
- 0x100,0x101,0x102, 0x103,0x104,0x105, 0x106,0x107,0x108, // 9..17 (secondary set)
- 0x006,0x007,0x008, 0xFFF,0xFFF }; // <- hw percussions, 0xFFF = no support for pitch/pan
+{
+ 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, // 0..8
+ 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, // 9..17 (secondary set)
+ 0x006, 0x007, 0x008, 0xFFF, 0xFFF
+}; // <- hw percussions, 0xFFF = no support for pitch/pan
/*
In OPL3 mode:
@@ -128,325 +136,407 @@ static const unsigned short Channels[23] =
struct OPL3
{
- friend class MIDIplay;
- unsigned NumChannels;
- ADL_MIDIPlayer* _parent;
-
- std::vector<DBOPL::Handler> cards;
-private:
- std::vector<unsigned short> ins; // index to adl[], cached, needed by Touch()
- std::vector<unsigned char> pit; // value poked to B0, cached, needed by NoteOff)(
- std::vector<unsigned char> regBD;
-
- std::vector<adlinsdata> dynamic_metainstruments; // Replaces adlins[] when CMF file
- std::vector<adldata> dynamic_instruments; // Replaces adl[] when CMF file
- const unsigned DynamicInstrumentTag = 0x8000u, DynamicMetaInstrumentTag = 0x4000000u;
- const adlinsdata& GetAdlMetaIns(unsigned n)
- {
- return (n & DynamicMetaInstrumentTag) ?
- dynamic_metainstruments[n & ~DynamicMetaInstrumentTag]
- : adlins[n];
- }
- unsigned GetAdlMetaNumber(unsigned midiins)
- {
- return (AdlBank == ~0u) ?
- (midiins | DynamicMetaInstrumentTag)
- : banks[AdlBank][midiins];
- }
- const adldata& GetAdlIns(unsigned short insno)
- {
- return (insno & DynamicInstrumentTag)
- ? dynamic_instruments[insno & ~DynamicInstrumentTag]
- : adl[insno];
- }
-
-public:
- unsigned int NumCards;
- unsigned int AdlBank;
- unsigned int NumFourOps;
- bool HighTremoloMode;
- bool HighVibratoMode;
- bool AdlPercussionMode;
- bool ScaleModulators;
- bool LogarithmicVolumes;
- OPL3() :
- NumCards(1),
- AdlBank(0),
- NumFourOps(0),
- HighTremoloMode(false),
- HighVibratoMode(false),
- AdlPercussionMode(false),
- LogarithmicVolumes(false)
- {}
- std::vector<char> four_op_category; // 1 = quad-master, 2 = quad-slave, 0 = regular
- // 3 = percussion BassDrum
- // 4 = percussion Snare
- // 5 = percussion Tom
- // 6 = percussion Crash cymbal
- // 7 = percussion Hihat
- // 8 = percussion slave
-
- void Poke(unsigned card, unsigned index, unsigned value)
- {
- cards[card].WriteReg(index, value);
- }
- void NoteOff(unsigned c)
- {
- unsigned card = c/23, cc = c%23;
- if(cc >= 18)
+ friend class MIDIplay;
+ uint32_t NumChannels;
+ char ____padding[4];
+ ADL_MIDIPlayer *_parent;
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+ std::vector<DBOPL::Handler> cards;
+#else
+ std::vector<_opl3_chip> cards;
+#endif
+ private:
+ std::vector<unsigned short> ins; // index to adl[], cached, needed by Touch()
+ std::vector<unsigned char> pit; // value poked to B0, cached, needed by NoteOff)(
+ std::vector<unsigned char> regBD;
+
+ std::vector<adlinsdata> dynamic_metainstruments; // Replaces adlins[] when CMF file
+ std::vector<adldata> dynamic_instruments; // Replaces adl[] when CMF file
+ const unsigned DynamicInstrumentTag = 0x8000u, DynamicMetaInstrumentTag = 0x4000000u;
+ const adlinsdata &GetAdlMetaIns(unsigned n)
{
- regBD[card] &= ~(0x10 >> (cc-18));
- Poke(card, 0xBD, regBD[card]);
- return;
+ return (n & DynamicMetaInstrumentTag) ?
+ dynamic_metainstruments[n & ~DynamicMetaInstrumentTag]
+ : adlins[n];
}
- Poke(card, 0xB0 + Channels[cc], pit[c] & 0xDF);
- }
- void NoteOn(unsigned c, double hertz) // Hertz range: 0..131071
- {
- unsigned card = c/23, cc = c%23;
- unsigned x = 0x2000;
- if(hertz < 0 || hertz > 131071) // Avoid infinite loop
- return;
- while(hertz >= 1023.5) { hertz /= 2.0; x += 0x400; } // Calculate octave
- x += (int)(hertz + 0.5);
- unsigned chn = Channels[cc];
- if(cc >= 18)
+ unsigned GetAdlMetaNumber(unsigned midiins)
{
- regBD[card] |= (0x10 >> (cc-18));
- Poke(card, 0x0BD, regBD[card]);
- x &= ~0x2000;
- //x |= 0x800; // for test
+ return (AdlBank == ~0u) ?
+ (midiins | DynamicMetaInstrumentTag)
+ : banks[AdlBank][midiins];
}
- if(chn != 0xFFF)
+ const adldata &GetAdlIns(unsigned short insno)
{
- Poke(card, 0xA0 + chn, x & 0xFF);
- Poke(card, 0xB0 + chn, pit[c] = x >> 8);
+ return (insno & DynamicInstrumentTag)
+ ? dynamic_instruments[insno & ~DynamicInstrumentTag]
+ : adl[insno];
}
- }
- void Touch_Real(unsigned c, unsigned volume)
- {
- if(volume > 63) volume = 63;
- unsigned card = c/23, cc = c%23;
- unsigned i = ins[c];
- unsigned o1 = Operators[cc*2+0];
- unsigned o2 = Operators[cc*2+1];
- const adldata& adli = GetAdlIns(i);
- unsigned x = adli.modulator_40, y = adli.carrier_40;
+ public:
+ unsigned int NumCards;
+ unsigned int AdlBank;
+ unsigned int NumFourOps;
+ bool HighTremoloMode;
+ bool HighVibratoMode;
+ bool AdlPercussionMode;
+ bool ScaleModulators;
+ bool LogarithmicVolumes;
+ OPL3() :
+ NumCards(1),
+ AdlBank(0),
+ NumFourOps(0),
+ HighTremoloMode(false),
+ HighVibratoMode(false),
+ AdlPercussionMode(false),
+ LogarithmicVolumes(false)
+ {}
+ char padding2[7];
+ std::vector<char> four_op_category; // 1 = quad-master, 2 = quad-slave, 0 = regular
+ // 3 = percussion BassDrum
+ // 4 = percussion Snare
+ // 5 = percussion Tom
+ // 6 = percussion Crash cymbal
+ // 7 = percussion Hihat
+ // 8 = percussion slave
+
+ void Poke(size_t card, unsigned index, unsigned value)
+ {
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+ cards[card].WriteReg(index, value);
+#else
+ OPL3_WriteReg(&cards[card], static_cast<Bit16u>(index), static_cast<Bit8u>(value));
+#endif
+ }
+ void PokeN(size_t card, uint16_t index, uint8_t value)
+ {
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+ cards[card].WriteReg(static_cast<Bit32u>(index), static_cast<Bit32u>(value));
+#else
+ OPL3_WriteReg(&cards[card], index, value);
+#endif
+ }
+ void NoteOff(size_t c)
+ {
+ size_t card = c / 23, cc = c % 23;
+
+ if(cc >= 18)
+ {
+ regBD[card] &= ~(0x10 >> (cc - 18));
+ Poke(card, 0xBD, regBD[card]);
+ return;
+ }
- unsigned mode = 1; // 2-op AM
- if(four_op_category[c] == 0 || four_op_category[c] == 3)
+ Poke(card, 0xB0 + Channels[cc], pit[c] & 0xDF);
+ }
+ void NoteOn(unsigned c, double hertz) // Hertz range: 0..131071
{
- mode = adli.feedconn & 1; // 2-op FM or 2-op AM
+ unsigned card = c / 23, cc = c % 23;
+ unsigned x = 0x2000;
+
+ if(hertz < 0 || hertz > 131071) // Avoid infinite loop
+ return;
+
+ while(hertz >= 1023.5)
+ {
+ hertz /= 2.0; // Calculate octave
+ x += 0x400;
+ }
+
+ x += static_cast<unsigned int>(hertz + 0.5);
+ unsigned chn = Channels[cc];
+
+ if(cc >= 18)
+ {
+ regBD[card] |= (0x10 >> (cc - 18));
+ Poke(card, 0x0BD, regBD[card]);
+ x &= ~0x2000u;
+ //x |= 0x800; // for test
+ }
+
+ if(chn != 0xFFF)
+ {
+ Poke(card, 0xA0 + chn, x & 0xFF);
+ Poke(card, 0xB0 + chn, pit[c] = static_cast<uint8_t>(x >> 8));
+ }
}
- else if(four_op_category[c] == 1 || four_op_category[c] == 2)
+ void Touch_Real(unsigned c, unsigned volume)
{
- unsigned i0, i1;
- if ( four_op_category[c] == 1 )
+ if(volume > 63) volume = 63;
+
+ unsigned card = c / 23, cc = c % 23;
+ uint16_t i = ins[c];
+ unsigned o1 = Operators[cc * 2 + 0];
+ unsigned o2 = Operators[cc * 2 + 1];
+ const adldata &adli = GetAdlIns(i);
+ unsigned x = adli.modulator_40, y = adli.carrier_40;
+ unsigned mode = 1; // 2-op AM
+
+ if(four_op_category[c] == 0 || four_op_category[c] == 3)
{
- i0 = i;
- i1 = ins[c + 3];
- mode = 2; // 4-op xx-xx ops 1&2
+ mode = adli.feedconn & 1; // 2-op FM or 2-op AM
+ }
+ else if(four_op_category[c] == 1 || four_op_category[c] == 2)
+ {
+ uint16_t i0, i1;
+
+ if(four_op_category[c] == 1)
+ {
+ i0 = i;
+ i1 = ins[c + 3];
+ mode = 2; // 4-op xx-xx ops 1&2
+ }
+ else
+ {
+ i0 = ins[c - 3];
+ i1 = i;
+ mode = 6; // 4-op xx-xx ops 3&4
+ }
+
+ mode += (GetAdlIns(i0).feedconn & 1) + (GetAdlIns(i1).feedconn & 1) * 2;
}
+
+ static const bool do_ops[10][2] =
+ {
+ { false, true }, /* 2 op FM */
+ { true, true }, /* 2 op AM */
+ { false, false }, /* 4 op FM-FM ops 1&2 */
+ { true, false }, /* 4 op AM-FM ops 1&2 */
+ { false, true }, /* 4 op FM-AM ops 1&2 */
+ { true, false }, /* 4 op AM-AM ops 1&2 */
+ { false, true }, /* 4 op FM-FM ops 3&4 */
+ { false, true }, /* 4 op AM-FM ops 3&4 */
+ { false, true }, /* 4 op FM-AM ops 3&4 */
+ { true, true } /* 4 op AM-AM ops 3&4 */
+ };
+ bool do_modulator = do_ops[ mode ][ 0 ] || ScaleModulators;
+ bool do_carrier = do_ops[ mode ][ 1 ] || ScaleModulators;
+ Poke(card, 0x40 + o1, do_modulator ? (x | 63) - volume + volume * (x & 63) / 63 : x);
+
+ if(o2 != 0xFFF)
+ Poke(card, 0x40 + o2, do_carrier ? (y | 63) - volume + volume * (y & 63) / 63 : y);
+
+ // Correct formula (ST3, AdPlug):
+ // 63-((63-(instrvol))/63)*chanvol
+ // Reduces to (tested identical):
+ // 63 - chanvol + chanvol*instrvol/63
+ // Also (slower, floats):
+ // 63 + chanvol * (instrvol / 63.0 - 1)
+ }
+ void Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127
+ {
+ if(LogarithmicVolumes)
+ Touch_Real(c, volume * 127 / (127 * 127 * 127));
else
{
- i0 = ins[c - 3];
- i1 = i;
- mode = 6; // 4-op xx-xx ops 3&4
+ // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A)
+ Touch_Real(c, volume > 8725 ? static_cast<unsigned int>(std::log(volume) * 11.541561 + (0.5 - 104.22845)) : 0);
+ // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A)
+ //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0);
}
- mode += (GetAdlIns(i0).feedconn & 1) + (GetAdlIns(i1).feedconn & 1) * 2;
}
- static const bool do_ops[10][2] =
- { { false, true }, /* 2 op FM */
- { true, true }, /* 2 op AM */
- { false, false }, /* 4 op FM-FM ops 1&2 */
- { true, false }, /* 4 op AM-FM ops 1&2 */
- { false, true }, /* 4 op FM-AM ops 1&2 */
- { true, false }, /* 4 op AM-AM ops 1&2 */
- { false, true }, /* 4 op FM-FM ops 3&4 */
- { false, true }, /* 4 op AM-FM ops 3&4 */
- { false, true }, /* 4 op FM-AM ops 3&4 */
- { true, true } /* 4 op AM-AM ops 3&4 */
- };
-
- bool do_modulator = do_ops[ mode ][ 0 ] || ScaleModulators;
- bool do_carrier = do_ops[ mode ][ 1 ] || ScaleModulators;
-
- Poke(card, 0x40+o1, do_modulator ? (x|63) - volume + volume*(x&63)/63 : x);
- if(o2 != 0xFFF)
- Poke(card, 0x40+o2, do_carrier ? (y|63) - volume + volume*(y&63)/63 : y);
- // Correct formula (ST3, AdPlug):
- // 63-((63-(instrvol))/63)*chanvol
- // Reduces to (tested identical):
- // 63 - chanvol + chanvol*instrvol/63
- // Also (slower, floats):
- // 63 + chanvol * (instrvol / 63.0 - 1)
- }
- void Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127
- {
- if(LogarithmicVolumes)
+ void Patch(uint16_t c, uint16_t i)
{
- Touch_Real(c, volume*127/(127*127*127));
+ uint16_t card = c / 23, cc = c % 23;
+ static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0};
+ ins[c] = i;
+ uint16_t o1 = Operators[cc * 2 + 0];
+ uint16_t o2 = Operators[cc * 2 + 1];
+ const adldata &adli = GetAdlIns(i);
+ unsigned x = adli.modulator_E862, y = adli.carrier_E862;
+
+ for(unsigned a = 0; a < 4; ++a, x >>= 8, y >>= 8)
+ {
+ Poke(card, data[a] + o1, x & 0xFF);
+
+ if(o2 != 0xFFF)
+ Poke(card, data[a] + o2, y & 0xFF);
+ }
}
- else
+ void Pan(unsigned c, unsigned value)
{
- // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A)
- Touch_Real(c, volume>8725 ? std::log(volume)*11.541561 + (0.5 - 104.22845) : 0);
+ unsigned card = c / 23, cc = c % 23;
- // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A)
- //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0);
+ if(Channels[cc] != 0xFFF)
+ Poke(card, 0xC0 + Channels[cc], GetAdlIns(ins[c]).feedconn | value);
}
- }
- void Patch(unsigned c, unsigned i)
- {
- unsigned card = c/23, cc = c%23;
- static const unsigned char data[4] = {0x20,0x60,0x80,0xE0};
- ins[c] = i;
- unsigned o1 = Operators[cc*2+0];
- unsigned o2 = Operators[cc*2+1];
-
- const adldata& adli = GetAdlIns(i);
- unsigned x = adli.modulator_E862, y = adli.carrier_E862;
- for(unsigned a=0; a<4; ++a, x>>=8, y>>=8)
+ void Silence() // Silence all OPL channels.
{
- Poke(card, data[a]+o1, x&0xFF);
- if(o2 != 0xFFF)
- Poke(card, data[a]+o2, y&0xFF);
+ for(unsigned c = 0; c < NumChannels; ++c)
+ {
+ NoteOff(c);
+ Touch_Real(c, 0);
+ }
}
- }
- void Pan(unsigned c, unsigned value)
- {
- unsigned card = c/23, cc = c%23;
- if(Channels[cc] != 0xFFF)
- Poke(card, 0xC0 + Channels[cc], GetAdlIns(ins[c]).feedconn | value);
- }
- void Silence() // Silence all OPL channels.
- {
- for(unsigned c=0; c<NumChannels; ++c) { NoteOff(c); Touch_Real(c,0); }
- }
- void updateFlags()
- {
- unsigned fours = NumFourOps;
- for(unsigned card=0; card<NumCards; ++card)
+ void updateFlags()
{
- Poke(card, 0x0BD, regBD[card] = (HighTremoloMode*0x80
- + HighVibratoMode*0x40
- + AdlPercussionMode*0x20) );
- unsigned fours_this_card = std::min(fours, 6u);
- Poke(card, 0x104, (1 << fours_this_card) - 1);
- fours -= fours_this_card;
- }
+ unsigned fours = NumFourOps;
- // Mark all channels that are reserved for four-operator function
- if(AdlPercussionMode == 1)
- for(unsigned a=0; a<NumCards; ++a)
+ for(unsigned card = 0; card < NumCards; ++card)
{
- for(unsigned b=0; b<5; ++b) four_op_category[a*23 + 18 + b] = b+3;
- for(unsigned b=0; b<3; ++b) four_op_category[a*23 + 6 + b] = 8;
+ Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80
+ + HighVibratoMode * 0x40
+ + AdlPercussionMode * 0x20));
+ unsigned fours_this_card = std::min(fours, 6u);
+ Poke(card, 0x104, (1 << fours_this_card) - 1);
+ fours -= fours_this_card;
}
- unsigned nextfour = 0;
- for(unsigned a=0; a<NumFourOps; ++a)
- {
- four_op_category[nextfour ] = 1;
- four_op_category[nextfour+3] = 2;
- switch(a % 6)
+ // Mark all channels that are reserved for four-operator function
+ if(AdlPercussionMode == 1)
+ for(unsigned a = 0; a < NumCards; ++a)
+ {
+ for(unsigned b = 0; b < 5; ++b)
+ four_op_category[a * 23 + 18 + b] = static_cast<char>(b + 3);
+
+ for(unsigned b = 0; b < 3; ++b)
+ four_op_category[a * 23 + 6 + b] = 8;
+ }
+
+ unsigned nextfour = 0;
+
+ for(unsigned a = 0; a < NumFourOps; ++a)
{
- case 0: case 1: nextfour += 1; break;
- case 2: nextfour += 9-2; break;
- case 3: case 4: nextfour += 1; break;
- case 5: nextfour += 23-9-2; break;
+ four_op_category[nextfour ] = 1;
+ four_op_category[nextfour + 3] = 2;
+
+ switch(a % 6)
+ {
+ case 0:
+ case 1:
+ nextfour += 1;
+ break;
+
+ case 2:
+ nextfour += 9 - 2;
+ break;
+
+ case 3:
+ case 4:
+ nextfour += 1;
+ break;
+
+ case 5:
+ nextfour += 23 - 9 - 2;
+ break;
+ }
}
}
- }
- void Reset()
- {
- LogarithmicVolumes = false;
- cards.resize(NumCards);
- NumChannels = NumCards * 23;
- ins.resize(NumChannels, 189);
- pit.resize(NumChannels, 0);
- regBD.resize(NumCards);
- four_op_category.resize(NumChannels);
- for(unsigned p=0, a=0; a<NumCards; ++a)
+ void Reset()
{
- for(unsigned b=0; b<18; ++b) four_op_category[p++] = 0;
- for(unsigned b=0; b< 5; ++b) four_op_category[p++] = 8;
- }
+ LogarithmicVolumes = false;
+ cards.resize(NumCards);
+ NumChannels = NumCards * 23;
+ ins.resize(NumChannels, 189);
+ pit.resize(NumChannels, 0);
+ regBD.resize(NumCards);
+ four_op_category.resize(NumChannels);
+
+ for(unsigned p = 0, a = 0; a < NumCards; ++a)
+ {
+ for(unsigned b = 0; b < 18; ++b) four_op_category[p++] = 0;
- static const short data[] =
- { 0x004,96, 0x004,128, // Pulse timer
- 0x105, 0, 0x105,1, 0x105,0, // Pulse OPL3 enable
- 0x001,32, 0x105,1 // Enable wave, OPL3 extensions
- };
- unsigned fours = NumFourOps;
- for(unsigned card=0; card<NumCards; ++card)
- {
- cards[card].Init(_parent->PCM_RATE);
- for(unsigned a=0; a< 18; ++a) Poke(card, 0xB0+Channels[a], 0x00);
- for(unsigned a=0; a< sizeof(data)/sizeof(*data); a+=2)
- Poke(card, data[a], data[a+1]);
- Poke(card, 0x0BD, regBD[card] = (HighTremoloMode*0x80
- + HighVibratoMode*0x40
- + AdlPercussionMode*0x20) );
- unsigned fours_this_card = std::min(fours, 6u);
- Poke(card, 0x104, (1 << fours_this_card) - 1);
- //fprintf(stderr, "Card %u: %u four-ops.\n", card, fours_this_card);
- fours -= fours_this_card;
- }
+ for(unsigned b = 0; b < 5; ++b) four_op_category[p++] = 8;
+ }
- // Mark all channels that are reserved for four-operator function
- if(AdlPercussionMode == 1)
- for(unsigned a=0; a<NumCards; ++a)
+ static const uint16_t data[] =
{
- for(unsigned b=0; b<5; ++b) four_op_category[a*23 + 18 + b] = b+3;
- for(unsigned b=0; b<3; ++b) four_op_category[a*23 + 6 + b] = 8;
- }
+ 0x004, 96, 0x004, 128, // Pulse timer
+ 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable
+ 0x001, 32, 0x105, 1 // Enable wave, OPL3 extensions
+ };
+ unsigned fours = NumFourOps;
- unsigned nextfour = 0;
- for(unsigned a=0; a<NumFourOps; ++a)
- {
- four_op_category[nextfour ] = 1;
- four_op_category[nextfour+3] = 2;
- switch(a % 6)
+ for(unsigned card = 0; card < NumCards; ++card)
{
- case 0: case 1: nextfour += 1; break;
- case 2: nextfour += 9-2; break;
- case 3: case 4: nextfour += 1; break;
- case 5: nextfour += 23-9-2; break;
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+ cards[card].Init(_parent->PCM_RATE);
+#else
+ OPL3_Reset(&cards[card], static_cast<Bit32u>(_parent->PCM_RATE));
+#endif
+
+ for(unsigned a = 0; a < 18; ++a) Poke(card, 0xB0 + Channels[a], 0x00);
+
+ for(unsigned a = 0; a < sizeof(data) / sizeof(*data); a += 2)
+ PokeN(card, data[a], static_cast<uint8_t>(data[a + 1]));
+
+ Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80
+ + HighVibratoMode * 0x40
+ + AdlPercussionMode * 0x20));
+ unsigned fours_this_card = std::min(fours, 6u);
+ Poke(card, 0x104, (1 << fours_this_card) - 1);
+ //fprintf(stderr, "Card %u: %u four-ops.\n", card, fours_this_card);
+ fours -= fours_this_card;
}
- }
- /**/
- /*
- In two-op mode, channels 0..8 go as follows:
- Op1[port] Op2[port]
- Channel 0: 00 00 03 03
- Channel 1: 01 01 04 04
- Channel 2: 02 02 05 05
- Channel 3: 06 08 09 0B
- Channel 4: 07 09 10 0C
- Channel 5: 08 0A 11 0D
- Channel 6: 12 10 15 13
- Channel 7: 13 11 16 14
- Channel 8: 14 12 17 15
- In four-op mode, channels 0..8 go as follows:
- Op1[port] Op2[port] Op3[port] Op4[port]
- Channel 0: 00 00 03 03 06 08 09 0B
- Channel 1: 01 01 04 04 07 09 10 0C
- Channel 2: 02 02 05 05 08 0A 11 0D
- Channel 3: CHANNEL 0 SLAVE
- Channel 4: CHANNEL 1 SLAVE
- Channel 5: CHANNEL 2 SLAVE
- Channel 6: 12 10 15 13
- Channel 7: 13 11 16 14
- Channel 8: 14 12 17 15
- Same goes principally for channels 9-17 respectively.
- */
+ // Mark all channels that are reserved for four-operator function
+ if(AdlPercussionMode == 1)
+ for(unsigned a = 0; a < NumCards; ++a)
+ {
+ for(unsigned b = 0; b < 5; ++b)
+ four_op_category[a * 23 + 18 + b] = static_cast<char>(b + 3);
- Silence();
- }
+ for(unsigned b = 0; b < 3; ++b)
+ four_op_category[a * 23 + 6 + b] = 8;
+ }
+
+ unsigned nextfour = 0;
+
+ for(unsigned a = 0; a < NumFourOps; ++a)
+ {
+ four_op_category[nextfour ] = 1;
+ four_op_category[nextfour + 3] = 2;
+
+ switch(a % 6)
+ {
+ case 0:
+ case 1:
+ nextfour += 1;
+ break;
+
+ case 2:
+ nextfour += 9 - 2;
+ break;
+
+ case 3:
+ case 4:
+ nextfour += 1;
+ break;
+
+ case 5:
+ nextfour += 23 - 9 - 2;
+ break;
+ }
+ }
+
+ /**/
+ /*
+ In two-op mode, channels 0..8 go as follows:
+ Op1[port] Op2[port]
+ Channel 0: 00 00 03 03
+ Channel 1: 01 01 04 04
+ Channel 2: 02 02 05 05
+ Channel 3: 06 08 09 0B
+ Channel 4: 07 09 10 0C
+ Channel 5: 08 0A 11 0D
+ Channel 6: 12 10 15 13
+ Channel 7: 13 11 16 14
+ Channel 8: 14 12 17 15
+ In four-op mode, channels 0..8 go as follows:
+ Op1[port] Op2[port] Op3[port] Op4[port]
+ Channel 0: 00 00 03 03 06 08 09 0B
+ Channel 1: 01 01 04 04 07 09 10 0C
+ Channel 2: 02 02 05 05 08 0A 11 0D
+ Channel 3: CHANNEL 0 SLAVE
+ Channel 4: CHANNEL 1 SLAVE
+ Channel 5: CHANNEL 2 SLAVE
+ Channel 6: 12 10 15 13
+ Channel 7: 13 11 16 14
+ Channel 8: 14 12 17 15
+ Same goes principally for channels 9-17 respectively.
+ */
+ Silence();
+ }
};
//static const char MIDIsymbols[256+1] =
@@ -467,911 +557,1048 @@ public:
//"????????????????" // Prc 96-111
//"????????????????"; // Prc 112-127
-static const char PercussionMap[256] =
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GM
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 3 = bass drum
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 4 = snare
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 5 = tom
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 6 = cymbal
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 7 = hihat
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GP0
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GP16
-//2 3 4 5 6 7 8 940 1 2 3 4 5 6 7
-"\0\0\0\3\3\7\4\7\4\5\7\5\7\5\7\5"//GP32
-//8 950 1 2 3 4 5 6 7 8 960 1 2 3
-"\5\6\5\6\6\0\5\6\0\6\0\6\5\5\5\5"//GP48
-//4 5 6 7 8 970 1 2 3 4 5 6 7 8 9
-"\5\0\0\0\0\0\7\0\0\0\0\0\0\0\0\0"//GP64
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+static const uint8_t PercussionMap[256] =
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GM
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 3 = bass drum
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 4 = snare
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 5 = tom
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 6 = cymbal
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 7 = hihat
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GP0
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GP16
+ //2 3 4 5 6 7 8 940 1 2 3 4 5 6 7
+ "\0\0\0\3\3\7\4\7\4\5\7\5\7\5\7\5"//GP32
+ //8 950 1 2 3 4 5 6 7 8 960 1 2 3
+ "\5\6\5\6\6\0\5\6\0\6\0\6\5\5\5\5"//GP48
+ //4 5 6 7 8 970 1 2 3 4 5 6 7 8 9
+ "\5\0\0\0\0\0\7\0\0\0\0\0\0\0\0\0"//GP64
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
class MIDIplay
{
- // Information about each track
- struct Position
- {
- bool began;
- double wait;
- struct TrackInfo
- {
- size_t ptr;
- long delay;
- int status;
-
- TrackInfo(): ptr(0), delay(0), status(0) { }
- };
- std::vector<TrackInfo> track;
-
- Position(): began(false), wait(0.0), track() { }
- } CurrentPosition, LoopBeginPosition, trackBeginPosition;
-
- std::map<std::string, unsigned> devices;
- std::map<unsigned/*track*/, unsigned/*channel begin index*/> current_device;
-
- // Persistent settings for each MIDI channel
- struct MIDIchannel
- {
- unsigned short portamento;
- unsigned char bank_lsb, bank_msb;
- unsigned char patch;
- unsigned char volume, expression;
- unsigned char panning, vibrato, sustain;
- double bend, bendsense;
- double vibpos, vibspeed, vibdepth;
- long vibdelay;
- unsigned char lastlrpn,lastmrpn; bool nrpn;
- struct NoteInfo
- {
- // Current pressure
- unsigned char vol;
- // Tone selected on noteon:
- short tone;
- // Patch selected on noteon; index to banks[AdlBank][]
- unsigned char midiins;
- // Index to physical adlib data structure, adlins[]
- unsigned short insmeta;
- // List of adlib channels it is currently occupying.
- std::map<unsigned short/*adlchn*/,
- unsigned short/*ins, inde to adl[]*/
- > phys;
- };
- typedef std::map<unsigned char,NoteInfo> activenotemap_t;
- typedef activenotemap_t::iterator activenoteiterator;
- activenotemap_t activenotes;
-
- MIDIchannel()
- : portamento(0),
- bank_lsb(0), bank_msb(0), patch(0),
- volume(100),expression(100),
- panning(0x30), vibrato(0), sustain(0),
- bend(0.0), bendsense(2 / 8192.0),
- vibpos(0), vibspeed(2*3.141592653*5.0),
- vibdepth(0.5/127), vibdelay(0),
- lastlrpn(0),lastmrpn(0),nrpn(false),
- activenotes() { }
- };
- std::vector<MIDIchannel> Ch;
- bool cmf_percussion_mode = false;
-
- // Additional information about AdLib channels
- struct AdlChannel
- {
- // For collisions
- struct Location
+ // Information about each track
+ struct Position
{
- unsigned short MidCh;
- unsigned char note;
- bool operator==(const Location&b) const
- { return MidCh==b.MidCh && note==b.note; }
- bool operator< (const Location&b) const
- { return MidCh<b.MidCh || (MidCh==b.MidCh&& note<b.note); }
- };
- struct LocationData
+ bool began;
+ char padding[7];
+ double wait;
+ struct TrackInfo
+ {
+ size_t ptr;
+ long delay;
+ int status;
+ char padding2[4];
+ TrackInfo(): ptr(0), delay(0), status(0) {}
+ };
+ std::vector<TrackInfo> track;
+
+ Position(): began(false), wait(0.0), track() { }
+ } CurrentPosition, LoopBeginPosition, trackBeginPosition;
+
+ std::map<std::string, uint64_t> devices;
+ std::map<uint64_t /*track*/, uint64_t /*channel begin index*/> current_device;
+
+ // Persistent settings for each MIDI channel
+ struct MIDIchannel
{
- bool sustained;
- unsigned short ins; // a copy of that in phys[]
- long kon_time_until_neglible;
- long vibdelay;
+ uint16_t portamento;
+ uint8_t bank_lsb, bank_msb;
+ uint8_t patch;
+ uint8_t volume, expression;
+ uint8_t panning, vibrato, sustain;
+ char ____padding[6];
+ double bend, bendsense;
+ double vibpos, vibspeed, vibdepth;
+ int64_t vibdelay;
+ uint8_t lastlrpn, lastmrpn;
+ bool nrpn;
+ struct NoteInfo
+ {
+ // Current pressure
+ uint8_t vol;
+ // Tone selected on noteon:
+ char ____padding[1];
+ int16_t tone;
+ // Patch selected on noteon; index to banks[AdlBank][]
+ uint8_t midiins;
+ // Index to physical adlib data structure, adlins[]
+ char ____padding2[3];
+ uint32_t insmeta;
+ char ____padding3[4];
+ // List of adlib channels it is currently occupying.
+ std::map<uint16_t /*adlchn*/, uint16_t /*ins, inde to adl[]*/ > phys;
+ };
+ typedef std::map<uint8_t, NoteInfo> activenotemap_t;
+ typedef activenotemap_t::iterator activenoteiterator;
+ char ____padding2[5];
+ activenotemap_t activenotes;
+
+ MIDIchannel()
+ : portamento(0),
+ bank_lsb(0), bank_msb(0), patch(0),
+ volume(100), expression(100),
+ panning(0x30), vibrato(0), sustain(0),
+ bend(0.0), bendsense(2 / 8192.0),
+ vibpos(0), vibspeed(2 * 3.141592653 * 5.0),
+ vibdepth(0.5 / 127), vibdelay(0),
+ lastlrpn(0), lastmrpn(0), nrpn(false),
+ activenotes() { }
};
- typedef std::map<Location, LocationData> users_t;
- users_t users;
-
- // If the channel is keyoff'd
- long koff_time_until_neglible;
- // For channel allocation:
- AdlChannel(): users(), koff_time_until_neglible(0) { }
+ std::vector<MIDIchannel> Ch;
+ bool cmf_percussion_mode = false;
- void AddAge(long ms)
+ // Additional information about AdLib channels
+ struct AdlChannel
{
- if(users.empty())
- koff_time_until_neglible =
- std::max(koff_time_until_neglible-ms, -0x1FFFFFFFl);
- else
+ // For collisions
+ struct Location
{
- koff_time_until_neglible = 0;
- for(users_t::iterator i = users.begin(); i != users.end(); ++i)
+ uint16_t MidCh;
+ uint8_t note;
+ bool operator==(const Location &b) const
{
- i->second.kon_time_until_neglible =
- std::max(i->second.kon_time_until_neglible-ms, -0x1FFFFFFFl);
- i->second.vibdelay += ms;
+ return MidCh == b.MidCh && note == b.note;
+ }
+ bool operator< (const Location &b) const
+ {
+ return MidCh < b.MidCh || (MidCh == b.MidCh && note < b.note);
+ }
+ char ____padding[1];
+ };
+ struct LocationData
+ {
+ bool sustained;
+ char ____padding[1];
+ uint16_t ins; // a copy of that in phys[]
+ char ____padding2[4];
+ int64_t kon_time_until_neglible;
+ int64_t vibdelay;
+ };
+ typedef std::map<Location, LocationData> users_t;
+ users_t users;
+
+ // If the channel is keyoff'd
+ long koff_time_until_neglible;
+ // For channel allocation:
+ AdlChannel(): users(), koff_time_until_neglible(0) { }
+
+ void AddAge(int64_t ms)
+ {
+ if(users.empty())
+ koff_time_until_neglible =
+ std::max(koff_time_until_neglible - ms, static_cast<int64_t>(-0x1FFFFFFFl));
+ else
+ {
+ koff_time_until_neglible = 0;
+
+ for(users_t::iterator i = users.begin(); i != users.end(); ++i)
+ {
+ i->second.kon_time_until_neglible =
+ std::max(i->second.kon_time_until_neglible - ms, static_cast<int64_t>(-0x1FFFFFFFl));
+ i->second.vibdelay += ms;
+ }
}
}
- }
- };
- std::vector<AdlChannel> ch;
-
- std::vector< std::vector<unsigned char> > TrackData;
-public:
- MIDIplay():
- cmf_percussion_mode(false),
- config(NULL),
- trackStart(false),
- loopStart(false),
- loopEnd(false),
- loopStart_passed(false),
- invalidLoop(false),
- loopStart_hit(false)
- {
- devices.clear();
- }
- ~MIDIplay()
- {}
-
- ADL_MIDIPlayer* config;
- std::string musTitle;
- fraction<long> InvDeltaTicks, Tempo;
- bool trackStart,
- loopStart,
- loopEnd,
- loopStart_passed /*Tells that "loopStart" already passed*/,
- invalidLoop /*Loop points are invalid (loopStart after loopEnd or loopStart and loopEnd are on same place)*/,
- loopStart_hit /*loopStart entry was hited in previous tick*/;
- OPL3 opl;
-public:
- static unsigned long ReadBEint(const void* buffer, unsigned nbytes)
- {
- unsigned long result=0;
- const unsigned char* data = (const unsigned char*) buffer;
- for(unsigned n=0; n<nbytes; ++n)
- result = (result << 8) + data[n];
- return result;
- }
- static unsigned long ReadLEint(const void* buffer, unsigned nbytes)
- {
- unsigned long result=0;
- const unsigned char* data = (const unsigned char*) buffer;
- for(unsigned n=0; n<nbytes; ++n)
- result = result + (data[n] << (n*8));
- return result;
- }
- unsigned long ReadVarLen(unsigned tk)
- {
- unsigned long result = 0;
- for(;;)
+ };
+ public:
+ char ____padding[7];
+ private:
+ std::vector<AdlChannel> ch;
+
+ std::vector<std::vector<uint8_t>> TrackData;
+ public:
+ MIDIplay():
+ cmf_percussion_mode(false),
+ config(NULL),
+ trackStart(false),
+ loopStart(false),
+ loopEnd(false),
+ loopStart_passed(false),
+ invalidLoop(false),
+ loopStart_hit(false)
{
- unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++];
- result = (result << 7) + (byte & 0x7F);
- if(!(byte & 0x80)) break;
+ devices.clear();
}
- return result;
- }
-
- /*
- * A little class gives able to read filedata from disk and also from a memory segment
- */
- class fileReader
- {
+ ~MIDIplay()
+ {}
+
+ ADL_MIDIPlayer *config;
+ std::string musTitle;
+ fraction<long> InvDeltaTicks, Tempo;
+ bool trackStart,
+ loopStart,
+ loopEnd,
+ loopStart_passed /*Tells that "loopStart" already passed*/,
+ invalidLoop /*Loop points are invalid (loopStart after loopEnd or loopStart and loopEnd are on same place)*/,
+ loopStart_hit /*loopStart entry was hited in previous tick*/;
+ char ____padding2[2];
+ OPL3 opl;
public:
- enum relTo
+ static uint64_t ReadBEint(const void *buffer, size_t nbytes)
{
- SET=0,
- CUR=1,
- END=2
- };
+ uint64_t result = 0;
+ const unsigned char *data = reinterpret_cast<const unsigned char *>(buffer);
- fileReader()
- {
- fp=NULL;
- mp=NULL;
- mp_size=0;
- mp_tell=0;
+ for(unsigned n = 0; n < nbytes; ++n)
+ result = (result << 8) + data[n];
+
+ return result;
}
- ~fileReader()
+ static uint64_t ReadLEint(const void *buffer, size_t nbytes)
{
- close();
- }
+ uint64_t result = 0;
+ const unsigned char *data = reinterpret_cast<const unsigned char *>(buffer);
- void openFile(const char *path)
- {
- fp = std::fopen(path, "rb");
- _fileName=path;
- mp=NULL;
- mp_size=0;
- mp_tell=0;
- }
+ for(unsigned n = 0; n < nbytes; ++n)
+ result = result + static_cast<uint64_t>(data[n] << (n * 8));
- void openData(void* mem, unsigned long lenght)
- {
- fp=NULL;
- mp=mem;
- mp_size=lenght;
- mp_tell=0;
+ return result;
}
-
- void seek(long pos, int rel_to)
+ uint64_t ReadVarLen(size_t tk)
{
- if(fp)
- std::fseek(fp, pos, rel_to);
- else
+ uint64_t result = 0;
+
+ for(;;)
{
- switch(rel_to)
- {
- case SET: mp_tell = pos; break;
- case END: mp_tell = mp_size - pos; break;
- case CUR: mp_tell = mp_tell + pos; break;
- }
- if(mp_tell > mp_size)
- mp_tell = mp_size;
+ unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ result = (result << 7) + (byte & 0x7F);
+
+ if(!(byte & 0x80)) break;
}
+
+ return result;
}
- long read(void *buf, long num, long size)
+ /*
+ * A little class gives able to read filedata from disk and also from a memory segment
+ */
+ class fileReader
{
- if(fp)
- return std::fread(buf, num, size, fp);
- else
- {
- int pos=0;
- int maxSize = (size*num);
- while( (pos < maxSize) && (mp_tell < mp_size) )
+ public:
+ enum relTo
+ {
+ SET = 0,
+ CUR = 1,
+ END = 2
+ };
+
+ fileReader()
{
- ((unsigned char*)buf)[pos]=((unsigned char*)mp)[mp_tell];
- mp_tell++;
- pos++;
+ fp = NULL;
+ mp = NULL;
+ mp_size = 0;
+ mp_tell = 0;
+ }
+ ~fileReader()
+ {
+ close();
}
- return pos;
- }
- }
- int getc()
- {
- if(fp)
- return std::getc(fp);
- else
- {
- if(mp_tell>=mp_size)
+ void openFile(const char *path)
{
- return -1;
+ fp = std::fopen(path, "rb");
+ _fileName = path;
+ mp = NULL;
+ mp_size = 0;
+ mp_tell = 0;
}
- int x=((unsigned char*)mp)[mp_tell]; mp_tell++;
- return x;
- }
- }
- unsigned long tell()
- {
- if(fp)
- return std::ftell(fp);
- else
- return mp_tell;
- }
+ void openData(void *mem, size_t lenght)
+ {
+ fp = NULL;
+ mp = mem;
+ mp_size = lenght;
+ mp_tell = 0;
+ }
- void close()
- {
- if(fp) std::fclose(fp);
- fp=NULL;
- mp=NULL;
- mp_size=0;
- mp_tell=0;
- }
+ void seek(long pos, int rel_to)
+ {
+ if(fp)
+ std::fseek(fp, pos, rel_to);
+ else
+ {
+ switch(rel_to)
+ {
+ case SET:
+ mp_tell = static_cast<size_t>(pos);
+ break;
- bool isValid()
- {
- return (fp)||(mp);
- }
+ case END:
+ mp_tell = mp_size - static_cast<size_t>(pos);
+ break;
- bool eof()
- {
- return mp_tell>=mp_size;
- }
- std::string _fileName;
- std::FILE* fp;
- void* mp;
- unsigned long mp_size;
- unsigned long mp_tell;
- };
-
- bool LoadMIDI(const std::string& filename)
- {
- fileReader file;
- file.openFile(filename.c_str());
- if(!LoadMIDI(file))
- {
- std::perror(filename.c_str());
- return false;
- }
- return true;
- }
+ case CUR:
+ mp_tell = mp_tell + static_cast<size_t>(pos);
+ break;
+ }
- bool LoadMIDI(void *data, unsigned long size)
- {
- fileReader file;
- file.openData(data, size);
- return LoadMIDI(file);
- }
+ if(mp_tell > mp_size)
+ mp_tell = mp_size;
+ }
+ }
- bool LoadMIDI(fileReader &fr)
- {
- #define qqq(x) (void)x
- int fsize;
- qqq(fsize);
- //std::FILE* fr = std::fopen(filename.c_str(), "rb");
- if(!fr.isValid()) { ADLMIDI_ErrorString = "Invalid data stream!"; return false; }
- const unsigned HeaderSize = 4+4+2+2+2; // 14
- char HeaderBuf[HeaderSize]="";
- riffskip:;
- fsize=fr.read(HeaderBuf, 1, HeaderSize);
- if(std::memcmp(HeaderBuf, "RIFF", 4) == 0)
- { fr.seek(6, SEEK_CUR); goto riffskip; }
- size_t DeltaTicks=192, TrackCount=1;
-
- config->stored_samples = 0;
- config->backup_samples_size = 0;
- opl.AdlPercussionMode = config->AdlPercussionMode;
- opl.HighTremoloMode = config->HighTremoloMode;
- opl.HighVibratoMode = config->HighVibratoMode;
- opl.ScaleModulators = config->ScaleModulators;
- opl.LogarithmicVolumes = config->LogarithmicVolumes;
- opl.NumCards = config->NumCards;
- opl.NumFourOps = config->NumFourOps;
- cmf_percussion_mode = false;
-
- opl.Reset();
-
- trackStart = true;
- loopStart = true;
- loopStart_passed = false;
- invalidLoop = false;
- loopStart_hit = false;
-
- bool is_GMF = false; // GMD/MUS files (ScummVM)
- bool is_MUS = false; // MUS/DMX files (Doom)
- bool is_IMF = false; // IMF
- bool is_CMF = false; // Creative Music format (CMF/CTMF)
- //std::vector<unsigned char> MUS_instrumentList;
-
- if(std::memcmp(HeaderBuf, "GMF\x1", 4) == 0)
- {
- // GMD/MUS files (ScummVM)
- fr.seek(7-(HeaderSize), SEEK_CUR);
- is_GMF = true;
- }
- else if(std::memcmp(HeaderBuf, "MUS\x1A", 4) == 0)
- {
- // MUS/DMX files (Doom)
- unsigned start = ReadLEint(HeaderBuf+6, 2);
- is_MUS = true;
- fr.seek(start, SEEK_SET);
- }
- else if(std::memcmp(HeaderBuf, "CTMF", 4) == 0)
- {
- opl.dynamic_instruments.clear();
- opl.dynamic_metainstruments.clear();
- // Creative Music Format (CMF).
- // When playing CTMF files, use the following commandline:
- // adlmidi song8.ctmf -p -v 1 1 0
- // i.e. enable percussion mode, deeper vibrato, and use only 1 card.
-
- is_CMF = true;
- //unsigned version = ReadLEint(HeaderBuf+4, 2);
- unsigned ins_start = ReadLEint(HeaderBuf+6, 2);
- unsigned mus_start = ReadLEint(HeaderBuf+8, 2);
- //unsigned deltas = ReadLEint(HeaderBuf+10, 2);
- unsigned ticks = ReadLEint(HeaderBuf+12, 2);
- // Read title, author, remarks start offsets in file
- fr.read(HeaderBuf, 1, 6);
- //unsigned long notes_starts[3] = {ReadLEint(HeaderBuf+0,2),ReadLEint(HeaderBuf+0,4),ReadLEint(HeaderBuf+0,6)};
- fr.seek(16, SEEK_CUR); // Skip the channels-in-use table
- fr.read(HeaderBuf, 1, 4);
- unsigned ins_count = ReadLEint(HeaderBuf+0, 2);//, basictempo = ReadLEint(HeaderBuf+2, 2);
- fr.seek(ins_start, SEEK_SET);
- //std::printf("%u instruments\n", ins_count);
- for(unsigned i=0; i<ins_count; ++i)
- {
- unsigned char InsData[16];
- fr.read(InsData, 1, 16);
- /*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
- i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7],
- InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/
- struct adldata adl;
- struct adlinsdata adlins;
- adl.modulator_E862 =
- (((unsigned int)(InsData[8]&0x07) << 24)&0xFF000000)//WaveForm
- | (((unsigned int)(InsData[6]) << 16)&0x00FF0000)//Sustain/Release
- | (((unsigned int)(InsData[4]) << 8)&0x0000FF00)//Attack/Decay
- | (((unsigned int)(InsData[0]) << 0)&0x000000FF);//MultKEVA
- adl.carrier_E862 =
- (((unsigned int)(InsData[9]&0x07) << 24)&0xFF000000)//WaveForm
- | (((unsigned int)(InsData[7]) << 16)&0x00FF0000)//Sustain/Release
- | (((unsigned int)(InsData[5]) << 8)&0x0000FF00)//Attack/Decay
- | (((unsigned int)(InsData[1]) << 0)&0x000000FF);//MultKEVA
- adl.modulator_40 = InsData[2];
- adl.carrier_40 = InsData[3];
- adl.feedconn = InsData[10]&0x0F;
- adl.finetune = 0;
-
- adlins.adlno1 = opl.dynamic_instruments.size() | opl.DynamicInstrumentTag;
- adlins.adlno2 = adlins.adlno1;
- adlins.ms_sound_kon = 1000;
- adlins.ms_sound_koff = 500;
- adlins.tone = 0;
- adlins.flags = 0;
- adlins.fine_tune = 0.0;
- opl.dynamic_metainstruments.push_back(adlins);
- opl.dynamic_instruments.push_back(adl);
- }
- fr.seek(mus_start, SEEK_SET);
- TrackCount = 1;
- DeltaTicks = ticks;
- opl.AdlBank = ~0u; // Ignore AdlBank number, use dynamic banks instead
- //std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo);
- opl.LogarithmicVolumes = true;
- opl.AdlPercussionMode = true;
- }
- else
- {
- // Try parsing as an IMF file
- {
- unsigned end = (unsigned char)HeaderBuf[0] + 256*(unsigned char)HeaderBuf[1];
- if(!end || (end & 3)) goto not_imf;
+ inline void seeku(unsigned long pos, int rel_to)
+ {
+ seek(static_cast<long>(pos), rel_to);
+ }
+
+ size_t read(void *buf, size_t num, size_t size)
+ {
+ if(fp)
+ return std::fread(buf, num, size, fp);
+ else
+ {
+ size_t pos = 0;
+ size_t maxSize = static_cast<size_t>(size * num);
+
+ while((pos < maxSize) && (mp_tell < mp_size))
+ {
+ reinterpret_cast<unsigned char *>(buf)[pos] = reinterpret_cast<unsigned char *>(mp)[mp_tell];
+ mp_tell++;
+ pos++;
+ }
- long backup_pos = fr.tell();
- unsigned sum1 = 0, sum2 = 0;
- fr.seek(2, SEEK_SET);
- for(unsigned n=0; n<42; ++n)
+ return pos;
+ }
+ }
+
+ int getc()
{
- unsigned value1 = fr.getc(); value1 += fr.getc() << 8; sum1 += value1;
- unsigned value2 = fr.getc(); value2 += fr.getc() << 8; sum2 += value2;
+ if(fp)
+ return std::getc(fp);
+ else
+ {
+ if(mp_tell >= mp_size)
+ return -1;
+
+ int x = reinterpret_cast<unsigned char *>(mp)[mp_tell];
+ mp_tell++;
+ return x;
+ }
}
- fr.seek(backup_pos, SEEK_SET);
- if(sum1 > sum2)
+
+ size_t tell()
{
- is_IMF = true;
- DeltaTicks = 1;
+ if(fp)
+ return static_cast<size_t>(std::ftell(fp));
+ else
+ return mp_tell;
}
- }
- if(!is_IMF)
- {
- not_imf:
- if(std::memcmp(HeaderBuf, "MThd\0\0\0\6", 8) != 0)
- { InvFmt:
- fr.close();
- ADLMIDI_ErrorString=fr._fileName+": Invalid format\n";
- return false;
+ void close()
+ {
+ if(fp) std::fclose(fp);
+
+ fp = NULL;
+ mp = NULL;
+ mp_size = 0;
+ mp_tell = 0;
+ }
+
+ bool isValid()
+ {
+ return (fp) || (mp);
+ }
+
+ bool eof()
+ {
+ return mp_tell >= mp_size;
}
- /*size_t Fmt =*/ ReadBEint(HeaderBuf+8, 2);
- TrackCount = ReadBEint(HeaderBuf+10, 2);
- DeltaTicks = ReadBEint(HeaderBuf+12, 2);
+ std::string _fileName;
+ std::FILE *fp;
+ void *mp;
+ size_t mp_size;
+ size_t mp_tell;
+ };
+
+ bool LoadMIDI(const std::string &filename)
+ {
+ fileReader file;
+ file.openFile(filename.c_str());
+
+ if(!LoadMIDI(file))
+ {
+ std::perror(filename.c_str());
+ return false;
}
+
+ return true;
}
- TrackData.resize(TrackCount);
- CurrentPosition.track.resize(TrackCount);
- InvDeltaTicks = fraction<long>(1, 1000000l * DeltaTicks);
- //Tempo = 1000000l * InvDeltaTicks;
- Tempo = fraction<long>(1, DeltaTicks);
- static const unsigned char EndTag[4] = {0xFF,0x2F,0x00,0x00};
+ bool LoadMIDI(void *data, unsigned long size)
+ {
+ fileReader file;
+ file.openData(data, size);
+ return LoadMIDI(file);
+ }
- int totalGotten=0;
- for(size_t tk = 0; tk < TrackCount; ++tk)
+ bool LoadMIDI(fileReader &fr)
{
- // Read track header
- size_t TrackLength;
- if(is_IMF)
+#define qqq(x) (void)x
+ size_t fsize;
+ qqq(fsize);
+
+ //std::FILE* fr = std::fopen(filename.c_str(), "rb");
+ if(!fr.isValid())
+ {
+ ADLMIDI_ErrorString = "Invalid data stream!";
+ return false;
+ }
+
+ const size_t HeaderSize = 4 + 4 + 2 + 2 + 2; // 14
+ char HeaderBuf[HeaderSize] = "";
+riffskip:
+ ;
+ fsize = fr.read(HeaderBuf, 1, HeaderSize);
+
+ if(std::memcmp(HeaderBuf, "RIFF", 4) == 0)
+ {
+ fr.seek(6l, SEEK_CUR);
+ goto riffskip;
+ }
+
+ size_t DeltaTicks = 192, TrackCount = 1;
+ config->stored_samples = 0;
+ config->backup_samples_size = 0;
+ opl.AdlPercussionMode = config->AdlPercussionMode;
+ opl.HighTremoloMode = config->HighTremoloMode;
+ opl.HighVibratoMode = config->HighVibratoMode;
+ opl.ScaleModulators = config->ScaleModulators;
+ opl.LogarithmicVolumes = config->LogarithmicVolumes;
+ opl.NumCards = config->NumCards;
+ opl.NumFourOps = config->NumFourOps;
+ cmf_percussion_mode = false;
+ opl.Reset();
+ trackStart = true;
+ loopStart = true;
+ loopStart_passed = false;
+ invalidLoop = false;
+ loopStart_hit = false;
+ bool is_GMF = false; // GMD/MUS files (ScummVM)
+ bool is_MUS = false; // MUS/DMX files (Doom)
+ bool is_IMF = false; // IMF
+ bool is_CMF = false; // Creative Music format (CMF/CTMF)
+ //std::vector<unsigned char> MUS_instrumentList;
+
+ if(std::memcmp(HeaderBuf, "GMF\x1", 4) == 0)
{
- //std::fprintf(stderr, "Reading IMF file...\n");
- long end = (unsigned char)HeaderBuf[0] + 256*(unsigned char)HeaderBuf[1];
-
- unsigned IMF_tempo = 1428;
- static const unsigned char imf_tempo[] = {0xFF,0x51,0x4,
- (unsigned char)(IMF_tempo>>24),
- (unsigned char)(IMF_tempo>>16),
- (unsigned char)(IMF_tempo>>8),
- (unsigned char)(IMF_tempo)};
- TrackData[tk].insert(TrackData[tk].end(), imf_tempo, imf_tempo + sizeof(imf_tempo));
- TrackData[tk].push_back(0x00);
-
- fr.seek(2, SEEK_SET);
- while(fr.tell() < (unsigned)end)
+ // GMD/MUS files (ScummVM)
+ fr.seek(7 - static_cast<long>(HeaderSize), SEEK_CUR);
+ is_GMF = true;
+ }
+ else if(std::memcmp(HeaderBuf, "MUS\x1A", 4) == 0)
+ {
+ // MUS/DMX files (Doom)
+ uint64_t start = ReadLEint(HeaderBuf + 6, 2);
+ is_MUS = true;
+ fr.seek(static_cast<long>(start), SEEK_SET);
+ }
+ else if(std::memcmp(HeaderBuf, "CTMF", 4) == 0)
+ {
+ opl.dynamic_instruments.clear();
+ opl.dynamic_metainstruments.clear();
+ // Creative Music Format (CMF).
+ // When playing CTMF files, use the following commandline:
+ // adlmidi song8.ctmf -p -v 1 1 0
+ // i.e. enable percussion mode, deeper vibrato, and use only 1 card.
+ is_CMF = true;
+ //unsigned version = ReadLEint(HeaderBuf+4, 2);
+ uint64_t ins_start = ReadLEint(HeaderBuf + 6, 2);
+ uint64_t mus_start = ReadLEint(HeaderBuf + 8, 2);
+ //unsigned deltas = ReadLEint(HeaderBuf+10, 2);
+ uint64_t ticks = ReadLEint(HeaderBuf + 12, 2);
+ // Read title, author, remarks start offsets in file
+ fr.read(HeaderBuf, 1, 6);
+ //unsigned long notes_starts[3] = {ReadLEint(HeaderBuf+0,2),ReadLEint(HeaderBuf+0,4),ReadLEint(HeaderBuf+0,6)};
+ fr.seek(16, SEEK_CUR); // Skip the channels-in-use table
+ fr.read(HeaderBuf, 1, 4);
+ uint64_t ins_count = ReadLEint(HeaderBuf + 0, 2); //, basictempo = ReadLEint(HeaderBuf+2, 2);
+ fr.seek(static_cast<long>(ins_start), SEEK_SET);
+
+ //std::printf("%u instruments\n", ins_count);
+ for(unsigned i = 0; i < ins_count; ++i)
{
- unsigned char special_event_buf[5];
- special_event_buf[0] = 0xFF;
- special_event_buf[1] = 0xE3;
- special_event_buf[2] = 0x02;
- special_event_buf[3] = fr.getc(); // port index
- special_event_buf[4] = fr.getc(); // port value
- unsigned delay = fr.getc(); delay += 256 * fr.getc();
- totalGotten += 4;
-
- //if(special_event_buf[3] <= 8) continue;
-
- //fprintf(stderr, "Put %02X <- %02X, plus %04X delay\n", special_event_buf[3],special_event_buf[4], delay);
-
- TrackData[tk].insert(TrackData[tk].end(), special_event_buf, special_event_buf+5);
- //if(delay>>21) TrackData[tk].push_back( 0x80 | ((delay>>21) & 0x7F ) );
- if(delay>>14) TrackData[tk].push_back( 0x80 | ((delay>>14) & 0x7F ) );
- if(delay>> 7) TrackData[tk].push_back( 0x80 | ((delay>> 7) & 0x7F ) );
- TrackData[tk].push_back( ((delay>>0) & 0x7F ) );
+ unsigned char InsData[16];
+ fr.read(InsData, 1, 16);
+ /*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7],
+ InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/
+ struct adldata adl;
+ struct adlinsdata adlins;
+ adl.modulator_E862 =
+ ((static_cast<uint32_t>(InsData[8] & 0x07) << 24) & 0xFF000000) //WaveForm
+ | ((static_cast<uint32_t>(InsData[6]) << 16) & 0x00FF0000) //Sustain/Release
+ | ((static_cast<uint32_t>(InsData[4]) << 8) & 0x0000FF00) //Attack/Decay
+ | ((static_cast<uint32_t>(InsData[0]) << 0) & 0x000000FF); //MultKEVA
+ adl.carrier_E862 =
+ ((static_cast<uint32_t>(InsData[9] & 0x07) << 24) & 0xFF000000) //WaveForm
+ | ((static_cast<uint32_t>(InsData[7]) << 16) & 0x00FF0000) //Sustain/Release
+ | ((static_cast<uint32_t>(InsData[5]) << 8) & 0x0000FF00) //Attack/Decay
+ | ((static_cast<uint32_t>(InsData[1]) << 0) & 0x000000FF); //MultKEVA
+ adl.modulator_40 = InsData[2];
+ adl.carrier_40 = InsData[3];
+ adl.feedconn = InsData[10] & 0x0F;
+ adl.finetune = 0;
+ adlins.adlno1 = static_cast<uint16_t>(opl.dynamic_instruments.size() | opl.DynamicInstrumentTag);
+ adlins.adlno2 = adlins.adlno1;
+ adlins.ms_sound_kon = 1000;
+ adlins.ms_sound_koff = 500;
+ adlins.tone = 0;
+ adlins.flags = 0;
+ adlins.fine_tune = 0.0;
+ opl.dynamic_metainstruments.push_back(adlins);
+ opl.dynamic_instruments.push_back(adl);
}
- TrackData[tk].insert(TrackData[tk].end(), EndTag+0, EndTag+4);
- CurrentPosition.track[tk].delay = 0;
- CurrentPosition.began = true;
- //std::fprintf(stderr, "Done reading IMF file\n");
- opl.NumFourOps = 0; //Don't use 4-operator channels for IMF playing!
+
+ fr.seeku(mus_start, SEEK_SET);
+ TrackCount = 1;
+ DeltaTicks = ticks;
+ opl.AdlBank = ~0u; // Ignore AdlBank number, use dynamic banks instead
+ //std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo);
+ opl.LogarithmicVolumes = true;
+ opl.AdlPercussionMode = true;
}
else
{
- if(is_GMF || is_CMF) // Take the rest of the file
+ // Try parsing as an IMF file
{
- long pos = fr.tell();
- fr.seek(0, SEEK_END);
- TrackLength = fr.tell() - pos;
- fr.seek(pos, SEEK_SET);
+ size_t end = static_cast<uint8_t>(HeaderBuf[0]) + 256 * static_cast<uint8_t>(HeaderBuf[1]);
+
+ if(!end || (end & 3))
+ goto not_imf;
+
+ size_t backup_pos = fr.tell();
+ int64_t sum1 = 0, sum2 = 0;
+ fr.seek(2, SEEK_SET);
+
+ for(unsigned n = 0; n < 42; ++n)
+ {
+ int64_t value1 = fr.getc();
+ value1 += fr.getc() << 8;
+ sum1 += value1;
+ int64_t value2 = fr.getc();
+ value2 += fr.getc() << 8;
+ sum2 += value2;
+ }
+
+ fr.seek(static_cast<long>(backup_pos), SEEK_SET);
+
+ if(sum1 > sum2)
+ {
+ is_IMF = true;
+ DeltaTicks = 1;
+ }
}
- else if(is_MUS) // Read TrackLength from file position 4
+
+ if(!is_IMF)
{
- long pos = fr.tell();
- fr.seek(4, SEEK_SET);
- TrackLength = fr.getc(); TrackLength += (fr.getc() << 8);
- fr.seek(pos, SEEK_SET);
+not_imf:
+
+ if(std::memcmp(HeaderBuf, "MThd\0\0\0\6", 8) != 0)
+ {
+InvFmt:
+ fr.close();
+ ADLMIDI_ErrorString = fr._fileName + ": Invalid format\n";
+ return false;
+ }
+
+ /*size_t Fmt =*/ ReadBEint(HeaderBuf + 8, 2);
+ TrackCount = ReadBEint(HeaderBuf + 10, 2);
+ DeltaTicks = ReadBEint(HeaderBuf + 12, 2);
}
- else
+ }
+
+ TrackData.resize(TrackCount);
+ CurrentPosition.track.resize(TrackCount);
+ InvDeltaTicks = fraction<long>(1, 1000000l * static_cast<long>(DeltaTicks));
+ //Tempo = 1000000l * InvDeltaTicks;
+ Tempo = fraction<long>(1, static_cast<long>(DeltaTicks));
+ static const unsigned char EndTag[4] = {0xFF, 0x2F, 0x00, 0x00};
+ int totalGotten = 0;
+
+ for(size_t tk = 0; tk < TrackCount; ++tk)
+ {
+ // Read track header
+ size_t TrackLength;
+
+ if(is_IMF)
{
- fsize=fr.read(HeaderBuf, 1, 8);
- if(std::memcmp(HeaderBuf, "MTrk", 4) != 0) goto InvFmt;
- TrackLength = ReadBEint(HeaderBuf+4, 4);
+ //std::fprintf(stderr, "Reading IMF file...\n");
+ size_t end = static_cast<uint8_t>(HeaderBuf[0]) + 256 * static_cast<uint8_t>(HeaderBuf[1]);
+ unsigned IMF_tempo = 1428;
+ static const unsigned char imf_tempo[] = {0xFF, 0x51, 0x4,
+ static_cast<uint8_t>(IMF_tempo >> 24),
+ static_cast<uint8_t>(IMF_tempo >> 16),
+ static_cast<uint8_t>(IMF_tempo >> 8),
+ static_cast<uint8_t>(IMF_tempo)
+ };
+ TrackData[tk].insert(TrackData[tk].end(), imf_tempo, imf_tempo + sizeof(imf_tempo));
+ TrackData[tk].push_back(0x00);
+ fr.seek(2, SEEK_SET);
+
+ while(fr.tell() < end && !fr.eof())
+ {
+ uint8_t special_event_buf[5];
+ special_event_buf[0] = 0xFF;
+ special_event_buf[1] = 0xE3;
+ special_event_buf[2] = 0x02;
+ special_event_buf[3] = static_cast<uint8_t>(fr.getc()); // port index
+ special_event_buf[4] = static_cast<uint8_t>(fr.getc()); // port value
+ uint32_t delay = static_cast<uint16_t>(fr.getc());
+ delay += 256 * static_cast<uint16_t>(fr.getc());
+ totalGotten += 4;
+ //if(special_event_buf[3] <= 8) continue;
+ //fprintf(stderr, "Put %02X <- %02X, plus %04X delay\n", special_event_buf[3],special_event_buf[4], delay);
+ TrackData[tk].insert(TrackData[tk].end(), special_event_buf, special_event_buf + 5);
+
+ //if(delay>>21) TrackData[tk].push_back( 0x80 | ((delay>>21) & 0x7F ) );
+ if(delay >> 14)
+ TrackData[tk].push_back(0x80 | ((delay >> 14) & 0x7F));
+
+ if(delay >> 7)
+ TrackData[tk].push_back(0x80 | ((delay >> 7) & 0x7F));
+
+ TrackData[tk].push_back(((delay >> 0) & 0x7F));
+ }
+
+ TrackData[tk].insert(TrackData[tk].end(), EndTag + 0, EndTag + 4);
+ CurrentPosition.track[tk].delay = 0;
+ CurrentPosition.began = true;
+ //std::fprintf(stderr, "Done reading IMF file\n");
+ opl.NumFourOps = 0; //Don't use 4-operator channels for IMF playing!
}
- // Read track data
- TrackData[tk].resize(TrackLength);
- fsize=fr.read(&TrackData[tk][0], 1, TrackLength);
- totalGotten += fsize;
- if(is_GMF || is_MUS) // Note: CMF does include the track end tag.
+ else
{
- TrackData[tk].insert(TrackData[tk].end(), EndTag+0, EndTag+4);
+ if(is_GMF || is_CMF) // Take the rest of the file
+ {
+ size_t pos = fr.tell();
+ fr.seek(0, SEEK_END);
+ TrackLength = fr.tell() - pos;
+ fr.seek(static_cast<long>(pos), SEEK_SET);
+ }
+ else if(is_MUS) // Read TrackLength from file position 4
+ {
+ size_t pos = fr.tell();
+ fr.seek(4, SEEK_SET);
+ TrackLength = static_cast<size_t>(fr.getc());
+ TrackLength += static_cast<size_t>(fr.getc() << 8);
+ fr.seek(static_cast<long>(pos), SEEK_SET);
+ }
+ else
+ {
+ fsize = fr.read(HeaderBuf, 1, 8);
+
+ if(std::memcmp(HeaderBuf, "MTrk", 4) != 0)
+ goto InvFmt;
+
+ TrackLength = ReadBEint(HeaderBuf + 4, 4);
+ }
+
+ // Read track data
+ TrackData[tk].resize(TrackLength);
+ fsize = fr.read(&TrackData[tk][0], 1, TrackLength);
+ totalGotten += fsize;
+
+ if(is_GMF || is_MUS) // Note: CMF does include the track end tag.
+ TrackData[tk].insert(TrackData[tk].end(), EndTag + 0, EndTag + 4);
+
+ // Read next event time
+ CurrentPosition.track[tk].delay = static_cast<long>(ReadVarLen(tk));
}
- // Read next event time
- CurrentPosition.track[tk].delay = ReadVarLen(tk);
}
+
+ for(size_t tk = 0; tk < TrackCount; ++tk)
+ totalGotten += TrackData[tk].size();
+
+ if(totalGotten == 0)
+ {
+ ADLMIDI_ErrorString = fr._fileName + ": Empty track data";
+ return false;
+ }
+
+ opl.Reset(); // Reset AdLib
+ //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
+ ch.clear();
+ ch.resize(opl.NumChannels);
+ return true;
}
- for(size_t tk = 0; tk < TrackCount; ++tk)
- totalGotten+= TrackData[tk].size();
- if(totalGotten==0)
+
+ /* Periodic tick handler.
+ * Input: s = seconds since last call
+ * Input: granularity = don't expect intervals smaller than this, in seconds
+ * Output: desired number of seconds until next call
+ */
+ double Tick(double s, double granularity)
{
- ADLMIDI_ErrorString=fr._fileName+": Empty track data";
- return false;
- }
+ if(CurrentPosition.began) CurrentPosition.wait -= s;
- opl.Reset(); // Reset AdLib
- //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
- ch.clear();
- ch.resize(opl.NumChannels);
- return true;
- }
+ int AntiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing
- /* Periodic tick handler.
- * Input: s = seconds since last call
- * Input: granularity = don't expect intervals smaller than this, in seconds
- * Output: desired number of seconds until next call
- */
- double Tick(double s, double granularity)
- {
- if(CurrentPosition.began) CurrentPosition.wait -= s;
- int AntiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing
- while( (CurrentPosition.wait <= granularity * 0.5) && (AntiFreezeCounter>0) )
- {
- //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait);
- ProcessEvents();
- if(CurrentPosition.wait <= 0.0)
- AntiFreezeCounter--;
- }
+ while((CurrentPosition.wait <= granularity * 0.5) && (AntiFreezeCounter > 0))
+ {
+ //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait);
+ ProcessEvents();
- if(AntiFreezeCounter <= 0)
- CurrentPosition.wait += 1.0;/* Add extra 1 second when over 10000 events
- with zero delay are been detected */
+ if(CurrentPosition.wait <= 0.0)
+ AntiFreezeCounter--;
+ }
- for(unsigned c = 0; c < opl.NumChannels; ++c)
- ch[c].AddAge(s * 1000);
+ if(AntiFreezeCounter <= 0)
+ CurrentPosition.wait += 1.0;/* Add extra 1 second when over 10000 events
- UpdateVibrato(s);
- UpdateArpeggio(s);
- return CurrentPosition.wait;
- }
+ with zero delay are been detected */
+
+ for(uint16_t c = 0; c < opl.NumChannels; ++c)
+ ch[c].AddAge(static_cast<int64_t>(s * 1000.0));
-private:
- enum { Upd_Patch = 0x1,
- Upd_Pan = 0x2,
- Upd_Volume = 0x4,
- Upd_Pitch = 0x8,
- Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch,
- Upd_Off = 0x20 };
+ UpdateVibrato(s);
+ UpdateArpeggio(s);
+ return CurrentPosition.wait;
+ }
- void NoteUpdate
- (unsigned MidCh,
+ private:
+ enum { Upd_Patch = 0x1,
+ Upd_Pan = 0x2,
+ Upd_Volume = 0x4,
+ Upd_Pitch = 0x8,
+ Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch,
+ Upd_Off = 0x20
+ };
+
+ void NoteUpdate
+ (uint16_t MidCh,
MIDIchannel::activenoteiterator i,
unsigned props_mask,
- int select_adlchn = -1)
- {
- MIDIchannel::NoteInfo& info = i->second;
- const int tone = info.tone;
- const int vol = info.vol;
- //const int midiins = info.midiins;
- const int insmeta = info.insmeta;
- const adlinsdata& ains = opl.GetAdlMetaIns(insmeta);
-
- AdlChannel::Location my_loc;
- my_loc.MidCh = MidCh;
- my_loc.note = i->first;
-
- for(std::map<unsigned short,unsigned short>::iterator
- jnext = info.phys.begin();
- jnext != info.phys.end();
- )
+ int32_t select_adlchn = -1)
{
- std::map<unsigned short,unsigned short>::iterator j(jnext++);
- int c = j->first;
- int ins = j->second;
- if(select_adlchn >= 0 && c != select_adlchn) continue;
-
- if(props_mask & Upd_Patch)
+ MIDIchannel::NoteInfo &info = i->second;
+ const int16_t tone = info.tone;
+ const uint8_t vol = info.vol;
+ //const int midiins = info.midiins;
+ const uint32_t insmeta = info.insmeta;
+ const adlinsdata &ains = opl.GetAdlMetaIns(insmeta);
+ AdlChannel::Location my_loc;
+ my_loc.MidCh = MidCh;
+ my_loc.note = i->first;
+
+ for(std::map<uint16_t, uint16_t>::iterator
+ jnext = info.phys.begin();
+ jnext != info.phys.end();
+ )
{
- opl.Patch(c, ins);
- AdlChannel::LocationData& d = ch[c].users[my_loc];
- d.sustained = false; // inserts if necessary
- d.vibdelay = 0;
- d.kon_time_until_neglible = ains.ms_sound_kon;
- d.ins = ins;
+ std::map<uint16_t, uint16_t>::iterator j(jnext++);
+ uint16_t c = j->first;
+ uint16_t ins = j->second;
+
+ if(select_adlchn >= 0 && c != select_adlchn) continue;
+
+ if(props_mask & Upd_Patch)
+ {
+ opl.Patch(c, ins);
+ AdlChannel::LocationData &d = ch[c].users[my_loc];
+ d.sustained = false; // inserts if necessary
+ d.vibdelay = 0;
+ d.kon_time_until_neglible = ains.ms_sound_kon;
+ d.ins = ins;
+ }
}
- }
- for(std::map<unsigned short,unsigned short>::iterator
- jnext = info.phys.begin();
- jnext != info.phys.end();
- )
- {
- std::map<unsigned short,unsigned short>::iterator j(jnext++);
- int c = j->first;
- int ins = j->second;
- if(select_adlchn >= 0 && c != select_adlchn) continue;
- if(props_mask & Upd_Off) // note off
+ for(std::map<unsigned short, unsigned short>::iterator
+ jnext = info.phys.begin();
+ jnext != info.phys.end();
+ )
{
- if(Ch[MidCh].sustain == 0)
+ std::map<unsigned short, unsigned short>::iterator j(jnext++);
+ uint16_t c = j->first;
+ uint16_t ins = j->second;
+
+ if(select_adlchn >= 0 && c != select_adlchn) continue;
+
+ if(props_mask & Upd_Off) // note off
{
- AdlChannel::users_t::iterator k = ch[c].users.find(my_loc);
- if(k != ch[c].users.end())
- ch[c].users.erase(k);
- //UI.IllustrateNote(c, tone, midiins, 0, 0.0);
+ if(Ch[MidCh].sustain == 0)
+ {
+ AdlChannel::users_t::iterator k = ch[c].users.find(my_loc);
+
+ if(k != ch[c].users.end())
+ ch[c].users.erase(k);
- if(ch[c].users.empty())
+ //UI.IllustrateNote(c, tone, midiins, 0, 0.0);
+
+ if(ch[c].users.empty())
+ {
+ opl.NoteOff(c);
+ ch[c].koff_time_until_neglible =
+ ains.ms_sound_koff;
+ }
+ }
+ else
{
- opl.NoteOff(c);
- ch[c].koff_time_until_neglible =
- ains.ms_sound_koff;
+ // Sustain: Forget about the note, but don't key it off.
+ // Also will avoid overwriting it very soon.
+ AdlChannel::LocationData &d = ch[c].users[my_loc];
+ d.sustained = true; // note: not erased!
+ //UI.IllustrateNote(c, tone, midiins, -1, 0.0);
}
+
+ info.phys.erase(j);
+ continue;
}
- else
+
+ if(props_mask & Upd_Pan)
+ opl.Pan(c, Ch[MidCh].panning);
+
+ if(props_mask & Upd_Volume)
{
- // Sustain: Forget about the note, but don't key it off.
- // Also will avoid overwriting it very soon.
- AdlChannel::LocationData& d = ch[c].users[my_loc];
- d.sustained = true; // note: not erased!
- //UI.IllustrateNote(c, tone, midiins, -1, 0.0);
+ uint32_t volume = vol * Ch[MidCh].volume * Ch[MidCh].expression;
+ /* If the channel has arpeggio, the effective volume of
+ * *this* instrument is actually lower due to timesharing.
+ * To compensate, add extra volume that corresponds to the
+ * time this note is *not* heard.
+ * Empirical tests however show that a full equal-proportion
+ * increment sounds wrong. Therefore, using the square root.
+ */
+ //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() ));
+ opl.Touch(c, volume);
}
- info.phys.erase(j);
- continue;
- }
- if(props_mask & Upd_Pan)
- {
- opl.Pan(c, Ch[MidCh].panning);
- }
- if(props_mask & Upd_Volume)
- {
- int volume = vol * Ch[MidCh].volume * Ch[MidCh].expression;
- /* If the channel has arpeggio, the effective volume of
- * *this* instrument is actually lower due to timesharing.
- * To compensate, add extra volume that corresponds to the
- * time this note is *not* heard.
- * Empirical tests however show that a full equal-proportion
- * increment sounds wrong. Therefore, using the square root.
- */
- //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() ));
- opl.Touch(c, volume);
- }
- if(props_mask & Upd_Pitch)
- {
- AdlChannel::LocationData& d = ch[c].users[my_loc];
- // Don't bend a sustained note
- if(!d.sustained)
+
+ if(props_mask & Upd_Pitch)
{
- double bend = Ch[MidCh].bend + opl.GetAdlIns(ins).finetune;
- double phase = 0.0;
+ AdlChannel::LocationData &d = ch[c].users[my_loc];
- if((ains.flags & adlinsdata::Flag_Pseudo4op) && ins == ains.adlno2)
+ // Don't bend a sustained note
+ if(!d.sustained)
{
- phase = ains.fine_tune;//0.125; // Detune the note slightly (this is what Doom does)
- }
+ double bend = Ch[MidCh].bend + opl.GetAdlIns(ins).finetune;
+ double phase = 0.0;
+
+ if((ains.flags & adlinsdata::Flag_Pseudo4op) && ins == ains.adlno2)
+ {
+ phase = ains.fine_tune;//0.125; // Detune the note slightly (this is what Doom does)
+ }
- if(Ch[MidCh].vibrato && d.vibdelay >= Ch[MidCh].vibdelay)
- bend += Ch[MidCh].vibrato * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos);
- opl.NoteOn(c, 172.00093 * std::exp(0.057762265 * (tone + bend + phase)));
- //UI.IllustrateNote(c, tone, midiins, vol, Ch[MidCh].bend);
+ if(Ch[MidCh].vibrato && d.vibdelay >= Ch[MidCh].vibdelay)
+ bend += Ch[MidCh].vibrato * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos);
+
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+#define BEND_COEFFICIENT 172.00093
+#else
+#define BEND_COEFFICIENT 172.4387
+#endif
+ opl.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (tone + bend + phase)));
+#undef BEND_COEFFICIENT
+ }
}
}
+
+ if(info.phys.empty())
+ Ch[MidCh].activenotes.erase(i);
}
- if(info.phys.empty())
- Ch[MidCh].activenotes.erase(i);
- }
- void ProcessEvents()
- {
- loopEnd = false;
- const size_t TrackCount = TrackData.size();
- const Position RowBeginPosition ( CurrentPosition );
- for(size_t tk = 0; tk < TrackCount; ++tk)
+ void ProcessEvents()
{
- if(CurrentPosition.track[tk].status >= 0
- && CurrentPosition.track[tk].delay <= 0)
- {
- // Handle event
- HandleEvent(tk);
- // Read next event time (unless the track just ended)
- if(CurrentPosition.track[tk].ptr >= TrackData[tk].size())
- CurrentPosition.track[tk].status = -1;
- if(CurrentPosition.track[tk].status >= 0)
- CurrentPosition.track[tk].delay += ReadVarLen(tk);
- }
- }
- // Find shortest delay from all track
- long shortest = -1;
- for(size_t tk=0; tk<TrackCount; ++tk)
- if(CurrentPosition.track[tk].status >= 0
- && (shortest == -1
- || CurrentPosition.track[tk].delay < shortest))
+ loopEnd = false;
+ const size_t TrackCount = TrackData.size();
+ const Position RowBeginPosition(CurrentPosition);
+
+ for(size_t tk = 0; tk < TrackCount; ++tk)
{
- shortest = CurrentPosition.track[tk].delay;
+ if(CurrentPosition.track[tk].status >= 0
+ && CurrentPosition.track[tk].delay <= 0)
+ {
+ // Handle event
+ HandleEvent(tk);
+
+ // Read next event time (unless the track just ended)
+ if(CurrentPosition.track[tk].ptr >= TrackData[tk].size())
+ CurrentPosition.track[tk].status = -1;
+
+ if(CurrentPosition.track[tk].status >= 0)
+ CurrentPosition.track[tk].delay += ReadVarLen(tk);
+ }
}
- //if(shortest > 0) UI.PrintLn("shortest: %ld", shortest);
- // Schedule the next playevent to be processed after that delay
- for(size_t tk=0; tk<TrackCount; ++tk)
- CurrentPosition.track[tk].delay -= shortest;
+ // Find shortest delay from all track
+ long shortest = -1;
- fraction<long> t = shortest * Tempo;
- if(CurrentPosition.began) CurrentPosition.wait += t.valuel();
+ for(size_t tk = 0; tk < TrackCount; ++tk)
+ if(CurrentPosition.track[tk].status >= 0
+ && (shortest == -1
+ || CurrentPosition.track[tk].delay < shortest))
+ shortest = CurrentPosition.track[tk].delay;
- //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel());
+ //if(shortest > 0) UI.PrintLn("shortest: %ld", shortest);
- /*
- if(CurrentPosition.track[0].ptr > 8119) loopEnd = true;
- // ^HACK: CHRONO TRIGGER LOOP
- */
+ // Schedule the next playevent to be processed after that delay
+ for(size_t tk = 0; tk < TrackCount; ++tk)
+ CurrentPosition.track[tk].delay -= shortest;
- if(loopStart_hit && (loopStart||loopEnd)) //Avoid invalid loops
- {
- invalidLoop=true;
- loopStart = false;
- loopEnd = false;
- LoopBeginPosition=trackBeginPosition;
- } else {
- loopStart_hit=false;
- }
+ fraction<long> t = shortest * Tempo;
- if(loopStart)
- {
- if(trackStart)
+ if(CurrentPosition.began) CurrentPosition.wait += t.valuel();
+
+ //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel());
+
+ /*
+ if(CurrentPosition.track[0].ptr > 8119) loopEnd = true;
+ // ^HACK: CHRONO TRIGGER LOOP
+ */
+
+ if(loopStart_hit && (loopStart || loopEnd)) //Avoid invalid loops
{
- trackBeginPosition=RowBeginPosition;
- trackStart=false;
+ invalidLoop = true;
+ loopStart = false;
+ loopEnd = false;
+ LoopBeginPosition = trackBeginPosition;
}
- LoopBeginPosition = RowBeginPosition;
- loopStart = false;
- loopStart_hit=true;
- }
- if(shortest < 0 || loopEnd)
- {
- // Loop if song end reached
- loopEnd = false;
- CurrentPosition = LoopBeginPosition;
- shortest = 0;
- if(opl._parent->QuitWithoutLooping==1)
+ else
+ loopStart_hit = false;
+
+ if(loopStart)
{
- opl._parent->QuitFlag = 1;
- //^ HACK: QUIT WITHOUT LOOPING
+ if(trackStart)
+ {
+ trackBeginPosition = RowBeginPosition;
+ trackStart = false;
+ }
+
+ LoopBeginPosition = RowBeginPosition;
+ loopStart = false;
+ loopStart_hit = true;
}
- }
- }
- void HandleEvent(size_t tk)
- {
- unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++];
- if(byte == 0xF7 || byte == 0xF0) // Ignore SysEx
- {
- unsigned length = ReadVarLen(tk);
- //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length );
- CurrentPosition.track[tk].ptr += length;
- //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/);
- return;
+ if(shortest < 0 || loopEnd)
+ {
+ // Loop if song end reached
+ loopEnd = false;
+ CurrentPosition = LoopBeginPosition;
+ shortest = 0;
+
+ if(opl._parent->QuitWithoutLooping == 1)
+ {
+ opl._parent->QuitFlag = 1;
+ //^ HACK: QUIT WITHOUT LOOPING
+ }
+ }
}
- if(byte == 0xFF)
+
+ void HandleEvent(size_t tk)
{
- // Special event FF
- unsigned char evtype = TrackData[tk][CurrentPosition.track[tk].ptr++];
- unsigned long length = ReadVarLen(tk);
- std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length );
- CurrentPosition.track[tk].ptr += length;
- if(evtype == 0x2F) { CurrentPosition.track[tk].status = -1; return; }
- if(evtype == 0x51) { Tempo = InvDeltaTicks * fraction<long>( (long) ReadBEint(data.data(), data.size())); return; }
- if(evtype == 6)
+ unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++];
+
+ if(byte == 0xF7 || byte == 0xF0) // Ignore SysEx
+ {
+ uint64_t length = ReadVarLen(tk);
+ //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length );
+ CurrentPosition.track[tk].ptr += length;
+ //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/);
+ return;
+ }
+
+ if(byte == 0xFF)
{
- for(size_t i=0;i<data.size();i++)
+ // Special event FF
+ unsigned char evtype = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ unsigned long length = ReadVarLen(tk);
+ std::string data(length ? (const char *) &TrackData[tk][CurrentPosition.track[tk].ptr] : 0, length);
+ CurrentPosition.track[tk].ptr += length;
+
+ if(evtype == 0x2F)
{
- if(data[i]<='Z' && data[i]>='A')
- data[i]=data[i]-('Z'-'z');
+ CurrentPosition.track[tk].status = -1;
+ return;
}
- if( (data == "loopstart") && (!invalidLoop) )
+ if(evtype == 0x51)
{
- loopStart = true;
- loopStart_passed=true;
+ Tempo = InvDeltaTicks * fraction<long>((long) ReadBEint(data.data(), data.size()));
+ return;
}
- if( (data == "loopend") && (!invalidLoop) )
+ if(evtype == 6)
{
- if((loopStart_passed) && (!loopStart))
- loopEnd=true;
- else
- invalidLoop=true;
+ for(size_t i = 0; i < data.size(); i++)
+ {
+ if(data[i] <= 'Z' && data[i] >= 'A')
+ data[i] = data[i] - ('Z' - 'z');
+ }
+
+ if((data == "loopstart") && (!invalidLoop))
+ {
+ loopStart = true;
+ loopStart_passed = true;
+ }
+
+ if((data == "loopend") && (!invalidLoop))
+ {
+ if((loopStart_passed) && (!loopStart))
+ loopEnd = true;
+ else
+ invalidLoop = true;
+ }
+ }
+
+ if(evtype == 9)
+ current_device[tk] = ChooseDevice(data);
+
+ //if(evtype >= 1 && evtype <= 6)
+ // UI.PrintLn("Meta %d: %s", evtype, data.c_str());
+
+ if(evtype == 0xE3) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib
+ {
+ uint8_t i = static_cast<uint8_t>(data[0]), v = static_cast<uint8_t>(data[1]);
+
+ if((i & 0xF0) == 0xC0)
+ v |= 0x30;
+
+ //std::printf("OPL poke %02X, %02X\n", i, v);
+ //std::fflush(stdout);
+ opl.PokeN(0, i, v);
}
+
+ return;
}
- if(evtype == 9) current_device[tk] = ChooseDevice(data);
- //if(evtype >= 1 && evtype <= 6)
- // UI.PrintLn("Meta %d: %s", evtype, data.c_str());
- if(evtype == 0xE3) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib
+ // Any normal event (80..EF)
+ if(byte < 0x80)
{
- unsigned char i = data[0], v = data[1];
- if( (i&0xF0) == 0xC0 ) v |= 0x30;
- //std::printf("OPL poke %02X, %02X\n", i, v);
- //std::fflush(stdout);
- opl.Poke(0, i,v);
+ byte = static_cast<uint8_t>(CurrentPosition.track[tk].status | 0x80);
+ CurrentPosition.track[tk].ptr--;
}
- return;
- }
- // Any normal event (80..EF)
- if(byte < 0x80)
- { byte = CurrentPosition.track[tk].status | 0x80;
- CurrentPosition.track[tk].ptr--; }
- if(byte == 0xF3) { CurrentPosition.track[tk].ptr += 1; return; }
- if(byte == 0xF2) { CurrentPosition.track[tk].ptr += 2; return; }
- /*UI.PrintLn("@%X Track %u: %02X %02X",
- CurrentPosition.track[tk].ptr-1, (unsigned)tk, byte,
- TrackData[tk][CurrentPosition.track[tk].ptr]);*/
- unsigned MidCh = byte & 0x0F, EvType = byte >> 4;
- MidCh += current_device[tk];
-
- CurrentPosition.track[tk].status = byte;
- switch(EvType)
- {
+
+ if(byte == 0xF3)
+ {
+ CurrentPosition.track[tk].ptr += 1;
+ return;
+ }
+
+ if(byte == 0xF2)
+ {
+ CurrentPosition.track[tk].ptr += 2;
+ return;
+ }
+
+ /*UI.PrintLn("@%X Track %u: %02X %02X",
+ CurrentPosition.track[tk].ptr-1, (unsigned)tk, byte,
+ TrackData[tk][CurrentPosition.track[tk].ptr]);*/
+ uint8_t MidCh = byte & 0x0F, EvType = byte >> 4;
+ MidCh += current_device[tk];
+ CurrentPosition.track[tk].status = byte;
+
+ switch(EvType)
+ {
case 0x8: // Note off
case 0x9: // Note on
{
- int note = TrackData[tk][CurrentPosition.track[tk].ptr++];
- int vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
//if(MidCh != 9) note -= 12; // HACK
NoteOff(MidCh, note);
+
// On Note on, Keyoff the note first, just in case keyoff
// was omitted; this fixes Dance of sugar-plum fairy
// by Microsoft. Now that we've done a Keyoff,
@@ -1379,8 +1606,10 @@ private:
// vol=0 and event 8x are both Keyoff-only.
if(vol == 0 || EvType == 0x8) break;
- unsigned midiins = Ch[MidCh].patch;
- if(MidCh%16 == 9) midiins = 128 + note; // Percussion instrument
+ uint8_t midiins = Ch[MidCh].patch;
+
+ if(MidCh % 16 == 9)
+ midiins = 128 + note; // Percussion instrument
/*
if(MidCh%16 == 9 || (midiins != 32 && midiins != 46 && midiins != 48 && midiins != 50))
@@ -1389,43 +1618,46 @@ private:
if(midiins == 48 || midiins == 50) vol /= 4; // HACK
*/
//if(midiins == 56) vol = vol*6/10; // HACK
+ static std::set<uint32_t> bank_warnings;
- static std::set<unsigned> bank_warnings;
if(Ch[MidCh].bank_msb)
{
- unsigned bankid = midiins + 256*Ch[MidCh].bank_msb;
- std::set<unsigned>::iterator
- i = bank_warnings.lower_bound(bankid);
+ uint32_t bankid = midiins + 256 * Ch[MidCh].bank_msb;
+ std::set<uint32_t>::iterator
+ i = bank_warnings.lower_bound(bankid);
+
if(i == bank_warnings.end() || *i != bankid)
{
ADLMIDI_ErrorString.clear();
std::stringstream s;
- s<<"[" << MidCh <<"]Bank " << Ch[MidCh].bank_msb <<
- " undefined, patch="<< ((midiins&128) ? 'P':'M') << (midiins&127);
- ADLMIDI_ErrorString=s.str();
+ s << "[" << MidCh << "]Bank " << Ch[MidCh].bank_msb <<
+ " undefined, patch=" << ((midiins & 128) ? 'P' : 'M') << (midiins & 127);
+ ADLMIDI_ErrorString = s.str();
bank_warnings.insert(i, bankid);
}
}
+
if(Ch[MidCh].bank_lsb)
{
- unsigned bankid = Ch[MidCh].bank_lsb*65536;
+ unsigned bankid = Ch[MidCh].bank_lsb * 65536;
std::set<unsigned>::iterator
- i = bank_warnings.lower_bound(bankid);
+ i = bank_warnings.lower_bound(bankid);
+
if(i == bank_warnings.end() || *i != bankid)
{
ADLMIDI_ErrorString.clear();
std::stringstream s;
- s<<"["<<MidCh<< "]Bank lsb " << Ch[MidCh].bank_lsb <<" undefined";
- ADLMIDI_ErrorString=s.str();
+ s << "[" << MidCh << "]Bank lsb " << Ch[MidCh].bank_lsb << " undefined";
+ ADLMIDI_ErrorString = s.str();
bank_warnings.insert(i, bankid);
}
}
//int meta = banks[opl.AdlBank][midiins];
- const unsigned meta = opl.GetAdlMetaNumber(midiins);
- const adlinsdata& ains = opl.GetAdlMetaIns(meta);
+ const uint32_t meta = opl.GetAdlMetaNumber(midiins);
+ const adlinsdata &ains = opl.GetAdlMetaIns(meta);
+ int16_t tone = note;
- int tone = note;
if(ains.tone)
{
if(ains.tone < 20)
@@ -1433,48 +1665,57 @@ private:
else if(ains.tone < 128)
tone = ains.tone;
else
- tone -= ains.tone-128;
+ tone -= ains.tone - 128;
}
- int i[2] = { ains.adlno1, ains.adlno2 };
+
+ uint16_t i[2] = { ains.adlno1, ains.adlno2 };
bool pseudo_4op = ains.flags & adlinsdata::Flag_Pseudo4op;
- if((opl.AdlPercussionMode==1) && PercussionMap[midiins & 0xFF]) i[1] = i[0];
+ if((opl.AdlPercussionMode == 1) && PercussionMap[midiins & 0xFF]) i[1] = i[0];
- static std::set<unsigned char> missing_warnings;
- if(!missing_warnings.count(midiins) && (ains.flags & adlinsdata::Flag_NoSound))
+ static std::set<uint8_t> missing_warnings;
+
+ if(!missing_warnings.count(static_cast<uint8_t>(midiins)) && (ains.flags & adlinsdata::Flag_NoSound))
{
//UI.PrintLn("[%i]Playing missing instrument %i", MidCh, midiins);
- missing_warnings.insert(midiins);
+ missing_warnings.insert(static_cast<uint8_t>(midiins));
}
// Allocate AdLib channel (the physical sound channel for the note)
- int adlchannel[2] = { -1, -1 };
- for(unsigned ccount = 0; ccount < 2; ++ccount)
+ int32_t adlchannel[2] = { -1, -1 };
+
+ for(uint32_t ccount = 0; ccount < 2; ++ccount)
{
if(ccount == 1)
{
if(i[0] == i[1]) break; // No secondary channel
- if(adlchannel[0] == -1) break; // No secondary if primary failed
+
+ if(adlchannel[0] == -1)
+ break; // No secondary if primary failed
}
- int c = -1;
+ int32_t c = -1;
long bs = -0x7FFFFFFFl;
- for(int a = 0; a < (int)opl.NumChannels; ++a)
+
+ for(uint32_t a = 0; a < opl.NumChannels; ++a)
{
- if(ccount == 1 && a == adlchannel[0]) continue;
+ if(ccount == 1 && static_cast<int32_t>(a) == adlchannel[0]) continue;
+
// ^ Don't use the same channel for primary&secondary
if(i[0] == i[1] || pseudo_4op)
{
// Only use regular channels
- int expected_mode = 0;
- if(opl.AdlPercussionMode==1)
+ uint8_t expected_mode = 0;
+
+ if(opl.AdlPercussionMode == 1)
{
if(cmf_percussion_mode)
- expected_mode = MidCh < 11 ? 0 : (3+MidCh-11); // CMF
+ expected_mode = MidCh < 11 ? 0 : (3 + MidCh - 11); // CMF
else
expected_mode = PercussionMap[midiins & 0xFF];
}
+
if(opl.four_op_category[a] != expected_mode)
continue;
}
@@ -1489,13 +1730,18 @@ private:
else
{
// The secondary must be played on a specific channel.
- if(a != adlchannel[0] + 3)
+ if(a != static_cast<uint32_t>(adlchannel[0]) + 3)
continue;
}
}
long s = CalculateAdlChannelGoodness(a, i[ccount], MidCh);
- if(s > bs) { bs=s; c = a; } // Best candidate wins
+
+ if(s > bs)
+ {
+ bs = s; // Best candidate wins
+ c = static_cast<int32_t>(a);
+ }
}
if(c < 0)
@@ -1503,144 +1749,219 @@ private:
//UI.PrintLn("ignored unplaceable note");
continue; // Could not play this note. Ignore it.
}
- PrepareAdlChannelForNewNote(c, i[ccount]);
+
+ PrepareAdlChannelForNewNote(static_cast<size_t>(c), i[ccount]);
adlchannel[ccount] = c;
}
+
if(adlchannel[0] < 0 && adlchannel[1] < 0)
{
// The note could not be played, at all.
break;
}
- //UI.PrintLn("i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]);
+ //UI.PrintLn("i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]);
// Allocate active note for MIDI channel
- std::pair<MIDIchannel::activenoteiterator,bool>
- ir = Ch[MidCh].activenotes.insert(
- std::make_pair(note, MIDIchannel::NoteInfo()));
+ std::pair<MIDIchannel::activenoteiterator, bool>
+ ir = Ch[MidCh].activenotes.insert(
+ std::make_pair(note, MIDIchannel::NoteInfo()));
ir.first->second.vol = vol;
ir.first->second.tone = tone;
ir.first->second.midiins = midiins;
ir.first->second.insmeta = meta;
- for(unsigned ccount=0; ccount<2; ++ccount)
+
+ for(unsigned ccount = 0; ccount < 2; ++ccount)
{
- int c = adlchannel[ccount];
- if(c < 0) continue;
- ir.first->second.phys[ adlchannel[ccount] ] = i[ccount];
+ int32_t c = adlchannel[ccount];
+
+ if(c < 0)
+ continue;
+
+ ir.first->second.phys[ static_cast<uint16_t>(adlchannel[ccount]) ] = i[ccount];
}
+
CurrentPosition.began = true;
NoteUpdate(MidCh, ir.first, Upd_All | Upd_Patch);
break;
}
+
case 0xA: // Note touch
{
- int note = TrackData[tk][CurrentPosition.track[tk].ptr++];
- int vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
MIDIchannel::activenoteiterator
- i = Ch[MidCh].activenotes.find(note);
+ i = Ch[MidCh].activenotes.find(note);
+
if(i == Ch[MidCh].activenotes.end())
{
// Ignore touch if note is not active
break;
}
+
i->second.vol = vol;
NoteUpdate(MidCh, i, Upd_Volume);
break;
}
+
case 0xB: // Controller change
{
- int ctrlno = TrackData[tk][CurrentPosition.track[tk].ptr++];
- int value = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ uint8_t ctrlno = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ uint8_t value = TrackData[tk][CurrentPosition.track[tk].ptr++];
+
switch(ctrlno)
{
- case 1: // Adjust vibrato
- //UI.PrintLn("%u:vibrato %d", MidCh,value);
- Ch[MidCh].vibrato = value; break;
- case 0: // Set bank msb (GM bank)
- Ch[MidCh].bank_msb = value;
- break;
- case 32: // Set bank lsb (XG bank)
- Ch[MidCh].bank_lsb = value;
- break;
- case 5: // Set portamento msb
- Ch[MidCh].portamento = (Ch[MidCh].portamento & 0x7F) | (value<<7);
- //UpdatePortamento(MidCh);
- break;
- case 37: // Set portamento lsb
- Ch[MidCh].portamento = (Ch[MidCh].portamento & 0x3F80) | (value);
- //UpdatePortamento(MidCh);
- break;
- case 65: // Enable/disable portamento
- // value >= 64 ? enabled : disabled
- //UpdatePortamento(MidCh);
- break;
- case 7: // Change volume
- Ch[MidCh].volume = value;
- NoteUpdate_All(MidCh, Upd_Volume);
- break;
- case 64: // Enable/disable sustain
- Ch[MidCh].sustain = value;
- if(!value) KillSustainingNotes( MidCh );
- break;
- case 11: // Change expression (another volume factor)
- Ch[MidCh].expression = value;
- NoteUpdate_All(MidCh, Upd_Volume);
- break;
- case 10: // Change panning
- Ch[MidCh].panning = 0x00;
- if(value < 64+32) Ch[MidCh].panning |= 0x10;
- if(value >= 64-32) Ch[MidCh].panning |= 0x20;
- NoteUpdate_All(MidCh, Upd_Pan);
- break;
- case 121: // Reset all controllers
- Ch[MidCh].bend = 0;
- Ch[MidCh].volume = 100;
- Ch[MidCh].expression = 100;
- Ch[MidCh].sustain = 0;
- Ch[MidCh].vibrato = 0;
- Ch[MidCh].vibspeed = 2*3.141592653*5.0;
- Ch[MidCh].vibdepth = 0.5/127;
- Ch[MidCh].vibdelay = 0;
- Ch[MidCh].panning = 0x30;
- Ch[MidCh].portamento = 0;
- //UpdatePortamento(MidCh);
- NoteUpdate_All(MidCh, Upd_Pan+Upd_Volume+Upd_Pitch);
- // Kill all sustained notes
- KillSustainingNotes(MidCh);
- break;
- case 123: // All notes off
- NoteUpdate_All(MidCh, Upd_Off);
- break;
- case 91: break; // Reverb effect depth. We don't do per-channel reverb.
- case 92: break; // Tremolo effect depth. We don't do...
- case 93: break; // Chorus effect depth. We don't do.
- case 94: break; // Celeste effect depth. We don't do.
- case 95: break; // Phaser effect depth. We don't do.
- case 98: Ch[MidCh].lastlrpn=value; Ch[MidCh].nrpn=true; break;
- case 99: Ch[MidCh].lastmrpn=value; Ch[MidCh].nrpn=true; break;
- case 100:Ch[MidCh].lastlrpn=value; Ch[MidCh].nrpn=false; break;
- case 101:Ch[MidCh].lastmrpn=value; Ch[MidCh].nrpn=false; break;
- case 113: break; // Related to pitch-bender, used by missimp.mid in Duke3D
- case 6: SetRPN(MidCh, value, true); break;
- case 38: SetRPN(MidCh, value, false); break;
- case 103: cmf_percussion_mode = value; break; // CMF (ctrl 0x67) rhythm mode
- case 111://LoopStart unofficial controller
- if(!invalidLoop)
- {
- loopStart = true;
- loopStart_passed=true;
- } break;
- default: break;
- //UI.PrintLn("Ctrl %d <- %d (ch %u)", ctrlno, value, MidCh);
+ case 1: // Adjust vibrato
+ //UI.PrintLn("%u:vibrato %d", MidCh,value);
+ Ch[MidCh].vibrato = value;
+ break;
+
+ case 0: // Set bank msb (GM bank)
+ Ch[MidCh].bank_msb = value;
+ break;
+
+ case 32: // Set bank lsb (XG bank)
+ Ch[MidCh].bank_lsb = value;
+ break;
+
+ case 5: // Set portamento msb
+ Ch[MidCh].portamento = static_cast<uint16_t>((Ch[MidCh].portamento & 0x7F) | (value << 7));
+ //UpdatePortamento(MidCh);
+ break;
+
+ case 37: // Set portamento lsb
+ Ch[MidCh].portamento = (Ch[MidCh].portamento & 0x3F80) | (value);
+ //UpdatePortamento(MidCh);
+ break;
+
+ case 65: // Enable/disable portamento
+ // value >= 64 ? enabled : disabled
+ //UpdatePortamento(MidCh);
+ break;
+
+ case 7: // Change volume
+ Ch[MidCh].volume = value;
+ NoteUpdate_All(MidCh, Upd_Volume);
+ break;
+
+ case 64: // Enable/disable sustain
+ Ch[MidCh].sustain = value;
+
+ if(!value) KillSustainingNotes(MidCh);
+
+ break;
+
+ case 11: // Change expression (another volume factor)
+ Ch[MidCh].expression = value;
+ NoteUpdate_All(MidCh, Upd_Volume);
+ break;
+
+ case 10: // Change panning
+ Ch[MidCh].panning = 0x00;
+
+ if(value < 64 + 32) Ch[MidCh].panning |= 0x10;
+
+ if(value >= 64 - 32) Ch[MidCh].panning |= 0x20;
+
+ NoteUpdate_All(MidCh, Upd_Pan);
+ break;
+
+ case 121: // Reset all controllers
+ Ch[MidCh].bend = 0;
+ Ch[MidCh].volume = 100;
+ Ch[MidCh].expression = 100;
+ Ch[MidCh].sustain = 0;
+ Ch[MidCh].vibrato = 0;
+ Ch[MidCh].vibspeed = 2 * 3.141592653 * 5.0;
+ Ch[MidCh].vibdepth = 0.5 / 127;
+ Ch[MidCh].vibdelay = 0;
+ Ch[MidCh].panning = 0x30;
+ Ch[MidCh].portamento = 0;
+ //UpdatePortamento(MidCh);
+ NoteUpdate_All(MidCh, Upd_Pan + Upd_Volume + Upd_Pitch);
+ // Kill all sustained notes
+ KillSustainingNotes(MidCh);
+ break;
+
+ case 123: // All notes off
+ NoteUpdate_All(MidCh, Upd_Off);
+ break;
+
+ case 91:
+ break; // Reverb effect depth. We don't do per-channel reverb.
+
+ case 92:
+ break; // Tremolo effect depth. We don't do...
+
+ case 93:
+ break; // Chorus effect depth. We don't do.
+
+ case 94:
+ break; // Celeste effect depth. We don't do.
+
+ case 95:
+ break; // Phaser effect depth. We don't do.
+
+ case 98:
+ Ch[MidCh].lastlrpn = value;
+ Ch[MidCh].nrpn = true;
+ break;
+
+ case 99:
+ Ch[MidCh].lastmrpn = value;
+ Ch[MidCh].nrpn = true;
+ break;
+
+ case 100:
+ Ch[MidCh].lastlrpn = value;
+ Ch[MidCh].nrpn = false;
+ break;
+
+ case 101:
+ Ch[MidCh].lastmrpn = value;
+ Ch[MidCh].nrpn = false;
+ break;
+
+ case 113:
+ break; // Related to pitch-bender, used by missimp.mid in Duke3D
+
+ case 6:
+ SetRPN(MidCh, value, true);
+ break;
+
+ case 38:
+ SetRPN(MidCh, value, false);
+ break;
+
+ case 103:
+ cmf_percussion_mode = value;
+ break; // CMF (ctrl 0x67) rhythm mode
+
+ case 111://LoopStart unofficial controller
+ if(!invalidLoop)
+ {
+ loopStart = true;
+ loopStart_passed = true;
+ }
+
+ break;
+
+ default:
+ break;
+ //UI.PrintLn("Ctrl %d <- %d (ch %u)", ctrlno, value, MidCh);
}
+
break;
}
+
case 0xC: // Patch change
Ch[MidCh].patch = TrackData[tk][CurrentPosition.track[tk].ptr++];
break;
+
case 0xD: // Channel after-touch
{
// TODO: Verify, is this correct action?
- int vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
+
for(MIDIchannel::activenoteiterator
i = Ch[MidCh].activenotes.begin();
i != Ch[MidCh].activenotes.end();
@@ -1649,391 +1970,444 @@ private:
// Set this pressure to all active notes on the channel
i->second.vol = vol;
}
+
NoteUpdate_All(MidCh, Upd_Volume);
break;
}
+
case 0xE: // Wheel/pitch bend
{
int a = TrackData[tk][CurrentPosition.track[tk].ptr++];
int b = TrackData[tk][CurrentPosition.track[tk].ptr++];
- Ch[MidCh].bend = (a + b*128 - 8192) * Ch[MidCh].bendsense;
+ Ch[MidCh].bend = (a + b * 128 - 8192) * Ch[MidCh].bendsense;
NoteUpdate_All(MidCh, Upd_Pitch);
break;
}
+ }
}
- }
- // Determine how good a candidate this adlchannel
- // would be for playing a note from this instrument.
- long CalculateAdlChannelGoodness
- (unsigned c, unsigned ins, unsigned /*MidCh*/) const
- {
- long s = -ch[c].koff_time_until_neglible;
-
- // Same midi-instrument = some stability
- //if(c == MidCh) s += 4;
- for(AdlChannel::users_t::const_iterator
- j = ch[c].users.begin();
- j != ch[c].users.end();
- ++j)
+ // Determine how good a candidate this adlchannel
+ // would be for playing a note from this instrument.
+ long CalculateAdlChannelGoodness
+ (unsigned c, uint16_t ins, uint16_t /*MidCh*/) const
{
- s -= 4000;
- if(!j->second.sustained)
- s -= j->second.kon_time_until_neglible;
- else
- s -= j->second.kon_time_until_neglible / 2;
+ long s = -ch[c].koff_time_until_neglible;
+
+ // Same midi-instrument = some stability
+ //if(c == MidCh) s += 4;
+ for(AdlChannel::users_t::const_iterator
+ j = ch[c].users.begin();
+ j != ch[c].users.end();
+ ++j)
+ {
+ s -= 4000;
- MIDIchannel::activenotemap_t::const_iterator
+ if(!j->second.sustained)
+ s -= j->second.kon_time_until_neglible;
+ else
+ s -= j->second.kon_time_until_neglible / 2;
+
+ MIDIchannel::activenotemap_t::const_iterator
k = Ch[j->first.MidCh].activenotes.find(j->first.note);
- if(k != Ch[j->first.MidCh].activenotes.end())
- {
- // Same instrument = good
- if(j->second.ins == ins)
+
+ if(k != Ch[j->first.MidCh].activenotes.end())
{
- s += 300;
- // Arpeggio candidate = even better
- if(j->second.vibdelay < 70
- || j->second.kon_time_until_neglible > 20000)
- s += 0;
+ // Same instrument = good
+ if(j->second.ins == ins)
+ {
+ s += 300;
+
+ // Arpeggio candidate = even better
+ if(j->second.vibdelay < 70
+ || j->second.kon_time_until_neglible > 20000)
+ s += 0;
+ }
+
+ // Percussion is inferior to melody
+ s += 50 * (k->second.midiins / 128);
+ /*
+ if(k->second.midiins >= 25
+ && k->second.midiins < 40
+ && j->second.ins != ins)
+ {
+ s -= 14000; // HACK: Don't clobber the bass or the guitar
+ }
+ */
}
- // Percussion is inferior to melody
- s += 50 * (k->second.midiins / 128);
- /*
- if(k->second.midiins >= 25
- && k->second.midiins < 40
- && j->second.ins != ins)
+ // If there is another channel to which this note
+ // can be evacuated to in the case of congestion,
+ // increase the score slightly.
+ unsigned n_evacuation_stations = 0;
+
+ for(unsigned c2 = 0; c2 < opl.NumChannels; ++c2)
{
- s -= 14000; // HACK: Don't clobber the bass or the guitar
+ if(c2 == c) continue;
+
+ if(opl.four_op_category[c2]
+ != opl.four_op_category[c]) continue;
+
+ for(AdlChannel::users_t::const_iterator
+ m = ch[c2].users.begin();
+ m != ch[c2].users.end();
+ ++m)
+ {
+ if(m->second.sustained) continue;
+
+ if(m->second.vibdelay >= 200) continue;
+
+ if(m->second.ins != j->second.ins) continue;
+
+ n_evacuation_stations += 1;
+ }
}
- */
+
+ s += n_evacuation_stations * 4;
}
- // If there is another channel to which this note
- // can be evacuated to in the case of congestion,
- // increase the score slightly.
- unsigned n_evacuation_stations = 0;
- for(unsigned c2 = 0; c2 < opl.NumChannels; ++c2)
+ return s;
+ }
+
+ // A new note will be played on this channel using this instrument.
+ // Kill existing notes on this channel (or don't, if we do arpeggio)
+ void PrepareAdlChannelForNewNote(size_t c, int ins)
+ {
+ if(ch[c].users.empty()) return; // Nothing to do
+
+ //bool doing_arpeggio = false;
+ for(AdlChannel::users_t::iterator
+ jnext = ch[c].users.begin();
+ jnext != ch[c].users.end();
+ )
{
- if(c2 == c) continue;
- if(opl.four_op_category[c2]
- != opl.four_op_category[c]) continue;
- for(AdlChannel::users_t::const_iterator
- m = ch[c2].users.begin();
- m != ch[c2].users.end();
- ++m)
+ AdlChannel::users_t::iterator j(jnext++);
+
+ if(!j->second.sustained)
{
- if(m->second.sustained) continue;
- if(m->second.vibdelay >= 200) continue;
- if(m->second.ins != j->second.ins) continue;
- n_evacuation_stations += 1;
+ // Collision: Kill old note,
+ // UNLESS we're going to do arpeggio
+ MIDIchannel::activenoteiterator i
+ (Ch[j->first.MidCh].activenotes.find(j->first.note));
+
+ // Check if we can do arpeggio.
+ if((j->second.vibdelay < 70
+ || j->second.kon_time_until_neglible > 20000)
+ && j->second.ins == ins)
+ {
+ // Do arpeggio together with this note.
+ //doing_arpeggio = true;
+ continue;
+ }
+
+ KillOrEvacuate(c, j, i);
+ // ^ will also erase j from ch[c].users.
}
}
- s += n_evacuation_stations * 4;
+
+ // Kill all sustained notes on this channel
+ // Don't keep them for arpeggio, because arpeggio requires
+ // an intact "activenotes" record. This is a design flaw.
+ KillSustainingNotes(-1, static_cast<int32_t>(c));
+
+ // Keyoff the channel so that it can be retriggered,
+ // unless the new note will be introduced as just an arpeggio.
+ if(ch[c].users.empty())
+ opl.NoteOff(c);
}
- return s;
- }
- // A new note will be played on this channel using this instrument.
- // Kill existing notes on this channel (or don't, if we do arpeggio)
- void PrepareAdlChannelForNewNote(int c, int ins)
- {
- if(ch[c].users.empty()) return; // Nothing to do
- //bool doing_arpeggio = false;
- for(AdlChannel::users_t::iterator
- jnext = ch[c].users.begin();
- jnext != ch[c].users.end();
- )
+ void KillOrEvacuate(
+ size_t from_channel,
+ AdlChannel::users_t::iterator j,
+ MIDIchannel::activenoteiterator i)
{
- AdlChannel::users_t::iterator j(jnext++);
- if(!j->second.sustained)
+ // Before killing the note, check if it can be
+ // evacuated to another channel as an arpeggio
+ // instrument. This helps if e.g. all channels
+ // are full of strings and we want to do percussion.
+ // FIXME: This does not care about four-op entanglements.
+ for(uint32_t c = 0; c < opl.NumChannels; ++c)
{
- // Collision: Kill old note,
- // UNLESS we're going to do arpeggio
+ uint16_t cs = static_cast<uint16_t>(c);
- MIDIchannel::activenoteiterator i
- ( Ch[j->first.MidCh].activenotes.find( j->first.note ) );
+ if(c > std::numeric_limits<uint32_t>::max())
+ break;
- // Check if we can do arpeggio.
- if((j->second.vibdelay < 70
- || j->second.kon_time_until_neglible > 20000)
- && j->second.ins == ins)
- {
- // Do arpeggio together with this note.
- //doing_arpeggio = true;
+ if(c == from_channel)
continue;
- }
- KillOrEvacuate(c,j,i);
- // ^ will also erase j from ch[c].users.
- }
- }
+ if(opl.four_op_category[c]
+ != opl.four_op_category[from_channel]
+ ) continue;
- // Kill all sustained notes on this channel
- // Don't keep them for arpeggio, because arpeggio requires
- // an intact "activenotes" record. This is a design flaw.
- KillSustainingNotes(-1, c);
+ for(AdlChannel::users_t::iterator
+ m = ch[c].users.begin();
+ m != ch[c].users.end();
+ ++m)
+ {
+ if(m->second.vibdelay >= 200
+ && m->second.kon_time_until_neglible < 10000) continue;
- // Keyoff the channel so that it can be retriggered,
- // unless the new note will be introduced as just an arpeggio.
- if(ch[c].users.empty())
- opl.NoteOff(c);
- }
+ if(m->second.ins != j->second.ins) continue;
- void KillOrEvacuate(
- unsigned from_channel,
- AdlChannel::users_t::iterator j,
- MIDIchannel::activenoteiterator i)
- {
- // Before killing the note, check if it can be
- // evacuated to another channel as an arpeggio
- // instrument. This helps if e.g. all channels
- // are full of strings and we want to do percussion.
- // FIXME: This does not care about four-op entanglements.
- for(unsigned c = 0; c < opl.NumChannels; ++c)
- {
- if(c == from_channel) continue;
- if(opl.four_op_category[c]
- != opl.four_op_category[from_channel]
- ) continue;
- for(AdlChannel::users_t::iterator
- m = ch[c].users.begin();
- m != ch[c].users.end();
- ++m)
- {
- if(m->second.vibdelay >= 200
- && m->second.kon_time_until_neglible < 10000) continue;
- if(m->second.ins != j->second.ins) continue;
-
- // the note can be moved here!
-// UI.IllustrateNote(
-// from_channel,
-// i->second.tone,
-// i->second.midiins, 0, 0.0);
-// UI.IllustrateNote(
-// c,
-// i->second.tone,
-// i->second.midiins,
-// i->second.vol,
-// 0.0);
-
- i->second.phys.erase(from_channel);
- i->second.phys[c] = j->second.ins;
- ch[c].users.insert( *j );
- ch[from_channel].users.erase( j );
- return;
+ // the note can be moved here!
+ // UI.IllustrateNote(
+ // from_channel,
+ // i->second.tone,
+ // i->second.midiins, 0, 0.0);
+ // UI.IllustrateNote(
+ // c,
+ // i->second.tone,
+ // i->second.midiins,
+ // i->second.vol,
+ // 0.0);
+ i->second.phys.erase(static_cast<uint16_t>(from_channel));
+ i->second.phys[cs] = j->second.ins;
+ ch[cs].users.insert(*j);
+ ch[from_channel].users.erase(j);
+ return;
+ }
}
- }
- /*UI.PrintLn(
- "collision @%u: [%ld] <- ins[%3u]",
- c,
- //ch[c].midiins<128?'M':'P', ch[c].midiins&127,
- ch[c].age, //adlins[ch[c].insmeta].ms_sound_kon,
- ins
- );*/
-
- // Kill it
- NoteUpdate(j->first.MidCh,
- i,
- Upd_Off,
- from_channel);
- }
+ /*UI.PrintLn(
+ "collision @%u: [%ld] <- ins[%3u]",
+ c,
+ //ch[c].midiins<128?'M':'P', ch[c].midiins&127,
+ ch[c].age, //adlins[ch[c].insmeta].ms_sound_kon,
+ ins
+ );*/
+ // Kill it
+ NoteUpdate(j->first.MidCh,
+ i,
+ Upd_Off,
+ static_cast<int32_t>(from_channel));
+ }
- void KillSustainingNotes(int MidCh = -1, int this_adlchn = -1)
- {
- unsigned first=0, last=opl.NumChannels;
- if(this_adlchn >= 0) { first=this_adlchn; last=first+1; }
- for(unsigned c = first; c < last; ++c)
+ void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1)
{
- if(ch[c].users.empty()) continue; // Nothing to do
- for(AdlChannel::users_t::iterator
- jnext = ch[c].users.begin();
- jnext != ch[c].users.end();
- )
+ uint32_t first = 0, last = opl.NumChannels;
+
+ if(this_adlchn >= 0)
{
- AdlChannel::users_t::iterator j(jnext++);
- if((MidCh < 0 || j->first.MidCh == MidCh)
- && j->second.sustained)
+ first = static_cast<uint32_t>(this_adlchn);
+ last = first + 1;
+ }
+
+ for(unsigned c = first; c < last; ++c)
+ {
+ if(ch[c].users.empty()) continue; // Nothing to do
+
+ for(AdlChannel::users_t::iterator
+ jnext = ch[c].users.begin();
+ jnext != ch[c].users.end();
+ )
{
- //int midiins = '?';
- //UI.IllustrateNote(c, j->first.note, midiins, 0, 0.0);
- ch[c].users.erase(j);
+ AdlChannel::users_t::iterator j(jnext++);
+
+ if((MidCh < 0 || j->first.MidCh == MidCh)
+ && j->second.sustained)
+ {
+ //int midiins = '?';
+ //UI.IllustrateNote(c, j->first.note, midiins, 0, 0.0);
+ ch[c].users.erase(j);
+ }
}
+
+ // Keyoff the channel, if there are no users left.
+ if(ch[c].users.empty())
+ opl.NoteOff(c);
}
- // Keyoff the channel, if there are no users left.
- if(ch[c].users.empty())
- opl.NoteOff(c);
}
- }
- void SetRPN(unsigned MidCh, unsigned value, bool MSB)
- {
- bool nrpn = Ch[MidCh].nrpn;
- unsigned addr = Ch[MidCh].lastmrpn*0x100 + Ch[MidCh].lastlrpn;
- switch(addr + nrpn*0x10000 + MSB*0x20000)
+ void SetRPN(unsigned MidCh, unsigned value, bool MSB)
{
+ bool nrpn = Ch[MidCh].nrpn;
+ unsigned addr = Ch[MidCh].lastmrpn * 0x100 + Ch[MidCh].lastlrpn;
+
+ switch(addr + nrpn * 0x10000 + MSB * 0x20000)
+ {
case 0x0000 + 0*0x10000 + 1*0x20000: // Pitch-bender sensitivity
- Ch[MidCh].bendsense = value/8192.0;
+ Ch[MidCh].bendsense = value / 8192.0;
break;
+
case 0x0108 + 1*0x10000 + 1*0x20000: // Vibrato speed
if(value == 64)
Ch[MidCh].vibspeed = 1.0;
else if(value < 100)
- Ch[MidCh].vibspeed = 1.0/(1.6e-2*(value?value:1));
+ Ch[MidCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1));
else
- Ch[MidCh].vibspeed = 1.0/(0.051153846*value-3.4965385);
- Ch[MidCh].vibspeed *= 2*3.141592653*5.0;
+ Ch[MidCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385);
+
+ Ch[MidCh].vibspeed *= 2 * 3.141592653 * 5.0;
break;
+
case 0x0109 + 1*0x10000 + 1*0x20000: // Vibrato depth
- Ch[MidCh].vibdepth = ((value-64)*0.15)*0.01;
+ Ch[MidCh].vibdepth = ((value - 64) * 0.15) * 0.01;
break;
+
case 0x010A + 1*0x10000 + 1*0x20000: // Vibrato delay in millisecons
Ch[MidCh].vibdelay =
value ? long(0.2092 * std::exp(0.0795 * value)) : 0.0;
break;
default:/* UI.PrintLn("%s %04X <- %d (%cSB) (ch %u)",
- "NRPN"+!nrpn, addr, value, "LM"[MSB], MidCh);*/ break;
+ "NRPN"+!nrpn, addr, value, "LM"[MSB], MidCh);*/
+ break;
+ }
}
- }
-// void UpdatePortamento(unsigned MidCh)
-// {
-// // mt = 2^(portamento/2048) * (1.0 / 5000.0)
-// /*
-// double mt = std::exp(0.00033845077 * Ch[MidCh].portamento);
-// NoteUpdate_All(MidCh, Upd_Pitch);
-// */
-// //UI.PrintLn("Portamento %u: %u (unimplemented)", MidCh, Ch[MidCh].portamento);
-// }
-
- void NoteUpdate_All(unsigned MidCh, unsigned props_mask)
- {
- for(MIDIchannel::activenoteiterator
- i = Ch[MidCh].activenotes.begin();
- i != Ch[MidCh].activenotes.end();
- )
+ // void UpdatePortamento(unsigned MidCh)
+ // {
+ // // mt = 2^(portamento/2048) * (1.0 / 5000.0)
+ // /*
+ // double mt = std::exp(0.00033845077 * Ch[MidCh].portamento);
+ // NoteUpdate_All(MidCh, Upd_Pitch);
+ // */
+ // //UI.PrintLn("Portamento %u: %u (unimplemented)", MidCh, Ch[MidCh].portamento);
+ // }
+
+ void NoteUpdate_All(uint16_t MidCh, unsigned props_mask)
{
- MIDIchannel::activenoteiterator j(i++);
- NoteUpdate(MidCh, j, props_mask);
+ for(MIDIchannel::activenoteiterator
+ i = Ch[MidCh].activenotes.begin();
+ i != Ch[MidCh].activenotes.end();
+ )
+ {
+ MIDIchannel::activenoteiterator j(i++);
+ NoteUpdate(MidCh, j, props_mask);
+ }
}
- }
- void NoteOff(unsigned MidCh, int note)
- {
- MIDIchannel::activenoteiterator
- i = Ch[MidCh].activenotes.find(note);
- if(i != Ch[MidCh].activenotes.end())
+ void NoteOff(uint16_t MidCh, uint8_t note)
{
- NoteUpdate(MidCh, i, Upd_Off);
+ MIDIchannel::activenoteiterator
+ i = Ch[MidCh].activenotes.find(note);
+
+ if(i != Ch[MidCh].activenotes.end())
+ NoteUpdate(MidCh, i, Upd_Off);
}
- }
- void UpdateVibrato(double amount)
- {
- for(unsigned a=0, b=Ch.size(); a<b; ++a)
- if(Ch[a].vibrato && !Ch[a].activenotes.empty())
- {
- NoteUpdate_All(a, Upd_Pitch);
- Ch[a].vibpos += amount * Ch[a].vibspeed;
- }
- else
- Ch[a].vibpos = 0.0;
- }
+ void UpdateVibrato(double amount)
+ {
+ for(size_t a = 0, b = Ch.size(); a < b; ++a)
+ if(Ch[a].vibrato && !Ch[a].activenotes.empty())
+ {
+ NoteUpdate_All(static_cast<uint16_t>(a), Upd_Pitch);
+ Ch[a].vibpos += amount * Ch[a].vibspeed;
+ }
+ else
+ Ch[a].vibpos = 0.0;
+ }
- void UpdateArpeggio(double /*amount*/) // amount = amount of time passed
- {
- // If there is an adlib channel that has multiple notes
- // simulated on the same channel, arpeggio them.
- #if 0
- const unsigned desired_arpeggio_rate = 40; // Hz (upper limit)
- #if 1
- static unsigned cache=0;
- amount=amount; // Ignore amount. Assume we get a constant rate.
- cache += MaxSamplesAtTime * desired_arpeggio_rate;
- if(cache < PCM_RATE) return;
- cache %= PCM_RATE;
- #else
- static double arpeggio_cache = 0;
- arpeggio_cache += amount * desired_arpeggio_rate;
- if(arpeggio_cache < 1.0) return;
- arpeggio_cache = 0.0;
- #endif
- #endif
- static unsigned arpeggio_counter = 0;
- ++arpeggio_counter;
-
- for(unsigned c = 0; c < opl.NumChannels; ++c)
+ void UpdateArpeggio(double /*amount*/) // amount = amount of time passed
{
- retry_arpeggio:;
- size_t n_users = ch[c].users.size();
- /*if(true)
- {
- UI.GotoXY(64,c+1); UI.Color(2);
- std::fprintf(stderr, "%7ld/%7ld,%3u\r",
- ch[c].keyoff,
- (unsigned) n_users);
- UI.x = 0;
- }*/
- if(n_users > 1)
+ // If there is an adlib channel that has multiple notes
+ // simulated on the same channel, arpeggio them.
+#if 0
+ const unsigned desired_arpeggio_rate = 40; // Hz (upper limit)
+#if 1
+ static unsigned cache = 0;
+ amount = amount; // Ignore amount. Assume we get a constant rate.
+ cache += MaxSamplesAtTime * desired_arpeggio_rate;
+
+ if(cache < PCM_RATE) return;
+
+ cache %= PCM_RATE;
+#else
+ static double arpeggio_cache = 0;
+ arpeggio_cache += amount * desired_arpeggio_rate;
+
+ if(arpeggio_cache < 1.0) return;
+
+ arpeggio_cache = 0.0;
+#endif
+#endif
+ static unsigned arpeggio_counter = 0;
+ ++arpeggio_counter;
+
+ for(uint32_t c = 0; c < opl.NumChannels; ++c)
{
- AdlChannel::users_t::const_iterator i = ch[c].users.begin();
- size_t rate_reduction = 3;
- if(n_users >= 3) rate_reduction = 2;
- if(n_users >= 4) rate_reduction = 1;
- std::advance(i, (arpeggio_counter / rate_reduction) % n_users);
- if(i->second.sustained == false)
+retry_arpeggio:
+
+ if(c > std::numeric_limits<int32_t>::max())
+ break;
+
+ size_t n_users = ch[c].users.size();
+
+ /*if(true)
+ {
+ UI.GotoXY(64,c+1); UI.Color(2);
+ std::fprintf(stderr, "%7ld/%7ld,%3u\r",
+ ch[c].keyoff,
+ (unsigned) n_users);
+ UI.x = 0;
+ }*/
+ if(n_users > 1)
{
- if(i->second.kon_time_until_neglible <= 0l)
+ AdlChannel::users_t::const_iterator i = ch[c].users.begin();
+ size_t rate_reduction = 3;
+
+ if(n_users >= 3) rate_reduction = 2;
+
+ if(n_users >= 4) rate_reduction = 1;
+
+ std::advance(i, (arpeggio_counter / rate_reduction) % n_users);
+
+ if(i->second.sustained == false)
{
+ if(i->second.kon_time_until_neglible <= 0l)
+ {
+ NoteUpdate(
+ i->first.MidCh,
+ Ch[ i->first.MidCh ].activenotes.find(i->first.note),
+ Upd_Off,
+ static_cast<int32_t>(c));
+ goto retry_arpeggio;
+ }
+
NoteUpdate(
i->first.MidCh,
- Ch[ i->first.MidCh ].activenotes.find( i->first.note ),
- Upd_Off,
- c);
- goto retry_arpeggio;
+ Ch[ i->first.MidCh ].activenotes.find(i->first.note),
+ Upd_Pitch | Upd_Volume | Upd_Pan,
+ static_cast<int32_t>(c));
}
- NoteUpdate(
- i->first.MidCh,
- Ch[ i->first.MidCh ].activenotes.find( i->first.note ),
- Upd_Pitch | Upd_Volume | Upd_Pan,
- c);
}
}
}
- }
-public:
- unsigned ChooseDevice(const std::string& name)
- {
- std::map<std::string, unsigned>::iterator i = devices.find(name);
- if(i != devices.end()) return i->second;
- size_t n = devices.size() * 16;
- devices.insert( std::make_pair(name, n) );
- Ch.resize(n+16);
- return n;
- }
+ public:
+ uint64_t ChooseDevice(const std::string &name)
+ {
+ std::map<std::string, uint64_t>::iterator i = devices.find(name);
+
+ if(i != devices.end())
+ return i->second;
+
+ size_t n = devices.size() * 16;
+ devices.insert(std::make_pair(name, n));
+ Ch.resize(n + 16);
+ return n;
+ }
};
#ifdef ADLMIDI_buildAsApp
static std::deque<short> AudioBuffer;
static MutexType AudioBuffer_lock;
-static void SDL_AudioCallbackX(void*, Uint8* stream, int len)
+static void SDL_AudioCallbackX(void *, Uint8 *stream, int len)
{
SDL_LockAudio();
- short* target = (short*) stream;
+ short *target = (short *) stream;
AudioBuffer_lock.Lock();
/*if(len != AudioBuffer.size())
fprintf(stderr, "len=%d stereo samples, AudioBuffer has %u stereo samples",
len/4, (unsigned) AudioBuffer.size()/2);*/
- unsigned ate = len/2; // number of shorts
+ unsigned ate = len / 2; // number of shorts
+
if(ate > AudioBuffer.size()) ate = AudioBuffer.size();
- for(unsigned a=0; a<ate; ++a)
- {
+
+ for(unsigned a = 0; a < ate; ++a)
target[a] = AudioBuffer[a];
- }
+
AudioBuffer.erase(AudioBuffer.begin(), AudioBuffer.begin() + ate);
AudioBuffer_lock.Unlock();
SDL_UnlockAudio();
@@ -2044,52 +2418,58 @@ struct FourChars
{
char ret[4];
- FourChars(const char* s)
+ FourChars(const char *s)
{
- for(unsigned c=0; c<4; ++c) ret[c] = s[c];
+ for(unsigned c = 0; c < 4; ++c)
+ ret[c] = s[c];
}
FourChars(unsigned w) // Little-endian
{
- for(unsigned c=0; c<4; ++c) ret[c] = (w >> (c*8)) & 0xFF;
+ for(unsigned c = 0; c < 4; ++c)
+ ret[c] = static_cast<int8_t>((w >>(c * 8)) & 0xFF);
}
};
-int adlRefreshNumCards(ADL_MIDIPlayer* device)
+int adlRefreshNumCards(ADL_MIDIPlayer *device)
{
- unsigned n_fourop[2] = {0,0}, n_total[2] = {0,0};
- for(unsigned a=0; a<256; ++a)
+ unsigned n_fourop[2] = {0, 0}, n_total[2] = {0, 0};
+
+ for(unsigned a = 0; a < 256; ++a)
{
unsigned insno = banks[device->AdlBank][a];
+
if(insno == 198) continue;
- ++n_total[a/128];
+
+ ++n_total[a / 128];
+
if(adlins[insno].adlno1 != adlins[insno].adlno2)
- ++n_fourop[a/128];
+ ++n_fourop[a / 128];
}
device->NumFourOps =
- (n_fourop[0] >= n_total[0]*7/8) ? device->NumCards * 6
- : (n_fourop[0] < n_total[0]*1/8) ? 0
- : (device->NumCards==1 ? 1 : device->NumCards*4);
-
- ((MIDIplay*)device->adl_midiPlayer)->opl.NumFourOps=device->NumFourOps;
+ (n_fourop[0] >= n_total[0] * 7 / 8) ? device->NumCards * 6
+ : (n_fourop[0] < n_total[0] * 1 / 8) ? 0
+ : (device->NumCards == 1 ? 1 : device->NumCards * 4);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.NumFourOps = device->NumFourOps;
- if(n_fourop[0] >= n_total[0]*15/16 && device->NumFourOps == 0)
+ if(n_fourop[0] >= n_total[0] * 15 / 16 && device->NumFourOps == 0)
{
ADLMIDI_ErrorString = "ERROR: You have selected a bank that consists almost exclusively of four-op patches.\n"
- " The results (silence + much cpu load) would be probably\n"
- " not what you want, therefore ignoring the request.\n";
+ " The results (silence + much cpu load) would be probably\n"
+ " not what you want, therefore ignoring the request.\n";
return -1;
}
+
return 0;
}
/*---------------------------EXPORTS---------------------------*/
-ADLMIDI_EXPORT struct ADL_MIDIPlayer* adl_init(long sample_rate)
+ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate)
{
- ADL_MIDIPlayer* _device;
- _device = (ADL_MIDIPlayer*)malloc(sizeof(ADL_MIDIPlayer));
- _device->PCM_RATE = sample_rate;
+ ADL_MIDIPlayer *_device;
+ _device = (ADL_MIDIPlayer *)malloc(sizeof(ADL_MIDIPlayer));
+ _device->PCM_RATE = static_cast<unsigned long>(sample_rate);
_device->AdlBank = 0;
_device->NumFourOps = 7;
_device->NumCards = 2;
@@ -2101,14 +2481,13 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer* adl_init(long sample_rate)
_device->SkipForward = 0;
_device->QuitWithoutLooping = 0;
_device->ScaleModulators = 0;
- _device->delay=0.0;
- _device->carry=0.0;
+ _device->delay = 0.0;
+ _device->carry = 0.0;
_device->mindelay = 1.0 / (double)_device->PCM_RATE;
_device->maxdelay = 512.0 / (double)_device->PCM_RATE;
-
- _device->stored_samples=0;
- _device->backup_samples_size=0;
- MIDIplay* player = new MIDIplay;
+ _device->stored_samples = 0;
+ _device->backup_samples_size = 0;
+ MIDIplay *player = new MIDIplay;
_device->adl_midiPlayer = player;
player->config = _device;
player->opl._parent = _device;
@@ -2127,34 +2506,41 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer* adl_init(long sample_rate)
ADLMIDI_EXPORT int adl_setNumCards(ADL_MIDIPlayer *device, int numCards)
{
- if(device==NULL) return -2;
- device->NumCards = numCards;
- ((MIDIplay*)device->adl_midiPlayer)->opl.NumCards=device->NumCards;
+ if(device == NULL) return -2;
+
+ device->NumCards = static_cast<unsigned int>(numCards);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.NumCards = device->NumCards;
+
if(device->NumCards < 1 || device->NumCards > MaxCards)
{
std::stringstream s;
- s<<"number of cards may only be 1.."<< MaxCards<<".\n";
+ s << "number of cards may only be 1.." << MaxCards << ".\n";
ADLMIDI_ErrorString = s.str();
return -1;
}
+
return adlRefreshNumCards(device);
}
ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank)
{
- const unsigned NumBanks = maxAdlBanks();
- int bankno = bank;
+ const uint32_t NumBanks = static_cast<uint32_t>(maxAdlBanks());
+ int32_t bankno = bank;
+
if(bankno < 0)
bankno = 0;
- device->AdlBank = bankno;
- ((MIDIplay*)device->adl_midiPlayer)->opl.AdlBank=device->AdlBank;
+
+ device->AdlBank = static_cast<uint32_t>(bankno);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.AdlBank = device->AdlBank;
+
if(device->AdlBank >= NumBanks)
{
std::stringstream s;
- s<<"bank number may only be 0.."<< (NumBanks-1)<<".\n";
+ s << "bank number may only be 0.." << (NumBanks - 1) << ".\n";
ADLMIDI_ErrorString = s.str();
return -1;
}
+
return adlRefreshNumCards(device);
}
@@ -2163,22 +2549,24 @@ ADLMIDI_EXPORT int adl_getBanksCount()
return maxAdlBanks();
}
-ADLMIDI_EXPORT const char * const* adl_getBankNames()
+ADLMIDI_EXPORT const char *const *adl_getBankNames()
{
return banknames;
}
ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4)
{
- device->NumFourOps = ops4;
- ((MIDIplay*)device->adl_midiPlayer)->opl.NumFourOps=device->NumFourOps;
+ device->NumFourOps = static_cast<unsigned int>(ops4);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.NumFourOps = device->NumFourOps;
+
if(device->NumFourOps > 6 * device->NumCards)
{
std::stringstream s;
- s<<"number of four-op channels may only be 0.."<<(6*(device->NumCards))<<" when "<<device->NumCards<<" OPL3 cards are used.\n";
+ s << "number of four-op channels may only be 0.." << (6 * (device->NumCards)) << " when " << device->NumCards << " OPL3 cards are used.\n";
ADLMIDI_ErrorString = s.str();
return -1;
}
+
return adlRefreshNumCards(device);
}
@@ -2186,77 +2574,93 @@ ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4)
ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod)
{
if(!device) return;
- device->AdlPercussionMode=percmod;
- ((MIDIplay*)device->adl_midiPlayer)->opl.AdlPercussionMode=(bool)device->AdlPercussionMode;
+
+ device->AdlPercussionMode = static_cast<unsigned int>(percmod);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.AdlPercussionMode = (bool)device->AdlPercussionMode;
}
ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro)
{
if(!device) return;
- device->HighVibratoMode=hvibro;
- ((MIDIplay*)device->adl_midiPlayer)->opl.HighVibratoMode=(bool)device->HighVibratoMode;
+
+ device->HighVibratoMode = static_cast<unsigned int>(hvibro);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.HighVibratoMode = (bool)device->HighVibratoMode;
}
ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo)
{
if(!device) return;
- device->HighTremoloMode=htremo;
- ((MIDIplay*)device->adl_midiPlayer)->opl.HighTremoloMode=(bool)device->HighTremoloMode;
+
+ device->HighTremoloMode = static_cast<unsigned int>(htremo);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.HighTremoloMode = (bool)device->HighTremoloMode;
}
ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod)
{
if(!device) return;
- device->ScaleModulators=smod;
- ((MIDIplay*)device->adl_midiPlayer)->opl.ScaleModulators=(bool)device->ScaleModulators;
+
+ device->ScaleModulators = static_cast<unsigned int>(smod);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.ScaleModulators = (bool)device->ScaleModulators;
}
ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn)
{
if(!device) return;
- device->QuitWithoutLooping=(int)(!(bool)loopEn);
+
+ device->QuitWithoutLooping = (loopEn != 0);
}
-ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer* device, int logvol)
+ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol)
{
if(!device) return;
- device->LogarithmicVolumes = logvol;
- ((MIDIplay*)device->adl_midiPlayer)->opl.LogarithmicVolumes = (bool)device->LogarithmicVolumes;
+
+ device->LogarithmicVolumes = static_cast<unsigned int>(logvol);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.LogarithmicVolumes = (bool)device->LogarithmicVolumes;
}
ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, char *filePath)
{
ADLMIDI_ErrorString.clear();
+
if(device && device->adl_midiPlayer)
{
- device->stored_samples=0;
- device->backup_samples_size=0;
- if(!((MIDIplay *)device->adl_midiPlayer)->LoadMIDI(filePath))
+ device->stored_samples = 0;
+ device->backup_samples_size = 0;
+
+ if(!reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->LoadMIDI(filePath))
{
if(ADLMIDI_ErrorString.empty())
- ADLMIDI_ErrorString="ADL MIDI: Can't load file";
+ ADLMIDI_ErrorString = "ADL MIDI: Can't load file";
+
return -1;
- } else return 0;
+ }
+ else return 0;
}
- ADLMIDI_ErrorString="Can't load file: ADL MIDI is not initialized";
+
+ ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized";
return -1;
}
-ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer* device, void *mem, long size)
+ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, void *mem, long size)
{
ADLMIDI_ErrorString.clear();
+
if(device && device->adl_midiPlayer)
{
- device->stored_samples=0;
- device->backup_samples_size=0;
- if(!((MIDIplay *)device->adl_midiPlayer)->LoadMIDI(mem, size))
+ device->stored_samples = 0;
+ device->backup_samples_size = 0;
+
+ if(!reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->LoadMIDI(mem, static_cast<size_t>(size)))
{
if(ADLMIDI_ErrorString.empty())
- ADLMIDI_ErrorString="ADL MIDI: Can't load data from memory";
+ ADLMIDI_ErrorString = "ADL MIDI: Can't load data from memory";
+
return -1;
- } else return 0;
+ }
+ else return 0;
}
- ADLMIDI_ErrorString="Can't load file: ADL MIDI is not initialized";
+
+ ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized";
return -1;
}
@@ -2269,96 +2673,139 @@ ADLMIDI_EXPORT const char *adl_errorString()
ADLMIDI_EXPORT const char *adl_getMusicTitle(ADL_MIDIPlayer *device)
{
if(!device) return "";
- return ((MIDIplay*)(device->adl_midiPlayer))->musTitle.c_str();
+
+ return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->musTitle.c_str();
}
ADLMIDI_EXPORT void adl_close(ADL_MIDIPlayer *device)
{
if(device->adl_midiPlayer)
- {
- delete ((MIDIplay*)(device->adl_midiPlayer));
- }
+ delete reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
+
device->adl_midiPlayer = NULL;
free(device);
- device=NULL;
+ device = NULL;
}
ADLMIDI_EXPORT void adl_reset(ADL_MIDIPlayer *device)
{
if(!device) return;
- device->stored_samples=0;
- device->backup_samples_size=0;
- ((MIDIplay*)device->adl_midiPlayer)->opl.Reset();
+
+ device->stored_samples = 0;
+ device->backup_samples_size = 0;
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.Reset();
}
-inline static void SendStereoAudio(ADL_MIDIPlayer*device,
- int& samples_requested,
- unsigned long& in_size,
- int* _in,
- int out_pos,
- short* _out)
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+inline static void SendStereoAudio(ADL_MIDIPlayer *device,
+ int &samples_requested,
+ ssize_t &in_size,
+ int *_in,
+ ssize_t out_pos,
+ short *_out)
{
if(!in_size) return;
- device->stored_samples=0;
- int out;
- int offset;
- for(unsigned long p = 0; p < in_size; ++p)
+
+ device->stored_samples = 0;
+ ssize_t out;
+ ssize_t offset;
+ ssize_t pos = static_cast<ssize_t>(out_pos);
+
+ for(ssize_t p = 0; p < in_size; ++p)
{
- for(unsigned w=0; w<2; ++w)
+ for(ssize_t w = 0; w < 2; ++w)
{
- out = _in[p*2+w];
- offset = out_pos+p*2+w;
- if(offset<samples_requested)
- {
- _out[offset] = (short)out;
- }
+ out = _in[p * 2 + w];
+ offset = pos + p * 2 + w;
+
+ if(offset < samples_requested)
+ _out[offset] = static_cast<short>(out);
else
{
- device->backup_samples[device->backup_samples_size]=out;
- device->backup_samples_size++; device->stored_samples++;
+ device->backup_samples[device->backup_samples_size] = static_cast<short>(out);
+ device->backup_samples_size++;
+ device->stored_samples++;
}
}
}
}
+#else
+inline static void SendStereoAudio(ADL_MIDIPlayer *device,
+ int &samples_requested,
+ ssize_t &in_size,
+ short *_in,
+ ssize_t out_pos,
+ short *_out)
+{
+ if(!in_size)
+ return;
+
+ device->stored_samples = 0;
+ size_t offset = static_cast<size_t>(out_pos);
+ size_t inSamples = static_cast<size_t>(in_size * 2);
+ size_t maxSamples = static_cast<size_t>(samples_requested) - offset;
+ size_t toCopy = std::min(maxSamples, inSamples);
+ memcpy(_out + out_pos, _in, toCopy * sizeof(short));
+
+ if(maxSamples < inSamples)
+ {
+ size_t appendSize = inSamples - maxSamples;
+ memcpy(device->backup_samples + device->backup_samples_size,
+ maxSamples + _in, appendSize * sizeof(short));
+ device->backup_samples_size += appendSize;
+ device->stored_samples += appendSize;
+ }
+}
+#endif
-ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer*device, int sampleCount, short *out)
+ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out)
{
if(!device) return 0;
- sampleCount -= sampleCount%2;//Avoid non-odd sample requests
- if(sampleCount<0) return 0;
- if(device->QuitFlag) return 0;
- int gotten_len=0;
+ sampleCount -= sampleCount % 2; //Avoid even sample requests
+
+ if(sampleCount < 0) return 0;
+
+ if(device->QuitFlag) return 0;
- unsigned long n_samples=512;
- unsigned long n_samples_2=n_samples*2;
+ ssize_t gotten_len = 0;
+ ssize_t n_samples = 512;
+ ssize_t n_samples_2 = n_samples * 2;
int left = sampleCount;
- while(left>0)
+
+ while(left > 0)
{
- if(device->backup_samples_size>0)
- { //Send reserved samples if exist
- int ate=0;
- while((ate<device->backup_samples_size) && (ate<left))
+ if(device->backup_samples_size > 0)
+ {
+ //Send reserved samples if exist
+ ssize_t ate = 0;
+
+ while((ate < device->backup_samples_size) && (ate < left))
{
- out[ate]=(short)device->backup_samples[ate]; ate++;
+ out[ate] = device->backup_samples[ate];
+ ate++;
}
- left-=ate;
- gotten_len+=ate;
- if(ate<device->backup_samples_size)
+
+ left -= ate;
+ gotten_len += ate;
+
+ if(ate < device->backup_samples_size)
{
- for(int j=0;
- j<ate;
- j++)
- device->backup_samples[(ate-1)-j]=device->backup_samples[(device->backup_samples_size-1)-j];
+ for(int j = 0;
+ j < ate;
+ j++)
+ device->backup_samples[(ate - 1) - j] = device->backup_samples[(device->backup_samples_size - 1) - j];
}
- device->backup_samples_size-=ate;
- } else {
+
+ device->backup_samples_size -= ate;
+ }
+ else
+ {
const double eat_delay = device->delay < device->maxdelay ? device->delay : device->maxdelay;
device->delay -= eat_delay;
-
device->carry += device->PCM_RATE * eat_delay;
- n_samples = (unsigned) device->carry;
+ n_samples = static_cast<ssize_t>(device->carry);
device->carry -= n_samples;
if(device->SkipForward > 0)
@@ -2366,47 +2813,72 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer*device, int sampleCount, short *out)
else
{
left -= n_samples_2;
- int buf[n_samples*2];
- unsigned long in_count=n_samples;
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+ std::vector<int> buf; //int buf[n_samples * 2];
+#else
+ std::vector<int16_t> buf;
+#endif
+ buf.resize(1024 /*n_samples * 2*/);
+ ssize_t in_count = (n_samples > 512) ? 512 : n_samples;
+ //fill buffer with zeros
+ size_t in_countSamples = static_cast<size_t>(in_count * 2);
if(device->NumCards == 1)
{
- ((MIDIplay*)(device->adl_midiPlayer))->opl.cards[0].GenerateArr(buf, &in_count);
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+ Bitu cnt = static_cast<Bitu>(in_count);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.cards[0].GenerateArr(buf.data(), &cnt);
+ in_count = static_cast<ssize_t>(cnt);
+#else
+ OPL3_GenerateStream(&(reinterpret_cast<MIDIplay *>(device->adl_midiPlayer))->opl.cards[0], buf.data(), static_cast<Bit32u>(in_count));
+#endif
/* Process it */
- SendStereoAudio(device, sampleCount, in_count, buf, gotten_len, out);
+ SendStereoAudio(device, sampleCount, in_count, buf.data(), gotten_len, out);
}
else if(n_samples > 0)
{
- int in[n_samples*2];
-
- //fill buffer with zeros
- for(unsigned long a=0; a<(in_count*2); ++a) buf[a] = 0;
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+ std::vector<int> in;
+ in.resize(1024); /*n_samples * 2 */
+ ssize_t in_count = n_samples;
+#endif
+ memset(buf.data(), 0, in_countSamples * sizeof(short));
- unsigned long in_count = n_samples;
for(unsigned card = 0; card < device->NumCards; ++card)
{
- ((MIDIplay*)(device->adl_midiPlayer))->opl.cards[card].GenerateArr( in, &in_count );
- for(unsigned long a=0; a<(in_count*2); ++a)
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+ Bitu cnt = static_cast<Bitu>(in_count);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.cards[card].GenerateArr(in.data(), &cnt);
+ in_count = static_cast<ssize_t>(cnt);
+ size_t in_sCount = static_cast<size_t>(in_count * 2);
+
+ for(size_t a = 0; a < in_sCount; ++a)
buf[a] += in[a];
+
+#else
+ OPL3_GenerateStreamMix(&(reinterpret_cast<MIDIplay *>(device->adl_midiPlayer))->opl.cards[card], buf.data(), static_cast<Bit32u>(in_count));
+#endif
}
/* Process it */
- SendStereoAudio(device, sampleCount, in_count, buf, gotten_len, out);
+ SendStereoAudio(device, sampleCount, in_count, buf.data(), gotten_len, out);
}
- gotten_len += (n_samples*2)-device->stored_samples;
+
+ gotten_len += (n_samples * 2) - device->stored_samples;
}
- device->delay = ((MIDIplay*)device->adl_midiPlayer)->Tick(eat_delay, device->mindelay);
+
+ device->delay = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->Tick(eat_delay, device->mindelay);
}
}
- return gotten_len;
+
+ return static_cast<int>(gotten_len);
}
#ifdef ADLMIDI_buildAsApp
-int main(int argc, char** argv)
+int main(int argc, char **argv)
{
-
if(argc < 2 || std::string(argv[1]) == "--help" || std::string(argv[1]) == "-h")
{
std::printf(
@@ -2418,11 +2890,13 @@ int main(int argc, char** argv)
" -nl Quit without looping\n"
" -w Write WAV file rather than playing\n"
);
- for(unsigned a=0; a<sizeof(banknames)/sizeof(*banknames); ++a)
+
+ for(unsigned a = 0; a < sizeof(banknames) / sizeof(*banknames); ++a)
std::printf("%10s%2u = %s\n",
- a?"":"Banks:",
- a,
- banknames[a]);
+ a ? "" : "Banks:",
+ a,
+ banknames[a]);
+
std::printf(
" Use banks 2-5 to play Descent \"q\" soundtracks.\n"
" Look up the relevant bank number from descent.sng.\n"
@@ -2433,7 +2907,7 @@ int main(int argc, char** argv)
" The Doom & Hexen sets require one or two, while\n"
" Miles four-op set requires the maximum of numcards*6.\n"
"\n"
- );
+ );
return 0;
}
@@ -2449,7 +2923,6 @@ int main(int argc, char** argv)
// the sum of these two buffers.
SDL_AudioSpec spec;
SDL_AudioSpec obtained;
-
spec.freq = 44100;
spec.format = AUDIO_S16SYS;
spec.channels = 2;
@@ -2462,20 +2935,21 @@ int main(int argc, char** argv)
std::fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
//return 1;
}
+
if(spec.samples != obtained.samples)
std::fprintf(stderr, "Wanted (samples=%u,rate=%u,channels=%u); obtained (samples=%u,rate=%u,channels=%u)\n",
- spec.samples, spec.freq, spec.channels,
- obtained.samples,obtained.freq,obtained.channels);
+ spec.samples, spec.freq, spec.channels,
+ obtained.samples, obtained.freq, obtained.channels);
- ADL_MIDIPlayer* myDevice;
+ ADL_MIDIPlayer *myDevice;
myDevice = adl_init(44100);
- if(myDevice==NULL)
+
+ if(myDevice == NULL)
{
std::fprintf(stderr, "Failed to init MIDI device!\n");
return 1;
}
-
while(argc > 2)
{
bool had_option = false;
@@ -2493,16 +2967,17 @@ int main(int argc, char** argv)
else break;
std::copy(argv + (had_option ? 4 : 3), argv + argc,
- argv+2);
+ argv + 2);
argc -= (had_option ? 2 : 1);
}
if(argc >= 3)
{
int bankno = std::atoi(argv[2]);
- if(adl_setBank(myDevice, bankno)!=0)
+
+ if(adl_setBank(myDevice, bankno) != 0)
{
- std::fprintf(stderr,"%s", adl_errorString());
+ std::fprintf(stderr, "%s", adl_errorString());
return 0;
}
}
@@ -2515,16 +2990,17 @@ int main(int argc, char** argv)
return 0;
}
}
+
if(argc >= 5)
{
- if(adl_setNumFourOpsChn(myDevice, std::atoi(argv[4]))!=0)
+ if(adl_setNumFourOpsChn(myDevice, std::atoi(argv[4])) != 0)
{
std::fprintf(stderr, "%s\n", adl_errorString());
return 0;
}
}
- if(adl_openFile(myDevice, argv[1])!=0)
+ if(adl_openFile(myDevice, argv[1]) != 0)
{
std::fprintf(stderr, "%s\n", adl_errorString());
return 2;
@@ -2535,21 +3011,22 @@ int main(int argc, char** argv)
while(1)
{
int buff[4096];
- unsigned long gotten=adl_play(myDevice, 4096, buff);
- if(gotten<=0) break;
+ unsigned long gotten = adl_play(myDevice, 4096, buff);
+
+ if(gotten <= 0) break;
AudioBuffer_lock.Lock();
- size_t pos = AudioBuffer.size();
- AudioBuffer.resize(pos + gotten);
- for(unsigned long p = 0; p < gotten; ++p)
- AudioBuffer[pos+p] = buff[p];
+ size_t pos = AudioBuffer.size();
+ AudioBuffer.resize(pos + gotten);
+
+ for(unsigned long p = 0; p < gotten; ++p)
+ AudioBuffer[pos + p] = buff[p];
+
AudioBuffer_lock.Unlock();
+ const SDL_AudioSpec &spec_ = obtained;
- const SDL_AudioSpec& spec_ = obtained;
- while(AudioBuffer.size() > spec_.samples + (spec_.freq*2) * OurHeadRoomLength)
- {
+ while(AudioBuffer.size() > spec_.samples + (spec_.freq * 2) * OurHeadRoomLength)
SDL_Delay(1);
- }
}
adl_close(myDevice);
@@ -2557,4 +3034,3 @@ int main(int argc, char** argv)
return 0;
}
#endif
-
diff --git a/src/adlmidi.h b/src/adlmidi.h
index c9aaa3e..e945f29 100644
--- a/src/adlmidi.h
+++ b/src/adlmidi.h
@@ -28,7 +28,8 @@
extern "C" {
#endif
-struct ADL_MIDIPlayer {
+struct ADL_MIDIPlayer
+{
unsigned int AdlBank;
unsigned int NumFourOps;
unsigned int NumCards;
@@ -49,9 +50,9 @@ struct ADL_MIDIPlayer {
double maxdelay;
/*For internal usage*/
- int stored_samples; //num of collected samples
- int backup_samples[1024]; //Backup sample storage.
- int backup_samples_size; //Backup sample storage.
+ int stored_samples; //num of collected samples
+ short backup_samples[1024]; //Backup sample storage.
+ int backup_samples_size; //Backup sample storage.
/*For internal usage*/
void *adl_midiPlayer;
@@ -59,62 +60,61 @@ struct ADL_MIDIPlayer {
};
/* Sets number of emulated sound cards (from 1 to 100). Emulation of multiple sound cards exchanges polyphony limits*/
-extern int adl_setNumCards(struct ADL_MIDIPlayer*device, int numCards);
+extern int adl_setNumCards(struct ADL_MIDIPlayer *device, int numCards);
/* Sets a number of the patches bank from 0 to N banks */
-extern int adl_setBank(struct ADL_MIDIPlayer* device, int bank);
+extern int adl_setBank(struct ADL_MIDIPlayer *device, int bank);
/* Returns total number of available banks */
extern int adl_getBanksCount();
/* Returns pointer to array of names of every bank */
-extern const char * const* adl_getBankNames();
+extern const char *const *adl_getBankNames();
/*Sets number of 4-chan operators*/
-extern int adl_setNumFourOpsChn(struct ADL_MIDIPlayer*device, int ops4);
+extern int adl_setNumFourOpsChn(struct ADL_MIDIPlayer *device, int ops4);
/*Enable or disable AdLib percussion mode*/
-extern void adl_setPercMode(struct ADL_MIDIPlayer* device, int percmod);
+extern void adl_setPercMode(struct ADL_MIDIPlayer *device, int percmod);
/*Enable or disable deep vibrato*/
-extern void adl_setHVibrato(struct ADL_MIDIPlayer* device, int hvibro);
+extern void adl_setHVibrato(struct ADL_MIDIPlayer *device, int hvibro);
/*Enable or disable deep tremolo*/
-extern void adl_setHTremolo(struct ADL_MIDIPlayer* device, int htremo);
+extern void adl_setHTremolo(struct ADL_MIDIPlayer *device, int htremo);
/*Enable or disable Enables scaling of modulator volumes*/
-extern void adl_setScaleModulators(struct ADL_MIDIPlayer* device, int smod);
+extern void adl_setScaleModulators(struct ADL_MIDIPlayer *device, int smod);
/*Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part)*/
-extern void adl_setLoopEnabled(struct ADL_MIDIPlayer* device, int loopEn);
+extern void adl_setLoopEnabled(struct ADL_MIDIPlayer *device, int loopEn);
/*Enable or disable Logariphmic volume changer */
-extern void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer* device, int logvol);
+extern void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol);
/*Returns string which contains last error message*/
-extern const char* adl_errorString();
+extern const char *adl_errorString();
/*Initialize ADLMIDI Player device*/
-extern struct ADL_MIDIPlayer* adl_init(long sample_rate);
+extern struct ADL_MIDIPlayer *adl_init(long sample_rate);
/*Load MIDI file from File System*/
-extern int adl_openFile(struct ADL_MIDIPlayer* device, char *filePath);
+extern int adl_openFile(struct ADL_MIDIPlayer *device, char *filePath);
/*Load MIDI file from memory data*/
-extern int adl_openData(struct ADL_MIDIPlayer* device, void* mem, long size);
+extern int adl_openData(struct ADL_MIDIPlayer *device, void *mem, long size);
/*Resets MIDI player*/
-extern void adl_reset(struct ADL_MIDIPlayer*device);
+extern void adl_reset(struct ADL_MIDIPlayer *device);
/*Close and delete ADLMIDI device*/
extern void adl_close(struct ADL_MIDIPlayer *device);
/*Take a sample buffer*/
-extern int adl_play(struct ADL_MIDIPlayer*device, int sampleCount, short out[]);
+extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short out[]);
#ifdef __cplusplus
- }
+}
#endif
#endif // ADLMIDI_H
-
diff --git a/src/dbopl.cpp b/src/dbopl.cpp
index 809d394..e21fde0 100644
--- a/src/dbopl.cpp
+++ b/src/dbopl.cpp
@@ -1,3 +1,5 @@
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+
#ifdef __MINGW32__
typedef struct vswprintf {} swprintf;
#endif
@@ -20,19 +22,19 @@ typedef struct vswprintf {} swprintf;
*/
/*
- DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator.
- Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2
- Except for the table generation it's all integer math
- Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms
- The generation was based on the MAME implementation but tried to have it use less memory and be faster in general
- MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times
-
- //TODO Don't delay first operator 1 sample in opl3 mode
- //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter
- //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though?
- //TODO Check if having the same accuracy in all frequency multipliers sounds better or not
-
- //DUNNO Keyon in 4op, switch to 2op without keyoff.
+ DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator.
+ Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2
+ Except for the table generation it's all integer math
+ Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms
+ The generation was based on the MAME implementation but tried to have it use less memory and be faster in general
+ MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times
+
+ //TODO Don't delay first operator 1 sample in opl3 mode
+ //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter
+ //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though?
+ //TODO Check if having the same accuracy in all frequency multipliers sounds better or not
+
+ //DUNNO Keyon in 4op, switch to 2op without keyoff.
*/
/* $Id: dbopl.cpp,v 1.10 2009-06-10 19:54:51 harekiet Exp $ */
@@ -48,1528 +50,1890 @@ typedef struct vswprintf {} swprintf;
#define PI 3.14159265358979323846
#endif
-namespace DBOPL {
+namespace DBOPL
+{
-#define OPLRATE ((double)(14318180.0 / 288.0))
+#define OPLRATE ((double)(14318180.0 / 288.0))
#define TREMOLO_TABLE 52
-//Try to use most precision for frequencies
-//Else try to keep different waves in synch
-//#define WAVE_PRECISION 1
+ //Try to use most precision for frequencies
+ //Else try to keep different waves in synch
+ //#define WAVE_PRECISION 1
#ifndef WAVE_PRECISION
-//Wave bits available in the top of the 32bit range
-//Original adlib uses 10.10, we use 10.22
-#define WAVE_BITS 10
+ //Wave bits available in the top of the 32bit range
+ //Original adlib uses 10.10, we use 10.22
+#define WAVE_BITS 10
#else
-//Need some extra bits at the top to have room for octaves and frequency multiplier
-//We support to 8 times lower rate
-//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits
-#define WAVE_BITS 14
+ //Need some extra bits at the top to have room for octaves and frequency multiplier
+ //We support to 8 times lower rate
+ //128 * 15 * 8 = 15350, 2^13.9, so need 14 bits
+#define WAVE_BITS 14
#endif
-#define WAVE_SH ( 32 - WAVE_BITS )
-#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 )
+#define WAVE_SH ( 32 - WAVE_BITS )
+#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 )
-//Use the same accuracy as the waves
+ //Use the same accuracy as the waves
#define LFO_SH ( WAVE_SH - 10 )
-//LFO is controlled by our tremolo 256 sample limit
+ //LFO is controlled by our tremolo 256 sample limit
#define LFO_MAX ( 256 << ( LFO_SH ) )
-//Maximum amount of attenuation bits
-//Envelope goes to 511, 9 bits
+ //Maximum amount of attenuation bits
+ //Envelope goes to 511, 9 bits
#if (DBOPL_WAVE == WAVE_TABLEMUL )
-//Uses the value directly
-#define ENV_BITS ( 9 )
+ //Uses the value directly
+#define ENV_BITS ( 9 )
#else
-//Add 3 bits here for more accuracy and would have to be shifted up either way
-#define ENV_BITS ( 9 )
+ //Add 3 bits here for more accuracy and would have to be shifted up either way
+#define ENV_BITS ( 9 )
#endif
-//Limits of the envelope with those bits and when the envelope goes silent
-#define ENV_MIN 0
-#define ENV_EXTRA ( ENV_BITS - 9 )
-#define ENV_MAX ( 511 << ENV_EXTRA )
-#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) )
+ //Limits of the envelope with those bits and when the envelope goes silent
+#define ENV_MIN 0
+#define ENV_EXTRA ( ENV_BITS - 9 )
+#define ENV_MAX ( 511 << ENV_EXTRA )
+#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) )
#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT )
-//Attack/decay/release rate counter shift
-#define RATE_SH 24
-#define RATE_MASK ( ( 1 << RATE_SH ) - 1 )
-//Has to fit within 16bit lookuptable
-#define MUL_SH 16
+ //Attack/decay/release rate counter shift
+#define RATE_SH 24
+#define RATE_MASK ( ( 1 << RATE_SH ) - 1 )
+ //Has to fit within 16bit lookuptable
+#define MUL_SH 16
-//Check some ranges
+ //Check some ranges
#if ENV_EXTRA > 3
#error Too many envelope bits
#endif
-//How much to substract from the base value for the final attenuation
-static const Bit8u KslCreateTable[16] = {
- //0 will always be be lower than 7 * 8
- 64, 32, 24, 19,
- 16, 12, 11, 10,
- 8, 6, 5, 4,
- 3, 2, 1, 0,
-};
+ //How much to substract from the base value for the final attenuation
+ static const Bit8u KslCreateTable[16] =
+ {
+ //0 will always be be lower than 7 * 8
+ 64, 32, 24, 19,
+ 16, 12, 11, 10,
+ 8, 6, 5, 4,
+ 3, 2, 1, 0,
+ };
#define M(_X_) ((Bit8u)( (_X_) * 2))
-static const Bit8u FreqCreateTable[16] = {
- M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ),
- M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15)
-};
+ static const Bit8u FreqCreateTable[16] =
+ {
+ M(0.5), M(1), M(2), M(3), M(4), M(5), M(6), M(7),
+ M(8), M(9), M(10), M(10), M(12), M(12), M(15), M(15)
+ };
#undef M
-//We're not including the highest attack rate, that gets a special value
-static const Bit8u AttackSamplesTable[13] = {
- 69, 55, 46, 40,
- 35, 29, 23, 20,
- 19, 15, 11, 10,
- 9
-};
-//On a real opl these values take 8 samples to reach and are based upon larger tables
-static const Bit8u EnvelopeIncreaseTable[13] = {
- 4, 5, 6, 7,
- 8, 10, 12, 14,
- 16, 20, 24, 28,
- 32,
-};
+ //We're not including the highest attack rate, that gets a special value
+ static const Bit8u AttackSamplesTable[13] =
+ {
+ 69, 55, 46, 40,
+ 35, 29, 23, 20,
+ 19, 15, 11, 10,
+ 9
+ };
+ //On a real opl these values take 8 samples to reach and are based upon larger tables
+ static const Bit8u EnvelopeIncreaseTable[13] =
+ {
+ 4, 5, 6, 7,
+ 8, 10, 12, 14,
+ 16, 20, 24, 28,
+ 32,
+ };
#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG )
-static Bit16u ExpTable[ 256 ];
+ static Bit16u ExpTable[ 256 ];
#endif
#if ( DBOPL_WAVE == WAVE_HANDLER )
-//PI table used by WAVEHANDLER
-static Bit16u SinTable[ 512 ];
+ //PI table used by WAVEHANDLER
+ static Bit16u SinTable[ 512 ];
#endif
#if ( DBOPL_WAVE > WAVE_HANDLER )
-//Layout of the waveform table in 512 entry intervals
-//With overlapping waves we reduce the table to half it's size
-
-// | |//\\|____|WAV7|//__|/\ |____|/\/\|
-// |\\//| | |WAV7| | \/| | |
-// |06 |0126|17 |7 |3 |4 |4 5 |5 |
-
-//6 is just 0 shifted and masked
-
-static Bit16s WaveTable[ 8 * 512 ];
-//Distance into WaveTable the wave starts
-static const Bit16u WaveBaseTable[8] = {
- 0x000, 0x200, 0x200, 0x800,
- 0xa00, 0xc00, 0x100, 0x400,
-
-};
-//Mask the counter with this
-static const Bit16u WaveMaskTable[8] = {
- 1023, 1023, 511, 511,
- 1023, 1023, 512, 1023,
-};
-
-//Where to start the counter on at keyon
-static const Bit16u WaveStartTable[8] = {
- 512, 0, 0, 0,
- 0, 512, 512, 256,
-};
+ //Layout of the waveform table in 512 entry intervals
+ //With overlapping waves we reduce the table to half it's size
+
+ // | |//\\|____|WAV7|//__|/\ |____|/\/\|
+ // |\\//| | |WAV7| | \/| | |
+ // |06 |0126|17 |7 |3 |4 |4 5 |5 |
+
+ //6 is just 0 shifted and masked
+
+ static Bit16s WaveTable[ 8 * 512 ];
+ //Distance into WaveTable the wave starts
+ static const Bit16u WaveBaseTable[8] =
+ {
+ 0x000, 0x200, 0x200, 0x800,
+ 0xa00, 0xc00, 0x100, 0x400,
+
+ };
+ //Mask the counter with this
+ static const Bit16u WaveMaskTable[8] =
+ {
+ 1023, 1023, 511, 511,
+ 1023, 1023, 512, 1023,
+ };
+
+ //Where to start the counter on at keyon
+ static const Bit16u WaveStartTable[8] =
+ {
+ 512, 0, 0, 0,
+ 0, 512, 512, 256,
+ };
#endif
#if ( DBOPL_WAVE == WAVE_TABLEMUL )
-static Bit16u MulTable[ 384 ];
+ static Bit16u MulTable[ 384 ];
#endif
-static Bit8u KslTable[ 8 * 16 ];
-static Bit8u TremoloTable[ TREMOLO_TABLE ];
-//Start of a channel behind the chip struct start
-static Bit16u ChanOffsetTable[32];
-//Start of an operator behind the chip struct start
-static Bit16u OpOffsetTable[64];
-
-//The lower bits are the shift of the operator vibrato value
-//The highest bit is right shifted to generate -1 or 0 for negation
-//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0
-static const Bit8s VibratoTable[ 8 ] = {
- 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00,
- 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80
-};
-
-//Shift strength for the ksl value determined by ksl strength
-static const Bit8u KslShiftTable[4] = {
- 31,1,2,0
-};
-
-//Generate a table index and table shift value using input value from a selected rate
-static void EnvelopeSelect( Bit8u val, Bit8u& index, Bit8u& shift ) {
- if ( val < 13 * 4 ) { //Rate 0 - 12
- shift = 12 - ( val >> 2 );
- index = val & 3;
- } else if ( val < 15 * 4 ) { //rate 13 - 14
- shift = 0;
- index = val - 12 * 4;
- } else { //rate 15 and up
- shift = 0;
- index = 12;
- }
-}
+ static Bit8u KslTable[ 8 * 16 ];
+ static Bit8u TremoloTable[ TREMOLO_TABLE ];
+ //Start of a channel behind the chip struct start
+ static Bit16u ChanOffsetTable[32];
+ //Start of an operator behind the chip struct start
+ static Bit16u OpOffsetTable[64];
+
+ //The lower bits are the shift of the operator vibrato value
+ //The highest bit is right shifted to generate -1 or 0 for negation
+ //So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0
+ static const Bit8s VibratoTable[ 8 ] =
+ {
+ 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00,
+ 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80
+ };
+
+ //Shift strength for the ksl value determined by ksl strength
+ static const Bit8u KslShiftTable[4] =
+ {
+ 31, 1, 2, 0
+ };
+
+ //Generate a table index and table shift value using input value from a selected rate
+ static void EnvelopeSelect(Bit8u val, Bit8u &index, Bit8u &shift)
+ {
+ if(val < 13 * 4) //Rate 0 - 12
+ {
+ shift = 12 - (val >> 2);
+ index = val & 3;
+ }
+ else if(val < 15 * 4) //rate 13 - 14
+ {
+ shift = 0;
+ index = val - 12 * 4;
+ }
+ else //rate 15 and up
+ {
+ shift = 0;
+ index = 12;
+ }
+ }
#if ( DBOPL_WAVE == WAVE_HANDLER )
-/*
- Generate the different waveforms out of the sine/exponetial table using handlers
-*/
-static inline Bits MakeVolume( Bitu wave, Bitu volume ) {
- Bitu total = wave + volume;
- Bitu index = total & 0xff;
- Bitu sig = ExpTable[ index ];
- Bitu exp = total >> 8;
+ /*
+ Generate the different waveforms out of the sine/exponetial table using handlers
+ */
+ static inline Bits MakeVolume(Bitu wave, Bitu volume)
+ {
+ Bitu total = wave + volume;
+ Bitu index = total & 0xff;
+ Bitu sig = ExpTable[ index ];
+ Bitu exp = total >> 8;
#if 0
- //Check if we overflow the 31 shift limit
- if ( exp >= 32 ) {
- LOG_MSG( "WTF %d %d", total, exp );
- }
+
+ //Check if we overflow the 31 shift limit
+ if(exp >= 32)
+ LOG_MSG("WTF %d %d", total, exp);
+
#endif
- return (sig >> exp);
-};
-
-static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) {
- Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
- Bitu wave = SinTable[i & 511];
- return (MakeVolume( wave, volume ) ^ neg) - neg;
-}
-static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) {
- Bit32u wave = SinTable[i & 511];
- wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
- return MakeVolume( wave, volume );
-}
-static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) {
- Bitu wave = SinTable[i & 511];
- return MakeVolume( wave, volume );
-}
-static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) {
- Bitu wave = SinTable[i & 255];
- wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 );
- return MakeVolume( wave, volume );
-}
-static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) {
- //Twice as fast
- i <<= 1;
- Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
- Bitu wave = SinTable[i & 511];
- wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
- return (MakeVolume( wave, volume ) ^ neg) - neg;
-}
-static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) {
- //Twice as fast
- i <<= 1;
- Bitu wave = SinTable[i & 511];
- wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
- return MakeVolume( wave, volume );
-}
-static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) {
- Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
- return (MakeVolume( 0, volume ) ^ neg) - neg;
-}
-static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) {
- //Negative is reversed here
- Bits neg = (( i >> 9) & 1) - 1;
- Bitu wave = (i << 3);
- //When negative the volume also runs backwards
- wave = ((wave ^ neg) - neg) & 4095;
- return (MakeVolume( wave, volume ) ^ neg) - neg;
-}
-
-static const WaveHandler WaveHandlerTable[8] = {
- WaveForm0, WaveForm1, WaveForm2, WaveForm3,
- WaveForm4, WaveForm5, WaveForm6, WaveForm7
-};
+ return (sig >> exp);
+ };
+
+ static Bits DB_FASTCALL WaveForm0(Bitu i, Bitu volume)
+ {
+ Bits neg = 0 - ((i >> 9) & 1); //Create ~0 or 0
+ Bitu wave = SinTable[i & 511];
+ return (MakeVolume(wave, volume) ^ neg) - neg;
+ }
+ static Bits DB_FASTCALL WaveForm1(Bitu i, Bitu volume)
+ {
+ Bit32u wave = SinTable[i & 511];
+ wave |= (((i ^ 512) & 512) - 1) >> (32 - 12);
+ return MakeVolume(wave, volume);
+ }
+ static Bits DB_FASTCALL WaveForm2(Bitu i, Bitu volume)
+ {
+ Bitu wave = SinTable[i & 511];
+ return MakeVolume(wave, volume);
+ }
+ static Bits DB_FASTCALL WaveForm3(Bitu i, Bitu volume)
+ {
+ Bitu wave = SinTable[i & 255];
+ wave |= (((i ^ 256) & 256) - 1) >> (32 - 12);
+ return MakeVolume(wave, volume);
+ }
+ static Bits DB_FASTCALL WaveForm4(Bitu i, Bitu volume)
+ {
+ //Twice as fast
+ i <<= 1;
+ Bits neg = 0 - ((i >> 9) & 1); //Create ~0 or 0
+ Bitu wave = SinTable[i & 511];
+ wave |= (((i ^ 512) & 512) - 1) >> (32 - 12);
+ return (MakeVolume(wave, volume) ^ neg) - neg;
+ }
+ static Bits DB_FASTCALL WaveForm5(Bitu i, Bitu volume)
+ {
+ //Twice as fast
+ i <<= 1;
+ Bitu wave = SinTable[i & 511];
+ wave |= (((i ^ 512) & 512) - 1) >> (32 - 12);
+ return MakeVolume(wave, volume);
+ }
+ static Bits DB_FASTCALL WaveForm6(Bitu i, Bitu volume)
+ {
+ Bits neg = 0 - ((i >> 9) & 1); //Create ~0 or 0
+ return (MakeVolume(0, volume) ^ neg) - neg;
+ }
+ static Bits DB_FASTCALL WaveForm7(Bitu i, Bitu volume)
+ {
+ //Negative is reversed here
+ Bits neg = ((i >> 9) & 1) - 1;
+ Bitu wave = (i << 3);
+ //When negative the volume also runs backwards
+ wave = ((wave ^ neg) - neg) & 4095;
+ return (MakeVolume(wave, volume) ^ neg) - neg;
+ }
+
+ static const WaveHandler WaveHandlerTable[8] =
+ {
+ WaveForm0, WaveForm1, WaveForm2, WaveForm3,
+ WaveForm4, WaveForm5, WaveForm6, WaveForm7
+ };
#endif
-/*
- Operator
-*/
+ /*
+ Operator
+ */
+
+ //We zero out when rate == 0
+ inline void Operator::UpdateAttack(const Chip *chip)
+ {
+ Bit8u rate = reg60 >> 4;
+
+ if(rate)
+ {
+ Bit8u val = (rate << 2) + ksr;
+ attackAdd = chip->attackRates[ val ];
+ rateZero &= ~(1 << ATTACK);
+ }
+ else
+ {
+ attackAdd = 0;
+ rateZero |= (1 << ATTACK);
+ }
+ }
+ inline void Operator::UpdateDecay(const Chip *chip)
+ {
+ Bit8u rate = reg60 & 0xf;
+
+ if(rate)
+ {
+ Bit8u val = (rate << 2) + ksr;
+ decayAdd = chip->linearRates[ val ];
+ rateZero &= ~(1 << DECAY);
+ }
+ else
+ {
+ decayAdd = 0;
+ rateZero |= (1 << DECAY);
+ }
+ }
+ inline void Operator::UpdateRelease(const Chip *chip)
+ {
+ Bit8u rate = reg80 & 0xf;
+
+ if(rate)
+ {
+ Bit8u val = (rate << 2) + ksr;
+ releaseAdd = chip->linearRates[ val ];
+ rateZero &= ~(1 << RELEASE);
+
+ if(!(reg20 & MASK_SUSTAIN))
+ rateZero &= ~(1 << SUSTAIN);
+ }
+ else
+ {
+ rateZero |= (1 << RELEASE);
+ releaseAdd = 0;
+
+ if(!(reg20 & MASK_SUSTAIN))
+ rateZero |= (1 << SUSTAIN);
+ }
+ }
-//We zero out when rate == 0
-inline void Operator::UpdateAttack( const Chip* chip ) {
- Bit8u rate = reg60 >> 4;
- if ( rate ) {
- Bit8u val = (rate << 2) + ksr;
- attackAdd = chip->attackRates[ val ];
- rateZero &= ~(1 << ATTACK);
- } else {
- attackAdd = 0;
- rateZero |= (1 << ATTACK);
- }
-}
-inline void Operator::UpdateDecay( const Chip* chip ) {
- Bit8u rate = reg60 & 0xf;
- if ( rate ) {
- Bit8u val = (rate << 2) + ksr;
- decayAdd = chip->linearRates[ val ];
- rateZero &= ~(1 << DECAY);
- } else {
- decayAdd = 0;
- rateZero |= (1 << DECAY);
- }
-}
-inline void Operator::UpdateRelease( const Chip* chip ) {
- Bit8u rate = reg80 & 0xf;
- if ( rate ) {
- Bit8u val = (rate << 2) + ksr;
- releaseAdd = chip->linearRates[ val ];
- rateZero &= ~(1 << RELEASE);
- if ( !(reg20 & MASK_SUSTAIN ) ) {
- rateZero &= ~( 1 << SUSTAIN );
- }
- } else {
- rateZero |= (1 << RELEASE);
- releaseAdd = 0;
- if ( !(reg20 & MASK_SUSTAIN ) ) {
- rateZero |= ( 1 << SUSTAIN );
- }
- }
-}
-
-inline void Operator::UpdateAttenuation( ) {
- Bit8u kslBase = (Bit8u)((chanData >> SHIFT_KSLBASE) & 0xff);
- Bit32u tl = reg40 & 0x3f;
- Bit8u kslShift = KslShiftTable[ reg40 >> 6 ];
- //Make sure the attenuation goes to the right bits
- totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max
- totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift;
-}
-
-void Operator::UpdateFrequency( ) {
- Bit32u freq = chanData & (( 1 << 10 ) - 1);
- Bit32u block = (chanData >> 10) & 0xff;
+ inline void Operator::UpdateAttenuation()
+ {
+ Bit8u kslBase = (Bit8u)((chanData >> SHIFT_KSLBASE) & 0xff);
+ Bit32u tl = reg40 & 0x3f;
+ Bit8u kslShift = KslShiftTable[ reg40 >> 6 ];
+ //Make sure the attenuation goes to the right bits
+ totalLevel = tl << (ENV_BITS - 7); //Total level goes 2 bits below max
+ totalLevel += (kslBase << ENV_EXTRA) >> kslShift;
+ }
+
+ void Operator::UpdateFrequency()
+ {
+ Bit32u freq = chanData & ((1 << 10) - 1);
+ Bit32u block = (chanData >> 10) & 0xff;
#ifdef WAVE_PRECISION
- block = 7 - block;
- waveAdd = ( freq * freqMul ) >> block;
+ block = 7 - block;
+ waveAdd = (freq * freqMul) >> block;
#else
- waveAdd = ( freq << block ) * freqMul;
+ waveAdd = (freq << block) * freqMul;
#endif
- if ( reg20 & MASK_VIBRATO ) {
- vibStrength = (Bit8u)(freq >> 7);
+ if(reg20 & MASK_VIBRATO)
+ {
+ vibStrength = (Bit8u)(freq >> 7);
#ifdef WAVE_PRECISION
- vibrato = ( vibStrength * freqMul ) >> block;
+ vibrato = (vibStrength * freqMul) >> block;
#else
- vibrato = ( vibStrength << block ) * freqMul;
+ vibrato = (vibStrength << block) * freqMul;
#endif
- } else {
- vibStrength = 0;
- vibrato = 0;
- }
-}
-
-void Operator::UpdateRates( const Chip* chip ) {
- //Mame seems to reverse this where enabling ksr actually lowers
- //the rate, but pdf manuals says otherwise?
- Bit8u newKsr = (Bit8u)((chanData >> SHIFT_KEYCODE) & 0xff);
- if ( !( reg20 & MASK_KSR ) ) {
- newKsr >>= 2;
- }
- if ( ksr == newKsr )
- return;
- ksr = newKsr;
- UpdateAttack( chip );
- UpdateDecay( chip );
- UpdateRelease( chip );
-}
-
-INLINE Bit32s Operator::RateForward( Bit32u add ) {
- rateIndex += add;
- Bit32s ret = rateIndex >> RATE_SH;
- rateIndex = rateIndex & RATE_MASK;
- return ret;
-}
-
-template< Operator::State yes>
-Bits Operator::TemplateVolume( ) {
- Bit32s vol = volume;
- Bit32s change;
- switch ( yes ) {
- case OFF:
- return ENV_MAX;
- case ATTACK:
- change = RateForward( attackAdd );
- if ( !change )
- return vol;
- vol += ( (~vol) * change ) >> 3;
- if ( vol < ENV_MIN ) {
- volume = ENV_MIN;
- rateIndex = 0;
- SetState( DECAY );
- return ENV_MIN;
- }
- break;
- case DECAY:
- vol += RateForward( decayAdd );
- if ( GCC_UNLIKELY(vol >= sustainLevel) ) {
- //Check if we didn't overshoot max attenuation, then just go off
- if ( GCC_UNLIKELY(vol >= ENV_MAX) ) {
- volume = ENV_MAX;
- SetState( OFF );
- return ENV_MAX;
- }
- //Continue as sustain
- rateIndex = 0;
- SetState( SUSTAIN );
- }
- break;
- case SUSTAIN:
- if ( reg20 & MASK_SUSTAIN ) {
- return vol;
- }
- //In sustain phase, but not sustaining, do regular release
- case RELEASE:
- vol += RateForward( releaseAdd );;
- if ( GCC_UNLIKELY(vol >= ENV_MAX) ) {
- volume = ENV_MAX;
- SetState( OFF );
- return ENV_MAX;
- }
- break;
- }
- volume = vol;
- return vol;
-}
-
-static const VolumeHandler VolumeHandlerTable[5] = {
- &Operator::TemplateVolume< Operator::OFF >,
- &Operator::TemplateVolume< Operator::RELEASE >,
- &Operator::TemplateVolume< Operator::SUSTAIN >,
- &Operator::TemplateVolume< Operator::DECAY >,
- &Operator::TemplateVolume< Operator::ATTACK >
-};
-
-INLINE Bitu Operator::ForwardVolume() {
- return currentLevel + (this->*volHandler)();
-}
-
-
-INLINE Bitu Operator::ForwardWave() {
- waveIndex += waveCurrent;
- return waveIndex >> WAVE_SH;
-}
-
-void Operator::Write20( const Chip* chip, Bit8u val ) {
- Bit8u change = (reg20 ^ val );
- if ( !change )
- return;
- reg20 = val;
- //Shift the tremolo bit over the entire register, saved a branch, YES!
- tremoloMask = (Bit8s)(val) >> 7;
- tremoloMask &= ~(( 1 << ENV_EXTRA ) -1);
- //Update specific features based on changes
- if ( change & MASK_KSR ) {
- UpdateRates( chip );
- }
- //With sustain enable the volume doesn't change
- if ( reg20 & MASK_SUSTAIN || ( !releaseAdd ) ) {
- rateZero |= ( 1 << SUSTAIN );
- } else {
- rateZero &= ~( 1 << SUSTAIN );
- }
- //Frequency multiplier or vibrato changed
- if ( change & (0xf | MASK_VIBRATO) ) {
- freqMul = chip->freqMul[ val & 0xf ];
- UpdateFrequency();
- }
-}
-
-void Operator::Write40( const Chip* /*chip*/, Bit8u val ) {
- if (!(reg40 ^ val ))
- return;
- reg40 = val;
- UpdateAttenuation( );
-}
-
-void Operator::Write60( const Chip* chip, Bit8u val ) {
- Bit8u change = reg60 ^ val;
- reg60 = val;
- if ( change & 0x0f ) {
- UpdateDecay( chip );
- }
- if ( change & 0xf0 ) {
- UpdateAttack( chip );
- }
-}
-
-void Operator::Write80( const Chip* chip, Bit8u val ) {
- Bit8u change = (reg80 ^ val );
- if ( !change )
- return;
- reg80 = val;
- Bit8u sustain = val >> 4;
- //Turn 0xf into 0x1f
- sustain |= ( sustain + 1) & 0x10;
- sustainLevel = sustain << ( ENV_BITS - 5 );
- if ( change & 0x0f ) {
- UpdateRelease( chip );
- }
-}
-
-void Operator::WriteE0( const Chip* chip, Bit8u val ) {
- if ( !(regE0 ^ val) )
- return;
- //in opl3 mode you can always selet 7 waveforms regardless of waveformselect
- Bit8u waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) );
- regE0 = val;
+ }
+ else
+ {
+ vibStrength = 0;
+ vibrato = 0;
+ }
+ }
+
+ void Operator::UpdateRates(const Chip *chip)
+ {
+ //Mame seems to reverse this where enabling ksr actually lowers
+ //the rate, but pdf manuals says otherwise?
+ Bit8u newKsr = (Bit8u)((chanData >> SHIFT_KEYCODE) & 0xff);
+
+ if(!(reg20 & MASK_KSR))
+ newKsr >>= 2;
+
+ if(ksr == newKsr)
+ return;
+
+ ksr = newKsr;
+ UpdateAttack(chip);
+ UpdateDecay(chip);
+ UpdateRelease(chip);
+ }
+
+ INLINE Bit32s Operator::RateForward(Bit32u add)
+ {
+ rateIndex += add;
+ Bit32s ret = rateIndex >> RATE_SH;
+ rateIndex = rateIndex & RATE_MASK;
+ return ret;
+ }
+
+ template< Operator::State yes>
+ Bits Operator::TemplateVolume()
+ {
+ Bit32s vol = volume;
+ Bit32s change;
+
+ switch(yes)
+ {
+ case OFF:
+ return ENV_MAX;
+
+ case ATTACK:
+ change = RateForward(attackAdd);
+
+ if(!change)
+ return vol;
+
+ vol += ((~vol) * change) >> 3;
+
+ if(vol < ENV_MIN)
+ {
+ volume = ENV_MIN;
+ rateIndex = 0;
+ SetState(DECAY);
+ return ENV_MIN;
+ }
+
+ break;
+
+ case DECAY:
+ vol += RateForward(decayAdd);
+
+ if(GCC_UNLIKELY(vol >= sustainLevel))
+ {
+ //Check if we didn't overshoot max attenuation, then just go off
+ if(GCC_UNLIKELY(vol >= ENV_MAX))
+ {
+ volume = ENV_MAX;
+ SetState(OFF);
+ return ENV_MAX;
+ }
+
+ //Continue as sustain
+ rateIndex = 0;
+ SetState(SUSTAIN);
+ }
+
+ break;
+
+ case SUSTAIN:
+ if(reg20 & MASK_SUSTAIN)
+ return vol;
+
+ //In sustain phase, but not sustaining, do regular release
+ case RELEASE:
+ vol += RateForward(releaseAdd);;
+
+ if(GCC_UNLIKELY(vol >= ENV_MAX))
+ {
+ volume = ENV_MAX;
+ SetState(OFF);
+ return ENV_MAX;
+ }
+
+ break;
+ }
+
+ volume = vol;
+ return vol;
+ }
+
+ static const VolumeHandler VolumeHandlerTable[5] =
+ {
+ &Operator::TemplateVolume< Operator::OFF >,
+ &Operator::TemplateVolume< Operator::RELEASE >,
+ &Operator::TemplateVolume< Operator::SUSTAIN >,
+ &Operator::TemplateVolume< Operator::DECAY >,
+ &Operator::TemplateVolume< Operator::ATTACK >
+ };
+
+ INLINE Bitu Operator::ForwardVolume()
+ {
+ return currentLevel + (this->*volHandler)();
+ }
+
+
+ INLINE Bitu Operator::ForwardWave()
+ {
+ waveIndex += waveCurrent;
+ return waveIndex >> WAVE_SH;
+ }
+
+ void Operator::Write20(const Chip *chip, Bit8u val)
+ {
+ Bit8u change = (reg20 ^ val);
+
+ if(!change)
+ return;
+
+ reg20 = val;
+ //Shift the tremolo bit over the entire register, saved a branch, YES!
+ tremoloMask = (Bit8s)(val) >> 7;
+ tremoloMask &= ~((1 << ENV_EXTRA) - 1);
+
+ //Update specific features based on changes
+ if(change & MASK_KSR)
+ UpdateRates(chip);
+
+ //With sustain enable the volume doesn't change
+ if(reg20 & MASK_SUSTAIN || (!releaseAdd))
+ rateZero |= (1 << SUSTAIN);
+ else
+ rateZero &= ~(1 << SUSTAIN);
+
+ //Frequency multiplier or vibrato changed
+ if(change & (0xf | MASK_VIBRATO))
+ {
+ freqMul = chip->freqMul[ val & 0xf ];
+ UpdateFrequency();
+ }
+ }
+
+ void Operator::Write40(const Chip * /*chip*/, Bit8u val)
+ {
+ if(!(reg40 ^ val))
+ return;
+
+ reg40 = val;
+ UpdateAttenuation();
+ }
+
+ void Operator::Write60(const Chip *chip, Bit8u val)
+ {
+ Bit8u change = reg60 ^ val;
+ reg60 = val;
+
+ if(change & 0x0f)
+ UpdateDecay(chip);
+
+ if(change & 0xf0)
+ UpdateAttack(chip);
+ }
+
+ void Operator::Write80(const Chip *chip, Bit8u val)
+ {
+ Bit8u change = (reg80 ^ val);
+
+ if(!change)
+ return;
+
+ reg80 = val;
+ Bit8u sustain = val >> 4;
+ //Turn 0xf into 0x1f
+ sustain |= (sustain + 1) & 0x10;
+ sustainLevel = sustain << (ENV_BITS - 5);
+
+ if(change & 0x0f)
+ UpdateRelease(chip);
+ }
+
+ void Operator::WriteE0(const Chip *chip, Bit8u val)
+ {
+ if(!(regE0 ^ val))
+ return;
+
+ //in opl3 mode you can always selet 7 waveforms regardless of waveformselect
+ Bit8u waveForm = val & ((0x3 & chip->waveFormMask) | (0x7 & chip->opl3Active));
+ regE0 = val;
#if ( DBOPL_WAVE == WAVE_HANDLER )
- waveHandler = WaveHandlerTable[ waveForm ];
+ waveHandler = WaveHandlerTable[ waveForm ];
#else
- waveBase = WaveTable + WaveBaseTable[ waveForm ];
- waveStart = WaveStartTable[ waveForm ] << WAVE_SH;
- waveMask = WaveMaskTable[ waveForm ];
+ waveBase = WaveTable + WaveBaseTable[ waveForm ];
+ waveStart = WaveStartTable[ waveForm ] << WAVE_SH;
+ waveMask = WaveMaskTable[ waveForm ];
#endif
-}
-
-INLINE void Operator::SetState( Bit8u s ) {
- state = s;
- volHandler = VolumeHandlerTable[ s ];
-}
-
-INLINE bool Operator::Silent() const {
- if ( !ENV_SILENT( totalLevel + volume ) )
- return false;
- if ( !(rateZero & ( 1 << state ) ) )
- return false;
- return true;
-}
-
-INLINE void Operator::Prepare( const Chip* chip ) {
- currentLevel = totalLevel + (chip->tremoloValue & tremoloMask);
- waveCurrent = waveAdd;
- if ( vibStrength >> chip->vibratoShift ) {
- Bit32s add = vibrato >> chip->vibratoShift;
- //Sign extend over the shift value
- Bit32s neg = chip->vibratoSign;
- //Negate the add with -1 or 0
- add = ( add ^ neg ) - neg;
- waveCurrent += add;
- }
-}
-
-void Operator::KeyOn( Bit8u mask ) {
- if ( !keyOn ) {
- //Restart the frequency generator
+ }
+
+ INLINE void Operator::SetState(Bit8u s)
+ {
+ state = s;
+ volHandler = VolumeHandlerTable[ s ];
+ }
+
+ INLINE bool Operator::Silent() const
+ {
+ if(!ENV_SILENT(totalLevel + volume))
+ return false;
+
+ if(!(rateZero & (1 << state)))
+ return false;
+
+ return true;
+ }
+
+ INLINE void Operator::Prepare(const Chip *chip)
+ {
+ currentLevel = totalLevel + (chip->tremoloValue & tremoloMask);
+ waveCurrent = waveAdd;
+
+ if(vibStrength >> chip->vibratoShift)
+ {
+ Bit32s add = vibrato >> chip->vibratoShift;
+ //Sign extend over the shift value
+ Bit32s neg = chip->vibratoSign;
+ //Negate the add with -1 or 0
+ add = (add ^ neg) - neg;
+ waveCurrent += add;
+ }
+ }
+
+ void Operator::KeyOn(Bit8u mask)
+ {
+ if(!keyOn)
+ {
+ //Restart the frequency generator
#if ( DBOPL_WAVE > WAVE_HANDLER )
- waveIndex = waveStart;
+ waveIndex = waveStart;
#else
- waveIndex = 0;
+ waveIndex = 0;
#endif
- rateIndex = 0;
- SetState( ATTACK );
- }
- keyOn |= mask;
-}
-
-void Operator::KeyOff( Bit8u mask ) {
- keyOn &= ~mask;
- if ( !keyOn ) {
- if ( state != OFF ) {
- SetState( RELEASE );
- }
- }
-}
-
-INLINE Bits Operator::GetWave( Bitu index, Bitu vol ) {
+ rateIndex = 0;
+ SetState(ATTACK);
+ }
+
+ keyOn |= mask;
+ }
+
+ void Operator::KeyOff(Bit8u mask)
+ {
+ keyOn &= ~mask;
+
+ if(!keyOn)
+ {
+ if(state != OFF)
+ SetState(RELEASE);
+ }
+ }
+
+ INLINE Bits Operator::GetWave(Bitu index, Bitu vol)
+ {
#if ( DBOPL_WAVE == WAVE_HANDLER )
- return waveHandler( index, vol << ( 3 - ENV_EXTRA ) );
+ return waveHandler(index, vol << (3 - ENV_EXTRA));
#elif ( DBOPL_WAVE == WAVE_TABLEMUL )
- return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH;
+ return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH;
#elif ( DBOPL_WAVE == WAVE_TABLELOG )
- Bit32s wave = waveBase[ index & waveMask ];
- Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA );
- Bit32s sig = ExpTable[ total & 0xff ];
- Bit32u exp = total >> 8;
- Bit32s neg = wave >> 16;
- return ((sig ^ neg) - neg) >> exp;
+ Bit32s wave = waveBase[ index & waveMask ];
+ Bit32u total = (wave & 0x7fff) + vol << (3 - ENV_EXTRA);
+ Bit32s sig = ExpTable[ total & 0xff ];
+ Bit32u exp = total >> 8;
+ Bit32s neg = wave >> 16;
+ return ((sig ^ neg) - neg) >> exp;
#else
#error "No valid wave routine"
#endif
-}
-
-Bits INLINE Operator::GetSample( Bits modulation ) {
- Bitu vol = ForwardVolume();
- if ( ENV_SILENT( vol ) ) {
- //Simply forward the wave
- waveIndex += waveCurrent;
- return 0;
- } else {
- Bitu index = ForwardWave();
- index += modulation;
- return GetWave( index, vol );
- }
-}
-
-Operator::Operator() {
- chanData = 0;
- freqMul = 0;
- waveIndex = 0;
- waveAdd = 0;
- waveCurrent = 0;
- keyOn = 0;
- ksr = 0;
- reg20 = 0;
- reg40 = 0;
- reg60 = 0;
- reg80 = 0;
- regE0 = 0;
- SetState( OFF );
- rateZero = (1 << OFF);
- sustainLevel = ENV_MAX;
- currentLevel = ENV_MAX;
- totalLevel = ENV_MAX;
- volume = ENV_MAX;
- releaseAdd = 0;
-}
+ }
-/*
- Channel
-*/
+ Bits INLINE Operator::GetSample(Bits modulation)
+ {
+ Bitu vol = ForwardVolume();
+
+ if(ENV_SILENT(vol))
+ {
+ //Simply forward the wave
+ waveIndex += waveCurrent;
+ return 0;
+ }
+ else
+ {
+ Bitu index = ForwardWave();
+ index += modulation;
+ return GetWave(index, vol);
+ }
+ }
-Channel::Channel() {
- old[0] = old[1] = 0;
- chanData = 0;
- regB0 = 0;
- regC0 = 0;
- maskLeft = -1;
- maskRight = -1;
- feedback = 31;
- fourMask = 0;
- synthHandler = &Channel::BlockTemplate< sm2FM >;
-};
-
-void Channel::SetChanData( const Chip* chip, Bit32u data ) {
- Bit32u change = chanData ^ data;
- chanData = data;
- Op( 0 )->chanData = data;
- Op( 1 )->chanData = data;
- //Since a frequency update triggered this, always update frequency
- Op( 0 )->UpdateFrequency();
- Op( 1 )->UpdateFrequency();
- if ( change & ( 0xff << SHIFT_KSLBASE ) ) {
- Op( 0 )->UpdateAttenuation();
- Op( 1 )->UpdateAttenuation();
- }
- if ( change & ( 0xff << SHIFT_KEYCODE ) ) {
- Op( 0 )->UpdateRates( chip );
- Op( 1 )->UpdateRates( chip );
- }
-}
-
-void Channel::UpdateFrequency( const Chip* chip, Bit8u fourOp ) {
- //Extrace the frequency bits
- Bit32u data = chanData & 0xffff;
- Bit32u kslBase = KslTable[ data >> 6 ];
- Bit32u keyCode = ( data & 0x1c00) >> 9;
- if ( chip->reg08 & 0x40 ) {
- keyCode |= ( data & 0x100)>>8; /* notesel == 1 */
- } else {
- keyCode |= ( data & 0x200)>>9; /* notesel == 0 */
- }
- //Add the keycode and ksl into the highest bits of chanData
- data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE );
- ( this + 0 )->SetChanData( chip, data );
- if ( fourOp & 0x3f ) {
- ( this + 1 )->SetChanData( chip, data );
- }
-}
-
-void Channel::WriteA0( const Chip* chip, Bit8u val ) {
- Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask;
- //Don't handle writes to silent fourop channels
- if ( fourOp > 0x80 )
- return;
- Bit32u change = (chanData ^ val ) & 0xff;
- if ( change ) {
- chanData ^= change;
- UpdateFrequency( chip, fourOp );
- }
-}
-
-void Channel::WriteB0( const Chip* chip, Bit8u val ) {
- Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask;
- //Don't handle writes to silent fourop channels
- if ( fourOp > 0x80 )
- return;
- Bitu change = (chanData ^ ( val << 8 ) ) & 0x1f00;
- if ( change ) {
- chanData ^= change;
- UpdateFrequency( chip, fourOp );
- }
- //Check for a change in the keyon/off state
- if ( !(( val ^ regB0) & 0x20))
- return;
- regB0 = val;
- if ( val & 0x20 ) {
- Op(0)->KeyOn( 0x1 );
- Op(1)->KeyOn( 0x1 );
- if ( fourOp & 0x3f ) {
- ( this + 1 )->Op(0)->KeyOn( 1 );
- ( this + 1 )->Op(1)->KeyOn( 1 );
- }
- } else {
- Op(0)->KeyOff( 0x1 );
- Op(1)->KeyOff( 0x1 );
- if ( fourOp & 0x3f ) {
- ( this + 1 )->Op(0)->KeyOff( 1 );
- ( this + 1 )->Op(1)->KeyOff( 1 );
- }
- }
-}
-
-void Channel::WriteC0( const Chip* chip, Bit8u val ) {
- Bit8u change = val ^ regC0;
- if ( !change )
- return;
- regC0 = val;
- feedback = ( val >> 1 ) & 7;
- if ( feedback ) {
- //We shift the input to the right 10 bit wave index value
- feedback = 9 - feedback;
- } else {
- feedback = 31;
- }
- //Select the new synth mode
- if ( chip->opl3Active ) {
- //4-op mode enabled for this channel
- if ( (chip->reg104 & fourMask) & 0x3f ) {
- Channel* chan0, *chan1;
- //Check if it's the 2nd channel in a 4-op
- if ( !(fourMask & 0x80 ) ) {
- chan0 = this;
- chan1 = this + 1;
- } else {
- chan0 = this - 1;
- chan1 = this;
- }
-
- Bit8u synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 );
- switch ( synth ) {
- case 0:
- chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >;
- break;
- case 1:
- chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >;
- break;
- case 2:
- chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >;
- break;
- case 3:
- chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >;
- break;
- }
- //Disable updating percussion channels
- } else if ((fourMask & 0x40) && ( chip->regBD & 0x20) ) {
-
- //Regular dual op, am or fm
- } else if ( val & 1 ) {
- synthHandler = &Channel::BlockTemplate< sm3AM >;
- } else {
- synthHandler = &Channel::BlockTemplate< sm3FM >;
- }
- maskLeft = ( val & 0x10 ) ? -1 : 0;
- maskRight = ( val & 0x20 ) ? -1 : 0;
- //opl2 active
- } else {
- //Disable updating percussion channels
- if ( (fourMask & 0x40) && ( chip->regBD & 0x20 ) ) {
-
- //Regular dual op, am or fm
- } else if ( val & 1 ) {
- synthHandler = &Channel::BlockTemplate< sm2AM >;
- } else {
- synthHandler = &Channel::BlockTemplate< sm2FM >;
- }
- }
-}
-
-void Channel::ResetC0( const Chip* chip ) {
- Bit8u val = regC0;
- regC0 ^= 0xff;
- WriteC0( chip, val );
-};
-
-template< bool opl3Mode>
-INLINE void Channel::GeneratePercussion( Chip* chip, Bit32s* output ) {
- Channel* chan = this;
-
- //BassDrum
- Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback;
- old[0] = old[1];
- old[1] = Op(0)->GetSample( mod );
-
- //When bassdrum is in AM mode first operator is ignoed
- if ( chan->regC0 & 1 ) {
- mod = 0;
- } else {
- mod = old[0];
- }
- Bit32s sample = Op(1)->GetSample( mod );
-
-
- //Precalculate stuff used by other outputs
- Bit32u noiseBit = chip->ForwardNoise() & 0x1;
- Bit32u c2 = Op(2)->ForwardWave();
- Bit32u c5 = Op(5)->ForwardWave();
- Bit32u phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00;
-
- //Hi-Hat
- Bit32u hhVol = Op(2)->ForwardVolume();
- if ( !ENV_SILENT( hhVol ) ) {
- Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 )));
- sample += Op(2)->GetWave( hhIndex, hhVol );
- }
- //Snare Drum
- Bit32u sdVol = Op(3)->ForwardVolume();
- if ( !ENV_SILENT( sdVol ) ) {
- Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 );
- sample += Op(3)->GetWave( sdIndex, sdVol );
- }
- //Tom-tom
- sample += Op(4)->GetSample( 0 );
-
- //Top-Cymbal
- Bit32u tcVol = Op(5)->ForwardVolume();
- if ( !ENV_SILENT( tcVol ) ) {
- Bit32u tcIndex = (1 + phaseBit) << 8;
- sample += Op(5)->GetWave( tcIndex, tcVol );
- }
- sample <<= 1;
- if ( opl3Mode ) {
- output[0] += sample;
- output[1] += sample;
- } else {
- output[0] += sample;
- }
-}
-
-template<SynthMode mode>
-Channel* Channel::BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ) {
- switch( mode ) {
- case sm2AM:
- case sm3AM:
- if ( Op(0)->Silent() && Op(1)->Silent() ) {
- old[0] = old[1] = 0;
- return (this + 1);
- }
- break;
- case sm2FM:
- case sm3FM:
- if ( Op(1)->Silent() ) {
- old[0] = old[1] = 0;
- return (this + 1);
- }
- break;
- case sm3FMFM:
- if ( Op(3)->Silent() ) {
- old[0] = old[1] = 0;
- return (this + 2);
- }
- break;
- case sm3AMFM:
- if ( Op(0)->Silent() && Op(3)->Silent() ) {
- old[0] = old[1] = 0;
- return (this + 2);
- }
- break;
- case sm3FMAM:
- if ( Op(1)->Silent() && Op(3)->Silent() ) {
- old[0] = old[1] = 0;
- return (this + 2);
- }
- break;
- case sm3AMAM:
- if ( Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent() ) {
- old[0] = old[1] = 0;
- return (this + 2);
- }
- break;
- default:break;
- }
- //Init the operators with the the current vibrato and tremolo values
- Op( 0 )->Prepare( chip );
- Op( 1 )->Prepare( chip );
- if ( mode > sm4Start ) {
- Op( 2 )->Prepare( chip );
- Op( 3 )->Prepare( chip );
- }
- if ( mode > sm6Start ) {
- Op( 4 )->Prepare( chip );
- Op( 5 )->Prepare( chip );
- }
- for ( Bitu i = 0; i < samples; i++ ) {
- //Early out for percussion handlers
- if ( mode == sm2Percussion ) {
- GeneratePercussion<false>( chip, output + i );
- continue; //Prevent some unitialized value bitching
- } else if ( mode == sm3Percussion ) {
- GeneratePercussion<true>( chip, output + i * 2 );
- continue; //Prevent some unitialized value bitching
- }
-
- //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise
- Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback;
- old[0] = old[1];
- old[1] = Op(0)->GetSample( mod );
- Bit32s sample;
- Bit32s out0 = old[0];
- if ( mode == sm2AM || mode == sm3AM ) {
- sample = out0 + Op(1)->GetSample( 0 );
- } else if ( mode == sm2FM || mode == sm3FM ) {
- sample = Op(1)->GetSample( out0 );
- } else if ( mode == sm3FMFM ) {
- Bits next = Op(1)->GetSample( out0 );
- next = Op(2)->GetSample( next );
- sample = Op(3)->GetSample( next );
- } else if ( mode == sm3AMFM ) {
- sample = out0;
- Bits next = Op(1)->GetSample( 0 );
- next = Op(2)->GetSample( next );
- sample += Op(3)->GetSample( next );
- } else if ( mode == sm3FMAM ) {
- sample = Op(1)->GetSample( out0 );
- Bits next = Op(2)->GetSample( 0 );
- sample += Op(3)->GetSample( next );
- } else if ( mode == sm3AMAM ) {
- sample = out0;
- Bits next = Op(1)->GetSample( 0 );
- sample += Op(2)->GetSample( next );
- sample += Op(3)->GetSample( 0 );
- }
- switch( mode ) {
- case sm2AM:
- case sm2FM:
- output[ i ] += sample;
- break;
- case sm3AM:
- case sm3FM:
- case sm3FMFM:
- case sm3AMFM:
- case sm3FMAM:
- case sm3AMAM:
- output[ i * 2 + 0 ] += sample & maskLeft;
- output[ i * 2 + 1 ] += sample & maskRight;
- break;
- default:break;
- }
- }
- switch( mode ) {
- case sm2AM:
- case sm2FM:
- case sm3AM:
- case sm3FM:
- return ( this + 1 );
- case sm3FMFM:
- case sm3AMFM:
- case sm3FMAM:
- case sm3AMAM:
- return( this + 2 );
- case sm2Percussion:
- case sm3Percussion:
- return( this + 3 );
- }
- return 0;
-}
+ Operator::Operator()
+ {
+ chanData = 0;
+ freqMul = 0;
+ waveIndex = 0;
+ waveAdd = 0;
+ waveCurrent = 0;
+ keyOn = 0;
+ ksr = 0;
+ reg20 = 0;
+ reg40 = 0;
+ reg60 = 0;
+ reg80 = 0;
+ regE0 = 0;
+ SetState(OFF);
+ rateZero = (1 << OFF);
+ sustainLevel = ENV_MAX;
+ currentLevel = ENV_MAX;
+ totalLevel = ENV_MAX;
+ volume = ENV_MAX;
+ releaseAdd = 0;
+ }
+
+ /*
+ Channel
+ */
+
+ Channel::Channel()
+ {
+ old[0] = old[1] = 0;
+ chanData = 0;
+ regB0 = 0;
+ regC0 = 0;
+ maskLeft = -1;
+ maskRight = -1;
+ feedback = 31;
+ fourMask = 0;
+ synthHandler = &Channel::BlockTemplate< sm2FM >;
+ };
+
+ void Channel::SetChanData(const Chip *chip, Bit32u data)
+ {
+ Bit32u change = chanData ^ data;
+ chanData = data;
+ Op(0)->chanData = data;
+ Op(1)->chanData = data;
+ //Since a frequency update triggered this, always update frequency
+ Op(0)->UpdateFrequency();
+ Op(1)->UpdateFrequency();
+
+ if(change & (0xff << SHIFT_KSLBASE))
+ {
+ Op(0)->UpdateAttenuation();
+ Op(1)->UpdateAttenuation();
+ }
+
+ if(change & (0xff << SHIFT_KEYCODE))
+ {
+ Op(0)->UpdateRates(chip);
+ Op(1)->UpdateRates(chip);
+ }
+ }
+
+ void Channel::UpdateFrequency(const Chip *chip, Bit8u fourOp)
+ {
+ //Extrace the frequency bits
+ Bit32u data = chanData & 0xffff;
+ Bit32u kslBase = KslTable[ data >> 6 ];
+ Bit32u keyCode = (data & 0x1c00) >> 9;
+
+ if(chip->reg08 & 0x40)
+ {
+ keyCode |= (data & 0x100) >> 8; /* notesel == 1 */
+ }
+ else
+ {
+ keyCode |= (data & 0x200) >> 9; /* notesel == 0 */
+ }
+
+ //Add the keycode and ksl into the highest bits of chanData
+ data |= (keyCode << SHIFT_KEYCODE) | (kslBase << SHIFT_KSLBASE);
+ (this + 0)->SetChanData(chip, data);
+
+ if(fourOp & 0x3f)
+ (this + 1)->SetChanData(chip, data);
+ }
+
+ void Channel::WriteA0(const Chip *chip, Bit8u val)
+ {
+ Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask;
+
+ //Don't handle writes to silent fourop channels
+ if(fourOp > 0x80)
+ return;
+
+ Bit32u change = (chanData ^ val) & 0xff;
+
+ if(change)
+ {
+ chanData ^= change;
+ UpdateFrequency(chip, fourOp);
+ }
+ }
+
+ void Channel::WriteB0(const Chip *chip, Bit8u val)
+ {
+ Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask;
+
+ //Don't handle writes to silent fourop channels
+ if(fourOp > 0x80)
+ return;
+
+ Bitu change = (chanData ^ (val << 8)) & 0x1f00;
+
+ if(change)
+ {
+ chanData ^= change;
+ UpdateFrequency(chip, fourOp);
+ }
+
+ //Check for a change in the keyon/off state
+ if(!((val ^ regB0) & 0x20))
+ return;
+
+ regB0 = val;
+
+ if(val & 0x20)
+ {
+ Op(0)->KeyOn(0x1);
+ Op(1)->KeyOn(0x1);
+
+ if(fourOp & 0x3f)
+ {
+ (this + 1)->Op(0)->KeyOn(1);
+ (this + 1)->Op(1)->KeyOn(1);
+ }
+ }
+ else
+ {
+ Op(0)->KeyOff(0x1);
+ Op(1)->KeyOff(0x1);
+
+ if(fourOp & 0x3f)
+ {
+ (this + 1)->Op(0)->KeyOff(1);
+ (this + 1)->Op(1)->KeyOff(1);
+ }
+ }
+ }
+
+ void Channel::WriteC0(const Chip *chip, Bit8u val)
+ {
+ Bit8u change = val ^ regC0;
+
+ if(!change)
+ return;
+
+ regC0 = val;
+ feedback = (val >> 1) & 7;
+
+ if(feedback)
+ {
+ //We shift the input to the right 10 bit wave index value
+ feedback = 9 - feedback;
+ }
+ else
+ feedback = 31;
+
+ //Select the new synth mode
+ if(chip->opl3Active)
+ {
+ //4-op mode enabled for this channel
+ if((chip->reg104 & fourMask) & 0x3f)
+ {
+ Channel *chan0, *chan1;
+
+ //Check if it's the 2nd channel in a 4-op
+ if(!(fourMask & 0x80))
+ {
+ chan0 = this;
+ chan1 = this + 1;
+ }
+ else
+ {
+ chan0 = this - 1;
+ chan1 = this;
+ }
+
+ Bit8u synth = ((chan0->regC0 & 1) << 0) | ((chan1->regC0 & 1) << 1);
+
+ switch(synth)
+ {
+ case 0:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >;
+ break;
+
+ case 1:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >;
+ break;
+
+ case 2:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >;
+ break;
+
+ case 3:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >;
+ break;
+ }
+
+ //Disable updating percussion channels
+ }
+ else if((fourMask & 0x40) && (chip->regBD & 0x20))
+ {
+ //Regular dual op, am or fm
+ }
+ else if(val & 1)
+ synthHandler = &Channel::BlockTemplate< sm3AM >;
+ else
+ synthHandler = &Channel::BlockTemplate< sm3FM >;
+
+ maskLeft = (val & 0x10) ? -1 : 0;
+ maskRight = (val & 0x20) ? -1 : 0;
+ //opl2 active
+ }
+ else
+ {
+ //Disable updating percussion channels
+ if((fourMask & 0x40) && (chip->regBD & 0x20))
+ {
+ //Regular dual op, am or fm
+ }
+ else if(val & 1)
+ synthHandler = &Channel::BlockTemplate< sm2AM >;
+ else
+ synthHandler = &Channel::BlockTemplate< sm2FM >;
+ }
+ }
+
+ void Channel::ResetC0(const Chip *chip)
+ {
+ Bit8u val = regC0;
+ regC0 ^= 0xff;
+ WriteC0(chip, val);
+ };
+
+ template< bool opl3Mode>
+ INLINE void Channel::GeneratePercussion(Chip *chip, Bit32s *output)
+ {
+ Channel *chan = this;
+ //BassDrum
+ Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback;
+ old[0] = old[1];
+ old[1] = Op(0)->GetSample(mod);
+
+ //When bassdrum is in AM mode first operator is ignoed
+ if(chan->regC0 & 1)
+ mod = 0;
+ else
+ mod = old[0];
+
+ Bit32s sample = Op(1)->GetSample(mod);
+ //Precalculate stuff used by other outputs
+ Bit32u noiseBit = chip->ForwardNoise() & 0x1;
+ Bit32u c2 = Op(2)->ForwardWave();
+ Bit32u c5 = Op(5)->ForwardWave();
+ Bit32u phaseBit = (((c2 & 0x88) ^ ((c2 << 5) & 0x80)) | ((c5 ^ (c5 << 2)) & 0x20)) ? 0x02 : 0x00;
+ //Hi-Hat
+ Bit32u hhVol = Op(2)->ForwardVolume();
+
+ if(!ENV_SILENT(hhVol))
+ {
+ Bit32u hhIndex = (phaseBit << 8) | (0x34 << (phaseBit ^ (noiseBit << 1)));
+ sample += Op(2)->GetWave(hhIndex, hhVol);
+ }
+
+ //Snare Drum
+ Bit32u sdVol = Op(3)->ForwardVolume();
+
+ if(!ENV_SILENT(sdVol))
+ {
+ Bit32u sdIndex = (0x100 + (c2 & 0x100)) ^ (noiseBit << 8);
+ sample += Op(3)->GetWave(sdIndex, sdVol);
+ }
+
+ //Tom-tom
+ sample += Op(4)->GetSample(0);
+ //Top-Cymbal
+ Bit32u tcVol = Op(5)->ForwardVolume();
+
+ if(!ENV_SILENT(tcVol))
+ {
+ Bit32u tcIndex = (1 + phaseBit) << 8;
+ sample += Op(5)->GetWave(tcIndex, tcVol);
+ }
+
+ sample <<= 1;
+
+ if(opl3Mode)
+ {
+ output[0] += sample;
+ output[1] += sample;
+ }
+ else
+ output[0] += sample;
+ }
+
+ template<SynthMode mode>
+ Channel *Channel::BlockTemplate(Chip *chip, Bit32u samples, Bit32s *output)
+ {
+ switch(mode)
+ {
+ case sm2AM:
+ case sm3AM:
+ if(Op(0)->Silent() && Op(1)->Silent())
+ {
+ old[0] = old[1] = 0;
+ return (this + 1);
+ }
+
+ break;
+
+ case sm2FM:
+ case sm3FM:
+ if(Op(1)->Silent())
+ {
+ old[0] = old[1] = 0;
+ return (this + 1);
+ }
+
+ break;
+
+ case sm3FMFM:
+ if(Op(3)->Silent())
+ {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+
+ break;
+
+ case sm3AMFM:
+ if(Op(0)->Silent() && Op(3)->Silent())
+ {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+
+ break;
+
+ case sm3FMAM:
+ if(Op(1)->Silent() && Op(3)->Silent())
+ {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+
+ break;
+
+ case sm3AMAM:
+ if(Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent())
+ {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ //Init the operators with the the current vibrato and tremolo values
+ Op(0)->Prepare(chip);
+ Op(1)->Prepare(chip);
+
+ if(mode > sm4Start)
+ {
+ Op(2)->Prepare(chip);
+ Op(3)->Prepare(chip);
+ }
+
+ if(mode > sm6Start)
+ {
+ Op(4)->Prepare(chip);
+ Op(5)->Prepare(chip);
+ }
+
+ for(Bitu i = 0; i < samples; i++)
+ {
+ //Early out for percussion handlers
+ if(mode == sm2Percussion)
+ {
+ GeneratePercussion<false>(chip, output + i);
+ continue; //Prevent some unitialized value bitching
+ }
+ else if(mode == sm3Percussion)
+ {
+ GeneratePercussion<true>(chip, output + i * 2);
+ continue; //Prevent some unitialized value bitching
+ }
+
+ //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise
+ Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback;
+ old[0] = old[1];
+ old[1] = Op(0)->GetSample(mod);
+ Bit32s sample;
+ Bit32s out0 = old[0];
+
+ if(mode == sm2AM || mode == sm3AM)
+ sample = out0 + Op(1)->GetSample(0);
+ else if(mode == sm2FM || mode == sm3FM)
+ sample = Op(1)->GetSample(out0);
+ else if(mode == sm3FMFM)
+ {
+ Bits next = Op(1)->GetSample(out0);
+ next = Op(2)->GetSample(next);
+ sample = Op(3)->GetSample(next);
+ }
+ else if(mode == sm3AMFM)
+ {
+ sample = out0;
+ Bits next = Op(1)->GetSample(0);
+ next = Op(2)->GetSample(next);
+ sample += Op(3)->GetSample(next);
+ }
+ else if(mode == sm3FMAM)
+ {
+ sample = Op(1)->GetSample(out0);
+ Bits next = Op(2)->GetSample(0);
+ sample += Op(3)->GetSample(next);
+ }
+ else if(mode == sm3AMAM)
+ {
+ sample = out0;
+ Bits next = Op(1)->GetSample(0);
+ sample += Op(2)->GetSample(next);
+ sample += Op(3)->GetSample(0);
+ }
+
+ switch(mode)
+ {
+ case sm2AM:
+ case sm2FM:
+ output[ i ] += sample;
+ break;
+
+ case sm3AM:
+ case sm3FM:
+ case sm3FMFM:
+ case sm3AMFM:
+ case sm3FMAM:
+ case sm3AMAM:
+ output[ i * 2 + 0 ] += sample & maskLeft;
+ output[ i * 2 + 1 ] += sample & maskRight;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ switch(mode)
+ {
+ case sm2AM:
+ case sm2FM:
+ case sm3AM:
+ case sm3FM:
+ return (this + 1);
+
+ case sm3FMFM:
+ case sm3AMFM:
+ case sm3FMAM:
+ case sm3AMAM:
+ return(this + 2);
+
+ case sm2Percussion:
+ case sm3Percussion:
+ return(this + 3);
+ }
+
+ return 0;
+ }
+
+ /*
+ Chip
+ */
+
+ Chip::Chip()
+ {
+ reg08 = 0;
+ reg04 = 0;
+ regBD = 0;
+ reg104 = 0;
+ opl3Active = 0;
+ }
+
+ INLINE Bit32u Chip::ForwardNoise()
+ {
+ noiseCounter += noiseAdd;
+ Bitu count = noiseCounter >> LFO_SH;
+ noiseCounter &= WAVE_MASK;
+
+ for(; count > 0; --count)
+ {
+ //Noise calculation from mame
+ noiseValue ^= (0x800302) & (0 - (noiseValue & 1));
+ noiseValue >>= 1;
+ }
+
+ return noiseValue;
+ }
+
+ INLINE Bit32u Chip::ForwardLFO(Bit32u samples)
+ {
+ //Current vibrato value, runs 4x slower than tremolo
+ vibratoSign = (VibratoTable[ vibratoIndex >> 2]) >> 7;
+ vibratoShift = (VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength;
+ tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength;
+ //Check hom many samples there can be done before the value changes
+ Bit32u todo = LFO_MAX - lfoCounter;
+ Bit32u count = (todo + lfoAdd - 1) / lfoAdd;
+
+ if(count > samples)
+ {
+ count = samples;
+ lfoCounter += count * lfoAdd;
+ }
+ else
+ {
+ lfoCounter += count * lfoAdd;
+ lfoCounter &= (LFO_MAX - 1);
+ //Maximum of 7 vibrato value * 4
+ vibratoIndex = (vibratoIndex + 1) & 31;
+
+ //Clip tremolo to the the table size
+ if(tremoloIndex + 1 < TREMOLO_TABLE)
+ ++tremoloIndex;
+ else
+ tremoloIndex = 0;
+ }
+
+ return count;
+ }
+
+
+ void Chip::WriteBD(Bit8u val)
+ {
+ Bit8u change = regBD ^ val;
+
+ if(!change)
+ return;
+
+ regBD = val;
+ //TODO could do this with shift and xor?
+ vibratoStrength = (val & 0x40) ? 0x00 : 0x01;
+ tremoloStrength = (val & 0x80) ? 0x00 : 0x02;
+
+ if(val & 0x20)
+ {
+ //Drum was just enabled, make sure channel 6 has the right synth
+ if(change & 0x20)
+ {
+ if(opl3Active)
+ chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >;
+ else
+ chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >;
+ }
+
+ //Bass Drum
+ if(val & 0x10)
+ {
+ chan[6].op[0].KeyOn(0x2);
+ chan[6].op[1].KeyOn(0x2);
+ }
+ else
+ {
+ chan[6].op[0].KeyOff(0x2);
+ chan[6].op[1].KeyOff(0x2);
+ }
+
+ //Hi-Hat
+ if(val & 0x1)
+ chan[7].op[0].KeyOn(0x2);
+ else
+ chan[7].op[0].KeyOff(0x2);
+
+ //Snare
+ if(val & 0x8)
+ chan[7].op[1].KeyOn(0x2);
+ else
+ chan[7].op[1].KeyOff(0x2);
+
+ //Tom-Tom
+ if(val & 0x4)
+ chan[8].op[0].KeyOn(0x2);
+ else
+ chan[8].op[0].KeyOff(0x2);
+
+ //Top Cymbal
+ if(val & 0x2)
+ chan[8].op[1].KeyOn(0x2);
+ else
+ chan[8].op[1].KeyOff(0x2);
+
+ //Toggle keyoffs when we turn off the percussion
+ }
+ else if(change & 0x20)
+ {
+ //Trigger a reset to setup the original synth handler
+ chan[6].ResetC0(this);
+ chan[6].op[0].KeyOff(0x2);
+ chan[6].op[1].KeyOff(0x2);
+ chan[7].op[0].KeyOff(0x2);
+ chan[7].op[1].KeyOff(0x2);
+ chan[8].op[0].KeyOff(0x2);
+ chan[8].op[1].KeyOff(0x2);
+ }
+ }
-/*
- Chip
-*/
-Chip::Chip() {
- reg08 = 0;
- reg04 = 0;
- regBD = 0;
- reg104 = 0;
- opl3Active = 0;
-}
-
-INLINE Bit32u Chip::ForwardNoise() {
- noiseCounter += noiseAdd;
- Bitu count = noiseCounter >> LFO_SH;
- noiseCounter &= WAVE_MASK;
- for ( ; count > 0; --count ) {
- //Noise calculation from mame
- noiseValue ^= ( 0x800302 ) & ( 0 - (noiseValue & 1 ) );
- noiseValue >>= 1;
- }
- return noiseValue;
-}
-
-INLINE Bit32u Chip::ForwardLFO( Bit32u samples ) {
- //Current vibrato value, runs 4x slower than tremolo
- vibratoSign = ( VibratoTable[ vibratoIndex >> 2] ) >> 7;
- vibratoShift = ( VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength;
- tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength;
-
- //Check hom many samples there can be done before the value changes
- Bit32u todo = LFO_MAX - lfoCounter;
- Bit32u count = (todo + lfoAdd - 1) / lfoAdd;
- if ( count > samples ) {
- count = samples;
- lfoCounter += count * lfoAdd;
- } else {
- lfoCounter += count * lfoAdd;
- lfoCounter &= (LFO_MAX - 1);
- //Maximum of 7 vibrato value * 4
- vibratoIndex = ( vibratoIndex + 1 ) & 31;
- //Clip tremolo to the the table size
- if ( tremoloIndex + 1 < TREMOLO_TABLE )
- ++tremoloIndex;
- else
- tremoloIndex = 0;
- }
- return count;
-}
-
-
-void Chip::WriteBD( Bit8u val ) {
- Bit8u change = regBD ^ val;
- if ( !change )
- return;
- regBD = val;
- //TODO could do this with shift and xor?
- vibratoStrength = (val & 0x40) ? 0x00 : 0x01;
- tremoloStrength = (val & 0x80) ? 0x00 : 0x02;
- if ( val & 0x20 ) {
- //Drum was just enabled, make sure channel 6 has the right synth
- if ( change & 0x20 ) {
- if ( opl3Active ) {
- chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >;
- } else {
- chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >;
- }
- }
- //Bass Drum
- if ( val & 0x10 ) {
- chan[6].op[0].KeyOn( 0x2 );
- chan[6].op[1].KeyOn( 0x2 );
- } else {
- chan[6].op[0].KeyOff( 0x2 );
- chan[6].op[1].KeyOff( 0x2 );
- }
- //Hi-Hat
- if ( val & 0x1 ) {
- chan[7].op[0].KeyOn( 0x2 );
- } else {
- chan[7].op[0].KeyOff( 0x2 );
- }
- //Snare
- if ( val & 0x8 ) {
- chan[7].op[1].KeyOn( 0x2 );
- } else {
- chan[7].op[1].KeyOff( 0x2 );
- }
- //Tom-Tom
- if ( val & 0x4 ) {
- chan[8].op[0].KeyOn( 0x2 );
- } else {
- chan[8].op[0].KeyOff( 0x2 );
- }
- //Top Cymbal
- if ( val & 0x2 ) {
- chan[8].op[1].KeyOn( 0x2 );
- } else {
- chan[8].op[1].KeyOff( 0x2 );
- }
- //Toggle keyoffs when we turn off the percussion
- } else if ( change & 0x20 ) {
- //Trigger a reset to setup the original synth handler
- chan[6].ResetC0( this );
- chan[6].op[0].KeyOff( 0x2 );
- chan[6].op[1].KeyOff( 0x2 );
- chan[7].op[0].KeyOff( 0x2 );
- chan[7].op[1].KeyOff( 0x2 );
- chan[8].op[0].KeyOff( 0x2 );
- chan[8].op[1].KeyOff( 0x2 );
- }
-}
-
-
-#define REGOP( _FUNC_ ) \
- index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \
- if ( OpOffsetTable[ index ] ) { \
- Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \
- regOp->_FUNC_( this, val ); \
- }
-
-#define REGCHAN( _FUNC_ ) \
- index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \
- if ( ChanOffsetTable[ index ] ) { \
- Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \
- regChan->_FUNC_( this, val ); \
- }
-
-void Chip::WriteReg( Bit32u reg, Bit8u val ) {
- Bitu index;
- switch ( (reg & 0xf0) >> 4 ) {
- case 0x00 >> 4:
- if ( reg == 0x01 ) {
- waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0;
- } else if ( reg == 0x104 ) {
- //Only detect changes in lowest 6 bits
- if ( !((reg104 ^ val) & 0x3f) )
- return;
- //Always keep the highest bit enabled, for checking > 0x80
- reg104 = 0x80 | ( val & 0x3f );
- } else if ( reg == 0x105 ) {
- //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register
- if ( !((opl3Active ^ val) & 1 ) )
- return;
- opl3Active = ( val & 1 ) ? 0xff : 0;
- //Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers
- for ( int i = 0; i < 18;i++ ) {
- chan[i].ResetC0( this );
- }
- } else if ( reg == 0x08 ) {
- reg08 = val;
- }
- case 0x10 >> 4:
- break;
- case 0x20 >> 4:
- case 0x30 >> 4:
- REGOP( Write20 );
- break;
- case 0x40 >> 4:
- case 0x50 >> 4:
- REGOP( Write40 );
- break;
- case 0x60 >> 4:
- case 0x70 >> 4:
- REGOP( Write60 );
- break;
- case 0x80 >> 4:
- case 0x90 >> 4:
- REGOP( Write80 );
- break;
- case 0xa0 >> 4:
- REGCHAN( WriteA0 );
- break;
- case 0xb0 >> 4:
- if ( reg == 0xbd ) {
- WriteBD( val );
- } else {
- REGCHAN( WriteB0 );
- }
- break;
- case 0xc0 >> 4:
- REGCHAN( WriteC0 );
- case 0xd0 >> 4:
- break;
- case 0xe0 >> 4:
- case 0xf0 >> 4:
- REGOP( WriteE0 );
- break;
- }
-}
-
-
-Bit32u Chip::WriteAddr( Bit32u port, Bit8u val ) {
- switch ( port & 3 ) {
- case 0:
- return val;
- case 2:
- if ( opl3Active || (val == 0x05) )
- return 0x100 | val;
- else
- return val;
- }
- return 0;
-}
-
-void Chip::GenerateBlock2( Bitu total, Bit32s* output ) {
- while ( total > 0 ) {
- Bit32u samples = ForwardLFO( total );
- memset(output, 0, sizeof(Bit32s) * samples);
- int count = 0;
- for( Channel* ch = chan; ch < chan + 9; ) {
- count++;
- ch = (ch->*(ch->synthHandler))( this, samples, output );
- }
- total -= samples;
- output += samples;
- }
-}
-
-void Chip::GenerateBlock3( Bitu total, Bit32s* output ) {
- while ( total > 0 ) {
- Bit32u samples = ForwardLFO( total );
- memset(output, 0, sizeof(Bit32s) * samples *2);
- int count = 0;
- for( Channel* ch = chan; ch < chan + 18; ) {
- count++;
- ch = (ch->*(ch->synthHandler))( this, samples, output );
- }
- total -= samples;
- output += samples * 2;
- }
-}
-
-void Chip::Setup( Bit32u rate ) {
- double original = OPLRATE;
-// double original = rate;
- double scale = original / (double)rate;
-
- //Noise counter is run at the same precision as general waves
- noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) );
- noiseCounter = 0;
- noiseValue = 1; //Make sure it triggers the noise xor the first time
- //The low frequency oscillation counter
- //Every time his overflows vibrato and tremoloindex are increased
- lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) );
- lfoCounter = 0;
- vibratoIndex = 0;
- tremoloIndex = 0;
-
- //With higher octave this gets shifted up
- //-1 since the freqCreateTable = *2
+#define REGOP( _FUNC_ ) \
+ index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \
+ if ( OpOffsetTable[ index ] ) { \
+ Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \
+ regOp->_FUNC_( this, val ); \
+ }
+
+#define REGCHAN( _FUNC_ ) \
+ index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \
+ if ( ChanOffsetTable[ index ] ) { \
+ Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \
+ regChan->_FUNC_( this, val ); \
+ }
+
+ void Chip::WriteReg(Bit32u reg, Bit8u val)
+ {
+ Bitu index;
+
+ switch((reg & 0xf0) >> 4)
+ {
+ case 0x00 >> 4:
+ if(reg == 0x01)
+ waveFormMask = (val & 0x20) ? 0x7 : 0x0;
+ else if(reg == 0x104)
+ {
+ //Only detect changes in lowest 6 bits
+ if(!((reg104 ^ val) & 0x3f))
+ return;
+
+ //Always keep the highest bit enabled, for checking > 0x80
+ reg104 = 0x80 | (val & 0x3f);
+ }
+ else if(reg == 0x105)
+ {
+ //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register
+ if(!((opl3Active ^ val) & 1))
+ return;
+
+ opl3Active = (val & 1) ? 0xff : 0;
+
+ //Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers
+ for(int i = 0; i < 18; i++)
+ chan[i].ResetC0(this);
+ }
+ else if(reg == 0x08)
+ reg08 = val;
+
+ case 0x10 >> 4:
+ break;
+
+ case 0x20 >> 4:
+ case 0x30 >> 4:
+ REGOP(Write20);
+ break;
+
+ case 0x40 >> 4:
+ case 0x50 >> 4:
+ REGOP(Write40);
+ break;
+
+ case 0x60 >> 4:
+ case 0x70 >> 4:
+ REGOP(Write60);
+ break;
+
+ case 0x80 >> 4:
+ case 0x90 >> 4:
+ REGOP(Write80);
+ break;
+
+ case 0xa0 >> 4:
+ REGCHAN(WriteA0);
+ break;
+
+ case 0xb0 >> 4:
+ if(reg == 0xbd)
+ WriteBD(val);
+ else
+ REGCHAN(WriteB0);
+
+ break;
+
+ case 0xc0 >> 4:
+ REGCHAN(WriteC0);
+
+ case 0xd0 >> 4:
+ break;
+
+ case 0xe0 >> 4:
+ case 0xf0 >> 4:
+ REGOP(WriteE0);
+ break;
+ }
+ }
+
+
+ Bit32u Chip::WriteAddr(Bit32u port, Bit8u val)
+ {
+ switch(port & 3)
+ {
+ case 0:
+ return val;
+
+ case 2:
+ if(opl3Active || (val == 0x05))
+ return 0x100 | val;
+ else
+ return val;
+ }
+
+ return 0;
+ }
+
+ void Chip::GenerateBlock2(Bitu total, Bit32s *output)
+ {
+ while(total > 0)
+ {
+ Bit32u samples = ForwardLFO(total);
+ memset(output, 0, sizeof(Bit32s) * samples);
+ int count = 0;
+
+ for(Channel *ch = chan; ch < chan + 9;)
+ {
+ count++;
+ ch = (ch->*(ch->synthHandler))(this, samples, output);
+ }
+
+ total -= samples;
+ output += samples;
+ }
+ }
+
+ void Chip::GenerateBlock3(Bitu total, Bit32s *output)
+ {
+ while(total > 0)
+ {
+ Bit32u samples = ForwardLFO(total);
+ memset(output, 0, sizeof(Bit32s) * samples * 2);
+ int count = 0;
+
+ for(Channel *ch = chan; ch < chan + 18;)
+ {
+ count++;
+ ch = (ch->*(ch->synthHandler))(this, samples, output);
+ }
+
+ total -= samples;
+ output += samples * 2;
+ }
+ }
+
+ void Chip::Setup(Bit32u rate)
+ {
+ double original = OPLRATE;
+ // double original = rate;
+ double scale = original / (double)rate;
+ //Noise counter is run at the same precision as general waves
+ noiseAdd = (Bit32u)(0.5 + scale * (1 << LFO_SH));
+ noiseCounter = 0;
+ noiseValue = 1; //Make sure it triggers the noise xor the first time
+ //The low frequency oscillation counter
+ //Every time his overflows vibrato and tremoloindex are increased
+ lfoAdd = (Bit32u)(0.5 + scale * (1 << LFO_SH));
+ lfoCounter = 0;
+ vibratoIndex = 0;
+ tremoloIndex = 0;
+ //With higher octave this gets shifted up
+ //-1 since the freqCreateTable = *2
#ifdef WAVE_PRECISION
- double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10));
- for ( int i = 0; i < 16; i++ ) {
- freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] );
- }
+ double freqScale = (1 << 7) * scale * (1 << (WAVE_SH - 1 - 10));
+
+ for(int i = 0; i < 16; i++)
+ freqMul[i] = (Bit32u)(0.5 + freqScale * FreqCreateTable[ i ]);
+
#else
- Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10)));
- for ( int i = 0; i < 16; i++ ) {
- freqMul[i] = freqScale * FreqCreateTable[ i ];
- }
+ Bit32u freqScale = (Bit32u)(0.5 + scale * (1 << (WAVE_SH - 1 - 10)));
+
+ for(int i = 0; i < 16; i++)
+ freqMul[i] = freqScale * FreqCreateTable[ i ];
+
#endif
- //-3 since the real envelope takes 8 steps to reach the single value we supply
- for ( Bit8u i = 0; i < 76; i++ ) {
- Bit8u index, shift;
- EnvelopeSelect( i, index, shift );
- linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 )));
- }
-
- if(rate == 48000)
- {
- /* BISQWIT ADD: Use precalculated table for this common sample-rate.
- * Because the actual generation code, below, is MOLASSES SLOW on DOS.
- */
- static const Bit32u precalculated_table[62] =
-{2152,2700,3228,3712,4304,5399,6456,7424,8608,10799,12912,14849,17216,21598,
-25824,29698,34432,43196,51650,59398,68864,86392,103310,118795,137746,172847,
-206619,237693,275559,345774,413238,475500,543030,678787,814545,950302,1086060,
-1357575,1629090,1900605,2172120,2715151,3258181,3801211,4344241,5430302,
-6516362,7602423,8688483,10860604,13032725,15204846,17376967,21721209,26065451,
-30409693,34753934,43442418,52130902,60819386,69507869,69507869 };
- for ( Bit8u i = 0; i < 62; i++ )
- attackRates[i] = precalculated_table[i];
+ //-3 since the real envelope takes 8 steps to reach the single value we supply
+ for(Bit8u i = 0; i < 76; i++)
+ {
+ Bit8u index, shift;
+ EnvelopeSelect(i, index, shift);
+ linearRates[i] = (Bit32u)(scale * (EnvelopeIncreaseTable[ index ] << (RATE_SH + ENV_EXTRA - shift - 3)));
+ }
+
+ if(rate == 48000)
+ {
+ /* BISQWIT ADD: Use precalculated table for this common sample-rate.
+ * Because the actual generation code, below, is MOLASSES SLOW on DOS.
+ */
+ static const Bit32u precalculated_table[62] =
+ {
+ 2152, 2700, 3228, 3712, 4304, 5399, 6456, 7424, 8608, 10799, 12912, 14849, 17216, 21598,
+ 25824, 29698, 34432, 43196, 51650, 59398, 68864, 86392, 103310, 118795, 137746, 172847,
+ 206619, 237693, 275559, 345774, 413238, 475500, 543030, 678787, 814545, 950302, 1086060,
+ 1357575, 1629090, 1900605, 2172120, 2715151, 3258181, 3801211, 4344241, 5430302,
+ 6516362, 7602423, 8688483, 10860604, 13032725, 15204846, 17376967, 21721209, 26065451,
+ 30409693, 34753934, 43442418, 52130902, 60819386, 69507869, 69507869
+ };
+
+ for(Bit8u i = 0; i < 62; i++)
+ attackRates[i] = precalculated_table[i];
+ }
+ else if(rate == 44100)
+ {
+ static const Bit32u precalculated_table[62] =
+ {
+ 2342, 2939, 3513, 4040, 4685, 5877, 7027, 8081, 9369, 11754, 14054, 16162, 18738, 23508,
+ 28108, 32325, 37478, 47018, 56219, 64649, 74965, 94044, 112448, 129292, 149929, 188132,
+ 224945, 258713, 300002, 376263, 449999, 517550, 591053, 738816, 886579, 1034343, 1182106,
+ 1477633, 1773159, 2068686, 2364213, 2955266, 3546319, 4137373, 4728426, 5910533,
+ 7092639, 8274746, 9456853, 11821066, 14185279, 16549492, 18913706, 23642132, 28370559,
+ 33098985, 37827412, 47284265, 56741118, 66197971, 75654824, 75654824
+ };
+
+ for(Bit8u i = 0; i < 62; i++)
+ attackRates[i] = precalculated_table[i];
+ }
+ else if(rate == 22050)
+ {
+ static const Bit32u precalculated_table[62] =
+ {
+ 4685, 5877, 7027, 8081, 9369, 11754, 14054, 16162, 18738, 23508, 28108, 32325, 37478,
+ 47018, 56219, 64649, 74965, 94044, 112448, 129292, 149929, 188132, 224945, 258713, 300002,
+ 376263, 449999, 517550, 591053, 738816, 886579, 1034343, 1182106, 1477633, 1773159,
+ 2068686, 2364213, 2955266, 3546319, 4137373, 4728426, 5910533, 7092639, 8274746,
+ 9456853, 11821066, 14185279, 16549492, 18913706, 23642132, 28370559, 33098985,
+ 37827412, 47284265, 56741118, 66197971, 75654824, 94568530, 113482236, 132395942,
+ 151309648, 151309648
+ };
+
+ for(Bit8u i = 0; i < 62; i++)
+ attackRates[i] = precalculated_table[i];
+ }
+ //Generate the best matching attack rate
+ else for(Bit8u i = 0; i < 62; i++)
+ {
+ Bit8u index, shift;
+ EnvelopeSelect(i, index, shift);
+ //Original amount of samples the attack would take
+ Bit32s original = (Bit32u)((AttackSamplesTable[ index ] << shift) / scale);
+ Bit32s guessAdd = (Bit32u)(scale * (EnvelopeIncreaseTable[ index ] << (RATE_SH - shift - 3)));
+ Bit32s bestAdd = guessAdd;
+ Bit32u bestDiff = 1 << 30;
+
+ for(Bit32u passes = 0; passes < 16; passes ++)
+ {
+ Bit32s volume = ENV_MAX;
+ Bit32s samples = 0;
+ Bit32u count = 0;
+
+ while(volume > 0 && samples < original * 2)
+ {
+ count += guessAdd;
+ Bit32s change = count >> RATE_SH;
+ count &= RATE_MASK;
+
+ if(GCC_UNLIKELY(change)) // less than 1 %
+ volume += (~volume * change) >> 3;
+
+ samples++;
+ }
+
+ Bit32s diff = original - samples;
+ Bit32u lDiff = labs(diff);
+
+ //Init last on first pass
+ if(lDiff < bestDiff)
+ {
+ bestDiff = lDiff;
+ bestAdd = guessAdd;
+
+ if(!bestDiff)
+ break;
+ }
+
+ //Below our target
+ if(diff < 0)
+ {
+ //Better than the last time
+ Bit32s mul = ((original - diff) << 12) / original;
+ guessAdd = ((guessAdd * mul) >> 12);
+ guessAdd++;
+ }
+ else if(diff > 0)
+ {
+ Bit32s mul = ((original - diff) << 12) / original;
+ guessAdd = (guessAdd * mul) >> 12;
+ guessAdd--;
+ }
+ }
+
+ attackRates[i] = bestAdd;
+ }
+
+ /*fprintf(stderr, "attack rate table: ");
+ for ( Bit8u i = 0; i < 62; i++ )
+ fprintf(stderr, ",%u", attackRates[i]);
+ fprintf(stderr, "\n");*/
+
+ for(Bit8u i = 62; i < 76; i++)
+ {
+ //This should provide instant volume maximizing
+ attackRates[i] = 8 << RATE_SH;
+ }
+
+ //Setup the channels with the correct four op flags
+ //Channels are accessed through a table so they appear linear here
+ chan[ 0].fourMask = 0x00 | (1 << 0);
+ chan[ 1].fourMask = 0x80 | (1 << 0);
+ chan[ 2].fourMask = 0x00 | (1 << 1);
+ chan[ 3].fourMask = 0x80 | (1 << 1);
+ chan[ 4].fourMask = 0x00 | (1 << 2);
+ chan[ 5].fourMask = 0x80 | (1 << 2);
+ chan[ 9].fourMask = 0x00 | (1 << 3);
+ chan[10].fourMask = 0x80 | (1 << 3);
+ chan[11].fourMask = 0x00 | (1 << 4);
+ chan[12].fourMask = 0x80 | (1 << 4);
+ chan[13].fourMask = 0x00 | (1 << 5);
+ chan[14].fourMask = 0x80 | (1 << 5);
+ //mark the percussion channels
+ chan[ 6].fourMask = 0x40;
+ chan[ 7].fourMask = 0x40;
+ chan[ 8].fourMask = 0x40;
+ //Clear Everything in opl3 mode
+ WriteReg(0x105, 0x1);
+
+ for(int i = 0; i < 512; i++)
+ {
+ if(i == 0x105)
+ continue;
+
+ WriteReg(i, 0xff);
+ WriteReg(i, 0x0);
+ }
+
+ WriteReg(0x105, 0x0);
+
+ //Clear everything in opl2 mode
+ for(int i = 0; i < 255; i++)
+ {
+ WriteReg(i, 0xff);
+ WriteReg(i, 0x0);
+ }
}
- else if(rate == 44100)
- {
- static const Bit32u precalculated_table[62] =
-{2342,2939,3513,4040,4685,5877,7027,8081,9369,11754,14054,16162,18738,23508,
-28108,32325,37478,47018,56219,64649,74965,94044,112448,129292,149929,188132,
-224945,258713,300002,376263,449999,517550,591053,738816,886579,1034343,1182106,
-1477633,1773159,2068686,2364213,2955266,3546319,4137373,4728426,5910533,
-7092639,8274746,9456853,11821066,14185279,16549492,18913706,23642132,28370559,
-33098985,37827412,47284265,56741118,66197971,75654824,75654824 };
- for ( Bit8u i = 0; i < 62; i++ )
- attackRates[i] = precalculated_table[i];
- }
- else if(rate == 22050)
- {
- static const Bit32u precalculated_table[62] =
-{4685,5877,7027,8081,9369,11754,14054,16162,18738,23508,28108,32325,37478,
-47018,56219,64649,74965,94044,112448,129292,149929,188132,224945,258713,300002,
-376263,449999,517550,591053,738816,886579,1034343,1182106,1477633,1773159,
-2068686,2364213,2955266,3546319,4137373,4728426,5910533,7092639,8274746,
-9456853,11821066,14185279,16549492,18913706,23642132,28370559,33098985,
-37827412,47284265,56741118,66197971,75654824,94568530,113482236,132395942,
-151309648,151309648 };
- for ( Bit8u i = 0; i < 62; i++ )
- attackRates[i] = precalculated_table[i];
- }
- //Generate the best matching attack rate
- else for ( Bit8u i = 0; i < 62; i++ ) {
- Bit8u index, shift;
- EnvelopeSelect( i, index, shift );
- //Original amount of samples the attack would take
- Bit32s original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale);
-
- Bit32s guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 )));
- Bit32s bestAdd = guessAdd;
- Bit32u bestDiff = 1 << 30;
- for( Bit32u passes = 0; passes < 16; passes ++ ) {
- Bit32s volume = ENV_MAX;
- Bit32s samples = 0;
- Bit32u count = 0;
- while ( volume > 0 && samples < original * 2 ) {
- count += guessAdd;
- Bit32s change = count >> RATE_SH;
- count &= RATE_MASK;
- if ( GCC_UNLIKELY(change) ) { // less than 1 %
- volume += ( ~volume * change ) >> 3;
- }
- samples++;
-
- }
- Bit32s diff = original - samples;
- Bit32u lDiff = labs( diff );
- //Init last on first pass
- if ( lDiff < bestDiff ) {
- bestDiff = lDiff;
- bestAdd = guessAdd;
- if ( !bestDiff )
- break;
- }
- //Below our target
- if ( diff < 0 ) {
- //Better than the last time
- Bit32s mul = ((original - diff) << 12) / original;
- guessAdd = ((guessAdd * mul) >> 12);
- guessAdd++;
- } else if ( diff > 0 ) {
- Bit32s mul = ((original - diff) << 12) / original;
- guessAdd = (guessAdd * mul) >> 12;
- guessAdd--;
- }
- }
- attackRates[i] = bestAdd;
- }
- /*fprintf(stderr, "attack rate table: ");
- for ( Bit8u i = 0; i < 62; i++ )
- fprintf(stderr, ",%u", attackRates[i]);
- fprintf(stderr, "\n");*/
-
- for ( Bit8u i = 62; i < 76; i++ ) {
- //This should provide instant volume maximizing
- attackRates[i] = 8 << RATE_SH;
- }
- //Setup the channels with the correct four op flags
- //Channels are accessed through a table so they appear linear here
- chan[ 0].fourMask = 0x00 | ( 1 << 0 );
- chan[ 1].fourMask = 0x80 | ( 1 << 0 );
- chan[ 2].fourMask = 0x00 | ( 1 << 1 );
- chan[ 3].fourMask = 0x80 | ( 1 << 1 );
- chan[ 4].fourMask = 0x00 | ( 1 << 2 );
- chan[ 5].fourMask = 0x80 | ( 1 << 2 );
-
- chan[ 9].fourMask = 0x00 | ( 1 << 3 );
- chan[10].fourMask = 0x80 | ( 1 << 3 );
- chan[11].fourMask = 0x00 | ( 1 << 4 );
- chan[12].fourMask = 0x80 | ( 1 << 4 );
- chan[13].fourMask = 0x00 | ( 1 << 5 );
- chan[14].fourMask = 0x80 | ( 1 << 5 );
-
- //mark the percussion channels
- chan[ 6].fourMask = 0x40;
- chan[ 7].fourMask = 0x40;
- chan[ 8].fourMask = 0x40;
-
- //Clear Everything in opl3 mode
- WriteReg( 0x105, 0x1 );
- for ( int i = 0; i < 512; i++ ) {
- if ( i == 0x105 )
- continue;
- WriteReg( i, 0xff );
- WriteReg( i, 0x0 );
- }
- WriteReg( 0x105, 0x0 );
- //Clear everything in opl2 mode
- for ( int i = 0; i < 255; i++ ) {
- WriteReg( i, 0xff );
- WriteReg( i, 0x0 );
- }
-}
-
-static bool doneTables = false;
-void InitTables( void ) {
- if ( doneTables )
- return;
- doneTables = true;
+
+ static bool doneTables = false;
+ void InitTables(void)
+ {
+ if(doneTables)
+ return;
+
+ doneTables = true;
#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG )
- //Exponential volume table, same as the real adlib
- for ( int i = 0; i < 256; i++ ) {
- //Save them in reverse
- ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 );
- ExpTable[i] += 1024; //or remove the -1 oh well :)
- //Preshift to the left once so the final volume can shift to the right
- ExpTable[i] *= 2;
- }
+
+ //Exponential volume table, same as the real adlib
+ for(int i = 0; i < 256; i++)
+ {
+ //Save them in reverse
+ ExpTable[i] = (int)(0.5 + (pow(2.0, (255 - i) * (1.0 / 256)) - 1) * 1024);
+ ExpTable[i] += 1024; //or remove the -1 oh well :)
+ //Preshift to the left once so the final volume can shift to the right
+ ExpTable[i] *= 2;
+ }
+
#endif
#if ( DBOPL_WAVE == WAVE_HANDLER )
- //Add 0.5 for the trunc rounding of the integer cast
- //Do a PI sinetable instead of the original 0.5 PI
- for ( int i = 0; i < 512; i++ ) {
- SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 );
- }
+
+ //Add 0.5 for the trunc rounding of the integer cast
+ //Do a PI sinetable instead of the original 0.5 PI
+ for(int i = 0; i < 512; i++)
+ SinTable[i] = (Bit16s)(0.5 - log10(sin((i + 0.5) * (PI / 512.0))) / log10(2.0) * 256);
+
#endif
#if ( DBOPL_WAVE == WAVE_TABLEMUL )
- //Multiplication based tables
- for ( int i = 0; i < 384; i++ ) {
- int s = i * 8;
- //TODO maybe keep some of the precision errors of the original table?
- double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH ));
- MulTable[i] = (Bit16u)(val);
- }
-
- //Sine Wave Base
- for ( int i = 0; i < 512; i++ ) {
- WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084);
- WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ];
- }
- //Exponential wave
- for ( int i = 0; i < 256; i++ ) {
- WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 );
- WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ];
- }
+
+ //Multiplication based tables
+ for(int i = 0; i < 384; i++)
+ {
+ int s = i * 8;
+ //TODO maybe keep some of the precision errors of the original table?
+ double val = (0.5 + (pow(2.0, -1.0 + (255 - s) * (1.0 / 256))) * (1 << MUL_SH));
+ MulTable[i] = (Bit16u)(val);
+ }
+
+ //Sine Wave Base
+ for(int i = 0; i < 512; i++)
+ {
+ WaveTable[ 0x0200 + i ] = (Bit16s)(sin((i + 0.5) * (PI / 512.0)) * 4084);
+ WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ];
+ }
+
+ //Exponential wave
+ for(int i = 0; i < 256; i++)
+ {
+ WaveTable[ 0x700 + i ] = (Bit16s)(0.5 + (pow(2.0, -1.0 + (255 - i * 8) * (1.0 / 256))) * 4085);
+ WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ];
+ }
+
#endif
#if ( DBOPL_WAVE == WAVE_TABLELOG )
- //Sine Wave Base
- for ( int i = 0; i < 512; i++ ) {
- WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 );
- WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i];
- }
- //Exponential wave
- for ( int i = 0; i < 256; i++ ) {
- WaveTable[ 0x700 + i ] = i * 8;
- WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8;
- }
-#endif
- // | |//\\|____|WAV7|//__|/\ |____|/\/\|
- // |\\//| | |WAV7| | \/| | |
- // |06 |0126|27 |7 |3 |4 |4 5 |5 |
+ //Sine Wave Base
+ for(int i = 0; i < 512; i++)
+ {
+ WaveTable[ 0x0200 + i ] = (Bit16s)(0.5 - log10(sin((i + 0.5) * (PI / 512.0))) / log10(2.0) * 256);
+ WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i];
+ }
+
+ //Exponential wave
+ for(int i = 0; i < 256; i++)
+ {
+ WaveTable[ 0x700 + i ] = i * 8;
+ WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8;
+ }
+#endif
+ // | |//\\|____|WAV7|//__|/\ |____|/\/\|
+ // |\\//| | |WAV7| | \/| | |
+ // |06 |0126|27 |7 |3 |4 |4 5 |5 |
#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL ))
- for ( int i = 0; i < 256; i++ ) {
- //Fill silence gaps
- WaveTable[ 0x400 + i ] = WaveTable[0];
- WaveTable[ 0x500 + i ] = WaveTable[0];
- WaveTable[ 0x900 + i ] = WaveTable[0];
- WaveTable[ 0xc00 + i ] = WaveTable[0];
- WaveTable[ 0xd00 + i ] = WaveTable[0];
- //Replicate sines in other pieces
- WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ];
- //double speed sines
- WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ];
- WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ];
- WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ];
- WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ];
- }
+
+ for(int i = 0; i < 256; i++)
+ {
+ //Fill silence gaps
+ WaveTable[ 0x400 + i ] = WaveTable[0];
+ WaveTable[ 0x500 + i ] = WaveTable[0];
+ WaveTable[ 0x900 + i ] = WaveTable[0];
+ WaveTable[ 0xc00 + i ] = WaveTable[0];
+ WaveTable[ 0xd00 + i ] = WaveTable[0];
+ //Replicate sines in other pieces
+ WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ];
+ //double speed sines
+ WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ];
+ WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ }
+
#endif
- //Create the ksl table
- for ( int oct = 0; oct < 8; oct++ ) {
- int base = oct * 8;
- for ( int i = 0; i < 16; i++ ) {
- int val = base - KslCreateTable[i];
- if ( val < 0 )
- val = 0;
- //*4 for the final range to match attenuation range
- KslTable[ oct * 16 + i ] = val * 4;
- }
- }
- //Create the Tremolo table, just increase and decrease a triangle wave
- for ( Bit8u i = 0; i < TREMOLO_TABLE / 2; i++ ) {
- Bit8u val = i << ENV_EXTRA;
- TremoloTable[i] = val;
- TremoloTable[TREMOLO_TABLE - 1 - i] = val;
- }
- //Create a table with offsets of the channels from the start of the chip
- DBOPL::Chip* chip = 0;
- for ( Bitu i = 0; i < 32; i++ ) {
- Bitu index = i & 0xf;
- if ( index >= 9 ) {
- ChanOffsetTable[i] = 0;
- continue;
- }
- //Make sure the four op channels follow eachother
- if ( index < 6 ) {
- index = (index % 3) * 2 + ( index / 3 );
- }
- //Add back the bits for highest ones
- if ( i >= 16 )
- index += 9;
- Bitu blah = reinterpret_cast<Bitu>( &(chip->chan[ index ]) );
- ChanOffsetTable[i] = blah;
- }
- //Same for operators
- for ( Bitu i = 0; i < 64; i++ ) {
- if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) {
- OpOffsetTable[i] = 0;
- continue;
- }
- Bitu chNum = (i / 8) * 3 + (i % 8) % 3;
- //Make sure we use 16 and up for the 2nd range to match the chanoffset gap
- if ( chNum >= 12 )
- chNum += 16 - 12;
- Bitu opNum = ( i % 8 ) / 3;
- DBOPL::Channel* chan = 0;
- Bitu blah = reinterpret_cast<Bitu>( &(chan->op[opNum]) );
- OpOffsetTable[i] = ChanOffsetTable[ chNum ] + blah;
- }
+ //Create the ksl table
+ for(int oct = 0; oct < 8; oct++)
+ {
+ int base = oct * 8;
+
+ for(int i = 0; i < 16; i++)
+ {
+ int val = base - KslCreateTable[i];
+
+ if(val < 0)
+ val = 0;
+
+ //*4 for the final range to match attenuation range
+ KslTable[ oct * 16 + i ] = val * 4;
+ }
+ }
+
+ //Create the Tremolo table, just increase and decrease a triangle wave
+ for(Bit8u i = 0; i < TREMOLO_TABLE / 2; i++)
+ {
+ Bit8u val = i << ENV_EXTRA;
+ TremoloTable[i] = val;
+ TremoloTable[TREMOLO_TABLE - 1 - i] = val;
+ }
+
+ //Create a table with offsets of the channels from the start of the chip
+ DBOPL::Chip *chip = 0;
+
+ for(Bitu i = 0; i < 32; i++)
+ {
+ Bitu index = i & 0xf;
+
+ if(index >= 9)
+ {
+ ChanOffsetTable[i] = 0;
+ continue;
+ }
+
+ //Make sure the four op channels follow eachother
+ if(index < 6)
+ index = (index % 3) * 2 + (index / 3);
+
+ //Add back the bits for highest ones
+ if(i >= 16)
+ index += 9;
+
+ Bitu blah = reinterpret_cast<Bitu>(&(chip->chan[ index ]));
+ ChanOffsetTable[i] = blah;
+ }
+
+ //Same for operators
+ for(Bitu i = 0; i < 64; i++)
+ {
+ if(i % 8 >= 6 || ((i / 8) % 4 == 3))
+ {
+ OpOffsetTable[i] = 0;
+ continue;
+ }
+
+ Bitu chNum = (i / 8) * 3 + (i % 8) % 3;
+
+ //Make sure we use 16 and up for the 2nd range to match the chanoffset gap
+ if(chNum >= 12)
+ chNum += 16 - 12;
+
+ Bitu opNum = (i % 8) / 3;
+ DBOPL::Channel *chan = 0;
+ Bitu blah = reinterpret_cast<Bitu>(&(chan->op[opNum]));
+ OpOffsetTable[i] = ChanOffsetTable[ chNum ] + blah;
+ }
+
#if 0
- //Stupid checks if table's are correct
- for ( Bitu i = 0; i < 18; i++ ) {
- Bit32u find = (Bit16u)( &(chip->chan[ i ]) );
- for ( Bitu c = 0; c < 32; c++ ) {
- if ( ChanOffsetTable[c] == find ) {
- find = 0;
- break;
- }
- }
- if ( find ) {
- find = find;
- }
- }
- for ( Bitu i = 0; i < 36; i++ ) {
- Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) );
- for ( Bitu c = 0; c < 64; c++ ) {
- if ( OpOffsetTable[c] == find ) {
- find = 0;
- break;
- }
- }
- if ( find ) {
- find = find;
- }
- }
+
+ //Stupid checks if table's are correct
+ for(Bitu i = 0; i < 18; i++)
+ {
+ Bit32u find = (Bit16u)(&(chip->chan[ i ]));
+
+ for(Bitu c = 0; c < 32; c++)
+ {
+ if(ChanOffsetTable[c] == find)
+ {
+ find = 0;
+ break;
+ }
+ }
+
+ if(find)
+ find = find;
+ }
+
+ for(Bitu i = 0; i < 36; i++)
+ {
+ Bit32u find = (Bit16u)(&(chip->chan[ i / 2 ].op[i % 2]));
+
+ for(Bitu c = 0; c < 64; c++)
+ {
+ if(OpOffsetTable[c] == find)
+ {
+ find = 0;
+ break;
+ }
+ }
+
+ if(find)
+ find = find;
+ }
+
#endif
-}
-
-Bit32u Handler::WriteAddr( Bit32u port, Bit8u val ) {
- return chip.WriteAddr( port, val );
-
-}
-void Handler::WriteReg( Bit32u addr, Bit8u val ) {
- chip.WriteReg( addr, val );
-}
-
-void Handler::Generate( void(*AddSamples_m32)(Bitu,Bit32s*),
- void(*AddSamples_s32)(Bitu,Bit32s*),
- Bitu samples ) {
- Bit32s buffer[ 512 * 2 ];
- if ( GCC_UNLIKELY(samples > 512) )
- samples = 512;
- if ( !chip.opl3Active ) {
- chip.GenerateBlock2( samples, buffer );
- AddSamples_m32( samples, buffer );
- } else {
- chip.GenerateBlock3( samples, buffer );
- AddSamples_s32( samples, buffer );
- }
-}
-
-void Handler::GenerateArr(Bit32s* out, Bitu *samples )
-{
- if ( GCC_UNLIKELY(*samples > 512) )
- *samples = 512;
- if ( !chip.opl3Active ) {
- chip.GenerateBlock2( *samples, out );
- } else {
- chip.GenerateBlock3( *samples, out );
}
-}
-void Handler::Init( Bitu rate ) {
- InitTables();
- chip.Setup( rate );
-}
+ Bit32u Handler::WriteAddr(Bit32u port, Bit8u val)
+ {
+ return chip.WriteAddr(port, val);
+ }
+ void Handler::WriteReg(Bit32u addr, Bit8u val)
+ {
+ chip.WriteReg(addr, val);
+ }
+
+ void Handler::Generate(void(*AddSamples_m32)(Bitu, Bit32s *),
+ void(*AddSamples_s32)(Bitu, Bit32s *),
+ Bitu samples)
+ {
+ Bit32s buffer[ 512 * 2 ];
+
+ if(GCC_UNLIKELY(samples > 512))
+ samples = 512;
+
+ if(!chip.opl3Active)
+ {
+ chip.GenerateBlock2(samples, buffer);
+ AddSamples_m32(samples, buffer);
+ }
+ else
+ {
+ chip.GenerateBlock3(samples, buffer);
+ AddSamples_s32(samples, buffer);
+ }
+ }
+
+ void Handler::GenerateArr(Bit32s *out, Bitu *samples)
+ {
+ if(GCC_UNLIKELY(*samples > 512))
+ *samples = 512;
+
+ if(!chip.opl3Active)
+ chip.GenerateBlock2(*samples, out);
+ else
+ chip.GenerateBlock3(*samples, out);
+ }
+
+ void Handler::Init(Bitu rate)
+ {
+ InitTables();
+ chip.Setup(rate);
+ }
+
+} //Namespace DBOPL
-} //Namespace DBOPL
+#endif //ADLMIDI_USE_DOSBOX_OPL
diff --git a/src/nukedopl3.c b/src/nukedopl3.c
new file mode 100644
index 0000000..2136856
--- /dev/null
+++ b/src/nukedopl3.c
@@ -0,0 +1,1429 @@
+//
+// Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+//
+// Nuked OPL3 emulator.
+// Thanks:
+// MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
+// Feedback and Rhythm part calculation information.
+// forums.submarine.org.uk(carbon14, opl3):
+// Tremolo and phase generator calculation information.
+// OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
+// OPL2 ROMs.
+//
+// version: 1.7.4
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "nukedopl3.h"
+
+#define RSM_FRAC 10
+
+// Channel types
+
+enum {
+ ch_2op = 0,
+ ch_4op = 1,
+ ch_4op2 = 2,
+ ch_drum = 3
+};
+
+// Envelope key types
+
+enum {
+ egk_norm = 0x01,
+ egk_drum = 0x02
+};
+
+
+//
+// logsin table
+//
+
+static const Bit16u logsinrom[256] = {
+ 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471,
+ 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365,
+ 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd,
+ 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261,
+ 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f,
+ 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd,
+ 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195,
+ 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166,
+ 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c,
+ 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118,
+ 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8,
+ 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db,
+ 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1,
+ 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9,
+ 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094,
+ 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081,
+ 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070,
+ 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060,
+ 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052,
+ 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045,
+ 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039,
+ 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f,
+ 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026,
+ 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e,
+ 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017,
+ 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011,
+ 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c,
+ 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007,
+ 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004,
+ 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002,
+ 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001,
+ 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000
+};
+
+//
+// exp table
+//
+
+static const Bit16u exprom[256] = {
+ 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014,
+ 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a,
+ 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042,
+ 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a,
+ 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072,
+ 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b,
+ 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4,
+ 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be,
+ 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9,
+ 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4,
+ 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110,
+ 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c,
+ 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149,
+ 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167,
+ 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185,
+ 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4,
+ 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4,
+ 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4,
+ 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205,
+ 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227,
+ 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249,
+ 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d,
+ 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291,
+ 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5,
+ 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db,
+ 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302,
+ 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329,
+ 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351,
+ 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a,
+ 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4,
+ 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf,
+ 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa
+};
+
+//
+// freq mult table multiplied by 2
+//
+// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15
+//
+
+static const Bit8u mt[16] = {
+ 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30
+};
+
+//
+// ksl table
+//
+
+static const Bit8u kslrom[16] = {
+ 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64
+};
+
+static const Bit8u kslshift[4] = {
+ 8, 1, 2, 0
+};
+
+//
+// envelope generator constants
+//
+
+static const Bit8u eg_incstep[3][4][8] = {
+ {
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },
+ {
+ { 0, 1, 0, 1, 0, 1, 0, 1 },
+ { 0, 1, 0, 1, 1, 1, 0, 1 },
+ { 0, 1, 1, 1, 0, 1, 1, 1 },
+ { 0, 1, 1, 1, 1, 1, 1, 1 }
+ },
+ {
+ { 1, 1, 1, 1, 1, 1, 1, 1 },
+ { 2, 2, 1, 1, 1, 1, 1, 1 },
+ { 2, 2, 1, 1, 2, 2, 1, 1 },
+ { 2, 2, 2, 2, 2, 2, 1, 1 }
+ }
+};
+
+static const Bit8u eg_incdesc[16] = {
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2
+};
+
+static const Bit8s eg_incsh[16] = {
+ 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2
+};
+
+//
+// address decoding
+//
+
+static const Bit8s ad_slot[0x20] = {
+ 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
+ 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static const Bit8u ch_slot[18] = {
+ 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32
+};
+
+//
+// Envelope generator
+//
+
+typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope);
+typedef void(*envelope_genfunc)(opl3_slot *slott);
+
+static Bit16s OPL3_EnvelopeCalcExp(Bit32u level)
+{
+ if (level > 0x1fff)
+ {
+ level = 0x1fff;
+ }
+ return (Bit16s)((exprom[(level & 0xff) ^ 0xff] | 0x400) << 1) >> (level >> 8);
+}
+
+static Bit16s OPL3_EnvelopeCalcSin0(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ Bit16u neg = 0;
+ phase &= 0x3ff;
+ if (phase & 0x200)
+ {
+ neg = ~0;
+ }
+ if (phase & 0x100)
+ {
+ out = logsinrom[(phase & 0xff) ^ 0xff];
+ }
+ else
+ {
+ out = logsinrom[phase & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg;
+}
+
+static Bit16s OPL3_EnvelopeCalcSin1(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ phase &= 0x3ff;
+ if (phase & 0x200)
+ {
+ out = 0x1000;
+ }
+ else if (phase & 0x100)
+ {
+ out = logsinrom[(phase & 0xff) ^ 0xff];
+ }
+ else
+ {
+ out = logsinrom[phase & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3));
+}
+
+static Bit16s OPL3_EnvelopeCalcSin2(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ phase &= 0x3ff;
+ if (phase & 0x100)
+ {
+ out = logsinrom[(phase & 0xff) ^ 0xff];
+ }
+ else
+ {
+ out = logsinrom[phase & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3));
+}
+
+static Bit16s OPL3_EnvelopeCalcSin3(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ phase &= 0x3ff;
+ if (phase & 0x100)
+ {
+ out = 0x1000;
+ }
+ else
+ {
+ out = logsinrom[phase & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3));
+}
+
+static Bit16s OPL3_EnvelopeCalcSin4(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ Bit16u neg = 0;
+ phase &= 0x3ff;
+ if ((phase & 0x300) == 0x100)
+ {
+ neg = ~0;
+ }
+ if (phase & 0x200)
+ {
+ out = 0x1000;
+ }
+ else if (phase & 0x80)
+ {
+ out = logsinrom[((phase ^ 0xff) << 1) & 0xff];
+ }
+ else
+ {
+ out = logsinrom[(phase << 1) & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg;
+}
+
+static Bit16s OPL3_EnvelopeCalcSin5(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ phase &= 0x3ff;
+ if (phase & 0x200)
+ {
+ out = 0x1000;
+ }
+ else if (phase & 0x80)
+ {
+ out = logsinrom[((phase ^ 0xff) << 1) & 0xff];
+ }
+ else
+ {
+ out = logsinrom[(phase << 1) & 0xff];
+ }
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3));
+}
+
+static Bit16s OPL3_EnvelopeCalcSin6(Bit16u phase, Bit16u envelope)
+{
+ Bit16u neg = 0;
+ phase &= 0x3ff;
+ if (phase & 0x200)
+ {
+ neg = ~0;
+ }
+ return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg;
+}
+
+static Bit16s OPL3_EnvelopeCalcSin7(Bit16u phase, Bit16u envelope)
+{
+ Bit16u out = 0;
+ Bit16u neg = 0;
+ phase &= 0x3ff;
+ if (phase & 0x200)
+ {
+ neg = ~0;
+ phase = (phase & 0x1ff) ^ 0x1ff;
+ }
+ out = phase << 3;
+ return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg;
+}
+
+static const envelope_sinfunc envelope_sin[8] = {
+ OPL3_EnvelopeCalcSin0,
+ OPL3_EnvelopeCalcSin1,
+ OPL3_EnvelopeCalcSin2,
+ OPL3_EnvelopeCalcSin3,
+ OPL3_EnvelopeCalcSin4,
+ OPL3_EnvelopeCalcSin5,
+ OPL3_EnvelopeCalcSin6,
+ OPL3_EnvelopeCalcSin7
+};
+
+static void OPL3_EnvelopeGenOff(opl3_slot *slot);
+static void OPL3_EnvelopeGenAttack(opl3_slot *slot);
+static void OPL3_EnvelopeGenDecay(opl3_slot *slot);
+static void OPL3_EnvelopeGenSustain(opl3_slot *slot);
+static void OPL3_EnvelopeGenRelease(opl3_slot *slot);
+
+envelope_genfunc envelope_gen[5] = {
+ OPL3_EnvelopeGenOff,
+ OPL3_EnvelopeGenAttack,
+ OPL3_EnvelopeGenDecay,
+ OPL3_EnvelopeGenSustain,
+ OPL3_EnvelopeGenRelease
+};
+
+enum envelope_gen_num
+{
+ envelope_gen_num_off = 0,
+ envelope_gen_num_attack = 1,
+ envelope_gen_num_decay = 2,
+ envelope_gen_num_sustain = 3,
+ envelope_gen_num_release = 4
+};
+
+static Bit8u OPL3_EnvelopeCalcRate(opl3_slot *slot, Bit8u reg_rate)
+{
+ Bit8u rate;
+ if (reg_rate == 0x00)
+ {
+ return 0x00;
+ }
+ rate = (reg_rate << 2)
+ + (slot->reg_ksr ? slot->channel->ksv : (slot->channel->ksv >> 2));
+ if (rate > 0x3c)
+ {
+ rate = 0x3c;
+ }
+ return rate;
+}
+
+static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot)
+{
+ Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2)
+ - ((0x08 - slot->channel->block) << 5);
+ if (ksl < 0)
+ {
+ ksl = 0;
+ }
+ slot->eg_ksl = (Bit8u)ksl;
+}
+
+static void OPL3_EnvelopeUpdateRate(opl3_slot *slot)
+{
+ switch (slot->eg_gen)
+ {
+ case envelope_gen_num_off:
+ case envelope_gen_num_attack:
+ slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_ar);
+ break;
+ case envelope_gen_num_decay:
+ slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_dr);
+ break;
+ case envelope_gen_num_sustain:
+ case envelope_gen_num_release:
+ slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_rr);
+ break;
+ }
+}
+
+static void OPL3_EnvelopeGenOff(opl3_slot *slot)
+{
+ slot->eg_rout = 0x1ff;
+}
+
+static void OPL3_EnvelopeGenAttack(opl3_slot *slot)
+{
+ if (slot->eg_rout == 0x00)
+ {
+ slot->eg_gen = envelope_gen_num_decay;
+ OPL3_EnvelopeUpdateRate(slot);
+ return;
+ }
+ slot->eg_rout += ((~slot->eg_rout) * slot->eg_inc) >> 3;
+ if (slot->eg_rout < 0x00)
+ {
+ slot->eg_rout = 0x00;
+ }
+}
+
+static void OPL3_EnvelopeGenDecay(opl3_slot *slot)
+{
+ if (slot->eg_rout >= slot->reg_sl << 4)
+ {
+ slot->eg_gen = envelope_gen_num_sustain;
+ OPL3_EnvelopeUpdateRate(slot);
+ return;
+ }
+ slot->eg_rout += slot->eg_inc;
+}
+
+static void OPL3_EnvelopeGenSustain(opl3_slot *slot)
+{
+ if (!slot->reg_type)
+ {
+ OPL3_EnvelopeGenRelease(slot);
+ }
+}
+
+static void OPL3_EnvelopeGenRelease(opl3_slot *slot)
+{
+ if (slot->eg_rout >= 0x1ff)
+ {
+ slot->eg_gen = envelope_gen_num_off;
+ slot->eg_rout = 0x1ff;
+ OPL3_EnvelopeUpdateRate(slot);
+ return;
+ }
+ slot->eg_rout += slot->eg_inc;
+}
+
+static void OPL3_EnvelopeCalc(opl3_slot *slot)
+{
+ Bit8u rate_h, rate_l;
+ Bit8u inc = 0;
+ rate_h = slot->eg_rate >> 2;
+ rate_l = slot->eg_rate & 3;
+ if (eg_incsh[rate_h] > 0)
+ {
+ if ((slot->chip->timer & ((1 << eg_incsh[rate_h]) - 1)) == 0)
+ {
+ inc = eg_incstep[eg_incdesc[rate_h]][rate_l]
+ [((slot->chip->timer)>> eg_incsh[rate_h]) & 0x07];
+ }
+ }
+ else
+ {
+ inc = eg_incstep[eg_incdesc[rate_h]][rate_l]
+ [slot->chip->timer & 0x07] << (-eg_incsh[rate_h]);
+ }
+ slot->eg_inc = inc;
+ slot->eg_out = slot->eg_rout + (slot->reg_tl << 2)
+ + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem;
+ envelope_gen[slot->eg_gen](slot);
+}
+
+static void OPL3_EnvelopeKeyOn(opl3_slot *slot, Bit8u type)
+{
+ if (!slot->key)
+ {
+ slot->eg_gen = envelope_gen_num_attack;
+ OPL3_EnvelopeUpdateRate(slot);
+ if ((slot->eg_rate >> 2) == 0x0f)
+ {
+ slot->eg_gen = envelope_gen_num_decay;
+ OPL3_EnvelopeUpdateRate(slot);
+ slot->eg_rout = 0x00;
+ }
+ slot->pg_phase = 0x00;
+ }
+ slot->key |= type;
+}
+
+static void OPL3_EnvelopeKeyOff(opl3_slot *slot, Bit8u type)
+{
+ if (slot->key)
+ {
+ slot->key &= (~type);
+ if (!slot->key)
+ {
+ slot->eg_gen = envelope_gen_num_release;
+ OPL3_EnvelopeUpdateRate(slot);
+ }
+ }
+}
+
+//
+// Phase Generator
+//
+
+static void OPL3_PhaseGenerate(opl3_slot *slot)
+{
+ Bit16u f_num;
+ Bit32u basefreq;
+
+ f_num = slot->channel->f_num;
+ if (slot->reg_vib)
+ {
+ Bit8s range;
+ Bit8u vibpos;
+
+ range = (f_num >> 7) & 7;
+ vibpos = slot->chip->vibpos;
+
+ if (!(vibpos & 3))
+ {
+ range = 0;
+ }
+ else if (vibpos & 1)
+ {
+ range >>= 1;
+ }
+ range >>= slot->chip->vibshift;
+
+ if (vibpos & 4)
+ {
+ range = -range;
+ }
+ f_num += range;
+ }
+ basefreq = (f_num << slot->channel->block) >> 1;
+ slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1;
+}
+
+//
+// Noise Generator
+//
+
+static void OPL3_NoiseGenerate(opl3_chip *chip)
+{
+ if (chip->noise & 0x01)
+ {
+ chip->noise ^= 0x800302;
+ }
+ chip->noise >>= 1;
+}
+
+//
+// Slot
+//
+
+static void OPL3_SlotWrite20(opl3_slot *slot, Bit8u data)
+{
+ if ((data >> 7) & 0x01)
+ {
+ slot->trem = &slot->chip->tremolo;
+ }
+ else
+ {
+ slot->trem = (Bit8u*)&slot->chip->zeromod;
+ }
+ slot->reg_vib = (data >> 6) & 0x01;
+ slot->reg_type = (data >> 5) & 0x01;
+ slot->reg_ksr = (data >> 4) & 0x01;
+ slot->reg_mult = data & 0x0f;
+ OPL3_EnvelopeUpdateRate(slot);
+}
+
+static void OPL3_SlotWrite40(opl3_slot *slot, Bit8u data)
+{
+ slot->reg_ksl = (data >> 6) & 0x03;
+ slot->reg_tl = data & 0x3f;
+ OPL3_EnvelopeUpdateKSL(slot);
+}
+
+static void OPL3_SlotWrite60(opl3_slot *slot, Bit8u data)
+{
+ slot->reg_ar = (data >> 4) & 0x0f;
+ slot->reg_dr = data & 0x0f;
+ OPL3_EnvelopeUpdateRate(slot);
+}
+
+static void OPL3_SlotWrite80(opl3_slot *slot, Bit8u data)
+{
+ slot->reg_sl = (data >> 4) & 0x0f;
+ if (slot->reg_sl == 0x0f)
+ {
+ slot->reg_sl = 0x1f;
+ }
+ slot->reg_rr = data & 0x0f;
+ OPL3_EnvelopeUpdateRate(slot);
+}
+
+static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data)
+{
+ slot->reg_wf = data & 0x07;
+ if (slot->chip->newm == 0x00)
+ {
+ slot->reg_wf &= 0x03;
+ }
+}
+
+static void OPL3_SlotGeneratePhase(opl3_slot *slot, Bit16u phase)
+{
+ slot->out = envelope_sin[slot->reg_wf](phase, slot->eg_out);
+}
+
+static void OPL3_SlotGenerate(opl3_slot *slot)
+{
+ OPL3_SlotGeneratePhase(slot, (Bit16u)(slot->pg_phase >> 9) + *slot->mod);
+}
+
+static void OPL3_SlotGenerateZM(opl3_slot *slot)
+{
+ OPL3_SlotGeneratePhase(slot, (Bit16u)(slot->pg_phase >> 9));
+}
+
+static void OPL3_SlotCalcFB(opl3_slot *slot)
+{
+ if (slot->channel->fb != 0x00)
+ {
+ slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb);
+ }
+ else
+ {
+ slot->fbmod = 0;
+ }
+ slot->prout = slot->out;
+}
+
+//
+// Channel
+//
+
+static void OPL3_ChannelSetupAlg(opl3_channel *channel);
+
+static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data)
+{
+ opl3_channel *channel6;
+ opl3_channel *channel7;
+ opl3_channel *channel8;
+ Bit8u chnum;
+
+ chip->rhy = data & 0x3f;
+ if (chip->rhy & 0x20)
+ {
+ channel6 = &chip->channel[6];
+ channel7 = &chip->channel[7];
+ channel8 = &chip->channel[8];
+ channel6->out[0] = &channel6->slots[1]->out;
+ channel6->out[1] = &channel6->slots[1]->out;
+ channel6->out[2] = &chip->zeromod;
+ channel6->out[3] = &chip->zeromod;
+ channel7->out[0] = &channel7->slots[0]->out;
+ channel7->out[1] = &channel7->slots[0]->out;
+ channel7->out[2] = &channel7->slots[1]->out;
+ channel7->out[3] = &channel7->slots[1]->out;
+ channel8->out[0] = &channel8->slots[0]->out;
+ channel8->out[1] = &channel8->slots[0]->out;
+ channel8->out[2] = &channel8->slots[1]->out;
+ channel8->out[3] = &channel8->slots[1]->out;
+ for (chnum = 6; chnum < 9; chnum++)
+ {
+ chip->channel[chnum].chtype = ch_drum;
+ }
+ OPL3_ChannelSetupAlg(channel6);
+ //hh
+ if (chip->rhy & 0x01)
+ {
+ OPL3_EnvelopeKeyOn(channel7->slots[0], egk_drum);
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel7->slots[0], egk_drum);
+ }
+ //tc
+ if (chip->rhy & 0x02)
+ {
+ OPL3_EnvelopeKeyOn(channel8->slots[1], egk_drum);
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel8->slots[1], egk_drum);
+ }
+ //tom
+ if (chip->rhy & 0x04)
+ {
+ OPL3_EnvelopeKeyOn(channel8->slots[0], egk_drum);
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel8->slots[0], egk_drum);
+ }
+ //sd
+ if (chip->rhy & 0x08)
+ {
+ OPL3_EnvelopeKeyOn(channel7->slots[1], egk_drum);
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel7->slots[1], egk_drum);
+ }
+ //bd
+ if (chip->rhy & 0x10)
+ {
+ OPL3_EnvelopeKeyOn(channel6->slots[0], egk_drum);
+ OPL3_EnvelopeKeyOn(channel6->slots[1], egk_drum);
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel6->slots[0], egk_drum);
+ OPL3_EnvelopeKeyOff(channel6->slots[1], egk_drum);
+ }
+ }
+ else
+ {
+ for (chnum = 6; chnum < 9; chnum++)
+ {
+ chip->channel[chnum].chtype = ch_2op;
+ OPL3_ChannelSetupAlg(&chip->channel[chnum]);
+ OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[0], egk_drum);
+ OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[1], egk_drum);
+ }
+ }
+}
+
+static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data)
+{
+ if (channel->chip->newm && channel->chtype == ch_4op2)
+ {
+ return;
+ }
+ channel->f_num = (channel->f_num & 0x300) | data;
+ channel->ksv = (channel->block << 1)
+ | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01);
+ OPL3_EnvelopeUpdateKSL(channel->slots[0]);
+ OPL3_EnvelopeUpdateKSL(channel->slots[1]);
+ OPL3_EnvelopeUpdateRate(channel->slots[0]);
+ OPL3_EnvelopeUpdateRate(channel->slots[1]);
+ if (channel->chip->newm && channel->chtype == ch_4op)
+ {
+ channel->pair->f_num = channel->f_num;
+ channel->pair->ksv = channel->ksv;
+ OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]);
+ OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]);
+ OPL3_EnvelopeUpdateRate(channel->pair->slots[0]);
+ OPL3_EnvelopeUpdateRate(channel->pair->slots[1]);
+ }
+}
+
+static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data)
+{
+ if (channel->chip->newm && channel->chtype == ch_4op2)
+ {
+ return;
+ }
+ channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8);
+ channel->block = (data >> 2) & 0x07;
+ channel->ksv = (channel->block << 1)
+ | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01);
+ OPL3_EnvelopeUpdateKSL(channel->slots[0]);
+ OPL3_EnvelopeUpdateKSL(channel->slots[1]);
+ OPL3_EnvelopeUpdateRate(channel->slots[0]);
+ OPL3_EnvelopeUpdateRate(channel->slots[1]);
+ if (channel->chip->newm && channel->chtype == ch_4op)
+ {
+ channel->pair->f_num = channel->f_num;
+ channel->pair->block = channel->block;
+ channel->pair->ksv = channel->ksv;
+ OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]);
+ OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]);
+ OPL3_EnvelopeUpdateRate(channel->pair->slots[0]);
+ OPL3_EnvelopeUpdateRate(channel->pair->slots[1]);
+ }
+}
+
+static void OPL3_ChannelSetupAlg(opl3_channel *channel)
+{
+ if (channel->chtype == ch_drum)
+ {
+ switch (channel->alg & 0x01)
+ {
+ case 0x00:
+ channel->slots[0]->mod = &channel->slots[0]->fbmod;
+ channel->slots[1]->mod = &channel->slots[0]->out;
+ break;
+ case 0x01:
+ channel->slots[0]->mod = &channel->slots[0]->fbmod;
+ channel->slots[1]->mod = &channel->chip->zeromod;
+ break;
+ }
+ return;
+ }
+ if (channel->alg & 0x08)
+ {
+ return;
+ }
+ if (channel->alg & 0x04)
+ {
+ channel->pair->out[0] = &channel->chip->zeromod;
+ channel->pair->out[1] = &channel->chip->zeromod;
+ channel->pair->out[2] = &channel->chip->zeromod;
+ channel->pair->out[3] = &channel->chip->zeromod;
+ switch (channel->alg & 0x03)
+ {
+ case 0x00:
+ channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
+ channel->pair->slots[1]->mod = &channel->pair->slots[0]->out;
+ channel->slots[0]->mod = &channel->pair->slots[1]->out;
+ channel->slots[1]->mod = &channel->slots[0]->out;
+ channel->out[0] = &channel->slots[1]->out;
+ channel->out[1] = &channel->chip->zeromod;
+ channel->out[2] = &channel->chip->zeromod;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ case 0x01:
+ channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
+ channel->pair->slots[1]->mod = &channel->pair->slots[0]->out;
+ channel->slots[0]->mod = &channel->chip->zeromod;
+ channel->slots[1]->mod = &channel->slots[0]->out;
+ channel->out[0] = &channel->pair->slots[1]->out;
+ channel->out[1] = &channel->slots[1]->out;
+ channel->out[2] = &channel->chip->zeromod;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ case 0x02:
+ channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
+ channel->pair->slots[1]->mod = &channel->chip->zeromod;
+ channel->slots[0]->mod = &channel->pair->slots[1]->out;
+ channel->slots[1]->mod = &channel->slots[0]->out;
+ channel->out[0] = &channel->pair->slots[0]->out;
+ channel->out[1] = &channel->slots[1]->out;
+ channel->out[2] = &channel->chip->zeromod;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ case 0x03:
+ channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
+ channel->pair->slots[1]->mod = &channel->chip->zeromod;
+ channel->slots[0]->mod = &channel->pair->slots[1]->out;
+ channel->slots[1]->mod = &channel->chip->zeromod;
+ channel->out[0] = &channel->pair->slots[0]->out;
+ channel->out[1] = &channel->slots[0]->out;
+ channel->out[2] = &channel->slots[1]->out;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ }
+ }
+ else
+ {
+ switch (channel->alg & 0x01)
+ {
+ case 0x00:
+ channel->slots[0]->mod = &channel->slots[0]->fbmod;
+ channel->slots[1]->mod = &channel->slots[0]->out;
+ channel->out[0] = &channel->slots[1]->out;
+ channel->out[1] = &channel->chip->zeromod;
+ channel->out[2] = &channel->chip->zeromod;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ case 0x01:
+ channel->slots[0]->mod = &channel->slots[0]->fbmod;
+ channel->slots[1]->mod = &channel->chip->zeromod;
+ channel->out[0] = &channel->slots[0]->out;
+ channel->out[1] = &channel->slots[1]->out;
+ channel->out[2] = &channel->chip->zeromod;
+ channel->out[3] = &channel->chip->zeromod;
+ break;
+ }
+ }
+}
+
+static void OPL3_ChannelWriteC0(opl3_channel *channel, Bit8u data)
+{
+ channel->fb = (data & 0x0e) >> 1;
+ channel->con = data & 0x01;
+ channel->alg = channel->con;
+ if (channel->chip->newm)
+ {
+ if (channel->chtype == ch_4op)
+ {
+ channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con);
+ channel->alg = 0x08;
+ OPL3_ChannelSetupAlg(channel->pair);
+ }
+ else if (channel->chtype == ch_4op2)
+ {
+ channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con);
+ channel->pair->alg = 0x08;
+ OPL3_ChannelSetupAlg(channel);
+ }
+ else
+ {
+ OPL3_ChannelSetupAlg(channel);
+ }
+ }
+ else
+ {
+ OPL3_ChannelSetupAlg(channel);
+ }
+ if (channel->chip->newm)
+ {
+ channel->cha = ((data >> 4) & 0x01) ? ~0 : 0;
+ channel->chb = ((data >> 5) & 0x01) ? ~0 : 0;
+ }
+ else
+ {
+ channel->cha = channel->chb = ~0;
+ }
+}
+
+static void OPL3_ChannelKeyOn(opl3_channel *channel)
+{
+ if (channel->chip->newm)
+ {
+ if (channel->chtype == ch_4op)
+ {
+ OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm);
+ OPL3_EnvelopeKeyOn(channel->pair->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOn(channel->pair->slots[1], egk_norm);
+ }
+ else if (channel->chtype == ch_2op || channel->chtype == ch_drum)
+ {
+ OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm);
+ }
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm);
+ }
+}
+
+static void OPL3_ChannelKeyOff(opl3_channel *channel)
+{
+ if (channel->chip->newm)
+ {
+ if (channel->chtype == ch_4op)
+ {
+ OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm);
+ OPL3_EnvelopeKeyOff(channel->pair->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOff(channel->pair->slots[1], egk_norm);
+ }
+ else if (channel->chtype == ch_2op || channel->chtype == ch_drum)
+ {
+ OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm);
+ }
+ }
+ else
+ {
+ OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm);
+ OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm);
+ }
+}
+
+static void OPL3_ChannelSet4Op(opl3_chip *chip, Bit8u data)
+{
+ Bit8u bit;
+ Bit8u chnum;
+ for (bit = 0; bit < 6; bit++)
+ {
+ chnum = bit;
+ if (bit >= 3)
+ {
+ chnum += 9 - 3;
+ }
+ if ((data >> bit) & 0x01)
+ {
+ chip->channel[chnum].chtype = ch_4op;
+ chip->channel[chnum + 3].chtype = ch_4op2;
+ }
+ else
+ {
+ chip->channel[chnum].chtype = ch_2op;
+ chip->channel[chnum + 3].chtype = ch_2op;
+ }
+ }
+}
+
+static Bit16s OPL3_ClipSample(Bit32s sample)
+{
+ if (sample > 32767)
+ {
+ sample = 32767;
+ }
+ else if (sample < -32768)
+ {
+ sample = -32768;
+ }
+ return (Bit16s)sample;
+}
+
+static void OPL3_GenerateRhythm1(opl3_chip *chip)
+{
+ opl3_channel *channel6;
+ opl3_channel *channel7;
+ opl3_channel *channel8;
+ Bit16u phase14;
+ Bit16u phase17;
+ Bit16u phase;
+ Bit16u phasebit;
+
+ channel6 = &chip->channel[6];
+ channel7 = &chip->channel[7];
+ channel8 = &chip->channel[8];
+ OPL3_SlotGenerate(channel6->slots[0]);
+ phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff;
+ phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff;
+ phase = 0x00;
+ //hh tc phase bit
+ phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04)
+ | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00;
+ //hh
+ phase = (phasebit << 9)
+ | (0x34 << ((phasebit ^ (chip->noise & 0x01)) << 1));
+ OPL3_SlotGeneratePhase(channel7->slots[0], phase);
+ //tt
+ OPL3_SlotGenerateZM(channel8->slots[0]);
+}
+
+static void OPL3_GenerateRhythm2(opl3_chip *chip)
+{
+ opl3_channel *channel6;
+ opl3_channel *channel7;
+ opl3_channel *channel8;
+ Bit16u phase14;
+ Bit16u phase17;
+ Bit16u phase;
+ Bit16u phasebit;
+
+ channel6 = &chip->channel[6];
+ channel7 = &chip->channel[7];
+ channel8 = &chip->channel[8];
+ OPL3_SlotGenerate(channel6->slots[1]);
+ phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff;
+ phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff;
+ phase = 0x00;
+ //hh tc phase bit
+ phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04)
+ | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00;
+ //sd
+ phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8);
+ OPL3_SlotGeneratePhase(channel7->slots[1], phase);
+ //tc
+ phase = 0x100 | (phasebit << 9);
+ OPL3_SlotGeneratePhase(channel8->slots[1], phase);
+}
+
+void OPL3_Generate(opl3_chip *chip, Bit16s *buf)
+{
+ Bit8u ii;
+ Bit8u jj;
+ Bit16s accm;
+
+ buf[1] = OPL3_ClipSample(chip->mixbuff[1]);
+
+ for (ii = 0; ii < 12; ii++)
+ {
+ OPL3_SlotCalcFB(&chip->slot[ii]);
+ OPL3_PhaseGenerate(&chip->slot[ii]);
+ OPL3_EnvelopeCalc(&chip->slot[ii]);
+ OPL3_SlotGenerate(&chip->slot[ii]);
+ }
+
+ for (ii = 12; ii < 15; ii++)
+ {
+ OPL3_SlotCalcFB(&chip->slot[ii]);
+ OPL3_PhaseGenerate(&chip->slot[ii]);
+ OPL3_EnvelopeCalc(&chip->slot[ii]);
+ }
+
+ if (chip->rhy & 0x20)
+ {
+ OPL3_GenerateRhythm1(chip);
+ }
+ else
+ {
+ OPL3_SlotGenerate(&chip->slot[12]);
+ OPL3_SlotGenerate(&chip->slot[13]);
+ OPL3_SlotGenerate(&chip->slot[14]);
+ }
+
+ chip->mixbuff[0] = 0;
+ for (ii = 0; ii < 18; ii++)
+ {
+ accm = 0;
+ for (jj = 0; jj < 4; jj++)
+ {
+ accm += *chip->channel[ii].out[jj];
+ }
+ chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha);
+ }
+
+ for (ii = 15; ii < 18; ii++)
+ {
+ OPL3_SlotCalcFB(&chip->slot[ii]);
+ OPL3_PhaseGenerate(&chip->slot[ii]);
+ OPL3_EnvelopeCalc(&chip->slot[ii]);
+ }
+
+ if (chip->rhy & 0x20)
+ {
+ OPL3_GenerateRhythm2(chip);
+ }
+ else
+ {
+ OPL3_SlotGenerate(&chip->slot[15]);
+ OPL3_SlotGenerate(&chip->slot[16]);
+ OPL3_SlotGenerate(&chip->slot[17]);
+ }
+
+ buf[0] = OPL3_ClipSample(chip->mixbuff[0]);
+
+ for (ii = 18; ii < 33; ii++)
+ {
+ OPL3_SlotCalcFB(&chip->slot[ii]);
+ OPL3_PhaseGenerate(&chip->slot[ii]);
+ OPL3_EnvelopeCalc(&chip->slot[ii]);
+ OPL3_SlotGenerate(&chip->slot[ii]);
+ }
+
+ chip->mixbuff[1] = 0;
+ for (ii = 0; ii < 18; ii++)
+ {
+ accm = 0;
+ for (jj = 0; jj < 4; jj++)
+ {
+ accm += *chip->channel[ii].out[jj];
+ }
+ chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb);
+ }
+
+ for (ii = 33; ii < 36; ii++)
+ {
+ OPL3_SlotCalcFB(&chip->slot[ii]);
+ OPL3_PhaseGenerate(&chip->slot[ii]);
+ OPL3_EnvelopeCalc(&chip->slot[ii]);
+ OPL3_SlotGenerate(&chip->slot[ii]);
+ }
+
+ OPL3_NoiseGenerate(chip);
+
+ if ((chip->timer & 0x3f) == 0x3f)
+ {
+ chip->tremolopos = (chip->tremolopos + 1) % 210;
+ }
+ if (chip->tremolopos < 105)
+ {
+ chip->tremolo = chip->tremolopos >> chip->tremoloshift;
+ }
+ else
+ {
+ chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift;
+ }
+
+ if ((chip->timer & 0x3ff) == 0x3ff)
+ {
+ chip->vibpos = (chip->vibpos + 1) & 7;
+ }
+
+ chip->timer++;
+
+ while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt)
+ {
+ if (!(chip->writebuf[chip->writebuf_cur].reg & 0x200))
+ {
+ break;
+ }
+ chip->writebuf[chip->writebuf_cur].reg &= 0x1ff;
+ OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg,
+ chip->writebuf[chip->writebuf_cur].data);
+ chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE;
+ }
+ chip->writebuf_samplecnt++;
+}
+
+void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf)
+{
+ while (chip->samplecnt >= chip->rateratio)
+ {
+ chip->oldsamples[0] = chip->samples[0];
+ chip->oldsamples[1] = chip->samples[1];
+ OPL3_Generate(chip, chip->samples);
+ chip->samplecnt -= chip->rateratio;
+ }
+ buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt)
+ + chip->samples[0] * chip->samplecnt) / chip->rateratio);
+ buf[1] = (Bit16s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt)
+ + chip->samples[1] * chip->samplecnt) / chip->rateratio);
+ chip->samplecnt += 1 << RSM_FRAC;
+}
+
+void OPL3_Reset(opl3_chip *chip, Bit32u samplerate)
+{
+ Bit8u slotnum;
+ Bit8u channum;
+
+ memset(chip, 0, sizeof(opl3_chip));
+ for (slotnum = 0; slotnum < 36; slotnum++)
+ {
+ chip->slot[slotnum].chip = chip;
+ chip->slot[slotnum].mod = &chip->zeromod;
+ chip->slot[slotnum].eg_rout = 0x1ff;
+ chip->slot[slotnum].eg_out = 0x1ff;
+ chip->slot[slotnum].eg_gen = envelope_gen_num_off;
+ chip->slot[slotnum].trem = (Bit8u*)&chip->zeromod;
+ }
+ for (channum = 0; channum < 18; channum++)
+ {
+ chip->channel[channum].slots[0] = &chip->slot[ch_slot[channum]];
+ chip->channel[channum].slots[1] = &chip->slot[ch_slot[channum] + 3];
+ chip->slot[ch_slot[channum]].channel = &chip->channel[channum];
+ chip->slot[ch_slot[channum] + 3].channel = &chip->channel[channum];
+ if ((channum % 9) < 3)
+ {
+ chip->channel[channum].pair = &chip->channel[channum + 3];
+ }
+ else if ((channum % 9) < 6)
+ {
+ chip->channel[channum].pair = &chip->channel[channum - 3];
+ }
+ chip->channel[channum].chip = chip;
+ chip->channel[channum].out[0] = &chip->zeromod;
+ chip->channel[channum].out[1] = &chip->zeromod;
+ chip->channel[channum].out[2] = &chip->zeromod;
+ chip->channel[channum].out[3] = &chip->zeromod;
+ chip->channel[channum].chtype = ch_2op;
+ chip->channel[channum].cha = ~0;
+ chip->channel[channum].chb = ~0;
+ OPL3_ChannelSetupAlg(&chip->channel[channum]);
+ }
+ chip->noise = 0x306600;
+ chip->rateratio = (samplerate << RSM_FRAC) / 49716;
+ chip->tremoloshift = 4;
+ chip->vibshift = 1;
+}
+
+void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v)
+{
+ Bit8u high = (reg >> 8) & 0x01;
+ Bit8u regm = reg & 0xff;
+ switch (regm & 0xf0)
+ {
+ case 0x00:
+ if (high)
+ {
+ switch (regm & 0x0f)
+ {
+ case 0x04:
+ OPL3_ChannelSet4Op(chip, v);
+ break;
+ case 0x05:
+ chip->newm = v & 0x01;
+ break;
+ }
+ }
+ else
+ {
+ switch (regm & 0x0f)
+ {
+ case 0x08:
+ chip->nts = (v >> 6) & 0x01;
+ break;
+ }
+ }
+ break;
+ case 0x20:
+ case 0x30:
+ if (ad_slot[regm & 0x1f] >= 0)
+ {
+ OPL3_SlotWrite20(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v);
+ }
+ break;
+ case 0x40:
+ case 0x50:
+ if (ad_slot[regm & 0x1f] >= 0)
+ {
+ OPL3_SlotWrite40(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v);
+ }
+ break;
+ case 0x60:
+ case 0x70:
+ if (ad_slot[regm & 0x1f] >= 0)
+ {
+ OPL3_SlotWrite60(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v);
+ }
+ break;
+ case 0x80:
+ case 0x90:
+ if (ad_slot[regm & 0x1f] >= 0)
+ {
+ OPL3_SlotWrite80(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v);
+ }
+ break;
+ case 0xe0:
+ case 0xf0:
+ if (ad_slot[regm & 0x1f] >= 0)
+ {
+ OPL3_SlotWriteE0(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v);
+ }
+ break;
+ case 0xa0:
+ if ((regm & 0x0f) < 9)
+ {
+ OPL3_ChannelWriteA0(&chip->channel[9 * high + (regm & 0x0f)], v);
+ }
+ break;
+ case 0xb0:
+ if (regm == 0xbd && !high)
+ {
+ chip->tremoloshift = (((v >> 7) ^ 1) << 1) + 2;
+ chip->vibshift = ((v >> 6) & 0x01) ^ 1;
+ OPL3_ChannelUpdateRhythm(chip, v);
+ }
+ else if ((regm & 0x0f) < 9)
+ {
+ OPL3_ChannelWriteB0(&chip->channel[9 * high + (regm & 0x0f)], v);
+ if (v & 0x20)
+ {
+ OPL3_ChannelKeyOn(&chip->channel[9 * high + (regm & 0x0f)]);
+ }
+ else
+ {
+ OPL3_ChannelKeyOff(&chip->channel[9 * high + (regm & 0x0f)]);
+ }
+ }
+ break;
+ case 0xc0:
+ if ((regm & 0x0f) < 9)
+ {
+ OPL3_ChannelWriteC0(&chip->channel[9 * high + (regm & 0x0f)], v);
+ }
+ break;
+ }
+}
+
+void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v)
+{
+ Bit64u time1, time2;
+
+ if (chip->writebuf[chip->writebuf_last].reg & 0x200)
+ {
+ OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff,
+ chip->writebuf[chip->writebuf_last].data);
+
+ chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE;
+ chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time;
+ }
+
+ chip->writebuf[chip->writebuf_last].reg = reg | 0x200;
+ chip->writebuf[chip->writebuf_last].data = v;
+ time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY;
+ time2 = chip->writebuf_samplecnt;
+
+ if (time1 < time2)
+ {
+ time1 = time2;
+ }
+
+ chip->writebuf[chip->writebuf_last].time = time1;
+ chip->writebuf_lasttime = time1;
+ chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE;
+}
+
+void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
+{
+ Bit32u i;
+
+ for(i = 0; i < numsamples; i++)
+ {
+ OPL3_GenerateResampled(chip, sndptr);
+ sndptr += 2;
+ }
+}
+
+void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples)
+{
+ Bit32u i;
+ Bit16s sample[2];
+
+ for(i = 0; i < numsamples; i++)
+ {
+ OPL3_GenerateResampled(chip, sample);
+ sndptr[0] += sample[0];
+ sndptr[1] += sample[1];
+ sndptr += 2;
+ }
+}
diff --git a/src/nukedopl3.h b/src/nukedopl3.h
new file mode 100644
index 0000000..e7f05ef
--- /dev/null
+++ b/src/nukedopl3.h
@@ -0,0 +1,151 @@
+//
+// Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+//
+// Nuked OPL3 emulator.
+// Thanks:
+// MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
+// Feedback and Rhythm part calculation information.
+// forums.submarine.org.uk(carbon14, opl3):
+// Tremolo and phase generator calculation information.
+// OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
+// OPL2 ROMs.
+//
+// version: 1.7.4
+//
+
+#ifndef OPL_OPL3_H
+#define OPL_OPL3_H
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OPL_WRITEBUF_SIZE 1024
+#define OPL_WRITEBUF_DELAY 2
+
+typedef uintptr_t Bitu;
+typedef intptr_t Bits;
+typedef uint64_t Bit64u;
+typedef int64_t Bit64s;
+typedef uint32_t Bit32u;
+typedef int32_t Bit32s;
+typedef uint16_t Bit16u;
+typedef int16_t Bit16s;
+typedef uint8_t Bit8u;
+typedef int8_t Bit8s;
+
+typedef struct _opl3_slot opl3_slot;
+typedef struct _opl3_channel opl3_channel;
+typedef struct _opl3_chip opl3_chip;
+
+#pragma pack(1)
+struct _opl3_slot
+{
+ opl3_channel *channel;
+ opl3_chip *chip;
+ Bit16s out;
+ Bit16s fbmod;
+ Bit16s *mod;
+ Bit16s prout;
+ Bit16s eg_rout;
+ Bit16s eg_out;
+ Bit8u eg_inc;
+ Bit8u eg_gen;
+ Bit8u eg_rate;
+ Bit8u eg_ksl;
+ Bit8u *trem;
+ Bit8u reg_vib;
+ Bit8u reg_type;
+ Bit8u reg_ksr;
+ Bit8u reg_mult;
+ Bit8u reg_ksl;
+ Bit8u reg_tl;
+ Bit8u reg_ar;
+ Bit8u reg_dr;
+ Bit8u reg_sl;
+ Bit8u reg_rr;
+ Bit8u reg_wf;
+ Bit8u key;
+ Bit32u pg_phase;
+ Bit32u timer;
+};
+
+struct _opl3_channel
+{
+ opl3_slot *slots[2];
+ opl3_channel *pair;
+ opl3_chip *chip;
+ Bit16s *out[4];
+ Bit8u chtype;
+ Bit16u f_num;
+ Bit8u block;
+ Bit8u fb;
+ Bit8u con;
+ Bit8u alg;
+ Bit8u ksv;
+ Bit16u cha, chb;
+};
+
+typedef struct _opl3_writebuf
+{
+ Bit64u time;
+ Bit16u reg;
+ Bit8u data;
+} opl3_writebuf;
+#pragma pack(0)
+
+struct _opl3_chip
+{
+ opl3_channel channel[18];
+ opl3_slot slot[36];
+ Bit16u timer;
+ Bit8u newm;
+ Bit8u nts;
+ Bit8u rhy;
+ Bit8u vibpos;
+ Bit8u vibshift;
+ Bit8u tremolo;
+ Bit8u tremolopos;
+ Bit8u tremoloshift;
+ Bit32u noise;
+ Bit16s zeromod;
+ Bit32s mixbuff[2];
+ //OPL3L
+ Bit32s rateratio;
+ Bit32s samplecnt;
+ Bit16s oldsamples[2];
+ Bit16s samples[2];
+
+ Bit64u writebuf_samplecnt;
+ Bit32u writebuf_cur;
+ Bit32u writebuf_last;
+ Bit64u writebuf_lasttime;
+ opl3_writebuf writebuf[OPL_WRITEBUF_SIZE];
+};
+
+void OPL3_Generate(opl3_chip *chip, Bit16s *buf);
+void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf);
+void OPL3_Reset(opl3_chip *chip, Bit32u samplerate);
+void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v);
+void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v);
+void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples);
+void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif