Architecture
This page is the working map of the Rast codebase: the crates, the Tauri command surface, the data flow from import to playback, and where each piece of state lives.
Stack overview
Rast is a Tauri v2 desktop app. The backend is a Rust workspace under rust/ plus the Tauri shell under src-tauri/. The frontend is Svelte 5 + TypeScript built by Vite. Heavy lifting (source separation, chord/beat/note inference) runs on ONNX models via the ort crate; the CLI shells out to ffmpeg for decoding and yt-dlp for YouTube ingest. Persistent state lives in a single SQLite database at ~/Rast/library.db plus a hash-keyed cache directory at ~/Rast/cache/<hash>/.
Workspace layout
The Cargo workspace is declared in rust/Cargo.toml:1. Members:
| Crate | Path | Responsibility |
|---|---|---|
rast-theory | rust/rast-theory/ | Pure music theory: 11 dromoi, chord labels, enharmonic spelling, bar-grid helpers. Zero I/O. |
rast-db | rust/rast-db/ | SQLite schema + queries (rusqlite, bundled). Schema version 7 — rust/rast-db/src/lib.rs:17. |
rast-analysis | rust/rast-analysis/ | DSP + ML: CQT, chroma, beat tracking (beat_this), chord inference (CREMA + BTC), Demucs/Spleeter separation, key detection, basic-pitch note transcription. |
rast-core | rust/rast-core/ | Pipeline orchestration, ONNX ModelManager, cache I/O, import helpers, default paths. |
bench-pipeline | rust/bench-pipeline/ | CLI binary that runs the full end-to-end pipeline against a single audio file. |
bench-key-from-chords | rust/bench-key-from-chords/ | CLI binary that re-runs only detect_key_refined against a precomputed chord JSON, used to iterate on the key-detection algorithm without paying for separation. |
Each library crate's public surface is its lib.rs — see rust/rast-theory/src/lib.rs:1, rust/rast-db/src/lib.rs:1, rust/rast-analysis/src/lib.rs:1, rust/rast-core/src/lib.rs:1.
The Tauri shell lives in src-tauri/src/. Its lib.rs registers tracing, opens the library DB on startup to clear stale rows, manages two app states (ActiveJobs, SimilarityCache), and registers all #[tauri::command] handlers — see src-tauri/src/lib.rs:25.
Crate dependency graph
┌──────────────────────────────────────┐
│ src-tauri (binary, Tauri v2 commands) │
└────────────────┬─────────────────────┘
│
┌──────────▼──────────┐
│ rast-core │ pipeline, cache, paths,
│ │ ONNX ModelManager, import
└─┬─────────┬───────┬─┘
│ │ │
┌─────────────▼─┐ ┌───▼────┐ ┌▼──────────┐
│ rast-analysis │ │ rast-db│ │rast-theory│
│ (ONNX, DSP) │ │(SQLite)│ │(pure music│
└────────┬──────┘ └────────┘ │ theory) │
│ └───────────┘
└────────► rast-theory (chords, scales)
bench-pipeline ─► rast-core, rast-analysis, rast-theory
bench-key-from-chords ─► rast-analysis, rast-theory, rast-core (proc only)rast-theory has no dependencies on the others — it is a pure-data crate. Everything that touches audio buffers, ONNX, or SQLite goes through rast-analysis, rast-db, or rast-core.
Data flow: import → separate → analyse → playback
┌──────────┐ ffmpeg ┌────────────┐
audio file ───▶│ Decode │───────────────────────▶│ PCM f64 │
or URL │ (44100Hz │ │ (L, R) │
│ stereo) │ └─────┬──────┘
└──────────┘ │
│ ┌───────▼────────┐
SHA-256 │ │ ONNX models │
(16 hex)│ │ (beat_this, │
│ │ CREMA / BTC, │
▼ │ Demucs / │
┌───────────────┐ │ Spleeter, │
│ library.db │ │ basic_pitch) │
│ INSERT songs │ └────────┬───────┘
└───────┬───────┘ │
│ │
▼ │
┌──────────────────────────────────┐ │
│ AnalysisPipeline.run_analysis │◀─────────┘
│ (rast-core::pipeline) │
│ │
│ 1. separation (Demucs|Spleeter) │
│ 2. beats || chords || notes │ (parallel, std::thread::scope)
│ 3. key detection (refined) │
│ 4. chord-snap to beat grid │
│ 5. beat-synchronous chroma │
└──┬───────────────┬───────────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────────────┐
│ cache/ │ │ library.db │
│ <hash>/ │ │ UPDATE analysis │
│ *.flac │ │ analysis_json, │
│ beat_chr │ │ key_*, bpm, │
└──────────┘ └─────┬────────────┘
│
▼
┌────────────────────┐
│ Playback (Svelte) │
│ WebAudio + │
│ SoundTouch worklet│
│ reads vocals.flac │
│ + instrumental │
│ via convertFileSrc│
└────────────────────┘The pipeline structure and parallel scope are in rust/rast-core/src/pipeline.rs:316. Stage weights (used to map per-stage progress onto a 0..1 overall percent the frontend renders) live at rust/rast-core/src/pipeline.rs:111. The Tauri asset-protocol scope $HOME/Rast/cache/** is whitelisted in src-tauri/tauri.conf.json so the WebView can fetch vocals.flac / instrumental.flac directly without a Rust streaming server.
Tauri command surface
All commands are registered in the invoke_handler! block at src-tauri/src/lib.rs:106. Grouped by file:
library.rs — library CRUD
| Command | What it does |
|---|---|
list_songs | Returns all songs joined with their analysis row, newest first. src-tauri/src/library.rs:23. |
delete_song | Refuses if a job is active or status is processing; otherwise removes the row and the cache dir. src-tauri/src/library.rs:60. |
rename_song | Trims, rejects empty, updates songs.title. src-tauri/src/library.rs:109. |
analysis.rs — per-song analysis DTOs and chord edits
| Command | What it does |
|---|---|
get_song_analysis | Reads analysis_json, snaps chords to the beat grid (idempotent on already-snapped data), returns an AnalysisDto. src-tauri/src/analysis.rs:72. |
update_chord | Edits a single chord cell; snapshots original_chords_json on first edit. src-tauri/src/analysis.rs:221. |
revert_chord | Restores one cell from the original snapshot. src-tauri/src/analysis.rs:294. |
save_chords | Replaces the full chord array. src-tauri/src/analysis.rs:345. |
restore_original_chords | Full revert from the snapshot. src-tauri/src/analysis.rs:383. |
save_count_in | Persists count-in bars (0..=8). src-tauri/src/analysis.rs:157. |
save_pitch | Persists per-song pitch shift (-24..=24 semitones). src-tauri/src/analysis.rs:172. |
import.rs — ingest
| Command | What it does |
|---|---|
import_file | Hash → ffmpeg decode → run AnalysisPipeline → write cache + DB row. Emits import://progress, import://done, import://error. src-tauri/src/import.rs:231. |
import_youtube | Detects yt-dlp (standalone or python -m yt_dlp), downloads as mp3, then takes the file-import path. UTF-8-safe for Greek titles. src-tauri/src/import.rs:590. |
reprocess.rs — re-run separation/analysis on imported songs
| Command | What it does |
|---|---|
reprocess_song | scope = "analysis" reuses cached stems; scope = "stems" invalidates and rebuilds them. Emits `reprocess://progress |
get_backend_availability | Probes installed ONNX files; returns { demucs, spleeter, btc_chord }. Used by the frontend to hide unavailable options. src-tauri/src/reprocess.rs:521. |
playback.rs — stem URL resolution
| Command | What it does |
|---|---|
get_stem_urls | Returns absolute paths to vocals.flac and instrumental.flac for the song's hash. The frontend wraps these with convertFileSrc to get an asset-protocol URL the WebView can stream. src-tauri/src/playback.rs:16. |
similarity.rs — beat-synchronous chroma similarity
| Command | What it does |
|---|---|
compute_beat_chroma | Lazy-loads the on-disk beat_chroma.bin for a song into the in-memory SimilarityCache. src-tauri/src/similarity.rs:35. |
find_similar_ranges | Queries the cached matrix for ranges similar to a beat range. src-tauri/src/similarity.rs:84. |
log_bridge.rs
| Command | What it does |
|---|---|
log_from_frontend | Routes Svelte logger lines (level, target, message) into the shared tracing subscriber so frontend events land in the same log file as backend events. src-tauri/src/log_bridge.rs:7. |
State storage
| Where | What | Notes |
|---|---|---|
~/Rast/library.db (songs table) | id, hash, title, source_type/path/url, duration_seconds, status, error_message, imported_at | Schema in rust/rast-db/src/lib.rs:106. Status sweep on startup at src-tauri/src/lib.rs:92. |
~/Rast/library.db (analysis table) | key_primary, key_root_pc, key_scale, key_confidence, bpm, analysis_json, user_chords_json, count_in_bars, original_chords_json, pitch_semitones | Schema in rust/rast-db/src/lib.rs:119. Includes legacy-column purge (rebuilds the table when stale columns are present) — rust/rast-db/src/lib.rs:184. |
analysis_json (TEXT blob inside analysis) | full AnalysisOutput: chords, beat_times, downbeat_times, key result, stage timings | Type at rust/rast-core/src/pipeline.rs:58. The beat_chroma field is #[serde(skip)]'d out of this blob and persisted separately to disk. |
~/Rast/cache/<hash>/original.flac | Lossless stereo mix, ffmpeg-converted from source | Required precondition for any reprocess. src-tauri/src/reprocess.rs:264. |
~/Rast/cache/<hash>/vocals.flac, instrumental.flac | Separation outputs | Both must be present for Scope::Analysis reprocess. Tauri asset-protocol scope $HOME/Rast/cache/** makes them addressable from the WebView. |
~/Rast/cache/<hash>/beat_chroma.bin | Per-beat chroma matrix for similarity search | Lazy-loaded into SimilarityCache (src-tauri/src/similarity.rs:23). |
~/Rast/logs/rast.log | Single per-run log, truncated on startup | Path at rust/rast-core/src/paths.rs:26. |
In-memory: ActiveJobs (Tauri state) | Per-song reprocess lock | src-tauri/src/jobs.rs:163. |
In-memory: SimilarityCache (Tauri state) | HashMap<song_id, Vec<[f64; 12]>> of beat-chroma matrices | src-tauri/src/similarity.rs:23. |
| Svelte stores (browser memory) | UI state | See below. |
Frontend structure
src/main.ts mounts App.svelte which composes the high-level layout. Top of src/App.svelte:1 shows the card composition — Titlebar, Sidebar (library), SongInfo, TimelineCard, ChordGridCard, TransportBar, ControlsPanel, plus dialogs (Toast, ImportCard, ShortcutsDialog).
Cards (src/lib/)
| Component | Role |
|---|---|
Titlebar.svelte | Custom frameless-window titlebar (the Tauri window has decorations: false). |
Sidebar.svelte | Library list and search; emits song selection. |
SongInfo.svelte | Title, key, BPM, status badge for the selected song. |
TimelineCard.svelte | Beat-aligned waveform + chord blocks + similarity highlights. |
ChordGridCard.svelte | Bar-grid chord editor; the primary surface for edit-mode actions. |
ChordPicker.svelte | Modal/popover for picking a new chord during edits. |
ControlsPanel.svelte | Vocal/instrumental volume, pitch shift, speed, metronome. |
TransportBar.svelte | Play/pause/seek + position display. |
ImportCard.svelte | File-picker / YouTube URL form, listens to import://* events. |
Toast.svelte, ShortcutsDialog.svelte, SongActionsMenu.svelte, Titlebar.svelte | Auxiliary surfaces. |
Stores (src/lib/stores/)
| Store | Holds | Notes |
|---|---|---|
selection.ts | selectedSongId, the analysis readable derived from it | Single source of truth for which song is loaded. src/lib/stores/selection.ts:34. |
transport.ts | isPlaying, positionSec, durationSec, loading, error | Drives a requestAnimationFrame loop while playing. src/lib/stores/transport.ts:29. |
controls.ts | vocal/instrumental volume, pitch (persisted), speed, metronome | Subscribes to its own changes to push values into the audio engine, and persists pitch via save_pitch. src/lib/stores/controls.ts:32. |
editMode.ts | editMode: boolean toggle | Gates Ctrl+Z / Ctrl+C / split / paste shortcuts in App.svelte. |
chordHistory.ts, chordMutations.ts, chordSelection.ts, chordClipboard.ts | Undo/redo stack, range selection, copy/cut/paste of chord cells | Pure-TS music ops live next door in src/lib/music/. |
viewMode.ts | Grid vs timeline view | |
metronome.ts, simplify.ts | Metronome tick rate, opt-in dromos-aware chord simplification | The simplification toggle is intentionally client-side — see the comment at rust/rast-core/src/pipeline.rs:903. |
import.ts, reprocess.rs (sic) | Active job state for the import / reprocess UI | Listens for import://progress and reprocess://progress events. |
Audio engine
src/lib/audio/engine.ts is a singleton AudioContext with a SoundTouchNode worklet for pitch/speed and a separate click-scheduler for the metronome. Stems load via convertFileSrc(...) against the asset-protocol scope, never via a Rust HTTP server. The worklet has a measured 100 ms latency; the click track is shifted forward by SOUNDTOUCH_LATENCY_SEC to keep clicks aligned with what the user hears — see src/lib/audio/engine.ts:35.
See also
- The narrative version of the pipeline: How it works → Overview.
- The 11-dromos table and key-detection rule lookup: Music theory.
- Build, run, and bench: Build.