Skip to content

[logging] Add default audio summary logging (devices/sinks/resampler)#2973

Merged
ten9876 merged 3 commits into
aethersdr:mainfrom
jensenpat:aether/audio-summary-logging
May 23, 2026
Merged

[logging] Add default audio summary logging (devices/sinks/resampler)#2973
ten9876 merged 3 commits into
aethersdr:mainfrom
jensenpat:aether/audio-summary-logging

Conversation

@jensenpat

Copy link
Copy Markdown
Collaborator

Summary

Implements #2971 by adding a dedicated default-on aether.audio.summary logging path for support-bundle-friendly audio routing and negotiation summaries.

This adds compact, deduped summary blocks for:

  • startup audio environment: selected/default input and output devices, saved-device presence, PC Audio state, and TX mic route intent
  • RX sink startup: actual output device, sample rate, channel count, sample format, resampling state, and fallback/substitution history
  • TX source startup: actual input device, negotiated rate/channels, mono/stereo state, sample format, resampling-to-24 kHz state, and fallback history
  • CW sidetone startup: backend, actual/backend-selected device, rate, backend substitution, and fallback history
  • auxiliary local sinks when instantiated: Quindar local sink and Aetherial monitor playback sink
  • final open failures: attempted rates/channels/formats, backend, device, failure reason, and fallback history

Why

The normal support log already caught many warning paths, but it did not reliably capture successful runtime sink/source negotiation. That left recurring audio/CW reports without the details needed to distinguish wrong endpoint, sample-rate mismatch, resampler path, backend fallback, and complete open failure cases.

Important Safety Detail

The startup summary uses a new shallow DeviceDiagnostics::buildAudioStartupSnapshot(...) helper. It intentionally does not call support-bundle-grade capability probes such as preferredFormat() or isFormatSupported() across devices at app startup. Full device capability probing remains in buildAudioDevicesSnapshot(...) for explicit diagnostics/support flows.

Failure summaries only report negotiation work the audio path already attempted; they do not add extra probing.

Historical Cases Covered

The summary path is designed around recurring issue/PR classes including:

Implementation Notes

  • Adds AudioSummaryLogger with canonical one-block formatters and dedupe by event key.
  • Adds aether.audio.summary as a default-on info category without enabling the full aether.audio info/debug stream.
  • Keeps detailed existing aether.audio diagnostics intact.
  • Documents the summary logging policy in docs/audio-pipeline.md.

Validation

  • cmake --build build -j22
  • ./build/device_diagnostics_test
  • ./build/cw_sidetone_test
  • git diff --check
  • Built and deployed a macOS test bundle; the primary test-machine replacement path is currently blocked by a Desktop delete ACL, so a separate quarantine-cleared test bundle was prepared.

👨🏼‍💻 Generated with OpenAI Codex (GPT-5.5 Pro 4/23) and tested by @jensenpat

Co-authored-by: Codex <noreply@openai.com>
@jensenpat jensenpat changed the title Add default audio summary logging [logging] Add default audio summary logging (devices/sinks/resampler) May 22, 2026
@jensenpat jensenpat marked this pull request as ready for review May 22, 2026 21:46
Co-authored-by: Codex <noreply@openai.com>
@jensenpat jensenpat linked an issue May 22, 2026 that may be closed by this pull request
8 tasks

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @jensenpat — this is a thoughtful piece of work. The "no extra probing at startup" safety boundary (shallow buildAudioStartupSnapshot vs the probe-heavy buildAudioDevicesSnapshot) is well-drawn and explicitly motivated. Dedupe-by-canonical-text keeps no-op restarts quiet without losing real route changes. Categorising every open path (RX/TX/CW/Quindar/Aetherial + a single failure summary on each path) hits exactly the support-bundle gap described.

A few small observations:

