Migration step: the prototype's audio stack, with its two satellite repos absorbed as workspace crates and the Python removed entirely. sounds/ is microduck_sounds rewritten in Rust: the seedable synth (personality, recipes, variants) plus a CLI whose ensure-bank replaces generate_sounds.sh — no venv, no numpy, no ffmpeg. It renders at 48 kHz natively (the resample step existed only because the synth was 22.05 kHz and the Radxa's I2S tree is 48k-family), with the two rate-tuned constants rescaled to keep their time-domain character. The RNG is a vendored xoshiro256++ with a pinned-stream test, because the generator IS the voice: every robot re-rolls once (bank v5, the same event as a synth retune), then stays itself forever. The seed derivation (SoC serial -> sha256 -> u32) is unchanged, so each robot keeps its personality traits' provenance. The parrot experiment stays unported. pet-detect/ is microduck_pet_detect plus the prototype's pet_worker: mel extractor, streaming detector, the arecord worker and the ambient sound sentry, the pet-detect/pet-features binaries, the vendored model (shipped as models/pet_detect.onnx) and the training script — which extracts features through the Rust binary, so train/infer parity crosses the port intact. robotd owns playback (root already, and the codec PCM is single- client): greet as the loop comes up, goodbye peck on shutdown, coo on petting (walk mode, per the prototype's launch lines), and the new robot.sound call (v10) for the rest. padd's triggers do what the prototype's did — RT quacks on its rising edge, LT rides the wheee, streamed start->loop->end into one aplay with the writer paced 250 ms ahead. The hold is a per-tick notification that decays, so a client that dies mid-ride leaves a ride that lands. robotctl quack replaces the quack binary, over the socket — so it also works from a laptop on a forwarded socket, which is when you actually need to know which duck you are talking to. Found by the fake-bus torture test: robotd restores SIGPIPE's default disposition at startup, so a write into a dead aplay killed the whole daemon — the wheee writer thread now blocks SIGPIPE and gets EPIPE like it expects. Provisioning: setup-board.sh gains configure_audio() — alsa-utils + DKMS toolchain, the Armbian vendor kernel (+headers), the i2c3 + codec/I2S overlays compiled from deploy/audio/, the aic3x DKMS module (the vendor kernel does not build SND_SOC_AIC3X), and the mixer-init service ordered Before=robotd. The voice bank is NOT provisioned there: every release install renders it via postinstall (sounds ensure-bank, idempotent by seed+version marker). Packaging ships the three new binaries and the model at all three sites, with a tripwire for the model like the policies have. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
19 lines
649 B
TOML
19 lines
649 B
TOML
# The robot's voice: a tiny seedable synth for pet vocalisations.
|
|
#
|
|
# Ported from `apirrone/microduck_sounds` (Python/numpy) so a release carries its own voice
|
|
# generator instead of a pip install — the bank renders on-device in a few seconds of Rust
|
|
# rather than minutes of numpy on a venv nobody provisioned. See src/lib.rs for what changed
|
|
# in the port and why every robot's voice shifts once.
|
|
[package]
|
|
name = "sounds"
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
rust-version.workspace = true
|
|
license.workspace = true
|
|
|
|
[dependencies]
|
|
anyhow = "1"
|
|
clap = { workspace = true, features = ["derive"] }
|
|
hound.workspace = true
|
|
sha2 = "0.11"
|