Skip to content

Commit

Permalink
Waveshaper improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
jatinchowdhury18 committed Oct 12, 2023
1 parent 94a7512 commit 2f42b5f
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 42 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.

## [UNRELEASED]
- Added gain and frequency labels to spectrum plots.
- Added gain control text slider for Waveshaper.
- Fixed overshoots when using "clipping" waveshapers.
- Fixed incorrect EQ filter type for First-Order Lowpass Filter.

## [1.0.0] - 2023-05-24
Expand Down
2 changes: 1 addition & 1 deletion modules/chowdsp_utils
Submodule chowdsp_utils updated 207 files
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ target_sources(ChowMultiTool PRIVATE
gui/Waveshaper/WaveshaperDrawer.cpp
gui/Waveshaper/WaveshaperMathView.cpp
gui/Waveshaper/WaveshaperPointsView.cpp
gui/Waveshaper/WaveshaperChyron.cpp
gui/Waveshaper/BottomBar.cpp
gui/Waveshaper/FoldFuzzControls.cpp

Expand Down
73 changes: 36 additions & 37 deletions src/dsp/Waveshaper/WaveshaperProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ void WaveshaperProcessor::prepare (const juce::dsp::ProcessSpec& spec)
freeDrawShaper.prepare (spec);
mathShaper.prepare (spec);
pointsShaper.prepare (spec);
clipGuard.prepare (spec);

static constexpr auto maxOSRatio = static_cast<int> (magic_enum::enum_values<OversamplingRatio>().back());
doubleBuffer.setMaxSize ((int) spec.numChannels, (int) spec.maximumBlockSize * maxOSRatio);
Expand All @@ -48,15 +49,12 @@ void WaveshaperProcessor::oversamplingRateChanged()
const juce::SpinLock::ScopedLockType lock { processingMutex };

const auto osRatio = static_cast<int> (params.oversampleParam->get());
if (osRatio > 1)
{
upsampler.prepare (processSpec, osRatio);
upsampler.prepare (processSpec, osRatio);

auto osProcessSpec = processSpec;
osProcessSpec.sampleRate *= (double) osRatio;
osProcessSpec.maximumBlockSize *= (uint32_t) osRatio;
downsampler.prepare (osProcessSpec, osRatio);
}
auto osProcessSpec = processSpec;
osProcessSpec.sampleRate *= (double) osRatio;
osProcessSpec.maximumBlockSize *= (uint32_t) osRatio;
downsampler.prepare (osProcessSpec, osRatio);

ssWaveshaper.prepare (processSpec.sampleRate * osRatio,
(int) processSpec.maximumBlockSize * osRatio,
Expand All @@ -72,66 +70,67 @@ void WaveshaperProcessor::processBlock (const chowdsp::BufferView<float>& buffer
gain.setGainDecibels (params.gainParam->getCurrentValue());
gain.process (buffer);

const auto osRatio = static_cast<int> (params.oversampleParam->get());
std::optional<chowdsp::BufferView<float>> osBufferView;
if (osRatio > 1)
{
osBufferView.emplace (upsampler.process (buffer), 0, -1);
}
else
{
osBufferView.emplace (buffer, 0, -1);
}
doubleBuffer.setCurrentSize (osBufferView->getNumChannels(), osBufferView->getNumSamples());
chowdsp::BufferMath::copyBufferData (*osBufferView, doubleBuffer);
const auto osBufferView = upsampler.process (buffer);

const auto clipLevel = juce::Decibels::decibelsToGain (18.0f);
for (auto [ch, data] : chowdsp::buffer_iters::channels (osBufferView))
juce::FloatVectorOperations::clip (data.data(), data.data(), -clipLevel, clipLevel, data.size());

doubleBuffer.setCurrentSize (osBufferView.getNumChannels(), osBufferView.getNumSamples());
chowdsp::BufferMath::copyBufferData (osBufferView, doubleBuffer);

if (params.shapeParam->get() == Shapes::Hard_Clip)
const auto shapeParam = params.shapeParam->get();
if (shapeParam == Shapes::Hard_Clip)
adaaHardClipper.processBlock (doubleBuffer);
else if (params.shapeParam->get() == Shapes::Tanh_Clip)
else if (shapeParam == Shapes::Tanh_Clip)
adaaTanhClipper.processBlock (doubleBuffer);
else if (params.shapeParam->get() == Shapes::Cubic_Clip)
else if (shapeParam == Shapes::Cubic_Clip)
adaaCubicClipper.processBlock (doubleBuffer);
else if (params.shapeParam->get() == Shapes::Nonic_Clip)
else if (shapeParam == Shapes::Nonic_Clip)
adaa9thOrderClipper.processBlock (doubleBuffer);
else if (params.shapeParam->get() == Shapes::Full_Wave_Rectify)
else if (shapeParam == Shapes::Full_Wave_Rectify)
fullWaveRectifier.processBlock (doubleBuffer);
else if (params.shapeParam->get() == Shapes::West_Coast)
else if (shapeParam == Shapes::West_Coast)
{
westCoastFolder.processBlock (doubleBuffer);
chowdsp::BufferMath::applyGain (doubleBuffer, juce::Decibels::decibelsToGain (-10.0));
}
else if (params.shapeParam->get() == Shapes::Wave_Multiply)
else if (shapeParam == Shapes::Wave_Multiply)
{
waveMultiplyFolder.processBlock (doubleBuffer);
chowdsp::BufferMath::applyGain (doubleBuffer, juce::Decibels::decibelsToGain (16.0));
}
else if (params.shapeParam->get() == Shapes::Fold_Fuzz)
else if (shapeParam == Shapes::Fold_Fuzz)
{
chowdsp::copyToSIMDBuffer (doubleBuffer, doubleSIMDBuffer);
ssWaveshaper.processBlock (doubleSIMDBuffer,
params.kParam->getCurrentValue(),
MRange.convertFrom0to1 (params.MParam->getCurrentValue()));
chowdsp::copyFromSIMDBuffer (doubleSIMDBuffer, doubleBuffer);
}
else if (params.shapeParam->get() == Shapes::Free_Draw)
else if (shapeParam == Shapes::Free_Draw)
{
freeDrawShaper.processBlock (doubleBuffer);
}
else if (params.shapeParam->get() == Shapes::Math)
else if (shapeParam == Shapes::Math)
{
mathShaper.processBlock (doubleBuffer);
}
else if (params.shapeParam->get() == Shapes::Spline)
else if (shapeParam == Shapes::Spline)
{
pointsShaper.processBlock (doubleBuffer);
}

chowdsp::BufferMath::copyBufferData (doubleBuffer, *osBufferView);
chowdsp::BufferMath::copyBufferData (doubleBuffer, osBufferView);

if (osRatio > 1)
{
const auto dsBuffer = downsampler.process (*osBufferView);
chowdsp::BufferMath::copyBufferData (dsBuffer, buffer);
}
downsampler.process (osBufferView, buffer);

clipGuard.setCeiling ((shapeParam == Shapes::Hard_Clip
|| shapeParam == Shapes::Tanh_Clip
|| shapeParam == Shapes::Cubic_Clip
|| shapeParam == Shapes::Nonic_Clip)
? 1.0f
: 100.0f);
clipGuard.processBlock (buffer);
}
} // namespace dsp::waveshaper
6 changes: 4 additions & 2 deletions src/dsp/Waveshaper/WaveshaperProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ struct Params : chowdsp::ParamHolder
chowdsp::EnumChoiceParameter<OversamplingRatio>::Ptr oversampleParam {
juce::ParameterID { "waveshaper_oversample", ParameterVersionHints::version1_0_0 },
"Waveshaper Oversampling",
OversamplingRatio::TwoX
OversamplingRatio::FourX
};
};