1. Out-of-scope behaviour change in the Windows RX path. src/core/AudioEngine.cpp line ~968 now calls m_rxBufferSampleRate.store(fmt.sampleRate()) on the Windows #ifdef Q_OS_WIN branch before startSidetoneStream(). Previously this store happened only on the non-Windows path after the #endif, so the Windows branch was leaving m_rxBufferSampleRate at its prior value. This looks like a latent bug being silently fixed alongside the summary work. Probably correct, but worth either splitting into its own commit or calling out in the PR description so it isn't lost — it's a behaviour change that has nothing to do with logging.

2. aether.audio.summary cannot be disabled from the LogManager UI. LogManager::applyFilterRules() unconditionally appends aether.audio.summary.info=true before the per-category loop, and the loop only emits .debug=true rules. So toggling the category off in the UI still leaves info-level summaries flowing. This is consistent with how warnings on aether.audio are always-on, so it may be intentional ("can't break support bundles by turning it off") — but worth confirming, since a user reading the LogManager dialog would reasonably expect the toggle to silence the category.

3. buildAudioStartupSnapshot duplicates ~80 lines of buildAudioDevicesSnapshot. The PR's justification (avoid format probes at startup) is correct and explicit, but the duplication is large enough that a small private helper for the "shallow fields" (counts, saved/selected/default IDs, rx_route, tx_route, volumes) — with the two callers differing only in deviceToJson vs basicDeviceToJson — would reduce drift risk if either side changes in future. Not a blocker.

4. Minor: audioErrorName() correctly guards QAudio::UnderrunError with QT_VERSION < 6.11.0. Nice catch.

No bugs found beyond #1 above; AppSettings is used correctly, RAII in AudioSummaryLogger (Meyer's-singleton mutex + hash) is fine, and the namespace resolution into AetherSDR::AudioSummaryLogger from within AudioEngine.cpp's namespace AetherSDR { ... } block works as intended.


🤖 aethersdr-agent · cost: $14.8697 · model: claude-opus-4-7

@jensenpat

Copy link
Copy Markdown
Collaborator Author

Thanks for catching the first point. That line was intentional, but I agree it should be called out because it can look like hidden Windows audio-flow work.

On Windows, startRxStream() opens the RX sink inside the #ifdef Q_OS_WIN branch and returns from that branch before reaching the later common/non-Windows m_rxBufferSampleRate.store(fmt.sampleRate()) path. So after a successful 48 kHz WASAPI negotiation, m_rxBufferSampleRate could remain at the RX-start default of 24 kHz.

That value is diagnostic state only. It is used by NetworkDiagnosticsDialog to convert RX buffer bytes into milliseconds/peak-buffer display values. The actual audio path does not use it for routing, sink selection, resampler choice, drain cadence, sidetone startup, or device negotiation; those continue to use m_resampleTo48k, the opened QAudioSink, and the negotiated fmt directly.

So the change corrects Windows diagnostic/reporting accuracy for the negotiated RX sink rate. It should not affect audio flow. I will call this out in the PR description so it does not surprise reviewers.

Main extracted findPortAudioOutputDevice / defaultPortAudioOutputDevice
into namespace-anonymous helpers (PR aethersdr#2899/aethersdr#2901 era), which moves the
JACK-default-output selection out of start() into a helper. Weave the
PR's new state-tracking calls (m_deviceDescription, m_fallbackOccurred,
m_fallbackReason) into main's post-extraction start(), and detect the
JACK-host-API selection by inspecting devInfo->hostApi after the
helper returns so we can still mark it as a fallback for the summary.

# Conflicts:
#	src/core/CwSidetonePortAudioSink.cpp
@ten9876 ten9876 merged commit b89f7ac into aethersdr:main May 23, 2026
4 checks passed
@ten9876

ten9876 commented May 23, 2026

Copy link
Copy Markdown
Collaborator

