31#include "CrossoverFilter.h"
32#include "Compressor.h"
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);
103 if (!prepared_.load(std::memory_order_relaxed))
return;
105 const int total = buffer.getNumSamples();
106 const int chunk = bandBuffers_[0].getNumSamples();
107 if (chunk <= 0)
return;
108 for (
int offset = 0; offset < total; offset += chunk)
109 processChunk(buffer.getSubView(offset, std::min(chunk, total - offset)));
119 const int nCh = std::min(buffer.getNumChannels(), bandBuffers_[0].getNumChannels());
120 const int nS = std::min(buffer.getNumSamples(), bandBuffers_[0].getNumSamples());
121 if (nCh <= 0 || nS <= 0)
return;
128 for (
int b = 0; b < MaxBands; ++b)
129 views_[b] = bandBuffers_[b].toView().getSubView(0, nS);
132 const int nb = crossover_.processBlock(buffer, views_.data(), MaxBands);
138 if (nb > lastNumBands_)
139 for (
int b = lastNumBands_; b < nb; ++b)
140 compressors_[b].
reset();
151 for (
int b = 0; b < nb; ++b)
155 for (
int ch = 0; ch < nCh; ++ch)
157 T*
const __restrict out = buffer.getChannel(ch);
158 const T*
const __restrict src0 = bandBuffers_[0].getChannel(ch);
161 std::copy(src0, src0 + nS, out);
164 for (
int b = 1; b < nb; ++b)
165 simd::add(out, bandBuffers_[b].getChannel(ch), nS);
186 crossover_.setNumBands(std::clamp(n, 2, MaxBands));
196 crossover_.setCrossoverFrequency(index, freqHz);
205 crossover_.setOrder(order);
214 crossover_.setFilterMode(mode);
226 assert(band >= 0 && band < MaxBands);
227 int safeBand = std::clamp(band, 0, MaxBands - 1);
228 return compressors_[safeBand];
238 assert(band >= 0 && band < MaxBands);
239 int safeBand = std::clamp(band, 0, MaxBands - 1);
240 return compressors_[safeBand];
252 if (band >= 0 && band < MaxBands)
253 compressors_[band].setThreshold(dB);
263 if (band >= 0 && band < MaxBands)
264 compressors_[band].setRatio(ratio);
274 if (band >= 0 && band < MaxBands)
275 compressors_[band].setAttack(ms);
285 if (band >= 0 && band < MaxBands)
286 compressors_[band].setRelease(ms);
302 if (band < 0 || band >= MaxBands)
return T(0);
303 return compressors_[band].getGainReductionDb();
307 [[nodiscard]]
int getNumBands() const noexcept {
return crossover_.getNumBands(); }
312 return crossover_.getCrossoverFrequency(index);
316 [[nodiscard]]
int getOrder() const noexcept {
return crossover_.getOrder(); }
321 return crossover_.getFilterMode();
330 const int nb = crossover_.getNumBands();
331 for (
int b = 0; b < nb && b < MaxBands; ++b)
332 maxBand = std::max(maxBand, compressors_[b].
getLatency());
333 return crossover_.getLatency() + maxBand;
340 for (
auto& c : compressors_) c.reset();
341 lastNumBands_ = MaxBands;
345 [[nodiscard]] std::vector<uint8_t>
getState()
const
348 const int n = crossover_.getNumBands();
349 w.
write(
"numBands", n);
350 w.
write(
"order", crossover_.getOrder());
351 w.
write(
"xoverMode",
static_cast<int32_t
>(crossover_.getFilterMode()));
353 for (
int i = 0; i < n - 1; ++i)
355 std::snprintf(key,
sizeof(key),
"x%d", i);
356 w.
write(key,
static_cast<float>(crossover_.getCrossoverFrequency(i)));
358 for (
int i = 0; i < n; ++i)
360 std::snprintf(key,
sizeof(key),
"band%d", i);
371 const int n = std::clamp(r.
read(
"numBands", 3), 2, MaxBands);
375 r.
read(
"xoverMode", 0)));
377 for (
int i = 0; i < n - 1; ++i)
379 std::snprintf(key,
sizeof(key),
"x%d", i);
380 const float f = r.
read(key, -1.0f);
383 for (
int i = 0; i < n; ++i)
385 std::snprintf(key,
sizeof(key),
"band%d", i);
386 const auto nested = r.
readBlob(key);
394 std::atomic<bool> prepared_ {
false };
395 int lastNumBands_ = MaxBands;
396 CrossoverFilter<T, MaxBands> crossover_;
397 std::array<Compressor<T>, MaxBands> compressors_ {};
398 std::array<AudioBuffer<T>, MaxBands> bandBuffers_ {};
399 std::array<AudioBufferView<T>, MaxBands> views_ {};
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.