42template <
typename T,
typename... Processors>
45 static_assert(
sizeof...(Processors) > 0,
"ProcessorChain requires at least one processor.");
47 "Every ProcessorChain slot must satisfy the dspark::AudioProcessor concept "
48 "(prepare(AudioSpec), processBlock(AudioBufferView<T>) noexcept, reset() noexcept).");
57 std::apply([&spec](
auto&... procs) {
58 (procs.prepare(spec), ...);
68 processBlockImpl(buffer, std::index_sequence_for<Processors...>{});
76 std::apply([](
auto&... procs) {
87 template <std::
size_t Index>
88 [[nodiscard]]
auto&
get() noexcept
90 static_assert(Index <
sizeof...(Processors),
"Processor index out of bounds.");
91 return std::get<Index>(processors_);
100 template <std::
size_t Index>
101 [[nodiscard]]
const auto&
get() const noexcept
103 static_assert(Index <
sizeof...(Processors),
"Processor index out of bounds.");
104 return std::get<Index>(processors_);
110 [[nodiscard]]
static constexpr std::size_t
size() noexcept
112 return sizeof...(Processors);
130 return getLatencyImpl(std::index_sequence_for<Processors...>{});
146 template <std::
size_t Index>
149 static_assert(Index <
sizeof...(Processors),
"Index out of range");
150 bypassed_[Index].store(bypassed, std::memory_order_relaxed);
156 template <std::
size_t Index>
159 static_assert(Index <
sizeof...(Processors),
"Index out of range");
160 return bypassed_[Index].load(std::memory_order_relaxed);
164 template <std::size_t... Is>
165 [[nodiscard]]
int getLatencyImpl(std::index_sequence<Is...>)
const noexcept
167 return (getProcessorLatency<Is>() + ...);
170 template <std::
size_t I>
171 [[nodiscard]]
int getProcessorLatency() const noexcept
173 using P = std::tuple_element_t<I, std::tuple<Processors...>>;
174 if constexpr (
requires(
const P& p) { { p.getLatency() } -> std::convertible_to<int>; })
176 return static_cast<int>(std::get<I>(processors_).getLatency());
183 static_assert(!
requires(P& p) { p.getLatency(); },
184 "Processor getLatency() must be const and return a value convertible to int "
185 "to be summed by ProcessorChain::getLatency().");
190 template <std::size_t... Is>
191 void processBlockImpl(AudioBufferView<T> buffer, std::index_sequence<Is...>)
noexcept
194 ((!bypassed_[Is].load(std::memory_order_relaxed) ? std::get<Is>(processors_).processBlock(buffer) : (void)0), ...);
197 std::tuple<Processors...> processors_;
198 std::array<std::atomic<bool>,
sizeof...(Processors)> bypassed_ {};
Owning audio buffer and non-owning view for real-time DSP processing.
Describes the audio processing environment (sample rate, block size, channels).
C++20 concepts defining the strictly real-time DSP processor contract.
Non-owning view over audio channel data.
Compile-time chain of audio processors.
void prepare(const AudioSpec &spec)
Prepares all processors in order.
auto & get() noexcept
Accesses the processor at the given index.
bool isBypassed() const noexcept
Thread-safe read of the bypass state of a processor.
void reset() noexcept
Resets the internal state of all processors (e.g., clearing delay lines).
void processBlock(AudioBufferView< T > buffer) noexcept
Processes a buffer through all non-bypassed processors in order.
void setBypassed(bool bypassed) noexcept
Thread-safe toggle to bypass or enable a processor.
int getLatency() const noexcept
Returns the total latency in samples across all processors.
static constexpr std::size_t size() noexcept
Returns the number of processors in the chain.
const auto & get() const noexcept
Const access to the processor at the given index.
A type that can prepare, process audio blocks, and reset state.
Main namespace for the DSPark framework.
Describes the audio environment for a DSP processor.