diff --git a/src/bin/daemon.rs b/src/bin/daemon.rs index ee9fcc6..057dacf 100644 --- a/src/bin/daemon.rs +++ b/src/bin/daemon.rs @@ -40,7 +40,11 @@ async fn main() -> Result<(), Box> { println!("Successfully linked player to virtual mic."); break; } - Err(e) => println!("{e}\t{i}/{max_retries}"), + Err(e) => { + if i == 0 || i == max_retries { + eprintln!("{e} (attempt {i}/{max_retries})"); + } + } } sleep(Duration::from_millis(1000)).await; @@ -174,19 +178,25 @@ async fn commands_loop(listener: UnixListener) -> Result<(), Box> { } async fn player_loop() { + let mut device_check_counter: u32 = 0; loop { - match get_audio_player().await { + let is_idle = match get_audio_player().await { Ok(player_mutex) => { let mut audio_player = player_mutex.lock().await; - audio_player.update().await; + let check_devices = device_check_counter == 0; + audio_player.update(check_devices).await; + audio_player.tracks.is_empty() } - Err(_err) => { - // To avoid spamming logs every 100ms when audio player fails to init - // we can just sleep, or you might prefer to print the error. - // Assuming it failed to initialize, no player update is possible. - } - } + Err(_err) => true, + }; - sleep(Duration::from_millis(100)).await; + if is_idle { + device_check_counter = 0; + sleep(Duration::from_secs(2)).await; + } else { + // Check devices every ~5 seconds (50 * 100ms) while playing + device_check_counter = (device_check_counter + 1) % 50; + sleep(Duration::from_millis(100)).await; + } } } diff --git a/src/types/audio_player.rs b/src/types/audio_player.rs index 1937fb3..31eaafa 100644 --- a/src/types/audio_player.rs +++ b/src/types/audio_player.rs @@ -53,7 +53,7 @@ pub struct PlayingSound { } pub struct AudioPlayer { - pub stream_handle: MixerDeviceSink, + stream_handle: Option, pub tracks: HashMap, pub next_id: u32, @@ -68,10 +68,8 @@ impl AudioPlayer { let daemon_config = get_daemon_config(); let default_volume = daemon_config.default_volume.unwrap_or(1.0); - let stream_handle = DeviceSinkBuilder::open_default_sink()?; - let mut audio_player = AudioPlayer { - stream_handle, + stream_handle: None, tracks: HashMap::new(), next_id: 1, @@ -88,6 +86,21 @@ impl AudioPlayer { Ok(audio_player) } + fn ensure_stream(&mut self) -> Result<&MixerDeviceSink, Box> { + if self.stream_handle.is_none() { + let mut sink = DeviceSinkBuilder::open_default_sink()?; + sink.log_on_drop(false); + self.stream_handle = Some(sink); + } + Ok(self.stream_handle.as_ref().unwrap()) + } + + fn drop_stream(&mut self) { + if self.stream_handle.is_some() { + self.stream_handle = None; + } + } + fn abort_link_thread(&mut self) { if let Some(sender) = &self.input_link_sender { match sender.send(Terminate {}) { @@ -179,6 +192,9 @@ impl AudioPlayer { } else { self.tracks.clear(); } + if self.tracks.is_empty() { + self.drop_stream(); + } } pub fn is_paused(&self) -> bool { @@ -299,12 +315,15 @@ impl AudioPlayer { self.tracks.clear(); } + self.ensure_stream()?; + let id = self.next_id; self.next_id += 1; let duration = source.total_duration().map(|d| d.as_secs_f32()); - let sink = Player::connect_new(self.stream_handle.mixer()); + let mixer = self.stream_handle.as_ref().unwrap().mixer(); + let sink = Player::connect_new(mixer); sink.set_volume(self.volume); // Default volume is 1.0 * master sink.append(source); sink.play(); @@ -358,20 +377,23 @@ impl AudioPlayer { tracks } - pub async fn update(&mut self) { - if let Some(input_device_name) = &self.input_device_name { - // Unlink devices if selected input device was removed - if self.input_link_sender.is_some() && get_device(input_device_name).await.is_err() { - // Selected input device was removed - eprintln!( - "Selected input device {} was removed, unlinking devices", - input_device_name - ); - self.abort_link_thread(); - } - // Link devices if not linked - else if self.input_link_sender.is_none() { - self.link_devices().await.ok(); + pub async fn update(&mut self, check_devices: bool) { + if check_devices { + if let Some(input_device_name) = &self.input_device_name { + // Unlink devices if selected input device was removed + if self.input_link_sender.is_some() + && get_device(input_device_name).await.is_err() + { + eprintln!( + "Selected input device {} was removed, unlinking devices", + input_device_name + ); + self.abort_link_thread(); + } + // Link devices if not linked + else if self.input_link_sender.is_none() { + self.link_devices().await.ok(); + } } } @@ -412,6 +434,10 @@ impl AudioPlayer { self.tracks .retain(|_, sound| !sound.sink.empty() || sound.looped); + + if self.tracks.is_empty() { + self.drop_stream(); + } } pub async fn set_current_input_device(&mut self, name: &str) -> Result<(), Box> {