migrating to the latest JUCE version

This commit is contained in:
2022-11-04 23:11:33 +01:00
committed by Nikolai Rodionov
parent 4257a0f8ba
commit faf8f18333
2796 changed files with 888518 additions and 784244 deletions

View File

@ -1,159 +1,159 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
Adds a DC offset (voltage bias) to the audio samples.
This is a useful preprocessor for asymmetric waveshaping when a waveshaper is
bookended by a bias on input and a DC-offset removing high pass filter on output.
This is an extremely simple bias implementation that simply adds a value to a signal.
More complicated bias behaviours exist in real circuits - for your homework ;).
@tags{DSP}
*/
template <typename FloatType>
class Bias
{
public:
Bias() noexcept = default;
//==============================================================================
/** Sets the DC bias
@param newBias DC offset in range [-1, 1]
*/
void setBias (FloatType newBias) noexcept
{
jassert (newBias >= static_cast<FloatType> (-1) && newBias <= static_cast<FloatType> (1));
bias.setTargetValue (newBias);
}
//==============================================================================
/** Returns the DC bias
@return DC bias, which should be in the range [-1, 1]
*/
FloatType getBias() const noexcept { return bias.getTargetValue(); }
/** Sets the length of the ramp used for smoothing gain changes. */
void setRampDurationSeconds (double newDurationSeconds) noexcept
{
if (rampDurationSeconds != newDurationSeconds)
{
rampDurationSeconds = newDurationSeconds;
updateRamp();
}
}
double getRampDurationSeconds() const noexcept { return rampDurationSeconds; }
//==============================================================================
/** Called before processing starts */
void prepare (const ProcessSpec& spec) noexcept
{
sampleRate = spec.sampleRate;
updateRamp();
}
void reset() noexcept
{
bias.reset (sampleRate, rampDurationSeconds);
}
//==============================================================================
/** Returns the result of processing a single sample. */
template <typename SampleType>
SampleType processSample (SampleType inputSample) noexcept
{
return inputSample + bias.getNextValue();
}
//==============================================================================
/** Processes the input and output buffers supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
auto&& inBlock = context.getInputBlock();
auto&& outBlock = context.getOutputBlock();
jassert (inBlock.getNumChannels() == outBlock.getNumChannels());
jassert (inBlock.getNumSamples() == outBlock.getNumSamples());
auto len = inBlock.getNumSamples();
auto numChannels = inBlock.getNumChannels();
if (context.isBypassed)
{
bias.skip (static_cast<int> (len));
if (context.usesSeparateInputAndOutputBlocks())
outBlock.copyFrom (inBlock);
return;
}
if (numChannels == 1)
{
auto* src = inBlock.getChannelPointer (0);
auto* dst = outBlock.getChannelPointer (0);
for (size_t i = 0; i < len; ++i)
dst[i] = src[i] + bias.getNextValue();
}
else
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6386)
auto* biases = static_cast<FloatType*> (alloca (sizeof (FloatType) * len));
for (size_t i = 0; i < len; ++i)
biases[i] = bias.getNextValue();
for (size_t chan = 0; chan < numChannels; ++chan)
FloatVectorOperations::add (outBlock.getChannelPointer (chan),
inBlock.getChannelPointer (chan),
biases, static_cast<int> (len));
JUCE_END_IGNORE_WARNINGS_MSVC
}
}
private:
//==============================================================================
SmoothedValue<FloatType> bias;
double sampleRate = 0, rampDurationSeconds = 0;
void updateRamp() noexcept
{
if (sampleRate > 0)
bias.reset (sampleRate, rampDurationSeconds);
}
};
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
Adds a DC offset (voltage bias) to the audio samples.
This is a useful preprocessor for asymmetric waveshaping when a waveshaper is
bookended by a bias on input and a DC-offset removing high pass filter on output.
This is an extremely simple bias implementation that simply adds a value to a signal.
More complicated bias behaviours exist in real circuits - for your homework ;).
@tags{DSP}
*/
template <typename FloatType>
class Bias
{
public:
Bias() noexcept = default;
//==============================================================================
/** Sets the DC bias
@param newBias DC offset in range [-1, 1]
*/
void setBias (FloatType newBias) noexcept
{
jassert (newBias >= static_cast<FloatType> (-1) && newBias <= static_cast<FloatType> (1));
bias.setTargetValue (newBias);
}
//==============================================================================
/** Returns the DC bias
@return DC bias, which should be in the range [-1, 1]
*/
FloatType getBias() const noexcept { return bias.getTargetValue(); }
/** Sets the length of the ramp used for smoothing gain changes. */
void setRampDurationSeconds (double newDurationSeconds) noexcept
{
if (rampDurationSeconds != newDurationSeconds)
{
rampDurationSeconds = newDurationSeconds;
updateRamp();
}
}
double getRampDurationSeconds() const noexcept { return rampDurationSeconds; }
//==============================================================================
/** Called before processing starts */
void prepare (const ProcessSpec& spec) noexcept
{
sampleRate = spec.sampleRate;
updateRamp();
}
void reset() noexcept
{
bias.reset (sampleRate, rampDurationSeconds);
}
//==============================================================================
/** Returns the result of processing a single sample. */
template <typename SampleType>
SampleType processSample (SampleType inputSample) noexcept
{
return inputSample + bias.getNextValue();
}
//==============================================================================
/** Processes the input and output buffers supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
auto&& inBlock = context.getInputBlock();
auto&& outBlock = context.getOutputBlock();
jassert (inBlock.getNumChannels() == outBlock.getNumChannels());
jassert (inBlock.getNumSamples() == outBlock.getNumSamples());
auto len = inBlock.getNumSamples();
auto numChannels = inBlock.getNumChannels();
if (context.isBypassed)
{
bias.skip (static_cast<int> (len));
if (context.usesSeparateInputAndOutputBlocks())
outBlock.copyFrom (inBlock);
return;
}
if (numChannels == 1)
{
auto* src = inBlock.getChannelPointer (0);
auto* dst = outBlock.getChannelPointer (0);
for (size_t i = 0; i < len; ++i)
dst[i] = src[i] + bias.getNextValue();
}
else
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6386)
auto* biases = static_cast<FloatType*> (alloca (sizeof (FloatType) * len));
for (size_t i = 0; i < len; ++i)
biases[i] = bias.getNextValue();
for (size_t chan = 0; chan < numChannels; ++chan)
FloatVectorOperations::add (outBlock.getChannelPointer (chan),
inBlock.getChannelPointer (chan),
biases, static_cast<int> (len));
JUCE_END_IGNORE_WARNINGS_MSVC
}
}
private:
//==============================================================================
SmoothedValue<FloatType> bias;
double sampleRate = 0, rampDurationSeconds = 0;
void updateRamp() noexcept
{
if (sampleRate > 0)
bias.reset (sampleRate, rampDurationSeconds);
}
};
} // namespace dsp
} // namespace juce

View File

@ -1,141 +1,141 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
Chorus<SampleType>::Chorus()
{
auto oscFunction = [] (SampleType x) { return std::sin (x); };
osc.initialise (oscFunction);
dryWet.setMixingRule (DryWetMixingRule::linear);
}
template <typename SampleType>
void Chorus<SampleType>::setRate (SampleType newRateHz)
{
jassert (isPositiveAndBelow (newRateHz, static_cast<SampleType> (100.0)));
rate = newRateHz;
update();
}
template <typename SampleType>
void Chorus<SampleType>::setDepth (SampleType newDepth)
{
jassert (isPositiveAndNotGreaterThan (newDepth, maxDepth));
depth = newDepth;
update();
}
template <typename SampleType>
void Chorus<SampleType>::setCentreDelay (SampleType newDelayMs)
{
jassert (isPositiveAndBelow (newDelayMs, maxCentreDelayMs));
centreDelay = jlimit (static_cast<SampleType> (1.0), maxCentreDelayMs, newDelayMs);
}
template <typename SampleType>
void Chorus<SampleType>::setFeedback (SampleType newFeedback)
{
jassert (newFeedback >= static_cast<SampleType> (-1.0) && newFeedback <= static_cast<SampleType> (1.0));
feedback = newFeedback;
update();
}
template <typename SampleType>
void Chorus<SampleType>::setMix (SampleType newMix)
{
jassert (isPositiveAndNotGreaterThan (newMix, static_cast<SampleType> (1.0)));
mix = newMix;
update();
}
//==============================================================================
template <typename SampleType>
void Chorus<SampleType>::prepare (const ProcessSpec& spec)
{
jassert (spec.sampleRate > 0);
jassert (spec.numChannels > 0);
sampleRate = spec.sampleRate;
const auto maxPossibleDelay = std::ceil ((maximumDelayModulation * maxDepth * oscVolumeMultiplier + maxCentreDelayMs)
* sampleRate / 1000.0);
delay = DelayLine<SampleType, DelayLineInterpolationTypes::Linear>{ static_cast<int> (maxPossibleDelay) };
delay.prepare (spec);
dryWet.prepare (spec);
feedbackVolume.resize (spec.numChannels);
lastOutput.resize (spec.numChannels);
osc.prepare (spec);
bufferDelayTimes.setSize (1, (int) spec.maximumBlockSize, false, false, true);
update();
reset();
}
template <typename SampleType>
void Chorus<SampleType>::reset()
{
std::fill (lastOutput.begin(), lastOutput.end(), static_cast<SampleType> (0));
delay.reset();
osc.reset();
dryWet.reset();
oscVolume.reset (sampleRate, 0.05);
for (auto& vol : feedbackVolume)
vol.reset (sampleRate, 0.05);
}
template <typename SampleType>
void Chorus<SampleType>::update()
{
osc.setFrequency (rate);
oscVolume.setTargetValue (depth * oscVolumeMultiplier);
dryWet.setWetMixProportion (mix);
for (auto& vol : feedbackVolume)
vol.setTargetValue (feedback);
}
//==============================================================================
template class Chorus<float>;
template class Chorus<double>;
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
Chorus<SampleType>::Chorus()
{
auto oscFunction = [] (SampleType x) { return std::sin (x); };
osc.initialise (oscFunction);
dryWet.setMixingRule (DryWetMixingRule::linear);
}
template <typename SampleType>
void Chorus<SampleType>::setRate (SampleType newRateHz)
{
jassert (isPositiveAndBelow (newRateHz, static_cast<SampleType> (100.0)));
rate = newRateHz;
update();
}
template <typename SampleType>
void Chorus<SampleType>::setDepth (SampleType newDepth)
{
jassert (isPositiveAndNotGreaterThan (newDepth, maxDepth));
depth = newDepth;
update();
}
template <typename SampleType>
void Chorus<SampleType>::setCentreDelay (SampleType newDelayMs)
{
jassert (isPositiveAndBelow (newDelayMs, maxCentreDelayMs));
centreDelay = jlimit (static_cast<SampleType> (1.0), maxCentreDelayMs, newDelayMs);
}
template <typename SampleType>
void Chorus<SampleType>::setFeedback (SampleType newFeedback)
{
jassert (newFeedback >= static_cast<SampleType> (-1.0) && newFeedback <= static_cast<SampleType> (1.0));
feedback = newFeedback;
update();
}
template <typename SampleType>
void Chorus<SampleType>::setMix (SampleType newMix)
{
jassert (isPositiveAndNotGreaterThan (newMix, static_cast<SampleType> (1.0)));
mix = newMix;
update();
}
//==============================================================================
template <typename SampleType>
void Chorus<SampleType>::prepare (const ProcessSpec& spec)
{
jassert (spec.sampleRate > 0);
jassert (spec.numChannels > 0);
sampleRate = spec.sampleRate;
const auto maxPossibleDelay = std::ceil ((maximumDelayModulation * maxDepth * oscVolumeMultiplier + maxCentreDelayMs)
* sampleRate / 1000.0);
delay = DelayLine<SampleType, DelayLineInterpolationTypes::Linear>{ static_cast<int> (maxPossibleDelay) };
delay.prepare (spec);
dryWet.prepare (spec);
feedbackVolume.resize (spec.numChannels);
lastOutput.resize (spec.numChannels);
osc.prepare (spec);
bufferDelayTimes.setSize (1, (int) spec.maximumBlockSize, false, false, true);
update();
reset();
}
template <typename SampleType>
void Chorus<SampleType>::reset()
{
std::fill (lastOutput.begin(), lastOutput.end(), static_cast<SampleType> (0));
delay.reset();
osc.reset();
dryWet.reset();
oscVolume.reset (sampleRate, 0.05);
for (auto& vol : feedbackVolume)
vol.reset (sampleRate, 0.05);
}
template <typename SampleType>
void Chorus<SampleType>::update()
{
osc.setFrequency (rate);
oscVolume.setTargetValue (depth * oscVolumeMultiplier);
dryWet.setWetMixProportion (mix);
for (auto& vol : feedbackVolume)
vol.setTargetValue (feedback);
}
//==============================================================================
template class Chorus<float>;
template class Chorus<double>;
} // namespace dsp
} // namespace juce

View File

@ -1,169 +1,169 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
A simple chorus DSP widget that modulates the delay of a delay line in order to
create sweeping notches in the magnitude frequency response.
This audio effect can be controlled via the speed and depth of the LFO controlling
the frequency response, a mix control, a feedback control, and the centre delay
of the modulation.
Note: To get classic chorus sounds try to use a centre delay time around 7-8 ms
with a low feeback volume and a low depth. This effect can also be used as a
flanger with a lower centre delay time and a lot of feedback, and as a vibrato
effect if the mix value is 1.
@tags{DSP}
*/
template <typename SampleType>
class Chorus
{
public:
//==============================================================================
/** Constructor. */
Chorus();
//==============================================================================
/** Sets the rate (in Hz) of the LFO modulating the chorus delay line. This rate
must be lower than 100 Hz.
*/
void setRate (SampleType newRateHz);
/** Sets the volume of the LFO modulating the chorus delay line (between 0 and 1).
*/
void setDepth (SampleType newDepth);
/** Sets the centre delay in milliseconds of the chorus delay line modulation.
This delay must be between 1 and 100 ms.
*/
void setCentreDelay (SampleType newDelayMs);
/** Sets the feedback volume (between -1 and 1) of the chorus delay line.
Negative values can be used to get specific chorus sounds.
*/
void setFeedback (SampleType newFeedback);
/** Sets the amount of dry and wet signal in the output of the chorus (between 0
for full dry and 1 for full wet).
*/
void setMix (SampleType newMix);
//==============================================================================
/** Initialises the processor. */
void prepare (const ProcessSpec& spec);
/** Resets the internal state variables of the processor. */
void reset();
//==============================================================================
/** Processes the input and output samples supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumChannels() == lastOutput.size());
jassert (inputBlock.getNumSamples() == numSamples);
if (context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
auto delayValuesBlock = AudioBlock<SampleType>(bufferDelayTimes).getSubBlock (0, numSamples);
auto contextDelay = ProcessContextReplacing<SampleType> (delayValuesBlock);
delayValuesBlock.clear();
osc.process (contextDelay);
delayValuesBlock.multiplyBy (oscVolume);
auto* delaySamples = bufferDelayTimes.getWritePointer (0);
for (size_t i = 0; i < numSamples; ++i)
{
auto lfo = jmax (static_cast<SampleType> (1.0), maximumDelayModulation * delaySamples[i] + centreDelay);
delaySamples[i] = static_cast<SampleType> (lfo * sampleRate / 1000.0);
}
dryWet.pushDrySamples (inputBlock);
for (size_t channel = 0; channel < numChannels; ++channel)
{
auto* inputSamples = inputBlock .getChannelPointer (channel);
auto* outputSamples = outputBlock.getChannelPointer (channel);
for (size_t i = 0; i < numSamples; ++i)
{
auto input = inputSamples[i];
auto output = input - lastOutput[channel];
delay.pushSample ((int) channel, output);
delay.setDelay (delaySamples[i]);
output = delay.popSample ((int) channel);
outputSamples[i] = output;
lastOutput[channel] = output * feedbackVolume[channel].getNextValue();
}
}
dryWet.mixWetSamples (outputBlock);
}
private:
//==============================================================================
void update();
//==============================================================================
Oscillator<SampleType> osc;
DelayLine<SampleType, DelayLineInterpolationTypes::Linear> delay;
SmoothedValue<SampleType, ValueSmoothingTypes::Linear> oscVolume;
std::vector<SmoothedValue<SampleType, ValueSmoothingTypes::Linear>> feedbackVolume { 2 };
DryWetMixer<SampleType> dryWet;
std::vector<SampleType> lastOutput { 2 };
AudioBuffer<SampleType> bufferDelayTimes;
double sampleRate = 44100.0;
SampleType rate = 1.0, depth = 0.25, feedback = 0.0, mix = 0.5,
centreDelay = 7.0;
static constexpr SampleType maxDepth = 1.0,
maxCentreDelayMs = 100.0,
oscVolumeMultiplier = 0.5,
maximumDelayModulation = 20.0;
};
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
A simple chorus DSP widget that modulates the delay of a delay line in order to
create sweeping notches in the magnitude frequency response.
This audio effect can be controlled via the speed and depth of the LFO controlling
the frequency response, a mix control, a feedback control, and the centre delay
of the modulation.
Note: To get classic chorus sounds try to use a centre delay time around 7-8 ms
with a low feedback volume and a low depth. This effect can also be used as a
flanger with a lower centre delay time and a lot of feedback, and as a vibrato
effect if the mix value is 1.
@tags{DSP}
*/
template <typename SampleType>
class Chorus
{
public:
//==============================================================================
/** Constructor. */
Chorus();
//==============================================================================
/** Sets the rate (in Hz) of the LFO modulating the chorus delay line. This rate
must be lower than 100 Hz.
*/
void setRate (SampleType newRateHz);
/** Sets the volume of the LFO modulating the chorus delay line (between 0 and 1).
*/
void setDepth (SampleType newDepth);
/** Sets the centre delay in milliseconds of the chorus delay line modulation.
This delay must be between 1 and 100 ms.
*/
void setCentreDelay (SampleType newDelayMs);
/** Sets the feedback volume (between -1 and 1) of the chorus delay line.
Negative values can be used to get specific chorus sounds.
*/
void setFeedback (SampleType newFeedback);
/** Sets the amount of dry and wet signal in the output of the chorus (between 0
for full dry and 1 for full wet).
*/
void setMix (SampleType newMix);
//==============================================================================
/** Initialises the processor. */
void prepare (const ProcessSpec& spec);
/** Resets the internal state variables of the processor. */
void reset();
//==============================================================================
/** Processes the input and output samples supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumChannels() == lastOutput.size());
jassert (inputBlock.getNumSamples() == numSamples);
if (context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
auto delayValuesBlock = AudioBlock<SampleType>(bufferDelayTimes).getSubBlock (0, numSamples);
auto contextDelay = ProcessContextReplacing<SampleType> (delayValuesBlock);
delayValuesBlock.clear();
osc.process (contextDelay);
delayValuesBlock.multiplyBy (oscVolume);
auto* delaySamples = bufferDelayTimes.getWritePointer (0);
for (size_t i = 0; i < numSamples; ++i)
{
auto lfo = jmax (static_cast<SampleType> (1.0), maximumDelayModulation * delaySamples[i] + centreDelay);
delaySamples[i] = static_cast<SampleType> (lfo * sampleRate / 1000.0);
}
dryWet.pushDrySamples (inputBlock);
for (size_t channel = 0; channel < numChannels; ++channel)
{
auto* inputSamples = inputBlock .getChannelPointer (channel);
auto* outputSamples = outputBlock.getChannelPointer (channel);
for (size_t i = 0; i < numSamples; ++i)
{
auto input = inputSamples[i];
auto output = input - lastOutput[channel];
delay.pushSample ((int) channel, output);
delay.setDelay (delaySamples[i]);
output = delay.popSample ((int) channel);
outputSamples[i] = output;
lastOutput[channel] = output * feedbackVolume[channel].getNextValue();
}
}
dryWet.mixWetSamples (outputBlock);
}
private:
//==============================================================================
void update();
//==============================================================================
Oscillator<SampleType> osc;
DelayLine<SampleType, DelayLineInterpolationTypes::Linear> delay;
SmoothedValue<SampleType, ValueSmoothingTypes::Linear> oscVolume;
std::vector<SmoothedValue<SampleType, ValueSmoothingTypes::Linear>> feedbackVolume { 2 };
DryWetMixer<SampleType> dryWet;
std::vector<SampleType> lastOutput { 2 };
AudioBuffer<SampleType> bufferDelayTimes;
double sampleRate = 44100.0;
SampleType rate = 1.0, depth = 0.25, feedback = 0.0, mix = 0.5,
centreDelay = 7.0;
static constexpr SampleType maxDepth = 1.0,
maxCentreDelayMs = 100.0,
oscVolumeMultiplier = 0.5,
maximumDelayModulation = 20.0;
};
} // namespace dsp
} // namespace juce

View File

@ -1,121 +1,121 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
Compressor<SampleType>::Compressor()
{
update();
}
//==============================================================================
template <typename SampleType>
void Compressor<SampleType>::setThreshold (SampleType newThreshold)
{
thresholddB = newThreshold;
update();
}
template <typename SampleType>
void Compressor<SampleType>::setRatio (SampleType newRatio)
{
jassert (newRatio >= static_cast<SampleType> (1.0));
ratio = newRatio;
update();
}
template <typename SampleType>
void Compressor<SampleType>::setAttack (SampleType newAttack)
{
attackTime = newAttack;
update();
}
template <typename SampleType>
void Compressor<SampleType>::setRelease (SampleType newRelease)
{
releaseTime = newRelease;
update();
}
//==============================================================================
template <typename SampleType>
void Compressor<SampleType>::prepare (const ProcessSpec& spec)
{
jassert (spec.sampleRate > 0);
jassert (spec.numChannels > 0);
sampleRate = spec.sampleRate;
envelopeFilter.prepare (spec);
update();
reset();
}
template <typename SampleType>
void Compressor<SampleType>::reset()
{
envelopeFilter.reset();
}
//==============================================================================
template <typename SampleType>
SampleType Compressor<SampleType>::processSample (int channel, SampleType inputValue)
{
// Ballistics filter with peak rectifier
auto env = envelopeFilter.processSample (channel, inputValue);
// VCA
auto gain = (env < threshold) ? static_cast<SampleType> (1.0)
: std::pow (env * thresholdInverse, ratioInverse - static_cast<SampleType> (1.0));
// Output
return gain * inputValue;
}
template <typename SampleType>
void Compressor<SampleType>::update()
{
threshold = Decibels::decibelsToGain (thresholddB, static_cast<SampleType> (-200.0));
thresholdInverse = static_cast<SampleType> (1.0) / threshold;
ratioInverse = static_cast<SampleType> (1.0) / ratio;
envelopeFilter.setAttackTime (attackTime);
envelopeFilter.setReleaseTime (releaseTime);
}
//==============================================================================
template class Compressor<float>;
template class Compressor<double>;
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
Compressor<SampleType>::Compressor()
{
update();
}
//==============================================================================
template <typename SampleType>
void Compressor<SampleType>::setThreshold (SampleType newThreshold)
{
thresholddB = newThreshold;
update();
}
template <typename SampleType>
void Compressor<SampleType>::setRatio (SampleType newRatio)
{
jassert (newRatio >= static_cast<SampleType> (1.0));
ratio = newRatio;
update();
}
template <typename SampleType>
void Compressor<SampleType>::setAttack (SampleType newAttack)
{
attackTime = newAttack;
update();
}
template <typename SampleType>
void Compressor<SampleType>::setRelease (SampleType newRelease)
{
releaseTime = newRelease;
update();
}
//==============================================================================
template <typename SampleType>
void Compressor<SampleType>::prepare (const ProcessSpec& spec)
{
jassert (spec.sampleRate > 0);
jassert (spec.numChannels > 0);
sampleRate = spec.sampleRate;
envelopeFilter.prepare (spec);
update();
reset();
}
template <typename SampleType>
void Compressor<SampleType>::reset()
{
envelopeFilter.reset();
}
//==============================================================================
template <typename SampleType>
SampleType Compressor<SampleType>::processSample (int channel, SampleType inputValue)
{
// Ballistics filter with peak rectifier
auto env = envelopeFilter.processSample (channel, inputValue);
// VCA
auto gain = (env < threshold) ? static_cast<SampleType> (1.0)
: std::pow (env * thresholdInverse, ratioInverse - static_cast<SampleType> (1.0));
// Output
return gain * inputValue;
}
template <typename SampleType>
void Compressor<SampleType>::update()
{
threshold = Decibels::decibelsToGain (thresholddB, static_cast<SampleType> (-200.0));
thresholdInverse = static_cast<SampleType> (1.0) / threshold;
ratioInverse = static_cast<SampleType> (1.0) / ratio;
envelopeFilter.setAttackTime (attackTime);
envelopeFilter.setReleaseTime (releaseTime);
}
//==============================================================================
template class Compressor<float>;
template class Compressor<double>;
} // namespace dsp
} // namespace juce

View File

@ -1,110 +1,110 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
A simple compressor with standard threshold, ratio, attack time and release time
controls.
@tags{DSP}
*/
template <typename SampleType>
class Compressor
{
public:
//==============================================================================
/** Constructor. */
Compressor();
//==============================================================================
/** Sets the threshold in dB of the compressor.*/
void setThreshold (SampleType newThreshold);
/** Sets the ratio of the compressor (must be higher or equal to 1).*/
void setRatio (SampleType newRatio);
/** Sets the attack time in milliseconds of the compressor.*/
void setAttack (SampleType newAttack);
/** Sets the release time in milliseconds of the compressor.*/
void setRelease (SampleType newRelease);
//==============================================================================
/** Initialises the processor. */
void prepare (const ProcessSpec& spec);
/** Resets the internal state variables of the processor. */
void reset();
//==============================================================================
/** Processes the input and output samples supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumSamples() == numSamples);
if (context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
for (size_t channel = 0; channel < numChannels; ++channel)
{
auto* inputSamples = inputBlock .getChannelPointer (channel);
auto* outputSamples = outputBlock.getChannelPointer (channel);
for (size_t i = 0; i < numSamples; ++i)
outputSamples[i] = processSample ((int) channel, inputSamples[i]);
}
}
/** Performs the processing operation on a single sample at a time. */
SampleType processSample (int channel, SampleType inputValue);
private:
//==============================================================================
void update();
//==============================================================================
SampleType threshold, thresholdInverse, ratioInverse;
BallisticsFilter<SampleType> envelopeFilter;
double sampleRate = 44100.0;
SampleType thresholddB = 0.0, ratio = 1.0, attackTime = 1.0, releaseTime = 100.0;
};
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
A simple compressor with standard threshold, ratio, attack time and release time
controls.
@tags{DSP}
*/
template <typename SampleType>
class Compressor
{
public:
//==============================================================================
/** Constructor. */
Compressor();
//==============================================================================
/** Sets the threshold in dB of the compressor.*/
void setThreshold (SampleType newThreshold);
/** Sets the ratio of the compressor (must be higher or equal to 1).*/
void setRatio (SampleType newRatio);
/** Sets the attack time in milliseconds of the compressor.*/
void setAttack (SampleType newAttack);
/** Sets the release time in milliseconds of the compressor.*/
void setRelease (SampleType newRelease);
//==============================================================================
/** Initialises the processor. */
void prepare (const ProcessSpec& spec);
/** Resets the internal state variables of the processor. */
void reset();
//==============================================================================
/** Processes the input and output samples supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumSamples() == numSamples);
if (context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
for (size_t channel = 0; channel < numChannels; ++channel)
{
auto* inputSamples = inputBlock .getChannelPointer (channel);
auto* outputSamples = outputBlock.getChannelPointer (channel);
for (size_t i = 0; i < numSamples; ++i)
outputSamples[i] = processSample ((int) channel, inputSamples[i]);
}
}
/** Performs the processing operation on a single sample at a time. */
SampleType processSample (int channel, SampleType inputValue);
private:
//==============================================================================
void update();
//==============================================================================
SampleType threshold, thresholdInverse, ratioInverse;
BallisticsFilter<SampleType> envelopeFilter;
double sampleRate = 44100.0;
SampleType thresholddB = 0.0, ratio = 1.0, attackTime = 1.0, releaseTime = 100.0;
};
} // namespace dsp
} // namespace juce

View File

@ -1,148 +1,148 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
Applies a gain to audio samples as single samples or AudioBlocks.
@tags{DSP}
*/
template <typename FloatType>
class Gain
{
public:
Gain() noexcept = default;
//==============================================================================
/** Applies a new gain as a linear value. */
void setGainLinear (FloatType newGain) noexcept { gain.setTargetValue (newGain); }
/** Applies a new gain as a decibel value. */
void setGainDecibels (FloatType newGainDecibels) noexcept { setGainLinear (Decibels::decibelsToGain<FloatType> (newGainDecibels)); }
/** Returns the current gain as a linear value. */
FloatType getGainLinear() const noexcept { return gain.getTargetValue(); }
/** Returns the current gain in decibels. */
FloatType getGainDecibels() const noexcept { return Decibels::gainToDecibels<FloatType> (getGainLinear()); }
/** Sets the length of the ramp used for smoothing gain changes. */
void setRampDurationSeconds (double newDurationSeconds) noexcept
{
if (rampDurationSeconds != newDurationSeconds)
{
rampDurationSeconds = newDurationSeconds;
reset();
}
}
/** Returns the ramp duration in seconds. */
double getRampDurationSeconds() const noexcept { return rampDurationSeconds; }
/** Returns true if the current value is currently being interpolated. */
bool isSmoothing() const noexcept { return gain.isSmoothing(); }
//==============================================================================
/** Called before processing starts. */
void prepare (const ProcessSpec& spec) noexcept
{
sampleRate = spec.sampleRate;
reset();
}
/** Resets the internal state of the gain */
void reset() noexcept
{
if (sampleRate > 0)
gain.reset (sampleRate, rampDurationSeconds);
}
//==============================================================================
/** Returns the result of processing a single sample. */
template <typename SampleType>
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType s) noexcept
{
return s * gain.getNextValue();
}
/** Processes the input and output buffers supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
auto&& inBlock = context.getInputBlock();
auto&& outBlock = context.getOutputBlock();
jassert (inBlock.getNumChannels() == outBlock.getNumChannels());
jassert (inBlock.getNumSamples() == outBlock.getNumSamples());
auto len = inBlock.getNumSamples();
auto numChannels = inBlock.getNumChannels();
if (context.isBypassed)
{
gain.skip (static_cast<int> (len));
if (context.usesSeparateInputAndOutputBlocks())
outBlock.copyFrom (inBlock);
return;
}
if (numChannels == 1)
{
auto* src = inBlock.getChannelPointer (0);
auto* dst = outBlock.getChannelPointer (0);
for (size_t i = 0; i < len; ++i)
dst[i] = src[i] * gain.getNextValue();
}
else
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6386)
auto* gains = static_cast<FloatType*> (alloca (sizeof (FloatType) * len));
for (size_t i = 0; i < len; ++i)
gains[i] = gain.getNextValue();
JUCE_END_IGNORE_WARNINGS_MSVC
for (size_t chan = 0; chan < numChannels; ++chan)
FloatVectorOperations::multiply (outBlock.getChannelPointer (chan),
inBlock.getChannelPointer (chan),
gains, static_cast<int> (len));
}
}
private:
//==============================================================================
SmoothedValue<FloatType> gain;
double sampleRate = 0, rampDurationSeconds = 0;
};
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
Applies a gain to audio samples as single samples or AudioBlocks.
@tags{DSP}
*/
template <typename FloatType>
class Gain
{
public:
Gain() noexcept = default;
//==============================================================================
/** Applies a new gain as a linear value. */
void setGainLinear (FloatType newGain) noexcept { gain.setTargetValue (newGain); }
/** Applies a new gain as a decibel value. */
void setGainDecibels (FloatType newGainDecibels) noexcept { setGainLinear (Decibels::decibelsToGain<FloatType> (newGainDecibels)); }
/** Returns the current gain as a linear value. */
FloatType getGainLinear() const noexcept { return gain.getTargetValue(); }
/** Returns the current gain in decibels. */
FloatType getGainDecibels() const noexcept { return Decibels::gainToDecibels<FloatType> (getGainLinear()); }
/** Sets the length of the ramp used for smoothing gain changes. */
void setRampDurationSeconds (double newDurationSeconds) noexcept
{
if (rampDurationSeconds != newDurationSeconds)
{
rampDurationSeconds = newDurationSeconds;
reset();
}
}
/** Returns the ramp duration in seconds. */
double getRampDurationSeconds() const noexcept { return rampDurationSeconds; }
/** Returns true if the current value is currently being interpolated. */
bool isSmoothing() const noexcept { return gain.isSmoothing(); }
//==============================================================================
/** Called before processing starts. */
void prepare (const ProcessSpec& spec) noexcept
{
sampleRate = spec.sampleRate;
reset();
}
/** Resets the internal state of the gain */
void reset() noexcept
{
if (sampleRate > 0)
gain.reset (sampleRate, rampDurationSeconds);
}
//==============================================================================
/** Returns the result of processing a single sample. */
template <typename SampleType>
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType s) noexcept
{
return s * gain.getNextValue();
}
/** Processes the input and output buffers supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
auto&& inBlock = context.getInputBlock();
auto&& outBlock = context.getOutputBlock();
jassert (inBlock.getNumChannels() == outBlock.getNumChannels());
jassert (inBlock.getNumSamples() == outBlock.getNumSamples());
auto len = inBlock.getNumSamples();
auto numChannels = inBlock.getNumChannels();
if (context.isBypassed)
{
gain.skip (static_cast<int> (len));
if (context.usesSeparateInputAndOutputBlocks())
outBlock.copyFrom (inBlock);
return;
}
if (numChannels == 1)
{
auto* src = inBlock.getChannelPointer (0);
auto* dst = outBlock.getChannelPointer (0);
for (size_t i = 0; i < len; ++i)
dst[i] = src[i] * gain.getNextValue();
}
else
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6386)
auto* gains = static_cast<FloatType*> (alloca (sizeof (FloatType) * len));
for (size_t i = 0; i < len; ++i)
gains[i] = gain.getNextValue();
JUCE_END_IGNORE_WARNINGS_MSVC
for (size_t chan = 0; chan < numChannels; ++chan)
FloatVectorOperations::multiply (outBlock.getChannelPointer (chan),
inBlock.getChannelPointer (chan),
gains, static_cast<int> (len));
}
}
private:
//==============================================================================
SmoothedValue<FloatType> gain;
double sampleRate = 0, rampDurationSeconds = 0;
};
} // namespace dsp
} // namespace juce

View File

@ -1,176 +1,176 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
LadderFilter<SampleType>::LadderFilter() : state (2)
{
setSampleRate (SampleType (1000)); // intentionally setting unrealistic default
// sample rate to catch missing initialisation bugs
setResonance (SampleType (0));
setDrive (SampleType (1.2));
mode = Mode::LPF24;
setMode (Mode::LPF12);
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::setMode (Mode newMode) noexcept
{
if (newMode == mode)
return;
switch (newMode)
{
case Mode::LPF12: A = {{ SampleType (0), SampleType (0), SampleType (1), SampleType (0), SampleType (0) }}; comp = SampleType (0.5); break;
case Mode::HPF12: A = {{ SampleType (1), SampleType (-2), SampleType (1), SampleType (0), SampleType (0) }}; comp = SampleType (0); break;
case Mode::BPF12: A = {{ SampleType (0), SampleType (0), SampleType (-1), SampleType (1), SampleType (0) }}; comp = SampleType (0.5); break;
case Mode::LPF24: A = {{ SampleType (0), SampleType (0), SampleType (0), SampleType (0), SampleType (1) }}; comp = SampleType (0.5); break;
case Mode::HPF24: A = {{ SampleType (1), SampleType (-4), SampleType (6), SampleType (-4), SampleType (1) }}; comp = SampleType (0); break;
case Mode::BPF24: A = {{ SampleType (0), SampleType (0), SampleType (1), SampleType (-2), SampleType (1) }}; comp = SampleType (0.5); break;
default: jassertfalse; break;
}
static constexpr auto outputGain = SampleType (1.2);
for (auto& a : A)
a *= outputGain;
mode = newMode;
reset();
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::prepare (const ProcessSpec& spec)
{
setSampleRate (SampleType (spec.sampleRate));
setNumChannels (spec.numChannels);
reset();
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::reset() noexcept
{
for (auto& s : state)
s.fill (SampleType (0));
cutoffTransformSmoother.setCurrentAndTargetValue (cutoffTransformSmoother.getTargetValue());
scaledResonanceSmoother.setCurrentAndTargetValue (scaledResonanceSmoother.getTargetValue());
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::setCutoffFrequencyHz (SampleType newCutoff) noexcept
{
jassert (newCutoff > SampleType (0));
cutoffFreqHz = newCutoff;
updateCutoffFreq();
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::setResonance (SampleType newResonance) noexcept
{
jassert (newResonance >= SampleType (0) && newResonance <= SampleType (1));
resonance = newResonance;
updateResonance();
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::setDrive (SampleType newDrive) noexcept
{
jassert (newDrive >= SampleType (1));
drive = newDrive;
gain = std::pow (drive, SampleType (-2.642)) * SampleType (0.6103) + SampleType (0.3903);
drive2 = drive * SampleType (0.04) + SampleType (0.96);
gain2 = std::pow (drive2, SampleType (-2.642)) * SampleType (0.6103) + SampleType (0.3903);
}
//==============================================================================
template <typename SampleType>
SampleType LadderFilter<SampleType>::processSample (SampleType inputValue, size_t channelToUse) noexcept
{
auto& s = state[channelToUse];
const auto a1 = cutoffTransformValue;
const auto g = a1 * SampleType (-1) + SampleType (1);
const auto b0 = g * SampleType (0.76923076923);
const auto b1 = g * SampleType (0.23076923076);
const auto dx = gain * saturationLUT (drive * inputValue);
const auto a = dx + scaledResonanceValue * SampleType (-4) * (gain2 * saturationLUT (drive2 * s[4]) - dx * comp);
const auto b = b1 * s[0] + a1 * s[1] + b0 * a;
const auto c = b1 * s[1] + a1 * s[2] + b0 * b;
const auto d = b1 * s[2] + a1 * s[3] + b0 * c;
const auto e = b1 * s[3] + a1 * s[4] + b0 * d;
s[0] = a;
s[1] = b;
s[2] = c;
s[3] = d;
s[4] = e;
return a * A[0] + b * A[1] + c * A[2] + d * A[3] + e * A[4];
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::updateSmoothers() noexcept
{
cutoffTransformValue = cutoffTransformSmoother.getNextValue();
scaledResonanceValue = scaledResonanceSmoother.getNextValue();
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::setSampleRate (SampleType newValue) noexcept
{
jassert (newValue > SampleType (0));
cutoffFreqScaler = SampleType (-2.0 * juce::MathConstants<double>::pi) / newValue;
static constexpr SampleType smootherRampTimeSec = SampleType (0.05);
cutoffTransformSmoother.reset (newValue, smootherRampTimeSec);
scaledResonanceSmoother.reset (newValue, smootherRampTimeSec);
updateCutoffFreq();
}
//==============================================================================
template class LadderFilter<float>;
template class LadderFilter<double>;
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
LadderFilter<SampleType>::LadderFilter() : state (2)
{
setSampleRate (SampleType (1000)); // intentionally setting unrealistic default
// sample rate to catch missing initialisation bugs
setResonance (SampleType (0));
setDrive (SampleType (1.2));
mode = Mode::LPF24;
setMode (Mode::LPF12);
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::setMode (Mode newMode) noexcept
{
if (newMode == mode)
return;
switch (newMode)
{
case Mode::LPF12: A = {{ SampleType (0), SampleType (0), SampleType (1), SampleType (0), SampleType (0) }}; comp = SampleType (0.5); break;
case Mode::HPF12: A = {{ SampleType (1), SampleType (-2), SampleType (1), SampleType (0), SampleType (0) }}; comp = SampleType (0); break;
case Mode::BPF12: A = {{ SampleType (0), SampleType (0), SampleType (-1), SampleType (1), SampleType (0) }}; comp = SampleType (0.5); break;
case Mode::LPF24: A = {{ SampleType (0), SampleType (0), SampleType (0), SampleType (0), SampleType (1) }}; comp = SampleType (0.5); break;
case Mode::HPF24: A = {{ SampleType (1), SampleType (-4), SampleType (6), SampleType (-4), SampleType (1) }}; comp = SampleType (0); break;
case Mode::BPF24: A = {{ SampleType (0), SampleType (0), SampleType (1), SampleType (-2), SampleType (1) }}; comp = SampleType (0.5); break;
default: jassertfalse; break;
}
static constexpr auto outputGain = SampleType (1.2);
for (auto& a : A)
a *= outputGain;
mode = newMode;
reset();
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::prepare (const ProcessSpec& spec)
{
setSampleRate (SampleType (spec.sampleRate));
setNumChannels (spec.numChannels);
reset();
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::reset() noexcept
{
for (auto& s : state)
s.fill (SampleType (0));
cutoffTransformSmoother.setCurrentAndTargetValue (cutoffTransformSmoother.getTargetValue());
scaledResonanceSmoother.setCurrentAndTargetValue (scaledResonanceSmoother.getTargetValue());
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::setCutoffFrequencyHz (SampleType newCutoff) noexcept
{
jassert (newCutoff > SampleType (0));
cutoffFreqHz = newCutoff;
updateCutoffFreq();
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::setResonance (SampleType newResonance) noexcept
{
jassert (newResonance >= SampleType (0) && newResonance <= SampleType (1));
resonance = newResonance;
updateResonance();
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::setDrive (SampleType newDrive) noexcept
{
jassert (newDrive >= SampleType (1));
drive = newDrive;
gain = std::pow (drive, SampleType (-2.642)) * SampleType (0.6103) + SampleType (0.3903);
drive2 = drive * SampleType (0.04) + SampleType (0.96);
gain2 = std::pow (drive2, SampleType (-2.642)) * SampleType (0.6103) + SampleType (0.3903);
}
//==============================================================================
template <typename SampleType>
SampleType LadderFilter<SampleType>::processSample (SampleType inputValue, size_t channelToUse) noexcept
{
auto& s = state[channelToUse];
const auto a1 = cutoffTransformValue;
const auto g = a1 * SampleType (-1) + SampleType (1);
const auto b0 = g * SampleType (0.76923076923);
const auto b1 = g * SampleType (0.23076923076);
const auto dx = gain * saturationLUT (drive * inputValue);
const auto a = dx + scaledResonanceValue * SampleType (-4) * (gain2 * saturationLUT (drive2 * s[4]) - dx * comp);
const auto b = b1 * s[0] + a1 * s[1] + b0 * a;
const auto c = b1 * s[1] + a1 * s[2] + b0 * b;
const auto d = b1 * s[2] + a1 * s[3] + b0 * c;
const auto e = b1 * s[3] + a1 * s[4] + b0 * d;
s[0] = a;
s[1] = b;
s[2] = c;
s[3] = d;
s[4] = e;
return a * A[0] + b * A[1] + c * A[2] + d * A[3] + e * A[4];
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::updateSmoothers() noexcept
{
cutoffTransformValue = cutoffTransformSmoother.getNextValue();
scaledResonanceValue = scaledResonanceSmoother.getNextValue();
}
//==============================================================================
template <typename SampleType>
void LadderFilter<SampleType>::setSampleRate (SampleType newValue) noexcept
{
jassert (newValue > SampleType (0));
cutoffFreqScaler = SampleType (-2.0 * juce::MathConstants<double>::pi) / newValue;
static constexpr SampleType smootherRampTimeSec = SampleType (0.05);
cutoffTransformSmoother.reset (newValue, smootherRampTimeSec);
scaledResonanceSmoother.reset (newValue, smootherRampTimeSec);
updateCutoffFreq();
}
//==============================================================================
template class LadderFilter<float>;
template class LadderFilter<double>;
} // namespace dsp
} // namespace juce

View File

@ -1,153 +1,153 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
enum class LadderFilterMode
{
LPF12, // low-pass 12 dB/octave
HPF12, // high-pass 12 dB/octave
BPF12, // band-pass 12 dB/octave
LPF24, // low-pass 24 dB/octave
HPF24, // high-pass 24 dB/octave
BPF24 // band-pass 24 dB/octave
};
/**
Multi-mode filter based on the Moog ladder filter.
@tags{DSP}
*/
template <typename SampleType>
class LadderFilter
{
public:
//==============================================================================
using Mode = LadderFilterMode;
//==============================================================================
/** Creates an uninitialised filter. Call prepare() before first use. */
LadderFilter();
/** Enables or disables the filter. If disabled it will simply pass through the input signal. */
void setEnabled (bool isEnabled) noexcept { enabled = isEnabled; }
/** Sets filter mode. */
void setMode (Mode newMode) noexcept;
/** Initialises the filter. */
void prepare (const ProcessSpec& spec);
/** Returns the current number of channels. */
size_t getNumChannels() const noexcept { return state.size(); }
/** Resets the internal state variables of the filter. */
void reset() noexcept;
/** Sets the cutoff frequency of the filter.
@param newCutoff cutoff frequency in Hz
*/
void setCutoffFrequencyHz (SampleType newCutoff) noexcept;
/** Sets the resonance of the filter.
@param newResonance a value between 0 and 1; higher values increase the resonance and can result in self oscillation!
*/
void setResonance (SampleType newResonance) noexcept;
/** Sets the amount of saturation in the filter.
@param newDrive saturation amount; it can be any number greater than or equal to one. Higher values result in more distortion.
*/
void setDrive (SampleType newDrive) noexcept;
//==============================================================================
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() <= getNumChannels());
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumSamples() == numSamples);
if (! enabled || context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
for (size_t n = 0; n < numSamples; ++n)
{
updateSmoothers();
for (size_t ch = 0; ch < numChannels; ++ch)
outputBlock.getChannelPointer (ch)[n] = processSample (inputBlock.getChannelPointer (ch)[n], ch);
}
}
protected:
//==============================================================================
SampleType processSample (SampleType inputValue, size_t channelToUse) noexcept;
void updateSmoothers() noexcept;
private:
//==============================================================================
void setSampleRate (SampleType newValue) noexcept;
void setNumChannels (size_t newValue) { state.resize (newValue); }
void updateCutoffFreq() noexcept { cutoffTransformSmoother.setTargetValue (std::exp (cutoffFreqHz * cutoffFreqScaler)); }
void updateResonance() noexcept { scaledResonanceSmoother.setTargetValue (jmap (resonance, SampleType (0.1), SampleType (1.0))); }
//==============================================================================
SampleType drive, drive2, gain, gain2, comp;
static constexpr size_t numStates = 5;
std::vector<std::array<SampleType, numStates>> state;
std::array<SampleType, numStates> A;
SmoothedValue<SampleType> cutoffTransformSmoother, scaledResonanceSmoother;
SampleType cutoffTransformValue, scaledResonanceValue;
LookupTableTransform<SampleType> saturationLUT { [] (SampleType x) { return std::tanh (x); },
SampleType (-5), SampleType (5), 128 };
SampleType cutoffFreqHz { SampleType (200) };
SampleType resonance;
SampleType cutoffFreqScaler;
Mode mode;
bool enabled = true;
};
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
enum class LadderFilterMode
{
LPF12, // low-pass 12 dB/octave
HPF12, // high-pass 12 dB/octave
BPF12, // band-pass 12 dB/octave
LPF24, // low-pass 24 dB/octave
HPF24, // high-pass 24 dB/octave
BPF24 // band-pass 24 dB/octave
};
/**
Multi-mode filter based on the Moog ladder filter.
@tags{DSP}
*/
template <typename SampleType>
class LadderFilter
{
public:
//==============================================================================
using Mode = LadderFilterMode;
//==============================================================================
/** Creates an uninitialised filter. Call prepare() before first use. */
LadderFilter();
/** Enables or disables the filter. If disabled it will simply pass through the input signal. */
void setEnabled (bool isEnabled) noexcept { enabled = isEnabled; }
/** Sets filter mode. */
void setMode (Mode newMode) noexcept;
/** Initialises the filter. */
void prepare (const ProcessSpec& spec);
/** Returns the current number of channels. */
size_t getNumChannels() const noexcept { return state.size(); }
/** Resets the internal state variables of the filter. */
void reset() noexcept;
/** Sets the cutoff frequency of the filter.
@param newCutoff cutoff frequency in Hz
*/
void setCutoffFrequencyHz (SampleType newCutoff) noexcept;
/** Sets the resonance of the filter.
@param newResonance a value between 0 and 1; higher values increase the resonance and can result in self oscillation!
*/
void setResonance (SampleType newResonance) noexcept;
/** Sets the amount of saturation in the filter.
@param newDrive saturation amount; it can be any number greater than or equal to one. Higher values result in more distortion.
*/
void setDrive (SampleType newDrive) noexcept;
//==============================================================================
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() <= getNumChannels());
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumSamples() == numSamples);
if (! enabled || context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
for (size_t n = 0; n < numSamples; ++n)
{
updateSmoothers();
for (size_t ch = 0; ch < numChannels; ++ch)
outputBlock.getChannelPointer (ch)[n] = processSample (inputBlock.getChannelPointer (ch)[n], ch);
}
}
protected:
//==============================================================================
SampleType processSample (SampleType inputValue, size_t channelToUse) noexcept;
void updateSmoothers() noexcept;
private:
//==============================================================================
void setSampleRate (SampleType newValue) noexcept;
void setNumChannels (size_t newValue) { state.resize (newValue); }
void updateCutoffFreq() noexcept { cutoffTransformSmoother.setTargetValue (std::exp (cutoffFreqHz * cutoffFreqScaler)); }
void updateResonance() noexcept { scaledResonanceSmoother.setTargetValue (jmap (resonance, SampleType (0.1), SampleType (1.0))); }
//==============================================================================
SampleType drive, drive2, gain, gain2, comp;
static constexpr size_t numStates = 5;
std::vector<std::array<SampleType, numStates>> state;
std::array<SampleType, numStates> A;
SmoothedValue<SampleType> cutoffTransformSmoother, scaledResonanceSmoother;
SampleType cutoffTransformValue, scaledResonanceValue;
LookupTableTransform<SampleType> saturationLUT { [] (SampleType x) { return std::tanh (x); },
SampleType (-5), SampleType (5), 128 };
SampleType cutoffFreqHz { SampleType (200) };
SampleType resonance;
SampleType cutoffFreqScaler;
Mode mode;
bool enabled = true;
};
} // namespace dsp
} // namespace juce

