API reference

faustax.processors

dasp-style batched wrappers around the Faust-compiled NNX modules.

Each Processor wraps one generated module and exposes:

  • process(x, **physical_params) — parameters by the Faust shortnames of the sliders declared in the .dsp source (equal to the bare slider label when labels are unique), as scalars (broadcast) or (batch,) arrays.

  • process_normalized(x, param_matrix) — a (batch, num_params) matrix of values on [0, 1], e.g. straight from a neural network controller. Column order is param_names (shortnames sorted alphabetically, so the layout is stable across code regeneration).

Audio is (batch, channels, samples). One-in/one-out Faust modules are vmapped over batch * channels (channels processed independently); all other modules require channels == module.num_inputs, are vmapped over batch, and return module.num_outputs channels (e.g. the mono-in stereo-out faustax.diffvox.PingPongDelay).

class Compressor(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

Bases: Processor

Feed-forward compressor with soft knee and makeup gain.

The level detector, quadratic soft-knee static curve and log(9) attack convention follow dasp-pytorch’s compressor; unlike dasp, release_ms is actually applied (switching one-pole ballistics per Giannoulis et al. 2012), and channels are compressed independently rather than through a summed sidechain.

Parameters: threshold_db, ratio, attack_ms, release_ms, knee_db, makeup_gain_db.

module_cls

alias of Compressor

class Distortion(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

Bases: Processor

Soft-clipping tanh distortion, tanh(x * 10^(drive_db/20)) (dasp-pytorch parity). Parameters: drive_db.

module_cls

alias of Distortion

class Freeverb(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

Bases: Processor

Mono Freeverb (Schroeder comb/allpass reverb) with dry/wet mix.

Parameters: room_size, damping, mix. The reverb tail is truncated at the input length (output length always equals input length).

module_cls

alias of Freeverb

class Gain(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

Bases: Processor

Gain in decibels. Parameters: gain_db.

module_cls

alias of Gain

class ParametricEQ(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

Bases: Processor

Six-section parametric EQ (dasp-pytorch parity): RBJ low shelf, four peaking bands, RBJ high shelf, coefficient-exact with dasp’s cookbook formulas but realized as exact per-sample biquads rather than dasp’s frequency-sampled approximation.

Parameters: low_shelf_gain_db, low_shelf_cutoff_freq, low_shelf_q_factor, band{0,1,2,3}_gain_db, band{0,1,2,3}_cutoff_freq, band{0,1,2,3}_q_factor, high_shelf_gain_db, high_shelf_cutoff_freq, high_shelf_q_factor.

module_cls

alias of ParametricEQ

class Processor(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

Bases: object

Batched, differentiable wrapper around a Faust-compiled NNX module.

Variables:
  • module – The underlying generated nnx.Module (use directly for streaming via initialize_carry()/process_block() or for NNX-native training).

  • param_names – Faust slider shortnames in the column order used by process_normalized().

  • param_ranges – Mapping of shortname to (min, max) physical range, introspected from the Faust slider declarations.

  • defaults – Mapping of shortname to the physical default value.

module_cls: Type[Module]
property num_params: int

Number of continuous parameters (columns of the normalized matrix).

process(x: Array, rng: Array | None = None, **params) Array

Process audio with physical parameter values.

Parameters:
  • x – Audio of shape (batch, channels, samples).

  • rng – JAX random key for stochastic DSPs (noise generators etc.). Deterministic effects ignore it; defaults to a fixed key.

  • **params – Physical values keyed by slider label. Scalars broadcast over the batch; arrays of shape (batch,) vary per item. Omitted parameters use the Faust slider defaults. Concrete out-of-range values raise ValueError; traced values are clipped to the slider range (Faust semantics).

Returns:

Audio of shape (batch, channels, samples).

process_normalized(x: Array, param_matrix: Array, rng: Array | None = None) Array

Process audio with normalized parameters on [0, 1].

Parameters:
  • x – Audio of shape (batch, channels, samples).

  • param_matrix – Parameters of shape (batch, num_params) on [0, 1], columns ordered as param_names.

  • rng – JAX random key for stochastic DSPs (noise generators etc.). Deterministic effects ignore it; defaults to a fixed key.

Returns:

Audio of shape (batch, channels, samples).

class StereoPanner(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

Bases: Processor

Mono-to-stereo panner with dasp-pytorch’s equal-power law (pan 0 = hard left, 0.5 = center at -4.5 dB, 1 = hard right). Input is (batch, 1, samples); output is (batch, 2, samples). Parameters: pan.

module_cls

alias of StereoPanner

class StereoWidener(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

Bases: Processor

Mid-side stereo widener (dasp-pytorch parity); width 0 collapses to mono, 0.5 is identity, 1 keeps only the side signal. Input and output are (batch, 2, samples). Parameters: width.

module_cls

alias of StereoWidener

faustax.modules

Public access to the Faust-compiled NNX module classes.

This is the supported import path for the raw generated modules — the per-sample nnx.Module classes the Faust NNX backend emits — as opposed to the batched faustax.Processor wrappers around some of them. Use these when you want the module surface itself: initialize_carry() / process_block() streaming, NNX-native training, synthesizers that take no audio input (ModalPiano), or research DSPs that have no wrapper.

The classes live in faustax._generated (compiler output, regenerated by tools/generate.py); import them from here so your code does not reach into a private package. tests/test_compile.py asserts this module re-exports every generated class. Each class carries the full generated docstring, so read them with help() rather than in this reference:

Effects with a faustax.Processor wrapper

Gain, Distortion, ParametricEQ, Compressor, Freeverb, StereoPanner, StereoWidener.

Instruments and research DSPs

ModalPiano (a modal piano string; no audio input), ModeDrive (a mode-selecting saturator with a learnable interior).

Learnable-structure demonstrations

LearnableControlResponse and LearnableTwoKnob fit the mapping from a known knob to the DSP (see the Parameter estimation page). LowpassAutomated lifts its cutoff slider to an audio-rate input channel, which makes a time-varying automation learnable; the source and the recipe are in examples/parameter_estimation/automation.py.

DiffVox ports

DiffVoxCompressor, DiffVoxEq, DiffVoxFDN, DiffVoxPingpong (see faustax.diffvox).

magic_clamp() is the architecture-level straight-through clamp (Hayes 2025) every generated module uses for normalized parameters. It is a pure function, and the architecture file emits an identical copy into every module, so this package re-exports the copy from the simplest generated module (gain) as the one public definition.

faustax.functional

Functional entry points, dasp-pytorch style.

Each function constructs (and caches per sample rate) the corresponding faustax.processors wrapper and calls Processor.process(). Parameters are physical values; scalars broadcast over the batch and (batch,) arrays vary per item.

compressor(x: Array, sample_rate: int, **params) Array

Apply the feed-forward compressor.

Parameters:
  • x – Audio of shape (batch, channels, samples).

  • sample_rate – Sample rate of x.

  • **params – Physical values keyed by slider label (see faustax.processors.Compressor).

Returns:

Audio of shape (batch, channels, samples).

distortion(x: Array, sample_rate: int, drive_db=0.0) Array

Apply soft-clipping tanh distortion, tanh(x * 10^(drive_db/20)).

Parameters:
  • x – Audio of shape (batch, channels, samples).

  • sample_rate – Sample rate of x.

  • drive_db – Drive in dB on [0, 24], scalar or (batch,).

Returns:

Audio of shape (batch, channels, samples).

freeverb(x: Array, sample_rate: int, **params) Array

Apply the Freeverb reverb with dry/wet mix.

Parameters:
  • x – Audio of shape (batch, channels, samples).

  • sample_rate – Sample rate of x.

  • **params – Physical values keyed by slider label (see faustax.processors.Freeverb).

Returns:

Audio of shape (batch, channels, samples).

gain(x: Array, sample_rate: int, gain_db=0.0) Array

Apply gain in decibels.

Parameters:
  • x – Audio of shape (batch, channels, samples).

  • sample_rate – Sample rate of x.

  • gain_db – Gain in dB, scalar or (batch,).

Returns:

Audio of shape (batch, channels, samples).

noise_shaped_reverberation(x: Array, sample_rate: int, rng: Array | None = None, num_samples: int = 65536, num_bandpass_taps: int = 1023, **params) Array

Apply 12-band noise-shaped reverberation (dasp-pytorch parity).

Parameters:
  • x – Audio of shape (batch, channels, samples), 1 or 2 channels.

  • sample_rate – Sample rate of x.

  • rng – Key for the shaping noise (fixed default key if omitted).

  • num_samples – Length of the synthesized impulse response.

  • num_bandpass_taps – FIR length of each octave-band filter (odd).

  • **params – Values on [0, 1] keyed by parameter name (band{0..11}_gain, band{0..11}_decay, mix; see faustax.reverb.NoiseShapedReverb).

Returns:

Stereo audio of shape (batch, 2, samples).

parametric_eq(x: Array, sample_rate: int, **params) Array

Apply the parametric EQ (low shelf, two peaking bands, high shelf).

Parameters:
  • x – Audio of shape (batch, channels, samples).

  • sample_rate – Sample rate of x.

  • **params – Physical values keyed by slider label (see faustax.processors.ParametricEQ).

Returns:

Audio of shape (batch, channels, samples).

stereo_bus(x: Array, sample_rate: int, send_db=0.0) Array

Sum stereo tracks to a stereo bus with per-track send levels.

A pure gain-and-sum (dasp-pytorch’s stereo_bus); there is no Faust module underneath because the track count is dynamic.

Parameters:
  • x – Stereo tracks of shape (batch, 2, num_tracks, samples).

  • sample_rate – Sample rate of x (unused; kept for API symmetry).

  • send_db – Send levels in dB: scalar, (num_tracks,), (batch, num_tracks), or dasp’s (batch, num_tracks, 1).

Returns:

Stereo audio of shape (batch, 2, samples).

stereo_panner(x: Array, sample_rate: int, pan=0.5) Array

Pan mono tracks across the stereo field with an equal-power law.

Follows dasp-pytorch’s stereo_panner, including its output layout.

Parameters:
  • x – Mono tracks of shape (batch, num_tracks, samples).

  • sample_rate – Sample rate of x.

  • pan – Position on [0, 1] (0 = left, 1 = right), scalar, (batch,) with one value per batch item, or (batch, num_tracks).

Returns:

Stereo audio of shape (batch, 2, num_tracks, samples).

stereo_widener(x: Array, sample_rate: int, width=0.5) Array

Apply mid-side stereo widening (0 = mono, 0.5 = identity, 1 = side only).

Parameters:
  • x – Stereo audio of shape (batch, 2, samples).

  • sample_rate – Sample rate of x.

  • width – Width on [0, 1], scalar or (batch,).

Returns:

Stereo audio of shape (batch, 2, samples).

faustax.reverb

Noise-shaped artificial reverberation, ported from dasp-pytorch.

This is the one processor in Faustax that is hand-written JAX rather than a compiled Faust program: the algorithm applies a synthesized 65536-tap FIR by convolution, which has no per-sample-recurrence formulation the NNX backend could express. The construction follows dasp_pytorch.functional. noise_shaped_reverberation (Steinmetz et al., WASPAA 2021) exactly: white noise filtered through a 12-band octave filterbank, shaped per band by an exponential decay envelope and gain, averaged into a stereo impulse response, and convolved with the input.

Unlike dasp (which draws unseeded torch.randn noise on every call), the noise here is keyed: pass rng for fresh noise, or omit it for a fixed key so results are reproducible by default.

class NoiseShapedReverb(sample_rate: int, num_samples: int = 65536, num_bandpass_taps: int = 1023, faust_float=jnp.float32)

Bases: object

dasp-style batched wrapper around the noise-shaped reverb.

Mirrors the faustax.Processor interface (param_names, param_ranges, defaults, process(), process_normalized()) without a Faust module underneath. Parameters are the twelve octave-band gains and decays plus a wet/dry mix, all on [0, 1], matching dasp_pytorch.modules.NoiseShapedReverb.

Audio is (batch, channels, samples) with 1 or 2 channels; the output is always stereo (batch, 2, samples) (mono input is duplicated, as in dasp).

property num_params: int

Number of continuous parameters (columns of the normalized matrix).

process(x: Array, rng: Array | None = None, **params) Array

Process audio with physical parameter values.

Parameters:
  • x – Audio of shape (batch, channels, samples), 1 or 2 channels.

  • rng – Key for the shaping noise. Defaults to a fixed key, so calls are deterministic; pass fresh keys for fresh noise.

  • **params – Values on [0, 1] keyed by parameter name. Scalars broadcast over the batch; arrays of shape (batch,) vary per item. Omitted parameters use the defaults.

Returns:

Stereo audio of shape (batch, 2, samples).

process_normalized(x: Array, param_matrix: Array, rng: Array | None = None) Array

Process audio with normalized parameters on [0, 1].

Every parameter’s physical range is [0, 1], so normalized and physical values coincide; columns are ordered as param_names.

Parameters:
  • x – Audio of shape (batch, channels, samples), 1 or 2 channels.

  • param_matrix – Parameters of shape (batch, num_params).

  • rng – Key for the shaping noise (fixed default key if omitted).

Returns:

Stereo audio of shape (batch, 2, samples).

faustax.audiotree

Use Faustax processors as audiotree JAX transforms.

Requires the audiotree extra (pip install faustax[audiotree]).

FaustFx wraps any faustax.Processor as an audiotree BaseRandomTransform: physical parameter values are drawn per batch item from distribution tuples, the processor runs vmapped over the batch, and the usual audiotree transform machinery (prob, scope, output_key, split_seed) applies.

Example:

from faustax import Compressor
from faustax.audiotree import FaustFx

transform = FaustFx(
    Compressor(sample_rate=44100),
    param_dists={
        "threshold_db": ("uniform", -40.0, -6.0),
        "ratio": ("choice", [2.0, 4.0, 8.0]),
        "attack_ms": ("const", 5.0),
    },
    prob=0.5,
)
batch = transform.random_map(batch, jax.random.key(0))

Distribution tuples follow the audiotools convention: ("const", value), ("uniform", low, high), or ("choice", [values...]). Parameters not listed use the Faust slider defaults.

class FaustFx(processor: Processor, param_dists: Dict[str, Tuple[Any, ...]] | None = None, prob: float = 1.0, split_seed: bool = True, scope: Dict[str, Any] | None = None, output_key: str | Callable[[List[str]], str] | None = None)

Bases: BaseRandomTransform

audiotree random transform that applies a Faustax processor.

Loudness metadata (lufs, lufs_windows) is cleared in _pre_transform — i.e. regardless of whether the probabilistic branch fires — because the effect changes spectral content and both branches of the prob selection must share one pytree structure.

Parameters:
  • processor – A constructed faustax.Processor. Its sample rate must match the AudioTrees this transform is applied to.

  • param_dists – Mapping of slider label to a distribution tuple (("const", v), ("uniform", lo, hi), or ("choice", [vs])) sampled per batch item. Omitted parameters use the Faust slider defaults. Bounds are validated against the slider ranges at construction time.

  • prob – Probability of applying the transform.

  • split_seed – Whether to use a different key per AudioTree leaf.

  • scope – Which pytree leaves to transform (audiotree convention).

  • output_key – Where to store the transformed output (audiotree convention).

static get_default_config() Dict[str, Any]

Get the default configuration for the transform.

Returns:

Default configuration dictionary

faustax.fx

argbind-ready audiotree transform factories, one per Faustax effect.

Every public callable in this module is a snake_case factory that returns a configured faustax.audiotree.FaustFx transform, with an introspected signature (one keyword per Faust slider) so argbind can expose each parameter as a <factory>.<param> config key. This matches the common trainer pattern of instantiating transforms by name from a scoped module:

import argbind
from faustax import fx as fx_lib

fx_lib = argbind.bind_module(fx_lib, "train", "val", "test", "gen")

@argbind.bind("train", "val", "test", "gen")
def augment_batch(rng, batch, transforms: list[str] = None):
    for name in transforms or []:
        transform = getattr(fx_lib, name)()   # args come from the config
        rng, subkey = jax.random.split(rng)
        batch = transform.random_map(batch, subkey)
    return batch

with a YAML config such as:

compressor_fx.sample_rate: 44100
compressor_fx.threshold_db: [uniform, -40.0, -6.0]
compressor_fx.ratio: [choice, [2.0, 4.0, 8.0]]
compressor_fx.attack_ms: 5.0

train/augment_batch.transforms: [compressor_fx]

Parameter values accept either a scalar (a fixed value, ("const", v)) or a distribution list [kind, ...] with kind uniform (low, high), choice (one list of values), or const. Omitted parameters use the Faust slider defaults.

compressor_fx(*, sample_rate: int = 44100, unroll: int = 1, attack_ms=None, knee_db=None, makeup_gain_db=None, ratio=None, release_ms=None, threshold_db=None, prob: float = 1.0, split_seed: bool = True, scope: dict = None, output_key: str = None) FaustFx

audiotree transform applying the Faustax Compressor effect.

Parameters:
  • sample_rate – Sample rate the DSP runs at (must match the batch).

  • unroll – Unroll factor for the per-sample scan.

  • attack_ms – Scalar or [kind, …] distribution over [0.1, 100.0] (slider default 10.0).

  • knee_db – Scalar or [kind, …] distribution over [0.0, 12.0] (slider default 0.0).

  • makeup_gain_db – Scalar or [kind, …] distribution over [0.0, 24.0] (slider default 0.0).

  • ratio – Scalar or [kind, …] distribution over [1.0, 20.0] (slider default 4.0).

  • release_ms – Scalar or [kind, …] distribution over [5.0, 500.0] (slider default 100.0).

  • threshold_db – Scalar or [kind, …] distribution over [-60.0, 0.0] (slider default -24.0).

  • prob – Probability of applying the transform.

  • split_seed – Whether to use a different key per AudioTree leaf.

  • scope – Which pytree leaves to transform (audiotree convention).

  • output_key – Where to store the transformed output.

distortion_fx(*, sample_rate: int = 44100, unroll: int = 1, drive_db=None, prob: float = 1.0, split_seed: bool = True, scope: dict = None, output_key: str = None) FaustFx

audiotree transform applying the Faustax Distortion effect.

Parameters:
  • sample_rate – Sample rate the DSP runs at (must match the batch).

  • unroll – Unroll factor for the per-sample scan.

  • drive_db – Scalar or [kind, …] distribution over [0.0, 24.0] (slider default 0.0).

  • prob – Probability of applying the transform.

  • split_seed – Whether to use a different key per AudioTree leaf.

  • scope – Which pytree leaves to transform (audiotree convention).

  • output_key – Where to store the transformed output.

freeverb_fx(*, sample_rate: int = 44100, unroll: int = 1, damping=None, mix=None, room_size=None, prob: float = 1.0, split_seed: bool = True, scope: dict = None, output_key: str = None) FaustFx

audiotree transform applying the Faustax Freeverb effect.

Parameters:
  • sample_rate – Sample rate the DSP runs at (must match the batch).

  • unroll – Unroll factor for the per-sample scan.

  • damping – Scalar or [kind, …] distribution over [0.0, 0.99] (slider default 0.5).

  • mix – Scalar or [kind, …] distribution over [0.0, 1.0] (slider default 0.33).

  • room_size – Scalar or [kind, …] distribution over [0.0, 0.99] (slider default 0.5).

  • prob – Probability of applying the transform.

  • split_seed – Whether to use a different key per AudioTree leaf.

  • scope – Which pytree leaves to transform (audiotree convention).

  • output_key – Where to store the transformed output.

gain_fx(*, sample_rate: int = 44100, unroll: int = 1, gain_db=None, prob: float = 1.0, split_seed: bool = True, scope: dict = None, output_key: str = None) FaustFx

audiotree transform applying the Faustax Gain effect.

Parameters:
  • sample_rate – Sample rate the DSP runs at (must match the batch).

  • unroll – Unroll factor for the per-sample scan.

  • gain_db – Scalar or [kind, …] distribution over [-60.0, 24.0] (slider default 0.0).

  • prob – Probability of applying the transform.

  • split_seed – Whether to use a different key per AudioTree leaf.

  • scope – Which pytree leaves to transform (audiotree convention).

  • output_key – Where to store the transformed output.

parametric_eq_fx(*, sample_rate: int = 44100, unroll: int = 1, band0_cutoff_freq=None, band0_gain_db=None, band0_q_factor=None, band1_cutoff_freq=None, band1_gain_db=None, band1_q_factor=None, band2_cutoff_freq=None, band2_gain_db=None, band2_q_factor=None, band3_cutoff_freq=None, band3_gain_db=None, band3_q_factor=None, high_shelf_cutoff_freq=None, high_shelf_gain_db=None, high_shelf_q_factor=None, low_shelf_cutoff_freq=None, low_shelf_gain_db=None, low_shelf_q_factor=None, prob: float = 1.0, split_seed: bool = True, scope: dict = None, output_key: str = None) FaustFx

audiotree transform applying the Faustax ParametricEQ effect.

Parameters:
  • sample_rate – Sample rate the DSP runs at (must match the batch).

  • unroll – Unroll factor for the per-sample scan.

  • band0_cutoff_freq – Scalar or [kind, …] distribution over [80.0, 2000.0] (slider default 600.0).

  • band0_gain_db – Scalar or [kind, …] distribution over [-20.0, 20.0] (slider default 0.0).

  • band0_q_factor – Scalar or [kind, …] distribution over [0.1, 6.0] (slider default 0.707).

  • band1_cutoff_freq – Scalar or [kind, …] distribution over [2000.0, 8000.0] (slider default 3000.0).

  • band1_gain_db – Scalar or [kind, …] distribution over [-20.0, 20.0] (slider default 0.0).

  • band1_q_factor – Scalar or [kind, …] distribution over [0.1, 6.0] (slider default 0.707).

  • band2_cutoff_freq – Scalar or [kind, …] distribution over [8000.0, 12000.0] (slider default 10000.0).

  • band2_gain_db – Scalar or [kind, …] distribution over [-20.0, 20.0] (slider default 0.0).

  • band2_q_factor – Scalar or [kind, …] distribution over [0.1, 6.0] (slider default 0.707).

  • band3_cutoff_freq – Scalar or [kind, …] distribution over [12000.0, 21050.0] (slider default 14000.0).

  • band3_gain_db – Scalar or [kind, …] distribution over [-20.0, 20.0] (slider default 0.0).

  • band3_q_factor – Scalar or [kind, …] distribution over [0.1, 6.0] (slider default 0.707).

  • high_shelf_cutoff_freq – Scalar or [kind, …] distribution over [4000.0, 21050.0] (slider default 8000.0).

  • high_shelf_gain_db – Scalar or [kind, …] distribution over [-20.0, 20.0] (slider default 0.0).

  • high_shelf_q_factor – Scalar or [kind, …] distribution over [0.1, 6.0] (slider default 0.707).

  • low_shelf_cutoff_freq – Scalar or [kind, …] distribution over [20.0, 2000.0] (slider default 200.0).

  • low_shelf_gain_db – Scalar or [kind, …] distribution over [-20.0, 20.0] (slider default 0.0).

  • low_shelf_q_factor – Scalar or [kind, …] distribution over [0.1, 6.0] (slider default 0.707).

  • prob – Probability of applying the transform.

  • split_seed – Whether to use a different key per AudioTree leaf.

  • scope – Which pytree leaves to transform (audiotree convention).

  • output_key – Where to store the transformed output.

faustax.ops

Differentiable recursive-filter primitives with efficient custom gradients.

The generated Faustax modules differentiate their per-sample scan by ordinary reverse-mode autodiff, which stores the whole carry at every time step. For the specific (and ubiquitous) case of an all-pole recursion there is a much better rule, due to Yu & Fazekas (torchlpc, DAFx 2024, arXiv:2404.07970): the vector-Jacobian product of an all-pole filter is the same filter run backwards in time with time-shifted coefficients, plus a per-sample outer product. The backward pass therefore only needs the filter output (one array of length T) instead of the per-step carry history, and it reuses the forward kernel, so higher-order derivatives compose for free.

allpole() implements that rule as a jax.custom_vjp(). The order-1 special case covers one-pole smoothers (compressor ballistics, leaky integrators); see ballistics() in faustax.dynamics.

Conventions follow torchlpc.sample_wise_lpc (unbatched here — add batch dimensions with jax.vmap()):

y[t] = x[t] - sum_{k=0}^{K-1} a[t, k] * y[t - k - 1]

with initial conditions zi[k] = y[-k - 1] (scipy lfilter delay ordering: zi[0] is the most recent past output).

This module is original JAX code, but the gradient rules and the argument conventions it follows are ported from Chin-Yun Yu’s PyTorch libraries, both MIT licensed: torchlpc (Copyright (c) 2023 Chin-Yun Yu) for the all-pole custom-VJP rule, and philtorch (Copyright (c) 2025 Chin-Yun Yu) for the DF2 decomposition and the state-space conventions and adjoint VJP. See section 3 of the repository’s NOTICE file for the full license text.

diag_state_space(a: Array, B: Array, C: Array, D: Array | None, x: Array, zi: Array) Array

Diagonal (modal) state space — the fast path of state_space().

When the state matrix is diagonal in its eigenbasis, A = diag(a), the recursion decouples into N independent one-pole modes:

h_i[n + 1] = a_i[n] * h_i[n] + (B[n] @ x[n])_i
y[n]       = C[n] @ h[n] + D[n] @ x[n]

with h[0] = zi. Each mode runs as a first-order linear_recurrence() (which supplies the efficient all-pole custom gradient), so the filter is O(T * N) instead of the dense O(T * N**2) and differentiates through the same rule. Follows philtorch’s diag_state_space.

A real resonant system is realized here by passing its complex eigenvalues in a with complex B/C; the output’s imaginary part then cancels, so take jnp.real(y). Conventions match state_space() (unbatched; jax.vmap for batch).

Parameters:
  • a – Diagonal state entries (eigenvalues), shape (T, N) or (N,); complex for resonant modes.

  • B – Input maps, shape (T, N, M) or (N, M).

  • C – Output maps, shape (T, P, N) or (P, N).

  • D – Feedthrough maps, shape (T, P, M) or (P, M); None for a feedthrough-free system.

  • x – Input signal of shape (T, M).

  • zi – Initial modal state h[0] of shape (N,).

Returns:

Output signal of shape (T, P) (complex if any map is complex).

filtfilt(b: Array, a: Array, x: Array) Array

Zero-phase forward-backward IIR filtering (constant coefficients).

Filters x with lfilter(), reverses, filters again, and reverses back, so the two passes cancel phase and the net response is zero-phase with squared magnitude. Matches scipy.signal.filtfilt with its defaults (padtype="odd", method="pad", padlen = 3 * max(len(a_full), len(b))): the signal is odd-extended at both ends and each pass starts from the steady-state delay-line history for that end’s boundary value, which suppresses edge transients.

Composes the differentiable lfilter() (which carries the custom all-pole VJP) with linear padding and reversals, so it needs no separate gradient rule. Only constant (LTI) coefficients are meaningful for a zero-phase filter.

Parameters:
  • b – Numerator taps of shape (Mb + 1,).

  • a – Denominator coefficients WITHOUT the leading 1, shape (Ma,) (the lfilter() convention); pass scipy.signal.butter(...)[1][1:].

  • x – Input signal of shape (T,).

Returns:

Zero-phase filtered signal of shape (T,).

Raises:

ValueError – If x is shorter than the padding length 3 * max(Ma + 1, Mb + 1) (scipy’s precondition).

fir(b: Array, x: Array, zi: Array | None = None) Array

Time-varying FIR filter y[t] = sum_k b[t, k] * x[t - k].

Feed-forward, so ordinary autodiff is already optimal — no custom VJP. Negative time indices read from zi (x[-k-1] = zi[k], most-recent-first, matching allpole()’s convention for outputs).

Parameters:
  • b – Taps of shape (T, K + 1), or (K + 1,) for time-invariant.

  • x – Input signal of shape (T,).

  • zi – Input history of shape (K,); defaults to zeros.

Returns:

Filtered signal of shape (T,).

lfilter(b: Array, a: Array, x: Array, zi: Array | None = None, return_zf: bool = False)

Direct-form-II IIR filter with time-varying (or constant) coefficients.

Computes, per philtorch’s DF2 decomposition (which is exact because DF2 has a single shared delay line w):

w[t] = x[t] - sum_{k=1..Ma} a[t, k-1] * w[t - k]   (allpole, custom VJP)
y[t] = sum_{k=0..Mb} b[t, k] * w[t - k]            (fir, plain autodiff)

The denominator is normalized: a EXCLUDES the leading unity coefficient (a = [a1, ..., aMa]), as in torchlpc/philtorch. With zi=None and constant coefficients this matches scipy.signal.lfilter([b0, ...], [1, a1, ...], x).

Parameters:
  • b – Numerator taps, shape (T, Mb + 1) or (Mb + 1,).

  • a – Denominator coefficients (without the leading 1), shape (T, Ma) or (Ma,).

  • x – Input signal of shape (T,).

  • zi – Shared delay-line history of shape (max(Ma, Mb),) with zi[k] = w[-k-1] (NOT scipy’s transposed-DF2 state). Defaults to zeros.

  • return_zf – When True, also return the final delay-line history zf[k] = w[T-1-k] of shape (max(Ma, Mb),) for streaming continuation (pass it as the next block’s zi).

Returns:

Filtered signal of shape (T,); with return_zf, the tuple (y, zf).

linear_recurrence(decay: Array, x: Array, zi: Array | None = None) Array

First-order linear recurrence y[t] = x[t] + decay[t] * y[t - 1].

A convenience wrapper over order-1 allpole() (which supplies the efficient custom gradient).

Parameters:
  • decay – Per-sample feedback coefficients of shape (T,).

  • x – Input signal of shape (T,).

  • zi – Scalar initial state y[-1]; defaults to 0.

Returns:

Output signal of shape (T,).

state_space(A: Array, B: Array, C: Array, D: Array | None, x: Array, zi: Array) Array

Linear state-space filter with time-varying (LPV) or constant (LTI) maps.

Computes, following philtorch’s state-space convention:

y[n] = C[n] @ h[n] + D[n] @ x[n]
h[n + 1] = A[n] @ h[n] + B[n] @ x[n]

with h[0] = zi. This generalizes allpole() (which is the companion-form special case of a single-output all-pole system) to an arbitrary MIMO system with N states, M inputs and P outputs.

The custom gradient is the classic adjoint (co-state) recursion — the transposed state space run backwards in time:

lam[n] = C[n].T @ g_y[n] + A[n].T @ lam[n + 1]    (lam[T] = 0)

so, like allpole(), the backward pass reuses the forward structure and keeps only the (T, N) state trajectory instead of the full scan graph, and higher-order derivatives compose.

Conventions follow the rest of this module: unbatched (add batch dimensions with jax.vmap()). Matrices may be passed per-sample or as a single constant matrix, which is broadcast over time:

Parameters:
  • A – State-transition maps, shape (T, N, N) or (N, N).

  • B – Input maps, shape (T, N, M) or (N, M).

  • C – Output maps, shape (T, P, N) or (P, N).

  • D – Feedthrough maps, shape (T, P, M) or (P, M); None for a feedthrough-free system (y[n] = C[n] @ h[n]).

  • x – Input signal of shape (T, M).

  • zi – Initial state h[0] of shape (N,). Pass jnp.zeros(N) for a zero initial state.

Returns:

Output signal of shape (T, P).

faustax.dynamics

Differentiable dynamic-range gain functions (JAX port of torchcomp).

These reproduce torchcomp’s user-facing API on top of the custom-VJP faustax.ops primitives, so gradients through the recursive smoothers keep O(T) residuals instead of per-step scan state.

All functions are unbatched — signals are shape (T,) and parameters are scalars; add batch dimensions with jax.vmap(). The returned gains are linear; multiply with the signal yourself (x * gain), as in torchcomp.

This module is original JAX code, but its API, ballistics and gain-computer conventions are ported from torchcomp (MIT, Copyright (c) 2024 Chin-Yun Yu). See section 3 of the repository’s NOTICE file for the full license text.

amp2db(x: Array) Array

Convert linear amplitude to decibels.

avg(rms: Array, avg_coef: Array) Array

Fixed-coefficient running average y[t] = c*rms[t] + (1-c)*y[t-1].

torchcomp’s recommended RMS-envelope smoother (zero initial state).

Parameters:
  • rms – Signal to smooth, shape (T,).

  • avg_coef – Averaging coefficient in (0, 1], scalar.

Returns:

Smoothed signal of shape (T,).

coef2ms(coef: Array, sample_rate: int) Array

Inverse of ms2coef().

compexp_gain(x_rms: Array, comp_thresh: Array, comp_ratio: Array, exp_thresh: Array, exp_ratio: Array, at: Array, rt: Array) Array

Compressor-expander gain (linear), matching torchcomp compexp_gain.

Hard-knee gain computer in dB — g = min(0, (1-1/CR)(CT-L), (1-1/ER)(ET-L)) with L = amp2db(x_rms) — followed by the switched attack/release smoother on the linear gain (initial state 1). Requires comp_ratio > 1 and 0 < exp_ratio < 1.

Parameters:
  • x_rms – Positive level envelope of shape (T,) (e.g. from avg() on sqrt(x**2)).

  • comp_thresh – Compressor threshold in dB, scalar.

  • comp_ratio – Compressor ratio (> 1), scalar.

  • exp_thresh – Expander threshold in dB, scalar.

  • exp_ratio – Expander ratio in (0, 1), scalar.

  • at – Attack coefficient in (0, 1), scalar (see ms2coef()).

  • rt – Release coefficient in (0, 1), scalar.

Returns:

Linear gain of shape (T,).

db2amp(x: Array) Array

Convert decibels to linear amplitude.

limiter_gain(x: Array, threshold: Array, at: Array, rt: Array) Array

Limiter gain (linear), matching torchcomp limiter_gain.

A switched peak detector on |x| (attack when the level rises — implemented by swapping the coefficients of faustax.ops.ballistics()), a hard min(1, threshold_amp / peak) gain computer, and the same switched smoother on the gain.

Parameters:
  • x – Input signal of shape (T,).

  • threshold – Limiter threshold in dB (<= 0), scalar.

  • at – Attack coefficient in (0, 1), scalar.

  • rt – Release coefficient in (0, 1), scalar.

Returns:

Linear gain of shape (T,).

ms2coef(ms: Array, sample_rate: int) Array

Convert an attack/release time in milliseconds to a smoothing coefficient.

Uses torchcomp’s 10%-90% rise-time convention: coef = 1 - exp(-2200 / (ms * sample_rate)). Note this differs from Faust’s ba.tau2pole (exact 1/e time constant) by a factor of 2.2.

faustax.diffvox

Load DiffVox curated vocal-effects presets and drive the Faustax ports.

DiffVox (Yu et al., “DiffVox: A Differentiable Model for Capturing and Analysing Vocal Effects Distributions”, DAFx25) fits a fixed vocal effects chain to paired dry/wet vocal stems by gradient descent and publishes the fitted parameters as two curated preset datasets, Internal (385 presets) and MedleyDB (70 presets), in the diffvox repository (MIT, Copyright (c) 2025 Chin-Yun Yu). This module reads those datasets — no torch required — and maps each preset onto the Faustax ports of the chain’s processors.

The chain, fit at 44.1 kHz, is:

dry -> Peak -> Peak -> LowShelf -> HighShelf -> LowPass -> HighPass   (eq)
    -> CompressorExpander                                           (comp)
    -> pan(direct) + pingpong(z) + fdn(z + send * pingpong(z))     (sends)

where z is the compressor output, the ping-pong delay and FDN reverb run as parallel sends summed with the panned direct signal, and send is a cross-send gain feeding the delay’s stereo output into the FDN’s input.

Faust ports: EQ covers the six EQ stages, Compressor the dynamics stage, PingPongDelay the delay send, and FDN the reverb send (six delay lines, the trained orthogonal feedback matrix, frequency-dependent decay as 97-tap linear-phase FIRs per line, and the four-band tone EQ), following diffvox’s own real-time references in modules/rt.py. Chain wires all four into the full send topology.

A preset is stored as the flat vector of the torch model’s raw (pre- parametrization) tensors, concatenated in state-dict order. The functions here reproduce diffvox’s forward parametrizations — sigmoid min-max ranges, plain sigmoids, |x| % period wrapping, and the matrix-exponential unitary parametrization of the FDN feedback matrix — to recover physical values.

class Chain(sample_rate: int = SAMPLE_RATE, **processor_kwargs)

The full DiffVox chain: EQ, compressor, and the panned direct signal summed with the ping-pong and FDN sends.

Reproduces diffvox’s SendFXsAndSum topology (cross-send from the delay into the FDN, direct signal panned):

z       = compressor(eq(x))
delay   = pingpong(z)
reverb  = fdn(z + send_to_fdn * delay)
output  = pan(z) + delay + reverb

where pan is diffvox’s sqrt(2)-scaled constant-power law. Unlike diffvox’s IR-based renderer, every send is a per-sample recurrence, so the output length equals the input length and the sends’ tails truncate with it.

process(x: Array, preset: Dict, rng: Array | None = None) Array

Render a preset over a batch of dry mono vocals.

Parameters:
  • x – Dry audio of shape (batch, 1, samples).

  • preset – Physical parameter dict from preset_params().

  • rng – Forwarded to the processors (all four are deterministic).

Returns:

Wet stereo audio of shape (batch, 2, samples).

class Compressor(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

The compressor-expander of the DiffVox chain, torchcomp conventions.

RMS one-pole detector, hard-knee compressor/expander gain computer, switched attack/release smoother, and a fractional-delay gain lookahead. Parameters: comp_threshold_db, comp_ratio, exp_threshold_db, exp_ratio, attack_coef, release_coef, avg_coef, makeup_db, lookahead_ms. Attack/release are torchcomp smoothing coefficients (see faustax.dynamics.ms2coef()). compressor_params() produces them from a curated preset.

module_cls

alias of DiffVoxCompressor

class EQ(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

The six-stage vocal EQ of the DiffVox chain (Yu et al., DAFx25).

Two peaking bands, low/high shelf, low/high pass, all RBJ biquads with torchaudio’s exact coefficient formulas. Parameters: peak{1,2}_{gain_db,freq,q}, {low,high}_shelf_{gain_db,freq}, {low,high}_pass_{freq,q}. eq_params() produces them from a curated preset.

module_cls

alias of DiffVoxEq

EXPECTED_PARAMS_KEYS: Tuple[Tuple[str, Tuple[int, ...]], ...] = (('0.params.gain', ()), ('0.params.parametrizations.freq.original', ()), ('0.params.parametrizations.Q.original', ()), ('1.params.gain', ()), ('1.params.parametrizations.freq.original', ()), ('1.params.parametrizations.Q.original', ()), ('2.params.gain', ()), ('2.params.parametrizations.freq.original', ()), ('3.params.gain', ()), ('3.params.parametrizations.freq.original', ()), ('4.params.parametrizations.freq.original', ()), ('4.params.parametrizations.Q.original', ()), ('5.params.parametrizations.freq.original', ()), ('5.params.parametrizations.Q.original', ()), ('6.params.cmp_th', ()), ('6.params.exp_th', ()), ('6.params.make_up', ()), ('6.params.parametrizations.lookahead.original', (1,)), ('6.params.parametrizations.at.original', ()), ('6.params.parametrizations.rt.original', ()), ('6.params.parametrizations.avg_coef.original', ()), ('6.params.parametrizations.cmp_ratio.original', ()), ('6.params.parametrizations.exp_ratio.original', ()), ('7.params.parametrizations.sends_0.original', (1,)), ('7.effects.0.params.parametrizations.delay.original', ()), ('7.effects.0.params.parametrizations.feedback.original', ()), ('7.effects.0.params.parametrizations.gain.original', ()), ('7.effects.0.eq.params.parametrizations.freq.original', ()), ('7.effects.0.eq.params.parametrizations.Q.original', ()), ('7.effects.0.odd_pan.params.parametrizations.pan.original', ()), ('7.effects.0.even_pan.params.parametrizations.pan.original', ()), ('7.effects.1.params.b', (6, 2)), ('7.effects.1.params.c', (2, 6)), ('7.effects.1.params.parametrizations.gamma.original', (49, 1)), ('7.effects.1.params.parametrizations.U.original', (6, 6)), ('7.effects.1.eq.0.params.gain', ()), ('7.effects.1.eq.0.params.parametrizations.freq.original', ()), ('7.effects.1.eq.0.params.parametrizations.Q.original', ()), ('7.effects.1.eq.1.params.gain', ()), ('7.effects.1.eq.1.params.parametrizations.freq.original', ()), ('7.effects.1.eq.1.params.parametrizations.Q.original', ()), ('7.effects.1.eq.2.params.gain', ()), ('7.effects.1.eq.2.params.parametrizations.freq.original', ()), ('7.effects.1.eq.3.params.gain', ()), ('7.effects.1.eq.3.params.parametrizations.freq.original', ()), ('7.pan.params.parametrizations.pan.original', ()))

(key, shape). load_preset_dataset refuses datasets whose info.json disagrees, because the raw vector layout below is hard-coded against this.

Type:

The state-dict entries of one preset, in storage order

class FDN(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

The FDN reverb send of the DiffVox chain, stereo in / stereo out.

Six fixed-length delay lines (the chain’s prime delays compensated for the decay FIRs’ 48-sample group delay), per-line 97-tap linear-phase decay FIRs, an orthogonal feedback matrix, trained input/output gain matrices, and a four-band tone EQ on the stereo output — the port of diffvox’s real-time reference rt_fdn/RealTimeFDN.

The 652 parameters are slider banks (b{i}_{ch}, c{ch}_{i}, u{i}_{j}, fir{i}_{k}, eq_*); build them with fdn_params() rather than by hand. The output is the wet reverb only (sum with the dry signal yourself).

module_cls

alias of DiffVoxFDN

FDN_DELAYS = (997, 1153, 1327, 1559, 1801, 2099)

FDN delay-line lengths in samples (fixed, not trained).

NUM_RAW_PARAMS = 151

Length of one raw preset vector.

class PingPongDelay(sample_rate: int, faust_float=jnp.float32, unroll: int = 1, seed: int = 0)

The ping-pong delay send of the DiffVox chain, mono in / stereo out.

Cross-fed feedback with an in-loop RBJ lowpass; odd and even repeats are panned independently. Parameters: delay_ms, feedback, gain, eq_freq, eq_q, odd_pan, even_pan (pans on [-100, 100]). Input must be one channel; the output is the stereo wet signal only (sum with the dry signal yourself). pingpong_params() produces the parameters from a curated preset.

module_cls

alias of DiffVoxPingpong

class PresetDataset(raw_params: ndarray, runs: Tuple[str, ...], dry_files: Tuple[str, ...], wet_files: Tuple[str, ...], alignment_shifts: Tuple[int, ...], feature_mask: ndarray | None, gaussian_mean: ndarray | None, gaussian_cov: ndarray | None, train_index: ndarray | None)

One curated DiffVox preset dataset, e.g. presets/internal.

Variables:
  • raw_params (numpy.ndarray) – Raw parameter matrix of shape (num_presets, 151).

  • runs (Tuple[str, ...]) – Training-run identifier per preset.

  • dry_files (Tuple[str, ...]) – Dry (input) audio path per preset, as recorded at training time.

  • wet_files (Tuple[str, ...]) – Wet (target) audio path per preset.

  • alignment_shifts (Tuple[int, ...]) – Dry/wet alignment shift in samples per preset.

  • feature_mask (numpy.ndarray | None) – Boolean mask of shape (151,) selecting the 130 dimensions that determine the effect (drops the redundant entries of the FDN feedback matrix’s raw parametrization), or None if the dataset has no feature_mask.npy.

  • gaussian_mean (numpy.ndarray | None) – Mean of the parameter Gaussian in masked space, shape (130,), or None if the dataset has no gaussian.npz.

  • gaussian_cov (numpy.ndarray | None) – Covariance of that Gaussian, shape (130, 130), or None.

  • train_index (numpy.ndarray | None) – Indices of the presets used to fit the Gaussian/PCA, or None if the dataset has no train_index.npy.

expand_masked(masked: ndarray) ndarray

Expand a masked-space vector to a full raw preset vector.

The masked-out dimensions only parametrize redundant entries of the FDN feedback matrix (its parametrization reads the strict upper triangle of the raw 6x6 block), so zero-filling them reproduces the effect exactly. Use this to turn a sample from the dataset’s Gaussian prior into a preset:

rng = np.random.default_rng(0)
sample = rng.multivariate_normal(ds.gaussian_mean, ds.gaussian_cov)
params = preset_params(ds.expand_masked(sample))
Parameters:

masked – Vector of shape (feature_mask.sum(),), e.g. a draw from the dataset’s Gaussian prior.

Returns:

Raw preset vector of shape (151,).

SAMPLE_RATE = 44100

Sample rate the curated presets were fit at.

compressor_params(preset: Dict) Dict[str, float]

Slider values for Compressor.

Parameters:

preset – Physical parameter dict from preset_params().

Returns:

Keyword arguments for Compressor.process.

eq_params(preset: Dict) Dict[str, float]

Slider values for EQ.

Parameters:

preset – Physical parameter dict from preset_params().

Returns:

Keyword arguments for EQ.process.

fdn_fir_coefficients(preset: Dict) Tuple[ndarray, ndarray]

Per-line FDN decay FIRs, as diffvox’s real-time reference computes them.

Follows modules/rt.py::RealTimeFDN: the shared 49-point decay curve is raised to delay / min(delay) per line (delay-independent decay), then turned into a 97-tap linear-phase FIR with scipy.signal.firwin2, and each line’s delay is shortened by the FIR’s 48-sample group delay. fdn_params() feeds the result into FDN, whose fixed line delays are the compensated lengths returned here.

Parameters:

preset – Physical parameter dict from preset_params().

Returns:

Tuple of the FIR coefficient matrix, shape (6, 97), and the compensated delay lengths in samples, shape (6,).

fdn_gamma_max() float

Per-sample decay ceiling of the FDN’s shortest delay line.

diffvox bounds the trained decay so the shortest line loses at most 60 dB over three quarters of the FDN’s impulse-response duration.

fdn_params(preset: Dict) Dict[str, float]

Slider values for FDN.

Flattens the preset’s FDN arrays into the processor’s slider banks: input gains b{i}_{ch}, output gains c{ch}_{i}, feedback matrix u{i}_{j}, per-line decay FIR taps fir{i}_{k} (from fdn_fir_coefficients()), and the eq_* tone controls.

Parameters:

preset – Physical parameter dict from preset_params().

Returns:

Keyword arguments for FDN.process.

load_preset_dataset(root) PresetDataset

Load one curated preset dataset from a diffvox checkout.

Parameters:

root – Dataset directory holding info.json and raw_params.npy (e.g. <diffvox>/presets/internal or <diffvox>/presets/medleydb), with optional feature_mask.npy, gaussian.npz and train_index.npy alongside.

Returns:

The loaded dataset.

Raises:

RuntimeError – If the dataset’s parameter layout differs from the hard-coded one this module maps (EXPECTED_PARAMS_KEYS).

pingpong_params(preset: Dict) Dict[str, float]

Slider values for PingPongDelay.

Parameters:

preset – Physical parameter dict from preset_params().

Returns:

Keyword arguments for PingPongDelay.process.

preset_params(raw: ndarray) Dict

Map one preset’s raw parameter vector to physical parameters.

Parameters:

raw – Raw (pre-parametrization) parameter vector of shape (NUM_RAW_PARAMS,), i.e. one row of a dataset’s raw_params.npy.

Returns:

peak1, peak2, low_shelf, high_shelf, low_pass, high_pass, compressor, pingpong, fdn, plus the chain-level send_to_fdn (delay-to-FDN cross-send gain) and pan (direct-signal pan on [-100, 100]). Frequencies are in Hz, gains and thresholds in dB unless suffixed otherwise, attack/release as torchcomp smoothing coefficients in (0, 1) (faustax.dynamics.coef2ms() converts to milliseconds), and the FDN entries are arrays: input/output gains b (6, 2) and c (2, 6), per-sample decay gamma (49,) sampled on a linear frequency grid from 0 to Nyquist, and the orthogonal feedback matrix matrix (6, 6).

Return type:

Nested dict of physical parameter values, keyed by chain stage

faustax.filters

Differentiable resonant filters built on faustax.ops.

A topology-preserving-transform (TPT) state-variable filter (Zavalishin; Cytomic’s trapezoidal SVF) is, per sample, a two-state linear system, so it is exactly an faustax.ops.state_space() — which makes it differentiable in its cutoff and resonance, stable under modulation, and able to emit its lowpass/bandpass/highpass/notch responses from one pass.

svf() is the functional core (physical Hz/Q, scalar or per-sample for modulation). SVF is a learnable flax.nnx.Module wrapper whose cutoff and resonance are trained through smooth unconstrained parameters.

class SVF(*args: Any, **kwargs: Any)

Bases: Module

Learnable state-variable filter with trainable cutoff and resonance.

Cutoff and resonance live in smooth, unconstrained parameters so gradient descent never stalls at a range boundary: the cutoff is a log-spaced sigmoid over [20 Hz, 0.49 * sample_rate] and the resonance is 0.5 + softplus(.). Train it like any NNX module (nnx.grad / nnx.value_and_grad over the module).

Variables:
  • sample_rate – Sample rate in Hz.

  • mode – The active filter response, one of MODES.

property cutoff: Array

Current cutoff frequency in Hz.

property q: Array

Current resonance (quality factor).

svf(x: Array, cutoff: Array, q: Array, sample_rate: float, mode: str = 'lowpass') Array

Differentiable state-variable filter (one mono channel).

Parameters:
  • x – Input signal of shape (T,).

  • cutoff – Cutoff frequency in Hz — a scalar, or (T,) for per-sample modulation (an LPV filter).

  • q – Resonance (quality factor) — a scalar or (T,). 0.707 is maximally flat; higher values resonate.

  • sample_rate – Sample rate in Hz.

  • mode – One of MODES"lowpass", "bandpass", "highpass" or "notch".

Returns:

Filtered signal of shape (T,).

Raises:

ValueError – If mode is not one of MODES.

faustax.reverb_losses

Reverb-tuned, level-normalized spectral losses for fitting to a reference.

When fitting a differentiable reverb to a reference (a plugin, hardware, or a measured room), the reference’s wet output is not sample-aligned to the model’s, so a waveform loss is meaningless; and a raw log-energy loss has a degenerate “quiet, fast-decay” minimum an optimizer will happily walk into. These losses, after Gloria Dal Santo et al. (“Similarity Metrics For Late Reverberation”, Asilomar 2024), are differentiable, magnitude-based (alignment-free), and normalized so absolute level cannot be gamed:

  • averaged_power_convergence() (L_PC) — local time-frequency power match; the primary spectral objective.

  • energy_decay_convergence() (L_EDC) — per-band Schroeder energy-decay curves, each normalized to 0 dB; the ungameable decay-rate anchor (compute it on impulse / noise / sweep probes).

  • sparsity_loss() — an FDN feedback-matrix density term (Dal Santo, DAFx23 “Optimizing Tiny Colorless FDNs”).

All signal losses take mono batches [B, N] and return a scalar (mean over the batch). See examples/studies/audio_loss_design.py for the multi-resolution STFT loss and the design rationale.

averaged_power_convergence(y: Array, t: Array, n: int = 1024, hop: int = 256, win: int = 64, stride: int = 4, eps: float = 1e-8) Array

Averaged power convergence L_PC between model y and target t.

Both signals’ magnitude-STFT power is locally averaged by a 2-D Hann window, then compared by spectral convergence — the Frobenius norm of the smoothed local-power difference, normalized by the target’s smoothed-power norm.

This is a numerically stable variant of Dal Santo’s averaged power convergence: their published form divides by the product of the two smoothed powers, well-behaved for measured RIRs (bounded below by a noise floor) but divergent on synthetic IRs that decay to true silence. Spectral-convergence normalization keeps the same “local averaged power match” objective, is bounded and level-normalized, and is zero exactly when the two agree.

Parameters:
  • y – Model mono audio [B, N].

  • t – Target mono audio [B, N].

  • n – STFT window length.

  • hop – STFT hop.

  • win – Side length of the square Hann smoothing window.

  • stride – Stride of the smoothing convolution.

  • eps – Denominator floor.

Returns:

Scalar loss.

energy_decay_convergence(y: Array, t: Array, sample_rate: int, bands: Sequence[tuple[float, float]] = None, eps: float = 1e-12) Array

Per-band energy-decay convergence L_EDC between y and t.

For each 1/3-octave band, the relative squared error between the model’s and the target’s 0-dB-normalized Schroeder EDCs, averaged over bands and batch. Because every band EDC starts at 0 dB, the loss constrains decay rate per band and cannot be satisfied by making the whole reverb quieter.

Parameters:
  • y – Model mono audio [B, N].

  • t – Target mono audio [B, N].

  • sample_rate – Sample rate (Hz).

  • bands – 1/3-octave (lo, hi) edges; defaults to 20 Hz-12.5 kHz.

  • eps – Numerical floor.

Returns:

Scalar loss.

sparsity_loss(a: Array) Array

FDN feedback-matrix density term (N√N - Σ|A_ij|) / (N(√N - 1)).

Zero when A is maximally mixing (all |A_ij| = 1/√N, Hadamard-like), larger as A grows sparser. Minimizing it favors a diffuse (colorless) tail (Dal Santo et al., “Optimizing Tiny Colorless FDNs”); it is a knob, not always a goal — for matching a less-diffuse reference, weight it low or zero.

Parameters:

a – The (orthogonal) feedback matrix [N, N].

Returns:

Scalar density term.

third_octave_bands(fmin: float = 20.0, fmax: float = 12500.0) list[tuple[float, float]]

Returns (lo, hi) edges of 1/3-octave bands spanning [fmin, fmax].

faustax.compile

Runtime compilation of Faust DSP source to NNX module classes.

Faustax normally ships ahead-of-time-generated modules, but research code (e.g. Terrapin’s difffaust) compiles arbitrary DSP strings at runtime. This module provides that as a stable API with pluggable compilation providers:

  • "cli" (the default today): invokes the faust binary with -lang nnx and the JAX architecture file. Requires a local Faust build from the NNX branch (FAUST_BIN env var, or faust on PATH).

  • "dawdreamer": uses DawDreamer’s bundled libfaust in-process (boxToSource(box, "nnx", ...)) — no local Faust build needed, just pip install dawdreamer. This becomes the preferred provider once the NNX backend is merged upstream and DawDreamer rebuilds against it; until then it raises, because released DawDreamer wheels predate the backend.

provider="auto" prefers the CLI when a binary is resolvable and falls back to DawDreamer otherwise, so callers can write against one function now and inherit the pip-installable path later without code changes.

Generated source is written to a per-process temporary directory (kept for the process lifetime so tracebacks can show source) and imported under a unique module name.

exception FaustCompileError

Raised when Faust compilation fails, with the compiler’s message.

compile_dsp(dsp_code: str, class_name: str = 'FaustModule', *, provider: str = 'auto', faust_bin: str | None = None, arch_file: str | None = None, faust_dirs: Sequence[str] = (), extra_args: Sequence[str] = (), vectorize: bool = False) type

Compiles Faust DSP source code to a generated NNX module class.

Parameters:
  • dsp_code – Faust source (must define process).

  • class_name – Name of the generated class.

  • provider"cli", "dawdreamer", or "auto" (CLI when a faust binary is resolvable, else DawDreamer).

  • faust_bin – Path to the faust binary (CLI provider).

  • arch_file – NNX architecture file; resolved from the binary’s source checkout when omitted (CLI provider).

  • faust_dirs – Extra -I import directories for library() files.

  • extra_args – Extra compiler flags (CLI provider), e.g. ["-double"].

  • vectorize – Reroll scalar-unrolled par() banks in the generated code into vector ops (faustax.vectorize). Bitwise-exact; large speedups for bank-heavy DSP (filter banks, reverbs).

Returns:

The generated class (an nnx.Module subclass); instantiate with cls(sample_rate=...).

compile_file(dsp_path: str, class_name: str | None = None, *, provider: str = 'auto', faust_bin: str | None = None, arch_file: str | None = None, faust_dirs: Sequence[str] = (), extra_args: Sequence[str] = (), vectorize: bool = False) type

Compiles a .dsp file; class name defaults to the CamelCased stem.

faust_root(faust_bin: str | None = None) Path

Root of the Faust source checkout providing the compiler binary.

Resolved from the binary location (<root>/build/bin/faust), so the checkout’s libraries/, examples/, and architecture/ trees can be located alongside the compiler. Only meaningful for a source checkout; an installed Faust keeps those trees under <prefix>/share/faust instead (see _arch_candidates()).

Raises:

FaustCompileError – If no faust binary can be resolved.

has_nnx_backend(faust_bin: str | None = None) bool

Whether a usable Faust NNX toolchain is available.

Runtime compilation needs more than a faust on PATH: the binary must advertise the NNX backend (absent from released Faust builds) and its architecture file must be findable. Tests and examples gate on this so a stock Faust install reports “unsupported” rather than failing mid-compile.

Parameters:

faust_bin – Explicit binary path; None uses the usual resolution.

Returns:

True if the resolved binary lists “DSP to NNX” and its architecture file exists.

faustax.contract

The Faust-generated NNX module contract, as an inheritable base class.

Faust’s NNX backend generates modules with a fixed surface: class attributes num_inputs/num_outputs, a carry-first process_block, an initialize_carry, normalized/physical parameter handling with Faust’s scale modes, and get_parameter_metadata describing every widget. Projects that write DSP by hand but want interchangeability with generated modules (e.g. Terrapin’s ddsp_faust processors) previously had to mirror that surface manually; this module makes it a real dependency instead.

Distinct from faustax.processors.Processor, which is a batched wrapper around generated modules — contract.Processor is the shape of a module, generated or hand-written.

The scale semantics (linear/log/exp) are copied exactly from the NNX backend’s architecture file so hand-written processors cannot drift from generated ones: log interpolates in log10 space (geometric spacing, positive minimum required); exp interpolates exp(normalized) over [1, e].

class ParameterMeta(zone: str, full_label: str, label: str, type: str = 'continuous', min: float = 0.0, max: float = 1.0, default: float = 0.0, step: float = 0.0, scale: str = 'linear', shortname: str | None = None)

Bases: object

Metadata for one widget, mirroring the generated-module dict entries.

Variables:
  • zone (str) – The generated state key (e.g. "fHslider0"), unique per DSP.

  • full_label (str) – Full widget path (e.g. "my_effect/cutoff").

  • label (str) – Bare widget label.

  • type (str) – Widget type ("hslider", "vslider", "nentry", "button", "checkbox").

  • min (float) – Physical minimum.

  • max (float) – Physical maximum.

  • default (float) – Physical default value.

  • step (float) – Slider step (0.0 when not meaningful).

  • scale (str) – "linear", "log", or "exp".

  • shortname (str | None) – Faust shortname (minimal unique path suffix), when known.

class Processor(*args: Any, **kwargs: Any)

Bases: Module

Base class fixing the Faust-generated NNX module surface.

Subclasses implement the three core methods and get_parameter_metadata(); the parameter-handling helpers are provided concretely on top of that metadata, with the same semantics as generated modules.

Class attributes num_inputs and num_outputs must be set by the subclass. Audio has no batch axis — shape (channels, samples) — and batching is done with jax.vmap, matching generated modules.

get_parameter_metadata() Dict[str, ParameterMeta]

Returns metadata for every widget, keyed by zone.

initialize_carry() Dict[str, Any]

Returns the initial streaming state dict.

label_to_zone(label: str) str

Resolves a full label, shortname, or bare label to its zone.

params_from_labels(params: Dict[str, Any]) Dict[str, Any]

Rekeys a label/shortname-keyed dict by zone.

process_block(carry, inputs=None, params=None, normalized_params=None)

Processes one block; returns (new_carry, outputs).

unnormalize_params(normalized_params: Dict[str, Any]) Dict[str, Any]

Maps a zone-keyed dict of [0, 1] values to physical values.

with_defaults(params: Dict[str, Any] | None = None) Dict[str, Any]

Physical defaults for every widget, overridden by params.

normalize_value(value, a_min: float, a_max: float, scale: str = 'linear')

Exact inverse of unnormalize_value() on the valid range.

unnormalize_value(normalized, a_min: float, a_max: float, scale: str = 'linear')

Maps a normalized [0, 1] value to the physical range, Faust semantics.

Parameters:
  • normalized – Value(s) on [0, 1]; clipped into range.

  • a_min – Physical minimum.

  • a_max – Physical maximum (must exceed a_min).

  • scale"linear", "log" (geometric; requires a_min > 0), or "exp".

Returns:

The physical value(s).

faustax.synth

Flat hybrid-parameter synthesizer wrapper and synthesizer registry.

FaustSynthesizer presents a generated module as a single flat parameter vector per batch item: all continuous sliders as normalized [0, 1] values in shortname-alphabetical order, followed by the concatenated one-hot encodings of every nentry categorical (also alphabetical). That is the layout reinforcement-learning policies with hybrid Beta/Categorical heads emit, so a policy’s raw output can drive the synthesizer directly.

The wrapped module has no batch axis; FaustSynthesizer.render_from_flat() vmaps over the batch with per-item parameters and PRNG keys.

Register your own .dsp sources with register_synthesizer(); they are compiled lazily on first use. No synthesizers ship with Faustax.

class CategoricalInfo(name: str, zone: str, num_classes: int, minimum: float, step: float, default_index: int)

Bases: object

One categorical (nentry) parameter of a synthesizer.

Variables:
  • name (str) – Faust shortname.

  • zone (str) – Generated state key.

  • num_classes (int) – Number of discrete choices ((max - min) / step + 1).

  • minimum (float) – Physical value of class 0.

  • step (float) – Physical distance between adjacent classes.

  • default_index (int) – Class index of the Faust default value.

class FaustSynthesizer(*args: Any, **kwargs: Any)

Bases: Module

Batched flat-parameter wrapper around a generated synthesizer module.

Parameters:

module – An instantiated generated NNX module (e.g. from faustax.compile.compile_file() or the registry).

Variables:
  • module – The wrapped module.

  • sample_rate – The module’s sample rate.

  • continuous_names – Continuous slider shortnames, alphabetical — the column order of the continuous block of the flat vector.

  • categorical_names – Categorical (nentry) shortnames, alphabetical.

  • total_param_dim – Flat vector length (continuous + summed one-hots).

combine_parameters(continuous_params: Array, discrete_params: Sequence[Array]) Array

Packs [B, C] continuous + per-variable one-hots into [B, D].

get_default_params() Array

Flat defaults of shape [1, total_param_dim].

Continuous defaults are normalized to [0, 1]; categoricals are one-hot at their default index.

load_numpy_params(path: str | Path) Tuple[ndarray, ndarray]

Loads flat params saved by save_numpy_params().

Also accepts a bare .npy array of params (no filenames).

property name: str

The wrapped module’s class name (e.g. "ModalPiano").

render_from_flat(flat_params: Array, inputs: Array, unroll: int = 1, rng: Array | None = None) Array

Renders a batch: [B, D] params + [B, C_in, T] inputs.

Parameters:
  • flat_params – Per-item flat parameter vectors.

  • inputs – Per-item input signals (e.g. [freq, gain, gate] channels for note-driven synths).

  • unroll – Scan unroll factor for the per-sample loop.

  • rng – PRNG key for stochastic DSPs; split per batch item. A fixed key is used when omitted.

Returns:

Audio of shape [B, num_outputs, T].

save_numpy_params(out_filename: str | Path, params: ndarray, filenames: Sequence[str] | None = None) None

Saves [N, total_param_dim] flat params (and names) as .npz.

split_parameters(flat_params: Array) Tuple[Array, List[Array]]

Inverse of combine_parameters().

get_synthesizer_class(name: str) Type

Returns the module class registered under name.

.dsp targets are compiled on first use and cached.

register_synthesizer(name: str, target: Type | str | Path) None

Registers a synthesizer under name.

Parameters:
  • name – Registry key (also the generated class name for .dsp targets).

  • target – Either an NNX module class, or a path to a .dsp file to compile lazily via faustax.compile.compile_file().