use std::collections::HashMap;
use std::io::Error;
use std::thread;
use std::{sync::mpsc, time::Duration};
use rodio::source::SineWave;
use rodio::{OutputStream, Sink, Source};
use crate::{BPMChoice, Note, WaveForm};
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub struct Song {
pub bpm: BPMChoice,
pub notes: Vec<Note>,
}
impl Song {
pub fn new(notes: Vec<Note>, bpm: BPMChoice) -> Self {
Self { bpm, notes }
}
}
impl Default for Song {
fn default() -> Self {
Self {
bpm: BPMChoice::Default,
notes: vec![Note::default()],
}
}
}
impl Song {
pub fn save_to_json(song: &Song, filename: &str) -> Result<(), Error> {
let json = serde_json::to_string_pretty(song)?;
std::fs::write(filename, json)?;
Ok(())
}
pub fn load_from_json(filename: &str) -> Result<Song, Error> {
let json = std::fs::read_to_string(filename)?;
let song = serde_json::from_str(&json)?;
Ok(song)
}
}
pub struct AudioManager {
tx: mpsc::Sender<AudioCommand>,
next_track_id: usize,
}
#[derive(Debug, Clone)]
pub(crate) enum AudioCommand {
PlayTrack { id: usize, song: Song },
StopTrack(usize),
SetVolume(usize, f32),
StopAll,
}
impl Default for AudioManager {
fn default() -> Self {
Self::new()
}
}
impl AudioManager {
pub fn new() -> Self {
let (tx, rx) = mpsc::channel();
let mut _next_track_id = 0;
thread::spawn(move || {
let (_stream, handle) = OutputStream::try_default().unwrap();
let mut sinks: HashMap<usize, Sink> = HashMap::new();
while let Ok(command) = rx.recv() {
match command {
AudioCommand::PlayTrack { id, song } => {
let sink = Sink::try_new(&handle).unwrap();
for note in song.notes {
let source = match note.wave {
WaveForm::Sine => Box::new(SineWave::new(note.freq as f32)),
WaveForm::Rest => {
std::thread::sleep(Duration::from_secs_f64(note.dur));
continue;
}
_ => Box::new(note.to_approx_sine()),
};
sink.append(
source
.take_duration(Duration::from_secs_f64(note.dur))
.amplify(note.vol),
);
}
sinks.insert(id, sink);
}
AudioCommand::StopTrack(id) => {
if let Some(sink) = sinks.get(&id) {
sink.stop(); sinks.remove(&id);
}
}
AudioCommand::SetVolume(id, volume) => {
if let Some(sink) = sinks.get(&id) {
sink.set_volume(volume);
}
}
AudioCommand::StopAll => {
for sink in sinks.values() {
sink.stop();
}
sinks.clear();
}
}
}
});
AudioManager {
tx,
next_track_id: 0,
}
}
pub fn play(&mut self, song: Song) -> usize {
let track_id = self.next_track_id;
self.next_track_id += 1;
let _ = self.tx.send(AudioCommand::PlayTrack { id: track_id, song });
track_id }
pub fn stop(&self, track_id: usize) {
let _ = self.tx.send(AudioCommand::StopTrack(track_id));
}
pub fn set_volume(&self, track_id: usize, volume: f32) {
let _ = self.tx.send(AudioCommand::SetVolume(track_id, volume));
}
pub fn stop_all(&self) {
let _ = self.tx.send(AudioCommand::StopAll);
}
}