wavegen/
lib.rs

1//! Rust waveform generator, with [no_std support](https://github.com/spitfire05/wavegen-rs#how-to-use-it).
2//!
3//! # Quickstart
4//!
5//! ```
6//! use wavegen::{wf, sine, dc_bias, sawtooth};
7//!
8//! // Define a Waveform with 200Hz sampling rate and three function components,
9//! // choosing f32 as the output type:
10//! let waveform = wf!(f32, 200., sine!(50., 10.), sawtooth!(20.), dc_bias!(-5.));
11//!
12//! // Use Waveform as an infinite iterator:
13//! let two_seconds_of_samples: Vec<f32> = waveform.iter().take(400).collect();
14//! ```
15//!
16//! Look into macros section for a complete list of defined periodic functions and their constructors.
17//!
18//! # Periodic function macros
19//! The macros for building predefined [PeriodicFunction]s generally have a form of:
20//!
21//! `function!(frequency, [amplitude, [phase]])`
22//!
23//! (Square braces "[]" indicate optional argument).
24//!
25//! They come in an annotated and non-annotated form, so for example a Sine function can be expressed in both ways:
26//! ```
27//! use wavegen::{wf, sine, PeriodicFunction};
28//!
29//! let _: PeriodicFunction<f32> = sine!(100., 20., 0.25);
30//! ```
31//!
32//! ```
33//! use wavegen::{wf, sine, PeriodicFunction};
34//!
35//! let _: PeriodicFunction<f32> = sine!(frequency: 100., amplitude: 20., phase: 0.25);
36//! ```
37//!
38//! Refer to Macros section for more info.
39//!
40//! # Custom periodic functions
41//! Supported, of course. Just define your custom function as `Box<Fn(f64) -> f64>` and use it with [`Waveform`].
42//!
43//! ```
44//! use wavegen::{wf, PeriodicFunction};
45//!
46//! let waveform = wf!(f64, 100.0, PeriodicFunction::custom(|x| x % 2.0));
47//! ```
48//!
49//! # Overflows
50//!
51//! As [`Waveform`] can be composed of multiple components, it is possible for it to overflow during samples collection.
52//! If overflow occurs, the sample's value will be clamped to the largest possible representation of sample's type.
53//!
54//! That means `+/- Inf` for floating point types, and `MAX/MIN` for integers.
55//!
56//! ```
57//! use wavegen::{Waveform, dc_bias};
58//!
59//! let wf = Waveform::<f64>::with_components(100.0, vec![dc_bias![f32::MAX], dc_bias![f32::MAX]]);
60//! let sample = wf.iter().take(1).collect::<Vec<_>>()[0];
61//!
62//! assert_eq!(sample, f64::INFINITY);
63//! ```
64//!
65//! ```
66//! use wavegen::{Waveform, dc_bias};
67//!
68//! let wf = Waveform::<i32>::with_components(100.0, vec![dc_bias![f32::MAX], dc_bias![f32::MAX]]);
69//! let sample = wf.iter().take(1).collect::<Vec<_>>()[0];
70//!
71//! assert_eq!(sample, i32::MAX);
72//! ```
73//!
74//! # Calculation precision
75//!
76//! By default, all calculations in [`Waveform`] use single floating point precision [`f32`]. This can be set to [`f64`] if needed, possibly in case of very high frequencies. To do so, set the `P` type parameter to [`f64`]:
77//!
78//! ```
79//! let double_precision_waveform = wavegen::Waveform::<f64, f64>::new(1e100);
80//! ```
81//!
82//! # Iterator infinity
83//!
84//! [`WaveformIterator`] is a *mostly* infinite iterator, with one exception:
85//!
86//! The `WaveformIterator::next` method can return [`None`] in some rare cases if it is not able to convert the inner sample type [`f64`] into the target sample type.
87//!
88//! `f64::NAN` cannot be represented as [`i32`]:
89//! ```
90//! use wavegen::{Waveform, PeriodicFunction};
91//!
92//! let mut wf = Waveform::<i32, f64>::new(100.0);
93//! wf.add_component(PeriodicFunction::dc_bias(f64::NAN));
94//!
95//! assert_eq!(None, wf.iter().next())
96//! ```
97//!
98//! This however is fine, as `f64::NAN` can be represented as `f32::NAN`:
99//! ```
100//! use wavegen::{Waveform, PeriodicFunction};
101//!
102//! let mut wf = Waveform::<f32, f64>::new(100.0);
103//! wf.add_component(PeriodicFunction::dc_bias(f64::NAN));
104//!
105//! assert!(wf.iter().next().unwrap().is_nan())
106//! ```
107//!
108//! It is probably a good practice to sanitize the parameters of the periodic function before it is constructed.
109//!
110//! # Note about Nyquist-Shannon rule enforcement
111//!
112//! As a rule of thumb in signal processing, the sampling frequency should be *at least* 2 times bigger than the highest frequency of sampled continous signal.
113//!
114//! This lib will **not** enforce the Nyquist-Shannon rule on the waveforms you create, therefore abominations like this are possible (altough not recommended):
115//!
116//! ```
117//! use wavegen::{Waveform, sine};
118//!
119//! // 100 Hz sampling of 80 Hz sine... will not yield realistic results.
120//! let wf = Waveform::<f32>::with_components(100.0, vec![sine!(80.)]);
121//! ```
122//!
123//! As it is often a case, it is you, the programmer, who's left in charge of making sure the input data makes sense.
124
125#![no_std]
126#![deny(missing_docs)]
127#![deny(clippy::unwrap_used)]
128#![deny(clippy::expect_used)]
129
130#[cfg(feature = "std")]
131extern crate std;
132
133#[cfg(all(not(feature = "libm"), not(feature = "std")))]
134compile_error!("at least one of \"libm\", \"std\" features has to be enabled");
135
136extern crate alloc;
137
138mod macros;
139
140use alloc::{boxed::Box, vec, vec::Vec};
141use core::iter::Sum;
142use core::marker::PhantomData;
143use core::ops::Add;
144use num_traits::{Bounded, Float, FloatConst, NumCast, One};
145
146/// Defines precision of inner [`Waveform`] and [`PeriodicFunction`] calcualtions.
147pub trait Precision: Float + FloatConst + Sum + Send + Sync + 'static {}
148
149impl<T> Precision for T where T: Float + FloatConst + Sum + Send + Sync + 'static {}
150
151trait Two {
152    fn two() -> Self;
153}
154
155impl<T> Two for T
156where
157    T: One + Add<Output = T>,
158{
159    #[inline(always)]
160    fn two() -> Self {
161        T::one() + T::one()
162    }
163}
164
165/// Helper trait defining all the types that can be used as [`Waveform`]'s sample type.
166pub trait SampleType: NumCast + Bounded {}
167
168impl<T> SampleType for T where T: NumCast + Bounded {}
169
170/// Struct representing a waveform, consisting of output numeric type, sampling rate and a vector of [`PeriodicFunction`]s.
171pub struct Waveform<T: SampleType, P: Precision = f32> {
172    sample_rate: P,
173    components: Vec<PeriodicFunction<P>>,
174    _phantom: PhantomData<T>,
175}
176
177impl<T: SampleType, P: Precision> Waveform<T, P> {
178    /// Initializes new empty [`Waveform`]
179    ///
180    /// # Panics
181    ///
182    /// This method will panic if `sample_rate` is not a finite, positive, non-zero number.
183    ///
184    /// # Examples
185    ///
186    /// ```
187    /// use wavegen::Waveform;
188    ///
189    /// let wf = Waveform::<f32>::new(500.0);
190    ///
191    /// assert!(wf.iter().take(100).all(|y| y == 0.0));
192    /// ```
193    pub fn new(sample_rate: impl Into<P>) -> Self {
194        let sample_rate = sample_rate.into();
195        Self::assert_sane(sample_rate);
196
197        Waveform {
198            sample_rate,
199            components: vec![],
200            _phantom: PhantomData,
201        }
202    }
203
204    /// Initializes new [`Waveform`] with predefined components
205    ///
206    /// # Panics
207    ///
208    /// This method will panic if `sample_rate` is not a finite, positive, non-zero number.
209    ///
210    /// # Examples
211    ///
212    /// ```
213    /// use wavegen::{Waveform, sine, dc_bias};
214    ///
215    /// let wf = Waveform::<f32>::with_components(100.0, vec![sine!(1.), dc_bias!(-50.)]);
216    /// ```
217    pub fn with_components(
218        sample_rate: impl Into<P>,
219        components: Vec<PeriodicFunction<P>>,
220    ) -> Self {
221        let sample_rate = sample_rate.into();
222        Self::assert_sane(sample_rate);
223
224        Waveform {
225            sample_rate,
226            components,
227            _phantom: PhantomData,
228        }
229    }
230
231    /// Ads a new component to existing [`Waveform`].
232    ///
233    /// # Examples
234    ///
235    /// ```
236    /// use wavegen::{Waveform, sine, dc_bias};
237    ///
238    /// let mut wf = Waveform::<f32>::new(100.0);
239    /// wf.add_component(sine!(10.));
240    /// wf.add_component(dc_bias!(5.));
241    ///
242    /// assert_eq!(2, wf.components().len());
243    /// ```
244    pub fn add_component(&mut self, component: PeriodicFunction<P>) {
245        self.components.push(component);
246    }
247
248    /// Gets sample rate of this [`Waveform`].
249    ///
250    /// # Examples
251    ///
252    /// ```
253    /// use wavegen::Waveform;
254    ///
255    /// let wf = Waveform::<f32>::new(42.0);
256    ///
257    /// assert_eq!(42.0, *wf.sample_rate());
258    /// ```
259    pub fn sample_rate(&self) -> &P {
260        &self.sample_rate
261    }
262
263    /// Returns list of components this [`Waveform`] consists of.
264    ///
265    /// # Examples
266    ///
267    /// ```
268    /// use wavegen::{Waveform, sine, dc_bias};
269    ///
270    /// let wf = Waveform::<f32>::with_components(42.0, vec![sine!(1.), dc_bias!(5.)]);
271    ///
272    /// assert_eq!(2, wf.components().len());
273    /// ```
274    pub fn components(&self) -> &Vec<PeriodicFunction<P>> {
275        &self.components
276    }
277
278    /// Returns an iterator over this [`Waveform`] samples.
279    ///
280    /// # Examples
281    ///
282    /// ```
283    /// use wavegen::{Waveform, sine};
284    ///
285    /// let wf = Waveform::<f32>::with_components(42.0, vec![sine!(1.)]);
286    /// let samples = wf.iter().take(100).collect::<Vec<_>>();
287    /// ```
288    pub fn iter(&self) -> WaveformIterator<T, P> {
289        WaveformIterator::<T, P> {
290            inner: self,
291            time: P::zero(),
292        }
293    }
294
295    #[inline(always)]
296    fn assert_sane(x: P) {
297        assert!(x.is_normal());
298        assert!(x.is_sign_positive());
299    }
300}
301
302impl<'a, T: SampleType, P: Precision> IntoIterator for &'a Waveform<T, P> {
303    type Item = T;
304
305    type IntoIter = WaveformIterator<'a, T, P>;
306
307    fn into_iter(self) -> Self::IntoIter {
308        WaveformIterator {
309            inner: self,
310            time: P::zero(),
311        }
312    }
313}
314
315/// An iterator that allows to sample a [`Waveform`].
316#[derive(Clone, Copy)]
317pub struct WaveformIterator<'a, T: SampleType, P: Precision> {
318    inner: &'a Waveform<T, P>,
319    time: P,
320}
321
322impl<'a, T: SampleType, P: Precision> WaveformIterator<'a, T, P> {
323    fn into_target_type_sanitized(sample: P) -> Option<T> {
324        let result = NumCast::from(sample);
325
326        result.or_else(|| {
327            if sample > P::zero() {
328                Some(T::max_value())
329            } else if sample < P::zero() {
330                Some(T::min_value())
331            } else {
332                None
333            }
334        })
335    }
336
337    fn increment_time(&mut self, n: usize) -> Result<(), ()> {
338        let new_time = self.time + (P::from(n).ok_or(())? * (P::one() / self.inner.sample_rate));
339        if new_time.is_finite() {
340            self.time = new_time;
341        } else {
342            self.time = (P::one() / self.inner.sample_rate) - (P::max_value() - self.time);
343        }
344
345        Ok(())
346    }
347
348    fn raw_sample(&self) -> P {
349        self.inner
350            .components
351            .iter()
352            .map(|x| x.sample(self.time))
353            .sum()
354    }
355}
356
357impl<'a, T: SampleType, P: Precision> Iterator for WaveformIterator<'a, T, P> {
358    type Item = T;
359
360    fn next(&mut self) -> Option<Self::Item> {
361        let sample = self.raw_sample();
362        self.increment_time(1).ok()?;
363
364        Self::into_target_type_sanitized(sample)
365    }
366
367    fn nth(&mut self, n: usize) -> Option<Self::Item> {
368        self.increment_time(n).ok()?;
369
370        self.next()
371    }
372
373    fn size_hint(&self) -> (usize, Option<usize>) {
374        (usize::MAX, None)
375    }
376}
377
378/// Wrapper struct for a periodic function (in most cases a `f32 -> f32` or `f64 -> f64` map).
379pub struct PeriodicFunction<P: Precision = f32> {
380    inner: Box<dyn Fn(P) -> P + Send + Sync>,
381}
382
383impl<P: Precision + 'static> PeriodicFunction<P> {
384    /// Initializes new [`PeriodicFunction`] with function defined by `f` parameter.
385    ///
386    /// # Examples
387    ///
388    /// ```
389    /// let _ = wavegen::PeriodicFunction::new(Box::new(|x: f32| x.cos()));
390    /// ```
391    pub fn new(f: Box<dyn Fn(P) -> P + Send + Sync>) -> Self {
392        Self { inner: f }
393    }
394
395    /// Helper for defining custom functions. Same as `PeriodicFunction::new` but with implicit Boxing.
396    ///
397    /// # Examples
398    ///
399    /// ```
400    /// let _ = wavegen::PeriodicFunction::custom(|x: f32| x.cos());
401    /// ```
402    pub fn custom<F: Fn(P) -> P + Send + Sync + 'static>(f: F) -> Self {
403        Self::new(Box::new(f))
404    }
405
406    /// DC Bias function builder. See the [`macro`] for more info.
407    ///
408    /// [`macro`]: ../macro.dc_bias.html
409    pub fn dc_bias(bias: impl Into<P>) -> Self {
410        let bias = bias.into();
411
412        Self::new(Box::new(move |_| bias))
413    }
414
415    /// Sawtooth function builder. See the [`macro`] for more info.
416    ///
417    /// [`macro`]: ../macro.sawtooth.html
418    pub fn sawtooth(frequency: impl Into<P>, amplitude: impl Into<P>, phase: impl Into<P>) -> Self {
419        let frequency = frequency.into();
420        let amplitude = amplitude.into();
421        let phase = phase.into();
422
423        Self::new(Box::new(move |t| {
424            P::two() * amplitude * (t * frequency + phase).fract() - amplitude
425        }))
426    }
427
428    /// Sine function builder. See the [`macro`] for more info.
429    ///
430    /// [`macro`]: ../macro.sine.html
431    #[inline(always)]
432    pub fn sine(frequency: impl Into<P>, amplitude: impl Into<P>, phase: impl Into<P>) -> Self {
433        let frequency = frequency.into();
434        let amplitude = amplitude.into();
435        let phase = phase.into();
436
437        Self::new(Box::new(move |t| {
438            let radians = (P::two() * P::PI() * frequency * t) + (phase * P::two() * P::PI());
439            let sine = radians.sin();
440
441            sine * amplitude
442        }))
443    }
444
445    /// Square function builder. See the [`macro`] for more info.
446    ///
447    /// [`macro`]: ../macro.square.html
448    #[inline(always)]
449    pub fn square(frequency: impl Into<P>, amplitude: impl Into<P>, phase: impl Into<P>) -> Self {
450        let frequency = frequency.into();
451        let amplitude = amplitude.into();
452        let phase = phase.into();
453
454        Self::new(Box::new(move |t| {
455            let power = (P::two() * (t - phase) * frequency).floor();
456
457            amplitude * (P::one().neg()).powf(power)
458        }))
459    }
460
461    /// Gets the inner function.
462    pub fn inner(&self) -> &(impl Fn(P) -> P + Send + Sync) {
463        &self.inner
464    }
465
466    /// Returns the sample value for given input.
467    pub fn sample(&self, t: P) -> P {
468        self.inner()(t)
469    }
470}
471
472#[cfg(test)]
473mod tests {
474    use super::*;
475    use crate::{dc_bias, sawtooth, sine, square};
476    use alloc::{vec, vec::Vec};
477    use float_cmp::approx_eq;
478    use paste::paste;
479
480    const EPS: f32 = 1e-3;
481
482    #[test]
483    fn square_of_high_frequency() {
484        let square = PeriodicFunction::<f64>::square(u32::MAX, 1.0, 0.0);
485        assert!(square.sample(1.0).is_finite());
486    }
487
488    #[test]
489    fn sine_waveform_has_default_amplitude_of_one() {
490        let wf = Waveform::<f32>::with_components(100.0, vec![sine!(1.)]);
491
492        let samples = wf.iter().take(100).collect::<Vec<_>>();
493
494        assert_eq!(samples[25], 1.0);
495        assert_eq!(samples[75], -1.0);
496    }
497
498    #[test]
499    fn sine_waveform_as_integers_has_amplitude_of_one() {
500        let wf = Waveform::<i32>::with_components(100.0, vec![sine!(1.)]);
501
502        let samples = wf.iter().take(100).collect::<Vec<_>>();
503
504        assert_eq!(samples[25], 1);
505        assert_eq!(samples[75], -1);
506    }
507
508    #[test]
509    fn sine_waveform_with_bias_has_correct_amplitude() {
510        let wf = Waveform::<f32>::with_components(100.0, vec![sine!(1.), dc_bias!(5.)]);
511
512        let samples = wf.iter().take(100).collect::<Vec<_>>();
513
514        assert_eq!(samples[25], 6.0);
515        assert_eq!(samples[75], 4.0);
516    }
517
518    macro_rules! test_no_default_bias {
519        ($($name:ident: $func:expr)*) => {
520            $(
521                paste! {
522                    #[test]
523                    fn [<default_ $name _waveforom_has_no_bias>]() {
524                        let wf = Waveform::<f32, f64>::with_components(100.0, vec![$func]);
525
526                        let bias = wf.iter().take(100).sum::<f32>() / 100.0;
527
528                        assert!(approx_eq!(f32, bias, 0.0, epsilon = EPS));
529                    }
530                }
531            )*
532        };
533    }
534
535    test_no_default_bias! {
536        sine: sine!(1.)
537        // sawtooth: sawtooth!(1) // does not pass currently, see https://github.com/spitfire05/wavegen-rs/issues/17
538        square: square!(1.)
539    }
540
541    #[test]
542    #[allow(clippy::iter_skip_next)]
543    fn waveform_iterator_is_infinite_single() {
544        let wf = Waveform::<f64>::new(f32::MIN_POSITIVE);
545        let mut iter = wf.iter().skip(usize::MAX);
546
547        assert_eq!(Some(0f64), iter.next());
548        assert_eq!(Some(0f64), iter.skip(usize::MAX).next())
549    }
550
551    #[test]
552    #[allow(clippy::iter_skip_next)]
553    fn waveform_iterator_is_infinite_double() {
554        let wf = Waveform::<f64, f64>::new(f64::MIN_POSITIVE);
555        let mut iter = wf.iter().skip(usize::MAX);
556
557        assert_eq!(Some(0f64), iter.next());
558        assert_eq!(Some(0f64), iter.skip(usize::MAX).next())
559    }
560
561    #[test]
562    fn oversaturated_amplitude_clips_to_max() {
563        let wf = Waveform::<u8>::with_components(100.0, vec![dc_bias!(300.)]);
564        let samples = wf.iter().take(1).collect::<Vec<_>>();
565
566        assert_eq!(samples.len(), 1);
567        assert_eq!(samples[0], u8::MAX);
568    }
569
570    #[test]
571    fn undersaturated_amplitude_clips_to_min() {
572        let wf = Waveform::<u8>::with_components(100.0, vec![dc_bias!(-300.)]);
573        let samples = wf.iter().take(1).collect::<Vec<_>>();
574
575        assert_eq!(samples.len(), 1);
576        assert_eq!(samples[0], u8::MIN);
577    }
578
579    macro_rules! test_wavefrom_panic {
580        ($($name:ident: $sample_rate:expr)*) => {
581            $(
582                paste! {
583                    #[test]
584                    #[should_panic]
585                    fn [<waveform_new_panics_on_ $name>]() {
586
587                        Waveform::<f64>::new($sample_rate);
588                    }
589
590
591                    #[test]
592                    #[should_panic]
593                    fn [<waveform_with_components_panics_on_ $name>]() {
594                        Waveform::<f64>::with_components($sample_rate, vec![]);
595                    }
596                }
597            )*
598        };
599    }
600
601    test_wavefrom_panic! {
602        nan: f32::NAN
603        negative: -1f32
604        zero: 0.0
605        infinity: f32::INFINITY
606        negative_infinity: f32::NEG_INFINITY
607    }
608
609    macro_rules! test_size_hint {
610        () => {
611            let wf = Waveform::<f32>::new(44100.0);
612            assert_eq!((usize::MAX, None), wf.iter().size_hint());
613        };
614        ($($component:expr),*) => {
615            let mut wf = Waveform::<f32>::new(44100.0);
616            $(
617                wf.add_component($component);
618            )*
619            assert_eq!((usize::MAX, None), wf.iter().size_hint());
620        };
621    }
622
623    #[test]
624    fn test_size_hint() {
625        test_size_hint!();
626        test_size_hint!(sine!(50.));
627        test_size_hint!(sine!(1.), sawtooth!(2.), square!(3.), dc_bias!(4.));
628    }
629
630    #[test]
631    #[allow(clippy::iter_nth_zero)]
632    #[allow(clippy::unwrap_used)]
633    fn nth_and_next_give_same_results() {
634        let wf = Waveform::<i32>::with_components(44100.0, vec![sine!(3000., i16::MAX)]);
635        let mut i1 = wf.iter();
636        let mut i2 = wf.iter();
637
638        for _ in 0..1000 {
639            assert_eq!(i1.next().unwrap(), i2.nth(0).unwrap());
640        }
641    }
642
643    #[test]
644    fn waveform_is_send() {
645        fn assert_send<T: Send>() {}
646        assert_send::<Waveform<f64>>();
647    }
648
649    #[test]
650    fn waveform_is_sync() {
651        fn assert_sync<T: Sync>() {}
652        assert_sync::<Waveform<f64>>();
653    }
654}