Sidechain Feedback VCA — User Guide

Buffer-domain feedback resynthesis driven by a controller Sound: pitch can steer a moving resonance, intensity shapes calibrated in-loop and post-loop VCAs, selectable excitation restores articulation when desired, and every iteration is re-levelled to a stable working peak.

Author: Shai Cohen Affiliation: Department of Music, Bar-Ilan University, Israel Version: 0.6.1 (2026) License: MIT License Repo: Praat AudioTools
Contents:

What this does

Sidechain Feedback VCA v0.6.1 uses one selected Sound as a controller for an iterative resonant resynthesis process. It extracts a pitch representation and a relative intensity envelope, builds an excitation buffer from noise, the controller, or both, then repeatedly filters and feeds the whole buffer back into a new whole-buffer pass. A calibrated output VCA restores the requested audible sidechain depth after the iterative stage.

The v0.6 gain structure deliberately separates level stability from timbre. Every iteration is re-levelled to a fixed working peak of 0.6; Base_Feedback, Damping_Factor, Drive, filter bandwidth and iteration count therefore shape the spectrum and nonlinear character without allowing uncontrolled exponential growth from pass to pass.

Terminology: this is not a physical no-input mixer model and not sample-to-sample feedback. There is no feedback delay and no previous-sample loop. Iterations is an outer computational axis: each pass processes the entire Sound. “Buffer-domain feedback resynthesis” is the accurate description.
Controller audibility depends on Excitation and Dry/Wet. With Noise seed only and Dry_Wet = 0, the direct controller waveform is absent from the rendered path. With Controller into the loop or Both, the controller itself excites the iterative process, so its attacks, rhythm and pitched content influence the wet signal even when the final dry mix is zero.

Quick start

  1. Select exactly one Sound in the Praat Objects window.
  2. Run Sidechain_Feedback_VCA.praat.
  3. Dialog 1 sets the preset, excitation source, resonance tracking, circuit controls, sidechain depth, dry/wet and harmonic-air exciter.
  4. Dialog 2 sets the spatial mode, multichannel policy, output-level policy, fades, random seed, visualization, playback and debug logging.
  5. Click Continue. The script analyses the controller, builds the excitation, runs the whole-buffer iterations, applies the audible sidechain VCA, spatializes, performs dry/wet and exciter processing, applies the output policy and leaves the final Sound selected.
Current defaults: Custom preset, Both excitation, Follow pitch contour, 24 iterations, Drive 0.45, 24 dB output sidechain depth, Stereo Wide, and Match loudness (RMS target, peak-guarded) at −20 dBFS RMS.

Controller analysis

Pitch analysis

Pitch analysis: To Pitch: 0.0, 75, 600 mean_Pitch = file-level mean F0 nominal resonance = mean_Pitch + Frequency_Offset_Hz

Multichannel controllers are converted to mono for pitch analysis. Praat's Pitch contour is interpolated and smoothed; unvoiced gaps are sample-and-held from the last usable value, while only a leading gap falls back to the file mean. The contour is then resampled to the audio sampling rate.

If no pitch is detected, the file-level mean falls back to 100 Hz. If the file is too short for the Pitch/Intensity analysis windows, pitch also falls back to 100 Hz and the intensity envelope is held at 1.

Fixed resonance versus pitch-contour tracking

The tracking filter mixes the current buffer down with the integrated pitch phase, low-passes its in-phase and quadrature components, then mixes them back up. This makes the resonance centre move with the controller rather than using one FFT band for the whole file. Tracking costs an additional filter pass per iteration and is therefore slower than Fixed mode.

Intensity envelope

Intensity analysis: To Intensity: 100, 0, "yes" maxDb = loudest analysed moment floorDb = maxDb - Envelope_range_dB env(t) = clamp((dB(t) - floorDb) / Envelope_range_dB, 0, 1)

The dB-to-0…1 mapping is performed on the original low-rate Intensity-derived signal before resampling to audio rate. This avoids ringing caused by resampling very large dB discontinuities and then clipping the overshoot.

The mapping is intentionally relative to the loudest moment in the selected file. It follows dynamics within that file rather than absolute level between files. If the analysed maximum is below −150 dB, the envelope is held at 0.

Two calibrated sidechain paths

