Real-time deployment

Each Faustax processor streams in real time. The initialize_carry() / process_block() API of the generated module is independent of the block size. The Faust impulse tests verify this property. faustax.realtime.RealtimeEffect packages this API for an audio callback:

from faustax import Freeverb
from faustax.realtime import RealtimeEffect

fx = RealtimeEffect(Freeverb(sample_rate=48000), block_size=512)
fx.set_params(room_size=0.8, mix=0.4)
fx.run()   # mic -> Freeverb -> speakers, Ctrl+C to stop

run() opens a sounddevice stream (pip install faustax[realtime]). The stream is duplex for effects and output-only for generators. To integrate with your own audio host, call process() from the callback of the audio host. This method uses the (frames, channels) float32 numpy convention of sounddevice.

Three properties make this system satisfactory for deployment:

  • The JIT compile occurs at construction, not at the first audio deadline. The constructor runs one warmup block and then resets the DSP state.

  • Parameter changes have no compile cost. Parameters are traced inputs to the jitted step. Thus set_params() between blocks never recompiles. A test makes sure that the compile-cache stays at one entry. You can change the parameter values at the block rate.

  • The block-wise output is bit-consistent with offline processing. The test suite streams Freeverb and the Compressor in 512-frame blocks. The test suite then compares the result with one-shot processing of the whole signal.

Headroom

The table shows the median per-block wall time at 48 kHz / 512 frames (10.67 ms budget). We measured the wall time through RealtimeEffect.process on a CPU (WSL2). We measured before the 0.0.1 dasp-parity rewrite. After that rewrite, the EQ increased from four sections to six sections:

processor

ms per block

headroom

Compressor

0.11

97x

ParametricEQ

1.49

7.2x

Freeverb

3.78

2.8x

A 2-second live duplex stream (Freeverb, WSLg pulse device) completes with no reported under-runs. For instruments, see the single-voice numbers in Performance. One DX7 voice is near the real-time limit at unroll=2. The effects have a large headroom.

Real-time block streaming for Faustax processors.

RealtimeEffect wraps a processor’s generated NNX module for block-by-block streaming: the scan carry is threaded (and donated) through a jitted step, parameters are per-call traced inputs — so changing them between blocks costs nothing, no recompilation — and the JIT compile happens at construction, before any audio deadline.

Use RealtimeEffect.process() from your own audio callback (it speaks sounddevice’s (frames, channels) float32 numpy convention), or RealtimeEffect.run() to open a sounddevice stream directly (pip install faustax[realtime]):

from faustax import Freeverb
from faustax.realtime import RealtimeEffect

fx = RealtimeEffect(Freeverb(sample_rate=48000), block_size=512)
fx.set_params(room_size=0.8, mix=0.4)
fx.run()   # mic -> Freeverb -> speakers, Ctrl+C to stop
class RealtimeEffect(processor: Processor, block_size: int = 512, unroll: int = 1, seed: int = 0)

Stream a Faustax processor block by block with live parameter control.

The block size is fixed at construction (one jit compilation); the per-sample scan is block-size independent, so any block size produces identical audio. Deterministic effects ignore the RNG; stochastic DSPs get a fresh key per block.

Parameters:
  • processor – A constructed faustax.Processor. Its sample rate should match the audio stream’s.

  • block_size – Frames per block. The constructor runs one warmup block so the JIT compile happens here, not at the first audio deadline.

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

  • seed – Seed for the per-block RNG keys.

process(block: ndarray) ndarray

Process one audio block in sounddevice’s array convention.

Parameters:

block – Input of shape (frames, channels) (frames must equal block_size). For generators (num_inputs == 0) pass an empty (frames, 0) array.

Returns:

float32 output of shape (frames, num_outputs).

reset() None

Clear the DSP state (delay lines, envelopes, filter memories).

run(duration: float | None = None, device=None) None

Open a sounddevice stream and run the effect.

Effects (num_inputs > 0) open a duplex stream (live input -> effect -> output); generators open an output-only stream. Requires the realtime extra (pip install faustax[realtime]).

Parameters:
  • duration – Seconds to run, or None to run until Ctrl+C.

  • device – sounddevice device id/name (or an (input, output) pair for duplex); None uses the defaults.

set_params(**params) None

Update physical parameter values by slider label.

Takes effect on the next block. Parameters are traced inputs to the jitted step, so this never recompiles. Unknown labels raise.