Songbird Rust Engine (rust/)
The Rust backend provides the complete audio engine, DSP plugins, clip management, state synchronization, and Tauri IPC layer for Songbird. It replaces the C++ Tracktion Engine / JUCE backend with a pure-Rust implementation (plus a thin FFI bridge for hosting third-party VST3/AU plugins).
Tech Stack
- Rust 2021 edition — Systems language with memory safety guarantees
- Tauri v2 — Desktop app shell (replaces JUCE WebBrowserComponent)
- cpal 0.15 — Cross-platform audio I/O
- midir 0.10 — Cross-platform MIDI I/O
- rayon 1.10 — Data-parallel graph processing
- serde / serde_json — Serialization (project files, IPC, state sync)
- hound 3.5 — WAV file reading/writing
- git2 0.19 — Git operations for sync engine
- crossbeam-channel 0.5 — Lock-free communication between audio and UI threads
Workspace Structure
The workspace is organized into six layers, bottom-up:Crate Dependency Graph
songbird-orchestrator) is the sole owner of
engine lifecycle — both desktop and headless binaries call into it rather
than duplicating session-build, device-management, and plugin-editor code.
Architecture Overview
Build & Development
Audio Processing Pipeline
The audio callback processes data through this pipeline each block:Real-Time Safety
The audio callback path is designed for zero allocation:- AudioBuffer — inline
[*mut f32; 8], no heap allocation - ScratchPool — pre-allocated per-node buffers, assigned in
rebuild_topology() - SpscRingBuffer — lock-free single-producer single-consumer for engine↔UI commands
- No Mutex in audio path — all shared state accessed via lock-free ring buffers
- Plugin trait —
process()operates on pre-allocated buffers
IPC Layer
The Tauri app exposes 202+ IPC commands organized by category:| Category | Commands | Examples |
|---|---|---|
| Transport | 10 | transport_play, set_bpm, get_position, set_loop_range |
| Tracks | 12 | add_track, remove_track, set_track_volume, duplicate_track |
| Plugins | 4 | add_plugin_to_track, set_plugin_param, get_plugin_params |
| MIDI | 7 | send_midi_note_on, send_midi_cc, send_midi_pitch_bend |
| Automation | 6 | get_automation_lanes, add_automation_point, get_automation_value |
| Project | 8 | save_project, open_project, undo, redo |
| Session | 6 | session_new, session_save, session_load, session_close |
| Export | 4 | export_master, export_stem, export_all_stems, bounce_track_cmd |
| Presets | 5 | list_presets, save_preset, load_preset, search_presets |
| Arrangement | 5 | add_marker, add_region, get_arrangement |
| Clip Launcher | 7 | launch_clip_slot, stop_clip_slot, launch_scene |
| Collaboration | 4 | get_collab_status, connect_collab, disconnect_collab |
| MIDI Devices | 2 | list_midi_devices, refresh_midi_devices |
| Metronome | 2 | set_metronome_enabled, get_metronome_state |
IpcResult<T>:
State Management
- Versioned mutations — monotonic counter, every change gets a version
- JSON Pointer diffing (RFC 6901) —
/tracks/0/gainpaths - Echo suppression —
ChangeSource::LocalvsChangeSource::Remote - Undo/redo — snapshot-based, up to 100 levels
Testing
The workspace has 5,240 tests organized by purpose:| Test Suite | Location | Count | What It Tests |
|---|---|---|---|
| Unit tests | Each crate’s mod tests | ~2,500 | Individual module correctness |
| E2E engine tests | engine_e2e_tests.rs | 15 | Full pipeline: transport → graph → clips → output |
| Audio output tests | audio_output_tests.rs | 25 | Waveform correctness, DSP parity, golden output |
| IPC roundtrip tests | ipc_roundtrip_tests.rs | 87 | Serialize/deserialize all IPC response types |
| IPC workflow tests | ipc_workflow_tests.rs | 15 | Full user workflows through IPC layer |
| IPC coverage tests | ipc_handler_coverage_tests.rs | 54 | All 202+ Tauri command handlers wired correctly |
| Engine controller tests | engine_controller.rs | 179 | Offline rendering, callbacks, cross-crate integration, robustness hardening |
| Edge case tests | edge_case_tests.rs | 25 | NaN, Inf, empty buffers, extreme values |
| Stress tests | stress_tests.rs | 15 | Concurrent mutations, MIDI floods, large graphs |
| Performance tests | perf_tests.rs | 15 | Graph throughput, parallel speedup, zero-alloc |
| RT safety tests | rt_safety_tests.rs | 25 | Zero allocation in audio callback path |
| Project format tests | project_format_tests.rs | 15 | JSON round-trip, forward compat, Unicode |
| Plugin factory tests | songbird-plugins/lib.rs | 3 | Create, round-trip state for all 16 plugins |
Key Design Decisions
Graph Topology (vs Tracktion’s Flat Model)
Tracktion uses a flat track-centric model. Songbird uses a connection-based DAG withConnection structs carrying port indices. This enables sidechain routing, parallel compression, and multi-bus routing without architecture changes. Topological sort via Kahn’s algorithm is cached in rebuild_topology().
Parallel Processing (via rayon)
Independent branches of the audio graph execute concurrently. The topological sort assigns levels — nodes at the same level have no dependencies and can be processed in parallel viarayon::scope. All metadata is cached so process() does zero allocation.
Nested Device Chains (Bitwig-inspired)
ThePluginChain supports ChainNode variants that can be either a Plugin or a Container holding sub-chains. This enables parallel processing chains, multiband splits, and mid/side processing at the track level.
Plugin Trait
All plugins — stock and external — implement the samePlugin trait:
FFI Bridge (for C++ Plugin Hosting)
Rust owns the audio thread. Third-party VST3/AU plugins are hosted via JUCE through a C ABI FFI layer (songbird-host-ffi). The bridge uses non-interleaved buffer layout matching JUCE’s AudioBuffer<float>. The Plugin trait does not assume shared memory, enabling future out-of-process hosting.
Migration Status
| Component | C++ (Tracktion/JUCE) | Rust | Status |
|---|---|---|---|
| Audio graph | Tracktion EditPlaybackContext | AudioGraph + rayon | Done |
| Transport | Tracktion TransportControl | Transport | Done |
| Tempo map | Tracktion TempoSequence | TempoMap | Done |
| Stock plugins (16) | C++ JUCE plugins | Pure Rust ports | Done |
| Clip scheduling | Tracktion Clip/Track | ClipScheduler | Done |
| Clip launcher | — | ClipLauncher (new) | Done |
| Automation | Tracktion AutomatableParameter | AutomationState | Done |
| State sync | ValueTree + StateSync.cpp (7 mechanisms) | StateStore (single design) | Done |
| Bird parser | TypeScript bird_parser.ts | Rust bird_parser.rs | Done |
| Export/render | Tracktion Renderer | songbird-export | Done |
| Recording | Tracktion RecordingContext | songbird-record | Done |
| Undo/redo | ValueTree UndoManager | StateStore snapshots | Done |
| Presets | C++ preset files | PresetManager | Done |
| Collaboration | Custom C++ bridge | songbird-state collab | Done |
| IPC layer | JUCE native functions | Tauri commands (202+) | Done |
| C++ plugin hosting | — | songbird-host-ffi | FFI stubs ready |
| Real audio I/O | JUCE AudioDeviceManager | cpal AudioDevice | Scaffolded |
Headless WebSocket Server
Thesongbird-headless crate runs a standalone WebSocket server that serves the same protocol as the Tauri app, enabling browser-based UI without Tauri:
{ "eventId": "<command>", "payload": <json> } format (same as Tauri IPC). Binary RT frames broadcast at ~30fps with metering, transport position, recording peaks, and Link status.
See ARCHITECTURE.md for the full system design.