Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Waveform now returns an Error on invalid sample rate provided #40

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# wavegen

## 0.4.0

- `Waveform` constructors now return `Result<Waveform, InvalidSampleRate>` to allow for handling the errors instead of outright panicking.

## 0.3.0

- `wf` helper macro added.
- `WaveformIterator` no longer will panic on failed conversion to output type.
- Switch to using `Into` instead of directly casting numeric types in numerous places.

# 0.2.2
## 0.2.2

- Implemented `size_hint`.

# 0.2.1
## 0.2.1

- Added `Sync` and `Send` constraints on `PeriodicFunction`.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wavegen"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
authors = ["Michal Borejszo <michael.borejszo@gmail.com>"]
license = "MIT"
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@

```toml
[dependencies]
wavegen = "0.3"
wavegen = "0.4"
```
Or, to use the *no_std* version (custom global allocator is required):

```toml
[dependencies]
wavegen = { version = "0.3", default-features = false, features = ["libm"] }
wavegen = { version = "0.4", default-features = false, features = ["libm"] }
```

2) Define a waveform with sampling frequency and function components:

```rust
let waveform = wf!(f64, 200, sine!(frequency: 100, amplitude: 10), dc_bias!(20));
let waveform = wf!(f64, 200, sine!(frequency: 100, amplitude: 10), dc_bias!(20)).unwrap();
```

3) Turn it into an iterator and sample:
Expand Down
3 changes: 2 additions & 1 deletion benches/waveform_benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ fn sample_waveform(n: usize) -> Vec<f64> {
let wf = Waveform::with_components(
44100.0,
vec![sine!(2048), sawtooth!(1024), square!(512), dc_bias!(0.1)],
);
)
.unwrap();

wf.iter().take(n).collect::<Vec<f64>>()
}
Expand Down
16 changes: 9 additions & 7 deletions examples/plot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
sample_rate,
"sine.png",
"Sine",
wf!(f32, sample_rate, sine!(1)),
wf!(f32, sample_rate, sine!(1))?,
)?;

draw(
sample_rate,
"sine_double.png",
"Sines",
wf!(f32, sample_rate, sine!(1.0), sine!(1.0, 1.0, 0.25)),
wf!(f32, sample_rate, sine!(1.0), sine!(1.0, 1.0, 0.25))?,
)?;

draw(
sample_rate,
"sawtooth.png",
"Sawtooth",
wf!(f32, sample_rate, sawtooth!(2, 1, 0.0)),
wf!(f32, sample_rate, sawtooth!(2, 1, 0.0))?,
)?;

draw(
Expand All @@ -35,14 +35,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
sample_rate,
sawtooth!(2, 1, 0.0),
sine!(frequency: 50, amplitude: 0.1)
),
)?,
)?;

draw(
sample_rate,
"square.png",
"Square",
wf!(f32, sample_rate, square!(2)),
wf!(f32, sample_rate, square!(2))?,
)?;

draw(
Expand All @@ -55,7 +55,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
sine!(10, 0.3),
sawtooth!(2, 0.3),
square!(3, 0.3)
),
)?,
)?;

draw(
Expand All @@ -67,7 +67,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
sample_rate,
sine!(frequency: 300),
sine!(frequency: 50, amplitude: 0.3)
),
)?,
)?;

