36#include "../Core/DspMath.h"
37#include "../Core/AudioSpec.h"
38#include "../Core/AudioBuffer.h"
39#include "../Core/DenormalGuard.h"
40#include "../Core/StateBlob.h"
76 if (!spec.isValid())
return;
89 if (!std::isfinite(sampleRate) || sampleRate <= 0.0)
return;
90 sampleRate_ = sampleRate;
102 const int nCh = std::min(buffer.getNumChannels(), kMaxChannels);
103 const int nS = buffer.getNumSamples();
105 const T attAmt = attackAmount_.load(std::memory_order_relaxed);
106 const T susAmt = sustainAmount_.load(std::memory_order_relaxed);
107 const bool odr = outputDepRecovery_.load(std::memory_order_relaxed);
110 constexpr T noiseFloor = T(1e-5);
111 constexpr T maxGainLog = T(2.77258);
114 for (
int ch = 0; ch < nCh; ++ch)
116 T*
const channelData = buffer.getChannel(ch);
117 T fast = envFast_[ch];
118 T slow = envSlow_[ch];
119 T lastOut = lastOutput_[ch];
122 for (
int i = 0; i < nS; ++i)
124 T sample = channelData[i];
125 T absSample = std::abs(sample) + noiseFloor;
128 T fastCoeff = (absSample > fast) ? fastAttackCoeff_ : fastReleaseCoeff_;
129 fast += fastCoeff * (absSample - fast);
132 T currentSlowRelCoeff = slowReleaseCoeff_;
137 T modifier = T(1) + std::abs(lastOut) * T(2.0);
138 currentSlowRelCoeff = std::min(fastReleaseCoeff_, currentSlowRelCoeff * modifier);
141 T slowCoeff = (absSample > slow) ? slowAttackCoeff_ : currentSlowRelCoeff;
142 slow += slowCoeff * (absSample - slow);
149 T gainLog = (diffLog > T(0)) ? (diffLog * attAmt) : (-diffLog * susAmt);
151 gainLog = std::clamp(gainLog, -maxGainLog, maxGainLog);
155 lastOut = sample * gain;
156 channelData[i] = lastOut;
162 lastOutput_[ch] = lastOut;
178 if (!std::isfinite(amount))
return;
179 attackAmount_.store(std::clamp(amount, T(-100), T(100)) / T(100), std::memory_order_relaxed);
188 if (!std::isfinite(amount))
return;
189 sustainAmount_.store(std::clamp(amount, T(-100), T(100)) / T(100), std::memory_order_relaxed);
198 outputDepRecovery_.store(enabled, std::memory_order_relaxed);
207 if (!std::isfinite(amount))
return;
208 T c = std::clamp(amount, T(-1), T(1));
209 attackAmount_.store(c, std::memory_order_relaxed);
210 sustainAmount_.store(-c * T(0.5), std::memory_order_relaxed);
218 envFast_.fill(T(1e-5));
219 envSlow_.fill(T(1e-5));
220 lastOutput_.fill(T(0));
225 [[nodiscard]] std::vector<uint8_t>
getState()
const
230 w.
write(
"attack",
static_cast<float>(attackAmount_.load(std::memory_order_relaxed)) * 100.0f);
231 w.
write(
"sustain",
static_cast<float>(sustainAmount_.load(std::memory_order_relaxed)) * 100.0f);
232 w.
write(
"outputDep", outputDepRecovery_.load(std::memory_order_relaxed));
248 static constexpr int kMaxChannels = 16;
250 void updateCoefficients() noexcept
252 if (!(sampleRate_ > 0.0))
return;
253 T fs =
static_cast<T
>(sampleRate_);
254 fastAttackCoeff_ = T(1) - std::exp(T(-1) / (fs * T(0.0001)));
255 fastReleaseCoeff_ = T(1) - std::exp(T(-1) / (fs * T(0.005)));
256 slowAttackCoeff_ = T(1) - std::exp(T(-1) / (fs * T(0.020)));
257 slowReleaseCoeff_ = T(1) - std::exp(T(-1) / (fs * T(0.200)));
260 double sampleRate_ = 48000.0;
263 std::atomic<T> attackAmount_ { T(0) };
264 std::atomic<T> sustainAmount_ { T(0) };
265 std::atomic<bool> outputDepRecovery_ {
false };
269 T fastAttackCoeff_ = T(0), fastReleaseCoeff_ = T(0);
270 T slowAttackCoeff_ = T(0), slowReleaseCoeff_ = T(0);
273 std::array<T, kMaxChannels> envFast_ {};
274 std::array<T, kMaxChannels> envSlow_ {};
275 std::array<T, kMaxChannels> lastOutput_ {};
Non-owning view over audio channel data.
RAII scope guard to disable denormalised (subnormal) floating-point numbers.
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.
Zero-allocation, thread-safe transient shaper.
void reset() noexcept
Clears all internal buffers and state. Must be lock-free.
void setSustain(T amount) noexcept
Sets sustain (body) emphasis.
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
void setOutputDepRecovery(bool enabled) noexcept
Enables output-dependent recovery (ODR).
void prepare(double sampleRate) noexcept
Prepares the processor with a specific sample rate.
void processBlock(AudioBufferView< T > buffer) noexcept
Processes an audio buffer in-place.
~TransientDesigner()=default
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
void prepare(const AudioSpec &spec) noexcept
Prepares the processor with the current audio specification.
void setAttack(T amount) noexcept
Sets attack (transient) emphasis.
void setCharacter(T amount) noexcept
Sets character as a single macro-knob (overwrites attack AND sustain).
Main namespace for the DSPark framework.
T fastLog(T x) noexcept
Fast natural logarithm approximation.
constexpr uint32_t stateId(const char(&tag)[5]) noexcept
Builds a FOURCC processor id, e.g. dspark::stateId("COMP").
T fastExp(T x) noexcept
Fast approximation of e^x via std::exp2 (~2x faster than std::exp on MSVC).
Describes the audio environment for a DSP processor.