use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use std::thread::JoinHandle;
use super::audio_decode::{DecodeError, decode_audio_file};
use super::audio_sample_format_convert::{ConvertError, PlaybackFormat, convert_sample_format};
#[derive(Debug)]
pub struct LoadClipRequest {
pub clip_id: u64,
pub uri: String,
pub target: PlaybackFormat,
}
#[derive(Debug, Clone)]
pub enum LoadedClipMessage {
Loaded {
clip_id: u64,
samples: Arc<Vec<f32>>,
channels: u16,
sample_rate: u32,
},
Failed {
clip_id: u64,
reason: String,
},
}
pub struct DecodeThreadHandle {
pub tx: Sender<LoadClipRequest>,
pub thread: Option<JoinHandle<()>>,
}
impl DecodeThreadHandle {
pub fn shutdown(mut self) {
drop(self.tx);
if let Some(t) = self.thread.take() {
let _ = t.join();
}
}
}
pub fn spawn_decode_thread(complete_tx: Sender<LoadedClipMessage>) -> DecodeThreadHandle {
let (tx, rx): (Sender<LoadClipRequest>, Receiver<LoadClipRequest>) = channel();
let thread = std::thread::Builder::new()
.name("cat-engine-audio-decode".into())
.spawn(move || worker_main(rx, complete_tx))
.expect("failed to spawn audio decode thread");
DecodeThreadHandle {
tx,
thread: Some(thread),
}
}
fn worker_main(rx: Receiver<LoadClipRequest>, complete_tx: Sender<LoadedClipMessage>) {
while let Ok(req) = rx.recv() {
let LoadClipRequest {
clip_id,
uri,
target,
} = req;
let msg = match decode_and_convert(&uri, target) {
Ok((samples, channels, sample_rate)) => LoadedClipMessage::Loaded {
clip_id,
samples,
channels,
sample_rate,
},
Err(reason) => LoadedClipMessage::Failed { clip_id, reason },
};
if complete_tx.send(msg).is_err() {
break;
}
}
}
fn decode_and_convert(
uri: &str,
target: PlaybackFormat,
) -> Result<(Arc<Vec<f32>>, u16, u32), String> {
let decoded = decode_audio_file(uri).map_err(|e: DecodeError| e.to_string())?;
let converted =
convert_sample_format(decoded, target).map_err(|e: ConvertError| e.to_string())?;
Ok((converted.samples, converted.channels, converted.sample_rate))
}