Tube Warmth
Tier: Color | ComponentType: 27 | Params: 5
Even and odd harmonic generation with controllable balance, modeling the nonlinear behavior of vacuum tube amplification stages.
Overview
TubeWarmth generates harmonic distortion by blending two distinct nonlinear processes: a quadratic (x²) path that produces even harmonics (2nd, 4th, 6th...) and a hyperbolic tangent path that produces odd harmonics (3rd, 5th, 7th...). This mirrors real tube behavior — triode stages tend toward even harmonics while push-pull configurations emphasize odd harmonics.
The Even/Odd balance control crossfades between these two distortion characters. At 0.0, the output is pure even-harmonic distortion (warm, "musical" coloring). At 1.0, it's pure odd-harmonic saturation (harder, more compressed). The default 0.3 gives a triode-like voicing with predominantly even harmonics and a touch of odd-order edge.
A DC blocker is essential because the x² term converts any signal into a unipolar (positive-only) waveform, generating a DC offset proportional to signal level. The one-pole lowpass tone filter rolls off harsh high-frequency harmonics, and a normalization stage compensates for the level increase from distortion.
File Locations
| Path | |
|---|---|
| Header | Sources/FolioDSP/include/FolioDSP/Color/TubeWarmth.h |
| Implementation | Sources/FolioDSP/src/Color/TubeWarmth.cpp |
| Tests | Tests/FolioDSPTests/TubeWarmthTests.swift |
| Bridge | Sources/FolioDSPBridge/src/FolioDSPBridge.mm (TubeWarmthBridge) |
Parameters
| Index | Name | Description | Min | Max | Default Min | Default Max | Default | Unit |
|---|---|---|---|---|---|---|---|---|
| 0 | Drive | Distortion intensity — scales the nonlinear terms | 1.0 | 20.0 | 1.0 | 5.0 | 2.0 | |
| 1 | Even/Odd | Balance between even (x²) and odd (tanh) harmonics | 0.0 | 1.0 | 0.0 | 1.0 | 0.3 | |
| 2 | Bias | DC offset added before distortion, shifts operating point | -0.2 | 0.2 | -0.1 | 0.1 | 0.02 | |
| 3 | Tone | One-pole lowpass cutoff, tames harsh upper harmonics | 1000 | 20000 | 3000 | 12000 | 8000 | Hz |
| 4 | Mix | Dry/wet blend | 0 | 100 | 0 | 100 | 100 | % |
Processing Algorithm
The process() function executes these steps for each input sample:
1. Bias Offset
A small DC offset is added to shift the signal's operating point on the transfer curve:
With the default bias of 0.02, the signal sits slightly off-center, which causes the even-harmonic (x²) path to produce asymmetric distortion — a characteristic of single-ended triode stages.
2. Even Harmonic Generation (x²)
The quadratic path adds the square of the biased signal, scaled by drive:
The x² term produces exclusively 2nd harmonic content (and DC). The drive - 1 factor means at drive = 1.0, no distortion is added. The (1 - bal) factor fades this path out as Even/Odd approaches 1.0.
3. Odd Harmonic Generation (tanh)
The hyperbolic tangent path provides soft-clipping saturation:
The tanh function symmetrically compresses the signal, generating odd-order harmonics (3rd, 5th, 7th...). Higher drive values push the signal further into saturation.
4. Blend
The two paths are crossfaded using the Even/Odd balance:
5. DC Blocker
A first-order highpass filter removes the DC offset introduced by the x² term:
The 10 Hz cutoff preserves all audible content while removing subsonic drift.
6. Tone Filter
A one-pole lowpass filter smooths harsh upper harmonics:
7. Normalization
A level correction compensates for the gain increase from distortion:
8. Dry/Wet Mix
Core Equations
Where \(x_b = x + \text{bias}\), \(b\) = Even/Odd balance, \(d\) = drive.
Snapshot Fields
| Field | Type | Range | Unit | Description |
|---|---|---|---|---|
| Input Level | Float | 0–1 | Smoothed input amplitude | |
| Output Level | Float | 0–1 | Smoothed output amplitude | |
| Even Harmonic Level | Float | 0–1 | Level of even-harmonic contribution | |
| Odd Harmonic Level | Float | 0–1 | Level of odd-harmonic contribution | |
| Bias | Float | -0.2–0.2 | Current bias value | |
| Tone Frequency | Float | 1000–20000 | Hz | Current tone filter cutoff |
| Transfer Curve | Float[16] | -1–1 | 16-point input→output transfer function |
Implementation Notes
- DC blocker uses the standard first-order highpass:
y = x - xprev + R * ywhereR = 1 - (2π·10/sr). This is essential — without it, the x² path generates a DC offset that grows with signal level. - Level tracking uses exponential smoothing with coefficient 0.01 for display-rate visualization.
- Transfer curve is computed in the snapshot emission path (not per-sample), sampling 16 points from x = -1 to +1 through the current distortion function.
- All parameters use
std::atomic<float>for lock-free thread safety. - Snapshot emission is decimated to ~60 fps (every 735 samples at 44.1 kHz).
Equation Summary
y = (1-b)·(xb + a_even·xb²) + b·tanh(xb·(1+a_odd)) where xb = x + bias