diff --git a/Components/OPL2/Implementation/EnvelopeGenerator.hpp b/Components/OPL2/Implementation/EnvelopeGenerator.hpp index 42f54dfe4..0f1566481 100644 --- a/Components/OPL2/Implementation/EnvelopeGenerator.hpp +++ b/Components/OPL2/Implementation/EnvelopeGenerator.hpp @@ -29,17 +29,60 @@ namespace OPL { this is largely for logical conformity with the phase generator that necessarily has to apply vibrato. */ -template class EnvelopeGenerator { +template class EnvelopeGenerator { public: /*! Advances the envelope generator a single step, given the current state of the low-frequency oscillator, @c oscillator. */ - void update(const LowFrequencyOscillator &oscillator); + void update(const LowFrequencyOscillator &oscillator) { + // Apply tremolo, which is fairly easy. + tremolo_ = tremolo_enable_ * oscillator.tremolo << 4; + + // Something something something... + const int key_scaling_rate = key_scale_rate_ >> key_scale_rate_shift_; + switch(phase_) { + case Phase::Damp: + update_decay(oscillator, 12); + if(attenuation_ == 511) { + will_attack_(); + phase_ = Phase::Attack; + } + break; + + case Phase::Attack: + update_attack(oscillator, attack_rate_ + key_scaling_rate); + + // Two possible terminating conditions: (i) the attack rate is 15; (ii) full volume has been reached. + if((attack_rate_ + key_scaling_rate) > 60 || attenuation_ <= 0) { + attenuation_ = 0; + phase_ = Phase::Decay; + } + break; + + case Phase::Decay: + update_decay(oscillator, decay_rate_ + key_scaling_rate); + if(attenuation_ >= sustain_level_) { + attenuation_ = sustain_level_; + phase_ = use_sustain_level_ ? Phase::Sustain : Phase::Release; + } + break; + + case Phase::Sustain: + // Nothing to do. + break; + + case Phase::Release: + update_decay(oscillator, release_rate_ + key_scaling_rate); + break; + } + } /*! @returns The current attenuation from this envelope generator. */ - int attenuation() const; + int attenuation() const { + return attenuation_ + tremolo_; + } /*! Enables or disables damping on this envelope generator. If damping is enabled then this envelope generator will @@ -48,63 +91,165 @@ template class EnvelopeGenerator { @param will_attack Supply a will_attack callback to enable damping mode; supply nullopt to disable damping mode. */ - void set_should_damp(const std::optional> &will_attack); + void set_should_damp(const std::optional> &will_attack) { + will_attack_ = will_attack; + } /*! Sets the current state of the key-on input. */ - void set_key_on(bool); + void set_key_on(bool key_on) { + // Do nothing if this is not a leading or trailing edge. + if(key_on == key_on_) return; + key_on_ = key_on; + + // Always transition to release upon a key off. + if(!key_on_) { + phase_ = Phase::Release; + return; + } + + // On key on: if this is an envelope generator with damping, and damping is required, + // schedule that. If damping is not required, announce a pending attack now and + // transition to attack. + if(will_attack_) { + if(attenuation_ != 511) { + phase_ = Phase::Damp; + return; + } + + will_attack_(); + } + phase_ = Phase::Attack; + } /*! Sets the attack rate, which should be in the range 0–15. */ - void set_attack_rate(int); + void set_attack_rate(int rate) { + attack_rate_ = rate << 2; + } /*! Sets the decay rate, which should be in the range 0–15. */ - void set_decay_rate(int); + void set_decay_rate(int rate) { + decay_rate_ = rate << 2; + } /*! Sets the release rate, which should be in the range 0–15. */ - void set_release_rate(int); + void set_release_rate(int rate) { + release_rate_ = rate << 2; + } /*! Sets the sustain level, which should be in the range 0–15. */ - void set_sustain_level(int); + void set_sustain_level(int level) { + sustain_level_ = level << 3; + // TODO: verify the shift level here. Especially re: precision. + } /*! Enables or disables use of the sustain level. If this is disabled, the envelope proceeds directly from decay to release. */ - void set_use_sustain_level(bool); + void set_use_sustain_level(bool use) { + use_sustain_level_ = use; + } /*! Enables or disables key-rate scaling. */ - void set_key_rate_scaling_enabled(bool enabled); + void set_key_scaling_rate_enabled(bool enabled) { + key_scale_rate_shift_ = int(enabled) * 2; + } /*! Enables or disables application of the low-frequency oscillator's tremolo. */ - void set_tremolo_enabled(bool enabled); + void set_tremolo_enabled(bool enabled) { + tremolo_enable_ = int(enabled); + } /*! Sets the current period associated with the channel that owns this envelope generator; this is used to select a key scaling rate if key-rate scaling is enabled. */ - void set_period(int period, int octave); + void set_period(int period, int octave) { + key_scale_rate_ = (octave << 1) | (period >> (period_precision - 1)); + } private: - enum class ADSRPhase { + enum class Phase { Attack, Decay, Sustain, Release, Damp - } adsr_phase_ = ADSRPhase::Attack; - int adsr_attenuation_ = 511; + } phase_ = Phase::Attack; + int attenuation_ = 511, tremolo_ = 0; bool key_on_ = false; std::optional> will_attack_; + + int key_scale_rate_ = 0; + int key_scale_rate_shift_ = 0; + + int tremolo_enable_ = 0; + + int attack_rate_ = 0; + int decay_rate_ = 0; + int release_rate_ = 0; + int sustain_level_ = 0; + bool use_sustain_level_ = false; + + void update_attack(const LowFrequencyOscillator &oscillator, int rate) { + // Rules: + // + // An attack rate of '13' has 32 samples in the attack phase; a rate of '12' has the same 32 steps, but spread out over 64 samples, etc. + // An attack rate of '14' uses a divide by four instead of two. + // 15 is instantaneous. + + if(rate >= 56) { + attenuation_ -= (attenuation_ >> 2) - 1; + } else { + const int sample_length = 1 << (14 - (rate >> 2)); // TODO: don't throw away KSR bits. + if(!(oscillator.counter & (sample_length - 1))) { + attenuation_ -= (attenuation_ >> 3) - 1; + } + } + } + + void update_decay(const LowFrequencyOscillator &oscillator, int rate) { + // Rules: + // + // (relative to a 511 scale) + // + // A rate of 0 is no decay at all. + // A rate of 1 means increase 4 per cycle. + // A rate of 2 means increase 2 per cycle. + // A rate of 3 means increase 1 per cycle. + // A rate of 4 means increase 1 every other cycle. + // A rate of 5 means increase once every fourth cycle. + // etc. + // eighth, sixteenth, 32nd, 64th, 128th, 256th, 512th, 1024th, 2048th, 4096th, 8192th + + if(rate) { + // TODO: don't throw away low two bits of the rate. + switch(rate >> 2) { + case 1: attenuation_ += 32; break; + case 2: attenuation_ += 16; break; + default: { + const int sample_length = 1 << ((rate >> 2) - 4); + if(!(oscillator.counter & (sample_length - 1))) { + attenuation_ += 8; + } + } break; + } + + // Clamp to the proper range. + attenuation_ = std::min(attenuation_, 511); + } + } }; } diff --git a/Components/OPL2/Implementation/KeyLevelScaler.hpp b/Components/OPL2/Implementation/KeyLevelScaler.hpp new file mode 100644 index 000000000..bbd5b7531 --- /dev/null +++ b/Components/OPL2/Implementation/KeyLevelScaler.hpp @@ -0,0 +1,58 @@ +// +// KeyLevelScaler.hpp +// Clock Signal +// +// Created by Thomas Harte on 02/05/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef KeyLevelScaler_h +#define KeyLevelScaler_h + +namespace Yamaha { +namespace OPL { + +template class KeyLevelScaler { + public: + + /*! + Sets the current period associated with the channel that owns this envelope generator; + this is used to select a key scaling rate if key-rate scaling is enabled. + */ + void set_period(int period, int octave) { + constexpr int key_level_scales[16] = {0, 48, 64, 74, 80, 86, 90, 94, 96, 100, 102, 104, 106, 108, 110, 112}; + constexpr int masks[2] = {~0, 0}; + + // A two's complement assumption is embedded below; the use of masks relies + // on the sign bit to clamp to zero. + level_ = key_level_scales[period >> (frequency_precision - 4)]; + level_ -= 16 * (octave ^ 7); + level_ &= masks[(key_scale_rate_ >> ((sizeof(int) * 8) - 1)) & 1]; + } + + /*! + Enables or disables key-rate scaling. + */ + void set_key_scaling_level(int level) { + // '7' is just a number large enough to render all possible scaling coefficients as 0. + constexpr int key_level_scale_shifts[4] = {7, 1, 2, 0}; + shift_ = key_level_scale_shifts[level]; + } + + /*! + @returns The current attenuation level due to key-level scaling. + */ + int attenuation() const { + return level_ >> shift_; + } + + private: + int level_ = 0; + int shift_ = 0; +}; + + +} +} + +#endif /* KeyLevelScaler_h */ diff --git a/Components/OPL2/Implementation/PhaseGenerator.hpp b/Components/OPL2/Implementation/PhaseGenerator.hpp index 9f85689a2..35ba2108e 100644 --- a/Components/OPL2/Implementation/PhaseGenerator.hpp +++ b/Components/OPL2/Implementation/PhaseGenerator.hpp @@ -38,7 +38,7 @@ template class PhaseGenerator { const int vibrato = (top_freq >> vibrato_shifts[oscillator.vibrato]) * vibrato_signs[oscillator.vibrato >> 2] * enable_vibrato_; // Apply phase update with vibrato from the low-frequency oscillator. - raw_phase_ += multiple_ * (period_ + vibrato) << octave_; + phase_ += multiple_ * (period_ + vibrato) << octave_; } @@ -48,7 +48,7 @@ template class PhaseGenerator { int phase() const { // My table if multipliers is multiplied by two, so shift by one more // than the stated precision. - return raw_phase_ >> precision_shift; + return phase_ >> precision_shift; } /*! @@ -85,21 +85,22 @@ template class PhaseGenerator { enable_vibrato_ = int(enabled); } - enum class Waveform { - Sine, HalfSine, AbsSine, PulseSine - }; + /*! + Resets the current phase. + */ + void reset() { + phase_ = 0; + } private: static constexpr int precision_shift = 1 + precision; - int raw_phase_ = 0; + int phase_ = 0; int multiple_ = 0; int period_ = 0; int octave_ = 0; int enable_vibrato_ = 0; - - Waveform waveform_ = Waveform::Sine; }; } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index e81862b03..3a3c52701 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1153,6 +1153,7 @@ 4B61908F24526E640013F202 /* LowFrequencyOscillator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LowFrequencyOscillator.hpp; sourceTree = ""; }; 4B619092245BC1000013F202 /* PhaseGenerator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PhaseGenerator.hpp; sourceTree = ""; }; 4B619093245CD63E0013F202 /* EnvelopeGenerator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EnvelopeGenerator.hpp; sourceTree = ""; }; + 4B619094245E73B90013F202 /* KeyLevelScaler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyLevelScaler.hpp; sourceTree = ""; }; 4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DisplayMetrics.cpp; path = ../../Outputs/DisplayMetrics.cpp; sourceTree = ""; }; 4B622AE4222E0AD5008B59F2 /* DisplayMetrics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DisplayMetrics.hpp; path = ../../Outputs/DisplayMetrics.hpp; sourceTree = ""; }; 4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = ""; }; @@ -3536,6 +3537,7 @@ 4B61908F24526E640013F202 /* LowFrequencyOscillator.hpp */, 4B619092245BC1000013F202 /* PhaseGenerator.hpp */, 4B619093245CD63E0013F202 /* EnvelopeGenerator.hpp */, + 4B619094245E73B90013F202 /* KeyLevelScaler.hpp */, ); path = Implementation; sourceTree = "";