Skip to content

Devices and latency

Latency is the time between something happening (a key pressed, a knob turned, your voice in the microphone) and hearing it. The engine computes 64 frames at a time, but the sound card asks for a whole buffer at once: that buffer, plus what the system adds, is the latency. Smaller buffers mean less delay but less time to compute each one; too small and the sound crackles.

Blocks of 64 frames filling four slots of a 256-frame buffer that then goes to the speakers
A buffer of 256 frames is 5.3 ms at 48 kHz: four blocks of the engine.

With no settings, audio.Engine() uses the system’s best audio API, its default device at the device’s own sample rate, and the smallest buffer that plays without dropouts on a typical machine:

System Audio API (host) Buffer Latency at 48 kHz
Windows WASAPI (ASIO when built with it) 256 frames (WASAPI in shared mode may choose its own) ~5–10 ms
macOS CoreAudio 128 frames ~3 ms
Linux PulseAudio, which also reaches PipeWire; ALSA when there is no sound server 256 frames ~5 ms

audio.recommended_settings() returns these choices for the current machine.

from pyzed import audio
audio.hosts() # ['PulseAudio', 'ALSA']: the APIs available here
audio.default_host() # 'PulseAudio'
for d in audio.devices(): # the devices of the default host (or devices("ALSA"))
print(d.name, d.outputs, d.inputs, d.sample_rate, d.buffer_range)
engine = audio.Engine(
host="ALSA", # an API from audio.hosts()
device="USB Audio", # a device's name or id (None: the default one)
sample_rate=48000, # None: the device's own rate
buffer_size=128, # frames; None: the recommended size
input=True, # also open the default input (for the audio_in module)
input_device=None, # or name another one
)
engine.start()
print(engine.info)

engine.start(**settings) also takes these settings, so you can stop and start again with a smaller buffer or another device; the patch stays as it is.

engine.info says what opened and how it runs:

Key Meaning
backend "device", "null" (no sound card: a clock that keeps time) or "offline"
host, device, input_device the API and the devices in use
sample_rate, buffer_size, channels the stream’s format (buffer_size is None when the system chose)
latency_ms from the moment a buffer is computed to the moment it leaves the speakers, as the system reports it
load the share of each buffer’s time spent computing it: above 1.0 the sound breaks up
underruns buffers that came out silent
error the last problem (a device unplugged, or why there is no sound)

A load that stays below 0.5 leaves room for a smaller buffer.

  • WASAPI is the default. In shared mode Windows mixes every program and decides the buffer (usually 10 ms); the engine asks for 256 frames and accepts what Windows gives.
  • ASIO gives the lowest latency with audio interfaces that ship an ASIO driver. It needs Steinberg’s ASIO SDK at build time, so it is not in the PyPI wheels: build Pyzed with maturin develop --release --features asio (with CPAL_ASIO_DIR pointing at the SDK), then use audio.Engine(host="ASIO").
  • For virtual MIDI ports, install loopMIDI.
Backend Use it for
audio.Engine("auto") The default: the sound card, or the null clock when there is none (info["error"] says why)
audio.Engine("device") The sound card or an error
audio.Engine("null") Real time with no sound: servers, CI, an interface that only shows the signals
audio.Engine("offline") No clock at all: engine.render(seconds) computes as fast as possible, for tests and files