commit d573e4ab09c5f7628941b42e0e73c0736c759b45 parent c57c0bb52429a9190f7126d5953e6e71e852c5a1 Author: jatinchowdhury18 <jatinchowdhury18@gmail.com> Date: Tue, 10 Jan 2023 23:04:13 -0800 Fix dead comments in FIR filter, and bump version number (#299) * Fix dead comments in FIR filter, and bump version number * Fixing CI failures * Apply clang-format * Fix compiling headless tool Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Diffstat:
20 files changed, 65 insertions(+), 231 deletions(-)
diff --git a/.github/workflows/auto-format.yml b/.github/workflows/auto-format.yml @@ -20,11 +20,9 @@ jobs: steps: - name: Install Linux Deps run: | - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 11 - sudo apt-get install clang-format-11 - clang-format-11 --version + sudo apt update + sudo apt -y install clang-format-14 + clang-format-14 --version - name: Checkout code uses: actions/checkout@v2 @@ -35,7 +33,7 @@ jobs: - name: Run clang-format shell: bash working-directory: ${{github.workspace}}/Plugin - run: find Source/ -iname *.h -o -iname *.cpp | xargs clang-format-11 -style=file -verbose -i + run: find Source/ -iname *.h -o -iname *.cpp | xargs clang-format-14 -style=file -verbose -i - name: Commit & Push changes uses: actions-js/push@master diff --git a/Plugin/CMakeLists.txt b/Plugin/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.15) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment target") set(CMAKE_CXX_STANDARD 17) -project(CHOWTapeModel VERSION 2.11.0) +project(CHOWTapeModel VERSION 2.11.1) if(IOS) option(CHOWTAPE_BUILD_CLAP "Create a CLAP target for ChowTape" OFF) diff --git a/Plugin/Source/GUI/AutoUpdating.cpp b/Plugin/Source/GUI/AutoUpdating.cpp @@ -56,7 +56,8 @@ const Colour textColour = Colour (0xFFEAA92C); AutoUpdater::AutoUpdater() { - auto setupButton = [=] (TextButton& button) { + auto setupButton = [=] (TextButton& button) + { addAndMakeVisible (button); button.setColour (TextButton::buttonColourId, backgroundColour); button.setColour (TextButton::textColourOffId, textColour); diff --git a/Plugin/Source/GUI/IOSOnly/TipJar.cpp b/Plugin/Source/GUI/IOSOnly/TipJar.cpp @@ -57,7 +57,8 @@ void TipJar::productsInfoReturned (const Array<InAppPurchases::Product>& product { if (info.second == products[i].identifier) { - rootMenu->addItem (info.first, [=] { doTipPurchase (info.second); }); + rootMenu->addItem (info.first, [=] + { doTipPurchase (info.second); }); break; } } diff --git a/Plugin/Source/GUI/ModulatableSlider.cpp b/Plugin/Source/GUI/ModulatableSlider.cpp @@ -70,7 +70,6 @@ void ModulatableSlider::timerCallback() repaint(); } - void ModulatableSlider::drawRotarySlider (juce::Graphics& g, int x, int y, int width, int height, float sliderPos, float modSliderPos) { int diameter = (width > height) ? height : width; @@ -152,7 +151,7 @@ void ModulatableSlider::drawLinearSlider (juce::Graphics& g, int x, int y, int w auto thumbWidth = getLookAndFeel().getSliderThumbRadius (*this); auto thumbRect = juce::Rectangle<float> (static_cast<float> (thumbWidth), static_cast<float> (thumbWidth)) - .withCentre (maxPoint); + .withCentre (maxPoint); sharedAssets->knob->drawWithin (g, thumbRect, juce::RectanglePlacement::stretchToFit, alphaMult); } @@ -218,7 +217,8 @@ void ModSliderItem::update() slider.startTimerHz (24); } - slider.setPluginEditorCallback ([this] { return magicBuilder.getMagicState().getProcessor()->getActiveEditor(); }); + slider.setPluginEditorCallback ([this] + { return magicBuilder.getMagicState().getProcessor()->getActiveEditor(); }); slider.setTitle (magicBuilder.getStyleProperty (foleys::IDs::name, configNode)); defaultHeight = magicBuilder.getStyleProperty (foleys::IDs::defaultHeight, configNode); @@ -270,7 +270,8 @@ void ModSliderItem::update() void ModSliderItem::resized() { - const auto sliderTextHeightToUse = [this] { + const auto sliderTextHeightToUse = [this] + { if (defaultHeight == 0) return sliderTextBoxHeight; diff --git a/Plugin/Source/GUI/OversamplingMenu.cpp b/Plugin/Source/GUI/OversamplingMenu.cpp @@ -32,7 +32,8 @@ void OversamplingMenu::generateComboBoxMenu() // update menu after parameter change has propagated Timer::callAfterDelay ( 50, - [this, safeComp = Component::SafePointer<OversamplingMenu> (this)] { + [this, safeComp = Component::SafePointer<OversamplingMenu> (this)] + { if (safeComp != nullptr) { BaseOSMenuType::generateComboBoxMenu(); diff --git a/Plugin/Source/GUI/SettingsButton.cpp b/Plugin/Source/GUI/SettingsButton.cpp @@ -16,7 +16,8 @@ SettingsButton::SettingsButton (const ChowtapeModelAudioProcessor& processor, ch auto cog = Drawable::createFromImageData (BinaryData::cogsolid_svg, BinaryData::cogsolid_svgSize); setImages (cog.get()); - onClick = [=] { showSettingsMenu(); }; + onClick = [=] + { showSettingsMenu(); }; } void SettingsButton::globalSettingChanged (SettingID settingID) @@ -42,9 +43,12 @@ void SettingsButton::showSettingsMenu() openGLMenu (menu, 100); menu.addSeparator(); - menu.addItem ("View Source Code", [=] { URL ("https://github.com/jatinchowdhury18/AnalogTapeModel").launchInDefaultBrowser(); }); - menu.addItem ("Copy Diagnostic Info", [=] { copyDiagnosticInfo(); }); - menu.addItem ("View User Manual", [=] { URL ("https://chowdsp.com/manuals/ChowTapeManual.pdf").launchInDefaultBrowser(); }); + menu.addItem ("View Source Code", [=] + { URL ("https://github.com/jatinchowdhury18/AnalogTapeModel").launchInDefaultBrowser(); }); + menu.addItem ("Copy Diagnostic Info", [=] + { copyDiagnosticInfo(); }); + menu.addItem ("View User Manual", [=] + { URL ("https://chowdsp.com/manuals/ChowTapeManual.pdf").launchInDefaultBrowser(); }); // get top level component that is big enough Component* parentComp = this; @@ -81,7 +85,8 @@ void SettingsButton::openGLMenu (PopupMenu& menu, int itemID) PopupMenu::Item item; item.itemID = ++itemID; item.text = "Use OpenGL"; - item.action = [=] { pluginSettings->setProperty (openglID, ! isCurrentlyOn); }; + item.action = [=] + { pluginSettings->setProperty (openglID, ! isCurrentlyOn); }; item.colour = isCurrentlyOn ? onColour : offColour; menu.addItem (item); diff --git a/Plugin/Source/GUI/TitleComp.cpp b/Plugin/Source/GUI/TitleComp.cpp @@ -12,7 +12,8 @@ void TitleComp::paint (Graphics& g) auto curFont = g.getCurrentFont(); auto b = getLocalBounds(); - auto drawText = [=, &g, &b] (const String& text) { + auto drawText = [=, &g, &b] (const String& text) + { auto width = curFont.getStringWidth (text); g.drawFittedText (text, b.removeFromLeft (width), Justification::left, 1); }; diff --git a/Plugin/Source/GUI/Visualizers/TapeScope.cpp b/Plugin/Source/GUI/Visualizers/TapeScope.cpp @@ -38,7 +38,8 @@ void TapeScope::createPlotPaths (Path& path, Path& filledPath, Rectangle<float> filledPath.clear(); // get strings for I/O dB meters - auto getDBString = [] (const MagicLevelSource& source, AudioType type) -> String { + auto getDBString = [] (const MagicLevelSource& source, AudioType type) -> String + { String prefix = type == Input ? "IN: " : "OUT: "; auto dBVal = Decibels::gainToDecibels (source.getRMSvalue (0), -80.0f); return prefix + String (dBVal, 1) + " dB"; @@ -53,7 +54,8 @@ void TapeScope::createPlotPaths (Path& path, Path& filledPath, Rectangle<float> const auto fontHeight = labelHeight * 0.9f; const auto font = Font (fontHeight); - auto drawLabel = [b, &filledPath, labelHeight, font] (const String& textStr, float x) { + auto drawLabel = [b, &filledPath, labelHeight, font] (const String& textStr, float x) + { auto width = font.getStringWidthFloat (textStr); x = x < b.getCentreX() ? x : x - width; diff --git a/Plugin/Source/GUI/WowFlutterMenu.cpp b/Plugin/Source/GUI/WowFlutterMenu.cpp @@ -57,7 +57,8 @@ WowFlutterMenu::WowFlutterMenu (const ChowtapeModelAudioProcessor& proc, const S const bool isFlutter = type == "Flutter"; setupRateParam (isFlutter); - auto snycToTapeSpeed = [=, &proc] { + auto snycToTapeSpeed = [=, &proc] + { const auto& vts = proc.getVTS(); if (auto speedParam = dynamic_cast<AudioParameterFloat*> (vts.getParameter ("speed"))) { @@ -75,7 +76,8 @@ WowFlutterMenu::WowFlutterMenu (const ChowtapeModelAudioProcessor& proc, const S } }; - auto syncToRhythm = [=, &proc] (float multipleOfQuarterNote) { + auto syncToRhythm = [=, &proc] (float multipleOfQuarterNote) + { const auto& posInfo = proc.getPositionInfo(); auto quarterNoteTime = 60.0f / (float) posInfo.bpm; @@ -116,7 +118,8 @@ void WowFlutterMenu::setupUI() lnf = std::make_unique<WowFlutterMenuLNF>(); setLookAndFeel (lnf.get()); - onChange = [=] { setSelectedItemIndex (-1); }; + onChange = [=] + { setSelectedItemIndex (-1); }; } void WowFlutterMenu::setupRateParam (bool isFlutter) diff --git a/Plugin/Source/Headless/FirBench.h b/Plugin/Source/Headless/FirBench.h @@ -1,89 +0,0 @@ -#include "../Processors/Loss_Effects/FIRFilter.h" - -namespace -{ -constexpr double pluginSampleRate = 48000.0; -constexpr int samplesPerBlock = 256; -} // namespace - -class FirBench : public ConsoleApplication::Command -{ -public: - FirBench() - { - this->commandOption = "--fir-bench"; - this->argumentDescription = "--fir-bench --size=FILTER_SIZE --length=AUDIO_LENGTH"; - this->shortDescription = "Runs benchmarks for ChowTapeModel FIR Filters"; - this->longDescription = ""; - this->command = std::bind (&FirBench::runBench, this, std::placeholders::_1); - } - - void runBench (const ArgumentList& args) - { - int firSize = 128; - if (args.containsOption ("--size")) - firSize = args.getValueForOption ("--size").getIntValue(); - - std::cout << "Creating FIR filter with size " << firSize << std::endl; - FIRFilter filter { firSize }; - setFilterCoefs (filter, (size_t) firSize); - - float audioLength = 30.0f; - if (args.containsOption ("--length")) - audioLength = args.getValueForOption ("--length").getFloatValue(); - - std::cout << "Creating audio buffer of length " << audioLength << " seconds" << std::endl; - const int numSamples = int (audioLength * pluginSampleRate); - auto buffer = createAudio (numSamples); - - std::cout << "Processing audio..." << std::endl; - filter.reset(); - auto time = timeAudioProcess (filter, buffer, samplesPerBlock); - - std::cout << "Results:" << std::endl; - std::cout << audioLength / time << "x real-time" << std::endl; - std::cout << time << " seconds" << std::endl; - } - -private: - void setFilterCoefs (FIRFilter& filter, const size_t size) - { - std::vector<float> coefs (size); - Random r; - for (size_t i = 0; i < size; ++i) - coefs[i] = r.nextFloat() * 2.0f + 1.0f; - - filter.setCoefs (coefs.data()); - } - - AudioBuffer<float> createAudio (const int numSamples) - { - AudioBuffer<float> buffer (1, numSamples); - Random r; - for (int i = 0; i < numSamples; ++i) - buffer.setSample (0, i, r.nextFloat() * 2.0f + 1.0f); - - return buffer; - } - - double timeAudioProcess (FIRFilter& filter, AudioBuffer<float>& audio, const int blockSize) - { - auto totalNumSamples = audio.getNumSamples(); - int samplePtr = 0; - - Time time; - auto start = time.getMillisecondCounterHiRes(); - while (totalNumSamples > 0) - { - auto curBlockSize = jmin (totalNumSamples, blockSize); - totalNumSamples -= curBlockSize; - - filter.process (audio.getWritePointer (0) + samplePtr, curBlockSize); - samplePtr += curBlockSize; - } - - return (time.getMillisecondCounterHiRes() - start) / 1000.0; - } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FirBench) -}; diff --git a/Plugin/Source/Headless/Main.cpp b/Plugin/Source/Headless/Main.cpp @@ -1,5 +1,4 @@ #include "Benchmarks.h" -#include "FirBench.h" #include "ScreenshotHelper.h" #include "UnitTests/UnitTests.h" @@ -34,9 +33,6 @@ int main (int argc, char* argv[]) Benchmarks benchmarks; app.addCommand (benchmarks); - FirBench firBench; - app.addCommand (firBench); - UnitTests unitTests; app.addCommand (unitTests); diff --git a/Plugin/Source/MixGroups/MixGroupsSharedData.cpp b/Plugin/Source/MixGroups/MixGroupsSharedData.cpp @@ -55,7 +55,8 @@ void MixGroupsSharedData::copyPluginState (int mixGroup, AudioProcessorValueTree void MixGroupsSharedData::setParameter (const String& paramID, int mixGroup, float value, String uuid) { paramMaps[(size_t) mixGroup - 1]->set (paramID, value); - MessageManager::callAsync ([=] { listeners.call (&Listener::mixGroupParamChanged, paramID, mixGroup, value, uuid); }); + MessageManager::callAsync ([=] + { listeners.call (&Listener::mixGroupParamChanged, paramID, mixGroup, value, uuid); }); } float MixGroupsSharedData::getParameter (const String& paramID, int mixGroup) const diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp @@ -226,9 +226,11 @@ AudioProcessorEditor* ChowtapeModelAudioProcessor::createEditor() builder->registerFactory ("SettingsButton", &SettingsButtonItem::factory); builder->registerFactory ("InfoComp", &chowdsp::InfoItem<ChowtapeModelAudioProcessor>::factory); - builder->registerFactory ("FlutterMenu", [] (foleys::MagicGUIBuilder& b, const ValueTree& node) -> std::unique_ptr<foleys::GuiItem> { return std::make_unique<WowFlutterMenuItem> (b, node, "Flutter"); }); + builder->registerFactory ("FlutterMenu", [] (foleys::MagicGUIBuilder& b, const ValueTree& node) -> std::unique_ptr<foleys::GuiItem> + { return std::make_unique<WowFlutterMenuItem> (b, node, "Flutter"); }); - builder->registerFactory ("WowMenu", [] (foleys::MagicGUIBuilder& b, const ValueTree& node) -> std::unique_ptr<foleys::GuiItem> { return std::make_unique<WowFlutterMenuItem> (b, node, "Wow"); }); + builder->registerFactory ("WowMenu", [] (foleys::MagicGUIBuilder& b, const ValueTree& node) -> std::unique_ptr<foleys::GuiItem> + { return std::make_unique<WowFlutterMenuItem> (b, node, "Wow"); }); builder->registerJUCELookAndFeels(); builder->registerLookAndFeel ("MyLNF", std::make_unique<MyLNF>()); @@ -240,11 +242,11 @@ AudioProcessorEditor* ChowtapeModelAudioProcessor::createEditor() { for (auto speed : { 3.75f, 7.5f, 15.0f, 30.0f }) { - magicState.addTrigger ("set_speed_" + String (speed, 2, false), [speedHandle, speed] { + magicState.addTrigger ("set_speed_" + String (speed, 2, false), [speedHandle, speed] + { speedHandle->beginChangeGesture(); speedHandle->setValueNotifyingHost (speedHandle->convertTo0to1 (speed)); - speedHandle->endChangeGesture(); - }); + speedHandle->endChangeGesture(); }); } } else diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisSTN.cpp b/Plugin/Source/Processors/Hysteresis/HysteresisSTN.cpp @@ -50,7 +50,8 @@ HysteresisSTN::HysteresisSTN() std::vector<std::future<void>> futures; for (const auto& width : widthTags) { - auto loadModelSet = [=] (size_t widthModelIdx) { + auto loadModelSet = [=] (size_t widthModelIdx) + { auto modelsStream = getModelFileStream ("hyst_width_" + width + ".json"); jassert (modelsStream != nullptr); @@ -66,7 +67,8 @@ HysteresisSTN::HysteresisSTN() }; futures.push_back (std::async (std::launch::async, - [=, &widthLoadIdx] { loadModelSet (widthLoadIdx++); })); + [=, &widthLoadIdx] + { loadModelSet (widthLoadIdx++); })); } for (auto& f : futures) diff --git a/Plugin/Source/Processors/Hysteresis/ToneControl.cpp b/Plugin/Source/Processors/Hysteresis/ToneControl.cpp @@ -19,7 +19,8 @@ void ToneStage::prepare (double sampleRate, int numChannels) for (size_t ch = 0; ch < (size_t) numChannels; ++ch) { - auto resetSmoothValue = [sampleRate] (SmoothGain& value, float startValue) { + auto resetSmoothValue = [sampleRate] (SmoothGain& value, float startValue) + { value.reset (sampleRate, slewTime); value.setCurrentAndTargetValue (startValue); }; diff --git a/Plugin/Source/Processors/Input_Filters/InputFilters.cpp b/Plugin/Source/Processors/Input_Filters/InputFilters.cpp @@ -23,7 +23,8 @@ void InputFilters::createParameterLayout (chowdsp::Parameters& params) NormalisableRange highFreqRange { 2000.0f, maxFreq }; highFreqRange.setSkewForCentre (10000.0f); - auto freqToString = [] (float freq) -> String { + auto freqToString = [] (float freq) -> String + { String suffix = " Hz"; if (freq > 1000.0f) { @@ -33,7 +34,8 @@ void InputFilters::createParameterLayout (chowdsp::Parameters& params) return String (freq, 2, false) + suffix; }; - auto stringToFreq = [] (const String& string) -> float { + auto stringToFreq = [] (const String& string) -> float + { float freq = string.getFloatValue(); if (string.getLastCharacter() == 'k') freq *= 1000.0f; diff --git a/Plugin/Source/Processors/Loss_Effects/FIRFilter.h b/Plugin/Source/Processors/Loss_Effects/FIRFilter.h @@ -1,81 +0,0 @@ -#ifndef FIRFILTER_H_INCLUDED -#define FIRFILTER_H_INCLUDED - -// include <Accelerate> on Apple devices so we can use vDSP_dotpr -#if JUCE_MAC || JUCE_IOS -#define Point CarbonDummyPointName -#define Component CarbonDummyCompName -//#include <Accelerate/Accelerate.h> -#undef Point -#undef Component -#endif - -#include <JuceHeader.h> -#include <numeric> - -/** FIR filter using a double-buffer and std::inner_product */ -class FIRFilter -{ -public: - FIRFilter (int filter_order) : order ((size_t) filter_order) - { - h.resize (order); - z.resize (2 * order); - } - - FIRFilter (FIRFilter&&) noexcept = default; - - void reset() - { - zPtr = 0; - FloatVectorOperations::fill (z.data(), 0.0f, 2 * (int) order); - } - - void setCoefs (float* coefs) - { - FloatVectorOperations::copy (h.data(), coefs, (int) order); - } - - inline void process (float* buffer, int numSamples) - { - float y = 0.0f; - for (int n = 0; n < numSamples; ++n) - { - // insert input into double-buffered state - z[zPtr] = buffer[n]; - z[zPtr + order] = buffer[n]; - -#if JUCE_MAC || JUCE_IOS - y = 0.0f; - //vDSP_dotpr (z.data() + zPtr, 1, h.data(), 1, &y, order); // use Acclerate inner product (if available) -#else - y = std::inner_product (z.data() + zPtr, z.data() + zPtr + order, h.data(), 0.0f); // comput inner product -#endif - - zPtr = (zPtr == 0 ? order - 1 : zPtr - 1); // iterate state pointer in reverse - buffer[n] = y; - } - } - - inline void processBypassed (const float* buffer, int numSamples) - { - for (int n = 0; n < numSamples; ++n) - { - z[zPtr] = buffer[n]; - z[zPtr + order] = buffer[n]; - zPtr = (zPtr == 0 ? order - 1 : zPtr - 1); - } - } - -protected: - std::vector<float> h; - const size_t order; - -private: - std::vector<float> z; - size_t zPtr = 0; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FIRFilter) -}; - -#endif //FIRFILTER_H_INCLUDED diff --git a/Plugin/Source/Processors/Loss_Effects/LossFilter.cpp b/Plugin/Source/Processors/Loss_Effects/LossFilter.cpp @@ -46,13 +46,9 @@ void LossFilter::prepare (float sampleRate, int samplesPerBlock, int numChannels for (auto& filter : filters) { - filter.clear(); - for (size_t ch = 0; ch < (size_t) numChannels; ++ch) - { - filter.emplace_back (curOrder); - filter[ch].reset(); - filter[ch].setCoefs (currentCoefs.getRawDataPointer()); - } + filter.setOrder (curOrder); + filter.prepare (numChannels); + filter.setCoefficients (currentCoefs.getRawDataPointer()); } prevSpeed = *speed; @@ -116,8 +112,7 @@ void LossFilter::processBlock (AudioBuffer<float>& buffer) if ((*speed != prevSpeed || *spacing != prevSpacing || *thickness != prevThickness || *gap != prevGap) && fadeCount == 0) { calcCoefs (bumpFilter[! activeFilter]); - for (auto& filt : filters[! activeFilter]) - filt.setCoefs (currentCoefs.getRawDataPointer()); + filters[! activeFilter].setCoefficients (currentCoefs.getRawDataPointer()); bumpFilter[! activeFilter].reset(); @@ -129,20 +124,14 @@ void LossFilter::processBlock (AudioBuffer<float>& buffer) } if (fadeCount > 0) - { fadeBuffer.makeCopyOf (buffer, true); - } else - { - for (size_t ch = 0; ch < (size_t) numChannels; ++ch) - filters[! activeFilter][ch].processBypassed (buffer.getReadPointer ((int) ch), numSamples); - } + filters[! activeFilter].processBlockBypassed (buffer); // normal processing here... { dsp::AudioBlock<float> block (buffer); - for (int ch = 0; ch < numChannels; ++ch) - filters[activeFilter][(size_t) ch].process (buffer.getWritePointer (ch), numSamples); + filters[activeFilter].processBlock (buffer); bumpFilter[activeFilter].process (dsp::ProcessContextReplacing<float> { block }); } @@ -150,8 +139,7 @@ void LossFilter::processBlock (AudioBuffer<float>& buffer) if (fadeCount > 0) { dsp::AudioBlock<float> fadeBlock (fadeBuffer); - for (int ch = 0; ch < numChannels; ++ch) - filters[! activeFilter][(size_t) ch].process (fadeBuffer.getWritePointer (ch), numSamples); + filters[! activeFilter].processBlock (fadeBuffer); bumpFilter[! activeFilter].process (dsp::ProcessContextReplacing<float> { fadeBlock }); diff --git a/Plugin/Source/Processors/Loss_Effects/LossFilter.h b/Plugin/Source/Processors/Loss_Effects/LossFilter.h @@ -3,7 +3,6 @@ #include "../BypassProcessor.h" #include "AzimuthProc.h" -#include "FIRFilter.h" class LossFilter { @@ -23,7 +22,7 @@ private: void calcCoefs (MultiChannelIIR& filter); static void calcHeadBumpFilter (float speedIps, float gapMeters, double fs, MultiChannelIIR& filter); - std::vector<FIRFilter> filters[2]; + std::array<chowdsp::FIRFilter<float>, 2> filters; MultiChannelIIR bumpFilter[2]; int activeFilter = 0; int fadeCount = 0;