From b8278cec0ec5eb6a62cff8d206d2c48924854b6c Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 11 Feb 2019 15:16:37 +0000 Subject: [PATCH] LinearSmoothedValue: Added some helpful member functions and deprecated setValue --- examples/Audio/MPEDemo.h | 12 +-- examples/BLOCKS/BlocksSynthDemo.h | 10 +-- examples/Plugins/SamplerPluginDemo.h | 16 ++-- .../utilities/juce_LinearSmoothedValue.h | 90 ++++++++++++------- .../juce_audio_basics/utilities/juce_Reverb.h | 10 +-- .../juce_dsp/frequency/juce_Convolution.cpp | 16 ++-- modules/juce_dsp/processors/juce_Bias.h | 2 +- modules/juce_dsp/processors/juce_Gain.h | 2 +- .../juce_dsp/processors/juce_LadderFilter.cpp | 4 +- .../juce_dsp/processors/juce_LadderFilter.h | 10 +-- modules/juce_dsp/processors/juce_Oscillator.h | 8 +- 11 files changed, 103 insertions(+), 77 deletions(-) diff --git a/examples/Audio/MPEDemo.h b/examples/Audio/MPEDemo.h index b7ef91e81d..30251011a9 100644 --- a/examples/Audio/MPEDemo.h +++ b/examples/Audio/MPEDemo.h @@ -722,9 +722,9 @@ public: jassert (currentlyPlayingNote.keyState == MPENote::keyDown || currentlyPlayingNote.keyState == MPENote::keyDownAndSustained); - level.setValue (currentlyPlayingNote.pressure.asUnsignedFloat()); - frequency.setValue (currentlyPlayingNote.getFrequencyInHertz()); - timbre.setValue (currentlyPlayingNote.timbre.asUnsignedFloat()); + level .setTargetValue (currentlyPlayingNote.pressure.asUnsignedFloat()); + frequency.setTargetValue (currentlyPlayingNote.getFrequencyInHertz()); + timbre .setTargetValue (currentlyPlayingNote.timbre.asUnsignedFloat()); phase = 0.0; auto cyclesPerSample = frequency.getNextValue() / currentSampleRate; @@ -756,17 +756,17 @@ public: void notePressureChanged() override { - level.setValue (currentlyPlayingNote.pressure.asUnsignedFloat()); + level.setTargetValue (currentlyPlayingNote.pressure.asUnsignedFloat()); } void notePitchbendChanged() override { - frequency.setValue (currentlyPlayingNote.getFrequencyInHertz()); + frequency.setTargetValue (currentlyPlayingNote.getFrequencyInHertz()); } void noteTimbreChanged() override { - timbre.setValue (currentlyPlayingNote.timbre.asUnsignedFloat()); + timbre.setTargetValue (currentlyPlayingNote.timbre.asUnsignedFloat()); } void noteKeyStateChanged() override {} diff --git a/examples/BLOCKS/BlocksSynthDemo.h b/examples/BLOCKS/BlocksSynthDemo.h index 0b91b4df32..b3abc67423 100644 --- a/examples/BLOCKS/BlocksSynthDemo.h +++ b/examples/BLOCKS/BlocksSynthDemo.h @@ -65,8 +65,8 @@ public: void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int) override { frequency = MidiMessage::getMidiNoteInHertz (midiNoteNumber); - phaseIncrement.setValue (((MathConstants::twoPi) * frequency) / sampleRate); - amplitude.setValue (velocity); + phaseIncrement.setTargetValue (((MathConstants::twoPi) * frequency) / sampleRate); + amplitude.setTargetValue (velocity); // Store the initial note and work out the maximum frequency deviations for pitch bend initialNote = midiNoteNumber; @@ -77,14 +77,14 @@ public: void stopNote (float, bool) override { clearCurrentNote(); - amplitude.setValue (0.0); + amplitude.setTargetValue (0.0); } void pitchWheelMoved (int newValue) override { // Change the phase increment based on pitch bend amount auto frequencyOffset = ((newValue > 0 ? maxFreq : minFreq) * (newValue / 127.0)); - phaseIncrement.setValue (((MathConstants::twoPi) * (frequency + frequencyOffset)) / sampleRate); + phaseIncrement.setTargetValue (((MathConstants::twoPi) * (frequency + frequencyOffset)) / sampleRate); } void controllerMoved (int, int) override {} @@ -92,7 +92,7 @@ public: void channelPressureChanged (int newChannelPressureValue) override { // Set the amplitude based on pressure value - amplitude.setValue (newChannelPressureValue / 127.0); + amplitude.setTargetValue (newChannelPressureValue / 127.0); } void renderNextBlock (AudioBuffer& outputBuffer, int startSample, int numSamples) override diff --git a/examples/Plugins/SamplerPluginDemo.h b/examples/Plugins/SamplerPluginDemo.h index ceca60b2e9..a4f07f9d78 100644 --- a/examples/Plugins/SamplerPluginDemo.h +++ b/examples/Plugins/SamplerPluginDemo.h @@ -250,12 +250,12 @@ public: jassert (currentlyPlayingNote.keyState == MPENote::keyDown || currentlyPlayingNote.keyState == MPENote::keyDownAndSustained); - level .setValue (currentlyPlayingNote.pressure.asUnsignedFloat()); - frequency.setValue (currentlyPlayingNote.getFrequencyInHertz()); + level .setTargetValue (currentlyPlayingNote.pressure.asUnsignedFloat()); + frequency.setTargetValue (currentlyPlayingNote.getFrequencyInHertz()); auto loopPoints = samplerSound->getLoopPointsInSeconds(); - loopBegin.setValue (loopPoints.getStart() * samplerSound->getSample()->getSampleRate()); - loopEnd .setValue (loopPoints.getEnd() * samplerSound->getSample()->getSampleRate()); + loopBegin.setTargetValue (loopPoints.getStart() * samplerSound->getSample()->getSampleRate()); + loopEnd .setTargetValue (loopPoints.getEnd() * samplerSound->getSample()->getSampleRate()); for (auto smoothed : { &level, &frequency, &loopBegin, &loopEnd }) smoothed->reset (currentSampleRate, smoothingLengthInSeconds); @@ -276,12 +276,12 @@ public: void notePressureChanged() override { - level.setValue (currentlyPlayingNote.pressure.asUnsignedFloat()); + level.setTargetValue (currentlyPlayingNote.pressure.asUnsignedFloat()); } void notePitchbendChanged() override { - frequency.setValue (currentlyPlayingNote.getFrequencyInHertz()); + frequency.setTargetValue (currentlyPlayingNote.getFrequencyInHertz()); } void noteTimbreChanged() override {} @@ -294,8 +294,8 @@ public: jassert (samplerSound->getSample() != nullptr); auto loopPoints = samplerSound->getLoopPointsInSeconds(); - loopBegin.setValue (loopPoints.getStart() * samplerSound->getSample()->getSampleRate()); - loopEnd .setValue (loopPoints.getEnd() * samplerSound->getSample()->getSampleRate()); + loopBegin.setTargetValue (loopPoints.getStart() * samplerSound->getSample()->getSampleRate()); + loopEnd .setTargetValue (loopPoints.getEnd() * samplerSound->getSample()->getSampleRate()); auto& data = samplerSound->getSample()->getBuffer(); diff --git a/modules/juce_audio_basics/utilities/juce_LinearSmoothedValue.h b/modules/juce_audio_basics/utilities/juce_LinearSmoothedValue.h index 3087b07976..e6f8806cfd 100644 --- a/modules/juce_audio_basics/utilities/juce_LinearSmoothedValue.h +++ b/modules/juce_audio_basics/utilities/juce_LinearSmoothedValue.h @@ -26,7 +26,7 @@ namespace juce //============================================================================== /** Utility class for linearly smoothed values like volume etc. that should - not change abruptly but as a linear ramp, to avoid audio glitches. + not change abruptly but as a linear ramp to avoid audio glitches. @tags{Audio} */ @@ -44,43 +44,50 @@ public: } //============================================================================== - /** Reset to a new sample rate and ramp length. - @param sampleRate The sampling rate - @param rampLengthInSeconds The duration of the ramp in seconds + /** Set a new sample rate and ramp length in seconds. + @param sampleRate The sampling rate + @param rampLengthInSeconds The duration of the ramp in seconds */ void reset (double sampleRate, double rampLengthInSeconds) noexcept { jassert (sampleRate > 0 && rampLengthInSeconds >= 0); - stepsToTarget = (int) std::floor (rampLengthInSeconds * sampleRate); - currentValue = target; - countdown = 0; + reset ((int) std::floor (rampLengthInSeconds * sampleRate)); } - //============================================================================== - /** Set a new target value. + /** Set a new ramp length directly in samples. + @param numSteps The number of samples over which the ramp should be active + */ + void reset (int numSteps) noexcept + { + stepsToTarget = numSteps; + setCurrentValueToTargetValue(); + } + /** Set the next value to ramp towards. @param newValue The new target value - @param force If true, the value will be set immediately, bypassing the ramp */ - void setValue (FloatType newValue, bool force = false) noexcept + void setTargetValue (FloatType newValue) noexcept { - if (force) + if (target == newValue) + return; + + target = newValue; + + if (stepsToTarget <= 0) { - target = currentValue = newValue; - countdown = 0; + setCurrentValueToTargetValue(); return; } - if (target != newValue) - { - target = newValue; - countdown = stepsToTarget; + countdown = stepsToTarget; + step = (target - currentValue) / static_cast (countdown); + } - if (countdown <= 0) - currentValue = target; - else - step = (target - currentValue) / (FloatType) countdown; - } + /** Sets the current value to the target value. */ + void setCurrentValueToTargetValue() noexcept + { + currentValue = target; + countdown = 0; } //============================================================================== @@ -89,7 +96,7 @@ public: */ FloatType getNextValue() noexcept { - if (countdown <= 0) + if (! isSmoothing()) return target; --countdown; @@ -98,16 +105,13 @@ public: } /** Returns true if the current value is currently being interpolated. */ - bool isSmoothing() const noexcept - { - return countdown > 0; - } + bool isSmoothing() const noexcept { return countdown > 0; } + + /** Returns the current value of the ramp. */ + FloatType getCurrentValue() const noexcept { return currentValue; } /** Returns the target value towards which the smoothed value is currently moving. */ - FloatType getTargetValue() const noexcept - { - return target; - } + FloatType getTargetValue() const noexcept { return target; } //============================================================================== /** Applies a linear smoothed gain to a stream of samples @@ -194,8 +198,7 @@ public: { if (numSamples >= countdown) { - currentValue = target; - countdown = 0; + setCurrentValueToTargetValue(); return target; } @@ -204,6 +207,25 @@ public: return currentValue; } + //============================================================================== + /** THIS FUNCTION IS DEPRECATED. + + Use `setTargetValue (float)` and `setCurrentValueToTargetValue()` instead: + + lsv.setValue (x, false); -> lsv.setTargetValue (x); + lsv.setValue (x, true); -> lsv.setTargetValue (x); lsv.setCurrentValueToTargetValue(); + + @param newValue The new target value + @param force If true, the value will be set immediately, bypassing the ramp + */ + JUCE_DEPRECATED_WITH_BODY (void setValue (FloatType newValue, bool force = false) noexcept, + { + setTargetValue (newValue); + + if (force) + setCurrentValueToTargetValue(); + }) + private: //============================================================================== FloatType currentValue = 0, target = 0, step = 0; diff --git a/modules/juce_audio_basics/utilities/juce_Reverb.h b/modules/juce_audio_basics/utilities/juce_Reverb.h index 6244351e9d..2dd94c5c6f 100644 --- a/modules/juce_audio_basics/utilities/juce_Reverb.h +++ b/modules/juce_audio_basics/utilities/juce_Reverb.h @@ -72,9 +72,9 @@ public: const float dryScaleFactor = 2.0f; const float wet = newParams.wetLevel * wetScaleFactor; - dryGain.setValue (newParams.dryLevel * dryScaleFactor); - wetGain1.setValue (0.5f * wet * (1.0f + newParams.width)); - wetGain2.setValue (0.5f * wet * (1.0f - newParams.width)); + dryGain.setTargetValue (newParams.dryLevel * dryScaleFactor); + wetGain1.setTargetValue (0.5f * wet * (1.0f + newParams.width)); + wetGain2.setTargetValue (0.5f * wet * (1.0f - newParams.width)); gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f; parameters = newParams; @@ -207,8 +207,8 @@ private: void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept { - damping.setValue (dampingToUse); - feedback.setValue (roomSizeToUse); + damping.setTargetValue (dampingToUse); + feedback.setTargetValue (roomSizeToUse); } //============================================================================== diff --git a/modules/juce_dsp/frequency/juce_Convolution.cpp b/modules/juce_dsp/frequency/juce_Convolution.cpp index 086d4cc246..434457904e 100644 --- a/modules/juce_dsp/frequency/juce_Convolution.cpp +++ b/modules/juce_dsp/frequency/juce_Convolution.cpp @@ -1006,13 +1006,13 @@ private: for (auto i = 0; i < 2; ++i) { - changeVolumes[i].setValue (1.0f); + changeVolumes[i].setTargetValue (1.0f); changeVolumes[i].reset (currentInfo.sampleRate, 0.05); - changeVolumes[i].setValue (0.0f); + changeVolumes[i].setTargetValue (0.0f); - changeVolumes[i + 2].setValue (0.0f); + changeVolumes[i + 2].setTargetValue (0.0f); changeVolumes[i + 2].reset (currentInfo.sampleRate, 0.05); - changeVolumes[i + 2].setValue (1.0f); + changeVolumes[i + 2].setTargetValue (1.0f); } @@ -1235,13 +1235,13 @@ void Convolution::processSamples (const AudioBlock& input, AudioBlock= static_cast (-1) && newBias <= static_cast (1)); - bias.setValue(newBias); + bias.setTargetValue (newBias); } //============================================================================== diff --git a/modules/juce_dsp/processors/juce_Gain.h b/modules/juce_dsp/processors/juce_Gain.h index db78578e7c..6950e52215 100644 --- a/modules/juce_dsp/processors/juce_Gain.h +++ b/modules/juce_dsp/processors/juce_Gain.h @@ -42,7 +42,7 @@ public: //============================================================================== /** Applies a new gain as a linear value. */ - void setGainLinear (FloatType newGain) noexcept { gain.setValue (newGain); } + void setGainLinear (FloatType newGain) noexcept { gain.setTargetValue (newGain); } /** Applies a new gain as a decibel value. */ void setGainDecibels (FloatType newGainDecibels) noexcept { setGainLinear (Decibels::decibelsToGain (newGainDecibels)); } diff --git a/modules/juce_dsp/processors/juce_LadderFilter.cpp b/modules/juce_dsp/processors/juce_LadderFilter.cpp index 6c1704dfb9..05c43e8e5c 100644 --- a/modules/juce_dsp/processors/juce_LadderFilter.cpp +++ b/modules/juce_dsp/processors/juce_LadderFilter.cpp @@ -78,8 +78,8 @@ void LadderFilter::reset() noexcept for (auto& s : state) s.fill (Type (0)); - cutoffTransformSmoother.setValue (cutoffTransformSmoother.getTargetValue(), true); - scaledResonanceSmoother.setValue (scaledResonanceSmoother.getTargetValue(), true); + cutoffTransformSmoother.setCurrentValueToTargetValue(); + scaledResonanceSmoother.setCurrentValueToTargetValue(); } //============================================================================== diff --git a/modules/juce_dsp/processors/juce_LadderFilter.h b/modules/juce_dsp/processors/juce_LadderFilter.h index 612917124e..a37382c9e9 100644 --- a/modules/juce_dsp/processors/juce_LadderFilter.h +++ b/modules/juce_dsp/processors/juce_LadderFilter.h @@ -118,10 +118,8 @@ private: std::vector> state; std::array A; - LinearSmoothedValue cutoffTransformSmoother; - LinearSmoothedValue scaledResonanceSmoother; - Type cutoffTransformValue; - Type scaledResonanceValue; + LinearSmoothedValue cutoffTransformSmoother, scaledResonanceSmoother; + Type cutoffTransformValue, scaledResonanceValue; LookupTableTransform saturationLUT { [] (Type x) { return std::tanh (x); }, Type (-5), Type (5), 128 }; @@ -136,8 +134,8 @@ private: //============================================================================== void setSampleRate (Type newValue) noexcept; void setNumChannels (size_t newValue) { state.resize (newValue); } - void updateCutoffFreq() noexcept { cutoffTransformSmoother.setValue (std::exp (cutoffFreqHz * cutoffFreqScaler)); } - void updateResonance() noexcept { scaledResonanceSmoother.setValue (jmap (resonance, Type (0.1), Type (1.0))); } + void updateCutoffFreq() noexcept { cutoffTransformSmoother.setTargetValue (std::exp (cutoffFreqHz * cutoffFreqScaler)); } + void updateResonance() noexcept { scaledResonanceSmoother.setTargetValue (jmap (resonance, Type (0.1), Type (1.0))); } }; } // namespace dsp diff --git a/modules/juce_dsp/processors/juce_Oscillator.h b/modules/juce_dsp/processors/juce_Oscillator.h index bb593dedf0..4048836f5e 100644 --- a/modules/juce_dsp/processors/juce_Oscillator.h +++ b/modules/juce_dsp/processors/juce_Oscillator.h @@ -82,7 +82,13 @@ public: //============================================================================== /** Sets the frequency of the oscillator. */ - void setFrequency (NumericType newFrequency, bool force = false) noexcept { frequency.setValue (newFrequency, force); } + void setFrequency (NumericType newFrequency, bool force = false) noexcept + { + frequency.setTargetValue (newFrequency); + + if (force) + frequency.setCurrentValueToTargetValue(); + } /** Returns the current frequency of the oscillator. */ NumericType getFrequency() const noexcept { return frequency.getTargetValue(); }