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:
Gered 2022-07-01 16:23:28 -04:00
parent 1a82436661
commit e276eb950d
5 changed files with 270 additions and 14 deletions

View file

@ -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);

View file

@ -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 {

View file

@ -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;

View 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(())
}
}
}
}

View file

@ -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