diff --git a/src/trx-client/src/audio_client.rs b/src/trx-client/src/audio_client.rs index 9d9a53c..5289943 100644 --- a/src/trx-client/src/audio_client.rs +++ b/src/trx-client/src/audio_client.rs @@ -570,35 +570,35 @@ pub async fn run_multi_rig_audio_manager( // // On each audio connection, register the connected rig's per-rig channels // so per-rig /audio?rig_id= subscribers get data. - let selected_clone = selected_rig_id.clone(); let rig_audio_rx_clone = rig_audio_rx.clone(); let rig_audio_info_clone = rig_audio_info.clone(); - // Spawn a task that keeps per-rig maps in sync with the selected rig. + // Spawn a task that keeps per-rig audio/info maps populated for ALL + // known rigs (not just the selected one). Non-connected rigs get valid + // but silent channels so `/audio?rig_id=X` can subscribe instantly + // instead of timing out. + let known_rigs_for_sync = known_rigs.clone(); let mut sync_shutdown = shutdown_rx.clone(); tokio::spawn(async move { - let mut last_rig: Option = None; let mut interval = time::interval(Duration::from_millis(500)); loop { tokio::select! { _ = interval.tick() => { - let current = selected_clone.lock().ok().and_then(|v| v.clone()); - if current != last_rig { - // Ensure per-rig broadcast exists for new rig. - if let Some(ref rig_id) = current { - if let Ok(mut map) = rig_audio_rx_clone.write() { - map.entry(rig_id.clone()) - .or_insert_with(|| broadcast::channel::(256).0); - } - if let Ok(mut map) = rig_audio_info_clone.write() { - map.entry(rig_id.clone()) - .or_insert_with(|| watch::channel(None).0); - } + let rig_ids: Vec = known_rigs_for_sync + .lock() + .ok() + .map(|entries| entries.iter().map(|e| e.rig_id.clone()).collect()) + .unwrap_or_default(); + for rig_id in &rig_ids { + if let Ok(mut map) = rig_audio_rx_clone.write() { + map.entry(rig_id.clone()) + .or_insert_with(|| broadcast::channel::(256).0); + } + if let Ok(mut map) = rig_audio_info_clone.write() { + map.entry(rig_id.clone()) + .or_insert_with(|| watch::channel(None).0); } - last_rig = current; } - // Mirror global audio data to the current rig's per-rig channel. - // (The actual mirroring happens in the RX read task below.) } changed = sync_shutdown.changed() => { if matches!(changed, Ok(()) | Err(_)) && *sync_shutdown.borrow() { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/audio.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/audio.rs index 9c1dac4..cdf6f97 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/src/audio.rs +++ b/src/trx-client/trx-frontend/trx-frontend-http/src/audio.rs @@ -519,15 +519,20 @@ pub async fn audio_ws( } } else if let Some(ref rig_id) = query.rig_id { // Per-rig audio: subscribe to the specific rig's broadcast. - match context.rig_audio_subscribe(rig_id) { - Some(rx) => rx, - None => { - // Rig not yet connected; fall back to global. - let Some(rx) = context.audio_rx.as_ref() else { - return Ok(HttpResponse::NotFound().body("audio not enabled")); - }; - rx.subscribe() + // Do NOT fall back to global — that would silently deliver the wrong + // rig's audio. Wait briefly for the per-rig channel to appear (it is + // lazily created by the audio relay sync task every 500ms). + let deadline = Instant::now() + Duration::from_secs(3); + loop { + if let Some(rx) = context.rig_audio_subscribe(rig_id) { + break rx; } + if Instant::now() >= deadline { + return Ok( + HttpResponse::NotFound().body(format!("audio not available for rig {rig_id}")) + ); + } + tokio::time::sleep(Duration::from_millis(100)).await; } } else { let Some(rx) = context.audio_rx.as_ref() else {