mirror of
https://github.com/RustAudio/rodio
synced 2024-12-13 13:42:34 +00:00
Several code review fixes
This commit is contained in:
parent
87cd605d73
commit
59a5b12367
5 changed files with 120 additions and 31 deletions
79
src/source/chirp.rs
Normal file
79
src/source/chirp.rs
Normal 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!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue