Physics-based loudspeaker, cabinet and microphone emulation: a Thiele-Small electromechanical model realised as a short chain of digital filters, plus voice-coil-inductance treble rolloff, a cone-breakup modal bank, a level-dependent BL(x) excursion compression stage, closed/open-back cabinet colour with baffle step and edge diffraction, and a microphone-position model. More...
#include <pulp/signal/biquad.hpp>#include <pulp/signal/dc_blocker.hpp>#include <pulp/signal/denormal.hpp>#include <pulp/signal/smoothed_value.hpp>#include <pulp/signal/tpt_filter.hpp>#include <pulp/signal/units.hpp>#include <algorithm>#include <cmath>#include <complex>Go to the source code of this file.
Classes | |
| struct | pulp::signal::SpeakerDriverArchetype |
| One driver's published-form Thiele-Small set plus the two geometry figures the cabinet and breakup stages need. More... | |
| class | pulp::signal::detail::DualTapResonatorT< SampleType > |
| Dual-tap TPT state-variable resonator (Zavalishin topology, identical to SvfT) returning the highpass and lowpass outputs from ONE tick. More... | |
| struct | pulp::signal::detail::DualTapResonatorT< SampleType >::Taps |
| class | pulp::signal::SpeakerModelT< SampleType > |
| Physics-based speaker + cabinet + microphone emulator. More... | |
| struct | pulp::signal::SpeakerModelT< SampleType >::BreakupMode |
| One DIMENSIONLESS breakup mode: a ratio to the archetype's anchor frequency plus a Q and a gain. More... | |
Namespaces | |
| namespace | pulp |
| namespace | pulp::signal |
| namespace | pulp::signal::detail |
| Round-2 effect-wave vocabulary. | |
Typedefs | |
| using | pulp::signal::SpeakerModel = SpeakerModelT<float> |
| using | pulp::signal::SpeakerModel64 = SpeakerModelT<double> |
Enumerations | |
| enum class | pulp::signal::SpeakerBoxType { pulp::signal::sealed , pulp::signal::open_back } |
| Cabinet topology. More... | |
Physics-based loudspeaker, cabinet and microphone emulation: a Thiele-Small electromechanical model realised as a short chain of digital filters, plus voice-coil-inductance treble rolloff, a cone-breakup modal bank, a level-dependent BL(x) excursion compression stage, closed/open-back cabinet colour with baffle step and edge diffraction, and a microphone-position model.
RT contract: after prepare(sample_rate), no method allocates, locks, or throws. process is a fixed straight-line chain: one TPT state-variable tick (HP and LP taps), one gain multiply (g_bl), a control-cadence coefficient recompute every kControlBlock samples, then the fixed inductance / breakup / cabinet / mic biquad chain. Excursion state x is read from the previous sample's LP tap — there is no zero-delay algebraic loop. All state is POD; reset() zeroes it; a zero-initialised instance is silent-safe. latency_samples() is the literal constant 0.
Both answer "what does this cabinet sound like", and neither subsumes the other:
Nothing is shared between them at the code level and nothing should be: the convolver owns partitioning, IR ingest and its own zero-latency argument, and this module states its own latency (also 0, for a different and much simpler reason — the chain is a cascade of minimum-phase filters with no block buffering anywhere). The two are alternatives, not layers.
Composed: BiquadT (peaking and shelving sections for the breakup bank, cabinet and mic stages), TptFilterT (the first-order rolloffs), DcBlocker, SmoothedValue, units.hpp, denormal.hpp.
The cone-breakup bank is peaking biquads, not ModalBankT. modal_bank.hpp is a bank of STRUCK RESONATORS — coupled-form phasors with a t60 decay, excited by an input and summed through pickup gains. Its transfer function is a resonator, so it can only ADD energy at a mode. The shipped breakup shape needs a −4 dB DIP at mode 3 (the honk-versus-cut interplay that gives a 12" its vocal midrange), which a parallel resonator bank cannot express, and it is parameterised by Q rather than t60. A cascade of four <tt>BiquadT::Type::peaking</tt> sections is the primitive that matches the specification. <tt>ModalBankT</tt>'s second-order-resonator reasoning still applies and is why the modes are two-pole sections at all. <strong>The BL(x) stage is not <tt>SaturatorT</tt>.</strong> <tt>saturator.hpp</tt> is a MEMORYLESS waveshaper: its output is a function of the instantaneous signal sample. The excursion compression here is a gain computed from cone DISPLACEMENT — the state-variable filter's lowpass tap, which is a different, heavily low-passed and phase-shifted signal from the pressure it multiplies. That distinction is the physical content of the model: a low note moves the cone and therefore compresses the treble riding on top of it, which is what a driven cab audibly does. A waveshaper on the pressure signal reproduces neither the low-frequency dominance nor the phase relationship, so it would be a different (and wrong) effect wearing the right name. <tt>g_bl</tt> is also EVEN in x — BL(x) falls symmetrically as the coil leaves the gap either way — so the compression is symmetric and <tt>SaturatorT</tt>'s bias construction, which exists to make asymmetric curves DC-free, has nothing to do here. <strong>The resonance stage taps HP and LP from one tick.</strong> <tt>SvfT::process()</tt> returns a single selected mode, so getting both taps from it would mean ticking two instances and advancing two copies of the same state. The dual-tap resonator below is the same Zavalishin TPT topology as <tt>SvfT</tt>, differing only in returning both outputs from one tick. A <tt>process_all()</tt> on <tt>SvfT</tt> would make this unnecessary; that file is not this module's. @subsection autotoc_md130 Anti-aliasing policy (series law 4): base rate, no oversampling The only nonlinearity is the excursion modulation, and its modulator is the SVF LOWPASS state — intrinsically band-limited, falling at −12 dB/oct above fc (~214 Hz for the default archetype and box). A slowly varying gain multiplying the through-signal places sidebands within a few hundred Hz of each carrier component, and the voice-coil rolloff (gone by ~5 kHz) attenuates high carriers before their products can fold. <tt>Aliasing floor</tt> measures the result rather than asserting the argument. Consequently there is no oversampler and <tt>latency_samples()</tt> is 0. If a future high-excursion archetype violates that measurement, the fix is to wrap the excursion stage in the house 65-tap Kaiser (beta = 8) half-band pair and report its group delay exactly — not to widen the test. @subsection autotoc_md131 References * A. N. Thiele, "Loudspeakers in Vented Boxes: Part I", J. Audio Eng. Soc. 19(5):382-392, 1971 — the second-order high-pass driver model. * R. H. Small, "Direct-Radiator Loudspeaker System Analysis", J. Audio Eng. Soc. 20(5):383-395, 1972 — piston-band pressure as a second-order high-pass; the Thiele-Small parameter framework. * R. H. Small, "Closed-Box Loudspeaker Systems - Part I: Analysis", J. Audio Eng. Soc. 20(10):798-808, 1972 — fc = fs*sqrt(1+alpha), Qtc = Qts*sqrt(1+alpha), alpha = Vas/Vb. * W. Klippel, "Tutorial: Loudspeaker Nonlinearities - Causes, Parameters, Symptoms", J. Audio Eng. Soc. 54(10):907-939, 2006 — BL(x) and Cms(x). * W. M. Leach Jr., "Loudspeaker Voice-Coil Inductance Losses", J. Audio Eng. Soc. 50(6):442-450, 2002 — the semi-inductive HF rolloff. * J. R. Wright, "An Empirical Model for Loudspeaker Motor Impedance", J. Audio Eng. Soc. 38(10):749-754, 1990 — motor-impedance model. * H. F. Olson, "Direct Radiator Loudspeaker Enclosures", J. Audio Eng. Soc. 17(1):22-29, 1969 — baffle step and edge-diffraction concepts.
The citations above ground concepts and topology. The driver archetype table contains engineered [design parameter] starting points inside the ranges published guitar/bass driver datasheets occupy — not a transcription of any one product.