Skip to content
KNOOP edited this page Aug 27, 2026 · 6 revisions

Wake Word Engines

Ava has two on-device wake word engines. Both run entirely locally — no audio leaves the device for wake detection. Compatible with Android 5-16.

0.7.0+ replaced the legacy vsWakeWord engine with OpenWakeWord — a native C++ streaming engine using a shared 96-dimensional acoustic embedding and lightweight ONNX keyword classifiers. 0.7.3 added Chorus Wake multi-device arbitration, graded WebView performance scheduling, and improved timer/announce handling. See Chorus Wake for multi-device wake arbitration.

Dual-engine design

The two engines are independent architectures, not two encodings of the same graph.

microWakeWord (default) uses TensorFlow Lite binary classifiers. Models are uint8 quantized at training time — typically 50–80KB, cheap enough to run always-on on Android 5+ devices, including 1GB RAM tablets.

OpenWakeWord is a native C++ streaming engine using a shared 96-dimensional acoustic embedding model and lightweight ONNX keyword classifiers. Feature extraction (melspectrogram), embedding inference, and keyword scoring all run in C++ on CPU. The engine is stateful and streams audio in 40 ms chunks (640 samples at 16 kHz). Model files are id.json (manifest) + id.onnx (float32 ONNX keyword classifier).

The engine runs inside a foreground service: the screen can be off, no browser is required, and detection continues in kiosk mode on Android 5+.

Engine Comparison

Dimension microWakeWord OpenWakeWord
Architecture TFLite uint8 binary classification Native C++ streaming: shared embedding + ONNX keyword classifiers
Model size 50–80KB, uint8 quantized at training ~500KB–2MB keyword .onnx + shared embedding model
Inference 10ms frame, stride 3 40ms chunks (640 samples), alternating A/B embedding phases
Frontend microfeatures (TFLite melspectrogram) Native C++ melspectrogram (32 mel bins, 76-sample window)
Shared embedding No — each model is independent Yes — all keywords share one 96-dim embedding model
Decision 5-frame sliding window mean > threshold Consecutive-window confirmation (default 2 hits) + per-keyword cooldown
Output Scalar 0–1 probability Scalar 0–1 score per keyword
Wake-word swap Full retraining required ONNX model hot-swap — drop in a new keyword classifier
CPU / memory Minimal (always-on, by design) Moderate; embedding computed once for all keywords
False wake defense Threshold + dual-stage verification Threshold + consecutive-hit gate + neural VAD + optional built-in verifier
Built-in models 9 micro (.tflite) 1 open (.onnx: ok_nabu) + shared embedding model
Best for Low-end Android 5+ persistent background Multi-keyword, noise-robust, community model ecosystem

How idle stays cheap

1. Stop-word detection is not always-on, and is not a model. Stop detection runs only when it is useful: when a timer alarm is ringing, or when a voice session is in progress (Listening / Processing / Responding). During idle standby it is skipped entirely. It is also no longer a TFLite classifier — Ava uses its own model-free native DSP detector (see Stop Words) whose per-frame cost is a tiny fraction of a model inference.

2. OpenWakeWord compute gate. After an adaptive 150–250 ms silence run, expensive native inference pauses. Confident silence closes quickly; VAD values near the speech boundary get more time. The gate reopens the moment voice-like audio returns.

3. Neural VAD gate. A TFLite MicroVad model runs before wake scoring. The native engine's only speech check is an RMS energy lookback, which passes any loud non-speech (TV, music, machinery). The VAD gate never blocks scoring — it only suppresses a wake the classifier already accepted, and it fails open if the model is unavailable.

4. Alternating A/B embedding phases. The native scheduler evaluates the two embedding phases on alternating 40 ms ticks, halving per-chunk compute. This is approximately an 86% reduction versus repeated full-window convolution work.

5. Shared embedding architecture. All active keywords share one 96-dimensional embedding model. Adding a keyword does not add a new full inference path — it adds a tiny classifier head that reads the shared embedding. 1 keyword or 8 keywords, the embedding runs once.

Net effect: In idle standby, wake-word CPU stays low because stop-word inference is skipped and OpenWakeWord's compute gate pauses during silence. When someone speaks, the VAD gate and consecutive-hit confirmation prevent false wakes while the shared embedding keeps multi-keyword cost flat.

