From 1a824366614595f7577b224d8376e2af68b6a082 Mon Sep 17 00:00:00 2001 From: gered Date: Fri, 1 Jul 2022 15:23:34 -0400 Subject: [PATCH] re-organize audio crates a bit --- libretrogd/src/audio/buffer/mod.rs | 54 ++++ libretrogd/src/audio/{ => buffer}/wav.rs | 2 +- libretrogd/src/audio/device.rs | 316 +++++++++++++++++++ libretrogd/src/audio/mod.rs | 373 +---------------------- 4 files changed, 376 insertions(+), 369 deletions(-) create mode 100644 libretrogd/src/audio/buffer/mod.rs rename libretrogd/src/audio/{ => buffer}/wav.rs (99%) create mode 100644 libretrogd/src/audio/device.rs diff --git a/libretrogd/src/audio/buffer/mod.rs b/libretrogd/src/audio/buffer/mod.rs new file mode 100644 index 0000000..b4498a8 --- /dev/null +++ b/libretrogd/src/audio/buffer/mod.rs @@ -0,0 +1,54 @@ +use crate::audio::*; + +pub use self::wav::*; + +pub mod wav; + +#[derive(Error, Debug)] +pub enum AudioBufferError { + #[error("Error during format conversion: {0}")] + ConversionError(String), +} + +#[derive(Debug, Clone)] +pub struct AudioBuffer { + spec: AudioSpec, + pub data: Vec, +} + +impl AudioBuffer { + pub fn new(spec: AudioSpec) -> Self { + AudioBuffer { + spec, + data: Vec::new(), + } + } + + #[inline] + pub fn spec(&self) -> &AudioSpec { + &self.spec + } + + pub fn convert(self, to_spec: &AudioSpec) -> Result { + if self.spec == *to_spec { + Ok(self) + } else { + let converter = sdl2::audio::AudioCVT::new( + self.spec.format(), + self.spec.channels(), + self.spec.frequency() as i32, + to_spec.format(), + to_spec.channels(), + to_spec.frequency() as i32, + ); + match converter { + Ok(converter) => { + let mut result = AudioBuffer::new(*to_spec); + result.data = converter.convert(self.data); + Ok(result) + } + Err(string) => Err(AudioBufferError::ConversionError(string)), + } + } + } +} diff --git a/libretrogd/src/audio/wav.rs b/libretrogd/src/audio/buffer/wav.rs similarity index 99% rename from libretrogd/src/audio/wav.rs rename to libretrogd/src/audio/buffer/wav.rs index 1861f95..c2066cb 100644 --- a/libretrogd/src/audio/wav.rs +++ b/libretrogd/src/audio/buffer/wav.rs @@ -7,7 +7,7 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use sdl2::audio::AudioFormat; use thiserror::Error; -use crate::audio::{AudioBuffer, AudioSpec}; +use crate::audio::*; use crate::utils::io::StreamSize; #[derive(Error, Debug)] diff --git a/libretrogd/src/audio/device.rs b/libretrogd/src/audio/device.rs new file mode 100644 index 0000000..c5afe43 --- /dev/null +++ b/libretrogd/src/audio/device.rs @@ -0,0 +1,316 @@ +use std::ops::{Index, IndexMut}; + +use sdl2::audio::AudioCallback; +use thiserror::Error; + +use crate::audio::*; + +pub struct AudioChannel { + pub playing: bool, + pub loops: bool, + pub data: Vec, + pub generator: Option>, + pub volume: f32, + pub position: usize, +} + +impl AudioChannel { + pub fn new() -> Self { + AudioChannel { + playing: false, + loops: false, + volume: 1.0, + position: 0, + generator: None, + data: Vec::new(), + } + } + + #[inline] + fn data_at(&mut self, position: usize) -> Option { + if let Some(generator) = &mut self.generator { + generator.gen_sample(position) + } else { + self.data.get(self.position).copied() + } + } + + /// Returns the next sample from this channel's buffer. If this channel's buffer is done + /// playing or there is no buffer data at all, `None` is returned. If the next sample was + /// successfully loaded from the buffer, the channel's current position is advanced by 1. + /// + /// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8` + /// value centered around 0 (meaning the returned sample will be within the range -128 to 127 + /// instead of 0 to 255). + #[inline] + fn next_sample(&mut self) -> Option { + if let Some(sample) = self.data_at(self.position) { + self.position += 1; + Some(sample as i16 - 128) + } else { + None + } + } + + /// Samples the channel's current audio buffer, advancing the position within that buffer by 1. + /// The channel will automatically stop playing when the end of the buffer is reached and if + /// the channel is not set to loop. `None` is returned if no data can be read from the buffer + /// for any reason, or if the channel is not currently playing. + /// + /// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8` + /// value centered around 0 (meaning the returned sample will be within the range -128 to 127 + /// instead of 0 to 255). + #[inline] + pub fn sample(&mut self) -> Option { + if !self.playing { + return None; + } + + if let Some(sample) = self.next_sample() { + Some((sample as f32 * self.volume) as i16) + } else { + if self.loops { + self.position = 0; + None + } else { + self.stop(); + None + } + } + } + + #[inline] + pub fn reset(&mut self) { + self.data.clear(); + self.generator = None; + self.position = 0; + self.playing = false; + } + + #[inline] + pub fn play_buffer(&mut self, buffer: &AudioBuffer, loops: bool) { + self.data.clear(); + self.data.extend(&buffer.data); + self.generator = None; + self.position = 0; + self.playing = true; + self.loops = loops; + } + + #[inline] + pub fn play_generator(&mut self, generator: impl AudioGenerator + 'static, loops: bool) { + self.data.clear(); + self.generator = Some(Box::new(generator)); + self.position = 0; + self.playing = true; + self.loops = loops; + } + + #[inline] + pub fn is_playable(&self) -> bool { + !self.data.is_empty() || self.generator.is_some() + } + + #[inline] + pub fn play(&mut self, loops: bool) { + if self.is_playable() { + self.position = 0; + self.playing = true; + self.loops = loops; + } + } + + #[inline] + pub fn stop(&mut self) { + self.playing = false; + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#[derive(Debug, Error)] +pub enum AudioDeviceError { + #[error("That buffer's AudioSpec does not match the device's AudioSpec")] + AudioSpecMismatch, + + #[error("The channel index {0} is out of range")] + ChannelIndexOutOfRange(usize), +} + +pub struct AudioDevice { + spec: AudioSpec, + channels: Vec, + pub volume: f32, +} + +impl AudioCallback for AudioDevice { + type Channel = u8; + + fn callback(&mut self, out: &mut [u8]) { + for dest in out.iter_mut() { + let mut sample: i16 = 0; + for channel in self.channels.iter_mut() { + if let Some(this_sample) = channel.sample() { + sample += this_sample; + } + } + sample = ((sample as f32) * self.volume) as i16; + *dest = (sample.clamp(-128, 127) + 128) as u8; + } + } +} + +impl AudioDevice { + pub fn new(spec: AudioSpec) -> Self { + let mut channels = Vec::new(); + for _ in 0..NUM_CHANNELS { + channels.push(AudioChannel::new()); + } + AudioDevice { + spec, + channels, + volume: 1.0, + } + } + + #[inline] + pub fn spec(&self) -> &AudioSpec { + &self.spec + } + + #[inline] + pub fn is_playing(&self) -> bool { + self.channels.iter().any(|channel| channel.playing) + } + + pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> { + if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.channels[channel_index].stop(); + Ok(()) + } + } + + pub fn stop_all(&mut self) { + for channel in self.channels.iter_mut() { + channel.stop(); + } + } + + pub fn play_buffer( + &mut self, + buffer: &AudioBuffer, + loops: bool, + ) -> Result, AudioDeviceError> { + if *buffer.spec() != self.spec { + Err(AudioDeviceError::AudioSpecMismatch) + } else { + if let Some(channel) = self.stopped_channels_iter_mut().next() { + channel.play_buffer(buffer, loops); + Ok(Some(channel)) + } else { + Ok(None) + } + } + } + + 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.channels[channel_index].play_buffer(buffer, loops); + Ok(()) + } + } + + pub fn play_generator( + &mut self, + generator: impl AudioGenerator + 'static, + loops: bool, + ) -> Result, AudioDeviceError> { + if let Some(channel) = self.stopped_channels_iter_mut().next() { + channel.play_generator(generator, loops); + Ok(Some(channel)) + } else { + Ok(None) + } + } + + pub fn play_generator_on_channel( + &mut self, + channel_index: usize, + generator: impl AudioGenerator + 'static, + loops: bool, + ) -> Result<(), AudioDeviceError> { + if channel_index >= NUM_CHANNELS { + Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) + } else { + self.channels[channel_index].play_generator(generator, loops); + Ok(()) + } + } + + #[inline] + pub fn playing_channels_iter(&mut self) -> impl Iterator { + self.channels.iter().filter(|channel| channel.playing) + } + + #[inline] + pub fn playing_channels_iter_mut(&mut self) -> impl Iterator { + self.channels.iter_mut().filter(|channel| channel.playing) + } + + #[inline] + pub fn stopped_channels_iter(&mut self) -> impl Iterator { + self.channels.iter().filter(|channel| !channel.playing) + } + + #[inline] + pub fn stopped_channels_iter_mut(&mut self) -> impl Iterator { + self.channels.iter_mut().filter(|channel| !channel.playing) + } + + #[inline] + pub fn channels_iter(&mut self) -> impl Iterator { + self.channels.iter() + } + + #[inline] + pub fn channels_iter_mut(&mut self) -> impl Iterator { + self.channels.iter_mut() + } + + #[inline] + pub fn get(&self, index: usize) -> Option<&AudioChannel> { + self.channels.get(index) + } + + #[inline] + pub fn get_mut(&mut self, index: usize) -> Option<&mut AudioChannel> { + self.channels.get_mut(index) + } +} + +impl Index for AudioDevice { + type Output = AudioChannel; + + #[inline] + fn index(&self, index: usize) -> &Self::Output { + self.get(index).unwrap() + } +} + +impl IndexMut for AudioDevice { + #[inline] + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + self.get_mut(index).unwrap() + } +} diff --git a/libretrogd/src/audio/mod.rs b/libretrogd/src/audio/mod.rs index 6186125..cbb4021 100644 --- a/libretrogd/src/audio/mod.rs +++ b/libretrogd/src/audio/mod.rs @@ -1,12 +1,12 @@ -use std::ops::{Index, IndexMut}; - -use sdl2::audio::{AudioCallback, AudioFormat, AudioFormatNum, AudioSpecDesired}; +use sdl2::audio::{AudioFormat, AudioFormatNum, AudioSpecDesired}; use sdl2::AudioSubsystem; use thiserror::Error; -pub use self::wav::*; +pub use self::buffer::*; +pub use self::device::*; -pub mod wav; +pub mod buffer; +pub mod device; pub const NUM_CHANNELS: usize = 8; @@ -61,318 +61,6 @@ pub trait AudioGenerator: Send { ////////////////////////////////////////////////////////////////////////////////////////////////// -pub struct AudioChannel { - pub playing: bool, - pub loops: bool, - pub data: Vec, - pub generator: Option>, - pub volume: f32, - pub position: usize, -} - -impl AudioChannel { - pub fn new() -> Self { - AudioChannel { - playing: false, - loops: false, - volume: 1.0, - position: 0, - generator: None, - data: Vec::new(), - } - } - - #[inline] - fn data_at(&mut self, position: usize) -> Option { - if let Some(generator) = &mut self.generator { - generator.gen_sample(position) - } else { - self.data.get(self.position).copied() - } - } - - /// Returns the next sample from this channel's buffer. If this channel's buffer is done - /// playing or there is no buffer data at all, `None` is returned. If the next sample was - /// successfully loaded from the buffer, the channel's current position is advanced by 1. - /// - /// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8` - /// value centered around 0 (meaning the returned sample will be within the range -128 to 127 - /// instead of 0 to 255). - #[inline] - fn next_sample(&mut self) -> Option { - if let Some(sample) = self.data_at(self.position) { - self.position += 1; - Some(sample as i16 - 128) - } else { - None - } - } - - /// Samples the channel's current audio buffer, advancing the position within that buffer by 1. - /// The channel will automatically stop playing when the end of the buffer is reached and if - /// the channel is not set to loop. `None` is returned if no data can be read from the buffer - /// for any reason, or if the channel is not currently playing. - /// - /// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8` - /// value centered around 0 (meaning the returned sample will be within the range -128 to 127 - /// instead of 0 to 255). - #[inline] - pub fn sample(&mut self) -> Option { - if !self.playing { - return None; - } - - if let Some(sample) = self.next_sample() { - Some((sample as f32 * self.volume) as i16) - } else { - if self.loops { - self.position = 0; - None - } else { - self.stop(); - None - } - } - } - - #[inline] - pub fn reset(&mut self) { - self.data.clear(); - self.generator = None; - self.position = 0; - self.playing = false; - } - - #[inline] - pub fn play_buffer(&mut self, buffer: &AudioBuffer, loops: bool) { - self.data.clear(); - self.data.extend(&buffer.data); - self.generator = None; - self.position = 0; - self.playing = true; - self.loops = loops; - } - - #[inline] - pub fn play_generator(&mut self, generator: impl AudioGenerator + 'static, loops: bool) { - self.data.clear(); - self.generator = Some(Box::new(generator)); - self.position = 0; - self.playing = true; - self.loops = loops; - } - - #[inline] - pub fn is_playable(&self) -> bool { - !self.data.is_empty() || self.generator.is_some() - } - - #[inline] - pub fn play(&mut self, loops: bool) { - if self.is_playable() { - self.position = 0; - self.playing = true; - self.loops = loops; - } - } - - #[inline] - pub fn stop(&mut self) { - self.playing = false; - } -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - -#[derive(Debug, Error)] -pub enum AudioDeviceError { - #[error("That buffer's AudioSpec does not match the device's AudioSpec")] - AudioSpecMismatch, - - #[error("The channel index {0} is out of range")] - ChannelIndexOutOfRange(usize), -} - -pub struct AudioDevice { - spec: AudioSpec, - channels: Vec, - pub volume: f32, -} - -impl AudioCallback for AudioDevice { - type Channel = u8; - - fn callback(&mut self, out: &mut [u8]) { - for dest in out.iter_mut() { - let mut sample: i16 = 0; - for channel in self.channels.iter_mut() { - if let Some(this_sample) = channel.sample() { - sample += this_sample; - } - } - sample = ((sample as f32) * self.volume) as i16; - *dest = (sample.clamp(-128, 127) + 128) as u8; - } - } -} - -impl AudioDevice { - pub fn new(spec: AudioSpec) -> Self { - let mut channels = Vec::new(); - for _ in 0..NUM_CHANNELS { - channels.push(AudioChannel::new()); - } - AudioDevice { - spec, - channels, - volume: 1.0, - } - } - - #[inline] - pub fn spec(&self) -> &AudioSpec { - &self.spec - } - - #[inline] - pub fn is_playing(&self) -> bool { - self.channels.iter().any(|channel| channel.playing) - } - - pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> { - if channel_index >= NUM_CHANNELS { - Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) - } else { - self.channels[channel_index].stop(); - Ok(()) - } - } - - pub fn stop_all(&mut self) { - for channel in self.channels.iter_mut() { - channel.stop(); - } - } - - pub fn play_buffer( - &mut self, - buffer: &AudioBuffer, - loops: bool, - ) -> Result, AudioDeviceError> { - if buffer.spec != self.spec { - Err(AudioDeviceError::AudioSpecMismatch) - } else { - if let Some(channel) = self.stopped_channels_iter_mut().next() { - channel.play_buffer(buffer, loops); - Ok(Some(channel)) - } else { - Ok(None) - } - } - } - - 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.channels[channel_index].play_buffer(buffer, loops); - Ok(()) - } - } - - pub fn play_generator( - &mut self, - generator: impl AudioGenerator + 'static, - loops: bool, - ) -> Result, AudioDeviceError> { - if let Some(channel) = self.stopped_channels_iter_mut().next() { - channel.play_generator(generator, loops); - Ok(Some(channel)) - } else { - Ok(None) - } - } - - pub fn play_generator_on_channel( - &mut self, - channel_index: usize, - generator: impl AudioGenerator + 'static, - loops: bool, - ) -> Result<(), AudioDeviceError> { - if channel_index >= NUM_CHANNELS { - Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index)) - } else { - self.channels[channel_index].play_generator(generator, loops); - Ok(()) - } - } - - #[inline] - pub fn playing_channels_iter(&mut self) -> impl Iterator { - self.channels.iter().filter(|channel| channel.playing) - } - - #[inline] - pub fn playing_channels_iter_mut(&mut self) -> impl Iterator { - self.channels.iter_mut().filter(|channel| channel.playing) - } - - #[inline] - pub fn stopped_channels_iter(&mut self) -> impl Iterator { - self.channels.iter().filter(|channel| !channel.playing) - } - - #[inline] - pub fn stopped_channels_iter_mut(&mut self) -> impl Iterator { - self.channels.iter_mut().filter(|channel| !channel.playing) - } - - #[inline] - pub fn channels_iter(&mut self) -> impl Iterator { - self.channels.iter() - } - - #[inline] - pub fn channels_iter_mut(&mut self) -> impl Iterator { - self.channels.iter_mut() - } - - #[inline] - pub fn get(&self, index: usize) -> Option<&AudioChannel> { - self.channels.get(index) - } - - #[inline] - pub fn get_mut(&mut self, index: usize) -> Option<&mut AudioChannel> { - self.channels.get_mut(index) - } -} - -impl Index for AudioDevice { - type Output = AudioChannel; - - #[inline] - fn index(&self, index: usize) -> &Self::Output { - self.get(index).unwrap() - } -} - -impl IndexMut for AudioDevice { - #[inline] - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - self.get_mut(index).unwrap() - } -} - -////////////////////////////////////////////////////////////////////////////////////////////////// - #[derive(Debug, Error)] pub enum AudioError { #[error("Failed to open audio device for playback: {0}")] @@ -441,54 +129,3 @@ impl Audio { self.sdl_audio_device.lock() } } - -////////////////////////////////////////////////////////////////////////////////////////////////// - -#[derive(Error, Debug)] -pub enum AudioBufferError { - #[error("Error during format conversion: {0}")] - ConversionError(String), -} - -#[derive(Debug, Clone)] -pub struct AudioBuffer { - spec: AudioSpec, - pub data: Vec, -} - -impl AudioBuffer { - pub fn new(spec: AudioSpec) -> Self { - AudioBuffer { - spec, - data: Vec::new(), - } - } - - #[inline] - pub fn spec(&self) -> &AudioSpec { - &self.spec - } - - pub fn convert(self, to_spec: &AudioSpec) -> Result { - if self.spec == *to_spec { - Ok(self) - } else { - let converter = sdl2::audio::AudioCVT::new( - self.spec.format(), - self.spec.channels(), - self.spec.frequency() as i32, - to_spec.format(), - to_spec.channels(), - to_spec.frequency() as i32, - ); - match converter { - Ok(converter) => { - let mut result = AudioBuffer::new(*to_spec); - result.data = converter.convert(self.data); - Ok(result) - } - Err(string) => Err(AudioBufferError::ConversionError(string)), - } - } - } -}