0
mirror of https://github.com/torvalds/GuitarPedal.git synced 2026-08-11 04:28:27 +00:00
Files
Linus Torvalds 886254f0f9 Documentation: what [KLON] actually measures like
The first per-effect page, and the shape for the other sixteen.  The
README's list is an overview and stays one or two lines each; this is
where an effect gets described properly - what it models, what the
controls do, what it gets right against the real circuit and what it
does not.

Curves are mermaid xychart blocks rather than images, which is the whole
trick: a mermaid series is a list of numbers, so it renders as a chart on
GitHub *and* diffs as measurements.  A PNG would say "binary files
differ" after a DSP change, which is the same blindness that let the last
analysis rot.

Three things about drawing them that took rendered pages to learn, and
none of which are visible in the source.

Mermaid has no logarithmic axis, so the frequency plots space their
points evenly and label them by octave, which draws a log axis by
construction.  But a category is tied 1:1 to a data point, so the point
count *is* the label count - there is no thinning a crowded axis, and
eleven points is about what fits.  Blanking the labels between does not
work: equal categories collapse onto one x and the line doubles back.
Labels above 1kHz are written 1.3k, 20k and so on to keep them short.

The default palette is pale lavender on white, and two lines of it are
indistinguishable.  plotColorPalette fixes that, under themeVariables
and not under xyChart - both parse, and only one reaches the plot, which
is worth knowing because the wrong one leaves a page claiming a colour
it does not have.  The pair is Okabe-Ito blue and vermillion, which stay
apart under the common colour vision deficiencies and have contrast on
both the light and the dark GitHub themes.

And xychart has no legend, so the titles name the colours.  Inelegant,
and better than two curves with no way to tell which is which.

Two things the measurements say that the source does not, both filed:

The clipper is tanhf(), which is odd, and nothing in the path breaks the
symmetry - so the even harmonics the comment promises are not small but
structurally zero, measured 0.00% at every gain setting from 0.0 to 1.0.

And the clean half of the blend is the raw input rather than the
conditioned one, so the DC blocker, the 30Hz coupling capacitor and the
15kHz bandwidth limit exist only in the dirty path.  Measured, the gain
knob therefore swings the response at 20Hz by 12.7dB relative to
midband: flat to 20Hz at Gain 0, and 12.66dB down at Gain 1.0.  On a
Centaur both halves come off the same input buffer.

check-readme.py grows a second job with the link: a relative link in the
effect list that points at nothing is a 404 on the front page of the
repository, which is worse than no link.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2026-08-10 17:06:04 -07:00

5.9 KiB
Raw Permalink Blame History

Klonlike [KLON]

A model of the Klon Centaur, originally written by Bryan Leavelle. Three controls — Gain, Treble, Output — all LINEAR(0 1).

The circuit it models: a charge pump for 18V of headroom, an input buffer, an op-amp driving a germanium diode pair to ground, and a clean/dirty blend that tracks the gain knob rather than being a mix control of its own. The treble control is a shelf with a presence peak below it.

The signature is the blend. On a real Centaur the clean and clipped paths are summed by a dual-gang gain pot, so turning up the gain does not just add distortion, it removes the clean signal. At low gain you get a clean sound with an edge on it; at maximum there is no clean path left. That is the whole character of the pedal and the model does reproduce it.

All numbers here are measured, not calculated, by Validation/analyse-klon.py driving the pedal's own audio core on the host. make check-analysis re-runs them and says if the code has moved underneath this page.

What the Gain knob does

Output and distortion against Gain, with a 18 dBFS tone in — about what a guitar actually produces.

