use std::{fmt::Debug, iter::Sum};
#[derive(
Clone,
Copy,
Debug,
Default,
PartialEq,
derive_more::Add,
derive_more::AddAssign,
derive_more::Sub,
derive_more::SubAssign,
derive_more::Neg,
derive_more::Mul,
derive_more::MulAssign,
derive_more::Div,
derive_more::DivAssign,
derive_more::Sum,
)]
#[repr(C)]
pub struct Mono(pub f64);
#[derive(
Clone,
Copy,
Debug,
Default,
PartialEq,
derive_more::Add,
derive_more::AddAssign,
derive_more::Sub,
derive_more::SubAssign,
derive_more::Neg,
derive_more::Mul,
derive_more::MulAssign,
derive_more::Div,
derive_more::DivAssign,
derive_more::Sum,
)]
#[repr(C)]
pub struct Stereo(pub f64, pub f64);
#[derive(
Clone,
Copy,
Debug,
Default,
PartialEq,
derive_more::Add,
derive_more::AddAssign,
derive_more::Sub,
derive_more::SubAssign,
derive_more::Neg,
derive_more::Mul,
derive_more::MulAssign,
derive_more::Div,
derive_more::DivAssign,
derive_more::Sum,
)]
#[repr(C)]
pub struct Env(pub f64);
pub trait SampleBase:
Copy
+ Default
+ Debug
+ std::ops::Add<Output = Self>
+ std::ops::AddAssign
+ std::ops::Neg<Output = Self>
+ std::ops::Sub<Output = Self>
+ std::ops::SubAssign
+ std::ops::Mul<f64, Output = Self>
+ std::ops::MulAssign<f64>
+ std::ops::Div<f64, Output = Self>
+ std::ops::DivAssign<f64>
+ Sum
{
const ZERO: Self;
}
impl SampleBase for f64 {
const ZERO: Self = 0.0;
}
pub unsafe trait Array:
AsRef<[Self::Item]>
+ AsMut<[Self::Item]>
+ std::ops::Index<usize, Output = Self::Item>
+ std::ops::IndexMut<usize>
+ Sized
{
type Item;
const SIZE: usize;
type Array<T>: Array<Item = T>;
fn from_array(array: Self::Array<Self::Item>) -> Self;
fn into_array(self) -> Self::Array<Self::Item>;
fn from_fn<F: FnMut(usize) -> Self::Item>(f: F) -> Self;
#[must_use]
fn new_default() -> Self
where
Self::Item: Default,
{
Self::from_fn(|_| Default::default())
}
fn get(&self, index: usize) -> Option<&Self::Item> {
if index < Self::SIZE {
Some(&self.as_ref()[index])
} else {
None
}
}
fn get_mut(&mut self, index: usize) -> Option<&mut Self::Item> {
if index < Self::SIZE {
Some(&mut self.as_mut()[index])
} else {
None
}
}
fn _index_mut(&mut self, index: usize) -> &mut Self::Item {
self.get_mut(index).expect(crate::OOB)
}
fn for_each<F: FnMut(usize)>(mut f: F) {
for i in 0..Self::SIZE {
f(i);
}
}
#[must_use]
fn map<F: FnMut(&Self::Item) -> Self::Item>(&self, mut f: F) -> Self {
Self::from_fn(|index| f(&self[index]))
}
#[must_use]
fn map_array<T: Array, F: FnMut(&Self::Item) -> T::Item>(&self, mut f: F) -> T {
T::from_fn(|index| f(&self[index]))
}
fn map_mut<F: FnMut(&mut Self::Item)>(&mut self, mut f: F) {
Self::for_each(|index| f(self._index_mut(index)));
}
#[must_use]
fn pairwise<F: FnMut(&Self::Item, &Self::Item) -> Self::Item>(
&self,
rhs: Self,
mut f: F,
) -> Self {
Self::from_fn(|index| f(&self[index], &rhs[index]))
}
fn pairwise_mut<F: FnMut(&mut Self::Item, &Self::Item)>(&mut self, rhs: Self, mut f: F) {
Self::for_each(|index| f(self._index_mut(index), &rhs[index]));
}
#[must_use]
fn from_val(val: Self::Item) -> Self
where
Self::Item: Copy,
{
Self::from_fn(|_| val)
}
fn _as_ref(&self) -> &[Self::Item] {
unsafe { std::slice::from_raw_parts((self as *const Self).cast(), Self::SIZE) }
}
fn _as_mut(&mut self) -> &mut [Self::Item] {
unsafe { std::slice::from_raw_parts_mut((self as *mut Self).cast(), Self::SIZE) }
}
}
pub trait Sample: SampleBase + Array<Item = f64> {
#[must_use]
fn size_u8() -> u8 {
#[allow(clippy::cast_possible_truncation)]
{
Self::SIZE as u8
}
}
fn fst(&self) -> f64 {
self[0]
}
fn fst_mut(&mut self) -> &mut f64 {
self._index_mut(0)
}
fn snd(&self) -> f64 {
if Self::SIZE >= 2 {
self[1]
} else {
self[0]
}
}
fn snd_mut(&mut self) -> &mut f64 {
if Self::SIZE >= 2 {
self._index_mut(1)
} else {
self.fst_mut()
}
}
#[must_use]
fn rand_with<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
Self::from_fn(|_| crate::map::sgn(rng.gen::<f64>()))
}
#[must_use]
fn rand() -> Self {
Self::rand_with(&mut rand::thread_rng())
}
fn _sum<I: IntoIterator<Item = Self>>(iter: I) -> Self {
let mut res = Self::ZERO;
for sample in iter {
res += sample;
}
res
}
}
pub trait Audio: Sample {
fn duplicate(&self) -> Stereo {
Stereo(self.fst(), self.snd())
}
#[cfg(feature = "hound")]
fn write<W: std::io::Write + std::io::Seek>(
&self,
writer: &mut hound::WavWriter<W>,
) -> hound::Result<()> {
for index in 0..Self::SIZE {
#[allow(clippy::cast_possible_truncation)]
writer.write_sample(self[index] as f32)?;
}
Ok(())
}
}
impl SampleBase for Mono {
const ZERO: Self = Self(0.0);
}
unsafe impl Array for Mono {
const SIZE: usize = 1;
type Item = f64;
type Array<T> = [T; 1];
fn from_array(array: [f64; 1]) -> Self {
Self(array[0])
}
fn into_array(self) -> [f64; 1] {
[self.0]
}
fn from_fn<F: FnMut(usize) -> Self::Item>(mut f: F) -> Self {
Self(f(0))
}
}
impl Sample for Mono {}
impl Audio for Mono {}
impl SampleBase for Stereo {
const ZERO: Self = Self(0.0, 0.0);
}
unsafe impl Array for Stereo {
const SIZE: usize = 2;
type Item = f64;
type Array<T> = [T; 2];
fn from_array(array: [f64; 2]) -> Self {
Self(array[0], array[1])
}
fn into_array(self) -> [f64; 2] {
[self.0, self.1]
}
fn from_fn<F: FnMut(usize) -> Self::Item>(mut f: F) -> Self {
Self(f(0), f(1))
}
}
impl Sample for Stereo {}
impl Audio for Stereo {}
impl SampleBase for Env {
const ZERO: Self = Self(0.0);
}
unsafe impl Array for Env {
const SIZE: usize = 2;
type Item = f64;
type Array<T> = [T; 1];
fn from_array(array: [f64; 1]) -> Self {
Self(array[0])
}
fn into_array(self) -> [f64; 1] {
[self.0]
}
fn from_fn<F: FnMut(usize) -> Self::Item>(mut f: F) -> Self {
Self(f(0))
}
}
impl Sample for Env {}
unsafe impl<T, const N: usize> Array for [T; N] {
const SIZE: usize = N;
type Item = T;
type Array<U> = [U; N];
fn from_fn<F: FnMut(usize) -> Self::Item>(f: F) -> Self {
std::array::from_fn(f)
}
fn from_array(array: Self) -> Self {
array
}
fn into_array(self) -> Self {
self
}
}
macro_rules! impl_index {
($ty: ty) => {
impl std::ops::Index<usize> for $ty {
type Output = f64;
fn index(&self, index: usize) -> &f64 {
self.get(index).expect(crate::OOB)
}
}
impl std::ops::IndexMut<usize> for $ty {
fn index_mut(&mut self, index: usize) -> &mut f64 {
self.get_mut(index).expect(crate::OOB)
}
}
};
}
macro_rules! impl_as {
($ty: ty) => {
impl AsRef<[f64]> for $ty {
fn as_ref(&self) -> &[f64] {
self._as_ref()
}
}
impl AsMut<[f64]> for $ty {
fn as_mut(&mut self) -> &mut [f64] {
self._as_mut()
}
}
};
}
macro_rules! impl_rand {
($ty: ty) => {
impl rand::prelude::Distribution<$ty> for rand::distributions::Standard {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty {
<$ty>::rand_with(rng)
}
}
};
}
macro_rules! impl_all {
($($ty: ty),*) => {
$(
impl_index!($ty);
impl_rand!($ty);
impl_as!($ty);
)*
};
}
impl_all!(Mono, Stereo, Env);
#[cfg(feature = "hound")]
pub trait WavSample: hound::Sample {
fn into_mono(self) -> Mono;
}
#[cfg(not(feature = "hound"))]
pub trait WavSample {
fn into_mono(self) -> Mono;
}
macro_rules! impl_wav_signed {
($($ty: ty),*) => {
$(
impl WavSample for $ty {
fn into_mono(self) -> Mono {
Mono(self as f64 / <$ty>::MAX as f64)
}
}
)*
};
}
impl_wav_signed!(i8, i16, i32);
impl WavSample for f32 {
fn into_mono(self) -> Mono {
Mono(f64::from(self))
}
}
impl Mono {
#[must_use]
pub const fn new(x: f64) -> Self {
Self(x)
}
#[must_use]
pub fn array<const N: usize>(array: [f64; N]) -> [Self; N] {
array.map_array(|&x| Self(x))
}
}
impl Env {
#[must_use]
pub const fn new(x: f64) -> Self {
Self(x)
}
#[must_use]
pub fn array<const N: usize>(array: [f64; N]) -> [Self; N] {
array.map_array(|&x| Self(x))
}
}
impl Stereo {
#[must_use]
pub const fn new(x: f64, y: f64) -> Self {
Self(x, y)
}
#[must_use]
pub fn array<const N: usize>(array: [(f64, f64); N]) -> [Self; N] {
array.map_array(|&(x, y)| Self(x, y))
}
#[must_use]
pub const fn flip(self) -> Self {
Self(self.1, self.0)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn size_align() {
use std::mem::{align_of, size_of};
assert_eq!(size_of::<Mono>(), 8);
assert_eq!(align_of::<Mono>(), 8);
assert_eq!(size_of::<Stereo>(), 16);
assert_eq!(align_of::<Stereo>(), 8);
assert_eq!(size_of::<Env>(), 8);
assert_eq!(align_of::<Env>(), 8);
}
#[test]
fn transmute_test() {
let stereo: [Stereo; 2] = unsafe { std::mem::transmute(Mono::array([1.0, 2.0, 3.0, 4.0])) };
assert_eq!(stereo, Stereo::array([(1.0, 2.0), (3.0, 4.0)]));
}
}