Skip to content

fix(cw): Int16 fallback for sidetone sink on Int16-only WASAPI devices (#2668). Principle I.#2669

Merged
aethersdr-agent[bot] merged 1 commit into
mainfrom
aetherclaude/issue-2668
May 14, 2026
Merged

fix(cw): Int16 fallback for sidetone sink on Int16-only WASAPI devices (#2668). Principle I.#2669
aethersdr-agent[bot] merged 1 commit into
mainfrom
aetherclaude/issue-2668

Conversation

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Summary

Fixes #2668

What was changed

fix(cw): Int16 fallback for sidetone sink on Int16-only WASAPI devices (#2668). Principle I.

Files modified

  • src/core/CwSidetoneQAudioSink.cpp
  • src/core/CwSidetoneQAudioSink.h
 src/core/CwSidetoneQAudioSink.cpp | 60 ++++++++++++++++++++++++++++++++-------
 src/core/CwSidetoneQAudioSink.h   |  7 +++++
 2 files changed, 56 insertions(+), 11 deletions(-)

Generated by AetherClaude (automated agent for AetherSDR)

#2668). Principle I.

VB-Audio Virtual Cable's WASAPI endpoint advertises Int16 (not Float),
so the float-only probe in CwSidetoneQAudioSink::start() silently failed
to open and no local CW sidetone reached the PC headset — the original
complaint in #2629. SmartSDR produces sidetone against the same device,
so this is a client-side gap, not a user configuration issue.

Probe Float first (historical path), then Int16. Store the chosen
QAudioFormat::SampleFormat as a member so the timer-tick pump stays
branchless after the first compare; keep a reusable float scratch buffer
for the int16 path and clamp+convert to int16_t (×32767, saturated) into
the output chunk. Buffer-size math now keys off the chosen sample size,
not sizeof(float).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@aethersdr-agent aethersdr-agent Bot requested a review from ten9876 as a code owner May 14, 2026 21:19
@aethersdr-agent aethersdr-agent Bot enabled auto-merge (squash) May 14, 2026 21:19
@aethersdr-agent aethersdr-agent Bot requested a review from jensenpat as a code owner May 14, 2026 21:19
@jensenpat jensenpat added the priority: high High priority label May 14, 2026

@ten9876 ten9876 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. Int16 fallback is the right fix for Int16-only WASAPI endpoints (VB-Audio Virtual Cable). Float-first probe preserves the existing optimal path; pump uses a reused scratch buffer so the per-tick allocation cost stays out of the hot path.

@aethersdr-agent aethersdr-agent Bot merged commit c7ed576 into main May 14, 2026
5 checks passed
@aethersdr-agent aethersdr-agent Bot deleted the aetherclaude/issue-2668 branch May 14, 2026 22:38
ten9876 pushed a commit that referenced this pull request Jun 6, 2026
…3397)

## What

First, **pure-addition** step of the consolidated audio sink factory
(#3306): a single shared, OS-parameterized format/rate **negotiation
policy** that every audio sink and source will migrate onto, replacing
the ~9 divergent per-sink fallback ladders and per-OS `#ifdef` branches
that are the root of a recurring class of platform audio bugs.

Zero behaviour change — this is the maintainer-endorsed *"land the
helper first, migrate sinks incrementally"* approach (#3306, and the
#3194 thread's "sensitive audio-stack changes must be separate PRs with
soak time").

## Why this shape

The historical bugs escaped CI because the per-OS rate ladders were
compiled behind `Q_OS_*`, so a Linux runner could only ever exercise the
Linux path. The new policy is a **pure function over an injected
`DeviceCaps` snapshot with `TargetOs` as a parameter, not an `#ifdef`**
— so one headless, hardware-free test binary exercises the Windows,
macOS *and* Linux ladders on any runner.

## Contents

- **`src/core/AudioFormatNegotiator.{h,cpp}`** — dependency-free policy
(links only `Qt6::Core`). One ladder owns:
- Windows force-48k + r8brain (#2120/#2123), macOS 48k/A2DP (#1705),
Linux native-24k — divergences preserved as **data**, not `#ifdef`.
- The **universal 44.1 kHz rung** that RX/Quindar/QSO are missing today
(#3385).
- `Int16`↔`Float32` + `preferredFormat()` catch-all (#2669 / #1090 /
#3231).
- macOS mic preferred-rate-first (#2930) and Bluetooth-HFP native rate
(#2615); Windows probe-at-open (#2929).
- The two stereo resampler strategies — **PreservePan** (RX/QSO) vs
**MonoCollapse** (TCI-TX/RADE) — kept deliberately distinct
(#2403/#2459).
- **`tests/audio_format_negotiation_test.cpp`** — table-driven golden
matrix, registered under CTest. **25/25 pass**, including the
44.1k-only-device regression guard.
- **`docs/audio-sink-factory.md`** — the full three-layer design (pure
policy → live Qt wrapper → device-ownership router), the
device-following **"uncoupling"** fix (CW / RADE / Aetherial-Audio
"Pudu" recorder following the selected output), the regression-guard
invariants, and the one-sink-per-PR migration plan.

## Test

```
$ ./build/audio_format_negotiation_test
...
25/25 checks passed
```

## Follow-ups (per the migration plan in the doc)

Live Qt wrapper → migrate RX speaker → migrate PC mic →
`AudioOutputRouter` (closes the uncoupling class) → TCI-TX client-rate →
PipeWire DAX shared resampler. Each a separate, soakable PR.

Refs #3306

💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat

Co-authored-by: Codex <noreply@openai.com>
G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
…#3306) (aethersdr#3397)

## What

First, **pure-addition** step of the consolidated audio sink factory
(aethersdr#3306): a single shared, OS-parameterized format/rate **negotiation
policy** that every audio sink and source will migrate onto, replacing
the ~9 divergent per-sink fallback ladders and per-OS `#ifdef` branches
that are the root of a recurring class of platform audio bugs.

Zero behaviour change — this is the maintainer-endorsed *"land the
helper first, migrate sinks incrementally"* approach (aethersdr#3306, and the
aethersdr#3194 thread's "sensitive audio-stack changes must be separate PRs with
soak time").

## Why this shape

The historical bugs escaped CI because the per-OS rate ladders were
compiled behind `Q_OS_*`, so a Linux runner could only ever exercise the
Linux path. The new policy is a **pure function over an injected
`DeviceCaps` snapshot with `TargetOs` as a parameter, not an `#ifdef`**
— so one headless, hardware-free test binary exercises the Windows,
macOS *and* Linux ladders on any runner.

## Contents

- **`src/core/AudioFormatNegotiator.{h,cpp}`** — dependency-free policy
(links only `Qt6::Core`). One ladder owns:
- Windows force-48k + r8brain (aethersdr#2120/aethersdr#2123), macOS 48k/A2DP (aethersdr#1705),
Linux native-24k — divergences preserved as **data**, not `#ifdef`.
- The **universal 44.1 kHz rung** that RX/Quindar/QSO are missing today
(aethersdr#3385).
- `Int16`↔`Float32` + `preferredFormat()` catch-all (aethersdr#2669 / aethersdr#1090 /
aethersdr#3231).
- macOS mic preferred-rate-first (aethersdr#2930) and Bluetooth-HFP native rate
(aethersdr#2615); Windows probe-at-open (aethersdr#2929).
- The two stereo resampler strategies — **PreservePan** (RX/QSO) vs
**MonoCollapse** (TCI-TX/RADE) — kept deliberately distinct
(aethersdr#2403/aethersdr#2459).
- **`tests/audio_format_negotiation_test.cpp`** — table-driven golden
matrix, registered under CTest. **25/25 pass**, including the
44.1k-only-device regression guard.
- **`docs/audio-sink-factory.md`** — the full three-layer design (pure
policy → live Qt wrapper → device-ownership router), the
device-following **"uncoupling"** fix (CW / RADE / Aetherial-Audio
"Pudu" recorder following the selected output), the regression-guard
invariants, and the one-sink-per-PR migration plan.

## Test

```
$ ./build/audio_format_negotiation_test
...
25/25 checks passed
```

## Follow-ups (per the migration plan in the doc)

Live Qt wrapper → migrate RX speaker → migrate PC mic →
`AudioOutputRouter` (closes the uncoupling class) → TCI-TX client-rate →
PipeWire DAX shared resampler. Each a separate, soakable PR.

Refs aethersdr#3306

💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat

Co-authored-by: Codex <noreply@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: high High priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Disagree with closing https://github.com/ten9876/AetherSDR/issues/2629

2 participants