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.dspsource (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 isparam_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:
ProcessorFeed-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_msis 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:
ProcessorSoft-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:
ProcessorMono 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:
ProcessorGain 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:
ProcessorSix-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:
objectBatched, differentiable wrapper around a Faust-compiled NNX module.
- Variables:
module – The underlying generated
nnx.Module(use directly for streaming viainitialize_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 raiseValueError; 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 asparam_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:
ProcessorMono-to-stereo panner with dasp-pytorch’s equal-power law (
pan0 = 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:
ProcessorMid-side stereo widener (dasp-pytorch parity);
width0 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.Processorwrapper 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
LearnableControlResponseandLearnableTwoKnobfit the mapping from a known knob to the DSP (see the Parameter estimation page).LowpassAutomatedlifts its cutoff slider to an audio-rate input channel, which makes a time-varying automation learnable; the source and the recipe are inexamples/parameter_estimation/automation.py.- DiffVox ports
DiffVoxCompressor,DiffVoxEq,DiffVoxFDN,DiffVoxPingpong(seefaustax.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; seefaustax.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:
objectdasp-style batched wrapper around the noise-shaped reverb.
Mirrors the
faustax.Processorinterface (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/drymix, all on[0, 1], matchingdasp_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 asparam_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:
BaseRandomTransformaudiotree 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 theprobselection 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 intoNindependent 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-orderlinear_recurrence()(which supplies the efficient all-pole custom gradient), so the filter isO(T * N)instead of the denseO(T * N**2)and differentiates through the same rule. Follows philtorch’sdiag_state_space.A real resonant system is realized here by passing its complex eigenvalues in
awith complexB/C; the output’s imaginary part then cancels, so takejnp.real(y). Conventions matchstate_space()(unbatched;jax.vmapfor 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);Nonefor 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
xwithlfilter(), reverses, filters again, and reverses back, so the two passes cancel phase and the net response is zero-phase with squared magnitude. Matchesscipy.signal.filtfiltwith 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,)(thelfilter()convention); passscipy.signal.butter(...)[1][1:].x – Input signal of shape
(T,).
- Returns:
Zero-phase filtered signal of shape
(T,).- Raises:
ValueError – If
xis shorter than the padding length3 * 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, matchingallpole()’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:
aEXCLUDES the leading unity coefficient (a = [a1, ..., aMa]), as in torchlpc/philtorch. Withzi=Noneand constant coefficients this matchesscipy.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),)withzi[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’szi).
- Returns:
Filtered signal of shape
(T,); withreturn_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 generalizesallpole()(which is the companion-form special case of a single-output all-pole system) to an arbitrary MIMO system withNstates,Minputs andPoutputs.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);Nonefor 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,). Passjnp.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,).
- 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))withL = amp2db(x_rms)— followed by the switched attack/release smoother on the linear gain (initial state 1). Requirescomp_ratio > 1and0 < exp_ratio < 1.- Parameters:
x_rms – Positive level envelope of shape
(T,)(e.g. fromavg()onsqrt(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 offaustax.ops.ballistics()), a hardmin(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’sba.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
SendFXsAndSumtopology (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
panis 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 (seefaustax.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_datasetrefuses datasets whoseinfo.jsondisagrees, 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 withfdn_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 nofeature_mask.npy.gaussian_mean (numpy.ndarray | None) – Mean of the parameter Gaussian in masked space, shape
(130,), or None if the dataset has nogaussian.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 todelay / min(delay)per line (delay-independent decay), then turned into a 97-tap linear-phase FIR withscipy.signal.firwin2, and each line’s delay is shortened by the FIR’s 48-sample group delay.fdn_params()feeds the result intoFDN, 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 gainsc{ch}_{i}, feedback matrixu{i}_{j}, per-line decay FIR tapsfir{i}_{k}(fromfdn_fir_coefficients()), and theeq_*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.jsonandraw_params.npy(e.g.<diffvox>/presets/internalor<diffvox>/presets/medleydb), with optionalfeature_mask.npy,gaussian.npzandtrain_index.npyalongside.- 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’sraw_params.npy.- Returns:
peak1,peak2,low_shelf,high_shelf,low_pass,high_pass,compressor,pingpong,fdn, plus the chain-levelsend_to_fdn(delay-to-FDN cross-send gain) andpan(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 gainsb(6, 2) andc(2, 6), per-sample decaygamma(49,) sampled on a linear frequency grid from 0 to Nyquist, and the orthogonal feedback matrixmatrix(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:
ModuleLearnable 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 is0.5 + softplus(.). Train it like any NNX module (nnx.grad/nnx.value_and_gradover 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.707is 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
modeis not one ofMODES.
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_PCbetween modelyand targett.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_EDCbetweenyandt.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
Ais maximally mixing (all|A_ij| = 1/√N, Hadamard-like), larger asAgrows 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 thefaustbinary with-lang nnxand the JAX architecture file. Requires a local Faust build from the NNX branch (FAUST_BINenv var, orfauston PATH)."dawdreamer": uses DawDreamer’s bundled libfaust in-process (boxToSource(box, "nnx", ...)) — no local Faust build needed, justpip 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
-Iimport directories forlibrary()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.Modulesubclass); instantiate withcls(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
.dspfile; 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’slibraries/,examples/, andarchitecture/trees can be located alongside the compiler. Only meaningful for a source checkout; an installed Faust keeps those trees under<prefix>/share/faustinstead (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
fauston 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;
Noneuses 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:
objectMetadata 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:
ModuleBase 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_inputsandnum_outputsmust be set by the subclass. Audio has no batch axis — shape(channels, samples)— and batching is done withjax.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; requiresa_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:
objectOne 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:
ModuleBatched 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
.npyarray 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..dsptargets 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
.dspfile to compile lazily viafaustax.compile.compile_file().