Harmonic Resonance — User Guide

Three resonance engines in one tool: a true harmonic resonator bank tuned to F0, 2F0, 3F0..., a corrected geometric feed-forward comb cascade, and the preserved legacy feedback-comb texture.

Author: Shai Cohen Affiliation: Department of Music, Bar-Ilan University, Israel Version: 0.7 (2026) License: MIT License Repo: GitHub

What this does

Harmonic Resonance offers three distinct processing characters. The default character is a bank of tuned second-order resonators centred on integer multiples of a detected or fixed fundamental. Two historical delay-based characters are retained as alternative textures.

CharacterCore processWhat “harmonic” means
True harmonic resonatorsParallel causal all-pole resonators at F0, 2F0, 3F0...Actual integer harmonic frequencies derived from a fundamental.
Geometric combIterated frozen feed-forward difference stages using geometrically shorter delays.Historical name only; the delays are geometric, not a true harmonic series.
Legacy feedback combOriginal in-place difference process, where the backward tap can read already-processed samples.Historical texture; again not an F0-based harmonic bank.
Default: Character 1 — true harmonic resonators (pitch-derived).

Quick start

  1. Select exactly one Sound object.
  2. Run Harmonic_Resonance.praat.
  3. Choose a preset or keep Custom.
  4. Choose the processing Character.
  5. For true harmonic resonators, choose pitch-derived or fixed fundamental, then set Q and wet mix.
  6. For geometric/legacy comb modes, choose the harmonic-base range or a fixed base.
  7. Set tail and fadeout as required, then run the script.

Character 1 — True harmonic resonators

This mode is the current main engine. It finds or accepts a fundamental frequency F0, then builds resonators at integer multiples below a 95% Nyquist limit:

f[k] = k × F0, k = 1 ... activeHarmonics activeHarmonics = min( Num_iterations, floor(0.95 × Nyquist / F0) )

Fundamental

Second-order resonator

For each harmonic, bandwidth is derived from Q:

bandwidth[k] = max(2 Hz, f[k] / Resonance_Q) r = exp(-π × bandwidth / sampleRate) p = -2r cos(2πf/sampleRate) q = r² y[n] = x[n] - p·y[n-1] - q·y[n-2]

This is a causal all-pole resonator. Because the source is first extended with silence, the recursive resonators can continue ringing into the tail.

Harmonic weights

rawWeight[k] = Decay_factor^(k-1) normalizedWeight[k] = rawWeight[k] / sqrt(Σ rawWeight²)

The weight vector is energy-normalized, so increasing the requested harmonic count mainly changes spectral colour rather than simply increasing total wet energy.

Dry/wet construction

dry = extendedSource × (1 - Resonance_mix) wet harmonic layer k: layerScale = Resonance_mix × normalizedWeight[k] × originalRMS / resonatorLayerRMS result = dry + Σ scaled harmonic layers

Resonance_mix is validated to 0–1. The original channel count is preserved throughout.

Characters 2–3 — Geometric comb textures

These modes preserve the older sound of the processor. A single geometric base is chosen once per run — either the fixed base or one random value drawn uniformly from Harmonic_base_min ... Harmonic_base_max. It is not re-randomized at every iteration.

shiftFactor[k] = base^k D[k] = round(totalExtendedSamples / shiftFactor[k]) halfD[k] = round(D[k] / 2) iterationWeight = 1 / k ampDecay = 1 - (k / Num_iterations) × Decay_factor

Because totalExtendedSamples includes the silent tail, Tail_duration_s changes the actual comb delays in Characters 2 and 3. As k increases, base^k grows and the delay becomes geometrically shorter.

Character 2 — corrected feed-forward

Each iteration first freezes the complete pre-iteration result. Both taps read that frozen snapshot:

if both taps are in range: y[n] = (xFrozen[n + D] - xFrozen[n - D/2]) × (1/k) else: y[n] = 0.5 × previousResult[n] then: y[n] = y[n] × ampDecay

This prevents accidental recursion within the same formula pass.

Character 3 — legacy feedback comb

The same formula uses self directly. Praat updates samples left-to-right, so the backward tap can read values that have already been processed during that pass. This creates the recursive/feedback-like character of the legacy version.

Presets

Presets set the resonance/comb parameters below, but they do not change Character, Fundamental mode, pitch bounds, fixed F0, Draw visualization, or Play result.

PresetTailIterationsQWetBase rangeDecayPeakFade
Subtle Harmonics1.5 s4180.351.3–2.20.620.960.8 s
Medium Harmonics2.0 s7240.501.5–4.00.720.951.0 s
Heavy Harmonics2.8 s10300.652.0–4.80.780.931.4 s
Extreme Harmonics4.0 s15360.752.5–6.00.840.911.8 s
Custom2.0 s7240.551.5–4.00.720.951.0 s

Parameters & validation

ParameterDefaultMeaning
CharacterTrue harmonic resonatorsSelects the F0-based bank, corrected geometric comb, or legacy comb.
Tail_duration_s2.0Extra zero-valued duration appended before processing; may be 0.
Num_iterations7Requested harmonics in Character 1 or iterations in Characters 2–3; valid 1–64.
Resonance_Q24Resonator sharpness in Character 1; must be > 0.
Resonance_mix0.55Dry/wet control for Character 1; 0–1.
Fundamental_modePitch-derived medianMedian voiced Pitch or fixed F0.
Pitch_floor_Hz / ceiling50 / 800Bounds for pitch-derived F0 analysis.
Fixed_fundamental_Hz110Fixed F0 and fallback F0.
Harmonic_base_min / max1.5 / 4.0Random geometric-base range for Characters 2–3; must satisfy 1 < min ≤ max.
Use_fixed_baseOffUse Fixed_harmonic_base instead of a random base.
Fixed_harmonic_base2.5Must be > 1.
Decay_factor0.72Harmonic weight decay in Character 1; historical per-iteration decay in Characters 2–3. Valid 0 ≤ value < 1.
Scale_peak0.95Character-dependent level handling; valid (0,1].
Fadeout_duration_s1.0Final cosine fade; may be 0 and is capped at total duration.
Draw_visualizationOnDraws waveform, process model, spectra and summary.
Play_resultOnPlays the created result.

Level handling & fadeout

Scale_peak has different semantics in the three characters:

The final fadeout is applied after this level stage:

effectiveFade = min(Fadeout_duration_s, totalDuration) gain(x) = 0.5 + 0.5 × cos( π × (x - fadeStart) / effectiveFade )

There is no second normalization after the fade.

Visualization

The Picture output uses the same Source/Output amplitude scale and shows four functional views:

The central process panel is explanatory, not a measured transfer-function plot. Its resonance peaks and delay ladder illustrate the implemented law without implying calibrated response magnitude.

Output behavior

Object names depend on Character:

True harmonic: <source>_harmonicRes_<preset> Geometric comb: <source>_geoComb_<preset> Legacy comb: <source>_legacyComb_<preset>