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:
parent
0ac8552a63
commit
adbf5fc219
3 changed files with 156 additions and 19 deletions
|
|
@ -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
|
||||
|
|
|
|||
142
modules/juce_dsp/processors/juce_ProcessorChain_test.cpp
Normal file
142
modules/juce_dsp/processors/juce_ProcessorChain_test.cpp
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue