Spawn-time seek desync
Emitters activated on the same frame would sum into comb-filter flanging. We seek each new voice to a random position in its grain, masked by a 60 ms fade-in.
The system is designed as a family of runtime modules sitting on top of the direct FMOD Core API. The first shipped module — AmbientWind — is a wind engine built from scratch: 13 scripts, ~5500 lines of C#, directional EQ, M/S stereo width, distance reverb, gust fronts, variable-length grains, listener-rotation smoothing. Further modules — surf, ambience beds, crowd — are in development against the same core.
Built independently as a technical exploration. Everything below describes the AmbientWind module.
Off-the-shelf spatial audio puts a static emitter in a room. Real wind moves, swells, wraps around you. A single looping stereo WAV can't do that without sounding like a loop.
A grid of emitters baked from a spline. Each one plays crossfading grain voices that drift along the wind axis, with per-voice DSP driven by your position and head rotation.
Recording is baked into a manifest of variable-length grains at authoring time. At runtime, WindGrainPlayer manages a pool of crossfading voices that route through a shared reverb before hitting FMOD Core.
Every voice's tonal balance, stereo spread, and reverb send are decomposed from three scalars — dotFwd, tSide, tBehind — computed each frame from the listener's forward vector. The radial below shows the decomposition at eight canonical zones. Hover a wedge to inspect.
Two emitter tracks crossfade continuously with variable-length grains. The gust front (around 60% of this timeline) sweeps left-to-right; subsequent grains are routed from the gust pool until the front passes.
Emitters activated on the same frame would sum into comb-filter flanging. We seek each new voice to a random position in its grain, masked by a 60 ms fade-in.
Voices drift through space. A bus EQ would average out directional information. Each voice gets its own MULTIBAND_EQ instance updated per frame.
Fixed 3 s grains lock emitters into a 0.33 Hz crossfade rhythm the ear learns to expect. Randomising grain length between 2–4.5 s breaks that periodicity permanently.
Fast head turns make EQ and width pop audibly. Exponential decay (τ = 0.25 s) on the forward vector gives the tonal field inertia — but only for DSP, never for the FMOD panner.
Guarantees WindGrainPlayer’s LateUpdate wins the last listener write over FMOD’s StudioListener.
Authoring → Bake → Playback. The runtime never writes back to the author. Clean, testable, Unity-idiomatic.