View File

@ -1,98 +1,98 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
void Limiter<SampleType>::setThreshold (SampleType newThreshold)
{
thresholddB = newThreshold;
update();
}
template <typename SampleType>
void Limiter<SampleType>::setRelease (SampleType newRelease)
{
releaseTime = newRelease;
update();
}
//==============================================================================
template <typename SampleType>
void Limiter<SampleType>::prepare (const ProcessSpec& spec)
{
jassert (spec.sampleRate > 0);
jassert (spec.numChannels > 0);
sampleRate = spec.sampleRate;
firstStageCompressor.prepare (spec);
secondStageCompressor.prepare (spec);
update();
reset();
}
template <typename SampleType>
void Limiter<SampleType>::reset()
{
firstStageCompressor.reset();
secondStageCompressor.reset();
outputVolume.reset (sampleRate, 0.001);
}
//==============================================================================
template <typename SampleType>
void Limiter<SampleType>::update()
{
firstStageCompressor.setThreshold ((SampleType) -10.0);
firstStageCompressor.setRatio ((SampleType) 4.0);
firstStageCompressor.setAttack ((SampleType) 2.0);
firstStageCompressor.setRelease ((SampleType) 200.0);
secondStageCompressor.setThreshold (thresholddB);
secondStageCompressor.setRatio ((SampleType) 1000.0);
secondStageCompressor.setAttack ((SampleType) 0.001);
secondStageCompressor.setRelease (releaseTime);
auto ratioInverse = (SampleType) (1.0 / 4.0);
auto gain = (SampleType) std::pow (10.0, 10.0 * (1.0 - ratioInverse) / 40.0);
gain *= Decibels::decibelsToGain (-thresholddB, (SampleType) -100.0);
outputVolume.setTargetValue (gain);
}
//==============================================================================
template class Limiter<float>;
template class Limiter<double>;
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
void Limiter<SampleType>::setThreshold (SampleType newThreshold)
{
thresholddB = newThreshold;
update();
}
template <typename SampleType>
void Limiter<SampleType>::setRelease (SampleType newRelease)
{
releaseTime = newRelease;
update();
}
//==============================================================================
template <typename SampleType>
void Limiter<SampleType>::prepare (const ProcessSpec& spec)
{
jassert (spec.sampleRate > 0);
jassert (spec.numChannels > 0);
sampleRate = spec.sampleRate;
firstStageCompressor.prepare (spec);
secondStageCompressor.prepare (spec);
update();
reset();
}
template <typename SampleType>
void Limiter<SampleType>::reset()
{
firstStageCompressor.reset();
secondStageCompressor.reset();
outputVolume.reset (sampleRate, 0.001);
}
//==============================================================================
template <typename SampleType>
void Limiter<SampleType>::update()
{
firstStageCompressor.setThreshold ((SampleType) -10.0);
firstStageCompressor.setRatio ((SampleType) 4.0);
firstStageCompressor.setAttack ((SampleType) 2.0);
firstStageCompressor.setRelease ((SampleType) 200.0);
secondStageCompressor.setThreshold (thresholddB);
secondStageCompressor.setRatio ((SampleType) 1000.0);
secondStageCompressor.setAttack ((SampleType) 0.001);
secondStageCompressor.setRelease (releaseTime);
auto ratioInverse = (SampleType) (1.0 / 4.0);
auto gain = (SampleType) std::pow (10.0, 10.0 * (1.0 - ratioInverse) / 40.0);
gain *= Decibels::decibelsToGain (-thresholddB, (SampleType) -100.0);
outputVolume.setTargetValue (gain);
}
//==============================================================================
template class Limiter<float>;
template class Limiter<double>;
} // namespace dsp
} // namespace juce

