Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ae66f91
refactor(audio): move TX voice DSP to 48 kHz float — Principle VIII.
Silent-Gloves Aug 9, 2026
a86fbc2
Applies unshaped TPDF at ±1 LSB immediately before int16 conversion, …
Silent-Gloves Aug 9, 2026
365d275
Update Audio Pipeline Architecture Documentation with Dither Information
Silent-Gloves Aug 10, 2026
114fcae
Applies 10kHz specific 12% SRC profile where appropriate and associat…
Silent-Gloves Aug 11, 2026
ba804a6
Corrects RNNoise dangling-pointer blocker
Silent-Gloves Aug 11, 2026
cc86794
Corrects oversized capture block blocker
Silent-Gloves Aug 11, 2026
1d4aacb
Restores Allocation-free Buffer Reuse by replacing clear() with resiz…
Silent-Gloves Aug 11, 2026
75fa4ee
Corrects Stale RADE Resampler State
Silent-Gloves Aug 11, 2026
aa7f104
Corrects Stereo Egress Misalignment via Safe Recovery from Frame-Coun…
Silent-Gloves Aug 11, 2026
e83a4b2
Implements RNNoise RateDomain Guard
Silent-Gloves Aug 11, 2026
3b870e6
Removes duplicate hard-coded 48000, stale comment update
Silent-Gloves Aug 11, 2026
469adc2
Implements Caller-owned Transport Handoff
Silent-Gloves Aug 11, 2026
4aa5b1d
Resets RADE TX resampler outside real-time callback
Silent-Gloves Aug 11, 2026
8b849e1
Correct Quindar TX Sample-Rate Comment
Silent-Gloves Aug 11, 2026
6e9126c
Adds complete Audio Pipeline SRC coverage
Silent-Gloves Aug 11, 2026
33125d0
Implements Frame-Level Exact Zero Preservation in Dither
Silent-Gloves Aug 11, 2026
e0acc25
Implements Route-Aware Resampling Diagnostics
Silent-Gloves Aug 11, 2026
5e4b4fa
Mid-flight Documentation Updates
Silent-Gloves Aug 11, 2026
51c9031
Registers new diagnostics
Silent-Gloves Aug 11, 2026
68bddc4
Removes redundant per-block call
Silent-Gloves Aug 12, 2026
1411105
Removes unused process48kStereo() overload
Silent-Gloves Aug 12, 2026
9acab14
Documentation Update
Silent-Gloves Aug 12, 2026
3263d82
Adds Coverage for Lower Sample Rate Edge Cases
Silent-Gloves Aug 12, 2026
a6eebe6
Defer Stereo Egress SRC Recover Outside Realtime Callback. Principle XI
Silent-Gloves Aug 12, 2026
9c77126
Documentation Updates
Silent-Gloves Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,7 @@ set(CORE_SOURCES
src/core/AetherDspModePolicy.cpp
src/core/AudioEngine.cpp
src/core/TxMicChannelNormalizer.cpp
src/core/TxVoiceProcessor.cpp
src/core/ChannelStripPresets.cpp
src/core/Biquad.cpp
src/core/StereoBiquad.cpp
Expand Down Expand Up @@ -3655,6 +3656,13 @@ target_include_directories(tx_mic_channel_normalizer_test PRIVATE
target_link_libraries(tx_mic_channel_normalizer_test PRIVATE Qt6::Core)
add_test(NAME tx_mic_channel_normalizer_test COMMAND tx_mic_channel_normalizer_test)

add_executable(tx_voice_processor_test
tests/tx_voice_processor_test.cpp
)
target_include_directories(tx_voice_processor_test PRIVATE src)
target_link_libraries(tx_voice_processor_test PRIVATE aethercore Qt6::Core)
add_test(NAME tx_voice_processor_test COMMAND tx_voice_processor_test)

# Pins the SkyRoof-parity WFM DSP chain: NCO offset correction removes the
# discriminator DC term (fixed pan + Doppler-stepped slice), twin linear-phase
# resamplers deliver exactly 48 kHz from any native DAX IQ rate, and streaming
Expand Down Expand Up @@ -5665,6 +5673,7 @@ add_executable(device_diagnostics_test
)
target_include_directories(device_diagnostics_test PRIVATE src)
target_link_libraries(device_diagnostics_test PRIVATE Qt6::Core)
add_test(NAME device_diagnostics_test COMMAND device_diagnostics_test)

add_executable(midi_settings_test
tests/midi_settings_test.cpp
Expand Down
319 changes: 229 additions & 90 deletions docs/architecture/audio-pipeline.md

Large diffs are not rendered by default.

21 changes: 11 additions & 10 deletions docs/architecture/pipelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,18 @@ TX AUDIO ROUTING SUMMARY: ◄── AUDIO THREAD
│ └─→ radio-native VITA PCC 0x0123
test tone → client TX DSP chain → post-DSP monitor
→ PC mic gain → Quindar → final limiter → meters/scopes
device-rate Int16 → canonical mono → 48 kHz float normalization
→ RN2 → test tone → client TX DSP chain → PC mic gain
→ Quindar → final limiter → 48-to-24 kHz SRC
→ linked TPDF + Int16 quantization → monitors/meters/scopes
├─→ Opus remote_audio_tx VITA PCC 0x8005
└─→ uncompressed VITA fallback PCC 0x03E3
▼ [queued to NETWORK]
PanadapterStream.sendToRadio()
Radio UDP 4991
├─→ backend TX seam (HL2/Icom, 24 kHz Int16)
│ └─→ backend-native conversion/modulation
└─→ Flex Opus PCC 0x8005 / uncompressed PCC 0x03E3
└─→ [queued to NETWORK]
PanadapterStream.sendToRadio()
└─→ Radio UDP 4991

The PSK Reporter WSPR beacon is a one-shot, operator-armed digital source. A
precise timer on the AudioEngine worker thread generates sample-accurate 4-FSK
Expand Down
21 changes: 19 additions & 2 deletions docs/architecture/tx-audio-signal-path.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ PC mic voice TX stream before VITA/Opus packetization. The radio receives
the already-shaped voice signal and treats it identically to any other
PC-mic input (enters at SC_MIC, meter 26).

Mic hardware remains Int16 at its negotiated device rate. The normal voice
path canonicalizes the selected channel, converts to float once, normalizes to
a fixed 48 kHz processing domain, and remains float through RN2 and the complete
channel strip. A 12%-transition-band SRC then converts 48 kHz to the existing
24 kHz Flex transport rate before linked TPDF dither and the sole Int16
quantization on the Flex path. See `audio-pipeline.md` for measured SRC delay
and the separate HL2/Icom backend seam behavior.

DAX/TCI TX and RADE are intentionally not part of this voice strip.
DAX/TCI bypasses client voice DSP in `AudioEngine::feedDaxTxAudio()`.
RADE branches early from `AudioEngine::onTxAudioReady()` and bypasses
Expand All @@ -29,10 +37,16 @@ to bypass, double-click to open the floating editor.

```
PC mic capture (QAudioSource)
▼ Int16 at negotiated device rate
Canonical channel selection → float conversion → 48 kHz normalization
Optional native-48 kHz RN2 → 48 kHz test tone
┌───────────────────────────────────────────────────────────────────┐
│ CHAIN widget — drag-drop ordered TX DSP pipeline
│ CHAIN widget — drag-drop ordered 48 kHz float TX DSP pipeline
│ │
│ [GATE] → [EQ] → [DESS] → [COMP] → [TUBE] → [PUDU] → [VERB] │
│ │
Expand All @@ -45,7 +59,7 @@ PC mic capture (QAudioSource)
│ ClientReverb — Freeverb (disabled by default) │
│ │
│ Audio thread loads the packed chain order once per block and │
│ dispatches each stage to its per-stage apply helper.
TxVoiceProcessor dispatches each enabled processor directly.
└─────────┬──────────────────────────────────────────────────────────┘
▼ (meters: per-stage inputPeak/outputPeak/GR, ClientEq FFT
Expand All @@ -55,6 +69,9 @@ PC mic capture (QAudioSource)
PC mic gain → Quindar → final limiter → meters/scopes
48-to-24 kHz SRC → linked TPDF dither → Int16 quantization
Opus remote_audio_tx / VITA encode → UDP → radio
```

Expand Down
7 changes: 4 additions & 3 deletions docs/audio-sink-factory.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
Every audio sink and source in AetherSDR currently answers two questions on its
own:

1. **"What rate / sample format does this device want, and how do I get my
24 kHz canonical audio onto it?"** — reimplemented ~9 times with ~6 divergent
1. **"What rate / sample format does this device want, and how do I bridge it
to the selected route's canonical device-boundary rate?"** — reimplemented
~9 times with ~6 divergent
fallback ladders and per-OS `#ifdef` branches that have drifted apart. This
is the root of a recurring class of platform bugs (44.1k-only devices
silently failing on some sinks, WASAPI Float32-only devices rejecting Int16,
Expand Down Expand Up @@ -84,7 +85,7 @@ The ladder, in one place, owns:
| Int16 ↔ Float32 | both tried per rate; Float-first output, Int16-first input | [#2669] / [#1090] |
| `preferredFormat()` catch-all | final rung for Float32-only virtual drivers / WASAPI | [#3231] |
| macOS mic preferred-rate-first | avoids silent 48 k open on 16 k-native mics | PR [#2930] |
| macOS Bluetooth-HFP mic | native 8/16/24 k first, never forced to 48 k | [#2615] |
| macOS Bluetooth-HFP mic | open native 8/16/24 k first; normal voice then normalizes to 48 k, while RADE converts separately to 24 k | [#2615] |
| Windows probe-at-open | skip unreliable `isFormatSupported`, try at open | [#2120] / [#2929] |

### Layer 2 — `AudioDeviceNegotiator` (live wrapper, Qt Multimedia) — next
Expand Down
Loading
Loading