Skip to content

Commit 6fe4da8

Browse files
jensenpatcodex
andauthored
[ux] Add audio device prompt suppression controls (#2926)
## Summary - Add a `Don't ask me again` checkbox to the Audio Device Detected dialog. - Persist that choice as `SuppressAudioDeviceNotifications=True` in `AppSettings` so future new-device notifications can be permanently suppressed. - Respect the persisted suppression setting in the audio device hotplug flow while preserving the existing fallback that resets missing selected devices to the system default. - Add an inverse-linked `Prompt on Audio Device Changes` checkbox in Radio Setup > Audio > PC Audio Devices so users can re-enable or disable prompting from settings. - Document both controls in the audio pipeline hotplug section. ## Behavior Notes The dialog-level checkbox is intentionally one-way from the prompt itself: checking `Don't ask me again` suppresses future new audio device prompts whether the current dialog is accepted or canceled. The Radio Setup checkbox is the user-facing toggle for changing that preference later: checked means prompts are enabled; unchecked means `SuppressAudioDeviceNotifications=True` is active. The suppression gate only bypasses new-device selection prompts. Removal handling is left intact, so if the selected input or output disappears, AetherSDR still clears that stale selection and falls back to the current system default as before. ## Validation - `cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo` - `cmake --build build -j22` - Passed after rerunning the macOS app build outside the sandbox for `iconutil`, per project build guidance. - `ctest --test-dir build --output-on-failure -j22` - 15/15 tests passed. - Manually verified by @jensenpat in the built app. 👨🏼‍💻 Generated with OpenAI Codex (GPT-5.5 Pro 4/23) and tested by @jensenpat Co-authored-by: Codex <noreply@openai.com>
1 parent 7fd24dc commit 6fe4da8

5 files changed

Lines changed: 66 additions & 0 deletions

File tree

docs/audio-pipeline.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,15 @@ When at least one new device appears, AetherSDR shows a selection dialog with
209209
the current input/output highlighted, newly detected devices marked, and system
210210
defaults available as explicit choices.
211211

212+
The dialog includes a "Don't ask me again" checkbox. Checking it persists
213+
`SuppressAudioDeviceNotifications=True` in `AppSettings`; future device-add
214+
events skip the selection dialog while preserving the existing fallback to
215+
system default when a selected device disappears.
216+
217+
The same setting is exposed in Radio Setup > Audio > PC Audio Devices as
218+
"Prompt on Audio Device Changes"; that checkbox is checked when notifications
219+
are enabled and unchecked when the suppression setting is active.
220+
212221
Accepting the dialog queues `AudioEngine::setInputDevice()` and
213222
`AudioEngine::setOutputDevice()` onto the audio worker thread. Those setters are
214223
the only place that persists the chosen device IDs and restarts the affected

src/gui/AudioDeviceChangeDialog.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <QAudioDevice>
77
#include <QColor>
8+
#include <QCheckBox>
89
#include <QFrame>
910
#include <QFont>
1011
#include <QHBoxLayout>
@@ -37,6 +38,11 @@ const char* kDialogStyle =
3738
"QLabel#title { color: #ffffff; background: transparent; font-size: 18px; font-weight: bold; }"
3839
"QLabel#body { color: #c8d8e8; background: transparent; }"
3940
"QLabel#sectionLabel { color: #00b4d8; background: transparent; font-weight: bold; }"
41+
"QCheckBox { color: #c8d8e8; spacing: 8px; }"
42+
"QCheckBox::indicator { width: 16px; height: 16px;"
43+
" border: 1px solid #304050; border-radius: 3px; background: #0a0a14; }"
44+
"QCheckBox::indicator:hover { border-color: #00b4d8; }"
45+
"QCheckBox::indicator:checked { background: #00b4d8; border-color: #00c8f0; }"
4046
"QListWidget {"
4147
" background: #0a0a14;"
4248
" border: 1px solid #304050;"
@@ -299,6 +305,11 @@ AudioDeviceChangeDialog::AudioDeviceChangeDialog(
299305
buttonRow->setContentsMargins(0, 4, 0, 0);
300306
buttonRow->setSpacing(8);
301307

308+
m_dontAskAgainCheck = new QCheckBox(tr("Don't ask me again"), content);
309+
m_dontAskAgainCheck->setCursor(Qt::PointingHandCursor);
310+
buttonRow->addWidget(m_dontAskAgainCheck);
311+
buttonRow->addStretch(1);
312+
302313
auto* cancel = new QPushButton(tr("Cancel"), content);
303314
cancel->setObjectName("cancelButton");
304315
cancel->setCursor(Qt::PointingHandCursor);
@@ -377,4 +388,9 @@ QAudioDevice AudioDeviceChangeDialog::selectedOutputDevice() const
377388
return selectedDevice(m_outputList, m_outputDevices);
378389
}
379390

391+
bool AudioDeviceChangeDialog::dontAskAgainChecked() const
392+
{
393+
return m_dontAskAgainCheck && m_dontAskAgainCheck->isChecked();
394+
}
395+
380396
} // namespace AetherSDR

