Damping and high/low cuts
This commit is contained in:
parent
713f3a309a
commit
a5c32ecaf8
78
reverb.h
78
reverb.h
@ -5,6 +5,7 @@ Released under the Boost Software License (see LICENSE.txt) */
|
|||||||
|
|
||||||
#include "dsp/delay.h"
|
#include "dsp/delay.h"
|
||||||
#include "dsp/mix.h"
|
#include "dsp/mix.h"
|
||||||
|
#include "dsp/filters.h"
|
||||||
SIGNALSMITH_DSP_VERSION_CHECK(1, 3, 3)
|
SIGNALSMITH_DSP_VERSION_CHECK(1, 3, 3)
|
||||||
|
|
||||||
#include "./stfx-library.h"
|
#include "./stfx-library.h"
|
||||||
@ -25,9 +26,12 @@ namespace signalsmith { namespace basics {
|
|||||||
ParamRange dry{1}, wet{0.5};
|
ParamRange dry{1}, wet{0.5};
|
||||||
ParamRange roomMs{80};
|
ParamRange roomMs{80};
|
||||||
ParamRange rt20{1};
|
ParamRange rt20{1};
|
||||||
ParamRange early{1};
|
ParamRange early{1.5};
|
||||||
ParamRange detune{2};
|
ParamRange detune{2};
|
||||||
|
|
||||||
|
ParamRange lowCutHz{80}, highCutHz{12000};
|
||||||
|
ParamRange lowDampRate{1.5}, highDampRate{2.5};
|
||||||
|
|
||||||
ReverbSTFX(double maxRoomMs=200, double detuneDepthMs=2) : maxRoomMs(maxRoomMs), detuneDepthMs(detuneDepthMs) {}
|
ReverbSTFX(double maxRoomMs=200, double detuneDepthMs=2) : maxRoomMs(maxRoomMs), detuneDepthMs(detuneDepthMs) {}
|
||||||
|
|
||||||
template<class Storage>
|
template<class Storage>
|
||||||
@ -35,8 +39,8 @@ namespace signalsmith { namespace basics {
|
|||||||
using namespace signalsmith::units;
|
using namespace signalsmith::units;
|
||||||
|
|
||||||
storage.info("[Basics] Reverb", "An FDN reverb");
|
storage.info("[Basics] Reverb", "An FDN reverb");
|
||||||
int version = storage.version(4);
|
int version = storage.version(5);
|
||||||
if (version != 4) return;
|
if (version != 5) return;
|
||||||
|
|
||||||
storage.param("dry", dry)
|
storage.param("dry", dry)
|
||||||
.info("dry", "dry signal gain")
|
.info("dry", "dry signal gain")
|
||||||
@ -74,6 +78,25 @@ namespace signalsmith { namespace basics {
|
|||||||
.info("detune", "Detuning rate (inside feedback loop)")
|
.info("detune", "Detuning rate (inside feedback loop)")
|
||||||
.range(0, 5, 50)
|
.range(0, 5, 50)
|
||||||
.unit("", 1);
|
.unit("", 1);
|
||||||
|
|
||||||
|
storage.param("lowCutHz", lowCutHz)
|
||||||
|
.info("low cut", "Removes low frequencies")
|
||||||
|
.range(10, 80, 500)
|
||||||
|
.unit("Hz", 0);
|
||||||
|
storage.param("lowDampRate", lowDampRate)
|
||||||
|
.info("low damp", "Reduce low frequencies over time")
|
||||||
|
.range(1, 2, 10)
|
||||||
|
.unit("", 1);
|
||||||
|
|
||||||
|
storage.param("highCutHz", highCutHz)
|
||||||
|
.info("high cut", "Removes high frequencies")
|
||||||
|
.range(1000, 5000, 20000)
|
||||||
|
.unit("kHz", 1, kHzToHz, hzToKHz, 1000, 1e100)
|
||||||
|
.unit("Hz", 0);
|
||||||
|
storage.param("highDampRate", highDampRate)
|
||||||
|
.info("high damp", "Reduce high frequencies over time")
|
||||||
|
.range(1, 2, 10)
|
||||||
|
.unit("", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Config>
|
template<class Config>
|
||||||
@ -89,7 +112,7 @@ namespace signalsmith { namespace basics {
|
|||||||
delay2.configure(maxRoomSamples, 1);
|
delay2.configure(maxRoomSamples, 1);
|
||||||
delay3.configure(maxRoomSamples, 0.5);
|
delay3.configure(maxRoomSamples, 0.5);
|
||||||
delay4.configure(maxRoomSamples, 0.25);
|
delay4.configure(maxRoomSamples, 0.25);
|
||||||
delayFeedback.configure(maxRoomSamples*1.36 + detuneDepthSamples, 1);
|
delayFeedback.configure(maxRoomSamples*1.5 + detuneDepthSamples, 1);
|
||||||
delayEarly.configure(maxRoomSamples, 0.25);
|
delayEarly.configure(maxRoomSamples, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +123,11 @@ namespace signalsmith { namespace basics {
|
|||||||
delayFeedback.reset();
|
delayFeedback.reset();
|
||||||
delayEarly.reset();
|
delayEarly.reset();
|
||||||
|
|
||||||
|
for (auto &f : lowCutFilters) f.reset();
|
||||||
|
for (auto &f : highCutFilters) f.reset();
|
||||||
|
for (auto &f : lowDampFilters) f.reset();
|
||||||
|
for (auto &f : highDampFilters) f.reset();
|
||||||
|
|
||||||
detuneLfoPhase = 0;
|
detuneLfoPhase = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +145,11 @@ namespace signalsmith { namespace basics {
|
|||||||
auto &&outputLeft = io.output[0];
|
auto &&outputLeft = io.output[0];
|
||||||
auto &&outputRight = io.output[1];
|
auto &&outputRight = io.output[1];
|
||||||
|
|
||||||
|
block.setupFade([&](){
|
||||||
|
updateDelays(roomMs.to()*0.001*config.sampleRate);
|
||||||
|
});
|
||||||
|
bool fading = block.fading();
|
||||||
|
|
||||||
auto smoothedDryGain = block.smooth(dry);
|
auto smoothedDryGain = block.smooth(dry);
|
||||||
Sample scalingFactor = stereoMixer.scalingFactor2()*0.015625; // 4 Hadamard mixes
|
Sample scalingFactor = stereoMixer.scalingFactor2()*0.015625; // 4 Hadamard mixes
|
||||||
auto smoothedWetGain = block.smooth(wet.from(), wet.to());
|
auto smoothedWetGain = block.smooth(wet.from(), wet.to());
|
||||||
@ -126,17 +159,14 @@ namespace signalsmith { namespace basics {
|
|||||||
double decayGainTo = dbToGain(getDecayDb(rt20.to(), roomMs.to()));
|
double decayGainTo = dbToGain(getDecayDb(rt20.to(), roomMs.to()));
|
||||||
auto smoothedDecayGain = block.smooth(decayGainFrom, decayGainTo);
|
auto smoothedDecayGain = block.smooth(decayGainFrom, decayGainTo);
|
||||||
auto smoothedInputGain = block.smooth( // scale according to the number of expected echoes in the first 100ms
|
auto smoothedInputGain = block.smooth( // scale according to the number of expected echoes in the first 100ms
|
||||||
scalingFactor*std::sqrt((1 - decayGainFrom)/(1 - std::pow(decayGainFrom, 100/roomMs.from()))),
|
2*scalingFactor*std::sqrt((1 - decayGainFrom)/(1 - std::pow(decayGainFrom, 100/roomMs.from()))),
|
||||||
scalingFactor*std::sqrt((1 - decayGainTo)/(1 - std::pow(decayGainTo, 100/roomMs.to())))
|
2*scalingFactor*std::sqrt((1 - decayGainTo)/(1 - std::pow(decayGainTo, 100/roomMs.to())))
|
||||||
);
|
);
|
||||||
auto smoothedEarlyGain = block.smooth(early, [&](double g) {
|
auto smoothedEarlyGain = block.smooth(early, [&](double g) {
|
||||||
return g*0.35; // tuned by ear
|
return g*0.35; // tuned by ear
|
||||||
});
|
});
|
||||||
|
|
||||||
block.setupFade([&](){
|
updateFilters(config.sampleRate, decayGainTo);
|
||||||
updateDelays(roomMs.to()*0.001*config.sampleRate);
|
|
||||||
});
|
|
||||||
bool fading = block.fading();
|
|
||||||
|
|
||||||
// Detuning LFO rate
|
// Detuning LFO rate
|
||||||
double detuneCentsPerLoop = detune*std::sqrt(roomMs*0.001);
|
double detuneCentsPerLoop = detune*std::sqrt(roomMs*0.001);
|
||||||
@ -170,6 +200,9 @@ namespace signalsmith { namespace basics {
|
|||||||
|
|
||||||
Array feedback = delayFeedback.readDetuned(lfoArray, fade);
|
Array feedback = delayFeedback.readDetuned(lfoArray, fade);
|
||||||
Householder::inPlace(feedback);
|
Householder::inPlace(feedback);
|
||||||
|
for (int c = 0; c < 8; ++c) {
|
||||||
|
feedback[c] = highDampFilters[c](lowDampFilters[c](feedback[c]));
|
||||||
|
}
|
||||||
Array feedbackInput;
|
Array feedbackInput;
|
||||||
for (int c = 0; c < 8; ++c) {
|
for (int c = 0; c < 8; ++c) {
|
||||||
int c2 = (c + 3)&7;
|
int c2 = (c + 3)&7;
|
||||||
@ -190,8 +223,11 @@ namespace signalsmith { namespace basics {
|
|||||||
samples = delay2.write(samples).read();
|
samples = delay2.write(samples).read();
|
||||||
Hadamard::unscaledInPlace(samples);
|
Hadamard::unscaledInPlace(samples);
|
||||||
|
|
||||||
Array feedback = delayFeedback.readDetuned(lfoArray, lfoSin);
|
Array feedback = delayFeedback.readDetuned(lfoArray);
|
||||||
Householder::inPlace(feedback);
|
Householder::inPlace(feedback);
|
||||||
|
for (int c = 0; c < 8; ++c) {
|
||||||
|
feedback[c] = highDampFilters[c](lowDampFilters[c](feedback[c]));
|
||||||
|
}
|
||||||
Array feedbackInput;
|
Array feedbackInput;
|
||||||
for (int c = 0; c < 8; ++c) {
|
for (int c = 0; c < 8; ++c) {
|
||||||
int c2 = (c + 3)&7;
|
int c2 = (c + 3)&7;
|
||||||
@ -211,6 +247,10 @@ namespace signalsmith { namespace basics {
|
|||||||
std::array<Sample, 2> stereoOut;
|
std::array<Sample, 2> stereoOut;
|
||||||
stereoMixer.multiToStereo(samples, stereoOut);
|
stereoMixer.multiToStereo(samples, stereoOut);
|
||||||
|
|
||||||
|
for (int c = 0; c < 2; ++c) {
|
||||||
|
stereoOut[c] = highCutFilters[c](lowCutFilters[c](stereoOut[c]));
|
||||||
|
}
|
||||||
|
|
||||||
Sample dryGain = smoothedDryGain.at(i);
|
Sample dryGain = smoothedDryGain.at(i);
|
||||||
Sample wetGain = smoothedWetGain.at(i);
|
Sample wetGain = smoothedWetGain.at(i);
|
||||||
outputLeft[i] = stereoIn[0]*dryGain + stereoOut[0]*wetGain;
|
outputLeft[i] = stereoIn[0]*dryGain + stereoOut[0]*wetGain;
|
||||||
@ -226,6 +266,22 @@ namespace signalsmith { namespace basics {
|
|||||||
double detuneLfoPhase = 0;
|
double detuneLfoPhase = 0;
|
||||||
double detuneDepthSamples = 0;
|
double detuneDepthSamples = 0;
|
||||||
|
|
||||||
|
using Filter = signalsmith::filters::BiquadStatic<Sample>;
|
||||||
|
std::array<Filter, 2> lowCutFilters, highCutFilters;
|
||||||
|
std::array<Filter, 8> lowDampFilters, highDampFilters;
|
||||||
|
|
||||||
|
void updateFilters(Sample sampleRate, Sample feedbackGain) {
|
||||||
|
for (auto &f : lowCutFilters) f.highpassQ(lowCutHz/sampleRate, 0.5);
|
||||||
|
for (auto &f : highCutFilters) f.lowpassQ(highCutHz/sampleRate, 0.5);
|
||||||
|
|
||||||
|
Sample lowDampHz = lowCutHz + 100;
|
||||||
|
Sample highDampHz = highCutHz*0.5;
|
||||||
|
Sample lowDampGain = std::max(std::pow(feedbackGain, lowDampRate), 1e-3);
|
||||||
|
Sample highDampGain = std::max(std::pow(feedbackGain, highDampRate), 1e-3);
|
||||||
|
for (auto &f : lowDampFilters) f.lowShelf(lowDampHz, lowDampGain, Filter::defaultQ);
|
||||||
|
for (auto &f : highDampFilters) f.highShelf(highDampHz, highDampGain, Filter::defaultQ);
|
||||||
|
}
|
||||||
|
|
||||||
static Sample getDecayDb(Sample rt20, Sample loopMs) {
|
static Sample getDecayDb(Sample rt20, Sample loopMs) {
|
||||||
Sample dbPerSecond = -20/rt20;
|
Sample dbPerSecond = -20/rt20;
|
||||||
Sample secondsPerLoop = loopMs*Sample(0.001);
|
Sample secondsPerLoop = loopMs*Sample(0.001);
|
||||||
|
|||||||
6
units.h
6
units.h
@ -19,6 +19,12 @@ namespace signalsmith { namespace units {
|
|||||||
static double ratioToPc(double linear) {
|
static double ratioToPc(double linear) {
|
||||||
return linear*100;
|
return linear*100;
|
||||||
}
|
}
|
||||||
|
static double kHzToHz(double kHz) {
|
||||||
|
return kHz*1000;
|
||||||
|
}
|
||||||
|
static double hzToKHz(double hz) {
|
||||||
|
return hz*0.001;
|
||||||
|
}
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
#endif // include guard
|
#endif // include guard
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user