Harmonic Remover — v1.0 User Guide

Selectively removes the fundamental frequency and/or chosen overtones from a sound. Uses pitch tracking and a bandpass subtraction method (Hann‑windowed frames, 75 % overlap) to isolate and subtract harmonic content, leaving the residual (noise, breath, or remaining partials). Ideal for spectral sculpting, debuzzing, noise extraction, and educational demonstrations.

Author: Shai Cohen Affiliation: Department of Music, Bar‑Ilan University, Israel Version: 1.0 (2025) License: MIT License Repo: GitHub
Contents:

What this does

Harmonic Remover extracts the pitched (harmonic) content from a sound and subtracts it, leaving the residual (noise, breath, or remaining partials). The algorithm:

Why bandpass subtraction instead of a spectral notch? A narrow spectral notch removes only the exact centre frequency, missing vibrato or pitch variation within a frame. Bandpass isolation captures the entire harmonic region (width = ±notch_bandwidth_Hz), then subtracts it, leaving the surrounding noise and other partials intact.

Quick start

  1. In Praat, select exactly one Sound object (mono or stereo – stereo will be downmixed to mono for processing).
  2. Run script…Harmonic_Remover.praat.
  3. Choose a Preset:
    • Remove Fundamental, Remove Odd Harmonics, Remove Even Harmonics, Remove All Overtones (keep F0 only), Remove F0 + All Overtones (noise/breath only), Remove High Harmonics (warm/mellow)
  4. For custom mode (preset = Custom), adjust parameters as desired:
    • Removal_mode – Fundamental only / Selected overtone numbers / All overtones (keep fundamental) / Fundamental + all overtones.
    • Overtone_numbers – comma‑separated list (e.g., 2,4,6) for selected mode.
    • Pitch_floor / Pitch_ceiling – range for F0 detection.
    • Number_of_harmonics – up to 20 partials considered.
    • Analysis_window_ms – frame length (20–200 ms).
    • Notch_bandwidth_Hz – half‑width of the bandpass filter around each harmonic (± value).
    • Min_amplitude_dB – skip frames below this RMS (avoid amplifying noise).
    • Wet_dry_percent – blend between processed (wet) and original (dry).
  5. Click OK. The script analyses pitch, processes frames, and creates a new Sound object named originalname_harmonicRemoved_preset.
Tip: Start with Remove Fundamental to hear the “de‑buzzed” effect (useful for removing hum). Remove All Overtones (keep F0 only) produces a sine‑like tone. Remove F0 + All Overtones extracts the noise/breath residual.
Important: This effect is implemented entirely in Praat – no Python required. Processing time depends on the number of frames and harmonics removed; for long files (e.g., 10 s, 2048 frames, 12 harmonics each), it may take 10–30 seconds.

The 6 presets (+ Custom)

PresetRemoval modeTarget harmonicsResult Remove FundamentalFundamental only1st harmonicRemoves the fundamental, leaving overtones – “hollow” or “bell‑like” texture. Remove Odd HarmonicsSelected overtones3,5,7,9,11Removes odd harmonics (square‑wave‑like filtering). Remove Even HarmonicsSelected overtones2,4,6,8,10Removes even harmonics – results in an “octave‑up” or hollow quality. Remove All Overtones (keep F0 only)All overtones (keep fundamental)2…NOutputs a near‑sinusoidal tone at the fundamental frequency. Remove F0 + All Overtones (noise/breath only)Fundamental + all overtones1…NExtracts the residual – breath, noise, or unvoiced content. Remove High Harmonics (warm/mellow)Selected overtones4…12Keeps harmonics 1‑3, removes higher partials – warm, mellow tone.

Method: bandpass subtraction

Processing pipeline:
  1. Pitch detectionTo Pitch with user‑defined floor/ceiling.
  2. Framing – Hann window, 75 % overlap (hop = win/4). COLA property: sum of windows = 2.0 at every point.
  3. For each voiced frame:
    • For each harmonic to be removed (fundamental and/or selected overtones):
    • Isolate the harmonic using Filter (pass Hann band) with centre = hn × F0, width = 2 × notch_bandwidth_Hz.
    • Subtract the isolated band from the frame.
  4. Unvoiced or low‑amplitude frames pass through unmodified (preserves noise).
  5. Overlap‑add – after OLA, divide by 2.0 for exact unity gain.
  6. Wet/dry mix – blend with original.