Expand Down Expand Up @@ -117,7 +117,7 @@ class WaveshaperProcessor

using AAFilter = chowdsp::EllipticFilter<8>;
chowdsp::Upsampler<float, AAFilter> upsampler;
chowdsp::Downsampler<float, AAFilter> downsampler;
chowdsp::Downsampler<float, AAFilter, false> downsampler;

chowdsp::Buffer<double> doubleBuffer;
chowdsp::Buffer<xsimd::batch<double>> doubleSIMDBuffer;
Expand All @@ -135,6 +135,8 @@ class WaveshaperProcessor
spline::SplineWaveshaper<spline::SplinePoints, spline::SplineADAA> mathShaper;
spline::SplineWaveshaper<spline::VectorSplinePoints, spline::VectorSplineADAA> pointsShaper;

chowdsp::OvershootLimiter<float> clipGuard { 64 };

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaveshaperProcessor)
};
} // namespace dsp::waveshaper
32 changes: 32 additions & 0 deletions src/gui/Waveshaper/WaveshaperChyron.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "WaveshaperChyron.h"
#include "gui/Shared/Colours.h"

namespace gui::waveshaper
{
WaveshaperChyron::WaveshaperChyron (chowdsp::PluginState& pluginState,
dsp::waveshaper::Params& params,
const chowdsp::HostContextProvider& hcp)
: state (pluginState),
gainSlider (state, params.gainParam.get(), &hcp)
{
gainSlider.setName ("Gain");
addAndMakeVisible (gainSlider);
}

void WaveshaperChyron::resized()
{
auto bounds = getLocalBounds();
gainSlider.setBounds (bounds.reduced (proportionOfHeight (0.2f)));
}

void WaveshaperChyron::paint (juce::Graphics& g)
{
const auto bounds = getLocalBounds();

g.setColour (juce::Colours::black.withAlpha (0.75f));
g.fillRoundedRectangle (bounds.toFloat(), 2.5f);

g.setColour (colours::linesColour);
g.drawRoundedRectangle (bounds.toFloat(), 2.5f, 1.0f);
}
} // namespace gui::waveshaper
29 changes: 29 additions & 0 deletions src/gui/Waveshaper/WaveshaperChyron.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include "dsp/Waveshaper/WaveshaperProcessor.h"
#include "gui/Shared/Fonts.h"
#include "gui/Shared/TextSlider.h"
#include "state/PluginState.h"

