Skip to content

Commit

Permalink
EQ Updates (#523)
Browse files Browse the repository at this point in the history
* Using memory arena for EQ bands

* BypassProcessor use chowdsp::NullType as intended

* Using memory arena for EQ bypass process

* Updating bypass processor tests

* Updating Equalizer plot

* Attempting to fix Teensy build
  • Loading branch information
jatinchowdhury18 authored Apr 13, 2024
1 parent 7411294 commit d3133c7
Show file tree
Hide file tree
Showing 18 changed files with 397 additions and 316 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/build-teensy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ jobs:
- name: Build
run: |
cc=~/.platformio/packages/toolchain-gccarmnoneeabi/bin/arm-none-eabi-g++
$cc -DCORE_TEENSY=1 --std=c++17 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 --specs=nosys.specs -Imodules/common -Imodules/dsp -DJUCE_MODULE_AVAILABLE_chowdsp_buffers=1 -c modules/common/chowdsp_core/chowdsp_core.cpp
$cc -DCORE_TEENSY=1 --std=c++17 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 --specs=nosys.specs -Imodules/common -Imodules/dsp -DJUCE_MODULE_AVAILABLE_chowdsp_buffers=1 -c modules/dsp/chowdsp_buffers/chowdsp_buffers.cpp
$cc -DCORE_TEENSY=1 --std=c++17 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 --specs=nosys.specs -Imodules/common -Imodules/dsp -DJUCE_MODULE_AVAILABLE_chowdsp_buffers=1 -c modules/dsp/chowdsp_dsp_data_structures/chowdsp_dsp_data_structures.cpp
$cc -DCORE_TEENSY=1 --std=c++17 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 --specs=nosys.specs -Imodules/common -Imodules/dsp -DJUCE_MODULE_AVAILABLE_chowdsp_buffers=1 -c modules/dsp/chowdsp_dsp_utils/chowdsp_dsp_utils.cpp
$cc -DCORE_TEENSY=1 --std=c++17 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 --specs=nosys.specs -Imodules/common -Imodules/dsp -DJUCE_MODULE_AVAILABLE_chowdsp_buffers=1 -c modules/dsp/chowdsp_math/chowdsp_math.cpp
$cc -DCORE_TEENSY=1 --std=c++17 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 --specs=nosys.specs -Imodules/common -Imodules/dsp -DJUCE_MODULE_AVAILABLE_chowdsp_buffers=1 -DJUCE_MODULE_AVAILABLE_chowdsp_data_structures=1 -c modules/common/chowdsp_core/chowdsp_core.cpp
$cc -DCORE_TEENSY=1 --std=c++17 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 --specs=nosys.specs -Imodules/common -Imodules/dsp -DJUCE_MODULE_AVAILABLE_chowdsp_buffers=1 -DJUCE_MODULE_AVAILABLE_chowdsp_data_structures=1 -c modules/dsp/chowdsp_buffers/chowdsp_buffers.cpp
$cc -DCORE_TEENSY=1 --std=c++17 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 --specs=nosys.specs -Imodules/common -Imodules/dsp -DJUCE_MODULE_AVAILABLE_chowdsp_buffers=1 -DJUCE_MODULE_AVAILABLE_chowdsp_data_structures=1 -c modules/dsp/chowdsp_dsp_data_structures/chowdsp_dsp_data_structures.cpp
$cc -DCORE_TEENSY=1 --std=c++17 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 --specs=nosys.specs -Imodules/common -Imodules/dsp -DJUCE_MODULE_AVAILABLE_chowdsp_buffers=1 -DJUCE_MODULE_AVAILABLE_chowdsp_data_structures=1 -c modules/dsp/chowdsp_dsp_utils/chowdsp_dsp_utils.cpp
$cc -DCORE_TEENSY=1 --std=c++17 -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16 --specs=nosys.specs -Imodules/common -Imodules/dsp -DJUCE_MODULE_AVAILABLE_chowdsp_buffers=1 -DJUCE_MODULE_AVAILABLE_chowdsp_data_structures=1 -c modules/dsp/chowdsp_math/chowdsp_math.cpp
4 changes: 4 additions & 0 deletions examples/SimpleEQ/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ target_sources(SimpleEQ
PluginEditor.cpp
FilterPlots.cpp
)
target_compile_definitions(SimpleEQ
PRIVATE
CHOWDSP_BUFFER_MAX_NUM_CHANNELS=2
)
2 changes: 1 addition & 1 deletion examples/SimpleEQ/SimpleEQPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ void SimpleEQPlugin::processAudioBlock (juce::AudioBuffer<float>& buffer)
// any thread, but we'll do it on the audio thread here.
setEQParams();

if (state.params.linPhaseMode.get())
if (state.params.linPhaseMode->get())
{
// Linear phase mode is on: processing the linear phase EQ here!
linPhaseEQ.processBlock (buffer);
Expand Down
18 changes: 18 additions & 0 deletions modules/dsp/chowdsp_buffers/Buffers/chowdsp_BufferView.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#pragma once

#if JUCE_MODULE_AVAILABLE_chowdsp_data_structures
#include <chowdsp_data_structures/chowdsp_data_structures.h>
#endif

namespace chowdsp
{
/**
Expand Down Expand Up @@ -372,4 +376,18 @@ template <typename BufferType,
std::is_same_v<BufferType, const Buffer<xsimd::batch<double>>> || (std::is_const_v<BufferType> && detail::is_static_buffer_v<std::remove_const_t<BufferType>, xsimd::batch<double>>)>>
BufferView (BufferType&, Ts...) -> BufferView<const xsimd::batch<double>>;
#endif // ! CHOWDSP_NO_XSIMD

#if JUCE_MODULE_AVAILABLE_chowdsp_data_structures
template <typename T>
BufferView<T> make_temp_buffer (ArenaAllocatorView arena, int num_channels, int num_samples)
{
std::array<T*, CHOWDSP_BUFFER_MAX_NUM_CHANNELS> channel_pointers {};
for (size_t ch = 0; ch < static_cast<size_t> (num_channels); ++ch)
{
channel_pointers[ch] = arena.allocate<T> (num_samples, SIMDUtils::defaultSIMDAlignment);
jassert (channel_pointers[ch] != nullptr);
}
return { channel_pointers.data(), num_channels, num_samples };
}
#endif
} // namespace chowdsp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::process (int numSample
}
}

template <typename FloatType, typename ValueSmoothingTypes>
void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::process (int numSamples, ArenaAllocatorView alloc)
{
bufferData = alloc.allocate<FloatType> (numSamples, bufferAlignment);
jassert (bufferData != nullptr); // arena allocator is out of memory!
process (numSamples);
}

template <typename FloatType, typename ValueSmoothingTypes>
void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::process (FloatType value, int numSamples)
{
Expand All @@ -117,6 +125,14 @@ void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::process (FloatType val
bufferData[n] = smoother.getNextValue();
}

template <typename FloatType, typename ValueSmoothingTypes>
void SmoothedBufferValue<FloatType, ValueSmoothingTypes>::process (FloatType value, int numSamples, ArenaAllocatorView alloc)
{
bufferData = alloc.allocate<FloatType> (numSamples, bufferAlignment);
jassert (bufferData != nullptr); // arena allocator is out of memory!
process (value, numSamples);
}

template class SmoothedBufferValue<float, juce::ValueSmoothingTypes::Linear>;
template class SmoothedBufferValue<double, juce::ValueSmoothingTypes::Linear>;
template class SmoothedBufferValue<float, juce::ValueSmoothingTypes::Multiplicative>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,25 +79,15 @@ class SmoothedBufferValue
*/
void process (int numSamples);

void process (int numSamples, ArenaAllocatorView alloc)
{
bufferData = alloc.allocate<FloatType> (numSamples, bufferAlignment);
jassert (bufferData != nullptr); // arena allocator is out of memory!
process (numSamples);
}
void process (int numSamples, ArenaAllocatorView alloc);

/**
* Process smoothing for the input value.
* If smoothing an audio parameter, it is recommended to use a parameter handle instead!
*/
void process (FloatType value, int numSamples);

void process (FloatType value, int numSamples, ArenaAllocatorView alloc)
{
bufferData = alloc.allocate<FloatType> (numSamples, bufferAlignment);
jassert (bufferData != nullptr); // arena allocator is out of memory!
process (value, numSamples);
}
void process (FloatType value, int numSamples, ArenaAllocatorView alloc);

/** Returns a pointer to the current smoothed buffer. */
[[nodiscard]] const FloatType* getSmoothedBuffer() const { return bufferData; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
namespace chowdsp
{
template <typename SampleType, typename DelayInterpType>
void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<std::is_same_v<DelayInterpType, std::nullptr_t>>>::prepare (const juce::dsp::ProcessSpec& spec, bool onOffParam)
void BypassProcessor<SampleType,
DelayInterpType,
std::enable_if_t<std::is_same_v<DelayInterpType, NullType>>>::prepare (const juce::dsp::ProcessSpec& spec, bool onOffParam, bool useInternalBuffer)
{
prevOnOffParam = onOffParam;
fadeBuffer.setMaxSize ((int) spec.numChannels, (int) spec.maximumBlockSize);
if (useInternalBuffer)
fadeBuffer.setMaxSize ((int) spec.numChannels, (int) spec.maximumBlockSize);
}

template <typename SampleType, typename DelayInterpType>
bool BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<std::is_same_v<DelayInterpType, std::nullptr_t>>>::processBlockIn (const BufferView<const SampleType>& block, bool onOffParam)
bool BypassProcessor<SampleType,
DelayInterpType,
std::enable_if_t<std::is_same_v<DelayInterpType, NullType>>>::processBlockIn (const BufferView<const SampleType>& block,
bool onOffParam,
std::optional<ArenaAllocatorView> arena)
{
if (! onOffParam && ! prevOnOffParam)
{
Expand All @@ -17,15 +24,21 @@ bool BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<std::is_same_

if (onOffParam != prevOnOffParam)
{
fadeBuffer.setCurrentSize (block.getNumChannels(), block.getNumSamples());
BufferMath::copyBufferData (block, fadeBuffer);
if (arena.has_value())
fadeBufferView = make_temp_buffer<SampleType> (*arena, block.getNumChannels(), block.getNumSamples());
else
fadeBufferView = BufferView { fadeBuffer, 0, block.getNumSamples(), 0, block.getNumChannels() };

BufferMath::copyBufferData (block, fadeBufferView);
}

return true;
}

template <typename SampleType, typename DelayInterpType>
void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<std::is_same_v<DelayInterpType, std::nullptr_t>>>::processBlockOut (const BufferView<SampleType>& block, bool onOffParam)
void BypassProcessor<SampleType,
DelayInterpType,
std::enable_if_t<std::is_same_v<DelayInterpType, NullType>>>::processBlockOut (const BufferView<SampleType>& block, bool onOffParam)
{
auto fadeOutputBuffer = [onOffParam] (auto* blockPtr, const auto* fadePtr, const int startSample, const int numSamples)
{
Expand Down Expand Up @@ -68,7 +81,7 @@ void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<std::is_same_
for (int ch = 0; ch < numChannels; ++ch)
{
auto* blockPtr = block.getWritePointer (ch);
const auto* fadePtr = fadeBuffer.getReadPointer (ch);
const auto* fadePtr = fadeBufferView.getReadPointer (ch);

fadeOutputBuffer (blockPtr, fadePtr, 0, numSamples);
}
Expand All @@ -79,29 +92,40 @@ void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<std::is_same_

//===========================================================
template <typename SampleType, typename DelayInterpType>
void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_same_v<DelayInterpType, std::nullptr_t>>>::prepare (const juce::dsp::ProcessSpec& spec, bool onOffParam)
void BypassProcessor<SampleType,
DelayInterpType,
std::enable_if_t<! std::is_same_v<DelayInterpType, NullType>>>::prepare (const juce::dsp::ProcessSpec& spec,
bool onOffParam,
bool useInternalBuffer)
{
prevOnOffParam = onOffParam;
fadeBuffer.setMaxSize ((int) spec.numChannels, (int) spec.maximumBlockSize);
if (useInternalBuffer)
fadeBuffer.setMaxSize ((int) spec.numChannels, (int) spec.maximumBlockSize);

compDelay.prepare (spec); // sample rate does not matter
}

template <typename SampleType, typename DelayInterpType>
void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_same_v<DelayInterpType, std::nullptr_t>>>::setLatencySamples (int delaySamples)
void BypassProcessor<SampleType,
DelayInterpType,
std::enable_if_t<! std::is_same_v<DelayInterpType, NullType>>>::setLatencySamples (int delaySamples)
{
setLatencySamplesInternal ((NumericType) delaySamples);
}

template <typename SampleType, typename DelayInterpType>
void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_same_v<DelayInterpType, std::nullptr_t>>>::setLatencySamples (NumericType delaySamples)
void BypassProcessor<SampleType,
DelayInterpType,
std::enable_if_t<! std::is_same_v<DelayInterpType, NullType>>>::setLatencySamples (NumericType delaySamples)
{
static_assert (! std::is_same_v<DelayInterpType, DelayLineInterpolationTypes::None>, "Attempting to set non-integer latency value without using delay interpolation!");
setLatencySamplesInternal (delaySamples);
}

template <typename SampleType, typename DelayInterpType>
void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_same_v<DelayInterpType, std::nullptr_t>>>::setLatencySamplesInternal (NumericType delaySamples)
void BypassProcessor<SampleType,
DelayInterpType,
std::enable_if_t<! std::is_same_v<DelayInterpType, NullType>>>::setLatencySamplesInternal (NumericType delaySamples)
{
if (juce::approximatelyEqual (delaySamples, prevDelay))
return;
Expand All @@ -115,7 +139,11 @@ void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_sam
}

template <typename SampleType, typename DelayInterpType>
bool BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_same_v<DelayInterpType, std::nullptr_t>>>::processBlockIn (const BufferView<SampleType>& block, bool onOffParam)
bool BypassProcessor<SampleType,
DelayInterpType,
std::enable_if_t<! std::is_same_v<DelayInterpType, NullType>>>::processBlockIn (const BufferView<SampleType>& block,
bool onOffParam,
std::optional<ArenaAllocatorView> arena)
{
enum class DelayOp
{
Expand Down Expand Up @@ -161,9 +189,13 @@ bool BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_sam

if (onOffParam != prevOnOffParam)
{
fadeBuffer.setCurrentSize (block.getNumChannels(), block.getNumSamples());
BufferMath::copyBufferData (block, fadeBuffer);
doDelayOp (fadeBuffer, compDelay, DelayOp::Pop);
if (arena.has_value())
fadeBufferView = make_temp_buffer<SampleType> (*arena, block.getNumChannels(), block.getNumSamples());
else
fadeBufferView = BufferView { fadeBuffer, 0, block.getNumSamples(), 0, block.getNumChannels() };

BufferMath::copyBufferData (block, fadeBufferView);
doDelayOp (fadeBufferView, compDelay, DelayOp::Pop);

if (onOffParam && latencySampleCount < 0)
latencySampleCount = (int) compDelay.getDelay();
Expand All @@ -177,7 +209,9 @@ bool BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_sam
}

template <typename SampleType, typename DelayInterpType>
void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_same_v<DelayInterpType, std::nullptr_t>>>::processBlockOut (const BufferView<SampleType>& block, bool onOffParam)
void BypassProcessor<SampleType,
DelayInterpType,
std::enable_if_t<! std::is_same_v<DelayInterpType, NullType>>>::processBlockOut (const BufferView<SampleType>& block, bool onOffParam)
{
auto fadeOutputBuffer = [onOffParam] (auto* blockPtr, const auto* fadePtr, const int startSample, const int numSamples)
{
Expand Down Expand Up @@ -222,7 +256,7 @@ void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_sam
for (int ch = 0; ch < numChannels; ++ch)
{
auto* blockPtr = block.getWritePointer (ch);
const auto* fadePtr = fadeBuffer.getReadPointer (ch);
const auto* fadePtr = fadeBufferView.getReadPointer (ch);

fadeOutputBuffer (blockPtr, fadePtr, startSample, numSamples);
}
Expand All @@ -232,7 +266,9 @@ void BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_sam
}

template <typename SampleType, typename DelayInterpType>
int BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_same_v<DelayInterpType, std::nullptr_t>>>::getFadeStartSample (const int numSamples)
int BypassProcessor<SampleType,
DelayInterpType,
std::enable_if_t<! std::is_same_v<DelayInterpType, NullType>>>::getFadeStartSample (const int numSamples)
{
if (latencySampleCount <= 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ namespace chowdsp
* }
* ```
*/
template <typename SampleType, typename DelayInterpType = std::nullptr_t, typename = void>
template <typename SampleType, typename DelayInterpType = NullType, typename = void>
class BypassProcessor;

template <typename SampleType, typename DelayInterpType>
class BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<std::is_same_v<DelayInterpType, std::nullptr_t>>>
class BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<std::is_same_v<DelayInterpType, NullType>>>
{
public:
using NumericType = SampleTypeHelpers::NumericType<SampleType>;
Expand All @@ -40,14 +40,14 @@ class BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<std::is_same
}

/** Allocated required memory, and resets the property */
void prepare (const juce::dsp::ProcessSpec& spec, bool onOffParam);
void prepare (const juce::dsp::ProcessSpec& spec, bool onOffParam, bool useInternalBuffer = true);

/**
* Call this at the start of your processBlock().
* If it returns false, you can safely skip all other
* processing.
*/
bool processBlockIn (const BufferView<const SampleType>& buffer, bool onOffParam);
bool processBlockIn (const BufferView<const SampleType>& buffer, bool onOffParam, std::optional<ArenaAllocatorView> arena = std::nullopt);

/**
* Call this at the end of your processBlock().
Expand All @@ -59,12 +59,13 @@ class BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<std::is_same
private:
bool prevOnOffParam = false;
Buffer<SampleType> fadeBuffer;
BufferView<SampleType> fadeBufferView;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BypassProcessor)
};

template <typename SampleType, typename DelayInterpType>
class BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_same_v<DelayInterpType, std::nullptr_t>>>
class BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_same_v<DelayInterpType, NullType>>>
{
public:
using NumericType = SampleTypeHelpers::NumericType<SampleType>;
Expand All @@ -78,7 +79,7 @@ class BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_sa
}

/** Allocated required memory, and resets the property */
void prepare (const juce::dsp::ProcessSpec& spec, bool onOffParam);
void prepare (const juce::dsp::ProcessSpec& spec, bool onOffParam, bool useInternalBuffer = true);

/**
* If the non-bypassed processing has some associated
Expand All @@ -101,7 +102,7 @@ class BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_sa
* If it returns false, you can safely skip all other
* processing.
*/
bool processBlockIn (const BufferView<SampleType>& buffer, bool onOffParam);
bool processBlockIn (const BufferView<SampleType>& buffer, bool onOffParam, std::optional<ArenaAllocatorView> arena = std::nullopt);

/**
* Call this at the end of your processBlock().
Expand All @@ -116,6 +117,7 @@ class BypassProcessor<SampleType, DelayInterpType, std::enable_if_t<! std::is_sa

bool prevOnOffParam = false;
Buffer<SampleType> fadeBuffer;
BufferView<SampleType> fadeBufferView;

DelayLine<SampleType, DelayInterpType> compDelay { 1 << 18 }; // max latency = 2^18 = 262144 samples
NumericType prevDelay {};
Expand Down
Loading

0 comments on commit d3133c7

Please sign in to comment.