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
This commit is contained in:
@@ -0,0 +1,405 @@
|
||||
# XM5 dictation startup: investigation handoff
|
||||
|
||||
Date: 2026-09-09. Status: **fixed and installed** (see the resolution section
|
||||
below). The older sections are kept as the investigation record.
|
||||
|
||||
## Resolution, 2026-09-09 afternoon session
|
||||
|
||||
Three independent faults were reproduced, fixed and re-measured with
|
||||
`tools/trial2.py` (kernel kprobes with the `mono` trace clock, the daemon's own
|
||||
level stream, the card profile before/during/after each take). Everything below
|
||||
is installed on this machine; nothing was submitted upstream.
|
||||
|
||||
### 1. Silent first start: btusb alternate-setting bug (patch `0002`)
|
||||
|
||||
Reproduced on the first stock attempt (`tools/stock-repro-1.log`): eSCO
|
||||
`connect()` at 0.76 s stuck behind two pages, `btusb_switch_alt_setting(0)` and
|
||||
the isochronous submit failure at 0.95 s, link up at 6.19 s with **zero** SCO
|
||||
URBs for its whole life. With the patched module the exact same race
|
||||
(`tools/patched-repro-3.log`: eSCO request stalled 3 s behind a page timeout,
|
||||
`HCI_NOTIFY_CONN_DEL` ignored, alt setting 6 programmed on
|
||||
`ENABLE_SCO_TRANSP`) delivered audio.
|
||||
|
||||
Installed: `/usr/lib/modules/7.2.3-arch1-3/updates/btusb.ko` (+ `depmod`);
|
||||
`modinfo -n btusb` resolves there. btusb is not in the UKI, so no initramfs
|
||||
rebuild was needed. A kernel upgrade replaces the whole modules directory and
|
||||
silently returns to the stock driver; rebuild `tools/btusb.c` against the new
|
||||
kernel and reinstall. `/etc/modprobe.d` needs nothing.
|
||||
|
||||
The trigger was `~/.config/wireplumber/wireplumber.conf.d/bluetooth-a2dp-autoconnect.conf`
|
||||
matching every card. It is now narrowed to `bluez_card.88_C9_E8_A7_EC_7E`.
|
||||
Disabling it entirely was tried first and is wrong: after a WirePlumber restart
|
||||
the XM5's A2DP profile does not come back on its own (the card offered only
|
||||
`headset-head-unit`), so the XM5 needs the rule. Absent devices are no longer
|
||||
paged (`journalctl -u bluetooth` shows no "Host is down" after a restart).
|
||||
|
||||
### 2. Autoswitch delay: 50 ms script now in effect
|
||||
|
||||
`~/.local/share/wireplumber/scripts/device/autoswitch-bluetooth-profile.lua`
|
||||
(the packaged script with `PROFILE_SWITCH_TIMEOUT_MSEC = 50`). eSCO connect
|
||||
moved from 0.65–0.85 s to 0.26–0.49 s after the start command. The
|
||||
ineffective copy under `~/.config/wireplumber/scripts/device/` is still there
|
||||
(this session could not delete it); it is harmless.
|
||||
|
||||
### 3. PipeWire: stale error replay and a hangup counted as failure (patches `0001`, `0003`)
|
||||
|
||||
With 1 and 2 in place, 1 take in 3 still came up silent with the stock plugin:
|
||||
`bluez_input ... running -> error` right at acquire, the `0001` replay
|
||||
(`tools/normal-5.log`, journal 11:26:46). `0001` is a real, independent bug,
|
||||
not a symptom of the paging.
|
||||
|
||||
With `0001` alone, the 4th of 4 quick takes failed with
|
||||
`spa.audioadapter: can't send command 2: Input/output error` and no eSCO
|
||||
request at all (`tools/pw-4.log`). Cause: every take ends with
|
||||
`Failure in Bluetooth audio transport .../fd60` because `media-sink.c`
|
||||
converts the SCO hangup (the headset drops SCO when the profile goes back to
|
||||
A2DP) into `SPA_BT_TRANSPORT_STATE_ERROR`, and `spa_bt_transport_acquire()`
|
||||
returns `-EIO` once `error_count >= 3` within a 6 s window
|
||||
(`TRANSPORT_ERROR_TIMEOUT = 2 * BLUEZ_ACTION_RATE_MSEC`). Four takes each
|
||||
started within ~8 s of the previous one ending is enough. Patch `0003` skips
|
||||
that escalation for an HFP sink that already wrote data (sco-io only writes
|
||||
after the first packet came in, so this implies the link was really up).
|
||||
|
||||
Installed: `~/.local/lib/spa-0.2/bluez5/libspa-bluez5.so` (PipeWire 1.6.8 +
|
||||
`0001` + `0003`, built with `--prefix=/usr` so `bluez-hardware.conf` resolves
|
||||
to `/usr/share`), selected by
|
||||
`~/.config/systemd/user/{pipewire,wireplumber}.service.d/spa-plugin-dir.conf`
|
||||
(`SPA_PLUGIN_DIR=/home/tank/.local/lib/spa-0.2:/usr/lib/spa-0.2`). Codec
|
||||
plugins still come from `/usr/lib/spa-0.2/bluez5`. WirePlumber is the process
|
||||
that maps the plugin. After a PipeWire upgrade rebuild against the new source
|
||||
(the build needs `gdbus-codegen`, extracted from `glib2-devel` without
|
||||
installing it, and a `gio-2.0.pc` copy pointing at it) or delete the two
|
||||
drop-ins to fall back to the packaged plugin. A copy of the built plugin is in
|
||||
`tools/libspa-bluez5.so`.
|
||||
|
||||
Result: 8 of 8 rapid takes (`tools/pw2-*.log`) and 6 more after that with no
|
||||
transport failure, no start error, A2DP restored 2 s after every take.
|
||||
|
||||
### 4. Honest "Listening" indicator (`daemon/sttd.py`)
|
||||
|
||||
The 40-chunk fallback lit the indicator at 2.1 s on a dead microphone. The
|
||||
level stream showed why a plain "first nonzero chunk" test is wrong too: the
|
||||
virtual mic emits one stray nonzero chunk at ~0.15 s (level 0.02, processing
|
||||
residue), then exact zeros until the headset floor ramps in from a few LSB
|
||||
with the odd all-zero chunk (`tools/win-*.log`). The daemon now turns
|
||||
`listening` on at RMS > 0.0005 or after three consecutive nonzero chunks, and
|
||||
never on a timer. Measured: 1.19–1.43 s after the start command, 0–0.14 s
|
||||
after the first nonzero chunk (`tools/ind2-*.log`).
|
||||
|
||||
### Timeline now (normal path, quiet room)
|
||||
|
||||
| after F13 | event |
|
||||
|---|---|
|
||||
| 0.26–0.49 s | eSCO link up |
|
||||
| 1.06–1.43 s | first nonzero PCM at the daemon |
|
||||
| 1.19–1.43 s | UI "Listening" |
|
||||
|
||||
The 0.7–0.9 s between link-up and first PCM is the headset's own silence plus
|
||||
the Camera Effects path; see the earlier analysis. The remaining Linux-side
|
||||
gain would be requesting the headset profile from the daemon at capture start
|
||||
(~0.25 s), not done: the autoswitch script only restores a profile it switched
|
||||
itself, so a daemon-side switch would leave the headset in mono after the take.
|
||||
|
||||
### Not fixable
|
||||
|
||||
Stereo playback while the headset microphone is in use: A2DP is one-way and
|
||||
the WH-1000XM5 has no LE Audio. Every OS drops to mono HFP for its mic.
|
||||
|
||||
### Files added this session
|
||||
|
||||
`0003-media-sink-hfp-hangup-after-audio-is-not-a-failure.patch`,
|
||||
`tools/trial2.py` (trial driver, replaces `trial.sh`, which needed a `/tmp`
|
||||
script that is gone), `tools/libspa-bluez5.so`, `tools/*.log`.
|
||||
`tools/kernel-probes.sh` now disables the events before rewriting them, so it
|
||||
can be re-run after a module reload.
|
||||
|
||||
---
|
||||
|
||||
## Earlier record (superseded where the resolution above says so)
|
||||
|
||||
## Update, 2026-09-09 later session: silent first start root-caused
|
||||
|
||||
Status: **the "first microphone start gets no audio at all" failure is fully
|
||||
explained and reproduced under kernel tracing; a kernel driver patch is written and
|
||||
built but not loaded. The remaining startup delay is measured and its floor is set
|
||||
by the headset, not by Linux.**
|
||||
|
||||
### The silent start is a btusb driver bug, triggered by paging absent devices
|
||||
|
||||
Reproduced twice out of two attempts by restarting WirePlumber and starting
|
||||
dictation about 10 s later. Kernel event order (kprobes, see `tools/`):
|
||||
|
||||
1. PipeWire `connect()`s the mSBC SCO socket: `hci_conn_add_unset(ESCO)` puts the
|
||||
pending link in the connection hash (counted by `hci_conn_num(SCO_LINK)`), and
|
||||
the Enhanced Setup Synchronous Connection command is queued.
|
||||
2. The command does **not** go out for 2.3 s: bluetoothd is paging another paired
|
||||
device at that moment, and the kernel's synchronous Create Connection request
|
||||
holds the HCI command queue until its Connect Complete arrives (page timeout,
|
||||
~5.1 s). Any dictation start that overlaps such a page stalls the same way.
|
||||
3. Connect Complete arrives with status 0x04 (Page Timeout) for that other device;
|
||||
its `hci_conn_del()` notifies btusb with `HCI_NOTIFY_CONN_DEL`.
|
||||
4. `btusb_notify()` only compares `hci_conn_num(SCO_LINK)` (now 1, the pending
|
||||
link) with its `data->sco_num` (0), stores CONN_DEL as the "air mode" and runs
|
||||
`btusb_work()`, which computes `new_alts = 0` and submits isochronous URBs on the
|
||||
alternate-setting-0 endpoints (wMaxPacketSize 0). `usb_submit_urb()` returns
|
||||
-EMSGSIZE: that is the `urb ... submission failed (90)` line. The `len 0 mtu 0`
|
||||
debug print from `__fill_isoc_descriptor` confirms it.
|
||||
5. 0.14 s later the eSCO link comes up (Synchronous Connect Complete, air mode
|
||||
transparent) and `HCI_NOTIFY_ENABLE_SCO_TRANSP` is sent, but the count already
|
||||
matches `data->sco_num`, so nothing is scheduled. The interface stays on
|
||||
alternate setting 0 for the life of the link: no SCO URBs, no audio either way.
|
||||
PipeWire waits for the first incoming packet (USB adapters) forever, the daemon
|
||||
sees silence, and "Listening" appears only from the 40-chunk fallback.
|
||||
|
||||
All four historical `submission failed (90)` entries (00:49, 00:53, 01:39, 02:17)
|
||||
sit 2–5 s after a WirePlumber restart, and the "physical HFP nodes enter error"
|
||||
failures in the stock trials are the same event seen from PipeWire (the pending
|
||||
SCO connect fails with the paging device's status). The `0001` PipeWire patch
|
||||
therefore addresses a symptom of this, not an independent bug.
|
||||
|
||||
Why a page was in flight: the user's own
|
||||
`~/.config/wireplumber/wireplumber.conf.d/bluetooth-a2dp-autoconnect.conf` sets
|
||||
`bluez5.auto-connect = [ a2dp_sink a2dp_source ]`. On every WirePlumber start
|
||||
PipeWire calls `ConnectProfile()` on every paired A2DP device that is not connected
|
||||
("Nothing Ear (3)" and "RB Meta 042Q" here), bluetoothd pages each for 5.1 s, and
|
||||
the whole HCI command queue is blocked while it does. In normal use that is the
|
||||
first ~11 s after login or after any WirePlumber restart; every absent paired
|
||||
device extends it by ~5 s. Dropping that rule, or narrowing its match to the
|
||||
devices it was written for, removes the trigger. The btusb bug itself fires on
|
||||
any non-SCO connection add/remove that lands while an eSCO link is pending (an
|
||||
LE device connecting, another ACL dropping), so it stays worth fixing.
|
||||
|
||||
Fix: `0002-btusb-program-isoc-alt-setting-on-every-sco-enable.patch` changes
|
||||
`btusb_notify()` to program the interface on every `ENABLE_SCO_*` notification and
|
||||
to ignore count increases from other events. `tools/btusb.ko` is that patch built
|
||||
against the exact stable 7.2.3 source (identical to the local driver source) with
|
||||
the installed headers, vermagic `7.2.3-arch1-3 SMP preempt mod_unload`. It is
|
||||
**not loaded**; test with `sudo rmmod btusb && sudo insmod tools/btusb.ko`
|
||||
(headset reconnects), revert with `sudo rmmod btusb && sudo modprobe btusb`.
|
||||
Not submitted upstream.
|
||||
|
||||
### Normal-path timing, stock, ACL in sniff mode (trace of 02:49:34.93)
|
||||
|
||||
| after F13 | event |
|
||||
|---|---|
|
||||
| 0.01 s | daemon recording, pw-record running |
|
||||
| 0.28 s | Camera Effects helper stream running, autoswitch script triggered |
|
||||
| 0.79 s | autoswitch 500 ms timer fires, profile set to headset-head-unit |
|
||||
| 0.82 s | SCO `connect()`; Exit Sniff Mode, Mode Change 65 ms later |
|
||||
| 1.01 s | eSCO up (Enhanced Setup Sync took 117 ms); first SCO packet 19 ms later |
|
||||
| ~1.2 s | first decoded PCM, all zeros (earlier instrumented runs) |
|
||||
| 1.61 s | first nonzero input at Camera Effects; 1.72 s first nonzero daemon level |
|
||||
| 2.07 s | UI "Listening" (40-chunk fallback; room noise is below the RMS threshold) |
|
||||
|
||||
The 0.5–0.6 s between eSCO link-up and the first nonzero sample is the headset:
|
||||
it sends encoded digital silence for roughly 300 ms after the link opens, plus
|
||||
mSBC sync. Linux cannot shorten it. What Linux can shorten:
|
||||
|
||||
- The 500 ms autoswitch timer (`PROFILE_SWITCH_TIMEOUT_MSEC` in
|
||||
`/usr/share/wireplumber/scripts/device/autoswitch-bluetooth-profile.lua`):
|
||||
~0.45 s. A copy with 50 ms placed at
|
||||
`~/.config/wireplumber/scripts/device/autoswitch-bluetooth-profile.lua` was
|
||||
**not** picked up (measured: still 500 ms). It is still there; remove it, or move
|
||||
it to `~/.local/share/wireplumber/scripts/device/` (where the user's other
|
||||
script lives) and re-measure the gap between `Triggering profile switch` and
|
||||
`Switching profile` in the WirePlumber journal at info level.
|
||||
- The ~0.27 s before the profile switch is even requested (Camera Effects 250 ms
|
||||
maintenance poll opening the helper stream, then WirePlumber noticing it).
|
||||
Requesting the headset profile directly when capture starts would remove most
|
||||
of it.
|
||||
|
||||
With both, eSCO link-up lands near 0.3–0.35 s and first real audio near 0.9 s.
|
||||
That is the floor with this headset. Words spoken before ~0.9 s are lost unless
|
||||
the microphone is already in HFP, which the user has rejected; the honest
|
||||
alternative is an indicator that turns on only when nonzero physical input
|
||||
arrives, which the current 0.0005 RMS threshold and 40-chunk fallback do not give
|
||||
(quiet-room noise measured RMS ~1e-4, Camera Effects residue ~4e-5).
|
||||
|
||||
### Stereo playback while the microphone is used
|
||||
|
||||
Not possible with this headset on any OS. A2DP is one-way; the microphone
|
||||
needs an HFP SCO link, and the WH-1000XM5 has no LE Audio. Windows and macOS drop
|
||||
to the same mono hands-free profile when its microphone is used.
|
||||
|
||||
### State after this session
|
||||
|
||||
Stock btusb loaded (srcversion matches the packaged module), all kprobes and
|
||||
dynamic-debug prints removed, no btmon running, WirePlumber restarted with default
|
||||
logging and `WIREPLUMBER_DEBUG` unset, dictation idle, warm mic off. The only
|
||||
persistent change is the ineffective script copy under
|
||||
`~/.config/wireplumber/scripts/device/`. `tools/` holds the probe installer
|
||||
(`kernel-probes.sh`, `off` to remove), the trial driver (`trial.sh`, needs
|
||||
`/tmp/stt-startup-trace.py`), the restart-and-retry reproducer (`repro-loop.sh`),
|
||||
and the patched driver source and module.
|
||||
|
||||
## Required outcome
|
||||
|
||||
F13 dictation must use the Sony WH-1000XM5's own microphone, start immediately
|
||||
without losing opening words, and preserve headphone playback quality. The user
|
||||
rejects substituting the webcam microphone and keeping HFP/mic capture permanently
|
||||
open. Do not compare this with Deadlock. Mac/Windows microphone selection was not
|
||||
verified; do not assume that comparison establishes an identical Bluetooth path.
|
||||
|
||||
## Verified state at handoff
|
||||
|
||||
- Stock WirePlumber is active/running, with no service drop-ins and no test SPA or
|
||||
data-directory environment overrides.
|
||||
- Camera Effects explicitly selects `bluez_input.88:C9:E8:A7:EC:7E` (WH-1000XM5).
|
||||
- Speech-to-text is idle, warm capture is false, Camera Effects is not capturing.
|
||||
- No btmon, pkexec, startup trace, or patched-test process remains running.
|
||||
- The earlier unwanted webcam-microphone substitution was reverted.
|
||||
- No kernel/module replacement, system package installation, permanent candidate
|
||||
PipeWire installation, or upstream submission was done.
|
||||
- Repository status is only untracked `diagnostics/`; STT runtime source was not
|
||||
changed by this investigation.
|
||||
|
||||
## Actual application path
|
||||
|
||||
`F13 -> speech-to-text/bin/stt -> daemon/sttd.py -> pw-record -> camera-effects-mic`
|
||||
|
||||
VoxType is the transcription engine here, not the ordinary VoxType microphone
|
||||
daemon. Testing its separate CLI does not reproduce the F13 startup path.
|
||||
|
||||
Camera Effects creates `camera-effects-mic-input`, which links through the
|
||||
WirePlumber Bluetooth loopback to the physical HFP source. A helper capture named
|
||||
`camera-effects-mic-headset` triggers automatic profile switching. The user's
|
||||
Camera Effects permission rule hides raw microphones from unrelated clients;
|
||||
do not bypass that rule by impersonating an allowed client.
|
||||
|
||||
STT requests raw S16, 16 kHz, mono audio in 50 ms chunks. Its `listening` flag
|
||||
becomes true when RMS exceeds 0.0005, or after 40 chunks (two seconds of PCM).
|
||||
This flag controls UI readiness, not recording commencement: preceding chunks
|
||||
are already collected. Therefore quiet input can show a two-second delay, and
|
||||
silence from a disconnected microphone can falsely become "Listening."
|
||||
|
||||
Camera Effects maintenance polls every 250 ms. Its virtual stream supplies
|
||||
silence before the physical mic is ready and may initially contain residual
|
||||
buffered audio. First virtual buffers or early virtual nonzero samples are not
|
||||
reliable proof that current headset speech is being captured. Read-only input
|
||||
metering and instrumentation at the Bluetooth decoder are more meaningful.
|
||||
|
||||
## Measurements
|
||||
|
||||
Tests invoked the same daemon start command used by F13, without keyboard
|
||||
dispatch. Six-second takes were cancelled, not pasted or saved as completed
|
||||
history. Live transcription may nevertheless have processed data during them.
|
||||
|
||||
Stock observations:
|
||||
|
||||
- Daemon recording state begins in roughly 12–20 ms; virtual PCM around 160 ms.
|
||||
- WirePlumber intentionally waits 500 ms before a headset-profile switch and
|
||||
restarts that timer on further relevant graph events. Restoration waits 2 s.
|
||||
- Profile change generally starts around 0.6–0.9 s.
|
||||
- HCI synchronous connection completion was around 0.87–1.02 s. That is not
|
||||
equivalent to microphone audio being ready.
|
||||
- Some trials fail entirely: physical HFP nodes enter error, or usable audio
|
||||
never arrives. A stock failing trial stopped accumulating PCM at 0.8 s.
|
||||
- Kernel logs sometimes contain `Bluetooth: hci0: urb ... submission failed (90)`.
|
||||
Error 90 is EMSGSIZE. Which USB submission path causes it is not yet proven.
|
||||
|
||||
With the candidate PipeWire fix and temporary decoder instrumentation, two
|
||||
successful trials measured: incoming encoded packets at 0.97–0.99 s; first decoded
|
||||
PCM at 1.16–1.19 s; first nonzero decoded PCM at 1.48–1.50 s; nonzero PCM sent to
|
||||
the graph at 1.53–1.55 s; Camera Effects input signal around 1.62 s; UI readiness
|
||||
around 2.14 s. This narrows the delay but does not establish speech intelligibility.
|
||||
Initial zero packets versus codec synchronization versus headset startup remains
|
||||
unresolved. The H2 reader itself parses a 60-byte frame without an explicit timer.
|
||||
|
||||
The first trial after the instrumented WirePlumber restart received no encoded
|
||||
audio within six seconds, with a USB submission error around 4.46 s.
|
||||
|
||||
## Candidate actual PipeWire bug
|
||||
|
||||
An HFP transport can retain ERROR from an earlier use. `sco_acquire_cb()` starts
|
||||
an asynchronous connection without changing that stale state. A second node
|
||||
acquiring the same transport takes the reference-count branch in
|
||||
`spa_bt_transport_acquire()` and re-emits the old ERROR, even though the new
|
||||
connection subsequently succeeds.
|
||||
|
||||
Observed debug sequence: 01:32:25.430 acquire, .447 second acquire/error replay,
|
||||
.538 successful connection (BST). The candidate sets PENDING while the asynchronous
|
||||
acquire is in progress. It does not signal readiness early or suppress real errors.
|
||||
|
||||
Files in this directory:
|
||||
|
||||
- `0001-bluez5-mark-sco-acquire-pending.patch`
|
||||
- `test_sco_acquire.py`: extracts the actual acquire functions and compiles them
|
||||
with mocked I/O. Original source fails; patched source passes cold/retry
|
||||
concurrent acquire, delayed readiness, synchronous success, and real failures.
|
||||
|
||||
The Bluetooth component builds. No stale-error replay was observed in the three
|
||||
initial patched trials, but silent startup remained. This is a partial candidate,
|
||||
not proof of an end-to-end fix. The relevant upstream master acquire function
|
||||
inspected during the investigation matched 1.6.8; nothing was submitted upstream.
|
||||
|
||||
## Important integration-test confounds
|
||||
|
||||
The local Meson build used its default `/usr/local` prefix. A later log showed
|
||||
that the test plugin could not load the installed Bluetooth hardware-quirks file:
|
||||
it searched `/usr/local/share/spa-0.2/bluez5/bluez-hardware.conf`, while the file is
|
||||
at `/usr/share/spa-0.2/bluez5/bluez-hardware.conf`. Correct build prefix/data paths
|
||||
before attributing any patched-versus-stock behavior to the candidate patch.
|
||||
|
||||
A separate 50 ms WirePlumber-delay experiment previously removed roughly 450 ms
|
||||
but did not eliminate failures. The latest attempted combined test did not run:
|
||||
`WIREPLUMBER_DATA_DIR` pointed to a copied system data directory without the user's
|
||||
required `camera-effects-hide-mics.lua`. WirePlumber hit its restart limit. The
|
||||
override was removed and stock service restored/reset successfully. Do not count
|
||||
this attempt as latency data. Preserve user scripts when building another overlay.
|
||||
|
||||
## Machine and relevant paths
|
||||
|
||||
- PipeWire 1.6.8, WirePlumber 0.5.17, BlueZ 5.87, kernel 7.2.3-arch1-3.
|
||||
- Intel AX210 Bluetooth USB 8087:0032; device `/sys/bus/usb/devices/1-9`,
|
||||
isochronous interface `1-9:1.1`, full-speed USB.
|
||||
- Sony WH-1000XM5 address 88:C9:E8:A7:EC:7E.
|
||||
- Observed profiles: A2DP AAC/SBC/SBC-XQ and HFP CVSD/mSBC. No LE Audio profile
|
||||
observed. Idle playback is AAC stereo; mic use selects mSBC mono HFP.
|
||||
- Those observations do not establish that a Linux-only update can provide the
|
||||
headset mic plus unchanged stereo playback. That part of the requirement is
|
||||
unresolved; do not promise it as a consequence of reducing switching delay.
|
||||
- Camera Effects running binary: `/usr/local/lib/camera-effects/camera-effects-server`.
|
||||
- Camera Effects sources: `/home/tank/.config/omarchy/plugins/alanfortlink.camera-effects/daemon/src/`.
|
||||
- STT config: `/home/tank/.config/speech-to-text/config.json`.
|
||||
- Camera Effects config: `/home/tank/.config/camera-effects/config.json`.
|
||||
- WirePlumber user scripts include both `~/.config/wireplumber/scripts/` and
|
||||
`~/.local/share/wireplumber/scripts/camera-effects-hide-mics.lua`.
|
||||
|
||||
## Temporary working materials (may disappear after reboot)
|
||||
|
||||
Root: `/tmp/stt-bluetooth-switch.uZDCcW/`
|
||||
|
||||
- `pipewire-1.6.8/`: official source plus candidate backend patch and temporary
|
||||
`media-source.c` timing instrumentation. `build-test/` holds its Meson build.
|
||||
- `backend-native.c`, `bluez5-dbus.c`, `media-source.c`: original source copies.
|
||||
- `patched-spa/bluez5/libspa-bluez5.so`: experimental component; **not suitable
|
||||
for permanent installation** given the caveat above.
|
||||
- `build-tools.ini`, `build-tools/`: extracted matching glib2 development tool
|
||||
used for gdbus-codegen, without installing system packages.
|
||||
- `run-patched-test.sh`: temporary service override, three cancelled captures,
|
||||
automatic restoration. Correct its environment setup before reuse.
|
||||
- `autoswitch-bluetooth-profile.lua`: packaged script with 500 changed to 50 ms.
|
||||
- `filter-hci.py`: btmon metadata-only filter, drops audio/keys/addresses. Fixed
|
||||
SCO parser recognizes actual `> BR-ESCO:` RX and `< BR-ESCO:` TX headers.
|
||||
Earlier zero RX/TX counts from the incorrect parser were invalid evidence.
|
||||
- `btusb.c`: upstream v7.2 driver reference, not yet checked against all 7.2.3
|
||||
changes; `bluez-packet.c`: BlueZ 5.87 monitor formatting reference.
|
||||
- `/tmp/stt-startup-trace.py`: daemon start/cancel, graph and input-meter timing.
|
||||
|
||||
## Most useful remaining work
|
||||
|
||||
1. Correct the test build paths and repeat controlled stock/patched measurements.
|
||||
2. Trace the first-start USB failure. `sudo -n true` succeeded at the end of this
|
||||
investigation, so passwordless scoped tracing may now be available. Earlier
|
||||
pending pkexec/btmon authentication was cancelled; it is not running.
|
||||
3. Tracefs exists at `/sys/kernel/tracing`, accessible with sudo. No probes were
|
||||
installed. No perf/bpftrace/bpftool is installed; kernel headers and BTF exist.
|
||||
4. Determine which `usb_submit_urb` returns EMSGSIZE and the associated endpoint,
|
||||
alternate setting, and packet lengths. USB alt6 permits 63 bytes; SCO frames
|
||||
observed were 60 bytes. Kernel SCO_OPTIONS reports 96, which PipeWire already
|
||||
knows is unreliable for USB and avoids by waiting for incoming packet size.
|
||||
5. Investigate before changing that wait: native SCO waits for readable data on
|
||||
USB before declaring transport ready; sco-io also waits for RX before TX.
|
||||
No-RX startup could involve this interaction, the driver, or the headset.
|
||||
6. Treat quality preservation as a separate capability question, not a side
|
||||
effect of a latency patch. Do not restore webcam or warm-mic workarounds.
|
||||
Reference in New Issue
Block a user