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}