diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/adlmidi.cpp | 3786 | ||||
-rw-r--r-- | src/adlmidi.h | 44 | ||||
-rw-r--r-- | src/dbopl.cpp | 3254 | ||||
-rw-r--r-- | src/nukedopl3.c | 1429 | ||||
-rw-r--r-- | src/nukedopl3.h | 151 |
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 |