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

2.8 KiB
Raw Blame History

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.

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).