Merged — thanks @jensenpat! The 'no extra probing at startup' boundary between buildAudioStartupSnapshot and buildAudioDevicesSnapshot is exactly the right safety boundary for a default-on logging path, and the dedupe-by-canonical-text keeps no-op restarts quiet without losing real route changes. Will quietly explain a lot of historical macOS Bluetooth / Float32-vs-Int16 / CW endpoint reports the next time they recur.

Resolved a stale-base conflict in CwSidetonePortAudioSink.cpp — main extracted findPortAudioOutputDevice / defaultPortAudioOutputDevice (PR #2899/#2901 era) which moved the JACK selection into a namespace-scope helper. Wove your state-tracking into the post-extraction start() and detect JACK after-return from the helper.

Shipping in the next release.

73,
Jeremy KK7GWY & Claude (AI dev partner)

G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
…aethersdr#2973)

## Summary

Implements aethersdr#2971 by adding a dedicated default-on `aether.audio.summary`
logging path for support-bundle-friendly audio routing and negotiation
summaries.

This adds compact, deduped summary blocks for:

- startup audio environment: selected/default input and output devices,
saved-device presence, PC Audio state, and TX mic route intent
- RX sink startup: actual output device, sample rate, channel count,
sample format, resampling state, and fallback/substitution history
- TX source startup: actual input device, negotiated rate/channels,
mono/stereo state, sample format, resampling-to-24 kHz state, and
fallback history
- CW sidetone startup: backend, actual/backend-selected device, rate,
backend substitution, and fallback history
- auxiliary local sinks when instantiated: Quindar local sink and
Aetherial monitor playback sink
- final open failures: attempted rates/channels/formats, backend,
device, failure reason, and fallback history

## Why

The normal support log already caught many warning paths, but it did not
reliably capture successful runtime sink/source negotiation. That left
recurring audio/CW reports without the details needed to distinguish
wrong endpoint, sample-rate mismatch, resampler path, backend fallback,
and complete open failure cases.

## Important Safety Detail

The startup summary uses a new shallow
`DeviceDiagnostics::buildAudioStartupSnapshot(...)` helper. It
intentionally does not call support-bundle-grade capability probes such
as `preferredFormat()` or `isFormatSupported()` across devices at app
startup. Full device capability probing remains in
`buildAudioDevicesSnapshot(...)` for explicit diagnostics/support flows.

Failure summaries only report negotiation work the audio path already
attempted; they do not add extra probing.

## Historical Cases Covered

The summary path is designed around recurring issue/PR classes
including:

- macOS Bluetooth/HFP route substitution and 24 kHz vs 48 kHz behavior
(aethersdr#1486, aethersdr#2634)
- CommonRadioAudio / Float32 vs Int16 format issues (aethersdr#1070, aethersdr#1090)
- CW sidetone endpoint/backend routing (aethersdr#2072, aethersdr#2075, aethersdr#2899, aethersdr#2901)
- CW sidetone missing/distorted reports (aethersdr#2629, aethersdr#2694)
- no-audio-with-active-meters / output negotiation support gaps (aethersdr#1855)

## Implementation Notes

- Adds `AudioSummaryLogger` with canonical one-block formatters and
dedupe by event key.
- Adds `aether.audio.summary` as a default-on info category without
enabling the full `aether.audio` info/debug stream.
- Keeps detailed existing `aether.audio` diagnostics intact.
- Documents the summary logging policy in `docs/audio-pipeline.md`.

## Validation

- `cmake --build build -j22`
- `./build/device_diagnostics_test`
- `./build/cw_sidetone_test`
- `git diff --check`
- Built and deployed a macOS test bundle; the primary test-machine
replacement path is currently blocked by a Desktop delete ACL, so a
separate quarantine-cleared test bundle was prepared.

👨🏼‍💻 Generated with OpenAI Codex (GPT-5.5 Pro 4/23) and tested by
@jensenpat

---------

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Default logs: consolidated audio sink / sample-rate summary for troubleshooting

2 participants