openWakeWord runs beside silero, in the ort that is already there #88
Labels
No labels
needs-info
needs-triage
ready-for-agent
ready-for-human
v2
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wayfinder:ticket
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
aiko/denpa#88
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The wake word needs no new dependency, no new thread, and no new audio path. Everything it wants, silero already has.
src-tauri/src/audio/engine.rsresamples capture to 16 kHz mono (Resampler, engine.rs:107;MODEL_SAMPLE_RATE, engine.rs:31) and pushes it through anortsession in fixed frames (Detector::probability, engine.rs:188). openWakeWord wants exactly that stream — 16 kHz, mono,f32— andortis already a dependency on both desktop and Android (Cargo.toml:47). So this is a second consumer of a buffer that is already being produced, not a new pipeline.This issue is inference only. Nothing changes about how the microphone behaves — the score is computed, published, and otherwise ignored.
#89is where it starts mattering. Splitting it this way means the detector can be tuned and watched on real audio before it has the power to swallow an utterance.Shape
A
WakeWordstruct besideDetector(engine.rs:158), holding three sessions rather than one, because openWakeWord is a chain and not a model:Two things differ from silero and will bite whoever assumes otherwise:
FRAME_SAMPLES(engine.rs:32) is the silero model's number. openWakeWord steps 1280 samples — 80 ms. The two consumers want different windows over the same stream, soWorker::feed(engine.rs:312) needs its own accumulator for this one rather than reusing silero's frame loop.WakeWord, not in the caller.Tee the resampled samples in
Worker::feedafter resampling and before — or alongside — the silero path. Publish the score the way the meter level already travels (publish_level, engine.rs:292, anddenpa://mic-level): adenpa://wake-scoreevent, rate-limited the same way, so the renderer can draw it while tuning. Nothing consumes it yet.It is not blocked on the model
#87produceshey_denpa.onnx, but openWakeWord ships pretrained heads —hey_jarvisis the usual one. Build and test against that, swap the file when#87lands. The two stages in front of the head are identical either way.Cost to watch
Silero scores a 512-sample frame; this chain runs three graphs every 80 ms, forever, whenever the mic is open. On the desktop that is noise. On the phone it is the whole question, and it is
#91's to answer — but leave the measurement reachable here rather than making someone add it later: log or expose the per-chunk inference time.While you are in here
package.json:24still lists@ricky0123/vad-web, andonnxruntime-webwith it. ADR-0002 moved capture to Rust and the renderer no longer opens a device — those two are dead weight, and worse, they are the first thing anyone reading this repo for "where does the wake word go" will find. They point at a JS answer that is wrong for this codebase. Removing them is not this issue's job, but somebody should.Done means
engine.rs— the chain loads, silence never scores high, a held-out clip of the phrase does.Related
#87— the model file#89— the first thing that consumes the score#91— whether the phone can afford it