Files
speech-to-text/diagnostics/bluetooth/STARTUP-TIME.md
T
alan 6f7f7ddd6c Bluetooth mic startup: honest Listening indicator, startup-time investigation
The bar turned green after 2 s on a timer even when the microphone delivered
nothing. It now turns green only on real audio: RMS above 0.0005, or three
consecutive chunks that are not digital silence (the virtual mic emits one
stray nonzero chunk right after start, and a Bluetooth headset's floor ramps
in from a few LSB). Measured 1.2-1.4 s after the key, within 0.14 s of the
first real samples.

diagnostics/bluetooth/ records how startup went from ~1.6 s (or never) to
~1.1-1.4 s: a btusb driver bug, two PipeWire bluez5 bugs, a 500 ms WirePlumber
switch timeout and an over-broad auto-connect rule. Those fixes are machine
level and live outside this repo; the patches, tools and measurements are here.
STARTUP-TIME.md is the summary and explains the ~0.9 s hardware floor.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xoc9DJCViR7dg9eKzzAcfQ
2026-09-09 11:58:16 +01:00

56 lines
2.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# How we cut Bluetooth microphone startup time
Measured on a Sony WH-1000XM5 over an Intel AX210, PipeWire 1.6.8, WirePlumber
0.5.17, kernel 7.2.3. Times are after the dictation key press.
| | before | after |
|---|---|---|
| mic link (eSCO) up | 0.71.0 s, sometimes never | 0.260.49 s |
| first real audio at the daemon | 1.51.7 s, or never | 1.061.43 s |
| "Listening" in the bar | 2.1 s on a timer, even with no audio | 1.191.43 s, only on real audio |
## What changed
| Change | Where it lives | Gain |
|---|---|---|
| Daemon indicator: green on real audio only (RMS > 0.0005 or three consecutive non-silent chunks), no timer | `daemon/sttd.py` | the bar cannot claim to listen to a dead mic |
| WirePlumber profile-switch timeout 500 ms → 50 ms | `~/.local/share/wireplumber/scripts/device/autoswitch-bluetooth-profile.lua` | link up ~0.45 s sooner |
| A2DP auto-connect rule narrowed to the XM5 | `~/.config/wireplumber/wireplumber.conf.d/bluetooth-a2dp-autoconnect.conf` | no 5 s pages of absent devices at login |
| btusb driver patch `0002` | `/usr/lib/modules/<kernel>/updates/btusb.ko` | mic link no longer comes up silent when a page overlaps it |
| PipeWire bluez5 patches `0001` + `0003` | `~/.local/lib/spa-0.2/bluez5/` via `SPA_PLUGIN_DIR` drop-ins | no stale-error dead takes; back-to-back takes work |
Only the first row is part of this repo. The rest are machine-level and are
described, with patches and measurements, in [HANDOFF.md](HANDOFF.md).
## Why ~0.9 s is the floor
```
key press
0.00 s ─┬─ daemon starts pw-record on camera-effects-mic
│ Camera Effects notices the client (250 ms poll),
│ opens its helper stream, WirePlumber waits 50 ms,
│ then asks BlueZ for the headset profile
0.30 s ─┼─ eSCO link up (the headset must leave sniff mode,
│ then the radio sets up the synchronous link: ~0.1 s)
│ headset sends encoded digital silence while its own
│ mic path starts and mSBC syncs: ~0.50.6 s
│ (measured at the decoder; Linux cannot shorten it)
0.85 s ─┼─ first non-zero samples leave the headset
│ Camera Effects processing + 50 ms chunking
0.90 s ─┴─ first real audio reaches the daemon
```
What is left on the Linux side is the ~0.25 s before the profile switch is
even requested. The daemon could ask for the headset profile itself when
capture starts, but the WirePlumber autoswitch script only restores a profile
it switched, so the daemon would have to restore A2DP too. Not done.
The headset's silence after link-up and the eSCO setup are hardware. Words
spoken in the first ~0.9 s are lost unless the microphone is already in
hands-free mode, which drops playback to mono, so it is off by default
(`warmMic`).