30#include "../Core/DspMath.h"
31#include "../Core/AudioSpec.h"
32#include "../Core/AudioBuffer.h"
33#include "../Core/StateBlob.h"
63 if (!(sampleRate > 0.0) || !std::isfinite(sampleRate))
return;
64 sampleRate_ = sampleRate;
65 widthMaxStep_ =
static_cast<T
>(1.0 / (sampleRate * 0.02));
79 if (buffer.getNumChannels() >= 2)
80 process(buffer.getChannel(0), buffer.getChannel(1), buffer.getNumSamples());
91 if (!std::isfinite(width))
return;
92 width_.store(std::max(T(0), width), std::memory_order_relaxed);
96 [[nodiscard]] T
getWidth() const noexcept {
return width_.load(std::memory_order_relaxed); }
108 if (cutoffHz > 0.0 && std::isfinite(cutoffHz))
110 bassMonoCutoff_.store(cutoffHz, std::memory_order_relaxed);
113 bassMonoEnabled_.store(enabled, std::memory_order_release);
122 void process(T* left, T* right,
int numSamples)
noexcept
124 if (numSamples <= 0)
return;
129 const T widthTarget = width_.load(std::memory_order_relaxed);
132 currentWidth_ = widthTarget;
135 const T widthStart = currentWidth_;
136 const T widthStep = widthMaxStep_;
137 const bool bassMono = bassMonoEnabled_.load(std::memory_order_acquire);
141 const T coeff = bassMonoCoeff_.load(std::memory_order_relaxed);
149 for (
int i = 0; i < numSamples; ++i)
154 const T width =
moveTowards(widthStart, widthTarget, widthStep *
static_cast<T
>(i + 1));
155 T mid = (l + r) * T(0.5);
156 T side = (l - r) * T(0.5) * width;
160 sideState_ += coeff * (sideLpIn - sideState_) + antiDenormal_;
161 sideState_ -= antiDenormal_;
162 side = sideLpIn - sideState_;
164 left[i] = mid + side;
165 right[i] = mid - side;
171 for (
int i = 0; i < numSamples; ++i)
173 const T width =
moveTowards(widthStart, widthTarget, widthStep *
static_cast<T
>(i + 1));
174 T mid = (left[i] + right[i]) * T(0.5);
175 T side = (left[i] - right[i]) * T(0.5) * width;
177 left[i] = mid + side;
178 right[i] = mid - side;
181 currentWidth_ =
moveTowards(widthStart, widthTarget, widthStep *
static_cast<T
>(numSamples));
193 [[nodiscard]] std::vector<uint8_t>
getState()
const
196 w.
write(
"width", width_.load(std::memory_order_relaxed));
197 w.
write(
"bassMono", bassMonoEnabled_.load(std::memory_order_relaxed));
198 w.
write(
"bassCutoff",
static_cast<float>(bassMonoCutoff_.load(std::memory_order_relaxed)));
209 static_cast<double>(r.
read(
"bassCutoff", 100.0f)));
216 if (sampleRate_ > 0.0)
219 T coeff =
static_cast<T
>(1.0 - std::exp(-std::numbers::pi * 2.0 * cutoff / sampleRate_));
220 bassMonoCoeff_.store(coeff, std::memory_order_relaxed);
225 double sampleRate_ = 48000.0;
228 std::atomic<T> width_ { T(1) };
229 std::atomic<bool> bassMonoEnabled_ {
false };
230 std::atomic<double> bassMonoCutoff_ { 100.0 };
231 std::atomic<T> bassMonoCoeff_ { T(0) };
237 T currentWidth_ = T(1);
238 T widthMaxStep_ = T(1.0 / 960.0);
239 bool snapWidth_ =
true;
242 static constexpr T antiDenormal_ =
static_cast<T
>(1e-15);
Non-owning view over audio channel data.
Tolerant reader: missing keys yield defaults, unknown keys are skipped.
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.
High-performance stereo image processor with phase-aligned bass mono.
void setWidth(T width) noexcept
Sets the overall stereo width factor.
void processBlock(AudioBufferView< T > buffer) noexcept
Processes an AudioBufferView in-place.
void process(T *left, T *right, int numSamples) noexcept
Process a full block of audio. Optimized for SIMD vectorization.
void prepare(const AudioSpec &spec) noexcept
Prepares from AudioSpec (unified API).
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
T getWidth() const noexcept
Returns current width setting.
void prepare(double sampleRate) noexcept
Prepares the processor and resets internal states.
void setBassMono(bool enabled, double cutoffHz=100.0) noexcept
Toggles Bass Mono and updates the crossover frequency.
void updateBassMonoCoeff(double cutoff) noexcept
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
void reset() noexcept
Clears the internal filter states to prevent artifact ringing.
Main namespace for the DSPark framework.
T moveTowards(T from, T to, T maxDelta) noexcept
Moves a value toward a target by at most a given distance.
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.