Spectral Panning Filter — User Guide

Frequency-based stereo separation: splits audio spectrum at a cutoff frequency, sending low frequencies to left channel and high frequencies to right channel for spectral panning effects.

Author: Shai Cohen Affiliation: Department of Music, Bar-Ilan University, Israel Version: 0.1 (2025) License: MIT License Repo: https://github.com/ShaiCohen-ops/Praat-plugin_AudioTools
Contents:

What this does

This script implements spectral panning — a frequency-based stereo separation technique that splits the audio spectrum at a user-defined cutoff frequency. Process: (1) Takes stereo input (converts mono to stereo if needed), (2) Separates into left/right channels, (3) Converts each channel to frequency domain via FFT, (4) Applies complementary filters: Left channel keeps frequencies < cutoff, Right channel keeps frequencies > cutoff, (5) Reconstructs filtered channels to time domain, (6) Combines into final stereo output with spectral panning effect.

Key Features:

What is spectral panning? Traditional panning: moves entire sound between left/right channels based on amplitude balance. Spectral panning: different frequency ranges placed in different channels. This creates unique spatial effects: Bass appears from left, treble from right (or vice versa). Applications: (1) Creative mixing: Unusual stereo image, (2) Sound design: Artificial width, (3) Analytical: Hearing frequency distribution spatially, (4) Therapeutic: Binaural frequency separation. Unlike EQ which affects both channels equally, spectral panning separates frequencies across the stereo field. The brain perceives this as spatial separation of spectral components rather than simple amplitude panning.

Technical Implementation: (1) Input handling: Stores original duration, copies sound, converts mono→stereo if needed. (2) Channel separation: Extracts left/right channels as separate Sound objects. (3) Frequency domain conversion: Converts each channel to Spectrum with padding ("yes" for FFT windowing). (4) Spectral filtering: Applies complementary formulas: Left spectrum keeps x < freq, zeroes x ≥ freq. Right spectrum keeps x > freq, zeroes x ≤ freq. (5) Reconstruction: Converts filtered spectra back to time domain Sounds. (6) Stereo combination: Combines filtered channels into stereo output. (7) Duration correction: Critical step: FFT padding adds extra samples, so extracts original duration portion using rectangular window. (8) Cleanup: Removes temporary objects, preserves only final result.

Quick start

  1. In Praat, select one Sound object (stereo preferred, mono auto-converted).
  2. Run script…panning_filter.praat.
  3. Set freq parameter (default 500Hz) — crossover frequency.
  4. Click OK — spectral panning applied.
  5. Output named "originalname_panned" appears in Objects window.
  6. Output automatically played for immediate evaluation.
Quick tip: Start with 500Hz crossover for clear bass/treble separation. Use lower frequencies (100-300Hz) to isolate sub-bass in left channel. Use higher frequencies (2000-5000Hz) to isolate high harmonics in right channel. Best results with full-spectrum material (music, speech with wide frequency range). Less effective with narrow-band sounds (sine waves, filtered signals). Listen with headphones for clearest stereo image. Processing adds FFT windowing artifacts at start/end — normal for spectral processing. Output duration matches original despite FFT padding.
Important: SHARP CUTOFF — uses rectangular filters (brickwall), not gentle slopes. Can create phase issues at cutoff frequency. FFT PADDING adds extra samples before/after — script corrects this but initial FFT artifacts may be audible. MONO CONVERSION — mono inputs duplicated to both channels before filtering. SPECTRAL GAP — frequencies exactly at cutoff may be attenuated in both channels (depends on FFT bin alignment). TRANSIENT SMUDGING — FFT processing can blur sharp transients. CHECK MONO COMPATIBILITY — extreme spectral panning may cause cancellation when summed to mono. Always verify on both stereo and mono systems.

Spectral Panning Theory

Frequency Domain Filtering

Spectral Representation

Sound in frequency domain:

Time domain: s_L(t), s_R(t) - left/right channel signals Frequency domain: S_L(f), S_R(f) - complex spectra FFT conversion: time → frequency Spectrum = To Spectrum: "yes" (with windowing) Complex values: real + imaginary components Magnitude represents amplitude at each frequency Phase represents timing relationships This script manipulates magnitude spectra directly Sets certain frequency bins to zero (filtering)

Complementary Filter Design

Left and right channel filters:

Cutoff frequency: f_c (user parameter "freq") Left channel filter (low-pass): H_L(f) = 1 if f < f_c H_L(f) = 0 if f ≥ f_c Right channel filter (high-pass): H_R(f) = 0 if f ≤ f_c H_R(f) = 1 if f > f_c Spectral application: S'_L(f) = S_L(f) · H_L(f) S'_R(f) = S_R(f) · H_R(f) Where S_L, S_R are original spectra S'_L, S'_R are filtered spectra Note: Filters are complementary but not perfect Overlap possible at f_c due to FFT resolution

📐 Filter Visualization

Left channel (low frequencies):

───────████████████████───────

Keeps: 0Hz to (freq-1)Hz

Discards: freqHz to Nyquist

Right channel (high frequencies):

───────████████████████───────

Discards: 0Hz to freqHz

Keeps: (freq+1)Hz to Nyquist

Gap at cutoff: Frequencies near f_c may be attenuated

FFT Processing Details

Windowing and Padding

Praat's FFT implementation:

To Spectrum: "yes" parameter: "yes" = apply Hann window, pad to power of 2 Reduces spectral leakage Adds samples before/after signal Windowing function (Hann): w[n] = 0.5·(1 - cos(2πn/(N-1))) Applied to time domain before FFT Reduces edge artifacts but smears transients Padding: Original N samples → padded to next power of 2 Example: 44100 samples → 65536 (2^16) Extra zeros added Increases frequency resolution but alters duration

Duration Preservation Challenge

The FFT padding problem:

Original sound: N samples at sample rate Fs
Duration = N / Fs seconds

FFT with padding: N' samples (power of 2 ≥ N)
Duration' = N' / Fs seconds
Longer than original due to padding

Solution in script:
1. Store original duration: originalDur = Get total duration
2. Process with padding (FFT needs it)
3. After reconstruction: Extract part: 0, originalDur
4. Rectangular window, no fading

Result: Output matches original timing
Padding artifacts removed
FFT windowing effects still present at edges

Stereo Perception Effects

Spatial Frequency Distribution

How brain interprets spectral panning:

Natural hearing: different frequencies have different spatial cues Low frequencies: less directional (wavelength > head size) High frequencies: more directional (shadowing by head) This script creates artificial version: All low frequencies come from left All high frequencies come from right Perceptual effects: 1. Unnatural but interesting stereo image 2. Can separate musical elements by frequency 3. Bass instruments appear left, cymbals right 4. Vocal sibilance (high frequencies) separated from fundamental Mono compatibility issue: When L+R summed: full spectrum restored But phase issues may cause cancellations

Cutoff Frequency Selection

Psychoacoustic considerations:

Human hearing range: 20Hz - 20kHz Critical bands: frequency resolution varies Recommended cutoff frequencies: 100-300Hz: Isolate sub-bass (kick, bass guitar) 500-800Hz: Speech fundamental separation 1000-2000Hz: Musical instrument separation 3000-5000Hz: Brightness/sibilance separation 8000Hz+: Ultra-high frequency effects Too low (<100Hz): mostly inaudible separation Too high (>10000Hz): limited effect (few components) Musical context matters: Bass-heavy music: higher cutoff (500-1000Hz) Speech/podcast: lower cutoff (200-500Hz) Full-range music: 800-2000Hz

Mathematical Implementation

Spectral Filtering Formulas

Praat Formula syntax:

Left channel spectrum: Formula: "if x < freq then self else 0 fi" Right channel spectrum: Formula: "if x > freq then self else 0 fi" Where: x = frequency value (Hz) for each FFT bin freq = cutoff frequency parameter self = original complex spectrum value 0 = zero (remove frequency component) Important: "if x < freq" not "≤" Left: keeps frequencies strictly less than freq Right: keeps frequencies strictly greater than freq Gap at exactly freq (depends on bin alignment) Complex values handled correctly: Sets both real and imaginary parts to zero Equivalent to removing frequency component entirely

FFT Bin Alignment

Discrete frequency bins:

