add initial support for playing custom audio waveform generators
with typical sine wave generator example added to the audio_playback example project
This commit is contained in:
parent
9e8727c02b
commit
28c964e37e
|
@ -1,3 +1,4 @@
|
|||
use std::f64::consts::PI;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
@ -27,6 +28,31 @@ fn load_and_convert_wav(path: &Path, target_spec: &AudioSpec) -> Result<AudioBuf
|
|||
Ok(sound)
|
||||
}
|
||||
|
||||
pub struct SineWaveGenerator {
|
||||
t: usize,
|
||||
}
|
||||
|
||||
impl SineWaveGenerator {
|
||||
pub fn new() -> Self {
|
||||
SineWaveGenerator {
|
||||
t: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioGenerator for SineWaveGenerator {
|
||||
fn gen_sample(&mut self, position: usize) -> Option<u8> {
|
||||
const MAX_TIME: usize = AUDIO_FREQUENCY_22KHZ as usize * 3; // 3 seconds
|
||||
if self.t < MAX_TIME {
|
||||
let sample = (self.t as f64 * 0.25).sin() * 80.0;
|
||||
self.t += 1;
|
||||
Some((sample + 128.0) as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut system = SystemBuilder::new().window_title("Audio Playback").vsync(true).build()?;
|
||||
|
||||
|
@ -73,6 +99,10 @@ fn main() -> Result<()> {
|
|||
audio_device.play_buffer(&sound5, false)?;
|
||||
}
|
||||
|
||||
if system.keyboard.is_key_pressed(Scancode::Num6) {
|
||||
audio_device.play_generator(Box::new(SineWaveGenerator::new()), false);
|
||||
}
|
||||
|
||||
for index in 0..NUM_CHANNELS {
|
||||
let channel = &audio_device[index];
|
||||
let mut status = &mut statuses[index];
|
||||
|
|
|
@ -55,11 +55,17 @@ impl AudioSpec {
|
|||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub trait AudioGenerator: Send {
|
||||
fn gen_sample(&mut self, position: usize) -> Option<u8>;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct AudioChannel {
|
||||
pub playing: bool,
|
||||
pub loops: bool,
|
||||
pub data: Vec<u8>,
|
||||
pub generator: Option<Box<dyn AudioGenerator>>,
|
||||
pub volume: f32,
|
||||
pub position: usize,
|
||||
}
|
||||
|
@ -71,10 +77,19 @@ impl AudioChannel {
|
|||
loops: false,
|
||||
volume: 1.0,
|
||||
position: 0,
|
||||
generator: None,
|
||||
data: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn data_at(&mut self, position: usize) -> Option<u8> {
|
||||
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.
|
||||
|
@ -84,9 +99,9 @@ impl AudioChannel {
|
|||
/// instead of 0 to 255).
|
||||
#[inline]
|
||||
fn next_sample(&mut self) -> Option<i16> {
|
||||
if let Some(sample) = self.data.get(self.position) {
|
||||
if let Some(sample) = self.data_at(self.position) {
|
||||
self.position += 1;
|
||||
Some((*sample as i16) - 128)
|
||||
Some(sample as i16 - 128)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -104,25 +119,25 @@ impl AudioChannel {
|
|||
pub fn sample(&mut self) -> Option<i16> {
|
||||
if !self.playing {
|
||||
return None;
|
||||
} else if self.position >= self.data.len() {
|
||||
if self.loops {
|
||||
self.position = 0;
|
||||
} else {
|
||||
self.stop();
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(raw_sample) = self.next_sample() {
|
||||
Some((raw_sample as f32 * self.volume) as i16)
|
||||
if let Some(sample) = self.next_sample() {
|
||||
Some((sample as f32 * self.volume) as i16)
|
||||
} else {
|
||||
None
|
||||
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;
|
||||
}
|
||||
|
@ -131,14 +146,27 @@ impl AudioChannel {
|
|||
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;
|
||||
}
|
||||
|
||||
pub fn play_generator(&mut self, generator: Box<dyn AudioGenerator>, loops: bool) {
|
||||
self.data.clear();
|
||||
self.generator = Some(generator);
|
||||
self.position = 0;
|
||||
self.playing = true;
|
||||
self.loops = loops;
|
||||
}
|
||||
|
||||
pub fn is_playable(&self) -> bool {
|
||||
!self.data.is_empty() || self.generator.is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn play(&mut self, loops: bool) {
|
||||
if !self.data.is_empty() {
|
||||
if self.is_playable() {
|
||||
self.position = 0;
|
||||
self.playing = true;
|
||||
self.loops = loops;
|
||||
|
@ -222,6 +250,15 @@ impl AudioDevice {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn play_generator(&mut self, generator: Box<dyn AudioGenerator>, loops: bool) -> Result<Option<&mut AudioChannel>, AudioDeviceError> {
|
||||
if let Some(channel) = self.stopped_channels_iter_mut().next() {
|
||||
channel.play_generator(generator, loops);
|
||||
Ok(Some(channel))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn playing_channels_iter(&mut self) -> impl Iterator<Item = &AudioChannel> {
|
||||
self.channels.iter().filter(|channel| channel.playing)
|
||||
|
|
Loading…
Reference in a new issue