use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink, Source};
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::sync::{Arc, Mutex};
pub struct AudioSystem {
_stream: OutputStream,
stream_handle: OutputStreamHandle,
sounds: Arc<Mutex<HashMap<String, Vec<u8>>>>,
music_sink: Option<Sink>,
}
impl AudioSystem {
pub fn new() -> Result<Self, String> {
let (stream, stream_handle) = OutputStream::try_default()
.map_err(|e| format!("Failed to create audio output: {}", e))?;
Ok(Self {
_stream: stream,
stream_handle,
sounds: Arc::new(Mutex::new(HashMap::new())),
music_sink: None,
})
}
pub fn load_sound(&mut self, name: &str, path: &str) -> Result<(), String> {
let file = File::open(path)
.map_err(|e| format!("Failed to open audio file {}: {}", path, e))?;
let mut reader = BufReader::new(file);
let mut buffer = Vec::new();
std::io::Read::read_to_end(&mut reader, &mut buffer)
.map_err(|e| format!("Failed to read audio file: {}", e))?;
self.sounds.lock().unwrap().insert(name.to_string(), buffer);
Ok(())
}
pub fn play_sound(&self, name: &str) -> Result<(), String> {
let sounds = self.sounds.lock().unwrap();
let sound_data = sounds.get(name)
.ok_or_else(|| format!("Sound '{}' not found", name))?;
let cursor = std::io::Cursor::new(sound_data.clone());
let source = Decoder::new(cursor)
.map_err(|e| format!("Failed to decode audio: {}", e))?;
self.stream_handle.play_raw(source.convert_samples())
.map_err(|e| format!("Failed to play sound: {}", e))?;
Ok(())
}
pub fn play_sound_with_volume(&self, name: &str, volume: f32) -> Result<(), String> {
let sounds = self.sounds.lock().unwrap();
let sound_data = sounds.get(name)
.ok_or_else(|| format!("Sound '{}' not found", name))?;
let cursor = std::io::Cursor::new(sound_data.clone());
let source = Decoder::new(cursor)
.map_err(|e| format!("Failed to decode audio: {}", e))?
.amplify(volume);
self.stream_handle.play_raw(source.convert_samples())
.map_err(|e| format!("Failed to play sound: {}", e))?;
Ok(())
}
pub fn play_music(&mut self, path: &str) -> Result<(), String> {
self.stop_music();
let file = File::open(path)
.map_err(|e| format!("Failed to open music file: {}", e))?;
let source = Decoder::new(BufReader::new(file))
.map_err(|e| format!("Failed to decode music: {}", e))?
.repeat_infinite();
let sink = Sink::try_new(&self.stream_handle)
.map_err(|e| format!("Failed to create music sink: {}", e))?;
sink.append(source);
self.music_sink = Some(sink);
Ok(())
}
pub fn play_music_with_volume(&mut self, path: &str, volume: f32) -> Result<(), String> {
self.play_music(path)?;
self.set_music_volume(volume);
Ok(())
}
pub fn stop_music(&mut self) {
if let Some(sink) = self.music_sink.take() {
sink.stop();
}
}
pub fn pause_music(&self) {
if let Some(sink) = &self.music_sink {
sink.pause();
}
}
pub fn resume_music(&self) {
if let Some(sink) = &self.music_sink {
sink.play();
}
}
pub fn set_music_volume(&self, volume: f32) {
if let Some(sink) = &self.music_sink {
sink.set_volume(volume);
}
}
pub fn is_music_playing(&self) -> bool {
self.music_sink.as_ref().map_or(false, |sink| !sink.is_paused())
}
}
impl Default for AudioSystem {
fn default() -> Self {
Self::new().expect("Failed to create audio system")
}
}