diff --git a/Components/OPL2/Implementation/Operator.cpp b/Components/OPL2/Implementation/Operator.cpp index e898218d9..048340022 100644 --- a/Components/OPL2/Implementation/Operator.cpp +++ b/Components/OPL2/Implementation/Operator.cpp @@ -62,45 +62,13 @@ bool Operator::is_audible(OperatorState &state, OperatorOverrides *overrides) { // MARK: - Update logic. -void Operator::update( +void Operator::update_adsr( OperatorState &state, - const OperatorState *phase_offset, const LowFrequencyOscillator &oscillator, bool key_on, int channel_period, int channel_octave, const OperatorOverrides *overrides) { - // Per the documentation: - // - // Delta phase = ( [desired freq] * 2^19 / [input clock / 72] ) / 2 ^ (b - 1) - // - // After experimentation, I think this gives rate calculation as formulated below. - - // This encodes the MUL -> multiple table given on page 12, - // multiplied by two. - constexpr int multipliers[] = { - 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 - }; - const int top_freq = channel_period >> 7; - assert(top_freq < 8); - constexpr int vibrato_shifts[8] = {3, 1, 0, 1, 3, 1, 0, 1}; - constexpr int vibrato_signs[2] = {1, -1}; - const int vibrato = (top_freq >> vibrato_shifts[oscillator.vibrato]) * vibrato_signs[oscillator.vibrato >> 2] * int(apply_vibrato_); - - // Update the raw phase. - state.raw_phase_ += multipliers[frequency_multiple_] * (channel_period + vibrato) << channel_octave; - - // Hence calculate phase. - constexpr int waveforms[4][4] = { - {1023, 1023, 1023, 1023}, // Sine: don't mask in any quadrant. - {511, 511, 0, 0}, // Half sine: keep the first half intact, lock to 0 in the second half. - {511, 511, 511, 511}, // AbsSine: endlessly repeat the first half of the sine wave. - {255, 0, 255, 0}, // PulseSine: act as if the first quadrant is in the first and third; lock the other two to 0. - }; - const int scaled_phase_offset = phase_offset ? power_two(phase_offset->attenuation, 11) : 0; - const int phase = (state.raw_phase_ + scaled_phase_offset) >> 11; - state.attenuation = negative_log_sin(phase & waveforms[int(waveform_)][(phase >> 8) & 3]); - // Key-on logic: any time it is false, be in the release state. // On the leading edge of it becoming true, enter the attack state. if(!key_on) { @@ -194,6 +162,50 @@ void Operator::update( break; } ++state.attack_time_; +} + +void Operator::update( + OperatorState &state, + const OperatorState *phase_offset, + const LowFrequencyOscillator &oscillator, + bool key_on, + int channel_period, + int channel_octave, + const OperatorOverrides *overrides) { + state.attenuation.reset(); + update_adsr(state, oscillator, key_on, channel_period, channel_octave, overrides); + + // Per the documentation: + // + // Delta phase = ( [desired freq] * 2^19 / [input clock / 72] ) / 2 ^ (b - 1) + // + // After experimentation, I think this gives rate calculation as formulated below. + + // This encodes the MUL -> multiple table given on page 12, + // multiplied by two. + constexpr int multipliers[] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 + }; + const int top_freq = channel_period >> 7; + assert(top_freq < 8); + constexpr int vibrato_shifts[8] = {3, 1, 0, 1, 3, 1, 0, 1}; + constexpr int vibrato_signs[2] = {1, -1}; + const int vibrato = (top_freq >> vibrato_shifts[oscillator.vibrato]) * vibrato_signs[oscillator.vibrato >> 2] * int(apply_vibrato_); + + // Update the raw phase. + state.raw_phase_ += multipliers[frequency_multiple_] * (channel_period + vibrato) << channel_octave; + + // Hence calculate phase. + constexpr int waveforms[4][4] = { + {1023, 1023, 1023, 1023}, // Sine: don't mask in any quadrant. + {511, 511, 0, 0}, // Half sine: keep the first half intact, lock to 0 in the second half. + {511, 511, 511, 511}, // AbsSine: endlessly repeat the first half of the sine wave. + {255, 0, 255, 0}, // PulseSine: act as if the first quadrant is in the first and third; lock the other two to 0. + }; + const int scaled_phase_offset = phase_offset ? power_two(phase_offset->attenuation, 11) : 0; + const int phase = (state.raw_phase_ + scaled_phase_offset) >> 11; + state.attenuation += negative_log_sin(phase & waveforms[int(waveform_)][(phase >> 8) & 3]); + // Calculate key-level scaling. Table is as per p14 of the YM3812 application manual, // converted into a fixed-point scheme. Compare with https://www.smspower.org/Development/RE12 @@ -214,22 +226,22 @@ void Operator::update( }; assert((channel_period >> 6) < 16); assert(channel_octave < 8); - state.attenuation.log += (key_level_scales[channel_octave][channel_period >> 6] >> key_level_scale_shifts[key_level_scaling_]) << 7; + state.attenuation += (key_level_scales[channel_octave][channel_period >> 6] >> key_level_scale_shifts[key_level_scaling_]) << 7; // Combine the ADSR attenuation and overall channel attenuation. if(overrides) { // Overrides here represent per-channel volume on an OPLL. The bits are defined to represent // attenuations of 24db to 3db; the main envelope generator is stated to have a resolution of // 0.325db (which I've assumed is supposed to say 0.375db). - state.attenuation.log += (state.adsr_attenuation_ << 3) + (overrides->attenuation << 7); + state.attenuation += (state.adsr_attenuation_ << 3) + (overrides->attenuation << 7); } else { // Overrides here represent per-channel volume on an OPLL. The bits are defined to represent // attenuations of 24db to 0.75db. - state.attenuation.log += (state.adsr_attenuation_ << 3) + (attenuation_ << 5); + state.attenuation += (state.adsr_attenuation_ << 3) + (attenuation_ << 5); } // Add optional tremolo. - state.attenuation.log += int(apply_amplitude_modulation_) * oscillator.tremolo << 4; + state.attenuation += int(apply_amplitude_modulation_) * oscillator.tremolo << 4; } // TODO: both the tremolo and ADSR envelopes should be half-resolution on an OPLL. diff --git a/Components/OPL2/Implementation/Operator.hpp b/Components/OPL2/Implementation/Operator.hpp index 97eb3c5c8..48d810dae 100644 --- a/Components/OPL2/Implementation/Operator.hpp +++ b/Components/OPL2/Implementation/Operator.hpp @@ -135,6 +135,14 @@ class Operator { enum class Waveform { Sine, HalfSine, AbsSine, PulseSine } waveform_ = Waveform::Sine; + +void update_adsr( + OperatorState &state, + const LowFrequencyOscillator &oscillator, + bool key_on, + int channel_period, + int channel_octave, + const OperatorOverrides *overrides); }; } diff --git a/Components/OPL2/Implementation/Tables.hpp b/Components/OPL2/Implementation/Tables.hpp index 8953cfca5..f772f0a9a 100644 --- a/Components/OPL2/Implementation/Tables.hpp +++ b/Components/OPL2/Implementation/Tables.hpp @@ -28,6 +28,22 @@ namespace OPL { struct LogSign { int log; int sign; + + void reset() { + log = 0; + sign = 1; + } + + LogSign &operator +=(int attenuation) { + log += attenuation; + return *this; + } + + LogSign &operator +=(LogSign log_sign) { + log += log_sign.log; + sign *= log_sign.sign; + return *this; + } }; /*!