The diode-to-ground clipper: series resistor, shunt diode network, optional shunt capacitor.
More...
|
| | DiodeClipperT () |
| void | prepare (double sample_rate) |
| void | set_diode_model (Model model) |
| void | set_symmetry (double symmetry) |
| | −1 = single diode (half-wave), 0 = matched pair, +1 = mismatched pair.
|
| void | set_resistance (double ohms) |
| void | set_capacitance (double farads) |
| | Zero is the degenerate memoryless configuration: a solver limit case for tests, not a shipped preset.
|
| void | reset () |
| | Antiderivative antialiasing is NOT offered on this class, deliberately.
|
| int | last_iteration_count () const |
| | Iterations the last solve took.
|
| SampleType | process (SampleType input) |
| double | voltage () const |
| | The node voltage without advancing — for tests that check the ODE's residual directly.
|
| double | resistive_residual (double vin, double v) const |
| | (vin − v)/R − i_D(v), the residual the memoryless (C = 0) solution must drive to zero.
|
template<typename SampleType = float>
class pulp::signal::DiodeClipperT< SampleType >
The diode-to-ground clipper: series resistor, shunt diode network, optional shunt capacitor.
C·dv/dt = (vin − v)/R − i_D(v), solved by TR-BDF2 with safeguarded Newton-Raphson inner solves.
template<typename SampleType = float>
Antiderivative antialiasing is NOT offered on this class, deliberately.
It was, and it diverged: a 440 Hz full-scale sine reached 5.8e25, growing geometrically at roughly 3300x per sample, on every diode model and at 8/48/192 kHz. It never reported failure because every value stayed finite, and no test saw it because the only ADAA assertion compared two renders for equality — which a deterministic divergence passes.
The cause is structural, not a bug to patch. ADAA replaces the diode term with its MEAN over [v(n-1), v(n)], but trapezoidal rule is already averaging the nonlinearity across the timestep. Stacking the two double-counts the averaging, so the residual's root moves outside the bracket [min(0, v(n-1), vin), max(...)] that makes the safeguarded solve safe; the solver then clamps to an endpoint and the state ratchets away. Matching the Jacobian to the residual (which was ALSO wrong here — it used the plain conductance) is necessary and not sufficient: with a finite-difference-verified derivative it still diverges.
Doing this properly means antialiasing the explicit output shaping rather than the implicit term, which is a different design and wants its own validation. Until then the honest position is that the x1 path has no antialiasing and the oversampled tiers are how you get it.
JunctionPair::adaa_current remains, and remains correct: it is sound for a MEMORYLESS shaper, which is how diode_bridge_compressor.hpp uses it. The unsoundness is specific to putting it inside this implicit solve.