//! The `audio` crate stores all audio assets that need to be loaded during runtime use raylib::audio::RaylibAudio; use std::{ collections::BinaryHeap, sync::atomic::Ordering, time::{Duration, Instant}, }; use channel::Device; use data::Data; use crate::{ channel::Channels, program::{Program, Track}, }; mod channel; mod data; mod parse; mod program; /// The `Error` type used within this crate pub type Error = Box; /// The `Result` type used witin this crate pub type Result = std::result::Result; const AUDIO_FPS: u32 = 60; const TIME_SLICE: Duration = Duration::from_millis(1000 / AUDIO_FPS as u64); /// The `Audio` container initalizes the audio subsystem /// for raylib, leaks it (to gurentee audio is statically loaded), /// then loads all needed audio samples pub struct Audio { device: Device<'static>, last: Instant, queue: BinaryHeap, pub data: Data, } impl Audio { pub fn load() -> crate::Result { // Phantom handle to the raylib audio subsystem // Raylib doesnt use a handle, but the rust bindings // have one to ensure memory safety. // // We must leak this handle after allocating it, // if we dont then all audio will be unloaded :( // // NOTE: would this cause issues if `Audio::load` was // called multiple times? let handle = Box::leak(Box::new(RaylibAudio::init_audio_device()?)); let device = Device::load(handle)?; let last = Instant::now(); let queue = BinaryHeap::new(); let data = Data::load()?; Ok(Self { device, last, queue, data, }) } pub fn schedule(&mut self, track: Track, priority: u32, looping: bool) { let program = Program::new(track, priority, looping); self.queue.push(program); } pub fn clear(&mut self) { self.queue.clear(); } pub fn update(&mut self) { if self.last.elapsed() >= TIME_SLICE { let res_opt = self .queue .peek_mut() .map(|mut program| (program.exec(), program.is_finished())); let channels = match res_opt { Some((res, finished)) => { if finished { self.queue.pop(); } res } // make the output quiet! None => Channels::new(), }; self.device.channels.store(channels, Ordering::Relaxed); self.last = Instant::now(); } } }