Several code review fixes

This commit is contained in:
Jamie Hardt 2024-08-19 17:14:06 -05:00
parent 87cd605d73
commit 59a5b12367
5 changed files with 120 additions and 31 deletions

79
src/source/chirp.rs Normal file
View file

@ -0,0 +1,79 @@
//! Chirp/sweep source.
use std::time::Duration;
use crate::Source;
/// Convenience function to create a new `Chirp` source.
#[inline]
pub fn chirp(
sample_rate: cpal::SampleRate,
start_frequency: f32,
end_frequency: f32,
duration: Duration,
) -> Chirp {
Chirp::new(sample_rate, start_frequency, end_frequency, duration)
}
/// Generate a sine wave with an instantaneous frequency that changes/sweeps linearly over time.
/// At the end of the chirp, once the `end_frequency` is reached, the source is exhausted.
#[derive(Clone, Debug)]
pub struct Chirp {
start_frequency: f32,
end_frequency: f32,
sample_rate: cpal::SampleRate,
total_samples: u64,
elapsed_samples: u64,
}
impl Chirp {
fn new(
sample_rate: cpal::SampleRate,
start_frequency: f32,
end_frequency: f32,
duration: Duration,
) -> Self {
Self {
sample_rate,
start_frequency,
end_frequency,
total_samples: (duration.as_secs_f64() * (sample_rate.0 as f64)) as u64,
elapsed_samples: 0,
}
}
}
impl Iterator for Chirp {
type Item = f32;
fn next(&mut self) -> Option<Self::Item> {
let i = self.elapsed_samples;
self.elapsed_samples += 1;
todo!()
}
}
impl Source for Chirp {
fn current_frame_len(&self) -> Option<usize> {
None
}
fn channels(&self) -> u16 {
1
}
fn sample_rate(&self) -> u32 {
self.sample_rate.0
}
fn total_duration(&self) -> Option<Duration> {
let secs: f64 = self.total_samples as f64 / self.sample_rate.0 as f64;
Some(Duration::new(1,0).mul_f64(secs))
}
fn try_seek(&mut self, pos: Duration) -> Result<(), super::SeekError> {
todo!()
}
}

View file

@ -10,6 +10,7 @@ pub use self::amplify::Amplify;
pub use self::blt::BltFilter; pub use self::blt::BltFilter;
pub use self::buffered::Buffered; pub use self::buffered::Buffered;
pub use self::channel_volume::ChannelVolume; pub use self::channel_volume::ChannelVolume;
pub use self::chirp::{chirp, Chirp};
pub use self::crossfade::Crossfade; pub use self::crossfade::Crossfade;
pub use self::delay::Delay; pub use self::delay::Delay;
pub use self::done::Done; pub use self::done::Done;
@ -41,6 +42,7 @@ pub use self::zero::Zero;
mod amplify; mod amplify;
mod blt; mod blt;
mod buffered; mod buffered;
mod chirp;
mod channel_volume; mod channel_volume;
mod crossfade; mod crossfade;
mod delay; mod delay;

View file

@ -8,13 +8,13 @@ use super::SeekError;
use rand::rngs::SmallRng; use rand::rngs::SmallRng;
use rand::{RngCore, SeedableRng}; use rand::{RngCore, SeedableRng};
/// Create a new `WhiteNoise` noise source. /// Convenience function to create a new `WhiteNoise` noise source.
#[inline] #[inline]
pub fn white(sample_rate: cpal::SampleRate) -> WhiteNoise { pub fn white(sample_rate: cpal::SampleRate) -> WhiteNoise {
WhiteNoise::new(sample_rate) WhiteNoise::new(sample_rate)
} }
/// Create a new `PinkNoise` noise source. /// Convenience function to create a new `PinkNoise` noise source.
#[inline] #[inline]
pub fn pink(sample_rate: cpal::SampleRate) -> PinkNoise { pub fn pink(sample_rate: cpal::SampleRate) -> PinkNoise {
PinkNoise::new(sample_rate) PinkNoise::new(sample_rate)
@ -51,8 +51,8 @@ impl Iterator for WhiteNoise {
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let randf = self.rng.next_u32() as f32 / u32::MAX as f32; let rand = self.rng.next_u32() as f32 / u32::MAX as f32;
let scaled = randf * 2.0 - 1.0; let scaled = rand * 2.0 - 1.0;
Some(scaled) Some(scaled)
} }
} }
@ -85,19 +85,22 @@ impl Source for WhiteNoise {
} }
} }
/// Generate an infinite stream of pink noise samples in [-1.0, 1.0]. /// Generates an infinite stream of pink noise samples in [-1.0, 1.0].
/// ///
/// The output of this source is the result of taking the output of the `WhiteNoise` source and /// The output of the source is the result of taking the output of the `WhiteNoise` source and
/// filtering it according to a weighted-sum of seven FIR filters after [Paul Kellett](https://www.musicdsp.org/en/latest/Filters/76-pink-noise-filter.html). /// filtering it according to a weighted-sum of seven FIR filters after [Paul Kellett's
/// method][pk_method] from *musicdsp.org*.
///
/// [pk_method]: https://www.musicdsp.org/en/latest/Filters/76-pink-noise-filter.html
pub struct PinkNoise { pub struct PinkNoise {
noise: WhiteNoise, white_noise: WhiteNoise,
b: [f32; 7], b: [f32; 7],
} }
impl PinkNoise { impl PinkNoise {
pub fn new(sample_rate: cpal::SampleRate) -> Self { pub fn new(sample_rate: cpal::SampleRate) -> Self {
Self { Self {
noise: WhiteNoise::new(sample_rate), white_noise: WhiteNoise::new(sample_rate),
b: [0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32], b: [0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32],
} }
} }
@ -107,7 +110,7 @@ impl Iterator for PinkNoise {
type Item = f32; type Item = f32;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let white = self.noise.next().unwrap(); let white = self.white_noise.next().unwrap();
self.b[0] = 0.99886 * self.b[0] + white * 0.0555179; self.b[0] = 0.99886 * self.b[0] + white * 0.0555179;
self.b[1] = 0.99332 * self.b[1] + white * 0.0750759; self.b[1] = 0.99332 * self.b[1] + white * 0.0750759;
self.b[2] = 0.969 * self.b[2] + white * 0.153852; self.b[2] = 0.969 * self.b[2] + white * 0.153852;
@ -140,7 +143,7 @@ impl Source for PinkNoise {
} }
fn sample_rate(&self) -> u32 { fn sample_rate(&self) -> u32 {
self.noise.sample_rate() self.white_noise.sample_rate()
} }
fn total_duration(&self) -> Option<std::time::Duration> { fn total_duration(&self) -> Option<std::time::Duration> {

View file

@ -12,7 +12,7 @@ const SAMPLE_RATE: u32 = 48000;
/// Always has a rate of 48kHz and one channel. /// Always has a rate of 48kHz and one channel.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SineWave { pub struct SineWave {
synth: TestWaveform, test_sine: TestWaveform,
} }
impl SineWave { impl SineWave {
@ -21,7 +21,7 @@ impl SineWave {
pub fn new(freq: f32) -> SineWave { pub fn new(freq: f32) -> SineWave {
let sr = cpal::SampleRate(SAMPLE_RATE); let sr = cpal::SampleRate(SAMPLE_RATE);
SineWave { SineWave {
synth: TestWaveform::new(sr, freq, TestWaveformFunction::Sine), test_sine: TestWaveform::new(sr, freq, TestWaveformFunction::Sine),
} }
} }
} }
@ -31,7 +31,7 @@ impl Iterator for SineWave {
#[inline] #[inline]
fn next(&mut self) -> Option<f32> { fn next(&mut self) -> Option<f32> {
self.synth.next() self.test_sine.next()
} }
} }
@ -58,6 +58,6 @@ impl Source for SineWave {
#[inline] #[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.synth.try_seek(pos) self.test_sine.try_seek(pos)
} }
} }

View file

@ -1,8 +1,8 @@
//! Generator sources for various periodic test waveforms. //! Generator sources for various periodic test waveforms.
//! //!
//! This module provides several periodic, deterministic waveforms for testing other sources and //! This module provides several periodic, deterministic waveforms for testing other sources and
//! for simple additive sound synthesis. Every source is monoaural and in the codomain `[-1.0f32, //! for simple additive sound synthesis. Every source is monoaural and in the codomain [-1.0f32,
//! 1.0f32]` //! 1.0f32].
//! //!
//! # Example //! # Example
//! //!
@ -17,12 +17,12 @@ use std::time::Duration;
use super::SeekError; use super::SeekError;
use crate::Source; use crate::Source;
/// Test waveform functions. /// Waveform functions.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum TestWaveformFunction { pub enum Function {
/// A sinusoidal waveform. /// A sinusoidal waveform.
Sine, Sine,
/// A triangle wave. /// A triangle waveform.
Triangle, Triangle,
/// A square wave, rising edge at t=0. /// A square wave, rising edge at t=0.
Square, Square,
@ -30,7 +30,7 @@ pub enum TestWaveformFunction {
Sawtooth, Sawtooth,
} }
impl TestWaveformFunction { impl Function {
/// Create a single sample for the given waveform /// Create a single sample for the given waveform
#[inline] #[inline]
fn render(&self, i: u64, period: f32) -> f32 { fn render(&self, i: u64, period: f32) -> f32 {
@ -38,7 +38,7 @@ impl TestWaveformFunction {
match self { match self {
Self::Sine => (TAU * i_div_p).sin(), Self::Sine => (TAU * i_div_p).sin(),
Self::Triangle => 04.0f32 * (i_div_p - (i_div_p + 0.5f32).floor()).abs() - 1f32, Self::Triangle => 4.0f32 * (i_div_p - (i_div_p + 0.5f32).floor()).abs() - 1f32,
Self::Square => { Self::Square => {
if i_div_p % 1.0f32 < 0.5f32 { if i_div_p % 1.0f32 < 0.5f32 {
1.0f32 1.0f32
@ -56,24 +56,29 @@ impl TestWaveformFunction {
pub struct TestWaveform { pub struct TestWaveform {
sample_rate: cpal::SampleRate, sample_rate: cpal::SampleRate,
period: f32, period: f32,
f: TestWaveformFunction, function: Function,
i: u64, i: u64,
} }
impl TestWaveform { impl TestWaveform {
/// Create a new `TestWaveform` object that generates an endless waveform /// Create a new `TestWaveform` object that generates an endless waveform
/// `f`. /// `f`.
///
/// # Panics
///
/// Will panic if `frequency` is equal to zero.
#[inline] #[inline]
pub fn new( pub fn new(
sample_rate: cpal::SampleRate, sample_rate: cpal::SampleRate,
frequency: f32, frequency: f32,
f: TestWaveformFunction, f: Function,
) -> TestWaveform { ) -> TestWaveform {
assert!(frequency != 0.0, "frequency must be greater than zero");
let period = sample_rate.0 as f32 / frequency; let period = sample_rate.0 as f32 / frequency;
TestWaveform { TestWaveform {
sample_rate, sample_rate,
period, period,
f, function: f,
i: 0, i: 0,
} }
} }
@ -86,7 +91,7 @@ impl Iterator for TestWaveform {
fn next(&mut self) -> Option<f32> { fn next(&mut self) -> Option<f32> {
let this_i = self.i; let this_i = self.i;
self.i += 1; self.i += 1;
Some(self.f.render(this_i, self.period)) Some(self.function.render(this_i, self.period))
} }
} }
@ -120,7 +125,7 @@ impl Source for TestWaveform {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::source::{TestWaveform, TestWaveformFunction}; use crate::source::{TestWaveform, Function};
use approx::assert_abs_diff_eq; use approx::assert_abs_diff_eq;
#[test] #[test]
@ -128,7 +133,7 @@ mod tests {
let mut wf = TestWaveform::new( let mut wf = TestWaveform::new(
cpal::SampleRate(2000), cpal::SampleRate(2000),
500.0f32, 500.0f32,
TestWaveformFunction::Square, Function::Square,
); );
assert_eq!(wf.next(), Some(1.0f32)); assert_eq!(wf.next(), Some(1.0f32));
assert_eq!(wf.next(), Some(1.0f32)); assert_eq!(wf.next(), Some(1.0f32));
@ -145,7 +150,7 @@ mod tests {
let mut wf = TestWaveform::new( let mut wf = TestWaveform::new(
cpal::SampleRate(8000), cpal::SampleRate(8000),
1000.0f32, 1000.0f32,
TestWaveformFunction::Triangle, Function::Triangle,
); );
assert_eq!(wf.next(), Some(-1.0f32)); assert_eq!(wf.next(), Some(-1.0f32));
assert_eq!(wf.next(), Some(-0.5f32)); assert_eq!(wf.next(), Some(-0.5f32));
@ -170,7 +175,7 @@ mod tests {
let mut wf = TestWaveform::new( let mut wf = TestWaveform::new(
cpal::SampleRate(200), cpal::SampleRate(200),
50.0f32, 50.0f32,
TestWaveformFunction::Sawtooth, Function::Sawtooth,
); );
assert_eq!(wf.next(), Some(0.0f32)); assert_eq!(wf.next(), Some(0.0f32));
assert_eq!(wf.next(), Some(0.5f32)); assert_eq!(wf.next(), Some(0.5f32));
@ -183,7 +188,7 @@ mod tests {
#[test] #[test]
fn sine() { fn sine() {
let mut wf = TestWaveform::new(cpal::SampleRate(1000), 100f32, TestWaveformFunction::Sine); let mut wf = TestWaveform::new(cpal::SampleRate(1000), 100f32, Function::Sine);
assert_abs_diff_eq!(wf.next().unwrap(), 0.0f32); assert_abs_diff_eq!(wf.next().unwrap(), 0.0f32);
assert_abs_diff_eq!(wf.next().unwrap(), 0.58778525f32); assert_abs_diff_eq!(wf.next().unwrap(), 0.58778525f32);