1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-09-20 09:04:43 +00:00
QB64-PE/internal/c/parts/audio/extras/radv2/opal.cpp
2024-05-09 14:38:33 +05:30

1303 lines
47 KiB
C++

/*
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
*/
// This is the Opal OPL3 emulator from Reality Adlib Tracker v2.0a (http://www.3eality.com/productions/reality-adlib-tracker).
// It was released by Shayde/Reality into the public domain.
// Minor modifications to silence some warnings and fix a bug in the envelope generator have been applied.
// Additional fixes by JP Cimalando.
// Soft panning support by Wohlstand.
#include <cstdint>
//==================================================================================================
// 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; }
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(const Opal &) = delete;
Opal(Opal &&) = delete;
~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];
};
// clang-format off
//--------------------------------------------------------------------------------------------------
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
};
// clang-format on
//==================================================================================================
// 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 <math.h>
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) {
// clang-format off
static constexpr int8_t 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,
};
// clang-format on
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 = static_cast<uint16_t>(i < 3 ? i : i + 6);
// cppcheck false-positive
// cppcheck-suppress arrayIndexOutOfBounds
Channel *primary = &Chan[chan];
// cppcheck false-positive
// cppcheck-suppress arrayIndexOutOfBounds
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];
// Registers Ax and Cx affect both channels
Channel *chans[2] = {&chan, chan.GetChannelPair()};
int numchans = chans[1] ? 2 : 1;
// Do specific registers
switch (reg_num & 0xF0) {
// Frequency low
case 0xA0: {
for (int i = 0; i < numchans; i++) {
chans[i]->SetFrequencyLow(val);
}
break;
}
// Key-on / Octave / Frequency High
case 0xB0: {
for (int i = 0; i < numchans; i++) {
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 = static_cast<int16_t>((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate);
*right = static_cast<int16_t>((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 = static_cast<int16_t>(leftmix);
if (rightmix < -0x8000)
right = -0x8000;
else if (rightmix > 0x7FFF)
right = 0x7FFF;
else
right = static_cast<int16_t>(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;
LeftEnable = true;
RightEnable = 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
vibrato <<= Octave;
if (clk & 4)
vibrato = -vibrato; // The second half positions are negative
}
// 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 = static_cast<uint16_t>(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] + 1024u) >> (mix >> 8u);
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) {
static const uint8_t kslShift[4] = {8, 1, 2, 0};
KeyScaleShift = kslShift[scale & 3];
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 = static_cast<uint16_t>(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 = static_cast<uint16_t>(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 = static_cast<uint16_t>(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() {
// clang-format off
static constexpr uint8_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,
};
// clang-format on
// 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;
}