mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-20 03:14:45 +00:00
Add Opal soft panning support
This commit is contained in:
parent
5b33b73da9
commit
2dcbe4be90
1 changed files with 72 additions and 12 deletions
|
@ -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.
|
||||
|
@ -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>
|
||||
|
||||
//==================================================================================================
|
||||
|
@ -134,6 +135,7 @@ class Opal {
|
|||
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);
|
||||
|
||||
|
@ -160,6 +162,7 @@ class Opal {
|
|||
Channel *ChannelPair;
|
||||
bool Enable;
|
||||
bool LeftEnable, RightEnable;
|
||||
uint16_t LeftPan, RightPan;
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -170,6 +173,7 @@ class 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:
|
||||
|
@ -195,6 +199,7 @@ class Opal {
|
|||
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
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
@ -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,
|
||||
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
|
||||
|
||||
//==================================================================================================
|
||||
|
@ -339,6 +366,13 @@ void Opal::Init(int sample_rate) {
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
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) {
|
||||
|
@ -439,7 +477,7 @@ void Opal::Port(uint16_t reg_num, uint8_t val) {
|
|||
|
||||
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()};
|
||||
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
|
||||
// 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
|
||||
int32_t omblend = SampleRate - SampleAccum;
|
||||
*left = static_cast<uint16_t>((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate);
|
||||
*right = static_cast<uint16_t>((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate);
|
||||
*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;
|
||||
}
|
||||
|
@ -582,14 +630,14 @@ void Opal::Output(int16_t &left, int16_t &right) {
|
|||
else if (leftmix > 0x7FFF)
|
||||
left = 0x7FFF;
|
||||
else
|
||||
left = static_cast<uint16_t>(leftmix);
|
||||
left = static_cast<int16_t>(leftmix);
|
||||
|
||||
if (rightmix < -0x8000)
|
||||
right = -0x8000;
|
||||
else if (rightmix > 0x7FFF)
|
||||
right = 0x7FFF;
|
||||
else
|
||||
right = static_cast<uint16_t>(rightmix);
|
||||
right = static_cast<int16_t>(rightmix);
|
||||
|
||||
Clock++;
|
||||
|
||||
|
@ -725,6 +773,9 @@ void Opal::Channel::Output(int16_t &left, int16_t &right) {
|
|||
|
||||
left = LeftEnable ? 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; }
|
||||
|
||||
//==================================================================================================
|
||||
// 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.
|
||||
//==================================================================================================
|
||||
|
@ -1117,8 +1177,8 @@ void Opal::Operator::SetFrequencyMultiplier(uint16_t scale) {
|
|||
//==================================================================================================
|
||||
void Opal::Operator::SetKeyScale(uint16_t scale) {
|
||||
|
||||
static constexpr uint8_t kslShift[4] = {8, 1, 2, 0};
|
||||
KeyScaleShift = kslShift[scale];
|
||||
static const uint8_t kslShift[4] = {8, 1, 2, 0};
|
||||
KeyScaleShift = kslShift[scale & 3];
|
||||
ComputeKeyScaleLevel();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue