Skip to content

Commit

Permalink
Generate Analog EQ filter plot on a background thread, and update sub…
Browse files Browse the repository at this point in the history
…module (#42)

* Generate Analog EQ filter plot on a background thread, and update submodule

* Apply clang-format

* Fixing params alignment

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
jatinchowdhury18 and github-actions[bot] authored Jun 11, 2024
1 parent 9452723 commit 70b59e6
Show file tree
Hide file tree
Showing 15 changed files with 138 additions and 120 deletions.
1 change: 1 addition & 0 deletions modules/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ target_link_libraries(juce_plugin_modules
PRIVATE
juce::juce_audio_utils
juce::juce_dsp
chowdsp::chowdsp_logging
chowdsp::chowdsp_plugin_base
chowdsp::chowdsp_plugin_state
chowdsp::chowdsp_plugin_utils
Expand Down
2 changes: 1 addition & 1 deletion modules/chowdsp_utils
Submodule chowdsp_utils updated 429 files
2 changes: 1 addition & 1 deletion src/dsp/AnalogEQ/PultecEQWDF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void PultecEqWDF::setParameters (float treble_boost, float treble_boost_q, float

// see sim/pultec_treble_boost_freq_curve_fit.py
static constexpr auto A_r = gcem::sqrt (1.0e7f);
const auto g_val = chowdsp::Polynomials::estrin<3> ({ -4.96665892e-17f, 1.73050404e-12f, -2.05889893e-08f, 9.75043069e-05f }, treble_boost_freq);
const auto g_val = chowdsp::Polynomials::estrin<3> (chowdsp::Polynomial<float, 3> { { -4.96665892e-17f, 1.73050404e-12f, -2.05889893e-08f, 9.75043069e-05f } }, treble_boost_freq);
const auto treble_boost_cap_val = g_val / A_r;
const auto treble_boost_ind_val = chowdsp::Power::ipow<2> (A_r) * treble_boost_cap_val;
C_treble_boost.setCapacitanceValue (treble_boost_cap_val);
Expand Down
8 changes: 7 additions & 1 deletion src/dsp/Brickwall/BrickwallProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ void BrickwallProcessor::prepare (const juce::dsp::ProcessSpec& spec)
filter.prepare (spec);
filter.setQValue (chowdsp::CoefficientCalculators::butterworthQ<float>);
postSpectrumAnalyserTask.prepareToPlay (spec.sampleRate, (int) spec.maximumBlockSize, (int) spec.numChannels);

const auto paddedChannelSize = chowdsp::Math::round_to_next_multiple (static_cast<size_t> (spec.maximumBlockSize),
chowdsp::SIMDUtils::defaultSIMDAlignment);
const auto requiredMemoryBytes = paddedChannelSize * sizeof (float) * 3 // per-band smoothed values
+ paddedChannelSize * spec.numChannels * sizeof (float); // per-band fade buffers
arena.reset (requiredMemoryBytes + 32);
}

void BrickwallProcessor::reset()
Expand All @@ -20,7 +26,7 @@ void BrickwallProcessor::processBlock (const chowdsp::BufferView<float>& buffer)
{
filter.setFilterType (getFilterTypeIndex());
filter.setCutoffFrequency (*params.cutoff);
filter.processBlock (buffer);
filter.processBlock (buffer, arena);

if (extraState.isEditorOpen.load() && extraState.showSpectrum.get())
postSpectrumAnalyserTask.processBlockInput (buffer.toAudioBuffer());
Expand Down
2 changes: 2 additions & 0 deletions src/dsp/Brickwall/BrickwallProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ class BrickwallProcessor
chowdsp::EllipticFilter<16, chowdsp::EllipticFilterType::Highpass>>;
EQBand filter;

chowdsp::ArenaAllocator<> arena;

gui::SpectrumAnalyserTask postSpectrumAnalyserTask;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BrickwallProcessor)
Expand Down
3 changes: 3 additions & 0 deletions src/dsp/Waveshaper/SignalSmithWaveshaper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ static constexpr auto TOL = chowdsp::ScientificRatio<1, -2>::value<double>;

void SignalSmithWaveshaper::prepare (double sample_rate, int block_size, int numChannels)
{
if (sample_rate == 0.0)
return;

k_smooth.prepare (sample_rate, block_size);
k_smooth.setRampLength (0.05);
M_smooth.prepare (sample_rate, block_size);
Expand Down
4 changes: 2 additions & 2 deletions src/dsp/Waveshaper/SplineWaveshaper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ double evaluateSpline (const Spline& spline, double x)
const auto splineSetIndex = juce::truncatePositiveToUnsignedInt (scaler * x + offset);

const auto& ss = spline[splineSetIndex];
return chowdsp::Polynomials::estrin<3> ({ ss.d, ss.c, ss.b, ss.a }, x - ss.x);
return chowdsp::Polynomials::estrin<3> (chowdsp::Polynomial<double, 3> { { ss.d, ss.c, ss.b, ss.a } }, x - ss.x);
}

// ADAA reference: https://www.desmos.com/calculator/o36ytu5rro
Expand Down Expand Up @@ -135,7 +135,7 @@ double evaluateSpline (const VectorSpline& spline, double x)
}

const auto& ss = spline[splineSetIndex];
return chowdsp::Polynomials::estrin<3> ({ ss.d, ss.c, ss.b, ss.a }, x - ss.x);
return chowdsp::Polynomials::estrin<3> (chowdsp::Polynomial<double, 3> { { ss.d, ss.c, ss.b, ss.a } }, x - ss.x);
}

