Repository navigation
perf: keep openWakeWord's raw audio in a numpy buffer - #32
Jacob Kaplan (qwertey6) wants to merge 1 commit into
Conversation
|
For security reasons, this PR must be labeled with |
|
Hey Jacob Kaplan (@qwertey6) — CI is green and no reviewer is assigned yet. Could you request one when you have a chance? Auto-comment from overwatch. Will not re-nudge for 7 days. |
|
Ale Paredes (@ale7714) / John Morales (@john-morales) when one of you gets a chance! |
openwakeword 0.6.0's AudioFeatures keeps the last 10 s of audio in a deque(maxlen=160000) of Python ints. Every 80 ms chunk it converts the new samples to a list to append them, then copies the whole deque into a list to take the last 1,760 samples for the melspectrogram. That copy was about half of the time openWakeWord spends on a chunk. With openwakeword 0.6.0, setup_oww now replaces the deque with _AudioTail, an int16 numpy buffer whose newest samples are a slice, along with the two methods that write and read it. The melspectrogram gets the same int16 samples it got before, so the features and scores are bit-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6072ca3 to
39b6f01
Compare
|
Jacob Kaplan (@qwertey6) could you try this alternative #34 on the pi? I'd prefer not to re-implement the internals of a library in the module and #34 gets you close enough to this gains (measured on my mac) with a much smaller change. |
|
Jacob Kaplan (@qwertey6) also i think your changes make sense but we should try to push them directly on the library upstream. |
|
Ale Paredes (@ale7714) agreed, upstream is the right home. I opened dscripka/openWakeWord#356, which is smaller than either PR here: openWakeWord hasn't merged a PR since October 2025, so #34 is a sensible way to get most of the saving now. I've built an arm64 benchmark of stock, #32 and #34 with speex on, frozen with the module's toolchain, for our CM5 kiosks. None of my test kiosks is reachable at the moment, so I don't have Pi numbers for #34 yet. I'll post them here and close #32 if #34 holds up. |
|
Ale Paredes (@ale7714) a correction to my last comment: upstream isn't a realistic home. openWakeWord's last commit was 2025-12-30, and its last release, 0.6.0, was February 2024. So even a merged #356 would never reach PyPI, and the module will keep running 0.6.0 as it is. I'll leave #356 open as a record for anyone else hitting this. So the fix lives in this module, as #32 or #34. Both are pinned to 0.6.0 by a version test, and since the library isn't changing, that pin is unlikely to break. I'll post CM5 numbers for both as soon as one of our kiosks is reachable. If #34 keeps most of the saving there, I'll close #32 in its favour. Longer term, if Viam wants fixes like this in the library itself, a Viam-owned fork of openWakeWord would be the place. |
|
Jacob Kaplan (@qwertey6) let me know how your test in the pi works with #34. we would prefer to keep the change small for now. |
|
Jacob Kaplan (@qwertey6) have you had a chance to test #34? |
|
Hey Jacob Kaplan (@qwertey6) — CI is green and no reviewer is assigned yet. Could you request one when you have a chance? Auto-comment from overwatch. Will not re-nudge for 7 days. |
2 similar comments
|
Hey Jacob Kaplan (@qwertey6) — CI is green and no reviewer is assigned yet. Could you request one when you have a chance? Auto-comment from overwatch. Will not re-nudge for 7 days. |
|
Hey Jacob Kaplan (@qwertey6) — CI is green and no reviewer is assigned yet. Could you request one when you have a chance? Auto-comment from overwatch. Will not re-nudge for 7 days. |
Summary
openwakeword 0.6.0 keeps the last 10 s of audio in a
deque(maxlen=160000)of Python ints. On every 80 ms chunk,AudioFeatures:_buffer_raw_data);_streaming_melspectrogram:list(self.raw_data_buffer)[-n_samples-160*3:]).That copy was about half of openWakeWord's per-chunk time.
setup_owwnow replaces the preprocessor's deque with_AudioTail, an int16 numpy buffer whose newest samples are a slice. It also replaces the two methods that write and read it.deque; other versions keep their own code.Model.reset()still works.Bit-identical
Every per-chunk score, plus the melspectrogram and feature buffers, matches stock openwakeword 0.6.0 exactly with real models (a custom wake-word model plus openWakeWord's bundled feature models):
Model.reset()calls mid-stream;Measured on a Raspberry Pi CM5
openWakeWord
predict()per 80 ms chunk: the module's frozen toolchain, pinned to one core, the same 60 s of audio. Two runs of four interleaved stock/fix rounds; scores identical in every round.Run-to-run spread is under 0.2 ms.
The whole module on the same device: base and fixed builds from the same toolchain, swapped in and out with interleaved 60 s windows. The device ran its normal workload, so these numbers are noisier.
Tests
_AudioTailagainst adeque;setup_owwinstalling it;ruff checkandruff format --checkare clean.🤖 Generated with Claude Code