DAX RX: deliver audio via Qt::DirectConnection (#1008)#2318
DAX RX: deliver audio via Qt::DirectConnection (#1008)#2318RiskAndReward1337 wants to merge 1 commit into
Conversation
Wire the PanadapterStream::daxAudioReady → DaxBridge::feedDaxAudio signal with Qt::DirectConnection so audio frames are delivered on PanadapterStream's network thread rather than queued through the GUI event loop. Under heavy GUI load (waterfall paints across multiple panadapters) the queued path was adding 10–50 ms of variable, GUI-correlated latency before samples reached the DAX bridge. DirectConnection removes that hop, which shows up as steadier WSJT-X DT under typical operating conditions. Split out from aethersdr#2312 per maintainer review so the connection-type change can be exercised in isolation against non-Linux DAX / TCI use cases. This PR depends on aethersdr#2312 for the Linux PipeWireAudioBridge::feedDaxAudio lock-free / atomic conversions; without those, calling feedDaxAudio off the GUI thread on Linux would race on m_open / m_channelGain / m_transmitting. On macOS DaxBridge resolves to VirtualAudioBridge — feedDaxAudio there should be reviewed for any Qt-thread-affine state before merging. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Claude here on Jeremy's behalf — Linux side is good now that #2312 landed, but the macOS path you flagged in the PR body is a real blocker. We'd like Option B: mirror #2312's hardening on Specifically
The PipeWireAudioBridge changes in #2312 are the template — same atomic conversions, same QTimer-deferred-to-main-thread pattern. Should be mostly a mechanical port. Jeremy can spot-check on his Mac mini once you push the hardening — stress test would be DAX RX into WSJT-X with heavy waterfall paint load on multiple panadapters, watching for Qt thread-affinity warnings or crashes. Thanks for splitting this out — keeps the review surfaces clean. 73, Jeremy KK7GWY & Claude (AI dev partner) |
|
Claude here — closing this as stale (no activity since May 4), but the design is captured at #2486 for whoever picks it up next. Aidan, the diagnosis was sound: GUI-event-queue jitter is real under multi-pan paint load and DirectConnection is the right call architecturally. The macOS prerequisite (VirtualAudioBridge thread-safety audit, mirroring what #2312 did for PipeWireAudioBridge) needed to land first to unblock the cross-platform version of your one-liner. Issue #2486 now captures the audit work concretely, and once it lands your PR's change becomes a clean, platform-agnostic merge. If you'd like to pick this back up after the prerequisite work, please reopen — your draft remains useful as the reference for what the final connect site should look like. Thanks for the careful diagnosis and for marking it draft pending macOS verification — that's exactly the right epistemic move when you can't test all platforms yourself. 73, Jeremy KK7GWY & Claude (AI dev partner) |
Mirrors the PipeWireAudioBridge thread-safety pattern (#2312) on the macOS audio bridge so PanadapterStream::daxAudioReady → feedDaxAudio can ship cross-platform under Qt::DirectConnection (unblocks #2318). Changes: * m_open, m_transmitting, m_channelGain converted to std::atomic so reads on PanadapterStream's network thread are correctly synchronised with GUI-thread writes (open/close, DaxApplet slider). * Added m_lastAudioMs atomic flag; feedDaxAudio stores wall-clock now on every RX packet — no Qt API touched on the fast path. * The silence-timer self-stop and ring-buffer readPos snap (originally done from feedDaxAudio, which would invoke QTimer::stop() across threads under DirectConnection) moved into feedSilenceToAllChannels, which runs on the GUI thread and observes m_lastAudioMs to decide when to self-stop. Matches PipeWireAudioBridge::feedSilenceToAllPipes field-for-field. * m_rxTiming left as plain POD with a comment — only touched on the audio thread today; atomic-ify if a GUI-thread inspector is ever added. Blast radius: codegraph does not index macOS-only VirtualAudioBridge (Linux-built graph), so impact was checked manually. Callers go through the `DaxBridge` typedef (`MainWindow.h:95`), and the PanadapterStream → DaxBridge::feedDaxAudio connection at MainWindow.cpp:13887 is the only invocation of the modified slot. setChannelGain / setTransmitting / isOpen all stay on the GUI thread. Scope is local to the two files touched. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… (#2762) ## Summary Fixes #2486 ### What was changed Mirrors the PipeWireAudioBridge thread-safety pattern (#2312) on the macOS audio bridge so PanadapterStream::daxAudioReady → feedDaxAudio can ship cross-platform under Qt::DirectConnection (unblocks #2318). - `m_open`, `m_transmitting`, `m_channelGain` converted to `std::atomic` so reads on PanadapterStream's network thread are correctly synchronised with GUI-thread writes (open/close, DaxApplet slider) - Cross-thread `QTimer::stop()` in `feedDaxAudio` replaced with an atomic `m_lastAudioMs` flag - Silence-stop / readPos-snap cleanup moved into the GUI-thread `feedSilenceToAllChannels` tick ### Recovery note This PR is being opened manually. The aetherclaude orchestrator (trace 5a19c310) pushed the signed commit at 15:18 PDT but was SIGTERM'd by a deploy.sh race before it could create the PR. Root cause + fix shipped as a follow-up to scripts/deploy.sh; this branch's commit is the orchestrator's own work, unmodified. --- Generated by AetherClaude (automated agent for AetherSDR) — PR creation completed manually after orchestrator interrupt. Co-authored-by: aethersdr-agent[bot] <273844287+aethersdr-agent[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Wires
PanadapterStream::daxAudioReady → DaxBridge::feedDaxAudiowithQt::DirectConnectionso DAX audio frames are delivered onPanadapterStream's network thread instead of being queued through the GUI event loop.Under heavy GUI load (waterfall paints across multiple panadapters) the queued path was adding 10–50 ms of variable, GUI-correlated latency before samples reached the DAX bridge. DirectConnection removes that hop and produces steadier WSJT-X DT under typical operating conditions.
Marked draft. Split out of #2312 at @jensenpat's request so this connection-type change can be exercised in isolation against non-Linux DAX / TCI use cases.
Dependency / ordering
This PR depends on #2312 for thread-safety on Linux:
DaxBridgeresolves toPipeWireAudioBridge. DAX RX: native pw_stream source on Linux — 400ms → 200ms (#1008) #2312 convertsm_open,m_channelGain,m_transmitting, andm_lastAudioMstostd::atomicand removes all Qt-thread-affine work fromfeedDaxAudio. Without those changes, callingfeedDaxAudiooff the GUI thread races on those fields.DaxBridgeresolves toVirtualAudioBridge.VirtualAudioBridge::feedDaxAudioshould be reviewed for any Qt-thread-affine state before this lands. I haven't been able to verify on macOS hardware — flagging for a maintainer with a Mac to confirm.elif(UNIX)gate inCMakeLists.txt), so thisconnect(...)site isn't compiled.daxAudioReadyconnection (see other call sites inMainWindow.cpp) and this PR only touches the bridge wire.Recommended merge order: #2312 first, then this PR.
Diff
One hunk in
src/gui/MainWindow.cpp::startDax()— addsQt::DirectConnectionto oneconnect()call plus a six-line comment explaining the rationale.Test plan
upstream/main(Qt 6.11.0, RelWithDebInfo) — only when stacked with DAX RX: native pw_stream source on Linux — 400ms → 200ms (#1008) #2312🤖 Generated with Claude Code