add AudioQueue
for queuing audio "commands" which is a much nicer way to interact with the audio system in general in a more complex application, as this lets you play audio in more of a "fire and forget" kind of way. Rc<AudioBuffer> play methods provided as an alternative because otherwise you end up with a somewhat gross double-copy of the audio buffer being played. but i didn't want to necessarily force use of Rc<AudioBuffer> just yet ... thus both versions of these methods are provided.
This commit is contained in:
parent
1a82436661
commit
e276eb950d
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<dyn AudioGenerator>, 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<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
) -> Result<Option<&mut AudioChannel>, 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<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if channel_index >= NUM_CHANNELS {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
199
libretrogd/src/audio/queue.rs
Normal file
199
libretrogd/src/audio/queue.rs
Normal file
|
@ -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<AudioBuffer>,
|
||||
loops: bool,
|
||||
},
|
||||
PlayBufferOnChannel {
|
||||
channel: usize,
|
||||
buffer: AudioBuffer,
|
||||
loops: bool,
|
||||
},
|
||||
PlayRcBufferOnChannel {
|
||||
channel: usize,
|
||||
buffer: Rc<AudioBuffer>,
|
||||
loops: bool,
|
||||
},
|
||||
PlayGenerator {
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
},
|
||||
PlayGeneratorOnChannel {
|
||||
channel: usize,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct AudioQueue {
|
||||
spec: AudioSpec,
|
||||
commands: VecDeque<AudioCommand>,
|
||||
}
|
||||
|
||||
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<AudioBuffer>,
|
||||
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<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::PlayRcBufferOnChannel {
|
||||
channel: channel_index,
|
||||
buffer: buffer.clone(),
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_generator(
|
||||
&mut self,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
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<dyn AudioGenerator>,
|
||||
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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue