Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synthesizer Waveforms #602

Merged
merged 33 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
fc22b37
Implemented synthesizer waveform source
iluvcapra Aug 8, 2024
128fa74
More synth work, but think I'm going to startover
iluvcapra Aug 8, 2024
8b08d12
Merge branch 'master' of https://github.com/RustAudio/rodio into synt…
iluvcapra Aug 9, 2024
eef2eda
Recent synth changes caught up with master
iluvcapra Aug 9, 2024
107454e
Implementation of synth waveforms, tests
iluvcapra Aug 9, 2024
7d4c9d7
Implemented `sine` source as a Synth
iluvcapra Aug 10, 2024
2783904
Dumb typo on sine sample rate
iluvcapra Aug 10, 2024
b6c8a7e
Implemented White and Pink noise generators
iluvcapra Aug 11, 2024
1eabb21
Some clippy and style changes
iluvcapra Aug 11, 2024
84a008d
Added noise_generator example
iluvcapra Aug 11, 2024
a015eb4
Using SmallRng for white noise generation
iluvcapra Aug 13, 2024
56d087a
Renamed Synth to Test
iluvcapra Aug 15, 2024
0907857
Documentation updates
iluvcapra Aug 15, 2024
87cd605
More documentation, WhiteNoise creation option.
iluvcapra Aug 15, 2024
59a5b12
Several code review fixes
iluvcapra Aug 19, 2024
a012ac1
Changes for code reviews
iluvcapra Aug 19, 2024
0c2c194
Made noise feature
iluvcapra Aug 19, 2024
e060bdd
Implemented chirp generator
iluvcapra Sep 14, 2024
f3e77f4
Removed `try_seek()` from chirp.
iluvcapra Sep 14, 2024
28b7fc1
Merge branch 'master' of https://github.com/RustAudio/rodio into synt…
iluvcapra Sep 15, 2024
c912f56
Removed white space for rustfmt
iluvcapra Sep 15, 2024
cde724a
Removed white space for rustfmt
iluvcapra Sep 15, 2024
56fcce7
Fixed a typo from @UnknownSuperficialNight
iluvcapra Sep 29, 2024
09c5f31
Merge branch 'master' of https://github.com/RustAudio/rodio into synt…
iluvcapra Sep 29, 2024
b373f52
Moved "noise" feature documentation
iluvcapra Sep 29, 2024
5fa0188
Added signal_generator.rs example
iluvcapra Sep 29, 2024
371bbda
Some cosmetic changes for rustfmt
iluvcapra Sep 29, 2024
9d20ea2
One more tweak for rustfmt
iluvcapra Sep 29, 2024
44cb217
Code review changes
iluvcapra Sep 29, 2024
8899e67
Renamed TestWaveform to SignalGenerator
iluvcapra Oct 1, 2024
a1fa144
Adding renamed signal_generator.rs
iluvcapra Oct 1, 2024
322bdd5
Rustfmt and a doc comment
iluvcapra Oct 1, 2024
1350246
rustfmt
iluvcapra Oct 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ minimp3_fixed = { version = "0.5.4", optional = true}
symphonia = { version = "0.5.4", optional = true, default-features = false }
crossbeam-channel = { version = "0.5.8", optional = true }
thiserror = "1.0.49"
rand = "0.8.5"

[features]
default = ["flac", "vorbis", "wav", "mp3"]
Expand Down
33 changes: 33 additions & 0 deletions examples/noise_generator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::thread;
use std::time::Duration;

use rodio::source::{pink, white, Source};

fn main() {
let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();

let noise_duration = Duration::from_millis(1000);
let interval_duration = Duration::from_millis(1500);

stream_handle
.play_raw(
white(cpal::SampleRate(48000))
.amplify(0.1)
.take_duration(noise_duration),
)
.unwrap();
println!("Playing white noise");

thread::sleep(interval_duration);

stream_handle
.play_raw(
pink(cpal::SampleRate(48000))
.amplify(0.1)
.take_duration(noise_duration),
)
.unwrap();
println!("Playing pink noise");

thread::sleep(interval_duration);
}
4 changes: 4 additions & 0 deletions src/source/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub use self::from_factory::{from_factory, FromFactoryIter};
pub use self::from_iter::{from_iter, FromIter};
pub use self::linear_ramp::LinearGainRamp;
pub use self::mix::Mix;
pub use self::noise::{pink, white, PinkNoise, WhiteNoise};
pub use self::pausable::Pausable;
pub use self::periodic::PeriodicAccess;
pub use self::position::TrackPosition;
Expand All @@ -32,6 +33,7 @@ pub use self::skippable::Skippable;
pub use self::spatial::Spatial;
pub use self::speed::Speed;
pub use self::stoppable::Stoppable;
pub use self::synth::{SynthWaveform, SynthWaveformFunction};
pub use self::take::TakeDuration;
pub use self::uniform::UniformSourceIterator;
pub use self::zero::Zero;
Expand All @@ -51,6 +53,7 @@ mod from_factory;
mod from_iter;
mod linear_ramp;
mod mix;
mod noise;
mod pausable;
mod periodic;
mod position;
Expand All @@ -62,6 +65,7 @@ mod skippable;
mod spatial;
mod speed;
mod stoppable;
mod synth;
mod take;
mod uniform;
mod zero;
Expand Down
135 changes: 135 additions & 0 deletions src/source/noise.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use crate::Source;

