33#include "../Core/AudioBuffer.h"
34#include "../Core/AudioSpec.h"
35#include "../Core/DspMath.h"
36#include "../Core/SimdOps.h"
37#include "../Core/StateBlob.h"
57template <FloatType T,
int MaxBands = 12>
60 static_assert(MaxBands >= 2,
"MultibandCompressor needs at least 2 bands (one split)");
78 prepared_.store(
false, std::memory_order_relaxed);
80 crossover_.prepare(spec);
81 for (
int b = 0; b < MaxBands; ++b)
84 compressors_[b].prepare(spec);
86 lastNumBands_ = MaxBands;
88 prepared_.store(
true, std::memory_order_relaxed);
102 if (!prepared_.load(std::memory_order_relaxed))
return;
107 const int nCh = std::min(buffer.getNumChannels(), bandBuffers_[0].getNumChannels());
108 const int nS = std::min(buffer.getNumSamples(), bandBuffers_[0].getNumSamples());
109 if (nCh <= 0 || nS <= 0)
return;
116 for (
int b = 0; b < MaxBands; ++b)
117 views_[b] = bandBuffers_[b].toView().getSubView(0, nS);
120 const int nb = crossover_.processBlock(buffer, views_.data(), MaxBands);
126 if (nb > lastNumBands_)
127 for (
int b = lastNumBands_; b < nb; ++b)
128 compressors_[b].
reset();
139 for (
int b = 0; b < nb; ++b)
143 for (
int ch = 0; ch < nCh; ++ch)
145 T*
const __restrict out = buffer.getChannel(ch);
146 const T*
const __restrict src0 = bandBuffers_[0].getChannel(ch);
149 std::copy(src0, src0 + nS, out);
152 for (
int b = 1; b < nb; ++b)
153 simd::add(out, bandBuffers_[b].getChannel(ch), nS);
173 crossover_.setNumBands(std::clamp(n, 2, MaxBands));
183 crossover_.setCrossoverFrequency(index, freqHz);
192 crossover_.setOrder(order);
201 crossover_.setFilterMode(mode);
213 assert(band >= 0 && band < MaxBands);
214 int safeBand = std::clamp(band, 0, MaxBands - 1);
215 return compressors_[safeBand];
225 assert(band >= 0 && band < MaxBands);
226 int safeBand = std::clamp(band, 0, MaxBands - 1);
227 return compressors_[safeBand];
239 if (band >= 0 && band < MaxBands)
240 compressors_[band].setThreshold(dB);
250 if (band >= 0 && band < MaxBands)
251 compressors_[band].setRatio(ratio);
261 if (band >= 0 && band < MaxBands)
262 compressors_[band].setAttack(ms);
272 if (band >= 0 && band < MaxBands)
273 compressors_[band].setRelease(ms);
289 if (band < 0 || band >= MaxBands)
return T(0);
290 return compressors_[band].getGainReductionDb();
294 [[nodiscard]]
int getNumBands() const noexcept {
return crossover_.getNumBands(); }
299 return crossover_.getCrossoverFrequency(index);
303 [[nodiscard]]
int getOrder() const noexcept {
return crossover_.getOrder(); }
308 return crossover_.getFilterMode();
317 const int nb = crossover_.getNumBands();
318 for (
int b = 0; b < nb && b < MaxBands; ++b)
319 maxBand = std::max(maxBand, compressors_[b].
getLatency());
320 return crossover_.getLatency() + maxBand;
327 for (
auto& c : compressors_) c.reset();
328 lastNumBands_ = MaxBands;
332 [[nodiscard]] std::vector<uint8_t>
getState()
const
335 const int n = crossover_.getNumBands();
336 w.
write(
"numBands", n);
337 w.
write(
"order", crossover_.getOrder());
338 w.
write(
"xoverMode",
static_cast<int32_t
>(crossover_.getFilterMode()));
340 for (
int i = 0; i < n - 1; ++i)
342 std::snprintf(key,
sizeof(key),
"x%d", i);
343 w.
write(key,
static_cast<float>(crossover_.getCrossoverFrequency(i)));
345 for (
int i = 0; i < n; ++i)
347 std::snprintf(key,
sizeof(key),
"band%d", i);
358 const int n = std::clamp(r.
read(
"numBands", 3), 2, MaxBands);
362 r.
read(
"xoverMode", 0)));
364 for (
int i = 0; i < n - 1; ++i)
366 std::snprintf(key,
sizeof(key),
"x%d", i);
367 const float f = r.
read(key, -1.0f);
370 for (
int i = 0; i < n; ++i)
372 std::snprintf(key,
sizeof(key),
"band%d", i);
373 const auto nested = r.
readBlob(key);
381 std::atomic<bool> prepared_ {
false };
382 int lastNumBands_ = MaxBands;
383 CrossoverFilter<T, MaxBands> crossover_;
384 std::array<Compressor<T>, MaxBands> compressors_ {};
385 std::array<AudioBuffer<T>, MaxBands> bandBuffers_ {};
386 std::array<AudioBufferView<T>, MaxBands> views_ {};
Modular dynamic range compressor with analog-modeled ballistics and Hilbert detection.
Linkwitz-Riley crossover filter for multi-band audio processing.
Non-owning view over audio channel data.
High-fidelity modular compressor designed for real-time applications.
FilterMode
Filter processing mode.
Multi-band compressor: crossover split, per-band compression, sum.
void setNumBands(int n) noexcept
Sets the number of active frequency bands.
void setBandRatio(int band, T ratio) noexcept
Sets the ratio for a specific band.
const Compressor< T > & getBandCompressor(int band) const noexcept
Direct constant access to a band's compressor for state queries.
void setOrder(int order) noexcept
Sets the crossover slope in dB/oct: 12, 24 or 48.
void processBlock(AudioBufferView< T > buffer) noexcept
Processes audio through the multi-band compressor.
bool setState(const uint8_t *data, size_t size)
Restores topology and band compressors from a blob.
void setCrossoverFrequency(int index, T freqHz) noexcept
Sets the crossover frequency for a specific split point.
std::vector< uint8_t > getState() const
Serializes crossover topology and per-band compressor states.
void setCrossoverMode(typename CrossoverFilter< T, MaxBands >::FilterMode mode) noexcept
Sets the phase/processing mode of the crossover (IIR or linear-phase).
void setBandThreshold(int band, T dB) noexcept
Sets the threshold for a specific band.
T getCrossoverFrequency(int index) const noexcept
Returns the target frequency of split point index in Hz.
int getLatency() const noexcept
Returns the total latency of the multi-band system.
int getNumBands() const noexcept
Returns the current number of active bands.
void setBandAttack(int band, T ms) noexcept
Sets the attack time for a specific band.
void prepare(const AudioSpec &spec)
Prepares the multiband compressor and internal buffers for processing.
T getBandGainReductionDb(int band) const noexcept
Gets the current gain reduction applied to a specific band.
Compressor< T > & getBandCompressor(int band) noexcept
Direct access to a band's compressor for full configuration.
void reset() noexcept
Resets all internal states (envelopes, delay lines, etc.).
CrossoverFilter< T, MaxBands >::FilterMode getCrossoverMode() const noexcept
Returns the crossover processing mode (IIR or linear-phase).
int getOrder() const noexcept
Returns the crossover slope in dB/oct (12, 24 or 48).
void setBandRelease(int band, T ms) noexcept
Sets the release time for a specific band.
Tolerant reader: missing keys yield defaults, unknown keys are skipped.
std::vector< uint8_t > readBlob(const char *key) const
Reads a nested blob; empty when the key is absent.
float read(const char *key, float defaultValue) const
Reads a float, or defaultValue when the key is absent.
bool isValid() const noexcept
uint32_t processorId() const noexcept
Serializes key/value parameters into a versioned blob.
std::vector< uint8_t > blob() const
Finalizes and returns the blob.
void write(const char *key, float value)
Writes a float parameter.
void add(float *DSPARK_RESTRICT dst, const float *DSPARK_RESTRICT src, int count) noexcept
Adds source samples into a destination buffer (no scaling).
Main namespace for the DSPark framework.
constexpr uint32_t stateId(const char(&tag)[5]) noexcept
Builds a FOURCC processor id, e.g. dspark::stateId("COMP").
Describes the audio environment for a DSP processor.
constexpr bool isValid() const noexcept
Checks if the specification contains valid, processable parameters.
int numChannels
Number of audio channels (e.g., 1 = mono, 2 = stereo).
int maxBlockSize
Maximum number of samples per processing block.