diff --git a/examples/audio_playback/src/main.rs b/examples/audio_playback/src/main.rs index 0a1658a..5c871dd 100644 --- a/examples/audio_playback/src/main.rs +++ b/examples/audio_playback/src/main.rs @@ -58,6 +58,7 @@ fn main() -> Result<()> { let mut system = SystemBuilder::new().window_title("Audio Playback").vsync(true).build()?; let mut is_running = true; + let mut using_queue_commands = false; let mut volume = 1.0; let sounds = [ @@ -77,48 +78,91 @@ fn main() -> Result<()> { } }); - let mut audio_device = system.audio.lock(); - audio_device.volume = volume; - if system.keyboard.is_key_pressed(Scancode::Escape) { is_running = false; } + let mut audio_device = system.audio.lock(); + audio_device.volume = volume; + if system.keyboard.is_key_pressed(Scancode::Num1) { - audio_device.play_buffer(&sounds[0], false)?; + if using_queue_commands { + system.audio_queue.play_buffer(&sounds[0], false); + } else { + audio_device.play_buffer(&sounds[0], false)?; + } } if system.keyboard.is_key_pressed(Scancode::Num2) { - audio_device.play_buffer(&sounds[1], false)?; + if using_queue_commands { + system.audio_queue.play_buffer(&sounds[1], false); + } else { + audio_device.play_buffer(&sounds[1], false)?; + } } if system.keyboard.is_key_pressed(Scancode::Num3) { - audio_device.play_buffer(&sounds[2], false)?; + if using_queue_commands { + system.audio_queue.play_buffer(&sounds[2], false); + + } else { + audio_device.play_buffer(&sounds[2], false)?; + } } if system.keyboard.is_key_pressed(Scancode::Num4) { - audio_device.play_buffer(&sounds[3], false)?; + if using_queue_commands { + system.audio_queue.play_buffer(&sounds[3], false); + + } else { + audio_device.play_buffer(&sounds[3], false)?; + } } if system.keyboard.is_key_pressed(Scancode::Num5) { - audio_device.play_buffer(&sounds[4], false)?; + if using_queue_commands { + system.audio_queue.play_buffer(&sounds[4], false); + } else { + audio_device.play_buffer(&sounds[4], false)?; + } } if system.keyboard.is_key_pressed(Scancode::Num6) { - audio_device.play_generator(SineWaveGenerator::new(), false); + if using_queue_commands { + system.audio_queue.play_generator(Box::new(SineWaveGenerator::new()), false); + } else { + audio_device.play_generator(Box::new(SineWaveGenerator::new()), false); + } } if system.keyboard.is_key_pressed(Scancode::Num7) { let index = rnd_value(0, sounds.len() - 1); - audio_device.play_buffer_on_channel(7, &sounds[index], false)?; + if using_queue_commands { + system.audio_queue.play_buffer_on_channel(7, &sounds[index], false)?; + } else { + audio_device.play_buffer_on_channel(7, &sounds[index], false)?; + } } + if system.keyboard.is_key_pressed(Scancode::S) { + if using_queue_commands { + system.audio_queue.stop_all(); + } else { + audio_device.stop_all(); + } + } + + system.audio_queue.apply(&mut audio_device)?; + if system.keyboard.is_key_pressed(Scancode::KpMinus) { volume -= 0.1; } if system.keyboard.is_key_pressed(Scancode::KpPlus) { volume += 0.1; } + if system.keyboard.is_key_pressed(Scancode::Q) { + using_queue_commands = !using_queue_commands; + } for index in 0..NUM_CHANNELS { let channel = &audio_device[index]; @@ -133,6 +177,14 @@ fn main() -> Result<()> { system.video.clear(0); system.video.print_string(&format!("Volume: {:2.2}", volume), 16, 16, FontRenderOpts::Color(10), &system.font); + system.video.print_string( + if using_queue_commands { + "Queueing Commands" + } else { + "Direct Commands" + }, + 160, 16, FontRenderOpts::Color(9), &system.font + ); system.video.print_string("Audio Channels", 16, 32, FontRenderOpts::Color(14), &system.font); diff --git a/libretrogd/src/audio/device.rs b/libretrogd/src/audio/device.rs index c5afe43..9e6c8e5 100644 --- a/libretrogd/src/audio/device.rs +++ b/libretrogd/src/audio/device.rs @@ -98,9 +98,9 @@ impl AudioChannel { } #[inline] - pub fn play_generator(&mut self, generator: impl AudioGenerator + 'static, loops: bool) { + pub fn play_generator(&mut self, generator: Box, loops: bool) { self.data.clear(); - self.generator = Some(Box::new(generator)); + self.generator = Some(generator); self.position = 0; self.playing = true; self.loops = loops; @@ -233,7 +233,7 @@ impl AudioDevice { pub fn play_generator( &mut self, - generator: impl AudioGenerator + 'static, + generator: Box, loops: bool, ) -> Result, AudioDeviceError> { if let Some(channel) = self.stopped_channels_iter_mut().next() { @@ -247,7 +247,7 @@ impl AudioDevice { pub fn play_generator_on_channel( &mut self, channel_index: usize, - generator: impl AudioGenerator + 'static, + generator: Box, loops: bool, ) -> Result<(), AudioDeviceError> { if channel_index >= NUM_CHANNELS { diff --git a/libretrogd/src/audio/mod.rs b/libretrogd/src/audio/mod.rs index cbb4021..8d22507 100644 --- a/libretrogd/src/audio/mod.rs +++ b/libretrogd/src/audio/mod.rs @@ -4,9 +4,11 @@ use thiserror::Error; pub use self::buffer::*; pub use self::device::*; +pub use self::queue::*; pub mod buffer; pub mod device; +pub mod queue; pub const NUM_CHANNELS: usize = 8; diff --git a/libretrogd/src/audio/queue.rs b/libretrogd/src/audio/queue.rs new file mode 100644 index 0000000..ba0e685 --- /dev/null +++ b/libretrogd/src/audio/queue.rs @@ -0,0 +1,199 @@ +use std::collections::VecDeque; +use std::rc::Rc; + +use crate::audio::*; + +pub enum AudioCommand { + StopChannel(usize), + StopAllChannels, + PlayBuffer { + buffer: AudioBuffer, + loops: bool, + }, + PlayRcBuffer { + buffer: Rc, + loops: bool, + }, + PlayBufferOnChannel { + channel: usize, + buffer: AudioBuffer, + loops: bool, + }, + PlayRcBufferOnChannel { + channel: usize, + buffer: Rc, + loops: bool, + }, + PlayGenerator { + generator: Box, + loops: bool, + }, + PlayGeneratorOnChannel { + channel: usize, + generator: Box, + loops: bool, + }, +} + +pub struct AudioQueue { + spec: AudioSpec, + commands: VecDeque, +} + +impl AudioQueue { + pub fn from(audio: &Audio) -> Self { + AudioQueue { + spec: audio.spec, + commands: VecDeque::new(), + } + } + + #[inline] + pub fn spec(&self) -> &AudioSpec { + &self.spec + } + + pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> { + if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.commands.push_back(AudioCommand::StopChannel(channel_index)); + Ok(()) + } + } + + pub fn stop_all(&mut self) { + self.commands.push_back(AudioCommand::StopAllChannels); + } + + pub fn play_buffer( + &mut self, + buffer: &AudioBuffer, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else { + self.commands.push_back(AudioCommand::PlayBuffer { + buffer: buffer.clone(), + loops, + }); + Ok(()) + } + } + + pub fn play_buffer_rc( + &mut self, + buffer: Rc, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else { + self.commands.push_back(AudioCommand::PlayRcBuffer { + buffer: buffer.clone(), + loops, + }); + Ok(()) + } + } + + pub fn play_buffer_on_channel( + &mut self, + channel_index: usize, + buffer: &AudioBuffer, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.commands.push_back(AudioCommand::PlayBufferOnChannel { + channel: channel_index, + buffer: buffer.clone(), + loops, + }); + Ok(()) + } + } + + pub fn play_buffer_rc_on_channel( + &mut self, + channel_index: usize, + buffer: Rc, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.commands.push_back(AudioCommand::PlayRcBufferOnChannel { + channel: channel_index, + buffer: buffer.clone(), + loops, + }); + Ok(()) + } + } + + pub fn play_generator( + &mut self, + generator: Box, + loops: bool, + ) -> Result<(), AudioDeviceError> { + self.commands.push_back(AudioCommand::PlayGenerator { generator, loops }); + Ok(()) + } + + pub fn play_generator_on_channel( + &mut self, + channel_index: usize, + generator: Box, + loops: bool, + ) -> Result<(), AudioDeviceError> { + self.commands.push_back(AudioCommand::PlayGeneratorOnChannel { + channel: channel_index, + generator, + loops, + }); + Ok(()) + } + + pub fn apply(&mut self, device: &mut AudioDevice) -> Result<(), AudioDeviceError> { + loop { + if let Some(command) = self.commands.pop_front() { + use AudioCommand::*; + match command { + StopChannel(channel_index) => { + device.stop_channel(channel_index)?; + }, + StopAllChannels => { + device.stop_all(); + }, + PlayBuffer { buffer, loops } => { + device.play_buffer(&buffer, loops)?; + } + PlayRcBuffer { buffer, loops } => { + device.play_buffer(&buffer, loops)?; + }, + PlayBufferOnChannel { channel, buffer, loops } => { + device.play_buffer_on_channel(channel, &buffer, loops)?; + } + PlayRcBufferOnChannel { channel, buffer, loops } => { + device.play_buffer_on_channel(channel, &buffer, loops)?; + }, + PlayGenerator { generator, loops } => { + device.play_generator(generator, loops); + }, + PlayGeneratorOnChannel { channel, generator, loops } => { + device.play_generator_on_channel(channel, generator, loops); + }, + } + } else { + return Ok(()) + } + } + } +} \ No newline at end of file diff --git a/libretrogd/src/system/mod.rs b/libretrogd/src/system/mod.rs index cc04367..14a2085 100644 --- a/libretrogd/src/system/mod.rs +++ b/libretrogd/src/system/mod.rs @@ -252,6 +252,7 @@ impl SystemBuilder { }; let mut audio = Audio::new(audio_spec, &sdl_audio_subsystem)?; audio.resume(); + let mut audio_queue = AudioQueue::from(&audio); // create input device objects, exposed to the application @@ -269,6 +270,7 @@ impl SystemBuilder { sdl_event_pump, texture_pixels, audio, + audio_queue, video: framebuffer, palette, font, @@ -302,6 +304,7 @@ pub struct System { next_tick: i64, pub audio: Audio, + pub audio_queue: AudioQueue, /// The primary backbuffer [`Bitmap`] that will be rendered to the screen whenever /// [`System::display`] is called. Regardless of the actual window size, this bitmap is always