Skip to content
Oakfield Operator Calculus Function Reference Site

Measurement Operators

sim_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.

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

Returns: Operator handle (userdata)

For a complex input z=zeiϕz = |z| e^{i\phi}:

out={zpeiϕif zthreshold0otherwise\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: z2eiϕ|z|^2 e^{i\phi}
ParameterTypeDefaultRangeDescription
thresholddouble 0.0≥0Magnitude gate; samples below are zeroed
exponentdouble 0.0≥0Power applied to magnitude (0 = unit magnitude)
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale accumulated writes by dt
  • Operates elementwise; no boundary handling required
  • Complex fields preserve phase; real fields treated as having zero phase
  • Output is complex when input is complex
  • 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
  • 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
-- Extract unit-magnitude phase direction
ooc.sim_add_phase_feature_operator(ctx, signal, phase_dir, {
exponent = 0.0
})
-- Magnitude gate with threshold
ooc.sim_add_phase_feature_operator(ctx, signal, gated, {
threshold = 0.1,
exponent = 1.0
})
-- Squared magnitude feature (energy-like)
ooc.sim_add_phase_feature_operator(ctx, signal, energy, {
exponent = 2.0
})
-- Accumulate phase-aligned energy over time
ooc.sim_add_phase_feature_operator(ctx, signal, accumulated, {
exponent = 2.0,
accumulate = true,
scale_by_dt = true
})
-- Square root compression (reduce dynamic range)
ooc.sim_add_phase_feature_operator(ctx, signal, compressed, {
exponent = 0.5,
threshold = 0.01
})

sim_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.

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

Returns: Operator handle (userdata)

1D Convolution (axis mode):

outi=j=rrkjini+js\text{out}_i = \sum_{j=-r}^{r} k_j \cdot \text{in}_{i + j \cdot s}

where kk is the kernel, r=L/2r = \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(kxin)\text{out} = k_y * (k_x * \text{in})

where * denotes 1D convolution along the specified axis.

2D Convolution (kernel_2d mode):

outi,j=m=ryryn=rxrxKm,nini+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.

Core Parameters:

ParameterTypeDefaultRangeDescription
modeenum "axis"see belowConvolution mode
axisenum "x"x, yAxis for 1D convolution
boundaryenum "periodic"see belowBoundary handling policy
strideinteger 1[1, 16]Stride between sampled elements
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale accumulated writes by dt

Mode options: axis, separable, kernel_2d

Boundary options: periodic, neumann, dirichlet, reflective

1D Kernel Parameters:

ParameterTypeDefaultRangeDescription
kernel_lengthinteger 33, 5, 7, 9Number of kernel coefficients
kerneldouble[] Kernel coefficients (comma-separated string or array)

2D Kernel Parameters (kernel_2d mode):

ParameterTypeDefaultRangeDescription
kernel_rowsinteger 33, 5, 7, 9Kernel height
kernel_colsinteger 33, 5, 7, 9Kernel width
kernel_2ddouble[] Flattened row-major 2D kernel

Deprecated:

ParameterTypeDefaultDescription
wrapboolean trueUse boundary = "periodic" instead
  • Periodic: Wraps around domain edges
  • Neumann: Zero-derivative at boundaries (mirrors edge values)
  • Dirichlet: Zero values outside domain
  • Reflective: Mirror-reflects values at boundaries
  • 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
  • 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

Read current configuration:

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

Update configuration:

ooc.sim_minimal_convolution_update(ctx, op_index, {
kernel_length = 5,
kernel = {1, 4, 6, 4, 1},
stride = 2,
boundary = "reflective"
})
-- 1D gradient kernel (central difference)
ooc.sim_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.sim_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.sim_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.sim_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.sim_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.sim_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.sim_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.sim_minimal_convolution_config(ctx, op_index)
ooc.log("conv: length=%d stride=%d boundary=%s",
cfg.kernel_length, cfg.stride, cfg.boundary)
ooc.sim_minimal_convolution_update(ctx, op_index, {
kernel_length = 7,
kernel = {1, 6, 15, 20, 15, 6, 1}, -- Pascal's triangle row
boundary = "periodic"
})

sim_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.

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

Returns: Operator handle (userdata)

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/2r = \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σ1Gσ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=wk1Nh_k = w_k \cdot \frac{1}{N}

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

Core Parameters:

ParameterTypeDefaultRangeDescription
modeenum see belowFiltering method (required)
tapsinteger odd, ≥3Kernel length (required)
gaindouble 1.0unboundedOutput gain multiplier
accumulateboolean falseAdd to output instead of overwriting
scale_by_dtboolean trueScale 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:

ParameterTypeDefaultRangeDescription
sigmadouble >0Gaussian standard deviation (samples)

DoG Parameters:

ParameterTypeDefaultRangeDescription
sigmadouble >0First Gaussian width
sigma2double >0Second Gaussian width (should differ from sigma)

Savitzky-Golay Parameters:

ParameterTypeDefaultRangeDescription
poly_orderinteger < tapsPolynomial order for fitting

Tukey Parameters:

ParameterTypeDefaultRangeDescription
sigmadouble [0, 1]Tukey alpha parameter (0 = rectangular, 1 = Hann)
  • 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
  • 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
  • Pre-computed kernels; O(N × taps) per application
  • Larger taps increases accuracy but also computation
  • Savitzky-Golay requires polynomial coefficient computation (one-time)
  • Consider sim_add_minimal_convolution_operator for simple fixed-size kernels
-- Gaussian low-pass smoothing
ooc.sim_add_sieve_operator(ctx, noisy, smooth, {
mode = "low_pass",
sigma = 1.2,
taps = 7
})
-- Gaussian high-pass (edge enhancement)
ooc.sim_add_sieve_operator(ctx, image, edges, {
mode = "high_pass",
sigma = 2.0,
taps = 13
})
-- Difference of Gaussians band-pass
ooc.sim_add_sieve_operator(ctx, signal, band, {
mode = "band_pass_dog",
sigma = 1.0,
sigma2 = 3.0,
taps = 19
})
-- DoG notch filter (band-stop)
ooc.sim_add_sieve_operator(ctx, signal, notched, {
mode = "band_stop_dog",
sigma = 2.0,
sigma2 = 4.0,
taps = 25
})
-- Savitzky-Golay smoothing (preserves peaks)
ooc.sim_add_sieve_operator(ctx, spectrum, smooth_spectrum, {
mode = "savgol_smooth",
taps = 11,
poly_order = 3
})
-- Savitzky-Golay derivative (noise-resistant)
ooc.sim_add_sieve_operator(ctx, signal, derivative, {
mode = "savgol_derivative",
taps = 7,
poly_order = 2
})
-- Hann-windowed low-pass
ooc.sim_add_sieve_operator(ctx, audio, filtered, {
mode = "hann_low_pass",
taps = 15
})
-- Blackman high-pass (steep cutoff)
ooc.sim_add_sieve_operator(ctx, signal, highpass, {
mode = "blackman_high_pass",
taps = 21
})
-- Tukey window (adjustable taper)
ooc.sim_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.sim_add_sieve_operator(ctx, input, output, {
mode = "low_pass",
sigma = 1.5,
taps = 9,
gain = 2.0,
accumulate = true
})

sim_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.

sim_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.

Sampling Modes:

For a field uu with NN samples:

  • mean: uˉ=1Ni=0N1ui\bar{u} = \frac{1}{N}\sum_{i=0}^{N-1} u_i
  • rms: urms=1Ni=0N1ui2u_{\text{rms}} = \sqrt{\frac{1}{N}\sum_{i=0}^{N-1} |u_i|^2}
  • point: usample_indexu_{\text{sample\_index}}
  • min/max: mini(ui)\min_i(u_i) or maxi(ui)\max_i(u_i)

Feature Translation:

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

param=clamp(base+scalef(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=Asin(2πfct+Isin(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.

Output Configuration:

ParameterTypeDefaultDescription
output_modeenum "controls"Output type: controls (parameter values) or raw_samples (audio buffer)
output_businteger 0Target audio bus index
output_senddouble 1.0Send level [0, 1]
output_pre_faderboolean falseSend before fader processing

Sampling Configuration:

ParameterTypeDefaultDescription
sampling_modeenum "mean"How to reduce field: mean, rms, point, min, max
sampling_domainenum "physical"Sample in physical or spectral domain
window_typeenum "rect"Window function: rect, hann, hamming, blackman
window_lengthinteger 0Window size (0 = full field)
window_offsetinteger 0Window start offset
sample_indexinteger 0Index for point sampling mode

Gain Translation:

ParameterTypeDefaultDescription
gain_sourceenum "off"Feature source: off, amplitude, energy, phase, mean_x, mean_y
gain_modulatorenum "none"Modulation source: none, self_field, external_field
gain_basedouble 1.0Base gain value
gain_scaledouble 1.0Gain scaling factor
gain_mindouble 0.0Minimum gain
gain_maxdouble 1.0Maximum gain

Pan Translation:

ParameterTypeDefaultDescription
pan_sourceenum "off"Feature source for panning
pan_modulatorenum "none"Modulation source
pan_centerdouble 0.0Pan center position [-1, 1]
pan_widthdouble 1.0Pan range width
pan_lawenum "linear"Pan law: linear, sqrt, power

Pitch Translation:

ParameterTypeDefaultDescription
pitch_sourceenum "off"Feature source for pitch
pitch_modulatorenum "none"Modulation source
pitch_base_hzdouble 440.0Base frequency in Hz
pitch_range_octavesdouble 1.0Pitch range in octaves
pitch_scaleenum "linear_hz"Scale mapping: linear_hz, log_semitone

FM Translation:

ParameterTypeDefaultDescription
fm_sourceenum "off"Feature source for FM depth
fm_modulatorenum "none"Modulation source
fm_depthdouble 0.0Maximum modulation index
fm_centerdouble 0.0FM center frequency offset
fm_ratiodouble 1.0Carrier-to-modulator frequency ratio
fm_clipdouble 0.0FM output clipping (0 = no clip)

Envelope:

ParameterTypeDefaultDescription
attack_msdouble 0.0Attack time in milliseconds
release_msdouble 0.0Release time in milliseconds
smoothing_taudouble 0.0Exponential smoothing time constant

Raw Sampling Mode:

ParameterTypeDefaultDescription
raw_sourceenum "real"Component: real, imag, magnitude, phase
raw_channel_modeenum "mono"Channel mode: mono, stereo
raw_gaindouble 1.0Raw output gain
raw_clipdouble 1.0Hard clipping threshold
raw_resample_modeenum "linear"Resampling: linear, nearest
  • 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
  • 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
  • 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
-- Basic amplitude-to-gain sonification
ooc.sim_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.sim_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.sim_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.sim_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.sim_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.sim_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.sim_add_sound_observation_operator(ctx, field, {
sampling_mode = "point",
sample_index = N / 2,
gain_source = "amplitude",
smoothing_tau = 0.01
})