From 2dcbe4be9031b75679221edb57a209030bc73e40 Mon Sep 17 00:00:00 2001 From: Samuel Gomes Date: Thu, 9 May 2024 14:38:33 +0530 Subject: [PATCH] Add Opal soft panning support --- internal/c/parts/audio/extras/radv2/opal.cpp | 84 +++++++++++++++++--- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/internal/c/parts/audio/extras/radv2/opal.cpp b/internal/c/parts/audio/extras/radv2/opal.cpp index 44cd409e9..3f86f1cbe 100644 --- a/internal/c/parts/audio/extras/radv2/opal.cpp +++ b/internal/c/parts/audio/extras/radv2/opal.cpp @@ -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 //================================================================================================== @@ -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(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((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate); - *right = static_cast((LastOutput[1] * omblend + CurrOutput[1] * SampleAccum) / SampleRate); + *left = static_cast((LastOutput[0] * omblend + CurrOutput[0] * SampleAccum) / SampleRate); + *right = static_cast((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(leftmix); + left = static_cast(leftmix); if (rightmix < -0x8000) right = -0x8000; else if (rightmix > 0x7FFF) right = 0x7FFF; else - right = static_cast(rightmix); + right = static_cast(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(); }