4.3 KiB
Test 2 — wakeword detection in C# — kickoff prompt
Paste the block below into a fresh Claude conversation in this repo's working directory. The session will load CLAUDE.md automatically (which points at findings.md).
We just landed Test 1 of the C# Pi-client probe (record/play) on the fresh-start branch — proven on real hardware. Now do Test 2: re-implement the wakeword probe in C# under tests/02-wakeword-cs/.
Before any code, read these in order:
CLAUDE.md— project context + SSH access to the Pi.findings.md— canonical "what we know works." Especially the## C# probe outcome (2026-06-12 — Test 1 ported to C#)section: PortAudioSharp2 1.0.6, the .NET/libc env-var split (Environment.SetEnvironmentVariabledoes NOT reachgetenv(), must P/Invokesetenv), the USB Speaker Phone's 48 kHz-only native rate (needsPA_ALSA_PLUGHW=1), and the open questions about wakeword engines.tests/01-record-play-cs/Program.csandtests/01-record-play-cs/Probe.csproj— the patterns we'll reuse: PortAudio init +Libc.setenvworkaround, callback InputStream, lock geometry,<AllowUnsafeBlocks>true</AllowUnsafeBlocks>.bin/probe-cs— the build + scp + ssh deploy pattern. New probe will need a similarbin/probe-cs-2(or reuse the same script with a target arg — decide in the brainstorm).tests/02-wakeword/main.py— the Python original. Threshold 0.5, cooldown 1 s, openwakeword's stock "alexa" model. Pass criteria are listed in its docstring.- The two prior spec/plan docs for shape:
docs/superpowers/specs/2026-06-12-record-play-csharp-probe-design.mdanddocs/superpowers/plans/2026-06-12-record-play-csharp-probe.md. We'll follow the same workflow.
Goal of Test 2 in C#: the same pass criteria as the Python version (from its docstring):
- "alexa" at normal volume from ~1 m triggers within ~1 s, ≥8/10 attempts.
- ≤1 false positive per minute of unrelated speech.
- Process stays under 50% of one core (check with
htopon the Pi).
Probe is throwaway under tests/02-wakeword-cs/. No automated tests. Verification is hardware.
The hard question — which wakeword engine? openwakeword has no .NET port. Three real options to brainstorm with me before committing to code:
- (A) Port openwakeword's inference pipeline to C# using
Microsoft.ML.OnnxRuntime. Three ONNX files (mel extractor → Google speech embedder → keyword classifier). ~200–400 lines of glue. We own the port; no commercial lock-in; same stock "alexa" model the Python probe used. Carries forward to the eventual custom wakeword we'll train later. - (B) Picovoice Porcupine's .NET SDK. First-class .NET binding, runs on linux-arm64, low CPU. Commercial / API-key gated. Free personal tier exists. Faster to integrate but locks us in.
- (C) Vosk + keyword spotting. Heavier model, .NET binding exists, overkill for one wakeword.
Use superpowers:brainstorming to walk through this with me. Lead with your recommendation and the trade-offs. Don't write spec/code until I've picked an approach.
Specific gotchas to expect:
- ALSA plug layer:
PA_ALSA_PLUGHW=1via libcsetenv(NOT justEnvironment.SetEnvironmentVariable). Pattern is already intests/01-record-play-cs/Program.cs. - onnxruntime on Pi prints
/sys/class/drm/card0warnings while probing for GPUs. The findings doc mentionsORT_DISABLE_GPU_DETECTION=1(or similar) as a possible suppressor — if you want that, set it viaLibc.setenvtoo. - The USB Speaker Phone is mic AND speaker. The Python probe uses
sd.play(beep) + sd.wait()on the same thread as the input — the findings doc flags this as a real bug (drops ~200 ms of input right after detection). Decide in the spec whether to fix it for the probe or carry over the bug for parity. Streamambiguity withSystem.IO.StreamwhenImplicitUsingsis on — sameusing Stream = PortAudioSharp.Stream;workaround.
Workflow: brainstorming → spec → plan → subagent-driven implementation, same flow used for Test 1. Spec at docs/superpowers/specs/2026-06-12-test-2-wakeword-csharp-design.md, plan at docs/superpowers/plans/2026-06-12-test-2-wakeword-csharp.md (or whatever date is current).
Pi: sshpass -p 'assistant' ssh pi@192.168.50.115. Branch you'll work on: fresh-start (already current, do not create a new one unless I ask).