src/gui/AudioDeviceChangeDialog.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <QList>
77

88
class QListWidget;
9+
class QCheckBox;
910
class QVBoxLayout;
1011
class QWidget;
1112

@@ -23,6 +24,7 @@ class AudioDeviceChangeDialog : public QDialog {
2324

2425
QAudioDevice selectedInputDevice() const;
2526
QAudioDevice selectedOutputDevice() const;
27+
bool dontAskAgainChecked() const;
2628
void setFramelessMode(bool on);
2729

2830
private:
@@ -33,6 +35,7 @@ class AudioDeviceChangeDialog : public QDialog {
3335
QVBoxLayout* m_bodyLayout{nullptr};
3436
QListWidget* m_inputList{nullptr};
3537
QListWidget* m_outputList{nullptr};
38+
QCheckBox* m_dontAskAgainCheck{nullptr};
3639
QList<QAudioDevice> m_inputDevices;
3740
QList<QAudioDevice> m_outputDevices;
3841
};

src/gui/MainWindow.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ constexpr int kSwrSweepTuneStopTimeoutMs = 1800;
229229
constexpr int kSwrSweepTgxlRestoreTimeoutMs = 3500;
230230
constexpr int kSwrSweepMaxPoints = 260;
231231
constexpr double kMemoryRevealTargetToleranceMhz = 0.000001;
232+
constexpr const char* kSuppressAudioDeviceNotificationsKey =
233+
"SuppressAudioDeviceNotifications";
232234

233235
bool isTransientAudioDeviceId(const QByteArray& id)
234236
{
@@ -6986,6 +6988,18 @@ void MainWindow::handleAudioDeviceListChanged()
69866988
return;
69876989
}
69886990

6991+
const bool suppressAudioDeviceNotifications =
6992+
AppSettings::instance()
6993+
.value(kSuppressAudioDeviceNotificationsKey, "False")
6994+
.toString() == "True";
6995+
if (suppressAudioDeviceNotifications) {
6996+
if (resetInput || resetOutput)
6997+
resetMissingAudioDevicesToDefault(resetInput,
6998+
resetOutput,
6999+
reinitializePcInput);
7000+
return;
7001+
}
7002+
69897003
m_audioDeviceDialogOpen = true;
69907004
AudioDeviceChangeDialog dialog(inputDevices,
69917005
outputDevices,
@@ -6997,6 +7011,12 @@ void MainWindow::handleAudioDeviceListChanged()
69977011
const int result = dialog.exec();
69987012
m_audioDeviceDialogOpen = false;
69997013

7014+
if (dialog.dontAskAgainChecked()) {
7015+
auto& settings = AppSettings::instance();
7016+
settings.setValue(kSuppressAudioDeviceNotificationsKey, "True");
7017+
settings.save();
7018+
}
7019+
70007020
if (result == QDialog::Accepted) {
70017021
const QAudioDevice selectedInput = dialog.selectedInputDevice();
70027022
const QAudioDevice selectedOutput = dialog.selectedOutputDevice();

src/gui/RadioSetupDialog.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ static const QString kEditStyle =
7676
"QLineEdit { background: #1a2a3a; border: 1px solid #304050; "
7777
"border-radius: 3px; color: #c8d8e8; font-size: 12px; padding: 2px 4px; }";
7878

79+
static constexpr const char* kSuppressAudioDeviceNotificationsKey =
80+
"SuppressAudioDeviceNotifications";
81+
7982
static QString normalizedOscillatorValue(QString value)
8083
{
8184
value = value.trimmed().toLower();
@@ -1889,6 +1892,21 @@ QWidget* RadioSetupDialog::buildAudioTab()
18891892
outRow->addWidget(outCombo, 1);
18901893
pcLayout->addLayout(outRow);
18911894

1895+
auto* promptCheck = new QCheckBox("Prompt on Audio Device Changes");
1896+
promptCheck->setStyleSheet("QCheckBox { color: #c8d8e8; font-size: 11px; }");
1897+
promptCheck->setToolTip("Show the Audio Device Detected dialog when a new PC audio device appears.");
1898+
const bool suppressAudioDeviceNotifications =
1899+
AppSettings::instance()
1900+
.value(kSuppressAudioDeviceNotificationsKey, "False")
1901+
.toString() == "True";
1902+
promptCheck->setChecked(!suppressAudioDeviceNotifications);
1903+
connect(promptCheck, &QCheckBox::toggled, this, [](bool on) {
1904+
auto& s = AppSettings::instance();
1905+
s.setValue(kSuppressAudioDeviceNotificationsKey, on ? "False" : "True");
1906+
s.save();
1907+
});
1908+
pcLayout->addWidget(promptCheck);
1909+
18921910
// Wire device changes to AudioEngine
18931911
if (m_audio) {
18941912
// Route through QueuedConnection so setInputDevice/setOutputDevice

0 commit comments

Comments
 (0)