Ok(())
Expand Down Expand Up @@ -118,5 +118,7 @@ fn draw_internal<I: IntoIterator<Item = (f32, f32)>, P: AsRef<Path>>(
.border_style(BLACK)
.draw()?;

root.present()?;

Ok(())
}
2 changes: 1 addition & 1 deletion examples/wave.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const WAVE_TIME_S: f32 = 1.0; // audio length in seconds
fn main() {
// Define waveform
// 500 Hz sine spanned from i16::MIN to i16::MAX
let wf = wf!(i16, SAMPLE_RATE, sine!(500, i16::MAX));
let wf = wf!(i16, SAMPLE_RATE, sine!(500, i16::MAX)).unwrap();

// WAVE file specification
let spec = hound::WavSpec {
Expand Down
13 changes: 13 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use core::fmt::Display;

#[derive(Debug, Clone, Copy)]
/// Error raised when cosntructing [`Waveform`] with sample rate of invalid value.
pub struct InvalidSampleRate(pub(crate) f64);

impl Display for InvalidSampleRate {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Invalid SamplingRate value: `{}`. SamplingRate has to be positive, non-zero and finite.", self.0)
}
}

impl std::error::Error for InvalidSampleRate {}
20 changes: 11 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//!
//! // Define a Waveform with 200Hz sampling rate and three function components,
//! // choosing f32 as the output type:
//! let waveform = wf!(f32, 200, sine!(50, 10), sawtooth!(20), dc_bias!(-5));
//! let waveform = wf!(f32, 200, sine!(50, 10), sawtooth!(20), dc_bias!(-5)).unwrap();
//!
//! // Use Waveform as an infinite iterator:
//! let two_seconds_of_samples: Vec<f32> = waveform.iter().take(400).collect();
Expand Down Expand Up @@ -43,7 +43,7 @@
//! ```
//! use wavegen::{wf, periodic_functions::custom};
//!
//! let waveform = wf!(f64, 100.0, custom(|x| x % 2.0));
//! let waveform = wf!(f64, 100.0, custom(|x| x % 2.0)).unwrap();
//! ```
//!
//! # Overflows
Expand All @@ -56,7 +56,7 @@
//! ```
//! use wavegen::{Waveform, dc_bias};
//!
//! let wf = Waveform::<f64>::with_components(100.0, vec![dc_bias![f64::MAX], dc_bias![f64::MAX]]);
//! let wf = Waveform::<f64>::with_components(100.0, vec![dc_bias![f64::MAX], dc_bias![f64::MAX]]).unwrap();
//! let sample = wf.iter().take(1).collect::<Vec<_>>()[0];
//!
//! assert_eq!(sample, f64::INFINITY);
Expand All @@ -65,7 +65,7 @@
//! ```
//! use wavegen::{Waveform, dc_bias};
//!
//! let wf = Waveform::<i32>::with_components(100.0, vec![dc_bias![f64::MAX], dc_bias![f64::MAX]]);
//! let wf = Waveform::<i32>::with_components(100.0, vec![dc_bias![f64::MAX], dc_bias![f64::MAX]]).unwrap();
//! let sample = wf.iter().take(1).collect::<Vec<_>>()[0];
//!
//! assert_eq!(sample, i32::MAX);
Expand All @@ -81,7 +81,7 @@
//! ```
//! use wavegen::{Waveform, dc_bias};
//!
//! let mut wf = Waveform::<i32>::new(100.0);
//! let mut wf = Waveform::<i32>::new(100.0).unwrap();
//! wf.add_component(dc_bias!(f64::NAN));
//!
//! assert_eq!(None, wf.iter().next())
Expand All @@ -91,7 +91,7 @@
//! ```
//! use wavegen::{Waveform, dc_bias};
//!
//! let mut wf = Waveform::<f32>::new(100.0);
//! let mut wf = Waveform::<f32>::new(100.0).unwrap();
//! wf.add_component(dc_bias!(f64::NAN));
//!
//! assert!(wf.iter().next().unwrap().is_nan())
Expand All @@ -101,15 +101,15 @@
//!
//! # Note about Nyquist-Shannon rule enforcement
//!
//! 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.
//! As a rule of thumb in signal processing, the sampling frequency should be *at least* 2 times bigger than the highest frequency of sampled continuos signal.
//!
//! This lib will **not** enforce the Nyquist-Shannon rule on the waveforms you create, therefore abominations like this are possible (altough not recommended):
//! This lib will **not** enforce the Nyquist-Shannon rule on the waveforms you create, therefore abominations like this are possible (although not recommended):
//!
//! ```
//! use wavegen::{Waveform, sine};
//!
//! // 100 Hz sampling of 80 Hz sine... will not yield realistic results.
//! let wf = Waveform::<f32>::with_components(100.0, vec![sine!(80)]);
//! let wf = Waveform::<f32>::with_components(100.0, vec![sine!(80)]).unwrap();
//! ```
//!
//! As it is often a case, it is you, the programmer, who's left in charge of making sure the input data makes sense.
Expand All @@ -129,11 +129,13 @@ extern crate alloc;

pub mod periodic_functions;

mod errors;
mod macros;
mod waveform;

use alloc::boxed::Box;

pub use errors::InvalidSampleRate;
pub use waveform::SampleType;
pub use waveform::Waveform;

Expand Down
22 changes: 11 additions & 11 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
/// ```
/// use wavegen::{wf, sine, square};
///
/// let empty_waveform = wf!(f32, 16000);
/// let empty_waveform = wf!(f32, 16000).unwrap();
/// assert_eq!(0, empty_waveform.get_components_len());
///
/// let sine_waveform = wf!(f64, 44100, sine!(50));
/// let sine_waveform = wf!(f64, 44100, sine!(50)).unwrap();
/// assert_eq!(1, sine_waveform.get_components_len());
///
/// let some_other_waveform = wf!(i64, 22000, sine!(100), square!(200));
/// let some_other_waveform = wf!(i64, 22000, sine!(100), square!(200)).unwrap();
/// assert_eq!(2, some_other_waveform.get_components_len());
/// ```
///
Expand Down Expand Up @@ -62,7 +62,7 @@ macro_rules! dc_bias {
///
/// | argument | unit | notes |
/// | -------- | ---- | ----- |
/// | frequency | Hz | Frequecy of the periodic function. Also: 1 / period |
/// | frequency | Hz | Frequency of the periodic function. Also: 1 / period |
/// | amplitude | *arbitrary* | The amplitude of the function in 0-peak notation. |
/// | phase | *periods* | The phase shift of the function. Value of 1 means full shift around.
///
Expand Down Expand Up @@ -95,7 +95,7 @@ macro_rules! sawtooth {
///
/// | argument | unit | notes |
/// | -------- | ---- | ----- |
/// | frequency | Hz | Frequecy of the periodic function. Also: 1 / period |
/// | frequency | Hz | Frequency of the periodic function. Also: 1 / period |
/// | amplitude | *arbitrary* | The amplitude of the function in 0-peak notation. |
/// | phase | *periods* | The phase shift of the function. Value of 1 means full shift around.
///
Expand Down Expand Up @@ -145,7 +145,7 @@ macro_rules! sine {
///
/// | argument | unit | notes |
/// | -------- | ---- | ----- |
/// | frequency | Hz | Frequecy of the periodic function. Also: 1 / period |
/// | frequency | Hz | Frequency of the periodic function. Also: 1 / period |
/// | amplitude | *arbitrary* | The amplitude of the function in 0-peak notation. |
/// | phase | *periods* | The phase shift of the function. Value of 1 means full shift around.
///
Expand Down Expand Up @@ -180,18 +180,18 @@ mod tests {

#[test]
fn empty_waveform_has_zero_components() {
let wf = wf!(f64, 44100);
let wf = wf!(f64, 44100).unwrap();
assert_eq!(0, wf.get_components_len());
}

#[test]
fn wavefrom_with_one_component() {
let wf = wf!(f64, 44100, sine!(500));
fn waveform_with_one_component() {
let wf = wf!(f64, 44100, sine!(500)).unwrap();
assert_eq!(1, wf.get_components_len());
}
#[test]
fn wavefrom_with_three_components() {
let wf = wf!(f64, 44100, sine!(500), square!(1000), sawtooth!(1500));
fn waveform_with_three_components() {
let wf = wf!(f64, 44100, sine!(500), square!(1000), sawtooth!(1500)).unwrap();
assert_eq!(3, wf.get_components_len());
}

Expand Down
Loading