In-loop depth: loopDepthDb = Input_Sensitivity * 30 perIterDb = loopDepthDb / Iterations loopVca(t) = 10^((env(t)-1) * perIterDb / 20) Audible post-loop VCA: outVca(t) = 10^((env(t)-1) * Sidechain_depth_dB / 20)

Input_Sensitivity no longer acts as a signed linear multiplier. It sets an accumulated in-loop modulation depth in dB; dividing that depth by the iteration count prevents the envelope from being exponentiated by repeated passes. Sidechain_depth_dB is a separate post-loop gain contour and is the main control for audible sidechain depth.

With positive depths, the loudest envelope value (1) is unity gain and quieter values are attenuated. Negative values invert that relationship: quiet portions are boosted relative to the loudest moments.

Excitation

The iterative loop must start from an explicit signal. v0.6.1 provides three choices:

Excitation sourceBehavior
Noise seed onlyCreates Gaussian noise, filters it around the effective resonance region, scales it to peak 0.5, duplicates it to stereo, and uses it as the loop seed. This is the closest option to the earlier no-input aesthetic.
Controller into the loopCopies the controller, converts non-stereo material to mono then stereo, scales it to peak 0.5, and uses it as the loop excitation. Attacks, rhythm and pitched content therefore enter the iterative circuit directly.
BothBuilds both paths. The noise is additionally shaped by 0.3 + 0.7*env(t), then noise and controller excitation are mixed 50/50 and the combined excitation is re-scaled to peak 0.5.

In tracking mode the noise-seed band is derived from the actual offset/clamped resonance trajectory; in Fixed mode it is derived from the nominal resonance centre. The final seed edges are clipped to 20 Hz…Nyquist.

Buffer-iteration process

Drive = 0 is a genuine linear bypass of the nonlinear stage. For every Drive value above 0, the arctan waveshaper is active; increasing Drive increases its curvature and lowers the asymptotic soft-clip ceiling.

Analog_Instability changes the band between whole-buffer passes, not continuously over playback time. In Fixed mode it jitters the centre in Hz and the bandwidth. In tracking mode the F0 trajectory itself moves over audio time, while each iteration can additionally multiply that trajectory by a small drift factor; unsafe upward drift is capped against Nyquist and reported.

Because each non-silent iteration is re-levelled to peak 0.6, the pre-relevel peak and RMS are diagnostic measures of how the loop would have grown or shrunk on that pass. The re-level is intentionally part of the v0.6 musical design.

Parameters & presets

Dialog 1 — Circuit & Resonance

ParameterDefaultMeaning
PresetCustomCustom or one of four retuned v0.6 recipes. Named presets override only the circuit parameters listed in the preset table.
Excitation_SourceBothNoise seed only, Controller into the loop, or Both.
Resonance_TrackingFollow pitch contourFixed mean-F0 resonance or audio-rate F0-following heterodyne resonance.
Base_Feedback0.8Gain of the filtered return branch before per-pass re-levelling.
Input_Sensitivity0.5Sets accumulated in-loop sidechain depth: Input_Sensitivity × 30 dB. Negative values invert the envelope relationship.
Envelope_range_dB40 dBRelative dB range below the file's loudest intensity mapped to 0…1.
Damping_Factor0.92Gain of the current unfiltered buffer in each pass.
Iterations24Number of whole-buffer passes. This is a computational/timbral depth, not elapsed feedback time.
Drive0.450 = linear bypass; values above 0 activate increasingly strong arctan soft saturation. Custom values outside 0…1 are rejected.
Frequency_Offset_Hz0Added to mean F0 in Fixed mode and to the F0 trajectory in tracking mode.
Bandwidth_Hz180 HzNominal resonance width; per-pass width can drift with Analog Instability.
Analog_Instability0.05Amount of random pass-to-pass frequency/width drift. Custom negative values are rejected.
Sidechain_depth_dB24 dBAudible post-loop VCA depth. Positive attenuates quieter envelope values; negative inverts the dynamics relationship.
Dry_Wet0.30 = generated/iteration path only; 1 = dry controller path only before any post-mix exciter and final output stage.
Exciter_positionAfter dry/wet mixRun the harmonic-air exciter after the final mix or on the wet path only.
High_Freq_Add0.3Amount of RMS-matched synthetic harmonic air. Values ≤0 disable the exciter.

Named presets

PresetBaseSensitivityDampingIterationsBandwidthInstabilityDrive
Gentle Resonance0.500.300.9018260 Hz0.030.25
Aggressive Feedback0.950.700.8030120 Hz0.080.70
Deep Iteration0.750.400.9560300 Hz0.020.35
Unstable Burst0.950.900.752690 Hz0.180.85
Preset scope: named presets override Base_Feedback, Input_Sensitivity, Damping_Factor, Iterations, Bandwidth_Hz, Analog_Instability and Drive. They do not override excitation source, resonance-tracking mode, envelope range, frequency offset, sidechain depth, dry/wet, exciter controls, spatial/output settings or random seed.

Dialog 2 — Spatial, Output & Debug

ParameterDefaultMeaning
Spatial_ModeStereo WideMono, Stereo Wide, Rotating, or Pseudo-Binaural (Delay/Filter).
Stereo_spread_percent1.2%Detune percentage for Stereo Wide. Values above 95% are capped at 95% and reported.
Interaural_delay_ms0.68 msRight-channel delay in Pseudo-Binaural mode, rounded to integer samples with a minimum of one sample.
Multichannel_policyDownmix to mono, then duplicateControls how a >2-channel controller is handled for the dry path; Refuse more than 2 channels aborts the run.
Output_modeMatch loudness (RMS target, peak-guarded)Legacy staged peak normalization, final-only peak normalization, preserve level, or RMS matching with a peak guard.
Target_RMS_dBFS−20 dBFSRMS target used only by Output mode 4.
Output_Gain1.0Positive master gain applied after normalization/RMS matching and after edge fades.
Fade_ms15 msHalf-cosine fade-in and fade-out. Values ≤0 disable fades; positive fades are capped at one third of the Sound duration.
Random_seed0Positive = reproducible random draws; 0 or below = unpredictable initialization.
Draw_visualizationOnDraws the measurement-based v0.6.1 diagnostic page.
Play_resultOnPlays the final selected Sound.
DebugOffWrites selected per-stage and per-iteration diagnostics to the Info window.

Dry/wet mixing and harmonic-air exciter

Dry/wet

result = wet * (1 - Dry_Wet) + dry_controller * Dry_Wet

The wet result is mono only in Spatial Mode = Mono; every other spatial mode produces stereo. The dry controller is converted as needed to match that output channel count.

For controllers above two channels, Multichannel_policy controls the dry-path conversion. Use the first two channels extracts channels 1 and 2 for a stereo dry result; the default downsamples the dry controller to mono and duplicates it when stereo is required. Refuse more than 2 channels stops the script before processing.

Controller excitation is separate from the dry-path policy: when the controller itself excites the loop and it is not already stereo, the excitation path is converted to mono and then stereo. The “first two channels” policy applies to the dry mix, not to that excitation conversion.
Dry_Wet = 1 is not automatically a sample-exact bypass. A post-mix exciter changes the spectrum; normalization/RMS matching changes level; Mono or stereo conversion can change the channel layout; fades and Output_Gain are still applied. For an unprocessed same-layout dry path, use the wet-path-only exciter, Preserve rendered level, Fade_ms ≤ 0, Output_Gain = 1, and a spatial/channel configuration that leaves the dry signal's channel layout unchanged.

Harmonic-air exciter

The v0.6 exciter is a band-limited cubic waveshaper rather than the old ring-modulation whistle. It generates harmonically related high-frequency energy and RMS-matches that component to the signal before applying High_Freq_Add.

source RMS = RMS(target) low-pass target to Nyquist/3 if non-silent: scale low-passed copy to peak 0.9 cubic = lowpassed^3 hpLow = clamp(2 * nominal_resonance, minimum 1200 Hz, maximum 0.6*Nyquist) high = Hann-band-pass(cubic, hpLow ... Nyquist) if both signals non-silent: high *= source_RMS / high_RMS result = target + high * High_Freq_Add

The exciter's high-pass reference uses the nominal resonance centre (mean F0 + offset), not the moving pitch contour. Low-passing to Nyquist/3 before cubing keeps the third-order products inside the available band.

Spatial modes

Four post-processing modes

Mono: converts the wet stereo loop to mono.

Stereo Wide: creates a detuned twin-resonance treatment around the nominal resonance centre.

Rotating: converts the wet path to mono, then pans that single signal at 0.2 Hz with a constant-power cosine/sine law.

Pseudo-Binaural (Delay/Filter): uses asymmetric filtering plus an integer-sample right-channel delay. It is not an HRTF renderer.

