feat: modularize sin wave generator
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
pub struct AudioPlayer {
|
||||
stream: Stream,
|
||||
}
|
||||
use super::Generator;
|
||||
use crate::generators::GeneratorFn;
|
||||
use cpal::{
|
||||
Device, Error, ErrorKind, FromSample, I24, OutputCallbackInfo, Sample, SampleFormat, SizedSample, Stream, StreamConfig, SupportedStreamConfig, traits::{DeviceTrait, StreamTrait},
|
||||
};
|
||||
@@ -27,7 +27,7 @@ impl AudioPlayer {
|
||||
/// Returns an error if:
|
||||
/// - the device reports a sample format `raust` does not support, or
|
||||
/// - CPAL fails to build the output stream.
|
||||
pub fn try_new(device: &Device, config: SupportedStreamConfig, generator: Generator) -> anyhow::Result<Self> {
|
||||
pub fn try_new(device: &Device, config: SupportedStreamConfig, generator: GeneratorFn) -> anyhow::Result<Self> {
|
||||
let stream = match config.sample_format() {
|
||||
SampleFormat::I8 => Self::create_stream::<i8>(device, config.into(), generator),
|
||||
SampleFormat::I16 => Self::create_stream::<i16>(device, config.into(), generator),
|
||||
@@ -57,7 +57,7 @@ impl AudioPlayer {
|
||||
pub fn create_stream<T>(
|
||||
device: &Device,
|
||||
config: StreamConfig,
|
||||
mut generator: Generator,
|
||||
mut generator: GeneratorFn,
|
||||
) -> Result<Stream, anyhow::Error>
|
||||
where
|
||||
T: SizedSample + FromSample<f32>,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
mod sin_wave;
|
||||
pub use sin_wave::SinWave;
|
||||
|
||||
pub trait Generator {
|
||||
fn generate(&mut self) -> f32;
|
||||
}
|
||||
|
||||
/// Produces one `f32` sample per call.
|
||||
///
|
||||
/// `AudioPlayer` consumes a `Generator` when it builds its stream and moves
|
||||
/// it onto the audio callback thread, so `Send + 'static` must hold — the
|
||||
/// same bound [`cpal::Device::build_output_stream`] places on its data
|
||||
/// callback.
|
||||
pub type GeneratorFn = Box<dyn FnMut() -> f32 + Send + 'static>;
|
||||
@@ -0,0 +1,30 @@
|
||||
use cpal::SampleRate;
|
||||
|
||||
use crate::{Phase, Pitch};
|
||||
|
||||
use super::Generator;
|
||||
|
||||
pub struct SinWave {
|
||||
phase: Phase,
|
||||
pitch: Pitch,
|
||||
sample_rate: f32
|
||||
}
|
||||
|
||||
impl SinWave {
|
||||
#[must_use]
|
||||
pub fn new(sample_rate: SampleRate, pitch: Pitch) -> Self {
|
||||
#[allow(clippy::cast_precision_loss, clippy::as_conversions)]
|
||||
let sample_rate = sample_rate as f32;
|
||||
Self {
|
||||
phase: Phase::new(sample_rate, None),
|
||||
pitch,
|
||||
sample_rate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator for SinWave {
|
||||
fn generate(&mut self) -> f32 {
|
||||
(self.phase.next() * self.pitch * 2.0 * std::f32::consts::PI / self.sample_rate).sin()
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,11 @@ mod audio_player;
|
||||
pub use audio_player::AudioPlayer;
|
||||
mod init;
|
||||
pub use init::*;
|
||||
pub mod generators;
|
||||
mod phase;
|
||||
pub(crate) use phase::Phase;
|
||||
|
||||
/// Produces one `f32` sample per call.
|
||||
///
|
||||
/// `AudioPlayer` consumes a `Generator` when it builds its stream and moves
|
||||
/// it onto the audio callback thread, so `Send + 'static` must hold — the
|
||||
/// same bound [`cpal::Device::build_output_stream`] places on its data
|
||||
/// callback.
|
||||
pub type Generator = Box<dyn FnMut() -> f32 + Send + 'static>;
|
||||
type Pitch = f32;
|
||||
|
||||
/// A single tone: a pitch held for a fixed duration.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
type PhaseFn = Box<dyn FnMut(f32, f32) -> f32 + Send + 'static>; // (current, sample_rate) -> next
|
||||
|
||||
pub struct Phase {
|
||||
current: f32,
|
||||
sample_rate: f32,
|
||||
next_fn: PhaseFn
|
||||
}
|
||||
|
||||
impl Phase {
|
||||
pub fn new(sample_rate: f32, next_fn: Option<PhaseFn>) -> Self {
|
||||
Self {
|
||||
current: 0.0,
|
||||
sample_rate,
|
||||
next_fn: next_fn.unwrap_or_else(|| {
|
||||
Box::new(|current: f32, sample_rate: f32| (current + 1.0) % sample_rate)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> f32 {
|
||||
self.current = (self.next_fn)(self.current, self.sample_rate);
|
||||
self.current
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user