DSPark 1.6.1
Header-only audio DSP framework in pure C++20 — zero dependencies
Loading...
Searching...
No Matches
TubePreamp.h File Reference

Tube preamp: Koren 12AX7 stages, WDF tone circuit, supply sag. More...

#include "../Core/AudioBuffer.h"
#include "../Core/AudioSpec.h"
#include "../Core/Biquad.h"
#include "../Core/DenormalGuard.h"
#include "../Core/DspMath.h"
#include "../Core/Oversampling.h"
#include "../Core/StateBlob.h"
#include "../Core/WDF.h"
#include <algorithm>
#include <array>
#include <atomic>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <numbers>
#include <vector>
Include dependency graph for TubePreamp.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

class  dspark::TubePreamp< T >
 One/two 12AX7 stages with sag and a WDF tone circuit. More...
 

Namespaces

namespace  dspark
 Main namespace for the DSPark framework.
 

Detailed Description

Tube preamp: Koren 12AX7 stages, WDF tone circuit, supply sag.

Circuit-level preamp modelling, not a waveshaper:

  • Triode stages (1 or 2) solved per sample with Newton-Raphson on the plate current. The tube follows Koren's improved SPICE model (Koren 1996; published 12AX7 parameters MU=100, EX=1.4, KG1=1060, KP=600, KVB=300) in a classic common-cathode stage: 300 V supply, 100 k ohm plate load, 1.5 k ohm cathode resistor with its 22 uF bypass capacitor integrated trapezoidally - the capacitor state is folded into the Newton equation, so each sample solves the true implicit system. Grid conduction is approximated by a soft clamp toward +0.7 V (full blocking distortion needs the input-coupling state and is left for a later pass).
  • Supply sag: the effective B+ droops with smoothed plate current (one-pole, ~70 ms) times a sag resistance - drive into the stage and the headroom breathes back, the classic touch response.
  • Tone stack: the full Fender '59 Bassman FMV treble/bass/middle network, solved exactly as a 12-port WDF R-type adaptor (wdf::ToneStackFMV - verified sample-exact against the symbolic transfer function of Yeh & Smith, DAFx-06). The stack sits between the stages and is driven by the first stage's real ~38 k ohm output impedance, so it loads the tube exactly like the hardware. Controls interact non-orthogonally - that is the circuit, not a bug.
  • Output level: the circuit's program response at the reference tone setting is measured once in prepare() (settled channel, pink-weighted multitone). A 3-section EQ designed from that measurement flattens the FMV stack's fixed ~10 dB mid-scoop envelope - neutral knobs sound neutral, the tone controls act relative to flat, and the triode's harmonic character is untouched. Loudness divides out the program gain MEASURED AT EACH DRIVE (prepare-time sweep LUT, so the link tracks the circuit's real compression) plus a +0.25 dB/dB residual slope: backing off is audible, pushing raises density at a gently rising level - the same contract as TapeMachine/TransformerModel. Use setOutput() for static trim; gain changes are ramped (~30 ms) so drags stay click-free.

The nonlinear core runs at 2x oversampling by DEFAULT. Per RF-009/ADR-011 the factor is configurable via setOversampling(int) (setup thread only, it reallocates and re-calibrates like prepare()): 1 = OFF (no internal resampling, zero added latency, but the triode/grid nonlinearity then aliases in-band unless you oversample the surrounding chain yourself), 2 = default (group-delay latency reported by getLatency()/getLatencySamples()), 4/8 = progressively lower alias floor at ~linearly higher CPU. Cost scales with the factor: the whole per-sample Newton-Raphson triode + WDF tone solve runs factor x oversampled samples, so 4x is ~2x the CPU of the 2x default and 1x is the cheapest. getLatency() always reflects the ACTIVE factor (0 at 1x) so hosts get correct PDC. THD signature verified in the suite: single-stage distortion is 2nd-harmonic dominant (asymmetric triode), DC operating point matches an independent high-precision solve of the same circuit equations (the check SPICE would perform).

Threading model: parameter setters/getters are std::atomic based and safe from any thread (non-finite values are ignored; changes are published with a release store and consumed at the next block). prepare() is setup-thread only (allocates and runs the reference calibration; invalid specs are ignored and an unprepared instance passes audio through). reset() belongs to the stream owner. getState()/setState() are setup/UI threads. getSupplyVoltage() is a metering-style read. The dry/wet mix is smoothed linearly over one block. Channels beyond the prepared count pass through untouched.

Dependencies: Core/WDF.h, Core/Oversampling.h, Core/Biquad.h, Core/AudioSpec.h, Core/AudioBuffer.h, Core/DspMath.h, Core/DenormalGuard.h, Core/StateBlob.h.

Definition in file TubePreamp.h.