double evaluateSplineADAA (const std::vector<SplineADAASection>& spline, double x)
Expand Down
102 changes: 68 additions & 34 deletions src/gui/AnalogEQ/AnalogEQPlot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ namespace
{
using SpectrumDotSlider::SpectrumDotSlider;
bool isSelected = false;
AnalogEQPlot::BandID bandID = AnalogEQPlot::BandID::None;
BandID bandID = BandID::None;
juce::dsp::FixedSizeFunction<16, void()> callback;

void paint (juce::Graphics& g) override
{
SpectrumDotSlider::paint (g);
Expand All @@ -30,6 +32,12 @@ namespace
g.drawEllipse (getThumbBounds().expanded (2.0f), 1.0f);
}
}

void mouseDown (const juce::MouseEvent& event) override
{
callback();
SpectrumDotSlider::mouseDown (event);
}
};

template <typename T, typename B, typename... Args>
Expand All @@ -40,6 +48,44 @@ namespace
}
} // namespace

AnalogEQPlot::BackgroundPlotter::BackgroundPlotter (chowdsp::SpectrumPlotBase& plotBase, juce::Component& parentComponent)
: parent (parentComponent),
filterPlotter (plotBase, chowdsp::GenericFilterPlotter::Params {
.sampleRate = sampleRate,
.freqSmoothOctaves = 1.0f / 12.0f,
.fftOrder = fftOrder,
})
{
}

AnalogEQPlot::BackgroundPlotter::~BackgroundPlotter()
{
sharedTimeSliceThread->removeTimeSliceClient (this);
if (sharedTimeSliceThread->getNumClients() == 0)
sharedTimeSliceThread->stopThread (-1);
}

void AnalogEQPlot::BackgroundPlotter::start()
{
useTimeSlice();
sharedTimeSliceThread->addTimeSliceClient (this);
if (! sharedTimeSliceThread->isThreadRunning())
sharedTimeSliceThread->startThread();
}

int AnalogEQPlot::BackgroundPlotter::useTimeSlice()
{
if (chowdsp::AtomicHelpers::compareNegate (needsUpdate))
{
filterPlotter.updateFilterPlot();

juce::MessageManagerLock mml {};
parent.repaint();
}

return 30;
}

AnalogEQPlot::AnalogEQPlot (State& pluginState,
dsp::analog_eq::Params& pultecParams,
dsp::analog_eq::ExtraState& analogEqExtraState,
Expand All @@ -50,11 +96,7 @@ AnalogEQPlot::AnalogEQPlot (State& pluginState,
.maxFrequencyHz = (float) maxFrequency,
.minMagnitudeDB = -21.0f,
.maxMagnitudeDB = 21.0f }),
filterPlotter (*this, chowdsp::GenericFilterPlotter::Params {
.sampleRate = sampleRate,
.freqSmoothOctaves = 1.0f / 12.0f,
.fftOrder = fftOrder,
}),
plotter (*this, *this),
extraState (analogEqExtraState),
pultecEQ (pultecParams, extraState),
chyron (pluginState, *pluginState.params.analogEQParams, hcp),
Expand Down Expand Up @@ -82,13 +124,14 @@ AnalogEQPlot::AnalogEQPlot (State& pluginState,
};

pultecEQ.prepare ({ sampleRate, (uint32_t) blockSize, 1 });
filterPlotter.runFilterCallback = [this] (const float* input, float* output, int numSamples)
plotter.filterPlotter.runFilterCallback = [this] (const float* input, float* output, int numSamples)
{
pultecEQ.reset();
juce::FloatVectorOperations::multiply (output, input, 0.1f, numSamples);
pultecEQ.processBlock (chowdsp::BufferView<float> { output, numSamples });
juce::FloatVectorOperations::multiply (output, output, 10.0f, numSamples);
};
plotter.start();

pultecParams.doForAllParameters (
[this, &pluginState] (juce::RangedAudioParameter& param, size_t)
Expand All @@ -103,23 +146,6 @@ AnalogEQPlot::AnalogEQPlot (State& pluginState,
};
});

callbacks += {
pluginState.addParameterListener (*pultecParams.bassFreqParam, chowdsp::ParameterListenerThread::MessageThread, [this]
{ setSelectedBand (BandID::Low); }),
pluginState.addParameterListener (*pultecParams.bassBoostParam, chowdsp::ParameterListenerThread::MessageThread, [this]
{ setSelectedBand (BandID::Low); }),
pluginState.addParameterListener (*pultecParams.bassCutParam, chowdsp::ParameterListenerThread::MessageThread, [this]
{ setSelectedBand (BandID::Low); }),
pluginState.addParameterListener (*pultecParams.trebleCutFreqParam, chowdsp::ParameterListenerThread::MessageThread, [this]
{ setSelectedBand (BandID::High_Cut); }),
pluginState.addParameterListener (*pultecParams.trebleCutParam, chowdsp::ParameterListenerThread::MessageThread, [this]
{ setSelectedBand (BandID::High_Cut); }),
pluginState.addParameterListener (*pultecParams.trebleBoostFreqParam, chowdsp::ParameterListenerThread::MessageThread, [this]
{ setSelectedBand (BandID::High_Boost); }),
pluginState.addParameterListener (*pultecParams.trebleBoostParam, chowdsp::ParameterListenerThread::MessageThread, [this]
{ setSelectedBand (BandID::High_Boost); }),
};

