From f742d3eca4a72af940da7399d69e0b93c0d981f5 Mon Sep 17 00:00:00 2001 From: Wohlstand Date: Fri, 26 May 2023 03:43:01 +0300 Subject: Opal: Convert entire Opal code into the Pure C --- src/chips/opal/README.old | 9 - src/chips/opal/old/LICENSE.txt | 19 + src/chips/opal/old/README.old | 9 + src/chips/opal/old/opal-orig.hpp | 1398 +++++++++++++++++++++++++++++++++++++ src/chips/opal/old/opal-pan.diff | 131 ++++ src/chips/opal/opal-pan.diff | 131 ---- src/chips/opal/opal.c | 1427 ++++++++++++++++++++++++++++++++++++++ src/chips/opal/opal.h | 165 +++++ src/chips/opal/opal.hpp | 1398 ------------------------------------- 9 files changed, 3149 insertions(+), 1538 deletions(-) delete mode 100644 src/chips/opal/README.old create mode 100644 src/chips/opal/old/LICENSE.txt create mode 100644 src/chips/opal/old/README.old create mode 100644 src/chips/opal/old/opal-orig.hpp create mode 100644 src/chips/opal/old/opal-pan.diff delete mode 100644 src/chips/opal/opal-pan.diff create mode 100644 src/chips/opal/opal.c create mode 100644 src/chips/opal/opal.h delete mode 100644 src/chips/opal/opal.hpp (limited to 'src/chips/opal') diff --git a/src/chips/opal/README.old b/src/chips/opal/README.old deleted file mode 100644 index 902dee5..0000000 --- a/src/chips/opal/README.old +++ /dev/null @@ -1,9 +0,0 @@ -Copy opal.cpp here to make this emulator available. -This is not distributed with the software while the license is undetermined. - -The opal.cpp file is part of REALITY ADLIB TRACKER V2. -SHA1: c034dd637887d81770cee92da309fc547ef5fe18 - -Configure the software with the option USE_OPAL_EMULATOR=ON. -To enable soft panning, it is needed to patch with the file `opal-pan.diff` provided. -for example: `patch -p0 -i opal-pan.diff` diff --git a/src/chips/opal/old/LICENSE.txt b/src/chips/opal/old/LICENSE.txt new file mode 100644 index 0000000..2ee2f03 --- /dev/null +++ b/src/chips/opal/old/LICENSE.txt @@ -0,0 +1,19 @@ +A quote from a letter sent by Willy Reeve to Vitaly Novichkov +as a reply from a letter sent by Reality's (https://www.3eality.com/) +contact form +---------------------------------------------------------------- +All source code supplied with RAD is in the public domain. +---------------------------------------------------------------- + +================================================================ +The complete letter: +================================================================ +Hi, + +All source code supplied with RAD is in the public domain. +We'll clarify that in the documentation for the next release. + +Later... +SHAYDE +================================================================ + diff --git a/src/chips/opal/old/README.old b/src/chips/opal/old/README.old new file mode 100644 index 0000000..902dee5 --- /dev/null +++ b/src/chips/opal/old/README.old @@ -0,0 +1,9 @@ +Copy opal.cpp here to make this emulator available. +This is not distributed with the software while the license is undetermined. + +The opal.cpp file is part of REALITY ADLIB TRACKER V2. +SHA1: c034dd637887d81770cee92da309fc547ef5fe18 + +Configure the software with the option USE_OPAL_EMULATOR=ON. +To enable soft panning, it is needed to patch with the file `opal-pan.diff` provided. +for example: `patch -p0 -i opal-pan.diff` diff --git a/src/chips/opal/old/opal-orig.hpp b/src/chips/opal/old/opal-orig.hpp new file mode 100644 index 0000000..e8110a5 --- /dev/null +++ b/src/chips/opal/old/opal-orig.hpp @@ -0,0 +1,1398 @@ +/* + + The Opal OPL3 emulator. + + Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes. + + Missing features compared to a real OPL3: + + - Timers/interrupts + - OPL3 enable bit (it defaults to always on) + - CSW mode + - Test register + - Percussion mode + +*/ +#define OPAL_HAVE_SOFT_PANNING 1 /* libADLMIDI */ + + + +#include + + + +//================================================================================================== +// Opal class. +//================================================================================================== +class Opal { + + class Channel; + + // Various constants + enum { + OPL3SampleRate = 49716, + NumChannels = 18, + NumOperators = 36, + + EnvOff = -1, + EnvAtt, + EnvDec, + EnvSus, + EnvRel + }; + + // A single FM operator + class Operator { + + public: + Operator(); + void SetMaster(Opal *opal) { Master = opal; } + void SetChannel(Channel *chan) { Chan = chan; } + + int16_t Output(uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod = 0, int16_t fbshift = 0); + + void SetKeyOn(bool on); + void SetTremoloEnable(bool on); + void SetVibratoEnable(bool on); + void SetSustainMode(bool on); + void SetEnvelopeScaling(bool on); + void SetFrequencyMultiplier(uint16_t scale); + void SetKeyScale(uint16_t scale); + void SetOutputLevel(uint16_t level); + void SetAttackRate(uint16_t rate); + void SetDecayRate(uint16_t rate); + void SetSustainLevel(uint16_t level); + void SetReleaseRate(uint16_t rate); + void SetWaveform(uint16_t wave); + + void ComputeRates(); + void ComputeKeyScaleLevel(); + + protected: + Opal * Master; // Master object + Channel * Chan; // Owning channel + uint32_t Phase; // The current offset in the selected waveform + uint16_t Waveform; // The waveform id this operator is using + uint16_t FreqMultTimes2; // Frequency multiplier * 2 + int EnvelopeStage; // Which stage the envelope is at (see Env* enums above) + int16_t EnvelopeLevel; // 0 - $1FF, 0 being the loudest + uint16_t OutputLevel; // 0 - $FF + uint16_t AttackRate; + uint16_t DecayRate; + uint16_t SustainLevel; + uint16_t ReleaseRate; + uint16_t AttackShift; + uint16_t AttackMask; + uint16_t AttackAdd; + const uint16_t *AttackTab; + uint16_t DecayShift; + uint16_t DecayMask; + uint16_t DecayAdd; + const uint16_t *DecayTab; + uint16_t ReleaseShift; + uint16_t ReleaseMask; + uint16_t ReleaseAdd; + const uint16_t *ReleaseTab; + uint16_t KeyScaleShift; + uint16_t KeyScaleLevel; + int16_t Out[2]; + bool KeyOn; + bool KeyScaleRate; // Affects envelope rate scaling + bool SustainMode; // Whether to sustain during the sustain phase, or release instead + bool TremoloEnable; + bool VibratoEnable; + }; + + // A single channel, which can contain two or more operators + class Channel { + + public: + Channel(); + void SetMaster(Opal *opal) { Master = opal; } + void SetOperators(Operator *a, Operator *b, Operator *c, Operator *d) { + Op[0] = a; + Op[1] = b; + Op[2] = c; + Op[3] = d; + if (a) + a->SetChannel(this); + if (b) + b->SetChannel(this); + if (c) + c->SetChannel(this); + if (d) + d->SetChannel(this); + } + + void Output(int16_t &left, int16_t &right); + void SetEnable(bool on) { Enable = on; } + void SetChannelPair(Channel *pair) { ChannelPair = pair; } + + void SetFrequencyLow(uint16_t freq); + void SetFrequencyHigh(uint16_t freq); + void SetKeyOn(bool on); + void SetOctave(uint16_t oct); + void SetLeftEnable(bool on); + void SetRightEnable(bool on); + void SetPan(uint8_t pan); + void SetFeedback(uint16_t val); + void SetModulationType(uint16_t type); + + uint16_t GetFreq() const { return Freq; } + uint16_t GetOctave() const { return Octave; } + uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; } + uint16_t GetModulationType() const { return ModulationType; } + Channel * GetChannelPair() const { return ChannelPair; } /* libADLMIDI */ + + void ComputeKeyScaleNumber(); + + protected: + void ComputePhaseStep(); + + Operator * Op[4]; + + Opal * Master; // Master object + uint16_t Freq; // Frequency; actually it's a phase stepping value + uint16_t Octave; // Also known as "block" in Yamaha parlance + uint32_t PhaseStep; + uint16_t KeyScaleNumber; + uint16_t FeedbackShift; + uint16_t ModulationType; + Channel * ChannelPair; + bool Enable; + bool LeftEnable, RightEnable; + uint16_t LeftPan, RightPan; + }; + + public: + Opal(int sample_rate); + ~Opal(); + + void SetSampleRate(int sample_rate); + void Port(uint16_t reg_num, uint8_t val); + void Pan(uint16_t reg_num, uint8_t pan); + void Sample(int16_t *left, int16_t *right); + + protected: + void Init(int sample_rate); + void Output(int16_t &left, int16_t &right); + + int32_t SampleRate; + int32_t SampleAccum; + int16_t LastOutput[2], CurrOutput[2]; + Channel Chan[NumChannels]; + Operator Op[NumOperators]; +// uint16_t ExpTable[256]; +// uint16_t LogSinTable[256]; + uint16_t Clock; + uint16_t TremoloClock; + uint16_t TremoloLevel; + uint16_t VibratoTick; + uint16_t VibratoClock; + bool NoteSel; + bool TremoloDepth; + bool VibratoDepth; + + static const uint16_t RateTables[4][8]; + static const uint16_t ExpTable[256]; + static const uint16_t LogSinTable[256]; + static const uint16_t PanLawTable[128]; +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::RateTables[4][8] = { + { 1, 0, 1, 0, 1, 0, 1, 0 }, + { 1, 0, 1, 0, 0, 0, 1, 0 }, + { 1, 0, 0, 0, 1, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 0, 0 }, +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::ExpTable[0x100] = { + 1018, 1013, 1007, 1002, 996, 991, 986, 980, 975, 969, 964, 959, 953, 948, 942, 937, + 932, 927, 921, 916, 911, 906, 900, 895, 890, 885, 880, 874, 869, 864, 859, 854, + 849, 844, 839, 834, 829, 824, 819, 814, 809, 804, 799, 794, 789, 784, 779, 774, + 770, 765, 760, 755, 750, 745, 741, 736, 731, 726, 722, 717, 712, 708, 703, 698, + 693, 689, 684, 680, 675, 670, 666, 661, 657, 652, 648, 643, 639, 634, 630, 625, + 621, 616, 612, 607, 603, 599, 594, 590, 585, 581, 577, 572, 568, 564, 560, 555, + 551, 547, 542, 538, 534, 530, 526, 521, 517, 513, 509, 505, 501, 496, 492, 488, + 484, 480, 476, 472, 468, 464, 460, 456, 452, 448, 444, 440, 436, 432, 428, 424, + 420, 416, 412, 409, 405, 401, 397, 393, 389, 385, 382, 378, 374, 370, 367, 363, + 359, 355, 352, 348, 344, 340, 337, 333, 329, 326, 322, 318, 315, 311, 308, 304, + 300, 297, 293, 290, 286, 283, 279, 276, 272, 268, 265, 262, 258, 255, 251, 248, + 244, 241, 237, 234, 231, 227, 224, 220, 217, 214, 210, 207, 204, 200, 197, 194, + 190, 187, 184, 181, 177, 174, 171, 168, 164, 161, 158, 155, 152, 148, 145, 142, + 139, 136, 133, 130, 126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, + 90, 87, 84, 81, 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, + 42, 40, 37, 34, 31, 28, 25, 22, 20, 17, 14, 11, 8, 6, 3, 0, +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::LogSinTable[0x100] = { + 2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, + 846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, + 598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, + 453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, + 352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, + 276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, + 215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, + 167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, + 127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, + 94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, + 67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, + 46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, + 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, + 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, +}; +//-------------------------------------------------------------------------------------------------- +const uint16_t Opal::PanLawTable[128] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, // Center left + 46340, // Center right + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + + + +//================================================================================================== +// This is the temporary code for generating the above tables. Maths and data from this nice +// reverse-engineering effort: +// +// https://docs.google.com/document/d/18IGx18NQY_Q1PJVZ-bHywao9bhsDoAqoIn1rIm42nwo/edit +//================================================================================================== +#if 0 +#include + +void GenerateTables() { + + // Build the exponentiation table (reversed from the official OPL3 ROM) + FILE *fd = fopen("exptab.txt", "wb"); + if (fd) { + for (int i = 0; i < 0x100; i++) { + int v = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; + if (i & 15) + fprintf(fd, " %4d,", v); + else + fprintf(fd, "\n\t%4d,", v); + } + fclose(fd); + } + + // Build the log-sin table + fd = fopen("sintab.txt", "wb"); + if (fd) { + for (int i = 0; i < 0x100; i++) { + int v = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; + if (i & 15) + fprintf(fd, " %4d,", v); + else + fprintf(fd, "\n\t%4d,", v); + } + fclose(fd); + } +} +#endif + + + +//================================================================================================== +// Constructor/destructor. +//================================================================================================== +Opal::Opal(int sample_rate) { + + Init(sample_rate); +} +//-------------------------------------------------------------------------------------------------- +Opal::~Opal() { +} + + + +//================================================================================================== +// Initialise the emulation. +//================================================================================================== +void Opal::Init(int sample_rate) { + + Clock = 0; + TremoloClock = 0; + TremoloLevel = 0; + VibratoTick = 0; + VibratoClock = 0; + NoteSel = false; + TremoloDepth = false; + VibratoDepth = false; + +// // Build the exponentiation table (reversed from the official OPL3 ROM) +// for (int i = 0; i < 0x100; i++) +// ExpTable[i] = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; +// +// // Build the log-sin table +// for (int i = 0; i < 0x100; i++) +// LogSinTable[i] = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; + + // Let sub-objects know where to find us + for (int i = 0; i < NumOperators; i++) + Op[i].SetMaster(this); + + for (int i = 0; i < NumChannels; i++) + Chan[i].SetMaster(this); + + // Add the operators to the channels. Note, some channels can't use all the operators + // FIXME: put this into a separate routine + const int chan_ops[] = { + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32, + }; + + for (int i = 0; i < NumChannels; i++) { + Channel *chan = &Chan[i]; + int op = chan_ops[i]; + if (i < 3 || (i >= 9 && i < 12)) + chan->SetOperators(&Op[op], &Op[op + 3], &Op[op + 6], &Op[op + 9]); + else + chan->SetOperators(&Op[op], &Op[op + 3], 0, 0); + } + + // Initialise the operator rate data. We can't do this in the Operator constructor as it + // relies on referencing the master and channel objects + for (int i = 0; i < NumOperators; i++) + Op[i].ComputeRates(); + + // Initialise channel panning at center. + for (int i = 0; i < NumChannels; i++) { + Chan[i].SetPan(64); + Chan[i].SetLeftEnable(true); + Chan[i].SetRightEnable(true); + } + + SetSampleRate(sample_rate); +} + + + +//================================================================================================== +// Change the sample rate. +//================================================================================================== +void Opal::SetSampleRate(int sample_rate) { + + // Sanity + if (sample_rate == 0) + sample_rate = OPL3SampleRate; + + SampleRate = sample_rate; + SampleAccum = 0; + LastOutput[0] = LastOutput[1] = 0; + CurrOutput[0] = CurrOutput[1] = 0; +} + + + +//================================================================================================== +// Write a value to an OPL3 register. +//================================================================================================== +void Opal::Port(uint16_t reg_num, uint8_t val) { + + const int op_lookup[] = { + // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + // 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + uint16_t type = reg_num & 0xE0; + + // Is it BD, the one-off register stuck in the middle of the register array? + if (reg_num == 0xBD) { + TremoloDepth = (val & 0x80) != 0; + VibratoDepth = (val & 0x40) != 0; + return; + } + + // Global registers + if (type == 0x00) { + + // 4-OP enables + if (reg_num == 0x104) { + + // Enable/disable channels based on which 4-op enables + uint8_t mask = 1; + for (int i = 0; i < 6; i++, mask <<= 1) { + + // The 4-op channels are 0, 1, 2, 9, 10, 11 + uint16_t chan = i < 3 ? i : i + 6; + Channel *primary = &Chan[chan]; + Channel *secondary = &Chan[chan + 3]; + + if (val & mask) { + + // Let primary channel know it's controlling the secondary channel + primary->SetChannelPair(secondary); + + // Turn off the second channel in the pair + secondary->SetEnable(false); + + } else { + + // Let primary channel know it's no longer controlling the secondary channel + primary->SetChannelPair(0); + + // Turn on the second channel in the pair + secondary->SetEnable(true); + } + } + + // CSW / Note-sel + } else if (reg_num == 0x08) { + + NoteSel = (val & 0x40) != 0; + + // Get the channels to recompute the Key Scale No. as this varies based on NoteSel + for (int i = 0; i < NumChannels; i++) + Chan[i].ComputeKeyScaleNumber(); + } + + // Channel registers + } else if (type >= 0xA0 && type <= 0xC0) { + + // Convert to channel number + int chan_num = reg_num & 15; + + // Valid channel? + if (chan_num >= 9) + return; + + // Is it the other bank of channels? + if (reg_num & 0x100) + chan_num += 9; + + Channel &chan = Chan[chan_num]; + + /* libADLMIDI: registers Ax and Cx affect both channels */ + Channel *chans[2] = {&chan, chan.GetChannelPair()}; + unsigned numchans = chans[1] ? 2 : 1; + + // Do specific registers + switch (reg_num & 0xF0) { + + // Frequency low + case 0xA0: { + for (unsigned i = 0; i < numchans; ++i) { /* libADLMIDI */ + chans[i]->SetFrequencyLow(val); + } + break; + } + + // Key-on / Octave / Frequency High + case 0xB0: { + for (unsigned i = 0; i < numchans; ++i) { /* libADLMIDI */ + chans[i]->SetKeyOn((val & 0x20) != 0); + chans[i]->SetOctave(val >> 2 & 7); + chans[i]->SetFrequencyHigh(val & 3); + } + break; + } + + // Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type + case 0xC0: { + chan.SetRightEnable((val & 0x20) != 0); + chan.SetLeftEnable((val & 0x10) != 0); + chan.SetFeedback(val >> 1 & 7); + chan.SetModulationType(val & 1); + break; + } + } + + // Operator registers + } else if ((type >= 0x20 && type <= 0x80) || type == 0xE0) { + + // Convert to operator number + int op_num = op_lookup[reg_num & 0x1F]; + + // Valid register? + if (op_num < 0) + return; + + // Is it the other bank of operators? + if (reg_num & 0x100) + op_num += 18; + + Operator &op = Op[op_num]; + + // Do specific registers + switch (type) { + + // Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier + case 0x20: { + op.SetTremoloEnable((val & 0x80) != 0); + op.SetVibratoEnable((val & 0x40) != 0); + op.SetSustainMode((val & 0x20) != 0); + op.SetEnvelopeScaling((val & 0x10) != 0); + op.SetFrequencyMultiplier(val & 15); + break; + } + + // Key Scale / Output Level + case 0x40: { + op.SetKeyScale(val >> 6); + op.SetOutputLevel(val & 0x3F); + break; + } + + // Attack Rate / Decay Rate + case 0x60: { + op.SetAttackRate(val >> 4); + op.SetDecayRate(val & 15); + break; + } + + // Sustain Level / Release Rate + case 0x80: { + op.SetSustainLevel(val >> 4); + op.SetReleaseRate(val & 15); + break; + } + + // Waveform + case 0xE0: { + op.SetWaveform(val & 7); + break; + } + } + } +} + + + +//================================================================================================== +// Set panning on the channel designated by the register number. +// This is extended functionality. +//================================================================================================== +void Opal::Pan(uint16_t reg_num, uint8_t pan) +{ + uint8_t high = (reg_num >> 8) & 1; + uint8_t regm = reg_num & 0xff; + Chan[9 * high + (regm & 0x0f)].SetPan(pan); +} + + + +//================================================================================================== +// Generate sample. Every time you call this you will get two signed 16-bit samples (one for each +// stereo channel) which will sound correct when played back at the sample rate given when the +// class was constructed. +//================================================================================================== +void Opal::Sample(int16_t *left, int16_t *right) { + + // If the destination sample rate is higher than the OPL3 sample rate, we need to skip ahead + while (SampleAccum >= SampleRate) { + + LastOutput[0] = CurrOutput[0]; + LastOutput[1] = CurrOutput[1]; + + Output(CurrOutput[0], CurrOutput[1]); + + SampleAccum -= SampleRate; + } + + // Mix with the partial accumulation + int32_t omblend = SampleRate - SampleAccum; + *left = (LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate; + *right = (LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate; + + SampleAccum += OPL3SampleRate; +} + + + +//================================================================================================== +// Produce final output from the chip. This is at the OPL3 sample-rate. +//================================================================================================== +void Opal::Output(int16_t &left, int16_t &right) { + + int32_t leftmix = 0, rightmix = 0; + + // Sum the output of each channel + for (int i = 0; i < NumChannels; i++) { + + int16_t chanleft, chanright; + Chan[i].Output(chanleft, chanright); + + leftmix += chanleft; + rightmix += chanright; + } + + // Clamp + if (leftmix < -0x8000) + left = -0x8000; + else if (leftmix > 0x7FFF) + left = 0x7FFF; + else + left = leftmix; + + if (rightmix < -0x8000) + right = -0x8000; + else if (rightmix > 0x7FFF) + right = 0x7FFF; + else + right = rightmix; + + Clock++; + + // Tremolo. According to this post, the OPL3 tremolo is a 13,440 sample length triangle wave + // with a peak at 26 and a trough at 0 and is simply added to the logarithmic level accumulator + // http://forums.submarine.org.uk/phpBB/viewtopic.php?f=9&t=1171 + TremoloClock = (TremoloClock + 1) % 13440; + TremoloLevel = ((TremoloClock < 13440 / 2) ? TremoloClock : 13440 - TremoloClock) / 256; + if (!TremoloDepth) + TremoloLevel >>= 2; + + // Vibrato. This appears to be a 8 sample long triangle wave with a magnitude of the three + // high bits of the channel frequency, positive and negative, divided by two if the vibrato + // depth is zero. It is only cycled every 1,024 samples. + VibratoTick++; + if (VibratoTick >= 1024) { + VibratoTick = 0; + VibratoClock = (VibratoClock + 1) & 7; + } +} + + + +//================================================================================================== +// Channel constructor. +//================================================================================================== +Opal::Channel::Channel() { + + Master = 0; + Freq = 0; + Octave = 0; + PhaseStep = 0; + KeyScaleNumber = 0; + FeedbackShift = 0; + ModulationType = 0; + ChannelPair = 0; + Enable = true; +} + + + +//================================================================================================== +// Produce output from channel. +//================================================================================================== +void Opal::Channel::Output(int16_t &left, int16_t &right) { + + // Has the channel been disabled? This is usually a result of the 4-op enables being used to + // disable the secondary channel in each 4-op pair + if (!Enable) { + left = right = 0; + return; + } + + int16_t vibrato = (Freq >> 7) & 7; + if (!Master->VibratoDepth) + vibrato >>= 1; + + // 0 3 7 3 0 -3 -7 -3 + uint16_t clk = Master->VibratoClock; + if (!(clk & 3)) + vibrato = 0; // Position 0 and 4 is zero + else { + if (clk & 1) + vibrato >>= 1; // Odd positions are half the magnitude + if (clk & 4) + vibrato = -vibrato; // The second half positions are negative + } + + vibrato <<= Octave; + + // Combine individual operator outputs + int16_t out, acc; + + // Running in 4-op mode? + if (ChannelPair) { + + // Get the secondary channel's modulation type. This is the only thing from the secondary + // channel that is used + if (ChannelPair->GetModulationType() == 0) { + + if (ModulationType == 0) { + + // feedback -> modulator -> modulator -> modulator -> carrier + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + out = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + out = Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + + } else { + + // (feedback -> carrier) + (modulator -> modulator -> carrier) + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + } + + } else { + + if (ModulationType == 0) { + + // (feedback -> modulator -> carrier) + (modulator -> carrier) + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + + } else { + + // (feedback -> carrier) + (modulator -> carrier) + carrier + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + out += Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); + out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); + } + } + + } else { + + // Standard 2-op mode + if (ModulationType == 0) { + + // Frequency modulation (well, phase modulation technically) + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); + + } else { + + // Additive + out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); + out += Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato); + } + } + + left = LeftEnable ? out : 0; + right = RightEnable ? out : 0; + + left = left * LeftPan / 65536; + right = right * RightPan / 65536; +} + + + +//================================================================================================== +// Set phase step for operators using this channel. +//================================================================================================== +void Opal::Channel::SetFrequencyLow(uint16_t freq) { + + Freq = (Freq & 0x300) | (freq & 0xFF); + ComputePhaseStep(); +} +//-------------------------------------------------------------------------------------------------- +void Opal::Channel::SetFrequencyHigh(uint16_t freq) { + + Freq = (Freq & 0xFF) | ((freq & 3) << 8); + ComputePhaseStep(); + + // Only the high bits of Freq affect the Key Scale No. + ComputeKeyScaleNumber(); +} + + + +//================================================================================================== +// Set the octave of the channel (0 to 7). +//================================================================================================== +void Opal::Channel::SetOctave(uint16_t oct) { + + Octave = oct & 7; + ComputePhaseStep(); + ComputeKeyScaleNumber(); +} + + + +//================================================================================================== +// Keys the channel on/off. +//================================================================================================== +void Opal::Channel::SetKeyOn(bool on) { + + Op[0]->SetKeyOn(on); + Op[1]->SetKeyOn(on); +} + + + +//================================================================================================== +// Enable left stereo channel. +//================================================================================================== +void Opal::Channel::SetLeftEnable(bool on) { + + LeftEnable = on; +} + + + +//================================================================================================== +// Enable right stereo channel. +//================================================================================================== +void Opal::Channel::SetRightEnable(bool on) { + + RightEnable = on; +} + + + +//================================================================================================== +// Pan the channel to the position given. +//================================================================================================== +void Opal::Channel::SetPan(uint8_t pan) +{ + pan &= 127; + LeftPan = PanLawTable[pan]; + RightPan = PanLawTable[127 - pan]; +} + + + +//================================================================================================== +// Set the channel feedback amount. +//================================================================================================== +void Opal::Channel::SetFeedback(uint16_t val) { + + FeedbackShift = val ? 9 - val : 0; +} + + + +//================================================================================================== +// Set frequency modulation/additive modulation +//================================================================================================== +void Opal::Channel::SetModulationType(uint16_t type) { + + ModulationType = type; +} + + + +//================================================================================================== +// Compute the stepping factor for the operator waveform phase based on the frequency and octave +// values of the channel. +//================================================================================================== +void Opal::Channel::ComputePhaseStep() { + + PhaseStep = uint32_t(Freq) << Octave; +} + + + +//================================================================================================== +// Compute the key scale number and key scale levels. +// +// From the Yamaha data sheet this is the block/octave number as bits 3-1, with bit 0 coming from +// the MSB of the frequency if NoteSel is 1, and the 2nd MSB if NoteSel is 0. +//================================================================================================== +void Opal::Channel::ComputeKeyScaleNumber() { + + uint16_t lsb = Master->NoteSel ? Freq >> 9 : (Freq >> 8) & 1; + KeyScaleNumber = Octave << 1 | lsb; + + // Get the channel operators to recompute their rates as they're dependent on this number. They + // also need to recompute their key scale level + for (int i = 0; i < 4; i++) { + + if (!Op[i]) + continue; + + Op[i]->ComputeRates(); + Op[i]->ComputeKeyScaleLevel(); + } +} + + + +//================================================================================================== +// Operator constructor. +//================================================================================================== +Opal::Operator::Operator() { + + Master = 0; + Chan = 0; + Phase = 0; + Waveform = 0; + FreqMultTimes2 = 1; + EnvelopeStage = EnvOff; + EnvelopeLevel = 0x1FF; + AttackRate = 0; + DecayRate = 0; + SustainLevel = 0; + ReleaseRate = 0; + KeyScaleShift = 0; + KeyScaleLevel = 0; + Out[0] = Out[1] = 0; + KeyOn = false; + KeyScaleRate = false; + SustainMode = false; + TremoloEnable = false; + VibratoEnable = false; +} + + + +//================================================================================================== +// Produce output from operator. +//================================================================================================== +int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, int16_t vibrato, int16_t mod, int16_t fbshift) { + + // Advance wave phase + if (VibratoEnable) + phase_step += vibrato; + Phase += (phase_step * FreqMultTimes2) / 2; + + uint16_t level = (EnvelopeLevel + OutputLevel + KeyScaleLevel + (TremoloEnable ? Master->TremoloLevel : 0)) << 3; + + switch (EnvelopeStage) { + + // Attack stage + case EnvAtt: { + uint16_t add = ((AttackAdd >> AttackTab[Master->Clock >> AttackShift & 7]) * ~EnvelopeLevel) >> 3; + if (AttackRate == 0) + add = 0; + if (AttackMask && (Master->Clock & AttackMask)) + add = 0; + EnvelopeLevel += add; + if (EnvelopeLevel <= 0) { + EnvelopeLevel = 0; + EnvelopeStage = EnvDec; + } + break; + } + + // Decay stage + case EnvDec: { + uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; + if (DecayRate == 0) + add = 0; + if (DecayMask && (Master->Clock & DecayMask)) + add = 0; + EnvelopeLevel += add; + if (EnvelopeLevel >= SustainLevel) { + EnvelopeLevel = SustainLevel; + EnvelopeStage = EnvSus; + } + break; + } + + // Sustain stage + case EnvSus: { + if (SustainMode) + break; + // Note: fall-through! + + }//fallthrough + + // Release stage + case EnvRel: { + uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7]; + if (ReleaseRate == 0) + add = 0; + if (ReleaseMask && (Master->Clock & ReleaseMask)) + add = 0; + EnvelopeLevel += add; + if (EnvelopeLevel >= 0x1FF) { + EnvelopeLevel = 0x1FF; + EnvelopeStage = EnvOff; + Out[0] = Out[1] = 0; + return 0; + } + break; + } + + // Envelope, and therefore the operator, is not running + default: + Out[0] = Out[1] = 0; + return 0; + } + + // Feedback? In that case we modulate by a blend of the last two samples + if (fbshift) + mod += (Out[0] + Out[1]) >> fbshift; + + uint16_t phase = (Phase >> 10) + mod; + uint16_t offset = phase & 0xFF; + uint16_t logsin; + bool negate = false; + + switch (Waveform) { + + //------------------------------------ + // Standard sine wave + //------------------------------------ + case 0: + if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + negate = (phase & 0x200) != 0; + break; + + //------------------------------------ + // Half sine wave + //------------------------------------ + case 1: + if (phase & 0x200) + offset = 0; + else if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Positive sine wave + //------------------------------------ + case 2: + if (phase & 0x100) + offset ^= 0xFF; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Quarter positive sine wave + //------------------------------------ + case 3: + if (phase & 0x100) + offset = 0; + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Double-speed sine wave + //------------------------------------ + case 4: + if (phase & 0x200) + offset = 0; + + else { + + if (phase & 0x80) + offset ^= 0xFF; + + offset = (offset + offset) & 0xFF; + negate = (phase & 0x100) != 0; + } + + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Double-speed positive sine wave + //------------------------------------ + case 5: + if (phase & 0x200) + offset = 0; + + else { + + offset = (offset + offset) & 0xFF; + if (phase & 0x80) + offset ^= 0xFF; + } + + logsin = Master->LogSinTable[offset]; + break; + + //------------------------------------ + // Square wave + //------------------------------------ + case 6: + logsin = 0; + negate = (phase & 0x200) != 0; + break; + + //------------------------------------ + // Exponentiation wave + //------------------------------------ + default: + logsin = phase & 0x1FF; + if (phase & 0x200) { + logsin ^= 0x1FF; + negate = true; + } + logsin <<= 3; + break; + } + + uint16_t mix = logsin + level; + if (mix > 0x1FFF) + mix = 0x1FFF; + + // From the OPLx decapsulated docs: + // "When such a table is used for calculation of the exponential, the table is read at the + // position given by the 8 LSB's of the input. The value + 1024 (the hidden bit) is then the + // significand of the floating point output and the yet unused MSB's of the input are the + // exponent of the floating point output." + int16_t v = Master->ExpTable[mix & 0xFF] + 1024; + v >>= mix >> 8; + v += v; + if (negate) + v = ~v; + + // Keep last two results for feedback calculation + Out[1] = Out[0]; + Out[0] = v; + + return v; +} + + + +//================================================================================================== +// Trigger operator. +//================================================================================================== +void Opal::Operator::SetKeyOn(bool on) { + + // Already on/off? + if (KeyOn == on) + return; + KeyOn = on; + + if (on) { + + // The highest attack rate is instant; it bypasses the attack phase + if (AttackRate == 15) { + EnvelopeStage = EnvDec; + EnvelopeLevel = 0; + } else + EnvelopeStage = EnvAtt; + + Phase = 0; + + } else { + + // Stopping current sound? + if (EnvelopeStage != EnvOff && EnvelopeStage != EnvRel) + EnvelopeStage = EnvRel; + } +} + + + +//================================================================================================== +// Enable amplitude vibrato. +//================================================================================================== +void Opal::Operator::SetTremoloEnable(bool on) { + + TremoloEnable = on; +} + + + +//================================================================================================== +// Enable frequency vibrato. +//================================================================================================== +void Opal::Operator::SetVibratoEnable(bool on) { + + VibratoEnable = on; +} + + + +//================================================================================================== +// Sets whether we release or sustain during the sustain phase of the envelope. 'true' is to +// sustain, otherwise release. +//================================================================================================== +void Opal::Operator::SetSustainMode(bool on) { + + SustainMode = on; +} + + + +//================================================================================================== +// Key scale rate. Sets how much the Key Scaling Number affects the envelope rates. +//================================================================================================== +void Opal::Operator::SetEnvelopeScaling(bool on) { + + KeyScaleRate = on; + ComputeRates(); +} + + + +//================================================================================================== +// Multiplies the phase frequency. +//================================================================================================== +void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) { + + // Needs to be multiplied by two (and divided by two later when we use it) because the first + // entry is actually .5 + const uint16_t mul_times_2[] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30, + }; + + FreqMultTimes2 = mul_times_2[scale & 15]; +} + + + +//================================================================================================== +// Attenuates output level towards higher pitch. +//================================================================================================== +void Opal::Operator::SetKeyScale(uint16_t scale) { + + /* libADLMIDI: KSL computation fix */ + const unsigned KeyScaleShiftTable[4] = {8, 1, 2, 0}; + KeyScaleShift = KeyScaleShiftTable[scale]; + + ComputeKeyScaleLevel(); +} + + + +//================================================================================================== +// Sets the output level (volume) of the operator. +//================================================================================================== +void Opal::Operator::SetOutputLevel(uint16_t level) { + + OutputLevel = level * 4; +} + + + +//================================================================================================== +// Operator attack rate. +//================================================================================================== +void Opal::Operator::SetAttackRate(uint16_t rate) { + + AttackRate = rate; + + ComputeRates(); +} + + + +//================================================================================================== +// Operator decay rate. +//================================================================================================== +void Opal::Operator::SetDecayRate(uint16_t rate) { + + DecayRate = rate; + + ComputeRates(); +} + + + +//================================================================================================== +// Operator sustain level. +//================================================================================================== +void Opal::Operator::SetSustainLevel(uint16_t level) { + + SustainLevel = level < 15 ? level : 31; + SustainLevel *= 16; +} + + + +//================================================================================================== +// Operator release rate. +//================================================================================================== +void Opal::Operator::SetReleaseRate(uint16_t rate) { + + ReleaseRate = rate; + + ComputeRates(); +} + + + +//================================================================================================== +// Assign the waveform this operator will use. +//================================================================================================== +void Opal::Operator::SetWaveform(uint16_t wave) { + + Waveform = wave & 7; +} + + + +//================================================================================================== +// Compute actual rate from register rate. From the Yamaha data sheet: +// +// Actual rate = Rate value * 4 + Rof, if Rate value = 0, actual rate = 0 +// +// Rof is set as follows depending on the KSR setting: +// +// Key scale 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// KSR = 0 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 +// KSR = 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// +// Note: zero rates are infinite, and are treated separately elsewhere +//================================================================================================== +void Opal::Operator::ComputeRates() { + + int combined_rate = AttackRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); + int rate_high = combined_rate >> 2; + int rate_low = combined_rate & 3; + + AttackShift = rate_high < 12 ? 12 - rate_high : 0; + AttackMask = (1 << AttackShift) - 1; + AttackAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + AttackTab = Master->RateTables[rate_low]; + + // Attack rate of 15 is always instant + if (AttackRate == 15) + AttackAdd = 0xFFF; + + combined_rate = DecayRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); + rate_high = combined_rate >> 2; + rate_low = combined_rate & 3; + + DecayShift = rate_high < 12 ? 12 - rate_high : 0; + DecayMask = (1 << DecayShift) - 1; + DecayAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + DecayTab = Master->RateTables[rate_low]; + + combined_rate = ReleaseRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); + rate_high = combined_rate >> 2; + rate_low = combined_rate & 3; + + ReleaseShift = rate_high < 12 ? 12 - rate_high : 0; + ReleaseMask = (1 << ReleaseShift) - 1; + ReleaseAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + ReleaseTab = Master->RateTables[rate_low]; +} + + + +//================================================================================================== +// Compute the operator's key scale level. This changes based on the channel frequency/octave and +// operator key scale value. +//================================================================================================== +void Opal::Operator::ComputeKeyScaleLevel() { + + static const uint16_t levtab[] = { + 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, 8, 12, 16, 20, 24, 28, 32, + 0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64, + 0, 0, 0, 20, 32, 44, 52, 60, 64, 72, 76, 80, 84, 88, 92, 96, + 0, 0, 32, 52, 64, 76, 84, 92, 96, 104, 108, 112, 116, 120, 124, 128, + 0, 32, 64, 84, 96, 108, 116, 124, 128, 136, 140, 144, 148, 152, 156, 160, + 0, 64, 96, 116, 128, 140, 148, 156, 160, 168, 172, 176, 180, 184, 188, 192, + 0, 96, 128, 148, 160, 172, 180, 188, 192, 200, 204, 208, 212, 216, 220, 224, + }; + + // This uses a combined value of the top four bits of frequency with the octave/block + uint16_t i = (Chan->GetOctave() << 4) | (Chan->GetFreq() >> 6); + KeyScaleLevel = levtab[i] >> KeyScaleShift; +} diff --git a/src/chips/opal/old/opal-pan.diff b/src/chips/opal/old/opal-pan.diff new file mode 100644 index 0000000..a00a9fc --- /dev/null +++ b/src/chips/opal/old/opal-pan.diff @@ -0,0 +1,131 @@ +--- opal.cpp.orig 2019-02-23 20:32:00.477658789 +0100 ++++ opal.cpp 2019-02-23 20:46:36.745113958 +0100 +@@ -13,6 +13,7 @@ + - Percussion mode + + */ ++#define OPAL_HAVE_SOFT_PANNING 1 /* libADLMIDI */ + + + +@@ -133,6 +134,7 @@ + void SetOctave(uint16_t oct); + void SetLeftEnable(bool on); + void SetRightEnable(bool on); ++ void SetPan(uint8_t pan); + void SetFeedback(uint16_t val); + void SetModulationType(uint16_t type); + +@@ -158,6 +160,7 @@ + Channel * ChannelPair; + bool Enable; + bool LeftEnable, RightEnable; ++ uint16_t LeftPan, RightPan; + }; + + public: +@@ -166,6 +169,7 @@ + + void SetSampleRate(int sample_rate); + void Port(uint16_t reg_num, uint8_t val); ++ void Pan(uint16_t reg_num, uint8_t pan); + void Sample(int16_t *left, int16_t *right); + + protected: +@@ -191,6 +195,7 @@ + static const uint16_t RateTables[4][8]; + static const uint16_t ExpTable[256]; + static const uint16_t LogSinTable[256]; ++ static const uint16_t PanLawTable[128]; + }; + //-------------------------------------------------------------------------------------------------- + const uint16_t Opal::RateTables[4][8] = { +@@ -237,6 +242,28 @@ + 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + }; ++//-------------------------------------------------------------------------------------------------- ++const uint16_t Opal::PanLawTable[128] = ++{ ++ 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, ++ 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, ++ 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, ++ 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, ++ 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, ++ 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, ++ 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, ++ 50433, 49912, 49383, 48846, 48302, 47750, 47191, ++ 46340, // Center left ++ 46340, // Center right ++ 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, ++ 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, ++ 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, ++ 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, ++ 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, ++ 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, ++ 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, ++ 4858, 4050, 3240, 2431, 1620, 810, 0 ++}; + + + +@@ -342,6 +369,10 @@ + for (int i = 0; i < NumOperators; i++) + Op[i].ComputeRates(); + ++ // Initialise channel panning at center. ++ for (int i = 0; i < NumChannels; i++) ++ Chan[i].SetPan(64); ++ + SetSampleRate(sample_rate); + } + +@@ -533,6 +564,19 @@ + + + //================================================================================================== ++// Set panning on the channel designated by the register number. ++// This is extended functionality. ++//================================================================================================== ++void Opal::Pan(uint16_t reg_num, uint8_t pan) ++{ ++ uint8_t high = (reg_num >> 8) & 1; ++ uint8_t regm = reg_num & 0xff; ++ Chan[9 * high + (regm & 0x0f)].SetPan(pan); ++} ++ ++ ++ ++//================================================================================================== + // Generate sample. Every time you call this you will get two signed 16-bit samples (one for each + // stereo channel) which will sound correct when played back at the sample rate given when the + // class was constructed. +@@ -727,6 +771,9 @@ + + left = LeftEnable ? out : 0; + right = RightEnable ? out : 0; ++ ++ left = left * LeftPan / 65536; ++ right = right * RightPan / 65536; + } + + +@@ -793,6 +840,18 @@ + } + + ++ ++//================================================================================================== ++// Pan the channel to the position given. ++//================================================================================================== ++void Opal::Channel::SetPan(uint8_t pan) ++{ ++ pan &= 127; ++ LeftPan = PanLawTable[pan]; ++ RightPan = PanLawTable[127 - pan]; ++} ++ ++ + + //================================================================================================== + // Set the channel feedback amount. diff --git a/src/chips/opal/opal-pan.diff b/src/chips/opal/opal-pan.diff deleted file mode 100644 index a00a9fc..0000000 --- a/src/chips/opal/opal-pan.diff +++ /dev/null @@ -1,131 +0,0 @@ ---- opal.cpp.orig 2019-02-23 20:32:00.477658789 +0100 -+++ opal.cpp 2019-02-23 20:46:36.745113958 +0100 -@@ -13,6 +13,7 @@ - - Percussion mode - - */ -+#define OPAL_HAVE_SOFT_PANNING 1 /* libADLMIDI */ - - - -@@ -133,6 +134,7 @@ - void SetOctave(uint16_t oct); - void SetLeftEnable(bool on); - void SetRightEnable(bool on); -+ void SetPan(uint8_t pan); - void SetFeedback(uint16_t val); - void SetModulationType(uint16_t type); - -@@ -158,6 +160,7 @@ - Channel * ChannelPair; - bool Enable; - bool LeftEnable, RightEnable; -+ uint16_t LeftPan, RightPan; - }; - - public: -@@ -166,6 +169,7 @@ - - void SetSampleRate(int sample_rate); - void Port(uint16_t reg_num, uint8_t val); -+ void Pan(uint16_t reg_num, uint8_t pan); - void Sample(int16_t *left, int16_t *right); - - protected: -@@ -191,6 +195,7 @@ - static const uint16_t RateTables[4][8]; - static const uint16_t ExpTable[256]; - static const uint16_t LogSinTable[256]; -+ static const uint16_t PanLawTable[128]; - }; - //-------------------------------------------------------------------------------------------------- - const uint16_t Opal::RateTables[4][8] = { -@@ -237,6 +242,28 @@ - 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, - 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - }; -+//-------------------------------------------------------------------------------------------------- -+const uint16_t Opal::PanLawTable[128] = -+{ -+ 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, -+ 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, -+ 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, -+ 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, -+ 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, -+ 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, -+ 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, -+ 50433, 49912, 49383, 48846, 48302, 47750, 47191, -+ 46340, // Center left -+ 46340, // Center right -+ 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, -+ 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, -+ 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, -+ 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, -+ 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, -+ 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, -+ 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, -+ 4858, 4050, 3240, 2431, 1620, 810, 0 -+}; - - - -@@ -342,6 +369,10 @@ - for (int i = 0; i < NumOperators; i++) - Op[i].ComputeRates(); - -+ // Initialise channel panning at center. -+ for (int i = 0; i < NumChannels; i++) -+ Chan[i].SetPan(64); -+ - SetSampleRate(sample_rate); - } - -@@ -533,6 +564,19 @@ - - - //================================================================================================== -+// Set panning on the channel designated by the register number. -+// This is extended functionality. -+//================================================================================================== -+void Opal::Pan(uint16_t reg_num, uint8_t pan) -+{ -+ uint8_t high = (reg_num >> 8) & 1; -+ uint8_t regm = reg_num & 0xff; -+ Chan[9 * high + (regm & 0x0f)].SetPan(pan); -+} -+ -+ -+ -+//================================================================================================== - // Generate sample. Every time you call this you will get two signed 16-bit samples (one for each - // stereo channel) which will sound correct when played back at the sample rate given when the - // class was constructed. -@@ -727,6 +771,9 @@ - - left = LeftEnable ? out : 0; - right = RightEnable ? out : 0; -+ -+ left = left * LeftPan / 65536; -+ right = right * RightPan / 65536; - } - - -@@ -793,6 +840,18 @@ - } - - -+ -+//================================================================================================== -+// Pan the channel to the position given. -+//================================================================================================== -+void Opal::Channel::SetPan(uint8_t pan) -+{ -+ pan &= 127; -+ LeftPan = PanLawTable[pan]; -+ RightPan = PanLawTable[127 - pan]; -+} -+ -+ - - //================================================================================================== - // Set the channel feedback amount. diff --git a/src/chips/opal/opal.c b/src/chips/opal/opal.c new file mode 100644 index 0000000..0cb2c6a --- /dev/null +++ b/src/chips/opal/opal.c @@ -0,0 +1,1427 @@ +/* + + The Opal OPL3 emulator. + + Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes. + + Missing features compared to a real OPL3: + + - Timers/interrupts + - OPL3 enable bit (it defaults to always on) + - CSW mode + - Test register + - Percussion mode + +*/ + +#include +#include "opal.h" + +/* Various constants */ +typedef enum OpalEnumPriv_t +{ + OpalEnvOff = -1, + OpalEnvAtt, + OpalEnvDec, + OpalEnvSus, + OpalEnvRel +} OpalEnumPriv; + +/*--------------------------------------------------------------------------------------------------*/ +static const uint16_t RateTables[4][8] = +{ + { 1, 0, 1, 0, 1, 0, 1, 0 }, + { 1, 0, 1, 0, 0, 0, 1, 0 }, + { 1, 0, 0, 0, 1, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 0, 0 }, +}; + +/*--------------------------------------------------------------------------------------------------*/ +static const uint16_t ExpTable[0x100] = +{ + 1018, 1013, 1007, 1002, 996, 991, 986, 980, 975, 969, 964, 959, 953, 948, 942, 937, + 932, 927, 921, 916, 911, 906, 900, 895, 890, 885, 880, 874, 869, 864, 859, 854, + 849, 844, 839, 834, 829, 824, 819, 814, 809, 804, 799, 794, 789, 784, 779, 774, + 770, 765, 760, 755, 750, 745, 741, 736, 731, 726, 722, 717, 712, 708, 703, 698, + 693, 689, 684, 680, 675, 670, 666, 661, 657, 652, 648, 643, 639, 634, 630, 625, + 621, 616, 612, 607, 603, 599, 594, 590, 585, 581, 577, 572, 568, 564, 560, 555, + 551, 547, 542, 538, 534, 530, 526, 521, 517, 513, 509, 505, 501, 496, 492, 488, + 484, 480, 476, 472, 468, 464, 460, 456, 452, 448, 444, 440, 436, 432, 428, 424, + 420, 416, 412, 409, 405, 401, 397, 393, 389, 385, 382, 378, 374, 370, 367, 363, + 359, 355, 352, 348, 344, 340, 337, 333, 329, 326, 322, 318, 315, 311, 308, 304, + 300, 297, 293, 290, 286, 283, 279, 276, 272, 268, 265, 262, 258, 255, 251, 248, + 244, 241, 237, 234, 231, 227, 224, 220, 217, 214, 210, 207, 204, 200, 197, 194, + 190, 187, 184, 181, 177, 174, 171, 168, 164, 161, 158, 155, 152, 148, 145, 142, + 139, 136, 133, 130, 126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, + 90, 87, 84, 81, 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, + 42, 40, 37, 34, 31, 28, 25, 22, 20, 17, 14, 11, 8, 6, 3, 0, +}; + +/*--------------------------------------------------------------------------------------------------*/ +static const uint16_t LogSinTable[0x100] = +{ + 2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, + 846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, + 598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, + 453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, + 352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, + 276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, + 215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, + 167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, + 127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, + 94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, + 67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, + 46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, + 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, + 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/*--------------------------------------------------------------------------------------------------*/ +static const uint16_t PanLawTable[128] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + + +/*================================================================================================== + * OpalOperator calls + *=================================================================================================*/ +static void OpalOperator_Init(OpalOperator *op); + +static void OpalOperator_SetMaster(OpalOperator *self, Opal* opal); +static void OpalOperator_SetChannel(OpalOperator *self, OpalChannel* chan); + +static int16_t OpalOperator_Output(OpalOperator *self, uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod /*= 0*/, int16_t fbshift /* = 0*/); + +static void OpalOperator_SetKeyOn(OpalOperator *self, opal_bool on); +static void OpalOperator_SetTremoloEnable(OpalOperator *self, opal_bool on); +static void OpalOperator_SetVibratoEnable(OpalOperator *self, opal_bool on); +static void OpalOperator_SetSustainMode(OpalOperator *self, opal_bool on); +static void OpalOperator_SetEnvelopeScaling(OpalOperator *self, opal_bool on); +static void OpalOperator_SetFrequencyMultiplier(OpalOperator *self, uint16_t scale); +static void OpalOperator_SetKeyScale(OpalOperator *self, uint16_t scale); +static void OpalOperator_SetOutputLevel(OpalOperator *self, uint16_t level); +static void OpalOperator_SetAttackRate(OpalOperator *self, uint16_t rate); +static void OpalOperator_SetDecayRate(OpalOperator *self, uint16_t rate); +static void OpalOperator_SetSustainLevel(OpalOperator *self, uint16_t level); +static void OpalOperator_SetReleaseRate(OpalOperator *self, uint16_t rate); +static void OpalOperator_SetWaveform(OpalOperator *self, uint16_t wave); + +static void OpalOperator_ComputeRates(OpalOperator *self); +static void OpalOperator_ComputeKeyScaleLevel(OpalOperator *self); + + +/*================================================================================================== + * OpalChannel calls + *=================================================================================================*/ +static void OpalChannel_Init(OpalChannel *self); + +/*public:*/ +static void OpalChannel_SetMaster(OpalChannel *self, Opal* opal); +static void OpalChannel_SetOperators(OpalChannel *self, OpalOperator* a, OpalOperator* b, OpalOperator* c, OpalOperator* d); + +static void OpalChannel_Output(OpalChannel *self, int16_t *left, int16_t *right); +static void OpalChannel_SetEnable(OpalChannel *self, opal_bool on); +static void OpalChannel_SetChannelPair(OpalChannel *self, OpalChannel* pair); + +static void OpalChannel_SetFrequencyLow(OpalChannel *self, uint16_t freq); +static void OpalChannel_SetFrequencyHigh(OpalChannel *self, uint16_t freq); +static void OpalChannel_SetKeyOn(OpalChannel *self, opal_bool on); +static void OpalChannel_SetOctave(OpalChannel *self, uint16_t oct); +static void OpalChannel_SetLeftEnable(OpalChannel *self, opal_bool on); +static void OpalChannel_SetRightEnable(OpalChannel *self, opal_bool on); +static void OpalChannel_SetPan(OpalChannel *self, uint8_t pan); +static void OpalChannel_SetFeedback(OpalChannel *self, uint16_t val); +static void OpalChannel_SetModulationType(OpalChannel *self, uint16_t type); + +static uint16_t OpalChannel_GetFreq(OpalChannel *self); +static uint16_t OpalChannel_GetOctave(OpalChannel *self); +static uint16_t OpalChannel_GetKeyScaleNumber(OpalChannel *self); +static uint16_t OpalChannel_GetModulationType(OpalChannel *self); +static OpalChannel* OpalChannel_GetChannelPair(OpalChannel *self); /* libADLMIDI */ + +static void OpalChannel_ComputeKeyScaleNumber(OpalChannel *self); + +/*protected:*/ +static void OpalChannel_ComputePhaseStep(OpalChannel *self); + +/*================================================================================================== + * Opal private calls + *=================================================================================================*/ +static void Opal_Output(Opal *self, int16_t *left, int16_t *right); + + + + +/*================================================================================================== + * Implementations * + *=================================================================================================*/ + + +static const int chan_ops[] = +{ + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32, +}; + +/*================================================================================================== + * Initialise the emulation. + *=================================================================================================*/ +void Opal_Init(Opal *self, int sample_rate) +{ + int i, op; + OpalChannel* chan; + + self->Clock = 0; + self->TremoloClock = 0; + self->TremoloLevel = 0; + self->VibratoTick = 0; + self->VibratoClock = 0; + self->NoteSel = OPAL_FALSE; + self->TremoloDepth = OPAL_FALSE; + self->VibratoDepth = OPAL_FALSE; + + for(i = 0; i < OpalNumOperators; i++) + OpalOperator_Init(&self->Op[i]); + + for(i = 0; i < OpalNumChannels; i++) + OpalChannel_Init(&self->Chan[i]); + + /* + // // Build the exponentiation table (reversed from the official OPL3 ROM) + // for (int i = 0; i < 0x100; i++) + // ExpTable[i] = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; + // + // // Build the log-sin table + // for (int i = 0; i < 0x100; i++) + // LogSinTable[i] = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; + */ + + /* Let sub-objects know where to find us */ + for(i = 0; i < OpalNumOperators; i++) + OpalOperator_SetMaster(&self->Op[i], self); + + for(i = 0; i < OpalNumChannels; i++) + OpalChannel_SetMaster(&self->Chan[i], self); + + /* Add the operators to the channels. Note, some channels can't use all the operators */ + /* FIXME: put this into a separate routine */ + for(i = 0; i < OpalNumChannels; i++) + { + chan = &self->Chan[i]; + op = chan_ops[i]; + + if(i < 3 || (i >= 9 && i < 12)) + OpalChannel_SetOperators(chan, &self->Op[op], &self->Op[op + 3], &self->Op[op + 6], &self->Op[op + 9]); + else + OpalChannel_SetOperators(chan, &self->Op[op], &self->Op[op + 3], 0, 0); + } + + /* Initialise the operator rate data. We can't do this in the Operator constructor as it + * relies on referencing the master and channel objects */ + for(i = 0; i < OpalNumOperators; i++) + OpalOperator_ComputeRates(&self->Op[i]); + + /* Initialise channel panning at center. */ + for(i = 0; i < OpalNumChannels; i++) + { + OpalChannel_SetPan(&self->Chan[i], 64); + OpalChannel_SetLeftEnable(&self->Chan[i], OPAL_TRUE); + OpalChannel_SetRightEnable(&self->Chan[i], OPAL_TRUE); + } + + Opal_SetSampleRate(self, sample_rate); +} + + + +/*================================================================================================== + * Change the sample rate. + *=================================================================================================*/ +void Opal_SetSampleRate(Opal *self, int sample_rate) +{ + /* Sanity */ + if(sample_rate == 0) + sample_rate = OpalOPL3SampleRate; + + self->SampleRate = sample_rate; + self->SampleAccum = 0; + self->LastOutput[0] = self->LastOutput[1] = 0; + self->CurrOutput[0] = self->CurrOutput[1] = 0; +} + + +static const int op_lookup[] = +{ + /* 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F */ + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + /* 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F */ + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +/*================================================================================================== + * Write a value to an OPL3 register. + *=================================================================================================*/ +void Opal_Port(Opal *self, uint16_t reg_num, uint8_t val) +{ + int i; + unsigned ui; + uint8_t mask; + unsigned numchans; + uint16_t chan; + int chan_num; + int op_num; + OpalChannel *primary, *secondary, *cchan; + OpalChannel* chans[2]; + uint16_t type = reg_num & 0xE0; + OpalOperator *op; + + /* Is it BD, the one-off register stuck in the middle of the register array? */ + if(reg_num == 0xBD) + { + self->TremoloDepth = (val & 0x80) != 0; + self->VibratoDepth = (val & 0x40) != 0; + return; + } + + /* Global registers */ + if(type == 0x00) + { + + /* 4-OP enables*/ + if(reg_num == 0x104) + { + + /* Enable/disable channels based on which 4-op enables */ + mask = 1; + + for(i = 0; i < 6; i++, mask <<= 1) + { + /* The 4-op channels are 0, 1, 2, 9, 10, 11 */ + chan = i < 3 ? i : i + 6; + primary = &self->Chan[chan]; + secondary = &self->Chan[chan + 3]; + + if(val & mask) + { + /* Let primary channel know it's controlling the secondary channel */ + OpalChannel_SetChannelPair(primary, secondary); + + /* Turn off the second channel in the pair */ + OpalChannel_SetEnable(secondary, OPAL_FALSE); + + } + else + { + /* Let primary channel know it's no longer controlling the secondary channel */ + OpalChannel_SetChannelPair(primary, 0); + + /* Turn on the second channel in the pair */ + OpalChannel_SetEnable(secondary, OPAL_TRUE); + } + } + + /* CSW / Note-sel */ + } + else if(reg_num == 0x08) + { + self->NoteSel = (val & 0x40) != 0; + + /* Get the channels to recompute the Key Scale No. as this varies based on NoteSel */ + for(i = 0; i < OpalNumChannels; i++) + OpalChannel_ComputeKeyScaleNumber(&self->Chan[i]); + } + + /* Channel registers */ + } + else if(type >= 0xA0 && type <= 0xC0) + { + /* Convert to channel number */ + chan_num = reg_num & 15; + + /* Valid channel? */ + if(chan_num >= 9) + return; + + /* Is it the other bank of channels? */ + if(reg_num & 0x100) + chan_num += 9; + + cchan = &self->Chan[chan_num]; + + /* libADLMIDI: registers Ax and Cx affect both channels */ + chans[0] = cchan; + chans[1] = OpalChannel_GetChannelPair(cchan); + numchans = chans[1] ? 2 : 1; + + /* Do specific registers */ + switch(reg_num & 0xF0) + { + + /* Frequency low */ + case 0xA0: + { + for(ui = 0; ui < numchans; ++ui) /* libADLMIDI */ + { + OpalChannel_SetFrequencyLow(chans[ui], val); + } + + break; + } + + /* Key-on / Octave / Frequency High */ + case 0xB0: + { + for(ui = 0; ui < numchans; ++ui) /* libADLMIDI */ + { + OpalChannel_SetKeyOn(chans[ui],(val & 0x20) != 0); + OpalChannel_SetOctave(chans[ui], val >> 2 & 7); + OpalChannel_SetFrequencyHigh(chans[ui], val & 3); + } + + break; + } + + /* Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type */ + case 0xC0: + { + OpalChannel_SetRightEnable(cchan, (val & 0x20) != 0); + OpalChannel_SetLeftEnable(cchan, (val & 0x10) != 0); + OpalChannel_SetFeedback(cchan, val >> 1 & 7); + OpalChannel_SetModulationType(cchan, val & 1); + break; + } + } + + /* Operator registers */ + } + else if((type >= 0x20 && type <= 0x80) || type == 0xE0) + { + /* Convert to operator number */ + op_num = op_lookup[reg_num & 0x1F]; + + /* Valid register? */ + if(op_num < 0) + return; + + /* Is it the other bank of operators? */ + if(reg_num & 0x100) + op_num += 18; + + op = &self->Op[op_num]; + + /* Do specific registers */ + switch(type) + { + + /* Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier */ + case 0x20: + { + OpalOperator_SetTremoloEnable(op, (val & 0x80) != 0); + OpalOperator_SetVibratoEnable(op, (val & 0x40) != 0); + OpalOperator_SetSustainMode(op, (val & 0x20) != 0); + OpalOperator_SetEnvelopeScaling(op, (val & 0x10) != 0); + OpalOperator_SetFrequencyMultiplier(op, val & 15); + break; + } + + /* Key Scale / Output Level */ + case 0x40: + { + OpalOperator_SetKeyScale(op, val >> 6); + OpalOperator_SetOutputLevel(op, val & 0x3F); + break; + } + + /* Attack Rate / Decay Rate */ + case 0x60: + { + OpalOperator_SetAttackRate(op, val >> 4); + OpalOperator_SetDecayRate(op, val & 15); + break; + } + + /* Sustain Level / Release Rate */ + case 0x80: + { + OpalOperator_SetSustainLevel(op, val >> 4); + OpalOperator_SetReleaseRate(op, val & 15); + break; + } + + /* Waveform */ + case 0xE0: + { + OpalOperator_SetWaveform(op, val & 7); + break; + } + } + } +} + + + +/*================================================================================================== + * Set panning on the channel designated by the register number. + * This is extended functionality. + *=================================================================================================*/ +void Opal_Pan(Opal *self, uint16_t reg_num, uint8_t pan) +{ + uint8_t high = (reg_num >> 8) & 1; + uint8_t regm = reg_num & 0xff; + OpalChannel_SetPan(&self->Chan[9 * high + (regm & 0x0f)], pan); +} + + + +/*================================================================================================== + * Generate sample. Every time you call this you will get two signed 16-bit samples (one for each + * stereo channel) which will sound correct when played back at the sample rate given when the + * class was constructed. + *=================================================================================================*/ +void Opal_Sample(Opal *self, int16_t* left, int16_t* right) +{ + int32_t omblend; + + /* If the destination sample rate is higher than the OPL3 sample rate, we need to skip ahead */ + while(self->SampleAccum >= self->SampleRate) + { + self->LastOutput[0] = self->CurrOutput[0]; + self->LastOutput[1] = self->CurrOutput[1]; + + Opal_Output(self, &self->CurrOutput[0], &self->CurrOutput[1]); + + self->SampleAccum -= self->SampleRate; + } + + /* Mix with the partial accumulation */ + omblend = self->SampleRate - self->SampleAccum; + *left = (self->LastOutput[0] * omblend + self->CurrOutput[0] * self->SampleAccum) / self->SampleRate; + *right = (self->LastOutput[1] * omblend + self->CurrOutput[1] * self->SampleAccum) / self->SampleRate; + + self->SampleAccum += OpalOPL3SampleRate; +} + + + +/*================================================================================================== + * Produce final output from the chip. This is at the OPL3 sample-rate. + *=================================================================================================*/ +static void Opal_Output(Opal *self, int16_t *left, int16_t *right) +{ + int i; + int16_t chanleft, chanright; + int32_t leftmix = 0, rightmix = 0; + + /* Sum the output of each channel */ + for(i = 0; i < OpalNumChannels; i++) + { + OpalChannel_Output(&self->Chan[i], &chanleft, &chanright); + leftmix += chanleft; + rightmix += chanright; + } + + /* Clamp */ + if(leftmix < -0x8000) + *left = -0x8000; + else if(leftmix > 0x7FFF) + *left = 0x7FFF; + else + *left = leftmix; + + if(rightmix < -0x8000) + *right = -0x8000; + else if(rightmix > 0x7FFF) + *right = 0x7FFF; + else + *right = rightmix; + + self->Clock++; + + /* Tremolo. According to this post, the OPL3 tremolo is a 13,440 sample length triangle wave + * with a peak at 26 and a trough at 0 and is simply added to the logarithmic level accumulator + * http://forums.submarine.org.uk/phpBB/viewtopic.php?f=9&t=1171 */ + self->TremoloClock = (self->TremoloClock + 1) % 13440; + self->TremoloLevel = ((self->TremoloClock < 13440 / 2) ? self->TremoloClock : 13440 - self->TremoloClock) / 256; + + if(!self->TremoloDepth) + self->TremoloLevel >>= 2; + + /* Vibrato. This appears to be a 8 sample long triangle wave with a magnitude of the three + * high bits of the channel frequency, positive and negative, divided by two if the vibrato + * depth is zero. It is only cycled every 1,024 samples. */ + self->VibratoTick++; + + if(self->VibratoTick >= 1024) + { + self->VibratoTick = 0; + self->VibratoClock = (self->VibratoClock + 1) & 7; + } +} + + + +/*================================================================================================== + * Channel constructor. + *=================================================================================================*/ +static void OpalChannel_Init(OpalChannel *self) +{ + self->Master = 0; + self->Freq = 0; + self->Octave = 0; + self->PhaseStep = 0; + self->KeyScaleNumber = 0; + self->FeedbackShift = 0; + self->ModulationType = 0; + self->ChannelPair = 0; + self->Enable = OPAL_TRUE; + self->LeftPan = 46340; + self->RightPan = 46340; +} + +static void OpalChannel_SetMaster(OpalChannel *self, Opal* opal) +{ + self->Master = opal; +} + + +static void OpalChannel_SetOperators(OpalChannel *self, OpalOperator* a, OpalOperator* b, OpalOperator* c, OpalOperator* d) +{ + self->Op[0] = a; + self->Op[1] = b; + self->Op[2] = c; + self->Op[3] = d; + + if(a) OpalOperator_SetChannel(a, self); + if(b) OpalOperator_SetChannel(b, self); + if(c) OpalOperator_SetChannel(c, self); + if(d) OpalOperator_SetChannel(d, self); +} + + +/*================================================================================================== + * Produce output from channel. + *==================================================================================================*/ +static void OpalChannel_Output(OpalChannel *self, int16_t *left, int16_t *right) +{ + int16_t vibrato; + uint16_t clk; + /* Combine individual operator outputs */ + int16_t out, acc; + + /* Has the channel been disabled? This is usually a result of the 4-op enables being used to + * disable the secondary channel in each 4-op pair */ + if(!self->Enable) + { + *left = *right = 0; + return; + } + + vibrato = (self->Freq >> 7) & 7; + + if(!self->Master->VibratoDepth) + vibrato >>= 1; + + /* 0 3 7 3 0 -3 -7 -3 */ + clk = self->Master->VibratoClock; + + if(!(clk & 3)) + vibrato = 0; /* Position 0 and 4 is zero */ + else + { + if(clk & 1) + vibrato >>= 1; /* Odd positions are half the magnitude */ + + if(clk & 4) + vibrato = -vibrato; /* The second half positions are negative */ + } + + vibrato <<= self->Octave; + + /* Combine individual operator outputs */ + + /* Running in 4-op mode? */ + if(self->ChannelPair) + { + + /* Get the secondary channel's modulation type. This is the only thing from the secondary + * channel that is used */ + if(OpalChannel_GetModulationType(self->ChannelPair) == 0) + { + if(self->ModulationType == 0) + { + /* feedback -> modulator -> modulator -> modulator -> carrier */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + out = OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, out, 0); + out = OpalOperator_Output(self->Op[2], self->KeyScaleNumber, self->PhaseStep, vibrato, out, 0); + out = OpalOperator_Output(self->Op[3], self->KeyScaleNumber, self->PhaseStep, vibrato, out, 0); + + } + else + { + /* (feedback -> carrier) + (modulator -> modulator -> carrier) */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + acc = OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, 0); + acc = OpalOperator_Output(self->Op[2], self->KeyScaleNumber, self->PhaseStep, vibrato, acc, 0); + out += OpalOperator_Output(self->Op[3], self->KeyScaleNumber, self->PhaseStep, vibrato, acc, 0); + } + + } + else + { + + if(self->ModulationType == 0) + { + /* (feedback -> modulator -> carrier) + (modulator -> carrier) */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + out = OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, out, 0); + acc = OpalOperator_Output(self->Op[2], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, 0); + out += OpalOperator_Output(self->Op[3], self->KeyScaleNumber, self->PhaseStep, vibrato, acc, 0); + + } + else + { + /* (feedback -> carrier) + (modulator -> carrier) + carrier */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + acc = OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, 0); + out += OpalOperator_Output(self->Op[2], self->KeyScaleNumber, self->PhaseStep, vibrato, acc, 0); + out += OpalOperator_Output(self->Op[3], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, 0); + } + } + + } + else + { + /* Standard 2-op mode */ + if(self->ModulationType == 0) + { + /* Frequency modulation (well, phase modulation technically) */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + out = OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, out, 0); + + } + else + { + /* Additive */ + out = OpalOperator_Output(self->Op[0], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, self->FeedbackShift); + out += OpalOperator_Output(self->Op[1], self->KeyScaleNumber, self->PhaseStep, vibrato, 0, 0); + } + } + + *left = self->LeftEnable ? out : 0; + *right = self->RightEnable ? out : 0; + + *left = (*left * self->LeftPan) / 65536; + *right = (*right * self->RightPan) / 65536; +} + +static void OpalChannel_SetEnable(OpalChannel *self, opal_bool on) +{ + self->Enable = on; +} + +static void OpalChannel_SetChannelPair(OpalChannel *self, OpalChannel* pair) +{ + self->ChannelPair = pair; +} + + + +/*================================================================================================== + * Set phase step for operators using this channel. + *=================================================================================================*/ +static void OpalChannel_SetFrequencyLow(OpalChannel *self, uint16_t freq) +{ + self->Freq = (self->Freq & 0x300) | (freq & 0xFF); + OpalChannel_ComputePhaseStep(self); +} + +/*-------------------------------------------------------------------------------------------------*/ +static void OpalChannel_SetFrequencyHigh(OpalChannel *self, uint16_t freq) +{ + self->Freq = (self->Freq & 0xFF) | ((freq & 3) << 8); + OpalChannel_ComputePhaseStep(self); + + /* Only the high bits of Freq affect the Key Scale No. */ + OpalChannel_ComputeKeyScaleNumber(self); +} + + + +/*================================================================================================== + * Set the octave of the channel (0 to 7). + *=================================================================================================*/ +static void OpalChannel_SetOctave(OpalChannel *self, uint16_t oct) +{ + self->Octave = oct & 7; + OpalChannel_ComputePhaseStep(self); + OpalChannel_ComputeKeyScaleNumber(self); +} + + + +/*================================================================================================== + * Keys the channel on/off. + *=================================================================================================*/ +static void OpalChannel_SetKeyOn(OpalChannel *self, opal_bool on) +{ + OpalOperator_SetKeyOn(self->Op[0], on); + OpalOperator_SetKeyOn(self->Op[1], on); +} + + + +/*================================================================================================== + * Enable left stereo channel. + *=================================================================================================*/ +static void OpalChannel_SetLeftEnable(OpalChannel *self, opal_bool on) +{ + self->LeftEnable = on; +} + + + +/*================================================================================================== + * Enable right stereo channel. + *=================================================================================================*/ +static void OpalChannel_SetRightEnable(OpalChannel *self, opal_bool on) +{ + self->RightEnable = on; +} + + + +/*================================================================================================== + * Pan the channel to the position given. + *=================================================================================================*/ +static void OpalChannel_SetPan(OpalChannel *self, uint8_t pan) +{ + pan &= 127; + self->LeftPan = PanLawTable[pan]; + self->RightPan = PanLawTable[127 - pan]; +} + + + +/*================================================================================================== + * Set the channel feedback amount. + *=================================================================================================*/ +static void OpalChannel_SetFeedback(OpalChannel *self, uint16_t val) +{ + self->FeedbackShift = val ? 9 - val : 0; +} + + + +/*================================================================================================== + * Set frequency modulation/additive modulation + *=================================================================================================*/ +static void OpalChannel_SetModulationType(OpalChannel *self, uint16_t type) +{ + self->ModulationType = type; +} + +static uint16_t OpalChannel_GetFreq(OpalChannel *self) +{ + return self->Freq; +} + +static uint16_t OpalChannel_GetOctave(OpalChannel *self) +{ + return self->Octave; +} + +static uint16_t OpalChannel_GetKeyScaleNumber(OpalChannel *self) +{ + return self->KeyScaleNumber; +} + +static uint16_t OpalChannel_GetModulationType(OpalChannel *self) +{ + return self->ModulationType; +} + +static OpalChannel* OpalChannel_GetChannelPair(OpalChannel *self) /* libADLMIDI */ +{ + return self->ChannelPair; +} + +/*================================================================================================== + Compute the stepping factor for the operator waveform phase based on the frequency and octave + values of the channel. + ==================================================================================================*/ +static void OpalChannel_ComputePhaseStep(OpalChannel *self) +{ + self->PhaseStep = (uint32_t)(self->Freq) << self->Octave; +} + + + +/*================================================================================================== + Compute the key scale number and key scale levels. + + From the Yamaha data sheet this is the block/octave number as bits 3-1, with bit 0 coming from + the MSB of the frequency if NoteSel is 1, and the 2nd MSB if NoteSel is 0. + ==================================================================================================*/ +static void OpalChannel_ComputeKeyScaleNumber(OpalChannel *self) +{ + int i; + uint16_t lsb = self->Master->NoteSel ? self->Freq >> 9 : (self->Freq >> 8) & 1; + self->KeyScaleNumber = self->Octave << 1 | lsb; + + /* Get the channel operators to recompute their rates as they're dependent on this number. + * They also need to recompute their key scale level */ + for(i = 0; i < 4; i++) + { + if(!self->Op[i]) + continue; + + OpalOperator_ComputeRates(self->Op[i]); + OpalOperator_ComputeKeyScaleLevel(self->Op[i]); + } +} + + +/*================================================================================================== + * Operator constructor. + *=================================================================================================*/ +static void OpalOperator_Init(OpalOperator *op) +{ + op->Master = 0; + op->Chan = 0; + op->Phase = 0; + op->Waveform = 0; + op->FreqMultTimes2 = 1; + op->EnvelopeStage = OpalEnvOff; + op->EnvelopeLevel = 0x1FF; + op->AttackRate = 0; + op->DecayRate = 0; + op->SustainLevel = 0; + op->ReleaseRate = 0; + op->KeyScaleShift = 0; + op->KeyScaleLevel = 0; + op->Out[0] = op->Out[1] = 0; + op->KeyOn = OPAL_FALSE; + op->KeyScaleRate = OPAL_FALSE; + op->SustainMode = OPAL_FALSE; + op->TremoloEnable = OPAL_FALSE; + op->VibratoEnable = OPAL_FALSE; +} + +static void OpalOperator_SetMaster(OpalOperator *self, Opal* opal) +{ + self->Master = opal; +} + +static void OpalOperator_SetChannel(OpalOperator *self, OpalChannel* chan) +{ + self->Chan = chan; +} + + +/*================================================================================================== + * Produce output from operator. + *=================================================================================================*/ +static int16_t OpalOperator_Output(OpalOperator *self, uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod, int16_t fbshift) +{ + int16_t v; + uint16_t mix, phase, offset, logsin, add, level, negate; + + (void)keyscalenum; + + /* Advance wave phase */ + if(self->VibratoEnable) + phase_step += vibrato; + + self->Phase += (phase_step * self->FreqMultTimes2) / 2; + + level = (self->EnvelopeLevel + self->OutputLevel + self->KeyScaleLevel + (self->TremoloEnable ? self->Master->TremoloLevel : 0)) << 3; + + switch(self->EnvelopeStage) + { + + /* Attack stage */ + case OpalEnvAtt: + { + add = ((self->AttackAdd >> self->AttackTab[self->Master->Clock >> self->AttackShift & 7]) * ~self->EnvelopeLevel) >> 3; + + if(self->AttackRate == 0) + add = 0; + + if(self->AttackMask && (self->Master->Clock & self->AttackMask)) + add = 0; + + self->EnvelopeLevel += add; + + if(self->EnvelopeLevel <= 0) + { + self->EnvelopeLevel = 0; + self->EnvelopeStage = OpalEnvDec; + } + + break; + } + + /* Decay stage */ + case OpalEnvDec: + { + add = self->DecayAdd >> self->DecayTab[self->Master->Clock >> self->DecayShift & 7]; + + if(self->DecayRate == 0) + add = 0; + + if(self->DecayMask && (self->Master->Clock & self->DecayMask)) + add = 0; + + self->EnvelopeLevel += add; + + if(self->EnvelopeLevel >= self->SustainLevel) + { + self->EnvelopeLevel = self->SustainLevel; + self->EnvelopeStage = OpalEnvSus; + } + + break; + } + + /* Sustain stage */ + case OpalEnvSus: + { + if(self->SustainMode) + break; + + /* Note: fall-through! */ + + } /* fallthrough */ + + /* Release stage */ + case OpalEnvRel: + { + add = self->ReleaseAdd >> self->ReleaseTab[self->Master->Clock >> self->ReleaseShift & 7]; + + if(self->ReleaseRate == 0) + add = 0; + + if(self->ReleaseMask && (self->Master->Clock & self->ReleaseMask)) + add = 0; + + self->EnvelopeLevel += add; + + if(self->EnvelopeLevel >= 0x1FF) + { + self->EnvelopeLevel = 0x1FF; + self->EnvelopeStage = OpalEnvOff; + self->Out[0] = self->Out[1] = 0; + return 0; + } + + break; + } + + /* Envelope, and therefore the operator, is not running */ + default: + self->Out[0] = self->Out[1] = 0; + return 0; + } + + /* Feedback? In that case we modulate by a blend of the last two samples */ + if(fbshift) + mod += (self->Out[0] + self->Out[1]) >> fbshift; + + phase = (self->Phase >> 10) + mod; + offset = phase & 0xFF; + negate = OPAL_FALSE; + + switch(self->Waveform) + { + + /*------------------------------------ + * Standard sine wave + *------------------------------------*/ + case 0: + if(phase & 0x100) + offset ^= 0xFF; + + logsin = LogSinTable[offset]; + negate = (phase & 0x200) != 0; + break; + + /*------------------------------------ + * Half sine wave + *------------------------------------*/ + case 1: + if(phase & 0x200) + offset = 0; + else if(phase & 0x100) + offset ^= 0xFF; + + logsin = LogSinTable[offset]; + break; + + /*------------------------------------ + * Positive sine wave + *------------------------------------*/ + case 2: + if(phase & 0x100) + offset ^= 0xFF; + + logsin = LogSinTable[offset]; + break; + + /*------------------------------------ + * Quarter positive sine wave + *------------------------------------*/ + case 3: + if(phase & 0x100) + offset = 0; + + logsin = LogSinTable[offset]; + break; + + /*------------------------------------ + * Double-speed sine wave + *------------------------------------*/ + case 4: + if(phase & 0x200) + offset = 0; + + else + { + + if(phase & 0x80) + offset ^= 0xFF; + + offset = (offset + offset) & 0xFF; + negate = (phase & 0x100) != 0; + } + + logsin = LogSinTable[offset]; + break; + + /*------------------------------------ + * Double-speed positive sine wave + *------------------------------------*/ + case 5: + if(phase & 0x200) + offset = 0; + + else + { + + offset = (offset + offset) & 0xFF; + + if(phase & 0x80) + offset ^= 0xFF; + } + + logsin = LogSinTable[offset]; + break; + + /*------------------------------------ + * Square wave + *------------------------------------*/ + case 6: + logsin = 0; + negate = (phase & 0x200) != 0; + break; + + /*------------------------------------ + * Exponentiation wave + *------------------------------------*/ + default: + logsin = phase & 0x1FF; + + if(phase & 0x200) + { + logsin ^= 0x1FF; + negate = OPAL_TRUE; + } + + logsin <<= 3; + break; + } + + mix = logsin + level; + + if(mix > 0x1FFF) + mix = 0x1FFF; + + /* From the OPLx decapsulated docs: + * "When such a table is used for calculation of the exponential, the table is read at the + * position given by the 8 LSB's of the input. The value + 1024 (the hidden bit) is then the + * significand of the floating point output and the yet unused MSB's of the input are the + * exponent of the floating point output." */ + v = ExpTable[mix & 0xFF] + 1024; + v >>= mix >> 8; + v += v; + + if(negate) + v = ~v; + + /* Keep last two results for feedback calculation */ + self->Out[1] = self->Out[0]; + self->Out[0] = v; + + return v; +} + + + +/*================================================================================================== + * Trigger operator. + *=================================================================================================*/ +void OpalOperator_SetKeyOn(OpalOperator* self, opal_bool on) +{ + /* Already on/off? */ + if(self->KeyOn == on) + return; + + self->KeyOn = on; + + if(on) + { + /* The highest attack rate is instant; it bypasses the attack phase */ + if(self->AttackRate == 15) + { + self->EnvelopeStage = OpalEnvDec; + self->EnvelopeLevel = 0; + } + else + self->EnvelopeStage = OpalEnvAtt; + + self->Phase = 0; + + } + else + { + /* Stopping current sound? */ + if(self->EnvelopeStage != OpalEnvOff && self->EnvelopeStage != OpalEnvRel) + self->EnvelopeStage = OpalEnvRel; + } +} + + +/*================================================================================================== + * Enable amplitude vibrato. + *=================================================================================================*/ +static void OpalOperator_SetTremoloEnable(OpalOperator *self, opal_bool on) +{ + self->TremoloEnable = on; +} + + + +/*================================================================================================== + * Enable frequency vibrato. + *=================================================================================================*/ +static void OpalOperator_SetVibratoEnable(OpalOperator *self, opal_bool on) +{ + self->VibratoEnable = on; +} + + + +/*================================================================================================== + * Sets whether we release or sustain during the sustain phase of the envelope. 'true' is to + * sustain, otherwise release. + *=================================================================================================*/ +static void OpalOperator_SetSustainMode(OpalOperator *self, opal_bool on) +{ + self->SustainMode = on; +} + + + +/*================================================================================================== + * Key scale rate. Sets how much the Key Scaling Number affects the envelope rates. + *==================================================================================================*/ +static void OpalOperator_SetEnvelopeScaling(OpalOperator *self, opal_bool on) +{ + self->KeyScaleRate = on; + OpalOperator_ComputeRates(self); +} + + +/* Needs to be multiplied by two (and divided by two later when we use it) because + * the first entry is actually .5 */ +static const uint16_t mul_times_2[] = +{ + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30, +}; + +/*================================================================================================== + * Multiplies the phase frequency. + *==================================================================================================*/ +static void OpalOperator_SetFrequencyMultiplier(OpalOperator *self, uint16_t scale) +{ + self->FreqMultTimes2 = mul_times_2[scale & 15]; +} + + + +/*================================================================================================== + * Attenuates output level towards higher pitch. + *==================================================================================================*/ +static void OpalOperator_SetKeyScale(OpalOperator *self, uint16_t scale) +{ + /* libADLMIDI: KSL computation fix */ + const unsigned KeyScaleShiftTable[4] = {8, 1, 2, 0}; + self->KeyScaleShift = KeyScaleShiftTable[scale]; + + OpalOperator_ComputeKeyScaleLevel(self); +} + + + +/*================================================================================================== + * Sets the output level (volume) of the operator. + *=================================================================================================*/ +static void OpalOperator_SetOutputLevel(OpalOperator *self, uint16_t level) +{ + self->OutputLevel = level * 4; +} + + + +/*================================================================================================== + * Operator attack rate. + *=================================================================================================*/ +static void OpalOperator_SetAttackRate(OpalOperator *self, uint16_t rate) +{ + self->AttackRate = rate; + OpalOperator_ComputeRates(self); +} + + + +/*================================================================================================== + * Operator decay rate. + *=================================================================================================*/ +static void OpalOperator_SetDecayRate(OpalOperator *self, uint16_t rate) +{ + self->DecayRate = rate; + OpalOperator_ComputeRates(self); +} + + + +/*================================================================================================== + * Operator sustain level. + *=================================================================================================*/ +static void OpalOperator_SetSustainLevel(OpalOperator *self, uint16_t level) +{ + self->SustainLevel = level < 15 ? level : 31; + self->SustainLevel *= 16; +} + + + +/*================================================================================================== + * Operator release rate. + *=================================================================================================*/ +static void OpalOperator_SetReleaseRate(OpalOperator *self, uint16_t rate) +{ + self->ReleaseRate = rate; + OpalOperator_ComputeRates(self); +} + + + +/*================================================================================================== + * Assign the waveform this operator will use. + *=================================================================================================*/ +static void OpalOperator_SetWaveform(OpalOperator *self, uint16_t wave) +{ + self->Waveform = wave & 7; +} + + + +/*================================================================================================== + * Compute actual rate from register rate. From the Yamaha data sheet: + * + * Actual rate = Rate value * 4 + Rof, if Rate value = 0, actual rate = 0 + * + * Rof is set as follows depending on the KSR setting: + * + * Key scale 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * KSR = 0 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 + * KSR = 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * + * Note: zero rates are infinite, and are treated separately elsewhere + *=================================================================================================*/ +static void OpalOperator_ComputeRates(OpalOperator *self) +{ + int combined_rate = self->AttackRate * 4 + (OpalChannel_GetKeyScaleNumber(self->Chan) >> (self->KeyScaleRate ? 0 : 2)); + int rate_high = combined_rate >> 2; + int rate_low = combined_rate & 3; + + self->AttackShift = rate_high < 12 ? 12 - rate_high : 0; + self->AttackMask = (1 << self->AttackShift) - 1; + self->AttackAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + self->AttackTab = RateTables[rate_low]; + + /* Attack rate of 15 is always instant */ + if(self->AttackRate == 15) + self->AttackAdd = 0xFFF; + + combined_rate = self->DecayRate * 4 + (OpalChannel_GetKeyScaleNumber(self->Chan) >> (self->KeyScaleRate ? 0 : 2)); + rate_high = combined_rate >> 2; + rate_low = combined_rate & 3; + + self->DecayShift = rate_high < 12 ? 12 - rate_high : 0; + self->DecayMask = (1 << self->DecayShift) - 1; + self->DecayAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + self->DecayTab = RateTables[rate_low]; + + combined_rate = self->ReleaseRate * 4 + (OpalChannel_GetKeyScaleNumber(self->Chan) >> (self->KeyScaleRate ? 0 : 2)); + rate_high = combined_rate >> 2; + rate_low = combined_rate & 3; + + self->ReleaseShift = rate_high < 12 ? 12 - rate_high : 0; + self->ReleaseMask = (1 << self->ReleaseShift) - 1; + self->ReleaseAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); + self->ReleaseTab = RateTables[rate_low]; +} + + +static const uint16_t levtab[] = +{ + 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, 8, 12, 16, 20, 24, 28, 32, + 0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64, + 0, 0, 0, 20, 32, 44, 52, 60, 64, 72, 76, 80, 84, 88, 92, 96, + 0, 0, 32, 52, 64, 76, 84, 92, 96, 104, 108, 112, 116, 120, 124, 128, + 0, 32, 64, 84, 96, 108, 116, 124, 128, 136, 140, 144, 148, 152, 156, 160, + 0, 64, 96, 116, 128, 140, 148, 156, 160, 168, 172, 176, 180, 184, 188, 192, + 0, 96, 128, 148, 160, 172, 180, 188, 192, 200, 204, 208, 212, 216, 220, 224, +}; + +/*=================================================================================================* + * Compute the operator's key scale level. This changes based on the channel frequency/octave and * + * operator key scale value. * + *=================================================================================================*/ +static void OpalOperator_ComputeKeyScaleLevel(OpalOperator *self) +{ + /* This uses a combined value of the top four bits of frequency with the octave/block */ + uint16_t i = (OpalChannel_GetOctave(self->Chan) << 4) | (OpalChannel_GetFreq(self->Chan) >> 6); + self->KeyScaleLevel = levtab[i] >> self->KeyScaleShift; +} diff --git a/src/chips/opal/opal.h b/src/chips/opal/opal.h new file mode 100644 index 0000000..0772d3e --- /dev/null +++ b/src/chips/opal/opal.h @@ -0,0 +1,165 @@ +/* + + The Opal OPL3 emulator. + + Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes. + + Missing features compared to a real OPL3: + + - Timers/interrupts + - OPL3 enable bit (it defaults to always on) + - CSW mode + - Test register + - Percussion mode + +*/ + +#ifndef OPAL_HHHH +#define OPAL_HHHH + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OPAL_TRUE 1 +#define OPAL_FALSE 0 +typedef int opal_bool; + +typedef struct OpalChannel_t OpalChannel; +typedef struct OpalOperator_t OpalOperator; +typedef struct Opal_t Opal; + +/* Various constants */ +typedef enum OpalEnum_t +{ + OpalOPL3SampleRate = 49716, + OpalNumChannels = 18, + OpalNumOperators = 36, +} OpalEnum; + +/* A single FM operator */ +struct OpalOperator_t +{ + Opal* Master; /* Master object */ + OpalChannel* Chan; /* Owning channel */ + uint32_t Phase; /* The current offset in the selected waveform */ + uint16_t Waveform; /* The waveform id this operator is using */ + uint16_t FreqMultTimes2; /* Frequency multiplier * 2 */ + int EnvelopeStage; /* Which stage the envelope is at (see Env* enums above) */ + int16_t EnvelopeLevel; /* 0 - $1FF, 0 being the loudest */ + uint16_t OutputLevel; /* 0 - $FF */ + uint16_t AttackRate; + uint16_t DecayRate; + uint16_t SustainLevel; + uint16_t ReleaseRate; + uint16_t AttackShift; + uint16_t AttackMask; + uint16_t AttackAdd; + const uint16_t* AttackTab; + uint16_t DecayShift; + uint16_t DecayMask; + uint16_t DecayAdd; + const uint16_t* DecayTab; + uint16_t ReleaseShift; + uint16_t ReleaseMask; + uint16_t ReleaseAdd; + const uint16_t* ReleaseTab; + uint16_t KeyScaleShift; + uint16_t KeyScaleLevel; + int16_t Out[2]; + opal_bool KeyOn; + opal_bool KeyScaleRate; /* Affects envelope rate scaling */ + opal_bool SustainMode; /* Whether to sustain during the sustain phase, or release instead */ + opal_bool TremoloEnable; + opal_bool VibratoEnable; +}; + +/* A single channel, which can contain two or more operators */ +struct OpalChannel_t +{ + OpalOperator* Op[4]; + + Opal* Master; /* Master object */ + uint16_t Freq; /* Frequency; actually it's a phase stepping value */ + uint16_t Octave; /* Also known as "block" in Yamaha parlance */ + uint32_t PhaseStep; + uint16_t KeyScaleNumber; + uint16_t FeedbackShift; + uint16_t ModulationType; + OpalChannel* ChannelPair; + opal_bool Enable; + opal_bool LeftEnable, RightEnable; + uint16_t LeftPan, RightPan; +}; + + +/*================================================================================================== + Opal instance. + ==================================================================================================*/ +struct Opal_t +{ + int32_t SampleRate; + int32_t SampleAccum; + int16_t LastOutput[2], CurrOutput[2]; + OpalChannel Chan[OpalNumChannels]; + OpalOperator Op[OpalNumOperators]; + uint16_t Clock; + uint16_t TremoloClock; + uint16_t TremoloLevel; + uint16_t VibratoTick; + uint16_t VibratoClock; + opal_bool NoteSel; + opal_bool TremoloDepth; + opal_bool VibratoDepth; +}; + + +/*================================================================================================== + Public API. + ==================================================================================================*/ +/*! + * \brief Initialize Opal with a given output sample rate + * \param self Pointer to the Opal instance + * \param sample_rate Desired output sample rate + */ +extern void Opal_Init(Opal *self, int sample_rate); + +/*! + * \brief Change the output sample rate of existing initialized instance + * \param self Pointer to the Opal instance + * \param sample_rate Desired output sample rate + */ +extern void Opal_SetSampleRate(Opal *self, int sample_rate); + +/*! + * \brief Send the register data to the Opal + * \param self Pointer to the Opal instance + * \param reg_num OPL3 Register address + * \param val Value to write + */ +extern void Opal_Port(Opal *self, uint16_t reg_num, uint8_t val); + +/*! + * \brief Panorama level per channel + * \param self Pointer to the Opal instance + * \param reg_num Channel number (multiplied by 256) + * \param pan Panorama level (0 - left, 64 - middle, 127 - right) + */ +extern void Opal_Pan(Opal *self, uint16_t reg_num, uint8_t pan); + +/*! + * \brief Generate one 16-bit PCM sample in system endian (there are two integers will be written) + * \param self Pointer to the Opal instance + * \param left Sample for the left chanel + * \param right Sample for the right channel + */ +extern void Opal_Sample(Opal *self, int16_t* left, int16_t* right); + + +#ifdef __cplusplus +} +#endif + +#endif /* OPAL_HHHH */ diff --git a/src/chips/opal/opal.hpp b/src/chips/opal/opal.hpp deleted file mode 100644 index e8110a5..0000000 --- a/src/chips/opal/opal.hpp +++ /dev/null @@ -1,1398 +0,0 @@ -/* - - The Opal OPL3 emulator. - - Note: this is not a complete emulator, just enough for Reality Adlib Tracker tunes. - - Missing features compared to a real OPL3: - - - Timers/interrupts - - OPL3 enable bit (it defaults to always on) - - CSW mode - - Test register - - Percussion mode - -*/ -#define OPAL_HAVE_SOFT_PANNING 1 /* libADLMIDI */ - - - -#include - - - -//================================================================================================== -// Opal class. -//================================================================================================== -class Opal { - - class Channel; - - // Various constants - enum { - OPL3SampleRate = 49716, - NumChannels = 18, - NumOperators = 36, - - EnvOff = -1, - EnvAtt, - EnvDec, - EnvSus, - EnvRel - }; - - // A single FM operator - class Operator { - - public: - Operator(); - void SetMaster(Opal *opal) { Master = opal; } - void SetChannel(Channel *chan) { Chan = chan; } - - int16_t Output(uint16_t keyscalenum, uint32_t phase_step, int16_t vibrato, int16_t mod = 0, int16_t fbshift = 0); - - void SetKeyOn(bool on); - void SetTremoloEnable(bool on); - void SetVibratoEnable(bool on); - void SetSustainMode(bool on); - void SetEnvelopeScaling(bool on); - void SetFrequencyMultiplier(uint16_t scale); - void SetKeyScale(uint16_t scale); - void SetOutputLevel(uint16_t level); - void SetAttackRate(uint16_t rate); - void SetDecayRate(uint16_t rate); - void SetSustainLevel(uint16_t level); - void SetReleaseRate(uint16_t rate); - void SetWaveform(uint16_t wave); - - void ComputeRates(); - void ComputeKeyScaleLevel(); - - protected: - Opal * Master; // Master object - Channel * Chan; // Owning channel - uint32_t Phase; // The current offset in the selected waveform - uint16_t Waveform; // The waveform id this operator is using - uint16_t FreqMultTimes2; // Frequency multiplier * 2 - int EnvelopeStage; // Which stage the envelope is at (see Env* enums above) - int16_t EnvelopeLevel; // 0 - $1FF, 0 being the loudest - uint16_t OutputLevel; // 0 - $FF - uint16_t AttackRate; - uint16_t DecayRate; - uint16_t SustainLevel; - uint16_t ReleaseRate; - uint16_t AttackShift; - uint16_t AttackMask; - uint16_t AttackAdd; - const uint16_t *AttackTab; - uint16_t DecayShift; - uint16_t DecayMask; - uint16_t DecayAdd; - const uint16_t *DecayTab; - uint16_t ReleaseShift; - uint16_t ReleaseMask; - uint16_t ReleaseAdd; - const uint16_t *ReleaseTab; - uint16_t KeyScaleShift; - uint16_t KeyScaleLevel; - int16_t Out[2]; - bool KeyOn; - bool KeyScaleRate; // Affects envelope rate scaling - bool SustainMode; // Whether to sustain during the sustain phase, or release instead - bool TremoloEnable; - bool VibratoEnable; - }; - - // A single channel, which can contain two or more operators - class Channel { - - public: - Channel(); - void SetMaster(Opal *opal) { Master = opal; } - void SetOperators(Operator *a, Operator *b, Operator *c, Operator *d) { - Op[0] = a; - Op[1] = b; - Op[2] = c; - Op[3] = d; - if (a) - a->SetChannel(this); - if (b) - b->SetChannel(this); - if (c) - c->SetChannel(this); - if (d) - d->SetChannel(this); - } - - void Output(int16_t &left, int16_t &right); - void SetEnable(bool on) { Enable = on; } - void SetChannelPair(Channel *pair) { ChannelPair = pair; } - - void SetFrequencyLow(uint16_t freq); - void SetFrequencyHigh(uint16_t freq); - void SetKeyOn(bool on); - void SetOctave(uint16_t oct); - void SetLeftEnable(bool on); - void SetRightEnable(bool on); - void SetPan(uint8_t pan); - void SetFeedback(uint16_t val); - void SetModulationType(uint16_t type); - - uint16_t GetFreq() const { return Freq; } - uint16_t GetOctave() const { return Octave; } - uint16_t GetKeyScaleNumber() const { return KeyScaleNumber; } - uint16_t GetModulationType() const { return ModulationType; } - Channel * GetChannelPair() const { return ChannelPair; } /* libADLMIDI */ - - void ComputeKeyScaleNumber(); - - protected: - void ComputePhaseStep(); - - Operator * Op[4]; - - Opal * Master; // Master object - uint16_t Freq; // Frequency; actually it's a phase stepping value - uint16_t Octave; // Also known as "block" in Yamaha parlance - uint32_t PhaseStep; - uint16_t KeyScaleNumber; - uint16_t FeedbackShift; - uint16_t ModulationType; - Channel * ChannelPair; - bool Enable; - bool LeftEnable, RightEnable; - uint16_t LeftPan, RightPan; - }; - - public: - Opal(int sample_rate); - ~Opal(); - - void SetSampleRate(int sample_rate); - void Port(uint16_t reg_num, uint8_t val); - void Pan(uint16_t reg_num, uint8_t pan); - void Sample(int16_t *left, int16_t *right); - - protected: - void Init(int sample_rate); - void Output(int16_t &left, int16_t &right); - - int32_t SampleRate; - int32_t SampleAccum; - int16_t LastOutput[2], CurrOutput[2]; - Channel Chan[NumChannels]; - Operator Op[NumOperators]; -// uint16_t ExpTable[256]; -// uint16_t LogSinTable[256]; - uint16_t Clock; - uint16_t TremoloClock; - uint16_t TremoloLevel; - uint16_t VibratoTick; - uint16_t VibratoClock; - bool NoteSel; - bool TremoloDepth; - bool VibratoDepth; - - static const uint16_t RateTables[4][8]; - static const uint16_t ExpTable[256]; - static const uint16_t LogSinTable[256]; - static const uint16_t PanLawTable[128]; -}; -//-------------------------------------------------------------------------------------------------- -const uint16_t Opal::RateTables[4][8] = { - { 1, 0, 1, 0, 1, 0, 1, 0 }, - { 1, 0, 1, 0, 0, 0, 1, 0 }, - { 1, 0, 0, 0, 1, 0, 0, 0 }, - { 1, 0, 0, 0, 0, 0, 0, 0 }, -}; -//-------------------------------------------------------------------------------------------------- -const uint16_t Opal::ExpTable[0x100] = { - 1018, 1013, 1007, 1002, 996, 991, 986, 980, 975, 969, 964, 959, 953, 948, 942, 937, - 932, 927, 921, 916, 911, 906, 900, 895, 890, 885, 880, 874, 869, 864, 859, 854, - 849, 844, 839, 834, 829, 824, 819, 814, 809, 804, 799, 794, 789, 784, 779, 774, - 770, 765, 760, 755, 750, 745, 741, 736, 731, 726, 722, 717, 712, 708, 703, 698, - 693, 689, 684, 680, 675, 670, 666, 661, 657, 652, 648, 643, 639, 634, 630, 625, - 621, 616, 612, 607, 603, 599, 594, 590, 585, 581, 577, 572, 568, 564, 560, 555, - 551, 547, 542, 538, 534, 530, 526, 521, 517, 513, 509, 505, 501, 496, 492, 488, - 484, 480, 476, 472, 468, 464, 460, 456, 452, 448, 444, 440, 436, 432, 428, 424, - 420, 416, 412, 409, 405, 401, 397, 393, 389, 385, 382, 378, 374, 370, 367, 363, - 359, 355, 352, 348, 344, 340, 337, 333, 329, 326, 322, 318, 315, 311, 308, 304, - 300, 297, 293, 290, 286, 283, 279, 276, 272, 268, 265, 262, 258, 255, 251, 248, - 244, 241, 237, 234, 231, 227, 224, 220, 217, 214, 210, 207, 204, 200, 197, 194, - 190, 187, 184, 181, 177, 174, 171, 168, 164, 161, 158, 155, 152, 148, 145, 142, - 139, 136, 133, 130, 126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, - 90, 87, 84, 81, 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, - 42, 40, 37, 34, 31, 28, 25, 22, 20, 17, 14, 11, 8, 6, 3, 0, -}; -//-------------------------------------------------------------------------------------------------- -const uint16_t Opal::LogSinTable[0x100] = { - 2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, - 846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, - 598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, - 453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, - 352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, - 276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, - 215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, - 167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, - 127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, - 94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, - 67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, - 46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, - 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, - 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, - 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, - 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, -}; -//-------------------------------------------------------------------------------------------------- -const uint16_t Opal::PanLawTable[128] = -{ - 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, - 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, - 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, - 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, - 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, - 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, - 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, - 50433, 49912, 49383, 48846, 48302, 47750, 47191, - 46340, // Center left - 46340, // Center right - 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, - 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, - 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, - 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, - 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, - 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, - 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, - 4858, 4050, 3240, 2431, 1620, 810, 0 -}; - - - -//================================================================================================== -// This is the temporary code for generating the above tables. Maths and data from this nice -// reverse-engineering effort: -// -// https://docs.google.com/document/d/18IGx18NQY_Q1PJVZ-bHywao9bhsDoAqoIn1rIm42nwo/edit -//================================================================================================== -#if 0 -#include - -void GenerateTables() { - - // Build the exponentiation table (reversed from the official OPL3 ROM) - FILE *fd = fopen("exptab.txt", "wb"); - if (fd) { - for (int i = 0; i < 0x100; i++) { - int v = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; - if (i & 15) - fprintf(fd, " %4d,", v); - else - fprintf(fd, "\n\t%4d,", v); - } - fclose(fd); - } - - // Build the log-sin table - fd = fopen("sintab.txt", "wb"); - if (fd) { - for (int i = 0; i < 0x100; i++) { - int v = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; - if (i & 15) - fprintf(fd, " %4d,", v); - else - fprintf(fd, "\n\t%4d,", v); - } - fclose(fd); - } -} -#endif - - - -//================================================================================================== -// Constructor/destructor. -//================================================================================================== -Opal::Opal(int sample_rate) { - - Init(sample_rate); -} -//-------------------------------------------------------------------------------------------------- -Opal::~Opal() { -} - - - -//================================================================================================== -// Initialise the emulation. -//================================================================================================== -void Opal::Init(int sample_rate) { - - Clock = 0; - TremoloClock = 0; - TremoloLevel = 0; - VibratoTick = 0; - VibratoClock = 0; - NoteSel = false; - TremoloDepth = false; - VibratoDepth = false; - -// // Build the exponentiation table (reversed from the official OPL3 ROM) -// for (int i = 0; i < 0x100; i++) -// ExpTable[i] = (pow(2, (0xFF - i) / 256.0) - 1) * 1024 + 0.5; -// -// // Build the log-sin table -// for (int i = 0; i < 0x100; i++) -// LogSinTable[i] = -log(sin((i + 0.5) * 3.1415926535897933 / 256 / 2)) / log(2) * 256 + 0.5; - - // Let sub-objects know where to find us - for (int i = 0; i < NumOperators; i++) - Op[i].SetMaster(this); - - for (int i = 0; i < NumChannels; i++) - Chan[i].SetMaster(this); - - // Add the operators to the channels. Note, some channels can't use all the operators - // FIXME: put this into a separate routine - const int chan_ops[] = { - 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32, - }; - - for (int i = 0; i < NumChannels; i++) { - Channel *chan = &Chan[i]; - int op = chan_ops[i]; - if (i < 3 || (i >= 9 && i < 12)) - chan->SetOperators(&Op[op], &Op[op + 3], &Op[op + 6], &Op[op + 9]); - else - chan->SetOperators(&Op[op], &Op[op + 3], 0, 0); - } - - // Initialise the operator rate data. We can't do this in the Operator constructor as it - // relies on referencing the master and channel objects - for (int i = 0; i < NumOperators; i++) - Op[i].ComputeRates(); - - // Initialise channel panning at center. - for (int i = 0; i < NumChannels; i++) { - Chan[i].SetPan(64); - Chan[i].SetLeftEnable(true); - Chan[i].SetRightEnable(true); - } - - SetSampleRate(sample_rate); -} - - - -//================================================================================================== -// Change the sample rate. -//================================================================================================== -void Opal::SetSampleRate(int sample_rate) { - - // Sanity - if (sample_rate == 0) - sample_rate = OPL3SampleRate; - - SampleRate = sample_rate; - SampleAccum = 0; - LastOutput[0] = LastOutput[1] = 0; - CurrOutput[0] = CurrOutput[1] = 0; -} - - - -//================================================================================================== -// Write a value to an OPL3 register. -//================================================================================================== -void Opal::Port(uint16_t reg_num, uint8_t val) { - - const int op_lookup[] = { - // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F - 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, - // 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F - 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - }; - - uint16_t type = reg_num & 0xE0; - - // Is it BD, the one-off register stuck in the middle of the register array? - if (reg_num == 0xBD) { - TremoloDepth = (val & 0x80) != 0; - VibratoDepth = (val & 0x40) != 0; - return; - } - - // Global registers - if (type == 0x00) { - - // 4-OP enables - if (reg_num == 0x104) { - - // Enable/disable channels based on which 4-op enables - uint8_t mask = 1; - for (int i = 0; i < 6; i++, mask <<= 1) { - - // The 4-op channels are 0, 1, 2, 9, 10, 11 - uint16_t chan = i < 3 ? i : i + 6; - Channel *primary = &Chan[chan]; - Channel *secondary = &Chan[chan + 3]; - - if (val & mask) { - - // Let primary channel know it's controlling the secondary channel - primary->SetChannelPair(secondary); - - // Turn off the second channel in the pair - secondary->SetEnable(false); - - } else { - - // Let primary channel know it's no longer controlling the secondary channel - primary->SetChannelPair(0); - - // Turn on the second channel in the pair - secondary->SetEnable(true); - } - } - - // CSW / Note-sel - } else if (reg_num == 0x08) { - - NoteSel = (val & 0x40) != 0; - - // Get the channels to recompute the Key Scale No. as this varies based on NoteSel - for (int i = 0; i < NumChannels; i++) - Chan[i].ComputeKeyScaleNumber(); - } - - // Channel registers - } else if (type >= 0xA0 && type <= 0xC0) { - - // Convert to channel number - int chan_num = reg_num & 15; - - // Valid channel? - if (chan_num >= 9) - return; - - // Is it the other bank of channels? - if (reg_num & 0x100) - chan_num += 9; - - Channel &chan = Chan[chan_num]; - - /* libADLMIDI: registers Ax and Cx affect both channels */ - Channel *chans[2] = {&chan, chan.GetChannelPair()}; - unsigned numchans = chans[1] ? 2 : 1; - - // Do specific registers - switch (reg_num & 0xF0) { - - // Frequency low - case 0xA0: { - for (unsigned i = 0; i < numchans; ++i) { /* libADLMIDI */ - chans[i]->SetFrequencyLow(val); - } - break; - } - - // Key-on / Octave / Frequency High - case 0xB0: { - for (unsigned i = 0; i < numchans; ++i) { /* libADLMIDI */ - chans[i]->SetKeyOn((val & 0x20) != 0); - chans[i]->SetOctave(val >> 2 & 7); - chans[i]->SetFrequencyHigh(val & 3); - } - break; - } - - // Right Stereo Channel Enable / Left Stereo Channel Enable / Feedback Factor / Modulation Type - case 0xC0: { - chan.SetRightEnable((val & 0x20) != 0); - chan.SetLeftEnable((val & 0x10) != 0); - chan.SetFeedback(val >> 1 & 7); - chan.SetModulationType(val & 1); - break; - } - } - - // Operator registers - } else if ((type >= 0x20 && type <= 0x80) || type == 0xE0) { - - // Convert to operator number - int op_num = op_lookup[reg_num & 0x1F]; - - // Valid register? - if (op_num < 0) - return; - - // Is it the other bank of operators? - if (reg_num & 0x100) - op_num += 18; - - Operator &op = Op[op_num]; - - // Do specific registers - switch (type) { - - // Tremolo Enable / Vibrato Enable / Sustain Mode / Envelope Scaling / Frequency Multiplier - case 0x20: { - op.SetTremoloEnable((val & 0x80) != 0); - op.SetVibratoEnable((val & 0x40) != 0); - op.SetSustainMode((val & 0x20) != 0); - op.SetEnvelopeScaling((val & 0x10) != 0); - op.SetFrequencyMultiplier(val & 15); - break; - } - - // Key Scale / Output Level - case 0x40: { - op.SetKeyScale(val >> 6); - op.SetOutputLevel(val & 0x3F); - break; - } - - // Attack Rate / Decay Rate - case 0x60: { - op.SetAttackRate(val >> 4); - op.SetDecayRate(val & 15); - break; - } - - // Sustain Level / Release Rate - case 0x80: { - op.SetSustainLevel(val >> 4); - op.SetReleaseRate(val & 15); - break; - } - - // Waveform - case 0xE0: { - op.SetWaveform(val & 7); - break; - } - } - } -} - - - -//================================================================================================== -// Set panning on the channel designated by the register number. -// This is extended functionality. -//================================================================================================== -void Opal::Pan(uint16_t reg_num, uint8_t pan) -{ - uint8_t high = (reg_num >> 8) & 1; - uint8_t regm = reg_num & 0xff; - Chan[9 * high + (regm & 0x0f)].SetPan(pan); -} - - - -//================================================================================================== -// Generate sample. Every time you call this you will get two signed 16-bit samples (one for each -// stereo channel) which will sound correct when played back at the sample rate given when the -// class was constructed. -//================================================================================================== -void Opal::Sample(int16_t *left, int16_t *right) { - - // If the destination sample rate is higher than the OPL3 sample rate, we need to skip ahead - while (SampleAccum >= SampleRate) { - - LastOutput[0] = CurrOutput[0]; - LastOutput[1] = CurrOutput[1]; - - Output(CurrOutput[0], CurrOutput[1]); - - SampleAccum -= SampleRate; - } - - // Mix with the partial accumulation - int32_t omblend = SampleRate - SampleAccum; - *left = (LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate; - *right = (LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate; - - SampleAccum += OPL3SampleRate; -} - - - -//================================================================================================== -// Produce final output from the chip. This is at the OPL3 sample-rate. -//================================================================================================== -void Opal::Output(int16_t &left, int16_t &right) { - - int32_t leftmix = 0, rightmix = 0; - - // Sum the output of each channel - for (int i = 0; i < NumChannels; i++) { - - int16_t chanleft, chanright; - Chan[i].Output(chanleft, chanright); - - leftmix += chanleft; - rightmix += chanright; - } - - // Clamp - if (leftmix < -0x8000) - left = -0x8000; - else if (leftmix > 0x7FFF) - left = 0x7FFF; - else - left = leftmix; - - if (rightmix < -0x8000) - right = -0x8000; - else if (rightmix > 0x7FFF) - right = 0x7FFF; - else - right = rightmix; - - Clock++; - - // Tremolo. According to this post, the OPL3 tremolo is a 13,440 sample length triangle wave - // with a peak at 26 and a trough at 0 and is simply added to the logarithmic level accumulator - // http://forums.submarine.org.uk/phpBB/viewtopic.php?f=9&t=1171 - TremoloClock = (TremoloClock + 1) % 13440; - TremoloLevel = ((TremoloClock < 13440 / 2) ? TremoloClock : 13440 - TremoloClock) / 256; - if (!TremoloDepth) - TremoloLevel >>= 2; - - // Vibrato. This appears to be a 8 sample long triangle wave with a magnitude of the three - // high bits of the channel frequency, positive and negative, divided by two if the vibrato - // depth is zero. It is only cycled every 1,024 samples. - VibratoTick++; - if (VibratoTick >= 1024) { - VibratoTick = 0; - VibratoClock = (VibratoClock + 1) & 7; - } -} - - - -//================================================================================================== -// Channel constructor. -//================================================================================================== -Opal::Channel::Channel() { - - Master = 0; - Freq = 0; - Octave = 0; - PhaseStep = 0; - KeyScaleNumber = 0; - FeedbackShift = 0; - ModulationType = 0; - ChannelPair = 0; - Enable = true; -} - - - -//================================================================================================== -// Produce output from channel. -//================================================================================================== -void Opal::Channel::Output(int16_t &left, int16_t &right) { - - // Has the channel been disabled? This is usually a result of the 4-op enables being used to - // disable the secondary channel in each 4-op pair - if (!Enable) { - left = right = 0; - return; - } - - int16_t vibrato = (Freq >> 7) & 7; - if (!Master->VibratoDepth) - vibrato >>= 1; - - // 0 3 7 3 0 -3 -7 -3 - uint16_t clk = Master->VibratoClock; - if (!(clk & 3)) - vibrato = 0; // Position 0 and 4 is zero - else { - if (clk & 1) - vibrato >>= 1; // Odd positions are half the magnitude - if (clk & 4) - vibrato = -vibrato; // The second half positions are negative - } - - vibrato <<= Octave; - - // Combine individual operator outputs - int16_t out, acc; - - // Running in 4-op mode? - if (ChannelPair) { - - // Get the secondary channel's modulation type. This is the only thing from the secondary - // channel that is used - if (ChannelPair->GetModulationType() == 0) { - - if (ModulationType == 0) { - - // feedback -> modulator -> modulator -> modulator -> carrier - out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); - out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); - out = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); - out = Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); - - } else { - - // (feedback -> carrier) + (modulator -> modulator -> carrier) - out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); - acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); - acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); - out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); - } - - } else { - - if (ModulationType == 0) { - - // (feedback -> modulator -> carrier) + (modulator -> carrier) - out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); - out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); - acc = Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); - out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); - - } else { - - // (feedback -> carrier) + (modulator -> carrier) + carrier - out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); - acc = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); - out += Op[2]->Output(KeyScaleNumber, PhaseStep, vibrato, acc, 0); - out += Op[3]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, 0); - } - } - - } else { - - // Standard 2-op mode - if (ModulationType == 0) { - - // Frequency modulation (well, phase modulation technically) - out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); - out = Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato, out, 0); - - } else { - - // Additive - out = Op[0]->Output(KeyScaleNumber, PhaseStep, vibrato, 0, FeedbackShift); - out += Op[1]->Output(KeyScaleNumber, PhaseStep, vibrato); - } - } - - left = LeftEnable ? out : 0; - right = RightEnable ? out : 0; - - left = left * LeftPan / 65536; - right = right * RightPan / 65536; -} - - - -//================================================================================================== -// Set phase step for operators using this channel. -//================================================================================================== -void Opal::Channel::SetFrequencyLow(uint16_t freq) { - - Freq = (Freq & 0x300) | (freq & 0xFF); - ComputePhaseStep(); -} -//-------------------------------------------------------------------------------------------------- -void Opal::Channel::SetFrequencyHigh(uint16_t freq) { - - Freq = (Freq & 0xFF) | ((freq & 3) << 8); - ComputePhaseStep(); - - // Only the high bits of Freq affect the Key Scale No. - ComputeKeyScaleNumber(); -} - - - -//================================================================================================== -// Set the octave of the channel (0 to 7). -//================================================================================================== -void Opal::Channel::SetOctave(uint16_t oct) { - - Octave = oct & 7; - ComputePhaseStep(); - ComputeKeyScaleNumber(); -} - - - -//================================================================================================== -// Keys the channel on/off. -//================================================================================================== -void Opal::Channel::SetKeyOn(bool on) { - - Op[0]->SetKeyOn(on); - Op[1]->SetKeyOn(on); -} - - - -//================================================================================================== -// Enable left stereo channel. -//================================================================================================== -void Opal::Channel::SetLeftEnable(bool on) { - - LeftEnable = on; -} - - - -//================================================================================================== -// Enable right stereo channel. -//================================================================================================== -void Opal::Channel::SetRightEnable(bool on) { - - RightEnable = on; -} - - - -//================================================================================================== -// Pan the channel to the position given. -//================================================================================================== -void Opal::Channel::SetPan(uint8_t pan) -{ - pan &= 127; - LeftPan = PanLawTable[pan]; - RightPan = PanLawTable[127 - pan]; -} - - - -//================================================================================================== -// Set the channel feedback amount. -//================================================================================================== -void Opal::Channel::SetFeedback(uint16_t val) { - - FeedbackShift = val ? 9 - val : 0; -} - - - -//================================================================================================== -// Set frequency modulation/additive modulation -//================================================================================================== -void Opal::Channel::SetModulationType(uint16_t type) { - - ModulationType = type; -} - - - -//================================================================================================== -// Compute the stepping factor for the operator waveform phase based on the frequency and octave -// values of the channel. -//================================================================================================== -void Opal::Channel::ComputePhaseStep() { - - PhaseStep = uint32_t(Freq) << Octave; -} - - - -//================================================================================================== -// Compute the key scale number and key scale levels. -// -// From the Yamaha data sheet this is the block/octave number as bits 3-1, with bit 0 coming from -// the MSB of the frequency if NoteSel is 1, and the 2nd MSB if NoteSel is 0. -//================================================================================================== -void Opal::Channel::ComputeKeyScaleNumber() { - - uint16_t lsb = Master->NoteSel ? Freq >> 9 : (Freq >> 8) & 1; - KeyScaleNumber = Octave << 1 | lsb; - - // Get the channel operators to recompute their rates as they're dependent on this number. They - // also need to recompute their key scale level - for (int i = 0; i < 4; i++) { - - if (!Op[i]) - continue; - - Op[i]->ComputeRates(); - Op[i]->ComputeKeyScaleLevel(); - } -} - - - -//================================================================================================== -// Operator constructor. -//================================================================================================== -Opal::Operator::Operator() { - - Master = 0; - Chan = 0; - Phase = 0; - Waveform = 0; - FreqMultTimes2 = 1; - EnvelopeStage = EnvOff; - EnvelopeLevel = 0x1FF; - AttackRate = 0; - DecayRate = 0; - SustainLevel = 0; - ReleaseRate = 0; - KeyScaleShift = 0; - KeyScaleLevel = 0; - Out[0] = Out[1] = 0; - KeyOn = false; - KeyScaleRate = false; - SustainMode = false; - TremoloEnable = false; - VibratoEnable = false; -} - - - -//================================================================================================== -// Produce output from operator. -//================================================================================================== -int16_t Opal::Operator::Output(uint16_t /*keyscalenum*/, uint32_t phase_step, int16_t vibrato, int16_t mod, int16_t fbshift) { - - // Advance wave phase - if (VibratoEnable) - phase_step += vibrato; - Phase += (phase_step * FreqMultTimes2) / 2; - - uint16_t level = (EnvelopeLevel + OutputLevel + KeyScaleLevel + (TremoloEnable ? Master->TremoloLevel : 0)) << 3; - - switch (EnvelopeStage) { - - // Attack stage - case EnvAtt: { - uint16_t add = ((AttackAdd >> AttackTab[Master->Clock >> AttackShift & 7]) * ~EnvelopeLevel) >> 3; - if (AttackRate == 0) - add = 0; - if (AttackMask && (Master->Clock & AttackMask)) - add = 0; - EnvelopeLevel += add; - if (EnvelopeLevel <= 0) { - EnvelopeLevel = 0; - EnvelopeStage = EnvDec; - } - break; - } - - // Decay stage - case EnvDec: { - uint16_t add = DecayAdd >> DecayTab[Master->Clock >> DecayShift & 7]; - if (DecayRate == 0) - add = 0; - if (DecayMask && (Master->Clock & DecayMask)) - add = 0; - EnvelopeLevel += add; - if (EnvelopeLevel >= SustainLevel) { - EnvelopeLevel = SustainLevel; - EnvelopeStage = EnvSus; - } - break; - } - - // Sustain stage - case EnvSus: { - if (SustainMode) - break; - // Note: fall-through! - - }//fallthrough - - // Release stage - case EnvRel: { - uint16_t add = ReleaseAdd >> ReleaseTab[Master->Clock >> ReleaseShift & 7]; - if (ReleaseRate == 0) - add = 0; - if (ReleaseMask && (Master->Clock & ReleaseMask)) - add = 0; - EnvelopeLevel += add; - if (EnvelopeLevel >= 0x1FF) { - EnvelopeLevel = 0x1FF; - EnvelopeStage = EnvOff; - Out[0] = Out[1] = 0; - return 0; - } - break; - } - - // Envelope, and therefore the operator, is not running - default: - Out[0] = Out[1] = 0; - return 0; - } - - // Feedback? In that case we modulate by a blend of the last two samples - if (fbshift) - mod += (Out[0] + Out[1]) >> fbshift; - - uint16_t phase = (Phase >> 10) + mod; - uint16_t offset = phase & 0xFF; - uint16_t logsin; - bool negate = false; - - switch (Waveform) { - - //------------------------------------ - // Standard sine wave - //------------------------------------ - case 0: - if (phase & 0x100) - offset ^= 0xFF; - logsin = Master->LogSinTable[offset]; - negate = (phase & 0x200) != 0; - break; - - //------------------------------------ - // Half sine wave - //------------------------------------ - case 1: - if (phase & 0x200) - offset = 0; - else if (phase & 0x100) - offset ^= 0xFF; - logsin = Master->LogSinTable[offset]; - break; - - //------------------------------------ - // Positive sine wave - //------------------------------------ - case 2: - if (phase & 0x100) - offset ^= 0xFF; - logsin = Master->LogSinTable[offset]; - break; - - //------------------------------------ - // Quarter positive sine wave - //------------------------------------ - case 3: - if (phase & 0x100) - offset = 0; - logsin = Master->LogSinTable[offset]; - break; - - //------------------------------------ - // Double-speed sine wave - //------------------------------------ - case 4: - if (phase & 0x200) - offset = 0; - - else { - - if (phase & 0x80) - offset ^= 0xFF; - - offset = (offset + offset) & 0xFF; - negate = (phase & 0x100) != 0; - } - - logsin = Master->LogSinTable[offset]; - break; - - //------------------------------------ - // Double-speed positive sine wave - //------------------------------------ - case 5: - if (phase & 0x200) - offset = 0; - - else { - - offset = (offset + offset) & 0xFF; - if (phase & 0x80) - offset ^= 0xFF; - } - - logsin = Master->LogSinTable[offset]; - break; - - //------------------------------------ - // Square wave - //------------------------------------ - case 6: - logsin = 0; - negate = (phase & 0x200) != 0; - break; - - //------------------------------------ - // Exponentiation wave - //------------------------------------ - default: - logsin = phase & 0x1FF; - if (phase & 0x200) { - logsin ^= 0x1FF; - negate = true; - } - logsin <<= 3; - break; - } - - uint16_t mix = logsin + level; - if (mix > 0x1FFF) - mix = 0x1FFF; - - // From the OPLx decapsulated docs: - // "When such a table is used for calculation of the exponential, the table is read at the - // position given by the 8 LSB's of the input. The value + 1024 (the hidden bit) is then the - // significand of the floating point output and the yet unused MSB's of the input are the - // exponent of the floating point output." - int16_t v = Master->ExpTable[mix & 0xFF] + 1024; - v >>= mix >> 8; - v += v; - if (negate) - v = ~v; - - // Keep last two results for feedback calculation - Out[1] = Out[0]; - Out[0] = v; - - return v; -} - - - -//================================================================================================== -// Trigger operator. -//================================================================================================== -void Opal::Operator::SetKeyOn(bool on) { - - // Already on/off? - if (KeyOn == on) - return; - KeyOn = on; - - if (on) { - - // The highest attack rate is instant; it bypasses the attack phase - if (AttackRate == 15) { - EnvelopeStage = EnvDec; - EnvelopeLevel = 0; - } else - EnvelopeStage = EnvAtt; - - Phase = 0; - - } else { - - // Stopping current sound? - if (EnvelopeStage != EnvOff && EnvelopeStage != EnvRel) - EnvelopeStage = EnvRel; - } -} - - - -//================================================================================================== -// Enable amplitude vibrato. -//================================================================================================== -void Opal::Operator::SetTremoloEnable(bool on) { - - TremoloEnable = on; -} - - - -//================================================================================================== -// Enable frequency vibrato. -//================================================================================================== -void Opal::Operator::SetVibratoEnable(bool on) { - - VibratoEnable = on; -} - - - -//================================================================================================== -// Sets whether we release or sustain during the sustain phase of the envelope. 'true' is to -// sustain, otherwise release. -//================================================================================================== -void Opal::Operator::SetSustainMode(bool on) { - - SustainMode = on; -} - - - -//================================================================================================== -// Key scale rate. Sets how much the Key Scaling Number affects the envelope rates. -//================================================================================================== -void Opal::Operator::SetEnvelopeScaling(bool on) { - - KeyScaleRate = on; - ComputeRates(); -} - - - -//================================================================================================== -// Multiplies the phase frequency. -//================================================================================================== -void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) { - - // Needs to be multiplied by two (and divided by two later when we use it) because the first - // entry is actually .5 - const uint16_t mul_times_2[] = { - 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30, - }; - - FreqMultTimes2 = mul_times_2[scale & 15]; -} - - - -//================================================================================================== -// Attenuates output level towards higher pitch. -//================================================================================================== -void Opal::Operator::SetKeyScale(uint16_t scale) { - - /* libADLMIDI: KSL computation fix */ - const unsigned KeyScaleShiftTable[4] = {8, 1, 2, 0}; - KeyScaleShift = KeyScaleShiftTable[scale]; - - ComputeKeyScaleLevel(); -} - - - -//================================================================================================== -// Sets the output level (volume) of the operator. -//================================================================================================== -void Opal::Operator::SetOutputLevel(uint16_t level) { - - OutputLevel = level * 4; -} - - - -//================================================================================================== -// Operator attack rate. -//================================================================================================== -void Opal::Operator::SetAttackRate(uint16_t rate) { - - AttackRate = rate; - - ComputeRates(); -} - - - -//================================================================================================== -// Operator decay rate. -//================================================================================================== -void Opal::Operator::SetDecayRate(uint16_t rate) { - - DecayRate = rate; - - ComputeRates(); -} - - - -//================================================================================================== -// Operator sustain level. -//================================================================================================== -void Opal::Operator::SetSustainLevel(uint16_t level) { - - SustainLevel = level < 15 ? level : 31; - SustainLevel *= 16; -} - - - -//================================================================================================== -// Operator release rate. -//================================================================================================== -void Opal::Operator::SetReleaseRate(uint16_t rate) { - - ReleaseRate = rate; - - ComputeRates(); -} - - - -//================================================================================================== -// Assign the waveform this operator will use. -//================================================================================================== -void Opal::Operator::SetWaveform(uint16_t wave) { - - Waveform = wave & 7; -} - - - -//================================================================================================== -// Compute actual rate from register rate. From the Yamaha data sheet: -// -// Actual rate = Rate value * 4 + Rof, if Rate value = 0, actual rate = 0 -// -// Rof is set as follows depending on the KSR setting: -// -// Key scale 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 -// KSR = 0 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 -// KSR = 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 -// -// Note: zero rates are infinite, and are treated separately elsewhere -//================================================================================================== -void Opal::Operator::ComputeRates() { - - int combined_rate = AttackRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); - int rate_high = combined_rate >> 2; - int rate_low = combined_rate & 3; - - AttackShift = rate_high < 12 ? 12 - rate_high : 0; - AttackMask = (1 << AttackShift) - 1; - AttackAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); - AttackTab = Master->RateTables[rate_low]; - - // Attack rate of 15 is always instant - if (AttackRate == 15) - AttackAdd = 0xFFF; - - combined_rate = DecayRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); - rate_high = combined_rate >> 2; - rate_low = combined_rate & 3; - - DecayShift = rate_high < 12 ? 12 - rate_high : 0; - DecayMask = (1 << DecayShift) - 1; - DecayAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); - DecayTab = Master->RateTables[rate_low]; - - combined_rate = ReleaseRate * 4 + (Chan->GetKeyScaleNumber() >> (KeyScaleRate ? 0 : 2)); - rate_high = combined_rate >> 2; - rate_low = combined_rate & 3; - - ReleaseShift = rate_high < 12 ? 12 - rate_high : 0; - ReleaseMask = (1 << ReleaseShift) - 1; - ReleaseAdd = (rate_high < 12) ? 1 : 1 << (rate_high - 12); - ReleaseTab = Master->RateTables[rate_low]; -} - - - -//================================================================================================== -// Compute the operator's key scale level. This changes based on the channel frequency/octave and -// operator key scale value. -//================================================================================================== -void Opal::Operator::ComputeKeyScaleLevel() { - - static const uint16_t levtab[] = { - 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, 8, 12, 16, 20, 24, 28, 32, - 0, 0, 0, 0, 0, 12, 20, 28, 32, 40, 44, 48, 52, 56, 60, 64, - 0, 0, 0, 20, 32, 44, 52, 60, 64, 72, 76, 80, 84, 88, 92, 96, - 0, 0, 32, 52, 64, 76, 84, 92, 96, 104, 108, 112, 116, 120, 124, 128, - 0, 32, 64, 84, 96, 108, 116, 124, 128, 136, 140, 144, 148, 152, 156, 160, - 0, 64, 96, 116, 128, 140, 148, 156, 160, 168, 172, 176, 180, 184, 188, 192, - 0, 96, 128, 148, 160, 172, 180, 188, 192, 200, 204, 208, 212, 216, 220, 224, - }; - - // This uses a combined value of the top four bits of frequency with the octave/block - uint16_t i = (Chan->GetOctave() << 4) | (Chan->GetFreq() >> 6); - KeyScaleLevel = levtab[i] >> KeyScaleShift; -} -- cgit v1.2.3