Fix RADE receive audio on macOS by enabling DAX stream lifecycle#2633
Conversation
There was a problem hiding this comment.
Thanks for tracking this down @NF0T — the root cause analysis (RADE never sees the stream create dax_rx on macOS when the HAL plugin is absent, so VITA-49 audio never reaches feedRxAudio) is right, and reusing the existing stream via daxStreamIdForChannel() is the correct shape.
Build break on Windows / Linux-without-PipeWire
m_daxBridge is declared only inside #if defined(Q_OS_MAC) || defined(HAVE_PIPEWIRE) in MainWindow.h (around line 670):
#if defined(Q_OS_MAC) || defined(HAVE_PIPEWIRE)
DaxBridge* m_daxBridge{nullptr};
...
#endifSo in deactivateRADE() the new line
bool daxBridgeActive = (m_daxBridge != nullptr);will fail to compile on Windows and on Linux builds without PipeWire — m_daxBridge is not a member there. The previous #if !defined(Q_OS_MAC) && !defined(HAVE_PIPEWIRE) block deliberately gated the whole teardown so it never touched the bridge on those platforms.
The cleanest fix is to keep the bridge-aware branch only where the member exists:
if (m_radeDaxStreamId) {
bool tciActive = m_tciServer && m_tciServer->clientCount() > 0;
bool daxBridgeActive = false;
#if defined(Q_OS_MAC) || defined(HAVE_PIPEWIRE)
daxBridgeActive = (m_daxBridge != nullptr);
#endif
if (!tciActive && !daxBridgeActive) {
m_radioModel.panStream()->unregisterDaxStream(m_radeDaxStreamId);
if (m_radioModel.isConnected()) {
m_radioModel.sendCommand(
QString("stream remove 0x%1")
.arg(m_radeDaxStreamId, 8, 16, QChar('0')));
}
}
m_radeDaxStreamId = 0;
}
disconnect(m_radeDaxStreamConn);The activateRADE() side is fine to unguard — that block only references m_radeDaxStreamId / m_radeDaxStreamConn, which live under #ifdef HAVE_RADE and exist on all platforms.
Minor: race on macOS when the HAL bridge is present
On macOS with the HAL driver installed, startDax() issues stream create type=dax_rx for each slice, but registration into PanadapterStream happens asynchronously through the statusReceived handler. If the user enables RADE before that registration round-trip completes, daxStreamIdForChannel(daxCh) returns 0 and activateRADE() will issue a second stream create for the same channel — exactly the duplicate-stream / double-audio case the comment warns about. Not a blocker (and the bridge-present path is the less-common one for the bug you're fixing), but worth a follow-up — the proper ref-counted PanadapterStream lifecycle the existing TODO calls out would cover this too.
PR body note
The body has some encoded artifacts (�ctivateRADE → activateRADE, ${PR_DIFF}${PR_DIFF} → &&, \t before tciServer). Worth cleaning up before merge.
Otherwise looks good — fix the Windows/Linux build break and this should be ready.
ten9876
left a comment
There was a problem hiding this comment.
Approved. Clean auto-merge with current main, Linux build of merged tree compiles cleanly. Right architectural shape — stop assuming a platform-specific path will create RADE's stream, always negotiate it via the existing reuse-if-present logic.
…ipeWire (#2662) PR #2633 added an unconditional reference to m_daxBridge in deactivateRADE() but m_daxBridge is only declared inside #if defined(Q_OS_MAC) || defined(HAVE_PIPEWIRE) — breaking Windows and Linux-without-PipeWire builds. Wrap the daxBridgeActive assignment in the same preprocessor guard. Default-false on platforms without a bridge preserves the expected behaviour (stream remove proceeds as before — no bridge means no bridge to potentially be borrowing the stream). macOS RADE RX fix from #2633 fully preserved. Co-Authored-By: NF0T <NF0T@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ipeWire PR aethersdr#2633 removed the platform guards from deactivateRADE() but left an unconditional reference to m_daxBridge, which is only a member under #if defined(Q_OS_MAC) || defined(HAVE_PIPEWIRE). This breaks the Windows and Linux-without-PipeWire build. Gate the daxBridgeActive assignment behind the same preprocessor guard that protects the member declaration, matching the pattern used in the rest of MainWindow.cpp. Reported by aethersdr-agent in PR aethersdr#2633 review. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Summary - Adds `src/gui/MainWindow.cpp` and `src/gui/MainWindow.h` to the `dorny/paths-filter` `build` group so the Windows CI job runs whenever the hot file changes. - The file is densely platform-guarded (`#ifdef Q_OS_WIN`, `Q_OS_MAC`, `HAVE_PIPEWIRE`), so Linux-passing edits can still break MSVC. ## Motivation Same class of failure has bitten us repeatedly: - **#2633 → #2662** — unconditional `m_daxBridge` reference inside a macOS-only guard, caught post-release. - **#2670** — Windows behaviour change not exercised by CI. - **v26.5.3 (#3024)** — `ClientPhaseRotator.cpp` bare `M_PI` broke MSVC (needs `_USE_MATH_DEFINES`). Different file, same shape: path not on the filter → `check-windows` didn't run → broken Windows build landed and required a follow-up patch. This PR closes the gap for the highest-leverage file. Other hot platform-guarded files can be added later if their breakage rate justifies the runner time. ## Cost Most PRs continue to skip `check-windows` (the filter still excludes most source paths). Cost increase is bounded to the subset of PRs that touch MainWindow.cpp/.h, which is exactly the subset that needs the coverage. ## Test plan - [ ] CI: `check-paths` job runs and reports `build-changed=true` on this PR (because `.github/workflows/**` is itself a watched path). - [ ] CI: `check-windows` job runs and passes (no source changed, so it should be a clean cache hit). - [ ] After merge: next PR that touches `src/gui/MainWindow.cpp` triggers `check-windows` automatically. Closes #2671. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary Drop the \`check-paths\` allow-list job that gated \`check-windows\` and \`check-macos\` based on a hand-maintained set of files. Both cross-platform CI builds now run on every PR. ## Why The path-filter approach has been a chronic leaker. Every time a new file gained a platform-guarded branch (\`#ifdef Q_OS_WIN\`, \`#ifdef Q_OS_MAC\`, \`HAVE_PIPEWIRE\`, etc.) that wasn't on the allow-list, CI silently skipped the relevant cross-platform check and the regression surfaced at release tag time. The follow-up was always to add another path to the filter — and then the same class of issue would resurface elsewhere a few PRs later. Pattern receipts: - #796 — original \"MSVC issue slipped past CI\" lesson - #2633 → #2662 — macOS \`m_daxBridge\` reference inside macOS-only guard, missed - #2670 — Windows behavior change not exercised by CI - #2671 — added MainWindow.cpp/.h to filter after a miss - #2929 → #3052 — WASAPI mono-mic recovery landed unverified; added AudioEngine.cpp/.h - #3210 — re-added AudioEngine after a relapse - v26.5.3 — ClientPhaseRotator.cpp \`M_PI\` MSVC break slipped through - #3241 (yesterday) — CwSidetonePortAudioSink WASAPI fix; entire \`#ifdef Q_OS_WIN\` block never compiled by CI because the file wasn't on the filter ## Trade-off Cost: ~15-20 min of GitHub Actions runner time per PR (Windows + macOS in parallel, both cached aggressively — cold ~5 min, warm ~2-3 min). Benefit: a whole class of release-time regression we've been paying for repeatedly. ## Test plan - [x] YAML valid (\`python3 -c 'import yaml; yaml.safe_load(...)'\`) - [ ] Confirm \`check-windows\` and \`check-macos\` actually run on this PR (no longer marked \`skipping\`) - [ ] Both pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… (aethersdr#3028) ## Summary - Adds `src/gui/MainWindow.cpp` and `src/gui/MainWindow.h` to the `dorny/paths-filter` `build` group so the Windows CI job runs whenever the hot file changes. - The file is densely platform-guarded (`#ifdef Q_OS_WIN`, `Q_OS_MAC`, `HAVE_PIPEWIRE`), so Linux-passing edits can still break MSVC. ## Motivation Same class of failure has bitten us repeatedly: - **aethersdr#2633 → aethersdr#2662** — unconditional `m_daxBridge` reference inside a macOS-only guard, caught post-release. - **aethersdr#2670** — Windows behaviour change not exercised by CI. - **v26.5.3 (aethersdr#3024)** — `ClientPhaseRotator.cpp` bare `M_PI` broke MSVC (needs `_USE_MATH_DEFINES`). Different file, same shape: path not on the filter → `check-windows` didn't run → broken Windows build landed and required a follow-up patch. This PR closes the gap for the highest-leverage file. Other hot platform-guarded files can be added later if their breakage rate justifies the runner time. ## Cost Most PRs continue to skip `check-windows` (the filter still excludes most source paths). Cost increase is bounded to the subset of PRs that touch MainWindow.cpp/.h, which is exactly the subset that needs the coverage. ## Test plan - [ ] CI: `check-paths` job runs and reports `build-changed=true` on this PR (because `.github/workflows/**` is itself a watched path). - [ ] CI: `check-windows` job runs and passes (no source changed, so it should be a clean cache hit). - [ ] After merge: next PR that touches `src/gui/MainWindow.cpp` triggers `check-windows` automatically. Closes aethersdr#2671. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary Drop the \`check-paths\` allow-list job that gated \`check-windows\` and \`check-macos\` based on a hand-maintained set of files. Both cross-platform CI builds now run on every PR. ## Why The path-filter approach has been a chronic leaker. Every time a new file gained a platform-guarded branch (\`#ifdef Q_OS_WIN\`, \`#ifdef Q_OS_MAC\`, \`HAVE_PIPEWIRE\`, etc.) that wasn't on the allow-list, CI silently skipped the relevant cross-platform check and the regression surfaced at release tag time. The follow-up was always to add another path to the filter — and then the same class of issue would resurface elsewhere a few PRs later. Pattern receipts: - aethersdr#796 — original \"MSVC issue slipped past CI\" lesson - aethersdr#2633 → aethersdr#2662 — macOS \`m_daxBridge\` reference inside macOS-only guard, missed - aethersdr#2670 — Windows behavior change not exercised by CI - aethersdr#2671 — added MainWindow.cpp/.h to filter after a miss - aethersdr#2929 → aethersdr#3052 — WASAPI mono-mic recovery landed unverified; added AudioEngine.cpp/.h - aethersdr#3210 — re-added AudioEngine after a relapse - v26.5.3 — ClientPhaseRotator.cpp \`M_PI\` MSVC break slipped through - aethersdr#3241 (yesterday) — CwSidetonePortAudioSink WASAPI fix; entire \`#ifdef Q_OS_WIN\` block never compiled by CI because the file wasn't on the filter ## Trade-off Cost: ~15-20 min of GitHub Actions runner time per PR (Windows + macOS in parallel, both cached aggressively — cold ~5 min, warm ~2-3 min). Benefit: a whole class of release-time regression we've been paying for repeatedly. ## Test plan - [x] YAML valid (\`python3 -c 'import yaml; yaml.safe_load(...)'\`) - [ ] Confirm \`check-windows\` and \`check-macos\` actually run on this PR (no longer marked \`skipping\`) - [ ] Both pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This PR resolves issue #2627, where RADE receive audio was non-functional on macOS due to restrictive platform-specific guards in the DAX stream management logic. Previously, the code assumed that the DAX audio bridge (e.g., the macOS HAL plugin) would handle stream registration; however, when the bridge is inactive, the radio never receives the required stream create command, leaving the RADE engine without input.
Changes
Testing Performed