# 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 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 warnings` and `cargo fmt --check`. - `node scripts/material-smoke.mjs`: a live import of `fixtures/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.