Hann bandpass filter is implemented with Praat’s built‑in Filter (pass Hann band), which uses a smooth, zero‑phase filter (no ringing). The transition width is set to notch_bandwidth_Hz / 2 to ensure the filter captures the full harmonic spread while not interfering with neighbouring partials.

📐 COLA normalisation

For a Hann window with 75 % overlap (hop = win/4), the sum of overlapping windows is exactly 2.0 at every sample. After OLA, the output is divided by 2.0 to restore unity gain. This is more accurate than peak‑based normalisation and preserves the original amplitude of non‑processed frames.

Parameters & defaults

Removal mode

ParameterOptionsDescription
Removal_modeFundamental only / Selected overtone numbers / All overtones (keep fundamental) / Fundamental + all overtonesWhich harmonic components to subtract.
Overtone_numberscomma‑separated integersUsed only in “Selected overtone numbers” mode. Example: 2,4,6 removes the 2nd, 4th, and 6th partials.

Pitch detection

ParameterRangeDefaultDescription
Pitch_floor≥ 50 Hz75 HzLowest expected fundamental frequency.
Pitch_ceiling≤ 2000 Hz600 HzHighest expected fundamental frequency.
Number_of_harmonics1–208How many partials to consider (for mode “All overtones” and “F0+all”).

Spectral removal

ParameterRangeDefaultDescription
Analysis_window_ms20–200 ms60 msSTFT frame length. Larger = better frequency resolution, poorer time resolution.
Notch_bandwidth_Hz5–200 Hz60 HzHalf‑width of the bandpass filter around each harmonic. Larger = more aggressive removal (captures vibrato).

Threshold & output

ParameterRangeDefaultDescription
Min_amplitude_dB-∞ … 0-50 dBFrames below this RMS level are not processed (avoid amplifying noise).
Wet_dry_percent0–100100100 = fully processed; 0 = fully original.
Scale_peak0–10.95Normalise output peak to this value.
Draw_visualizationyes/noyesShow 5‑panel visualisation.
Play_resultyes/noyesAuto‑play after processing.

Visualization (5‑panel Praat picture)

When Draw_visualization = 1, the script draws:

Tip: The notch diagram is invaluable for verifying that you’ve selected the correct overtones. Red bands show exactly which frequency regions will be subtracted.

FAQ / troubleshooting

No pitched content detected / all frames unvoiced

Adjust Pitch_floor and Pitch_ceiling to match your source. For speech, 75–600 Hz is typical; for bass instruments, lower the floor to 40 Hz; for high soprano voices, raise the ceiling to 800 Hz. The script prints the median F0 in the Info window – if it’s 0, no pitch was found.

Output has clicks / artefacts

The OLA normalisation (division by 2.0) ensures perfect reconstruction for unprocessed frames. If clicks appear, try reducing Notch_bandwidth_Hz (sharper filters) or increasing Analysis_window_ms (smoother spectral transitions). Also ensure Min_amplitude_dB is not set too high – low‑amplitude frames are passed through unmodified, which can cause discontinuities if they are adjacent to heavily processed frames. A small wet/dry mix (e.g., 80 %) can smooth transitions.

Removal not effective for vibrato / pitch‑varying sounds

The bandpass filter width is centred on the instantaneous F0 per frame. If vibrato is very wide, increase Notch_bandwidth_Hz to capture the full frequency sweep. For very fast pitch changes (e.g., glissandi), consider using a smaller analysis window (20–30 ms) to better track the trajectory.

Why not use a spectral notch (FFT bin zeroing)?

Zeroing FFT bins introduces severe ringing artefacts (pre‑echo/post‑echo) because the abrupt truncation in the frequency domain creates a sinc‑shaped impulse response. The bandpass subtraction method uses a smooth, zero‑phase filter, preserving the time‑domain envelope of the harmonic while removing its energy. It also naturally handles vibrato and pitch variation within the frame.

Processing time

For a 10‑second sound at 44.1 kHz, analysis_window_ms = 60 gives about 665 frames. If removing 12 harmonics per frame, that’s ~8000 bandpass filter calls. Each filter is implemented in Praat’s C‑level code, so it’s reasonably fast (10–30 seconds). The progress indicator updates every 50 frames.

```