%%{init: {'themeVariables': {'xyChart': {'plotColorPalette': '#0072b2'}}}}%%
xychart-beta
    title "THD against Gain, -18 dBFS in"
    x-axis "Gain, %" [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
    y-axis "THD, dB" -145 --> 0
    line [-138.5, -80.4, -57.7, -42.5, -32.3, -25.1, -19.9, -14.9, -10.9, -9.2, -8.3]
%%{init: {'themeVariables': {'xyChart': {'plotColorPalette': '#0072b2'}}}}%%
xychart-beta
    title "Output against Gain, -18 dBFS in"
    x-axis "Gain, %" [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
    y-axis "Gain, dB" -5 --> 25
    line [-0.83, -0.62, 1.52, 5.6, 10.14, 14.13, 17.28, 19.4, 19.81, 19.95, 20.01]

Two things worth reading off those. The output curve flattens after about 0.7 — the last third of the knob is almost all distortion and almost no level, which is what you want from a drive pedal and is a fair reproduction of the real thing. And at Gain 0 the effect is essentially a wire, 0.83 dB and 138.5 dB THD, so it can be left in the chain switched down.

The Treble control

Small-signal response at Gain 0.5, with Treble at each end.

%%{init: {'themeVariables': {'xyChart': {'plotColorPalette': '#0072b2, #d55e00'}}}}%%
xychart-beta
    title "Treble 0.0 blue, Treble 1.0 orange, at Gain 0.5"
    x-axis "Hz" [20, 40, 80, 160, 320, 640, "1.3k", "2.6k", "5.1k", "10k", "20k"]
    y-axis "dB" 0 --> 24
    line [4.6, 11.38, 14.21, 15.07, 15.27, 15.13, 13.66, 9.64, 8.32, 7.2, 5.89]
    line [4.38, 11.17, 14.01, 14.88, 15.1, 15.21, 17.34, 21.77, 20.89, 19.45, 17.9]

The two ends differ by about 12 dB above 2 kHz and are within 0.3 dB of each other below 640 Hz, so it is a treble control and not a tilt. The peak at 2 kHz on the bright setting is the 1.7 kHz presence filter riding on the shelf, which is why it sits above its own centre frequency. The model doubles the peak's boost relative to the shelf, which the comment in the source notes was true of the original code too.

What it gets wrong

Two things, both filed.

It cannot produce even harmonics at all. The clipper is tanhf(), which is odd, and nothing in the signal path breaks the symmetry — measured even content is 0.00 % at every gain setting, from 0.0 to 1.0. The comment in the source says germanium hard clipping "gives even harmonic content from the soft knee", and a real mismatched diode pair does. This one does not, and even-order content is most of what people mean when they call a drive "warm" rather than "fuzzy".

The clean path skips every input filter. klon_step() builds a conditioned signal — DC blocker, 30 Hz coupling capacitor, 15 kHz input bandwidth — and then blends the raw input with the clipped one, not the conditioned input. So all three filters exist only in the dirty half, and the gain knob moves the frequency response as a side effect:

%%{init: {'themeVariables': {'xyChart': {'plotColorPalette': '#0072b2, #d55e00'}}}}%%
xychart-beta
    title "Gain 0.0 blue, Gain 1.0 orange, Treble 0.5, normalised"
    x-axis "Hz" [20, 40, 80, 160, 320, 640, "1.3k", "2.6k", "5.1k", "10k", "20k"]
    y-axis "dB, relative to 320 Hz" -14 --> 4
    line [-0.1, -0.06, -0.05, -0.04, 0.0, 0.15, 1.54, 0.98, 0.09, -0.02, -0.04]
    line [-12.66, -4.36, -1.22, -0.28, 0.0, 0.22, 1.64, 0.78, -0.62, -2.06, -3.63]

At Gain 0 the response is flat to 20 Hz and flat to 20 kHz — no coupling capacitor and no bandwidth limit anywhere. At Gain 1.0 the same filters are fully present, 12.7 dB down at 20 Hz and 3.6 dB down at 20 kHz. On the real Centaur both halves come off the same input buffer through the same coupling cap, so the split happens after the input stage, not before it. One character in the source — blending pre instead of in — but it changes the voicing at every setting below maximum.

What it gets right

  • The blend really is a gain-tracked crossfade, not a mix knob, and the clean path really does ground out as the gain comes up.
  • The knee is soft. The level sweep at Gain 0.7 shows THD rising smoothly from 96 dB at 60 dBFS to 7 dB at 0 dBFS with no discontinuity, and the waveform corner sharpness stays near 1.5 until the signal is loud enough to fold, which is germanium-like rather than silicon-like.
  • It stays clean when it is told to. 138.5 dB THD at Gain 0.

One caveat for the top of the knob: aliasing rises from 123 dB at low gain to 50.7 dB at Gain 1.0, because the clipper generates harmonics well above Nyquist and there is no oversampling. That is audible as a hard edge rather than as recognisable tones, and it is worst exactly where the input bandwidth limit is bypassed by the issue above.

Reproducing this

cd Validation
make bench          # a stale bench measures a pedal you no longer have
./analyse-klon.py