Docs · Keyword Spotting · Linux

Keyword Spotting for Linux

Run the VoxRT 14-command keyword spotter on Linux x86_64 (AVX2) and aarch64: Python, Node.js, Go, C/C++ and Rust wrappers on one glibc-2.17+ runtime — install, quick starts, tuning, and deployment from Raspberry Pi to Graviton.

Always-on 14-way keyword spotter on the VoxRT custom on-device inference runtime. ~636K-parameter streaming Conformer-Medium, 16 kHz mono in, per-class sigmoid posteriors + threshold-crossing detection events. Detects the phrases "yes / no / cancel / play / pause / next / previous / up / down / back / on / off / voxrt / hey_vox".

  • Status: production-ready for x86_64 + aarch64 Linux. Runtime numeric parity vs PyTorch reference validated bit-identically across AVX2 (x86_64) and NEON (aarch64) on the deployed model — see the Performance section for measured numbers.
  • Current version: v0.1.0
  • Target platforms: x86_64 Linux (servers, dev boxes, containers — Haswell 2013+ / Excavator 2015+ with AVX2) and aarch64 Linux (Raspberry Pi 3 / 4 / 5 / Zero 2, NVIDIA Jetson, AWS Graviton, Rock Pi / Orange Pi / other aarch64 SBCs)
  • glibc baseline: 2.17 (manylinux2014_x86_64 + manylinux2014_aarch64 — Pi OS Bullseye/Bookworm, Jetson L4T 32.x/35.x/36.x, Ubuntu 18.04+, Debian 10+, RHEL/CentOS 7+, Amazon Linux 2, AWS Graviton)
  • Languages shipped: Python (PyPI wheel × 2 arches), Node.js (npm, both arches in one package), Go (go get, both arches bundled), C / C++ (2 tarballs — one per arch — plus CMake + pkg-config), Rust (git dependency)
  • License: Apache-2.0 (wrapper sources) · proprietary (compiled runtime, redistribution allowed via this SDK)
  • Keyword-model weights: proprietary in-house (synthetic + open training data; no upstream license obligations)

What is VoxRT?

VoxRT is a from-scratch inference runtime for on-device speech models. No ONNX Runtime, no PyTorch Mobile, no LiteRT — a custom Rust core sized and tuned for streaming voice workloads on phone-class and edge-Linux hardware.

voxrt-kws is the keyword-spotting product on that runtime, alongside voxrt-wake-word-linux (always-on wake phrase) and voxrt-asr-linux (streaming ASR). Same runtime crate, same NEON / AVX2 kernel set, same .vxrt model format — pick the SDK that matches your task. The wake-word SDK triggers a single phrase; the KWS SDK classifies utterances across a fixed vocabulary; the ASR SDK produces free-form transcripts.

Custom keyword sets (your own command vocabulary, brand-specific commands, additional languages) are part of the commercial VoxRT SDK tier. Contact [email protected].

Model quality

Held-out speaker-disjoint test split, per-class threshold-swept macro-F1 on the 14-way vocabulary:

  • F1 macro: 0.9671 at the default operating point (threshold 0.9, consecutive-3 firing, cooldown 25 emits).
  • Preserves the noise-robustness win of the M10 augmentation recipe while matching the M9 clean-set headroom (data + capacity co-tuning — see the internal docs/kws/M13-STATUS.md).

Each keyword's per-class ROC AUC ≥ 0.99 on the held-out set. Default operating point tuned for false-fire suppression in an always-on setting; lower the threshold at runtime if your application can tolerate more triggers in exchange for higher recall.

Performance

x86_64 release builds detect AVX2 + FMA at runtime and dispatch every hot kernel (mel frontend, Conformer MHA, depthwise conv module, FFN, LayerNorm, softmax, sigmoid) into 8-wide AVX2 + FMA. aarch64 release builds use NEON 4-wide FMA in the same kernels. All ARMv8-A hardware mandates NEON, so no runtime detection is needed.

