pointillism/
sample.rs

1//! Defines the [`Sample`] trait, and implements it for types [`Mono`], [`Stereo`], and [`Env`].
2//!
3//! [`Mono`] and [`Stereo`] are [`Audio`] samples, meaning that they can be written to a WAV file in
4//! order to produce sound. [`Env`] is reserved for outputs from envelopes, such as an
5//! [`Adsr`](crate::eff::env::Adsr).
6//!
7//! The abbreviation for this namespace is `smp`.
8
9use std::{fmt::Debug, iter::Sum};
10
11/// A sample of mono audio, typically holding a value between `-1.0` and `1.0`.
12///
13/// This is distinguished from [`Env`], as they have different uses. There shouldn't be much reason
14/// to convert one to the other.
15#[derive(
16    Clone,
17    Copy,
18    Debug,
19    Default,
20    PartialEq,
21    derive_more::Add,
22    derive_more::AddAssign,
23    derive_more::Sub,
24    derive_more::SubAssign,
25    derive_more::Neg,
26    derive_more::Mul,
27    derive_more::MulAssign,
28    derive_more::Div,
29    derive_more::DivAssign,
30    derive_more::Sum,
31)]
32#[repr(C)]
33pub struct Mono(pub f64);
34
35/// A sample of stereo audio, typically holding values between `-1.0` and `1.0`.
36///
37/// The left channel is stored in `.0`, the right channel is stored in `.1`.
38#[derive(
39    Clone,
40    Copy,
41    Debug,
42    Default,
43    PartialEq,
44    derive_more::Add,
45    derive_more::AddAssign,
46    derive_more::Sub,
47    derive_more::SubAssign,
48    derive_more::Neg,
49    derive_more::Mul,
50    derive_more::MulAssign,
51    derive_more::Div,
52    derive_more::DivAssign,
53    derive_more::Sum,
54)]
55#[repr(C)]
56pub struct Stereo(pub f64, pub f64);
57
58/// A data sample from an envelope, typically holding a value between `-1.0` and `1.0`.
59///
60/// This is distinguished from [`Mono`], as they have different uses. There shouldn't be much reason
61/// to convert one to the other.
62#[derive(
63    Clone,
64    Copy,
65    Debug,
66    Default,
67    PartialEq,
68    derive_more::Add,
69    derive_more::AddAssign,
70    derive_more::Sub,
71    derive_more::SubAssign,
72    derive_more::Neg,
73    derive_more::Mul,
74    derive_more::MulAssign,
75    derive_more::Div,
76    derive_more::DivAssign,
77    derive_more::Sum,
78)]
79#[repr(C)]
80pub struct Env(pub f64);
81
82/// A trait for things that one can perform arithmetic on, like a sample. This includes anything
83/// implementing [`Sample`] as well as `f64`.
84///
85/// This trait exists mostly for convenient, general implementations of methods such as
86/// [`buf::int::linear`](crate::buf::int::linear), which make sense both for samples and for
87/// floating point values.
88pub trait SampleBase:
89    Copy
90    + Default
91    + Debug
92    + std::ops::Add<Output = Self>
93    + std::ops::AddAssign
94    + std::ops::Neg<Output = Self>
95    + std::ops::Sub<Output = Self>
96    + std::ops::SubAssign
97    + std::ops::Mul<f64, Output = Self>
98    + std::ops::MulAssign<f64>
99    + std::ops::Div<f64, Output = Self>
100    + std::ops::DivAssign<f64>
101    + Sum
102{
103    /// The zero value.
104    const ZERO: Self;
105}
106
107impl SampleBase for f64 {
108    const ZERO: Self = 0.0;
109}
110
111/// A trait for array-like types that store a compile-time amount of data contiguously.
112///
113/// This trait serves two purposes:
114///
115/// - Provide simple convenience functions for the [`Sample`] types.
116/// - Allow functions on [`Samples`](Sample) to return an array type of the corresponding size,
117///   which can be generically manipulated.
118///
119/// ## Safety
120///
121/// Implementors of the trait must guarantee that the type has the same size and alignment as
122/// `[Self::Item; Self::SIZE]`.
123pub unsafe trait Array:
124    AsRef<[Self::Item]>
125    + AsMut<[Self::Item]>
126    + std::ops::Index<usize, Output = Self::Item>
127    + std::ops::IndexMut<usize>
128    + Sized
129{
130    /// The type of items this array stores.
131    type Item;
132
133    /// The size of the array.
134    const SIZE: usize;
135
136    /// The array type with the same number of elements as `SIZE`.
137    ///
138    /// If we could use `[T; Self::SIZE]`, this wouldn't be needed.
139    type Array<T>: Array<Item = T>;
140
141    /// Creates a value from an array.
142    fn from_array(array: Self::Array<Self::Item>) -> Self;
143
144    /// Turns this value into an array.
145    fn into_array(self) -> Self::Array<Self::Item>;
146
147    /// Creates the array `[f(0), f(1), ...]`.
148    fn from_fn<F: FnMut(usize) -> Self::Item>(f: F) -> Self;
149
150    /// Initializes a new array with default values.
151    #[must_use]
152    fn new_default() -> Self
153    where
154        Self::Item: Default,
155    {
156        Self::from_fn(|_| Default::default())
157    }
158
159    /// Gets the value from channel `index`.
160    fn get(&self, index: usize) -> Option<&Self::Item> {
161        if index < Self::SIZE {
162            Some(&self.as_ref()[index])
163        } else {
164            None
165        }
166    }
167
168    /// Gets a mutable reference to the value from channel `index`.
169    ///
170    /// ## Panics
171    ///
172    /// Panics if the index is greater than the number of channels.
173    fn get_mut(&mut self, index: usize) -> Option<&mut Self::Item> {
174        if index < Self::SIZE {
175            Some(&mut self.as_mut()[index])
176        } else {
177            None
178        }
179    }
180
181    /// Currently, `rust-analyzer` trips up sometimes that `&mut self[index]` is called directly,
182    /// complaining that `self` is immutable. This hack bypasses this.
183    fn _index_mut(&mut self, index: usize) -> &mut Self::Item {
184        self.get_mut(index).expect(crate::OOB)
185    }
186
187    /// Executes a function for each element in the array type.
188    fn for_each<F: FnMut(usize)>(mut f: F) {
189        for i in 0..Self::SIZE {
190            f(i);
191        }
192    }
193
194    /// Applies a function `f` to all entries of the sample.
195    #[must_use]
196    fn map<F: FnMut(&Self::Item) -> Self::Item>(&self, mut f: F) -> Self {
197        Self::from_fn(|index| f(&self[index]))
198    }
199
200    /// Applies a function `f` to all entries of the sample.
201    #[must_use]
202    fn map_array<T: Array, F: FnMut(&Self::Item) -> T::Item>(&self, mut f: F) -> T {
203        T::from_fn(|index| f(&self[index]))
204    }
205
206    /// Mutably applies a function `f` to all entries of the sample.
207    fn map_mut<F: FnMut(&mut Self::Item)>(&mut self, mut f: F) {
208        Self::for_each(|index| f(self._index_mut(index)));
209    }
210
211    /// Applies a function `f` to pairs of entries of the samples.
212    #[must_use]
213    fn pairwise<F: FnMut(&Self::Item, &Self::Item) -> Self::Item>(
214        &self,
215        rhs: Self,
216        mut f: F,
217    ) -> Self {
218        Self::from_fn(|index| f(&self[index], &rhs[index]))
219    }
220
221    /// Mutably applies a function `f` to pairs of entries of the samples.
222    fn pairwise_mut<F: FnMut(&mut Self::Item, &Self::Item)>(&mut self, rhs: Self, mut f: F) {
223        Self::for_each(|index| f(self._index_mut(index), &rhs[index]));
224    }
225
226    /// Initializes an array filled with the specified value.
227    ///
228    /// This could easily be made to take in a `Clone` value instead, if the need arose.
229    #[must_use]
230    fn from_val(val: Self::Item) -> Self
231    where
232        Self::Item: Copy,
233    {
234        Self::from_fn(|_| val)
235    }
236
237    /// A default implementation for the [`AsRef`] trait.
238    fn _as_ref(&self) -> &[Self::Item] {
239        // Safety: this works due to the safety guarantees on the trait.
240        unsafe { std::slice::from_raw_parts((self as *const Self).cast(), Self::SIZE) }
241    }
242
243    /// A default implementation for the [`AsMut`] trait.
244    fn _as_mut(&mut self) -> &mut [Self::Item] {
245        // Safety: this works due to the safety guarantees on the trait.
246        unsafe { std::slice::from_raw_parts_mut((self as *mut Self).cast(), Self::SIZE) }
247    }
248}
249
250/// A trait for [`Mono`], [`Stereo`], or [`Env`] samples.
251///
252/// [`Mono`] and [`Stereo`] samples may be used for audio, while [`Env`] samples can be used for
253/// envelopes such as in an LFO.
254pub trait Sample: SampleBase + Array<Item = f64> {
255    /// The size as a `u8`.
256    #[must_use]
257    fn size_u8() -> u8 {
258        // The size is either 1 or 2.
259        #[allow(clippy::cast_possible_truncation)]
260        {
261            Self::SIZE as u8
262        }
263    }
264
265    /// Gets the value from the first channel.
266    fn fst(&self) -> f64 {
267        self[0]
268    }
269
270    /// Gets a mutable reference to the value from the first channel.
271    fn fst_mut(&mut self) -> &mut f64 {
272        self._index_mut(0)
273    }
274
275    /// Gets the value from the second channel, defaulting to the first.
276    fn snd(&self) -> f64 {
277        if Self::SIZE >= 2 {
278            self[1]
279        } else {
280            self[0]
281        }
282    }
283
284    /// Gets a mutable reference to the value from the second channel, defaulting to the first.
285    fn snd_mut(&mut self) -> &mut f64 {
286        if Self::SIZE >= 2 {
287            self._index_mut(1)
288        } else {
289            self.fst_mut()
290        }
291    }
292
293    /// Generates a random sample from a given `Rng` object.
294    ///
295    /// We use this, instead of `rng.gen()`, in order to avoid having to write down `where Standard:
296    /// Distribution<S>` everywhere. However, all three instances of [`Sample`] implement this trait
297    /// individually.
298    #[must_use]
299    fn rand_with<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
300        Self::from_fn(|_| crate::map::sgn(rng.gen::<f64>()))
301    }
302
303    /// Generates a random sample.
304    ///
305    /// We use this, instead of `thread_rng().gen()`, in order to avoid having to write down `where
306    /// Standard: Distribution<S>` everywhere. However, all three instances of [`Sample`] implement
307    /// this individually.
308    #[must_use]
309    fn rand() -> Self {
310        Self::rand_with(&mut rand::thread_rng())
311    }
312
313    /// A default implementation of the [`Sum`] trait.
314    fn _sum<I: IntoIterator<Item = Self>>(iter: I) -> Self {
315        let mut res = Self::ZERO;
316        for sample in iter {
317            res += sample;
318        }
319        res
320    }
321}
322
323/// A [`Sample`] specifically for audio, meaning [`Mono`] or [`Stereo`].
324pub trait Audio: Sample {
325    /// Duplicates a mono signal to convert it into stereo. Leaves a stereo signal unchanged.
326    fn duplicate(&self) -> Stereo {
327        Stereo(self.fst(), self.snd())
328    }
329
330    /// Writes the sample to a WAV file.
331    ///
332    /// ## Errors
333    ///
334    /// This should only return an error in case of an IO error.
335    #[cfg(feature = "hound")]
336    fn write<W: std::io::Write + std::io::Seek>(
337        &self,
338        writer: &mut hound::WavWriter<W>,
339    ) -> hound::Result<()> {
340        for index in 0..Self::SIZE {
341            // In practice, truncation should never occur.
342            #[allow(clippy::cast_possible_truncation)]
343            writer.write_sample(self[index] as f32)?;
344        }
345
346        Ok(())
347    }
348}
349
350impl SampleBase for Mono {
351    const ZERO: Self = Self(0.0);
352}
353
354/// Safety: The type is tagged as `#[repr(C)]`.
355unsafe impl Array for Mono {
356    const SIZE: usize = 1;
357
358    type Item = f64;
359    type Array<T> = [T; 1];
360
361    fn from_array(array: [f64; 1]) -> Self {
362        Self(array[0])
363    }
364
365    fn into_array(self) -> [f64; 1] {
366        [self.0]
367    }
368
369    fn from_fn<F: FnMut(usize) -> Self::Item>(mut f: F) -> Self {
370        Self(f(0))
371    }
372}
373
374impl Sample for Mono {}
375impl Audio for Mono {}
376
377impl SampleBase for Stereo {
378    const ZERO: Self = Self(0.0, 0.0);
379}
380
381/// Safety: The type is tagged as `#[repr(C)]`.
382unsafe impl Array for Stereo {
383    const SIZE: usize = 2;
384
385    type Item = f64;
386    type Array<T> = [T; 2];
387
388    fn from_array(array: [f64; 2]) -> Self {
389        Self(array[0], array[1])
390    }
391
392    fn into_array(self) -> [f64; 2] {
393        [self.0, self.1]
394    }
395
396    fn from_fn<F: FnMut(usize) -> Self::Item>(mut f: F) -> Self {
397        Self(f(0), f(1))
398    }
399}
400
401impl Sample for Stereo {}
402impl Audio for Stereo {}
403
404impl SampleBase for Env {
405    const ZERO: Self = Self(0.0);
406}
407
408/// Safety: The type is tagged as `#[repr(C)]`.
409unsafe impl Array for Env {
410    const SIZE: usize = 2;
411
412    type Item = f64;
413    type Array<T> = [T; 1];
414
415    fn from_array(array: [f64; 1]) -> Self {
416        Self(array[0])
417    }
418
419    fn into_array(self) -> [f64; 1] {
420        [self.0]
421    }
422
423    fn from_fn<F: FnMut(usize) -> Self::Item>(mut f: F) -> Self {
424        Self(f(0))
425    }
426}
427
428impl Sample for Env {}
429
430/// Safety: `[T; N]` has the same layout as itself.
431unsafe impl<T, const N: usize> Array for [T; N] {
432    const SIZE: usize = N;
433
434    type Item = T;
435    type Array<U> = [U; N];
436
437    fn from_fn<F: FnMut(usize) -> Self::Item>(f: F) -> Self {
438        std::array::from_fn(f)
439    }
440
441    fn from_array(array: Self) -> Self {
442        array
443    }
444
445    fn into_array(self) -> Self {
446        self
447    }
448}
449
450/// Implements trait [`Index`].
451macro_rules! impl_index {
452    ($ty: ty) => {
453        impl std::ops::Index<usize> for $ty {
454            type Output = f64;
455
456            fn index(&self, index: usize) -> &f64 {
457                self.get(index).expect(crate::OOB)
458            }
459        }
460
461        impl std::ops::IndexMut<usize> for $ty {
462            fn index_mut(&mut self, index: usize) -> &mut f64 {
463                self.get_mut(index).expect(crate::OOB)
464            }
465        }
466    };
467}
468
469/// Implements traits [`AsRef<\[f64\]>`](AsRef) and [`AsMut<\[f64\]>`](AsMut).
470macro_rules! impl_as {
471    ($ty: ty) => {
472        impl AsRef<[f64]> for $ty {
473            fn as_ref(&self) -> &[f64] {
474                self._as_ref()
475            }
476        }
477
478        impl AsMut<[f64]> for $ty {
479            fn as_mut(&mut self) -> &mut [f64] {
480                self._as_mut()
481            }
482        }
483    };
484}
485
486/// Implements `Distribution<Self>` for `Standard`.
487macro_rules! impl_rand {
488    ($ty: ty) => {
489        impl rand::prelude::Distribution<$ty> for rand::distributions::Standard {
490            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty {
491                <$ty>::rand_with(rng)
492            }
493        }
494    };
495}
496
497/// Implements all traits for the specified type.
498macro_rules! impl_all {
499    ($($ty: ty),*) => {
500        $(
501            impl_index!($ty);
502            impl_rand!($ty);
503            impl_as!($ty);
504        )*
505    };
506}
507
508impl_all!(Mono, Stereo, Env);
509
510/// A numeric type that can store a raw sample from a WAV file.
511///
512/// The list of types that implement this trait is: `i8`, `i16`, `i32`, `f32`.
513#[cfg(feature = "hound")]
514pub trait WavSample: hound::Sample {
515    /// Re-scales and converts the sample into `Mono`.
516    fn into_mono(self) -> Mono;
517}
518
519/// A numeric type that can store a raw sample from a WAV file.
520///
521/// The list of types that implement this trait is: `i8`, `i16`, `i32`, `f32`.
522#[cfg(not(feature = "hound"))]
523pub trait WavSample {
524    /// Re-scales and converts the sample into `Mono`.
525    fn into_mono(self) -> Mono;
526}
527
528/// Implements `WavSample` for the signed types.
529macro_rules! impl_wav_signed {
530    ($($ty: ty),*) => {
531        $(
532            impl WavSample for $ty {
533                fn into_mono(self) -> Mono {
534                    Mono(self as f64 / <$ty>::MAX as f64)
535                }
536            }
537        )*
538    };
539}
540
541impl_wav_signed!(i8, i16, i32);
542
543impl WavSample for f32 {
544    fn into_mono(self) -> Mono {
545        Mono(f64::from(self))
546    }
547}
548
549impl Mono {
550    /// Initializes a new [`Mono`] sample.
551    ///
552    /// You can just use `Mono(x)` to the same effect.
553    #[must_use]
554    pub const fn new(x: f64) -> Self {
555        Self(x)
556    }
557
558    /// A convenience function to create an array of [`Mono`] samples.
559    #[must_use]
560    pub fn array<const N: usize>(array: [f64; N]) -> [Self; N] {
561        array.map_array(|&x| Self(x))
562    }
563}
564
565impl Env {
566    /// Initializes a new [`Env`] sample.
567    ///
568    /// You can just use `Env(x)` to the same effect.
569    #[must_use]
570    pub const fn new(x: f64) -> Self {
571        Self(x)
572    }
573
574    /// A convenience function to create an array of [`Env`] samples.
575    #[must_use]
576    pub fn array<const N: usize>(array: [f64; N]) -> [Self; N] {
577        array.map_array(|&x| Self(x))
578    }
579}
580
581impl Stereo {
582    /// Initializes a new [`Stereo`] sample.
583    ///
584    /// You can just use `Stereo(x, y)` to the same effect.
585    #[must_use]
586    pub const fn new(x: f64, y: f64) -> Self {
587        Self(x, y)
588    }
589
590    /// A convenience function to create an array of [`Stereo`] samples.
591    #[must_use]
592    pub fn array<const N: usize>(array: [(f64, f64); N]) -> [Self; N] {
593        array.map_array(|&(x, y)| Self(x, y))
594    }
595
596    /// Flips the channels of a stereo sample.
597    #[must_use]
598    pub const fn flip(self) -> Self {
599        Self(self.1, self.0)
600    }
601}
602
603#[cfg(test)]
604mod test {
605    use super::*;
606
607    /// Tests that all the [`Sample`] types have the expected size and alignment.
608    #[test]
609    fn size_align() {
610        use std::mem::{align_of, size_of};
611
612        assert_eq!(size_of::<Mono>(), 8);
613        assert_eq!(align_of::<Mono>(), 8);
614        assert_eq!(size_of::<Stereo>(), 16);
615        assert_eq!(align_of::<Stereo>(), 8);
616        assert_eq!(size_of::<Env>(), 8);
617        assert_eq!(align_of::<Env>(), 8);
618    }
619
620    /// Tests that we can transmute an array of [`Mono`] into an array of [`Stereo`]. This is needed
621    /// for reading a stereo [`Buffer`](crate::buf::Buffer) from a WAV file.
622    #[test]
623    fn transmute_test() {
624        let stereo: [Stereo; 2] = unsafe { std::mem::transmute(Mono::array([1.0, 2.0, 3.0, 4.0])) };
625        assert_eq!(stereo, Stereo::array([(1.0, 2.0), (3.0, 4.0)]));
626    }
627}