Skip to content

Commit 587a679

Browse files
committed
Harden Win10 startup with staged audio init guards
1 parent ca0bc84 commit 587a679

File tree

2 files changed

+118
-33
lines changed

2 files changed

+118
-33
lines changed

src/gui/app.cpp

Lines changed: 112 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -811,16 +811,27 @@ App::App(const Options& opts) : options_(opts), sim_ui_visible_(opts.enable_sim)
811811
initAudio();
812812
if (audio_initialized_) {
813813
std::string output_dev = getOutputDeviceName();
814-
audio_.openOutput(output_dev);
815-
audio_.startPlayback();
816-
startRadioRx();
814+
if (audio_.openOutput(output_dev)) {
815+
audio_.startPlayback();
816+
if (audio_.isPlaying()) {
817+
deferred_radio_rx_start_pending_ = true;
818+
deferred_radio_rx_start_deadline_ms_ = SDL_GetTicks() + 250;
819+
deferred_radio_rx_start_attempts_ = 0;
820+
guiLog("Startup audio stage: playback active, delaying RX capture by 250ms");
821+
} else {
822+
guiLog("Startup audio stage: playback did not start, capture deferred");
823+
}
824+
} else {
825+
guiLog("Startup audio stage: openOutput failed");
826+
}
817827
}
818828
ultra::gui::startupTrace("App", "init-audio-exit");
819829
} else {
820830
guiLog("Safe startup enabled: deferred audio/simulator initialization");
821831
deferred_audio_auto_init_pending_ = true;
822832
deferred_audio_auto_init_deadline_ms_ = SDL_GetTicks() + 300;
823833
deferred_audio_auto_init_attempts_ = 0;
834+
deferred_audio_wait_logged_ = false;
824835
ultra::gui::startupTrace("App", "deferred-audio-scheduled");
825836
}
826837
ultra::gui::startupTrace("App", "ctor-body-exit");
@@ -1321,6 +1332,8 @@ void App::initAudio() {
13211332
// Populate settings window device lists
13221333
settings_window_.input_devices = input_devices_;
13231334
settings_window_.output_devices = output_devices_;
1335+
guiLog("initAudio: enumerated %zu input device(s), %zu output device(s)",
1336+
input_devices_.size(), output_devices_.size());
13241337

13251338
audio_initialized_ = true;
13261339
ultra::gui::startupTrace("App", "initAudio-exit");
@@ -1484,6 +1497,7 @@ void App::updateAdaptiveAdvisory(float snr_db, float fading_index) {
14841497

14851498
void App::render() {
14861499
static bool first_render = true;
1500+
render_frames_seen_++;
14871501
if (first_render) {
14881502
ultra::gui::startupTrace("App", "render-enter");
14891503
}
@@ -1500,40 +1514,82 @@ void App::render() {
15001514
// Avoids feeding modem state directly from SDL callback threads.
15011515
pollRadioRx();
15021516

1503-
// Safe-startup mode: auto-start audio shortly after first frame.
1517+
// Safe-startup mode: auto-start audio shortly after first frames.
15041518
// Keeps process bring-up lightweight while preserving "auto-listen" behavior.
15051519
if (deferred_audio_auto_init_pending_ &&
15061520
!simulation_enabled_ &&
15071521
!audio_initialized_) {
1508-
uint32_t now_ms = SDL_GetTicks();
1509-
if (now_ms >= deferred_audio_auto_init_deadline_ms_) {
1510-
ultra::gui::startupTrace("App", "deferred-audio-init-enter");
1511-
initAudio();
1512-
if (audio_initialized_) {
1513-
ultra::gui::startupTrace("App", "deferred-audio-open-output-enter");
1514-
std::string output_dev = getOutputDeviceName();
1515-
if (audio_.openOutput(output_dev)) {
1516-
audio_.startPlayback();
1517-
startRadioRx();
1518-
guiLog("Deferred audio auto-init succeeded");
1519-
ultra::gui::startupTrace("App", "deferred-audio-open-output-exit");
1520-
deferred_audio_auto_init_pending_ = false;
1522+
if (render_frames_seen_ < 6) {
1523+
if (!deferred_audio_wait_logged_) {
1524+
guiLog("Deferred audio guard: waiting for UI warm-up frames (%u/6)",
1525+
render_frames_seen_);
1526+
deferred_audio_wait_logged_ = true;
1527+
}
1528+
} else {
1529+
deferred_audio_wait_logged_ = false;
1530+
uint32_t now_ms = SDL_GetTicks();
1531+
if (now_ms >= deferred_audio_auto_init_deadline_ms_) {
1532+
ultra::gui::startupTrace("App", "deferred-audio-init-enter");
1533+
initAudio();
1534+
if (audio_initialized_) {
1535+
ultra::gui::startupTrace("App", "deferred-audio-open-output-enter");
1536+
std::string output_dev = getOutputDeviceName();
1537+
if (audio_.openOutput(output_dev)) {
1538+
audio_.startPlayback();
1539+
if (audio_.isPlaying()) {
1540+
deferred_radio_rx_start_pending_ = true;
1541+
deferred_radio_rx_start_deadline_ms_ = now_ms + 250;
1542+
deferred_radio_rx_start_attempts_ = 0;
1543+
guiLog("Deferred audio stage 1/2 complete: playback active, waiting 250ms before capture");
1544+
ultra::gui::startupTrace("App", "deferred-audio-open-output-exit");
1545+
deferred_audio_auto_init_pending_ = false;
1546+
} else {
1547+
guiLog("Deferred audio auto-init: playback did not start");
1548+
ultra::gui::startupTrace("App", "deferred-audio-open-output-fail");
1549+
deferred_audio_auto_init_pending_ = false;
1550+
}
1551+
} else {
1552+
guiLog("Deferred audio auto-init: openOutput failed");
1553+
ultra::gui::startupTrace("App", "deferred-audio-open-output-fail");
1554+
deferred_audio_auto_init_pending_ = false;
1555+
}
15211556
} else {
1522-
guiLog("Deferred audio auto-init: openOutput failed");
1523-
ultra::gui::startupTrace("App", "deferred-audio-open-output-fail");
1524-
deferred_audio_auto_init_pending_ = false;
1557+
deferred_audio_auto_init_attempts_++;
1558+
ultra::gui::startupTrace("App", "deferred-audio-init-fail");
1559+
if (deferred_audio_auto_init_attempts_ >= 3) {
1560+
guiLog("Deferred audio auto-init failed after %d attempts, manual init required",
1561+
deferred_audio_auto_init_attempts_);
1562+
deferred_audio_auto_init_pending_ = false;
1563+
} else {
1564+
deferred_audio_auto_init_deadline_ms_ = now_ms + 700;
1565+
guiLog("Deferred audio auto-init retry %d/3 scheduled",
1566+
deferred_audio_auto_init_attempts_ + 1);
1567+
}
15251568
}
1569+
}
1570+
}
1571+
}
1572+
1573+
// Stage 2: start RX capture only after playback is confirmed and warm.
1574+
if (deferred_radio_rx_start_pending_ &&
1575+
!simulation_enabled_ &&
1576+
audio_initialized_) {
1577+
uint32_t now_ms = SDL_GetTicks();
1578+
if (now_ms >= deferred_radio_rx_start_deadline_ms_) {
1579+
if (startRadioRx()) {
1580+
guiLog("Deferred audio stage 2/2 complete: RX capture started");
1581+
deferred_radio_rx_start_pending_ = false;
1582+
deferred_radio_rx_start_attempts_ = 0;
15261583
} else {
1527-
deferred_audio_auto_init_attempts_++;
1528-
ultra::gui::startupTrace("App", "deferred-audio-init-fail");
1529-
if (deferred_audio_auto_init_attempts_ >= 3) {
1530-
guiLog("Deferred audio auto-init failed after %d attempts, manual init required",
1531-
deferred_audio_auto_init_attempts_);
1532-
deferred_audio_auto_init_pending_ = false;
1584+
deferred_radio_rx_start_attempts_++;
1585+
if (deferred_radio_rx_start_attempts_ >= 5) {
1586+
guiLog("Deferred audio stage 2/2 failed after %d attempts; manual audio init required",
1587+
deferred_radio_rx_start_attempts_);
1588+
deferred_radio_rx_start_pending_ = false;
15331589
} else {
1534-
deferred_audio_auto_init_deadline_ms_ = now_ms + 700;
1535-
guiLog("Deferred audio auto-init retry %d/3 scheduled",
1536-
deferred_audio_auto_init_attempts_ + 1);
1590+
deferred_radio_rx_start_deadline_ms_ = now_ms + 500;
1591+
guiLog("Deferred audio stage 2/2 retry %d/5 scheduled",
1592+
deferred_radio_rx_start_attempts_ + 1);
15371593
}
15381594
}
15391595
}
@@ -1720,20 +1776,44 @@ std::string App::getOutputDeviceName() const {
17201776
return settings_.output_device;
17211777
}
17221778

1723-
void App::startRadioRx() {
1724-
if (!audio_initialized_ || simulation_enabled_) return;
1779+
bool App::startRadioRx() {
1780+
if (!audio_initialized_) {
1781+
guiLog("startRadioRx guard: audio not initialized");
1782+
return false;
1783+
}
1784+
if (simulation_enabled_) {
1785+
guiLog("startRadioRx guard: simulation enabled");
1786+
return false;
1787+
}
1788+
if (!audio_.isPlaying()) {
1789+
guiLog("startRadioRx guard: playback not active yet");
1790+
return false;
1791+
}
1792+
if (radio_rx_enabled_) {
1793+
return true;
1794+
}
17251795

17261796
std::string input_dev = getInputDeviceName();
17271797
if (!audio_.openInput(input_dev)) {
1728-
return;
1798+
guiLog("startRadioRx: openInput failed for '%s'",
1799+
input_dev.empty() ? "Default" : input_dev.c_str());
1800+
return false;
17291801
}
17301802

17311803
// Main thread polls captured samples via pollRadioRx().
17321804
audio_.setRxCallback(AudioEngine::RxCallback{});
17331805

17341806
audio_.setLoopbackEnabled(false);
17351807
audio_.startCapture();
1808+
if (!audio_.isCapturing()) {
1809+
guiLog("startRadioRx: capture did not start");
1810+
audio_.closeInput();
1811+
return false;
1812+
}
17361813
radio_rx_enabled_ = true;
1814+
guiLog("startRadioRx: capture started on '%s'",
1815+
input_dev.empty() ? "Default" : input_dev.c_str());
1816+
return true;
17371817
}
17381818

17391819
void App::stopRadioRx() {

src/gui/app.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ class App {
7272
bool deferred_audio_auto_init_pending_ = false;
7373
uint32_t deferred_audio_auto_init_deadline_ms_ = 0;
7474
int deferred_audio_auto_init_attempts_ = 0;
75+
bool deferred_audio_wait_logged_ = false;
76+
bool deferred_radio_rx_start_pending_ = false;
77+
uint32_t deferred_radio_rx_start_deadline_ms_ = 0;
78+
int deferred_radio_rx_start_attempts_ = 0;
79+
uint32_t render_frames_seen_ = 0;
7580
std::atomic<bool> tx_in_progress_{false}; // Thread-safe TX flag for waterfall control
7681
std::chrono::steady_clock::time_point tx_end_time_; // When current TX finishes
7782

@@ -195,7 +200,7 @@ class App {
195200
void onDataReceived(const std::string& text);
196201
void resetAdaptiveAdvisory();
197202
void updateAdaptiveAdvisory(float snr_db, float fading_index);
198-
void startRadioRx();
203+
bool startRadioRx();
199204
void stopRadioRx();
200205
void pollRadioRx();
201206

0 commit comments

Comments
 (0)