| @@ -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 {} | |||
| @@ -65,8 +65,8 @@ public: | |||
| void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int) override | |||
| { | |||
| frequency = MidiMessage::getMidiNoteInHertz (midiNoteNumber); | |||
| phaseIncrement.setValue (((MathConstants<double>::twoPi) * frequency) / sampleRate); | |||
| amplitude.setValue (velocity); | |||
| phaseIncrement.setTargetValue (((MathConstants<double>::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<double>::twoPi) * (frequency + frequencyOffset)) / sampleRate); | |||
| phaseIncrement.setTargetValue (((MathConstants<double>::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<float>& outputBuffer, int startSample, int numSamples) override | |||
| @@ -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(); | |||
| @@ -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<FloatType> (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; | |||
| @@ -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); | |||
| } | |||
| //============================================================================== | |||
| @@ -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<float>& input, AudioBlock<flo | |||
| for (size_t channel = 0; channel < numChannels; ++channel) | |||
| { | |||
| volumeDry[channel].setValue (isBypassed ? 0.0f : 1.0f); | |||
| volumeDry[channel].setTargetValue (isBypassed ? 0.0f : 1.0f); | |||
| volumeDry[channel].reset (sampleRate, 0.05); | |||
| volumeDry[channel].setValue (isBypassed ? 1.0f : 0.0f); | |||
| volumeDry[channel].setTargetValue (isBypassed ? 1.0f : 0.0f); | |||
| volumeWet[channel].setValue (isBypassed ? 1.0f : 0.0f); | |||
| volumeWet[channel].setTargetValue (isBypassed ? 1.0f : 0.0f); | |||
| volumeWet[channel].reset (sampleRate, 0.05); | |||
| volumeWet[channel].setValue (isBypassed ? 0.0f : 1.0f); | |||
| volumeWet[channel].setTargetValue (isBypassed ? 0.0f : 1.0f); | |||
| } | |||
| } | |||
| } | |||
| @@ -53,7 +53,7 @@ public: | |||
| void setBias (FloatType newBias) noexcept | |||
| { | |||
| jassert (newBias >= static_cast<FloatType> (-1) && newBias <= static_cast<FloatType> (1)); | |||
| bias.setValue(newBias); | |||
| bias.setTargetValue (newBias); | |||
| } | |||
| //============================================================================== | |||
| @@ -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<FloatType> (newGainDecibels)); } | |||
| @@ -78,8 +78,8 @@ void LadderFilter<Type>::reset() noexcept | |||
| for (auto& s : state) | |||
| s.fill (Type (0)); | |||
| cutoffTransformSmoother.setValue (cutoffTransformSmoother.getTargetValue(), true); | |||
| scaledResonanceSmoother.setValue (scaledResonanceSmoother.getTargetValue(), true); | |||
| cutoffTransformSmoother.setCurrentValueToTargetValue(); | |||
| scaledResonanceSmoother.setCurrentValueToTargetValue(); | |||
| } | |||
| //============================================================================== | |||
| @@ -118,10 +118,8 @@ private: | |||
| std::vector<std::array<Type, numStates>> state; | |||
| std::array<Type, numStates> A; | |||
| LinearSmoothedValue<Type> cutoffTransformSmoother; | |||
| LinearSmoothedValue<Type> scaledResonanceSmoother; | |||
| Type cutoffTransformValue; | |||
| Type scaledResonanceValue; | |||
| LinearSmoothedValue<Type> cutoffTransformSmoother, scaledResonanceSmoother; | |||
| Type cutoffTransformValue, scaledResonanceValue; | |||
| LookupTableTransform<Type> 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 | |||
| @@ -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(); } | |||