43#include "AudioBuffer.h"
44#include "Oversampling.h"
45#include "Interpolation.h"
91 assert(tableSize >= 4);
98 tableSize = std::clamp(tableSize, 4, 1 << 20);
99 if (!(xMax > T(0))) xMax = T(8);
101 tableSize_ = tableSize;
102 xMax_ = std::max(xMax, T(0.001));
103 invRange_ = T(1) / (T(2) * xMax_);
107 table_.resize(
static_cast<size_t>(tableSize) + 3);
109 for (
int i = 0; i < tableSize; ++i)
111 T x = -xMax_ + T(2) * xMax_ *
static_cast<T
>(i) /
static_cast<T
>(tableSize - 1);
112 table_[
static_cast<size_t>(i + 1)] = func(x);
116 table_[0] = table_[1];
117 table_[
static_cast<size_t>(tableSize + 1)] = table_[
static_cast<size_t>(tableSize)];
118 table_[
static_cast<size_t>(tableSize + 2)] = table_[
static_cast<size_t>(tableSize)];
123 step_ = 2.0 *
static_cast<double>(xMax_) /
static_cast<double>(tableSize - 1);
124 integral_.assign(
static_cast<size_t>(tableSize), 0.0);
125 for (
int i = 0; i + 1 < tableSize; ++i)
126 integral_[
static_cast<size_t>(i + 1)] = integral_[
static_cast<size_t>(i)]
127 + step_ * segmentIntegral(i + 1, 1.0);
149 return std::clamp(x, -threshold, threshold);
160 if (x > T(1))
return T(2.0 / 3.0);
161 if (x < T(-1))
return T(-2.0 / 3.0);
162 return x - (x * x * x) / T(3);
173 return x >= T(0) ? std::tanh(x * T(1.2)) : std::tanh(x * T(0.8));
185 [[nodiscard]]
inline T
process(T input, T preGain = T(1), T postGain = T(1)) const noexcept
192 T driven = std::max(-xMax_, std::min(xMax_, input * preGain));
195 T pos = (driven + xMax_) * invRange_ *
static_cast<T
>(tableSize_ - 1);
197 int idx =
static_cast<int>(pos);
198 T frac = pos -
static_cast<T
>(idx);
201 const T* t = table_.data() + idx + 1;
218 void process(T* __restrict data,
int numSamples, T preGain = T(1), T postGain = T(1)) const noexcept
220 for (
int i = 0; i < numSamples; ++i)
221 data[i] =
process(data[i], preGain, postGain);
234 oversampler_->prepare(spec);
244 assert(factor >= 1 && (factor & (factor - 1)) == 0);
248 oversampler_ = std::make_unique<Oversampling<T>>(factor);
252 oversamplingFactor_ = oversampler_->getFactor();
254 oversampler_->prepare(spec_);
258 oversamplingFactor_ = 1;
259 oversampler_.reset();
273 const bool adaa = antialias_;
275 auto shape = [&](T* data,
int n,
int ch) {
276 if (adaa && ch < kAntialiasChannels)
277 processAntialiased(data, n, preGain, postGain, ch);
279 process(data, n, preGain, postGain);
282 if (oversamplingFactor_ > 1 && oversampler_)
284 auto upView = oversampler_->upsample(buffer);
285 for (
int ch = 0; ch < upView.getNumChannels(); ++ch)
286 shape(upView.getChannel(ch), upView.getNumSamples(), ch);
287 oversampler_->downsample(buffer);
315 if (enabled && !antialias_) resetAntialiasing();
316 antialias_ = enabled;
324 if (oversampler_) oversampler_->reset();
329 [[nodiscard]]
bool isReady() const noexcept {
return tableSize_ > 0; }
341 return (oversampler_ && oversamplingFactor_ > 1) ? oversampler_->getLatency() : 0;
345 static constexpr int kAntialiasChannels = 16;
349 [[nodiscard]]
double segmentIntegral(
int i,
double u)
const noexcept
351 const double y0 = table_[
static_cast<size_t>(i - 1)];
352 const double y1 = table_[
static_cast<size_t>(i)];
353 const double y2 = table_[
static_cast<size_t>(i + 1)];
354 const double y3 = table_[
static_cast<size_t>(i + 2)];
356 const double c = (y2 - y0) * 0.5;
357 const double v = y1 - y2;
358 const double w = c + v;
359 const double a = w + v + (y3 - y1) * 0.5;
360 const double b = w + a;
361 return (((a * 0.25 * u - b / 3.0) * u + c * 0.5) * u + y1) * u;
366 [[nodiscard]]
double antiderivative(
double x)
const noexcept
368 const double xMax =
static_cast<double>(xMax_);
369 const int last = tableSize_ - 1;
371 return integral_[
static_cast<size_t>(last)]
372 +
static_cast<double>(table_[
static_cast<size_t>(last + 1)]) * (x - xMax);
374 return static_cast<double>(table_[1]) * (x + xMax);
375 const double pos = (x + xMax) / step_;
376 const int idx = std::min(
static_cast<int>(pos), last - 1);
377 return integral_[
static_cast<size_t>(idx)] + step_ * segmentIntegral(idx + 1, pos - idx);
380 void processAntialiased(T* data,
int numSamples, T preGain, T postGain,
int ch)
noexcept
384 const double limit = 1.0e3 *
static_cast<double>(xMax_);
385 const auto c =
static_cast<size_t>(ch);
386 double x0 = adaaX_[c];
387 double f0 = adaaF_[c];
388 const double post =
static_cast<double>(postGain);
389 for (
int i = 0; i < numSamples; ++i)
391 const double x = std::max(-limit, std::min(limit,
static_cast<double>(data[i] * preGain)));
392 const double f = antiderivative(x);
397 adaaPrimed_[c] =
true;
399 const double dx = x - x0;
401 if (std::abs(dx) > 1.0e-6)
404 y =
static_cast<double>(
process(
static_cast<T
>(0.5 * (x + x0))));
405 data[i] =
static_cast<T
>(y * post);
413 void resetAntialiasing() noexcept { adaaPrimed_.fill(
false); }
415 std::vector<T> table_;
416 std::vector<double> integral_;
418 bool antialias_ =
false;
419 std::array<double, kAntialiasChannels> adaaX_ {};
420 std::array<double, kAntialiasChannels> adaaF_ {};
421 std::array<bool, kAntialiasChannels> adaaPrimed_ {};
424 T invRange_ = T(1) / T(16);
427 std::unique_ptr<Oversampling<T>> oversampler_;
428 int oversamplingFactor_ = 1;
Non-owning view over audio channel data.
int getNumSamples() const noexcept
Returns the number of samples per channel.
int getNumChannels() const noexcept
Returns the number of channels in this view.
T * getChannel(int ch) const noexcept
Returns a pointer to the sample data for the given channel.
Zero-latency lookup-table waveshaper with RT-safe gain modulation.
void buildTanh(int tableSize=4096)
Builds a normalized tanh (soft clip) table.
bool isAntialiasingEnabled() const noexcept
True when processBlock() applies ADAA.
int getLatency() const noexcept
Reports the processing latency in samples.
void buildSoftClip(int tableSize=4096)
Builds a cubic soft-clip table.
int getTableSize() const noexcept
T process(T input, T preGain=T(1), T postGain=T(1)) const noexcept
Processes a single sample through the waveshaper with Hermite interpolation.
void buildAsymmetric(int tableSize=4096)
Builds an asymmetric clipping table for even-harmonic generation.
void prepare(const AudioSpec &spec)
Prepares the waveshaper for oversampled block processing.
void buildHardClip(T threshold=T(0.8), int tableSize=4096)
Builds a hard-clip table.
WaveshapeTable()
Constructor. Initializes a safe passthrough table to prevent RT crashes.
bool isReady() const noexcept
void buildFromFunction(std::function< T(T)> func, int tableSize=4096, T xMax=T(8))
Builds the lookup table from an arbitrary transfer function.
void setAntialiasing(bool enabled) noexcept
Enables first-order antiderivative anti-aliasing (ADAA) in processBlock().
void setOversampling(int factor)
Enables oversampling.
void processBlock(AudioBufferView< T > buffer, T preGain=T(1), T postGain=T(1)) noexcept
Processes a buffer view with optional oversampling.
void process(T *__restrict data, int numSamples, T preGain=T(1), T postGain=T(1)) const noexcept
Processes a buffer in-place.
T getInputRange() const noexcept
Returns the half-range of the table's input domain.
int getOversamplingFactor() const noexcept
Main namespace for the DSPark framework.
T interpolateHermite(T y0, T y1, T y2, T y3, T frac) noexcept
4-point, 3rd-order Hermite interpolation (optimized x-form).
Describes the audio environment for a DSP processor.
double sampleRate
Sample rate in Hz.