@@ -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
14851498void 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
17391819void App::stopRadioRx () {
0 commit comments