1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-09-20 04:24:48 +00:00

Add Opal soft panning support

This commit is contained in:
Samuel Gomes 2024-05-09 14:38:33 +05:30
parent 5b33b73da9
commit 2dcbe4be90

View file

@ -1,8 +1,3 @@
// 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.
/* /*
The Opal OPL3 emulator. The Opal OPL3 emulator.
@ -19,6 +14,12 @@
*/ */
// 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> #include <cstdint>
//================================================================================================== //==================================================================================================
@ -134,6 +135,7 @@ class Opal {
void SetOctave(uint16_t oct); void SetOctave(uint16_t oct);
void SetLeftEnable(bool on); void SetLeftEnable(bool on);
void SetRightEnable(bool on); void SetRightEnable(bool on);
void SetPan(uint8_t pan);
void SetFeedback(uint16_t val); void SetFeedback(uint16_t val);
void SetModulationType(uint16_t type); void SetModulationType(uint16_t type);
@ -160,6 +162,7 @@ class Opal {
Channel *ChannelPair; Channel *ChannelPair;
bool Enable; bool Enable;
bool LeftEnable, RightEnable; bool LeftEnable, RightEnable;
uint16_t LeftPan, RightPan;
}; };
public: public:
@ -170,6 +173,7 @@ class Opal {
void SetSampleRate(int sample_rate); void SetSampleRate(int sample_rate);
void Port(uint16_t reg_num, uint8_t val); 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); void Sample(int16_t *left, int16_t *right);
protected: protected:
@ -195,6 +199,7 @@ class Opal {
static const uint16_t RateTables[4][8]; static const uint16_t RateTables[4][8];
static const uint16_t ExpTable[256]; static const uint16_t ExpTable[256];
static const uint16_t LogSinTable[256]; static const uint16_t LogSinTable[256];
static const uint16_t PanLawTable[128];
}; };
// clang-format off // clang-format off
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@ -242,6 +247,28 @@ const uint16_t Opal::LogSinTable[0x100] = {
7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 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, 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 // clang-format on
//================================================================================================== //==================================================================================================
@ -339,6 +366,13 @@ void Opal::Init(int sample_rate) {
for (int i = 0; i < NumOperators; i++) for (int i = 0; i < NumOperators; i++)
Op[i].ComputeRates(); 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); SetSampleRate(sample_rate);
} }
@ -392,7 +426,11 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
// The 4-op channels are 0, 1, 2, 9, 10, 11 // The 4-op channels are 0, 1, 2, 9, 10, 11
uint16_t chan = static_cast<uint16_t>(i < 3 ? i : i + 6); uint16_t chan = static_cast<uint16_t>(i < 3 ? i : i + 6);
// cppcheck false-positive
// cppcheck-suppress arrayIndexOutOfBounds
Channel *primary = &Chan[chan]; Channel *primary = &Chan[chan];
// cppcheck false-positive
// cppcheck-suppress arrayIndexOutOfBounds
Channel *secondary = &Chan[chan + 3]; Channel *secondary = &Chan[chan + 3];
if (val & mask) { if (val & mask) {
@ -439,7 +477,7 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
Channel &chan = Chan[chan_num]; Channel &chan = Chan[chan_num];
// Registers Ax and Bx affect both channels // Registers Ax and Cx affect both channels
Channel *chans[2] = {&chan, chan.GetChannelPair()}; Channel *chans[2] = {&chan, chan.GetChannelPair()};
int numchans = chans[1] ? 2 : 1; int numchans = chans[1] ? 2 : 1;
@ -533,6 +571,16 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
} }
} }
//==================================================================================================
// 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 // 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 // stereo channel) which will sound correct when played back at the sample rate given when the
@ -553,8 +601,8 @@ void Opal::Sample(int16_t *left, int16_t *right) {
// Mix with the partial accumulation // Mix with the partial accumulation
int32_t omblend = SampleRate - SampleAccum; int32_t omblend = SampleRate - SampleAccum;
*left = static_cast<uint16_t>((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate); *left = static_cast<int16_t>((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate);
*right = static_cast<uint16_t>((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate); *right = static_cast<int16_t>((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate);
SampleAccum += OPL3SampleRate; SampleAccum += OPL3SampleRate;
} }
@ -582,14 +630,14 @@ void Opal::Output(int16_t &left, int16_t &right) {
else if (leftmix > 0x7FFF) else if (leftmix > 0x7FFF)
left = 0x7FFF; left = 0x7FFF;
else else
left = static_cast<uint16_t>(leftmix); left = static_cast<int16_t>(leftmix);
if (rightmix < -0x8000) if (rightmix < -0x8000)
right = -0x8000; right = -0x8000;
else if (rightmix > 0x7FFF) else if (rightmix > 0x7FFF)
right = 0x7FFF; right = 0x7FFF;
else else
right = static_cast<uint16_t>(rightmix); right = static_cast<int16_t>(rightmix);
Clock++; Clock++;
@ -725,6 +773,9 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) {
left = LeftEnable ? out : 0; left = LeftEnable ? out : 0;
right = RightEnable ? out : 0; right = RightEnable ? out : 0;
left = left * LeftPan / 65536;
right = right * RightPan / 65536;
} }
//================================================================================================== //==================================================================================================
@ -774,6 +825,15 @@ void Opal::Channel::SetLeftEnable(bool on) { LeftEnable = on; }
//================================================================================================== //==================================================================================================
void Opal::Channel::SetRightEnable(bool on) { RightEnable = on; } 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. // Set the channel feedback amount.
//================================================================================================== //==================================================================================================
@ -1117,8 +1177,8 @@ void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) {
//================================================================================================== //==================================================================================================
void Opal::Operator::SetKeyScale(uint16_t scale) { void Opal::Operator::SetKeyScale(uint16_t scale) {
static constexpr uint8_t kslShift[4] = {8, 1, 2, 0}; static const uint8_t kslShift[4] = {8, 1, 2, 0};
KeyScaleShift = kslShift[scale]; KeyScaleShift = kslShift[scale & 3];
ComputeKeyScaleLevel(); ComputeKeyScaleLevel();
} }