Native bench = 60 s of silence pushed in 512-sample (32 ms) chunks after 100-chunk warmup, single-thread. RTF = wall-time / audio-time (lower is better):

DeviceBackendRTFThroughputms / chunk (mean / p99)
WSL x86_64 (Ryzen dev laptop)AVX2 + FMA1.03 %97.2× realtime0.33 / 0.55
Raspberry Pi Zero 2 WNEON A53 @ 1 GHz32.2 %3.1× realtime10.3 / 13.0
Raspberry Pi 3 A+ / B / B+ (extrapolated)NEON A53 @ 1.2–1.4 GHz~24–28 %~3.5–4×scaled
Raspberry Pi 4 B / 400 (extrapolated)NEON A72 @ 1.5–1.8 GHz~13–16 %~6–8×scaled
Raspberry Pi 5 (extrapolated)NEON A76 @ 2.4 GHz~6–8 %~12–17×scaled
NVIDIA Jetson Nano / Orin Nano (extrapolated)NEON A57 / A78AE≲ Pi 4–5≳ 6–17×scaled
AWS Graviton / Ampere Altra (extrapolated)NEON Neoverse N1 / V1≪ 1 %server-classscaled
x86_64 servers (AVX2, Haswell 2013+)AVX2 + FMA≲ 1 %≳ 100×scaled

The Pi Zero 2 W row is a direct measurement (RTF 32.2 % sustained across 1875 consecutive 32 ms chunks; p99 chunk = 41 % of the 32 ms real-time budget → 59 % headroom even in the worst chunk). The WSL x86_64 row is similarly a direct measurement. All other rows scale from these two baselines by clock + µarch — conservative estimates, same binary, no per-device tuning.

Cross-architecture numeric parity (5-sample PyTorch-reference diff, WSL AVX2 vs Pi NEON, same model):

MetricWSL AVX2Pi NEON
posterior max \Δ\across 5 samples7.2 × 10⁻⁴7.2 × 10⁻⁴ (identical)
firing score max \Δ\1.6 × 10⁻⁴1.6 × 10⁻⁴ (identical)
firing emit \Δ\max00
firing counts / class match5 / 55 / 5

Both backends produce bit-identical output vs the PyTorch reference on real audio inputs — the AVX2 and NEON kernels sum in the same order, and 4-wide vs 8-wide accumulation happens to converge at these tensor shapes. Post-diff sits at 7 × the abs-tolerance floor used for the golden CI (5 × 10⁻³).

Binary footprint

  • C tarball (x86_64) (voxrt-kws-0.1.0-x86_64-linux-gnu.tar.gz): ~350 KB compressed. Contains libvoxrt_kws.so (~600 KB stripped, glibc-2.17 baseline), voxrt_kws.h, pkg-config + CMake configs, examples/, LICENSE × 2, README.
  • C tarball (aarch64) (voxrt-kws-0.1.0-aarch64-linux-gnu.tar.gz): ~300 KB compressed. Same layout, ~520 KB .so.
  • PyPI wheel (voxrt_kws-...-cp39-abi3-manylinux_2_17_{x86_64,aarch64}.whl): two per version, one per arch. ~350 KB / ~300 KB compressed. Single abi3 wheel covers Python 3.9 / 3.10 / 3.11 / 3.12 / 3.13 via the pyo3 stable-ABI bridge.
  • npm package (voxrt-kws-0.1.0.tgz, imports as @voxrt/kws): ~650 KB compressed. One tgz carries both voxrt-kws.linux-x64-gnu.node + voxrt-kws.linux-arm64-gnu.node — install once, runs on both arches.
  • Go module (in-repo at go/): both .sos + Go source. Fetched by go get github.com/VoxRT/voxrt-kws-linux/go.
  • Keyword model voxrt_kws.vxrt (downloaded separately from voxrt-kws-models): ~1.3 MB fp16 encrypted (M13 Conformer-Medium, d_model=96, 4 blocks, 636 K params).

