What crosses the Rust/webview boundary for audio? #43
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.
Depends on
Reference
aiko/denpa#43
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?
Question
denpa#35moves mic capture and VAD into Rust on both desktop (cpal+ silerovia
ort) and Android (KotlinAudioRecord). Neither half is written.#36states the rule that binds this one: the command signature must be the one
Android can implement — do not design around what happens to be easy on Windows.
So the interface has to be decided before either path is built. Specifically:
denpa#10's meter binds topreviousTriggeredProbability— a peak-hold that never falls mid-utterance andresets on speech end or
stopMic. What does Rust publish so that shapesurvives, and at what rate?
threshold pair the readout displays. Events, or polled state?
denpa#33exists becausestopMic()destroys theVAD instance without flushing, so a short press sends nothing. Under the new
boundary, who holds the recorded buffer, and what does release emit?
the desktop shell — one recorder or several.
The answer is a command-and-event surface, not an implementation. Use
/codebase-designfor the seam.Related
denpa#35,denpa#33,denpa#36,denpa#10denpa/docs/adr/0002-hardware-goes-through-rust.mdsrc/renderer/src/context/vad-context.tsx— what exists todayMap:
aiko/denpa#41Unblocked — #44 is resolved. Read its resolution comment before starting; findings in
.denpa-work/research/ort-android-audio.md.Two things it changes about this ticket's scope:
denpa#35's premise that "cpal under Tauri's Android target is unreliable" is refuted by primary sources. cpal's Android backend is first-class and Tauri does initialisendk_context. So KotlinAudioRecordis no longer the assumed Android path — it is now a live choice against cpal.input_preset, so no AEC and noVOICE_COMMUNICATIONpreset (PR #995, open). Android also returns zeroes rather than an error when it arbitrates the mic away.So this ticket now has to answer a question it did not start with: does VAD have to work while she is speaking aloud? If yes, AEC matters and Android needs
AudioRecord; if no, one cpal backend spans both platforms and the boundary is far simpler.Windows-side constraint for the same decision: WASAPI never rebinds
IAudioClient, so whatever owns the stream must rebuild it onStreamInvalidated/DeviceNotAvailable, andtimeoutis ignored so it needs its own watchdog.Resolution
Barge-in is supported, and it is a setting the operator can turn off.
You can talk over her while she is speaking and voice-activity detection will pick you up — but it is togglable, not unconditional. That answers the question this ticket was reduced to by #44, and it decides the backend.
What follows immediately
Android takes the Kotlin path.
AudioRecordwithMediaRecorder.AudioSource.VOICE_COMMUNICATION, for the platform AEC.#44refuted the idea that cpal is unusable on Android, so this is chosen for echo cancellation — the capability — and not for reliability.#35's scope note should be read with that correction: the conclusion survives, the reason changes.The boundary carries two modes, not one. With barge-in off, the mic can be closed while she speaks and no echo exists to cancel. With it on, capture runs concurrently with playback and the stream must be echo-cancelled. So the command surface has to express:
The carrier meter's shape is unchanged by this. It still binds to a peak-hold that never falls mid-utterance and resets on speech end or
stopMic(#10, viapreviousTriggeredProbability). Whatever Rust publishes preserves that, in both modes.Press-to-talk is unaffected. On release it emits what was captured during the press regardless of what VAD thought — that is
#33's fix and it does not depend on barge-in.What is deliberately not decided here
The Windows echo-cancellation path is unknown.
#44established only thatcpalcannot ask for an echo-cancelled stream; it never looked at what Windows offers instead. Filed as #54, which now blocks the Windows half of#35.That research also has to answer a question with reach beyond audio: if Windows needs a software AEC, it needs a reference signal of what is being played — and Denpa plays TTS through Web Audio in the renderer. ADR-0002 names playback "the open edge" and lists exactly this as the condition that would move it into Rust. So the Windows AEC answer may enlarge
#35from capture to capture-and-playback.The toggle's default is not fixed by this ticket. Recommendation, revisable: off by default, because barge-in cannot be honoured until the Windows path exists, and a setting that silently does nothing on the surface you use most is worse than one you turn on. The toggle belongs in 調整 (
#29).Sequencing this hands
#35ort, no echo cancellation required. This is buildable today.ort's Android prebuilts target it, and r27 fails at runtime with adlopenlibc++ symbol error rather than at build time. Only 27.2.12479018 is present on this machine.Echo cancellation is needed on exactly the surfaces that have it natively. Operator account, 2026-07-30:
AudioRecord'sMediaRecorder.AudioSource.VOICE_COMMUNICATION, which is what #43 already chose.So the platform that cannot echo-cancel is the one that does not need to, and the one that needs it gets it from the API already selected. That is a happier position than the research implied.
Consequence for the Windows work. #54 found the supported Windows AEC path (
eCommunicationsrole +SetClientProperties(AudioCategory_Communications)+IAudioEffectsManager/IAcousticEchoCancellationControl), and that reaching it means abandoningcpalforwasapi— cpal hardcodeseConsoleand exposes noSetClientProperties. That swap is deferred, not cancelled. It becomes necessary only if the desktop is ever driven on speakers.What this changes in the implementation:
set_barge_inno longer refuses when the backend cannot echo-cancel. Refusing would have blocked the case that actually works — headphones — while the case that does not (desktop speakers) is what theecho_cancellationcapability flag exists to let the UI describe. Capability informs; it does not forbid.This is recorded because it is not derivable from the code: a future reader finds a capture path with no echo cancellation and a barge-in toggle that permits itself anyway, and the reason is a fact about hardware rather than about software.