Skip to content

Commit

Permalink
initial spectrum analyser implementation (#29)
Browse files Browse the repository at this point in the history
* initial spectrum analyser commit

* Apply clang-format

* windowing, scaling and smoothing modified for magnitude response

* Apply clang-format

* accidentally removed the changes to PluginEditor.cpp via merge, restoring changes

* Apply clang-format

* Stuff

* Apply clang-format

* Scaling Adjusted to Cover a Wider Dynamic Range

* Apply clang-format

* Jatin tweaks

* Apply clang-format

* Spectrum Analyser Task Refactor and Checks for UI Instance

* Apply clang-format

* pre/post eq tasks configured

* Apply clang-format

* Tweaks for spectrum analyzer stuff and add right-click options to disable analyzers

* Apply clang-format

* comments and unused methods removed

* PR Feedback Addressed

* Apply clang-format

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: jatin <[email protected]>
  • Loading branch information
3 people authored Nov 29, 2023
1 parent 20402e8 commit 168b876
Show file tree
Hide file tree
Showing 24 changed files with 440 additions and 27 deletions.
2 changes: 1 addition & 1 deletion modules/chowdsp_utils
Submodule chowdsp_utils updated 38 files
+3 −3 .github/workflows/run-tests.yml
+25 −26 examples/ForwardingTestPlugin/InternalPlugins/BandSplitPlugin.cpp
+17 −10 examples/ForwardingTestPlugin/InternalPlugins/BandSplitPlugin.h
+18 −0 modules/common/chowdsp_core/Types/chowdsp_TypeTraits.h
+17 −11 modules/common/chowdsp_data_structures/Structures/chowdsp_ArenaAllocator.h
+1 −1 modules/common/chowdsp_data_structures/Structures/chowdsp_OptionalPointer.h
+50 −5 modules/common/chowdsp_data_structures/Structures/chowdsp_StringLiteral.h
+1 −1 modules/common/chowdsp_data_structures/chowdsp_data_structures.h
+1 −11 modules/common/chowdsp_serialization/Serialization/chowdsp_BaseSerializer.h
+7 −5 modules/dsp/chowdsp_buffers/Buffers/chowdsp_BufferView.h
+41 −22 modules/dsp/chowdsp_buffers/Buffers/chowdsp_SIMDBufferHelpers.h
+27 −4 modules/dsp/chowdsp_dsp_data_structures/Other/chowdsp_SmoothedBufferValue.cpp
+16 −6 modules/dsp/chowdsp_dsp_data_structures/Other/chowdsp_SmoothedBufferValue.h
+1 −1 modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_BypassProcessor.cpp
+1 −1 modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_BypassProcessor.h
+6 −0 modules/dsp/chowdsp_eq/EQ/chowdsp_LinearPhaseEQ.cpp
+2 −1 modules/dsp/chowdsp_eq/EQ/chowdsp_LinearPhaseEQ.h
+4 −1 modules/dsp/chowdsp_eq/EQ/chowdsp_StandardEQParameters.cpp
+1 −1 modules/dsp/chowdsp_eq/EQ/chowdsp_StandardEQParameters.h
+166 −0 modules/dsp/chowdsp_filters/Other/chowdsp_CrossoverFilter.h
+12 −4 modules/dsp/chowdsp_filters/Other/chowdsp_FIRFilter.cpp
+4 −4 modules/dsp/chowdsp_filters/Other/chowdsp_FIRFilter.h
+4 −43 modules/dsp/chowdsp_filters/Other/chowdsp_ThreeWayCrossoverFilter.h
+1 −0 modules/dsp/chowdsp_filters/chowdsp_filters.h
+35 −0 modules/gui/chowdsp_gui/PluginComponents/chowdsp_ParametersView.cpp
+4 −1 modules/gui/chowdsp_gui/PluginComponents/chowdsp_ParametersView.h
+4 −4 modules/plugin/chowdsp_parameters/ParamUtils/chowdsp_RhythmParameter.cpp
+3 −1 modules/plugin/chowdsp_parameters/ParamUtils/chowdsp_RhythmParameter.h
+3 −0 modules/plugin/chowdsp_plugin_state/Backend/chowdsp_ParamHolder.h
+13 −12 modules/plugin/chowdsp_plugin_state/Backend/chowdsp_StateValue.h
+4 −5 tests/common_tests/chowdsp_data_structures_test/ArenaAllocatorTest.cpp
+1 −1 tests/common_tests/chowdsp_data_structures_test/CMakeLists.txt
+4 −4 tests/common_tests/chowdsp_data_structures_test/IteratorsTest.cpp
+12 −0 tests/common_tests/chowdsp_data_structures_test/StringLiteralTest.cpp
+1 −1 tests/dsp_tests/chowdsp_dsp_juce_test/DiffuserTest.cpp
+1 −0 tests/dsp_tests/chowdsp_filters_test/CMakeLists.txt
+94 −0 tests/dsp_tests/chowdsp_filters_test/CrossoverFilterTest.cpp
+6 −2 tests/plugin_tests/chowdsp_plugin_state_test/StateSerializationTest.cpp
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ target_sources(ChowMultiTool PRIVATE
gui/Shared/TextSlider.cpp
gui/Shared/LookAndFeels.cpp
gui/Shared/VerticalSlider.cpp
gui/Shared/SpectrumAnalyser.cpp
gui/Shared/SpectrumAnalyserTask.cpp
)

target_precompile_headers(ChowMultiTool PRIVATE pch.h)
target_precompile_headers(ChowMultiTool PRIVATE pch.h)
1 change: 1 addition & 0 deletions src/ChowMultiTool.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class ChowMultiTool : public chowdsp::PluginBase<State>
void processAudioBlock (juce::AudioBuffer<float>&) override {}
bool isBusesLayoutSupported (const BusesLayout& layout) const override;
bool acceptsMidi() const override { return true; }
dsp::MultiToolProcessor& getProcessor() { return processor; }

juce::AudioProcessorEditor* createEditor() override;

Expand Down
15 changes: 15 additions & 0 deletions src/dsp/EQ/EQProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

namespace dsp::eq
{
EQProcessor::EQProcessor (const EQToolParams& eqParams, const ExtraState& es)
: params (eqParams),
extraState (es)
{
}

void EQProcessor::prepare (const juce::dsp::ProcessSpec& spec)
{
linPhaseEQ.updatePrototypeEQParameters = [] (auto& pEQ, auto& eqParams)
Expand All @@ -14,10 +20,16 @@ void EQProcessor::prepare (const juce::dsp::ProcessSpec& spec)
EQToolParams::EQParams::setEQParameters (eq, eqParams);
eq.prepare (spec);
linPhaseEQ.prepare (spec, getEQParams());

preSpectrumAnalyserTask.prepareToPlay (spec.sampleRate, (int) spec.maximumBlockSize, (int) spec.numChannels);
postSpectrumAnalyserTask.prepareToPlay (spec.sampleRate, (int) spec.maximumBlockSize, (int) spec.numChannels);
}

void EQProcessor::processBlock (const chowdsp::BufferView<float>& buffer)
{
if (extraState.isEditorOpen.load() && extraState.showPreSpectrum.get())
preSpectrumAnalyserTask.processBlockInput (buffer.toAudioBuffer());

const auto&& eqParams = getEQParams();
EQToolParams::EQParams::setEQParameters (eq, eqParams);
linPhaseEQ.setParameters (eqParams);
Expand All @@ -37,6 +49,9 @@ void EQProcessor::processBlock (const chowdsp::BufferView<float>& buffer)

chowdsp::BufferMath::copyBufferData (doubleBuffer, buffer);
}

if (extraState.isEditorOpen.load() && extraState.showPostSpectrum.get())
postSpectrumAnalyserTask.processBlockInput (buffer.toAudioBuffer());
}

int EQProcessor::getLatencySamples() const
Expand Down
14 changes: 13 additions & 1 deletion src/dsp/EQ/EQProcessor.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "gui/Shared/SpectrumAnalyserTask.h"
#include <pch.h>

namespace dsp::eq
Expand Down Expand Up @@ -105,18 +106,27 @@ struct EQToolParams : chowdsp::ParamHolder
false };
};

struct ExtraState
{
std::atomic<bool> isEditorOpen { false };
chowdsp::StateValue<std::atomic_bool, bool> showPreSpectrum { "eq_show_pre_spectrum", true };
chowdsp::StateValue<std::atomic_bool, bool> showPostSpectrum { "eq_show_post_spectrum", true };
};

class EQProcessor
{
public:
explicit EQProcessor (const EQToolParams& eqParams) : params (eqParams) {}
EQProcessor (const EQToolParams& eqParams, const ExtraState& extraState);

void prepare (const juce::dsp::ProcessSpec& spec);
void processBlock (const chowdsp::BufferView<float>& buffer);

int getLatencySamples() const;
std::pair<gui::SpectrumAnalyserTask&, gui::SpectrumAnalyserTask&> getSpectrumAnalyserTasks() { return { preSpectrumAnalyserTask, postSpectrumAnalyserTask }; }

private:
const EQToolParams& params;
const ExtraState& extraState;

auto getEQParams()
{
Expand Down Expand Up @@ -148,6 +158,8 @@ class EQProcessor

using LinearPhaseProtoEQ = chowdsp::EQ::LinearPhasePrototypeEQ<double, EQToolParams::EQParams::Params, EQToolParams::EQParams::EQNumBands, EQBand<double>>;
chowdsp::EQ::LinearPhaseEQ<LinearPhaseProtoEQ> linPhaseEQ;
gui::SpectrumAnalyserTask preSpectrumAnalyserTask;
gui::SpectrumAnalyserTask postSpectrumAnalyserTask;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EQProcessor)
};
Expand Down
5 changes: 2 additions & 3 deletions src/dsp/MultiToolProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ namespace detail
{
const auto& params = pluginState.params;
return {
tool_maker<eq::EQProcessor> ([&params]
{ return eq::EQProcessor { *params.eqParams }; }),
tool_maker<eq::EQProcessor> ([&params, &pluginState]
{ return eq::EQProcessor { *params.eqParams, *pluginState.nonParams.eqExtraState }; }),
tool_maker<waveshaper::WaveshaperProcessor> ([&pluginState]
{ return waveshaper::WaveshaperProcessor {
pluginState,
Expand Down Expand Up @@ -112,7 +112,6 @@ void MultiToolProcessor::processBlock (juce::AudioBuffer<float>& buffer, const j
buffer.clear (ch, 0, buffer.getNumSamples());
};


const auto isOn = ! params.bypassParam->get();
if (! bypass.processBlockIn (mainBusBuffer, isOn))
{
Expand Down
1 change: 1 addition & 0 deletions src/dsp/MultiToolProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class MultiToolProcessor

void prepare (const juce::dsp::ProcessSpec& spec);
void processBlock (juce::AudioBuffer<float>& buffer, const juce::MidiBuffer& midiBuffer);
ToolTypes::Types& getTools() { return tools; }

private:
void recalculateLatency();
Expand Down
1 change: 1 addition & 0 deletions src/dsp/SVF/SVFProcessor.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "gui/Shared/SpectrumAnalyserTask.h"
#include <pch.h>

namespace dsp::svf
Expand Down
6 changes: 3 additions & 3 deletions src/gui/Brickwall/BrickwallChyron.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ class BrickwallChyron : public juce::Component
{
public:
BrickwallChyron (chowdsp::PluginState& pluginState,
dsp::brickwall::Params& params,
const chowdsp::HostContextProvider& hcp);
dsp::brickwall::Params& params,
const chowdsp::HostContextProvider& hcp);

void resized() override;
void paint (juce::Graphics& g) override;
Expand All @@ -25,4 +25,4 @@ class BrickwallChyron : public juce::Component

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BrickwallChyron)
};
}
} // namespace gui::brickwall
2 changes: 1 addition & 1 deletion src/gui/Brickwall/BrickwallPlot.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once

#include "BrickwallChyron.h"
#include "dsp/Brickwall/BrickwallProcessor.h"
#include "state/PluginState.h"
#include "BrickwallChyron.h"

namespace gui::brickwall
{
Expand Down
27 changes: 17 additions & 10 deletions src/gui/EQ/EQChyron.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,26 @@ EQChyron::EQChyron (chowdsp::PluginState& pluginState,

void EQChyron::setSelectedBand (int newSelectedBand)
{
setVisible (newSelectedBand >= 0);
if (! selectedBand.has_value() && newSelectedBand < 0)
return; // no band selected -> no band selected

if (newSelectedBand < 0)
return;
if (selectedBand.has_value() && newSelectedBand == *selectedBand)
{
const auto& activeParams = eqParams.eqParams[(size_t) *selectedBand];
const auto newFilterType = helpers::getFilterType (activeParams.typeParam->getIndex());

auto& activeParams = eqParams.eqParams[(size_t) selectedBand];
const auto newFilterType = helpers::getFilterType (activeParams.typeParam->getIndex());
if (filterType == newFilterType)
return;
}

if (selectedBand == newSelectedBand && filterType == newFilterType)
return;
if (newSelectedBand >= 0)
selectedBand.emplace (newSelectedBand);
else
selectedBand.reset();

selectedBand = newSelectedBand;
updateValues();

setVisible (selectedBand.has_value());
}

void EQChyron::updateValues()
Expand All @@ -40,13 +47,13 @@ void EQChyron::updateValues()
gainSlider.reset();
};

if (selectedBand < 0)
if (! selectedBand.has_value())
{
reset();
return;
}

auto& activeParams = eqParams.eqParams[(size_t) selectedBand];
auto& activeParams = eqParams.eqParams[(size_t) *selectedBand];
if (! activeParams.onOffParam->get())
{
reset();
Expand Down
2 changes: 1 addition & 1 deletion src/gui/EQ/EQChyron.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class EQChyron : public juce::Component
private:
void updateValues();

int selectedBand = -1;
std::optional<int> selectedBand { std::nullopt };
chowdsp::EQ::EQPlotFilterType filterType = chowdsp::EQ::EQPlotFilterType::LPF1;

chowdsp::PluginState& state;
Expand Down
67 changes: 65 additions & 2 deletions src/gui/EQ/EQEditor.cpp
Original file line number Diff line number Diff line change
@@ -1,23 +1,48 @@
#include "EQEditor.h"
#include "BottomBar.h"
#include "gui/Shared/Colours.h"
#include "gui/Shared/LookAndFeels.h"

namespace gui::eq
{
EQEditor::EQEditor (State& pluginState, dsp::eq::EQToolParams& eqParams, const chowdsp::HostContextProvider& hcp)
: plot (pluginState, eqParams.eqParams, hcp),
EQEditor::EQEditor (State& pluginState,
dsp::eq::EQToolParams& eqParams,
dsp::eq::ExtraState& eqExtraState,
const chowdsp::HostContextProvider& hcp,
std::pair<SpectrumAnalyserTask&, SpectrumAnalyserTask&> spectrumAnalyserTasks)
: params (eqParams),
extraState (eqExtraState),
plot (pluginState, eqParams.eqParams, hcp),
paramsView (pluginState, eqParams),
linearPhaseButton ("Vector/arrow-right-arrow-left-solid.svg", colours::thumbColours[0], colours::linesColour),
linearPhaseAttach (eqParams.linearPhaseMode, pluginState, linearPhaseButton),
spectrumAnalyser (plot, spectrumAnalyserTasks),
drawButton ("Vector/pencil-solid.svg", colours::linesColour, colours::linesColour),
drawCheckButton ("Vector/square-check-regular.svg", colours::linesColour, colours::linesColour),
drawXButton ("Vector/rectangle-xmark-regular.svg", colours::linesColour, colours::linesColour)
{
addMouseListener (this, true);
extraState.isEditorOpen.store (true);
spectrumAnalyser.setShowPreEQ (extraState.showPreSpectrum.get());
spectrumAnalyser.setShowPostEQ (extraState.showPostSpectrum.get());
callbacks += {
extraState.showPreSpectrum.changeBroadcaster.connect ([this]
{
spectrumAnalyser.setShowPreEQ(extraState.showPreSpectrum.get());
spectrumAnalyser.repaint(); }),
extraState.showPostSpectrum.changeBroadcaster.connect ([this]
{
spectrumAnalyser.setShowPostEQ(extraState.showPostSpectrum.get());
spectrumAnalyser.repaint(); }),
};

bottomBar = std::make_unique<BottomBar> (pluginState, eqParams);

addAndMakeVisible (plot);
addAndMakeVisible (bottomBar.get());
addAndMakeVisible (linearPhaseButton);
addAndMakeVisible (spectrumAnalyser);
spectrumAnalyser.toBack();

linearPhaseButton.setTooltip ("Linear Phase");

Expand Down Expand Up @@ -50,6 +75,43 @@ EQEditor::EQEditor (State& pluginState, dsp::eq::EQToolParams& eqParams, const c
};
}

EQEditor::~EQEditor()
{
removeMouseListener (this);
extraState.isEditorOpen.store (false);
}

void EQEditor::mouseDown (const juce::MouseEvent& event)
{
if (event.mods.isPopupMenu())
{
chowdsp::SharedLNFAllocator lnfAllocator;
juce::PopupMenu menu;

juce::PopupMenu::Item preSpectrumItem;
preSpectrumItem.itemID = 100;
preSpectrumItem.text = extraState.showPreSpectrum.get() ? "Disable Pre-EQ Visualizer" : "Enable Pre-EQ Visualizer";
preSpectrumItem.action = [this]
{
extraState.showPreSpectrum.set (! extraState.showPreSpectrum.get());
};
menu.addItem (preSpectrumItem);

juce::PopupMenu::Item postSpectrumItem;
postSpectrumItem.itemID = 101;
postSpectrumItem.text = extraState.showPostSpectrum.get() ? "Disable Post-EQ Visualizer" : "Enable Post-EQ Visualizer";
postSpectrumItem.action = [this]
{
extraState.showPostSpectrum.set (! extraState.showPostSpectrum.get());
};
menu.addItem (postSpectrumItem);

menu.setLookAndFeel (lnfAllocator->getLookAndFeel<lnf::MenuLNF>());
menu.showMenuAsync (juce::PopupMenu::Options {}
.withParentComponent (getParentComponent()));
}
}

void EQEditor::paint (juce::Graphics& g)
{
g.setGradientFill (juce::ColourGradient { colours::backgroundLight,
Expand All @@ -74,5 +136,6 @@ void EQEditor::resized()
drawButton.setBounds (linearPhaseButton.getBoundsInParent().translated (-pad - buttonDim, 0));
drawCheckButton.setBounds (drawButton.getBoundsInParent());
drawXButton.setBounds (linearPhaseButton.getBoundsInParent());
spectrumAnalyser.setBounds (getLocalBounds());
}
} // namespace gui::eq
15 changes: 14 additions & 1 deletion src/gui/EQ/EQEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "EQPlot.h"
#include "gui/Shared/IconButton.h"
#include "gui/Shared/SpectrumAnalyser.h"

// @TODO:
// - Figure out type-in values for chyron
Expand All @@ -12,18 +13,30 @@ namespace gui::eq
class EQEditor : public juce::Component
{
public:
EQEditor (State& pluginState, dsp::eq::EQToolParams& eqParams, const chowdsp::HostContextProvider& hcp);
EQEditor (State& pluginState,
dsp::eq::EQToolParams& eqParams,
dsp::eq::ExtraState& extraState,
const chowdsp::HostContextProvider& hcp,
std::pair<SpectrumAnalyserTask&, SpectrumAnalyserTask&> spectrumAnalyserTasks);
~EQEditor() override;

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

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

private:
dsp::eq::EQToolParams& params;
dsp::eq::ExtraState& extraState;
chowdsp::ScopedCallbackList callbacks;

EQPlot plot;
std::unique_ptr<juce::Component> bottomBar;
chowdsp::ParametersView paramsView;

IconButton linearPhaseButton;
chowdsp::ButtonAttachment linearPhaseAttach;
SpectrumAnalyser spectrumAnalyser;

IconButton drawButton;
IconButton drawCheckButton;
Expand Down
6 changes: 5 additions & 1 deletion src/gui/PluginEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,11 @@ void PluginEditor::refreshEditor()
auto& pluginState = plugin.getState();

if constexpr (std::is_same_v<ToolType, dsp::eq::EQProcessor>)
editorComponent = std::make_unique<eq::EQEditor> (pluginState, *pluginState.params.eqParams, hostContextProvider);
editorComponent = std::make_unique<eq::EQEditor> (pluginState,
*pluginState.params.eqParams,
*pluginState.nonParams.eqExtraState,
hostContextProvider,
std::get<dsp::eq::EQProcessor> (plugin.getProcessor().getTools()).getSpectrumAnalyserTasks());
else if constexpr (std::is_same_v<ToolType, dsp::waveshaper::WaveshaperProcessor>)
editorComponent = std::make_unique<waveshaper::WaveshaperEditor> (pluginState, *pluginState.params.waveshaperParams, hostContextProvider);
else if constexpr (std::is_same_v<ToolType, dsp::signal_gen::SignalGeneratorProcessor>)
Expand Down
2 changes: 2 additions & 0 deletions src/gui/PluginEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "Toolbar/Toolbar.h"
#include "gui/Shared/ErrorMessageView.h"
#include "gui/Shared/SpectrumAnalyserTask.h"
#include <pch.h>

class ChowMultiTool;

Expand Down
1 change: 1 addition & 0 deletions src/gui/SVF/SVFPlot.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "SVFChyron.h"
#include "dsp/SVF/SVFProcessor.h"
#include "gui/Shared/DotSlider.h"
#include "gui/Shared/SpectrumAnalyser.h"
#include "state/PluginState.h"

namespace gui::svf
Expand Down
Loading

0 comments on commit 168b876

Please sign in to comment.