fix(audio): name CW sidetone JACK client "AetherSDR CW Sidetone"#3517
Conversation
The CW sidetone defaults to the PortAudio backend, which on Linux runs over
PipeWire's JACK API and registers a JACK client under PortAudio's hardcoded
default name "PortAudio". The sidetone sink opens alongside RX and stays open
for the whole RX session, so qpwgraph/JACK/WSJT-X show a persistent node
literally named "PortAudio" with no indication it belongs to AetherSDR -- the
most opaque label of any AetherSDR audio node.
Call PaJack_SetClientName("AetherSDR CW Sidetone") immediately before
Pa_Initialize(). The pa_jack contract requires the call precede Pa_Initialize
and references (does not copy) the string, so it uses a static-lifetime
const char[]. Guarded with `#if defined(Q_OS_LINUX) && __has_include(<pa_jack.h>)`
so macOS CoreAudio / Windows WASAPI builds (no pa_jack.h, no JACK node) compile
unaffected. CW sidetone is AetherSDR's only PortAudio user, so naming the
process-global JACK client is unambiguous.
Verified A/B on Linux/PipeWire (Nobara 43, Qt 6.10.3): the sidetone node's
node.name / node.description / client.name change from "PortAudio" to
"AetherSDR CW Sidetone" in pw-dump while RX is running.
Co-authored-by: Don @ cloaked.agency <don@cloaked.agency> & K6OZY
There was a problem hiding this comment.
Reviewed the diff — this looks good. Small, well-scoped, and correctly implemented:
- Placement is right. The
PaJack_SetClientName()call sits inside theif (!m_paInitialized)block immediately beforePa_Initialize(), which satisfies the pa_jack contract, and it re-runs correctly if the sink is ever re-initialized afterPa_Terminate()in the destructor. - Lifetime is right.
static const char[]satisfies pa_jack's reference-not-copy semantics; the comment documents why, which future readers will appreciate. - The "only PortAudio user" claim is accurate. I verified
CwSidetonePortAudioSink.{h,cpp}is the only code touchingportaudio.h/Pa_Initializein the tree, so naming the process-global JACK client here is unambiguous as stated. - Guard works.
Q_OS_LINUXis defined by the time the preprocessor reaches the check (the header pulls in QtCore viaQString/CwSidetoneSinkBackend.h), and macOS/Windows builds are unaffected.
One non-blocking note: the __has_include(<pa_jack.h>) guard tests header presence, not whether libportaudio was actually built with JACK support. A hand-built portaudio with JACK disabled but headers installed would fail at link time on PaJack_SetClientName. All mainstream distro packages build with JACK enabled (and CI uses pkg-config portaudio), so this is a theoretical edge case — just worth knowing if a user ever reports a link error against a custom portaudio. Not asking for a change; a CMake symbol check would be heavier than this 13-line fix deserves.
Ignoring the PaJack_SetClientName return value is fine — the pa_jack implementation just stores the pointer and always returns paNoError.
Nice test plan with the pw-dump A/B ground truth, and good continuity with #3438/#3440. Thanks for the contribution, K6OZY! 73
🤖 aethersdr-agent · cost: $7.4429 · model: claude-fable-5
There was a problem hiding this comment.
Reviewed the diff against the surrounding code — this looks good. Verified the details that matter for this kind of change:
- Guard evaluates correctly. The new
#if defined(Q_OS_LINUX) && __has_include(<pa_jack.h>)sits after#include "CwSidetonePortAudioSink.h", which pulls in<QString>, soQ_OS_LINUXis already defined at that point. No silent no-op on Linux. - Call ordering and lifetime are right.
PaJack_SetClientNameis called inside the!m_paInitializedblock immediately beforePa_Initialize(), and re-runs correctly on any Pa_Terminate/re-init cycle. The function-localstatic const char[]satisfies pa_jack's reference-not-copy contract. - "Only PortAudio user" claim holds.
Pa_Initializeappears only in this file;AudioEnginereferencesHAVE_PORTAUDIOfor backend selection but never initializes PortAudio itself. Naming the process-global JACK client here is unambiguous as stated. - Build safety. PortAudio comes from pkg-config (system package), and
pa_jack.his only installed when portaudio was built with JACK support, so the__has_includeguard doubles as a link-safety check in practice. macOS/Windows are excluded cleanly.
One optional nit, not blocking: PaJack_SetClientName returns a PaError that's silently discarded. A failure just leaves the default name (and the 21-char name is well under JACK's 64-char limit), so this is harmless — but a qCWarning(lcAudio) on non-paNoError would match the error-logging style of the rest of this file if you want it.
Nice, tightly-scoped fix with a solid pw-dump A/B test plan — thanks for continuing the patchbay-identifiability work from #3438, @Ozy311!
🤖 aethersdr-agent · cost: $4.8654 · model: claude-fable-5
Address the aethersdr-agent review note on aethersdr#3517: PaJack_SetClientName returns a PaError that was silently discarded. Capture it and qCWarning(lcAudio) on non-paNoError, mirroring the Pa_Initialize error path in the same file. Non-fatal — a naming failure only leaves the default JACK/PipeWire node label — so we log and continue rather than abort. No behavior change on any current path (pa_jack returns paNoError today); this codes to the published PaError contract and keeps the file’s error-logging uniform. Co-authored-by: Don @ cloaked.agency <don@cloaked.agency> & K6OZY
NF0T
left a comment
There was a problem hiding this comment.
Reviewed the diff, both commits, and the related patchbay-naming work (#3438, #3440).
Technical sign-off:
- Call ordering is correct —
PaJack_SetClientNamesits inside!m_paInitializedimmediately beforePa_Initialize(), satisfying the pa_jack pre-init contract. Re-runs correctly on anyPa_Terminate/re-init cycle. static const char[]lifetime is correct — pa_jack references, doesn't copy; static storage outlives the PortAudio session.- Verified independently that
Pa_Initializeappears only in this file — the "only PortAudio user, naming the process-global JACK client is unambiguous" claim holds. - Guard evaluates correctly on Linux —
Q_OS_LINUXis defined before the#ifvia the existing Qt includes. - Second commit fully resolves the bot's return-value nit;
qCWarning(lcAudio)on non-paNoErrornow mirrors thePa_Initializeerror path in style.
One non-blocking note for the record: __has_include(<pa_jack.h>) guards header presence but not whether the installed PortAudio was compiled with JACK support. A hand-built PortAudio with JACK disabled but headers still on disk would fail at link time. All mainstream distros package portaudio with JACK enabled and CI uses pkg-config, so this is purely theoretical — not requesting a change, just noting it for anyone who hits a link error against a custom PortAudio build.
Principle XI — Fix is demonstrated: pw-dump A/B ground truth on a live FLEX-6700 is exactly the right evidence standard for a JACK node-naming change. Nice work continuing the patchbay-identifiability line from #3438.
…hersdr#3517) ## Summary The CW sidetone uses the PortAudio backend, which on Linux runs over PipeWire's JACK API and registers a JACK client under PortAudio's hardcoded default name **`PortAudio`**. The sidetone sink opens alongside RX and stays open for the whole RX session, so qpwgraph / JACK patchbays / WSJT-X / FLDIGI show a persistent node literally named `PortAudio` with no indication it belongs to AetherSDR — the most opaque label of any AetherSDR audio node. ## Fix Call `PaJack_SetClientName("AetherSDR CW Sidetone")` immediately before `Pa_Initialize()`. The `pa_jack` contract requires the call precede `Pa_Initialize` and references (does not copy) the string, so it uses a static-lifetime `const char[]`. Guarded with `#if defined(Q_OS_LINUX) && __has_include(<pa_jack.h>)` so macOS CoreAudio / Windows WASAPI builds (no `pa_jack.h`, no JACK node) compile unaffected. CW sidetone is AetherSDR's only PortAudio user, so naming the process-global JACK client is unambiguous. ## Why - Completes the "every AetherSDR audio node is identifiable in patchbays" line of work alongside the merged DAX/TX pipe-naming fix (aethersdr#3438) and the still-open main RX/TX stream naming (aethersdr#3440). - A node named `PortAudio` is indistinguishable from any other PortAudio app and confuses routing in WSJT-X / FLDIGI device pickers. ## Scope - 13 lines added in one file (`src/core/CwSidetonePortAudioSink.cpp`). - Linux/PipeWire-only effect (guarded); no protocol, persistence, or UX change. ## Test plan - [x] Local incremental `ninja -C build` clean on Linux (Nobara 43, Qt 6.10.3, gcc 15.2.1). - [x] **Live A/B on a FLEX-6700 (RX-only), `pw-dump` ground truth:** - Baseline (upstream/main `753eb689`): sidetone node `client.name` / `node.name` / `node.description` all read **`PortAudio`**. - This branch: all three read **`AetherSDR CW Sidetone`**. - [x] macOS CoreAudio / Windows WASAPI: compiles unaffected (guard excludes the call; no JACK node on those backends) — maintainer/secondary. Related: aethersdr#3438, aethersdr#3440 --- 73, Ozy **K6OZY** AI compute partnership: [cloaked.agency](https://cloaked.agency) — drafted with Don, our VP of Engineering agent, on Linux dev stack (`nobara-dell`).
Summary
The CW sidetone uses the PortAudio backend, which on Linux runs over PipeWire's JACK API and registers a JACK client under PortAudio's hardcoded default name
PortAudio. The sidetone sink opens alongside RX and stays open for the whole RX session, so qpwgraph / JACK patchbays / WSJT-X / FLDIGI show a persistent node literally namedPortAudiowith no indication it belongs to AetherSDR — the most opaque label of any AetherSDR audio node.Fix
Call
PaJack_SetClientName("AetherSDR CW Sidetone")immediately beforePa_Initialize(). Thepa_jackcontract requires the call precedePa_Initializeand references (does not copy) the string, so it uses a static-lifetimeconst char[]. Guarded with#if defined(Q_OS_LINUX) && __has_include(<pa_jack.h>)so macOS CoreAudio / Windows WASAPI builds (nopa_jack.h, no JACK node) compile unaffected. CW sidetone is AetherSDR's only PortAudio user, so naming the process-global JACK client is unambiguous.Why
PortAudiois indistinguishable from any other PortAudio app and confuses routing in WSJT-X / FLDIGI device pickers.Scope
src/core/CwSidetonePortAudioSink.cpp).Test plan
ninja -C buildclean on Linux (Nobara 43, Qt 6.10.3, gcc 15.2.1).pw-dumpground truth:753eb689): sidetone nodeclient.name/node.name/node.descriptionall readPortAudio.AetherSDR CW Sidetone.Related: #3438, #3440
73, Ozy K6OZY
AI compute partnership: cloaked.agency — drafted with Don, our VP of Engineering agent, on Linux dev stack (
nobara-dell).