use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum NoiseType {
White,
Pink,
Brown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Xorshift32 {
state: u32,
}
impl Xorshift32 {
fn new(seed: u32) -> Self {
Self {
state: if seed == 0 { 1 } else { seed },
}
}
#[inline]
fn next_f32(&mut self) -> f32 {
crate::dsp_util::xorshift32_signed_f32(&mut self.state)
}
}
const PINK_OCTAVES: usize = 16;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NoiseGenerator {
noise_type: NoiseType,
rng: Xorshift32,
pink_octaves: [f32; PINK_OCTAVES],
pink_counter: u32,
pink_running_sum: f32,
brown_prev: f32,
}
impl NoiseGenerator {
#[must_use]
pub fn new(noise_type: NoiseType, seed: u32) -> Self {
let mut rng = Xorshift32::new(seed);
let mut pink_octaves = [0.0f32; PINK_OCTAVES];
let mut pink_running_sum = 0.0f32;
if noise_type == NoiseType::Pink {
for octave in &mut pink_octaves {
let val = rng.next_f32();
*octave = val;
pink_running_sum += val;
}
}
Self {
noise_type,
rng,
pink_octaves,
pink_counter: 0,
pink_running_sum,
brown_prev: 0.0,
}
}
#[inline]
#[must_use]
pub fn noise_type(&self) -> NoiseType {
self.noise_type
}
#[inline]
#[must_use]
pub fn next_sample(&mut self) -> f32 {
match self.noise_type {
NoiseType::White => self.white_noise(),
NoiseType::Pink => self.pink_noise(),
NoiseType::Brown => self.brown_noise(),
}
}
#[inline]
fn white_noise(&mut self) -> f32 {
self.rng.next_f32()
}
#[inline]
fn pink_noise(&mut self) -> f32 {
self.pink_counter = self.pink_counter.wrapping_add(1);
let changed_bits = self.pink_counter ^ self.pink_counter.wrapping_sub(1);
for i in 0..PINK_OCTAVES {
if changed_bits & (1 << i) != 0 {
self.pink_running_sum -= self.pink_octaves[i];
let new_val = self.rng.next_f32();
self.pink_octaves[i] = new_val;
self.pink_running_sum += new_val;
}
}
let white = self.rng.next_f32();
(self.pink_running_sum + white) / (PINK_OCTAVES as f32 + 1.0)
}
#[inline]
fn brown_noise(&mut self) -> f32 {
let white = self.rng.next_f32();
self.brown_prev += white * 0.02;
self.brown_prev = self.brown_prev.clamp(-1.0, 1.0);
self.brown_prev *= 0.999;
self.brown_prev
}
#[inline]
pub fn fill_buffer(&mut self, buffer: &mut [f32]) {
for sample in buffer.iter_mut() {
*sample = self.next_sample();
}
}
}
#[must_use]
pub fn white_noise_sample(seed: &mut u32) -> f32 {
crate::dsp_util::xorshift32_signed_f32(seed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_white_noise_range() {
let mut ngen = NoiseGenerator::new(NoiseType::White, 42);
for _ in 0..10000 {
let s = ngen.next_sample();
assert!((-1.0..=1.0).contains(&s), "white noise out of range: {s}");
}
}
#[test]
fn test_pink_noise_range() {
let mut ngen = NoiseGenerator::new(NoiseType::Pink, 42);
for _ in 0..10000 {
let s = ngen.next_sample();
assert!((-2.0..=2.0).contains(&s), "pink noise out of range: {s}");
}
}
#[test]
fn test_brown_noise_range() {
let mut ngen = NoiseGenerator::new(NoiseType::Brown, 42);
for _ in 0..10000 {
let s = ngen.next_sample();
assert!((-1.0..=1.0).contains(&s), "brown noise out of range: {s}");
}
}
#[test]
fn test_deterministic() {
let mut ngen1 = NoiseGenerator::new(NoiseType::White, 42);
let mut ngen2 = NoiseGenerator::new(NoiseType::White, 42);
for _ in 0..100 {
assert!((ngen1.next_sample() - ngen2.next_sample()).abs() < f32::EPSILON);
}
}
#[test]
fn test_fill_buffer() {
let mut ngen = NoiseGenerator::new(NoiseType::White, 42);
let mut buf = [0.0f32; 256];
ngen.fill_buffer(&mut buf);
assert!(buf.iter().any(|&s| s != 0.0));
}
#[test]
fn test_serde_roundtrip() {
let ngen = NoiseGenerator::new(NoiseType::Pink, 123);
let json = serde_json::to_string(&ngen).unwrap();
let back: NoiseGenerator = serde_json::from_str(&json).unwrap();
assert_eq!(ngen.noise_type(), back.noise_type());
}
}