mittens_engine/engine/ecs/system/
audio_decode_thread.rs1use std::sync::Arc;
11use std::sync::mpsc::{Receiver, Sender, channel};
12use std::thread::JoinHandle;
13
14use super::audio_decode::{DecodeError, decode_audio_file};
15use super::audio_sample_format_convert::{ConvertError, PlaybackFormat, convert_sample_format};
16
17#[derive(Debug)]
19pub struct LoadClipRequest {
20 pub clip_id: u64,
21 pub uri: String,
22 pub target: PlaybackFormat,
23}
24
25#[derive(Debug, Clone)]
27pub enum LoadedClipMessage {
28 Loaded {
29 clip_id: u64,
30 samples: Arc<Vec<f32>>,
31 channels: u16,
32 sample_rate: u32,
33 },
34 Failed {
35 clip_id: u64,
36 reason: String,
37 },
38}
39
40pub struct DecodeThreadHandle {
42 pub tx: Sender<LoadClipRequest>,
43 pub thread: Option<JoinHandle<()>>,
44}
45
46impl DecodeThreadHandle {
47 pub fn shutdown(mut self) {
48 drop(self.tx);
49 if let Some(t) = self.thread.take() {
50 let _ = t.join();
51 }
52 }
53}
54
55pub fn spawn_decode_thread(complete_tx: Sender<LoadedClipMessage>) -> DecodeThreadHandle {
58 let (tx, rx): (Sender<LoadClipRequest>, Receiver<LoadClipRequest>) = channel();
59
60 let thread = std::thread::Builder::new()
61 .name("cat-engine-audio-decode".into())
62 .spawn(move || worker_main(rx, complete_tx))
63 .expect("failed to spawn audio decode thread");
64
65 DecodeThreadHandle {
66 tx,
67 thread: Some(thread),
68 }
69}
70
71fn worker_main(rx: Receiver<LoadClipRequest>, complete_tx: Sender<LoadedClipMessage>) {
72 while let Ok(req) = rx.recv() {
73 let LoadClipRequest {
74 clip_id,
75 uri,
76 target,
77 } = req;
78
79 let msg = match decode_and_convert(&uri, target) {
80 Ok((samples, channels, sample_rate)) => LoadedClipMessage::Loaded {
81 clip_id,
82 samples,
83 channels,
84 sample_rate,
85 },
86 Err(reason) => LoadedClipMessage::Failed { clip_id, reason },
87 };
88 if complete_tx.send(msg).is_err() {
90 break;
91 }
92 }
93}
94
95fn decode_and_convert(
96 uri: &str,
97 target: PlaybackFormat,
98) -> Result<(Arc<Vec<f32>>, u16, u32), String> {
99 let decoded = decode_audio_file(uri).map_err(|e: DecodeError| e.to_string())?;
100 let converted =
101 convert_sample_format(decoded, target).map_err(|e: ConvertError| e.to_string())?;
102 Ok((converted.samples, converted.channels, converted.sample_rate))
103}