Granular synthesis: source motion, grain pitch and bounded processing
Research behind the two-layer grain engine, sample recall, independent scan/pitch and expressive motion.
Technical research · edition 2 · reviewed 2026-10-24

Prismal Labs native editor capture, illustrating the corresponding software project. View full-size image
What we found
The granular study led to a useful separation: the location of a new grain, the speed at which that grain reads the source, and the motion of future grain starts are three different controls. Treating them as one cursor makes pitch change with scan speed and makes XY automation disturb grains already sounding.
Our two-layer engine keeps those clocks separate, preserves source-rate metadata, and divides a fixed grain budget across the requested polyphony. The resulting findings are useful for samplers, live buffers and other engines that combine prepared sample data with bounded realtime scheduling.
Separate source traversal from grain pitch
For a source recorded at Fs_source, rendered at Fs_host, and transposed by s semitones relative to its declared root, the source-frame increment is:
The implementation applies the note/root difference, layer tuning, scatter and selected pitch quantization before forming this base step; channel bend and per-note tuning can then modify it. A 44.1 kHz source at a 48 kHz host needs an increment of 0.91875 even at unity musical pitch. Omitting that factor shifts both pitch and timing.
The normalized scan cursor instead advances by scan / (sourceDuration * Fs_host). This moves the locations from which future grains start. Each running grain retains its own read point. A conceptual output is sum_g window_g[n] * source(position_g[n]); normalization and finite grain admission are additional policies, not properties guaranteed by that sum.
The prepared window choices include Hann, Gaussian, triangle and trapezoid. Interpolation and source anti-alias pyramids address sample reading, while density/window overlap affects level and texture. These are separate design questions and should be tested separately.
A bounded grain budget has musical consequences
The shared budget is 512 grains. Each layer may use up to 64 grains per voice, but at sixteen voices its quota is sixteen. New starts above the quota are skipped with overlap normalization. Requested density therefore differs from admitted density under load; a UI should not imply unlimited overlap.
An approximate unconstrained overlap count is density * durationSeconds. At 120 grains/second and 0.5-second duration, that is about sixty grains per layer per voice. Sixteen such voices on two layers would request about 1,920 overlapping grains, well above 512. This calculation is a planning estimate, not a substitute for the actual scheduler's counters.
Source loading, prepared representations and the bounded material cache run outside processing. The audio callback uses fixed voice/grain storage. This gives a reusable admission-control pattern: make capacity a first-class musical policy instead of waiting for allocation failure.
XY motion changes future starts
The XY Motion page places layer A's grain position on X and layer B's on Y. Drag the pad to set both centers together, use arrow keys for small changes, or press Center XY. The green ring shows the chosen center; the blue marker shows the mean position controls reported by the playing audio engine. The outline uses the engine's current LFO phases, including live pattern/rate changes. Other routed modulation can also influence that marker. Scan and scatter still affect individual grain read points. Motion affects new grain starts; already-running grains keep their own playback positions.
Choose Orbit, Figure-eight or Drift. Orbit uses a sine X and triangle Y with equal four-bar periods. Figure-eight doubles X's speed relative to Y. Drift uses independent free rates. Set each axis's shape, free rate, tempo division and signed depth, or choose Off to stop its dedicated position modulation. Selecting a pattern preserves the centers and nonzero depths; zero depths become 25%. Shapes and speed use the two shared LFOs, so other LFO routes follow them too. The Modulation page remains available for custom routing.
XY gestures, rates, divisions and depths are host-automatable and saved in the existing version-1 patch/host state. No new IDs or state fields were introduced.
Evidence: GranularSynth/README.md.
Recall and live capture have different lifetimes
Imported WAVs retain their rate and are embedded in host/patch state. Moving the original file therefore does not break recall. Live capture uses an eight-second ring and remains transient; freezing the ring does not turn it into a persisted imported asset.
The library has 100 authored stereo materials and 160 complete presets, including 158 immediately playable sounds and two input templates. Those counts describe different entities. The engine tests cover factory states, independent XY phase/timing, tempo changes, free-rate invariance and allocation-free rendering. Host recall and input routing are checked separately.
Applying the findings
Use a known periodic source at more than one source rate. Hold pitch fixed while changing scan speed, then hold scan fixed while transposing. Automate the start position during a long grain and verify that the existing read trajectory stays continuous. Stress the maximum voices/density/duration combination and count admitted grains.
For sample portability, test a saved state after the original WAV has moved. For live capture, explicitly test reset/reopen behavior instead of assuming the ring survives. The same tests apply to granular delays and texture generators.
Limits and open questions
Root-period alignment and warm starts can improve predictable sources; they do not detect a fundamental or guarantee stable pitch in arbitrary noise. Spectral resynthesis and commercial vendor algorithms are outside the implementation. The finite quota and skipped starts are intentional limits, and audible behavior still needs listening checks at extreme overlap.
Primary references
Research provenance
The methods and results above are drawn from these retained research records. Dates and product versions inside the article identify the scope of each experiment; publication review does not mean the hardware or numerical tests were rerun.
GranularSynth/source/engine.cpp— SHA-256c0c6922fbe675d682d6d6773f06d76ff1ea1d5efcef99cc43963dbdd0980640bGranularSynth/README.md— SHA-2564ddcf99dbcb91fd26e31e92e741b8eb826861ef0f816d7ee7517567e86652a29
