use {
super::*,
std::thread,
termimad::crossbeam::channel::{
self,
Sender,
select,
},
};
const MAX_QUEUED_SOUNDS: usize = 2;
pub struct SoundPlayer {
thread: Option<thread::JoinHandle<()>>,
s_die: Option<Sender<()>>,
s_sound: Sender<PlaySoundCommand>,
}
impl SoundPlayer {
pub fn new(base_volume: Volume) -> anyhow::Result<Self> {
let (s_sound, r_sound) = channel::bounded::<PlaySoundCommand>(MAX_QUEUED_SOUNDS);
let (s_die, r_die) = channel::bounded(1);
let thread = thread::spawn(move || {
loop {
select! {
recv(r_die) -> _ => {
info!("sound player thread is stopping");
break;
}
recv(r_sound) -> ps => {
match ps {
Ok(mut ps) => {
if !r_die.is_empty() {
continue;
}
ps.volume = ps.volume * base_volume;
match play_sound(&ps, r_die.clone()) {
Ok(()) => {
debug!("sound played");
}
Err(SoundError::Interrupted) => {
info!("sound interrupted");
break;
}
Err(e) => {
error!("sound error: {}", e);
}
}
}
Err(e) => {
error!("sound player channel error: {}", e);
break;
}
}
}
}
}
});
Ok(Self {
thread: Some(thread),
s_die: Some(s_die),
s_sound,
})
}
pub fn play(
&self,
sound_command: PlaySoundCommand,
) {
if self.s_sound.try_send(sound_command).is_err() {
warn!("Too many sounds in the queue, dropping one");
}
}
pub fn die(&mut self) {
if let Some(sender) = self.s_die.take() {
if let Err(e) = sender.send(()) {
warn!("failed to send 'kill' signal: {e}");
}
}
if let Some(thread) = self.thread.take() {
if thread.join().is_err() {
warn!("child_thread.join() failed"); } else {
info!("SoundPlayer gracefully stopped");
}
}
}
}
impl Drop for SoundPlayer {
fn drop(&mut self) {
self.die();
}
}