microWakeWord (default)

microWakeWord is the default engine. It uses TensorFlow Lite with tiny uint8 models quantized at training time. Each wake word is a separate .tflite model file paired with a .json config. The detector runs a sliding window average over the last 5 frames and triggers when the average probability exceeds the cutoff.

Built-in micro models (9):

Model ID Wake Word Author
hey_jarvis Hey Jarvis Kevin Ahrendt
alexa Alexa Kevin Ahrendt
hey_home_assistant Hey Home Assistant Michael Hansen
hey_mycroft Hey Mycroft Kevin Ahrendt
hey_luna Hey Luna adamlonsdale
hey_peppa_pig Hey Peppa Pig Michael Hansen
okay_computer Okay Computer Michael Hansen
okay_nabu OK Nabu Kevin Ahrendt
choo_choo_homie Choo Choo Homie Michael Hansen

Stop word: "stop" — detected by Ava's own model-free native DSP detector (no TFLite model), independent of the wake-word engine.

microWakeWord Dual-Stage Verification

microWakeWord performs a fast second verification over the captured wake phrase. A chance confidence spike is no longer enough: the same model must confirm the contiguous audio sample at a slightly stronger threshold before Ava opens the assistant. This dual-stage path specifically reduces false wakes caused by television and background conversation while preserving the existing voiceprint check when voiceprint protection is enabled.

OpenWakeWord

OpenWakeWord loads float32 .onnx keyword classifier graphs that read from a shared 96-dimensional acoustic embedding model. The entire pipeline — melspectrogram extraction, embedding inference, keyword scoring, consecutive-hit confirmation, cooldown — runs in native C++.

Architecture

Audio (16 kHz, 40 ms chunks)
    ↓
Native C++ melspectrogram (32 mel bins, 76-sample window)
    ↓
Shared embedding model (ONNX, 96-dim output, 16-frame window)
    ↓ Alternating A/B phases per 40 ms tick
Keyword classifier 1 (ONNX)  →  score
Keyword classifier 2 (ONNX)  →  score
...
    ↓
Consecutive-hit gate (default 2) + per-keyword cooldown
    ↓
Neural VAD gate (suppresses non-speech false wakes)
    ↓
Wake dispatched

Shared Embedding Model

All keywords share one acoustic embedding model. Adding a keyword does not add a new full inference path — it adds a tiny classifier head that reads the shared embedding. 1 keyword or 8 keywords, the embedding runs once.

The embedding model is a float32 ONNX graph that takes a short window of melspectrogram frames and produces a compact acoustic fingerprint. The melspectrogram is computed in native C++ — no separate melspectrogram model is shipped. Classifier tensor names carry a per-model hash suffix; the engine binds by index, not by name, so models from different training runs all load correctly.

Built-in OpenWakeWord Model (1)

Model ID Wake Word Author
ok_nabu OK Nabu Home Assistant community, collected by Florian Wartner (fwartner)

Source: fwartner/home-assistant-wakewords-collection (MIT license)

OpenWakeWord Manifest

Each keyword model is a pair: id.json (manifest) + id.onnx (float32 ONNX classifier). The manifest defines the keyword's threshold, confirmation gate, and cooldown.

Key manifest fields:

Field Default Description
format openwakeword-v1 Manifest format version
id Keyword identifier
wake_word Human-readable wake word
model id.onnx ONNX model filename
openwakeword.threshold 0.5 Trigger cutoff (0.15–0.99)
openwakeword.required_hits 2 Consecutive windows above threshold to trigger
openwakeword.cooldown_ms 2000 Cooldown before same keyword can trigger again
openwakeword.sliding_window_size 1 Sliding window size
built_in_verifier null Model carries its own second-stage verifier (ONNX If gate)
stop_classifier false Ava rejects these (stop detection is a built-in DSP detector)

Cutoff Policy

Community ONNX models are calibrated near 0.5. On the real engine they peak around 0.84–0.91; setting the cutoff too high drops recall rapidly — 0.92 fires nothing on ok_nabu.

Ava's sensitivity slider is bounded by what the model can actually score. An out-of-reach strict request clamps down to the ceiling, never falls back to the manifest default. This prevents a leftover high sensitivity value from a different engine from silently making the detector deaf.