View File

@ -1,105 +1,105 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
A simple limiter with standard threshold and release time controls, featuring
two compressors and a hard clipper at 0 dB.
@tags{DSP}
*/
template <typename SampleType>
class Limiter
{
public:
//==============================================================================
/** Constructor. */
Limiter() = default;
//==============================================================================
/** Sets the threshold in dB of the limiter.*/
void setThreshold (SampleType newThreshold);
/** Sets the release time in milliseconds of the limiter.*/
void setRelease (SampleType newRelease);
//==============================================================================
/** Initialises the processor. */
void prepare (const ProcessSpec& spec);
/** Resets the internal state variables of the processor. */
void reset();
//==============================================================================
/** Processes the input and output samples supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumSamples() == numSamples);
if (context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
firstStageCompressor.process (context);
auto secondContext = ProcessContextReplacing<SampleType> (outputBlock);
secondStageCompressor.process (secondContext);
outputBlock.multiplyBy (outputVolume);
for (size_t channel = 0; channel < numChannels; ++channel)
{
FloatVectorOperations::clip (outputBlock.getChannelPointer (channel), outputBlock.getChannelPointer (channel),
(SampleType) -1.0, (SampleType) 1.0, (int) numSamples);
}
}
private:
//==============================================================================
void update();
//==============================================================================
Compressor<SampleType> firstStageCompressor, secondStageCompressor;
SmoothedValue<SampleType, ValueSmoothingTypes::Linear> outputVolume;
double sampleRate = 44100.0;
SampleType thresholddB = -10.0, releaseTime = 100.0;
};
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
A simple limiter with standard threshold and release time controls, featuring
two compressors and a hard clipper at 0 dB.
@tags{DSP}
*/
template <typename SampleType>
class Limiter
{
public:
//==============================================================================
/** Constructor. */
Limiter() = default;
//==============================================================================
/** Sets the threshold in dB of the limiter.*/
void setThreshold (SampleType newThreshold);
/** Sets the release time in milliseconds of the limiter.*/
void setRelease (SampleType newRelease);
//==============================================================================
/** Initialises the processor. */
void prepare (const ProcessSpec& spec);
/** Resets the internal state variables of the processor. */
void reset();
//==============================================================================
/** Processes the input and output samples supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumSamples() == numSamples);
if (context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
firstStageCompressor.process (context);
auto secondContext = ProcessContextReplacing<SampleType> (outputBlock);
secondStageCompressor.process (secondContext);
outputBlock.multiplyBy (outputVolume);
for (size_t channel = 0; channel < numChannels; ++channel)
{
FloatVectorOperations::clip (outputBlock.getChannelPointer (channel), outputBlock.getChannelPointer (channel),
(SampleType) -1.0, (SampleType) 1.0, (int) numSamples);
}
}
private:
//==============================================================================
void update();
//==============================================================================
Compressor<SampleType> firstStageCompressor, secondStageCompressor;
SmoothedValue<SampleType, ValueSmoothingTypes::Linear> outputVolume;
double sampleRate = 44100.0;
SampleType thresholddB = -10.0, releaseTime = 100.0;
};
} // namespace dsp
} // namespace juce

View File

@ -1,129 +1,129 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
NoiseGate<SampleType>::NoiseGate()
{
update();
RMSFilter.setLevelCalculationType (BallisticsFilterLevelCalculationType::RMS);
RMSFilter.setAttackTime (static_cast<SampleType> (0.0));
RMSFilter.setReleaseTime (static_cast<SampleType> (50.0));
}
template <typename SampleType>
void NoiseGate<SampleType>::setThreshold (SampleType newValue)
{
thresholddB = newValue;
update();
}
template <typename SampleType>
void NoiseGate<SampleType>::setRatio (SampleType newRatio)
{
jassert (newRatio >= static_cast<SampleType> (1.0));
ratio = newRatio;
update();
}
template <typename SampleType>
void NoiseGate<SampleType>::setAttack (SampleType newAttack)
{
attackTime = newAttack;
update();
}
template <typename SampleType>
void NoiseGate<SampleType>::setRelease (SampleType newRelease)
{
releaseTime = newRelease;
update();
}
//==============================================================================
template <typename SampleType>
void NoiseGate<SampleType>::prepare (const ProcessSpec& spec)
{
jassert (spec.sampleRate > 0);
jassert (spec.numChannels > 0);
sampleRate = spec.sampleRate;
RMSFilter.prepare (spec);
envelopeFilter.prepare (spec);
update();
reset();
}
template <typename SampleType>
void NoiseGate<SampleType>::reset()
{
RMSFilter.reset();
envelopeFilter.reset();
}
//==============================================================================
template <typename SampleType>
SampleType NoiseGate<SampleType>::processSample (int channel, SampleType sample)
{
// RMS ballistics filter
auto env = RMSFilter.processSample (channel, sample);
// Ballistics filter
env = envelopeFilter.processSample (channel, env);
// VCA
auto gain = (env > threshold) ? static_cast<SampleType> (1.0)
: std::pow (env * thresholdInverse, currentRatio - static_cast<SampleType> (1.0));
// Output
return gain * sample;
}
template <typename SampleType>
void NoiseGate<SampleType>::update()
{
threshold = Decibels::decibelsToGain (thresholddB, static_cast<SampleType> (-200.0));
thresholdInverse = static_cast<SampleType> (1.0) / threshold;
currentRatio = ratio;
envelopeFilter.setAttackTime (attackTime);
envelopeFilter.setReleaseTime (releaseTime);
}
//==============================================================================
template class NoiseGate<float>;
template class NoiseGate<double>;
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
NoiseGate<SampleType>::NoiseGate()
{
update();
RMSFilter.setLevelCalculationType (BallisticsFilterLevelCalculationType::RMS);
RMSFilter.setAttackTime (static_cast<SampleType> (0.0));
RMSFilter.setReleaseTime (static_cast<SampleType> (50.0));
}
template <typename SampleType>
void NoiseGate<SampleType>::setThreshold (SampleType newValue)
{
thresholddB = newValue;
update();
}
template <typename SampleType>
void NoiseGate<SampleType>::setRatio (SampleType newRatio)
{
jassert (newRatio >= static_cast<SampleType> (1.0));
ratio = newRatio;
update();
}
template <typename SampleType>
void NoiseGate<SampleType>::setAttack (SampleType newAttack)
{
attackTime = newAttack;
update();
}
template <typename SampleType>
void NoiseGate<SampleType>::setRelease (SampleType newRelease)
{
releaseTime = newRelease;
update();
}
//==============================================================================
template <typename SampleType>
void NoiseGate<SampleType>::prepare (const ProcessSpec& spec)
{
jassert (spec.sampleRate > 0);
jassert (spec.numChannels > 0);
sampleRate = spec.sampleRate;
RMSFilter.prepare (spec);
envelopeFilter.prepare (spec);
update();
reset();
}
template <typename SampleType>
void NoiseGate<SampleType>::reset()
{
RMSFilter.reset();
envelopeFilter.reset();
}
//==============================================================================
template <typename SampleType>
SampleType NoiseGate<SampleType>::processSample (int channel, SampleType sample)
{
// RMS ballistics filter
auto env = RMSFilter.processSample (channel, sample);
// Ballistics filter
env = envelopeFilter.processSample (channel, env);
// VCA
auto gain = (env > threshold) ? static_cast<SampleType> (1.0)
: std::pow (env * thresholdInverse, currentRatio - static_cast<SampleType> (1.0));
// Output
return gain * sample;
}
template <typename SampleType>
void NoiseGate<SampleType>::update()
{
threshold = Decibels::decibelsToGain (thresholddB, static_cast<SampleType> (-200.0));
thresholdInverse = static_cast<SampleType> (1.0) / threshold;
currentRatio = ratio;
envelopeFilter.setAttackTime (attackTime);
envelopeFilter.setReleaseTime (releaseTime);
}
//==============================================================================
template class NoiseGate<float>;
template class NoiseGate<double>;
} // namespace dsp
} // namespace juce

View File

@ -1,110 +1,110 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
A simple noise gate with standard threshold, ratio, attack time and
release time controls. Can be used as an expander if the ratio is low.
@tags{DSP}
*/
template <typename SampleType>
class NoiseGate
{
public:
//==============================================================================
/** Constructor. */
NoiseGate();
//==============================================================================
/** Sets the threshold in dB of the noise-gate.*/
void setThreshold (SampleType newThreshold);
/** Sets the ratio of the noise-gate (must be higher or equal to 1).*/
void setRatio (SampleType newRatio);
/** Sets the attack time in milliseconds of the noise-gate.*/
void setAttack (SampleType newAttack);
/** Sets the release time in milliseconds of the noise-gate.*/
void setRelease (SampleType newRelease);
//==============================================================================
/** Initialises the processor. */
void prepare (const ProcessSpec& spec);
/** Resets the internal state variables of the processor. */
void reset();
//==============================================================================
/** Processes the input and output samples supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumSamples() == numSamples);
if (context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
for (size_t channel = 0; channel < numChannels; ++channel)
{
auto* inputSamples = inputBlock .getChannelPointer (channel);
auto* outputSamples = outputBlock.getChannelPointer (channel);
for (size_t i = 0; i < numSamples; ++i)
outputSamples[i] = processSample ((int) channel, inputSamples[i]);
}
}
/** Performs the processing operation on a single sample at a time. */
SampleType processSample (int channel, SampleType inputValue);
private:
//==============================================================================
void update();
//==============================================================================
SampleType threshold, thresholdInverse, currentRatio;
BallisticsFilter<SampleType> envelopeFilter, RMSFilter;
double sampleRate = 44100.0;
SampleType thresholddB = -100, ratio = 10.0, attackTime = 1.0, releaseTime = 100.0;
};
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
A simple noise gate with standard threshold, ratio, attack time and
release time controls. Can be used as an expander if the ratio is low.
@tags{DSP}
*/
template <typename SampleType>
class NoiseGate
{
public:
//==============================================================================
/** Constructor. */
NoiseGate();
//==============================================================================
/** Sets the threshold in dB of the noise-gate.*/
void setThreshold (SampleType newThreshold);
/** Sets the ratio of the noise-gate (must be higher or equal to 1).*/
void setRatio (SampleType newRatio);
/** Sets the attack time in milliseconds of the noise-gate.*/
void setAttack (SampleType newAttack);
/** Sets the release time in milliseconds of the noise-gate.*/
void setRelease (SampleType newRelease);
//==============================================================================
/** Initialises the processor. */
void prepare (const ProcessSpec& spec);
/** Resets the internal state variables of the processor. */
void reset();
//==============================================================================
/** Processes the input and output samples supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumSamples() == numSamples);
if (context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
for (size_t channel = 0; channel < numChannels; ++channel)
{
auto* inputSamples = inputBlock .getChannelPointer (channel);
auto* outputSamples = outputBlock.getChannelPointer (channel);
for (size_t i = 0; i < numSamples; ++i)
outputSamples[i] = processSample ((int) channel, inputSamples[i]);
}
}
/** Performs the processing operation on a single sample at a time. */
SampleType processSample (int channel, SampleType inputValue);
private:
//==============================================================================
void update();
//==============================================================================
SampleType threshold, thresholdInverse, currentRatio;
BallisticsFilter<SampleType> envelopeFilter, RMSFilter;
double sampleRate = 44100.0;
SampleType thresholddB = -100, ratio = 10.0, attackTime = 1.0, releaseTime = 100.0;
};
} // namespace dsp
} // namespace juce

