shadow_engine_2d 2.0.1

A modern, high-performance 2D game engine built in Rust with ECS, physics, particles, audio, and more
Documentation
use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink, Source};
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::sync::{Arc, Mutex};

/// Audio system for playing sounds and music
pub struct AudioSystem {
    _stream: OutputStream,
    stream_handle: OutputStreamHandle,
    sounds: Arc<Mutex<HashMap<String, Vec<u8>>>>,
    music_sink: Option<Sink>,
}

impl AudioSystem {
    /// Create a new audio system
    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,
        })
    }

    /// Load a sound file into memory
    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(())
    }

    /// Play a sound effect
    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(())
    }

    /// Play a sound effect with volume control (0.0 to 1.0)
    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(())
    }

    /// Play background music (looping)
    pub fn play_music(&mut self, path: &str) -> Result<(), String> {
        // Stop existing music
        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(())
    }

    /// Play background music with volume control
    pub fn play_music_with_volume(&mut self, path: &str, volume: f32) -> Result<(), String> {
        self.play_music(path)?;
        self.set_music_volume(volume);
        Ok(())
    }

    /// Stop the currently playing music
    pub fn stop_music(&mut self) {
        if let Some(sink) = self.music_sink.take() {
            sink.stop();
        }
    }

    /// Pause the currently playing music
    pub fn pause_music(&self) {
        if let Some(sink) = &self.music_sink {
            sink.pause();
        }
    }

    /// Resume the paused music
    pub fn resume_music(&self) {
        if let Some(sink) = &self.music_sink {
            sink.play();
        }
    }

    /// Set music volume (0.0 to 1.0)
    pub fn set_music_volume(&self, volume: f32) {
        if let Some(sink) = &self.music_sink {
            sink.set_volume(volume);
        }
    }

    /// Check if music is playing
    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")
    }
}