mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
ProcessorChain: Avoid runtime branching on Context::usesSeparateInputAndOutputBlocks
This commit is contained in:
parent
bd01175d55
commit
8407b5ea74
2 changed files with 57 additions and 23 deletions
|
|
@ -37,9 +37,8 @@ namespace detail
|
|||
{
|
||||
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)... }))
|
||||
{
|
||||
(void) std::initializer_list<int> { ((void) fn (std::get<Ix> (tuple), Ix), 0)... };
|
||||
(void) std::initializer_list<int> { ((void) fn (std::get<Ix> (tuple), std::integral_constant<size_t, Ix>()), 0)... };
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
@ -47,10 +46,16 @@ namespace detail
|
|||
|
||||
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>{})))
|
||||
{
|
||||
forEachInTuple (std::forward<Fn> (fn), std::forward<Tuple> (tuple), TupleIndexSequence<Tuple>{});
|
||||
}
|
||||
|
||||
// This could be a template variable, but that code causes an internal compiler error in MSVC 19.00.24215
|
||||
template <typename Context, size_t Ix>
|
||||
struct UseContextDirectly
|
||||
{
|
||||
static constexpr auto value = ! Context::usesSeparateInputAndOutputBlocks() || Ix == 0;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -80,40 +85,43 @@ public:
|
|||
/** Prepare all inner processors with the provided `ProcessSpec`. */
|
||||
void prepare (const ProcessSpec& spec)
|
||||
{
|
||||
detail::forEachInTuple ([&] (auto& proc, size_t) { proc.prepare (spec); }, processors);
|
||||
detail::forEachInTuple ([&] (auto& proc, auto) { proc.prepare (spec); }, processors);
|
||||
}
|
||||
|
||||
/** Reset all inner processors. */
|
||||
void reset()
|
||||
{
|
||||
detail::forEachInTuple ([] (auto& proc, size_t) { proc.reset(); }, processors);
|
||||
detail::forEachInTuple ([] (auto& proc, auto) { proc.reset(); }, processors);
|
||||
}
|
||||
|
||||
/** Process `context` through all inner processors in sequence. */
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) 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 = (bypassed[index] || context.isBypassed);
|
||||
|
||||
proc.process (replacingContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessContext contextCopy (context);
|
||||
contextCopy.isBypassed = (bypassed[index] || context.isBypassed);
|
||||
|
||||
proc.process (contextCopy);
|
||||
}
|
||||
}, processors);
|
||||
detail::forEachInTuple ([this, &context] (auto& proc, auto index) noexcept { this->processOne (context, proc, index); },
|
||||
processors);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Context, typename Proc, size_t Ix, std::enable_if_t<! detail::UseContextDirectly<Context, Ix>::value, int> = 0>
|
||||
void processOne (const Context& context, Proc& proc, std::integral_constant<size_t, Ix>) noexcept
|
||||
{
|
||||
jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels());
|
||||
ProcessContextReplacing<typename Context::SampleType> replacingContext (context.getOutputBlock());
|
||||
replacingContext.isBypassed = (bypassed[Ix] || context.isBypassed);
|
||||
|
||||
proc.process (replacingContext);
|
||||
}
|
||||
|
||||
template <typename Context, typename Proc, size_t Ix, std::enable_if_t<detail::UseContextDirectly<Context, Ix>::value, int> = 0>
|
||||
void processOne (const Context& context, Proc& proc, std::integral_constant<size_t, Ix>) noexcept
|
||||
{
|
||||
auto contextCopy = context;
|
||||
contextCopy.isBypassed = (bypassed[Ix] || context.isBypassed);
|
||||
|
||||
proc.process (contextCopy);
|
||||
}
|
||||
|
||||
std::tuple<Processors...> processors;
|
||||
std::array<bool, sizeof...(Processors)> bypassed { {} };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -132,6 +132,32 @@ public:
|
|||
expect (get<0> (chain).bufferWasClear);
|
||||
expect (! get<1> (chain).bufferWasClear);
|
||||
}
|
||||
|
||||
beginTest ("Chains with trailing items that only support replacing contexts can be built");
|
||||
{
|
||||
AudioBuffer<float> inBuf (1, 1), outBuf (1, 1);
|
||||
juce::dsp::AudioBlock<float> in (inBuf), out (outBuf);
|
||||
|
||||
struct OnlyReplacing
|
||||
{
|
||||
void prepare (const juce::dsp::ProcessSpec&) {}
|
||||
void process (const juce::dsp::ProcessContextReplacing<float>& c)
|
||||
{
|
||||
c.getOutputBlock().multiplyBy (2.0f);
|
||||
}
|
||||
void reset() {}
|
||||
};
|
||||
|
||||
{
|
||||
juce::dsp::ProcessorChain<juce::dsp::Gain<float>, OnlyReplacing, OnlyReplacing> c;
|
||||
juce::dsp::ProcessContextNonReplacing<float> context (in, out);
|
||||
get<0> (c).setGainLinear (1.0f);
|
||||
c.prepare (ProcessSpec{});
|
||||
inBuf.setSample (0, 0, 1.0f);
|
||||
c.process (context);
|
||||
expectEquals (outBuf.getSample (0, 0), 4.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue