3.3 KiB
P1a · material and the quarry
P0 proved the spine: Rust audio, a session, a protocol, a surface. P1a puts material into it: a file becomes a core, a core becomes sixteen faces, a face becomes a voice in the pattern.
The path of a sound
file input (browser)
→ ArrayBuffer
→ ws: import_core { id, name, seal } + binary audio
→ zhila-material: symphonia decode → mono f32 at the source rate
→ onset analysis: 10 ms energy hops, positive flux, local peaks
→ sixteen faces (sample ranges + RMS + rank)
→ waveform peaks (1400 bins, normalised)
→ zhila-core: Core { summary, Arc<[f32]>, faces }
→ Session::import_core: archive + journal + sync_material
→ engine: Arc<MaterialSnapshot> through the control queue
→ snapshot broadcast → the quarry
The pattern keeps its procedural voices until a row is given a face. set_pattern_face writes the reference and syncs a new material snapshot; the audio thread triggers a sample voice for that row's steps and leaves the other rows procedural. set_pattern_step edits the rhythm; both are journaled. strike_face auditions a face without touching history.
Pieces
| Piece | What it does |
|---|---|
zhila-material |
decode (symphonia), downmix to mono, onset analysis, sixteen faces, ranks, waveform peaks, content-hash id |
zhila-core |
Core, AudioCore, AudioFace, MaterialSnapshot; archive; pattern face/step commands; journal; engine sync |
zhila-engine |
sample voices alongside procedural voices; a step plan that resolves a row's face to a core/face; rate-correct playback; 4 ms fades |
zhila-daemon |
binary import after a text header, decoded in spawn_blocking; broadcast snapshot; new commands flow through the session |
zhila-protocol |
core/face summaries, face references, import messages, pattern commands/events |
ui |
quarry: import, core strip, waveform, face wall, audition, assign, pattern grid; the descent remains the nave |
Verification
cargo test --workspace: 10 core tests, 2 material tests, 5 protocol tests.cargo clippy --workspace --all-targets -- -D warningsandcargo fmt --check.node scripts/material-smoke.mjs: a live import offixtures/clicks-01.wav, sixteen faces, a face laid on row 1, playback, non-zero meters,face_struck.npm --prefix ui run quarry-smoke: the browser imports the fixture, selects a face, assigns it, plays, screenshots the quarry and the nave, checks 80 steps and 16 faces.npm --prefix ui run smoke: the P0 regression still passes.
The screenshots are preview-p1-quarry.png, preview-p1-nave.png and the empty state preview-p1-quarry-empty.png.
Limits
- In-memory archive and pattern; no project file, no save/load, no asset persistence. A daemon restart loses the material and edits.
- No recording, no waveform trim, no manual cut/loop points. Seams are automatic.
- No undo/redo for pattern edits; the journal is written but not exposed.
- One face per pattern row; no per-step face, pitch or probability yet.
- Formats are whatever symphonia decodes; WAV is the tested path.
- The Tauri shell now builds and runs; the engine remains the separate daemon (embedding is next).
Next
P1b: a project folder with SQLite, content-addressed assets, autosave and reload, pattern-edit undo, recording, and waveform trim. Then the arrangement and the agent.