use crate::model::Sample; use std::sync::Arc; pub const MAX_CHANNELS: usize = 16; pub const OUTPUT_SAMPLE_RATE: u32 = 48000; #[derive(Clone, Copy, Debug, PartialEq)] pub enum LoopMode { Off, On, } pub struct Channel { pub sample: Option>, pub pos: f64, pub pitch: f64, pub volume: u8, // 0..255 pub pan: u8, // 0..255 pub loop_mode: LoopMode, pub priority: u8, } impl Default for Channel { fn default() -> Self { Self { sample: None, pos: 0.0, pitch: 1.0, volume: 255, pan: 127, loop_mode: LoopMode::Off, priority: 0, } } } pub enum AudioCommand { Play { sample: Arc, voice_id: usize, volume: u8, pan: u8, pitch: f64, priority: u8, loop_mode: LoopMode, }, Stop { voice_id: usize, }, SetVolume { voice_id: usize, volume: u8, }, SetPan { voice_id: usize, pan: u8, }, SetPitch { voice_id: usize, pitch: f64, }, } pub struct Audio { pub voices: [Channel; MAX_CHANNELS], pub commands: Vec, } impl Audio { pub fn new() -> Self { Self { voices: Default::default(), commands: Vec::new(), } } pub fn play(&mut self, sample: Arc, voice_id: usize, volume: u8, pan: u8, pitch: f64, priority: u8, loop_mode: LoopMode) { if voice_id < MAX_CHANNELS { self.commands.push(AudioCommand::Play { sample, voice_id, volume, pan, pitch, priority, loop_mode, }); } } pub fn stop(&mut self, voice_id: usize) { if voice_id < MAX_CHANNELS { self.commands.push(AudioCommand::Stop { voice_id }); } } pub fn set_volume(&mut self, voice_id: usize, volume: u8) { if voice_id < MAX_CHANNELS { self.commands.push(AudioCommand::SetVolume { voice_id, volume }); } } pub fn set_pan(&mut self, voice_id: usize, pan: u8) { if voice_id < MAX_CHANNELS { self.commands.push(AudioCommand::SetPan { voice_id, pan }); } } pub fn set_pitch(&mut self, voice_id: usize, pitch: f64) { if voice_id < MAX_CHANNELS { self.commands.push(AudioCommand::SetPitch { voice_id, pitch }); } } pub fn is_playing(&self, voice_id: usize) -> bool { if voice_id < MAX_CHANNELS { self.voices[voice_id].sample.is_some() } else { false } } /// Clears the command queue. The Host should consume this every frame. pub fn clear_commands(&mut self) { self.commands.clear(); } }