diff --git a/Components/OPx/Implementation/EnvelopeGenerator.hpp b/Components/OPx/Implementation/EnvelopeGenerator.hpp index e4c1d7ff8..3d7b80c19 100644 --- a/Components/OPx/Implementation/EnvelopeGenerator.hpp +++ b/Components/OPx/Implementation/EnvelopeGenerator.hpp @@ -84,7 +84,8 @@ template class EnvelopeGenerator @returns The current attenuation from this envelope generator. This is independent of the envelope precision. */ int attenuation() const { - return (attenuation_ + tremolo_) << 3; + // TODO: if this envelope is fully released, should tremolo still be able to vocalise it? + return (attenuation_ << 3) + tremolo_; } /*! diff --git a/Components/OPx/Implementation/PhaseGenerator.hpp b/Components/OPx/Implementation/PhaseGenerator.hpp index 851b4073e..89ca27ebd 100644 --- a/Components/OPx/Implementation/PhaseGenerator.hpp +++ b/Components/OPx/Implementation/PhaseGenerator.hpp @@ -26,7 +26,7 @@ template class PhaseGenerator { Advances the phase generator a single step, given the current state of the low-frequency oscillator, @c oscillator. */ void update(const LowFrequencyOscillator &oscillator) { - constexpr int vibrato_shifts[8] = {3, 1, 0, 1, 3, 1, 0, 1}; + constexpr int vibrato_shifts[4] = {3, 1, 0, 1}; constexpr int vibrato_signs[2] = {1, -1}; // Get just the top three bits of the period_. @@ -35,10 +35,10 @@ template class PhaseGenerator { // Cacluaute applicable vibrato as a function of (i) the top three bits of the // oscillator period; (ii) the current low-frequency oscillator vibrato output; and // (iii) whether vibrato is enabled. - const int vibrato = (top_freq >> vibrato_shifts[oscillator.vibrato]) * vibrato_signs[oscillator.vibrato >> 2] * enable_vibrato_; + const int vibrato = (top_freq >> vibrato_shifts[oscillator.vibrato & 3]) * vibrato_signs[oscillator.vibrato >> 2] * enable_vibrato_; // Apply phase update with vibrato from the low-frequency oscillator. - phase_ += multiple_ * (period_ + vibrato) << octave_; + phase_ += (multiple_ * ((period_ << 1) + vibrato) << octave_) >> 1; } diff --git a/Components/OPx/OPLL.cpp b/Components/OPx/OPLL.cpp index 886293e63..b5abd1460 100644 --- a/Components/OPx/OPLL.cpp +++ b/Components/OPx/OPLL.cpp @@ -28,6 +28,14 @@ OPLL::OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, int audio_divider, phase_generators_[6 + 9].reset(); }); + // Set the other drums to damp, but only the TomTom to affect phase. + rhythm_envelope_generators_[TomTom].set_should_damp([this] { + phase_generators_[8 + 9].reset(); + }); + rhythm_envelope_generators_[Snare].set_should_damp({}); + rhythm_envelope_generators_[Cymbal].set_should_damp({}); + rhythm_envelope_generators_[HighHat].set_should_damp({}); + // Crib the proper rhythm envelope generator settings by installing // the rhythm instruments and copying them over. rhythm_mode_enabled_ = true; @@ -45,7 +53,7 @@ OPLL::OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, int audio_divider, // Return to ordinary default mode. rhythm_mode_enabled_ = false; - // Set up proper damping management. + // Set up damping for the melodic channels. for(int c = 0; c < 9; ++c) { envelope_generators_[c].set_should_damp([this, c] { // Propagate attack mode to the modulator, and reset both phases. @@ -372,10 +380,12 @@ void OPLL::update_all_channels() { // TODO: verify attenuation scales pervasively below. +#define ATTENUATION(x) ((x) << 7) + int OPLL::melodic_output(int channel) { // The modulator always updates after the carrier, oddly enough. So calculate actual output first, based on the modulator's last value. auto carrier = WaveformGenerator::wave(channels_[channel].carrier_waveform, phase_generators_[channel].scaled_phase(), channels_[channel].modulator_output); - carrier += envelope_generators_[channel].attenuation() + (channels_[channel].attenuation << 7) + key_level_scalers_[channel].attenuation(); + carrier += envelope_generators_[channel].attenuation() + ATTENUATION(channels_[channel].attenuation) + key_level_scalers_[channel].attenuation(); // Get the modulator's new value. auto modulation = WaveformGenerator::wave(channels_[channel].modulator_waveform, phase_generators_[channel + 9].phase()); @@ -394,7 +404,7 @@ int OPLL::bass_drum() { modulation += rhythm_envelope_generators_[RhythmIndices::BassModulator].attenuation(); auto carrier = WaveformGenerator::wave(Waveform::Sine, phase_generators_[6].scaled_phase(), modulation); - carrier += rhythm_envelope_generators_[RhythmIndices::BassCarrier].attenuation() + (channels_[6].attenuation << 7); + carrier += rhythm_envelope_generators_[RhythmIndices::BassCarrier].attenuation() + ATTENUATION(channels_[6].attenuation); return carrier.level(); } @@ -402,7 +412,7 @@ int OPLL::tom_tom() { // Use modulator 8 and the 'instrument' selection for channel 8 as an attenuation. auto tom_tom = WaveformGenerator::wave(Waveform::Sine, phase_generators_[8 + 9].phase()); tom_tom += rhythm_envelope_generators_[RhythmIndices::TomTom].attenuation(); - tom_tom += channels_[8].instrument << 7; + tom_tom += ATTENUATION(channels_[8].instrument); return tom_tom.level(); } @@ -410,7 +420,7 @@ int OPLL::snare_drum() { // Use modulator 7 and the carrier attenuation level for channel 7. LogSign snare = WaveformGenerator::snare(oscillator_, phase_generators_[7 + 9].phase()); snare += rhythm_envelope_generators_[RhythmIndices::Snare].attenuation(); - snare += channels_[7].attenuation << 7; + snare += ATTENUATION(channels_[7].attenuation); return snare.level(); } @@ -418,7 +428,7 @@ int OPLL::cymbal() { // Use modulator 7, carrier 8 and the attenuation level for channel 8. LogSign cymbal = WaveformGenerator::cymbal(phase_generators_[8].phase(), phase_generators_[7 + 9].phase()); cymbal += rhythm_envelope_generators_[RhythmIndices::Cymbal].attenuation(); - cymbal += channels_[8].attenuation << 7; + cymbal += ATTENUATION(channels_[8].attenuation); return cymbal.level(); } @@ -426,6 +436,8 @@ int OPLL::high_hat() { // Use modulator 7, carrier 8 a and the 'instrument' selection for channel 7 as an attenuation. LogSign high_hat = WaveformGenerator::high_hat(oscillator_, phase_generators_[8].phase(), phase_generators_[7 + 9].phase()); high_hat += rhythm_envelope_generators_[RhythmIndices::HighHat].attenuation(); - high_hat += channels_[7].instrument << 7; + high_hat += ATTENUATION(channels_[7].instrument); return high_hat.level(); } + +#undef ATTENUATION diff --git a/Outputs/Speaker/Implementation/SampleSource.hpp b/Outputs/Speaker/Implementation/SampleSource.hpp index c0742e0e5..17a2e6a5d 100644 --- a/Outputs/Speaker/Implementation/SampleSource.hpp +++ b/Outputs/Speaker/Implementation/SampleSource.hpp @@ -63,6 +63,10 @@ class SampleSource { a certain proportion of the allocated volume range. This commonly happens in sample sources that use a time-multiplexed sound output — for example, if one were to output only every other sample then it would return 0.5. + + This is permitted to vary over time but there is no contract as to when it will be + used by a speaker. If it varies, it should do so very infrequently and only to + represent changes in hardware configuration. */ double get_average_output_peak() const { return 1.0; } };