use awedio::NextSample; use awedio::Sound; use std::sync::mpsc; /// Heavily Based on awedios SoundList and Controllable implementations pub struct Player { sounds: Vec>, was_empty: bool, song_prefetch: u32, } type Command = Box; pub struct PlayerControllable { inner: Player, command_receiver: mpsc::Receiver>, queue_next_song_sender: tokio::sync::mpsc::Sender<()>, finished: bool, } pub struct PlayerController { command_sender: mpsc::Sender>, queue_next_song_receiver: tokio::sync::mpsc::Receiver<()>, } impl Player { /// Create a new empty Player. pub fn new(song_prefetch: u32) -> (PlayerControllable, PlayerController) { let (queue_next_song_sender, queue_next_song_receiver) = tokio::sync::mpsc::channel(1); let inner = Player { sounds: Vec::new(), was_empty: false, song_prefetch, }; let (command_sender, command_receiver) = mpsc::channel::>(); let controllable = PlayerControllable { inner, queue_next_song_sender, command_receiver, finished: false, }; let controller = PlayerController { command_sender, queue_next_song_receiver, }; (controllable, controller) } /// Add a Sound to be played after any existing sounds have `Finished`. pub fn add(&mut self, sound: Box) { if self.sounds.is_empty() { self.was_empty = true; } self.sounds.push(sound); } fn last_song_playing_or_empty(&self) -> bool { self.sounds.len() <= self.song_prefetch as usize } } // Returned only when no sounds exist so they shouldn't be used in practice. const DEFAULT_CHANNEL_COUNT: u16 = 2; const DEFAULT_SAMPLE_RATE: u32 = 44100; impl Sound for Player { fn channel_count(&self) -> u16 { self.sounds .first() .map(|s| s.channel_count()) .unwrap_or(DEFAULT_CHANNEL_COUNT) } fn sample_rate(&self) -> u32 { self.sounds .first() .map(|s| s.sample_rate()) .unwrap_or(DEFAULT_SAMPLE_RATE) } fn on_start_of_batch(&mut self) { for sound in &mut self.sounds { sound.on_start_of_batch(); } } fn next_sample(&mut self) -> Result { let Some(next_sound) = self.sounds.first_mut() else { return Ok(NextSample::Finished); }; if self.was_empty { self.was_empty = false; return Ok(NextSample::MetadataChanged); } let next_sample = next_sound.next_sample(); if let Err(e) = &next_sample { println!("Error playing track: {:?}", e); } let ret = match next_sample { Ok(NextSample::Sample(_) | NextSample::MetadataChanged | NextSample::Paused) => next_sample.unwrap(), Ok(NextSample::Finished) | Err(_) => { // Just ignore the error self.sounds.remove(0); if self.sounds.is_empty() { NextSample::Finished } else { // The next sample might have different metadata. Instead of // normalizing here let downstream normalize. NextSample::MetadataChanged } } }; Ok(ret) } } impl Sound for PlayerControllable { fn channel_count(&self) -> u16 { self.inner.channel_count() } fn sample_rate(&self) -> u32 { self.inner.sample_rate() } fn next_sample(&mut self) -> Result { let next = self.inner.next_sample()?; match next { awedio::NextSample::Sample(_) | awedio::NextSample::MetadataChanged | awedio::NextSample::Paused => Ok(next), // Since this is controllable we might add another sound later. // Ideally we would do this only if the inner sound can have sounds // added to it but I don't think we can branch on S: AddSound here. // We could add a Sound::is_addable but lets avoid that until we see // a reason why it is necessary. awedio::NextSample::Finished => { if self.finished { Ok(awedio::NextSample::Finished) } else { Ok(awedio::NextSample::Paused) } } } } fn on_start_of_batch(&mut self) { loop { match self.command_receiver.try_recv() { Ok(command) => command(&mut self.inner), Err(mpsc::TryRecvError::Empty) => break, Err(mpsc::TryRecvError::Disconnected) => { self.finished = true; break; } } } if self.inner.last_song_playing_or_empty() { let _ = self.queue_next_song_sender.try_send(()); } self.inner.on_start_of_batch(); } } impl PlayerController { pub fn send_command(&mut self, command: Command) { // Ignore the error since it only happens if the receiver // has been dropped which is not expected after it has been // sent to the manager. let _ = self.command_sender.send(command); } } impl PlayerController { pub fn add(&mut self, sound: Box) { self.send_command(Box::new(|s: &mut Player| s.add(sound))); } pub async fn wait_for_queue(&mut self) { self.queue_next_song_receiver.recv().await; } }