aboutsummaryrefslogtreecommitdiff
path: root/src/chips/opal/opal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/chips/opal/opal.c')
-rw-r--r--src/chips/opal/opal.c1427
1 files changed, 1427 insertions, 0 deletions
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 <stdint.h>
+#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;
+}