diff options
Diffstat (limited to 'src/adlmidi_opl3.cpp')
-rw-r--r-- | src/adlmidi_opl3.cpp | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp new file mode 100644 index 0000000..a94b553 --- /dev/null +++ b/src/adlmidi_opl3.cpp @@ -0,0 +1,515 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi> + * ADLMIDI Library API: Copyright (c) 2017 Vitaly Novichkov <admin@wohlnet.ru> + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * 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 3 of the License, or + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "adlmidi_private.hpp" + +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 + +/* + In OPL3 mode: + 0 1 2 6 7 8 9 10 11 16 17 18 + op0 op1 op2 op12 op13 op14 op18 op19 op20 op30 op31 op32 + op3 op4 op5 op15 op16 op17 op21 op22 op23 op33 op34 op35 + 3 4 5 13 14 15 + op6 op7 op8 op24 op25 op26 + op9 op10 op11 op27 op28 op29 + Ports: + +0 +1 +2 +10 +11 +12 +100 +101 +102 +110 +111 +112 + +3 +4 +5 +13 +14 +15 +103 +104 +105 +113 +114 +115 + +8 +9 +A +108 +109 +10A + +B +C +D +10B +10C +10D + + Percussion: + bassdrum = op(0): 0xBD bit 0x10, operators 12 (0x10) and 15 (0x13) / channels 6, 6b + snare = op(3): 0xBD bit 0x08, operators 16 (0x14) / channels 7b + tomtom = op(4): 0xBD bit 0x04, operators 14 (0x12) / channels 8 + cym = op(5): 0xBD bit 0x02, operators 17 (0x17) / channels 8b + hihat = op(2): 0xBD bit 0x01, operators 13 (0x11) / channels 7 + + + In OPTi mode ("extended FM" in 82C924, 82C925, 82C931 chips): + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + op0 op4 op6 op10 op12 op16 op18 op22 op24 op28 op30 op34 op36 op38 op40 op42 op44 op46 + op1 op5 op7 op11 op13 op17 op19 op23 op25 op29 op31 op35 op37 op39 op41 op43 op45 op47 + op2 op8 op14 op20 op26 op32 + op3 op9 op15 op21 op27 op33 for a total of 6 quad + 12 dual + Ports: ??? +*/ + + +const adlinsdata &OPL3::GetAdlMetaIns(unsigned n) +{ + return (n & DynamicMetaInstrumentTag) ? + dynamic_metainstruments[n & ~DynamicMetaInstrumentTag] + : adlins[n]; +} + +unsigned OPL3::GetAdlMetaNumber(unsigned midiins) +{ + return (AdlBank == ~0u) ? + (midiins | DynamicMetaInstrumentTag) + : banks[AdlBank][midiins]; +} + + +const adldata &OPL3::GetAdlIns(unsigned short insno) +{ + return (insno & DynamicInstrumentTag) + ? dynamic_instruments[insno & ~DynamicInstrumentTag] + : adl[insno]; +} + + +OPL3::OPL3() : + DynamicInstrumentTag(0x8000u), + DynamicMetaInstrumentTag(0x4000000u), + NumCards(1), + AdlBank(0), + NumFourOps(0), + HighTremoloMode(false), + HighVibratoMode(false), + AdlPercussionMode(false), + LogarithmicVolumes(false), + m_volumeScale(VOLUME_Generic) +{} + +void OPL3::Poke(size_t card, uint32_t index, uint32_t value) +{ + #ifdef ADLMIDI_USE_DOSBOX_OPL + cards[card].WriteReg(index, static_cast<uint8_t>(value)); + #else + OPL3_WriteReg(&cards[card], static_cast<Bit16u>(index), static_cast<Bit8u>(value)); + #endif +} + +void OPL3::PokeN(size_t card, uint16_t index, uint8_t value) +{ + #ifdef ADLMIDI_USE_DOSBOX_OPL + cards[card].WriteReg(static_cast<Bit32u>(index), value); + #else + OPL3_WriteReg(&cards[card], index, value); + #endif +} + + +void OPL3::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; + } + + Poke(card, 0xB0 + Channels[cc], pit[c] & 0xDF); +} + +void OPL3::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; // 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)); + } +} + +void OPL3::Touch_Real(unsigned c, unsigned volume) +{ + 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) + { + 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 OPL3::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 +{ + if(LogarithmicVolumes) + Touch_Real(c, volume * 127 / (127 * 127 * 127) / 2); + else + { + // 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); + } +}*/ + +void OPL3::Patch(uint16_t c, uint16_t i) +{ + 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); + } +} + +void OPL3::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 OPL3::Silence() // Silence all OPL channels. +{ + for(unsigned c = 0; c < NumChannels; ++c) + { + NoteOff(c); + Touch_Real(c, 0); + } +} + +void OPL3::updateFlags() +{ + unsigned fours = NumFourOps; + + for(unsigned card = 0; card < NumCards; ++card) + { + 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; + } + + // 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) + { + 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 OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel) +{ + switch(volumeModel) + { + case ADLMIDI_VolumeModel_AUTO://Do nothing until restart playing + break; + + case ADLMIDI_VolumeModel_Generic: + m_volumeScale = OPL3::VOLUME_Generic; + break; + + case ADLMIDI_VolumeModel_CMF: + LogarithmicVolumes = true; + m_volumeScale = OPL3::VOLUME_CMF; + break; + + case ADLMIDI_VolumeModel_DMX: + m_volumeScale = OPL3::VOLUME_DMX; + break; + + case ADLMIDI_VolumeModel_APOGEE: + m_volumeScale = OPL3::VOLUME_APOGEE; + break; + + case ADLMIDI_VolumeModel_9X: + m_volumeScale = OPL3::VOLUME_9X; + break; + } +} + +void OPL3::Reset() +{ + LogarithmicVolumes = false; + #ifdef ADLMIDI_USE_DOSBOX_OPL + DBOPL::Handler emptyChip; //Constructors inside are will initialize necessary fields + #else + _opl3_chip emptyChip; + memset(&emptyChip, 0, sizeof(_opl3_chip)); + #endif + cards.clear(); + ins.clear(); + pit.clear(); + regBD.clear(); + cards.resize(NumCards, emptyChip); + NumChannels = NumCards * 23; + ins.resize(NumChannels, 189); + pit.resize(NumChannels, 0); + regBD.resize(NumCards, 0); + four_op_category.resize(NumChannels, 0); + + for(unsigned p = 0, a = 0; a < NumCards; ++a) + { + for(unsigned b = 0; b < 18; ++b) + four_op_category[p++] = 0; + + for(unsigned b = 0; b < 5; ++b) + four_op_category[p++] = 8; + } + + static const uint16_t 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) + { + #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; + } + + // 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) + { + 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(); +} |