Net effect on a shipped app: ~1.9 MB once the runtime .so + .vxrt + language wrapper are bundled.

Install

Python

pip install voxrt-kws

One abi3 wheel per arch covers Python 3.9 / 3.10 / 3.11 / 3.12 / 3.13. pip picks the wheel that matches your interpreter's platform automatically.

Node.js

npm install @voxrt/kws

TypeScript definitions ship with the package. Both linux-x64-gnu and linux-arm64-gnu native addons are in the same tarball — require('@voxrt/kws') resolves the right one at load time.

Go

go get github.com/VoxRT/voxrt-kws-linux/[email protected]

The go/ subdirectory of this repository IS the Go module — no separate publish step. cgo picks up the bundled .so via ${SRCDIR}-relative rpath at build time; no LD_LIBRARY_PATH or system install required.

C / C++

Pick the tarball that matches your host arch:

# x86_64 host
curl -L https://github.com/VoxRT/voxrt-kws-linux/releases/download/v0.1.0/voxrt-kws-0.1.0-x86_64-linux-gnu.tar.gz \
    | sudo tar -xz -C /usr/local --strip-components=1
# aarch64 host
curl -L https://github.com/VoxRT/voxrt-kws-linux/releases/download/v0.1.0/voxrt-kws-0.1.0-aarch64-linux-gnu.tar.gz \
    | sudo tar -xz -C /usr/local --strip-components=1

sudo ldconfig

After extract:

gcc main.c $(pkg-config --cflags --libs voxrt-kws) -o my_app

Or in CMake:

find_package(VoxRTKws 0.1.0 REQUIRED)
target_link_libraries(my_app PRIVATE VoxRT::Kws)

Rust

Pin the crate via git until the crates.io decision is finalised:

[dependencies]
voxrt-kws = { git = "https://github.com/VoxRT/voxrt-kws-linux", tag = "v0.1.0" }

Get the keyword model

The voxrt_kws.vxrt model file (AES-256-GCM encrypted, ~1.3 MB) is distributed separately from voxrt-kws-models so the SDK ships without weights and the two version independently. All v0.1.x SDK releases use the same model v0.1.0.

  • Download once at deploy time:

``sh curl -LO https://github.com/VoxRT/voxrt-kws-models/releases/download/v0.1.0/voxrt_kws.vxrt `` Bundle alongside your application binary.

  • Download on first run + SHA-256 verify — recommended if you want to swap models without a redeploy.
  • Embed as a build-time resource — for Python: importlib.resources. For Node.js: require.resolve + fs.readFileSync. For Go: //go:embed. For C: any resource-embedder. Feed the bytes to from_bytes(...) on your language's KwsEngine constructor.

Quick start

Python — the smallest possible end-to-end pipeline:

from voxrt_kws import KwsEngine

engine = KwsEngine.from_path("voxrt_kws.vxrt")
engine.threshold = 0.9

for chunk in mic_iter():                # int16 mono @ 16 kHz, any chunk size
    for d in engine.push_pcm_i16(chunk):
        print(f"detect! {d.class_name} t={d.timestamp_sec:.3f}s score={d.score:.4f}")

Every language ships an equivalent end-to-end example under examples/. All quickstarts use only stdlib WAV parsing — no numpy / sounddevice / go-audio dependency at the entry level.

Examples

Two flavours are shipped in every language channel — a live-microphone example (aimed at edge deployments — Pi, Jetson, kiosk boxes, industrial SBCs) and a WebSocket server example (aimed at server / browser-facing deployments — a WS endpoint accepting audio frames from a browser mic and streaming detection events back). The example set:

LanguageExampleWhat it does
Cexamples/c/alsa-mic-quickstartLive ALSA microphone → keyword detection events on stdout. Build with make, run against plughw:0.
C++examples/c/alsa-mic-quickstart-cppSame live-mic demo in idiomatic C++17: RAII wrappers over voxrt_kws_t* + snd_pcm_t*, std::atomic Ctrl-C, exception-based errors.
Cexamples/c/ws-detect-serverlibwebsockets-based WebSocket endpoint that streams keyword detections as JSON frames. Skeleton for a browser mic → server backend.
C++examples/c/ws-detect-server-cppC++17 variant of the WebSocket server. Same wire protocol.
C++ / CMakeexamples/c/cmake-consumerSkeleton CMake project consuming the SDK via find_package(VoxRTKws ...). Copy-paste into a real project.
Pythonexamples/python/quickstartWalk a 16 kHz WAV file in 32 ms chunks, print detections + RTF. Pure stdlib (wave + struct).
Pythonexamples/python/alsa-mic-quickstartLive mic via sounddevice.
Pythonexamples/python/ws-detect-serverwebsockets-based server, same JSON protocol as the C variant.
Node.jsexamples/nodejs/quickstartSame shape, JavaScript. Manual WAV header parse via fs + Buffer, no npm deps.
Node.jsexamples/nodejs/alsa-mic-quickstartLive mic via the mic npm package.
Node.jsexamples/nodejs/ws-detect-serverWebSocket server via ws.
Goexamples/go/quickstartSame shape, Go. encoding/binary for the WAV header. cgo picks up the bundled .so.
Goexamples/go/alsa-mic-quickstartLive mic via github.com/gordonklaus/portaudio.
Goexamples/go/ws-detect-serverWebSocket server via github.com/gorilla/websocket.

Tuning

Threshold. Sigmoid-space [0, 1], shared across every keyword class. Default 0.9. Lower increases recall (more detections, more false positives); raise increases precision.

Consecutive frames. Number of consecutive emits a class posterior must stay ≥ threshold before firing. Default 3 (120 ms at 25 fps). Lower for tighter reaction; raise if you're getting brief spurious triggers.

Cooldown. Per-class post-detection suppression in emits. Default 25 (1.0 s at 25 fps). Prevents stuttering when a keyword lingers above threshold across many emits.

CPU affinity. Not exposed as a public knob — the SDK does not pin threads on Linux. If you're on a big.LITTLE Pi (Pi 4/5) and want the KWS thread to stick to the perf cluster, wrap push_pcm_i16 in a thread you affine yourself via sched_setaffinity (POSIX C), os.sched_setaffinity (Python), or runtime.LockOSThread + unix.SchedSetaffinity (Go).

All three knobs are mutable at any time — change them between chunks safely.

License

  • Wrapper sources (Python / Node / Go / C headers / examples / docs): Apache-2.0.
  • Compiled binaries (.so, .node, .whl, prebuilt tarball artefact): proprietary, see LICENSE-BINARY.
  • Keyword model weights (voxrt_kws.vxrt): proprietary in-house, same terms as LICENSE-BINARY.

Commercial licensing for custom keyword vocabularies, additional languages, or bulk-device deployments: [email protected].

  • Brand + docs: <https://voxrt.com>
  • Wake-word SDK (Linux): <https://github.com/VoxRT/voxrt-wake-word-linux>
  • ASR SDK (Linux): <https://github.com/VoxRT/voxrt-asr-linux>
  • Browser SDK (WASM): <https://github.com/VoxRT/voxrt-kws-browser>
  • Keyword-model releases: <https://github.com/VoxRT/voxrt-kws-models>
  • Issues: <https://github.com/VoxRT/voxrt-kws-linux/issues>

Synced from voxrt-kws-linux on 2026-07-22 — the GitHub repo is the source of truth. Found a mismatch? The repo wins.

Need a custom wake phrase?

The published "Hey Assistant" model is free for commercial use. Custom phrases are tuned per customer.

Talk to us View on GitHub