Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::signal::TransformerBracketT< SampleType > Class Template Reference

One input or output transformer: band-limit, mild saturation, DC-blocked. More...

#include <diode_bridge_compressor.hpp>

Public Member Functions

void prepare (double sample_rate)
void reset ()
void set_character (double character01)
 0..1 colour depth, shared with the bridge.
void set_adaa (bool on)
double saturate (double u) const
 u − (a/2)·u·|u| − (a·ε/2)·u².
double saturate_antiderivative (double u) const
 ∫sat du.
SampleType process (SampleType input)

Static Public Attributes

static constexpr double kLowCornerHz = 20.0
 Low-frequency corner: the transformer's LF droop, and the stage's DC block.
static constexpr double kHighCornerHz = 28000.0
 High-frequency corner: winding bandwidth.
static constexpr double kSaturationDepth = 0.15
 Saturation depth at character = 1.
static constexpr double kEvenAsymmetry = 0.5
 How much of the saturation is asymmetric, i.e.
static constexpr double kMonotonicMargin = 0.98
 Fraction of the 1/(a(1+ε)) monotonicity limit the input clamp uses.
static constexpr double kAdaaEpsilon = 1e-5
 ADAA ill-conditioning guard, as DiodeBridgeGainT::kAdaaEpsilon.
static constexpr double kPeakGain = 1.0
 The peak gain this stage can present, at any frequency and any amplitude.

Detailed Description

template<typename SampleType = float>
class pulp::signal::TransformerBracketT< SampleType >

One input or output transformer: band-limit, mild saturation, DC-blocked.

Peak gain is exactly 1.0, and that is load-bearing — it is the Tpeak the compressor's worst-case-gain bound multiplies by, so it has to be a property of the construction rather than a measured approximation. Three stages, each with magnitude ≤ 1 at every frequency and amplitude: a one-pole high-pass, a compressive saturator whose gain is 1 − (a/2)|u| (≤ 1 by inspection, = 1 only at the origin), and a one-pole low-pass.

Why not DcBlocker. The spec composes DcBlocker for the LF corner. Its transfer function (1 − z⁻¹)/(1 − p·z⁻¹) has magnitude 2/(1 + p) at Nyquist — 1.0025 at the default pole. Two brackets would therefore have a peak gain of 1.005, and the Tpeak = 1.0 the worst-case bound cites would be false by construction. The house TPT one-pole's high-pass output is 1 − LP, whose magnitude is ≤ 1 everywhere with equality only at Nyquist, and it is still an exact DC block (H(0) = 0). Same existing primitive family, one that does not void the invariant it is used inside.

Order: saturate, then high-pass. The saturator's even-harmonic term rectifies, so it generates a program-dependent DC offset. Placing the high-pass after it makes one filter do both jobs the spec names — the documented LF droop AND the DC block — and keeps that offset out of the output sum and out of the feedback detector, which would otherwise read it as signal and hold gain down on a silent passage.

The even harmonics are real, and they need an asymmetry to exist. The spec prescribes sat(u) = u − (a/2)·u·|u| and describes it as adding a second harmonic. It does not: u·|u| is an ODD function, so that shaper is odd-symmetric and produces odd harmonics only — which is also the physically right answer for a SYMMETRIC magnetic core. Even harmonics in a transformer come from asymmetry. kEvenAsymmetry is the parameter that supplies it, and at kEvenAsymmetry = 0 this reduces to the spec's formula exactly:

sat(u) = u − (a/2)·u·|u| − (a·ε/2)·u² slope(0) = 1
F1(u) = u²/2 − (a/6)·u²·|u| − (a·ε/6)·u³
@ a
Definition formant_filter_bank.hpp:20
@ u
Definition formant_filter_bank.hpp:20

RT contract: as the file doc block. Nothing allocates.

Member Function Documentation

◆ prepare()

template<typename SampleType = float>
void pulp::signal::TransformerBracketT< SampleType >::prepare ( double sample_rate)
inline

◆ process()

template<typename SampleType = float>
SampleType pulp::signal::TransformerBracketT< SampleType >::process ( SampleType input)
inline

◆ reset()

template<typename SampleType = float>
void pulp::signal::TransformerBracketT< SampleType >::reset ( )
inline

◆ saturate()

template<typename SampleType = float>
double pulp::signal::TransformerBracketT< SampleType >::saturate ( double u) const
inline

u − (a/2)·u·|u| − (a·ε/2)·u².

Slope 1 at the origin; |sat(u)/u| ≤ 1 everywhere inside the monotonic region.

◆ saturate_antiderivative()

template<typename SampleType = float>
double pulp::signal::TransformerBracketT< SampleType >::saturate_antiderivative ( double u) const
inline

∫sat du.

Continuous and continuously differentiable at the origin, which is what makes the ADAA quotient valid across a zero crossing.

◆ set_adaa()

template<typename SampleType = float>
void pulp::signal::TransformerBracketT< SampleType >::set_adaa ( bool on)
inline

◆ set_character()

template<typename SampleType = float>
void pulp::signal::TransformerBracketT< SampleType >::set_character ( double character01)
inline

0..1 colour depth, shared with the bridge.

Member Data Documentation

◆ kAdaaEpsilon

template<typename SampleType = float>
double pulp::signal::TransformerBracketT< SampleType >::kAdaaEpsilon = 1e-5
staticconstexpr

ADAA ill-conditioning guard, as DiodeBridgeGainT::kAdaaEpsilon.

[design parameter] default 1e-5, range 1e-7 .. 1e-4.

◆ kEvenAsymmetry

template<typename SampleType = float>
double pulp::signal::TransformerBracketT< SampleType >::kEvenAsymmetry = 0.5
staticconstexpr

How much of the saturation is asymmetric, i.e.

how even-harmonic-forward the bracket is. 0 is the spec's odd-only formula; 1 leaves the negative half of the curve untouched entirely. [design parameter] default 0.5, range 0.0 .. 1.0.

◆ kHighCornerHz

template<typename SampleType = float>
double pulp::signal::TransformerBracketT< SampleType >::kHighCornerHz = 28000.0
staticconstexpr

High-frequency corner: winding bandwidth.

[design parameter] default 28 kHz, range 18 .. 40 kHz.

◆ kLowCornerHz

template<typename SampleType = float>
double pulp::signal::TransformerBracketT< SampleType >::kLowCornerHz = 20.0
staticconstexpr

Low-frequency corner: the transformer's LF droop, and the stage's DC block.

[design parameter] default 20 Hz, range 10 .. 40 Hz.

◆ kMonotonicMargin

template<typename SampleType = float>
double pulp::signal::TransformerBracketT< SampleType >::kMonotonicMargin = 0.98
staticconstexpr

Fraction of the 1/(a(1+ε)) monotonicity limit the input clamp uses.

At the default depth that limit is ~4.4 — far outside any real signal, so this is a guard against a caller feeding the bracket something wild, not a shaping stage. [design parameter] default 0.98, range 0.90 .. 0.99.

◆ kPeakGain

template<typename SampleType = float>
double pulp::signal::TransformerBracketT< SampleType >::kPeakGain = 1.0
staticconstexpr

The peak gain this stage can present, at any frequency and any amplitude.

Exactly 1 by construction — see the class doc block. Named rather than written as a literal because the compressor's worst-case bound multiplies two of them.

◆ kSaturationDepth

template<typename SampleType = float>
double pulp::signal::TransformerBracketT< SampleType >::kSaturationDepth = 0.15
staticconstexpr

Saturation depth at character = 1.

Small: a line-level bracket runs at far lower flux than a tape head, which is also why hysteresis is out of scope here (see the module's scope notes). [design parameter] default 0.15, range 0.0 .. 0.5.


The documentation for this class was generated from the following file: