1use std::collections::HashMap;
2use std::thread;
3use std::{sync::mpsc, time::Duration};
4
5use rodio::source::SineWave;
6use rodio::{OutputStream, Sink, Source};
7
8use crate::{BPMChoice, Note, WaveForm};
9
10#[derive(Debug, Clone)]
11pub struct Song {
12 pub bpm: BPMChoice,
13 pub notes: Vec<Note>,
14}
15
16impl Song {
17 pub fn new(notes: Vec<Note>, bpm: BPMChoice) -> Self {
18 Self { bpm, notes }
19 }
20}
21
22impl Default for Song {
23 fn default() -> Self {
24 Self {
25 bpm: BPMChoice::Default,
26 notes: vec![Note::default()],
27 }
28 }
29}
30
31pub struct AudioManager {
32 tx: mpsc::Sender<AudioCommand>,
33 next_track_id: usize,
34}
35
36#[derive(Debug, Clone)]
37pub enum AudioCommand {
38 PlayTrack { id: usize, song: Song },
39 StopTrack(usize),
40 SetVolume(usize, f32),
41 StopAll,
42}
43
44impl AudioManager {
45 pub fn new() -> Self {
46 let (tx, rx) = mpsc::channel();
47 let mut _next_track_id = 0;
48
49 thread::spawn(move || {
51 let (_stream, handle) = OutputStream::try_default().unwrap();
52 let mut sinks: HashMap<usize, Sink> = HashMap::new();
53
54 while let Ok(command) = rx.recv() {
56 match command {
57 AudioCommand::PlayTrack { id, song } => {
58 let sink = Sink::try_new(&handle).unwrap();
60
61 for note in song.notes {
62 let source = match note.wave {
63 WaveForm::Sine => Box::new(SineWave::new(note.freq as f32)),
64 _ => Box::new(note.to_approx_sine()),
65 };
66 sink.append(
67 source
68 .take_duration(Duration::from_secs_f64(note.dur))
69 .amplify(note.vol),
70 );
71 }
72
73 sinks.insert(id, sink);
75 }
76 AudioCommand::StopTrack(id) => {
77 if let Some(sink) = sinks.get(&id) {
78 sink.stop(); sinks.remove(&id);
80 }
81 }
82 AudioCommand::SetVolume(id, volume) => {
83 if let Some(sink) = sinks.get(&id) {
84 sink.set_volume(volume);
85 }
86 }
87 AudioCommand::StopAll => {
88 for sink in sinks.values() {
89 sink.stop();
90 }
91 sinks.clear();
92 }
93 }
94 }
95 });
96
97 AudioManager {
98 tx,
99 next_track_id: 0,
100 }
101 }
102
103 pub fn play(&mut self, song: Song) -> usize {
104 let track_id = self.next_track_id;
105 self.next_track_id += 1;
106
107 let _ = self.tx.send(AudioCommand::PlayTrack { id: track_id, song });
108
109 track_id }
111
112 pub fn stop(&self, track_id: usize) {
113 let _ = self.tx.send(AudioCommand::StopTrack(track_id));
114 }
115
116 pub fn set_volume(&self, track_id: usize, volume: f32) {
117 let _ = self.tx.send(AudioCommand::SetVolume(track_id, volume));
118 }
119
120 pub fn stop_all(&self) {
121 let _ = self.tx.send(AudioCommand::StopAll);
122 }
123}