Loading...
Searching...
No Matches
juce_audio_basics/utilities/juce_Reverb.h
/*
==============================================================================
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.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
class Reverb
{
public:
//==============================================================================
Reverb()
{
setParameters (Parameters());
setSampleRate (44100.0);
}
//==============================================================================
struct Parameters
{
float roomSize = 0.5f;
float damping = 0.5f;
float wetLevel = 0.33f;
float dryLevel = 0.4f;
float width = 1.0f;
float freezeMode = 0.0f;
};
//==============================================================================
const Parameters& getParameters() const noexcept { return parameters; }
void setParameters (const Parameters& newParams)
{
const float wetScaleFactor = 3.0f;
const float dryScaleFactor = 2.0f;
const float wet = newParams.wetLevel * wetScaleFactor;
dryGain.setTargetValue (newParams.dryLevel * dryScaleFactor);
wetGain1.setTargetValue (0.5f * wet * (1.0f + newParams.width));
wetGain2.setTargetValue (0.5f * wet * (1.0f - newParams.width));
gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f;
parameters = newParams;
updateDamping();
}
//==============================================================================
void setSampleRate (const double sampleRate)
{
jassert (sampleRate > 0);
static const short combTunings[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; // (at 44100Hz)
static const short allPassTunings[] = { 556, 441, 341, 225 };
const int stereoSpread = 23;
const int intSampleRate = (int) sampleRate;
for (int i = 0; i < numCombs; ++i)
{
comb[0][i].setSize ((intSampleRate * combTunings[i]) / 44100);
comb[1][i].setSize ((intSampleRate * (combTunings[i] + stereoSpread)) / 44100);
}
for (int i = 0; i < numAllPasses; ++i)
{
allPass[0][i].setSize ((intSampleRate * allPassTunings[i]) / 44100);
allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100);
}
const double smoothTime = 0.01;
damping .reset (sampleRate, smoothTime);
feedback.reset (sampleRate, smoothTime);
dryGain .reset (sampleRate, smoothTime);
wetGain1.reset (sampleRate, smoothTime);
wetGain2.reset (sampleRate, smoothTime);
}
void reset()
{
for (int j = 0; j < numChannels; ++j)
{
for (int i = 0; i < numCombs; ++i)
comb[j][i].clear();
for (int i = 0; i < numAllPasses; ++i)
allPass[j][i].clear();
}
}
//==============================================================================
void processStereo (float* const left, float* const right, const int numSamples) noexcept
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
jassert (left != nullptr && right != nullptr);
for (int i = 0; i < numSamples; ++i)
{
// NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
const float input = (left[i] + right[i]) * gain;
float outL = 0, outR = 0;
const float damp = damping.getNextValue();
const float feedbck = feedback.getNextValue();
for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
{
outL += comb[0][j].process (input, damp, feedbck);
outR += comb[1][j].process (input, damp, feedbck);
}
for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
{
outL = allPass[0][j].process (outL);
outR = allPass[1][j].process (outR);
}
const float dry = dryGain.getNextValue();
const float wet1 = wetGain1.getNextValue();
const float wet2 = wetGain2.getNextValue();
left[i] = outL * wet1 + outR * wet2 + left[i] * dry;
right[i] = outR * wet1 + outL * wet2 + right[i] * dry;
}
JUCE_END_IGNORE_WARNINGS_MSVC
}
void processMono (float* const samples, const int numSamples) noexcept
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
jassert (samples != nullptr);
for (int i = 0; i < numSamples; ++i)
{
const float input = samples[i] * gain;
float output = 0;
const float damp = damping.getNextValue();
const float feedbck = feedback.getNextValue();
for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
output += comb[0][j].process (input, damp, feedbck);
for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
output = allPass[0][j].process (output);
const float dry = dryGain.getNextValue();
const float wet1 = wetGain1.getNextValue();
samples[i] = output * wet1 + samples[i] * dry;
}
JUCE_END_IGNORE_WARNINGS_MSVC
}
private:
//==============================================================================
static bool isFrozen (const float freezeMode) noexcept { return freezeMode >= 0.5f; }
void updateDamping() noexcept
{
const float roomScaleFactor = 0.28f;
const float roomOffset = 0.7f;
const float dampScaleFactor = 0.4f;
if (isFrozen (parameters.freezeMode))
setDamping (0.0f, 1.0f);
else
setDamping (parameters.damping * dampScaleFactor,
parameters.roomSize * roomScaleFactor + roomOffset);
}
void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept
{
damping.setTargetValue (dampingToUse);
feedback.setTargetValue (roomSizeToUse);
}
//==============================================================================
class CombFilter
{
public:
CombFilter() noexcept {}
void setSize (const int size)
{
if (size != bufferSize)
{
bufferIndex = 0;
buffer.malloc (size);
bufferSize = size;
}
clear();
}
void clear() noexcept
{
last = 0;
buffer.clear ((size_t) bufferSize);
}
float process (const float input, const float damp, const float feedbackLevel) noexcept
{
const float output = buffer[bufferIndex];
last = (output * (1.0f - damp)) + (last * damp);
JUCE_UNDENORMALISE (last);
float temp = input + (last * feedbackLevel);
JUCE_UNDENORMALISE (temp);
buffer[bufferIndex] = temp;
bufferIndex = (bufferIndex + 1) % bufferSize;
return output;
}
private:
HeapBlock<float> buffer;
int bufferSize = 0, bufferIndex = 0;
float last = 0.0f;
JUCE_DECLARE_NON_COPYABLE (CombFilter)
};
//==============================================================================
class AllPassFilter
{
public:
AllPassFilter() noexcept {}
void setSize (const int size)
{
if (size != bufferSize)
{
bufferIndex = 0;
buffer.malloc (size);
bufferSize = size;
}
clear();
}
void clear() noexcept
{
buffer.clear ((size_t) bufferSize);
}
float process (const float input) noexcept
{
const float bufferedValue = buffer [bufferIndex];
float temp = input + (bufferedValue * 0.5f);
JUCE_UNDENORMALISE (temp);
buffer [bufferIndex] = temp;
bufferIndex = (bufferIndex + 1) % bufferSize;
return bufferedValue - input;
}
private:
HeapBlock<float> buffer;
int bufferSize = 0, bufferIndex = 0;
JUCE_DECLARE_NON_COPYABLE (AllPassFilter)
};
//==============================================================================
enum { numCombs = 8, numAllPasses = 4, numChannels = 2 };
Parameters parameters;
float gain;
CombFilter comb [numChannels][numCombs];
AllPassFilter allPass [numChannels][numAllPasses];
SmoothedValue<float> damping, feedback, dryGain, wetGain1, wetGain2;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb)
};
} // namespace juce
juce::Reverb::Parameters::roomSize
float roomSize
Definition juce_audio_basics/utilities/juce_Reverb.h:52
juce::Reverb::processMono
void processMono(float *const samples, const int numSamples) noexcept
Definition juce_audio_basics/utilities/juce_Reverb.h:169
juce::Reverb::Parameters::width
float width
Definition juce_audio_basics/utilities/juce_Reverb.h:56
juce::Reverb::reset
void reset()
Definition juce_audio_basics/utilities/juce_Reverb.h:118
juce::Reverb::processStereo
void processStereo(float *const left, float *const right, const int numSamples) noexcept
Definition juce_audio_basics/utilities/juce_Reverb.h:132
juce::Reverb::Parameters::dryLevel
float dryLevel
Definition juce_audio_basics/utilities/juce_Reverb.h:55
juce::Reverb::Parameters::wetLevel
float wetLevel
Definition juce_audio_basics/utilities/juce_Reverb.h:54
juce::Reverb::Parameters::freezeMode
float freezeMode
Definition juce_audio_basics/utilities/juce_Reverb.h:57
juce::Reverb::setParameters
void setParameters(const Parameters &newParams)
Definition juce_audio_basics/utilities/juce_Reverb.h:69
juce::Reverb::getParameters
const Parameters & getParameters() const noexcept
Definition juce_audio_basics/utilities/juce_Reverb.h:63
juce::Reverb::setSampleRate
void setSampleRate(const double sampleRate)
Definition juce_audio_basics/utilities/juce_Reverb.h:88
juce::Reverb::Parameters::damping
float damping
Definition juce_audio_basics/utilities/juce_Reverb.h:53
juce::Reverb::Parameters
Definition juce_audio_basics/utilities/juce_Reverb.h:51