View File

@ -1,251 +1,251 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
Generates a signal based on a user-supplied function.
@tags{DSP}
*/
template <typename SampleType>
class Oscillator
{
public:
/** The NumericType is the underlying primitive type used by the SampleType (which
could be either a primitive or vector)
*/
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
/** Creates an uninitialised oscillator. Call initialise before first use. */
Oscillator() = default;
/** Creates an oscillator with a periodic input function (-pi..pi).
If lookup table is not zero, then the function will be approximated
with a lookup table.
*/
Oscillator (const std::function<NumericType (NumericType)>& function,
size_t lookupTableNumPoints = 0)
{
initialise (function, lookupTableNumPoints);
}
/** Returns true if the Oscillator has been initialised. */
bool isInitialised() const noexcept { return static_cast<bool> (generator); }
/** Initialises the oscillator with a waveform. */
void initialise (const std::function<NumericType (NumericType)>& function,
size_t lookupTableNumPoints = 0)
{
if (lookupTableNumPoints != 0)
{
auto* table = new LookupTableTransform<NumericType> (function,
-MathConstants<NumericType>::pi,
MathConstants<NumericType>::pi,
lookupTableNumPoints);
lookupTable.reset (table);
generator = [table] (NumericType x) { return (*table) (x); };
}
else
{
generator = function;
}
}
//==============================================================================
/** Sets the frequency of the oscillator. */
void setFrequency (NumericType newFrequency, bool force = false) noexcept
{
if (force)
{
frequency.setCurrentAndTargetValue (newFrequency);
return;
}
frequency.setTargetValue (newFrequency);
}
/** Returns the current frequency of the oscillator. */
NumericType getFrequency() const noexcept { return frequency.getTargetValue(); }
//==============================================================================
/** Called before processing starts. */
void prepare (const ProcessSpec& spec) noexcept
{
sampleRate = static_cast<NumericType> (spec.sampleRate);
rampBuffer.resize ((int) spec.maximumBlockSize);
reset();
}
/** Resets the internal state of the oscillator */
void reset() noexcept
{
phase.reset();
if (sampleRate > 0)
frequency.reset (sampleRate, 0.05);
}
//==============================================================================
/** Returns the result of processing a single sample. */
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType input) noexcept
{
jassert (isInitialised());
auto increment = MathConstants<NumericType>::twoPi * frequency.getNextValue() / sampleRate;
return input + generator (phase.advance (increment) - MathConstants<NumericType>::pi);
}
/** Processes the input and output buffers supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
jassert (isInitialised());
auto&& outBlock = context.getOutputBlock();
auto&& inBlock = context.getInputBlock();
// this is an output-only processor
jassert (outBlock.getNumSamples() <= static_cast<size_t> (rampBuffer.size()));
auto len = outBlock.getNumSamples();
auto numChannels = outBlock.getNumChannels();
auto inputChannels = inBlock.getNumChannels();
auto baseIncrement = MathConstants<NumericType>::twoPi / sampleRate;
if (context.isBypassed)
context.getOutputBlock().clear();
if (frequency.isSmoothing())
{
auto* buffer = rampBuffer.getRawDataPointer();
for (size_t i = 0; i < len; ++i)
buffer[i] = phase.advance (baseIncrement * frequency.getNextValue())
- MathConstants<NumericType>::pi;
if (! context.isBypassed)
{
size_t ch;
if (context.usesSeparateInputAndOutputBlocks())
{
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
{
auto* dst = outBlock.getChannelPointer (ch);
auto* src = inBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] = src[i] + generator (buffer[i]);
}
}
else
{
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
{
auto* dst = outBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] += generator (buffer[i]);
}
}
for (; ch < numChannels; ++ch)
{
auto* dst = outBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] = generator (buffer[i]);
}
}
}
else
{
auto freq = baseIncrement * frequency.getNextValue();
auto p = phase;
if (context.isBypassed)
{
frequency.skip (static_cast<int> (len));
p.advance (freq * static_cast<NumericType> (len));
}
else
{
size_t ch;
if (context.usesSeparateInputAndOutputBlocks())
{
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
{
p = phase;
auto* dst = outBlock.getChannelPointer (ch);
auto* src = inBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] = src[i] + generator (p.advance (freq) - MathConstants<NumericType>::pi);
}
}
else
{
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
{
p = phase;
auto* dst = outBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] += generator (p.advance (freq) - MathConstants<NumericType>::pi);
}
}
for (; ch < numChannels; ++ch)
{
p = phase;
auto* dst = outBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] = generator (p.advance (freq) - MathConstants<NumericType>::pi);
}
}
phase = p;
}
}
private:
//==============================================================================
std::function<NumericType (NumericType)> generator;
std::unique_ptr<LookupTableTransform<NumericType>> lookupTable;
Array<NumericType> rampBuffer;
SmoothedValue<NumericType> frequency { static_cast<NumericType> (440.0) };
NumericType sampleRate = 48000.0;
Phase<NumericType> phase;
};
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
Generates a signal based on a user-supplied function.
@tags{DSP}
*/
template <typename SampleType>
class Oscillator
{
public:
/** The NumericType is the underlying primitive type used by the SampleType (which
could be either a primitive or vector)
*/
using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
/** Creates an uninitialised oscillator. Call initialise before first use. */
Oscillator() = default;
/** Creates an oscillator with a periodic input function (-pi..pi).
If lookup table is not zero, then the function will be approximated
with a lookup table.
*/
Oscillator (const std::function<NumericType (NumericType)>& function,
size_t lookupTableNumPoints = 0)
{
initialise (function, lookupTableNumPoints);
}
/** Returns true if the Oscillator has been initialised. */
bool isInitialised() const noexcept { return static_cast<bool> (generator); }
/** Initialises the oscillator with a waveform. */
void initialise (const std::function<NumericType (NumericType)>& function,
size_t lookupTableNumPoints = 0)
{
if (lookupTableNumPoints != 0)
{
auto* table = new LookupTableTransform<NumericType> (function,
-MathConstants<NumericType>::pi,
MathConstants<NumericType>::pi,
lookupTableNumPoints);
lookupTable.reset (table);
generator = [table] (NumericType x) { return (*table) (x); };
}
else
{
generator = function;
}
}
//==============================================================================
/** Sets the frequency of the oscillator. */
void setFrequency (NumericType newFrequency, bool force = false) noexcept
{
if (force)
{
frequency.setCurrentAndTargetValue (newFrequency);
return;
}
frequency.setTargetValue (newFrequency);
}
/** Returns the current frequency of the oscillator. */
NumericType getFrequency() const noexcept { return frequency.getTargetValue(); }
//==============================================================================
/** Called before processing starts. */
void prepare (const ProcessSpec& spec) noexcept
{
sampleRate = static_cast<NumericType> (spec.sampleRate);
rampBuffer.resize ((int) spec.maximumBlockSize);
reset();
}
/** Resets the internal state of the oscillator */
void reset() noexcept
{
phase.reset();
if (sampleRate > 0)
frequency.reset (sampleRate, 0.05);
}
//==============================================================================
/** Returns the result of processing a single sample. */
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType input) noexcept
{
jassert (isInitialised());
auto increment = MathConstants<NumericType>::twoPi * frequency.getNextValue() / sampleRate;
return input + generator (phase.advance (increment) - MathConstants<NumericType>::pi);
}
/** Processes the input and output buffers supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
jassert (isInitialised());
auto&& outBlock = context.getOutputBlock();
auto&& inBlock = context.getInputBlock();
// this is an output-only processor
jassert (outBlock.getNumSamples() <= static_cast<size_t> (rampBuffer.size()));
auto len = outBlock.getNumSamples();
auto numChannels = outBlock.getNumChannels();
auto inputChannels = inBlock.getNumChannels();
auto baseIncrement = MathConstants<NumericType>::twoPi / sampleRate;
if (context.isBypassed)
context.getOutputBlock().clear();
if (frequency.isSmoothing())
{
auto* buffer = rampBuffer.getRawDataPointer();
for (size_t i = 0; i < len; ++i)
buffer[i] = phase.advance (baseIncrement * frequency.getNextValue())
- MathConstants<NumericType>::pi;
if (! context.isBypassed)
{
size_t ch;
if (context.usesSeparateInputAndOutputBlocks())
{
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
{
auto* dst = outBlock.getChannelPointer (ch);
auto* src = inBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] = src[i] + generator (buffer[i]);
}
}
else
{
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
{
auto* dst = outBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] += generator (buffer[i]);
}
}
for (; ch < numChannels; ++ch)
{
auto* dst = outBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] = generator (buffer[i]);
}
}
}
else
{
auto freq = baseIncrement * frequency.getNextValue();
auto p = phase;
if (context.isBypassed)
{
frequency.skip (static_cast<int> (len));
p.advance (freq * static_cast<NumericType> (len));
}
else
{
size_t ch;
if (context.usesSeparateInputAndOutputBlocks())
{
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
{
p = phase;
auto* dst = outBlock.getChannelPointer (ch);
auto* src = inBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] = src[i] + generator (p.advance (freq) - MathConstants<NumericType>::pi);
}
}
else
{
for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch)
{
p = phase;
auto* dst = outBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] += generator (p.advance (freq) - MathConstants<NumericType>::pi);
}
}
for (; ch < numChannels; ++ch)
{
p = phase;
auto* dst = outBlock.getChannelPointer (ch);
for (size_t i = 0; i < len; ++i)
dst[i] = generator (p.advance (freq) - MathConstants<NumericType>::pi);
}
}
phase = p;
}
}
private:
//==============================================================================
std::function<NumericType (NumericType)> generator;
std::unique_ptr<LookupTableTransform<NumericType>> lookupTable;
Array<NumericType> rampBuffer;
SmoothedValue<NumericType> frequency { static_cast<NumericType> (440.0) };
NumericType sampleRate = 48000.0;
Phase<NumericType> phase;
};
} // namespace dsp
} // namespace juce

View File

@ -1,154 +1,154 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
Phaser<SampleType>::Phaser()
{
auto oscFunction = [] (SampleType x) { return std::sin (x); };
osc.initialise (oscFunction);
for (auto n = 0; n < numStages; ++n)
{
filters.add (new FirstOrderTPTFilter<SampleType>());
filters[n]->setType (FirstOrderTPTFilterType::allpass);
}
dryWet.setMixingRule (DryWetMixingRule::linear);
}
template <typename SampleType>
void Phaser<SampleType>::setRate (SampleType newRateHz)
{
jassert (isPositiveAndBelow (newRateHz, static_cast<SampleType> (100.0)));
rate = newRateHz;
update();
}
template <typename SampleType>
void Phaser<SampleType>::setDepth (SampleType newDepth)
{
jassert (isPositiveAndNotGreaterThan (newDepth, static_cast<SampleType> (1.0)));
depth = newDepth;
update();
}
template <typename SampleType>
void Phaser<SampleType>::setCentreFrequency (SampleType newCentreHz)
{
jassert (isPositiveAndBelow (newCentreHz, static_cast<SampleType> (sampleRate * 0.5)));
centreFrequency = newCentreHz;
normCentreFrequency = mapFromLog10 (centreFrequency, static_cast<SampleType> (20.0), static_cast<SampleType> (jmin (20000.0, 0.49 * sampleRate)));
}
template <typename SampleType>
void Phaser<SampleType>::setFeedback (SampleType newFeedback)
{
jassert (newFeedback >= static_cast<SampleType> (-1.0) && newFeedback <= static_cast<SampleType> (1.0));
feedback = newFeedback;
update();
}
template <typename SampleType>
void Phaser<SampleType>::setMix (SampleType newMix)
{
jassert (isPositiveAndNotGreaterThan (newMix, static_cast<SampleType> (1.0)));
mix = newMix;
update();
}
//==============================================================================
template <typename SampleType>
void Phaser<SampleType>::prepare (const ProcessSpec& spec)
{
jassert (spec.sampleRate > 0);
jassert (spec.numChannels > 0);
sampleRate = spec.sampleRate;
for (auto n = 0; n < numStages; ++n)
filters[n]->prepare (spec);
dryWet.prepare (spec);
feedbackVolume.resize (spec.numChannels);
lastOutput.resize (spec.numChannels);
auto specDown = spec;
specDown.sampleRate /= (double) maxUpdateCounter;
specDown.maximumBlockSize = specDown.maximumBlockSize / (uint32) maxUpdateCounter + 1;
osc.prepare (specDown);
bufferFrequency.setSize (1, (int) specDown.maximumBlockSize, false, false, true);
update();
reset();
}
template <typename SampleType>
void Phaser<SampleType>::reset()
{
std::fill (lastOutput.begin(), lastOutput.end(), static_cast<SampleType> (0));
for (auto n = 0; n < numStages; ++n)
filters[n]->reset();
osc.reset();
dryWet.reset();
oscVolume.reset (sampleRate / (double) maxUpdateCounter, 0.05);
for (auto& vol : feedbackVolume)
vol.reset (sampleRate, 0.05);
updateCounter = 0;
}
template <typename SampleType>
void Phaser<SampleType>::update()
{
osc.setFrequency (rate);
oscVolume.setTargetValue (depth * (SampleType) 0.5);
dryWet.setWetMixProportion (mix);
for (auto& vol : feedbackVolume)
vol.setTargetValue (feedback);
}
//==============================================================================
template class Phaser<float>;
template class Phaser<double>;
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
//==============================================================================
template <typename SampleType>
Phaser<SampleType>::Phaser()
{
auto oscFunction = [] (SampleType x) { return std::sin (x); };
osc.initialise (oscFunction);
for (auto n = 0; n < numStages; ++n)
{
filters.add (new FirstOrderTPTFilter<SampleType>());
filters[n]->setType (FirstOrderTPTFilterType::allpass);
}
dryWet.setMixingRule (DryWetMixingRule::linear);
}
template <typename SampleType>
void Phaser<SampleType>::setRate (SampleType newRateHz)
{
jassert (isPositiveAndBelow (newRateHz, static_cast<SampleType> (100.0)));
rate = newRateHz;
update();
}
template <typename SampleType>
void Phaser<SampleType>::setDepth (SampleType newDepth)
{
jassert (isPositiveAndNotGreaterThan (newDepth, static_cast<SampleType> (1.0)));
depth = newDepth;
update();
}
template <typename SampleType>
void Phaser<SampleType>::setCentreFrequency (SampleType newCentreHz)
{
jassert (isPositiveAndBelow (newCentreHz, static_cast<SampleType> (sampleRate * 0.5)));
centreFrequency = newCentreHz;
normCentreFrequency = mapFromLog10 (centreFrequency, static_cast<SampleType> (20.0), static_cast<SampleType> (jmin (20000.0, 0.49 * sampleRate)));
}
template <typename SampleType>
void Phaser<SampleType>::setFeedback (SampleType newFeedback)
{
jassert (newFeedback >= static_cast<SampleType> (-1.0) && newFeedback <= static_cast<SampleType> (1.0));
feedback = newFeedback;
update();
}
template <typename SampleType>
void Phaser<SampleType>::setMix (SampleType newMix)
{
jassert (isPositiveAndNotGreaterThan (newMix, static_cast<SampleType> (1.0)));
mix = newMix;
update();
}
//==============================================================================
template <typename SampleType>
void Phaser<SampleType>::prepare (const ProcessSpec& spec)
{
jassert (spec.sampleRate > 0);
jassert (spec.numChannels > 0);
sampleRate = spec.sampleRate;
for (auto n = 0; n < numStages; ++n)
filters[n]->prepare (spec);
dryWet.prepare (spec);
feedbackVolume.resize (spec.numChannels);
lastOutput.resize (spec.numChannels);
auto specDown = spec;
specDown.sampleRate /= (double) maxUpdateCounter;
specDown.maximumBlockSize = specDown.maximumBlockSize / (uint32) maxUpdateCounter + 1;
osc.prepare (specDown);
bufferFrequency.setSize (1, (int) specDown.maximumBlockSize, false, false, true);
update();
reset();
}
template <typename SampleType>
void Phaser<SampleType>::reset()
{
std::fill (lastOutput.begin(), lastOutput.end(), static_cast<SampleType> (0));
for (auto n = 0; n < numStages; ++n)
filters[n]->reset();
osc.reset();
dryWet.reset();
oscVolume.reset (sampleRate / (double) maxUpdateCounter, 0.05);
for (auto& vol : feedbackVolume)
vol.reset (sampleRate, 0.05);
updateCounter = 0;
}
template <typename SampleType>
void Phaser<SampleType>::update()
{
osc.setFrequency (rate);
oscVolume.setTargetValue (depth * (SampleType) 0.5);
dryWet.setWetMixProportion (mix);
for (auto& vol : feedbackVolume)
vol.setTargetValue (feedback);
}
//==============================================================================
template class Phaser<float>;
template class Phaser<double>;
} // namespace dsp
} // namespace juce

View File

@ -1,206 +1,206 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
A 6 stage phaser that modulates first order all-pass filters to create sweeping
notches in the magnitude frequency response.
This audio effect can be controlled with standard phaser parameters: the speed
and depth of the LFO controlling the frequency response, a mix control, a
feedback control, and the centre frequency of the modulation.
@tags{DSP}
*/
template <typename SampleType>
class Phaser
{
public:
//==============================================================================
/** Constructor. */
Phaser();
//==============================================================================
/** Sets the rate (in Hz) of the LFO modulating the phaser all-pass filters. This
rate must be lower than 100 Hz.
*/
void setRate (SampleType newRateHz);
/** Sets the volume (between 0 and 1) of the LFO modulating the phaser all-pass
filters.
*/
void setDepth (SampleType newDepth);
/** Sets the centre frequency (in Hz) of the phaser all-pass filters modulation.
*/
void setCentreFrequency (SampleType newCentreHz);
/** Sets the feedback volume (between -1 and 1) of the phaser. Negative can be
used to get specific phaser sounds.
*/
void setFeedback (SampleType newFeedback);
/** Sets the amount of dry and wet signal in the output of the phaser (between 0
for full dry and 1 for full wet).
*/
void setMix (SampleType newMix);
//==============================================================================
/** Initialises the processor. */
void prepare (const ProcessSpec& spec);
/** Resets the internal state variables of the processor. */
void reset();
//==============================================================================
/** Processes the input and output samples supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumChannels() == lastOutput.size());
jassert (inputBlock.getNumSamples() == numSamples);
if (context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
int numSamplesDown = 0;
auto counter = updateCounter;
for (size_t i = 0; i < numSamples; ++i)
{
if (counter == 0)
numSamplesDown++;
counter++;
if (counter == maxUpdateCounter)
counter = 0;
}
if (numSamplesDown > 0)
{
auto freqBlock = AudioBlock<SampleType>(bufferFrequency).getSubBlock (0, (size_t) numSamplesDown);
auto contextFreq = ProcessContextReplacing<SampleType> (freqBlock);
freqBlock.clear();
osc.process (contextFreq);
freqBlock.multiplyBy (oscVolume);
}
auto* freqSamples = bufferFrequency.getWritePointer (0);
for (int i = 0; i < numSamplesDown; ++i)
{
auto lfo = jlimit (static_cast<SampleType> (0.0),
static_cast<SampleType> (1.0),
freqSamples[i] + normCentreFrequency);
freqSamples[i] = mapToLog10 (lfo, static_cast<SampleType> (20.0),
static_cast<SampleType> (jmin (20000.0, 0.49 * sampleRate)));
}
auto currentFrequency = filters[0]->getCutoffFrequency();
dryWet.pushDrySamples (inputBlock);
for (size_t channel = 0; channel < numChannels; ++channel)
{
counter = updateCounter;
int k = 0;
auto* inputSamples = inputBlock .getChannelPointer (channel);
auto* outputSamples = outputBlock.getChannelPointer (channel);
for (size_t i = 0; i < numSamples; ++i)
{
auto input = inputSamples[i];
auto output = input - lastOutput[channel];
if (i == 0 && counter != 0)
for (int n = 0; n < numStages; ++n)
filters[n]->setCutoffFrequency (currentFrequency);
if (counter == 0)
{
for (int n = 0; n < numStages; ++n)
filters[n]->setCutoffFrequency (freqSamples[k]);
k++;
}
for (int n = 0; n < numStages; ++n)
output = filters[n]->processSample ((int) channel, output);
outputSamples[i] = output;
lastOutput[channel] = output * feedbackVolume[channel].getNextValue();
counter++;
if (counter == maxUpdateCounter)
counter = 0;
}
}
dryWet.mixWetSamples (outputBlock);
updateCounter = (updateCounter + (int) numSamples) % maxUpdateCounter;
}
private:
//==============================================================================
void update();
//==============================================================================
Oscillator<SampleType> osc;
OwnedArray<FirstOrderTPTFilter<SampleType>> filters;
SmoothedValue<SampleType, ValueSmoothingTypes::Linear> oscVolume;
std::vector<SmoothedValue<SampleType, ValueSmoothingTypes::Linear>> feedbackVolume { 2 };
DryWetMixer<SampleType> dryWet;
std::vector<SampleType> lastOutput { 2 };
AudioBuffer<SampleType> bufferFrequency;
SampleType normCentreFrequency = 0.5;
double sampleRate = 44100.0;
int updateCounter = 0;
static constexpr int maxUpdateCounter = 4;
SampleType rate = 1.0, depth = 0.5, feedback = 0.0, mix = 0.5;
SampleType centreFrequency = 1300.0;
static constexpr int numStages = 6;
};
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
A 6 stage phaser that modulates first order all-pass filters to create sweeping
notches in the magnitude frequency response.
This audio effect can be controlled with standard phaser parameters: the speed
and depth of the LFO controlling the frequency response, a mix control, a
feedback control, and the centre frequency of the modulation.
@tags{DSP}
*/
template <typename SampleType>
class Phaser
{
public:
//==============================================================================
/** Constructor. */
Phaser();
//==============================================================================
/** Sets the rate (in Hz) of the LFO modulating the phaser all-pass filters. This
rate must be lower than 100 Hz.
*/
void setRate (SampleType newRateHz);
/** Sets the volume (between 0 and 1) of the LFO modulating the phaser all-pass
filters.
*/
void setDepth (SampleType newDepth);
/** Sets the centre frequency (in Hz) of the phaser all-pass filters modulation.
*/
void setCentreFrequency (SampleType newCentreHz);
/** Sets the feedback volume (between -1 and 1) of the phaser. Negative can be
used to get specific phaser sounds.
*/
void setFeedback (SampleType newFeedback);
/** Sets the amount of dry and wet signal in the output of the phaser (between 0
for full dry and 1 for full wet).
*/
void setMix (SampleType newMix);
//==============================================================================
/** Initialises the processor. */
void prepare (const ProcessSpec& spec);
/** Resets the internal state variables of the processor. */
void reset();
//==============================================================================
/** Processes the input and output samples supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumChannels() == numChannels);
jassert (inputBlock.getNumChannels() == lastOutput.size());
jassert (inputBlock.getNumSamples() == numSamples);
if (context.isBypassed)
{
outputBlock.copyFrom (inputBlock);
return;
}
int numSamplesDown = 0;
auto counter = updateCounter;
for (size_t i = 0; i < numSamples; ++i)
{
if (counter == 0)
numSamplesDown++;
counter++;
if (counter == maxUpdateCounter)
counter = 0;
}
if (numSamplesDown > 0)
{
auto freqBlock = AudioBlock<SampleType>(bufferFrequency).getSubBlock (0, (size_t) numSamplesDown);
auto contextFreq = ProcessContextReplacing<SampleType> (freqBlock);
freqBlock.clear();
osc.process (contextFreq);
freqBlock.multiplyBy (oscVolume);
}
auto* freqSamples = bufferFrequency.getWritePointer (0);
for (int i = 0; i < numSamplesDown; ++i)
{
auto lfo = jlimit (static_cast<SampleType> (0.0),
static_cast<SampleType> (1.0),
freqSamples[i] + normCentreFrequency);
freqSamples[i] = mapToLog10 (lfo, static_cast<SampleType> (20.0),
static_cast<SampleType> (jmin (20000.0, 0.49 * sampleRate)));
}
auto currentFrequency = filters[0]->getCutoffFrequency();
dryWet.pushDrySamples (inputBlock);
for (size_t channel = 0; channel < numChannels; ++channel)
{
counter = updateCounter;
int k = 0;
auto* inputSamples = inputBlock .getChannelPointer (channel);
auto* outputSamples = outputBlock.getChannelPointer (channel);
for (size_t i = 0; i < numSamples; ++i)
{
auto input = inputSamples[i];
auto output = input - lastOutput[channel];
if (i == 0 && counter != 0)
for (int n = 0; n < numStages; ++n)
filters[n]->setCutoffFrequency (currentFrequency);
if (counter == 0)
{
for (int n = 0; n < numStages; ++n)
filters[n]->setCutoffFrequency (freqSamples[k]);
k++;
}
for (int n = 0; n < numStages; ++n)
output = filters[n]->processSample ((int) channel, output);
outputSamples[i] = output;
lastOutput[channel] = output * feedbackVolume[channel].getNextValue();
counter++;
if (counter == maxUpdateCounter)
counter = 0;
}
}
dryWet.mixWetSamples (outputBlock);
updateCounter = (updateCounter + (int) numSamples) % maxUpdateCounter;
}
private:
//==============================================================================
void update();
//==============================================================================
Oscillator<SampleType> osc;
OwnedArray<FirstOrderTPTFilter<SampleType>> filters;
SmoothedValue<SampleType, ValueSmoothingTypes::Linear> oscVolume;
std::vector<SmoothedValue<SampleType, ValueSmoothingTypes::Linear>> feedbackVolume { 2 };
DryWetMixer<SampleType> dryWet;
std::vector<SampleType> lastOutput { 2 };
AudioBuffer<SampleType> bufferFrequency;
SampleType normCentreFrequency = 0.5;
double sampleRate = 44100.0;
int updateCounter = 0;
static constexpr int maxUpdateCounter = 4;
SampleType rate = 1.0, depth = 0.5, feedback = 0.0, mix = 0.5;
SampleType centreFrequency = 1300.0;
static constexpr int numStages = 6;
};
} // namespace dsp
} // namespace juce

View File

@ -1,115 +1,115 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
Processor wrapper around juce::Reverb for easy integration into ProcessorChain.
@tags{DSP}
*/
class Reverb
{
public:
//==============================================================================
/** Creates an uninitialised Reverb processor. Call prepare() before first use. */
Reverb() = default;
//==============================================================================
using Parameters = juce::Reverb::Parameters;
/** Returns the reverb's current parameters. */
const Parameters& getParameters() const noexcept { return reverb.getParameters(); }
/** 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) { reverb.setParameters (newParams); }
/** Returns true if the reverb is enabled. */
bool isEnabled() const noexcept { return enabled; }
/** Enables/disables the reverb. */
void setEnabled (bool newValue) noexcept { enabled = newValue; }
//==============================================================================
/** Initialises the reverb. */
void prepare (const ProcessSpec& spec)
{
reverb.setSampleRate (spec.sampleRate);
}
/** Resets the reverb's internal state. */
void reset() noexcept
{
reverb.reset();
}
//==============================================================================
/** Applies the reverb to a mono or stereo buffer. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numInChannels = inputBlock.getNumChannels();
const auto numOutChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumSamples() == numSamples);
outputBlock.copyFrom (inputBlock);
if (! enabled || context.isBypassed)
return;
if (numInChannels == 1 && numOutChannels == 1)
{
reverb.processMono (outputBlock.getChannelPointer (0), (int) numSamples);
}
else if (numInChannels == 2 && numOutChannels == 2)
{
reverb.processStereo (outputBlock.getChannelPointer (0),
outputBlock.getChannelPointer (1),
(int) numSamples);
}
else
{
jassertfalse; // invalid channel configuration
}
}
private:
//==============================================================================
juce::Reverb reverb;
bool enabled = true;
};
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
Processor wrapper around juce::Reverb for easy integration into ProcessorChain.
@tags{DSP}
*/
class Reverb
{
public:
//==============================================================================
/** Creates an uninitialised Reverb processor. Call prepare() before first use. */
Reverb() = default;
//==============================================================================
using Parameters = juce::Reverb::Parameters;
/** Returns the reverb's current parameters. */
const Parameters& getParameters() const noexcept { return reverb.getParameters(); }
/** 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) { reverb.setParameters (newParams); }
/** Returns true if the reverb is enabled. */
bool isEnabled() const noexcept { return enabled; }
/** Enables/disables the reverb. */
void setEnabled (bool newValue) noexcept { enabled = newValue; }
//==============================================================================
/** Initialises the reverb. */
void prepare (const ProcessSpec& spec)
{
reverb.setSampleRate (spec.sampleRate);
}
/** Resets the reverb's internal state. */
void reset() noexcept
{
reverb.reset();
}
//==============================================================================
/** Applies the reverb to a mono or stereo buffer. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
const auto& inputBlock = context.getInputBlock();
auto& outputBlock = context.getOutputBlock();
const auto numInChannels = inputBlock.getNumChannels();
const auto numOutChannels = outputBlock.getNumChannels();
const auto numSamples = outputBlock.getNumSamples();
jassert (inputBlock.getNumSamples() == numSamples);
outputBlock.copyFrom (inputBlock);
if (! enabled || context.isBypassed)
return;
if (numInChannels == 1 && numOutChannels == 1)
{
reverb.processMono (outputBlock.getChannelPointer (0), (int) numSamples);
}
else if (numInChannels == 2 && numOutChannels == 2)
{
reverb.processStereo (outputBlock.getChannelPointer (0),
outputBlock.getChannelPointer (1),
(int) numSamples);
}
else
{
jassertfalse; // invalid channel configuration
}
}
private:
//==============================================================================
juce::Reverb reverb;
bool enabled = true;
};
} // namespace dsp
} // namespace juce

View File

@ -1,85 +1,83 @@
/*
==============================================================================
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.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
Applies waveshaping to audio samples as single samples or AudioBlocks.
@tags{DSP}
*/
template <typename FloatType, typename Function = FloatType (*) (FloatType)>
struct WaveShaper
{
Function functionToUse;
//==============================================================================
/** Called before processing starts. */
void prepare (const ProcessSpec&) noexcept {}
//==============================================================================
/** Returns the result of processing a single sample. */
template <typename SampleType>
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType inputSample) const noexcept
{
return functionToUse (inputSample);
}
/** Processes the input and output buffers supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) const noexcept
{
if (context.isBypassed)
{
if (context.usesSeparateInputAndOutputBlocks())
context.getOutputBlock().copyFrom (context.getInputBlock());
}
else
{
AudioBlock<FloatType>::process (context.getInputBlock(),
context.getOutputBlock(),
functionToUse);
}
}
void reset() noexcept {}
};
//==============================================================================
// Although clang supports C++17, their standard library still has no invoke_result
// support. Remove the "|| JUCE_CLANG" once clang supports this properly!
#if (! JUCE_CXX17_IS_AVAILABLE) || (JUCE_CLANG && ! JUCE_WINDOWS)
template <typename Functor>
static WaveShaper<typename std::result_of<Functor>, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; }
#else
template <typename Functor>
static WaveShaper<typename std::invoke_result<Functor>, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; }
#endif
} // namespace dsp
} // namespace juce
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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 dsp
{
/**
Applies waveshaping to audio samples as single samples or AudioBlocks.
@tags{DSP}
*/
template <typename FloatType, typename Function = FloatType (*) (FloatType)>
struct WaveShaper
{
Function functionToUse;
//==============================================================================
/** Called before processing starts. */
void prepare (const ProcessSpec&) noexcept {}
//==============================================================================
/** Returns the result of processing a single sample. */
template <typename SampleType>
SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType inputSample) const noexcept
{
return functionToUse (inputSample);
}
/** Processes the input and output buffers supplied in the processing context. */
template <typename ProcessContext>
void process (const ProcessContext& context) const noexcept
{
if (context.isBypassed)
{
if (context.usesSeparateInputAndOutputBlocks())
context.getOutputBlock().copyFrom (context.getInputBlock());
}
else
{
AudioBlock<FloatType>::process (context.getInputBlock(),
context.getOutputBlock(),
functionToUse);
}
}
void reset() noexcept {}
};
//==============================================================================
#if JUCE_CXX17_IS_AVAILABLE && ! ((JUCE_MAC || JUCE_IOS) && JUCE_CLANG && __clang_major__ < 10)
template <typename Functor>
static WaveShaper<typename std::invoke_result<Functor>, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; }
#else
template <typename Functor>
static WaveShaper<typename std::result_of<Functor>, Functor> CreateWaveShaper (Functor functionToUse) { return {functionToUse}; }
#endif
} // namespace dsp
} // namespace juce