diff --git a/src/trx-server/src/audio.rs b/src/trx-server/src/audio.rs index c405acf..191f49f 100644 --- a/src/trx-server/src/audio.rs +++ b/src/trx-server/src/audio.rs @@ -241,6 +241,7 @@ pub fn spawn_audio_capture( cfg: &AudioConfig, tx: broadcast::Sender, pcm_tx: Option>>, + shutdown_rx: watch::Receiver, ) -> std::thread::JoinHandle<()> { let sample_rate = cfg.sample_rate; let channels = cfg.channels as u16; @@ -257,6 +258,7 @@ pub fn spawn_audio_capture( device_name, tx, pcm_tx, + shutdown_rx, ) { error!("Audio capture thread error: {}", e); } @@ -271,6 +273,7 @@ fn run_capture( device_name: Option, tx: broadcast::Sender, pcm_tx: Option>>, + shutdown_rx: watch::Receiver, ) -> Result<(), Box> { use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use std::sync::mpsc::{RecvTimeoutError, TryRecvError as StdTryRecvError}; @@ -305,6 +308,11 @@ fn run_capture( let mut capturing = false; loop { + if *shutdown_rx.borrow() { + info!("Audio capture: shutdown signal received, exiting"); + return Ok(()); + } + // Re-enumerate the device on every recovery cycle: after POLLERR the // existing device handle can be stale (especially for USB audio). let host = cpal::default_host(); @@ -383,6 +391,11 @@ fn run_capture( } loop { + if *shutdown_rx.borrow() { + info!("Audio capture: shutdown signal received, exiting"); + return Ok(()); + } + match stream_err_rx.try_recv() { Ok(()) | Err(StdTryRecvError::Disconnected) => { warn!("Audio capture: backend stream error, recreating"); @@ -458,6 +471,7 @@ fn run_capture( pub fn spawn_audio_playback( cfg: &AudioConfig, rx: mpsc::Receiver, + shutdown_rx: watch::Receiver, ) -> std::thread::JoinHandle<()> { let sample_rate = cfg.sample_rate; let channels = cfg.channels as u16; @@ -465,7 +479,9 @@ pub fn spawn_audio_playback( let device_name = cfg.device.clone(); std::thread::spawn(move || { - if let Err(e) = run_playback(sample_rate, channels, frame_duration_ms, device_name, rx) { + if let Err(e) = + run_playback(sample_rate, channels, frame_duration_ms, device_name, rx, shutdown_rx) + { error!("Audio playback thread error: {}", e); } }) @@ -477,6 +493,7 @@ fn run_playback( frame_duration_ms: u16, device_name: Option, mut rx: mpsc::Receiver, + shutdown_rx: watch::Receiver, ) -> Result<(), Box> { use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use std::sync::mpsc::TryRecvError as StdTryRecvError; @@ -513,6 +530,11 @@ fn run_playback( let mut channel_closed = false; loop { + if *shutdown_rx.borrow() { + info!("Audio playback: shutdown signal received, exiting"); + return Ok(()); + } + // Re-enumerate the device on every recovery cycle: after POLLERR the // existing device handle can be stale (especially for USB audio). let host = cpal::default_host(); @@ -600,6 +622,11 @@ fn run_playback( } loop { + if *shutdown_rx.borrow() { + info!("Audio playback: shutdown signal received, exiting"); + return Ok(()); + } + match stream_err_rx.try_recv() { Ok(()) | Err(StdTryRecvError::Disconnected) => { warn!("Audio playback: backend stream error, recreating"); diff --git a/src/trx-server/src/main.rs b/src/trx-server/src/main.rs index 3d94624..7bb89fa 100644 --- a/src/trx-server/src/main.rs +++ b/src/trx-server/src/main.rs @@ -474,6 +474,7 @@ fn spawn_rig_audio_stack( &rig_cfg.audio, rx_audio_tx.clone(), Some(pcm_tx.clone()), + shutdown_rx.clone(), ); } @@ -542,7 +543,8 @@ fn spawn_rig_audio_stack( } if rig_cfg.audio.tx_enabled { - let _playback_thread = audio::spawn_audio_playback(&rig_cfg.audio, tx_audio_rx); + let _playback_thread = + audio::spawn_audio_playback(&rig_cfg.audio, tx_audio_rx, shutdown_rx.clone()); } let audio_shutdown_rx = shutdown_rx.clone();