plotPainter.painter = [this] (juce::Graphics& g)
{
drawMagnitudeLabels (g, *this, { -20.0f, -15.0f, -10.0f, -5.0f, 0.0f, 5.0f, 10.0f, 15.0f, 20.0f });
Expand All @@ -129,11 +155,19 @@ AnalogEQPlot::AnalogEQPlot (State& pluginState,
gui::drawMagnitudeLines (*this, g, { -20.0f, -15.0f, -10.0f, -5.0f, 5.0f, 10.0f, 15.0f, 20.0f }, { 0.0f }, colours::majorLinesColour, colours::minorLinesColour);

g.setColour (juce::Colours::red);
g.strokePath (filterPlotter.getPath(), juce::PathStrokeType { 1.5f });
const juce::ScopedLock pathLock { plotter.filterPlotter.pathMutex };
g.strokePath (plotter.filterPlotter.getPath(), juce::PathStrokeType { 1.5f });
};
plotPainter.setInterceptsMouseClicks (false, false);
addAndMakeVisible (plotPainter);

const auto setBandID = [this] (SelectableDotSlider& slider, BandID id)
{
slider.bandID = id;
slider.callback = [this, id]
{ setSelectedBand (id); };
};

auto& lfControl = make_unique_component<SelectableDotSlider> (lowFreqControl,
*pultecParams.bassFreqParam,
pluginState,
Expand All @@ -153,7 +187,7 @@ AnalogEQPlot::AnalogEQPlot (State& pluginState,
&hcp);
lbControl.setColour (juce::Slider::thumbColourId, juce::Colours::goldenrod);
lbControl.widthProportion = thumbSizeFactor;
lbControl.bandID = BandID::Low;
setBandID (lbControl, BandID::Low);
lbControl.getXCoordinate = [this, &bassFreqParam = *pultecParams.bassFreqParam]
{
return getXCoordinateForFrequency (bassFreqParam.get() * 0.7f);
Expand All @@ -168,7 +202,7 @@ AnalogEQPlot::AnalogEQPlot (State& pluginState,
&hcp);
lcControl.setColour (juce::Slider::thumbColourId, juce::Colours::goldenrod);
lcControl.widthProportion = thumbSizeFactor;
lcControl.bandID = BandID::Low;
setBandID (lcControl, BandID::Low);
lcControl.getXCoordinate = [this, &bassFreqParam = *pultecParams.bassFreqParam]
{
return getXCoordinateForFrequency (bassFreqParam.get() * 1.0f / 0.7f);
Expand All @@ -182,7 +216,7 @@ AnalogEQPlot::AnalogEQPlot (State& pluginState,
gui::SpectrumDotSlider::MagnitudeOriented);
highBoostGain.setColour (juce::Slider::thumbColourId, juce::Colours::teal);
highBoostGain.widthProportion = thumbSizeFactor;
highBoostGain.bandID = BandID::High_Boost;
setBandID (highBoostGain, BandID::High_Boost);
highBoostGain.getXCoordinate = [this, &trebleBoostFreqParam = *pultecParams.trebleBoostFreqParam]
{
return getXCoordinateForFrequency (trebleBoostFreqParam.get());
Expand All @@ -196,7 +230,7 @@ AnalogEQPlot::AnalogEQPlot (State& pluginState,
gui::SpectrumDotSlider::FrequencyOriented);
highBoostFreq.setColour (juce::Slider::thumbColourId, juce::Colours::teal);
highBoostFreq.widthProportion = thumbSizeFactor;
highBoostFreq.bandID = BandID::High_Boost;
setBandID (highBoostFreq, BandID::High_Boost);
highBoostFreq.getYCoordinate = [this, &trebleBoostParam = *pultecParams.trebleBoostParam]
{
return getYCoordinateForDecibels (trebleBoostParam.get());
Expand All @@ -215,7 +249,7 @@ AnalogEQPlot::AnalogEQPlot (State& pluginState,
gui::SpectrumDotSlider::MagnitudeOriented);
highCutGain.setColour (juce::Slider::thumbColourId, juce::Colours::limegreen);
highCutGain.widthProportion = thumbSizeFactor;
highCutGain.bandID = BandID::High_Cut;
setBandID (highCutGain, BandID::High_Cut);
highCutGain.getXCoordinate = [this, &trebleCutFreqParam = *pultecParams.trebleCutFreqParam]
{
return getXCoordinateForFrequency (trebleCutFreqParam.get());
Expand All @@ -229,7 +263,7 @@ AnalogEQPlot::AnalogEQPlot (State& pluginState,
gui::SpectrumDotSlider::FrequencyOriented);
highCutFreq.setColour (juce::Slider::thumbColourId, juce::Colours::limegreen);
highCutFreq.widthProportion = thumbSizeFactor;
highCutFreq.bandID = BandID::High_Cut;
setBandID (highCutFreq, BandID::High_Cut);
highCutFreq.getYCoordinate = [this, &trebleCutParam = *pultecParams.trebleCutParam]
{
return getYCoordinateForDecibels (trebleCutParam.get());
Expand All @@ -256,12 +290,12 @@ AnalogEQPlot::~AnalogEQPlot()

void AnalogEQPlot::updatePlot()
{
filterPlotter.updateFilterPlot();
repaint();
plotter.needsUpdate.store (true, std::memory_order_release);
}

void AnalogEQPlot::setSelectedBand (BandID band)
{
chyron.setSelectedBand (band);
for (juce::Slider* slider : { lowFreqControl.get(),
lowBoostControl.get(),
lowCutControl.get(),
Expand Down Expand Up @@ -299,7 +333,7 @@ void AnalogEQPlot::resized()

void AnalogEQPlot::mouseDown (const juce::MouseEvent& event)
{
chyron.setSelectedBand (EQBand::None);
setSelectedBand (BandID::None);
if (event.mods.isPopupMenu())
{
chowdsp::SharedLNFAllocator lnfAllocator;
Expand Down
30 changes: 21 additions & 9 deletions src/gui/AnalogEQ/AnalogEQPlot.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ namespace gui::analog_eq
{
class DotSlider;

enum class BandID
{
None,
Low,
High_Cut,
High_Boost,
};

class AnalogEQPlot : public chowdsp::SpectrumPlotBase
{
public:
Expand All @@ -21,21 +29,25 @@ class AnalogEQPlot : public chowdsp::SpectrumPlotBase

void resized() override;

enum class BandID
{
None,
Low,
High_Cut,
High_Boost,
};

private:
void mouseDown (const juce::MouseEvent& event) override;

void updatePlot();
void setSelectedBand (BandID bandID);

chowdsp::GenericFilterPlotter filterPlotter;
struct BackgroundPlotter : juce::TimeSliceClient
{
BackgroundPlotter (chowdsp::SpectrumPlotBase& plotBase, juce::Component& parent);
~BackgroundPlotter() override;
void start();
int useTimeSlice() override;

std::atomic_bool needsUpdate { true };
juce::Component& parent;
chowdsp::GenericFilterPlotter filterPlotter;
juce::SharedResourcePointer<chowdsp::detail::TimeSliceBackgroundTask::TimeSliceThread> sharedTimeSliceThread;
} plotter;

dsp::analog_eq::ExtraState& extraState;
dsp::analog_eq::AnalogEQProcessor pultecEQ;

Expand Down
Loading

0 comments on commit 70b59e6

Please sign in to comment.