fix(audio): name CW sidetone JACK client "AetherSDR CW Sidetone"#3517
fix(audio): name CW sidetone JACK client "AetherSDR CW Sidetone"#3517Ozy311 wants to merge 2 commits into
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
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).