arcane_engine/audio/
mod.rs1use std::collections::HashMap;
2use std::io::Cursor;
3use std::sync::mpsc;
4
5pub enum AudioCommand {
7 LoadSound { id: u32, data: Vec<u8> },
8 PlaySound { id: u32, volume: f32, looping: bool },
9 StopSound { id: u32 },
10 StopAll,
11 SetMasterVolume { volume: f32 },
12 Shutdown,
13}
14
15pub type AudioSender = mpsc::Sender<AudioCommand>;
16pub type AudioReceiver = mpsc::Receiver<AudioCommand>;
17
18pub fn audio_channel() -> (AudioSender, AudioReceiver) {
20 mpsc::channel()
21}
22
23pub fn start_audio_thread(rx: AudioReceiver) -> std::thread::JoinHandle<()> {
25 std::thread::spawn(move || {
26 let stream_handle = match rodio::OutputStream::try_default() {
28 Ok((stream, handle)) => {
29 std::mem::forget(stream);
31 handle
32 }
33 Err(e) => {
34 eprintln!("[audio] Failed to initialize audio output: {e}");
35 while let Ok(cmd) = rx.recv() {
37 if matches!(cmd, AudioCommand::Shutdown) {
38 break;
39 }
40 }
41 return;
42 }
43 };
44
45 let mut sounds: HashMap<u32, Vec<u8>> = HashMap::new();
46 let mut sinks: HashMap<u32, rodio::Sink> = HashMap::new();
47 let mut master_volume: f32 = 1.0;
48
49 loop {
50 let cmd = match rx.recv() {
51 Ok(cmd) => cmd,
52 Err(_) => break, };
54
55 match cmd {
56 AudioCommand::LoadSound { id, data } => {
57 sounds.insert(id, data);
58 }
59 AudioCommand::PlaySound { id, volume, looping } => {
60 if let Some(data) = sounds.get(&id) {
61 match rodio::Sink::try_new(&stream_handle) {
62 Ok(sink) => {
63 sink.set_volume(volume * master_volume);
64 let cursor = Cursor::new(data.clone());
65 match rodio::Decoder::new(cursor) {
66 Ok(source) => {
67 if looping {
68 sink.append(rodio::source::Source::repeat_infinite(source));
69 } else {
70 sink.append(source);
71 }
72 sink.play();
73 sinks.insert(id, sink);
74 }
75 Err(e) => {
76 eprintln!("[audio] Failed to decode sound {id}: {e}");
77 }
78 }
79 }
80 Err(e) => {
81 eprintln!("[audio] Failed to create sink for sound {id}: {e}");
82 }
83 }
84 }
85 }
86 AudioCommand::StopSound { id } => {
87 if let Some(sink) = sinks.remove(&id) {
88 sink.stop();
89 }
90 }
91 AudioCommand::StopAll => {
92 for (_, sink) in sinks.drain() {
93 sink.stop();
94 }
95 }
96 AudioCommand::SetMasterVolume { volume } => {
97 master_volume = volume;
98 for sink in sinks.values() {
99 sink.set_volume(volume);
100 }
101 }
102 AudioCommand::Shutdown => break,
103 }
104 }
105 })
106}