Skip to content

Karplus-Strong

Tier: Primitives | ComponentType: 39 | Params: 5

Plucked string physical model using a filtered delay loop with noise excitation.

Overview

KarplusStrong simulates a plucked string by filling a delay buffer with filtered noise (the "pluck") and then circulating it through a feedback loop with lowpass damping and energy decay. The delay length determines the pitch, the damping filter simulates frequency-dependent string loss, and the decay parameter controls how quickly the string dies away.

A brightness parameter filters the initial noise excitation (pre-pluck), and a pluck position parameter applies a comb filter to the excitation, mimicking the tonal effect of plucking at different positions along the string (near bridge = bright, near center = mellow).

File Locations

Path
Header Sources/FolioDSP/include/FolioDSP/Primitives/KarplusStrong.h
Implementation Sources/FolioDSP/src/Primitives/KarplusStrong.cpp
Tests Tests/FolioDSPTests/KarplusStrongTests.swift
Bridge Sources/FolioDSPBridge/src/FolioDSPBridge.mm (KarplusStrongBridge)

Parameters

Index Name Description Min Max Default Min Default Max Default Unit
0 Frequency Fundamental pitch of the string 20.0 5000.0 60.0 1000.0 220.0 Hz
1 Damping Lowpass damping in feedback loop (0=bright, 1=dark) 0.0 1.0 0.0 1.0 0.5
2 Decay Energy retention per cycle (0.99=fast, 1.0=infinite) 0.9 1.0 0.99 1.0 0.998
3 Pluck Position Position along string (0=bridge, 0.5=center) 0.0 0.5 0.0 0.5 0.0
4 Brightness Pre-filter on excitation noise (0=dark, 1=bright) 0.0 1.0 0.0 1.0 1.0

Processing Algorithm

1. Excitation (on excite() call)

Noise burst fills the delay buffer behind the write pointer:

\[\text{noise}_i = \text{xorshift32} \cdot A\]

Brightness pre-filter (one-pole LP on noise):

\[\text{filt} = b \cdot \text{noise} + (1-b) \cdot \text{filt}_{prev}\]

Pluck position comb filter (subtracts delayed copy):

\[\text{buf}[i] \mathrel{-}= \text{buf}[i - p \cdot L]\]

where \(p\) = pluck position, \(L\) = delay length.

2. Read from Delay (linear interpolation)

\[d = f_s / f, \quad y = \text{buf}[\text{write} - d]\]

3. One-Pole LP Damping

\[y_f = (1 - \text{damp}) \cdot y + \text{damp} \cdot y_f[n-1]\]

4. Decay

\[y_f \mathrel{\times}= \text{decay}\]

5. Write Back

\[\text{buf}[\text{write}] = y_f + x_{\text{input}}\]

6. DC Blocker

\[y_{\text{out}}[n] = y[n] - y[n-1] + R \cdot y_{\text{out}}[n-1]\]

where \(R = 1 - 2\pi \cdot 10 / f_s\).

Snapshot Fields

Field Type Range Unit Description
Frequency Float 20–5000 Hz Current frequency
Damping Float 0–1 Current damping
Decay Float 0.9–1.0 Current decay
Pluck Position Float 0–0.5 Current position
Output Level Float 0–1 Smoothed output
Energy Float 0–1 Remaining string energy
Ringing Bool 0–1 Whether string is still sounding

Implementation Notes

  • 4096-sample raw delay buffer (NOT CircularBuffer template) -- supports frequencies down to ~10.7 Hz
  • xorshift32 PRNG for noise excitation
  • Noise is written BEHIND writePos so the delay reader picks it up immediately
  • Pluck position comb filter creates spectral nulls at harmonics corresponding to the pluck point
  • ParamSmoother on all parameters for stable real-time modulation
  • DC blocker essential for removing subsonic drift from the feedback loop

Equation Summary

y = delay[f] · LP(damping) · decay