Skip to content

Stochastic Source

Tier: Core | ComponentType: 25 | Params: 16

Eight stochastic process types for random modulation with configurable rate, amplitude, and process-specific parameters.

Overview

StochasticSource is a modulation generator that implements eight distinct stochastic processes, each producing a different character of randomness. It has no audio input — it generates a modulation signal in the range [-1, 1] that can be used to control parameters or modulate audio amplitude in the signal chain.

The eight process types span a range from smooth, continuous random walks to discrete event-based triggers:

  • Brownian motion — Gaussian random walk with reflection at boundaries
  • Ornstein-Uhlenbeck — Mean-reverting random walk that orbits a configurable center
  • Perlin noise — Smooth, organic noise with multi-octave layering
  • Levy flight — Heavy-tailed jumps for intermittent, burst-like modulation
  • Poisson — Random pulse triggers with configurable duration
  • Shot noise — Pool of decaying impulses triggered by Poisson process
  • Markov chain — 8-state discrete chain with configurable transition matrix and 4 presets
  • Sample and Hold — Random value held until next trigger (clock or Poisson)

A 64-sample history ring buffer (decimated every 64 samples) provides a scrolling trace for visualization. The xorshift32 PRNG can be seeded for deterministic reproducibility via the Seed parameter.

In chain mode, StochasticSource modulates the input amplitude: output = input * (0.5 + 0.5 * value).

File Locations

Path
Header Sources/FolioDSP/include/FolioDSP/Core/StochasticSource.h
Implementation Sources/FolioDSP/src/Core/StochasticSource.cpp
Tests Tests/FolioDSPTests/StochasticSourceTests.swift
Bridge Sources/FolioDSPBridge/src/FolioDSPBridge.mm (StochasticSourceBridge)

Parameters

Index Name Description Min Max Default Min Default Max Default Unit
0 Process Stochastic process type (0-7) 0.0 7.0 0.0 7.0 0.0
1 Rate Update/modulation rate 0.01 100.0 0.1 20.0 1.0 Hz
2 Amplitude Output amplitude scaling 0.0 1.0 0.0 1.0 1.0
3 Seed PRNG seed (0=default, nonzero=deterministic) 0.0 65535.0 0.0 65535.0 0.0
4 Step Size Random walk step size (Brownian/OU) 0.0 1.0 0.01 0.5 0.1
5 Gravity Mean-reversion strength (OU) 0.0 10.0 0.1 5.0 1.0
6 Center Mean-reversion target (OU) -1.0 1.0 -1.0 1.0 0.0
7 Octaves Perlin noise octave count 1.0 8.0 1.0 8.0 4.0
8 Persistence Perlin amplitude decay per octave 0.0 1.0 0.1 0.9 0.5
9 Lacunarity Perlin frequency multiplier per octave 1.5 4.0 1.5 4.0 2.0
10 Alpha Levy stability parameter 0.5 2.0 0.5 2.0 1.5
11 Duration Poisson pulse duration 1.0 1000.0 1.0 200.0 10.0 samples
12 Decay Shot noise impulse decay time 1.0 500.0 5.0 200.0 50.0 ms
13 Glide Markov state transition smoothing 0.0 100.0 0.0 100.0 20.0 %
14 Clock Mode S&H trigger mode (0=Poisson, 1=Clock) 0.0 1.0 0.0 1.0 1.0
15 Slew S&H output smoothing 0.0 1.0 0.0 1.0 0.0

Processing Algorithm

The process() function executes once per sample, dispatching to the selected process type.

0. Seed Management

When the Seed parameter changes to a nonzero value, the PRNG is reseeded via Knuth multiplicative hash:

\[\text{rngState} = \text{seed} \times 2654435761 \pmod{2^{32}}\]

A seed of 0 uses the default (non-deterministic) PRNG state.

1. Brownian Motion

A Gaussian random walk with boundary reflection:

\[v[n] = v[n-1] + \mathcal{N}(0,1) \cdot \sigma \cdot \frac{f_{\text{rate}}}{f_s}\]
\[\text{if } v > 1: v \leftarrow 2 - v; \quad \text{if } v < -1: v \leftarrow -2 - v\]

where \(\sigma\) is the step size. Reflection at \(\pm 1\) keeps the output bounded without hard clamping artifacts.

2. Ornstein-Uhlenbeck

A mean-reverting stochastic differential equation:

\[v[n] = v[n-1] + \left(-\gamma(v[n-1] - c) + \mathcal{N}(0,1) \cdot \sigma\right) \cdot \frac{f_{\text{rate}}}{f_s}\]

where \(\gamma\) is gravity (mean-reversion strength), \(c\) is the center target, and \(\sigma\) is step size. The pull term \(-\gamma(v - c)\) creates an elastic restoring force toward the center.

3. Perlin Noise

Multi-octave 1D Perlin noise with smoothstep interpolation:

\[\text{phase} \leftarrow \text{phase} + \frac{f_{\text{rate}}}{f_s}\]
\[v = \frac{\sum_{i=0}^{\text{oct}-1} \text{perlin1D}(\text{phase} \cdot \lambda^i) \cdot p^i}{\sum_{i=0}^{\text{oct}-1} p^i}\]

where \(\lambda\) is lacunarity (frequency multiplier) and \(p\) is persistence (amplitude decay). The 1D Perlin function uses a 256-entry permutation table with gradient noise and smoothstep interpolation:

\[u = x_f^2 (3 - 2x_f), \quad \text{perlin}(x) = g_0 + u \cdot (g_1 - g_0)\]

4. Levy Flight

The Chambers-Mallows-Stuck algorithm generates alpha-stable random variables:

\[V \sim \text{Uniform}(-\pi/2, \pi/2), \quad W \sim \text{Exp}(1)\]
\[L = \frac{\sin(\alpha V)}{(\cos V)^{1/\alpha}} \cdot \left(\frac{\cos((1-\alpha)V)}{W}\right)^{(1-\alpha)/\alpha}\]
\[v[n] = v[n-1] + \text{clamp}(L, -10, 10) \cdot \sigma \cdot \frac{f_{\text{rate}}}{f_s}\]
\[v = \tanh(v) \quad \text{(soft clamp)}\]

When \(\alpha = 2\), this reduces to Gaussian. When \(\alpha = 1\), it produces Cauchy-distributed jumps (\(L = \tan(V)\)). Lower \(\alpha\) values produce heavier tails with more extreme jumps.

5. Poisson

Random pulse triggers with a per-sample probability:

\[P(\text{trigger}) = \frac{f_{\text{rate}}}{f_s}\]

When triggered, the output holds at 1.0 for the specified duration (in samples), then returns to 0.0.

6. Shot Noise

A pool of 32 decaying impulses, triggered by Poisson process:

\[P(\text{trigger}) = \frac{f_{\text{rate}}}{f_s}\]

Each impulse has a random amplitude \(a \sim \text{Uniform}(-1, 1)\) and decays exponentially:

\[\text{impulse}_i(t) = a_i \cdot e^{-t / T_{\text{decay}}}\]
\[T_{\text{decay}} = \frac{\text{decay}_{\text{ms}}}{1000} \cdot f_s\]

Active impulses are summed and soft-clipped:

\[v = \tanh\!\left(\sum_{i \in \text{active}} \text{impulse}_i\right)\]

Impulses are deactivated when their envelope falls below 0.001.

7. Markov Chain

An 8-state discrete Markov chain with configurable transition matrix:

\[\text{phase} \leftarrow \text{phase} + \frac{f_{\text{rate}}}{f_s}\]
\[\text{if phase} \geq 1: \text{state} \leftarrow \text{next state from } P[\text{state}][\cdot]\]

State transitions use cumulative probability sampling against a uniform random value. Each state maps to a configurable value via stateValues[]. Output is optionally smoothed by glide:

\[T_{\text{glide}} = \frac{\text{glide\%}}{100} \cdot \frac{f_s}{f_{\text{rate}}}\]
\[y[n] = y[n-1] + \frac{1}{T_{\text{glide}}} \cdot (\text{stateValue} - y[n-1])\]

