-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add OvershootLimiter processor (#460)
- Loading branch information
1 parent
7046f87
commit 4936768
Showing
6 changed files
with
136 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_OvershootLimiter.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
#pragma once | ||
|
||
namespace chowdsp | ||
{ | ||
/** | ||
* A simple limiter designed to be used to protect against overshoot. | ||
* | ||
* A typical use-case might be after a clipper or limiter being applied | ||
* in an up-sampled process. Since the down-sampling process might introduce | ||
* overshoot not present in the up-sampled signal, this limiter can be used | ||
* to maintain the "ceiling" provided by the clipper/limiter after down-sampling. | ||
* | ||
* Note that this limiter is designed to provide something like 0.1 dB of gain | ||
* reduction. Using it as you would a normal limiter (maybe 1-5 dB of gain reduction) | ||
* probably won't work very well. | ||
*/ | ||
template <typename SampleType> | ||
class OvershootLimiter | ||
{ | ||
public: | ||
/** Constructs the limiter with a given lookahead characteristic */ | ||
explicit OvershootLimiter (int lookaheadInSamples = 32) | ||
: lookaheadSamples (lookaheadInSamples) | ||
{ | ||
} | ||
|
||
/** Prepares the limiter */ | ||
void prepare (const juce::dsp::ProcessSpec spec) | ||
{ | ||
makeupDelay.prepare (spec); | ||
makeupDelay.setDelay ((SampleType) lookaheadSamples); | ||
makeupGain.reset (lookaheadSamples); | ||
|
||
reset(); | ||
} | ||
|
||
/** Prepares the limiter state */ | ||
void reset() | ||
{ | ||
makeupDelay.reset(); | ||
makeupGain.setCurrentAndTargetValue ((SampleType) 1); | ||
lastBlockMakeupGain = (SampleType) 1; | ||
} | ||
|
||
/** Sets the limiter's "ceiling" (default value is 1) */ | ||
void setCeiling (SampleType newCeiling) | ||
{ | ||
ceiling = newCeiling; | ||
} | ||
|
||
/** Returns the latency added by the limiter due to its "lookahead" characteristic */ | ||
[[nodiscard]] int getLatencySamples() const noexcept | ||
{ | ||
return lookaheadSamples; | ||
} | ||
|
||
/** Processes a buffer of audio */ | ||
void processBlock (const BufferView<SampleType>& buffer) noexcept | ||
{ | ||
auto signalAbsMax = (SampleType) ceiling; | ||
for (auto [ch, data] : buffer_iters::channels (buffer)) | ||
{ | ||
signalAbsMax = juce::jmax (signalAbsMax, | ||
FloatVectorOperations::findAbsoluteMaximum (data.data(), (int) data.size())); | ||
} | ||
|
||
const auto thisBlockMakeupGain = ceiling / signalAbsMax; | ||
jassert (thisBlockMakeupGain <= (SampleType) 1); | ||
makeupGain.setTargetValue (juce::jmin (thisBlockMakeupGain, lastBlockMakeupGain)); | ||
lastBlockMakeupGain = thisBlockMakeupGain; | ||
|
||
BufferMath::applyGainSmoothed (buffer, makeupGain); | ||
} | ||
|
||
private: | ||
const int lookaheadSamples = 32; | ||
DelayLine<SampleType, DelayLineInterpolationTypes::None> makeupDelay { lookaheadSamples }; | ||
juce::SmoothedValue<SampleType, juce::ValueSmoothingTypes::Multiplicative> makeupGain { (SampleType) 1 }; | ||
SampleType lastBlockMakeupGain { (SampleType) 1 }; | ||
|
||
SampleType ceiling = (SampleType) 1; | ||
|
||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OvershootLimiter) | ||
}; | ||
} // namespace chowdsp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
tests/dsp_tests/chowdsp_dsp_utils_test/OvershootLimiterTest.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#include <CatchUtils.h> | ||
#include <chowdsp_dsp_utils/chowdsp_dsp_utils.h> | ||
|
||
TEST_CASE ("Overshoot Limiter Test", "[dsp][misc]") | ||
{ | ||
constexpr double fs = 48000.0; | ||
constexpr int N = 4800; | ||
|
||
chowdsp::OvershootLimiter<float> limiter { 64 }; | ||
limiter.prepare ({ fs, (uint32_t) N, 1 }); | ||
REQUIRE (limiter.getLatencySamples() == 64); | ||
|
||
auto signal = test_utils::makeSineWave (100.0f, (float) fs, N); | ||
|
||
SECTION ("Ceiling == 1") | ||
{ | ||
chowdsp::BufferMath::applyGain (signal, 1.1f); | ||
|
||
limiter.setCeiling (1.0f); | ||
limiter.processBlock (signal); | ||
REQUIRE (chowdsp::BufferMath::getMagnitude (signal) <= 1.0f); | ||
} | ||
|
||
SECTION ("Ceiling == 0.5") | ||
{ | ||
limiter.setCeiling (0.5f); | ||
limiter.processBlock (signal); | ||
REQUIRE (chowdsp::BufferMath::getMagnitude (signal) <= 0.5f); | ||
} | ||
} |