Pitch-Tracked Additive Resynthesizer - User Guide
Sample-accurate additive synthesis driven by tracked F0 and intensity. Re-synthesises a sound with complete control over partial families (harmonic, inharmonic, FM, ring modulation), amplitude laws (1/k, spectral tilt, formant band, random), voicing policies, and stereo rendering.
What this does
This script implements pitch-tracked additive resynthesis — a classic technique that tracks the fundamental frequency (F0) and intensity envelope of a sound, then re-synthesises it using additive synthesis where partial frequencies are derived mathematically from the F0 curve via phase accumulation (not naive sin(2π·f₀(t)·t)). The result is a flexible, controllable reconstruction that can range from natural resynthesis to extreme timbral transformations.
Key Features:
- 8 Partial Families — harmonic, odd-only, even-only, subharmonic, inharmonic_power, frequency_shifted, ring_sidebands, fm_sidebands
- 7 Amplitude Laws — 1/k, 1/k², equal, spectral tilt (dB/octave), Gaussian formant band, random static, random slow
- 8 Presets — natural_voice, bright_harmonic, hollow_odd, inharmonic_bells, ring_metal, fm_electric, subharmonic_bass, formant_vowel
- Voicing Policies — silence unvoiced, noise unvoiced (coloured noise), copy original unvoiced
- Stereo Modes — mono, Haas width (0.8 ms delay), partial spread (decorrelation)
- Attack/Release Smoothing — envelope smoothing for natural attacks
Quick start
- In Praat, select exactly one Sound object (mono or stereo).
- Run script… →
PitchTrackedAdditive.praat. - Choose a preset from the dropdown (natural_voice, bright_harmonic, hollow_odd, inharmonic_bells, ring_metal, fm_electric, subharmonic_bass, formant_vowel).
- Adjust Num partials (1–128, default 16).
- Set Pitch floor/ceiling (30–600 Hz, default 75–600 Hz).
- Select Voicing policy for unvoiced regions.
- Click OK — script extracts pitch/intensity, calls Python additive engine, and creates output
originalname_ptadd.
pip install numpy scipy soundfile. The script uses phase accumulation (not naive sin(2π·f₀·t)) so frequency modulation and time-varying pitch work correctly. Output duration can be longer or shorter than input (default = input duration). For unvoiced regions (consonants, silence), the voicing policy determines behaviour.
8 Presets
| Preset | Partial Family | Amplitude Law | Partials | Stereo | Voicing | Character |
|---|---|---|---|---|---|---|
| natural_voice | harmonic | 1/k | 24 | mono | copy original | Warm, natural resynthesis — preserves original unvoiced regions. |
| bright_harmonic | harmonic | equal | 32 | Haas | noise | Bright, full spectrum — all partials equal amplitude. |
| hollow_odd | odd_only | 1/k | 16 | mono | silence | Clarinet-like — only odd harmonics. |
| inharmonic_bells | inharmonic_power (β=1.08) | 1/k² | 12 | partial spread | silence | Bell-like — stretched partials, rapid decay. |
| ring_metal | ring_sidebands (120 Hz) | equal | 8 | Haas | silence | Metallic, ring-modulated texture. |
| fm_electric | fm_sidebands (ratio=2, index=3.5) | equal | 8 | Haas | noise | Classic FM synthesis — electric piano / bell tones. |
| subharmonic_bass | subharmonic | 1/k | 8 | mono | noise | Sub-octave bass — reinforces low frequencies. |
| formant_vowel | harmonic | gaussian formant (1200 Hz, BW 400 Hz) | 20 | mono | copy original | Vowel-like formant emphasis. |
Partial Families — Instantaneous Frequency Generation
k = partial index (1, 2, 3, … N)
| harmonic | fₖ(t) = k × f₀(t) | — natural harmonic series |
| odd_only | fₖ(t) = (2k-1) × f₀(t) | — clarinet-like (odd harmonics only) |
| even_only | fₖ(t) = 2k × f₀(t) | — even harmonics only |
| subharmonic | fₖ(t) = f₀(t) / k | — sub-octave series (bass reinforcement) |
| inharmonic_power | fₖ(t) = k^β × f₀(t) | — stretched partials (bell, gong), β ≈ 1.02–1.2 |
| frequency_shifted | fₖ(t) = k × f₀(t) + Δf | — constant frequency shift (transposition) |
| ring_sidebands | fₖ⁺(t) = |k×f₀(t) + fmod|, fₖ⁻(t) = |k×f₀(t) – fmod| | — upper & lower sidebands from ring modulation |
| fm_sidebands | carrier = f₀(t), modulator = r×f₀(t) fₖ(t) = |f₀(t) ± n×r×f₀(t)|, n = 0…N | — Bessel-weighted sidebands, n=0 (carrier), n≥1 (upper/lower pairs) |
Phase accumulation (sample-accurate synthesis)
For each partial k and sample index n:
φₖ[n] = φₖ[n-1] + 2π·fₖ[n] / fs
y[n] = envelope[n] × Σₖ Aₖ[n] × sin(φₖ[n] + θₖ)
This formulation correctly handles time-varying frequencies (vibrato, glissandi) whereas sin(2π·f₀(t)·t) does not.
Amplitude Laws — Per-Partial Weighting
Aₖ ∝ 1/k — natural harmonic decay (~6 dB/octave)
Aₖ ∝ 1/k² — steep high-frequency falloff
Aₖ = 1 — all partials same amplitude (bright, buzzy)
Aₖ = 10^{(tilt × log₂(k)) / 20} — tilt control in dB/octave
Aₖ = exp(-0.5 × ((fₖ – f₀)/BW)²) — Gaussian peak around formant center
Aₖ = random(0.1, 1.0) — fixed random per partial (static)
Aₖ(t) = base + depth × 0.5 × sin(2π·fLFO·t + φ) — time-varying random LFO per partial
Parameters
Core Parameters
| Parameter | Range/Options | Default | Description |
|---|---|---|---|
| Preset | 8 presets + custom | natural_voice | Quick configuration for different musical characters |
| Partial_family | harmonic, odd_only, even_only, subharmonic, inharmonic_power, frequency_shifted, ring_sidebands, fm_sidebands | harmonic | How partial frequencies relate to fundamental |
| Amplitude_law | 1/k, 1/k², equal, spectral_tilt, gaussian_formant, random_static, random_slow | 1/k | Amplitude weighting across partials |
| Num_partials | 1–128 | 16 | Number of sinusoidal partials |
| Pitch_floor/ceiling | 30–600 Hz | 75/600 | Range for pitch tracking (adjust to input source) |
| Voicing_policy | silence_unvoiced, noise_unvoiced, copy_original_unvoiced | silence_unvoiced | Behaviour in unvoiced (non-pitched) regions |
Advanced Parameters (fixed per preset, editable in form)
frequency_shift_hz — constant shift for frequency_shifted family
ring_modulator_hz — modulator frequency for ring_sidebands (80 Hz default)
fm_modulator_ratio — modulator/carrier ratio for fm_sidebands (2.0 default)
fm_index — modulation index (Bessel sideband weighting) for fm_sidebands (2.0 default)
spectral_tilt_db_per_octave — dB/octave slope for spectral_tilt law (-6 default)
formant_center_hz / formant_bw_hz — Gaussian formant peak (1200 Hz / 500 Hz default)
attack_release_smoothing_ms — envelope smoothing (15 ms default)
stereo_mode — mono, haas_width (0.8 ms delay), partial_spread (L/R decorrelation)
normalize_mode — none, peak (scale to -1 dBFS), rms (match input loudness)
Applications
Faithful Resynthesis (natural_voice)
Use case: Reconstruct an acoustic sound with natural harmonic decay and original unvoiced regions (consonants, breath). Output matches input perceptually but allows parameter tweaks.
Settings: partial_family=harmonic, amplitude_law=1/k, num_partials=24, voicing_policy=copy_original_unvoiced.
Harmonic Transformation (odd_only / even_only)
Use case: Clarinet-like (odd harmonics) or flute-like (even harmonics) timbres from any input.
Settings: hollow_odd preset — removes even harmonics, creates hollow, reedy character.
Bell / Inharmonic Textures (inharmonic_bells)
Use case: Transform melodic input into bell-like, metallic sounds with stretched partials.
Settings: inharmonic_power with β=1.08, amplitude_law=1/k² (fast decay), stereo=partial_spread.
FM Synthesis from Acoustic Input (fm_electric)
Use case: Use the pitch curve of a voice or instrument to drive an FM synthesis engine — create electric piano, bell, or brass-like sounds that follow the original pitch contour.
Settings: fm_sidebands, ratio=2.0, index=3.5, amplitude_law=equal, voicing=noise_unvoiced.
Ring Modulation / Metallic Textures (ring_metal)
Use case: Add metallic inharmonic sidebands to any sound.
Settings: ring_sidebands with 120 Hz modulator, amplitude_law=equal, stereo=Haas.
Workflow: Voice → FM Electric Piano
Target: Sung melody (monophonic voice).
Settings: fm_electric preset (ratio=2, index=3.5), num_partials=12, voicing=noise_unvoiced.
Result: The pitch contour of the voice drives FM synthesis — output sounds like an electric piano playing the same melody, with breath replaced by filtered noise.
Workflow: Piano → Bell Choir
Target: Piano recording.
Settings: inharmonic_bells preset (β=1.08), amplitude_law=1/k², stereo=partial_spread, num_partials=16.
Result: Each piano note becomes a bell-like tone with stretched partials and fast decay — a celestial bell choir.
• No output or very quiet: Check F0 tracking — if input has no clear pitch (percussion, noise), the script may output silence. Use noise_unvoiced or copy_original_unvoiced policy.
• Metallic or buzzy sound: Increase num_partials or change amplitude law to 1/k for natural decay. For inharmonic families, reduce β closer to 1.0.
• Clicking artifacts: Increase attack_release_smoothing_ms (15–30 ms) and ensure voiced/unvoiced fade length is adequate.
• FM synthesis sounds wrong: Check that partial_family=fm_sidebands and fm_index is reasonable (1–5). Low index = few sidebands (pure tone), high index = many sidebands (harsh/bright).
• Pitch tracking fails for low/high voices: Adjust pitch_floor and pitch_ceiling to match input. For bass (50–200 Hz), set floor=50, ceiling=300. For soprano (200–800 Hz), set floor=150, ceiling=800.
Voicing Policies Explained
noise_unvoiced: Bandpassed white noise (200–6000 Hz) shaped by envelope replaces unvoiced regions.
copy_original_unvoiced: Original audio is mixed back in only where unvoiced — preserves consonants, breath, and noise while replacing voiced regions with synthesis.
Mathematical Deep Dive
Phase Accumulation vs. Naive Sinusoids
Naive (incorrect for time-varying f): y(t) = sin(2π·f₀(t)·t)
Derivative gives d/dt (f₀(t)·t) = f₀(t) + t·f₀′(t) — extra term causes wrong frequency.
Phase accumulation (correct): φ[n] = φ[n-1] + 2π·f[n] / fₛ
y[n] = sin(φ[n]) — instantaneous frequency = f[n] exactly.
F0 Smoothing Chain
2. Lowpass Butterworth (cutoff = 12 Hz) — smooths vibrato and natural pitch variation.
3. Voiced/unvoiced fade — cosine ramp (5 ms) to avoid clicks at transitions.
Envelope Shaping
Attack/release smoothing: one-pole IIR filter α = exp(-1/(τ·fₛ))
y[n] = (1-α)·x[n] + α·y[n-1]