Four presets configure the transition matrix:

  • Drift: 70% self-transition, 12.5% to adjacent states (slow meandering)
  • Cycle: 80% to next state, 10% self (deterministic-ish rotation)
  • Random: uniform distribution across all states (maximum entropy)
  • Absorbing: states 0-1 are sticky (90% self), states 2-3 are uniform (gravitates toward low states)

8. Sample and Hold

Holds a random value until the next trigger:

Clock mode (\(\text{clockMode} = 1\)):

\[\text{phase} \leftarrow \text{phase} + \frac{f_{\text{rate}}}{f_s}; \quad \text{if phase} \geq 1: \text{trigger, sample new value}\]

Poisson mode (\(\text{clockMode} = 0\)):

\[P(\text{trigger}) = \frac{f_{\text{rate}}}{f_s}\]

On trigger: \(h \sim \text{Uniform}(-1, 1)\). Slew smoothing:

\[T_{\text{slew}} = \text{slew} \cdot \frac{f_s}{f_{\text{rate}}}\]
\[y[n] = y[n-1] + \frac{1}{T_{\text{slew}}} \cdot (h - y[n-1])\]

9. Output Scaling

The raw process output is scaled by the amplitude parameter:

\[\text{output} = \text{raw} \times \text{amplitude}\]

Core Equations

\[\text{Brownian: } v[n] = v[n-1] + \mathcal{N}(0,1) \cdot \sigma \cdot r_s\]
\[\text{OU: } v[n] = v[n-1] + (-\gamma(v-c) + \mathcal{N}\cdot\sigma) \cdot r_s\]
\[\text{Perlin: } v = \sum \text{perlin1D}(\phi \cdot \lambda^i) \cdot p^i\]
\[\text{Levy: } v = \tanh(v + \text{CMS}(\alpha) \cdot \sigma \cdot r_s)\]

Where \(r_s = f_{\text{rate}} / f_s\).

Snapshot Fields

Field Type Range Unit Description
Process Type Uint8 0–7 Active stochastic process index
Value Float -1–1 Current output value (after amplitude scaling)
Rate Float 0.01–100 Hz Current modulation rate
Amplitude Float 0–1 Current amplitude scaling
History Float[64] -1–1 Decimated output history ring buffer
History Index Float 0–63 Current write position in history buffer
Trigger Float 0–1 Trigger indicator (Poisson/ShotNoise/S&H)
Markov State Uint8 0–7 Current Markov chain state index
Dist From Center Float 0–2 Distance from OU center parameter

Implementation Notes

  • xorshift32 PRNG provides all randomness. Box-Muller transform generates Gaussian variates from uniform pairs. Exponential variates via \(-\ln(u)\).
  • Seed parameter uses Knuth multiplicative hash (\(\times 2654435761\)) for good bit distribution. Changing the seed also re-shuffles the Perlin permutation table.
  • ParamSmoother is applied to Rate, Amplitude, Step Size, Gravity, Center, Decay, and Slew parameters. The smoother uses prepare(sampleRate) for coefficient calculation.
  • Shot noise pool is fixed at 32 impulses (no heap allocation). When all slots are active, new triggers are silently dropped.
  • Perlin permutation table is a 256-entry Fisher-Yates shuffled array. The gradient function returns either \(+x\) or \(-x\) based on the least significant bit of the hash.
  • Levy flight clamps individual steps to \([-10, 10]\) before accumulation to prevent numerical overflow, then applies tanh soft clamping to the accumulated value.
  • History decimation writes every 64 samples to avoid overwhelming the snapshot with per-sample data. The 64-sample ring buffer provides approximately 1.5 seconds of history at 44.1 kHz.
  • Markov transition matrix is an \(8 \times 8\) array of floats. Rows should sum to 1.0 for valid probability distributions. The setMarkovTransition method sets individual rows; setMarkovPreset configures the entire matrix.
  • All parameters use std::atomic<float> (or std::atomic<int>) for lock-free thread safety.
  • Snapshot emission is decimated to ~60 fps (every 735 samples at 44.1 kHz).

Equation Summary

y = Brownian|OU|Perlin|Levy|Poisson|S&H