|
|
@@ -29,109 +29,135 @@ namespace juce |
|
|
|
namespace dsp
|
|
|
|
{
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
#ifndef DOXYGEN
|
|
|
|
namespace ProcessorHelpers // Internal helper classes used in building the ProcessorChain
|
|
|
|
/** The contents of this namespace are used to implement ProcessorChain and should
|
|
|
|
not be used elsewhere. Their interfaces (and existence) are liable to change!
|
|
|
|
*/
|
|
|
|
namespace detail
|
|
|
|
{
|
|
|
|
template <int arg>
|
|
|
|
struct AccessHelper
|
|
|
|
template <typename Fn, typename Tuple, size_t... Ix>
|
|
|
|
constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple, std::index_sequence<Ix...>)
|
|
|
|
noexcept (noexcept (std::initializer_list<int> { (fn (std::get<Ix> (tuple), Ix), 0)... }))
|
|
|
|
{
|
|
|
|
template <typename ProcessorType>
|
|
|
|
static auto& get (ProcessorType& a) noexcept { return AccessHelper<arg - 1>::get (a.processors); }
|
|
|
|
|
|
|
|
template <typename ProcessorType>
|
|
|
|
static const auto& get (const ProcessorType& a) noexcept { return AccessHelper<arg - 1>::get (a.processors); }
|
|
|
|
(void) std::initializer_list<int> { ((void) fn (std::get<Ix> (tuple), Ix), 0)... };
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename ProcessorType>
|
|
|
|
static void setBypassed (ProcessorType& a, bool bypassed) { AccessHelper<arg - 1>::setBypassed (a.processors, bypassed); }
|
|
|
|
};
|
|
|
|
template <typename T>
|
|
|
|
using TupleIndexSequence = std::index_sequence_for<std::remove_cv_t<std::remove_reference_t<T>>>;
|
|
|
|
|
|
|
|
template <>
|
|
|
|
struct AccessHelper<0>
|
|
|
|
template <typename Fn, typename Tuple>
|
|
|
|
constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple)
|
|
|
|
noexcept (noexcept (forEachInTuple (std::forward<Fn> (fn), std::forward<Tuple> (tuple), TupleIndexSequence<Tuple>{})))
|
|
|
|
{
|
|
|
|
template <typename ProcessorType>
|
|
|
|
static auto& get (ProcessorType& a) noexcept { return a.getProcessor(); }
|
|
|
|
forEachInTuple (std::forward<Fn> (fn), std::forward<Tuple> (tuple), TupleIndexSequence<Tuple>{});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
template <typename ProcessorType>
|
|
|
|
static const auto& get (const ProcessorType& a) noexcept { return a.getProcessor(); }
|
|
|
|
/** This variadically-templated class lets you join together any number of processor
|
|
|
|
classes into a single processor which will call process() on them all in sequence.
|
|
|
|
*/
|
|
|
|
template <typename... Processors>
|
|
|
|
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 <typename ProcessorType>
|
|
|
|
static void setBypassed (ProcessorType& a, bool bypassed) { a.isBypassed = bypassed; }
|
|
|
|
};
|
|
|
|
/** Get a reference to the processor at index `Index`. */
|
|
|
|
template <int Index> const auto& get() const noexcept { return std::get<Index> (processors).processor; }
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
template <bool isFirst, typename Processor, typename Subclass>
|
|
|
|
struct ChainElement
|
|
|
|
/** 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; }
|
|
|
|
|
|
|
|
/** Query whether the processor at index `Index` is bypassed. */
|
|
|
|
template <int Index>
|
|
|
|
bool isBypassed() const noexcept { return std::get<Index> (processors).isBypassed; }
|
|
|
|
|
|
|
|
/** Prepare all inner processors with the provided `ProcessSpec`. */
|
|
|
|
void prepare (const ProcessSpec& spec)
|
|
|
|
{
|
|
|
|
void prepare (const ProcessSpec& spec)
|
|
|
|
{
|
|
|
|
processor.prepare (spec);
|
|
|
|
}
|
|
|
|
detail::forEachInTuple ([&] (auto& item, size_t) { item.processor.prepare (spec); }, processors);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Reset all inner processors. */
|
|
|
|
void reset()
|
|
|
|
{
|
|
|
|
detail::forEachInTuple ([] (auto& item, size_t) { item.processor.reset(); }, processors);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename ProcessContext>
|
|
|
|
void process (const ProcessContext& context) noexcept
|
|
|
|
/** 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
|
|
|
|
{
|
|
|
|
if (context.usesSeparateInputAndOutputBlocks() && ! isFirst)
|
|
|
|
if (context.usesSeparateInputAndOutputBlocks() && index != 0)
|
|
|
|
{
|
|
|
|
jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels());
|
|
|
|
ProcessContextReplacing<typename ProcessContext::SampleType> replacingContext (context.getOutputBlock());
|
|
|
|
replacingContext.isBypassed = (isBypassed || context.isBypassed);
|
|
|
|
replacingContext.isBypassed = (item.isBypassed || context.isBypassed);
|
|
|
|
|
|
|
|
processor.process (replacingContext);
|
|
|
|
item.processor.process (replacingContext);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ProcessContext contextCopy (context);
|
|
|
|
contextCopy.isBypassed = (isBypassed || context.isBypassed);
|
|
|
|
contextCopy.isBypassed = (item.isBypassed || context.isBypassed);
|
|
|
|
|
|
|
|
processor.process (contextCopy);
|
|
|
|
item.processor.process (contextCopy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset()
|
|
|
|
{
|
|
|
|
processor.reset();
|
|
|
|
}
|
|
|
|
}, processors);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isBypassed = false;
|
|
|
|
private:
|
|
|
|
template <typename Processor>
|
|
|
|
struct ProcessorWithBypass
|
|
|
|
{
|
|
|
|
Processor processor;
|
|
|
|
|
|
|
|
Processor& getProcessor() noexcept { return processor; }
|
|
|
|
const Processor& getProcessor() const noexcept { return processor; }
|
|
|
|
Subclass& getThis() noexcept { return *static_cast<Subclass*> (this); }
|
|
|
|
const Subclass& getThis() const noexcept { return *static_cast<const Subclass*> (this); }
|
|
|
|
|
|
|
|
template <int arg> auto& get() noexcept { return AccessHelper<arg>::get (getThis()); }
|
|
|
|
template <int arg> const auto& get() const noexcept { return AccessHelper<arg>::get (getThis()); }
|
|
|
|
template <int arg> void setBypassed (bool bypassed) noexcept { AccessHelper<arg>::setBypassed (getThis(), bypassed); }
|
|
|
|
bool isBypassed = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
template <bool isFirst, typename FirstProcessor, typename... SubsequentProcessors>
|
|
|
|
struct ChainBase : public ChainElement<isFirst, FirstProcessor, ChainBase<isFirst, FirstProcessor, SubsequentProcessors...>>
|
|
|
|
{
|
|
|
|
using Base = ChainElement<isFirst, FirstProcessor, ChainBase<isFirst, FirstProcessor, SubsequentProcessors...>>;
|
|
|
|
|
|
|
|
template <typename ProcessContext>
|
|
|
|
void process (const ProcessContext& context) noexcept { Base::process (context); processors.process (context); }
|
|
|
|
void prepare (const ProcessSpec& spec) { Base::prepare (spec); processors.prepare (spec); }
|
|
|
|
void reset() { Base::reset(); processors.reset(); }
|
|
|
|
std::tuple<ProcessorWithBypass<Processors>...> processors;
|
|
|
|
};
|
|
|
|
|
|
|
|
ChainBase<false, SubsequentProcessors...> processors;
|
|
|
|
};
|
|
|
|
/** Non-member equivalent of ProcessorChain::get which avoids awkward
|
|
|
|
member template syntax.
|
|
|
|
*/
|
|
|
|
template <int Index, typename... Processors>
|
|
|
|
inline auto& get (ProcessorChain<Processors...>& chain) noexcept
|
|
|
|
{
|
|
|
|
return chain.template get<Index>();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <bool isFirst, typename ProcessorType>
|
|
|
|
struct ChainBase<isFirst, ProcessorType> : public ChainElement<isFirst, ProcessorType, ChainBase<isFirst, ProcessorType>> {};
|
|
|
|
/** Non-member equivalent of ProcessorChain::get which avoids awkward
|
|
|
|
member template syntax.
|
|
|
|
*/
|
|
|
|
template <int Index, typename... Processors>
|
|
|
|
inline auto& get (const ProcessorChain<Processors...>& chain) noexcept
|
|
|
|
{
|
|
|
|
return chain.template get<Index>();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/** Non-member equivalent of ProcessorChain::setBypassed which avoids awkward
|
|
|
|
member template syntax.
|
|
|
|
*/
|
|
|
|
template <int Index, typename... Processors>
|
|
|
|
inline void setBypassed (ProcessorChain<Processors...>& chain, bool bypassed) noexcept
|
|
|
|
{
|
|
|
|
chain.template setBypassed<Index> (bypassed);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
/**
|
|
|
|
This variadically-templated class lets you join together any number of processor
|
|
|
|
classes into a single processor which will call process() on them all in sequence.
|
|
|
|
/** Non-member equivalent of ProcessorChain::isBypassed which avoids awkward
|
|
|
|
member template syntax.
|
|
|
|
*/
|
|
|
|
template <typename... Processors>
|
|
|
|
using ProcessorChain = ProcessorHelpers::ChainBase<true, Processors...>;
|
|
|
|
template <int Index, typename... Processors>
|
|
|
|
inline bool isBypassed (const ProcessorChain<Processors...>& chain) noexcept
|
|
|
|
{
|
|
|
|
return chain.template isBypassed<Index>();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace dsp
|
|
|
|
} // namespace juce
|