Stereo Wide

det = Stereo_spread_percent / 100 left centre = nominal_resonance * (1 - det) right centre = nominal_resonance * (1 + det) Each original channel is mixed with a Hann-band-filtered copy: wide = original*0.6 + resonance_band*0.8

The detune percentage is capped at 95%. Effective centres are constrained below Nyquist and the filter edges are clipped to 20 Hz…Nyquist. This is a post-processing twin-resonance effect based on the nominal mean-F0 centre; it does not itself follow the time-varying tracking contour.

Rotating

theta(t) = pi/4 + (pi/4)*sin(2*pi*0.2*(t - sourceStart)) Left = mono * cos(theta) Right = mono * sin(theta)

Because both channels are derived from one mono wet source, cos²(theta) + sin²(theta) = 1 gives a true constant-power pan law. The phase uses time relative to the Sound's start, so changing a Praat object's absolute xmin does not change where the rotation begins.

Pseudo-Binaural (Delay/Filter)

The left channel is Hann-band-filtered approximately 50…3000 Hz, limited by Nyquist. The right channel is delayed by round(Interaural_delay_ms/1000 * samplingRate) samples (minimum one sample), then filtered approximately 200…6000 Hz, also limited by Nyquist. The Sound duration is preserved: zeros enter at the beginning of the delayed channel and the displaced tail is not extended beyond the existing time domain.

Output level, fades and reproducibility

Output modes

ModeBehavior
Normalize each stage to 0.95 (v0.3 legacy)Peak-normalizes the wet render when non-silent; normalizes again after a wet-path exciter, the dry path before mixing, after dry/wet mixing, and after a post-mix exciter where those stages occur. This deliberately suppresses intermediate level differences.
Normalize only at the endLeaves intermediate stages intact, then scales the pre-fade final render to peak 0.95 if non-silent.
Preserve rendered levelDoes no peak or RMS normalization. The rendered level passes directly to fades and Output Gain.
Match loudness (RMS target, peak-guarded)Scales the pre-fade render to Target_RMS_dBFS. If that produces a peak above 0.98, the Sound is instead peak-scaled to 0.98. This is the default mode.
Ordering matters: output normalization or RMS matching happens before edge fades, and Output_Gain happens after the fades. Therefore the final measured RMS may differ slightly from Target_RMS_dBFS, and Output Gain can raise the final peak above the normalization/peak-guard value.

Edge fades

Positive Fade_ms applies symmetric half-cosine fades at the beginning and end to avoid discontinuities from a steady-state iterative buffer. The effective fade is limited to at most one third of the Sound duration. Zero or negative values skip the fades.

Output object

Random seed

Randomness affects the noise excitation and iteration-domain spectral drift. A positive Random_seed initializes Praat predictably; the same input and settings then reproduce the random sequence. A value of 0 or below uses unpredictable initialization.

Visualization

The v0.6.1 figure is measurement-based; the former block diagram has been removed. It uses mono analysis copies for waveform/spectral measurements so stereo channel stacking does not distort the overlays.

  1. Controller waveform + sidechain envelope: the controller waveform is drawn on its own amplitude scale; the blue 0…1 sidechain envelope is overlaid with a separate control axis.
  2. Result waveform + RMS envelope: the result waveform is drawn on its own amplitude scale and the measured windowed RMS envelope is drawn above and below zero on that same amplitude scale. The label is literal RMS; no ×√2 conversion is used.
  3. Resonance trajectory: tracking mode plots the actual base trajectory after F0 + offset and Nyquist clamp, with the nominal bandwidth shaded. Fixed mode plots the constant mean F0 + offset. Per-iteration drift is not folded into this time-domain curve; it is shown in Panel 4.
  4. Iteration convergence: pre-relevel loop peak and RMS are each normalized to their own run maxima; a small green trace shows the measured band-centre drift across iterations. This panel is diagnostic and its vertical axis is normalized, not Hz or linear amplitude.
  5. Input/output spectra: power density is averaged in 130 logarithmically spaced frequency bands and drawn on a shared dB/Hz range over a logarithmic frequency axis.

A summary strip reports the input, circuit, sidechain and output state. Do not compare the visual height of the controller and result waveforms as a gain measurement: those two waveform panels use separate vertical amplitude ranges.

Limitations and interpretation