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).