FFT produces discrete frequency bins: bin k represents frequency f_k = k·Fs/N' Where: Fs = sample rate N' = FFT size (power of 2) k = 0...(N'/2) (DC to Nyquist) Cutoff freq may fall between bins: Example: Fs=44100, N'=65536, freq=500Hz Bin width = 44100/65536 ≈ 0.673Hz 500Hz corresponds to bin ≈ 743.2 Not integer → cutoff between bins 743 and 744 Praat's "x" in Formula = continuous frequency But filtering applies to entire bins Result: transition not exactly at freq

Processing Workflow

Step-by-Step Processing

📥 Step 1: Input Preparation

Actions:

  • Store original name and duration
  • Create temporary copy of sound
  • Check channel count
  • Convert mono to stereo if needed

Code:

originalID = selected("Sound") originalName$ = selected$("Sound") originalDur = Get total duration Copy: "tmp" numChan = Get number of channels if numChan = 1 stereoID = Convert to stereo tmpSound = stereoID endif

🎛️ Step 2: Channel Separation

Actions:

  • Extract left and right channels as separate Sounds
  • Prepare for independent processing

Code:

Extract all channels ch1 = selected("Sound", 1) # Left channel ch2 = selected("Sound", 2) # Right channel

📊 Step 3: Frequency Domain Conversion

Actions:

  • Convert each channel to Spectrum with windowing
  • Apply FFT with padding

Code:

selectObject: ch1 spectrum_ch1 = To Spectrum: "yes" # Windowing applied selectObject: ch2 spectrum_ch2 = To Spectrum: "yes"

✂️ Step 4: Spectral Filtering

Actions:

  • Apply low-pass filter to left channel spectrum
  • Apply high-pass filter to right channel spectrum
  • Complementary filtering creates panning effect

Code:

# Left channel: keep frequencies < cutoff Formula: "if x < freq then self else 0 fi" # Right channel: keep frequencies > cutoff Formula: "if x > freq then self else 0 fi"

🔄 Step 5: Time Domain Reconstruction

Actions:

  • Convert filtered spectra back to Sounds
  • Inverse FFT transforms frequency→time

Code:

selectObject: spectrum_ch1 sound_ch1 = To Sound # Inverse FFT selectObject: spectrum_ch2 sound_ch2 = To Sound

🎧 Step 6: Stereo Combination & Duration Correction

Actions:

  • Combine filtered channels into stereo sound
  • Extract original duration to remove FFT padding
  • Rename with descriptive name

Code:

selectObject: sound_ch1 plusObject: sound_ch2 combinedID = Combine to stereo # Critical: remove FFT padding finalSound = Extract part: 0, originalDur, "rectangular", 1, "no" Rename: originalName$ + "_panned"

🧹 Step 7: Cleanup & Playback

Actions:

  • Remove temporary objects
  • Play result for immediate evaluation
  • Leave final sound selected

Code:

selectObject: tmpSound plusObject: ch1 plusObject: ch2 plusObject: spectrum_ch1 plusObject: spectrum_ch2 plusObject: sound_ch1 plusObject: sound_ch2 plusObject: combinedID Remove selectObject: finalSound Play

Flowchart Summary

INPUT → [Store duration & name] → [Copy sound] ↓ [Check channels: mono?] → Yes → [Convert to stereo] ↓ [Extract L/R channels separately] ↓ ↓ [ch1: To Spectrum] [ch2: To Spectrum] ↓ ↓ [Filter: f < cutoff] [Filter: f > cutoff] ↓ ↓ [To Sound] [To Sound] ↓ ↓ [Combine to stereo] → [Extract original duration] → [Rename] ↓ OUTPUT: originalname_panned ↓ [Play] → [Cleanup temporary objects]

Parameters & Effects

Core Parameter

ParameterTypeDefaultDescription
freqpositive500Cutoff frequency in Hz (crossover point)

Frequency Range Recommendations

ApplicationFrequency RangeEffect Description
Sub-bass isolation80-150 HzExtreme low frequencies in left channel only
Bass separation150-300 HzBass instruments left, everything else right
Voice/music separation300-800 HzVoice fundamentals left, harmonics right
Mid-range panning800-2000 HzMusical instrument separation
Brightness separation2000-5000 HzHigh harmonics/sibilance in right channel
Air/brilliance5000-10000 HzUltra-high frequencies isolated right

Automatic Parameters

Script-managed parameters (not user adjustable):
  • Windowing: Always "yes" (Hann window applied)
  • FFT size: Automatic power-of-2 padding
  • Extraction method: "rectangular" window (no fade)
  • Preserve times: "no" (extract from time 0)
  • Channel handling: Auto mono→stereo conversion

Expected Effects by Frequency

🔊 100 Hz Cutoff

Left channel: Only sub-bass (kick drum fundamental, bass guitar rumble)

Right channel: Everything else (mid/high frequencies)

Perception: Extreme separation, bass completely isolated left

Best for: Experimental effects, bass emphasis

🎤 500 Hz Cutoff (Default)

Left channel: Bass and lower midrange (fundamentals)

Right channel: Upper midrange and highs (harmonics, brightness)

Perception: Natural-sounding separation, good for music

Best for: General purpose, musical material

🎸 2000 Hz Cutoff

Left channel: Most musical fundamentals (bass, guitar, voice body)

Right channel: High harmonics, cymbals, sibilance

Perception: Brightness separated right, body left

Best for: Enhancing stereo width without losing center

Applications

Creative Mixing

Use case: Unconventional stereo imaging for artistic effect

Technique: Use moderate cutoff (500-1000Hz) on full mixes

Example: Entire track processed → bass elements left, vocals/cymbals right

Sound Design

Use case: Creating artificial width from mono sources

Technique: Process mono sounds with varying cutoff frequencies

Workflow:

Analytical Listening

Use case: Hearing frequency distribution spatially

Technique: Sweep cutoff frequency while listening

Application: Identify frequency ranges of different instruments

Educational Tool

Use case: Teaching frequency perception and stereo imaging

Advantages:

Therapeutic Applications

Use case: Binaural frequency separation for audio therapy

Technique: Different frequencies to different ears

Example: Soothing low frequencies left, stimulating highs right

Practical Workflow Examples

🎵 Music Production - Wide Stereo Bass

Goal: Create unusual stereo image with bass isolated

Settings:

  • Cutoff: 200 Hz
  • Process: Full music track

Result: Kick/bass completely left, everything else right

Creative use: Blend with dry signal for enhanced width

🎤 Vocal Processing - Sibilance Control

Goal: Separate vocal sibilance from body

Settings:

  • Cutoff: 4000 Hz
  • Process: Vocal track only

Result: Vocal body left, sibilance/air right

Creative use: De-ess by attenuating right channel

🎧 Headphone Testing - Frequency Check

Goal: Test headphone frequency response balance

Settings:

  • Cutoff: 1000 Hz
  • Process: Pink noise

Result: Low/mids left, highs right

Diagnostic use: Identify L/R imbalances in playback system

Advanced Techniques

Layered processing:
  • Multiple cutoffs: Process same sound with different frequencies
  • Inverted panning: Modify script to swap L/R filtering
  • Frequency sweep: Automate cutoff over time
  • Mid-side conversion: Process M/S encoded material

Combine with other processing for complex spectral effects

Script modifications:
  • Gentler slopes: Change rectangular filters to gradual
  • Band-pass variants: Keep mid frequencies, discard extremes
  • Multi-band: Split into more than two bands
  • Dynamic cutoff: Cutoff follows envelope or pitch

Customize for specific applications

Troubleshooting Common Issues

Problem: Output has clicks/pops at edges
Cause: FFT windowing artifacts, rectangular extraction
Solution: Normal, expected with FFT processing. Can fade in/out manually if problematic.
Problem: Effect too subtle
Cause: Cutoff too high/low for material, narrow frequency content
Solution: Try different cutoff, use full-spectrum source material
Problem: Phase cancellation in mono
Cause: Extreme spectral separation, FFT phase shifts
Solution: Check mono compatibility, use moderate cutoff
Problem: Output longer than input
Cause: Script bug - duration extraction not working
Solution: Ensure "Extract part: 0, originalDur" executes correctly

Comparison with Other Techniques

TechniqueMethodProsConsBest For
Spectral Panning (this script)Frequency-based L/R separationUnique effect, frequency controlPhase issues, FFT artifactsCreative effects, sound design
Amplitude PanningVolume balance between channelsNatural, standard techniqueAll frequencies move togetherTraditional mixing
Haas EffectTime delays between channelsWide stereo, psychoacousticMono compatibility issuesWidening, spatial effects
Mid-Side ProcessingM/S encoding then processingFlexible, preserves monoMore complex setupProfessional mixing

Technical Notes

FFT Considerations

Windowing Trade-offs

Hann window effects:

Pros of windowing ("yes" parameter): Reduces spectral leakage Smoothes FFT artifacts Better frequency resolution Cons of windowing: Smears transients (temporal blurring) Reduces amplitude (window gain) Alters start/end of signal Alternative: To Spectrum: "no" No windowing, rectangular window Preserves transients better But more spectral leakage Worse frequency localization This script uses "yes" for cleaner filtering Trade-off: some transient smearing acceptable

Padding and Duration

Why padding matters:

FFT requires power-of-2 length:
Input: N samples (any number)
FFT: N' = next power of 2 ≥ N
Example: 30000 samples → 32768 (2^15)
2776 zero samples added (padding)

Duration change:
Original: duration = N/Fs
Padded: duration' = N'/Fs > duration

Script solution:
1. Store originalDur = N/Fs
2. Process with padding (needed for FFT)
3. Extract: 0 to originalDur (removes padding)

Edge effects:
Windowing affects start/end anyway
Padding removal creates sharp cut
May cause clicks at boundaries

Filter Characteristics

Rectangular Filter Response

Frequency and time domain effects:

Frequency domain: ideal brickwall filter Perfect rejection above cutoff No transition band Linear phase (all frequencies delayed equally) Time domain: sinc function impulse response sin(πf_c t) / (πt) shape Infinite duration in theory Ringing before/after transients Practical implementation (FFT): Finite FFT size → windowed sinc Some transition band due to bin discretization Limited time smear but still present Result: some pre-echo and ringing Especially noticeable with sharp transients Musical material often masks this

Phase Response

Linear phase characteristic:

FFT filtering preserves linear phase: All frequency components delayed equally Waveform shape preserved (just delayed) No phase distortion within passband Delay amount: approximately N'/(2·Fs) seconds Where N' = FFT size Example: Fs=44100, N'=65536 → delay ≈ 0.74s But script extracts original duration: Removes this delay However, windowing affects start/end phases Phase at cutoff: Sharp transition causes phase rotation May create cancellations at crossover