perf(desktop): coalesce wake.feed frames

Sending one 80 ms frame per RPC is ~12.5 gateway calls/s for as long as the
ear is armed. Drain up to 4 queued frames into a single wake.feed payload
(backend feed() already splits long buffers into engine frames) — ~3 RPCs/s
steady-state. Fix the wake.feed size-cap comment (64000 bytes = 2 s, not
0.5 s).
This commit is contained in:
Brooklyn Nicholson 2026-08-05 10:06:49 -06:00
parent 60808dcf72
commit 6df3912e0a
2 changed files with 9 additions and 4 deletions

View File

@ -113,6 +113,9 @@ export async function startClientWakeCapture(
// frames so the detector still sees contiguous recent PCM rather than gaps
// from fire-and-forget discard-while-inflight.
const MAX_QUEUED_FRAMES = 24 // ~1.9s at 80 ms/frame
// Coalesce queued frames into one wake.feed call (backend splits them back
// into engine frames). 4 × 80 ms ≈ 3 RPCs/s steady-state instead of 12.5.
const MAX_FRAMES_PER_FEED = 4
const queue: Float32Array[] = []
let draining = false
@ -123,12 +126,14 @@ export async function startClientWakeCapture(
draining = true
try {
while (!stopped && queue.length > 0) {
const frame = queue.shift()
if (!frame) {
const batch = queue.splice(0, MAX_FRAMES_PER_FEED)
if (batch.length === 0) {
break
}
try {
const pcm = floatToInt16LE(frame)
const merged = new Float32Array(batch.length * frameLength)
batch.forEach((frame, i) => merged.set(frame, i * frameLength))
const pcm = floatToInt16LE(merged)
await options.request('wake.feed', {
pcm: bytesToBase64(pcm),
sample_rate: TARGET_RATE

View File

@ -13400,7 +13400,7 @@ def _(rid, params: dict) -> dict:
return _err(rid, 4001, f"invalid base64 pcm: {e}")
if not pcm:
return _ok(rid, {"fed": False, "reason": "empty"})
# Soft size cap (~0.5s of 16kHz int16 mono = 16000 bytes)
# Soft size cap: 64000 bytes = 2s of 16 kHz int16 mono
if len(pcm) > 64000:
return _err(rid, 4001, "pcm frame too large")
sr = params.get("sample_rate")