Consecutive-hit gate: Every false positive observed in extended non-wake speech testing was a single-frame spike. Every genuine positive held multiple consecutive frames above threshold. Two consecutive hits reject every observed spike while preserving recall — a single-frame classifier spike can no longer trigger a wake.

Built-in verifier exemption: Models that carry their own second-stage verifier (an ONNX If gating a verifier network, e.g. official hey_jarvis) emit a single-frame spike per utterance by design — the verifier already did the debouncing. For them the manifest gate runs as written, because the verifier holds every genuine negative well below threshold.

Offline Echo Verification

Ava can burst-score the playback reference (echo) with the same keyword configs as the live engine. This offline pass runs in a separate engine instance with its own lock — never the live detect path — so a ~100–400 ms burst pass cannot stall live detection mid-playback. When the echo itself triggers the keyword, the wake is suppressed.

Stop Words

Saying "stop" interrupts the current conversation or stops Ava's response (e.g., timer alarm).

Built-in DSP Stop (default, no model)

The bundled stop.tflite classifier was removed because it produced false triggers; it is replaced by Ava's own model-free native DSP detector. It matches the phonetic time structure of an isolated spoken "stop" — the specific sequence of fricative, closure, vowel, and abrupt ending that distinguishes "stop" from hisses, isolated vowels, and running speech.

All energy thresholds are relative to an adaptive noise floor, so detection is microphone-gain invariant. A quiet room produces zero false triggers — something the old classifier could not guarantee.

Stop detection only runs when actually needed: when a timer alarm is actively ringing, or when a voice session is in progress (Listening, Processing, Responding). During idle standby with no alarm, stop detection is skipped — cutting CPU load and heat.

Configurable Stop Word (0.7.3)

The stop word is now configurable in Voice Configuration:

Option Description
Built-in DSP "Stop" Default — no model, no sensitivity slider, zero inference cost
Any wake-engine model Use any model from the current wake engine as the stop word, with its own sensitivity
Off Disable stop detection entirely

Path: Settings → Voice Config → Stop word

Timer Ring and Stop-Word Interaction

When a Home Assistant timer finishes, Ava enters timer-ringing mode:

  1. Ava ducks media audio and plays a timer-finished chime
  2. Stop-word detection activates (it is normally off during idle)
  3. Saying "stop" stops the timer ring
  4. The chime cycle is designed so the quiet gap between chimes is wide enough for a reactive "stop" to land in the quiet part — regardless of echo level
  5. When the timer is cancelled or stopped, media audio un-ducks and stop detection returns to its normal gating

Timer STARTED/UPDATED/CANCELLED events from HA do not affect the ring — HA drops a timer from its registry the moment it fires, so a cancel can only refer to a still-pending timer, never the one ringing.

Engine Switching

Each engine stores wake words independently (microWakeWords / openWakeWords). Switching engines auto-restores the last selection — no more lost models or silent failures. Cross-engine ID mapping: micro's okay_nabu auto-maps to open's ok_nabu. HA-configured wake words also resolve correctly across engines. Service auto-restarts on engine switch, keeping the detector in sync with settings.

Legacy migration: Settings previously stored as vsWakeWords are automatically read into openWakeWords. The serializer maps VS_WAKE_WORD to OPEN_WAKE_WORD. No user action needed.


Wake Word Library & Catalog

Ava ships with a built-in Wake Word Library — a complete model management system that lets you browse, download, import, and switch wake words without ever touching a desktop or editing APK files.

Two Tiers of Models

Tier What it is Where it shows up
Curated catalog Hand-picked models hosted in the Ava repository, tested and calibrated for Android Wake Word 1/2 picker — the dropdown you use to select a wake word
Community catalog Open community model collections, auto-synced from upstream sources Wake Word Library — browse, download, then set as active

The curated catalog is what you see in the wake-word picker. Community models live in the library — download one, then set it as your active wake word.

microWakeWord Community Catalog

Ava reads a live micro_community.json from the Ava repository, which indexes the microWakeWords V3 community library — over 100 pretrained models covering dozens of wake phrases in multiple languages.

Source Author Credit
TaterTotterson/microWakeWords Kevin Ahrendt, Michael Hansen, and community contributors The V3 training pipeline and community model library that Ava's micro catalog builds on
Bundled models Kevin Ahrendt, Michael Hansen, adamlonslide 9 models ship in the APK — Hey Jarvis, Alexa, Hey Mycroft, OK Nabu, Hey Home Assistant, Hey Luna, Hey Peppa Pig, Okay Computer, Choo Choo Homie

Thanks: The microWakeWord project by Kevin Ahrendt and the V3 community library maintained by Michael Hansen (TaterTotterson) made on-device wake-word detection practical for Home Assistant. Ava builds on their work.

OpenWakeWord Community Catalog

Ava reads a live open_community.json from the Ava repository, which indexes community-trained OpenWakeWord ONNX classifiers.

Source Author Credit
dscripka/openWakeWord David Scripka The original openWakeWord project — shared acoustic embedding architecture and training pipeline
fwartner/home-assistant-wakewords-collection Florian Wartner Home Assistant community wake-word collection, including the bundled ok_nabu model
Bundled model Florian Wartner (fwartner) ok_nabu ships in the APK — the only OpenWakeWord model bundled by default

Thanks: David Scripka created the openWakeWord architecture. Florian Wartner collected and trained the Home Assistant community models. Ava's OpenWakeWord engine is built on their work.

What Makes Ava's Wake Word Engine Stronger Than the Upstream

The upstream openWakeWord project runs on desktop Python. Ava ported the entire inference path to native C++ on Android and added several layers that the upstream does not have:

Capability Upstream openWakeWord Ava Pro
Runtime Python on desktop Native C++ on Android 5+
VAD compute gate No Neural VAD gates the embedding — silent frames skip ~96% of compute
Echo self-trigger protection No Offline burst-scores playback reference; suppresses wake when the speaker itself triggers the keyword
Consecutive-hit gate Single threshold Requires consecutive frames above threshold — rejects single-frame spikes that a raw threshold cannot
Built-in verifier support Partial Detects models with their own second-stage verifier and adjusts the hit gate accordingly
Glue-rescue head No Optional sidecar classifier that rescues wake words spoken in one breath with the command
Adaptive idle scheduling No After confirmed silence, expensive inference pauses within 150–250 ms — idle CPU drops to near zero
Multi-keyword efficiency One embedding pass per chunk Same — 1 keyword or 8 keywords, the embedding runs once
Mirror racing for downloads No zh/ru locales race GitHub proxy mirrors concurrently; first success wins and sticks for subsequent downloads
SHA-256 verification No Every downloaded model is SHA-256 verified against the catalog entry

Imagine a user named Riley who tried running upstream openWakeWord on a Raspberry Pi: it works, but it needs Python, a virtual environment, and constant CPU. On Ava, the same models run on a $30 Android tablet from 2018, with VAD gating, echo protection, and idle scheduling — things the upstream simply does not offer.

Import Your Own Models

Beyond the catalogs, you can import your own trained models directly:

  • microWakeWord: .tflite + .json pair (V2/V3 format)
  • OpenWakeWord: .onnx + .json pair (openwakeword-v1 format), or just an .onnx file (Ava auto-generates the manifest)
  • ZIP import: Pack multiple models in one ZIP — Ava unpacks and installs them in batch

See Custom Wake Words for the full import walkthrough.

Path: Settings → Voice Config → Wake Word Library

How to Change Wake Word

  1. Open Ava app
  2. Go to Settings -> Voice Config
  3. Find Wake Word Engine and choose microWakeWord or OpenWakeWord
  4. Find Wake Word 1 option
  5. Select your preferred wake word from the list
  6. Optionally configure Wake Word 2 for dual wake word mode
  7. New wake word takes effect after service restart (auto)

Wake Word Sensitivity

Adjust sensitivity to control how easily the wake word triggers:

  • Higher sensitivity = easier to trigger, but more false positives
  • Lower sensitivity = fewer false positives, but may miss quiet speech

OpenWakeWord sensitivity range: The slider is bounded by the model's calibration. For ok_nabu (manifest threshold 0.5), the slider ranges from 0.15 to 0.80. Above 0.80 the classifier no longer has headroom — ok_nabu measured 6/8 at 0.80, 5/8 at 0.87, 0/8 at 0.92. An out-of-reach strict request clamps down to the ceiling, never falls back to the manifest default.

Wake Sound

Each wake word can have its own wake sound:

  • Wake Word 1 Sound: Played when Wake Word 1 is detected
  • Wake Word 2 Sound: Played when Wake Word 2 is detected
  • Default Sound: Used if no custom sound is set
  • None: Silent recording start

Wake Visual Feedback

Ava provides clear visual feedback during wake and conversation:

Wake Instant:

  • Colorful ripple expanding from screen center
  • Android 13+: RuntimeShader with distorted halo + star particles
  • Android 7: Soft circular diffusion
  • Compatibility paths for other versions

Conversation (when Floating Subtitle is disabled):

  • Full-screen edge glow that changes with state:
    • Listening: Edge light breathes with microphone volume
    • Processing: Slow breathing animation
    • Speaking: Pulsates with TTS energy

Dual Wake-Word Color Coding:

  • Wake Word 1 = green (default), Wake Word 2 = blue (default)
  • Ripple and edge light match the triggered wake word
  • Custom colors available in Settings → Extensions → Interface → Voice feedback colors
  • 7 rainbow presets (red through purple) also available

Technical Notes:

  • Edge glow uses pre-rendered Gaussian blur bitmaps for performance
  • Ripple animation driven by system uptime (prevents Kiosk devices with "animation duration = 0" from killing the effect)
  • Android 7.0/7.1 optimized to clean circular diffusion without Shader dependency

Wake Continuity (0.7.3)

Three improvements that make wake feel instant and never drops a sentence.

Pre-roll Capture

The moment a wake fires, Ava starts buffering microphone frames — before the wake sound plays, before Chorus Wake arbitration, before the uplink opens. When the uplink finally opens, those buffered frames are spliced to the queue head. Home Assistant hears your voice as the first frame, not the wake-sound tail.

Imagine a user named Jordan who says "Hey Nabu, turn on the kitchen lights" in one breath. Without pre-roll, HA would miss "turn on the kitchen lights" because the uplink wasn't open yet. With pre-roll, the entire phrase is captured and delivered.

Continuous Conversation

In continuous conversation mode, after TTS finishes a continue chime plays and the satellite re-enters listening with the same HA conversation ID — no wake word needed. You can issue command after command in a natural flow.

TTS Echo Self-trigger Protection

When the assistant's response text contains the wake phrase (e.g. "I've set the Nabu timer"), wake detection is held for the estimated TTS duration so the assistant cannot wake itself. Wake and stop channels are separated — a response mentioning the stop phrase does not lock wake, and a response mentioning the wake phrase does not lock stop.


Performance Benchmarks

Ava's wake-word engines were benchmarked against a deliberately difficult stress corpus and Sherpa-ONNX (a third-party ONNX speech recognition toolkit).

Test Corpus

Property Value
Total clips 1,056
Positive clips 864
Negative clips 192
Speakers 12
Locales 11
Speech styles Isolated and glued
Noise conditions Clean, pink, babble, brown, white, room, room+pink, narrowband
Noise levels ~5–20 dB SNR

Results

Engine / Configuration Recall False-fire rate
microWakeWord default (c=0.79, w=5) 38.3% 2.08%
microWakeWord sensitive (c=0.50, w=1) 56.7% 7.29%
OpenWakeWord default 24.0% 6.25%
OpenWakeWord strict 15.3% 1.56%
Sherpa-ONNX 4.3% 0.00%

Grouped Results

Condition microWakeWord OpenWakeWord Sherpa-ONNX
Clean isolated 94.4% 38.9% 19.4%
Noisy isolated 56.6% 26.8% 3.0%
Glued speech 16.9% 20.1% 4.2%
Isolated speech 59.7% 27.8% 4.4%

OpenWakeWord Host Throughput

OpenWakeWord's shared embedding architecture keeps per-keyword cost flat. On a modern host, a single keyword processes a 40 ms chunk in under 1 ms — roughly 85× realtime. Adding more keywords adds only the tiny classifier head cost, not another full inference path. Even with 8 active keywords, the engine stays well above 15× realtime.

Caution: These are repository-host and stress-corpus measurements, not universal Android-device guarantees. The corpus is deliberately difficult. Sherpa-ONNX's zero false-fire rate reflects the selected threshold/test setup, not a universal statement about the entire Sherpa-ONNX ecosystem.


Back to Voice Control

Clone this wiki locally