1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

DSP: Add ProcessorChain tests

This commit is contained in:
reuk 2020-03-16 16:55:10 +00:00
parent 0ac8552a63
commit adbf5fc219
3 changed files with 156 additions and 19 deletions

View file

@ -86,4 +86,5 @@
#include "containers/juce_AudioBlock_test.cpp"
#include "frequency/juce_FFT_test.cpp"
#include "processors/juce_FIRFilter_test.cpp"
#include "processors/juce_ProcessorChain_test.cpp"
#endif

View file

@ -63,64 +63,58 @@ class ProcessorChain
{
public:
/** Get a reference to the processor at index `Index`. */
template <int Index> auto& get() noexcept { return std::get<Index> (processors).processor; }
template <int Index> auto& get() noexcept { return std::get<Index> (processors); }
/** Get a reference to the processor at index `Index`. */
template <int Index> const auto& get() const noexcept { return std::get<Index> (processors).processor; }
template <int Index> const auto& get() const noexcept { return std::get<Index> (processors); }
/** Set the processor at index `Index` to be bypassed or enabled. */
template <int Index>
void setBypassed (bool b) noexcept { std::get<Index> (processors).isBypassed = b; }
void setBypassed (bool b) noexcept { bypassed[(size_t) Index] = b; }
/** Query whether the processor at index `Index` is bypassed. */
template <int Index>
bool isBypassed() const noexcept { return std::get<Index> (processors).isBypassed; }
bool isBypassed() const noexcept { return bypassed[(size_t) Index]; }
/** Prepare all inner processors with the provided `ProcessSpec`. */
void prepare (const ProcessSpec& spec)
{
detail::forEachInTuple ([&] (auto& item, size_t) { item.processor.prepare (spec); }, processors);
detail::forEachInTuple ([&] (auto& proc, size_t) { proc.prepare (spec); }, processors);
}
/** Reset all inner processors. */
void reset()
{
detail::forEachInTuple ([] (auto& item, size_t) { item.processor.reset(); }, processors);
detail::forEachInTuple ([] (auto& proc, size_t) { proc.reset(); }, processors);
}
/** Process `context` through all inner processors in sequence. */
template <typename ProcessContext>
void process (const ProcessContext& context) noexcept
{
detail::forEachInTuple ([&] (auto& item, size_t index) noexcept
detail::forEachInTuple ([&] (auto& proc, size_t index) noexcept
{
if (context.usesSeparateInputAndOutputBlocks() && index != 0)
{
jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels());
ProcessContextReplacing<typename ProcessContext::SampleType> replacingContext (context.getOutputBlock());
replacingContext.isBypassed = (item.isBypassed || context.isBypassed);
replacingContext.isBypassed = (bypassed[index] || context.isBypassed);
item.processor.process (replacingContext);
proc.process (replacingContext);
}
else
{
ProcessContext contextCopy (context);
contextCopy.isBypassed = (item.isBypassed || context.isBypassed);
contextCopy.isBypassed = (bypassed[index] || context.isBypassed);
item.processor.process (contextCopy);
proc.process (contextCopy);
}
}, processors);
}
private:
template <typename Processor>
struct ProcessorWithBypass
{
Processor processor;
bool isBypassed = false;
};
std::tuple<ProcessorWithBypass<Processors>...> processors;
std::tuple<Processors...> processors;
std::array<bool, sizeof...(Processors)> bypassed { {} };
};
/** Non-member equivalent of ProcessorChain::get which avoids awkward

View file

@ -0,0 +1,142 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace dsp
{
class ProcessorChainTest : public UnitTest
{
template <int AddValue>
struct MockProcessor
{
void prepare (const ProcessSpec&) noexcept { isPrepared = true; }
void reset() noexcept { isReset = true; }
template <typename Context>
void process (const Context& context) noexcept
{
bufferWasClear = context.getInputBlock().getSample (0, 0) == 0;
if (! context.isBypassed)
context.getOutputBlock().add (AddValue);
}
bool isPrepared = false;
bool isReset = false;
bool bufferWasClear = false;
};
public:
ProcessorChainTest()
: UnitTest ("ProcessorChain", UnitTestCategories::dsp) {}
void runTest() override
{
beginTest ("After calling setBypass, processor is bypassed");
{
ProcessorChain<MockProcessor<1>, MockProcessor<2>> chain;
setBypassed<0> (chain, true);
expect (isBypassed<0> (chain));
setBypassed<0> (chain, false);
expect (! isBypassed<0> (chain));
setBypassed<1> (chain, true);
expect (isBypassed<1> (chain));
setBypassed<1> (chain, false);
expect (! isBypassed<1> (chain));
}
beginTest ("After calling prepare, all processors are prepared");
{
ProcessorChain<MockProcessor<1>, MockProcessor<2>> chain;
expect (! get<0> (chain).isPrepared);
expect (! get<1> (chain).isPrepared);
chain.prepare (ProcessSpec{});
expect (get<0> (chain).isPrepared);
expect (get<1> (chain).isPrepared);
}
beginTest ("After calling reset, all processors are reset");
{
ProcessorChain<MockProcessor<1>, MockProcessor<2>> chain;
expect (! get<0> (chain).isReset);
expect (! get<1> (chain).isReset);
chain.reset();
expect (get<0> (chain).isReset);
expect (get<1> (chain).isReset);
}
beginTest ("After calling process, all processors contribute to processing");
{
ProcessorChain<MockProcessor<1>, MockProcessor<2>> chain;
AudioBuffer<float> buffer (1, 1);
AudioBlock<float> block (buffer);
ProcessContextReplacing<float> context (block);
block.clear();
chain.process (context);
expectEquals (buffer.getSample (0, 0), 3.0f);
expect (get<0> (chain).bufferWasClear);
expect (! get<1> (chain).bufferWasClear);
setBypassed<0> (chain, true);
block.clear();
chain.process (context);
expectEquals (buffer.getSample (0, 0), 2.0f);
expect (get<0> (chain).bufferWasClear);
expect (get<1> (chain).bufferWasClear);
setBypassed<1> (chain, true);
block.clear();
chain.process (context);
expectEquals (buffer.getSample (0, 0), 0.0f);
expect (get<0> (chain).bufferWasClear);
expect (get<1> (chain).bufferWasClear);
setBypassed<0> (chain, false);
block.clear();
chain.process (context);
expectEquals (buffer.getSample (0, 0), 1.0f);
expect (get<0> (chain).bufferWasClear);
expect (! get<1> (chain).bufferWasClear);
}
}
};
static ProcessorChainTest processorChainUnitTest;
} // namespace dsp
} // namespace juce