use rodio::{
source::{Buffered, Source},
Decoder, OutputStream, OutputStreamHandle, Sink,
};
use std::collections::HashMap;
use std::io::{Cursor, Read};
use std::{fs::File, path::Path};
pub mod prelude {
pub use crate::Audio;
}
#[derive(Default)]
pub struct Audio {
clips: HashMap<String, Buffered<Decoder<Cursor<Vec<u8>>>>>,
channels: Vec<Sink>,
current_channel: usize,
output: Option<(OutputStream, OutputStreamHandle)>,
}
impl Audio {
pub fn new() -> Self {
if let Ok(output) = OutputStream::try_default() {
let clips = HashMap::new();
let mut channels: Vec<Sink> = Vec::new();
for i in 0..4 {
let sink = Sink::try_new(&output.1)
.unwrap_or_else(|_| panic!("Failed to create sound channel {}", i));
channels.push(sink);
}
Self {
clips,
channels,
current_channel: 0,
output: Some(output),
}
} else {
Self {
clips: HashMap::new(),
channels: Vec::new(),
current_channel: 0,
output: None,
}
}
}
pub fn disabled(&self) -> bool {
self.output.is_none()
}
pub fn add<S: AsRef<str>, P: AsRef<Path>>(&mut self, name: S, path: P) {
if self.disabled() {
return;
}
let mut file_vec: Vec<u8> = Vec::new();
File::open(path.as_ref())
.expect("Couldn't find audio file to add.")
.read_to_end(&mut file_vec)
.expect("Failed reading in opened audio file.");
let cursor = Cursor::new(file_vec);
let decoder = Decoder::new(cursor).unwrap();
let buffered = decoder.buffered();
let warm = buffered.clone();
for i in warm {
#[allow(clippy::drop_copy)]
drop(i);
}
self.clips.insert(name.as_ref().to_string(), buffered);
}
pub fn play<S: AsRef<str>>(&mut self, name: S) {
if self.disabled() {
return;
}
let buffer = self
.clips
.get(name.as_ref())
.expect("No clip by that name.")
.clone();
self.channels[self.current_channel].append(buffer);
self.current_channel += 1;
if self.current_channel >= self.channels.len() {
self.current_channel = 0;
}
}
pub fn wait(&self) {
if self.disabled() {
return;
}
loop {
if self.channels.iter().any(|x| !x.empty()) {
std::thread::sleep(std::time::Duration::from_millis(50));
continue;
}
break;
}
}
}