use rand::SeedableRng;
use rand::rngs::SmallRng;
use rand_distr::{Distribution, Normal, Triangular, Uniform};
use std::fmt;
use crate::NUM_CHANNELS;
pub trait Ditherer {
fn new() -> Self
where
Self: Sized;
fn name(&self) -> &'static str;
fn noise(&mut self) -> f64;
}
impl fmt::Display for dyn Ditherer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
fn create_rng() -> SmallRng {
SmallRng::from_os_rng()
}
pub struct TriangularDitherer {
cached_rng: SmallRng,
distribution: Triangular<f64>,
}
impl Ditherer for TriangularDitherer {
fn new() -> Self {
Self {
cached_rng: create_rng(),
distribution: Triangular::new(-1.0, 1.0, 0.0).unwrap(),
}
}
fn name(&self) -> &'static str {
Self::NAME
}
#[inline]
fn noise(&mut self) -> f64 {
self.distribution.sample(&mut self.cached_rng)
}
}
impl TriangularDitherer {
pub const NAME: &'static str = "tpdf";
}
pub struct GaussianDitherer {
cached_rng: SmallRng,
distribution: Normal<f64>,
}
impl Ditherer for GaussianDitherer {
fn new() -> Self {
Self {
cached_rng: create_rng(),
distribution: Normal::new(0.0, 0.6).unwrap(),
}
}
fn name(&self) -> &'static str {
Self::NAME
}
#[inline]
fn noise(&mut self) -> f64 {
self.distribution.sample(&mut self.cached_rng)
}
}
impl GaussianDitherer {
pub const NAME: &'static str = "gpdf";
}
pub struct HighPassDitherer {
active_channel: usize,
previous_noises: [f64; NUM_CHANNELS as usize],
cached_rng: SmallRng,
distribution: Uniform<f64>,
}
impl Ditherer for HighPassDitherer {
fn new() -> Self {
Self {
active_channel: 0,
previous_noises: [0.0; NUM_CHANNELS as usize],
cached_rng: create_rng(),
distribution: Uniform::new_inclusive(-0.5, 0.5)
.expect("Failed to create uniform distribution"),
}
}
fn name(&self) -> &'static str {
Self::NAME
}
#[inline]
fn noise(&mut self) -> f64 {
let new_noise = self.distribution.sample(&mut self.cached_rng);
let high_passed_noise = new_noise - self.previous_noises[self.active_channel];
self.previous_noises[self.active_channel] = new_noise;
self.active_channel ^= 1;
high_passed_noise
}
}
impl HighPassDitherer {
pub const NAME: &'static str = "tpdf_hp";
}
pub fn mk_ditherer<D: Ditherer + 'static>() -> Box<dyn Ditherer> {
Box::new(D::new())
}
pub type DithererBuilder = fn() -> Box<dyn Ditherer>;
pub fn find_ditherer(name: Option<String>) -> Option<DithererBuilder> {
match name.as_deref() {
Some(TriangularDitherer::NAME) => Some(mk_ditherer::<TriangularDitherer>),
Some(GaussianDitherer::NAME) => Some(mk_ditherer::<GaussianDitherer>),
Some(HighPassDitherer::NAME) => Some(mk_ditherer::<HighPassDitherer>),
_ => None,
}
}