Does silero-via-ort run on Android, and does cpal hold up on Windows? #44
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.
Blocks
Reference
aiko/denpa#44
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
Facts the audio boundary decision waits on, all outside this working directory:
ortcrate build and run inference on Android under Tauri'sAndroid target — architectures, ONNX Runtime binary availability, model
loading from assets, and what the binary costs?
denpa#35assumes silero movesinto Rust; if
ortcannot reach Android, the Android VAD story is differentfrom the desktop one and the boundary must absorb that.
cpalbehave on Windows for a long-lived capture stream — deviceselection, what happens when the default input device changes or disappears
mid-session, and whether exclusive mode is ever needed?
denpa#35asserts "cpalunder Tauri's Android target is unreliable".Verify or refute that against primary sources rather than inheriting it.
AudioRecordrequire alongsideRECORD_AUDIOwhen aforeground service is already running —
denpa#23had to reason about thisonce already.
Answer from primary sources: crate docs, upstream issue trackers, Android
developer documentation. Cite everything.
Related
denpa#35,denpa#23Map:
aiko/denpa#41Resolution
Findings:
.denpa-work/research/ort-android-audio.md— 659 lines, primary sources only, every claim carries a URL and anything unsourced is marked unverified.The Rust audio boundary is viable on both targets, but it is not symmetric.
1.
orton Android — yes, arm64-v8a only. pyke has shipped a prebuilt static ONNX Runtime foraarch64-linux-android(NNAPI EP) sinceort2.0.0-rc.11, 2026-01-07. There is no prebuilt forarmv7,x86_64ori686. Tauri v2 builds all four ABIs by default, so an unconstrainedtauri android buildfails to link on three of four legs — pin--target aarch64. The x86_64 emulator is out; testing is on real hardware only, which makes#48(get a phone attached) load-bearing rather than convenient.2. The NDK version is load-bearing. pyke builds against NDK r28 / API 24.
ortissue #514 was precisely an NDK mismatch, surfacing as a runtimedlopen failed: cannot locate symbol _ZTT...__cxx11.... Maintainer, verbatim: "The binaries target r28, so NDK 27 definitely won't work." r28 also brings 16 KB page alignment, required by Play for API 35+ since 2025-11-01.3. Size: the runtime dominates the model tenfold. ONNX Runtime arm64 is 27.3 MiB shared / ~119 MiB static archive; Silero VAD itself is 1.2–2.2 MiB. Contribution after
--gc-sections/LTO is unverified.4. Model loading is not
std::fs. Tauri resources on Android live behindasset://localhost/. The documented path isinclude_bytes!plus commit-from-memory; Rust-sideAAssetManageraccess under Tauri is undocumented.5.
cpalon Windows capture is sound, but you own the lifecycle. WASAPI in cpal never rebindsIAudioClient, so a default-device change or removal yieldsStreamInvalidated/DeviceNotAvailableand the stream must be rebuilt by you.timeoutis ignored on WASAPI — write a watchdog. Exclusive mode is unsupported by cpal (open since 2016) and Microsoft steers away from it for capture anyway; shared mode is correct.6. Version-pin risk. Three fixes that bear directly on long-lived capture — #1264 panic on device loss, #1267 correct error kind, #1281 false first-buffer xrun — landed on master after 0.18.1 (2026-06-07) and are in no published release. Also note 0.18 removed
Device::name()andStreamError.7. "
cpalunder Tauri's Android target is unreliable" — refuted as phrased.denpa#35asserts this; primary sources do not support it. cpal lists Android as first-class (AAudio; the oboe C++ dependency has been gone since 0.16.0, 2025-06-07), input is fully implemented, no open issue reports Android audio broken, and Tauri'smobile_entry_point→android_binding!→ taoonCreatechain does initialisendk_context, which is cpal's one Tauri-sensitive requirement. Tauri's own repos are silent on the claim. Likely formed against a pre-0.16 oboe-era cpal.8. What is genuinely true is narrower, and worth putting in the ADR. API 26 floor. The app must declare and runtime-request
RECORD_AUDIOitself — cpal does nothing and Tauri adds nothing. No acoustic echo cancellation: there is noinput_presetcontrol (PR #995, open since 2025-07-31). And Android hands you zeroes rather than an error when it arbitrates the mic away. Those last two produce exactly the "unreliable" symptom that was being attributed to cpal itself.9. The foreground-service rules are the hardest constraint, and they bite the overlay specifically. Mic needs
FOREGROUND_SERVICE_MICROPHONE(API 34+),android:foregroundServiceType="microphone"in the manifest, and themicrophonebit in thestartForeground()mask.SYSTEM_ALERT_WINDOWexempts you from the background-start restriction only — it is not on the while-in-use exemption list. So an already-running overlay FGS does not let you start or promote to a microphone-typed FGS from the background; the docs require a visible activity, not a visible overlay. Android 15+ narrows theSYSTEM_ALERT_WINDOWexemption further. Workable shape: one service carryingspecialUse|microphone, promoted by a secondstartForeground()at a moment when a real activity is visible.What this hands
#43: the interface can be one Rust command surface across both platforms, but it must tolerate an asymmetric backend — a supervisor on Windows, and on Android either cpal-with-no-AEC or a KotlinAudioRecordpath. The real argument forAudioRecordis acoustic echo cancellation and theVOICE_COMMUNICATIONpreset, not reliability — and whether AEC matters depends on whether VAD has to work while she is speaking out loud. That is now a sharp question#43has to answer.What this hands
#47: the overlay's foreground service does not help microphone access, and cannot promote to it from the background. It is a cost, not a shared asset.Two of this issue's findings need correcting, from building the Android microphone in
#35(merged458f0c6) and running it on a Pixel 7 Pro, 2026-07-30.1.
ndk_contextis not initialised. This issue says Tauri'smobile_entry_point→android_binding!→ taoonCreatechain "does initialisendk_context, which is cpal's one Tauri-sensitive requirement." It does not — verified by calling it.And the failure is not a graceful one.
ndk_context::android_context()isANDROID_CONTEXT.expect("android context was not initialized")(ndk-context-0.1.1/src/lib.rs:72), so the first call aborts the process —SIGABRT, a Rust panic unwinding across theextern "C"boundary of Tauri's JNI IPC handler,panic_cannot_unwind→abort. No error to handle, no log to read except a tombstone.This matters beyond
#35: it was the stated basis for cpal being viable on Android under Tauri, and anything else reaching for the Activity or the JavaVM from Rust will hit the same wall. Tauri's ownrun_on_android_contextexists but lives on the runtime handle, which is not public API. What works is Kotlin handing the JVM over atonCreate— needed regardless, because a class looked up from a natively-attached thread resolves against the system classloader and will never find an app class.2. NDK r27's failure is narrower than recorded. This issue says r27 "produces a runtime
dlopenlibc++ symbol failure". A standalone aarch64 binary built against r27 — linking the sameortprebuilt, loading silero from memory and scoring a frame — runs fine on the Pixel 7 Pro. So the failure is about what ends up in the APK rather than about the compiler; r27'slibc++_shared.sois 1.79 MB against r28's 9.24 MB.The conclusion is unchanged — build with r28 (r28.2.13676358 is installed) — but "a clean build proves nothing, and neither does a clean standalone run" is the more accurate warning.
Confirmed, for the record: the arm64-v8a-only prebuilt is real, and
--target aarch64must be pinned or Tauri fails to link the other three ABIs.A third thing worth adding to the file, because it is the same class of trap and cost more than either of the above:
RECORD_AUDIOis a foreground app-op. Granted, on a locked or sleeping screen,AudioRecordreturns full buffers of zeros — no error, no short read, indistinguishable from a quiet room. That is the same shape as thedefault_input_device()silence that cost#35an evening on Windows.docs/android-build.mdrecords all of it.