upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)
This commit is contained in:
		
							
								
								
									
										255
									
								
								modules/juce_audio_basics/utilities/juce_ADSR.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								modules/juce_audio_basics/utilities/juce_ADSR.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,255 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A very simple ADSR envelope class.
 | 
			
		||||
 | 
			
		||||
    To use it, call setSampleRate() with the current sample rate and give it some parameters
 | 
			
		||||
    with setParameters() then call getNextSample() to get the envelope value to be applied
 | 
			
		||||
    to each audio sample or applyEnvelopeToBuffer() to apply the envelope to a whole buffer.
 | 
			
		||||
*/
 | 
			
		||||
class ADSR
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    ADSR()
 | 
			
		||||
    {
 | 
			
		||||
        setSampleRate (44100.0);
 | 
			
		||||
        setParameters ({});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Holds the parameters being used by an ADSR object. */
 | 
			
		||||
    struct Parameters
 | 
			
		||||
    {
 | 
			
		||||
        /** Attack time in seconds. */
 | 
			
		||||
        float attack  = 0.1f;
 | 
			
		||||
 | 
			
		||||
        /** Decay time in seconds. */
 | 
			
		||||
        float decay   = 0.1f;
 | 
			
		||||
 | 
			
		||||
        /** Sustain level. */
 | 
			
		||||
        float sustain = 1.0f;
 | 
			
		||||
 | 
			
		||||
        /** Release time in seconds. */
 | 
			
		||||
        float release = 0.1f;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Sets the parameters that will be used by an ADSR object.
 | 
			
		||||
 | 
			
		||||
        You must have called setSampleRate() with the correct sample rate before
 | 
			
		||||
        this otherwise the values may be incorrect!
 | 
			
		||||
 | 
			
		||||
        @see getParameters
 | 
			
		||||
    */
 | 
			
		||||
    void setParameters (const Parameters& newParameters)
 | 
			
		||||
    {
 | 
			
		||||
        currentParameters = newParameters;
 | 
			
		||||
 | 
			
		||||
        sustainLevel = newParameters.sustain;
 | 
			
		||||
        calculateRates (newParameters);
 | 
			
		||||
 | 
			
		||||
        if (currentState != State::idle)
 | 
			
		||||
            checkCurrentState();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Returns the parameters currently being used by an ADSR object.
 | 
			
		||||
 | 
			
		||||
        @see setParameters
 | 
			
		||||
    */
 | 
			
		||||
    const Parameters& getParameters() const    { return currentParameters; }
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the envelope is in its attack, decay, sustain or release stage. */
 | 
			
		||||
    bool isActive() const noexcept             { return currentState != State::idle; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the sample rate that will be used for the envelope.
 | 
			
		||||
 | 
			
		||||
        This must be called before the getNextSample() or setParameters() methods.
 | 
			
		||||
    */
 | 
			
		||||
    void setSampleRate (double sampleRate)
 | 
			
		||||
    {
 | 
			
		||||
        jassert (sampleRate > 0.0);
 | 
			
		||||
        sr = sampleRate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Resets the envelope to an idle state. */
 | 
			
		||||
    void reset()
 | 
			
		||||
    {
 | 
			
		||||
        envelopeVal = 0.0f;
 | 
			
		||||
        currentState = State::idle;
 | 
			
		||||
 | 
			
		||||
        if (resetReleaseRate)
 | 
			
		||||
        {
 | 
			
		||||
            releaseRate = static_cast<float> (sustainLevel / (currentParameters.release * sr));
 | 
			
		||||
            resetReleaseRate = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Starts the attack phase of the envelope. */
 | 
			
		||||
    void noteOn()
 | 
			
		||||
    {
 | 
			
		||||
        if (attackRate > 0.0f)
 | 
			
		||||
        {
 | 
			
		||||
            currentState = State::attack;
 | 
			
		||||
        }
 | 
			
		||||
        else if (decayRate > 0.0f)
 | 
			
		||||
        {
 | 
			
		||||
            envelopeVal = 1.0f;
 | 
			
		||||
            currentState = State::decay;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            currentState = State::sustain;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Starts the release phase of the envelope. */
 | 
			
		||||
    void noteOff()
 | 
			
		||||
    {
 | 
			
		||||
        if (currentState != State::idle)
 | 
			
		||||
        {
 | 
			
		||||
            if (releaseRate > 0.0f)
 | 
			
		||||
            {
 | 
			
		||||
                if (currentState != State::sustain)
 | 
			
		||||
                {
 | 
			
		||||
                    releaseRate = static_cast<float> (envelopeVal / (currentParameters.release * sr));
 | 
			
		||||
                    resetReleaseRate = true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                currentState = State::release;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                reset();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the next sample value for an ADSR object.
 | 
			
		||||
 | 
			
		||||
        @see applyEnvelopeToBuffer
 | 
			
		||||
    */
 | 
			
		||||
    float getNextSample()
 | 
			
		||||
    {
 | 
			
		||||
        if (currentState == State::idle)
 | 
			
		||||
            return 0.0f;
 | 
			
		||||
 | 
			
		||||
        if (currentState == State::attack)
 | 
			
		||||
        {
 | 
			
		||||
            envelopeVal += attackRate;
 | 
			
		||||
 | 
			
		||||
            if (envelopeVal >= 1.0f)
 | 
			
		||||
            {
 | 
			
		||||
                envelopeVal = 1.0f;
 | 
			
		||||
 | 
			
		||||
                if (decayRate > 0.0f)
 | 
			
		||||
                    currentState = State::decay;
 | 
			
		||||
                else
 | 
			
		||||
                    currentState = State::sustain;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (currentState == State::decay)
 | 
			
		||||
        {
 | 
			
		||||
            envelopeVal -= decayRate;
 | 
			
		||||
 | 
			
		||||
            if (envelopeVal <= sustainLevel)
 | 
			
		||||
            {
 | 
			
		||||
                envelopeVal = sustainLevel;
 | 
			
		||||
                currentState = State::sustain;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (currentState == State::sustain)
 | 
			
		||||
        {
 | 
			
		||||
            envelopeVal = sustainLevel;
 | 
			
		||||
        }
 | 
			
		||||
        else if (currentState == State::release)
 | 
			
		||||
        {
 | 
			
		||||
            envelopeVal -= releaseRate;
 | 
			
		||||
 | 
			
		||||
            if (envelopeVal <= 0.0f)
 | 
			
		||||
                reset();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return envelopeVal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** This method will conveniently apply the next numSamples number of envelope values
 | 
			
		||||
        to an AudioBuffer.
 | 
			
		||||
 | 
			
		||||
        @see getNextSample
 | 
			
		||||
    */
 | 
			
		||||
    template<typename FloatType>
 | 
			
		||||
    void applyEnvelopeToBuffer (AudioBuffer<FloatType>& buffer, int startSample, int numSamples)
 | 
			
		||||
    {
 | 
			
		||||
        jassert (startSample + numSamples <= buffer.getNumSamples());
 | 
			
		||||
 | 
			
		||||
        auto numChannels = buffer.getNumChannels();
 | 
			
		||||
 | 
			
		||||
        while (--numSamples >= 0)
 | 
			
		||||
        {
 | 
			
		||||
            auto env = getNextSample();
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < numChannels; ++i)
 | 
			
		||||
                buffer.getWritePointer (i)[startSample] *= env;
 | 
			
		||||
 | 
			
		||||
            ++startSample;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void calculateRates (const Parameters& parameters)
 | 
			
		||||
    {
 | 
			
		||||
        // need to call setSampleRate() first!
 | 
			
		||||
        jassert (sr > 0.0);
 | 
			
		||||
 | 
			
		||||
        attackRate  = (parameters.attack  > 0.0f ? static_cast<float> (1.0f                  / (parameters.attack * sr))  : -1.0f);
 | 
			
		||||
        decayRate   = (parameters.decay   > 0.0f ? static_cast<float> ((1.0f - sustainLevel) / (parameters.decay * sr))   : -1.0f);
 | 
			
		||||
        releaseRate = (parameters.release > 0.0f ? static_cast<float> (sustainLevel          / (parameters.release * sr)) : -1.0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void checkCurrentState()
 | 
			
		||||
    {
 | 
			
		||||
        if      (currentState == State::attack  && attackRate <= 0.0f)   currentState = decayRate > 0.0f ? State::decay : State::sustain;
 | 
			
		||||
        else if (currentState == State::decay   && decayRate <= 0.0f)    currentState = State::sustain;
 | 
			
		||||
        else if (currentState == State::release && releaseRate <= 0.0f)  reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum class State { idle, attack, decay, sustain, release };
 | 
			
		||||
 | 
			
		||||
    State currentState = State::idle;
 | 
			
		||||
    Parameters currentParameters;
 | 
			
		||||
 | 
			
		||||
    double sr = 0.0;
 | 
			
		||||
    float envelopeVal = 0.0f, sustainLevel = 0.0f, attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f;
 | 
			
		||||
    bool resetReleaseRate = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
@ -0,0 +1,75 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
struct CatmullRomAlgorithm
 | 
			
		||||
{
 | 
			
		||||
    static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        auto y0 = inputs[3];
 | 
			
		||||
        auto y1 = inputs[2];
 | 
			
		||||
        auto y2 = inputs[1];
 | 
			
		||||
        auto y3 = inputs[0];
 | 
			
		||||
 | 
			
		||||
        auto halfY0 = 0.5f * y0;
 | 
			
		||||
        auto halfY3 = 0.5f * y3;
 | 
			
		||||
 | 
			
		||||
        return y1 + offset * ((0.5f * y2 - halfY0)
 | 
			
		||||
                                + (offset * (((y0 + 2.0f * y2) - (halfY3 + 2.5f * y1))
 | 
			
		||||
                                              + (offset * ((halfY3 + 1.5f * y1) - (halfY0 + 1.5f * y2))))));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CatmullRomInterpolator::CatmullRomInterpolator() noexcept  { reset(); }
 | 
			
		||||
CatmullRomInterpolator::~CatmullRomInterpolator() noexcept {}
 | 
			
		||||
 | 
			
		||||
void CatmullRomInterpolator::reset() noexcept
 | 
			
		||||
{
 | 
			
		||||
    subSamplePos = 1.0;
 | 
			
		||||
 | 
			
		||||
    for (auto& s : lastInputSamples)
 | 
			
		||||
        s = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int CatmullRomInterpolator::process (double actualRatio, const float* in, float* out, int numOut, int available, int wrap) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return interpolate<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int CatmullRomInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return interpolate<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int CatmullRomInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, int available, int wrap, float gain) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return interpolateAdding<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap, gain);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int CatmullRomInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return interpolateAdding<CatmullRomAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
@ -0,0 +1,143 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Interpolator for resampling a stream of floats using Catmull-Rom interpolation.
 | 
			
		||||
 | 
			
		||||
    Note that the resampler is stateful, so when there's a break in the continuity
 | 
			
		||||
    of the input stream you're feeding it, you should call reset() before feeding
 | 
			
		||||
    it any new data. And like with any other stateful filter, if you're resampling
 | 
			
		||||
    multiple channels, make sure each one uses its own CatmullRomInterpolator
 | 
			
		||||
    object.
 | 
			
		||||
 | 
			
		||||
    @see LagrangeInterpolator
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  CatmullRomInterpolator
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    CatmullRomInterpolator() noexcept;
 | 
			
		||||
    ~CatmullRomInterpolator() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resets the state of the interpolator.
 | 
			
		||||
        Call this when there's a break in the continuity of the input data stream.
 | 
			
		||||
    */
 | 
			
		||||
    void reset() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resamples a stream of samples.
 | 
			
		||||
 | 
			
		||||
        @param speedRatio       the number of input samples to use for each output sample
 | 
			
		||||
        @param inputSamples     the source data to read from. This must contain at
 | 
			
		||||
                                least (speedRatio * numOutputSamplesToProduce) samples.
 | 
			
		||||
        @param outputSamples    the buffer to write the results into
 | 
			
		||||
        @param numOutputSamplesToProduce    the number of output samples that should be created
 | 
			
		||||
 | 
			
		||||
        @returns the actual number of input samples that were used
 | 
			
		||||
    */
 | 
			
		||||
    int process (double speedRatio,
 | 
			
		||||
                 const float* inputSamples,
 | 
			
		||||
                 float* outputSamples,
 | 
			
		||||
                 int numOutputSamplesToProduce) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resamples a stream of samples.
 | 
			
		||||
 | 
			
		||||
        @param speedRatio       the number of input samples to use for each output sample
 | 
			
		||||
        @param inputSamples     the source data to read from. This must contain at
 | 
			
		||||
                                least (speedRatio * numOutputSamplesToProduce) samples.
 | 
			
		||||
        @param outputSamples    the buffer to write the results into
 | 
			
		||||
        @param numOutputSamplesToProduce    the number of output samples that should be created
 | 
			
		||||
        @param available        the number of available input samples. If it needs more samples
 | 
			
		||||
                                than available, it either wraps back for wrapAround samples, or
 | 
			
		||||
                                it feeds zeroes
 | 
			
		||||
        @param wrapAround       if the stream exceeds available samples, it wraps back for
 | 
			
		||||
                                wrapAround samples. If wrapAround is set to 0, it will feed zeroes.
 | 
			
		||||
 | 
			
		||||
        @returns the actual number of input samples that were used
 | 
			
		||||
    */
 | 
			
		||||
    int process (double speedRatio,
 | 
			
		||||
                 const float* inputSamples,
 | 
			
		||||
                 float* outputSamples,
 | 
			
		||||
                 int numOutputSamplesToProduce,
 | 
			
		||||
                 int available,
 | 
			
		||||
                 int wrapAround) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resamples a stream of samples, adding the results to the output data
 | 
			
		||||
        with a gain.
 | 
			
		||||
 | 
			
		||||
        @param speedRatio       the number of input samples to use for each output sample
 | 
			
		||||
        @param inputSamples     the source data to read from. This must contain at
 | 
			
		||||
                                least (speedRatio * numOutputSamplesToProduce) samples.
 | 
			
		||||
        @param outputSamples    the buffer to write the results to - the result values will be added
 | 
			
		||||
                                to any pre-existing data in this buffer after being multiplied by
 | 
			
		||||
                                the gain factor
 | 
			
		||||
        @param numOutputSamplesToProduce    the number of output samples that should be created
 | 
			
		||||
        @param gain             a gain factor to multiply the resulting samples by before
 | 
			
		||||
                                adding them to the destination buffer
 | 
			
		||||
 | 
			
		||||
        @returns the actual number of input samples that were used
 | 
			
		||||
    */
 | 
			
		||||
    int processAdding (double speedRatio,
 | 
			
		||||
                       const float* inputSamples,
 | 
			
		||||
                       float* outputSamples,
 | 
			
		||||
                       int numOutputSamplesToProduce,
 | 
			
		||||
                       float gain) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resamples a stream of samples, adding the results to the output data
 | 
			
		||||
        with a gain.
 | 
			
		||||
 | 
			
		||||
        @param speedRatio       the number of input samples to use for each output sample
 | 
			
		||||
        @param inputSamples     the source data to read from. This must contain at
 | 
			
		||||
                                least (speedRatio * numOutputSamplesToProduce) samples.
 | 
			
		||||
        @param outputSamples    the buffer to write the results to - the result values will be added
 | 
			
		||||
                                to any pre-existing data in this buffer after being multiplied by
 | 
			
		||||
                                the gain factor
 | 
			
		||||
        @param numOutputSamplesToProduce    the number of output samples that should be created
 | 
			
		||||
        @param available        the number of available input samples. If it needs more samples
 | 
			
		||||
                                than available, it either wraps back for wrapAround samples, or
 | 
			
		||||
                                it feeds zeroes
 | 
			
		||||
        @param wrapAround       if the stream exceeds available samples, it wraps back for
 | 
			
		||||
                                wrapAround samples. If wrapAround is set to 0, it will feed zeroes.
 | 
			
		||||
        @param gain             a gain factor to multiply the resulting samples by before
 | 
			
		||||
                                adding them to the destination buffer
 | 
			
		||||
 | 
			
		||||
        @returns the actual number of input samples that were used
 | 
			
		||||
    */
 | 
			
		||||
    int processAdding (double speedRatio,
 | 
			
		||||
                       const float* inputSamples,
 | 
			
		||||
                       float* outputSamples,
 | 
			
		||||
                       int numOutputSamplesToProduce,
 | 
			
		||||
                       int available,
 | 
			
		||||
                       int wrapAround,
 | 
			
		||||
                       float gain) noexcept;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    float lastInputSamples[5];
 | 
			
		||||
    double subSamplePos;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CatmullRomInterpolator)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										112
									
								
								modules/juce_audio_basics/utilities/juce_Decibels.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								modules/juce_audio_basics/utilities/juce_Decibels.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,112 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    This class contains some helpful static methods for dealing with decibel values.
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class Decibels
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Converts a dBFS value to its equivalent gain level.
 | 
			
		||||
 | 
			
		||||
        A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values. Any
 | 
			
		||||
        decibel value lower than minusInfinityDb will return a gain of 0.
 | 
			
		||||
    */
 | 
			
		||||
    template <typename Type>
 | 
			
		||||
    static Type decibelsToGain (Type decibels,
 | 
			
		||||
                                Type minusInfinityDb = Type (defaultMinusInfinitydB))
 | 
			
		||||
    {
 | 
			
		||||
        return decibels > minusInfinityDb ? std::pow (Type (10.0), decibels * Type (0.05))
 | 
			
		||||
                                          : Type();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Converts a gain level into a dBFS value.
 | 
			
		||||
 | 
			
		||||
        A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values.
 | 
			
		||||
        If the gain is 0 (or negative), then the method will return the value
 | 
			
		||||
        provided as minusInfinityDb.
 | 
			
		||||
    */
 | 
			
		||||
    template <typename Type>
 | 
			
		||||
    static Type gainToDecibels (Type gain,
 | 
			
		||||
                                Type minusInfinityDb = Type (defaultMinusInfinitydB))
 | 
			
		||||
    {
 | 
			
		||||
        return gain > Type() ? jmax (minusInfinityDb, static_cast<Type> (std::log10 (gain)) * Type (20.0))
 | 
			
		||||
                             : minusInfinityDb;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Converts a decibel reading to a string.
 | 
			
		||||
 | 
			
		||||
        By default the returned string will have the 'dB' suffix added, but this can be removed by
 | 
			
		||||
        setting the shouldIncludeSuffix argument to false. If a customMinusInfinityString argument
 | 
			
		||||
        is provided this will be returned if the value is lower than minusInfinityDb, otherwise
 | 
			
		||||
        the return value will be "-INF".
 | 
			
		||||
    */
 | 
			
		||||
    template <typename Type>
 | 
			
		||||
    static String toString (Type decibels,
 | 
			
		||||
                            int decimalPlaces = 2,
 | 
			
		||||
                            Type minusInfinityDb = Type (defaultMinusInfinitydB),
 | 
			
		||||
                            bool shouldIncludeSuffix = true,
 | 
			
		||||
                            StringRef customMinusInfinityString = {})
 | 
			
		||||
    {
 | 
			
		||||
        String s;
 | 
			
		||||
        s.preallocateBytes (20);
 | 
			
		||||
 | 
			
		||||
        if (decibels <= minusInfinityDb)
 | 
			
		||||
        {
 | 
			
		||||
            if (customMinusInfinityString.isEmpty())
 | 
			
		||||
                s << "-INF";
 | 
			
		||||
            else
 | 
			
		||||
                s << customMinusInfinityString;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (decibels >= Type())
 | 
			
		||||
                s << '+';
 | 
			
		||||
 | 
			
		||||
            if (decimalPlaces <= 0)
 | 
			
		||||
                s << roundToInt (decibels);
 | 
			
		||||
            else
 | 
			
		||||
                s << String (decibels, decimalPlaces);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (shouldIncludeSuffix)
 | 
			
		||||
            s << " dB";
 | 
			
		||||
 | 
			
		||||
        return s;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum { defaultMinusInfinitydB = -100 };
 | 
			
		||||
 | 
			
		||||
    Decibels() = delete; // This class can't be instantiated, it's just a holder for static methods..
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										336
									
								
								modules/juce_audio_basics/utilities/juce_IIRFilter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								modules/juce_audio_basics/utilities/juce_IIRFilter.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,336 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
IIRCoefficients::IIRCoefficients() noexcept
 | 
			
		||||
{
 | 
			
		||||
    zeromem (coefficients, sizeof (coefficients));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients::~IIRCoefficients() noexcept {}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients::IIRCoefficients (const IIRCoefficients& other) noexcept
 | 
			
		||||
{
 | 
			
		||||
    memcpy (coefficients, other.coefficients, sizeof (coefficients));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients& IIRCoefficients::operator= (const IIRCoefficients& other) noexcept
 | 
			
		||||
{
 | 
			
		||||
    memcpy (coefficients, other.coefficients, sizeof (coefficients));
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients::IIRCoefficients (double c1, double c2, double c3,
 | 
			
		||||
                                  double c4, double c5, double c6) noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto a = 1.0 / c4;
 | 
			
		||||
 | 
			
		||||
    coefficients[0] = (float) (c1 * a);
 | 
			
		||||
    coefficients[1] = (float) (c2 * a);
 | 
			
		||||
    coefficients[2] = (float) (c3 * a);
 | 
			
		||||
    coefficients[3] = (float) (c5 * a);
 | 
			
		||||
    coefficients[4] = (float) (c6 * a);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeLowPass (double sampleRate,
 | 
			
		||||
                                              double frequency) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return makeLowPass (sampleRate, frequency, 1.0 / MathConstants<double>::sqrt2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeLowPass (double sampleRate,
 | 
			
		||||
                                              double frequency,
 | 
			
		||||
                                              double Q) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (sampleRate > 0.0);
 | 
			
		||||
    jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
 | 
			
		||||
    jassert (Q > 0.0);
 | 
			
		||||
 | 
			
		||||
    auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
 | 
			
		||||
    auto nSquared = n * n;
 | 
			
		||||
    auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
 | 
			
		||||
 | 
			
		||||
    return IIRCoefficients (c1,
 | 
			
		||||
                            c1 * 2.0,
 | 
			
		||||
                            c1,
 | 
			
		||||
                            1.0,
 | 
			
		||||
                            c1 * 2.0 * (1.0 - nSquared),
 | 
			
		||||
                            c1 * (1.0 - 1.0 / Q * n + nSquared));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeHighPass (double sampleRate,
 | 
			
		||||
                                               double frequency) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return makeHighPass (sampleRate, frequency, 1.0 / std::sqrt(2.0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeHighPass (double sampleRate,
 | 
			
		||||
                                               double frequency,
 | 
			
		||||
                                               double Q) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (sampleRate > 0.0);
 | 
			
		||||
    jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
 | 
			
		||||
    jassert (Q > 0.0);
 | 
			
		||||
 | 
			
		||||
    auto n = std::tan (MathConstants<double>::pi * frequency / sampleRate);
 | 
			
		||||
    auto nSquared = n * n;
 | 
			
		||||
    auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
 | 
			
		||||
 | 
			
		||||
    return IIRCoefficients (c1,
 | 
			
		||||
                            c1 * -2.0,
 | 
			
		||||
                            c1,
 | 
			
		||||
                            1.0,
 | 
			
		||||
                            c1 * 2.0 * (nSquared - 1.0),
 | 
			
		||||
                            c1 * (1.0 - 1.0 / Q * n + nSquared));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeBandPass (double sampleRate,
 | 
			
		||||
                                               double frequency) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return makeBandPass (sampleRate, frequency, 1.0 / MathConstants<double>::sqrt2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeBandPass (double sampleRate,
 | 
			
		||||
                                               double frequency,
 | 
			
		||||
                                               double Q) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (sampleRate > 0.0);
 | 
			
		||||
    jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
 | 
			
		||||
    jassert (Q > 0.0);
 | 
			
		||||
 | 
			
		||||
    auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
 | 
			
		||||
    auto nSquared = n * n;
 | 
			
		||||
    auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
 | 
			
		||||
 | 
			
		||||
    return IIRCoefficients (c1 * n / Q,
 | 
			
		||||
                            0.0,
 | 
			
		||||
                            -c1 * n / Q,
 | 
			
		||||
                            1.0,
 | 
			
		||||
                            c1 * 2.0 * (1.0 - nSquared),
 | 
			
		||||
                            c1 * (1.0 - 1.0 / Q * n + nSquared));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeNotchFilter (double sampleRate,
 | 
			
		||||
                                                  double frequency) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return makeNotchFilter (sampleRate, frequency, 1.0 / MathConstants<double>::sqrt2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeNotchFilter (double sampleRate,
 | 
			
		||||
                                                  double frequency,
 | 
			
		||||
                                                  double Q) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (sampleRate > 0.0);
 | 
			
		||||
    jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
 | 
			
		||||
    jassert (Q > 0.0);
 | 
			
		||||
 | 
			
		||||
    auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
 | 
			
		||||
    auto nSquared = n * n;
 | 
			
		||||
    auto c1 = 1.0 / (1.0 + n / Q + nSquared);
 | 
			
		||||
 | 
			
		||||
    return IIRCoefficients (c1 * (1.0 + nSquared),
 | 
			
		||||
                            2.0 * c1 * (1.0 - nSquared),
 | 
			
		||||
                            c1 * (1.0 + nSquared),
 | 
			
		||||
                            1.0,
 | 
			
		||||
                            c1 * 2.0 * (1.0 - nSquared),
 | 
			
		||||
                            c1 * (1.0 - n / Q + nSquared));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeAllPass (double sampleRate,
 | 
			
		||||
                                              double frequency) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return makeAllPass (sampleRate, frequency, 1.0 / MathConstants<double>::sqrt2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeAllPass (double sampleRate,
 | 
			
		||||
                                              double frequency,
 | 
			
		||||
                                              double Q) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (sampleRate > 0.0);
 | 
			
		||||
    jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
 | 
			
		||||
    jassert (Q > 0.0);
 | 
			
		||||
 | 
			
		||||
    auto n = 1.0 / std::tan (MathConstants<double>::pi * frequency / sampleRate);
 | 
			
		||||
    auto nSquared = n * n;
 | 
			
		||||
    auto c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
 | 
			
		||||
 | 
			
		||||
    return IIRCoefficients (c1 * (1.0 - n / Q + nSquared),
 | 
			
		||||
                            c1 * 2.0 * (1.0 - nSquared),
 | 
			
		||||
                            1.0,
 | 
			
		||||
                            1.0,
 | 
			
		||||
                            c1 * 2.0 * (1.0 - nSquared),
 | 
			
		||||
                            c1 * (1.0 - n / Q + nSquared));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeLowShelf (double sampleRate,
 | 
			
		||||
                                               double cutOffFrequency,
 | 
			
		||||
                                               double Q,
 | 
			
		||||
                                               float gainFactor) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (sampleRate > 0.0);
 | 
			
		||||
    jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5);
 | 
			
		||||
    jassert (Q > 0.0);
 | 
			
		||||
 | 
			
		||||
    auto A = jmax (0.0f, std::sqrt (gainFactor));
 | 
			
		||||
    auto aminus1 = A - 1.0;
 | 
			
		||||
    auto aplus1 = A + 1.0;
 | 
			
		||||
    auto omega = (MathConstants<double>::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate;
 | 
			
		||||
    auto coso = std::cos (omega);
 | 
			
		||||
    auto beta = std::sin (omega) * std::sqrt (A) / Q;
 | 
			
		||||
    auto aminus1TimesCoso = aminus1 * coso;
 | 
			
		||||
 | 
			
		||||
    return IIRCoefficients (A * (aplus1 - aminus1TimesCoso + beta),
 | 
			
		||||
                            A * 2.0 * (aminus1 - aplus1 * coso),
 | 
			
		||||
                            A * (aplus1 - aminus1TimesCoso - beta),
 | 
			
		||||
                            aplus1 + aminus1TimesCoso + beta,
 | 
			
		||||
                            -2.0 * (aminus1 + aplus1 * coso),
 | 
			
		||||
                            aplus1 + aminus1TimesCoso - beta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makeHighShelf (double sampleRate,
 | 
			
		||||
                                                double cutOffFrequency,
 | 
			
		||||
                                                double Q,
 | 
			
		||||
                                                float gainFactor) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (sampleRate > 0.0);
 | 
			
		||||
    jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5);
 | 
			
		||||
    jassert (Q > 0.0);
 | 
			
		||||
 | 
			
		||||
    auto A = jmax (0.0f, std::sqrt (gainFactor));
 | 
			
		||||
    auto aminus1 = A - 1.0;
 | 
			
		||||
    auto aplus1 = A + 1.0;
 | 
			
		||||
    auto omega = (MathConstants<double>::twoPi * jmax (cutOffFrequency, 2.0)) / sampleRate;
 | 
			
		||||
    auto coso = std::cos (omega);
 | 
			
		||||
    auto beta = std::sin (omega) * std::sqrt (A) / Q;
 | 
			
		||||
    auto aminus1TimesCoso = aminus1 * coso;
 | 
			
		||||
 | 
			
		||||
    return IIRCoefficients (A * (aplus1 + aminus1TimesCoso + beta),
 | 
			
		||||
                            A * -2.0 * (aminus1 + aplus1 * coso),
 | 
			
		||||
                            A * (aplus1 + aminus1TimesCoso - beta),
 | 
			
		||||
                            aplus1 - aminus1TimesCoso + beta,
 | 
			
		||||
                            2.0 * (aminus1 - aplus1 * coso),
 | 
			
		||||
                            aplus1 - aminus1TimesCoso - beta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRCoefficients IIRCoefficients::makePeakFilter (double sampleRate,
 | 
			
		||||
                                                 double frequency,
 | 
			
		||||
                                                 double Q,
 | 
			
		||||
                                                 float gainFactor) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (sampleRate > 0.0);
 | 
			
		||||
    jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
 | 
			
		||||
    jassert (Q > 0.0);
 | 
			
		||||
 | 
			
		||||
    auto A = jmax (0.0f, std::sqrt (gainFactor));
 | 
			
		||||
    auto omega = (MathConstants<double>::twoPi * jmax (frequency, 2.0)) / sampleRate;
 | 
			
		||||
    auto alpha = 0.5 * std::sin (omega) / Q;
 | 
			
		||||
    auto c2 = -2.0 * std::cos (omega);
 | 
			
		||||
    auto alphaTimesA = alpha * A;
 | 
			
		||||
    auto alphaOverA = alpha / A;
 | 
			
		||||
 | 
			
		||||
    return IIRCoefficients (1.0 + alphaTimesA,
 | 
			
		||||
                            c2,
 | 
			
		||||
                            1.0 - alphaTimesA,
 | 
			
		||||
                            1.0 + alphaOverA,
 | 
			
		||||
                            c2,
 | 
			
		||||
                            1.0 - alphaOverA);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
IIRFilter::IIRFilter() noexcept
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRFilter::IIRFilter (const IIRFilter& other) noexcept  : active (other.active)
 | 
			
		||||
{
 | 
			
		||||
    const SpinLock::ScopedLockType sl (other.processLock);
 | 
			
		||||
    coefficients = other.coefficients;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRFilter::~IIRFilter() noexcept
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void IIRFilter::makeInactive() noexcept
 | 
			
		||||
{
 | 
			
		||||
    const SpinLock::ScopedLockType sl (processLock);
 | 
			
		||||
    active = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcept
 | 
			
		||||
{
 | 
			
		||||
    const SpinLock::ScopedLockType sl (processLock);
 | 
			
		||||
    coefficients = newCoefficients;
 | 
			
		||||
    active = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void IIRFilter::reset() noexcept
 | 
			
		||||
{
 | 
			
		||||
    const SpinLock::ScopedLockType sl (processLock);
 | 
			
		||||
    v1 = v2 = 0.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float IIRFilter::processSingleSampleRaw (float in) noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto out = coefficients.coefficients[0] * in + v1;
 | 
			
		||||
 | 
			
		||||
    JUCE_SNAP_TO_ZERO (out);
 | 
			
		||||
 | 
			
		||||
    v1 = coefficients.coefficients[1] * in - coefficients.coefficients[3] * out + v2;
 | 
			
		||||
    v2 = coefficients.coefficients[2] * in - coefficients.coefficients[4] * out;
 | 
			
		||||
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IIRFilter::processSamples (float* const samples, const int numSamples) noexcept
 | 
			
		||||
{
 | 
			
		||||
    const SpinLock::ScopedLockType sl (processLock);
 | 
			
		||||
 | 
			
		||||
    if (active)
 | 
			
		||||
    {
 | 
			
		||||
        auto c0 = coefficients.coefficients[0];
 | 
			
		||||
        auto c1 = coefficients.coefficients[1];
 | 
			
		||||
        auto c2 = coefficients.coefficients[2];
 | 
			
		||||
        auto c3 = coefficients.coefficients[3];
 | 
			
		||||
        auto c4 = coefficients.coefficients[4];
 | 
			
		||||
        auto lv1 = v1, lv2 = v2;
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numSamples; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto in = samples[i];
 | 
			
		||||
            auto out = c0 * in + lv1;
 | 
			
		||||
            samples[i] = out;
 | 
			
		||||
 | 
			
		||||
            lv1 = c1 * in - c3 * out + lv2;
 | 
			
		||||
            lv2 = c2 * in - c4 * out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        JUCE_SNAP_TO_ZERO (lv1);  v1 = lv1;
 | 
			
		||||
        JUCE_SNAP_TO_ZERO (lv2);  v2 = lv2;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										217
									
								
								modules/juce_audio_basics/utilities/juce_IIRFilter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								modules/juce_audio_basics/utilities/juce_IIRFilter.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,217 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class IIRFilter;
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A set of coefficients for use in an IIRFilter object.
 | 
			
		||||
 | 
			
		||||
    @see IIRFilter
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  IIRCoefficients
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a null set of coefficients (which will produce silence). */
 | 
			
		||||
    IIRCoefficients() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Directly constructs an object from the raw coefficients.
 | 
			
		||||
        Most people will want to use the static methods instead of this, but
 | 
			
		||||
        the constructor is public to allow tinkerers to create their own custom
 | 
			
		||||
        filters!
 | 
			
		||||
    */
 | 
			
		||||
    IIRCoefficients (double c1, double c2, double c3,
 | 
			
		||||
                     double c4, double c5, double c6) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates a copy of another filter. */
 | 
			
		||||
    IIRCoefficients (const IIRCoefficients&) noexcept;
 | 
			
		||||
    /** Creates a copy of another filter. */
 | 
			
		||||
    IIRCoefficients& operator= (const IIRCoefficients&) noexcept;
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~IIRCoefficients() noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the coefficients for a low-pass filter. */
 | 
			
		||||
    static IIRCoefficients makeLowPass (double sampleRate,
 | 
			
		||||
                                        double frequency) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the coefficients for a low-pass filter with variable Q. */
 | 
			
		||||
    static IIRCoefficients makeLowPass (double sampleRate,
 | 
			
		||||
                                        double frequency,
 | 
			
		||||
                                        double Q) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the coefficients for a high-pass filter. */
 | 
			
		||||
    static IIRCoefficients makeHighPass (double sampleRate,
 | 
			
		||||
                                         double frequency) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the coefficients for a high-pass filter with variable Q. */
 | 
			
		||||
    static IIRCoefficients makeHighPass (double sampleRate,
 | 
			
		||||
                                         double frequency,
 | 
			
		||||
                                         double Q) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the coefficients for a band-pass filter. */
 | 
			
		||||
    static IIRCoefficients makeBandPass (double sampleRate, double frequency) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the coefficients for a band-pass filter with variable Q. */
 | 
			
		||||
    static IIRCoefficients makeBandPass (double sampleRate,
 | 
			
		||||
                                         double frequency,
 | 
			
		||||
                                         double Q) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the coefficients for a notch filter. */
 | 
			
		||||
    static IIRCoefficients makeNotchFilter (double sampleRate, double frequency) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the coefficients for a notch filter with variable Q. */
 | 
			
		||||
    static IIRCoefficients makeNotchFilter (double sampleRate,
 | 
			
		||||
                                            double frequency,
 | 
			
		||||
                                            double Q) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the coefficients for an all-pass filter. */
 | 
			
		||||
    static IIRCoefficients makeAllPass (double sampleRate, double frequency) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the coefficients for an all-pass filter with variable Q. */
 | 
			
		||||
    static IIRCoefficients makeAllPass (double sampleRate,
 | 
			
		||||
                                        double frequency,
 | 
			
		||||
                                        double Q) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the coefficients for a low-pass shelf filter with variable Q and gain.
 | 
			
		||||
 | 
			
		||||
        The gain is a scale factor that the low frequencies are multiplied by, so values
 | 
			
		||||
        greater than 1.0 will boost the low frequencies, values less than 1.0 will
 | 
			
		||||
        attenuate them.
 | 
			
		||||
    */
 | 
			
		||||
    static IIRCoefficients makeLowShelf (double sampleRate,
 | 
			
		||||
                                         double cutOffFrequency,
 | 
			
		||||
                                         double Q,
 | 
			
		||||
                                         float gainFactor) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the coefficients for a high-pass shelf filter with variable Q and gain.
 | 
			
		||||
 | 
			
		||||
        The gain is a scale factor that the high frequencies are multiplied by, so values
 | 
			
		||||
        greater than 1.0 will boost the high frequencies, values less than 1.0 will
 | 
			
		||||
        attenuate them.
 | 
			
		||||
    */
 | 
			
		||||
    static IIRCoefficients makeHighShelf (double sampleRate,
 | 
			
		||||
                                          double cutOffFrequency,
 | 
			
		||||
                                          double Q,
 | 
			
		||||
                                          float gainFactor) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the coefficients for a peak filter centred around a
 | 
			
		||||
        given frequency, with a variable Q and gain.
 | 
			
		||||
 | 
			
		||||
        The gain is a scale factor that the centre frequencies are multiplied by, so
 | 
			
		||||
        values greater than 1.0 will boost the centre frequencies, values less than
 | 
			
		||||
        1.0 will attenuate them.
 | 
			
		||||
    */
 | 
			
		||||
    static IIRCoefficients makePeakFilter (double sampleRate,
 | 
			
		||||
                                           double centreFrequency,
 | 
			
		||||
                                           double Q,
 | 
			
		||||
                                           float gainFactor) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The raw coefficients.
 | 
			
		||||
        You should leave these numbers alone unless you really know what you're doing.
 | 
			
		||||
    */
 | 
			
		||||
    float coefficients[5];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    An IIR filter that can perform low, high, or band-pass filtering on an
 | 
			
		||||
    audio signal.
 | 
			
		||||
 | 
			
		||||
    @see IIRCoefficient, IIRFilterAudioSource
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  IIRFilter
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a filter.
 | 
			
		||||
 | 
			
		||||
        Initially the filter is inactive, so will have no effect on samples that
 | 
			
		||||
        you process with it. Use the setCoefficients() method to turn it into the
 | 
			
		||||
        type of filter needed.
 | 
			
		||||
    */
 | 
			
		||||
    IIRFilter() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates a copy of another filter. */
 | 
			
		||||
    IIRFilter (const IIRFilter&) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~IIRFilter() noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Clears the filter so that any incoming data passes through unchanged. */
 | 
			
		||||
    void makeInactive() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Applies a set of coefficients to this filter. */
 | 
			
		||||
    void setCoefficients (const IIRCoefficients& newCoefficients) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the coefficients that this filter is using. */
 | 
			
		||||
    IIRCoefficients getCoefficients() const noexcept    { return coefficients; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Resets the filter's processing pipeline, ready to start a new stream of data.
 | 
			
		||||
 | 
			
		||||
        Note that this clears the processing state, but the type of filter and
 | 
			
		||||
        its coefficients aren't changed. To put a filter into an inactive state, use
 | 
			
		||||
        the makeInactive() method.
 | 
			
		||||
    */
 | 
			
		||||
    void reset() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Performs the filter operation on the given set of samples. */
 | 
			
		||||
    void processSamples (float* samples, int numSamples) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Processes a single sample, without any locking or checking.
 | 
			
		||||
 | 
			
		||||
        Use this if you need fast processing of a single value, but be aware that
 | 
			
		||||
        this isn't thread-safe in the way that processSamples() is.
 | 
			
		||||
    */
 | 
			
		||||
    float processSingleSampleRaw (float sample) noexcept;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    SpinLock processLock;
 | 
			
		||||
    IIRCoefficients coefficients;
 | 
			
		||||
    float v1 = 0, v2 = 0;
 | 
			
		||||
    bool active = false;
 | 
			
		||||
 | 
			
		||||
    // The exact meaning of an assignment operator would be ambiguous since the filters are
 | 
			
		||||
    // stateful. If you want to copy the coefficients, then just use setCoefficients().
 | 
			
		||||
    IIRFilter& operator= (const IIRFilter&) = delete;
 | 
			
		||||
 | 
			
		||||
    JUCE_LEAK_DETECTOR (IIRFilter)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
@ -0,0 +1,467 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
    static forcedinline void pushInterpolationSample (float* lastInputSamples, float newValue) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        lastInputSamples[4] = lastInputSamples[3];
 | 
			
		||||
        lastInputSamples[3] = lastInputSamples[2];
 | 
			
		||||
        lastInputSamples[2] = lastInputSamples[1];
 | 
			
		||||
        lastInputSamples[1] = lastInputSamples[0];
 | 
			
		||||
        lastInputSamples[0] = newValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static forcedinline void pushInterpolationSamples (float* lastInputSamples, const float* input, int numOut) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        if (numOut >= 5)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < 5; ++i)
 | 
			
		||||
                lastInputSamples[i] = input[--numOut];
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < numOut; ++i)
 | 
			
		||||
                pushInterpolationSample (lastInputSamples, input[i]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static forcedinline void pushInterpolationSamples (float* lastInputSamples, const float* input,
 | 
			
		||||
                                                       int numOut, int available, int wrapAround) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        if (numOut >= 5)
 | 
			
		||||
        {
 | 
			
		||||
            if (available >= 5)
 | 
			
		||||
            {
 | 
			
		||||
                for (int i = 0; i < 5; ++i)
 | 
			
		||||
                    lastInputSamples[i] = input[--numOut];
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                for (int i = 0; i < available; ++i)
 | 
			
		||||
                    lastInputSamples[i] = input[--numOut];
 | 
			
		||||
 | 
			
		||||
                if (wrapAround > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    numOut -= wrapAround;
 | 
			
		||||
 | 
			
		||||
                    for (int i = available; i < 5; ++i)
 | 
			
		||||
                        lastInputSamples[i] = input[--numOut];
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    for (int i = available; i < 5; ++i)
 | 
			
		||||
                        lastInputSamples[i] = 0.0f;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (numOut > available)
 | 
			
		||||
            {
 | 
			
		||||
                for (int i = 0; i < available; ++i)
 | 
			
		||||
                    pushInterpolationSample (lastInputSamples, input[i]);
 | 
			
		||||
 | 
			
		||||
                if (wrapAround > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    for (int i = 0; i < numOut - available; ++i)
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, input[i + available - wrapAround]);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    for (int i = 0; i < numOut - available; ++i)
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, 0);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                for (int i = 0; i < numOut; ++i)
 | 
			
		||||
                    pushInterpolationSample (lastInputSamples, input[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename InterpolatorType>
 | 
			
		||||
    static int interpolate (float* lastInputSamples, double& subSamplePos, double actualRatio,
 | 
			
		||||
                            const float* in, float* out, int numOut) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        auto pos = subSamplePos;
 | 
			
		||||
 | 
			
		||||
        if (actualRatio == 1.0 && pos == 1.0)
 | 
			
		||||
        {
 | 
			
		||||
            memcpy (out, in, (size_t) numOut * sizeof (float));
 | 
			
		||||
            pushInterpolationSamples (lastInputSamples, in, numOut);
 | 
			
		||||
            return numOut;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int numUsed = 0;
 | 
			
		||||
 | 
			
		||||
        while (numOut > 0)
 | 
			
		||||
        {
 | 
			
		||||
            while (pos >= 1.0)
 | 
			
		||||
            {
 | 
			
		||||
                pushInterpolationSample (lastInputSamples, in[numUsed++]);
 | 
			
		||||
                pos -= 1.0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            *out++ = InterpolatorType::valueAtOffset (lastInputSamples, (float) pos);
 | 
			
		||||
            pos += actualRatio;
 | 
			
		||||
            --numOut;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subSamplePos = pos;
 | 
			
		||||
        return numUsed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename InterpolatorType>
 | 
			
		||||
    static int interpolate (float* lastInputSamples, double& subSamplePos, double actualRatio,
 | 
			
		||||
                            const float* in, float* out, int numOut, int available, int wrap) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        if (actualRatio == 1.0)
 | 
			
		||||
        {
 | 
			
		||||
            if (available >= numOut)
 | 
			
		||||
            {
 | 
			
		||||
                memcpy (out, in, (size_t) numOut * sizeof (float));
 | 
			
		||||
                pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                memcpy (out, in, (size_t) available * sizeof (float));
 | 
			
		||||
                pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap);
 | 
			
		||||
 | 
			
		||||
                if (wrap > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    memcpy (out + available, in + available - wrap, (size_t) (numOut - available) * sizeof (float));
 | 
			
		||||
                    pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    for (int i = 0; i < numOut - available; ++i)
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, 0);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return numOut;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto originalIn = in;
 | 
			
		||||
        auto pos = subSamplePos;
 | 
			
		||||
        bool exceeded = false;
 | 
			
		||||
 | 
			
		||||
        if (actualRatio < 1.0)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = numOut; --i >= 0;)
 | 
			
		||||
            {
 | 
			
		||||
                if (pos >= 1.0)
 | 
			
		||||
                {
 | 
			
		||||
                    if (exceeded)
 | 
			
		||||
                    {
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, 0);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, *in++);
 | 
			
		||||
 | 
			
		||||
                        if (--available <= 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (wrap > 0)
 | 
			
		||||
                            {
 | 
			
		||||
                                in -= wrap;
 | 
			
		||||
                                available += wrap;
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                exceeded = true;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    pos -= 1.0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                *out++ = InterpolatorType::valueAtOffset (lastInputSamples, (float) pos);
 | 
			
		||||
                pos += actualRatio;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = numOut; --i >= 0;)
 | 
			
		||||
            {
 | 
			
		||||
                while (pos < actualRatio)
 | 
			
		||||
                {
 | 
			
		||||
                    if (exceeded)
 | 
			
		||||
                    {
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, 0);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, *in++);
 | 
			
		||||
 | 
			
		||||
                        if (--available <= 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (wrap > 0)
 | 
			
		||||
                            {
 | 
			
		||||
                                in -= wrap;
 | 
			
		||||
                                available += wrap;
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                exceeded = true;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    pos += 1.0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                pos -= actualRatio;
 | 
			
		||||
                *out++ = InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subSamplePos = pos;
 | 
			
		||||
 | 
			
		||||
        if (wrap == 0)
 | 
			
		||||
            return (int) (in - originalIn);
 | 
			
		||||
 | 
			
		||||
        return ((int) (in - originalIn) + wrap) % wrap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename InterpolatorType>
 | 
			
		||||
    static int interpolateAdding (float* lastInputSamples, double& subSamplePos, double actualRatio,
 | 
			
		||||
                                  const float* in, float* out, int numOut,
 | 
			
		||||
                                  int available, int wrap, float gain) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        if (actualRatio == 1.0)
 | 
			
		||||
        {
 | 
			
		||||
            if (available >= numOut)
 | 
			
		||||
            {
 | 
			
		||||
                FloatVectorOperations::addWithMultiply (out, in, gain, numOut);
 | 
			
		||||
                pushInterpolationSamples (lastInputSamples, in, numOut, available, wrap);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                FloatVectorOperations::addWithMultiply (out, in, gain, available);
 | 
			
		||||
                pushInterpolationSamples (lastInputSamples, in, available, available, wrap);
 | 
			
		||||
 | 
			
		||||
                if (wrap > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    FloatVectorOperations::addWithMultiply (out, in - wrap, gain, numOut - available);
 | 
			
		||||
                    pushInterpolationSamples (lastInputSamples, in - wrap, numOut - available, available, wrap);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    for (int i = 0; i < numOut-available; ++i)
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, 0.0);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return numOut;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto originalIn = in;
 | 
			
		||||
        auto pos = subSamplePos;
 | 
			
		||||
        bool exceeded = false;
 | 
			
		||||
 | 
			
		||||
        if (actualRatio < 1.0)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = numOut; --i >= 0;)
 | 
			
		||||
            {
 | 
			
		||||
                if (pos >= 1.0)
 | 
			
		||||
                {
 | 
			
		||||
                    if (exceeded)
 | 
			
		||||
                    {
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, 0.0);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, *in++);
 | 
			
		||||
 | 
			
		||||
                        if (--available <= 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (wrap > 0)
 | 
			
		||||
                            {
 | 
			
		||||
                                in -= wrap;
 | 
			
		||||
                                available += wrap;
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                exceeded = true;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    pos -= 1.0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                *out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, (float) pos);
 | 
			
		||||
                pos += actualRatio;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = numOut; --i >= 0;)
 | 
			
		||||
            {
 | 
			
		||||
                while (pos < actualRatio)
 | 
			
		||||
                {
 | 
			
		||||
                    if (exceeded)
 | 
			
		||||
                    {
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, 0.0);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        pushInterpolationSample (lastInputSamples, *in++);
 | 
			
		||||
 | 
			
		||||
                        if (--available <= 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (wrap > 0)
 | 
			
		||||
                            {
 | 
			
		||||
                                in -= wrap;
 | 
			
		||||
                                available += wrap;
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                exceeded = true;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    pos += 1.0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                pos -= actualRatio;
 | 
			
		||||
                *out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subSamplePos = pos;
 | 
			
		||||
 | 
			
		||||
        if (wrap == 0)
 | 
			
		||||
            return (int) (in - originalIn);
 | 
			
		||||
 | 
			
		||||
        return ((int) (in - originalIn) + wrap) % wrap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename InterpolatorType>
 | 
			
		||||
    static int interpolateAdding (float* lastInputSamples, double& subSamplePos, double actualRatio,
 | 
			
		||||
                                  const float* in, float* out, int numOut, float gain) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        auto pos = subSamplePos;
 | 
			
		||||
 | 
			
		||||
        if (actualRatio == 1.0 && pos == 1.0)
 | 
			
		||||
        {
 | 
			
		||||
            FloatVectorOperations::addWithMultiply (out, in, gain, numOut);
 | 
			
		||||
            pushInterpolationSamples (lastInputSamples, in, numOut);
 | 
			
		||||
            return numOut;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int numUsed = 0;
 | 
			
		||||
 | 
			
		||||
        while (numOut > 0)
 | 
			
		||||
        {
 | 
			
		||||
            while (pos >= 1.0)
 | 
			
		||||
            {
 | 
			
		||||
                pushInterpolationSample (lastInputSamples, in[numUsed++]);
 | 
			
		||||
                pos -= 1.0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            *out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, (float) pos);
 | 
			
		||||
            pos += actualRatio;
 | 
			
		||||
            --numOut;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subSamplePos = pos;
 | 
			
		||||
        return numUsed;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
template <int k>
 | 
			
		||||
struct LagrangeResampleHelper
 | 
			
		||||
{
 | 
			
		||||
    static forcedinline void calc (float& a, float b) noexcept   { a *= b * (1.0f / k); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
struct LagrangeResampleHelper<0>
 | 
			
		||||
{
 | 
			
		||||
    static forcedinline void calc (float&, float) noexcept {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct LagrangeAlgorithm
 | 
			
		||||
{
 | 
			
		||||
    static forcedinline float valueAtOffset (const float* inputs, float offset) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        return calcCoefficient<0> (inputs[4], offset)
 | 
			
		||||
             + calcCoefficient<1> (inputs[3], offset)
 | 
			
		||||
             + calcCoefficient<2> (inputs[2], offset)
 | 
			
		||||
             + calcCoefficient<3> (inputs[1], offset)
 | 
			
		||||
             + calcCoefficient<4> (inputs[0], offset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <int k>
 | 
			
		||||
    static forcedinline float calcCoefficient (float input, float offset) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        LagrangeResampleHelper<0 - k>::calc (input, -2.0f - offset);
 | 
			
		||||
        LagrangeResampleHelper<1 - k>::calc (input, -1.0f - offset);
 | 
			
		||||
        LagrangeResampleHelper<2 - k>::calc (input,  0.0f - offset);
 | 
			
		||||
        LagrangeResampleHelper<3 - k>::calc (input,  1.0f - offset);
 | 
			
		||||
        LagrangeResampleHelper<4 - k>::calc (input,  2.0f - offset);
 | 
			
		||||
        return input;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
LagrangeInterpolator::LagrangeInterpolator() noexcept  { reset(); }
 | 
			
		||||
LagrangeInterpolator::~LagrangeInterpolator() noexcept {}
 | 
			
		||||
 | 
			
		||||
void LagrangeInterpolator::reset() noexcept
 | 
			
		||||
{
 | 
			
		||||
    subSamplePos = 1.0;
 | 
			
		||||
 | 
			
		||||
    for (auto& s : lastInputSamples)
 | 
			
		||||
        s = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int LagrangeInterpolator::process (double actualRatio, const float* in, float* out, int numOut, int available, int wrap) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return interpolate<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int LagrangeInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return interpolate<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int LagrangeInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, int available, int wrap, float gain) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return interpolateAdding<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, available, wrap, gain);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int LagrangeInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return interpolateAdding<LagrangeAlgorithm> (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										143
									
								
								modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,143 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Interpolator for resampling a stream of floats using 4-point lagrange interpolation.
 | 
			
		||||
 | 
			
		||||
    Note that the resampler is stateful, so when there's a break in the continuity
 | 
			
		||||
    of the input stream you're feeding it, you should call reset() before feeding
 | 
			
		||||
    it any new data. And like with any other stateful filter, if you're resampling
 | 
			
		||||
    multiple channels, make sure each one uses its own LagrangeInterpolator
 | 
			
		||||
    object.
 | 
			
		||||
 | 
			
		||||
    @see CatmullRomInterpolator
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  LagrangeInterpolator
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    LagrangeInterpolator() noexcept;
 | 
			
		||||
    ~LagrangeInterpolator() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resets the state of the interpolator.
 | 
			
		||||
        Call this when there's a break in the continuity of the input data stream.
 | 
			
		||||
    */
 | 
			
		||||
    void reset() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resamples a stream of samples.
 | 
			
		||||
 | 
			
		||||
        @param speedRatio       the number of input samples to use for each output sample
 | 
			
		||||
        @param inputSamples     the source data to read from. This must contain at
 | 
			
		||||
                                least (speedRatio * numOutputSamplesToProduce) samples.
 | 
			
		||||
        @param outputSamples    the buffer to write the results into
 | 
			
		||||
        @param numOutputSamplesToProduce    the number of output samples that should be created
 | 
			
		||||
 | 
			
		||||
        @returns the actual number of input samples that were used
 | 
			
		||||
    */
 | 
			
		||||
    int process (double speedRatio,
 | 
			
		||||
                 const float* inputSamples,
 | 
			
		||||
                 float* outputSamples,
 | 
			
		||||
                 int numOutputSamplesToProduce) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resamples a stream of samples.
 | 
			
		||||
 | 
			
		||||
        @param speedRatio       the number of input samples to use for each output sample
 | 
			
		||||
        @param inputSamples     the source data to read from. This must contain at
 | 
			
		||||
                                least (speedRatio * numOutputSamplesToProduce) samples.
 | 
			
		||||
        @param outputSamples    the buffer to write the results into
 | 
			
		||||
        @param numOutputSamplesToProduce    the number of output samples that should be created
 | 
			
		||||
        @param available        the number of available input samples. If it needs more samples
 | 
			
		||||
                                than available, it either wraps back for wrapAround samples, or
 | 
			
		||||
                                it feeds zeroes
 | 
			
		||||
        @param wrapAround       if the stream exceeds available samples, it wraps back for
 | 
			
		||||
                                wrapAround samples. If wrapAround is set to 0, it will feed zeroes.
 | 
			
		||||
 | 
			
		||||
        @returns the actual number of input samples that were used
 | 
			
		||||
    */
 | 
			
		||||
    int process (double speedRatio,
 | 
			
		||||
                 const float* inputSamples,
 | 
			
		||||
                 float* outputSamples,
 | 
			
		||||
                 int numOutputSamplesToProduce,
 | 
			
		||||
                 int available,
 | 
			
		||||
                 int wrapAround) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resamples a stream of samples, adding the results to the output data
 | 
			
		||||
        with a gain.
 | 
			
		||||
 | 
			
		||||
        @param speedRatio       the number of input samples to use for each output sample
 | 
			
		||||
        @param inputSamples     the source data to read from. This must contain at
 | 
			
		||||
                                least (speedRatio * numOutputSamplesToProduce) samples.
 | 
			
		||||
        @param outputSamples    the buffer to write the results to - the result values will be added
 | 
			
		||||
                                to any pre-existing data in this buffer after being multiplied by
 | 
			
		||||
                                the gain factor
 | 
			
		||||
        @param numOutputSamplesToProduce    the number of output samples that should be created
 | 
			
		||||
        @param gain             a gain factor to multiply the resulting samples by before
 | 
			
		||||
                                adding them to the destination buffer
 | 
			
		||||
 | 
			
		||||
        @returns the actual number of input samples that were used
 | 
			
		||||
    */
 | 
			
		||||
    int processAdding (double speedRatio,
 | 
			
		||||
                       const float* inputSamples,
 | 
			
		||||
                       float* outputSamples,
 | 
			
		||||
                       int numOutputSamplesToProduce,
 | 
			
		||||
                       float gain) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resamples a stream of samples, adding the results to the output data
 | 
			
		||||
        with a gain.
 | 
			
		||||
 | 
			
		||||
        @param speedRatio       the number of input samples to use for each output sample
 | 
			
		||||
        @param inputSamples     the source data to read from. This must contain at
 | 
			
		||||
                                least (speedRatio * numOutputSamplesToProduce) samples.
 | 
			
		||||
        @param outputSamples    the buffer to write the results to - the result values will be added
 | 
			
		||||
                                to any pre-existing data in this buffer after being multiplied by
 | 
			
		||||
                                the gain factor
 | 
			
		||||
        @param numOutputSamplesToProduce    the number of output samples that should be created
 | 
			
		||||
        @param available        the number of available input samples. If it needs more samples
 | 
			
		||||
                                than available, it either wraps back for wrapAround samples, or
 | 
			
		||||
                                it feeds zeroes
 | 
			
		||||
        @param wrapAround       if the stream exceeds available samples, it wraps back for
 | 
			
		||||
                                wrapAround samples. If wrapAround is set to 0, it will feed zeroes.
 | 
			
		||||
        @param gain             a gain factor to multiply the resulting samples by before
 | 
			
		||||
                                adding them to the destination buffer
 | 
			
		||||
 | 
			
		||||
        @returns the actual number of input samples that were used
 | 
			
		||||
    */
 | 
			
		||||
    int processAdding (double speedRatio,
 | 
			
		||||
                       const float* inputSamples,
 | 
			
		||||
                       float* outputSamples,
 | 
			
		||||
                       int numOutputSamplesToProduce,
 | 
			
		||||
                       int available,
 | 
			
		||||
                       int wrapAround,
 | 
			
		||||
                       float gain) noexcept;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    float lastInputSamples[5];
 | 
			
		||||
    double subSamplePos;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										313
									
								
								modules/juce_audio_basics/utilities/juce_Reverb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								modules/juce_audio_basics/utilities/juce_Reverb.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,313 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Performs a simple reverb effect on a stream of audio data.
 | 
			
		||||
 | 
			
		||||
    This is a simple stereo reverb, based on the technique and tunings used in FreeVerb.
 | 
			
		||||
    Use setSampleRate() to prepare it, and then call processStereo() or processMono() to
 | 
			
		||||
    apply the reverb to your audio data.
 | 
			
		||||
 | 
			
		||||
    @see ReverbAudioSource
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
class Reverb
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Reverb()
 | 
			
		||||
    {
 | 
			
		||||
        setParameters (Parameters());
 | 
			
		||||
        setSampleRate (44100.0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Holds the parameters being used by a Reverb object. */
 | 
			
		||||
    struct Parameters
 | 
			
		||||
    {
 | 
			
		||||
        float roomSize   = 0.5f;     /**< Room size, 0 to 1.0, where 1.0 is big, 0 is small. */
 | 
			
		||||
        float damping    = 0.5f;     /**< Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped. */
 | 
			
		||||
        float wetLevel   = 0.33f;    /**< Wet level, 0 to 1.0 */
 | 
			
		||||
        float dryLevel   = 0.4f;     /**< Dry level, 0 to 1.0 */
 | 
			
		||||
        float width      = 1.0f;     /**< Reverb width, 0 to 1.0, where 1.0 is very wide. */
 | 
			
		||||
        float freezeMode = 0.0f;     /**< Freeze mode - values < 0.5 are "normal" mode, values > 0.5
 | 
			
		||||
                                          put the reverb into a continuous feedback loop. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the reverb's current parameters. */
 | 
			
		||||
    const Parameters& getParameters() const noexcept    { return parameters; }
 | 
			
		||||
 | 
			
		||||
    /** Applies a new set of parameters to the reverb.
 | 
			
		||||
        Note that this doesn't attempt to lock the reverb, so if you call this in parallel with
 | 
			
		||||
        the process method, you may get artifacts.
 | 
			
		||||
    */
 | 
			
		||||
    void setParameters (const Parameters& newParams)
 | 
			
		||||
    {
 | 
			
		||||
        const float wetScaleFactor = 3.0f;
 | 
			
		||||
        const float dryScaleFactor = 2.0f;
 | 
			
		||||
 | 
			
		||||
        const float wet = newParams.wetLevel * wetScaleFactor;
 | 
			
		||||
        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;
 | 
			
		||||
        updateDamping();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the sample rate that will be used for the reverb.
 | 
			
		||||
        You must call this before the process methods, in order to tell it the correct sample rate.
 | 
			
		||||
    */
 | 
			
		||||
    void setSampleRate (const double sampleRate)
 | 
			
		||||
    {
 | 
			
		||||
        jassert (sampleRate > 0);
 | 
			
		||||
 | 
			
		||||
        static const short combTunings[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; // (at 44100Hz)
 | 
			
		||||
        static const short allPassTunings[] = { 556, 441, 341, 225 };
 | 
			
		||||
        const int stereoSpread = 23;
 | 
			
		||||
        const int intSampleRate = (int) sampleRate;
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numCombs; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            comb[0][i].setSize ((intSampleRate * combTunings[i]) / 44100);
 | 
			
		||||
            comb[1][i].setSize ((intSampleRate * (combTunings[i] + stereoSpread)) / 44100);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numAllPasses; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            allPass[0][i].setSize ((intSampleRate * allPassTunings[i]) / 44100);
 | 
			
		||||
            allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const double smoothTime = 0.01;
 | 
			
		||||
        damping .reset (sampleRate, smoothTime);
 | 
			
		||||
        feedback.reset (sampleRate, smoothTime);
 | 
			
		||||
        dryGain .reset (sampleRate, smoothTime);
 | 
			
		||||
        wetGain1.reset (sampleRate, smoothTime);
 | 
			
		||||
        wetGain2.reset (sampleRate, smoothTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Clears the reverb's buffers. */
 | 
			
		||||
    void reset()
 | 
			
		||||
    {
 | 
			
		||||
        for (int j = 0; j < numChannels; ++j)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < numCombs; ++i)
 | 
			
		||||
                comb[j][i].clear();
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < numAllPasses; ++i)
 | 
			
		||||
                allPass[j][i].clear();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Applies the reverb to two stereo channels of audio data. */
 | 
			
		||||
    void processStereo (float* const left, float* const right, const int numSamples) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        jassert (left != nullptr && right != nullptr);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numSamples; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            const float input = (left[i] + right[i]) * gain;
 | 
			
		||||
            float outL = 0, outR = 0;
 | 
			
		||||
 | 
			
		||||
            const float damp    = damping.getNextValue();
 | 
			
		||||
            const float feedbck = feedback.getNextValue();
 | 
			
		||||
 | 
			
		||||
            for (int j = 0; j < numCombs; ++j)  // accumulate the comb filters in parallel
 | 
			
		||||
            {
 | 
			
		||||
                outL += comb[0][j].process (input, damp, feedbck);
 | 
			
		||||
                outR += comb[1][j].process (input, damp, feedbck);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (int j = 0; j < numAllPasses; ++j)  // run the allpass filters in series
 | 
			
		||||
            {
 | 
			
		||||
                outL = allPass[0][j].process (outL);
 | 
			
		||||
                outR = allPass[1][j].process (outR);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const float dry  = dryGain.getNextValue();
 | 
			
		||||
            const float wet1 = wetGain1.getNextValue();
 | 
			
		||||
            const float wet2 = wetGain2.getNextValue();
 | 
			
		||||
 | 
			
		||||
            left[i]  = outL * wet1 + outR * wet2 + left[i]  * dry;
 | 
			
		||||
            right[i] = outR * wet1 + outL * wet2 + right[i] * dry;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Applies the reverb to a single mono channel of audio data. */
 | 
			
		||||
    void processMono (float* const samples, const int numSamples) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        jassert (samples != nullptr);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numSamples; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            const float input = samples[i] * gain;
 | 
			
		||||
            float output = 0;
 | 
			
		||||
 | 
			
		||||
            const float damp    = damping.getNextValue();
 | 
			
		||||
            const float feedbck = feedback.getNextValue();
 | 
			
		||||
 | 
			
		||||
            for (int j = 0; j < numCombs; ++j)  // accumulate the comb filters in parallel
 | 
			
		||||
                output += comb[0][j].process (input, damp, feedbck);
 | 
			
		||||
 | 
			
		||||
            for (int j = 0; j < numAllPasses; ++j)  // run the allpass filters in series
 | 
			
		||||
                output = allPass[0][j].process (output);
 | 
			
		||||
 | 
			
		||||
            const float dry  = dryGain.getNextValue();
 | 
			
		||||
            const float wet1 = wetGain1.getNextValue();
 | 
			
		||||
 | 
			
		||||
            samples[i] = output * wet1 + samples[i] * dry;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    static bool isFrozen (const float freezeMode) noexcept  { return freezeMode >= 0.5f; }
 | 
			
		||||
 | 
			
		||||
    void updateDamping() noexcept
 | 
			
		||||
    {
 | 
			
		||||
        const float roomScaleFactor = 0.28f;
 | 
			
		||||
        const float roomOffset = 0.7f;
 | 
			
		||||
        const float dampScaleFactor = 0.4f;
 | 
			
		||||
 | 
			
		||||
        if (isFrozen (parameters.freezeMode))
 | 
			
		||||
            setDamping (0.0f, 1.0f);
 | 
			
		||||
        else
 | 
			
		||||
            setDamping (parameters.damping * dampScaleFactor,
 | 
			
		||||
                        parameters.roomSize * roomScaleFactor + roomOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        damping.setTargetValue (dampingToUse);
 | 
			
		||||
        feedback.setTargetValue (roomSizeToUse);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class CombFilter
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        CombFilter() noexcept {}
 | 
			
		||||
 | 
			
		||||
        void setSize (const int size)
 | 
			
		||||
        {
 | 
			
		||||
            if (size != bufferSize)
 | 
			
		||||
            {
 | 
			
		||||
                bufferIndex = 0;
 | 
			
		||||
                buffer.malloc (size);
 | 
			
		||||
                bufferSize = size;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            clear();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void clear() noexcept
 | 
			
		||||
        {
 | 
			
		||||
            last = 0;
 | 
			
		||||
            buffer.clear ((size_t) bufferSize);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        float process (const float input, const float damp, const float feedbackLevel) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            const float output = buffer[bufferIndex];
 | 
			
		||||
            last = (output * (1.0f - damp)) + (last * damp);
 | 
			
		||||
            JUCE_UNDENORMALISE (last);
 | 
			
		||||
 | 
			
		||||
            float temp = input + (last * feedbackLevel);
 | 
			
		||||
            JUCE_UNDENORMALISE (temp);
 | 
			
		||||
            buffer[bufferIndex] = temp;
 | 
			
		||||
            bufferIndex = (bufferIndex + 1) % bufferSize;
 | 
			
		||||
            return output;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        HeapBlock<float> buffer;
 | 
			
		||||
        int bufferSize = 0, bufferIndex = 0;
 | 
			
		||||
        float last = 0.0f;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE (CombFilter)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class AllPassFilter
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        AllPassFilter() noexcept {}
 | 
			
		||||
 | 
			
		||||
        void setSize (const int size)
 | 
			
		||||
        {
 | 
			
		||||
            if (size != bufferSize)
 | 
			
		||||
            {
 | 
			
		||||
                bufferIndex = 0;
 | 
			
		||||
                buffer.malloc (size);
 | 
			
		||||
                bufferSize = size;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            clear();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void clear() noexcept
 | 
			
		||||
        {
 | 
			
		||||
            buffer.clear ((size_t) bufferSize);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        float process (const float input) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            const float bufferedValue = buffer [bufferIndex];
 | 
			
		||||
            float temp = input + (bufferedValue * 0.5f);
 | 
			
		||||
            JUCE_UNDENORMALISE (temp);
 | 
			
		||||
            buffer [bufferIndex] = temp;
 | 
			
		||||
            bufferIndex = (bufferIndex + 1) % bufferSize;
 | 
			
		||||
            return bufferedValue - input;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        HeapBlock<float> buffer;
 | 
			
		||||
        int bufferSize = 0, bufferIndex = 0;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE (AllPassFilter)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum { numCombs = 8, numAllPasses = 4, numChannels = 2 };
 | 
			
		||||
 | 
			
		||||
    Parameters parameters;
 | 
			
		||||
    float gain;
 | 
			
		||||
 | 
			
		||||
    CombFilter comb [numChannels][numCombs];
 | 
			
		||||
    AllPassFilter allPass [numChannels][numAllPasses];
 | 
			
		||||
 | 
			
		||||
    SmoothedValue<float> damping, feedback, dryGain, wetGain1, wetGain2;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										92
									
								
								modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,92 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2018 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
#if JUCE_UNIT_TESTS
 | 
			
		||||
 | 
			
		||||
static CommonSmoothedValueTests <SmoothedValue<float, ValueSmoothingTypes::Linear>> commonLinearSmoothedValueTests;
 | 
			
		||||
static CommonSmoothedValueTests <SmoothedValue<float, ValueSmoothingTypes::Multiplicative>> commonMultiplicativeSmoothedValueTests;
 | 
			
		||||
 | 
			
		||||
class SmoothedValueTests  : public UnitTest
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    SmoothedValueTests()
 | 
			
		||||
        : UnitTest ("SmoothedValueTests", "SmoothedValues")
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    void runTest() override
 | 
			
		||||
    {
 | 
			
		||||
        beginTest ("Linear moving target");
 | 
			
		||||
        {
 | 
			
		||||
            SmoothedValue<float, ValueSmoothingTypes::Linear> sv;
 | 
			
		||||
 | 
			
		||||
            sv.reset (12);
 | 
			
		||||
            float initialValue = 0.0f;
 | 
			
		||||
            sv.setCurrentAndTargetValue (initialValue);
 | 
			
		||||
            sv.setTargetValue (1.0f);
 | 
			
		||||
 | 
			
		||||
            auto delta = sv.getNextValue() - initialValue;
 | 
			
		||||
 | 
			
		||||
            sv.skip (6);
 | 
			
		||||
 | 
			
		||||
            auto newInitialValue = sv.getCurrentValue();
 | 
			
		||||
            sv.setTargetValue (newInitialValue + 2.0f);
 | 
			
		||||
            auto doubleDelta = sv.getNextValue() - newInitialValue;
 | 
			
		||||
 | 
			
		||||
            expectWithinAbsoluteError (doubleDelta, delta * 2.0f, 1.0e-7f);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        beginTest ("Multiplicative curve");
 | 
			
		||||
        {
 | 
			
		||||
            SmoothedValue<double, ValueSmoothingTypes::Multiplicative> sv;
 | 
			
		||||
 | 
			
		||||
            auto numSamples = 12;
 | 
			
		||||
            AudioBuffer<double> values (2, numSamples + 1);
 | 
			
		||||
 | 
			
		||||
            sv.reset (numSamples);
 | 
			
		||||
            sv.setCurrentAndTargetValue (1.0);
 | 
			
		||||
            sv.setTargetValue (2.0f);
 | 
			
		||||
 | 
			
		||||
            values.setSample (0, 0, sv.getCurrentValue());
 | 
			
		||||
 | 
			
		||||
            for (int i = 1; i < values.getNumSamples(); ++i)
 | 
			
		||||
                values.setSample (0, i, sv.getNextValue());
 | 
			
		||||
 | 
			
		||||
            sv.setTargetValue (1.0f);
 | 
			
		||||
            values.setSample (1, values.getNumSamples() - 1, sv.getCurrentValue());
 | 
			
		||||
 | 
			
		||||
            for (int i = values.getNumSamples() - 2; i >= 0 ; --i)
 | 
			
		||||
                values.setSample (1, i, sv.getNextValue());
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < values.getNumSamples(); ++i)
 | 
			
		||||
                expectWithinAbsoluteError (values.getSample (0, i), values.getSample (1, i), 1.0e-9);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SmoothedValueTests smoothedValueTests;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										622
									
								
								modules/juce_audio_basics/utilities/juce_SmoothedValue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										622
									
								
								modules/juce_audio_basics/utilities/juce_SmoothedValue.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,622 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A base class for the smoothed value classes.
 | 
			
		||||
 | 
			
		||||
    This class is used to provide common functionality to the SmoothedValue and
 | 
			
		||||
    dsp::LogRampedValue classes.
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
template <typename SmoothedValueType>
 | 
			
		||||
class SmoothedValueBase
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    template <typename T> struct FloatTypeHelper;
 | 
			
		||||
 | 
			
		||||
    template <template <typename> class SmoothedValueClass, typename FloatType>
 | 
			
		||||
    struct FloatTypeHelper <SmoothedValueClass <FloatType>>
 | 
			
		||||
    {
 | 
			
		||||
        using Type = FloatType;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    template <template <typename, typename> class SmoothedValueClass, typename FloatType, typename SmoothingType>
 | 
			
		||||
    struct FloatTypeHelper <SmoothedValueClass <FloatType, SmoothingType>>
 | 
			
		||||
    {
 | 
			
		||||
        using Type = FloatType;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    using FloatType = typename FloatTypeHelper<SmoothedValueType>::Type;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Constructor. */
 | 
			
		||||
    SmoothedValueBase() = default;
 | 
			
		||||
 | 
			
		||||
    virtual ~SmoothedValueBase() {}
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns true if the current value is currently being interpolated. */
 | 
			
		||||
    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; }
 | 
			
		||||
 | 
			
		||||
    /** Sets the current value and the target value.
 | 
			
		||||
        @param newValue    the new value to take
 | 
			
		||||
    */
 | 
			
		||||
    void setCurrentAndTargetValue (FloatType newValue)
 | 
			
		||||
    {
 | 
			
		||||
        target = currentValue = newValue;
 | 
			
		||||
        countdown = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Applies a smoothed gain to a stream of samples
 | 
			
		||||
        S[i] *= gain
 | 
			
		||||
        @param samples Pointer to a raw array of samples
 | 
			
		||||
        @param numSamples Length of array of samples
 | 
			
		||||
    */
 | 
			
		||||
    void applyGain (FloatType* samples, int numSamples) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        jassert (numSamples >= 0);
 | 
			
		||||
 | 
			
		||||
        if (isSmoothing())
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < numSamples; ++i)
 | 
			
		||||
                samples[i] *= getNextSmoothedValue();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            FloatVectorOperations::multiply (samples, target, numSamples);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Computes output as a smoothed gain applied to a stream of samples.
 | 
			
		||||
        Sout[i] = Sin[i] * gain
 | 
			
		||||
        @param samplesOut A pointer to a raw array of output samples
 | 
			
		||||
        @param samplesIn  A pointer to a raw array of input samples
 | 
			
		||||
        @param numSamples The length of the array of samples
 | 
			
		||||
    */
 | 
			
		||||
    void applyGain (FloatType* samplesOut, const FloatType* samplesIn, int numSamples) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        jassert (numSamples >= 0);
 | 
			
		||||
 | 
			
		||||
        if (isSmoothing())
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < numSamples; ++i)
 | 
			
		||||
                samplesOut[i] = samplesIn[i] * getNextSmoothedValue();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Applies a smoothed gain to a buffer */
 | 
			
		||||
    void applyGain (AudioBuffer<FloatType>& buffer, int numSamples) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        jassert (numSamples >= 0);
 | 
			
		||||
 | 
			
		||||
        if (isSmoothing())
 | 
			
		||||
        {
 | 
			
		||||
            if (buffer.getNumChannels() == 1)
 | 
			
		||||
            {
 | 
			
		||||
                auto* samples = buffer.getWritePointer (0);
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < numSamples; ++i)
 | 
			
		||||
                    samples[i] *= getNextSmoothedValue();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                for (auto i = 0; i < numSamples; ++i)
 | 
			
		||||
                {
 | 
			
		||||
                    auto gain = getNextSmoothedValue();
 | 
			
		||||
 | 
			
		||||
                    for (int channel = 0; channel < buffer.getNumChannels(); channel++)
 | 
			
		||||
                        buffer.setSample (channel, i, buffer.getSample (channel, i) * gain);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            buffer.applyGain (0, numSamples, target);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    FloatType getNextSmoothedValue() noexcept
 | 
			
		||||
    {
 | 
			
		||||
        return static_cast <SmoothedValueType*> (this)->getNextValue();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    FloatType currentValue = 0;
 | 
			
		||||
    FloatType target = currentValue;
 | 
			
		||||
    int countdown = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A namespace containing a set of types used for specifying the smoothing
 | 
			
		||||
    behaviour of the SmoothedValue class.
 | 
			
		||||
 | 
			
		||||
    For example:
 | 
			
		||||
    @code
 | 
			
		||||
    SmoothedValue<float, ValueSmoothingTypes::Multiplicative> frequency (1.0f);
 | 
			
		||||
    @endcode
 | 
			
		||||
*/
 | 
			
		||||
namespace ValueSmoothingTypes
 | 
			
		||||
{
 | 
			
		||||
    /** Used to indicate a linear smoothing between values. */
 | 
			
		||||
    struct Linear {};
 | 
			
		||||
 | 
			
		||||
    /** Used to indicate a smoothing between multiplicative values. */
 | 
			
		||||
    struct Multiplicative {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A utility class for values that need smoothing to avoid audio glitches.
 | 
			
		||||
 | 
			
		||||
    A ValueSmoothingTypes::Linear template parameter selects linear smoothing,
 | 
			
		||||
    which increments the SmoothedValue linearly towards its target value.
 | 
			
		||||
 | 
			
		||||
    @code
 | 
			
		||||
    SmoothedValue<float, ValueSmoothingTypes::Linear> yourSmoothedValue;
 | 
			
		||||
    @endcode
 | 
			
		||||
 | 
			
		||||
    A ValueSmoothingTypes::Multiplicative template parameter selects
 | 
			
		||||
    multiplicative smoothing increments towards the target value.
 | 
			
		||||
 | 
			
		||||
    @code
 | 
			
		||||
    SmoothedValue<float, ValueSmoothingTypes::Multiplicative> yourSmoothedValue;
 | 
			
		||||
    @endcode
 | 
			
		||||
 | 
			
		||||
    Multiplicative smoothing is useful when you are dealing with
 | 
			
		||||
    exponential/logarithmic values like volume in dB or frequency in Hz. For
 | 
			
		||||
    example a 12 step ramp from 440.0 Hz (A4) to 880.0 Hz (A5) will increase the
 | 
			
		||||
    frequency with an equal temperament tuning across the octave. A 10 step
 | 
			
		||||
    smoothing from 1.0 (0 dB) to 3.16228 (10 dB) will increase the value in
 | 
			
		||||
    increments of 1 dB.
 | 
			
		||||
 | 
			
		||||
    Note that when you are using multiplicative smoothing you cannot ever reach a
 | 
			
		||||
    target value of zero!
 | 
			
		||||
 | 
			
		||||
    @tags{Audio}
 | 
			
		||||
*/
 | 
			
		||||
template <typename FloatType, typename SmoothingType = ValueSmoothingTypes::Linear>
 | 
			
		||||
class SmoothedValue   : public SmoothedValueBase <SmoothedValue <FloatType, SmoothingType>>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Constructor. */
 | 
			
		||||
    SmoothedValue() noexcept
 | 
			
		||||
        : SmoothedValue ((FloatType) (std::is_same<SmoothingType, ValueSmoothingTypes::Linear>::value ? 0 : 1))
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Constructor. */
 | 
			
		||||
    SmoothedValue (FloatType initialValue) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        // Multiplicative smoothed values cannot ever reach 0!
 | 
			
		||||
        jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && initialValue == 0));
 | 
			
		||||
 | 
			
		||||
        // Visual Studio can't handle base class initialisation with CRTP
 | 
			
		||||
        this->currentValue = initialValue;
 | 
			
		||||
        this->target = this->currentValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Reset to a new sample rate and ramp length.
 | 
			
		||||
        @param sampleRate           The sample rate
 | 
			
		||||
        @param rampLengthInSeconds  The duration of the ramp in seconds
 | 
			
		||||
    */
 | 
			
		||||
    void reset (double sampleRate, double rampLengthInSeconds) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
 | 
			
		||||
        reset ((int) std::floor (rampLengthInSeconds * sampleRate));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 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;
 | 
			
		||||
        this->setCurrentAndTargetValue (this->target);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Set the next value to ramp towards.
 | 
			
		||||
        @param newValue     The new target value
 | 
			
		||||
    */
 | 
			
		||||
    void setTargetValue (FloatType newValue) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        if (newValue == this->target)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (stepsToTarget <= 0)
 | 
			
		||||
        {
 | 
			
		||||
            this->setCurrentAndTargetValue (newValue);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Multiplicative smoothed values cannot ever reach 0!
 | 
			
		||||
        jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && newValue == 0));
 | 
			
		||||
 | 
			
		||||
        this->target = newValue;
 | 
			
		||||
        this->countdown = stepsToTarget;
 | 
			
		||||
 | 
			
		||||
        setStepSize();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Compute the next value.
 | 
			
		||||
        @returns Smoothed value
 | 
			
		||||
    */
 | 
			
		||||
    FloatType getNextValue() noexcept
 | 
			
		||||
    {
 | 
			
		||||
        if (! this->isSmoothing())
 | 
			
		||||
            return this->target;
 | 
			
		||||
 | 
			
		||||
        --(this->countdown);
 | 
			
		||||
 | 
			
		||||
        if (this->isSmoothing())
 | 
			
		||||
            setNextValue();
 | 
			
		||||
        else
 | 
			
		||||
            this->currentValue = this->target;
 | 
			
		||||
 | 
			
		||||
        return this->currentValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Skip the next numSamples samples.
 | 
			
		||||
        This is identical to calling getNextValue numSamples times. It returns
 | 
			
		||||
        the new current value.
 | 
			
		||||
        @see getNextValue
 | 
			
		||||
    */
 | 
			
		||||
    FloatType skip (int numSamples) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        if (numSamples >= this->countdown)
 | 
			
		||||
        {
 | 
			
		||||
            this->setCurrentAndTargetValue (this->target);
 | 
			
		||||
            return this->target;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        skipCurrentValue (numSamples);
 | 
			
		||||
 | 
			
		||||
        this->countdown -= numSamples;
 | 
			
		||||
        return this->currentValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** THIS FUNCTION IS DEPRECATED.
 | 
			
		||||
 | 
			
		||||
        Use `setTargetValue (float)` and `setCurrentAndTargetValue()` instead:
 | 
			
		||||
 | 
			
		||||
        lsv.setValue (x, false); -> lsv.setTargetValue (x);
 | 
			
		||||
        lsv.setValue (x, true);  -> lsv.setCurrentAndTargetValue (x);
 | 
			
		||||
 | 
			
		||||
        @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,
 | 
			
		||||
    {
 | 
			
		||||
        if (force)
 | 
			
		||||
        {
 | 
			
		||||
            this->setCurrentAndTargetValue (newValue);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setTargetValue (newValue);
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    using LinearVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Linear>::value, void>::type;
 | 
			
		||||
 | 
			
		||||
    template <typename T>
 | 
			
		||||
    using MultiplicativeVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Multiplicative>::value, void>::type;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    template <typename T = SmoothingType>
 | 
			
		||||
    LinearVoid<T> setStepSize() noexcept
 | 
			
		||||
    {
 | 
			
		||||
        step = (this->target - this->currentValue) / (FloatType) this->countdown;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename T = SmoothingType>
 | 
			
		||||
    MultiplicativeVoid<T> setStepSize()
 | 
			
		||||
    {
 | 
			
		||||
        step = std::exp ((std::log (std::abs (this->target)) - std::log (std::abs (this->currentValue))) / this->countdown);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    template <typename T = SmoothingType>
 | 
			
		||||
    LinearVoid<T> setNextValue() noexcept
 | 
			
		||||
    {
 | 
			
		||||
        this->currentValue += step;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename T = SmoothingType>
 | 
			
		||||
    MultiplicativeVoid<T> setNextValue() noexcept
 | 
			
		||||
    {
 | 
			
		||||
        this->currentValue *= step;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    template <typename T = SmoothingType>
 | 
			
		||||
    LinearVoid<T> skipCurrentValue (int numSamples) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        this->currentValue += step * (FloatType) numSamples;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template <typename T = SmoothingType>
 | 
			
		||||
    MultiplicativeVoid<T> skipCurrentValue (int numSamples)
 | 
			
		||||
    {
 | 
			
		||||
        this->currentValue *= (FloatType) std::pow (step, numSamples);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    FloatType step = FloatType();
 | 
			
		||||
    int stepsToTarget = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename FloatType>
 | 
			
		||||
using LinearSmoothedValue = SmoothedValue <FloatType, ValueSmoothingTypes::Linear>;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
//==============================================================================
 | 
			
		||||
#if JUCE_UNIT_TESTS
 | 
			
		||||
 | 
			
		||||
template <class SmoothedValueType>
 | 
			
		||||
class CommonSmoothedValueTests  : public UnitTest
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    CommonSmoothedValueTests()
 | 
			
		||||
        : UnitTest ("CommonSmoothedValueTests", "SmoothedValues")
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    void runTest() override
 | 
			
		||||
    {
 | 
			
		||||
        beginTest ("Initial state");
 | 
			
		||||
        {
 | 
			
		||||
            SmoothedValueType sv;
 | 
			
		||||
 | 
			
		||||
            auto value = sv.getCurrentValue();
 | 
			
		||||
            expectEquals (sv.getTargetValue(), value);
 | 
			
		||||
 | 
			
		||||
            sv.getNextValue();
 | 
			
		||||
            expectEquals (sv.getCurrentValue(), value);
 | 
			
		||||
            expect (! sv.isSmoothing());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        beginTest ("Resetting");
 | 
			
		||||
        {
 | 
			
		||||
            auto initialValue = 15.0f;
 | 
			
		||||
 | 
			
		||||
            SmoothedValueType sv (initialValue);
 | 
			
		||||
            sv.reset (3);
 | 
			
		||||
            expectEquals (sv.getCurrentValue(), initialValue);
 | 
			
		||||
 | 
			
		||||
            auto targetValue = initialValue + 1.0f;
 | 
			
		||||
            sv.setTargetValue (targetValue);
 | 
			
		||||
            expectEquals (sv.getTargetValue(), targetValue);
 | 
			
		||||
            expectEquals (sv.getCurrentValue(), initialValue);
 | 
			
		||||
            expect (sv.isSmoothing());
 | 
			
		||||
 | 
			
		||||
            auto currentValue = sv.getNextValue();
 | 
			
		||||
            expect (currentValue > initialValue);
 | 
			
		||||
            expectEquals (sv.getCurrentValue(), currentValue);
 | 
			
		||||
            expectEquals (sv.getTargetValue(), targetValue);
 | 
			
		||||
            expect (sv.isSmoothing());
 | 
			
		||||
 | 
			
		||||
            sv.reset (5);
 | 
			
		||||
 | 
			
		||||
            expectEquals (sv.getCurrentValue(), targetValue);
 | 
			
		||||
            expectEquals (sv.getTargetValue(),  targetValue);
 | 
			
		||||
            expect (! sv.isSmoothing());
 | 
			
		||||
 | 
			
		||||
            sv.getNextValue();
 | 
			
		||||
            expectEquals (sv.getCurrentValue(), targetValue);
 | 
			
		||||
 | 
			
		||||
            sv.setTargetValue (1.5f);
 | 
			
		||||
            sv.getNextValue();
 | 
			
		||||
 | 
			
		||||
            float newStart = 0.2f;
 | 
			
		||||
            sv.setCurrentAndTargetValue (newStart);
 | 
			
		||||
            expectEquals (sv.getNextValue(), newStart);
 | 
			
		||||
            expectEquals (sv.getTargetValue(), newStart);
 | 
			
		||||
            expectEquals (sv.getCurrentValue(), newStart);
 | 
			
		||||
            expect (! sv.isSmoothing());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        beginTest ("Sample rate");
 | 
			
		||||
        {
 | 
			
		||||
            SmoothedValueType svSamples { 3.0f };
 | 
			
		||||
            auto svTime = svSamples;
 | 
			
		||||
 | 
			
		||||
            auto numSamples = 12;
 | 
			
		||||
 | 
			
		||||
            svSamples.reset (numSamples);
 | 
			
		||||
            svTime.reset (numSamples * 2, 1.0);
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < numSamples; ++i)
 | 
			
		||||
            {
 | 
			
		||||
                svTime.skip (1);
 | 
			
		||||
                expectWithinAbsoluteError (svSamples.getNextValue(),
 | 
			
		||||
                                           svTime.getNextValue(),
 | 
			
		||||
                                           1.0e-7f);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        beginTest ("Block processing");
 | 
			
		||||
        {
 | 
			
		||||
            SmoothedValueType sv (1.0f);
 | 
			
		||||
 | 
			
		||||
            sv.reset (12);
 | 
			
		||||
            sv.setTargetValue (2.0f);
 | 
			
		||||
 | 
			
		||||
            const auto numSamples = 15;
 | 
			
		||||
 | 
			
		||||
            AudioBuffer<float> referenceData (1, numSamples);
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < numSamples; ++i)
 | 
			
		||||
                referenceData.setSample (0, i, sv.getNextValue());
 | 
			
		||||
 | 
			
		||||
            expect (referenceData.getSample (0, 0) > 0);
 | 
			
		||||
            expect (referenceData.getSample (0, 10) < sv.getTargetValue());
 | 
			
		||||
            expectWithinAbsoluteError (referenceData.getSample (0, 11),
 | 
			
		||||
                                       sv.getTargetValue(),
 | 
			
		||||
                                       1.0e-7f);
 | 
			
		||||
 | 
			
		||||
            auto getUnitData = [] (int numSamplesToGenerate)
 | 
			
		||||
            {
 | 
			
		||||
                AudioBuffer<float> result (1, numSamplesToGenerate);
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < numSamplesToGenerate; ++i)
 | 
			
		||||
                    result.setSample (0, i, 1.0f);
 | 
			
		||||
 | 
			
		||||
                return result;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            auto compareData = [this](const AudioBuffer<float>& test,
 | 
			
		||||
                                      const AudioBuffer<float>& reference)
 | 
			
		||||
            {
 | 
			
		||||
                for (int i = 0; i < test.getNumSamples(); ++i)
 | 
			
		||||
                    expectWithinAbsoluteError (test.getSample (0, i),
 | 
			
		||||
                                               reference.getSample (0, i),
 | 
			
		||||
                                               1.0e-7f);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            auto testData = getUnitData (numSamples);
 | 
			
		||||
            sv.setCurrentAndTargetValue (1.0f);
 | 
			
		||||
            sv.setTargetValue (2.0f);
 | 
			
		||||
            sv.applyGain (testData.getWritePointer (0), numSamples);
 | 
			
		||||
            compareData (testData, referenceData);
 | 
			
		||||
 | 
			
		||||
            testData = getUnitData (numSamples);
 | 
			
		||||
            AudioBuffer<float> destData (1, numSamples);
 | 
			
		||||
            sv.setCurrentAndTargetValue (1.0f);
 | 
			
		||||
            sv.setTargetValue (2.0f);
 | 
			
		||||
            sv.applyGain (destData.getWritePointer (0),
 | 
			
		||||
                           testData.getReadPointer (0),
 | 
			
		||||
                           numSamples);
 | 
			
		||||
            compareData (destData, referenceData);
 | 
			
		||||
            compareData (testData, getUnitData (numSamples));
 | 
			
		||||
 | 
			
		||||
            testData = getUnitData (numSamples);
 | 
			
		||||
            sv.setCurrentAndTargetValue (1.0f);
 | 
			
		||||
            sv.setTargetValue (2.0f);
 | 
			
		||||
            sv.applyGain (testData, numSamples);
 | 
			
		||||
            compareData (testData, referenceData);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        beginTest ("Skip");
 | 
			
		||||
        {
 | 
			
		||||
            SmoothedValueType sv;
 | 
			
		||||
 | 
			
		||||
            sv.reset (12);
 | 
			
		||||
            sv.setCurrentAndTargetValue (1.0f);
 | 
			
		||||
            sv.setTargetValue (2.0f);
 | 
			
		||||
 | 
			
		||||
            Array<float> reference;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < 15; ++i)
 | 
			
		||||
                reference.add (sv.getNextValue());
 | 
			
		||||
 | 
			
		||||
            sv.setCurrentAndTargetValue (1.0f);
 | 
			
		||||
            sv.setTargetValue (2.0f);
 | 
			
		||||
 | 
			
		||||
            expectWithinAbsoluteError (sv.skip (1), reference[0], 1.0e-6f);
 | 
			
		||||
            expectWithinAbsoluteError (sv.skip (1), reference[1], 1.0e-6f);
 | 
			
		||||
            expectWithinAbsoluteError (sv.skip (2), reference[3], 1.0e-6f);
 | 
			
		||||
            sv.skip (3);
 | 
			
		||||
            expectWithinAbsoluteError (sv.getCurrentValue(), reference[6], 1.0e-6f);
 | 
			
		||||
            expectEquals (sv.skip (300), sv.getTargetValue());
 | 
			
		||||
            expectEquals (sv.getCurrentValue(), sv.getTargetValue());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        beginTest ("Negative");
 | 
			
		||||
        {
 | 
			
		||||
            SmoothedValueType sv;
 | 
			
		||||
 | 
			
		||||
            auto numValues = 12;
 | 
			
		||||
            sv.reset (numValues);
 | 
			
		||||
 | 
			
		||||
            std::vector<std::pair<float, float>> ranges = { { -1.0f, -2.0f },
 | 
			
		||||
                                                            { -100.0f, -3.0f } };
 | 
			
		||||
 | 
			
		||||
            for (auto range : ranges)
 | 
			
		||||
            {
 | 
			
		||||
                auto start = range.first, end = range.second;
 | 
			
		||||
 | 
			
		||||
                sv.setCurrentAndTargetValue (start);
 | 
			
		||||
                sv.setTargetValue (end);
 | 
			
		||||
 | 
			
		||||
                auto val = sv.skip (numValues / 2);
 | 
			
		||||
 | 
			
		||||
                if (end > start)
 | 
			
		||||
                    expect (val > start && val < end);
 | 
			
		||||
                else
 | 
			
		||||
                    expect (val < start && val > end);
 | 
			
		||||
 | 
			
		||||
                auto nextVal = sv.getNextValue();
 | 
			
		||||
                expect (end > start ? (nextVal > val) : (nextVal < val));
 | 
			
		||||
 | 
			
		||||
                auto endVal = sv.skip (500);
 | 
			
		||||
                expectEquals (endVal, end);
 | 
			
		||||
                expectEquals (sv.getNextValue(), end);
 | 
			
		||||
                expectEquals (sv.getCurrentValue(), end);
 | 
			
		||||
 | 
			
		||||
                sv.setCurrentAndTargetValue (start);
 | 
			
		||||
                sv.setTargetValue (end);
 | 
			
		||||
 | 
			
		||||
                SmoothedValueType positiveSv { -start };
 | 
			
		||||
                positiveSv.reset (numValues);
 | 
			
		||||
                positiveSv.setTargetValue (-end);
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < numValues + 2; ++i)
 | 
			
		||||
                    expectEquals (sv.getNextValue(), -positiveSv.getNextValue());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
		Reference in New Issue
	
	Block a user