Operator Calculus, Co.
Browse Studio documentation
On this page

Phase Feature ใ€ฐ๏ธ

add_phase_feature_operator(ctx, src, dst, opts)

Extract phase-aligned magnitude features from complex fields. Produces a complex output that preserves the phase direction while applying magnitude transformations. Useful for creating phase-preserving envelopes, spectral features, and instantaneous amplitude analysis.

Method Signature

add_phase_feature_operator(ctx, input, output, [options]) -> operator

Returns: Operator handle (userdata)

Mathematical Formulation

For a complex input z=โˆฃzโˆฃeiฯ•z = |z| e^{i\phi}:

out={โˆฃzโˆฃpโ‹…eiฯ•ifย โˆฃzโˆฃโ‰ฅthreshold0otherwise\text{out} = \begin{cases} |z|^p \cdot e^{i\phi} & \text{if } |z| \geq \text{threshold} \\ 0 & \text{otherwise} \end{cases}

where:

  • โˆฃzโˆฃ|z| is the magnitude
  • ฯ•=argโก(z)\phi = \arg(z) is the phase
  • pp is the exponent parameter

Special cases:

  • exponent = 0: Output has unit magnitude with original phase: eiฯ•e^{i\phi}
  • exponent = 1: Output equals input (identity for above-threshold samples)
  • exponent = 2: Output has squared magnitude: โˆฃzโˆฃ2eiฯ•|z|^2 e^{i\phi}

Parameters

Parameter Type Default Range Description
threshold double 0.0 โ‰ฅ0 Magnitude gate; samples below are zeroed
exponent double 0.0 โ‰ฅ0 Power applied to magnitude (0 = unit magnitude)
accumulate boolean false โ€” Add to output instead of overwriting
scale_by_dt boolean true โ€” Scale accumulated writes by dt

Boundary & Initial Conditions

  • Operates elementwise; no boundary handling required
  • Complex fields preserve phase; real fields treated as having zero phase
  • Output is complex when input is complex

Stability & Convergence

  • Unconditionally stable (bounded output for bounded input)
  • Threshold acts as a noise gate; useful for suppressing low-amplitude noise
  • exponent > 1 amplifies magnitude differences; exponent < 1 compresses them

Performance Notes

  • Requires magnitude calculation (square root) and phase extraction (atan2)
  • Phase preservation requires sin/cos for reconstruction
  • threshold = 0 skips the comparison; exponent = 1 skips power computation

Examples

-- Extract unit-magnitude phase direction
ooc.add_phase_feature_operator(ctx, signal, phase_dir, {
    exponent = 0.0
})

-- Magnitude gate with threshold
ooc.add_phase_feature_operator(ctx, signal, gated, {
    threshold = 0.1,
    exponent = 1.0
})

-- Squared magnitude feature (energy-like)
ooc.add_phase_feature_operator(ctx, signal, energy, {
    exponent = 2.0
})

-- Accumulate phase-aligned energy over time
ooc.add_phase_feature_operator(ctx, signal, accumulated, {
    exponent = 2.0,
    accumulate = true,
    scale_by_dt = true
})

-- Square root compression (reduce dynamic range)
ooc.add_phase_feature_operator(ctx, signal, compressed, {
    exponent = 0.5,
    threshold = 0.01
})

Minimal Convolution ๐Ÿงฉ

add_minimal_convolution_operator(ctx, src, dst, opts)

Apply small odd-length convolution kernels for 1D or 2D fields. Supports axis-aligned, separable, and full 2D convolution modes with configurable boundary conditions. Efficient for gradient operators, smoothing, edge detection, and custom filters.

Method Signature

add_minimal_convolution_operator(ctx, input, output, [options]) -> operator

Returns: Operator handle (userdata)

Mathematical Formulation

1D Convolution (axis mode):

outi=โˆ‘j=โˆ’rrkjโ‹…ini+jโ‹…s\text{out}_i = \sum_{j=-r}^{r} k_j \cdot \text{in}_{i + j \cdot s}

where kk is the kernel, r=โŒŠL/2โŒ‹r = \lfloor L/2 \rfloor is the kernel radius, LL is kernel_length, and ss is stride.

Separable Convolution:

Two-pass 1D convolution: first along X, then along Y (or vice versa):

out=kyโˆ—(kxโˆ—in)\text{out} = k_y * (k_x * \text{in})

where โˆ—* denotes 1D convolution along the specified axis.

2D Convolution (kernel_2d mode):

outi,j=โˆ‘m=โˆ’ryryโˆ‘n=โˆ’rxrxKm,nโ‹…ini+m,j+n\text{out}_{i,j} = \sum_{m=-r_y}^{r_y} \sum_{n=-r_x}^{r_x} K_{m,n} \cdot \text{in}_{i+m, j+n}

where KK is the 2D kernel in row-major order.

Parameters

Core Parameters:

Parameter Type Default Range Description
mode enum "axis" see below Convolution mode
axis enum "x" x, y Axis for 1D convolution
boundary enum "periodic" see below Boundary handling policy
stride integer 1 [1, 16] Stride between sampled elements
accumulate boolean false โ€” Add to output instead of overwriting
scale_by_dt boolean true โ€” Scale accumulated writes by dt

Mode options: axis, separable, kernel_2d

Boundary options: periodic, neumann, dirichlet, reflective

1D Kernel Parameters:

Parameter Type Default Range Description
kernel_length integer 3 3, 5, 7, 9 Number of kernel coefficients
kernel double[] โ€” โ€” Kernel coefficients (comma-separated string or array)

2D Kernel Parameters (kernel_2d mode):

Parameter Type Default Range Description
kernel_rows integer 3 3, 5, 7, 9 Kernel height
kernel_cols integer 3 3, 5, 7, 9 Kernel width
kernel_2d double[] โ€” โ€” Flattened row-major 2D kernel

Deprecated:

Parameter Type Default Description
wrap boolean true Use boundary = "periodic" instead

Boundary & Initial Conditions

  • Periodic: Wraps around domain edges
  • Neumann: Zero-derivative at boundaries (mirrors edge values)
  • Dirichlet: Zero values outside domain
  • Reflective: Mirror-reflects values at boundaries

Stability & Convergence

  • Convolution is unconditionally stable for bounded kernels
  • Kernel normalization affects output scale:
    • Smoothing kernels should sum to 1.0
    • Derivative kernels should sum to 0.0
  • stride > 1 produces downsampled output; ensure output field is sized accordingly

Performance Notes

  • Fixed kernel sizes (3, 5, 7, 9) enable compile-time optimization
  • Separable convolution is O(2NยทL) vs O(NยทLยฒ) for full 2D
  • Kernel coefficients can be passed as Lua array or comma-separated string
  • Memory access pattern is cache-friendly for axis-aligned convolution

Runtime Configuration

Read current configuration:

local cfg = ooc.minimal_convolution_config(ctx, op_index)
-- Returns: { kernel_length, stride, accumulate, wrap, boundary, kernel }

Update configuration:

ooc.minimal_convolution_update(ctx, op_index, {
    kernel_length = 5,
    kernel = {1, 4, 6, 4, 1},
    stride = 2,
    boundary = "reflective"
})

Examples

-- 1D gradient kernel (central difference)
ooc.add_minimal_convolution_operator(ctx, u, du_dx, {
    mode = "axis",
    axis = "x",
    kernel_length = 3,
    kernel = {-0.5, 0.0, 0.5}
})

-- 1D gradient with 5-tap stencil
ooc.add_minimal_convolution_operator(ctx, u, du_dx, {
    mode = "axis",
    axis = "x",
    kernel_length = 5,
    kernel = "-1,0,0,0,1"  -- string format
})

-- Separable Gaussian smoothing
ooc.add_minimal_convolution_operator(ctx, u, smoothed, {
    mode = "separable",
    kernel_length = 3,
    kernel = {0.25, 0.5, 0.25}
})

-- Separable 5-tap binomial filter
ooc.add_minimal_convolution_operator(ctx, u, smoothed, {
    mode = "separable",
    kernel_length = 5,
    kernel = {1/16, 4/16, 6/16, 4/16, 1/16}
})

-- Full 2D Laplacian (3x3)
ooc.add_minimal_convolution_operator(ctx, u, laplacian, {
    mode = "kernel_2d",
    kernel_rows = 3,
    kernel_cols = 3,
    kernel_2d = "0,1,0,1,-4,1,0,1,0",
    boundary = "neumann"
})

-- 2D Sobel edge detection (X direction)
ooc.add_minimal_convolution_operator(ctx, image, edges_x, {
    mode = "kernel_2d",
    kernel_rows = 3,
    kernel_cols = 3,
    kernel_2d = {-1, 0, 1, -2, 0, 2, -1, 0, 1}
})

-- Strided convolution for downsampling
ooc.add_minimal_convolution_operator(ctx, high_res, low_res, {
    mode = "separable",
    kernel_length = 5,
    kernel = {1, 4, 6, 4, 1},
    stride = 2,
    boundary = "reflective"
})

-- Runtime kernel swap
local cfg = ooc.minimal_convolution_config(ctx, op_index)
ooc.log("conv: length=%d stride=%d boundary=%s",
    cfg.kernel_length, cfg.stride, cfg.boundary)

ooc.minimal_convolution_update(ctx, op_index, {
    kernel_length = 7,
    kernel = {1, 6, 15, 20, 15, 6, 1},  -- Pascal's triangle row
    boundary = "periodic"
})

Sieve ๐Ÿงน

add_sieve_operator(ctx, src, dst, opts)

Apply spectral filtering and smoothing using various window functions and polynomial methods. Supports low-pass, high-pass, band-pass, and band-stop filters with Gaussian, Hann, Blackman, Tukey windows, and Savitzky-Golay polynomial filtering.

Method Signature

add_sieve_operator(ctx, input, output, [options]) -> operator

Returns: Operator handle (userdata)

Mathematical Formulation

Gaussian Low-Pass:

hk=12ฯ€ฯƒexpโก(โˆ’k22ฯƒ2)h_k = \frac{1}{\sqrt{2\pi}\sigma} \exp\left(-\frac{k^2}{2\sigma^2}\right)

where kโˆˆ[โˆ’r,r]k \in [-r, r] and r=โŒŠtaps/2โŒ‹r = \lfloor \text{taps}/2 \rfloor.

Gaussian High-Pass:

hHP=ฮดโˆ’hLPh_{\text{HP}} = \delta - h_{\text{LP}}

where ฮด\delta is the Dirac delta (identity for convolution).

Difference of Gaussians (DoG):

hDoG=Gฯƒ1โˆ’Gฯƒ2h_{\text{DoG}} = G_{\sigma_1} - G_{\sigma_2}

where ฯƒ1<ฯƒ2\sigma_1 <\sigma_2 creates a band-pass filter centered between the two scales.

Savitzky-Golay:

Fits a polynomial of order pp to a sliding window of taps samples, evaluating at the center (smoothing) or computing the derivative.

Windowed Filters (Hann, Blackman, Tukey):

hk=wkโ‹…1Nh_k = w_k \cdot \frac{1}{N}

where wkw_k is the window function value at position kk. High-pass versions subtract from identity.

Parameters

Core Parameters:

Parameter Type Default Range Description
mode enum see below Filtering method (required)
taps integer โ€” odd, โ‰ฅ3 Kernel length (required)
gain double 1.0 unbounded Output gain multiplier
accumulate boolean false โ€” Add to output instead of overwriting
scale_by_dt boolean true โ€” Scale accumulated writes by dt

Mode options:

  • Gaussian: low_pass, high_pass
  • DoG: band_pass_dog, band_stop_dog
  • Savitzky-Golay: savgol_smooth, savgol_derivative
  • Hann: hann_low_pass, hann_high_pass
  • Blackman: blackman_low_pass, blackman_high_pass
  • Tukey: tukey_low_pass, tukey_high_pass

Gaussian Parameters:

Parameter Type Default Range Description
sigma double >0 Gaussian standard deviation (samples)

DoG Parameters:

Parameter Type Default Range Description
sigma double >0 First Gaussian width
sigma2 double >0 Second Gaussian width (should differ from sigma)

Savitzky-Golay Parameters:

Parameter Type Default Range Description
poly_order integer โ€” < taps Polynomial order for fitting

Tukey Parameters:

Parameter Type Default Range Description
sigma double [0, 1] Tukey alpha parameter (0 = rectangular, 1 = Hann)

Boundary & Initial Conditions

  • Convolution-based; uses periodic boundaries by default
  • Kernel is truncated to taps samples; ensure sufficient taps for the chosen sigma
  • Rule of thumb: taps >= 6 * sigma for Gaussian filters

Stability & Convergence

  • All filters are unconditionally stable (bounded output for bounded input)
  • Low-pass filters preserve DC component; high-pass filters remove it
  • DoG filters are zero-sum (no DC response)
  • Savitzky-Golay preserves polynomial moments up to order poly_order

Performance Notes

  • Pre-computed kernels; O(N ร— taps) per application
  • Larger taps increases accuracy but also computation
  • Savitzky-Golay requires polynomial coefficient computation (one-time)
  • Consider add_minimal_convolution_operator for simple fixed-size kernels

Examples

-- Gaussian low-pass smoothing
ooc.add_sieve_operator(ctx, noisy, smooth, {
    mode = "low_pass",
    sigma = 1.2,
    taps = 7
})

-- Gaussian high-pass (edge enhancement)
ooc.add_sieve_operator(ctx, image, edges, {
    mode = "high_pass",
    sigma = 2.0,
    taps = 13
})

-- Difference of Gaussians band-pass
ooc.add_sieve_operator(ctx, signal, band, {
    mode = "band_pass_dog",
    sigma = 1.0,
    sigma2 = 3.0,
    taps = 19
})

-- DoG notch filter (band-stop)
ooc.add_sieve_operator(ctx, signal, notched, {
    mode = "band_stop_dog",
    sigma = 2.0,
    sigma2 = 4.0,
    taps = 25
})

-- Savitzky-Golay smoothing (preserves peaks)
ooc.add_sieve_operator(ctx, spectrum, smooth_spectrum, {
    mode = "savgol_smooth",
    taps = 11,
    poly_order = 3
})

-- Savitzky-Golay derivative (noise-resistant)
ooc.add_sieve_operator(ctx, signal, derivative, {
    mode = "savgol_derivative",
    taps = 7,
    poly_order = 2
})

-- Hann-windowed low-pass
ooc.add_sieve_operator(ctx, audio, filtered, {
    mode = "hann_low_pass",
    taps = 15
})

-- Blackman high-pass (steep cutoff)
ooc.add_sieve_operator(ctx, signal, highpass, {
    mode = "blackman_high_pass",
    taps = 21
})

-- Tukey window (adjustable taper)
ooc.add_sieve_operator(ctx, signal, tapered, {
    mode = "tukey_low_pass",
    taps = 17,
    sigma = 0.5  -- alpha parameter for Tukey
})

-- Accumulate filtered output with gain
ooc.add_sieve_operator(ctx, input, output, {
    mode = "low_pass",
    sigma = 1.5,
    taps = 9,
    gain = 2.0,
    accumulate = true
})

Sound Observation ๐Ÿ”Š

add_sound_observation_operator(ctx, input, [modulator], opts)

Sample a field and map features to audio control values or raw audio samples. Enables sonification of simulation state for monitoring, artistic expression, or accessibility.

Method Signature

add_sound_observation_operator(ctx, input, [modulator], [options]) -> operator

Returns: Operator handle (userdata)

Note: The optional modulator field provides an external modulation source for gain, pan, pitch, or FM synthesis.

Mathematical Formulation

Sampling Modes:

For a field uu with NN samples:

  • mean: uห‰=1Nโˆ‘i=0Nโˆ’1ui\bar{u} = \frac{1}{N}\sum_{i=0}^{N-1} u_i
  • rms: urms=1Nโˆ‘i=0Nโˆ’1โˆฃuiโˆฃ2u_{\text{rms}} = \sqrt{\frac{1}{N}\sum_{i=0}^{N-1} |u_i|^2}
  • point: usample_indexu_{\text{sample\_index}}
  • min/max: minโกi(ui)\min_i(u_i) or maxโกi(ui)\max_i(u_i)

Feature Translation:

Extracted features are mapped to audio parameters through configurable translation chains:

param=clamp(base+scaleโ‹…f(source),min,max)\text{param} = \text{clamp}\left(\text{base} + \text{scale} \cdot f(\text{source}), \text{min}, \text{max}\right)

where ff applies the selected feature extraction (amplitude, energy, phase, mean).

FM Synthesis:

When fm_source is active:

output=Aโ‹…sinโก(2ฯ€fct+Iโ‹…sinโก(2ฯ€fmt))\text{output} = A \cdot \sin\left(2\pi f_c t + I \cdot \sin(2\pi f_m t)\right)

where II is the modulation index controlled by fm_depth and the extracted feature.

Parameters

Output Configuration:

Parameter Type Default Description
output_mode enum "controls" Output type: controls (parameter values) or raw_samples (audio buffer)
output_bus integer 0 Target audio bus index
output_send double 1.0 Send level [0, 1]
output_pre_fader boolean false Send before fader processing

Sampling Configuration:

Parameter Type Default Description
sampling_mode enum "mean" How to reduce field: mean, rms, point, min, max
sampling_domain enum "physical" Sample in physical or spectral domain
window_type enum "rect" Window function: rect, hann, hamming, blackman
window_length integer 0 Window size (0 = full field)
window_offset integer 0 Window start offset
sample_index integer 0 Index for point sampling mode

Gain Translation:

Parameter Type Default Description
gain_source enum "off" Feature source: off, amplitude, energy, phase, mean_x, mean_y
gain_modulator enum "none" Modulation source: none, self_field, external_field
gain_base double 1.0 Base gain value
gain_scale double 1.0 Gain scaling factor
gain_min double 0.0 Minimum gain
gain_max double 1.0 Maximum gain

Pan Translation:

Parameter Type Default Description
pan_source enum "off" Feature source for panning
pan_modulator enum "none" Modulation source
pan_center double 0.0 Pan center position [-1, 1]
pan_width double 1.0 Pan range width
pan_law enum "linear" Pan law: linear, sqrt, power

Pitch Translation:

Parameter Type Default Description
pitch_source enum "off" Feature source for pitch
pitch_modulator enum "none" Modulation source
pitch_base_hz double 440.0 Base frequency in Hz
pitch_range_octaves double 1.0 Pitch range in octaves
pitch_scale enum "linear_hz" Scale mapping: linear_hz, log_semitone

FM Translation:

Parameter Type Default Description
fm_source enum "off" Feature source for FM depth
fm_modulator enum "none" Modulation source
fm_depth double 0.0 Maximum modulation index
fm_center double 0.0 FM center frequency offset
fm_ratio double 1.0 Carrier-to-modulator frequency ratio
fm_clip double 0.0 FM output clipping (0 = no clip)

Envelope:

Parameter Type Default Description
attack_ms double 0.0 Attack time in milliseconds
release_ms double 0.0 Release time in milliseconds
smoothing_tau double 0.0 Exponential smoothing time constant

Raw Sampling Mode:

Parameter Type Default Description
raw_source enum "real" Component: real, imag, magnitude, phase
raw_channel_mode enum "mono" Channel mode: mono, stereo
raw_gain double 1.0 Raw output gain
raw_clip double 1.0 Hard clipping threshold
raw_resample_mode enum "linear" Resampling: linear, nearest

Boundary & Initial Conditions

  • Operates on field data; no spatial boundary handling
  • Window parameters must fit within field bounds
  • Initial envelope state starts at zero; transient during attack phase

Stability & Convergence

  • Feature extraction is unconditionally stable
  • Smoothing with large smoothing_tau introduces lag
  • FM synthesis can produce aliasing at high modulation depths; use fm_clip to limit

Performance Notes

  • Windowed sampling modes (hann, hamming, blackman) require per-sample multiplication
  • Spectral domain sampling requires FFT (handled internally if field is physical)
  • External modulator field adds a second field read per sample
  • Raw sample mode may require resampling if field size differs from audio buffer

Examples

-- Basic amplitude-to-gain sonification
ooc.add_sound_observation_operator(ctx, field, {
    gain_source = "amplitude",
    gain_base = 0.0,
    gain_scale = 2.0,
    gain_max = 1.0
})

-- RMS-based pitch control
ooc.add_sound_observation_operator(ctx, field, {
    sampling_mode = "rms",
    pitch_source = "amplitude",
    pitch_base_hz = 220.0,
    pitch_range_octaves = 2.0,
    pitch_scale = "log_semitone"
})

-- Stereo panning based on mean position
ooc.add_sound_observation_operator(ctx, field, {
    pan_source = "mean_x",
    pan_center = 0.0,
    pan_width = 2.0,
    pan_law = "sqrt"
})

-- FM synthesis driven by field energy
ooc.add_sound_observation_operator(ctx, carrier, modulator, {
    fm_source = "energy",
    fm_depth = 5.0,
    fm_ratio = 2.0,
    pitch_base_hz = 440.0
})

-- Raw audio output from complex field
ooc.add_sound_observation_operator(ctx, waveform, {
    output_mode = "raw_samples",
    raw_source = "real",
    raw_channel_mode = "stereo",
    raw_gain = 0.5,
    raw_clip = 0.95
})

-- Windowed spectral observation
ooc.add_sound_observation_operator(ctx, field, {
    sampling_mode = "rms",
    sampling_domain = "spectral",
    window_type = "hann",
    window_length = 256,
    gain_source = "amplitude",
    attack_ms = 10.0,
    release_ms = 100.0
})

-- Point sampling at field center
local N = 512
ooc.add_sound_observation_operator(ctx, field, {
    sampling_mode = "point",
    sample_index = N / 2,
    gain_source = "amplitude",
    smoothing_tau = 0.01
})