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