25bd5d8adb
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
270 lines
8.0 KiB
C++
270 lines
8.0 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library.
|
|
Copyright (c) 2020 - Raw Material Software Limited
|
|
|
|
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.
|
|
|
|
@tags{Audio}
|
|
*/
|
|
class JUCE_API ADSR
|
|
{
|
|
public:
|
|
//==============================================================================
|
|
ADSR()
|
|
{
|
|
recalculateRates();
|
|
}
|
|
|
|
//==============================================================================
|
|
/**
|
|
Holds the parameters being used by an ADSR object.
|
|
|
|
@tags{Audio}
|
|
*/
|
|
struct JUCE_API Parameters
|
|
{
|
|
Parameters() = default;
|
|
|
|
Parameters (float attackTimeSeconds,
|
|
float decayTimeSeconds,
|
|
float sustainLevel,
|
|
float releaseTimeSeconds)
|
|
: attack (attackTimeSeconds),
|
|
decay (decayTimeSeconds),
|
|
sustain (sustainLevel),
|
|
release (releaseTimeSeconds)
|
|
{
|
|
}
|
|
|
|
float attack = 0.1f, decay = 0.1f, sustain = 1.0f, 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)
|
|
{
|
|
// need to call setSampleRate() first!
|
|
jassert (sampleRate > 0.0);
|
|
|
|
parameters = newParameters;
|
|
recalculateRates();
|
|
}
|
|
|
|
/** Returns the parameters currently being used by an ADSR object.
|
|
|
|
@see setParameters
|
|
*/
|
|
const Parameters& getParameters() const noexcept { return parameters; }
|
|
|
|
/** Returns true if the envelope is in its attack, decay, sustain or release stage. */
|
|
bool isActive() const noexcept { return state != 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 newSampleRate) noexcept
|
|
{
|
|
jassert (newSampleRate > 0.0);
|
|
sampleRate = newSampleRate;
|
|
}
|
|
|
|
//==============================================================================
|
|
/** Resets the envelope to an idle state. */
|
|
void reset() noexcept
|
|
{
|
|
envelopeVal = 0.0f;
|
|
state = State::idle;
|
|
}
|
|
|
|
/** Starts the attack phase of the envelope. */
|
|
void noteOn() noexcept
|
|
{
|
|
if (attackRate > 0.0f)
|
|
{
|
|
state = State::attack;
|
|
}
|
|
else if (decayRate > 0.0f)
|
|
{
|
|
envelopeVal = 1.0f;
|
|
state = State::decay;
|
|
}
|
|
else
|
|
{
|
|
state = State::sustain;
|
|
}
|
|
}
|
|
|
|
/** Starts the release phase of the envelope. */
|
|
void noteOff() noexcept
|
|
{
|
|
if (state != State::idle)
|
|
{
|
|
if (parameters.release > 0.0f)
|
|
{
|
|
releaseRate = (float) (envelopeVal / (parameters.release * sampleRate));
|
|
state = State::release;
|
|
}
|
|
else
|
|
{
|
|
reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
/** Returns the next sample value for an ADSR object.
|
|
|
|
@see applyEnvelopeToBuffer
|
|
*/
|
|
float getNextSample() noexcept
|
|
{
|
|
if (state == State::idle)
|
|
return 0.0f;
|
|
|
|
if (state == State::attack)
|
|
{
|
|
envelopeVal += attackRate;
|
|
|
|
if (envelopeVal >= 1.0f)
|
|
{
|
|
envelopeVal = 1.0f;
|
|
goToNextState();
|
|
}
|
|
}
|
|
else if (state == State::decay)
|
|
{
|
|
envelopeVal -= decayRate;
|
|
|
|
if (envelopeVal <= parameters.sustain)
|
|
{
|
|
envelopeVal = parameters.sustain;
|
|
goToNextState();
|
|
}
|
|
}
|
|
else if (state == State::sustain)
|
|
{
|
|
envelopeVal = parameters.sustain;
|
|
}
|
|
else if (state == State::release)
|
|
{
|
|
envelopeVal -= releaseRate;
|
|
|
|
if (envelopeVal <= 0.0f)
|
|
goToNextState();
|
|
}
|
|
|
|
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());
|
|
|
|
if (state == State::idle)
|
|
{
|
|
buffer.clear (startSample, numSamples);
|
|
return;
|
|
}
|
|
|
|
if (state == State::sustain)
|
|
{
|
|
buffer.applyGain (startSample, numSamples, parameters.sustain);
|
|
return;
|
|
}
|
|
|
|
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 recalculateRates() noexcept
|
|
{
|
|
auto getRate = [] (float distance, float timeInSeconds, double sr)
|
|
{
|
|
return timeInSeconds > 0.0f ? (float) (distance / (timeInSeconds * sr)) : -1.0f;
|
|
};
|
|
|
|
attackRate = getRate (1.0f, parameters.attack, sampleRate);
|
|
decayRate = getRate (1.0f - parameters.sustain, parameters.decay, sampleRate);
|
|
releaseRate = getRate (parameters.sustain, parameters.release, sampleRate);
|
|
|
|
if ((state == State::attack && attackRate <= 0.0f)
|
|
|| (state == State::decay && (decayRate <= 0.0f || envelopeVal <= parameters.sustain))
|
|
|| (state == State::release && releaseRate <= 0.0f))
|
|
{
|
|
goToNextState();
|
|
}
|
|
}
|
|
|
|
void goToNextState() noexcept
|
|
{
|
|
if (state == State::attack)
|
|
state = (decayRate > 0.0f ? State::decay : State::sustain);
|
|
else if (state == State::decay)
|
|
state = State::sustain;
|
|
else if (state == State::release)
|
|
reset();
|
|
}
|
|
|
|
//==============================================================================
|
|
enum class State { idle, attack, decay, sustain, release };
|
|
|
|
State state = State::idle;
|
|
Parameters parameters;
|
|
|
|
double sampleRate = 44100.0;
|
|
float envelopeVal = 0.0f, attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f;
|
|
};
|
|
|
|
} // namespace juce
|