Skip to content

Processes and threads

Thread or process Created in What it does What it may not do
Main / UI thread Tauri Runs synchronous Tauri commands (most edits, including audio import with its decoding and resampling), the native menu, the GTK main loop on Linux, the 30 ms idle timer (Session::idle: plug-in controller idle, latency changes, draining Garbage, automation follow and writes), and taking autosaves. All VST3 controllers and editor windows live here. Block for long. Renders are async commands for that reason.
audio-io audio_io::start Builds and owns the cpal streams, then waits until the device is closed.
Audio callback cpal (output stream) Calls Engine::process once per device buffer, inside catch_unwind; the input stream’s callback only fills the input ring. Runs with real-time priority (rtkit on Linux). Allocate, lock, block, do I/O. See Real-time rules.
marbble-dsp-N dsp/pool.rs, started by Session::sync_parallel Plays strips alongside the audio callback when Settings › Audio › Processing Threads allows it and a project is heavy enough, then blocks until the next block. Real-time like the callback. See Multi-core processing. The same as the audio callback.
recorder recorder.rs Every 10 ms, drains the ring buffer of each armed track into its WAV file and an in-memory copy, and keeps a peak preview for the UI. Touch the engine or the project.
ui-tick src-tauri/src/main.rs Every 33 ms: locks the session, builds a Tick (playhead, meters, input levels, recording previews, CPU, latency, analyzer spectrum, warnings, meters of open built-in effect panels, the clip indicators’ held peaks, each strip’s gain reduction) and emits it as the tick event. Edit anything.
MIDI refresh main.rs Every 3 s: Session::refresh_midi, so devices plugged in later get connected.
MIDI input callbacks midir, in midi_io.rs (one per port) Drop system messages, take assigned controllers (all of them while learning) into a queue for Session::idle (controller assignments), apply the velocity curve, push the rest into the live_midi ring the engine reads, and log the message for Settings › MIDI.
Async command workers Tauri’s async runtime (#[tauri::command(async)]) Offline renders (bounce, export, Bounce in Place, Freeze), plug-in rescans, waveform and transient requests, device lists, recent projects, the input level check, folder listings. Renders hold the session lock and the engine lock for their whole length; the parts that edit tracks or plug-ins are handed back to the main thread with run_on_main_thread. Touch plug-in controllers.
Autosave writer main.rs::autosave Writes an AutosaveJob (already serialized on the main thread) to disk, atomically.
Plug-in scan subprocess plugin_db.rs marbble --scan-vst3 <bundle> loads one bundle in a child process and prints its descriptors, so a crashing plug-in can’t take the app down. Four scan worker threads start the children and wait for them (at first launch before the window opens, otherwise from Rescan). Load plug-ins in the app’s own process.

On non-Linux platforms there is no GTK timer; a helper thread posts the idle work to the main thread with run_on_main_thread every 30 ms instead (main.rs).

The session is behind a parking_lot::Mutex in AppState. The engine is behind its own Mutex, but the audio callback only ever try_locks it: while an offline render holds the engine lock, the device outputs silence (see dsp/offline.rs).