Summary
On 8000-series FLEX radios (FLEX-8600M / FLEX-8400M), DAX RX audio is delivered at ~2× the rate the DAX audio pipeline consumes, so roughly half of every second is trimmed. The result is chopped / "crackling" DAX recordings that are also very quiet at 100%. This is the audio half of #3626 — split out here because it is an independent root cause from the connection-quality storm (which is fixed in #3796).
Evidence
VirtualAudioBridge's per-channel DAX RX ch N summary lines show, every ~1000 ms interval, the sink keeping up at real-time plus trimming almost another full second:
DAX RX ch 2 summary: interval_ms=1004 written_ms=1018.7 backlog_ms=82.7 trim_events=6 trimmed_ms=992.0 overruns=0
DAX RX ch 1 summary: interval_ms=1000 written_ms=986.7 backlog_ms=56.0 trim_events=6 trimmed_ms=992.0 overruns=0
written_ms + trimmed_ms ≈ 2000 ms per 1000 ms interval → the stream arrives at 2× real-time. Reproduced on both a FLEX-8600M (#3626 reporter log) and a FLEX-8400M (test capture aethersdr-20260626-101017), on every DAX RX channel, constant from stream start, independent of the #3626 stream-storm.
Root cause (analysis)
The reduced-bandwidth DAX audio packets are pcc=0x0123, bytes=284 → 284 − 28 (VITA49_HEADER_BYTES) = 256 payload bytes = 128 int16 mono samples per packet. At the observed packet rate that is 48 kHz, but the DAX audio pipeline assumes 24 kHz everywhere:
src/core/VirtualAudioBridge.cpp — samplesToMs() divides by 24000 * 2; kRxTargetBacklogFrames / kRxMaxBacklogFrames are sized "@ 24 kHz"; openShmSegment() sets sampleRate = 24000; feedDaxAudio()'s comment states "float32 stereo PCM @ 24 kHz … HAL plugin also runs at 24 kHz, so no resampling needed."
- The macOS DAX HAL plugin reads the shared-memory
sampleRate (24000) and runs at 24 kHz.
src/core/PipeWireAudioBridge.cpp (Linux) makes the same assumption.
Because the bridge writes ~48000 stereo frames/sec into a ring sized for 24 kHz, the backlog grows and the live-edge trim discards ~50 % of samples each second → periodic chops (the "crackling") and, depending on the consuming app's resampling, low apparent level (the "very quiet at 100%").
This is plausibly a 6000-vs-8000-series difference: the 8000-series PA / DAX path appears to deliver audio at 48 kHz where the 6000-series uses 24 kHz.
Proposed direction
Make the DAX audio rate radio/stream-aware instead of hardcoded 24 kHz, coordinated across the three consumers that must agree on the shared-memory sampleRate:
VirtualAudioBridge.cpp — samplesToMs(), backlog frame constants, openShmSegment() sampleRate, feedDaxAudio().
- The macOS DAX HAL plugin (reads the shm
sampleRate).
PipeWireAudioBridge.cpp (Linux).
Confirm the true 8000-series DAX audio rate (FlexLib / radio-advertised) before wiring it; the shm sampleRate field is the natural carrier so the plugin and app stay in lockstep.
Notes
💻 Filed by Claude Code (Opus 4.8) with architecture by @jensenpat
Summary
On 8000-series FLEX radios (FLEX-8600M / FLEX-8400M), DAX RX audio is delivered at ~2× the rate the DAX audio pipeline consumes, so roughly half of every second is trimmed. The result is chopped / "crackling" DAX recordings that are also very quiet at 100%. This is the audio half of #3626 — split out here because it is an independent root cause from the connection-quality storm (which is fixed in #3796).
Evidence
VirtualAudioBridge's per-channelDAX RX ch N summarylines show, every ~1000 ms interval, the sink keeping up at real-time plus trimming almost another full second:written_ms + trimmed_ms ≈ 2000 msper 1000 ms interval → the stream arrives at 2× real-time. Reproduced on both a FLEX-8600M (#3626 reporter log) and a FLEX-8400M (test captureaethersdr-20260626-101017), on every DAX RX channel, constant from stream start, independent of the #3626 stream-storm.Root cause (analysis)
The reduced-bandwidth DAX audio packets are
pcc=0x0123,bytes=284→284 − 28 (VITA49_HEADER_BYTES) = 256payload bytes = 128 int16 mono samples per packet. At the observed packet rate that is 48 kHz, but the DAX audio pipeline assumes 24 kHz everywhere:src/core/VirtualAudioBridge.cpp—samplesToMs()divides by24000 * 2;kRxTargetBacklogFrames/kRxMaxBacklogFramesare sized "@ 24 kHz";openShmSegment()setssampleRate = 24000;feedDaxAudio()'s comment states "float32 stereo PCM @ 24 kHz … HAL plugin also runs at 24 kHz, so no resampling needed."sampleRate(24000) and runs at 24 kHz.src/core/PipeWireAudioBridge.cpp(Linux) makes the same assumption.Because the bridge writes ~48000 stereo frames/sec into a ring sized for 24 kHz, the backlog grows and the live-edge trim discards ~50 % of samples each second → periodic chops (the "crackling") and, depending on the consuming app's resampling, low apparent level (the "very quiet at 100%").
This is plausibly a 6000-vs-8000-series difference: the 8000-series PA / DAX path appears to deliver audio at 48 kHz where the 6000-series uses 24 kHz.
Proposed direction
Make the DAX audio rate radio/stream-aware instead of hardcoded 24 kHz, coordinated across the three consumers that must agree on the shared-memory
sampleRate:VirtualAudioBridge.cpp—samplesToMs(), backlog frame constants,openShmSegment()sampleRate,feedDaxAudio().sampleRate).PipeWireAudioBridge.cpp(Linux).Confirm the true 8000-series DAX audio rate (FlexLib / radio-advertised) before wiring it; the shm
sampleRatefield is the natural carrier so the plugin and app stay in lockstep.Notes
💻 Filed by Claude Code (Opus 4.8) with architecture by @jensenpat