Latency, three ways, all inside mic.rs:
- 48 kHz stereo 20 ms becomes mono 10 ms: speech gains nothing from a
second channel, the shorter frame shaves a buffering interval off the
uplink, and the host already decodes any Opus frame <= 120 ms with its
stereo decoder (mono packets upmix) — no protocol change. The encoder
follows: 48 kbps, complexity 5, in-band FEC at an assumed 10% loss so
a dropped datagram reconstructs from its successor instead of a hole.
- The latency ratchet is gone: the capture callback drops the NEWEST
chunk when the hand-off channel fills, so an encode-side stall used to
convert into standing mic delay that never drained. The encode loop
now drains the whole backlog in one lump and, past ~60 ms, jumps to
the newest ~20 ms (one audible blip, live again), counting what it
shed in the periodic log line. The realtime callback stays exactly as
allocation-free as it was.
- The encode thread registers with the client's hot-thread set, so the
ADPF session keeps mic encode on a fast core alongside audio decode.
No .frames_per_data_callback() pin: AAudio's own docs say leaving it
unset is the lowest-latency path (the callback runs at the device's
optimal burst), and the encode side re-chunks to 10 ms frames anyway.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>