Adaptive Grain Cloud Synthesis — User Guide

Granular resynthesis with sample-quantized event scheduling, stochastic source-position and pitch variation, optional content-adaptive grain duration, random grain reversal, and multi-track overlap-add rendering.

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

What this does

Adaptive Grain Cloud Synthesis resynthesizes the selected Sound as a cloud of short, overlapping grains. The source is converted to mono, grains are read from positions that follow the source from beginning to end (or remain fixed for the Spectral Freeze preset), and each grain is placed on an exact sample-quantized output event grid.

The cloud can vary in density, overlap, source-position scatter, pitch scatter, grain duration, and direction. When Adaptive_duration is enabled, grain duration responds to the source's spectral-centroid profile: relatively darker regions produce longer grains and relatively brighter regions produce shorter grains. This adaptation affects duration only; pitch scatter remains controlled separately by Pitch_scatter_semitones.

Important distinction: the script does not analyze a new spectrum for every individual grain. It pre-analyzes the source in a small set of Hanning-windowed regions, measures spectral centroid for those regions, and uses the corresponding regional value when each grain is scheduled.

What is a grain cloud?

A grain is a short excerpt of sound. A grain cloud is a collection of many such excerpts whose start times, source positions, durations, pitch shifts, and directions can vary. When grains overlap densely, the individual fragments merge perceptually into a continuous texture; with lower density or larger spacing, the individual events remain more audible.

Grain anatomy in this script

Source interval: a short Hanning-windowed excerpt from the mono source.

Output event: a sample-quantized onset on the cloud timeline.

Optional transformations: adaptive duration, Gaussian pitch scatter, source-position scatter, and 50% random reversal when enabled.

Cloud rendering: grains are distributed over several non-overlapping tracks and the tracks are summed.

Quick start

  1. In Praat, select exactly one Sound object.
  2. Run Adaptive_Grain_Cloud_Synthesis.praat.
  3. Choose a preset, or keep Custom.
  4. Set Grain_size_ms, Grain_overlap, and Density.
  5. Use Pitch_scatter_semitones for random per-grain pitch variation and Position_scatter for random variation of the source read position.
  6. Enable Adaptive_duration if you want spectral centroid to control grain duration.
  7. Enable Reverse_random if approximately half of the grains should be reversed.
  8. Set Output_duration_factor to define the requested cloud duration relative to the source.
  9. Click OK. The output is created as <source>_grainCloud.
Useful starting point: Custom defaults are 50 ms grains, 0.5 overlap, density 2.0, no pitch scatter, position scatter 0.2, adaptive duration on, and random reversal off.

Presets

Choosing a named preset overrides the grain, scatter, processing, and output-duration values shown below. The preset does not change Draw_visualization or Play_result.

PresetGrainOverlapDensityPitch σPosition scatterAdaptiveReverseDuration
Dense Cloud40 ms0.73.00.0 st0.1OnOff1.0×
Sparse Cloud100 ms0.40.50.5 st0.3OnOn1.0×
Micro-Grains10 ms0.55.01.0 st0.1OffOff1.0×
Long Grains200 ms0.81.50.0 st0.05OnOff1.0×
Spectral Freeze80 ms0.92.00.0 st0.0OffOff2.0×
Rhythmic Scatter30 ms0.02.00.2 st0.5OffOff1.0×
Chaotic Swarm25 ms0.64.02.0 st0.8OnOn1.5×
Time Stretch 2x60 ms0.751.00.0 st0.0OnOff2.0×
Time Compress 0.5x40 ms0.71.00.0 st0.0OnOff0.5×
Spectral Freeze: this preset does not perform an FFT freeze. It freezes the source read pointer at the midpoint of the valid grain-start range and repeatedly granulates that region for a 2× output duration.

Adaptive duration

When Adaptive_duration is enabled, the script divides the source into a small number of analysis windows: 20 for files of at least 1 second, 10 for files shorter than 1 second, and 5 for files shorter than 0.25 seconds. Each window is Hanning-windowed, transformed to a Spectrum, and measured with Get centre of gravity: 2.

centroid range = maxC - minC if range < 100 Hz: duration multiplier = 1.0 otherwise: normC = (localCentroid - minC) / (maxC - minC) duration multiplier = 1.4 - 0.8 × normC therefore: darkest analyzed region -> 1.4× base grain duration brightest analyzed region -> 0.6× base grain duration

The mapping is relative to the spectral-centroid range of the selected source, not to a fixed brightness threshold such as 2000 Hz. A source with little centroid variation uses the neutral 1.0× duration multiplier.

Scheduling & rendering

Event rate

baseHop = grainDuration × (1 - overlap) requestedEventHop = baseHop / density

Density therefore changes the actual event rate. At density 2, the requested event hop is half the base hop; at density 0.5 it is twice the base hop.

Sample-quantized scheduler

The requested event hop is rounded to an integer number of samples. A minimum event hop of 1 ms is enforced. The scheduler may further increase the hop when necessary to respect two internal safety limits: at most 10,000 grains and at most 24 tracks. If this happens, the Info window reports that the effective density was reduced.

Source traversal and position scatter

Outside Spectral Freeze, the nominal source read position travels linearly from the beginning toward the last valid base-grain start as output time advances. Position_scatter adds Gaussian displacement to that source read position:

source-position sigma = Position_scatter × sourceDuration × 0.15

The result is clamped to the valid source range. Position scatter does not jitter output event times.

Pitch scatter

Each grain receives a Gaussian random pitch shift with standard deviation Pitch_scatter_semitones. The draw is bounded to ±3σ and also to an absolute maximum of ±24 semitones. Shifts whose absolute value is 0.1 semitone or less are left unchanged.

Pitch shifting is implemented by overriding the grain sampling frequency by 2^(shift/12) and resampling back to the source rate. Because this method changes the grain's sample count, pitch shifting also changes grain duration.

Tracks and mixing

Scheduled grains are interleaved over enough tracks that grains assigned to the same track do not overlap. Each grain is followed by silence to fill one exact track slot, every track is forced to the exact requested output sample count, and all tracks are then summed. Before summing, each track is scaled by 1 / sqrt(number_of_tracks).

Parameters

ParameterDefaultBehavior
PresetCustomSelects Custom or one of nine named parameter sets.
Grain_size_ms50Base grain duration. Must fit inside the source.
Grain_overlap0.5Range 0–0.9. Defines base hop before Density is applied.
Density2.0Divides the base hop. Positive value; effective density may be reduced by safety limits.
Pitch_scatter_semitones0.0Gaussian σ in semitones; must be non-negative.
Position_scatter0.2Range 0–1. Gaussian scatter of the source read position.
Adaptive_durationOnMaps regional spectral centroid to 0.6×–1.4× grain duration.
Reverse_randomOffWhen enabled, each grain has a 50% chance of reversal.
Output_duration_factor1.0Requested output sample count = round(source samples × factor).
Draw_visualizationOnDraw source, grain time map, output, and summary.
Play_resultOnAutomatically plays the resulting Sound.
Input format: the selected Sound is converted to mono before analysis and synthesis. The resulting grain cloud is therefore mono.

Visualization

When Draw_visualization is enabled, the Picture window shows four components:

The Source and Output waveform panels use the same amplitude scale, so their displayed heights can be compared directly.

Output, limits & behavior

Normalization matters: because the final output is always scaled to a 0.9 sample peak when non-silent, absolute level differences created by density, track scaling, or grain overlap are not preserved at the final output peak.