use super::SeekError;

/// Create a new white noise source.
#[inline]
pub fn white(sample_rate: cpal::SampleRate) -> WhiteNoise {
WhiteNoise::new(sample_rate)
}

/// Create a new pink noise source.
#[inline]
pub fn pink(sample_rate: cpal::SampleRate) -> PinkNoise {
PinkNoise::new(sample_rate)
}

/// Generates an infinite stream of random samples in [=1.0, 1.0]
#[derive(Clone, Debug)]
pub struct WhiteNoise {
sample_rate: cpal::SampleRate,
}

impl WhiteNoise {
/// Create a new white noise generator.
pub fn new(sample_rate: cpal::SampleRate) -> Self {
Self { sample_rate }
}
}

impl Iterator for WhiteNoise {
type Item = f32;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
let randf = rand::random::<f32>();
iluvcapra marked this conversation as resolved.
Show resolved Hide resolved
let scaled = randf * 2.0 - 1.0;
Some(scaled)
}
}

impl Source for WhiteNoise {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}

#[inline]
fn channels(&self) -> u16 {
1
}

#[inline]
fn sample_rate(&self) -> u32 {
self.sample_rate.0
}

#[inline]
fn total_duration(&self) -> Option<std::time::Duration> {
None
}

#[inline]
fn try_seek(&mut self, _: std::time::Duration) -> Result<(), SeekError> {
// Does nothing, should do nothing
Ok(())
}
}

// https://www.musicdsp.org/en/latest/Filters/76-pink-noise-filter.html
//
/// Generate an infinite stream of pink noise samples in [-1.0, 1.0].
pub struct PinkNoise {
noise: WhiteNoise,
iluvcapra marked this conversation as resolved.
Show resolved Hide resolved
b: [f32; 7],
}

impl PinkNoise {
pub fn new(sample_rate: cpal::SampleRate) -> Self {
Self {
noise: WhiteNoise::new(sample_rate),
b: [0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32],
}
}
}

impl Iterator for PinkNoise {
type Item = f32;

fn next(&mut self) -> Option<Self::Item> {
let white = self.noise.next().unwrap();
self.b[0] = 0.99886 * self.b[0] + white * 0.0555179;
self.b[1] = 0.99332 * self.b[1] + white * 0.0750759;
self.b[2] = 0.969 * self.b[2] + white * 0.153852;
self.b[3] = 0.8665 * self.b[3] + white * 0.3104856;
self.b[4] = 0.550 * self.b[4] + white * 0.5329522;
self.b[5] = -0.7616 * self.b[5] - white * 0.016898;

let pink = self.b[0]
+ self.b[1]
+ self.b[2]
+ self.b[3]
+ self.b[4]
+ self.b[5]
+ self.b[6]
+ white * 0.5362;

self.b[6] = white * 0.115926;

Some(pink)
}
}

impl Source for PinkNoise {
fn current_frame_len(&self) -> Option<usize> {
None
}

fn channels(&self) -> u16 {
1
}

fn sample_rate(&self) -> u32 {
self.noise.sample_rate()
}

fn total_duration(&self) -> Option<std::time::Duration> {
None
}

#[inline]
fn try_seek(&mut self, _: std::time::Duration) -> Result<(), SeekError> {
// Does nothing, should do nothing
Ok(())
}
}
26 changes: 10 additions & 16 deletions src/source/sine.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
use std::f32::consts::PI;
use std::time::Duration;

use crate::source::{SynthWaveform, SynthWaveformFunction};
use crate::Source;

use super::SeekError;

const SAMPLE_RATE: u32 = 48000;

/// An infinite source that produces a sine.
///
/// Always has a rate of 48kHz and one channel.
#[derive(Clone, Debug)]
pub struct SineWave {
freq: f32,
num_sample: usize,
synth: SynthWaveform,
}

impl SineWave {
/// The frequency of the sine.
#[inline]
pub fn new(freq: f32) -> SineWave {
let sr = cpal::SampleRate(SAMPLE_RATE);
SineWave {
freq,
num_sample: 0,
synth: SynthWaveform::new(sr, freq, SynthWaveformFunction::Sine),
}
}
}
Expand All @@ -30,10 +31,7 @@ impl Iterator for SineWave {

#[inline]
fn next(&mut self) -> Option<f32> {
self.num_sample = self.num_sample.wrapping_add(1);

let value = 2.0 * PI * self.freq * self.num_sample as f32 / 48000.0;
Some(value.sin())
self.synth.next()
}
}

Expand All @@ -50,7 +48,7 @@ impl Source for SineWave {

#[inline]
fn sample_rate(&self) -> u32 {
48000
SAMPLE_RATE
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this:

struct Test;

impl Test {
    const A: usize = 5;
}

is legal rust. So we could move SAMPLE_RATE into Sine like that. Then you could call Self::SAMPLE_RATE here which is slightly prettier?.

}

#[inline]
Expand All @@ -59,11 +57,7 @@ impl Source for SineWave {
}

#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
// This is a constant sound, normal seeking would not have any effect.
// While changing the phase of the sine wave could change how it sounds in
// combination with another sound (beating) such precision is not the intend
// of seeking
Ok(())
iluvcapra marked this conversation as resolved.
Show resolved Hide resolved
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.synth.try_seek(pos)
}
}
Loading