use std::fmt::Debug;
use std::sync::mpsc::{Receiver, Sender, SendError};
use std::sync::mpsc;
use std::thread;
use std::thread::JoinHandle;
use rodio::OutputStream;
use crate::config::{BankSampleRef, Config, SwitchRef};
use crate::error::{readable_thread_panic_error, SoundThreadError};
use crate::sample_loader::{SampleLoader};
use crate::sound_bank::{SoundBank, SoundBankState};
#[derive(Debug)]
pub enum Operation {
Stop,
SwitchPressed {
switch_ref: SwitchRef,
},
}
#[derive(Debug)]
pub enum SoundThreadEvent {
PlayedSample(BankSampleRef),
}
struct SoundThreadBody {
operation_receiver: Receiver<Operation>,
event_sender: Sender<SoundThreadEvent>,
config: Config,
banks: Vec<SoundBankState>,
_sound_output: OutputStream,
}
impl SoundThreadBody {
fn new (
config: Config,
sample_loader: Box<dyn SampleLoader>,
operation_receiver: Receiver<Operation>,
event_sender: Sender<SoundThreadEvent>,
) -> Result<Self, SoundThreadError> {
let mut loader = sample_loader;
loader.load_banks(&config.banks)?;
let banks = SoundBank::new_all(&*loader, config.banks.clone())
.expect("SoundThread: Failed to find sound sample, which should just have been loaded");
let (_sound_output, sound_output_handle) = OutputStream::try_default()?;
let banks = SoundBankState::new_all(&sound_output_handle, banks);
Ok(Self { operation_receiver, event_sender, config, banks, _sound_output })
}
fn handle_operation_switch_pressed(&mut self, switch_ref: SwitchRef) -> Result<(), SoundThreadError> {
let switch_config = self.config.switch(switch_ref);
if switch_config.stop_sounds {
for bank in self.banks.iter_mut() {
bank.stop();
}
}
if let Some(play) = &switch_config.play {
let bank_sample_ref = play.bank_sample_ref;
let bank_state = &mut self.banks[bank_sample_ref.bank.bank_index];
bank_state.play(bank_sample_ref.sample)?;
self.event_sender.send(SoundThreadEvent::PlayedSample(bank_sample_ref))?;
}
if let Some(play) = &switch_config.play_random {
let bank_ref = play.bank_ref;
let bank_state = &mut self.banks[bank_ref.bank_index];
if let Some(bank_sample_ref) = bank_state.play_random()? {
self.event_sender.send(SoundThreadEvent::PlayedSample(bank_sample_ref))?;
}
}
if let Some(play) = &switch_config.play_step {
let bank_state = &mut self.banks[play.bank_ref.bank_index];
if let Some(bank_sample_ref) = bank_state.play_step(play.steps)? {
self.event_sender.send(SoundThreadEvent::PlayedSample(bank_sample_ref))?;
}
}
Ok(())
}
fn thread_body(mut self) -> Result<(), SoundThreadError> {
loop {
let received: Operation = self.operation_receiver.recv()?;
match received {
Operation::Stop => {
return Ok(());
}
Operation::SwitchPressed { switch_ref } => {
if let Err(err) = self.handle_operation_switch_pressed(switch_ref) {
eprintln!("SoundThread: Failed to handle switch press: {:?}", err);
}
}
};
}
}
}
pub struct SoundThread {
operation_sender: Sender<Operation>,
handle: JoinHandle<()>,
}
impl SoundThread {
pub fn new(config: &Config, sample_loader: Box<dyn SampleLoader + Send>) -> Result<(Self, Receiver<SoundThreadEvent>), SoundThreadError> {
let config = config.clone();
let (operation_sender, operation_receiver) = mpsc::channel();
let (startup_sender, startup_receiver) = mpsc::channel();
let (event_sender, event_receiver) = mpsc::channel();
let handle = thread::spawn(move || {
let result = SoundThreadBody::new(config, sample_loader, operation_receiver, event_sender);
match result {
Ok(body) => {
startup_sender.send(Ok(()))
.expect("SoundThread: Failed to send startup result to parent thread");
body.thread_body()
.expect("SoundThread: Error during SoundThreadBody.thread_body()");
}
Err(err) => {
startup_sender.send(Err(err))
.expect("SoundThread: Failed to send startup result to parent thread");
}
}
});
startup_receiver.recv()??;
let sound_thread = SoundThread { operation_sender, handle };
Ok((sound_thread, event_receiver))
}
pub fn stop(self) -> Result<(), SoundThreadError> {
if let Err(err) = self.operation_sender.send(Operation::Stop) {
eprintln!("Failed to send stop operation to SoundThread: {}", err);
}
let handle = self.handle;
match handle.join() {
Ok(_) => Ok(()),
Err(join_error) => {
let join_error_str = readable_thread_panic_error(&join_error);
Err(SoundThreadError::JoinPanic {
join_error,
join_error_str,
})
}
}
}
}
pub struct SoundThreadRpc {
operation_sender: Sender<Operation>,
}
impl SoundThreadRpc {
pub fn new(thread: &SoundThread) -> Self {
SoundThreadRpc { operation_sender: thread.operation_sender.clone() }
}
pub fn switch_pressed(&self, switch_ref: SwitchRef) -> Result<(), SendError<Operation>> {
self.operation_sender.send(Operation::SwitchPressed { switch_ref })
}
}