namespace gui::waveshaper
{
class WaveshaperChyron : public juce::Component
{
public:
WaveshaperChyron (chowdsp::PluginState& pluginState,
dsp::waveshaper::Params& params,
const chowdsp::HostContextProvider& hcp);

void resized() override;
void paint (juce::Graphics& g) override;

private:
chowdsp::PluginState& state;

TextSlider gainSlider;

SharedFonts fonts;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaveshaperChyron)
};
} // namespace gui::waveshaper
16 changes: 15 additions & 1 deletion src/gui/Waveshaper/WaveshaperPlot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ WaveshaperPlot::WaveshaperPlot (State& pluginState, dsp::waveshaper::Params& wsP
drawArea (*pluginState.nonParams.waveshaperExtraState, *pluginState.undoManager),
mathArea (*pluginState.nonParams.waveshaperExtraState, *pluginState.undoManager),
pointsArea (*pluginState.nonParams.waveshaperExtraState, *pluginState.undoManager),
chyron (pluginState, wsParams, hcp),
shapeParam (*wsParams.shapeParam),
gainAttach (*wsParams.gainParam, pluginState, *this),
hostContextProvider (hcp)
Expand Down Expand Up @@ -177,13 +178,16 @@ WaveshaperPlot::WaveshaperPlot (State& pluginState, dsp::waveshaper::Params& wsP
addChildComponent (drawArea);
addChildComponent (mathArea);
addChildComponent (pointsArea);

addAndMakeVisible (chyron);
}

void WaveshaperPlot::toggleDrawMode (bool isDrawMode)
{
drawMode = isDrawMode;

drawArea.setVisible (drawMode);
chyron.setVisible (! drawMode);

if (drawMode)
{
Expand All @@ -202,6 +206,7 @@ void WaveshaperPlot::toggleMathMode (bool isMathMode)
mathMode = isMathMode;

mathArea.setVisible (mathMode);
chyron.setVisible (! mathMode);

if (mathMode)
{
Expand All @@ -220,6 +225,7 @@ void WaveshaperPlot::togglePointsMode (bool isPointsMode)
pointsMode = isPointsMode;

pointsArea.setVisible (pointsMode);
chyron.setVisible (! pointsMode);

if (mathMode)
{
Expand Down Expand Up @@ -291,14 +297,22 @@ void WaveshaperPlot::resized()
drawArea.setBounds (getLocalBounds());
mathArea.setBounds (getLocalBounds());
pointsArea.setBounds (getLocalBounds());

const auto pad = proportionOfWidth (0.005f);
const auto chyronWidth = proportionOfWidth (0.15f);
const auto chyronHeight = proportionOfWidth (0.05f);
chyron.setBounds (getWidth() - pad - chyronWidth,
getHeight() - pad - chyronHeight,
chyronWidth,
chyronHeight);
}

void WaveshaperPlot::mouseDown (const juce::MouseEvent& e)
{
if (e.mods.isPopupMenu())
{
chowdsp::SharedLNFAllocator lnfAllocator;
hostContextProvider.showParameterContextPopupMenu (gainAttach.getParameter(),
hostContextProvider.showParameterContextPopupMenu (*gainAttach.getParameter(),
{},
lnfAllocator->getLookAndFeel<lnf::MenuLNF>());
return;
Expand Down
3 changes: 3 additions & 0 deletions src/gui/Waveshaper/WaveshaperPlot.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "WaveshaperPointsView.h"
#include "dsp/Waveshaper/WaveshaperProcessor.h"
#include "state/PluginState.h"
#include "WaveshaperChyron.h"

namespace gui::waveshaper
{
Expand All @@ -27,6 +28,8 @@ class WaveshaperPlot : public juce::Slider
WaveshaperMathView mathArea;
WaveshaperPointsView pointsArea;

WaveshaperChyron chyron;

chowdsp::ScopedCallbackList callbacks;
const chowdsp::EnumChoiceParameter<dsp::waveshaper::Shapes>& shapeParam;
chowdsp::SliderAttachment gainAttach;
Expand Down

0 comments on commit 2f42b5f

Please sign in to comment.