Files
speech-to-text/diagnostics/bluetooth/README.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

114 lines
5.7 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.
# F13 Bluetooth startup investigation
Status: **fixed and installed on 2026-09-09** — three faults (btusb
alternate-setting bug, PipeWire stale-error replay, PipeWire counting the
end-of-take SCO hangup as a failure), the 500 ms autoswitch delay, and the
daemon's false "Listening" indicator. Stereo playback with the headset
microphone is not possible with this headset. Full account in the resolution
section at the top of [HANDOFF.md](HANDOFF.md).
## Reproduction
Environment: PipeWire 1.6.8, WirePlumber 0.5.17, BlueZ 5.87,
Linux 7.2.3-arch1-3, Intel AX210 USB Bluetooth, Sony WH-1000XM5.
Measurements taken on 2026-09-09, with the dictation microphone initially idle.
The F13 extension uses its Python daemon and `pw-record`, targeting
`camera-effects-mic`. VoxType performs transcription, not microphone startup.
Tests invoked the same daemon start command, omitting keyboard dispatch, and
cancelled the take without pasting or adding completed recordings to history.
Observed on the stock system:
- Recording state starts in roughly 1220 ms.
- The virtual microphone supplies buffers before the Bluetooth mic connects;
even an early nonzero level can be buffered data, not current microphone input.
- Successful HCI synchronous-connection completion was measured around
0.871.02 seconds after the start command. This does not establish when usable
speech first arrives.
- A failing trial accumulated 0.8 seconds of PCM, then stayed at that duration
through cancellation at six seconds. Both physical HFP nodes entered error.
- The UI also has a separate signal threshold / two-seconds-of-PCM fallback.
Its “Listening” indication is not proof of physical microphone readiness.
## Candidate PipeWire fix
An HFP transport can retain `SPA_BT_TRANSPORT_STATE_ERROR` from an earlier use.
`sco_acquire_cb()` starts an asynchronous connection but does not change that
state while waiting. A second node acquiring the same transport executes
`spa_bt_transport_acquire()`'s reference-count path, which re-emits the retained
error to both nodes. This can happen even though the new connection succeeds
shortly afterward.
The debug trace reproduced that sequence: acquire at 01:32:25.430,
second acquire and error re-emission at .447, successful completion at .538 BST.
`0001-bluez5-mark-sco-acquire-pending.patch` sets the state to `PENDING` during
the asynchronous acquire. It does not report readiness early, disable genuine
errors, change the Bluetooth codec, keep the microphone open, or shorten
WirePlumber's profile-switch delay. It applies to PipeWire 1.6.8; the corresponding
acquire function in the upstream master source inspected that day was unchanged.
## Validation and limits
`test_sco_acquire.py` compiles the actual two acquire functions with mocked I/O.
It fails against the unmodified source and passes with the patch. It covers
concurrent cold/retry acquisition, delayed readiness, synchronous success, and
real connection failures. It is a focused reproduction harness, not the full
PipeWire integration suite.
Run it against a PipeWire source checkout:
```sh
python3 diagnostics/bluetooth/test_sco_acquire.py \
/path/to/pipewire/spa/plugins/bluez5/backend-native.c \
/path/to/pipewire/spa/plugins/bluez5/bluez5-dbus.c
```
The patched Bluetooth component compiled and was loaded in WirePlumber using
a temporary, process-specific SPA plugin path. Across three cancelled startup
trials, the HFP error-state replay was not observed, but the first trial still
produced only silence. The kernel logged `urb ... submission failed (90)` during
that trial. The other trials produced signal but indicated readiness at about
2.2 seconds. Similar USB errors had also occurred in earlier unpatched tests;
their cause and relation to ordinary cold-start delay remain unproven.
The temporary service override was removed and the stock service restored.
No system packages, permanent microphone settings, or speech-to-text behavior
were changed. Nothing has been submitted upstream.
Further work must distinguish the USB/SCO first-start failure from the
error-state replay and measure current microphone input, not virtual-buffer
arrival or the UI indicator. This patch alone does not meet the requested
instant-start / full-quality experience.
## Later measurements and test-environment caveat
Temporary instrumentation in `media-source.c` measured two successful starts
with the candidate patch and the stock WirePlumber 500 ms switch delay:
- First encoded SCO data: 0.970.99 s after the start request.
- First decoded PCM: 1.161.19 s.
- First nonzero decoded PCM: 1.481.50 s.
- First nonzero PCM delivered to the graph: 1.531.55 s.
- Camera Effects input meter first nonzero: about 1.62 s.
- UI listening indication: about 2.14 s.
Nonzero samples establish signal, not intelligibility or preservation of opening
words. The first trial after restarting WirePlumber had no encoded data during
the six-second capture and logged a USB submission error at about 4.46 s.
A later combined test with a 50 ms WirePlumber delay did **not run**: the temporary
data-directory override omitted the user's required `camera-effects-hide-mics.lua`
script, so WirePlumber failed to start. The override was removed, the service's
restart limit reset, and the stock service successfully started again.
That startup log also exposed a build-environment confound: the locally built
PipeWire plugin searched `/usr/local/share/spa-0.2/bluez5/bluez-hardware.conf`,
whereas the installed quirks file is under `/usr/share/spa-0.2/bluez5/`.
Correct the build prefix/data paths before relying on patched-versus-stock
integration comparisons. The focused acquire-function regression test is
independent of this configuration problem.
See [HANDOFF.md](HANDOFF.md) for the complete investigation handoff.