Spaces:
Running
Running
File size: 5,486 Bytes
8aa3e04 073ffab 8aa3e04 d51d114 073ffab d51d114 073ffab d51d114 8aa3e04 073ffab d8622a2 073ffab c6af819 d8622a2 073ffab d51d114 89686a6 d51d114 89686a6 d51d114 073ffab d51d114 d8622a2 073ffab d8622a2 8aa3e04 073ffab | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | # Architecture
## One engine, many hosts
```
crates/microquack-core crates/microquack-llm
(canonical: all sound (the droid mind: tiny
behavior) transformer inference)
\ / |
crates/microquack-wasm trained in llm/
/ (browser binding) (PyTorch -> binary weights)
crates/microquack-cli
(render / validate / fit / schema)
|
apps/web (TypeScript: UI, canvas, WebAudio, 3D stage only)
|
microduck robot (native crates, 48 kHz PCM) <- planned consumer
```
`microquack-core` contains everything that affects the sound: score types and
parsing, pitch curves, envelopes, oscillators and partials, ring modulation,
noise, the seeded PRNG, phrase grammar, the canonical semantic emotion
registry and voice-mode shaping, contour output for motion, and rendering β
both full-phrase (`render_score`) and incremental
blocks (`Engine::render_into`, which `render_score` itself uses, so offline
and streaming output are identical). It contains no WebAudio, WASM APIs,
device access, filesystem loading, browser code, or Python-specific code.
## The score contract
A score is a JSON list of syllables with snake_case fields (`f0_start`,
`amp_points`, ...), defined once as Serde types in `microquack-core`.
`schema/score.schema.json` is generated from those types (`pnpm run schema`);
nothing else defines the format. `schema/emotions.json` and
`apps/web/src/emotions.generated.ts` are likewise generated from the Rust
registry with `pnpm run emotions`. Python uses the JSON file for teacher-data
generation. Scores use seconds and hertz, so they are sample-rate
independent; the renderer owns all rate-specific behavior. The TypeScript app
treats scores as opaque JSON strings β it never reimplements or redeclares the
format.
## The chat and the droid mind
The visitor reads and writes English; the robot answers in droid speak with
an English gloss. `crates/microquack-llm` runs a ~1.3M-parameter
character-level transformer (trained from scratch in `llm/` on a synthetic
space-adventure corpus) with a KV cache and greedy decoding, compiled into
the same WASM module as the synth. Its output is `(emotion, reply)`; the
emotion drives phrase generation and the body-language gestures. Rust/
PyTorch parity is enforced by `cargo test -p microquack-llm` against the
committed model (`apps/web/public/assets/llm/droidmind.bin`) and test
vectors. The current model artifact supports its original eight emotions.
Rust validates that list against the canonical 73-state registry, then maps
each semantic state to one of 12 broad voice modes and four shared affect
values. The expanded 1,000-row teacher set is review data only; it does not
replace the production weights. Between conversations the robot utters an
idle thought every 10β15 seconds.
## The web app
`apps/web` owns UI, canvas, and WebAudio only:
- `src/wasm/` β generated wasm-bindgen output (`pnpm run build:wasm`).
- `robot.ts` β assembles the real microduck from its mesh library
(`microduck.glb`) and kinematic tree (`kinematics.json`, MuJoCo
convention), exposes head joints and a synthetic jaw hinge.
- `scene.ts` β the procedural night-plain world (three moons, stars, rocks)
and the speech-driven articulation.
- `mind.ts` / `droidtext.ts` / `gestures.ts` β the chat brain wrapper,
droid-speak captions, and generic body language driven by the Rust voice
mode and affect values. TypeScript does not map the 73 semantic states.
- Short phrases render whole (`render_score` β `AudioBuffer`). Continuous
synthesis, when it arrives, must run the `Engine` in an AudioWorklet β
WASM alone does not prevent main-thread stalls.
## Research
Fitting is a CLI subcommand (`microquack fit`): it analyzes a real WAV
(pitch track, envelope, harmonics, noisiness) and emits a breakpoint score,
rendering every candidate through the same engine that ships. `research/`
holds the fitted corpus (`scores/fits.json`), pregenerated phrase scores,
and the frozen conformance fixtures.
## Conformance gate
`crates/microquack-core/tests/conformance.rs` renders all 90 frozen scores
(79 fitted + 6 phrases + 5 presets) against `research/fixtures/`: exact
sample counts, RMS/peak within 1e-6, and 21 full waveforms within 1e-4 per
sample. Streaming must equal offline rendering exactly, and phrase generation
must be seed-deterministic. Any DSP change that breaks these is a behavior
change and requires refreezing fixtures deliberately.
## Device integration (planned)
The Microduck repository consumes `microquack-core` at an exact version
(`microquack-core = "=x.y.z"`, or a pinned git revision until published) β
never vendored source. The device adapter feeds `Engine::render_into` into
its existing 48 kHz PCM path and reads `ControlFrame` values to animate the
physical beak and body. The 79 fitted scores must be revalidated at 48 kHz
before the robot ships them.
## Release model
One git tag should produce, all reporting the same version and commit:
- the `microquack-core` crate (device),
- the WASM/npm package (website),
- the CLI binary (render, validate, fit, schema, emotions),
- the score corpus and generated schemas.
## Roadmap
See [VISION.md](VISION.md): preset conversations, the droid-speak language
model, procedural full-body motion, and browser-side locomotion policies from
`microduck_rl` via ONNX Runtime Web.
|