From ee10fe3d2c57cf70a1136c60870509e7df3ad41a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 26 Apr 2020 00:18:09 -0400 Subject: [PATCH] Fully separates updates and outputs in operators; takes a shot at the snare. --- Components/OPL2/Implementation/Channel.cpp | 21 ++-- Components/OPL2/Implementation/Channel.hpp | 4 +- Components/OPL2/Implementation/Operator.cpp | 133 ++++++++++++-------- Components/OPL2/Implementation/Operator.hpp | 27 ++-- Components/OPL2/Implementation/Tables.hpp | 7 ++ Components/OPL2/OPL2.cpp | 14 +-- 6 files changed, 126 insertions(+), 80 deletions(-) diff --git a/Components/OPL2/Implementation/Channel.cpp b/Components/OPL2/Implementation/Channel.cpp index 6ec3c6a80..6ca25e4b6 100644 --- a/Components/OPL2/Implementation/Channel.cpp +++ b/Components/OPL2/Implementation/Channel.cpp @@ -34,22 +34,27 @@ void Channel::set_feedback_mode(uint8_t value) { } int Channel::update_melodic(const LowFrequencyOscillator &oscillator, Operator *modulator, Operator *carrier, bool force_key_on, OperatorOverrides *modulator_overrides, OperatorOverrides *carrier_overrides) { + modulator->update(modulator_state_, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, modulator_overrides); + carrier->update(carrier_state_, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, carrier_overrides); + if(use_fm_synthesis_) { // Get modulator level, use that as a phase-adjusting input to the carrier and then return the carrier level. - modulator->update(modulator_state_, nullptr, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, modulator_overrides); - carrier->update(carrier_state_, &modulator_state_, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, carrier_overrides); - return carrier_state_.level(); + const LogSign modulator_output = modulator->melodic_output(modulator_state_); + return carrier->melodic_output(carrier_state_, &modulator_output).level(); } else { // Get modulator and carrier levels separately, return their sum. - modulator->update(modulator_state_, nullptr, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, modulator_overrides); - carrier->update(carrier_state_, nullptr, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, carrier_overrides); - return (modulator_state_.level() + carrier_state_.level()) >> 1; + return (carrier->melodic_output(carrier_state_).level() + modulator->melodic_output(carrier_state_).level()) >> 1; } } int Channel::update_tom_tom(const LowFrequencyOscillator &oscillator, Operator *modulator, bool force_key_on, OperatorOverrides *modulator_overrides) { - modulator->update(modulator_state_, nullptr, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, modulator_overrides); - return modulator_state_.level(); + modulator->update(modulator_state_, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, modulator_overrides); + return modulator->melodic_output(modulator_state_).level(); +} + +int Channel::update_snare(const LowFrequencyOscillator &oscillator, Operator *carrier, bool force_key_on, OperatorOverrides *carrier_overrides) { + carrier->update(carrier_state_, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, carrier_overrides); + return carrier->snare_output(modulator_state_).level(); } bool Channel::is_audible(Operator *carrier, OperatorOverrides *carrier_overrides) { diff --git a/Components/OPL2/Implementation/Channel.hpp b/Components/OPL2/Implementation/Channel.hpp index e0a1ea0a0..f69cd889b 100644 --- a/Components/OPL2/Implementation/Channel.hpp +++ b/Components/OPL2/Implementation/Channel.hpp @@ -46,10 +46,10 @@ class Channel { int update_tom_tom(const LowFrequencyOscillator &oscillator, Operator *modulator, bool force_key_on, OperatorOverrides *modulator_overrides = nullptr); /// Updates this channel, using the carrier to produce a snare drum and the modulator to produce a tom tom. - int update_snare_tom_tom(const LowFrequencyOscillator &oscillator, Operator *modulator, Operator *carrier, OperatorOverrides *modulator_overrides = nullptr, OperatorOverrides *carrier_overrides = nullptr); + int update_snare(const LowFrequencyOscillator &oscillator, Operator *carrier, bool force_key_on, OperatorOverrides *carrier_overrides = nullptr); /// Updates this channel, using the carrier to produce a cymbal and the modulator to produce a high-hat. - int update_symbal_high_hat(const LowFrequencyOscillator &oscillator, Operator *modulator, Operator *carrier, OperatorOverrides *modulator_overrides = nullptr, OperatorOverrides *carrier_overrides = nullptr); + int update_cymbal_high_hat(const LowFrequencyOscillator &oscillator, Operator *modulator, Operator *carrier, OperatorOverrides *modulator_overrides = nullptr, OperatorOverrides *carrier_overrides = nullptr); /// @returns @c true if this channel is currently producing any audio; @c false otherwise; bool is_audible(Operator *carrier, OperatorOverrides *carrier_overrides = nullptr); diff --git a/Components/OPL2/Implementation/Operator.cpp b/Components/OPL2/Implementation/Operator.cpp index 19924c05c..376c153c1 100644 --- a/Components/OPL2/Implementation/Operator.cpp +++ b/Components/OPL2/Implementation/Operator.cpp @@ -15,10 +15,6 @@ using namespace Yamaha::OPL; // MARK: - Setters -int OperatorState::level() { - return power_two(attenuation); -} - void Operator::set_attack_decay(uint8_t value) { attack_rate_ = (value & 0xf0) >> 2; decay_rate_ = (value & 0x0f) << 2; @@ -167,46 +163,6 @@ void Operator::update_adsr( ++state.attack_time_; } -void Operator::apply_key_level_scaling(OperatorState &state, int channel_period, int channel_octave) { - // 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 - // and apologies for the highly ad hoc indentation. - constexpr int key_level_scale_shifts[4] = {7, 1, 2, 0}; // '7' is just a number large enough to render all the numbers below as 0. - constexpr int key_level_scales[8][16] = { -#define _ 0 - // 6 db attenuations. - {_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _}, - {_, _, _, _, _, _, _, _, _, 4, 6, 8, 10, 12, 14, 16}, - {_, _, _, _, _, 6, 10, 14, 16, 20, 22, 24, 26, 28, 30, 32}, - {_, _, _, 10, 16, 22, 26, 30, 32, 36, 38, 40, 42, 44, 46, 48}, - {_, _, 16, 26, 32, 38, 42, 46, 48, 52, 54, 56, 58, 60, 62, 64}, - {_, 16, 32, 42, 48, 54, 58, 62, 64, 68, 70, 72, 74, 76, 78, 80}, - {_, 32, 48, 58, 64, 70, 74, 78, 80, 84, 86, 88, 90, 92, 94, 96}, - {_, 48, 64, 74, 80, 86, 90, 94, 96, 100, 102, 104, 106, 108, 110, 112}, -#undef _ - }; - assert((channel_period >> 6) < 16); - assert(channel_octave < 8); - state.attenuation += (key_level_scales[channel_octave][channel_period >> 6] >> key_level_scale_shifts[key_level_scaling_]) << 7; -} - -void Operator::apply_attenuation_adsr(OperatorState &state, const LowFrequencyOscillator &oscillator, const OperatorOverrides *overrides) { - // 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 += (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 += (state.adsr_attenuation_ << 3) + (attenuation_ << 5); - } - - // Add optional tremolo. - state.attenuation += int(apply_amplitude_modulation_) * oscillator.tremolo << 4; -} - void Operator::update_phase(OperatorState &state, const LowFrequencyOscillator &oscillator, int channel_period, int channel_octave) { // Per the documentation: // @@ -229,9 +185,50 @@ void Operator::update_phase(OperatorState &state, const LowFrequencyOscillator & state.raw_phase_ += multipliers[frequency_multiple_] * (channel_period + vibrato) << channel_octave; } +int Operator::key_level_scaling(OperatorState &state, int channel_period, int channel_octave) { + // 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 + // and apologies for the highly ad hoc indentation. + constexpr int key_level_scale_shifts[4] = {7, 1, 2, 0}; // '7' is just a number large enough to render all the numbers below as 0. + constexpr int key_level_scales[8][16] = { +#define _ 0 + // 6 db attenuations. + {_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _}, + {_, _, _, _, _, _, _, _, _, 4, 6, 8, 10, 12, 14, 16}, + {_, _, _, _, _, 6, 10, 14, 16, 20, 22, 24, 26, 28, 30, 32}, + {_, _, _, 10, 16, 22, 26, 30, 32, 36, 38, 40, 42, 44, 46, 48}, + {_, _, 16, 26, 32, 38, 42, 46, 48, 52, 54, 56, 58, 60, 62, 64}, + {_, 16, 32, 42, 48, 54, 58, 62, 64, 68, 70, 72, 74, 76, 78, 80}, + {_, 32, 48, 58, 64, 70, 74, 78, 80, 84, 86, 88, 90, 92, 94, 96}, + {_, 48, 64, 74, 80, 86, 90, 94, 96, 100, 102, 104, 106, 108, 110, 112}, +#undef _ + }; + assert((channel_period >> 6) < 16); + assert(channel_octave < 8); + return (key_level_scales[channel_octave][channel_period >> 6] >> key_level_scale_shifts[key_level_scaling_]) << 7; +} + +int Operator::attenuation_adsr(OperatorState &state, const LowFrequencyOscillator &oscillator, const OperatorOverrides *overrides) { + int attenuation = 0; + + // 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). + 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. + attenuation += (state.adsr_attenuation_ << 3) + (attenuation_ << 5); + } + + // Add optional tremolo. + return attenuation + (int(apply_amplitude_modulation_) * oscillator.tremolo << 4); +} + void Operator::update( OperatorState &state, - const OperatorState *phase_offset, const LowFrequencyOscillator &oscillator, bool key_on, int channel_period, @@ -239,7 +236,16 @@ void Operator::update( const OperatorOverrides *overrides) { update_adsr(state, oscillator, key_on, channel_period, channel_octave, overrides); update_phase(state, oscillator, channel_period, channel_octave); + state.key_level_scaling_ = key_level_scaling(state, channel_period, channel_octave); + state.channel_adsr_attenuation_ = attenuation_adsr(state, oscillator, overrides); + state.lfsr_ = oscillator.lfsr; +} +// TODO: both the tremolo and ADSR envelopes should be half-resolution on an OPLL. + +// MARK: - Output Generators. + +LogSign Operator::melodic_output(OperatorState &state, const LogSign *phase_offset) { // Calculate raw attenuation level. constexpr int waveforms[4][4] = { {1023, 1023, 1023, 1023}, // Sine: don't mask in any quadrant. @@ -247,12 +253,39 @@ void Operator::update( {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 scaled_phase_offset = phase_offset ? phase_offset->level(11) : 0; const int phase = (state.raw_phase_ + scaled_phase_offset) >> 11; - state.attenuation = negative_log_sin(phase & waveforms[int(waveform_)][(phase >> 8) & 3]); - apply_key_level_scaling(state, channel_period, channel_octave); - apply_attenuation_adsr(state, oscillator, overrides); + LogSign result = negative_log_sin(phase & waveforms[int(waveform_)][(phase >> 8) & 3]); + result += state.key_level_scaling_; + result += state.channel_adsr_attenuation_; + return result; } -// TODO: both the tremolo and ADSR envelopes should be half-resolution on an OPLL. +LogSign Operator::snare_output(OperatorState &state) { + LogSign result; + + // If noise is 0, output is positive. + // If noise is 1, output is negative. + // If (noise ^ sign) is 0, output is 0. Otherwise it is max. +// const int angle = ((state.lfsr_ << 10) ^ (state.raw_phase_ >> 12)) & 0x100; +// +// result = negative_log_sin((state.raw_phase_ >> 11) &; +// constexpr int masks[] = {~0, 0}; +// result += masks[state.lfsr_ + + if((state.raw_phase_ >> 11) & 0x200) { + // Result is -max if LFSR is 0, otherwise -0. + result = negative_log_sin(1024 + ((state.lfsr_^1) << 8)); + } else { + // Result is +max if LFSR is 1, otherwise +0. + result = negative_log_sin(state.lfsr_ << 8); + } + + +// printf("%d %d: %d/%d\n", state.lfsr_, (state.raw_phase_ >> 11) & 1023, result.log, result.sign); + + result += state.key_level_scaling_; + result += state.channel_adsr_attenuation_; + return result; +} diff --git a/Components/OPL2/Implementation/Operator.hpp b/Components/OPL2/Implementation/Operator.hpp index d0a627b83..23bbde677 100644 --- a/Components/OPL2/Implementation/Operator.hpp +++ b/Components/OPL2/Implementation/Operator.hpp @@ -17,25 +17,24 @@ namespace Yamaha { namespace OPL { /*! - Describes the ephemeral state of an operator. + Opaquely describes the ephemeral state of an operator. */ struct OperatorState { - public: - /// @returns The linear output level for the operator with this state.. - int level(); - private: - LogSign attenuation; - int raw_phase_ = 0; + friend class Operator; + int raw_phase_ = 0; enum class ADSRPhase { Attack, Decay, Sustain, Release } adsr_phase_ = ADSRPhase::Attack; - int attack_time_ = 0; int adsr_attenuation_ = 511; - bool last_key_on_ = false; + int attack_time_ = 0; - friend class Operator; + int key_level_scaling_; + int channel_adsr_attenuation_; + int lfsr_; + + bool last_key_on_ = false; }; /*! @@ -86,7 +85,6 @@ class Operator { /// Provides one clock tick to the operator, along with the relevant parameters of its channel. void update( OperatorState &state, - const OperatorState *phase_offset, const LowFrequencyOscillator &oscillator, bool key_on, int channel_period, @@ -96,6 +94,9 @@ class Operator { /// @returns @c true if this channel currently has a non-zero output; @c false otherwise. bool is_audible(OperatorState &state, OperatorOverrides *overrides = nullptr); + LogSign melodic_output(OperatorState &state, const LogSign *phase_offset = nullptr); + LogSign snare_output(OperatorState &state); + private: /// If true then an amplitude modulation of "3.7Hz" is applied, /// with a depth "determined by the AM-DEPTH of the BD register"? @@ -149,10 +150,10 @@ class Operator { void update_phase(OperatorState &state, const LowFrequencyOscillator &oscillator, int channel_period, int channel_octave); /// Adds key-level scaling to the current output state. - void apply_key_level_scaling(OperatorState &state, int channel_period, int channel_octave); + int key_level_scaling(OperatorState &state, int channel_period, int channel_octave); /// Adds ADSR and general channel attenuations to the output state. - void apply_attenuation_adsr(OperatorState &state, const LowFrequencyOscillator &oscillator, const OperatorOverrides *overrides); + int attenuation_adsr(OperatorState &state, const LowFrequencyOscillator &oscillator, const OperatorOverrides *overrides); }; diff --git a/Components/OPL2/Implementation/Tables.hpp b/Components/OPL2/Implementation/Tables.hpp index f772f0a9a..d50f2c391 100644 --- a/Components/OPL2/Implementation/Tables.hpp +++ b/Components/OPL2/Implementation/Tables.hpp @@ -44,6 +44,8 @@ struct LogSign { sign *= log_sign.sign; return *this; } + + int level(int fractional = 0) const; }; /*! @@ -214,6 +216,11 @@ constexpr uint8_t percussion_patch_set[] = { 0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55, }; + +inline int LogSign::level(int fractional) const { + return power_two(*this, fractional); +} + } } diff --git a/Components/OPL2/OPL2.cpp b/Components/OPL2/OPL2.cpp index 7d13b3df6..335b59218 100644 --- a/Components/OPL2/OPL2.cpp +++ b/Components/OPL2/OPL2.cpp @@ -68,7 +68,8 @@ void OPLL::get_samples(std::size_t number_of_samples, std::int16_t *target) { while(number_of_samples--) { if(!audio_offset_) update_all_chanels(); - if(!(audio_offset_&3)) oscillator_.update_lfsr(); + if(!(audio_offset_&3)) + oscillator_.update_lfsr(); *target = int16_t(output_levels_[audio_offset_ / channel_output_period]); ++target; @@ -140,7 +141,7 @@ void OPLL::write_register(uint8_t address, uint8_t value) { // Register 0xe is a cut-down version of the OPLL's register 0xbd. if(address == 0xe) { depth_rhythm_control_ = value & 0x3f; -// if(depth_rhythm_control_ & 0x04) +// if(depth_rhythm_control_ & 0x08) // printf("%02x\n", depth_rhythm_control_); return; } @@ -225,8 +226,9 @@ void OPLL::update_all_chanels() { output_levels_[1] = output_levels_[14] = VOLUME(channels_[7].update_tom_tom(oscillator_, &operators_[34], depth_rhythm_control_ & 0x04)); - // TODO: snare. - output_levels_[6] = output_levels_[16] = 0; + // Use the carrier from channel 7 for the snare. + output_levels_[6] = output_levels_[16] = + VOLUME(channels_[7].update_snare(oscillator_, &operators_[35], depth_rhythm_control_ & 0x08)); // TODO: cymbal. output_levels_[7] = output_levels_[17] = 0; @@ -250,9 +252,7 @@ void OPLL::update_all_chanels() { // Test! // for(int c = 0; c < 18; ++c) { -//// if(c != 1 && c != 14 && c != 2 && c != 15) -//// output_levels_[c] = 0; -// if(c != 1 && c != 14) +// if(c != 6 && c != 16) // output_levels_[c] = 0; // }