fix(ios): VPIO silent playback + quiet speaker in stereo/studio presets
Some checks failed
Build Linux Binaries / linux/amd64 (push) Has been cancelled
Build Linux Binaries / linux/arm64 (push) Has been cancelled

Two on-device bugs in the native iOS Voice-Processing path (Swift-only;
no core/ABI change).

1. Voice Chat (VPIO) silent playback: doStartMicStream() called
   audioRestart() BEFORE startStream, so when the engine was already
   running (a remote stream had started it) it reopened with
   external_capture=false and opened a hardware miniaudio capture device.
   The announce-result restart then early-returns (engine already running)
   so that device was never dropped and fought the AVAudioEngine VPIO unit,
   silencing playback. Now: setExternalPlayback first, then startStream
   (stores external_feed synchronously), THEN audioRestart() — the core
   reopens in full external mode (no hardware devices). Added VPIO
   diagnostics: graph/route formats at start, ring written/read totals at
   teardown.

2. Stereo Mic / Studio quiet earpiece: the .builtInMicBtA2dp presets omit
   .defaultToSpeaker (it breaks A2DP) and skip forceSpeaker, so with no
   Bluetooth connected output pinned to the quiet receiver. New
   IOSAudioRouter.applyA2dpSpeakerFallback() overrides to the built-in
   speaker when no external (A2DP/wired/AirPlay) output is present and
   clears the override when one is — called after activation and on
   device-change route changes.
This commit is contained in:
2026-06-22 03:43:00 +02:00
parent 6c17881cc0
commit fb73b694d0
5 changed files with 91 additions and 19 deletions

View File

@@ -30,10 +30,24 @@ up instantly. Newest status at the top.
to speaker; SettingsView shows AEC/NS state. **Rebuild the xcframework** before building the app:
`clients/apple/scripts/build-xcframework.sh --all` (new ABI symbols). `xcodebuild` iOS sim Debug
BUILD SUCCEEDED.
- **Post-verification fixes (2026-06-22, Swift-only — no core/ABI change):** two on-device bugs fixed.
- **Voice Chat (VPIO) silent playback:** `SessionState.doStartMicStream()` called `audioRestart()`
BEFORE `startStream`, so when the engine was already running (a remote stream had started it) it
reopened with `external_capture=false` and opened a hardware miniaudio capture device; the
announce-result restart then early-returned (engine already running) so that device was never
dropped and fought the `AVAudioEngine` VPIO unit, silencing playback. Fix: set
`setExternalPlayback` first, then `startStream` (which stores `external_feed` synchronously), THEN
`audioRestart()` — the core reopens in full external mode (no hardware devices). Added VPIO
diagnostics (graph/route formats at start; ring written/read totals at teardown).
- **Stereo Mic / Studio quiet earpiece:** the `.builtInMicBtA2dp` presets omit `.defaultToSpeaker`
(it breaks A2DP) and skip `forceSpeaker`, so with no Bluetooth connected output pinned to the quiet
receiver. New `IOSAudioRouter.applyA2dpSpeakerFallback()` overrides to the built-in speaker when no
external (A2DP/wired/AirPlay) output is present, clears the override when one is — called after
activation and on device-change route changes (`AudioSessionManager`).
- **Next (user, on device):** two iPhones on speaker, Voice Chat preset → confirm (a) no echo, (b)
background noise suppressed, (c) speaker output by default; then Studio preset still works (raw, no
AEC) — i.e. VPIO is scoped to the AEC presets. Tune the mixer-timer/ring sizing if there's
under/overrun.
background noise suppressed, (c) speaker output by default AND remote audio is now audible; then
Stereo Mic / Studio with no BT → confirm loud speaker (not earpiece), and A2DP takes over when a BT
headset connects. Tune the mixer-timer/ring sizing if there's under/overrun.
- **Done (2026-06-21):** **Docker + Linux deployment + GitHub Actions cross-build.** Added the complete Linux server
deployment story (the only missing platform — Windows and macOS already have native