diff --git a/.github/workflows/dasp.yml b/.github/workflows/dasp.yml new file mode 100644 index 00000000..4e9eb3d6 --- /dev/null +++ b/.github/workflows/dasp.yml @@ -0,0 +1,247 @@ +name: dasp +on: [push, pull_request] +jobs: + cargo-fmt-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: rustfmt + - name: cargo fmt check + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + cargo-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt update + - name: Install alsa + run: sudo apt-get install libasound2-dev + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: cargo test + uses: actions-rs/cargo@v1 + with: + command: test + args: --all --verbose + + cargo-test-no-default-features: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt update + - name: Install alsa + run: sudo apt-get install libasound2-dev + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + # Can't do `--no-default-features` for all pkgs, so do them one by one. + - name: cargo test dasp (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp/Cargo.toml --no-default-features --verbose + - name: cargo test dasp_envelope (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_envelope/Cargo.toml --no-default-features --verbose + - name: cargo test dasp_frame (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_frame/Cargo.toml --no-default-features --verbose + - name: cargo test dasp_interpolate (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_interpolate/Cargo.toml --no-default-features --verbose + - name: cargo test dasp_peak (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_peak/Cargo.toml --no-default-features --verbose + - name: cargo test dasp_ring_buffer (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_ring_buffer/Cargo.toml --no-default-features --verbose + - name: cargo test dasp_rms (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_rms/Cargo.toml --no-default-features --verbose + - name: cargo test dasp_sample (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_sample/Cargo.toml --no-default-features --verbose + - name: cargo test dasp_signal (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_signal/Cargo.toml --no-default-features --verbose + - name: cargo test dasp_slice (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_slice/Cargo.toml --no-default-features --verbose + - name: cargo test dasp_window (no default features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_window/Cargo.toml --no-default-features --verbose + + cargo-test-all-features: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt update + - name: Install alsa + run: sudo apt-get install libasound2-dev + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: cargo test (all features) + uses: actions-rs/cargo@v1 + with: + command: test + args: --all --all-features --verbose + + cargo-test-all-features-no-std: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt update + - name: Install alsa + run: sudo apt-get install libasound2-dev + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + # Can't do `--no-default-features` or `--features` for all pkgs, so do them one by one. + - name: cargo test dasp (all features no std) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp/Cargo.toml --no-default-features --features "all-no-std" --verbose + - name: cargo test dasp_signal (all features no std) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_signal/Cargo.toml --no-default-features --features "all-no-std" --verbose + - name: cargo test dasp_slice (all features no std) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_slice/Cargo.toml --no-default-features --features "all-no-std" --verbose + - name: cargo test dasp_interpolate (all features no std) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_interpolate/Cargo.toml --no-default-features --features "all-no-std" --verbose + - name: cargo test window (all features no std) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_window/Cargo.toml --no-default-features --features "all-no-std" --verbose + - name: cargo test dasp_envelope (all features no std) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path dasp_envelope/Cargo.toml --no-default-features --features "all-no-std" --verbose + + cargo-doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt update + - name: Install alsa + run: sudo apt-get install libasound2-dev + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: cargo doc + uses: actions-rs/cargo@v1 + with: + command: doc + args: --all --all-features --verbose + + cargo-publish: + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + env: + CRATESIO_TOKEN: ${{ secrets.CRATESIO_TOKEN }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt update + - name: Install alsa dev tools + run: sudo apt-get install libasound2-dev + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: cargo publish dasp_sample + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_sample/Cargo.toml + - name: cargo publish dasp_frame + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_frame/Cargo.toml + - name: cargo publish dasp_slice + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_slice/Cargo.toml + - name: cargo publish dasp_ring_buffer + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_ring_buffer/Cargo.toml + - name: cargo publish dasp_peak + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_peak/Cargo.toml + - name: cargo publish dasp_rms + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_rms/Cargo.toml + - name: cargo publish dasp_interpolate + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_interpolate/Cargo.toml + - name: cargo publish dasp_window + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_window/Cargo.toml + - name: cargo publish dasp_envelope + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_envelope/Cargo.toml + - name: cargo publish dasp_signal + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_signal/Cargo.toml + - name: cargo publish dasp + continue-on-error: true + run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp/Cargo.toml diff --git a/.gitignore b/.gitignore index 91784e50..fd896f39 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,3 @@ -# RUST STUFF - -# Compiled files -*.o -*.so -*.rlib -*.dll - -# Executables -*.exe - -# Generated by Cargo -/target/ +assets/two_vowels_10k.wav +target/ Cargo.lock - - - -# MAC STUFF - -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear on external disk -.Spotlight-V100 -.Trashes - -# Ignore the generated WAV file -/assets/two_vowels_10k.wav diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3fc6bc00..00000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: rust -rust: -- stable -- beta -- nightly -script: -- | - ([ "$TRAVIS_RUST_VERSION" = "nightly" ] && cargo check -v --no-default-features) || [ "$TRAVIS_RUST_VERSION" != "nightly" ] -- cargo check -v -- cargo test -v -- cargo test --release -v -- cargo doc -v diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..d450b0c7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,49 @@ +# Unreleased + +*No unreleased changes as of yet.* + +--- + +# 0.11.0 (2020-05-29) + +- Refactor the `sample` crate into a modular collection of crates under `dasp`. +- Rename repository from `sample` to `dasp`, where `dasp` stands for digital + audio signal processing. +- Add a suite of useful feature gates: + - Add `std` to all crates. Can be disabled in order to use `no_std`. + - Add a `all-features-no-std` feature to `dasp`, `dasp_envelope`, + `dasp_interpolate`, `dasp_signal`, `dasp_slice` and `dasp_window`. Enables + all features within a `no_std` context. + - `dasp_envelope` crate: + - `peak` - enables peak detector implementation. + - `rms` - enables RMS detector implementation. + - `dasp_interpolate` crate: + - `floor` - enables `Floor` `Interpolate` implementation. + - `linear` - enables `Linear` `Interpolate` implementation. + - `sinc` - enables `Sinc` `Interpolate` implementation. + - `dasp_signal` crate: + - `boxed` - enables `Signal` implementation for `Box`. + - `bus` - enables `SignalBus` trait. + - `envelope` - enables `SignalEnvelope` trait. + - `rms` - enables `SignalRms` trait. + - `window` - enables `signal::window` module. + - `window-hanning` - enables *hanning* window constructor. + - `window-rectangle` - enables *rectangle* window constructor. + - `dasp_slice` crate: + - `boxed` - enables conversions between boxed slices. + - The `dasp` crate has a feature for each of the above. +- Make **Window** trait generic over its phase and amplitude type. Update the + `dasp_signal::window` module items accordingly. +- Remove unsafe uninitialized ring buffer constructors. +- Remove `equilibrium()` and `identity()` constructors from `Sample` and `Frame` + traitsin favour of `EQUILIBRIUM` and `IDENTITY` associated consts. +- Remove `Frame::n_channels` function in favour of `Frame::CHANNELS` associated + const. +- Add implementation of `Frame` for all primitive `Sample` types where each are + assumed to represent a frame of a monophonic signal. This greatly simplifies + working with monophonic signal sources as demonstrated in the updated + `dasp_signal` crate. + +--- + +*CHANGELOG begins...* diff --git a/Cargo.toml b/Cargo.toml index f2533471..223b9ca8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,15 @@ -[package] -name = "sample" -description = "A crate providing the fundamentals for working with audio PCM DSP." -version = "0.10.0" -authors = ["mitchmindtree "] -readme = "README.md" -keywords = ["dsp", "bit-depth", "rate", "pcm", "audio"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/RustAudio/sample.git" -homepage = "https://github.com/RustAudio/sample" - -[dev-dependencies] -find_folder = "0.3" -hound = "2.0" -portaudio = "0.7" - -[features] -default = ["std"] -std = [] +[workspace] +members = [ + "dasp", + "dasp_envelope", + "dasp_frame", + "dasp_interpolate", + "dasp_peak", + "dasp_ring_buffer", + "dasp_rms", + "dasp_sample", + "dasp_signal", + "dasp_slice", + "dasp_window", + "examples", +] diff --git a/README.md b/README.md index 7781c079..caba0026 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,51 @@ -# sample [![Build Status](https://travis-ci.org/RustAudio/sample.svg?branch=master)](https://travis-ci.org/RustAudio/sample) [![Crates.io](https://img.shields.io/crates/v/sample.svg)](https://crates.io/crates/sample) [![Crates.io](https://img.shields.io/crates/l/sample.svg)](https://github.com/RustAudio/sample/blob/master/LICENSE-MIT) [![docs.rs](https://docs.rs/sample/badge.svg)](https://docs.rs/sample/) +# dasp [![Actions Status][dasp-actions-svg]][dasp-actions] [![docs.rs][dasp-docs-rs-svg]][dasp-docs-rs] -A crate providing the fundamentals for working with PCM (pulse-code modulation) -DSP (digital signal processing). In other words, `sample` provides a suite of -low-level, high-performance tools including types, traits and functions for -working with digital audio signals. +**Digital Audio Signal Processing in Rust.** -The `sample` crate requires **no dynamic allocations**1 and has **no -dependencies**. The goal is to design a library akin to the **std, but for audio -DSP**; keeping the focus on portable and fast fundamentals. +*Formerly the [`sample` crate](https://crates.io/crates/sample).* -1: Besides the `Signal::bus` method, which is only necessary when -converting a `Signal` tree into a directed acyclic graph. +A suite of crates providing the fundamentals for working with PCM (pulse-code +modulation) DSP (digital signal processing). In other words, `dasp` provides a +suite of low-level, high-performance tools including types, traits and functions +for working with digital audio signals. -Find the [API documentation here](https://docs.rs/sample/). +The `dasp` libraries require **no dynamic allocations**1 and have +**no dependencies**. The goal is to design a library akin to the **std, but for +audio DSP**; keeping the focus on portable and fast fundamentals. +1: Besides the feature-gated `SignalBus` trait, which is occasionally +useful when converting a `Signal` tree into a directed acyclic graph. -Features --------- +Find the [API documentation here][dasp-docs-rs]. + + +## Crates + +**dasp** is a modular collection of crates, allowing users to select the precise +set of tools required for their project. The following crates are included +within this repository: + +| **Library** | **Links** | **Description** | +| --- | --- | --- | +| [**`dasp`**][dasp] | [![Crates.io][dasp-crates-io-svg]][dasp-crates-io] [![docs.rs][dasp-docs-rs-svg]][dasp-docs-rs] | Top-level API with features for all crates. | +| [**`dasp_sample`**][dasp_sample] | [![Crates.io][dasp_sample-crates-io-svg]][dasp_sample-crates-io] [![docs.rs][dasp_sample-docs-rs-svg]][dasp_sample-docs-rs] | Sample trait, types, conversions and operations. | +| [**`dasp_frame`**][dasp_frame] | [![Crates.io][dasp_frame-crates-io-svg]][dasp_frame-crates-io] [![docs.rs][dasp_frame-docs-rs-svg]][dasp_frame-docs-rs] | Frame trait, types, conversions and operations. | +| [**`dasp_slice`**][dasp_slice] | [![Crates.io][dasp_slice-crates-io-svg]][dasp_slice-crates-io] [![docs.rs][dasp_slice-docs-rs-svg]][dasp_slice-docs-rs] | Conversions and ops for slices of samples/frames. | +| [**`dasp_ring_buffer`**][dasp_ring_buffer] | [![Crates.io][dasp_ring_buffer-crates-io-svg]][dasp_ring_buffer-crates-io] [![docs.rs][dasp_ring_buffer-docs-rs-svg]][dasp_ring_buffer-docs-rs] | Simple fixed and bounded ring buffers. | +| [**`dasp_peak`**][dasp_peak] | [![Crates.io][dasp_peak-crates-io-svg]][dasp_peak-crates-io] [![docs.rs][dasp_peak-docs-rs-svg]][dasp_peak-docs-rs] | Peak detection with half/full pos/neg wave rectifiers. | +| [**`dasp_rms`**][dasp_rms] | [![Crates.io][dasp_rms-crates-io-svg]][dasp_rms-crates-io] [![docs.rs][dasp_rms-docs-rs-svg]][dasp_rms-docs-rs] | RMS detection with configurable window. | +| [**`dasp_envelope`**][dasp_envelope] | [![Crates.io][dasp_envelope-crates-io-svg]][dasp_envelope-crates-io] [![docs.rs][dasp_envelope-docs-rs-svg]][dasp_envelope-docs-rs] | Envelope detection with peak and RMS impls. | +| [**`dasp_interpolate`**][dasp_interpolate] | [![Crates.io][dasp_interpolate-crates-io-svg]][dasp_interpolate-crates-io] [![docs.rs][dasp_interpolate-docs-rs-svg]][dasp_interpolate-docs-rs] | Inter-frame rate interpolation (linear, sinc, etc). | +| [**`dasp_window`**][dasp_window] | [![Crates.io][dasp_window-crates-io-svg]][dasp_window-crates-io] [![docs.rs][dasp_window-docs-rs-svg]][dasp_window-docs-rs] | Windowing function abstraction (hanning, rectangle). | +| [**`dasp_signal`**][dasp_signal] | [![Crates.io][dasp_signal-crates-io-svg]][dasp_signal-crates-io] [![docs.rs][dasp_signal-docs-rs-svg]][dasp_signal-docs-rs] | Iterator-like API for streams of audio frames. | + +[![deps-graph][deps-graph]][deps-graph] + +*Red dotted lines indicate optional dependencies, while black lines indicate +required dependencies.* + + +## Features Use the **Sample** trait to convert between and remain generic over any bit-depth in an optimal, performance-sensitive manner. Implementations are @@ -29,7 +58,7 @@ assert_eq!((-1.0).to_sample::(), 0); assert_eq!(0.0.to_sample::(), 128); assert_eq!(0i32.to_sample::(), 2_147_483_648); assert_eq!(I24::new(0).unwrap(), Sample::from_sample(0.0)); -assert_eq!(0.0, Sample::equilibrium()); +assert_eq!(0.0, Sample::EQUILIBRIUM); ``` Use the **Frame** trait to remain generic over the number of channels at a @@ -41,20 +70,20 @@ let foo = [0.1, 0.2, -0.1, -0.2]; let bar = foo.scale_amp(2.0); assert_eq!(bar, [0.2, 0.4, -0.2, -0.4]); -assert_eq!(Mono::::equilibrium(), [0.0]); -assert_eq!(Stereo::::equilibrium(), [0.0, 0.0]); -assert_eq!(<[f32; 3]>::equilibrium(), [0.0, 0.0, 0.0]); +assert_eq!(Mono::::EQUILIBRIUM, [0.0]); +assert_eq!(Stereo::::EQUILIBRIUM, [0.0, 0.0]); +assert_eq!(<[f32; 3]>::EQUILIBRIUM, [0.0, 0.0, 0.0]); let foo = [0i16, 0]; let bar: [u8; 2] = foo.map(Sample::to_sample); assert_eq!(bar, [128u8, 128]); ``` -Use the **Signal** trait for working with infinite-iterator-like types that -yield `Frame`s. **Signal** provides methods for adding, scaling, offsetting, -multiplying, clipping, generating, monitoring and buffering streams of `Frame`s. -Working with **Signal**s allows for easy, readable creation of rich and complex -DSP graphs with a simple and familiar API. +Use the **Signal** trait (enabled by the "signal" feature) for working with +infinite-iterator-like types that yield `Frame`s. **Signal** provides methods +for adding, scaling, offsetting, multiplying, clipping, generating, monitoring +and buffering streams of `Frame`s. Working with **Signal**s allows for easy, +readable creation of rich and complex DSP graphs with a simple and familiar API. ```rust // Clip to an amplitude of 0.9. @@ -63,23 +92,25 @@ let clipped: Vec<_> = signal::from_iter(frames.iter().cloned()).clip_amp(0.9).ta assert_eq!(clipped, vec![[0.9, 0.8], [-0.7, -0.9]]); // Add `a` with `b` and yield the result. -let a = [[0.2], [-0.6], [0.5]]; -let b = [[0.2], [0.1], [-0.8]]; +let a = [0.2, -0.6, 0.5]; +let b = [0.2, 0.1, -0.8]; let a_signal = signal::from_iter(a.iter().cloned()); let b_signal = signal::from_iter(b.iter().cloned()); -let added: Vec<[f32; 1]> = a_signal.add_amp(b_signal).take(3).collect(); -assert_eq!(added, vec![[0.4], [-0.5], [-0.3]]); +let added: Vec = a_signal.add_amp(b_signal).take(3).collect(); +assert_eq!(added, vec![0.4, -0.5, -0.3]); // Scale the playback rate by `0.5`. -let foo = [[0.0], [1.0], [0.0], [-1.0]]; +let foo = [0.0, 1.0, 0.0, -1.0]; let mut source = signal::from_iter(foo.iter().cloned()); -let interp = Linear::from_source(&mut source); +let a = source.next(); +let b = source.next(); +let interp = Linear::new(a, b); let frames: Vec<_> = source.scale_hz(interp, 0.5).take(8).collect(); -assert_eq!(&frames[..], &[[0.0], [0.5], [1.0], [0.5], [0.0], [-0.5], [-1.0], [-0.5]][..]); +assert_eq!(&frames[..], &[0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -0.5][..]); // Convert a signal to its RMS. let signal = signal::rate(44_100.0).const_hz(440.0).sine();; -let ring_buffer = ring_buffer::Fixed::from([[0.0]; WINDOW_SIZE]); +let ring_buffer = ring_buffer::Fixed::from([0.0; WINDOW_SIZE]); let mut rms_signal = signal.rms(ring_buffer); ``` @@ -98,42 +129,30 @@ including: - `Gen` (generate frames from a Fn() -> F) - `GenMut` (generate frames from a FnMut() -> F) -Use the **slice** module functions for processing chunks of `Frame`s. -Conversion functions are provided for safely converting between slices of -interleaved `Sample`s and slices of `Frame`s without requiring any allocation. -For example: +Use the **slice** module functions (enabled via the "slice" feature) for +processing chunks of `Frame`s. Conversion functions are provided for safely +converting between slices of interleaved `Sample`s and slices of `Frame`s +without requiring any allocation. For example: ```rust let frames = &[[0.0, 0.5], [0.0, -0.5]][..]; -let samples = sample::slice::to_sample_slice(frames); +let samples = slice::to_sample_slice(frames); assert_eq!(samples, &[0.0, 0.5, 0.0, -0.5][..]); let samples = &[0.0, 0.5, 0.0, -0.5][..]; -let frames = sample::slice::to_frame_slice(samples); +let frames = slice::to_frame_slice(samples); assert_eq!(frames, Some(&[[0.0, 0.5], [0.0, -0.5]][..])); let samples = &[0.0, 0.5, 0.0][..]; -let frames = sample::slice::to_frame_slice(samples); +let frames = slice::to_frame_slice(samples); assert_eq!(frames, None::<&[[f32; 2]]>); ``` -The **conv** module provides pure functions and traits for more specific -conversions. A function is provided for converting between every possible pair -of sample types. Traits include: - -- `FromSample`, `ToSample`, `Duplex`, -- `FromSampleSlice`, `ToSampleSlice`, `DuplexSampleSlice`, -- `FromSampleSliceMut`, `ToSampleSliceMut`, `DuplexSampleSliceMut`, -- `FromFrameSlice`, `ToFrameSlice`, `DuplexFrameSlice`, -- `FromFrameSliceMut`, `ToFrameSliceMut`, `DuplexFrameSliceMut`, -- `DuplexSlice`, `DuplexSliceMut`, - -The **interpolate** module provides a **Converter** type, for converting and -interpolating the rate of **Signal**s. This can be useful for both sample rate -conversion and playback rate multiplication. **Converter**s can use a range of -interpolation methods, with Floor, Linear, and Sinc interpolation provided in -the library. (NB: Sinc interpolation currently requires heap allocation, as it -uses VecDeque.) +The **signal::interpolate** module provides a **Converter** type, for converting +and interpolating the rate of **Signal**s. This can be useful for both sample +rate conversion and playback rate multiplication. **Converter**s can use a range +of interpolation methods, with Floor, Linear, and Sinc interpolation provided in +the library. The **ring_buffer** module provides generic **Fixed** and **Bounded** ring buffer types, both of which may be used with owned, borrowed, stack and @@ -160,32 +179,33 @@ let detector = envelope::Detector::peak(attack, release); let mut envelope = signal.detect_envelope(detector); assert_eq!( envelope.take(4).collect::>(), - vec![[0.0], [0.6321205496788025], [0.23254416035257117], [0.7176687675647109]] + vec![0.0, 0.6321205496788025, 0.23254416035257117, 0.7176687675647109] ); ``` -Using in a `no_std` environment -------------------------------- +## `no_std` + +All crates may be compiled with and without the std library. The std library is +enabled by default, however it may be disabled via `--no-default-features`. -This crate is largely dependency free, even of things outside `core`. The -`no_std` cargo feature will enable using `sample` in these environments. -Currently, only nightly is supported, because it explicitly depends on the -`alloc` and `collections` for datastructures and `core_intrinsics` for some of -the math. If this restriction is onerous for you, it can be lifted with minor -loss of functionality (the `Signal::bus` method), so open an issue! +To enable all of a crate's features *without* the std library, you may use +`--no-default-features --features "all-no-std"`. +Please note that some of the crates require the `core_intrinsics` feature in +order to be able to perform operations like `sin`, `cos` and `powf32` in a +`no_std` context. This means that these crates require the nightly toolchain in +order to build in a `no_std` context. -Contributions -------------- -If the **sample** crate is missing types, conversions or other fundamental -functionality that you wish it had, feel free to open an issue or pull request! -The more hands on deck, the merrier :) +## Contributing +If **dasp** is missing types, conversions or other fundamental functionality +that you wish it had, feel free to open an issue or pull request! The more +hands on deck, the merrier :) -License -------- + +## License Licensed under either of @@ -199,3 +219,63 @@ at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + + +[dasp-actions]: https://github.com/nannou-org/dasp/actions +[dasp-actions-svg]: https://github.com/rustaudio/dasp/workflows/dasp/badge.svg +[deps-graph]: ./assets/deps-graph.png +[dasp]: ./dasp +[dasp-crates-io]: https://crates.io/crates/dasp +[dasp-crates-io-svg]: https://img.shields.io/crates/v/dasp.svg +[dasp-docs-rs]: https://docs.rs/dasp/ +[dasp-docs-rs-svg]: https://docs.rs/dasp/badge.svg +[dasp_envelope]: ./dasp_envelope +[dasp_envelope-crates-io]: https://crates.io/crates/dasp_envelope +[dasp_envelope-crates-io-svg]: https://img.shields.io/crates/v/dasp_envelope.svg +[dasp_envelope-docs-rs]: https://docs.rs/dasp_envelope/ +[dasp_envelope-docs-rs-svg]: https://docs.rs/dasp_envelope/badge.svg +[dasp_frame]: ./dasp_frame +[dasp_frame-crates-io]: https://crates.io/crates/dasp_frame +[dasp_frame-crates-io-svg]: https://img.shields.io/crates/v/dasp_frame.svg +[dasp_frame-docs-rs]: https://docs.rs/dasp_frame/ +[dasp_frame-docs-rs-svg]: https://docs.rs/dasp_frame/badge.svg +[dasp_interpolate]: ./dasp_interpolate +[dasp_interpolate-crates-io]: https://crates.io/crates/dasp_interpolate +[dasp_interpolate-crates-io-svg]: https://img.shields.io/crates/v/dasp_interpolate.svg +[dasp_interpolate-docs-rs]: https://docs.rs/dasp_interpolate/ +[dasp_interpolate-docs-rs-svg]: https://docs.rs/dasp_interpolate/badge.svg +[dasp_peak]: ./dasp_peak +[dasp_peak-crates-io]: https://crates.io/crates/dasp_peak +[dasp_peak-crates-io-svg]: https://img.shields.io/crates/v/dasp_peak.svg +[dasp_peak-docs-rs]: https://docs.rs/dasp_peak/ +[dasp_peak-docs-rs-svg]: https://docs.rs/dasp_peak/badge.svg +[dasp_ring_buffer]: ./dasp_ring_buffer +[dasp_ring_buffer-crates-io]: https://crates.io/crates/dasp_ring_buffer +[dasp_ring_buffer-crates-io-svg]: https://img.shields.io/crates/v/dasp_ring_buffer.svg +[dasp_ring_buffer-docs-rs]: https://docs.rs/dasp_ring_buffer/ +[dasp_ring_buffer-docs-rs-svg]: https://docs.rs/dasp_ring_buffer/badge.svg +[dasp_rms]: ./dasp_rms +[dasp_rms-crates-io]: https://crates.io/crates/dasp_rms +[dasp_rms-crates-io-svg]: https://img.shields.io/crates/v/dasp_rms.svg +[dasp_rms-docs-rs]: https://docs.rs/dasp_rms/ +[dasp_rms-docs-rs-svg]: https://docs.rs/dasp_rms/badge.svg +[dasp_sample]: ./dasp_sample +[dasp_sample-crates-io]: https://crates.io/crates/dasp_sample +[dasp_sample-crates-io-svg]: https://img.shields.io/crates/v/dasp_sample.svg +[dasp_sample-docs-rs]: https://docs.rs/dasp_sample/ +[dasp_sample-docs-rs-svg]: https://docs.rs/dasp_sample/badge.svg +[dasp_signal]: ./dasp_signal +[dasp_signal-crates-io]: https://crates.io/crates/dasp_signal +[dasp_signal-crates-io-svg]: https://img.shields.io/crates/v/dasp_signal.svg +[dasp_signal-docs-rs]: https://docs.rs/dasp_signal/ +[dasp_signal-docs-rs-svg]: https://docs.rs/dasp_signal/badge.svg +[dasp_slice]: ./dasp_slice +[dasp_slice-crates-io]: https://crates.io/crates/dasp_slice +[dasp_slice-crates-io-svg]: https://img.shields.io/crates/v/dasp_slice.svg +[dasp_slice-docs-rs]: https://docs.rs/dasp_slice/ +[dasp_slice-docs-rs-svg]: https://docs.rs/dasp_slice/badge.svg +[dasp_window]: ./dasp_window +[dasp_window-crates-io]: https://crates.io/crates/dasp_window +[dasp_window-crates-io-svg]: https://img.shields.io/crates/v/dasp_window.svg +[dasp_window-docs-rs]: https://docs.rs/dasp_window/ +[dasp_window-docs-rs-svg]: https://docs.rs/dasp_window/badge.svg diff --git a/assets/deps-graph.png b/assets/deps-graph.png new file mode 100644 index 00000000..b61436f0 Binary files /dev/null and b/assets/deps-graph.png differ diff --git a/assets/thumbpiano A#3.wav b/assets/thumbpiano A#3.wav old mode 100755 new mode 100644 diff --git a/dasp/Cargo.toml b/dasp/Cargo.toml new file mode 100644 index 00000000..ccd7d280 --- /dev/null +++ b/dasp/Cargo.toml @@ -0,0 +1,90 @@ +[package] +name = "dasp" +description = "A crate providing the fundamentals for working with audio PCM DSP." +version = "0.11.0" +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["dsp", "bit-depth", "rate", "pcm", "audio"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/dasp.git" +homepage = "https://github.com/rustaudio/dasp" +edition = "2018" + +[dependencies] +dasp_envelope = { version = "0.11", path = "../dasp_envelope", default-features = false, optional = true } +dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } +dasp_interpolate = { version = "0.11", path = "../dasp_interpolate", default-features = false, optional = true } +dasp_peak = { version = "0.11", path = "../dasp_peak", default-features = false, optional = true } +dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false, optional = true } +dasp_rms = { version = "0.11", path = "../dasp_rms", default-features = false, optional = true } +dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } +dasp_signal = { version = "0.11", path = "../dasp_signal", default-features = false, optional = true } +dasp_slice = { version = "0.11", path = "../dasp_slice", default-features = false, optional = true } +dasp_window = { version = "0.11", path = "../dasp_window", default-features = false, optional = true } + +[features] +default = ["std"] +all = ["std", "all-no-std"] +all-no-std = [ + "envelope", + "envelope-peak", + "envelope-rms", + "interpolate", + "interpolate-floor", + "interpolate-linear", + "interpolate-sinc", + "peak", + "ring_buffer", + "rms", + "signal", + "signal-boxed", + "signal-bus", + "signal-envelope", + "signal-rms", + "signal-window", + "signal-window-hanning", + "signal-window-rectangle", + "slice", + "slice-boxed", + "window", + "window-hanning", + "window-rectangle", +] +std = [ + "dasp_envelope/std", + "dasp_frame/std", + "dasp_interpolate/std", + "dasp_peak/std", + "dasp_ring_buffer/std", + "dasp_rms/std", + "dasp_sample/std", + "dasp_signal/std", + "dasp_slice/std", + "dasp_window/std", +] +envelope = ["dasp_envelope"] +envelope-peak = ["dasp_envelope/peak"] +envelope-rms = ["dasp_envelope/rms"] +interpolate = ["dasp_interpolate"] +interpolate-floor = ["dasp_interpolate/floor"] +interpolate-linear = ["dasp_interpolate/linear"] +interpolate-sinc = ["dasp_interpolate/sinc"] +peak = ["dasp_peak"] +ring_buffer = ["dasp_ring_buffer"] +rms = ["dasp_rms"] +signal = ["dasp_signal"] +signal-boxed = ["dasp_signal/boxed"] +signal-bus = ["dasp_signal/bus"] +signal-envelope = ["dasp_signal/envelope", "envelope"] +signal-rms = ["dasp_signal/rms", "rms"] +signal-window = ["dasp_signal/window", "window"] +signal-window-hanning = ["dasp_signal/window-hanning", "window-hanning"] +signal-window-rectangle = ["dasp_signal/window-rectangle", "window-rectangle"] +slice = ["dasp_slice"] +slice-boxed = ["dasp_slice/boxed"] +window = ["dasp_window"] +window-hanning = ["dasp_window/hanning"] +window-rectangle = ["dasp_window/rectangle"] + +[package.metadata.docs.rs] +all-features = true diff --git a/dasp/src/lib.rs b/dasp/src/lib.rs new file mode 100644 index 00000000..95112bb9 --- /dev/null +++ b/dasp/src/lib.rs @@ -0,0 +1,116 @@ +//! **dasp** (formerly known as ***sample***) is a suite of crates providing the fundamentals for +//! working with pulse-code modulation **digital audio signal processing**. In other words, +//! **dasp** provides a suite of low-level, high-performance tools including types, traits and +//! functions for working with digital audio signals. +//! +//! Each of the **dasp** crates are re-exported under their respective +//! [modules](file:///home/mindtree/programming/rust/dasp/target/doc/dasp/index.html#modules). +//! +//! ## Highlights +//! +//! The following are some of the more interesting items within the dasp collection: +//! +//! - Use the [**Sample** trait](./trait.Sample.html) to remain generic across bit-depth. +//! - Use the [**Frame** trait](./frame/trait.Frame.html) to remain generic over channel layout. +//! - Use the [**Signal** trait](./signal/trait.Signal.html) for working with **Iterators** that +//! yield **Frames**. +//! - See the [**signal** module](./signal/index.html) for a collection of interesting signal +//! constructors (e.g. `sine`, `noise`, `from_iter`, etc). +//! - Use the [**slice** module](./slice/index.html) for working with slices of **Samples** and +//! **Frames**. +//! - See the [**sample::types** module](./sample/types/index.html) for provided custom sample +//! types. +//! - See the [**Converter** type](./signal/interpolate/struct.Converter.html) for sample rate +//! conversion and scaling. +//! - See the [**ring_buffer** module](./ring_buffer/index.html) for fast FIFO queue options. +//! +//! ## Optional Features +//! +//! By default, only the **sample** and **frame** modules and their respective traits are included +//! within this crate. You may pick and choose between the following features for additional +//! functionality. +//! +//! - The **all** feature enables all of the following features. +//! - The **std** feature enables the std library. This is enabled by default. +//! - The **all-no-std** feature enables all of the following features (without std). +//! +//! The following features map to each of the sub-crates and their respective features. +//! +//! - The **envelope** feature enables the `dasp_envelope` crate via the +//! [envelope](./envelope/index.html) module. +//! - The **envelope-peak** feature enables peak envelope detection. +//! - The **envelope-rms** feature enables RMS envelope detection. +//! - The **interpolate** feature enables the `dasp_interpolate` crate via the +//! [interpolate](./interpolate/index.html) module. +//! - The **interpolate-floor** feature enables a floor interpolation implementation. +//! - The **interpolate-linear** feature enables a linear interpolation implementation. +//! - The **interpolate-sinc** feature enables a sinc interpolation implementation. +//! - The **peak** feature enables the `dasp_peak` crate via the [peak](./peak/index.html) module. +//! - The **ring_buffer** feature enables the `dasp_ring_buffer` crate via the +//! [ring_buffer](./peak/index.html) module. +//! - The **rms** feature enables the `dasp_rms` crate via the [rms](./rms/index.html) module. +//! - The **signal** feature enables the `dasp_signal` crate via the [signal](./signal/index.html) +//! module. +//! - The **signal-boxed** feature enables an implementation of **Signal** for `Box`. +//! - The **signal-bus** feature enables the [**SignalBus**](./signal/bus/trait.SignalBus.html) +//! trait. +//! - The **signal-envelope** feature enables the +//! [**SignalEnvelope**](./signal/envelope/trait.SignalEnvelope.html) trait. +//! - The **signal-rms** feature enables the [**SignalRms**](./signal/rms/trait.SignalRms.html) +//! trait. +//! - The **signal-window** feature enables the +//! [**signal::window**](./signal/window/index.html) module. +//! - The **signal-window-hanning** enables the +//! [**signal::window::hanning**](./signal/window/fn.hanning.html) window constructor. +//! - The **signal-window-rectangle** enables the +//! [**signal::window::rectangle**](./signal/window/fn.rectangle.html) window constructor. +//! - The **slice** feature enables the `dasp_slice` crate via the [slice](./slice/index.html) +//! module. +//! - The **slice-boxed** feature enables boxed slice conversion traits and functions. +//! - The **window** feature enables the `dasp_window` crate via the [window](./window/index.html) +//! module. +//! - The **window-hanning** feature enables the [**Hanning**](./window/struct.Hanning.html) +//! window implementation. +//! - The **window-rectangle** feature enables the +//! [**Rectangle**](./window/struct.Rectangle.html) window implementation. +//! +//! You can also enable all of the above features with the `--all-features` flag. +//! +//! ### no_std +//! +//! If working in a `no_std` context, you can disable the default **std** feature with +//! `--no-default-features`. +//! +//! To enable all of the above features in a `no_std` context, enable the **all-no-std** feature. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "envelope")] +#[doc(inline)] +pub use dasp_envelope as envelope; +#[doc(inline)] +pub use dasp_frame::{self as frame, Frame}; +#[cfg(feature = "interpolate")] +#[doc(inline)] +pub use dasp_interpolate as interpolate; +#[cfg(feature = "peak")] +#[doc(inline)] +pub use dasp_peak as peak; +#[cfg(feature = "ring_buffer")] +#[doc(inline)] +pub use dasp_ring_buffer as ring_buffer; +#[cfg(feature = "rms")] +#[doc(inline)] +pub use dasp_rms as rms; +#[doc(inline)] +pub use dasp_sample::{self as sample, Sample}; +#[cfg(feature = "signal")] +#[doc(inline)] +pub use dasp_signal::{self as signal, Signal}; +#[cfg(feature = "slice")] +#[doc(inline)] +pub use dasp_slice as slice; +#[cfg(feature = "signal")] +#[doc(inline)] +pub use dasp_window as window; diff --git a/dasp_envelope/Cargo.toml b/dasp_envelope/Cargo.toml new file mode 100644 index 00000000..02444346 --- /dev/null +++ b/dasp_envelope/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "dasp_envelope" +version = "0.11.0" +description = "Audio PCM DSP envelope detection with peak and RMS implementations." +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["envelope", "detector", "follower", "peak", "rms"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/dasp.git" +homepage = "https://github.com/rustaudio/dasp" +edition = "2018" + +[dependencies] +dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } +dasp_peak = { version = "0.11", path = "../dasp_peak", default-features = false, optional = true } +dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false } +dasp_rms = { version = "0.11", path = "../dasp_rms", default-features = false, optional = true } +dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } + +[features] +default = ["std"] +all = ["std", "all-no-std"] +all-no-std = [ + "peak", + "rms", +] +std = [ + "dasp_frame/std", + "dasp_peak/std", + "dasp_ring_buffer/std", + "dasp_rms/std", + "dasp_sample/std", +] +peak = ["dasp_peak"] +rms = ["dasp_rms"] + +[package.metadata.docs.rs] +all-features = true diff --git a/dasp_envelope/src/detect/mod.rs b/dasp_envelope/src/detect/mod.rs new file mode 100644 index 00000000..e4c3c72a --- /dev/null +++ b/dasp_envelope/src/detect/mod.rs @@ -0,0 +1,89 @@ +use dasp_frame::Frame; +use dasp_sample::Sample; +use ops::f32::powf32; + +#[cfg(feature = "peak")] +pub use self::peak::Peak; + +mod ops; +#[cfg(feature = "peak")] +mod peak; +#[cfg(feature = "rms")] +mod rms; + +/// A type that can be used to detect the envelope of a signal. +#[derive(Clone, Debug)] +pub struct Detector +where + F: Frame, + D: Detect, +{ + last_env_frame: D::Output, + attack_gain: f32, + release_gain: f32, + detect: D, +} + +/// Types that may be used to detect an envelope over a signal. +pub trait Detect +where + F: Frame, +{ + /// The result of detection. + type Output: Frame; + /// Given some frame, return the detected envelope over each channel. + fn detect(&mut self, frame: F) -> Self::Output; +} + +fn calc_gain(n_frames: f32) -> f32 { + if n_frames == 0.0 { + 0.0 + } else { + powf32(core::f32::consts::E, -1.0 / n_frames) + } +} + +impl Detector +where + F: Frame, + D: Detect, +{ + /// Construct a **Detector** with the given **Detect** implementation. + pub fn new(detect: D, attack_frames: f32, release_frames: f32) -> Self { + Detector { + last_env_frame: D::Output::EQUILIBRIUM, + attack_gain: calc_gain(attack_frames), + release_gain: calc_gain(release_frames), + detect: detect, + } + } + + /// Set the **Detector**'s attack time as a number of frames. + pub fn set_attack_frames(&mut self, frames: f32) { + self.attack_gain = calc_gain(frames); + } + + /// Set the **Detector**'s release time as a number of frames. + pub fn set_release_frames(&mut self, frames: f32) { + self.release_gain = calc_gain(frames); + } + + /// Given the next input signal frame, detect and return the next envelope frame. + pub fn next(&mut self, frame: F) -> D::Output { + let Detector { + attack_gain, + release_gain, + ref mut detect, + ref mut last_env_frame, + } = *self; + + let detected_frame = detect.detect(frame); + let new_env_frame = last_env_frame.zip_map(detected_frame, |l, d| { + let gain = if l < d { attack_gain } else { release_gain }; + let diff = l.add_amp(-d.to_signed_sample()); + d.add_amp(diff.mul_amp(gain.to_sample()).to_sample()) + }); + *last_env_frame = new_env_frame; + new_env_frame + } +} diff --git a/dasp_envelope/src/detect/ops.rs b/dasp_envelope/src/detect/ops.rs new file mode 100644 index 00000000..252c7667 --- /dev/null +++ b/dasp_envelope/src/detect/ops.rs @@ -0,0 +1,12 @@ +#![allow(dead_code)] + +pub mod f32 { + #[cfg(feature = "std")] + pub fn powf32(a: f32, b: f32) -> f32 { + a.powf(b) + } + #[cfg(not(feature = "std"))] + pub fn powf32(a: f32, b: f32) -> f32 { + unsafe { core::intrinsics::powf32(a, b) } + } +} diff --git a/dasp_envelope/src/detect/peak.rs b/dasp_envelope/src/detect/peak.rs new file mode 100644 index 00000000..24f8efd7 --- /dev/null +++ b/dasp_envelope/src/detect/peak.rs @@ -0,0 +1,142 @@ +//! Peak detector implementations. +//! +//! ### Required Features +//! +//! - When using `dasp_envelope`, this module requires the **peak** feature to be enabled. +//! - When using `dasp`, this module requires the **envelope-peak** feature to be enabled. + +use crate::{Detect, Detector}; +use dasp_frame::Frame; +use dasp_peak as peak; + +/// A `Peak` detector, generic over the `FullWave`, `PositiveHalfWave`, `NegativeHalfWave` +/// rectifiers. +/// +/// ### Required Features +/// +/// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. +/// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Peak { + rectifier: R, +} + +impl Peak { + /// A signal rectifier that produces the absolute amplitude from samples. + /// + /// ### Required Features + /// + /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. + /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. + pub fn full_wave() -> Self { + peak::FullWave.into() + } +} + +impl Peak { + /// A signal rectifier that produces only the positive samples. + /// + /// ### Required Features + /// + /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. + /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. + pub fn positive_half_wave() -> Self { + peak::PositiveHalfWave.into() + } +} + +impl Peak { + /// A signal rectifier that produces only the negative samples. + /// + /// ### Required Features + /// + /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. + /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. + pub fn negative_half_wave() -> Self { + peak::NegativeHalfWave.into() + } +} + +impl Detector> +where + F: Frame, + R: peak::Rectifier, +{ + /// Construct a new **Peak** **Detector** that uses the given rectifier. + /// + /// ### Required Features + /// + /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. + /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. + pub fn peak_from_rectifier(rectifier: R, attack_frames: f32, release_frames: f32) -> Self { + let peak = rectifier.into(); + Self::new(peak, attack_frames, release_frames) + } +} + +impl Detector> +where + F: Frame, +{ + /// Construct a new full wave **Peak** **Detector**. + /// + /// ### Required Features + /// + /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. + /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. + pub fn peak(attack_frames: f32, release_frames: f32) -> Self { + let peak = Peak::full_wave(); + Self::new(peak, attack_frames, release_frames) + } +} + +impl Detector> +where + F: Frame, +{ + /// Construct a new positive half wave **Peak** **Detector**. + /// + /// ### Required Features + /// + /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. + /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. + pub fn peak_positive_half_wave(attack_frames: f32, release_frames: f32) -> Self { + let peak = Peak::positive_half_wave(); + Self::new(peak, attack_frames, release_frames) + } +} + +impl Detector> +where + F: Frame, +{ + /// Construct a new positive half wave **Peak** **Detector**. + /// + /// ### Required Features + /// + /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. + /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. + pub fn peak_negative_half_wave(attack_frames: f32, release_frames: f32) -> Self { + let peak = Peak::negative_half_wave(); + Self::new(peak, attack_frames, release_frames) + } +} + +impl Detect for Peak +where + F: Frame, + R: peak::Rectifier, +{ + type Output = R::Output; + fn detect(&mut self, frame: F) -> Self::Output { + self.rectifier.rectify(frame) + } +} + +impl From for Peak { + fn from(rectifier: R) -> Self { + Peak { + rectifier: rectifier, + } + } +} diff --git a/dasp_envelope/src/detect/rms.rs b/dasp_envelope/src/detect/rms.rs new file mode 100644 index 00000000..ede849ca --- /dev/null +++ b/dasp_envelope/src/detect/rms.rs @@ -0,0 +1,32 @@ +use crate::{Detect, Detector}; +use dasp_frame::Frame; +use dasp_ring_buffer as ring_buffer; +use dasp_rms as rms; + +impl Detect for rms::Rms +where + F: Frame, + S: ring_buffer::Slice + ring_buffer::SliceMut, +{ + type Output = F::Float; + fn detect(&mut self, frame: F) -> Self::Output { + self.next(frame) + } +} + +impl Detector> +where + F: Frame, + S: ring_buffer::Slice + ring_buffer::SliceMut, +{ + /// Construct a new **Rms** **Detector**. + /// + /// ### Required Features + /// + /// - When using `dasp_envelope`, this item requires the **rms** feature to be enabled. + /// - When using `dasp`, this item requires the **envelope-rms** feature to be enabled. + pub fn rms(buffer: ring_buffer::Fixed, attack_frames: f32, release_frames: f32) -> Self { + let rms = rms::Rms::new(buffer); + Self::new(rms, attack_frames, release_frames) + } +} diff --git a/dasp_envelope/src/lib.rs b/dasp_envelope/src/lib.rs new file mode 100644 index 00000000..d05205b5 --- /dev/null +++ b/dasp_envelope/src/lib.rs @@ -0,0 +1,30 @@ +//! An abstraction supporting different kinds of envelope detection. +//! +//! - The [**Detect**](./trait.Detect.html) trait provides an abstraction for generalising over +//! types of envelope detection. +//! - The [**Detector**](./struct.Detector.html) type allows for applying a **Detect** +//! implementation in order to detect the envelope of a signal. +//! +//! See the `dasp_signal` crate (or `dasp::signal` module) **SignalWindow** trait for a convenient +//! way to detect envelopes over arbitrary signals. +//! +//! ### Optional Features +//! +//! - The **peak** feature (or **envelope-peak** feature if using `dasp`) provides a peak envelope +//! detection implementation. +//! - The **rms** feature (or **envelope-rms** feature if using `dasp`) provides an RMS envelope +//! detection implementation. +//! +//! ### no_std +//! +//! If working in a `no_std` context, you can disable the default **std** feature with +//! `--no-default-features`. +//! +//! To enable all of the above features in a `no_std` context, enable the **all-no-std** feature. + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] + +pub mod detect; + +pub use self::detect::{Detect, Detector}; diff --git a/dasp_frame/Cargo.toml b/dasp_frame/Cargo.toml new file mode 100644 index 00000000..ce3ff5a7 --- /dev/null +++ b/dasp_frame/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "dasp_frame" +description = "An abstraction for audio PCM DSP frames, along with useful conversions and operations." +version = "0.11.0" +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["dsp", "frame", "channel", "pcm", "audio"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/dasp.git" +homepage = "https://github.com/rustaudio/dasp" +edition = "2018" + +[dependencies] +dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } + +[features] +default = ["std"] +std = ["dasp_sample/std"] + +[package.metadata.docs.rs] +all-features = true diff --git a/src/frame.rs b/dasp_frame/src/lib.rs similarity index 70% rename from src/frame.rs rename to dasp_frame/src/lib.rs index d0bc873b..47bf8249 100644 --- a/src/frame.rs +++ b/dasp_frame/src/lib.rs @@ -1,17 +1,19 @@ -//! Use the Frame trait to remain generic over the number of channels at +//! Use the [**Frame**](./trait.Frame.html) trait to remain generic over the number of channels at //! a single discrete moment in time. //! //! Implementations are provided for all fixed-size arrays up to 32 elements in length. -use Sample; +#![cfg_attr(not(feature = "std"), no_std)] -pub type Mono = [S; 1]; -pub type Stereo = [S; 2]; +use dasp_sample::Sample; /// Represents one sample from each channel at a single discrete instance in time within a /// PCM signal. /// -/// We provide implementations for `Frame` for all fixed-size arrays up to a length of 32 elements. +/// Implementations are provided for: +/// +/// - All fixed-size arrays up to a length of 32 elements. +/// - All primitive types that implement `Sample`. These implementations assume `CHANNELS = 1`. pub trait Frame: Copy + Clone + PartialEq { /// The type of PCM sample stored at each channel within the frame. type Sample: Sample; @@ -29,24 +31,35 @@ pub trait Frame: Copy + Clone + PartialEq { /// The equilibrium value for the wave that this `Sample` type represents. This is normally the /// value that is equal distance from both the min and max ranges of the sample. /// - /// **NOTE:** This will likely be changed to an "associated const" if the feature lands. - /// /// # Examples /// /// ```rust - /// extern crate sample; + /// use dasp_frame::{Frame, Mono, Stereo}; + /// + /// fn main() { + /// assert_eq!(Mono::::EQUILIBRIUM, [0.0]); + /// assert_eq!(Stereo::::EQUILIBRIUM, [0.0, 0.0]); + /// assert_eq!(<[f32; 3]>::EQUILIBRIUM, [0.0, 0.0, 0.0]); + /// assert_eq!(<[u8; 2]>::EQUILIBRIUM, [128u8, 128]); + /// } + /// ``` + const EQUILIBRIUM: Self; + + /// The total number of channels within the frame. /// - /// use sample::Frame; - /// use sample::frame::{Mono, Stereo}; + /// # Examples + /// + /// ```rust + /// use dasp_frame::{Frame, Mono, Stereo}; /// /// fn main() { - /// assert_eq!(Mono::::equilibrium(), [0.0]); - /// assert_eq!(Stereo::::equilibrium(), [0.0, 0.0]); - /// assert_eq!(<[f32; 3]>::equilibrium(), [0.0, 0.0, 0.0]); - /// assert_eq!(<[u8; 2]>::equilibrium(), [128u8, 128]); + /// assert_eq!(Mono::::CHANNELS, 1); + /// assert_eq!(Stereo::::CHANNELS, 2); + /// assert_eq!(<[f32; 3]>::CHANNELS, 3); + /// assert_eq!(<[u8; 2]>::CHANNELS, 2); /// } /// ``` - fn equilibrium() -> Self; + const CHANNELS: usize; /// Create a new `Frame` where the `Sample` for each channel is produced by the given function. /// @@ -65,9 +78,6 @@ pub trait Frame: Copy + Clone + PartialEq { where I: Iterator; - /// The total number of channels (and in turn samples) stored within the frame. - fn n_channels() -> usize; - /// Converts the frame into an iterator yielding the sample for each channel in the frame. fn channels(self) -> Self::Channels; @@ -88,9 +98,8 @@ pub trait Frame: Copy + Clone + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{Frame, Sample}; + /// use dasp_frame::Frame; + /// use dasp_sample::Sample; /// /// fn main() { /// let foo = [0i16, 0]; @@ -119,9 +128,7 @@ pub trait Frame: Copy + Clone + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Frame; + /// use dasp_frame::Frame; /// /// fn main() { /// let foo = [128u8; 2]; @@ -136,9 +143,7 @@ pub trait Frame: Copy + Clone + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Frame; + /// use dasp_frame::Frame; /// /// fn main() { /// let foo = [128u8; 2]; @@ -154,9 +159,7 @@ pub trait Frame: Copy + Clone + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Frame; + /// use dasp_frame::Frame; /// /// fn main() { /// assert_eq!([0.25, -0.5].offset_amp(0.5), [0.75, 0.0]); @@ -180,9 +183,7 @@ pub trait Frame: Copy + Clone + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Frame; + /// use dasp_frame::Frame; /// /// fn main() { /// assert_eq!([0.1, 0.2, -0.1, -0.2].scale_amp(2.0), [0.2, 0.4, -0.2, -0.4]); @@ -198,9 +199,7 @@ pub trait Frame: Copy + Clone + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Frame; + /// use dasp_frame::Frame; /// /// fn main() { /// let foo = [0.25, 0.5].add_amp([-0.75, 0.25]); @@ -220,16 +219,14 @@ pub trait Frame: Copy + Clone + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Frame; + /// use dasp_frame::Frame; /// /// fn main() { /// let foo = [0.25, 0.4].mul_amp([0.2, 0.5]); /// assert_eq!(foo, [0.05, 0.2]); /// - /// let bar = [192u8, 64].mul_amp([0.0, -2.0]); - /// assert_eq!(bar, [128, 0]); + /// let bar = [192u8, 64].mul_amp([0.0, -1.0]); + /// assert_eq!(bar, [128, 192]); /// } /// ``` #[inline] @@ -241,13 +238,6 @@ pub trait Frame: Copy + Clone + PartialEq { } } -/// An iterator that yields the sample for each channel in the frame by value. -#[derive(Clone)] -pub struct Channels { - next_idx: usize, - frame: F, -} - /// Restricts the types that may be used as the `Frame::NumChannels` associated type. /// /// `NumChannels` allows us to enforce the number of channels that a `Frame` must have in certain @@ -258,7 +248,17 @@ pub struct Channels { /// This trait is implemented for types `N1`...`N32`. pub trait NumChannels {} -macro_rules! impl_frame { +pub type Mono = [S; 1]; +pub type Stereo = [S; 2]; + +/// An iterator that yields the sample for each channel in the frame by value. +#[derive(Clone)] +pub struct Channels { + next_idx: usize, + frame: F, +} + +macro_rules! impl_frame_for_fixed_size_array { ($($NChan:ident $N:expr, [$($idx:expr)*],)*) => { $( /// A typified version of a number of channels. @@ -275,15 +275,8 @@ macro_rules! impl_frame { type Float = [S::Float; $N]; type Signed = [S::Signed; $N]; - #[inline] - fn equilibrium() -> Self { - [S::equilibrium(); $N] - } - - #[inline] - fn n_channels() -> usize { - $N - } + const EQUILIBRIUM: Self = [S::EQUILIBRIUM; $N]; + const CHANNELS: usize = $N; #[inline] fn channels(self) -> Self::Channels { @@ -391,13 +384,12 @@ macro_rules! impl_frame { [$(self[$idx].add_amp(*other.channel_unchecked($idx)), )*] } } - } )* }; } -impl_frame!{ +impl_frame_for_fixed_size_array! { N1 1, [0], N2 2, [0 1], N3 3, [0 1 2], @@ -432,6 +424,135 @@ impl_frame!{ N32 32, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31], } +macro_rules! impl_frame_for_sample { + ($($T:ty)*) => { + $( + impl Frame for $T { + type Sample = $T; + type NumChannels = N1; + type Channels = Channels; + type Float = <$T as Sample>::Float; + type Signed = <$T as Sample>::Signed; + + const EQUILIBRIUM: Self = <$T as Sample>::EQUILIBRIUM; + const CHANNELS: usize = 1; + + #[inline] + fn channels(self) -> Self::Channels { + Channels { + next_idx: 0, + frame: self, + } + } + + #[inline] + fn channel(&self, idx: usize) -> Option<&Self::Sample> { + if idx == 0 { + Some(self) + } else { + None + } + } + + #[inline] + fn from_fn(mut from: F) -> Self + where + F: FnMut(usize) -> Self::Sample, + { + from(0) + } + + #[inline] + fn from_samples(samples: &mut I) -> Option + where + I: Iterator + { + samples.next() + } + + #[inline(always)] + unsafe fn channel_unchecked(&self, _idx: usize) -> &Self::Sample { + self + } + + #[inline] + fn to_signed_frame(self) -> Self::Signed { + self.to_signed_sample() + } + + #[inline] + fn to_float_frame(self) -> Self::Float { + self.to_float_sample() + } + + #[inline] + fn map(self, mut map: M) -> F + where + F: Frame, + M: FnMut(Self::Sample) -> F::Sample, + { + F::from_fn(|channel_idx| { + // Here we do not require run-time bounds checking as we have asserted that + // the two arrays have the same number of channels at compile time with our + // where clause, i.e. + // + // `F: Frame` + unsafe { map(*self.channel_unchecked(channel_idx)) } + }) + } + + #[inline] + fn zip_map(self, other: O, mut zip_map: M) -> F + where + O: Frame, + F: Frame, + M: FnMut(Self::Sample, O::Sample) -> F::Sample + { + F::from_fn(|channel_idx| { + // Here we do not require run-time bounds checking as we have asserted that the two + // arrays have the same number of channels at compile time with our where clause, i.e. + // + // ``` + // O: Frame + // F: Frame + // ``` + unsafe { + zip_map(*self.channel_unchecked(channel_idx), + *other.channel_unchecked(channel_idx)) + } + }) + } + + #[inline] + fn scale_amp(self, amp: <$T as Sample>::Float) -> Self { + Sample::mul_amp(self, amp) + } + + #[inline] + fn add_amp(self, other: F) -> Self + where + F: Frame::Signed, NumChannels=N1>, + { + // Here we do not require run-time bounds checking as we have asserted that the two + // arrays have the same number of channels at compile time with our where clause, i.e. + unsafe { + Sample::add_amp(self, *other.channel_unchecked(0)) + } + } + } + )* + }; +} + +impl_frame_for_sample! { + i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 +} +impl_frame_for_sample! { + dasp_sample::types::I24 + dasp_sample::types::I48 + dasp_sample::types::U24 + dasp_sample::types::U48 +} impl Iterator for Channels where @@ -453,6 +574,6 @@ where { #[inline] fn len(&self) -> usize { - F::n_channels() - self.next_idx + F::CHANNELS - self.next_idx } } diff --git a/dasp_interpolate/Cargo.toml b/dasp_interpolate/Cargo.toml new file mode 100644 index 00000000..47c82d7a --- /dev/null +++ b/dasp_interpolate/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "dasp_interpolate" +description = "An abstraction for audio PCM DSP rate interpolation, including floor, linear and sinc." +version = "0.11.0" +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["dsp", "interpolate", "sample", "rate", "pcm"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/dasp.git" +homepage = "https://github.com/rustaudio/dasp" +edition = "2018" + +[dependencies] +dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } +dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false } +dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } + +[features] +default = ["std"] +all = ["std", "all-no-std"] +all-no-std = [ + "floor", + "linear", + "sinc", +] +std = [ + "dasp_frame/std", + "dasp_ring_buffer/std", + "dasp_sample/std", +] +floor = [] +linear = [] +sinc = [] + +[package.metadata.docs.rs] +all-features = true diff --git a/dasp_interpolate/src/floor.rs b/dasp_interpolate/src/floor.rs new file mode 100644 index 00000000..72c04c84 --- /dev/null +++ b/dasp_interpolate/src/floor.rs @@ -0,0 +1,48 @@ +//! A floor interpolator implementation. +//! +//! ### Required Features +//! +//! - When using `dasp_interpolate`, this module requires the **floor** feature to be enabled. +//! - When using `dasp`, this module requires the **interpolate-floor** feature to be enabled. + +use crate::Interpolator; +use dasp_frame::Frame; +use dasp_sample::Duplex; + +/// Interpolator that rounds off any values to the previous value from the source. +/// +/// ### Required Features +/// +/// - When using `dasp_interpolate`, this item requires the **floor** feature to be enabled. +/// - When using `dasp`, this item requires the **interpolate-floor** feature to be enabled. +pub struct Floor { + left: F, +} + +impl Floor { + /// Create a new Floor Interpolator. + /// + /// ### Required Features + /// + /// - When using `dasp_interpolate`, this item requires the **floor** feature to be enabled. + /// - When using `dasp`, this item requires the **interpolate-floor** feature to be enabled. + pub fn new(left: F) -> Floor { + Floor { left: left } + } +} + +impl Interpolator for Floor +where + F: Frame, + F::Sample: Duplex, +{ + type Frame = F; + + fn interpolate(&self, _x: f64) -> Self::Frame { + self.left + } + + fn next_source_frame(&mut self, source_frame: Self::Frame) { + self.left = source_frame; + } +} diff --git a/dasp_interpolate/src/lib.rs b/dasp_interpolate/src/lib.rs new file mode 100644 index 00000000..2e9d02cb --- /dev/null +++ b/dasp_interpolate/src/lib.rs @@ -0,0 +1,51 @@ +//! An abstraction for sample/frame rate interpolation. +//! +//! The [**Interpolator**](./trait.Interpolator.html) trait provides an abstraction over different +//! types of rate interpolation. +//! +//! See the `dasp_signal` crate (or `dasp::signal` module) **Converter** type for a convenient way +//! to interpolate the rate of arbitrary signals. +//! +//! ### Optional Features +//! +//! - The **floor** feature (or **interpolate-floor** feature if using `dasp`) provides a floor +//! interpolator implementation. +//! - The **linear** feature (or **interpolate-linear** feature if using `dasp`) provides a linear +//! interpolator implementation. +//! - The **sinc** feature (or **interpolate-sinc** feature if using `dasp`) provides a sinc +//! interpolator implementation. +//! +//! ### no_std +//! +//! If working in a `no_std` context, you can disable the default **std** feature with +//! `--no-default-features`. +//! +//! To enable all of the above features in a `no_std` context, enable the **all-no-std** feature. + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] + +use dasp_frame::Frame; + +#[cfg(feature = "floor")] +pub mod floor; +#[cfg(feature = "linear")] +pub mod linear; +#[cfg(feature = "sinc")] +pub mod sinc; + +/// Types that can interpolate between two values. +/// +/// Implementations should keep track of the necessary data both before and after the current +/// frame. +pub trait Interpolator { + /// The type of frame over which the interpolate may operate. + type Frame: Frame; + + /// Given a distance between [0.0 and 1.0) toward the following sample, return the interpolated + /// value. + fn interpolate(&self, x: f64) -> Self::Frame; + + /// To be called whenever the Interpolator value steps passed 1.0. + fn next_source_frame(&mut self, source_frame: Self::Frame); +} diff --git a/dasp_interpolate/src/linear.rs b/dasp_interpolate/src/linear.rs new file mode 100644 index 00000000..9e63327b --- /dev/null +++ b/dasp_interpolate/src/linear.rs @@ -0,0 +1,62 @@ +//! A linear interpolator implementation. +//! +//! ### Required Features +//! +//! - When using `dasp_interpolate`, this module requires the **linear** feature to be enabled. +//! - When using `dasp`, this module requires the **interpolate-linear** feature to be enabled. + +use crate::Interpolator; +use dasp_frame::Frame; +use dasp_sample::{Duplex, Sample}; + +/// Interpolator that interpolates linearly between the previous value and the next value +/// +/// ### Required Features +/// +/// - When using `dasp_interpolate`, this item requires the **linear** feature to be enabled. +/// - When using `dasp`, this item requires the **interpolate-linear** feature to be enabled. +pub struct Linear { + left: F, + right: F, +} + +impl Linear { + /// Create a new Linear Interpolator, where `left` and `right` are the first two frames to be + /// interpolated. + /// + /// ### Required Features + /// + /// - When using `dasp_interpolate`, this item requires the **linear** feature to be enabled. + /// - When using `dasp`, this item requires the **interpolate-linear** feature to be enabled. + pub fn new(left: F, right: F) -> Linear { + Linear { + left: left, + right: right, + } + } +} + +impl Interpolator for Linear +where + F: Frame, + F::Sample: Duplex, +{ + type Frame = F; + + /// Converts linearly from the previous value, using the next value to interpolate. It is + /// possible, although not advisable, to provide an x > 1.0 or < 0.0, but this will just + /// continue to be a linear ramp in one direction or another. + fn interpolate(&self, x: f64) -> Self::Frame { + self.left.zip_map(self.right, |l, r| { + let l_f = l.to_sample::(); + let r_f = r.to_sample::(); + let diff = r_f - l_f; + ((diff * x) + l_f).to_sample::<::Sample>() + }) + } + + fn next_source_frame(&mut self, source_frame: Self::Frame) { + self.left = self.right; + self.right = source_frame; + } +} diff --git a/dasp_interpolate/src/sinc/mod.rs b/dasp_interpolate/src/sinc/mod.rs new file mode 100644 index 00000000..3557b252 --- /dev/null +++ b/dasp_interpolate/src/sinc/mod.rs @@ -0,0 +1,124 @@ +//! A sinc interpolator implementation. +//! +//! ### Required Features +//! +//! - When using `dasp_interpolate`, this module requires the **sinc** feature to be enabled. +//! - When using `dasp`, this module requires the **interpolate-sinc** feature to be enabled. + +use crate::Interpolator; +use core::f64::consts::PI; +use dasp_frame::Frame; +use dasp_ring_buffer as ring_buffer; +use dasp_sample::{Duplex, Sample}; +use ops::f64::{cos, sin}; + +mod ops; + +/// Interpolator for sinc interpolation. +/// +/// Generally accepted as one of the better sample rate converters, although it uses significantly +/// more computation. +/// +/// ### Required Features +/// +/// - When using `dasp_interpolate`, this item requires the **sinc** feature to be enabled. +/// - When using `dasp`, this item requires the **interpolate-sinc** feature to be enabled. +pub struct Sinc { + frames: ring_buffer::Fixed, + idx: usize, +} + +impl Sinc { + /// Create a new **Sinc** interpolator with the given ring buffer. + /// + /// The given ring buffer should have a length twice that of the desired sinc interpolation + /// `depth`. + /// + /// The initial contents of the ring_buffer will act as padding for the interpolated signal. + /// + /// **panic!**s if the given ring buffer's length is not a multiple of `2`. + /// + /// ### Required Features + /// + /// - When using `dasp_interpolate`, this item requires the **sinc** feature to be enabled. + /// - When using `dasp`, this item requires the **interpolate-sinc** feature to be enabled. + pub fn new(frames: ring_buffer::Fixed) -> Self + where + S: ring_buffer::SliceMut, + S::Element: Frame, + { + assert!(frames.len() % 2 == 0); + Sinc { + frames: frames, + idx: 0, + } + } + + fn depth(&self) -> usize + where + S: ring_buffer::Slice, + { + self.frames.len() / 2 + } +} + +impl Interpolator for Sinc +where + S: ring_buffer::SliceMut, + S::Element: Frame, + ::Sample: Duplex, +{ + type Frame = S::Element; + + /// Sinc interpolation + fn interpolate(&self, x: f64) -> Self::Frame { + let phil = x; + let phir = 1.0 - x; + let nl = self.idx; + let nr = self.idx + 1; + let depth = self.depth(); + + let rightmost = nl + depth; + let leftmost = nr as isize - depth as isize; + let max_depth = if rightmost >= self.frames.len() { + self.frames.len() - depth + } else if leftmost < 0 { + (depth as isize + leftmost) as usize + } else { + depth + }; + + (0..max_depth).fold(Self::Frame::EQUILIBRIUM, |mut v, n| { + v = { + let a = PI * (phil + n as f64); + let first = if a == 0.0 { 1.0 } else { sin(a) / a }; + let second = 0.5 + 0.5 * cos(a / (phil + max_depth as f64)); + v.zip_map(self.frames[nr - n], |vs, r_lag| { + vs.add_amp( + (first * second * r_lag.to_sample::()) + .to_sample::<::Sample>() + .to_signed_sample(), + ) + }) + }; + + let a = PI * (phir + n as f64); + let first = if a == 0.0 { 1.0 } else { sin(a) / a }; + let second = 0.5 + 0.5 * cos(a / (phir + max_depth as f64)); + v.zip_map(self.frames[nl + n], |vs, r_lag| { + vs.add_amp( + (first * second * r_lag.to_sample::()) + .to_sample::<::Sample>() + .to_signed_sample(), + ) + }) + }) + } + + fn next_source_frame(&mut self, source_frame: Self::Frame) { + let _old_frame = self.frames.push(source_frame); + if self.idx < self.depth() { + self.idx += 1; + } + } +} diff --git a/dasp_interpolate/src/sinc/ops.rs b/dasp_interpolate/src/sinc/ops.rs new file mode 100644 index 00000000..edc9b7a0 --- /dev/null +++ b/dasp_interpolate/src/sinc/ops.rs @@ -0,0 +1,21 @@ +#![allow(dead_code)] + +pub mod f64 { + #[cfg(not(feature = "std"))] + pub fn sin(x: f64) -> f64 { + unsafe { core::intrinsics::sinf64(x) } + } + #[cfg(feature = "std")] + pub fn sin(x: f64) -> f64 { + x.sin() + } + + #[cfg(not(feature = "std"))] + pub fn cos(x: f64) -> f64 { + unsafe { core::intrinsics::cosf64(x) } + } + #[cfg(feature = "std")] + pub fn cos(x: f64) -> f64 { + x.cos() + } +} diff --git a/dasp_peak/Cargo.toml b/dasp_peak/Cargo.toml new file mode 100644 index 00000000..90560ea9 --- /dev/null +++ b/dasp_peak/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "dasp_peak" +description = "A DSP peak detection library generic over the rectifier." +version = "0.11.0" +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["peak", "rectifier", "full", "half", "wave"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/dasp.git" +homepage = "https://github.com/rustaudio/dasp" +edition = "2018" + +[dependencies] +dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } +dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } + +[features] +default = ["std"] +std = ["dasp_frame/std", "dasp_sample/std"] + +[package.metadata.docs.rs] +all-features = true diff --git a/src/peak.rs b/dasp_peak/src/lib.rs similarity index 82% rename from src/peak.rs rename to dasp_peak/src/lib.rs index bea83cea..fda9eb12 100644 --- a/src/peak.rs +++ b/dasp_peak/src/lib.rs @@ -1,44 +1,19 @@ //! Peak envelope detection over a signal. -use {Frame, Sample}; +#![cfg_attr(not(feature = "std"), no_std)] -/// A signal rectifier that produces the absolute amplitude from samples. -pub fn full_wave(frame: F) -> F::Signed -where - F: Frame, -{ - frame.map(|s| { - let signed = s.to_signed_sample(); - if signed < Sample::equilibrium() { - -signed - } else { - signed - } - }) -} +use dasp_frame::Frame; +use dasp_sample::Sample; -/// A signal rectifier that produces only the positive samples. -pub fn positive_half_wave(frame: F) -> F -where - F: Frame, -{ - frame.map(|s| if s < Sample::equilibrium() { - Sample::equilibrium() - } else { - s - }) -} - -/// A signal rectifier that produces only the negative samples. -pub fn negative_half_wave(frame: F) -> F +/// Types that may be used to rectify a signal of frames `F` for a `Peak` detector. +pub trait Rectifier where F: Frame, { - frame.map(|s| if s > Sample::equilibrium() { - Sample::equilibrium() - } else { - s - }) + /// Frames that can be detected. + type Output: Frame; + /// Rectify the given frame. + fn rectify(&mut self, frame: F) -> Self::Output; } /// A signal rectifier that produces the absolute amplitude from samples. @@ -51,17 +26,6 @@ pub struct PositiveHalfWave; #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct NegativeHalfWave; -/// Types that may be used to rectify a signal of frames `F` for a `Peak` detector. -pub trait Rectifier -where - F: Frame, -{ - /// Frames that can be detected. - type Output: Frame; - /// Rectify the given frame. - fn rectify(&mut self, frame: F) -> Self::Output; -} - impl Rectifier for FullWave where F: Frame, @@ -91,3 +55,46 @@ where negative_half_wave(frame) } } + +/// A signal rectifier that produces the absolute amplitude from samples. +pub fn full_wave(frame: F) -> F::Signed +where + F: Frame, +{ + frame.map(|s| { + let signed = s.to_signed_sample(); + if signed < Sample::EQUILIBRIUM { + -signed + } else { + signed + } + }) +} + +/// A signal rectifier that produces only the positive samples. +pub fn positive_half_wave(frame: F) -> F +where + F: Frame, +{ + frame.map(|s| { + if s < Sample::EQUILIBRIUM { + Sample::EQUILIBRIUM + } else { + s + } + }) +} + +/// A signal rectifier that produces only the negative samples. +pub fn negative_half_wave(frame: F) -> F +where + F: Frame, +{ + frame.map(|s| { + if s > Sample::EQUILIBRIUM { + Sample::EQUILIBRIUM + } else { + s + } + }) +} diff --git a/dasp_ring_buffer/Cargo.toml b/dasp_ring_buffer/Cargo.toml new file mode 100644 index 00000000..18d80e7a --- /dev/null +++ b/dasp_ring_buffer/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "dasp_ring_buffer" +description = "Simple fixed and bounded ring buffers for audio PCM DSP." +version = "0.11.0" +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["ring", "buffer", "dsp", "pcm", "audio"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/dasp.git" +homepage = "https://github.com/rustaudio/dasp" +edition = "2018" + +[features] +default = ["std"] +std = [] + +[package.metadata.docs.rs] +all-features = true diff --git a/src/ring_buffer.rs b/dasp_ring_buffer/src/lib.rs similarity index 82% rename from src/ring_buffer.rs rename to dasp_ring_buffer/src/lib.rs index 9b6b9de4..59fae5d8 100644 --- a/src/ring_buffer.rs +++ b/dasp_ring_buffer/src/lib.rs @@ -7,13 +7,27 @@ //! - The [Fixed](./struct.Fixed.html) ring buffer type. //! - The [Bounded](./struct.Bounded.html) ring buffer type. -use Box; -use core::mem; +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc; + use core::iter::{Chain, Cycle, FromIterator, Skip, Take}; -use core::ptr; +use core::mem; use core::ops::{Index, IndexMut}; +use core::ptr; use core::slice; -use Vec; + +#[cfg(not(feature = "std"))] +type Vec = alloc::vec::Vec; +#[cfg(feature = "std")] +#[allow(dead_code)] +type Vec = std::vec::Vec; + +#[cfg(not(feature = "std"))] +type Box = alloc::boxed::Box; +#[cfg(feature = "std")] +type Box = std::boxed::Box; //////////////////////// ///// SLICE TRAITS ///// @@ -137,25 +151,23 @@ impl_slice_for_arrays! { /// A `Fixed` ring buffer can be created around any type with a slice to write to. /// /// ``` -/// extern crate sample; -/// /// fn main() { /// // From a fixed size array. -/// sample::ring_buffer::Fixed::from([1, 2, 3, 4]); +/// dasp_ring_buffer::Fixed::from([1, 2, 3, 4]); /// /// // From a Vec. -/// sample::ring_buffer::Fixed::from(vec![1, 2, 3, 4]); +/// dasp_ring_buffer::Fixed::from(vec![1, 2, 3, 4]); /// /// // From a Boxed slice. -/// sample::ring_buffer::Fixed::from(vec![1, 2, 3].into_boxed_slice()); +/// dasp_ring_buffer::Fixed::from(vec![1, 2, 3].into_boxed_slice()); /// /// // From a mutably borrowed slice. /// let mut slice = [1, 2, 3, 4]; -/// sample::ring_buffer::Fixed::from(&mut slice[..]); +/// dasp_ring_buffer::Fixed::from(&mut slice[..]); /// /// // An immutable ring buffer from an immutable slice. /// let slice = [1, 2, 3, 4]; -/// sample::ring_buffer::Fixed::from(&slice[..]); +/// dasp_ring_buffer::Fixed::from(&slice[..]); /// } /// ``` #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -171,12 +183,8 @@ where /// The fixed length of the buffer. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let rb = ring_buffer::Fixed::from([0; 4]); + /// let rb = dasp_ring_buffer::Fixed::from([0; 4]); /// assert_eq!(rb.len(), 4); /// } /// ``` @@ -189,12 +197,8 @@ where /// queue, ensuring that the length is retained. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let mut rb = ring_buffer::Fixed::from([0, 1, 2, 3]); + /// let mut rb = dasp_ring_buffer::Fixed::from([0, 1, 2, 3]); /// assert_eq!(rb.push(4), 0); /// assert_eq!(rb.push(5), 1); /// assert_eq!(rb.push(6), 2); @@ -223,12 +227,8 @@ where /// If `index` is out of range it will be looped around the length of the data slice. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let mut rb = ring_buffer::Fixed::from([0, 1, 2]); + /// let mut rb = dasp_ring_buffer::Fixed::from([0, 1, 2]); /// assert_eq!(*rb.get(0), 0); /// assert_eq!(*rb.get(1), 1); /// assert_eq!(*rb.get(2), 2); @@ -260,12 +260,8 @@ where /// If `index` is out of range it will be looped around the length of the data slice. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let mut rb = ring_buffer::Fixed::from([0, 1, 2, 3]); + /// let mut rb = dasp_ring_buffer::Fixed::from([0, 1, 2, 3]); /// assert_eq!(rb[0], 0); /// rb.set_first(2); /// assert_eq!(rb[0], 2); @@ -285,10 +281,8 @@ where /// The first slice is always aligned contiguously behind the second slice. /// /// ``` - /// extern crate sample; - /// /// fn main() { - /// let mut ring_buffer = sample::ring_buffer::Fixed::from([0; 4]); + /// let mut ring_buffer = dasp_ring_buffer::Fixed::from([0; 4]); /// assert_eq!(ring_buffer.slices(), (&[0, 0, 0, 0][..], &[][..])); /// ring_buffer.push(1); /// ring_buffer.push(2); @@ -433,45 +427,23 @@ where /// popping elements. /// /// ``` -/// extern crate sample; -/// -/// use sample::ring_buffer; -/// /// fn main() { /// // From a fixed size array. -/// ring_buffer::Bounded::from([0; 4]); +/// dasp_ring_buffer::Bounded::from([0; 4]); /// /// // From a Vec. -/// ring_buffer::Bounded::from(vec![0; 4]); +/// dasp_ring_buffer::Bounded::from(vec![0; 4]); /// /// // From a Boxed slice. -/// ring_buffer::Bounded::from(vec![0; 3].into_boxed_slice()); +/// dasp_ring_buffer::Bounded::from(vec![0; 3].into_boxed_slice()); /// /// // From a mutably borrowed slice. /// let mut slice = [0; 4]; -/// ring_buffer::Bounded::from(&mut slice[..]); +/// dasp_ring_buffer::Bounded::from(&mut slice[..]); /// /// // An immutable ring buffer from an immutable slice. /// let slice = [0; 4]; -/// ring_buffer::Bounded::from(&slice[..]); -/// } -/// ``` -/// -/// Two slightly more efficient constructors are provided for fixed-size arrays and boxed slices. -/// These are generally more efficient as they do not require initialising elements. -/// -/// ``` -/// extern crate sample; -/// -/// use sample::ring_buffer; -/// -/// fn main() { -/// // Fixed-size array. -/// ring_buffer::Bounded::<[i32; 4]>::array(); -/// -/// // Boxed slice. -/// let mut rb = ring_buffer::Bounded::boxed_slice(4); -/// rb.push(1); +/// dasp_ring_buffer::Bounded::from(&slice[..]); /// } /// ``` #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -489,81 +461,17 @@ pub struct DrainBounded<'a, S: 'a> { bounded: &'a mut Bounded, } -impl Bounded> -where - T: Copy, -{ - /// A `Bounded` ring buffer that uses a `Box`ed slice with the given maximum length to - /// represent the data. - /// - /// Slightly more efficient than using the similar `From` constructor as this creates the - /// underlying slice with uninitialised memory. - /// - /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// - /// fn main() { - /// let mut rb = ring_buffer::Bounded::boxed_slice(4); - /// assert_eq!(rb.max_len(), 4); - /// assert_eq!(rb.len(), 0); - /// rb.push(1); - /// rb.push(2); - /// } - /// ``` - pub fn boxed_slice(max_len: usize) -> Self { - let mut vec = Vec::new(); - vec.reserve_exact(max_len); - unsafe { - vec.set_len(max_len); - let data = vec.into_boxed_slice(); - Self::from_raw_parts(0, 0, data) - } - } -} - impl Bounded where S: Slice, S::Element: Copy, { - /// A `Bounded` buffer that uses a fixed-size array to represent data. - /// - /// Slightly more efficient than using the similar `From` constructor as this creates the - /// underlying array with uninitialised memory. - /// - /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// - /// fn main() { - /// let mut rb = ring_buffer::Bounded::<[f32; 3]>::array(); - /// assert_eq!(rb.len(), 0); - /// assert_eq!(rb.max_len(), 3); - /// } - /// ``` - pub fn array() -> Self - where - S: FixedSizeArray, - { - unsafe { - let data = mem::uninitialized(); - Self::from_raw_parts(0, 0, data) - } - } - /// The same as the `From` implementation, but assumes that the given `data` is full of valid /// elements and initialises the ring buffer with a length equal to `max_len`. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let mut rb = ring_buffer::Bounded::from_full([0, 1, 2, 3]); + /// let mut rb = dasp_ring_buffer::Bounded::from_full([0, 1, 2, 3]); /// assert_eq!(rb.len(), rb.max_len()); /// assert_eq!(rb.pop(), Some(0)); /// assert_eq!(rb.pop(), Some(1)); @@ -580,10 +488,8 @@ where /// front of the buffer. /// /// ``` - /// extern crate sample; - /// /// fn main() { - /// let mut ring_buffer = sample::ring_buffer::Bounded::<[i32; 3]>::array(); + /// let mut ring_buffer = dasp_ring_buffer::Bounded::from([0i32; 3]); /// assert_eq!(ring_buffer.max_len(), 3); /// } /// ``` @@ -595,10 +501,8 @@ where /// The current length of the ring buffer. /// /// ``` - /// extern crate sample; - /// /// fn main() { - /// let mut ring_buffer = sample::ring_buffer::Bounded::<[i32; 3]>::array(); + /// let mut ring_buffer = dasp_ring_buffer::Bounded::from([0i32; 3]); /// assert_eq!(ring_buffer.len(), 0); /// } /// ``` @@ -612,12 +516,8 @@ where /// Equivalent to `self.len() == 0`. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let mut rb = ring_buffer::Bounded::<[i32; 2]>::array(); + /// let mut rb = dasp_ring_buffer::Bounded::from([0i32; 2]); /// assert!(rb.is_empty()); /// rb.push(0); /// assert!(!rb.is_empty()); @@ -632,12 +532,8 @@ where /// Equivalent to `self.len() == self.max_len()`. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let mut rb = ring_buffer::Bounded::<[i32; 2]>::array(); + /// let mut rb = dasp_ring_buffer::Bounded::from([0i32; 2]); /// assert!(!rb.is_full()); /// rb.push(0); /// rb.push(1); @@ -656,10 +552,8 @@ where /// The first slice is always aligned contiguously behind the second slice. /// /// ``` - /// extern crate sample; - /// /// fn main() { - /// let mut ring_buffer = sample::ring_buffer::Bounded::<[i32; 4]>::array(); + /// let mut ring_buffer = dasp_ring_buffer::Bounded::from([0i32; 4]); /// assert_eq!(ring_buffer.slices(), (&[][..], &[][..])); /// ring_buffer.push(1); /// ring_buffer.push(2); @@ -703,12 +597,8 @@ where /// This method uses the `slices` method internally. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let mut rb = ring_buffer::Bounded::<[i32; 3]>::array(); + /// let mut rb = dasp_ring_buffer::Bounded::from([0i32; 3]); /// assert_eq!(rb.iter().count(), 0); /// rb.push(1); /// rb.push(2); @@ -738,12 +628,8 @@ where /// Returns `None` if there is no element at the given index. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let mut rb = ring_buffer::Bounded::<[i32; 4]>::array(); + /// let mut rb = dasp_ring_buffer::Bounded::from([0i32; 4]); /// assert_eq!(rb.get(1), None); /// rb.push(0); /// rb.push(1); @@ -771,10 +657,7 @@ where return None; } let wrapped_index = index % self.max_len(); - unsafe { - Some(self.data.slice_mut().get_unchecked_mut(wrapped_index) as - &mut _) - } + unsafe { Some(self.data.slice_mut().get_unchecked_mut(wrapped_index) as &mut _) } } /// Pushes the given element to the back of the buffer. @@ -786,10 +669,8 @@ where /// the buffer and increases the length of the buffer by `1`. `None` is returned. /// /// ``` - /// extern crate sample; - /// /// fn main() { - /// let mut ring_buffer = sample::ring_buffer::Bounded::<[i32; 3]>::array(); + /// let mut ring_buffer = dasp_ring_buffer::Bounded::from([0i32; 3]); /// assert_eq!(ring_buffer.push(1), None); /// assert_eq!(ring_buffer.push(2), None); /// assert_eq!(ring_buffer.len(), 2); @@ -834,12 +715,8 @@ where /// If the buffer is empty, this returns `None`. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let mut rb = ring_buffer::Bounded::from_full([0, 1, 2]); + /// let mut rb = dasp_ring_buffer::Bounded::from_full([0, 1, 2]); /// assert_eq!(rb.len(), rb.max_len()); /// assert_eq!(rb.pop(), Some(0)); /// assert_eq!(rb.pop(), Some(1)); @@ -877,12 +754,8 @@ where /// That is, all non-yielded elements will remain in the ring buffer. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// /// fn main() { - /// let mut rb = ring_buffer::Bounded::from_full([0, 1, 2, 3]); + /// let mut rb = dasp_ring_buffer::Bounded::from_full([0, 1, 2, 3]); /// assert_eq!(rb.drain().take(2).collect::>(), vec![0, 1]); /// assert_eq!(rb.pop(), Some(2)); /// assert_eq!(rb.pop(), Some(3)); @@ -890,9 +763,7 @@ where /// } /// ``` pub fn drain(&mut self) -> DrainBounded { - DrainBounded { - bounded: self, - } + DrainBounded { bounded: self } } /// Creates a `Bounded` ring buffer from its start index, length and data slice. @@ -904,8 +775,7 @@ where /// elements when using this method. /// /// **Note:** This method should only be necessary if you require specifying the `start` and - /// initial `len`. Please see the `Bounded::array` and `Bounded::boxed_slice` functions for - /// simpler constructor options that do not require manually passing indices. + /// initial `len`. /// /// **Panic!**s if the following conditions are not met: /// diff --git a/tests/ring_buffer.rs b/dasp_ring_buffer/tests/ring_buffer.rs similarity index 78% rename from tests/ring_buffer.rs rename to dasp_ring_buffer/tests/ring_buffer.rs index 25e5cb81..938ec085 100644 --- a/tests/ring_buffer.rs +++ b/dasp_ring_buffer/tests/ring_buffer.rs @@ -1,10 +1,8 @@ -extern crate sample; - -use sample::ring_buffer; +use dasp_ring_buffer as ring_buffer; #[test] fn test_bounded_boxed_slice() { - let mut rb = ring_buffer::Bounded::boxed_slice(3); + let mut rb = ring_buffer::Bounded::from(vec![0; 3].into_boxed_slice()); assert_eq!(rb.push(1), None); assert_eq!(rb.push(2), None); assert_eq!(rb.push(3), None); @@ -13,7 +11,7 @@ fn test_bounded_boxed_slice() { #[test] fn test_bounded_array() { - let mut rb = ring_buffer::Bounded::<[i32; 3]>::array(); + let mut rb = ring_buffer::Bounded::from([0i32; 3]); assert_eq!(rb.push(1), None); assert_eq!(rb.push(2), None); assert_eq!(rb.push(3), None); @@ -38,6 +36,6 @@ fn test_bounded_from_vec() { #[test] #[should_panic] fn test_bounded_get_out_of_range() { - let rb = ring_buffer::Bounded::<[i32; 3]>::array(); + let rb = ring_buffer::Bounded::from([0i32; 3]); let _ = rb[0]; } diff --git a/dasp_rms/Cargo.toml b/dasp_rms/Cargo.toml new file mode 100644 index 00000000..218a6cb0 --- /dev/null +++ b/dasp_rms/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "dasp_rms" +description = "RMS detection with configurable window for audio PCM DSP." +version = "0.11.0" +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["dsp", "rms", "envelope", "pcm", "audio"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/dasp.git" +homepage = "https://github.com/rustaudio/dasp" +edition = "2018" + +[dependencies] +dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } +dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false } +dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } + +[features] +default = ["std"] +std = ["dasp_frame/std", "dasp_ring_buffer/std", "dasp_sample/std"] + +[package.metadata.docs.rs] +all-features = true diff --git a/src/rms.rs b/dasp_rms/src/lib.rs similarity index 79% rename from src/rms.rs rename to dasp_rms/src/lib.rs index 621b80c1..8322885e 100644 --- a/src/rms.rs +++ b/dasp_rms/src/lib.rs @@ -2,10 +2,13 @@ //! //! The primary type of interest in this module is the [**Rms**](./struct.Rms). -use {FloatSample, Frame, Sample}; +#![cfg_attr(not(feature = "std"), no_std)] + use core::fmt; use core::marker::PhantomData; -use ring_buffer; +use dasp_frame::Frame; +use dasp_ring_buffer as ring_buffer; +use dasp_sample::{FloatSample, Sample}; /// Iteratively extracts the RMS (root mean square) envelope from a window over a signal of sample /// `Frame`s. @@ -37,10 +40,8 @@ where /// The window size of the **Rms** is equal to the length of the given ring buffer. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// use sample::rms::Rms; + /// use dasp_ring_buffer as ring_buffer; + /// use dasp_rms::Rms; /// /// fn main() { /// let window = ring_buffer::Fixed::from([[0.0]; 4]); @@ -52,17 +53,15 @@ where Rms { frame: PhantomData, window: ring_buffer, - square_sum: Frame::equilibrium(), + square_sum: Frame::EQUILIBRIUM, } } /// Zeroes the square_sum and the buffer of the `window`. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// use sample::rms::Rms; + /// use dasp_ring_buffer as ring_buffer; + /// use dasp_rms::Rms; /// /// fn main() { /// let window = ring_buffer::Fixed::from([[0.0]; 4]); @@ -78,18 +77,16 @@ where S: ring_buffer::SliceMut, { for sample_square in self.window.iter_mut() { - *sample_square = Frame::equilibrium(); + *sample_square = Frame::EQUILIBRIUM; } - self.square_sum = Frame::equilibrium(); + self.square_sum = Frame::EQUILIBRIUM; } /// The length of the window as a number of frames. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// use sample::rms::Rms; + /// use dasp_ring_buffer as ring_buffer; + /// use dasp_rms::Rms; /// /// fn main() { /// let window = ring_buffer::Fixed::from([[0.0]; 4]); @@ -114,10 +111,8 @@ where /// This method uses `Rms::next_squared` internally and then calculates the square root. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// use sample::rms::Rms; + /// use dasp_ring_buffer as ring_buffer; + /// use dasp_rms::Rms; /// /// fn main() { /// let window = ring_buffer::Fixed::from([[0.0]; 4]); @@ -148,35 +143,35 @@ where // Push back the new frame_square. let removed_frame_square = self.window.push(new_frame_square); // Add the new frame square and subtract the removed frame square. - self.square_sum = self.square_sum.add_amp(new_frame_square).zip_map( - removed_frame_square, - |s, r| { - let diff = s - r; - // Don't let floating point rounding errors put us below 0.0. - if diff < Sample::equilibrium() { - Sample::equilibrium() - } else { - diff - } - }, - ); + self.square_sum = + self.square_sum + .add_amp(new_frame_square) + .zip_map(removed_frame_square, |s, r| { + let diff = s - r; + // Don't let floating point rounding errors put us below 0.0. + if diff < Sample::EQUILIBRIUM { + Sample::EQUILIBRIUM + } else { + diff + } + }); self.calc_rms_squared() } /// Consumes the **Rms** and returns its inner ring buffer of squared frames along with a frame /// representing the sum of all frame squares contained within the ring buffer. pub fn into_parts(self) -> (ring_buffer::Fixed, S::Element) { - let Rms { window, square_sum, .. } = self; + let Rms { + window, square_sum, .. + } = self; (window, square_sum) } /// Calculates the RMS of all frames currently stored within the inner window. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// use sample::rms::Rms; + /// use dasp_ring_buffer as ring_buffer; + /// use dasp_rms::Rms; /// /// fn main() { /// let window = ring_buffer::Fixed::from([[0.0]; 4]); @@ -206,12 +201,10 @@ where S: fmt::Debug + ring_buffer::Slice, { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - f, - "Rms {{ frame: {:?}, window: {:?}, square_sum: {:?} }}", - &self.frame, - &self.window, - &self.square_sum - ) + f.debug_struct("Rms") + .field("frame", &self.frame) + .field("window", &self.window) + .field("square_sum", &self.square_sum) + .finish() } } diff --git a/dasp_sample/Cargo.toml b/dasp_sample/Cargo.toml new file mode 100644 index 00000000..1849abe7 --- /dev/null +++ b/dasp_sample/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "dasp_sample" +description = "An abstraction for audio PCM DSP samples, along with useful conversions and operations." +version = "0.11.0" +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["dsp", "bit-depth", "sample", "pcm", "audio"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/sample.git" +homepage = "https://github.com/rustaudio/sample" +edition = "2018" + +[features] +default = ["std"] +std = [] + +[package.metadata.docs.rs] +all-features = true diff --git a/src/conv.rs b/dasp_sample/src/conv.rs similarity index 55% rename from src/conv.rs rename to dasp_sample/src/conv.rs index cacfa50d..f1ed3f90 100644 --- a/src/conv.rs +++ b/dasp_sample/src/conv.rs @@ -13,13 +13,9 @@ //! Note that floating point conversions use the range -1.0 <= v < 1.0: //! `(1.0 as f64).to_sample::()` will overflow! -use {Box, Frame, Sample}; -use core; -use types::{I24, U24, I48, U48}; - +use crate::types::{I24, I48, U24, U48}; macro_rules! conversion_fn { - ($Rep:ty, $s:ident to_i8 { $body:expr }) => { #[inline] pub fn to_i8($s: $Rep) -> i8 { @@ -117,7 +113,6 @@ macro_rules! conversion_fn { $body } }; - } macro_rules! conversion_fns { @@ -137,7 +132,6 @@ macro_rules! conversions { }; } - conversions!(i8, i8 { s to_i16 { (s as i16) << 8 } s to_i24 { I24::new_unchecked((s as i32) << 16) } @@ -289,7 +283,7 @@ conversions!(i32, i32 { } s to_u32 { if s < 0 { - ((s + 2_147_483_647 + 1) as u32) + (s + 2_147_483_647 + 1) as u32 } else { s as u32 + 2_147_483_648 } @@ -570,7 +564,6 @@ conversions!(f64, f64 { s to_f32 { s as f32 } }); - /// Similar to the std `From` trait, but specifically for converting between sample types. /// /// We use this trait to be generic over the `Sample::to_sample` and `Sample::from_sample` methods. @@ -599,85 +592,85 @@ macro_rules! impl_from_sample { }; } -impl_from_sample!{i8, to_i8 from +impl_from_sample! {i8, to_i8 from {i16:i16} {I24:i24} {i32:i32} {I48:i48} {i64:i64} {u8:u8} {u16:u16} {U24:u24} {u32:u32} {U48:u48} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{i16, to_i16 from +impl_from_sample! {i16, to_i16 from {i8:i8} {I24:i24} {i32:i32} {I48:i48} {i64:i64} {u8:u8} {u16:u16} {U24:u24} {u32:u32} {U48:u48} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{I24, to_i24 from +impl_from_sample! {I24, to_i24 from {i8:i8} {i16:i16} {i32:i32} {I48:i48} {i64:i64} {u8:u8} {u16:u16} {U24:u24} {u32:u32} {U48:u48} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{i32, to_i32 from +impl_from_sample! {i32, to_i32 from {i8:i8} {i16:i16} {I24:i24} {I48:i48} {i64:i64} {u8:u8} {u16:u16} {U24:u24} {u32:u32} {U48:u48} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{I48, to_i48 from +impl_from_sample! {I48, to_i48 from {i8:i8} {i16:i16} {I24:i24} {i32:i32} {i64:i64} {u8:u8} {u16:u16} {U24:u24} {u32:u32} {U48:u48} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{i64, to_i64 from +impl_from_sample! {i64, to_i64 from {i8:i8} {i16:i16} {I24:i24} {i32:i32} {I48:i48} {u8:u8} {u16:u16} {U24:u24} {u32:u32} {U48:u48} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{u8, to_u8 from +impl_from_sample! {u8, to_u8 from {i8:i8} {i16:i16} {I24:i24} {i32:i32} {I48:i48} {i64:i64} {u16:u16} {U24:u24} {u32:u32} {U48:u48} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{u16, to_u16 from +impl_from_sample! {u16, to_u16 from {i8:i8} {i16:i16} {I24:i24} {i32:i32} {I48:i48} {i64:i64} {u8:u8} {U24:u24} {u32:u32} {U48:u48} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{U24, to_u24 from +impl_from_sample! {U24, to_u24 from {i8:i8} {i16:i16} {I24:i24} {i32:i32} {I48:i48} {i64:i64} {u8:u8} {u16:u16} {u32:u32} {U48:u48} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{u32, to_u32 from +impl_from_sample! {u32, to_u32 from {i8:i8} {i16:i16} {I24:i24} {i32:i32} {I48:i48} {i64:i64} {u8:u8} {u16:u16} {U24:u24} {U48:u48} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{U48, to_u48 from +impl_from_sample! {U48, to_u48 from {i8:i8} {i16:i16} {I24:i24} {i32:i32} {I48:i48} {i64:i64} {u8:u8} {u16:u16} {U24:u24} {u32:u32} {u64:u64} {f32:f32} {f64:f64} } -impl_from_sample!{u64, to_u64 from +impl_from_sample! {u64, to_u64 from {i8:i8} {i16:i16} {I24:i24} {i32:i32} {I48:i48} {i64:i64} {u8:u8} {u16:u16} {U24:u24} {u32:u32} {U48:u48} {f32:f32} {f64:f64} } -impl_from_sample!{f32, to_f32 from +impl_from_sample! {f32, to_f32 from {i8:i8} {i16:i16} {I24:i24} {i32:i32} {I48:i48} {i64:i64} {u8:u8} {u16:u16} {U24:u24} {u32:u32} {U48:u48} {u64:u64} {f64:f64} } -impl_from_sample!{f64, to_f64 from +impl_from_sample! {f64, to_f64 from {i8:i8} {i16:i16} {I24:i24} {i32:i32} {I48:i48} {i64:i64} {u8:u8} {u16:u16} {U24:u24} {u32:u32} {U48:u48} {u64:u64} {f32:f32} @@ -703,554 +696,4 @@ where /// Sample types which may be converted to and from some type `S`. pub trait Duplex: FromSample + ToSample {} -impl Duplex for T -where - T: FromSample + ToSample, -{ -} - - -///// DSP Slice Conversion Traits - - -/// For converting from a slice of `Sample`s to a slice of `Frame`s. -pub trait FromSampleSlice<'a, S>: Sized -where - S: Sample, -{ - fn from_sample_slice(slice: &'a [S]) -> Option; -} - -/// For converting from a mutable slice of `Sample`s to a mutable slice of `Frame`s. -pub trait FromSampleSliceMut<'a, S>: Sized -where - S: Sample, -{ - fn from_sample_slice_mut(slice: &'a mut [S]) -> Option; -} - -/// For converting a boxed slice of `Sample`s to a boxed slice of `Frame`s. -pub trait FromBoxedSampleSlice: Sized -where - S: Sample, -{ - fn from_boxed_sample_slice(slice: Box<[S]>) -> Option; -} - -/// For converting from a slice of `Frame`s to a slice of `Sample`s. -pub trait FromFrameSlice<'a, F> -where - F: Frame, -{ - fn from_frame_slice(slice: &'a [F]) -> Self; -} - -/// For converting from a slice of `Frame`s to a slice of `Sample`s. -pub trait FromFrameSliceMut<'a, F> -where - F: Frame, -{ - fn from_frame_slice_mut(slice: &'a mut [F]) -> Self; -} - -/// For converting from a boxed slice of `Frame`s to a boxed slice of `Sample`s. -pub trait FromBoxedFrameSlice -where - F: Frame, -{ - fn from_boxed_frame_slice(slice: Box<[F]>) -> Self; -} - -/// For converting from a slice of `Frame`s to a slice of `Sample`s. -pub trait ToSampleSlice<'a, S> -where - S: Sample, -{ - fn to_sample_slice(self) -> &'a [S]; -} - -/// For converting from a mutable slice of `Frame`s to a mutable slice of `Sample`s. -pub trait ToSampleSliceMut<'a, S> -where - S: Sample, -{ - fn to_sample_slice_mut(self) -> &'a mut [S]; -} - -/// For converting from a boxed slice of `Frame`s to a boxed slice of `Sample`s. -pub trait ToBoxedSampleSlice -where - S: Sample, -{ - fn to_boxed_sample_slice(self) -> Box<[S]>; -} - -/// For converting from a slice of `Sample`s to a slice of `Frame`s. -pub trait ToFrameSlice<'a, F> -where - F: Frame, -{ - fn to_frame_slice(self) -> Option<&'a [F]>; -} - -/// For converting from a mutable slice of `Sample`s to a mutable slice of `Frame`s. -pub trait ToFrameSliceMut<'a, F> -where - F: Frame, -{ - fn to_frame_slice_mut(self) -> Option<&'a mut [F]>; -} - -/// For converting from a boxed slice of `Sample`s to a boxed slice of `Frame`s. -pub trait ToBoxedFrameSlice -where - F: Frame, -{ - fn to_boxed_frame_slice(self) -> Option>; -} - - -///// DSP Slice Conversion Trait Implementations - - -impl<'a, S> FromSampleSlice<'a, S> for &'a [S] -where - S: Sample, -{ - #[inline] - fn from_sample_slice(slice: &'a [S]) -> Option { - Some(slice) - } -} - -impl<'a, S> FromSampleSliceMut<'a, S> for &'a mut [S] -where - S: Sample, -{ - #[inline] - fn from_sample_slice_mut(slice: &'a mut [S]) -> Option { - Some(slice) - } -} - -impl FromBoxedSampleSlice for Box<[S]> -where - S: Sample, -{ - #[inline] - fn from_boxed_sample_slice(slice: Box<[S]>) -> Option { - Some(slice) - } -} - -impl<'a, F> FromFrameSlice<'a, F> for &'a [F] -where - F: Frame, -{ - #[inline] - fn from_frame_slice(slice: &'a [F]) -> Self { - slice - } -} - -impl<'a, F> FromFrameSliceMut<'a, F> for &'a mut [F] -where - F: Frame, -{ - #[inline] - fn from_frame_slice_mut(slice: &'a mut [F]) -> Self { - slice - } -} - -impl FromBoxedFrameSlice for Box<[F]> -where - F: Frame, -{ - #[inline] - fn from_boxed_frame_slice(slice: Box<[F]>) -> Self { - slice - } -} - -impl<'a, S> ToSampleSlice<'a, S> for &'a [S] -where - S: Sample, -{ - #[inline] - fn to_sample_slice(self) -> &'a [S] { - self - } -} - -impl<'a, S> ToSampleSliceMut<'a, S> for &'a mut [S] -where - S: Sample, -{ - #[inline] - fn to_sample_slice_mut(self) -> &'a mut [S] { - self - } -} - -impl ToBoxedSampleSlice for Box<[S]> -where - S: Sample, -{ - #[inline] - fn to_boxed_sample_slice(self) -> Box<[S]> { - self - } -} - -impl<'a, F> ToFrameSlice<'a, F> for &'a [F] -where - F: Frame, -{ - #[inline] - fn to_frame_slice(self) -> Option<&'a [F]> { - Some(self) - } -} - -impl<'a, F> ToFrameSliceMut<'a, F> for &'a mut [F] -where - F: Frame, -{ - #[inline] - fn to_frame_slice_mut(self) -> Option<&'a mut [F]> { - Some(self) - } -} - -impl ToBoxedFrameSlice for Box<[F]> -where - F: Frame, -{ - #[inline] - fn to_boxed_frame_slice(self) -> Option> { - Some(self) - } -} - -/// A macro for implementing all audio slice conversion traits for each fixed-size array. -macro_rules! impl_from_slice_conversions { - ($($N:expr)*) => { - $( - - impl<'a, S> FromSampleSlice<'a, S> for &'a [[S; $N]] - where S: Sample, - [S; $N]: Frame, - { - #[inline] - fn from_sample_slice(slice: &'a [S]) -> Option { - let len = slice.len(); - if len % $N == 0 { - let new_len = len / $N; - let ptr = slice.as_ptr() as *const _; - let new_slice = unsafe { - core::slice::from_raw_parts(ptr, new_len) - }; - Some(new_slice) - } else { - None - } - } - } - - impl<'a, S> FromSampleSliceMut<'a, S> for &'a mut [[S; $N]] - where S: Sample, - [S; $N]: Frame, - { - #[inline] - fn from_sample_slice_mut(slice: &'a mut [S]) -> Option { - let len = slice.len(); - if len % $N == 0 { - let new_len = len / $N; - let ptr = slice.as_ptr() as *mut _; - let new_slice = unsafe { - core::slice::from_raw_parts_mut(ptr, new_len) - }; - Some(new_slice) - } else { - None - } - } - } - - impl FromBoxedSampleSlice for Box<[[S; $N]]> - where S: Sample, - [S; $N]: Frame, - { - #[inline] - fn from_boxed_sample_slice(mut slice: Box<[S]>) -> Option { - - // First, we need a raw pointer to the slice and to make sure that the `Box` is - // forgotten so that our slice does not get deallocated. - let len = slice.len(); - let slice_ptr = &mut slice as &mut [S] as *mut [S]; - core::mem::forget(slice); - let sample_slice = unsafe { - core::slice::from_raw_parts_mut((*slice_ptr).as_mut_ptr(), len) - }; - - // Convert to our frame slice if possible. - let frame_slice = match <&mut [[S; $N]]>::from_sample_slice_mut(sample_slice) { - Some(slice) => slice, - None => return None, - }; - let ptr = frame_slice as *mut [[S; $N]]; - - // Take ownership over the slice again before returning it. - let new_slice = unsafe { - Box::from_raw(ptr) - }; - - Some(new_slice) - } - } - - impl<'a, S> FromFrameSlice<'a, [S; $N]> for &'a [S] - where [S; $N]: Frame, - { - #[inline] - fn from_frame_slice(slice: &'a [[S; $N]]) -> Self { - let new_len = slice.len() * $N; - let ptr = slice.as_ptr() as *const _; - unsafe { - core::slice::from_raw_parts(ptr, new_len) - } - } - } - - impl<'a, S> FromFrameSliceMut<'a, [S; $N]> for &'a mut [S] - where [S; $N]: Frame, - { - #[inline] - fn from_frame_slice_mut(slice: &'a mut [[S; $N]]) -> Self { - let new_len = slice.len() * $N; - let ptr = slice.as_ptr() as *mut _; - unsafe { - core::slice::from_raw_parts_mut(ptr, new_len) - } - } - } - - impl FromBoxedFrameSlice<[S; $N]> for Box<[S]> - where [S; $N]: Frame, - { - #[inline] - fn from_boxed_frame_slice(mut slice: Box<[[S; $N]]>) -> Self { - let new_len = slice.len() * $N; - let frame_slice_ptr = &mut slice as &mut [[S; $N]] as *mut [[S; $N]]; - core::mem::forget(slice); - let sample_slice_ptr = frame_slice_ptr as *mut [S]; - unsafe { - let ptr = (*sample_slice_ptr).as_mut_ptr(); - let sample_slice = core::slice::from_raw_parts_mut(ptr, new_len); - Box::from_raw(sample_slice as *mut _) - } - } - } - - impl<'a, S> ToSampleSlice<'a, S> for &'a [[S; $N]] - where S: Sample, - { - #[inline] - fn to_sample_slice(self) -> &'a [S] { - FromFrameSlice::from_frame_slice(self) - } - } - - impl<'a, S> ToSampleSliceMut<'a, S> for &'a mut [[S; $N]] - where S: Sample, - { - #[inline] - fn to_sample_slice_mut(self) -> &'a mut [S] { - FromFrameSliceMut::from_frame_slice_mut(self) - } - } - - impl ToBoxedSampleSlice for Box<[[S; $N]]> - where S: Sample, - { - #[inline] - fn to_boxed_sample_slice(self) -> Box<[S]> { - FromBoxedFrameSlice::from_boxed_frame_slice(self) - } - } - - impl<'a, S> ToFrameSlice<'a, [S; $N]> for &'a [S] - where S: Sample, - [S; $N]: Frame, - { - #[inline] - fn to_frame_slice(self) -> Option<&'a [[S; $N]]> { - FromSampleSlice::from_sample_slice(self) - } - } - - impl<'a, S> ToFrameSliceMut<'a, [S; $N]> for &'a mut [S] - where S: Sample, - [S; $N]: Frame, - { - #[inline] - fn to_frame_slice_mut(self) -> Option<&'a mut [[S; $N]]> { - FromSampleSliceMut::from_sample_slice_mut(self) - } - } - - impl ToBoxedFrameSlice<[S; $N]> for Box<[S]> - where S: Sample, - [S; $N]: Frame, - { - #[inline] - fn to_boxed_frame_slice(self) -> Option> { - FromBoxedSampleSlice::from_boxed_sample_slice(self) - } - } - - )* - }; -} - -impl_from_slice_conversions! { - 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 -} - - -///// Bi-Directional DSP Slice Conversion Traits - - -pub trait DuplexSampleSlice<'a, S> - : FromSampleSlice<'a, S> + ToSampleSlice<'a, S> -where - S: Sample -{ -} - -pub trait DuplexFrameSlice<'a, F>: FromFrameSlice<'a, F> + ToFrameSlice<'a, F> -where - F: Frame -{ -} - -pub trait DuplexSlice<'a, S, F> - : DuplexSampleSlice<'a, S> + DuplexFrameSlice<'a, F> -where - S: Sample, - F: Frame -{ -} - -pub trait DuplexSampleSliceMut<'a, S> - : FromSampleSliceMut<'a, S> + ToSampleSliceMut<'a, S> -where - S: Sample -{ -} - -pub trait DuplexFrameSliceMut<'a, F> - : FromFrameSliceMut<'a, F> + ToFrameSliceMut<'a, F> -where - F: Frame -{ -} - -pub trait DuplexSliceMut<'a, S, F> - : DuplexSampleSliceMut<'a, S> + DuplexFrameSliceMut<'a, F> -where - S: Sample, - F: Frame -{ -} - -pub trait DuplexBoxedSampleSlice - : FromBoxedSampleSlice + ToBoxedSampleSlice -where - S: Sample -{ -} - -pub trait DuplexBoxedFrameSlice - : FromBoxedFrameSlice + ToBoxedFrameSlice -where - F: Frame -{ -} - -pub trait DuplexBoxedSlice - : DuplexBoxedSampleSlice + DuplexBoxedFrameSlice -where - S: Sample, - F: Frame -{ -} - -///// Bi-Directional DSP Slice Conversion Trait Implementations - - -impl<'a, S, T> DuplexSampleSlice<'a, S> for T -where - S: Sample, - T: FromSampleSlice<'a, S> + ToSampleSlice<'a, S>, -{ -} - -impl<'a, F, T> DuplexFrameSlice<'a, F> for T -where - F: Frame, - T: FromFrameSlice<'a, F> + ToFrameSlice<'a, F>, -{ -} - -impl<'a, S, F, T> DuplexSlice<'a, S, F> for T -where - S: Sample, - F: Frame, - T: DuplexSampleSlice<'a, S> + DuplexFrameSlice<'a, F>, -{ -} - -impl<'a, S, T> DuplexSampleSliceMut<'a, S> for T -where - S: Sample, - T: FromSampleSliceMut<'a, S> + ToSampleSliceMut<'a, S> {} - -impl<'a, F, T> DuplexFrameSliceMut<'a, F> for T -where - F: Frame, - T: FromFrameSliceMut<'a, F> + ToFrameSliceMut<'a, F>, -{ -} - -impl<'a, S, F, T> DuplexSliceMut<'a, S, F> for T -where - S: Sample, - F: Frame, - T: DuplexSampleSliceMut<'a, S> - + DuplexFrameSliceMut<'a, F>, -{ -} - -impl DuplexBoxedSampleSlice for T -where - S: Sample, - T: FromBoxedSampleSlice + ToBoxedSampleSlice, -{ -} - -impl DuplexBoxedFrameSlice for T -where - F: Frame, - T: FromBoxedFrameSlice + ToBoxedFrameSlice, -{ -} - -impl DuplexBoxedSlice for T -where - S: Sample, - F: Frame, - T: DuplexBoxedSampleSlice + DuplexBoxedFrameSlice, -{ -} +impl Duplex for T where T: FromSample + ToSample {} diff --git a/src/lib.rs b/dasp_sample/src/lib.rs similarity index 55% rename from src/lib.rs rename to dasp_sample/src/lib.rs index 6142892e..ea25acd8 100644 --- a/src/lib.rs +++ b/dasp_sample/src/lib.rs @@ -1,149 +1,21 @@ -//! A crate of fundamentals for audio PCM DSP. +//! Use the [**Sample**](./trait.Sample.html) trait to remain generic over sample types, easily +//! access sample type conversions, apply basic audio operations and more. //! -//! - Use the [**Sample** trait](./trait.Sample.html) to remain generic across bit-depth. -//! - Use the [**Frame** trait](./frame/trait.Frame.html) to remain generic over channel layout. -//! - Use the [**Signal** trait](./signal/trait.Signal.html) for working with **Iterators** that yield **Frames**. -//! - Use the [**slice** module](./slice/index.html) for working with slices of **Samples** and **Frames**. -//! - See the [**conv** module](./conv/index.html) for fast conversions between slices, frames and samples. -//! - See the [**types** module](./types/index.html) for provided custom sample types. -//! - See the [**interpolate** module](./interpolate/index.html) for sample rate conversion and scaling. -//! - See the [**ring_buffer** module](./ring_buffer/index.html) for fast FIFO queue options. +//! The **Sample** trait is the core abstraction throughout dasp on which most other abstractions +//! are based. -#![recursion_limit="512"] #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc, core_intrinsics))] - -#[cfg(feature = "std")] -extern crate core; +#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #[cfg(not(feature = "std"))] extern crate alloc; -#[cfg(not(feature = "std"))] -type BTreeMap = alloc::collections::btree_map::BTreeMap; -#[cfg(feature = "std")] -type BTreeMap = std::collections::BTreeMap; - -#[cfg(not(feature = "std"))] -type Vec = alloc::vec::Vec; -#[cfg(feature = "std")] -#[allow(dead_code)] -type Vec = std::vec::Vec; - -#[cfg(not(feature = "std"))] -type VecDeque = alloc::collections::vec_deque::VecDeque; -#[cfg(feature = "std")] -type VecDeque = std::collections::vec_deque::VecDeque; - -#[cfg(not(feature = "std"))] -pub type Box = alloc::boxed::Box; -#[cfg(feature = "std")] -pub type Box = std::boxed::Box; - -#[cfg(not(feature = "std"))] -type Rc = alloc::rc::Rc; -#[cfg(feature = "std")] -type Rc = std::rc::Rc; - -pub use conv::{FromSample, ToSample, Duplex, FromSampleSlice, ToSampleSlice, DuplexSampleSlice, - FromSampleSliceMut, ToSampleSliceMut, DuplexSampleSliceMut, FromBoxedSampleSlice, - ToBoxedSampleSlice, DuplexBoxedSampleSlice, FromFrameSlice, ToFrameSlice, - DuplexFrameSlice, FromFrameSliceMut, ToFrameSliceMut, DuplexFrameSliceMut, - FromBoxedFrameSlice, ToBoxedFrameSlice, DuplexBoxedFrameSlice, DuplexSlice, - DuplexSliceMut, DuplexBoxedSlice}; -pub use frame::Frame; -pub use signal::Signal; -pub use types::{I24, U24, I48, U48}; +pub use conv::{Duplex, FromSample, ToSample}; +pub use types::{I24, I48, U24, U48}; -pub mod slice; pub mod conv; -pub mod envelope; -pub mod frame; -pub mod peak; -pub mod ring_buffer; -pub mod rms; -pub mod signal; +mod ops; pub mod types; -pub mod window; -pub mod interpolate; - -mod ops { - pub mod f32 { - #[allow(unused_imports)] - use core; - - #[cfg(not(feature = "std"))] - pub fn sqrt(x: f32) -> f32 { - unsafe { core::intrinsics::sqrtf32(x) } - } - #[cfg(feature = "std")] - pub fn sqrt(x: f32) -> f32 { - x.sqrt() - } - - #[cfg(feature = "std")] - pub fn powf32(a: f32, b: f32) -> f32 { - a.powf(b) - } - #[cfg(not(feature = "std"))] - pub fn powf32(a: f32, b: f32) -> f32 { - unsafe { core::intrinsics::powf32(a, b) } - } - } - - pub mod f64 { - #[allow(unused_imports)] - use core; - - #[cfg(not(feature = "std"))] - pub fn floor(x: f64) -> f64 { - unsafe { core::intrinsics::floorf64(x) } - } - #[cfg(feature = "std")] - pub fn floor(x: f64) -> f64 { - x.floor() - } - - #[cfg(not(feature = "std"))] - #[allow(dead_code)] - pub fn ceil(x: f64) -> f64 { - unsafe { core::intrinsics::ceilf64(x) } - } - #[cfg(feature = "std")] - #[allow(dead_code)] - pub fn ceil(x: f64) -> f64 { - x.ceil() - } - - #[cfg(not(feature = "std"))] - pub fn sin(x: f64) -> f64 { - unsafe { core::intrinsics::sinf64(x) } - } - #[cfg(feature = "std")] - pub fn sin(x: f64) -> f64 { - x.sin() - } - - #[cfg(not(feature = "std"))] - pub fn cos(x: f64) -> f64 { - unsafe { core::intrinsics::cosf64(x) } - } - #[cfg(feature = "std")] - pub fn cos(x: f64) -> f64 { - x.cos() - } - - #[cfg(not(feature = "std"))] - pub fn sqrt(x: f64) -> f64 { - unsafe { core::intrinsics::sqrtf64(x) } - } - #[cfg(feature = "std")] - pub fn sqrt(x: f64) -> f64 { - x.sqrt() - } - } -} - /// A trait for working generically across different **Sample** format types. /// @@ -154,16 +26,14 @@ mod ops { /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{I24, Sample}; +/// use dasp_sample::{I24, Sample}; /// /// fn main() { /// assert_eq!((-1.0).to_sample::(), 0); /// assert_eq!(0.0.to_sample::(), 128); /// assert_eq!(0i32.to_sample::(), 2_147_483_648); /// assert_eq!(I24::new(0).unwrap(), Sample::from_sample(0.0)); -/// assert_eq!(0.0, Sample::equilibrium()); +/// assert_eq!(0.0, Sample::EQUILIBRIUM); /// } /// ``` pub trait Sample: Copy + Clone + PartialOrd + PartialEq { @@ -202,20 +72,18 @@ pub trait Sample: Copy + Clone + PartialOrd + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Sample; + /// use dasp_sample::Sample; /// /// fn main() { - /// assert_eq!(0.0, f32::equilibrium()); - /// assert_eq!(0, i32::equilibrium()); - /// assert_eq!(128, u8::equilibrium()); - /// assert_eq!(32_768_u16, Sample::equilibrium()); + /// assert_eq!(0.0, f32::EQUILIBRIUM); + /// assert_eq!(0, i32::EQUILIBRIUM); + /// assert_eq!(128, u8::EQUILIBRIUM); + /// assert_eq!(32_768_u16, Sample::EQUILIBRIUM); /// } /// ``` /// /// **Note:** This will likely be changed to an "associated const" if the feature lands. - fn equilibrium() -> Self; + const EQUILIBRIUM: Self; /// The multiplicative identity of the signal. /// @@ -227,21 +95,16 @@ pub trait Sample: Copy + Clone + PartialOrd + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{Sample, U48}; + /// use dasp_sample::{Sample, U48}; /// /// fn main() { - /// assert_eq!(1.0, f32::identity()); - /// assert_eq!(1.0, i8::identity()); - /// assert_eq!(1.0, u8::identity()); - /// assert_eq!(1.0, U48::identity()); + /// assert_eq!(1.0, f32::IDENTITY); + /// assert_eq!(1.0, i8::IDENTITY); + /// assert_eq!(1.0, u8::IDENTITY); + /// assert_eq!(1.0, U48::IDENTITY); /// } /// ``` - #[inline] - fn identity() -> Self::Float { - ::identity() - } + const IDENTITY: Self::Float = ::IDENTITY; /// Convert `self` to any type that implements `FromSample`. /// @@ -250,9 +113,7 @@ pub trait Sample: Copy + Clone + PartialOrd + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Sample; + /// use dasp_sample::Sample; /// /// fn main() { /// assert_eq!(0.0.to_sample::(), 0); @@ -275,9 +136,7 @@ pub trait Sample: Copy + Clone + PartialOrd + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{Sample, I24}; + /// use dasp_sample::{Sample, I24}; /// /// fn main() { /// assert_eq!(f32::from_sample(128_u8), 0.0); @@ -285,6 +144,7 @@ pub trait Sample: Copy + Clone + PartialOrd + PartialEq { /// assert_eq!(I24::from_sample(0.0), I24::new(0).unwrap()); /// } /// ``` + #[inline] fn from_sample(s: S) -> Self where @@ -301,9 +161,7 @@ pub trait Sample: Copy + Clone + PartialOrd + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Sample; + /// use dasp_sample::Sample; /// /// fn main() { /// assert_eq!(128_u8.to_signed_sample(), 0i8); @@ -321,9 +179,7 @@ pub trait Sample: Copy + Clone + PartialOrd + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Sample; + /// use dasp_sample::Sample; /// /// fn main() { /// assert_eq!(128_u8.to_float_sample(), 0.0); @@ -342,9 +198,7 @@ pub trait Sample: Copy + Clone + PartialOrd + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Sample; + /// use dasp_sample::Sample; /// /// fn main() { /// assert_eq!(0.25.add_amp(0.5), 0.75); @@ -362,7 +216,7 @@ pub trait Sample: Copy + Clone + PartialOrd + PartialEq { /// - `amp` > 1.0 amplifies the sample. /// - `amp` < 1.0 attenuates the sample. /// - `amp` == 1.0 yields the same sample. - /// - `amp` == 0.0 yields the `Sample::equilibrium`. + /// - `amp` == 0.0 yields the `Sample::EQUILIBRIUM`. /// /// `Self` will be converted to `Self::Float`, the multiplication will occur and then the /// result will be converted back to `Self`. These conversions allow us to correctly handle the @@ -371,9 +225,7 @@ pub trait Sample: Copy + Clone + PartialOrd + PartialEq { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::Sample; + /// use dasp_sample::Sample; /// /// fn main() { /// assert_eq!(64_i8.mul_amp(0.5), 32); @@ -393,49 +245,46 @@ macro_rules! impl_sample { ($($T:ty: Signed: $Addition:ty, Float: $Modulation:ty, - equilibrium: $equilibrium:expr),*) => + EQUILIBRIUM: $EQUILIBRIUM:expr),*) => { $( impl Sample for $T { type Signed = $Addition; type Float = $Modulation; - #[inline] - fn equilibrium() -> Self { - $equilibrium - } + const EQUILIBRIUM: Self = $EQUILIBRIUM; } )* } } // Expands to `Sample` implementations for all of the following types. -impl_sample!{ - i8: Signed: i8, Float: f32, equilibrium: 0, - i16: Signed: i16, Float: f32, equilibrium: 0, - I24: Signed: I24, Float: f32, equilibrium: types::i24::EQUILIBRIUM, - i32: Signed: i32, Float: f32, equilibrium: 0, - I48: Signed: I48, Float: f64, equilibrium: types::i48::EQUILIBRIUM, - i64: Signed: i64, Float: f64, equilibrium: 0, - u8: Signed: i8, Float: f32, equilibrium: 128, - u16: Signed: i16, Float: f32, equilibrium: 32_768, - U24: Signed: i32, Float: f32, equilibrium: types::u24::EQUILIBRIUM, - u32: Signed: i32, Float: f32, equilibrium: 2_147_483_648, - U48: Signed: i64, Float: f64, equilibrium: types::u48::EQUILIBRIUM, - u64: Signed: i64, Float: f64, equilibrium: 9_223_372_036_854_775_808, - f32: Signed: f32, Float: f32, equilibrium: 0.0, - f64: Signed: f64, Float: f64, equilibrium: 0.0 +impl_sample! { + i8: Signed: i8, Float: f32, EQUILIBRIUM: 0, + i16: Signed: i16, Float: f32, EQUILIBRIUM: 0, + I24: Signed: I24, Float: f32, EQUILIBRIUM: types::i24::EQUILIBRIUM, + i32: Signed: i32, Float: f32, EQUILIBRIUM: 0, + I48: Signed: I48, Float: f64, EQUILIBRIUM: types::i48::EQUILIBRIUM, + i64: Signed: i64, Float: f64, EQUILIBRIUM: 0, + u8: Signed: i8, Float: f32, EQUILIBRIUM: 128, + u16: Signed: i16, Float: f32, EQUILIBRIUM: 32_768, + U24: Signed: i32, Float: f32, EQUILIBRIUM: types::u24::EQUILIBRIUM, + u32: Signed: i32, Float: f32, EQUILIBRIUM: 2_147_483_648, + U48: Signed: i64, Float: f64, EQUILIBRIUM: types::u48::EQUILIBRIUM, + u64: Signed: i64, Float: f64, EQUILIBRIUM: 9_223_372_036_854_775_808, + f32: Signed: f32, Float: f32, EQUILIBRIUM: 0.0, + f64: Signed: f64, Float: f64, EQUILIBRIUM: 0.0 } - /// Integral and floating-point **Sample** format types whose equilibrium is at 0. /// /// **Sample**s often need to be converted to some mutual **SignedSample** type for signal /// addition. -pub trait SignedSample - : Sample +pub trait SignedSample: + Sample + core::ops::Add + core::ops::Sub - + core::ops::Neg { + + core::ops::Neg +{ } macro_rules! impl_signed_sample { ($($T:ty)*) => { $( impl SignedSample for $T {} )* } } impl_signed_sample!(i8 i16 I24 i32 I48 i64 f32 f64); @@ -444,24 +293,22 @@ impl_signed_sample!(i8 i16 I24 i32 I48 i64 f32 f64); /// /// **Sample**s often need to be converted to some mutual **FloatSample** type for signal scaling /// and modulation. -pub trait FloatSample - : Sample +pub trait FloatSample: + Sample + SignedSample + core::ops::Mul + core::ops::Div + Duplex - + Duplex { + + Duplex +{ /// Represents the multiplicative identity of the floating point signal. - fn identity() -> Self; + const IDENTITY: Self; /// Calculate the square root of `Self`. fn sample_sqrt(self) -> Self; } impl FloatSample for f32 { - #[inline] - fn identity() -> Self { - 1.0 - } + const IDENTITY: Self = 1.0; #[inline] fn sample_sqrt(self) -> Self { ops::f32::sqrt(self) @@ -469,10 +316,7 @@ impl FloatSample for f32 { } impl FloatSample for f64 { - #[inline] - fn identity() -> Self { - 1.0 - } + const IDENTITY: Self = 1.0; #[inline] fn sample_sqrt(self) -> Self { ops::f64::sqrt(self) diff --git a/dasp_sample/src/ops.rs b/dasp_sample/src/ops.rs new file mode 100644 index 00000000..dbafe7ac --- /dev/null +++ b/dasp_sample/src/ops.rs @@ -0,0 +1,27 @@ +pub mod f32 { + #[allow(unused_imports)] + use core; + + #[cfg(not(feature = "std"))] + pub fn sqrt(x: f32) -> f32 { + unsafe { core::intrinsics::sqrtf32(x) } + } + #[cfg(feature = "std")] + pub fn sqrt(x: f32) -> f32 { + x.sqrt() + } +} + +pub mod f64 { + #[allow(unused_imports)] + use core; + + #[cfg(not(feature = "std"))] + pub fn sqrt(x: f64) -> f64 { + unsafe { core::intrinsics::sqrtf64(x) } + } + #[cfg(feature = "std")] + pub fn sqrt(x: f64) -> f64 { + x.sqrt() + } +} diff --git a/src/types.rs b/dasp_sample/src/types.rs similarity index 99% rename from src/types.rs rename to dasp_sample/src/types.rs index 79369ac8..6c48b821 100644 --- a/src/types.rs +++ b/dasp_sample/src/types.rs @@ -1,15 +1,14 @@ //! A collection of custom, non-std **Sample** types. pub use self::i11::I11; -pub use self::u11::U11; pub use self::i20::I20; -pub use self::u20::U20; pub use self::i24::I24; -pub use self::u24::U24; pub use self::i48::I48; +pub use self::u11::U11; +pub use self::u20::U20; +pub use self::u24::U24; pub use self::u48::U48; - macro_rules! impl_from { ($T:ident: $Rep:ident from {$U:ident : $URep:ty}) => { impl From<$U> for $T { @@ -241,23 +240,12 @@ pub mod i11 { impl_neg!(I11); } -pub mod u11 { - new_sample_type!(U11: i16, eq: 1024, min: 0, max: 2047, total: 2048, - from: u8); - impl_neg!(U11); -} - pub mod i20 { use super::{I11, U11}; new_sample_type!(I20: i32, eq: 0, min: -524_288, max: 524_287, total: 1_048_576, from: i8, {I11:i16}, i16, u8, {U11:i16}, u16); } -pub mod u20 { - new_sample_type!(U20: i32, eq: 524_288, min: 0, max: 1_048_575, total: 1_048_576, - from: u8, u16); -} - pub mod i24 { use super::{I20, U20}; new_sample_type!(I24: i32, eq: 0, min: -8_388_608, max: 8_388_607, total: 16_777_216, @@ -265,12 +253,6 @@ pub mod i24 { impl_neg!(I24); } -pub mod u24 { - use super::U20; - new_sample_type!(U24: i32, eq: 8_388_608, min: 0, max: 16_777_215, total: 16_777_216, - from: u8, u16, {U20:i32}); -} - pub mod i48 { use super::{I20, I24, U20, U24}; new_sample_type!(I48: i64, eq: 0, min: -140_737_488_355_328, max: 140_737_488_355_327, total: 281_474_976_710_656, @@ -278,6 +260,23 @@ pub mod i48 { impl_neg!(I48); } +pub mod u11 { + new_sample_type!(U11: i16, eq: 1024, min: 0, max: 2047, total: 2048, + from: u8); + impl_neg!(U11); +} + +pub mod u20 { + new_sample_type!(U20: i32, eq: 524_288, min: 0, max: 1_048_575, total: 1_048_576, + from: u8, u16); +} + +pub mod u24 { + use super::U20; + new_sample_type!(U24: i32, eq: 8_388_608, min: 0, max: 16_777_215, total: 16_777_216, + from: u8, u16, {U20:i32}); +} + pub mod u48 { use super::{U20, U24}; new_sample_type!(U48: i64, eq: 140_737_488_355_328, min: 0, max: 281_474_976_710_655, total: 281_474_976_710_656, diff --git a/tests/conv.rs b/dasp_sample/tests/conv.rs similarity index 98% rename from tests/conv.rs rename to dasp_sample/tests/conv.rs index 25490a09..b442d246 100644 --- a/tests/conv.rs +++ b/dasp_sample/tests/conv.rs @@ -4,8 +4,6 @@ //! We assert that each sample type's minimum, maximum and centre are correctly converted to the //! min, max and centre of every other available sample type. -extern crate sample; - /// Expands to an `assert_eq` for each pre-conversion and post-conversion pair. /// /// Literals that must be wrapped by a custom sample type are wrapped using $T/$U::new_unchecked. @@ -20,7 +18,10 @@ macro_rules! conv_cmp { assert_eq!($fn_name($T::new_unchecked($pre_conv)), $post_conv); }; ($T:ident; $fn_name:ident: $U:ident, $pre_conv:expr, $post_conv:expr) => { - assert_eq!($fn_name($T::new_unchecked($pre_conv)), $U::new_unchecked($post_conv)); + assert_eq!( + $fn_name($T::new_unchecked($pre_conv)), + $U::new_unchecked($post_conv) + ); }; } @@ -273,21 +274,20 @@ macro_rules! test_fns { macro_rules! tests { ($T:ident { $($rest:tt)* }) => { pub mod $T { - use sample::conv::$T::*; - use sample::types::{I24, U24, I48, U48}; + use dasp_sample::conv::$T::*; + use dasp_sample::types::{I24, U24, I48, U48}; test_fns!($($rest)*); } }; ($T:ident: $mod_name:ident { $($rest:tt)* }) => { pub mod $mod_name { - use sample::conv::$mod_name::*; - use sample::types::{I24, U24, I48, U48}; + use dasp_sample::conv::$mod_name::*; + use dasp_sample::types::{I24, U24, I48, U48}; test_fns!($T: $($rest)*); } }; } - tests!(i8 { to_i16 { -128, -32_768; 0, 0; 127, 32_512; } to_i24 { -128, -8_388_608; 0, 0; 127, 8_323_072; } diff --git a/dasp_sample/tests/types.rs b/dasp_sample/tests/types.rs new file mode 100644 index 00000000..c8a9a642 --- /dev/null +++ b/dasp_sample/tests/types.rs @@ -0,0 +1,82 @@ +/// Expands to a unique module with a variety of tests for the given sample newtype. +/// +/// Tests include basic operations and over/underflow checks. +macro_rules! test_type { + ($T:ident, $mod_name:ident) => { + mod $mod_name { + #[test] + fn ops() { + use dasp_sample::types::$mod_name::$T; + assert_eq!( + $T::new(8).unwrap() + $T::new(12).unwrap(), + $T::new(20).unwrap() + ); + assert_eq!( + $T::new(12).unwrap() - $T::new(4).unwrap(), + $T::new(8).unwrap() + ); + assert_eq!( + $T::new(2).unwrap() * $T::new(2).unwrap(), + $T::new(4).unwrap() + ); + assert_eq!( + $T::new(3).unwrap() * $T::new(3).unwrap(), + $T::new(9).unwrap() + ); + assert_eq!( + $T::new(5).unwrap() * $T::new(10).unwrap(), + $T::new(50).unwrap() + ); + assert_eq!( + $T::new(16).unwrap() / $T::new(8).unwrap(), + $T::new(2).unwrap() + ); + assert_eq!( + $T::new(8).unwrap() % $T::new(3).unwrap(), + $T::new(2).unwrap() + ); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn add_panic_debug() { + use dasp_sample::types::$mod_name::{self, $T}; + let _ = $mod_name::MAX + $T::new(1).unwrap(); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn sub_panic_debug() { + use dasp_sample::types::$mod_name::{self, $T}; + let _ = $mod_name::MIN - $T::new(1).unwrap(); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn mul_panic_debug() { + use dasp_sample::types::$mod_name::{self, $T}; + let _ = $mod_name::MAX * $T::new(2).unwrap(); + } + + #[cfg(not(debug_assertions))] + #[test] + fn release_wrapping() { + use dasp_sample::types::$mod_name::{self, $T}; + assert_eq!($mod_name::MIN - $T::new(1).unwrap(), $mod_name::MAX); + assert_eq!($mod_name::MAX + $T::new(1).unwrap(), $mod_name::MIN); + } + } + }; +} + +test_type!(I11, i11); +test_type!(U11, u11); +test_type!(I20, i20); +test_type!(U20, u20); +test_type!(I24, i24); +test_type!(U24, u24); +test_type!(I48, i48); +test_type!(U48, u48); diff --git a/dasp_signal/Cargo.toml b/dasp_signal/Cargo.toml new file mode 100644 index 00000000..0af0374c --- /dev/null +++ b/dasp_signal/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "dasp_signal" +description = "An iterator-like API for audio PCM DSP streams." +version = "0.11.0" +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["dsp", "signal", "rate", "pcm", "audio"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/dasp.git" +homepage = "https://github.com/rustaudio/dasp" +edition = "2018" + +[dependencies] +dasp_envelope = { version = "0.11", path = "../dasp_envelope", default-features = false, optional = true } +dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } +dasp_interpolate = { version = "0.11", path = "../dasp_interpolate", default-features = false } +dasp_peak = { version = "0.11", path = "../dasp_peak", default-features = false } +dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false } +dasp_rms = { version = "0.11", path = "../dasp_rms", default-features = false, optional = true } +dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } +dasp_window = { version = "0.11", path = "../dasp_window", default-features = false, optional = true } + +[dev-dependencies] +dasp_envelope = { version = "0.11", path = "../dasp_envelope", default-features = false, features = ["peak"] } +dasp_interpolate = { version = "0.11", path = "../dasp_interpolate", default-features = false, features = ["floor", "linear", "sinc"] } +dasp_window = { version = "0.11", path = "../dasp_window", default-features = false, features = ["hanning"] } + +[features] +default = ["std"] +all = ["std", "all-no-std"] +all-no-std = [ + "boxed", + "bus", + "envelope", + "rms", + "window", + "window-hanning", + "window-rectangle", +] +std = [ + "dasp_envelope/std", + "dasp_frame/std", + "dasp_interpolate/std", + "dasp_peak/std", + "dasp_ring_buffer/std", + "dasp_rms/std", + "dasp_sample/std", + "dasp_window/std", +] +boxed = [] +bus = [] +envelope = ["dasp_envelope"] +rms = ["dasp_rms"] +window = ["dasp_window"] +window-hanning = ["dasp_window/hanning"] +window-rectangle = ["dasp_window/rectangle"] + +[package.metadata.docs.rs] +all-features = true diff --git a/dasp_signal/src/boxed.rs b/dasp_signal/src/boxed.rs new file mode 100644 index 00000000..2cbfa489 --- /dev/null +++ b/dasp_signal/src/boxed.rs @@ -0,0 +1,20 @@ +#[cfg(not(feature = "std"))] +type Box = alloc::boxed::Box; +#[cfg(feature = "std")] +type Box = std::boxed::Box; + +impl Signal for Box +where + S: Signal + ?Sized, +{ + type Frame = S::Frame; + #[inline] + fn next(&mut self) -> Self::Frame { + (**self).next() + } + + #[inline] + fn is_exhausted(&self) -> bool { + (**self).is_exhausted() + } +} diff --git a/dasp_signal/src/bus.rs b/dasp_signal/src/bus.rs new file mode 100644 index 00000000..50f12d7b --- /dev/null +++ b/dasp_signal/src/bus.rs @@ -0,0 +1,293 @@ +//! An extension to the **Signal** trait that enables multiple signal outputs. +//! +//! ### Required Features +//! +//! - When using `dasp_signal`, this item requires the **bus** feature to be enabled. +//! - When using `dasp`, this item requires the **signal-bus** feature to be enabled. + +use crate::{Rc, Signal}; + +#[cfg(not(feature = "std"))] +type BTreeMap = alloc::collections::btree_map::BTreeMap; +#[cfg(feature = "std")] +type BTreeMap = std::collections::BTreeMap; + +#[cfg(not(feature = "std"))] +type VecDeque = alloc::collections::vec_deque::VecDeque; +#[cfg(feature = "std")] +type VecDeque = std::collections::vec_deque::VecDeque; + +/// An extension to the **Signal** trait that enables multiple signal outputs. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. +pub trait SignalBus: Signal { + /// Moves the `Signal` into a `Bus` from which its output may be divided into multiple other + /// `Signal`s in the form of `Output`s. + /// + /// This method allows to create more complex directed acyclic graph structures that + /// incorporate concepts like sends, side-chaining, etc, rather than being restricted to tree + /// structures where signals can only ever be joined but never divided. + /// + /// Note: When using multiple `Output`s in this fashion, you will need to be sure to pull the + /// frames from each `Output` in sync (whether per frame or per buffer). This is because when + /// output A requests `Frame`s before output B, those frames must remain available for output + /// B and in turn must be stored in an intermediary ring buffer. + /// + /// # Example + /// + /// ```rust + /// use dasp_signal::{self as signal, Signal}; + /// use dasp_signal::bus::SignalBus; + /// + /// fn main() { + /// let frames = [[0.1], [0.2], [0.3], [0.4], [0.5], [0.6]]; + /// let signal = signal::from_iter(frames.iter().cloned()); + /// let bus = signal.bus(); + /// let mut a = bus.send(); + /// let mut b = bus.send(); + /// assert_eq!(a.by_ref().take(3).collect::>(), vec![[0.1], [0.2], [0.3]]); + /// assert_eq!(b.by_ref().take(3).collect::>(), vec![[0.1], [0.2], [0.3]]); + /// + /// let c = bus.send(); + /// assert_eq!(c.take(3).collect::>(), vec![[0.4], [0.5], [0.6]]); + /// assert_eq!(b.take(3).collect::>(), vec![[0.4], [0.5], [0.6]]); + /// assert_eq!(a.take(3).collect::>(), vec![[0.4], [0.5], [0.6]]); + /// } + /// ``` + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. + fn bus(self) -> Bus + where + Self: Sized, + { + Bus::new(self, BTreeMap::new()) + } +} + +/// The data shared between each `Output`. +struct SharedNode +where + S: Signal, +{ + signal: S, + // The buffer of frames that have not yet been consumed by all outputs. + buffer: VecDeque, + // The number of frames in `buffer` that have already been read for each output. + frames_read: BTreeMap, + // The next output key. + next_key: usize, +} + +/// A type which allows for `send`ing a single `Signal` to multiple outputs. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. +pub struct Bus +where + S: Signal, +{ + node: Rc>>, +} + +/// An output node to which some signal `S` is `Output`ing its frames. +/// +/// It may be more accurate to say that the `Output` "pull"s frames from the signal. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. +pub struct Output +where + S: Signal, +{ + key: usize, + node: Rc>>, +} + +impl Bus +where + S: Signal, +{ + fn new(signal: S, frames_read: BTreeMap) -> Self { + Bus { + node: Rc::new(core::cell::RefCell::new(SharedNode { + signal: signal, + buffer: VecDeque::new(), + frames_read: frames_read, + next_key: 0, + })), + } + } + + /// Produce a new Output node to which the signal `S` will output its frames. + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. + #[inline] + pub fn send(&self) -> Output { + let mut node = self.node.borrow_mut(); + + // Get the key and increment for the next output. + let key = node.next_key; + node.next_key = node.next_key.wrapping_add(1); + + // Insert the number of frames read by the new output. + let num_frames = node.buffer.len(); + node.frames_read.insert(key, num_frames); + + Output { + key: key, + node: self.node.clone(), + } + } +} + +impl SharedNode +where + S: Signal, +{ + // Requests the next frame for the `Output` at the given key. + // + // If there are no frames pending for the output, a new frame will be requested from the + // signal and appended to the ring buffer to be received by the other outputs. + fn next_frame(&mut self, key: usize) -> S::Frame { + let num_frames = self.buffer.len(); + let frames_read = self + .frames_read + .remove(&key) + .expect("no frames_read for Output"); + + let frame = if frames_read < num_frames { + self.buffer[frames_read] + } else { + let frame = self.signal.next(); + self.buffer.push_back(frame); + frame + }; + + // If the number of frames read by this output is the lowest, then we can pop the frame + // from the front. + let least_frames_read = !self + .frames_read + .values() + .any(|&other_frames_read| other_frames_read <= frames_read); + + // If this output had read the least number of frames, pop the front frame and decrement + // the frames read counters for each of the other outputs. + let new_frames_read = if least_frames_read { + self.buffer.pop_front(); + for other_frames_read in self.frames_read.values_mut() { + *other_frames_read -= 1; + } + frames_read + } else { + frames_read + 1 + }; + + self.frames_read.insert(key, new_frames_read); + + frame + } + + #[inline] + fn pending_frames(&self, key: usize) -> usize { + self.buffer.len() - self.frames_read[&key] + } + + // Drop the given output from the `Bus`. + // + // Called by the `Output::drop` implementation. + fn drop_output(&mut self, key: usize) { + self.frames_read.remove(&key); + let least_frames_read = self + .frames_read + .values() + .fold(self.buffer.len(), |a, &b| core::cmp::min(a, b)); + if least_frames_read > 0 { + for frames_read in self.frames_read.values_mut() { + *frames_read -= least_frames_read; + } + for _ in 0..least_frames_read { + self.buffer.pop_front(); + } + } + } +} + +impl Output +where + S: Signal, +{ + /// The number of frames that have been requested from the `Signal` `S` by some other `Output` + /// that have not yet been requested by this `Output`. + /// + /// This is useful when using an `Output` to "monitor" some signal, allowing the user to drain + /// only frames that have already been requested by some other `Output`. + /// + /// # Example + /// + /// ``` + /// use dasp_signal::{self as signal, Signal}; + /// use dasp_signal::bus::SignalBus; + /// + /// fn main() { + /// let frames = [[0.1], [0.2], [0.3]]; + /// let bus = signal::from_iter(frames.iter().cloned()).bus(); + /// let signal = bus.send(); + /// let mut monitor = bus.send(); + /// assert_eq!(signal.take(3).collect::>(), vec![[0.1], [0.2], [0.3]]); + /// assert_eq!(monitor.pending_frames(), 3); + /// assert_eq!(monitor.next(), [0.1]); + /// assert_eq!(monitor.pending_frames(), 2); + /// } + /// ``` + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. + #[inline] + pub fn pending_frames(&self) -> usize { + self.node.borrow().pending_frames(self.key) + } +} + +impl SignalBus for T where T: Signal {} + +impl Signal for Output +where + S: Signal, +{ + type Frame = S::Frame; + + #[inline] + fn next(&mut self) -> Self::Frame { + self.node.borrow_mut().next_frame(self.key) + } + + #[inline] + fn is_exhausted(&self) -> bool { + let node = self.node.borrow(); + node.pending_frames(self.key) == 0 && node.signal.is_exhausted() + } +} + +impl Drop for Output +where + S: Signal, +{ + fn drop(&mut self) { + self.node.borrow_mut().drop_output(self.key) + } +} diff --git a/dasp_signal/src/envelope.rs b/dasp_signal/src/envelope.rs new file mode 100644 index 00000000..5383beb9 --- /dev/null +++ b/dasp_signal/src/envelope.rs @@ -0,0 +1,127 @@ +//! An extension to the **Signal** trait that enables envelope detection. +//! +//! ### Required Features +//! +//! - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. +//! - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. + +use crate::Signal; +use dasp_envelope as envelope; + +/// An extension to the **Signal** trait that enables envelope detection. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. +pub trait SignalEnvelope: Signal { + /// An adaptor that detects and yields the envelope of the signal. + /// + /// # Example + /// + /// ``` + /// use dasp_envelope as envelope; + /// use dasp_signal::{self as signal, Signal}; + /// use dasp_signal::envelope::SignalEnvelope; + /// + /// fn main() { + /// let signal = signal::rate(4.0).const_hz(1.0).sine(); + /// let attack = 1.0; + /// let release = 1.0; + /// let detector = envelope::Detector::peak(attack, release); + /// let mut envelope = signal.detect_envelope(detector); + /// assert_eq!( + /// envelope.take(4).collect::>(), + /// vec![0.0, 0.6321205496788025, 0.23254416035257117, 0.7176687675647109] + /// ); + /// } + /// ``` + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. + fn detect_envelope( + self, + detector: envelope::Detector, + ) -> DetectEnvelope + where + Self: Sized, + D: envelope::Detect, + { + DetectEnvelope { + signal: self, + detector: detector, + } + } +} + +/// An adaptor that detects and yields the envelope of the signal. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. +#[derive(Clone)] +pub struct DetectEnvelope +where + S: Signal, + D: envelope::Detect, +{ + signal: S, + detector: envelope::Detector, +} + +impl DetectEnvelope +where + S: Signal, + D: envelope::Detect, +{ + /// Set the **Detector**'s attack time as a number of frames. + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. + pub fn set_attack_frames(&mut self, frames: f32) { + self.detector.set_attack_frames(frames); + } + + /// Set the **Detector**'s release time as a number of frames. + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. + pub fn set_release_frames(&mut self, frames: f32) { + self.detector.set_release_frames(frames); + } + + /// Consumes `Self` and returns the inner signal `S` and `Detector`. + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. + pub fn into_parts(self) -> (S, envelope::Detector) { + let DetectEnvelope { signal, detector } = self; + (signal, detector) + } +} + +impl Signal for DetectEnvelope +where + S: Signal, + D: envelope::Detect, +{ + type Frame = D::Output; + fn next(&mut self) -> Self::Frame { + self.detector.next(self.signal.next()) + } + + fn is_exhausted(&self) -> bool { + self.signal.is_exhausted() + } +} + +impl SignalEnvelope for T where T: Signal {} diff --git a/dasp_signal/src/interpolate.rs b/dasp_signal/src/interpolate.rs new file mode 100644 index 00000000..fd28540d --- /dev/null +++ b/dasp_signal/src/interpolate.rs @@ -0,0 +1,144 @@ +//! The [**Converter**](./struct.Converter.html) type for interpolating the rate of a signal. + +use crate::Signal; +use dasp_interpolate::Interpolator; + +/// A signal type that converts the rate at which frames are yielded from some source signal to +/// some target rate. +/// +/// Other names for `sample::interpolate::Converter` might include: +/// +/// - Sample rate converter. +/// - {Up/Down}sampler. +/// - Sample interpolater. +/// - Sample decimator. +#[derive(Clone)] +pub struct Converter +where + S: Signal, + I: Interpolator, +{ + source: S, + interpolator: I, + interpolation_value: f64, + source_to_target_ratio: f64, +} + +impl Converter +where + S: Signal, + I: Interpolator, +{ + /// Construct a new `Converter` from the source frames and the source and target sample rates + /// (in Hz). + #[inline] + pub fn from_hz_to_hz(source: S, interpolator: I, source_hz: f64, target_hz: f64) -> Self { + Self::scale_playback_hz(source, interpolator, source_hz / target_hz) + } + + /// Construct a new `Converter` from the source frames and the amount by which the current + /// ***playback*** **rate** (not sample rate) should be multiplied to reach the new playback + /// rate. + /// + /// For example, if our `source_frames` is a sine wave oscillating at a frequency of 2hz and + /// we wanted to convert it to a frequency of 3hz, the given `scale` should be `1.5`. + #[inline] + pub fn scale_playback_hz(source: S, interpolator: I, scale: f64) -> Self { + assert!( + scale > 0.0, + "We can't yield any frames at 0 times a second!" + ); + Converter { + source: source, + interpolator: interpolator, + interpolation_value: 0.0, + source_to_target_ratio: scale, + } + } + + /// Construct a new `Converter` from the source frames and the amount by which the current + /// ***sample*** **rate** (not playback rate) should be multiplied to reach the new sample + /// rate. + /// + /// If our `source_frames` are being sampled at a rate of 44_100hz and we want to + /// convert to a sample rate of 96_000hz, the given `scale` should be `96_000.0 / 44_100.0`. + /// + /// This is the same as calling `Converter::scale_playback_hz(source_frames, 1.0 / scale)`. + #[inline] + pub fn scale_sample_hz(source: S, interpolator: I, scale: f64) -> Self { + Self::scale_playback_hz(source, interpolator, 1.0 / scale) + } + + /// Update the `source_to_target_ratio` internally given the source and target hz. + /// + /// This method might be useful for changing the sample rate during playback. + #[inline] + pub fn set_hz_to_hz(&mut self, source_hz: f64, target_hz: f64) { + self.set_playback_hz_scale(source_hz / target_hz) + } + + /// Update the `source_to_target_ratio` internally given a new **playback rate** multiplier. + /// + /// This method is useful for dynamically changing rates. + #[inline] + pub fn set_playback_hz_scale(&mut self, scale: f64) { + self.source_to_target_ratio = scale; + } + + /// Update the `source_to_target_ratio` internally given a new **sample rate** multiplier. + /// + /// This method is useful for dynamically changing rates. + #[inline] + pub fn set_sample_hz_scale(&mut self, scale: f64) { + self.set_playback_hz_scale(1.0 / scale); + } + + /// Borrow the `source_frames` Interpolator from the `Converter`. + #[inline] + pub fn source(&self) -> &S { + &self.source + } + + /// Mutably borrow the `source_frames` Iterator from the `Converter`. + #[inline] + pub fn source_mut(&mut self) -> &mut S { + &mut self.source + } + + /// Drop `self` and return the internal `source_frames` Iterator. + #[inline] + pub fn into_source(self) -> S { + self.source + } +} + +impl Signal for Converter +where + S: Signal, + I: Interpolator, +{ + type Frame = S::Frame; + + fn next(&mut self) -> Self::Frame { + let Converter { + ref mut source, + ref mut interpolator, + ref mut interpolation_value, + source_to_target_ratio, + } = *self; + + // Advance frames + while *interpolation_value >= 1.0 { + interpolator.next_source_frame(source.next()); + *interpolation_value -= 1.0; + } + + let out = interpolator.interpolate(*interpolation_value); + *interpolation_value += source_to_target_ratio; + out + } + + fn is_exhausted(&self) -> bool { + self.source.is_exhausted() && self.interpolation_value >= 1.0 + } +} diff --git a/src/signal.rs b/dasp_signal/src/lib.rs similarity index 66% rename from src/signal.rs rename to dasp_signal/src/lib.rs index a40f1bfc..da36d9f2 100644 --- a/src/signal.rs +++ b/dasp_signal/src/lib.rs @@ -1,6 +1,6 @@ -//! Use the **Signal** trait for working with **Iterator**s that yield **Frame**s. To complement -//! the **Iterator** trait, **Signal** provides methods for adding, scaling, offsetting, -//! multiplying, clipping and generating frame iterators and more. +//! Use the [**Signal**](./trait.Signal.html) trait to abstract over infinite-iterator-like types +//! that yield **Frame**s. The **Signal** trait provides methods for adding, scaling, offsetting, +//! multiplying, clipping, generating frame iterators and more. //! //! You may also find a series of **Signal** source functions, including: //! @@ -19,20 +19,70 @@ //! //! Working with **Signal**s allows for easy, readable creation of rich and complex DSP graphs with //! a simple and familiar API. +//! +//! ### Optional Features +//! +//! - The **boxed** feature (or **signal-boxed** feature if using `dasp`) provides a **Signal** +//! implementation for `Box`. +//! - The **bus** feature (or **signal-bus** feature if using `dasp`) provides the +//! [**SignalBus**](./bus/trait.SignalBus.html) trait. +//! - The **envelope** feature (or **signal-envelope** feature if using `dasp`) provides the +//! [**SignalEnvelope**](./envelope/trait.SignalEnvelope.html) trait. +//! - The **rms** feature (or **signal-rms** feature if using `dasp`) provides the +//! [**SignalRms**](./rms/trait.SignalRms.html) trait. +//! - The **window** feature (or **signal-window** feature if using `dasp`) provides the +//! [**window**](./window/index.html) module. +//! +//! ### no_std +//! +//! If working in a `no_std` context, you can disable the default **std** feature with +//! `--no-default-features`. +//! +//! To enable all of the above features in a `no_std` context, enable the **all-no-std** feature. -use {BTreeMap, Duplex, Frame, Sample, Rc, VecDeque, Box}; -use core; -use core::cell::RefCell; -use envelope; -use interpolate::{Converter, Interpolator}; -use ring_buffer; -use rms; +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] +#[cfg(not(feature = "std"))] +extern crate alloc; -/// Types that yield `Frame`s as a multi-channel PCM signal. +use core; +use core::cell::RefCell; +use dasp_frame::Frame; +use dasp_interpolate::Interpolator; +use dasp_ring_buffer as ring_buffer; +use dasp_sample::{Duplex, Sample}; +use interpolate::Converter; + +pub mod interpolate; +mod ops; + +#[cfg(features = "boxed")] +mod boxed; +#[cfg(feature = "bus")] +pub mod bus; +#[cfg(feature = "envelope")] +pub mod envelope; +#[cfg(feature = "rms")] +pub mod rms; +#[cfg(feature = "window")] +pub mod window; + +#[cfg(not(feature = "std"))] +type Rc = alloc::rc::Rc; +#[cfg(feature = "std")] +type Rc = std::rc::Rc; + +/// Types that yield `Frame`s of a one-or-more-channel PCM signal. /// /// For example, `Signal` allows us to add two signals, modulate a signal's amplitude by another -/// signal, scale a signals amplitude and much more. +/// signal, scale a signal's amplitude and much more. +/// +/// The **Signal** trait is inspired by the `Iterator` trait but is different in the sense that it +/// will always yield frames and will never return `None`. That said, implementors of `Signal` may +/// optionally indicate exhaustian via the `is_exhausted` method. This allows for converting +/// exhaustive signals back to iterators that are well behaved. Calling **next** on an exhausted +/// signal should always yield `Self::Frame::EQUILIBRIUM`. pub trait Signal { /// The `Frame` type returned by the `Signal`. type Frame: Frame; @@ -41,17 +91,31 @@ pub trait Signal { /// /// # Example /// + /// An example of a mono (single-channel) signal. + /// /// ```rust - /// extern crate sample; + /// use dasp_signal::{self as signal, Signal}; + /// + /// fn main() { + /// let frames = [0.2, -0.6, 0.4]; + /// let mut signal = signal::from_iter(frames.iter().cloned()); + /// assert_eq!(signal.next(), 0.2); + /// assert_eq!(signal.next(), -0.6); + /// assert_eq!(signal.next(), 0.4); + /// } + /// ``` + /// + /// An example of a stereo (dual-channel) signal. /// - /// use sample::{signal, Signal}; + /// ```rust + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = [[0.2], [-0.6], [0.4]]; + /// let frames = [[0.2, 0.2], [-0.6, -0.6], [0.4, 0.4]]; /// let mut signal = signal::from_iter(frames.iter().cloned()); - /// assert_eq!(signal.next(), [0.2]); - /// assert_eq!(signal.next(), [-0.6]); - /// assert_eq!(signal.next(), [0.4]); + /// assert_eq!(signal.next(), [0.2, 0.2]); + /// assert_eq!(signal.next(), [-0.6, -0.6]); + /// assert_eq!(signal.next(), [0.4, 0.4]); /// } /// ``` fn next(&mut self) -> Self::Frame; @@ -75,9 +139,7 @@ pub trait Signal { /// when either `A` or `B` begins returning `None`. /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// // Infinite signals always return `false`. @@ -85,15 +147,15 @@ pub trait Signal { /// assert_eq!(sine_signal.is_exhausted(), false); /// /// // Signals over iterators return `true` when the inner iterator is exhausted. - /// let frames = [[0.2], [-0.6], [0.4]]; + /// let frames = [0.2, -0.6, 0.4]; /// let mut iter_signal = signal::from_iter(frames.iter().cloned()); /// assert_eq!(iter_signal.is_exhausted(), false); /// iter_signal.by_ref().take(3).count(); /// assert_eq!(iter_signal.is_exhausted(), true); /// /// // Adaptors return `true` when the first signal becomes exhausted. - /// let a = [[1], [2]]; - /// let b = [[1], [2], [3], [4]]; + /// let a = [1, 2]; + /// let b = [1, 2, 3, 4]; /// let a_signal = signal::from_iter(a.iter().cloned()); /// let b_signal = signal::from_iter(b.iter().cloned()); /// let mut added = a_signal.add_amp(b_signal); @@ -112,13 +174,11 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = signal::gen(|| [0.5]); - /// let mut mapper = frames.map(|f| [f[0], 0.25]); + /// let frames = signal::gen(|| 0.5); + /// let mut mapper = frames.map(|f| [f, 0.25]); /// assert_eq!(mapper.next(), [0.5, 0.25]); /// assert_eq!(mapper.next(), [0.5, 0.25]); /// assert_eq!(mapper.next(), [0.5, 0.25]); @@ -128,18 +188,18 @@ pub trait Signal { /// This can also be useful for monitoring the peak values of a signal. /// /// ``` - /// extern crate sample; - /// - /// use sample::{peak, signal, Frame, Signal}; + /// use dasp_frame::Frame; + /// use dasp_peak as peak; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let sine_wave = signal::rate(4.0).const_hz(1.0).sine(); /// let mut peak = sine_wave /// .map(peak::full_wave) - /// .map(|f| [f[0].round()]); + /// .map(|f| f.round()); /// assert_eq!( /// peak.take(4).collect::>(), - /// vec![[0.0], [1.0], [0.0], [1.0]] + /// vec![0.0, 1.0, 0.0, 1.0] /// ); /// } /// ``` @@ -161,14 +221,12 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = signal::gen(|| [0.5]); - /// let more_frames = signal::gen(|| [0.25]); - /// let mut mapper = frames.zip_map(more_frames, |f, o| [f[0], o[0]]); + /// let frames = signal::gen(|| 0.5); + /// let more_frames = signal::gen(|| 0.25); + /// let mut mapper = frames.zip_map(more_frames, |f, o| [f, o]); /// assert_eq!(mapper.next(), [0.5, 0.25]); /// assert_eq!(mapper.next(), [0.5, 0.25]); /// assert_eq!(mapper.next(), [0.5, 0.25]); @@ -195,17 +253,15 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let a = [[0.2], [-0.6], [0.4]]; - /// let b = [[0.2], [0.1], [-0.8]]; + /// let a = [0.2, -0.6, 0.4]; + /// let b = [0.2, 0.1, -0.8]; /// let a_signal = signal::from_iter(a.iter().cloned()); /// let b_signal = signal::from_iter(b.iter().cloned()); /// let added: Vec<_> = a_signal.add_amp(b_signal).take(3).collect(); - /// assert_eq!(added, vec![[0.4], [-0.5], [-0.4]]); + /// assert_eq!(added, vec![0.4, -0.5, -0.4]); /// } /// ``` #[inline] @@ -227,17 +283,15 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let a = [[0.25], [-0.8], [-0.5]]; - /// let b = [[0.2], [0.5], [0.8]]; + /// let a = [0.25, -0.8, -0.5]; + /// let b = [0.2, 0.5, 0.8]; /// let a_signal = signal::from_iter(a.iter().cloned()); /// let b_signal = signal::from_iter(b.iter().cloned()); /// let added: Vec<_> = a_signal.mul_amp(b_signal).take(3).collect(); - /// assert_eq!(added, vec![[0.05], [-0.4], [-0.4]]); + /// assert_eq!(added, vec![0.05, -0.4, -0.4]); /// } /// ``` #[inline] @@ -259,9 +313,7 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let frames = [[0.25, 0.4], [-0.2, -0.5]]; @@ -290,15 +342,13 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = [[0.2], [-0.5], [-0.4], [0.3]]; + /// let frames = [0.2, -0.5, -0.4, 0.3]; /// let signal = signal::from_iter(frames.iter().cloned()); /// let scaled: Vec<_> = signal.scale_amp(2.0).take(4).collect(); - /// assert_eq!(scaled, vec![[0.4], [-1.0], [-0.8], [0.6]]); + /// assert_eq!(scaled, vec![0.4, -1.0, -0.8, 0.6]); /// } /// ``` #[inline] @@ -318,9 +368,7 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let frames = [[0.5, 0.3], [-0.25, 0.9]]; @@ -350,9 +398,7 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let frames = [[0.2, -0.5], [-0.4, 0.3]]; @@ -384,25 +430,25 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; - /// use sample::interpolate::Linear; + /// use dasp_interpolate::linear::Linear; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let foo = [[0.0], [1.0], [0.0], [-1.0]]; - /// let mul = [[1.0], [1.0], [0.5], [0.5], [0.5], [0.5]]; + /// let foo = [0.0, 1.0, 0.0, -1.0]; + /// let mul = [1.0, 1.0, 0.5, 0.5, 0.5, 0.5]; /// let mut source = signal::from_iter(foo.iter().cloned()); - /// let interp = Linear::from_source(&mut source); + /// let a = source.next(); + /// let b = source.next(); + /// let interp = Linear::new(a, b); /// let hz_signal = signal::from_iter(mul.iter().cloned()); /// let frames: Vec<_> = source.mul_hz(interp, hz_signal).take(6).collect(); - /// assert_eq!(&frames[..], &[[0.0], [1.0], [0.0], [-0.5], [-1.0], [-0.5]][..]); + /// assert_eq!(&frames[..], &[0.0, 1.0, 0.0, -0.5, -1.0, -0.5][..]); /// } /// ``` fn mul_hz(self, interpolator: I, mul_per_frame: M) -> MulHz where Self: Sized, - M: Signal, + M: Signal, I: Interpolator, { MulHz { @@ -416,17 +462,17 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; - /// use sample::interpolate::Linear; + /// use dasp_interpolate::linear::Linear; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let foo = [[0.0], [1.0], [0.0], [-1.0]]; + /// let foo = [0.0, 1.0, 0.0, -1.0]; /// let mut source = signal::from_iter(foo.iter().cloned()); - /// let interp = Linear::from_source(&mut source); + /// let a = source.next(); + /// let b = source.next(); + /// let interp = Linear::new(a, b); /// let frames: Vec<_> = source.from_hz_to_hz(interp, 1.0, 2.0).take(8).collect(); - /// assert_eq!(&frames[..], &[[0.0], [0.5], [1.0], [0.5], [0.0], [-0.5], [-1.0], [-0.5]][..]); + /// assert_eq!(&frames[..], &[0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -0.5][..]); /// } /// ``` fn from_hz_to_hz(self, interpolator: I, source_hz: f64, target_hz: f64) -> Converter @@ -442,17 +488,17 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; - /// use sample::interpolate::Linear; + /// use dasp_interpolate::linear::Linear; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let foo = [[0.0], [1.0], [0.0], [-1.0]]; + /// let foo = [0.0, 1.0, 0.0, -1.0]; /// let mut source = signal::from_iter(foo.iter().cloned()); - /// let interp = Linear::from_source(&mut source); + /// let a = source.next(); + /// let b = source.next(); + /// let interp = Linear::new(a, b); /// let frames: Vec<_> = source.scale_hz(interp, 0.5).take(8).collect(); - /// assert_eq!(&frames[..], &[[0.0], [0.5], [1.0], [0.5], [0.0], [-0.5], [-1.0], [-0.5]][..]); + /// assert_eq!(&frames[..], &[0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -0.5][..]); /// } /// ``` fn scale_hz(self, interpolator: I, multi: f64) -> Converter @@ -465,21 +511,19 @@ pub trait Signal { /// Delays the `Signal` by the given number of frames. /// - /// The delay is performed by yielding `Frame::equilibrium()` `n_frames` times before + /// The delay is performed by yielding `Frame::EQUILIBRIUM` `n_frames` times before /// continuing to yield frames from `signal`. /// /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = [[0.2], [0.4]]; + /// let frames = [0.2, 0.4]; /// let signal = signal::from_iter(frames.iter().cloned()); /// let delayed: Vec<_> = signal.delay(2).take(4).collect(); - /// assert_eq!(delayed, vec![[0.0], [0.0], [0.2], [0.4]]); + /// assert_eq!(delayed, vec![0.0, 0.0, 0.2, 0.4]); /// } /// ``` fn delay(self, n_frames: usize) -> Delay @@ -497,9 +541,7 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let frames = [[0.1, 0.2], [0.3, 0.4]]; @@ -526,9 +568,7 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let frames = [[1.2, 0.8], [-0.7, -1.4]]; @@ -552,22 +592,20 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let mut f = [0.0]; + /// let mut f = 0.0; /// let mut signal = signal::gen_mut(move || { - /// f[0] += 0.1; + /// f += 0.1; /// f /// }); - /// let func = |x: &[f64; 1]| { - /// assert_eq!(*x, [0.1]); + /// let func = |x: &f64| { + /// assert_eq!(*x, 0.1); /// }; /// let mut inspected = signal.inspect(func); /// let out = inspected.next(); - /// assert_eq!(out, [0.1]); + /// assert_eq!(out, 0.1); /// } /// ``` fn inspect(self, inspect: F) -> Inspect @@ -603,13 +641,12 @@ pub trait Signal { /// behaviour. /// /// ``` - /// extern crate sample; - /// - /// use sample::{ring_buffer, signal, Signal}; + /// use dasp_ring_buffer as ring_buffer; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let signal = signal::rate(44_100.0).const_hz(440.0).sine(); - /// let ring_buffer = ring_buffer::Bounded::<[[f64; 1]; 64]>::array(); + /// let ring_buffer = ring_buffer::Bounded::from([0f64; 64]); /// let mut fork = signal.fork(ring_buffer); /// /// // Forks can be split into their branches via reference. @@ -635,7 +672,7 @@ pub trait Signal { fn fork(self, ring_buffer: ring_buffer::Bounded) -> Fork where Self: Sized, - S: ring_buffer::SliceMut, + S: ring_buffer::SliceMut, { assert!(ring_buffer.is_empty()); let shared = ForkShared { @@ -643,48 +680,9 @@ pub trait Signal { ring_buffer: ring_buffer, pending: Fork::::B, }; - Fork { shared: RefCell::new(shared) } - } - - /// Moves the `Signal` into a `Bus` from which its output may be divided into multiple other - /// `Signal`s in the form of `Output`s. - /// - /// This method allows to create more complex directed acyclic graph structures that - /// incorporate concepts like sends, side-chaining, etc, rather than being restricted to tree - /// structures where signals can only ever be joined but never divided. - /// - /// Note: When using multiple `Output`s in this fashion, you will need to be sure to pull the - /// frames from each `Output` in sync (whether per frame or per buffer). This is because when - /// output A requests `Frame`s before output B, those frames must remain available for output - /// B and in turn must be stored in an intermediary ring buffer. - /// - /// # Example - /// - /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; - /// - /// fn main() { - /// let frames = [[0.1], [0.2], [0.3], [0.4], [0.5], [0.6]]; - /// let signal = signal::from_iter(frames.iter().cloned()); - /// let bus = signal.bus(); - /// let mut a = bus.send(); - /// let mut b = bus.send(); - /// assert_eq!(a.by_ref().take(3).collect::>(), vec![[0.1], [0.2], [0.3]]); - /// assert_eq!(b.by_ref().take(3).collect::>(), vec![[0.1], [0.2], [0.3]]); - /// - /// let c = bus.send(); - /// assert_eq!(c.take(3).collect::>(), vec![[0.4], [0.5], [0.6]]); - /// assert_eq!(b.take(3).collect::>(), vec![[0.4], [0.5], [0.6]]); - /// assert_eq!(a.take(3).collect::>(), vec![[0.4], [0.5], [0.6]]); - /// } - /// ``` - fn bus(self) -> Bus - where - Self: Sized, - { - Bus::new(self, BTreeMap::new()) + Fork { + shared: RefCell::new(shared), + } } /// Converts the `Signal` into an `Iterator` that will yield the given number for `Frame`s @@ -693,15 +691,13 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = [[0.1], [0.2], [0.3], [0.4]]; + /// let frames = [0.1, 0.2, 0.3, 0.4]; /// let mut signal = signal::from_iter(frames.iter().cloned()).take(2); - /// assert_eq!(signal.next(), Some([0.1])); - /// assert_eq!(signal.next(), Some([0.2])); + /// assert_eq!(signal.next(), Some(0.1)); + /// assert_eq!(signal.next(), Some(0.2)); /// assert_eq!(signal.next(), None); /// } /// ``` @@ -718,12 +714,10 @@ pub trait Signal { /// # Example /// /// ``` - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = [[1], [2]]; + /// let frames = [1, 2]; /// let signal = signal::from_iter(frames.iter().cloned()); /// assert_eq!(signal.until_exhausted().count(), 2); /// } @@ -732,9 +726,7 @@ pub trait Signal { where Self: Sized, { - UntilExhausted { - signal: self, - } + UntilExhausted { signal: self } } /// Buffers the signal using the given ring buffer. @@ -745,43 +737,40 @@ pub trait Signal { /// popped from the front of the ring buffer and immediately returned. /// /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// use sample::{signal, Signal}; + /// use dasp_ring_buffer as ring_buffer; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = [[0.1], [0.2], [0.3], [0.4]]; + /// let frames = [0.1, 0.2, 0.3, 0.4]; /// let signal = signal::from_iter(frames.iter().cloned()); - /// let ring_buffer = ring_buffer::Bounded::<[[f32; 1]; 2]>::array(); + /// let ring_buffer = ring_buffer::Bounded::from([0f32; 2]); /// let mut buffered_signal = signal.buffered(ring_buffer); - /// assert_eq!(buffered_signal.next(), [0.1]); - /// assert_eq!(buffered_signal.next(), [0.2]); - /// assert_eq!(buffered_signal.next(), [0.3]); - /// assert_eq!(buffered_signal.next(), [0.4]); - /// assert_eq!(buffered_signal.next(), [0.0]); + /// assert_eq!(buffered_signal.next(), 0.1); + /// assert_eq!(buffered_signal.next(), 0.2); + /// assert_eq!(buffered_signal.next(), 0.3); + /// assert_eq!(buffered_signal.next(), 0.4); + /// assert_eq!(buffered_signal.next(), 0.0); /// } /// ``` /// /// If the given ring buffer already contains frames, those will be yielded first. /// /// ``` - /// extern crate sample; - /// use sample::ring_buffer; - /// use sample::{signal, Signal}; + /// use dasp_ring_buffer as ring_buffer; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = [[0.1], [0.2], [0.3], [0.4]]; + /// let frames = [0.1, 0.2, 0.3, 0.4]; /// let signal = signal::from_iter(frames.iter().cloned()); - /// let ring_buffer = ring_buffer::Bounded::from_full([[0.8], [0.9]]); + /// let ring_buffer = ring_buffer::Bounded::from_full([0.8, 0.9]); /// let mut buffered_signal = signal.buffered(ring_buffer); - /// assert_eq!(buffered_signal.next(), [0.8]); - /// assert_eq!(buffered_signal.next(), [0.9]); - /// assert_eq!(buffered_signal.next(), [0.1]); - /// assert_eq!(buffered_signal.next(), [0.2]); - /// assert_eq!(buffered_signal.next(), [0.3]); - /// assert_eq!(buffered_signal.next(), [0.4]); - /// assert_eq!(buffered_signal.next(), [0.0]); + /// assert_eq!(buffered_signal.next(), 0.8); + /// assert_eq!(buffered_signal.next(), 0.9); + /// assert_eq!(buffered_signal.next(), 0.1); + /// assert_eq!(buffered_signal.next(), 0.2); + /// assert_eq!(buffered_signal.next(), 0.3); + /// assert_eq!(buffered_signal.next(), 0.4); + /// assert_eq!(buffered_signal.next(), 0.0); /// } /// ``` fn buffered(self, ring_buffer: ring_buffer::Bounded) -> Buffered @@ -795,75 +784,6 @@ pub trait Signal { } } - /// An adaptor that yields the RMS of the signal. - /// - /// The window size of the RMS detector is equal to the given ring buffer length. - /// - /// # Example - /// - /// ``` - /// extern crate sample; - /// - /// use sample::ring_buffer; - /// use sample::{signal, Signal}; - /// - /// fn main() { - /// let frames = [[0.9], [-0.8], [0.6], [-0.9]]; - /// let signal = signal::from_iter(frames.iter().cloned()); - /// let ring_buffer = ring_buffer::Fixed::from([[0.0]; 2]); - /// let mut rms_signal = signal.rms(ring_buffer); - /// assert_eq!( - /// [rms_signal.next(), rms_signal.next(), rms_signal.next()], - /// [[0.6363961030678927], [0.8514693182963201], [0.7071067811865476]] - /// ); - /// } - /// ``` - fn rms(self, ring_buffer: ring_buffer::Fixed) -> Rms - where - Self: Sized, - S: ring_buffer::Slice::Float> + ring_buffer::SliceMut, - { - Rms { - signal: self, - rms: rms::Rms::new(ring_buffer), - } - } - - /// An adaptor that detects and yields the envelope of the signal. - /// - /// # Example - /// - /// ``` - /// extern crate sample; - /// - /// use sample::{envelope, signal, Signal}; - /// - /// fn main() { - /// let signal = signal::rate(4.0).const_hz(1.0).sine(); - /// let attack = 1.0; - /// let release = 1.0; - /// let detector = envelope::Detector::peak(attack, release); - /// let mut envelope = signal.detect_envelope(detector); - /// assert_eq!( - /// envelope.take(4).collect::>(), - /// vec![[0.0], [0.6321205496788025], [0.23254416035257117], [0.7176687675647109]] - /// ); - /// } - /// ``` - fn detect_envelope( - self, - detector: envelope::Detector, - ) -> DetectEnvelope - where - Self: Sized, - D: envelope::Detect, - { - DetectEnvelope { - signal: self, - detector: detector, - } - } - /// Borrows a Signal rather than consuming it. /// /// This is useful to allow applying signal adaptors while still retaining ownership of the @@ -872,17 +792,15 @@ pub trait Signal { /// # Example /// /// ```rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = [[0], [1], [2], [3], [4]]; + /// let frames = [0, 1, 2, 3, 4]; /// let mut signal = signal::from_iter(frames.iter().cloned()); - /// assert_eq!(signal.next(), [0]); - /// assert_eq!(signal.by_ref().take(2).collect::>(), vec![[1], [2]]); - /// assert_eq!(signal.next(), [3]); - /// assert_eq!(signal.next(), [4]); + /// assert_eq!(signal.next(), 0); + /// assert_eq!(signal.by_ref().take(2).collect::>(), vec![1, 2]); + /// assert_eq!(signal.next(), 3); + /// assert_eq!(signal.next(), 4); /// } /// ``` fn by_ref(&mut self) -> &mut Self @@ -893,7 +811,6 @@ pub trait Signal { } } - /// Consumes the given `Iterator`, converts it to a `Signal`, applies the given function to the /// `Signal` and returns an `Iterator` that will become exhausted when the consumed `Iterator` /// does. @@ -904,14 +821,12 @@ pub trait Signal { /// # Example /// /// ``` -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { -/// let frames = vec![[0], [1], [2], [3]]; +/// let frames = vec![0, 1, 2, 3]; /// let offset_frames = signal::lift(frames, |signal| signal.offset_amp(2)); -/// assert_eq!(offset_frames.collect::>(), vec![[2], [3], [4], [5]]); +/// assert_eq!(offset_frames.collect::>(), vec![2, 3, 4, 5]); /// } /// ``` pub fn lift(iter: I, f: F) -> UntilExhausted @@ -927,10 +842,8 @@ where new_signal.until_exhausted() } - ///// Signal Types - /// An iterator that endlessly yields `Frame`s of type `F` at equilibrium. #[derive(Clone)] pub struct Equilibrium { @@ -1011,7 +924,6 @@ pub struct Hz { rate: Rate, } - /// An iterator that yields a phase, useful for waveforms like Sine or Saw. #[derive(Clone)] pub struct Phase { @@ -1118,7 +1030,7 @@ where /// Delays the `signal` by the given number of frames. /// -/// The delay is performed by yielding `Frame::equilibrium()` `n_frames` times before +/// The delay is performed by yielding `Frame::EQUILIBRIUM` `n_frames` times before /// continuing to yield frames from `signal`. #[derive(Clone)] pub struct Delay { @@ -1203,8 +1115,12 @@ impl Fork { pub fn by_rc(self) -> (BranchRcA, BranchRcB) { let Fork { shared } = self; let shared_fork = Rc::new(shared); - let a = BranchRcA { shared_fork: shared_fork.clone() }; - let b = BranchRcB { shared_fork: shared_fork }; + let a = BranchRcA { + shared_fork: shared_fork.clone(), + }; + let b = BranchRcB { + shared_fork: shared_fork, + }; (a, b) } @@ -1215,8 +1131,12 @@ impl Fork { /// less ergonomic in some cases as the returned branches are bound to the lifetime of `Fork`. pub fn by_ref(&mut self) -> (BranchRefA, BranchRefB) { let Fork { ref shared } = *self; - let a = BranchRefA { shared_fork: shared }; - let b = BranchRefB { shared_fork: shared }; + let a = BranchRefA { + shared_fork: shared, + }; + let b = BranchRefB { + shared_fork: shared, + }; (a, b) } } @@ -1237,7 +1157,7 @@ macro_rules! define_branch { impl Signal for $TRc where S: Signal, - D: ring_buffer::SliceMut, + D: ring_buffer::SliceMut, { type Frame = S::Frame; fn next(&mut self) -> Self::Frame { @@ -1257,7 +1177,7 @@ macro_rules! define_branch { impl<'a, S, D> Signal for $TRef<'a, S, D> where S: 'a + Signal, - D: 'a + ring_buffer::SliceMut, + D: 'a + ring_buffer::SliceMut, { type Frame = S::Frame; fn next(&mut self) -> Self::Frame { @@ -1311,42 +1231,6 @@ macro_rules! define_branch { define_branch!(BranchRcA, BranchRefA, A, B); define_branch!(BranchRcB, BranchRefB, B, A); - -/// A type which allows for `send`ing a single `Signal` to multiple outputs. -/// -/// This type manages -pub struct Bus -where - S: Signal, -{ - node: Rc>>, -} - -/// The data shared between each `Output`. -struct SharedNode -where - S: Signal, -{ - signal: S, - // The buffer of frames that have not yet been consumed by all outputs. - buffer: VecDeque, - // The number of frames in `buffer` that have already been read for each output. - frames_read: BTreeMap, - // The next output key. - next_key: usize, -} - -/// An output node to which some signal `S` is `Output`ing its frames. -/// -/// It may be more accurate to say that the `Output` "pull"s frames from the signal. -pub struct Output -where - S: Signal, -{ - key: usize, - node: Rc>>, -} - /// An iterator that yields `n` number of `Frame`s from the inner `signal`. #[derive(Clone)] pub struct Take @@ -1377,48 +1261,20 @@ pub struct BufferedFrames<'a, D: 'a> { ring_buffer: &'a mut ring_buffer::Bounded, } -/// An adaptor that yields the RMS of the signal. -/// -/// The window size of the RMS detector is equal to the given ring buffer length. -#[derive(Clone)] -pub struct Rms -where - S: Signal, - D: ring_buffer::Slice::Float>, -{ - signal: S, - rms: rms::Rms, -} - -/// An adaptor that detects and yields the envelope of the signal. -#[derive(Clone)] -pub struct DetectEnvelope -where - S: Signal, - D: envelope::Detect, -{ - signal: S, - detector: envelope::Detector, -} - - ///// Signal Constructors - /// Provides an iterator that endlessly yields `Frame`s of type `F` at equilibrium. /// /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::Signal; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { -/// let equilibrium: Vec<[f32; 1]> = sample::signal::equilibrium().take(4).collect(); -/// assert_eq!(equilibrium, vec![[0.0], [0.0], [0.0], [0.0]]); +/// let equilibrium: Vec = signal::equilibrium().take(4).collect(); +/// assert_eq!(equilibrium, vec![0.0, 0.0, 0.0, 0.0]); /// -/// let equilibrium: Vec<[u8; 2]> = sample::signal::equilibrium().take(3).collect(); +/// let equilibrium: Vec<[u8; 2]> = signal::equilibrium().take(3).collect(); /// assert_eq!(equilibrium, vec![[128, 128], [128, 128], [128, 128]]); /// } /// ``` @@ -1426,10 +1282,11 @@ pub fn equilibrium() -> Equilibrium where F: Frame, { - Equilibrium { frame: core::marker::PhantomData } + Equilibrium { + frame: core::marker::PhantomData, + } } - /// A signal that generates frames using the given function. /// /// The resulting signal is assumed to be infinite and `is_exhausted` will always return `false`. @@ -1438,9 +1295,7 @@ where /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let mut frames = signal::gen(|| [0.5]); @@ -1460,7 +1315,6 @@ where } } - /// A signal that generates frames using the given function which may mutate some state. /// /// The resulting signal is assumed to be infinite and `is_exhausted` will always return `false`. @@ -1469,9 +1323,7 @@ where /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let mut f = [0.0]; @@ -1496,7 +1348,6 @@ where } } - /// Create a new `Signal` from the given `Frame`-yielding `Iterator`. /// /// When the `Iterator` is exhausted, the new `Signal` will yield `F::equilibrium`. @@ -1507,9 +1358,7 @@ where /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let frames = [[1], [-3], [5], [6]]; @@ -1534,7 +1383,6 @@ where } } - /// Create a new `Signal` from the given `Frame`-yielding `Iterator`. /// /// When the `Iterator` is exhausted, the new `Signal` will yield `F::equilibrium`. @@ -1542,9 +1390,7 @@ where /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let foo = [0, 1, 2, 3]; @@ -1575,26 +1421,23 @@ where } } - /// Creates a `Phase` that continuously steps forward by the given `step` size yielder. /// /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// let step = signal::rate(4.0).const_hz(1.0); /// // Note that this is the same as `step.phase()`, a composable alternative. /// let mut phase = signal::phase(step); -/// assert_eq!(phase.next(), [0.0]); -/// assert_eq!(phase.next(), [0.25]); -/// assert_eq!(phase.next(), [0.5]); -/// assert_eq!(phase.next(), [0.75]); -/// assert_eq!(phase.next(), [0.0]); -/// assert_eq!(phase.next(), [0.25]); +/// assert_eq!(phase.next(), 0.0); +/// assert_eq!(phase.next(), 0.25); +/// assert_eq!(phase.next(), 0.5); +/// assert_eq!(phase.next(), 0.75); +/// assert_eq!(phase.next(), 0.0); +/// assert_eq!(phase.next(), 0.25); /// } /// ``` pub fn phase(step: S) -> Phase @@ -1607,7 +1450,6 @@ where } } - /// Creates a frame `Rate` (aka sample rate) representing the rate at which a signal may be /// sampled. /// @@ -1617,23 +1459,20 @@ pub fn rate(hz: f64) -> Rate { Rate { hz: hz } } - /// Produces a `Signal` that yields a sine wave oscillating at the given hz. /// /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// // Generates a sine wave signal at 1hz to be sampled 4 times per second. /// let mut signal = signal::rate(4.0).const_hz(1.0).sine(); -/// assert_eq!(signal.next(), [0.0]); -/// assert_eq!(signal.next(), [1.0]); +/// assert_eq!(signal.next(), 0.0); +/// assert_eq!(signal.next(), 1.0); /// signal.next(); -/// assert_eq!(signal.next(), [-1.0]); +/// assert_eq!(signal.next(), -1.0); /// } /// ``` pub fn sine(phase: Phase) -> Sine { @@ -1645,17 +1484,15 @@ pub fn sine(phase: Phase) -> Sine { /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// // Generates a saw wave signal at 1hz to be sampled 4 times per second. /// let mut signal = signal::rate(4.0).const_hz(1.0).saw(); -/// assert_eq!(signal.next(), [1.0]); -/// assert_eq!(signal.next(), [0.5]); -/// assert_eq!(signal.next(), [0.0]); -/// assert_eq!(signal.next(), [-0.5]); +/// assert_eq!(signal.next(), 1.0); +/// assert_eq!(signal.next(), 0.5); +/// assert_eq!(signal.next(), 0.0); +/// assert_eq!(signal.next(), -0.5); /// } /// ``` pub fn saw(phase: Phase) -> Saw { @@ -1667,17 +1504,15 @@ pub fn saw(phase: Phase) -> Saw { /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// // Generates a square wave signal at 1hz to be sampled 4 times per second. /// let mut signal = signal::rate(4.0).const_hz(1.0).square(); -/// assert_eq!(signal.next(), [1.0]); -/// assert_eq!(signal.next(), [1.0]); -/// assert_eq!(signal.next(), [-1.0]); -/// assert_eq!(signal.next(), [-1.0]); +/// assert_eq!(signal.next(), 1.0); +/// assert_eq!(signal.next(), 1.0); +/// assert_eq!(signal.next(), -1.0); +/// assert_eq!(signal.next(), -1.0); /// } /// ``` pub fn square(phase: Phase) -> Square { @@ -1689,14 +1524,12 @@ pub fn square(phase: Phase) -> Square { /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { -/// let mut noise = sample::signal::noise(0); +/// let mut noise = signal::noise(0); /// for n in noise.take(1_000_000) { -/// assert!(-1.0 <= n[0] && n[0] < 1.0); +/// assert!(-1.0 <= n && n < 1.0); /// } /// } /// ``` @@ -1711,15 +1544,13 @@ pub fn noise(seed: u64) -> Noise { /// # Example /// /// ```rust -/// extern crate sample; -/// -/// use sample::{signal, Signal}; +/// use dasp_signal::{self as signal, Signal}; /// /// fn main() { /// // Creates a simplex noise signal oscillating at 440hz sampled 44_100 times per second. /// let mut signal = signal::rate(44_100.0).const_hz(440.0).noise_simplex(); /// for n in signal.take(1_000_000) { -/// assert!(-1.0 <= n[0] && n[0] < 1.0); +/// assert!(-1.0 <= n && n < 1.0); /// } /// } /// ``` @@ -1727,32 +1558,14 @@ pub fn noise_simplex(phase: Phase) -> NoiseSimplex { NoiseSimplex { phase: phase } } - //// Trait Implementations for Signal Types. - impl<'a, S> Signal for &'a mut S where S: Signal + ?Sized, { type Frame = S::Frame; - #[inline] - fn next(&mut self) -> Self::Frame { - (**self).next() - } - #[inline] - fn is_exhausted(&self) -> bool { - (**self).is_exhausted() - } -} - - -impl Signal for Box -where - S: Signal + ?Sized, -{ - type Frame = S::Frame; #[inline] fn next(&mut self) -> Self::Frame { (**self).next() @@ -1764,21 +1577,21 @@ where } } - impl Signal for FromIterator where I: Iterator, I::Item: Frame, { type Frame = I::Item; + #[inline] fn next(&mut self) -> Self::Frame { match self.next.take() { Some(frame) => { self.next = self.iter.next(); frame - }, - None => Frame::equilibrium(), + } + None => Frame::EQUILIBRIUM, } } @@ -1788,7 +1601,6 @@ where } } - impl Signal for FromInterleavedSamplesIterator where I: Iterator, @@ -1796,14 +1608,15 @@ where F: Frame, { type Frame = F; + #[inline] fn next(&mut self) -> Self::Frame { match self.next.take() { Some(frame) => { self.next = F::from_samples(&mut self.samples); frame - }, - None => F::equilibrium(), + } + None => F::EQUILIBRIUM, } } @@ -1813,45 +1626,44 @@ where } } - impl Signal for Equilibrium where F: Frame, { type Frame = F; + #[inline] fn next(&mut self) -> Self::Frame { - F::equilibrium() + F::EQUILIBRIUM } } - impl Signal for Gen where G: Fn() -> F, F: Frame, { type Frame = F; + #[inline] fn next(&mut self) -> Self::Frame { (self.gen)() } } - impl Signal for GenMut where G: FnMut() -> F, F: Frame, { type Frame = F; + #[inline] fn next(&mut self) -> Self::Frame { (self.gen_mut)() } } - impl Signal for Map where S: Signal, @@ -1859,6 +1671,7 @@ where F: Frame, { type Frame = F; + #[inline] fn next(&mut self) -> Self::Frame { (self.map)(self.signal.next()) @@ -1869,7 +1682,6 @@ where } } - impl Signal for ZipMap where S: Signal, @@ -1878,6 +1690,7 @@ where F: Frame, { type Frame = F; + #[inline] fn next(&mut self) -> Self::Frame { (self.map)(self.this.next(), self.other.next()) @@ -1888,15 +1701,15 @@ where } } - impl Signal for Hz where - S: Signal, + S: Signal, { - type Frame = [f64; 1]; + type Frame = f64; + #[inline] fn next(&mut self) -> Self::Frame { - [self.step()] + self.step() } #[inline] @@ -1905,68 +1718,71 @@ where } } - impl Signal for ConstHz { - type Frame = [f64; 1]; + type Frame = f64; + #[inline] fn next(&mut self) -> Self::Frame { - [self.step()] + self.step() } } - impl Signal for Phase where S: Step, { - type Frame = [f64; 1]; + type Frame = f64; + #[inline] fn next(&mut self) -> Self::Frame { - [self.next_phase()] + self.next_phase() } } - impl Signal for Sine where S: Step, { - type Frame = [f64; 1]; + type Frame = f64; + #[inline] fn next(&mut self) -> Self::Frame { const PI_2: f64 = core::f64::consts::PI * 2.0; let phase = self.phase.next_phase(); - [super::ops::f64::sin(PI_2 * phase)] + ops::f64::sin(PI_2 * phase) } } - impl Signal for Saw where S: Step, { - type Frame = [f64; 1]; + type Frame = f64; + #[inline] fn next(&mut self) -> Self::Frame { let phase = self.phase.next_phase(); - [phase * -2.0 + 1.0] + phase * -2.0 + 1.0 } } - impl Signal for Square where S: Step, { - type Frame = [f64; 1]; + type Frame = f64; + #[inline] fn next(&mut self) -> Self::Frame { let phase = self.phase.next_phase(); - [if phase < 0.5 { 1.0 } else { -1.0 }] + if phase < 0.5 { + 1.0 + } else { + -1.0 + } } } - impl Rate { /// Create a `ConstHz` signal which consistently yields `hz / rate`. pub fn const_hz(self, hz: f64) -> ConstHz { @@ -1975,39 +1791,34 @@ impl Rate { /// Create a `Hz` signal which yields phase step sizes controlled by an input /// signal `hz`. - /// + /// /// # Example - /// + /// /// ``` rust - /// extern crate sample; - /// - /// use sample::{signal, Signal}; - /// + /// use dasp_signal::{self as signal, Signal}; + /// /// fn main() { - /// let step = signal::rate(4.0).hz(signal::gen(|| [1.0])); + /// let step = signal::rate(4.0).hz(signal::gen(|| 1.0)); /// let mut phase = signal::phase(step); - /// assert_eq!(phase.next(), [0.0]); - /// assert_eq!(phase.next(), [0.25]); - /// assert_eq!(phase.next(), [0.5]); - /// assert_eq!(phase.next(), [0.75]); - /// assert_eq!(phase.next(), [0.0]); - /// assert_eq!(phase.next(), [0.25]); + /// assert_eq!(phase.next(), 0.0); + /// assert_eq!(phase.next(), 0.25); + /// assert_eq!(phase.next(), 0.5); + /// assert_eq!(phase.next(), 0.75); + /// assert_eq!(phase.next(), 0.0); + /// assert_eq!(phase.next(), 0.25); /// } /// ``` pub fn hz(self, hz: S) -> Hz where - S: Signal, + S: Signal, { - Hz { - hz: hz, - rate: self, - } + Hz { hz: hz, rate: self } } } impl Hz where - S: Signal, + S: Signal, { /// Construct a `Phase` iterator that, for every `hz` yielded by `self`, yields a phase that is /// stepped by `hz / self.rate.hz`. @@ -2095,16 +1906,15 @@ impl Step for ConstHz { impl Step for Hz where - S: Signal, + S: Signal, { #[inline] fn step(&mut self) -> f64 { - let hz = self.hz.next()[0]; + let hz = self.hz.next(); hz / self.rate.hz } } - impl Phase where S: Step, @@ -2149,7 +1959,6 @@ where } } - impl Noise { #[inline] pub fn next_sample(&mut self) -> f64 { @@ -2163,10 +1972,15 @@ impl Noise { const PRIME_2: u64 = 789_221; const PRIME_3: u64 = 1_376_312_589; let x = (seed << 13) ^ seed; - 1.0 - - (x.wrapping_mul(x.wrapping_mul(x).wrapping_mul(PRIME_1).wrapping_add( - PRIME_2, - )).wrapping_add(PRIME_3) & 0x7fffffff) as f64 / 1_073_741_824.0 + 1.0 - (x + .wrapping_mul( + x.wrapping_mul(x) + .wrapping_mul(PRIME_1) + .wrapping_add(PRIME_2), + ) + .wrapping_add(PRIME_3) + & 0x7fffffff) as f64 + / 1_073_741_824.0 } let noise = noise_1(self.seed); @@ -2176,14 +1990,13 @@ impl Noise { } impl Signal for Noise { - type Frame = [f64; 1]; + type Frame = f64; #[inline] fn next(&mut self) -> Self::Frame { - [self.next_sample()] + self.next_sample() } } - impl NoiseSimplex where S: Step, @@ -2208,265 +2021,23 @@ where // This function and the enclosing functions have been adapted from SRombauts' MIT licensed // C++ implementation at the following link: https://github.com/SRombauts/SimplexNoise fn simplex_noise_1d(x: f64) -> f64 { - // Permutation table. This is a random jumble of all numbers 0...255. const PERM: [u8; 256] = [ - 151, - 160, - 137, - 91, - 90, - 15, - 131, - 13, - 201, - 95, - 96, - 53, - 194, - 233, - 7, - 225, - 140, - 36, - 103, - 30, - 69, - 142, - 8, - 99, - 37, - 240, - 21, - 10, - 23, - 190, - 6, - 148, - 247, - 120, - 234, - 75, - 0, - 26, - 197, - 62, - 94, - 252, - 219, - 203, - 117, - 35, - 11, - 32, - 57, - 177, - 33, - 88, - 237, - 149, - 56, - 87, - 174, - 20, - 125, - 136, - 171, - 168, - 68, - 175, - 74, - 165, - 71, - 134, - 139, - 48, - 27, - 166, - 77, - 146, - 158, - 231, - 83, - 111, - 229, - 122, - 60, - 211, - 133, - 230, - 220, - 105, - 92, - 41, - 55, - 46, - 245, - 40, - 244, - 102, - 143, - 54, - 65, - 25, - 63, - 161, - 1, - 216, - 80, - 73, - 209, - 76, - 132, - 187, - 208, - 89, - 18, - 169, - 200, - 196, - 135, - 130, - 116, - 188, - 159, - 86, - 164, - 100, - 109, - 198, - 173, - 186, - 3, - 64, - 52, - 217, - 226, - 250, - 124, - 123, - 5, - 202, - 38, - 147, - 118, - 126, - 255, - 82, - 85, - 212, - 207, - 206, - 59, - 227, - 47, - 16, - 58, - 17, - 182, - 189, - 28, - 42, - 223, - 183, - 170, - 213, - 119, - 248, - 152, - 2, - 44, - 154, - 163, - 70, - 221, - 153, - 101, - 155, - 167, - 43, - 172, - 9, - 129, - 22, - 39, - 253, - 19, - 98, - 108, - 110, - 79, - 113, - 224, - 232, - 178, - 185, - 112, - 104, - 218, - 246, - 97, - 228, - 251, - 34, - 242, - 193, - 238, - 210, - 144, - 12, - 191, - 179, - 162, - 241, - 81, - 51, - 145, - 235, - 249, - 14, - 239, - 107, - 49, - 192, - 214, - 31, - 181, - 199, - 106, - 157, - 184, - 84, - 204, - 176, - 115, - 121, - 50, - 45, - 127, - 4, - 150, - 254, - 138, - 236, - 205, - 93, - 222, - 114, - 67, - 29, - 24, - 72, - 243, - 141, - 128, - 195, - 78, - 66, - 215, - 61, - 156, - 180, + 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, + 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, + 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, + 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, + 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, + 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, + 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, + 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, + 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, + 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, + 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, + 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, + 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, + 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, + 215, 61, 156, 180, ]; // Hashes the given integer with the above permutation table. @@ -2489,7 +2060,7 @@ where } // Corners coordinates (nearest integer values). - let i0 = super::ops::f64::floor(x) as i64; + let i0 = ops::f64::floor(x) as i64; let i1 = i0 + 1; // Distances to corners (between 0 and 1); @@ -2518,22 +2089,25 @@ impl Signal for NoiseSimplex where S: Step, { - type Frame = [f64; 1]; + type Frame = f64; + #[inline] fn next(&mut self) -> Self::Frame { - [self.next_sample()] + self.next_sample() } } - impl Signal for AddAmp where A: Signal, B: Signal, - B::Frame: Frame::Sample as Sample>::Signed, - NumChannels=::NumChannels>, + B::Frame: Frame< + Sample = <::Sample as Sample>::Signed, + NumChannels = ::NumChannels, + >, { type Frame = A::Frame; + #[inline] fn next(&mut self) -> Self::Frame { self.a.next().add_amp(self.b.next()) @@ -2545,15 +2119,17 @@ where } } - impl Signal for MulAmp where A: Signal, B: Signal, - B::Frame: Frame::Sample as Sample>::Float, - NumChannels=::NumChannels>, + B::Frame: Frame< + Sample = <::Sample as Sample>::Float, + NumChannels = ::NumChannels, + >, { type Frame = A::Frame; + #[inline] fn next(&mut self) -> Self::Frame { self.a.next().mul_amp(self.b.next()) @@ -2565,12 +2141,12 @@ where } } - impl Signal for ScaleAmp where S: Signal, { type Frame = S::Frame; + #[inline] fn next(&mut self) -> Self::Frame { self.signal.next().scale_amp(self.amp) @@ -2582,14 +2158,16 @@ where } } - impl Signal for ScaleAmpPerChannel where S: Signal, - F: Frame::Sample as Sample>::Float, - NumChannels=::NumChannels>, + F: Frame< + Sample = <::Sample as Sample>::Float, + NumChannels = ::NumChannels, + >, { type Frame = S::Frame; + #[inline] fn next(&mut self) -> Self::Frame { self.signal.next().mul_amp(self.amp_frame) @@ -2601,12 +2179,12 @@ where } } - impl Signal for OffsetAmp where S: Signal, { type Frame = S::Frame; + #[inline] fn next(&mut self) -> Self::Frame { self.signal.next().offset_amp(self.offset) @@ -2618,14 +2196,16 @@ where } } - impl Signal for OffsetAmpPerChannel where S: Signal, - F: Frame::Sample as Sample>::Signed, - NumChannels=::NumChannels>, + F: Frame< + Sample = <::Sample as Sample>::Signed, + NumChannels = ::NumChannels, + >, { type Frame = S::Frame; + #[inline] fn next(&mut self) -> Self::Frame { self.signal.next().add_amp(self.amp_frame) @@ -2637,18 +2217,18 @@ where } } - impl Signal for MulHz where S: Signal, ::Sample: Duplex, - M: Signal, + M: Signal, I: Interpolator, { type Frame = S::Frame; + #[inline] fn next(&mut self) -> Self::Frame { - let mul = self.mul_per_frame.next()[0]; + let mul = self.mul_per_frame.next(); self.signal.set_playback_hz_scale(mul); self.signal.next() } @@ -2659,17 +2239,17 @@ where } } - impl Signal for Delay where S: Signal, { type Frame = S::Frame; + #[inline] fn next(&mut self) -> Self::Frame { if self.n_frames > 0 { self.n_frames -= 1; - Self::Frame::equilibrium() + Self::Frame::EQUILIBRIUM } else { self.signal.next() } @@ -2681,13 +2261,13 @@ where } } - impl Signal for Inspect where S: Signal, F: FnMut(&S::Frame), { type Frame = S::Frame; + #[inline] fn next(&mut self) -> Self::Frame { let out = self.signal.next(); @@ -2701,7 +2281,6 @@ where } } - impl IntoInterleavedSamples where S: Signal, @@ -2729,6 +2308,7 @@ where S: Signal, { type Item = ::Sample; + #[inline] fn next(&mut self) -> Option { Some(self.samples.next_sample()) @@ -2770,16 +2350,18 @@ where { #[inline] fn clone(&self) -> Self { - IntoInterleavedSamplesIterator { samples: self.samples.clone() } + IntoInterleavedSamplesIterator { + samples: self.samples.clone(), + } } } - impl Signal for ClipAmp where S: Signal, { type Frame = S::Frame; + #[inline] fn next(&mut self) -> Self::Frame { let f = self.signal.next(); @@ -2791,7 +2373,8 @@ where -self.thresh } else { s - }.to_sample() + } + .to_sample() }) } @@ -2801,177 +2384,12 @@ where } } - -impl Bus -where - S: Signal, -{ - fn new(signal: S, frames_read: BTreeMap) -> Self { - Bus { - node: Rc::new(core::cell::RefCell::new(SharedNode { - signal: signal, - buffer: VecDeque::new(), - frames_read: frames_read, - next_key: 0, - })), - } - } - - /// Produce a new Output node to which the signal `S` will output its frames. - #[inline] - pub fn send(&self) -> Output { - let mut node = self.node.borrow_mut(); - - // Get the key and increment for the next output. - let key = node.next_key; - node.next_key = node.next_key.wrapping_add(1); - - // Insert the number of frames read by the new output. - let num_frames = node.buffer.len(); - node.frames_read.insert(key, num_frames); - - Output { - key: key, - node: self.node.clone(), - } - } -} - -impl SharedNode -where - S: Signal, -{ - // Requests the next frame for the `Output` at the given key. - // - // If there are no frames pending for the output, a new frame will be requested from the - // signal and appended to the ring buffer to be received by the other outputs. - fn next_frame(&mut self, key: usize) -> S::Frame { - let num_frames = self.buffer.len(); - let frames_read = self.frames_read.remove(&key).expect( - "no frames_read for Output", - ); - - let frame = if frames_read < num_frames { - self.buffer[frames_read] - } else { - let frame = self.signal.next(); - self.buffer.push_back(frame); - frame - }; - - // If the number of frames read by this output is the lowest, then we can pop the frame - // from the front. - let least_frames_read = !self.frames_read.values().any(|&other_frames_read| { - other_frames_read <= frames_read - }); - - // If this output had read the least number of frames, pop the front frame and decrement - // the frames read counters for each of the other outputs. - let new_frames_read = if least_frames_read { - self.buffer.pop_front(); - for other_frames_read in self.frames_read.values_mut() { - *other_frames_read -= 1; - } - frames_read - } else { - frames_read + 1 - }; - - self.frames_read.insert(key, new_frames_read); - - frame - } - - #[inline] - fn pending_frames(&self, key: usize) -> usize { - self.buffer.len() - self.frames_read[&key] - } - - // Drop the given output from the `Bus`. - // - // Called by the `Output::drop` implementation. - fn drop_output(&mut self, key: usize) { - self.frames_read.remove(&key); - let least_frames_read = self.frames_read.values().fold(self.buffer.len(), |a, &b| { - core::cmp::min(a, b) - }); - if least_frames_read > 0 { - for frames_read in self.frames_read.values_mut() { - *frames_read -= least_frames_read; - } - for _ in 0..least_frames_read { - self.buffer.pop_front(); - } - } - } -} - -impl Output -where - S: Signal, -{ - /// The number of frames that have been requested from the `Signal` `S` by some other `Output` - /// that have not yet been requested by this `Output`. - /// - /// This is useful when using an `Output` to "monitor" some signal, allowing the user to drain - /// only frames that have already been requested by some other `Output`. - /// - /// # Example - /// - /// ``` - /// extern crate sample; - /// - /// use sample::{signal, Signal}; - /// - /// fn main() { - /// let frames = [[0.1], [0.2], [0.3]]; - /// let bus = signal::from_iter(frames.iter().cloned()).bus(); - /// let signal = bus.send(); - /// let mut monitor = bus.send(); - /// assert_eq!(signal.take(3).collect::>(), vec![[0.1], [0.2], [0.3]]); - /// assert_eq!(monitor.pending_frames(), 3); - /// assert_eq!(monitor.next(), [0.1]); - /// assert_eq!(monitor.pending_frames(), 2); - /// } - /// ``` - #[inline] - pub fn pending_frames(&self) -> usize { - self.node.borrow().pending_frames(self.key) - } -} - -impl Signal for Output -where - S: Signal, -{ - type Frame = S::Frame; - #[inline] - fn next(&mut self) -> Self::Frame { - self.node.borrow_mut().next_frame(self.key) - } - - #[inline] - fn is_exhausted(&self) -> bool { - let node = self.node.borrow(); - node.pending_frames(self.key) == 0 && node.signal.is_exhausted() - } -} - -impl Drop for Output -where - S: Signal, -{ - fn drop(&mut self) { - self.node.borrow_mut().drop_output(self.key) - } -} - - impl Iterator for Take where S: Signal, { type Item = S::Frame; + #[inline] fn next(&mut self) -> Option { if self.n == 0 { @@ -3008,19 +2426,17 @@ where /// filled using `Buffered`'s inner `signal` before `BufferedFrames` is returned. /// /// ``` - /// extern crate sample; - /// - /// use sample::signal::{self, Signal}; - /// use sample::ring_buffer; + /// use dasp_ring_buffer as ring_buffer; + /// use dasp_signal::{self as signal, Signal}; /// /// fn main() { - /// let frames = [[0.1], [0.2], [0.3], [0.4]]; + /// let frames = [0.1, 0.2, 0.3, 0.4]; /// let signal = signal::from_iter(frames.iter().cloned()); - /// let ring_buffer = ring_buffer::Bounded::<[[f32; 1]; 2]>::array(); + /// let ring_buffer = ring_buffer::Bounded::from([0f32; 2]); /// let mut buffered_signal = signal.buffered(ring_buffer); - /// assert_eq!(buffered_signal.next_frames().collect::>(), vec![[0.1], [0.2]]); - /// assert_eq!(buffered_signal.next_frames().collect::>(), vec![[0.3], [0.4]]); - /// assert_eq!(buffered_signal.next_frames().collect::>(), vec![[0.0], [0.0]]); + /// assert_eq!(buffered_signal.next_frames().collect::>(), vec![0.1, 0.2]); + /// assert_eq!(buffered_signal.next_frames().collect::>(), vec![0.3, 0.4]); + /// assert_eq!(buffered_signal.next_frames().collect::>(), vec![0.0, 0.0]); /// } /// ``` pub fn next_frames(&mut self) -> BufferedFrames { @@ -3033,7 +2449,9 @@ where ring_buffer.push(signal.next()); } } - BufferedFrames { ring_buffer: ring_buffer } + BufferedFrames { + ring_buffer: ring_buffer, + } } /// Consumes the `Buffered` signal and returns its inner signal `S` and bounded ring buffer. @@ -3049,17 +2467,23 @@ where impl Signal for Buffered where S: Signal, - D: ring_buffer::Slice + ring_buffer::SliceMut, + D: ring_buffer::Slice + ring_buffer::SliceMut, { type Frame = S::Frame; + fn next(&mut self) -> Self::Frame { - let Buffered { ref mut signal, ref mut ring_buffer } = *self; + let Buffered { + ref mut signal, + ref mut ring_buffer, + } = *self; loop { match ring_buffer.pop() { Some(frame) => return frame, - None => for _ in 0..ring_buffer.max_len() { - ring_buffer.push(signal.next()); - }, + None => { + for _ in 0..ring_buffer.max_len() { + ring_buffer.push(signal.next()); + } + } } } } @@ -3079,77 +2503,3 @@ where self.ring_buffer.pop() } } - -impl Rms -where - S: Signal, - D: ring_buffer::Slice::Float> + ring_buffer::SliceMut, -{ -/// The same as `Signal::next` but does not calculate the final square root required to -/// determine the RMS. - pub fn next_squared(&mut self) -> ::Frame { - self.rms.next_squared(self.signal.next()) - } - -/// Consumes the `Rms` signal and returns its inner signal `S` and `Rms` detector. - pub fn into_parts(self) -> (S, rms::Rms) { - let Rms { - signal, - rms, - } = self; - (signal, rms) - } -} - -impl Signal for Rms -where - S: Signal, - D: ring_buffer::Slice::Float> - + ring_buffer::SliceMut, -{ - type Frame = ::Float; - fn next(&mut self) -> Self::Frame { - self.rms.next(self.signal.next()) - } - - fn is_exhausted(&self) -> bool { - self.signal.is_exhausted() - } -} - -impl DetectEnvelope -where - S: Signal, - D: envelope::Detect, -{ - /// Set the **Detector**'s attack time as a number of frames. - pub fn set_attack_frames(&mut self, frames: f32) { - self.detector.set_attack_frames(frames); - } - - /// Set the **Detector**'s release time as a number of frames. - pub fn set_release_frames(&mut self, frames: f32) { - self.detector.set_release_frames(frames); - } - - /// Consumes `Self` and returns the inner signal `S` and `Detector`. - pub fn into_parts(self) -> (S, envelope::Detector) { - let DetectEnvelope { signal, detector } = self; - (signal, detector) - } -} - -impl Signal for DetectEnvelope -where - S: Signal, - D: envelope::Detect, -{ - type Frame = D::Output; - fn next(&mut self) -> Self::Frame { - self.detector.next(self.signal.next()) - } - - fn is_exhausted(&self) -> bool { - self.signal.is_exhausted() - } -} diff --git a/dasp_signal/src/ops.rs b/dasp_signal/src/ops.rs new file mode 100644 index 00000000..5df09ba2 --- /dev/null +++ b/dasp_signal/src/ops.rs @@ -0,0 +1,33 @@ +pub mod f64 { + #[allow(unused_imports)] + use core; + + #[cfg(not(feature = "std"))] + pub fn floor(x: f64) -> f64 { + unsafe { core::intrinsics::floorf64(x) } + } + #[cfg(feature = "std")] + pub fn floor(x: f64) -> f64 { + x.floor() + } + + #[cfg(not(feature = "std"))] + #[allow(dead_code)] + pub fn ceil(x: f64) -> f64 { + unsafe { core::intrinsics::ceilf64(x) } + } + #[cfg(feature = "std")] + #[allow(dead_code)] + pub fn ceil(x: f64) -> f64 { + x.ceil() + } + + #[cfg(not(feature = "std"))] + pub fn sin(x: f64) -> f64 { + unsafe { core::intrinsics::sinf64(x) } + } + #[cfg(feature = "std")] + pub fn sin(x: f64) -> f64 { + x.sin() + } +} diff --git a/dasp_signal/src/rms.rs b/dasp_signal/src/rms.rs new file mode 100644 index 00000000..461d8439 --- /dev/null +++ b/dasp_signal/src/rms.rs @@ -0,0 +1,120 @@ +//! An extension to the **Signal** trait that monitors the RMS of a signal. +//! +//! ### Required Features +//! +//! - When using `dasp_signal`, this module requires the **rms** feature to be enabled. +//! - When using `dasp`, this module requires the **signal-rms** feature to be enabled. + +use crate::Signal; +use dasp_frame::Frame; +use dasp_ring_buffer as ring_buffer; +use dasp_rms as rms; + +/// An extension to the **Signal** trait that monitors the RMS of a signal. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **rms** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-rms** feature to be enabled. +pub trait SignalRms: Signal { + /// An adaptor that yields the RMS of the signal. + /// + /// The window size of the RMS detector is equal to the given ring buffer length. + /// + /// # Example + /// + /// ``` + /// use dasp_ring_buffer as ring_buffer; + /// use dasp_signal::{self as signal, Signal}; + /// use dasp_signal::rms::SignalRms; + /// + /// fn main() { + /// let frames = [[0.9], [-0.8], [0.6], [-0.9]]; + /// let signal = signal::from_iter(frames.iter().cloned()); + /// let ring_buffer = ring_buffer::Fixed::from([[0.0]; 2]); + /// let mut rms_signal = signal.rms(ring_buffer); + /// assert_eq!( + /// [rms_signal.next(), rms_signal.next(), rms_signal.next()], + /// [[0.6363961030678927], [0.8514693182963201], [0.7071067811865476]] + /// ); + /// } + /// ``` + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **rms** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-rms** feature to be enabled. + fn rms(self, ring_buffer: ring_buffer::Fixed) -> Rms + where + Self: Sized, + S: ring_buffer::Slice::Float> + ring_buffer::SliceMut, + { + Rms { + signal: self, + rms: rms::Rms::new(ring_buffer), + } + } +} + +/// An adaptor that yields the RMS of the signal. +/// +/// The window size of the RMS detector is equal to the given ring buffer length. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **rms** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-rms** feature to be enabled. +#[derive(Clone)] +pub struct Rms +where + S: Signal, + D: ring_buffer::Slice::Float>, +{ + signal: S, + rms: rms::Rms, +} + +impl Rms +where + S: Signal, + D: ring_buffer::Slice::Float> + ring_buffer::SliceMut, +{ + /// The same as `Signal::next` but does not calculate the final square root required to + /// determine the RMS. + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **rms** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-rms** feature to be enabled. + pub fn next_squared(&mut self) -> ::Frame { + self.rms.next_squared(self.signal.next()) + } + + /// Consumes the `Rms` signal and returns its inner signal `S` and `Rms` detector. + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **rms** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-rms** feature to be enabled. + pub fn into_parts(self) -> (S, rms::Rms) { + let Rms { signal, rms } = self; + (signal, rms) + } +} + +impl Signal for Rms +where + S: Signal, + D: ring_buffer::Slice::Float> + ring_buffer::SliceMut, +{ + type Frame = ::Float; + fn next(&mut self) -> Self::Frame { + self.rms.next(self.signal.next()) + } + + fn is_exhausted(&self) -> bool { + self.signal.is_exhausted() + } +} + +impl SignalRms for T where T: Signal {} diff --git a/dasp_signal/src/window/hanning.rs b/dasp_signal/src/window/hanning.rs new file mode 100644 index 00000000..a4dc3545 --- /dev/null +++ b/dasp_signal/src/window/hanning.rs @@ -0,0 +1,31 @@ +use super::{Window, Windower}; +use dasp_frame::Frame; +use dasp_window::Hanning; + +impl<'a, F> Windower<'a, F, Hanning> +where + F: 'a + Frame, +{ + /// Constructor for a `Windower` using the `Hanning` window function. + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **window-hanning** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-window-hanning** feature to be enabled. + pub fn hanning(frames: &'a [F], bin: usize, hop: usize) -> Self { + Windower::new(frames, bin, hop) + } +} + +/// A helper function for constructing a `Window` that uses a `Hanning` `Type` function. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **window-hanning** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-window-hanning** feature to be enabled. +pub fn hanning(num_frames: usize) -> Window +where + F: Frame, +{ + Window::new(num_frames) +} diff --git a/src/window.rs b/dasp_signal/src/window/mod.rs similarity index 55% rename from src/window.rs rename to dasp_signal/src/window/mod.rs index 87040257..8e1d0b4d 100644 --- a/src/window.rs +++ b/dasp_signal/src/window/mod.rs @@ -1,48 +1,50 @@ -//! Module for windowing over a batch of Frames. Includes default Hanning and Rectangle window -//! types. +//! Items to ease the application of windowing functions to signals. -use {FloatSample, Sample}; -use core; +use crate::{ConstHz, FromIterator, Phase, Signal}; use core::marker::PhantomData; -use frame::Frame; -use signal::{self, Signal}; +use dasp_frame::Frame; +use dasp_sample::Sample; +use dasp_window::Window as WindowType; -/// The window function used within a `Window`. -pub trait Type { - /// Returns the amplitude for the given phase, given as some `Sample` type. - fn at_phase(phase: S) -> S; -} - -/// A type of window function, also known as the "raised cosine window". -/// -/// [Wiki entry](https://en.wikipedia.org/wiki/Window_function#Hann_.28Hanning.29_window). -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Hanning; +#[cfg(feature = "window-hanning")] +pub use hanning::hanning; +#[cfg(feature = "window-rectangle")] +pub use rectangle::rectangle; -/// The simplest window type, equivalent to replacing all but *N* values of data sequence by -/// zeroes, making it appear as though the waveform suddenly turns on and off. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Rectangle; +#[cfg(feature = "window-hanning")] +mod hanning; +#[cfg(feature = "window-rectangle")] +mod rectangle; /// A `Signal` type that for every yielded `phase`, yields the amplitude across the `window::Type` /// for that phase. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **window** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-window** feature to be enabled. #[derive(Clone)] pub struct Window where F: Frame, - W: Type, + W: WindowType, { /// Yields phase stepped at a constant rate to be passed to the window function `W`. - pub phase: signal::Phase, + pub phase: Phase, marker: PhantomData<(F, W)>, } /// Takes a long slice of frames and yields `Windowed` chunks of size `bin` once every `hop` frames. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **window** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-window** feature to be enabled. #[derive(Clone)] pub struct Windower<'a, F, W> where F: 'a + Frame, - W: Type, + W: WindowType, { /// The size of each `Windowed` chunk to be yielded. pub bin: usize, @@ -56,56 +58,52 @@ where /// An Iterator that multiplies a Signal with a Window. /// /// Returns `None` once the `Window` has been exhausted. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **window** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-window** feature to be enabled. #[derive(Clone)] pub struct Windowed where S: Signal, - W: Type, + W: WindowType, { signal: S, window: Window<::Float, W>, } - -impl Type for Hanning { - fn at_phase(phase: S) -> S { - const PI_2: f64 = core::f64::consts::PI * 2.0; - let v = phase.to_float_sample().to_sample() * PI_2; - (0.5 * (1.0 - super::ops::f64::cos(v))) - .to_sample::() - .to_sample::() - } -} - -impl Type for Rectangle { - fn at_phase(_phase: S) -> S { - ::identity().to_sample::() - } -} - - impl Window where F: Frame, - W: Type, + W: WindowType, { /// Construct a new `Window` with the given length as a number of frames. + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **window** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-window** feature to be enabled. pub fn new(len: usize) -> Self { - let step = signal::rate(len as f64 - 1.0).const_hz(1.0); + let step = crate::rate(len as f64 - 1.0).const_hz(1.0); Window { - phase: signal::phase(step), + phase: crate::phase(step), marker: PhantomData, } } } - impl<'a, F, W> Windower<'a, F, W> where F: 'a + Frame, - W: Type, + W: WindowType, { /// Constructor for a new `Windower` iterator. + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **window** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-window** feature to be enabled. pub fn new(frames: &'a [F], bin: usize, hop: usize) -> Self { Windower { bin: bin, @@ -116,36 +114,15 @@ where } } -impl<'a, F> Windower<'a, F, Rectangle> -where - F: 'a + Frame, -{ - /// Constructor for a `Windower` using the `Rectangle` window function. - pub fn rectangle(frames: &'a [F], bin: usize, hop: usize) -> Self { - Windower::new(frames, bin, hop) - } -} - -impl<'a, F> Windower<'a, F, Hanning> -where - F: 'a + Frame, -{ - /// Constructor for a `Windower` using the `Hanning` window function. - pub fn hanning(frames: &'a [F], bin: usize, hop: usize) -> Self { - Windower::new(frames, bin, hop) - } -} - - impl Iterator for Window where F: Frame, - W: Type, + W: WindowType, { type Item = F; fn next(&mut self) -> Option { - let v = W::at_phase(self.phase.next_phase()); + let v = W::window(self.phase.next_phase()); let v_f: ::Float = v.to_sample(); Some(F::from_fn(|_| v_f.to_sample::())) } @@ -154,9 +131,9 @@ where impl<'a, F, W> Iterator for Windower<'a, F, W> where F: 'a + Frame, - W: Type, + W: WindowType, { - type Item = Windowed>>, W>; + type Item = Windowed>>, W>; fn next(&mut self) -> Option { let num_frames = self.frames.len(); @@ -169,7 +146,7 @@ where &[] }; Some(Windowed { - signal: signal::from_iter(frames.iter().cloned()), + signal: crate::from_iter(frames.iter().cloned()), window: window, }) } else { @@ -198,7 +175,7 @@ where impl Iterator for Windowed where S: Signal, - W: Type, + W: WindowType, { type Item = S::Frame; fn next(&mut self) -> Option { @@ -208,19 +185,3 @@ where }) } } - -/// A helper function for constructing a `Window` that uses a `Hanning` `Type` function. -pub fn hanning(num_frames: usize) -> Window -where - F: Frame, -{ - Window::new(num_frames) -} - -/// A helper function for constructing a `Window` that uses a `Rectangle` `Type` function. -pub fn rectangle(num_frames: usize) -> Window -where - F: Frame, -{ - Window::new(num_frames) -} diff --git a/dasp_signal/src/window/rectangle.rs b/dasp_signal/src/window/rectangle.rs new file mode 100644 index 00000000..9ad33e39 --- /dev/null +++ b/dasp_signal/src/window/rectangle.rs @@ -0,0 +1,31 @@ +use super::{Window, Windower}; +use dasp_frame::Frame; +use dasp_window::Rectangle; + +impl<'a, F> Windower<'a, F, Rectangle> +where + F: 'a + Frame, +{ + /// Constructor for a `Windower` using the `Rectangle` window function. + /// + /// ### Required Features + /// + /// - When using `dasp_signal`, this item requires the **window-rectangle** feature to be enabled. + /// - When using `dasp`, this item requires the **signal-window-rectangle** feature to be enabled. + pub fn rectangle(frames: &'a [F], bin: usize, hop: usize) -> Self { + Windower::new(frames, bin, hop) + } +} + +/// A helper function for constructing a `Window` that uses a `Rectangle` `Type` function. +/// +/// ### Required Features +/// +/// - When using `dasp_signal`, this item requires the **window-rectangle** feature to be enabled. +/// - When using `dasp`, this item requires the **signal-window-rectangle** feature to be enabled. +pub fn rectangle(num_frames: usize) -> Window +where + F: Frame, +{ + Window::new(num_frames) +} diff --git a/tests/interpolate.rs b/dasp_signal/tests/interpolate.rs similarity index 54% rename from tests/interpolate.rs rename to dasp_signal/tests/interpolate.rs index ec4f2f8f..065ad74d 100644 --- a/tests/interpolate.rs +++ b/dasp_signal/tests/interpolate.rs @@ -1,71 +1,73 @@ //! Tests for the `Converter` and `Interpolator` traits -extern crate sample; - -use sample::interpolate::{Converter, Floor, Linear, Sinc}; -use sample::ring_buffer; -use sample::{signal, Signal}; +use dasp_interpolate::{floor::Floor, linear::Linear, sinc::Sinc}; +use dasp_ring_buffer as ring_buffer; +use dasp_signal::{self as signal, interpolate::Converter, Signal}; #[test] fn test_floor_converter() { - let frames: [[f64; 1]; 3] = [[0.0], [1.0], [2.0]]; + let frames: [f64; 3] = [0.0, 1.0, 2.0]; let mut source = signal::from_iter(frames.iter().cloned()); - let interp = Floor::from_source(&mut source); + let interp = Floor::new(source.next()); let mut conv = Converter::scale_playback_hz(source, interp, 0.5); - assert_eq!(conv.next(), [0.0]); - assert_eq!(conv.next(), [0.0]); - assert_eq!(conv.next(), [1.0]); - assert_eq!(conv.next(), [1.0]); + assert_eq!(conv.next(), 0.0); + assert_eq!(conv.next(), 0.0); + assert_eq!(conv.next(), 1.0); + assert_eq!(conv.next(), 1.0); // It may seem odd that we are emitting two values, but consider this: no matter what the next // value would be, Floor would always yield the same frame until we hit an interpolation_value // of 1.0 and had to advance the frame. We don't know what the future holds, so we should // continue yielding frames. - assert_eq!(conv.next(), [2.0]); - assert_eq!(conv.next(), [2.0]); + assert_eq!(conv.next(), 2.0); + assert_eq!(conv.next(), 2.0); } #[test] fn test_linear_converter() { - let frames: [[f64; 1]; 3] = [[0.0], [1.0], [2.0]]; + let frames: [f64; 3] = [0.0, 1.0, 2.0]; let mut source = signal::from_iter(frames.iter().cloned()); - let interp = Linear::from_source(&mut source); + let a = source.next(); + let b = source.next(); + let interp = Linear::new(a, b); let mut conv = Converter::scale_playback_hz(source, interp, 0.5); - assert_eq!(conv.next(), [0.0]); - assert_eq!(conv.next(), [0.5]); - assert_eq!(conv.next(), [1.0]); - assert_eq!(conv.next(), [1.5]); - assert_eq!(conv.next(), [2.0]); + assert_eq!(conv.next(), 0.0); + assert_eq!(conv.next(), 0.5); + assert_eq!(conv.next(), 1.0); + assert_eq!(conv.next(), 1.5); + assert_eq!(conv.next(), 2.0); // There's nothing else here to interpolate toward, but we do want to ensure that we're // emitting the correct number of frames. - assert_eq!(conv.next(), [1.0]); + assert_eq!(conv.next(), 1.0); } #[test] fn test_scale_playback_rate() { // Scale the playback rate by `0.5` - let foo = [[0.0], [1.0], [0.0], [-1.0]]; + let foo = [0.0, 1.0, 0.0, -1.0]; let mut source = signal::from_iter(foo.iter().cloned()); - let interp = Linear::from_source(&mut source); + let a = source.next(); + let b = source.next(); + let interp = Linear::new(a, b); let frames: Vec<_> = source.scale_hz(interp, 0.5).take(8).collect(); assert_eq!( &frames[..], - &[[0.0], [0.5], [1.0], [0.5], [0.0], [-0.5], [-1.0], [-0.5]][..] + &[0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -0.5][..] ); } #[test] fn test_sinc() { - let foo = [[0.0f64], [1.0], [0.0], [-1.0]]; + let foo = [0f64, 1.0, 0.0, -1.0]; let source = signal::from_iter(foo.iter().cloned()); - let frames = ring_buffer::Fixed::from(vec![[0.0]; 50]); + let frames = ring_buffer::Fixed::from(vec![0.0; 50]); let interp = Sinc::new(frames); let resampled = source.from_hz_to_hz(interp, 44100.0, 11025.0); assert_eq!( - resampled.until_exhausted().find(|sample| sample[0].is_nan()), + resampled.until_exhausted().find(|sample| sample.is_nan()), None ); -} \ No newline at end of file +} diff --git a/tests/signal.rs b/dasp_signal/tests/signal.rs similarity index 51% rename from tests/signal.rs rename to dasp_signal/tests/signal.rs index 94a46803..146789df 100644 --- a/tests/signal.rs +++ b/dasp_signal/tests/signal.rs @@ -1,33 +1,31 @@ //! Tests for the `Signal` trait. -extern crate sample; - -use sample::{signal, Signal}; +use dasp_signal::{self as signal, Signal}; #[test] fn test_equilibrium() { - let equilibrium: Vec<[i8; 1]> = sample::signal::equilibrium().take(4).collect(); - assert_eq!(equilibrium, vec![[0], [0], [0], [0]]); + let equilibrium: Vec = signal::equilibrium().take(4).collect(); + assert_eq!(equilibrium, vec![0, 0, 0, 0]); } #[test] fn test_scale_amp() { - let foo = [[0.5], [0.8], [-0.4], [-0.2]]; + let foo = [0.5, 0.8, -0.4, -0.2]; let amp = 0.5; let amp_scaled: Vec<_> = signal::from_iter(foo.iter().cloned()) .scale_amp(amp) .take(4) .collect(); - assert_eq!(amp_scaled, vec![[0.25], [0.4], [-0.2], [-0.1]]); + assert_eq!(amp_scaled, vec![0.25, 0.4, -0.2, -0.1]); } #[test] fn test_offset_amp() { - let foo = [[0.5], [0.9], [-0.4], [-0.2]]; + let foo = [0.5, 0.9, -0.4, -0.2]; let amp = -0.5; let amp_offset: Vec<_> = signal::from_iter(foo.iter().cloned()) .offset_amp(amp) .take(4) .collect(); - assert_eq!(amp_offset, vec![[0.0], [0.4], [-0.9], [-0.7]]); + assert_eq!(amp_offset, vec![0.0, 0.4, -0.9, -0.7]); } diff --git a/tests/window.rs b/dasp_signal/tests/window.rs similarity index 51% rename from tests/window.rs rename to dasp_signal/tests/window.rs index ba4ab14c..b199fc98 100644 --- a/tests/window.rs +++ b/dasp_signal/tests/window.rs @@ -1,40 +1,32 @@ -extern crate sample; +#![cfg(feature = "window")] -use sample::frame::Frame; -use sample::window::Windower; +use dasp_frame::Frame; +use dasp_signal::window::{self, Windower}; +#[cfg(feature = "window-hanning")] #[test] fn test_window_at_phase() { - let window = sample::window::hanning::<[f64; 1]>(9); + let window = window::hanning::(9); let expected = [ - 0.0, - 0.1464, - 0.5000, - 0.8536, - 1., - 0.8536, - 0.5000, - 0.1464, - 0., - 0.1464, + 0.0, 0.1464, 0.5000, 0.8536, 1., 0.8536, 0.5000, 0.1464, 0., 0.1464, ]; for (r, e) in window.zip(&expected) { - println!("Expected: {}\t\tFound: {}", e, r[0]); - assert!((r[0] - e).abs() < 0.001); + println!("Expected: {}\t\tFound: {}", e, r); + assert!((r - e).abs() < 0.001); } } #[test] fn test_windower() { - let data = [[0.1f64], [0.1], [0.2], [0.2], [0.3], [0.3], [0.4], [0.4]]; + let data = [0.1f64, 0.1, 0.2, 0.2, 0.3, 0.3, 0.4, 0.4]; let expected = [ - [[0.1], [0.1]], - [[0.1], [0.2]], - [[0.2], [0.2]], - [[0.2], [0.3]], - [[0.3], [0.3]], - [[0.3], [0.4]], - [[0.4], [0.4]], + [0.1f64, 0.1], + [0.1, 0.2], + [0.2, 0.2], + [0.2, 0.3], + [0.3, 0.3], + [0.3, 0.4], + [0.4, 0.4], ]; let windower = Windower::rectangle(&data, 2, 1); for (chunk, expected_chunk) in windower.zip(&expected) { @@ -47,11 +39,12 @@ fn test_windower() { } } +#[cfg(feature = "window-hanning")] #[test] fn test_window_size() { - let v = [[1f32; 1]; 16]; + let v = [1f32; 16]; let windows: Vec> = Windower::hanning(&v, 8, 4) - .map(|i| i.take(8).collect::>()) + .map(|i| i.take(8).collect::>()) .take(3) .collect(); assert_eq!(windows.len(), 3); diff --git a/dasp_slice/Cargo.toml b/dasp_slice/Cargo.toml new file mode 100644 index 00000000..04933d80 --- /dev/null +++ b/dasp_slice/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "dasp_slice" +description = "Conversions and operations for slices of audio PCM DSP samples and frames." +version = "0.11.0" +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["dsp", "bit-depth", "rate", "pcm", "audio"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/dasp.git" +homepage = "https://github.com/rustaudio/dasp" +edition = "2018" + +[dependencies] +dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } +dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } + +[features] +default = ["std"] +all = ["std", "all-no-std"] +all-no-std = [ + "boxed", +] +std = [ + "dasp_sample/std", + "dasp_frame/std", +] +boxed = [] + +[package.metadata.docs.rs] +all-features = true diff --git a/dasp_slice/src/boxed.rs b/dasp_slice/src/boxed.rs new file mode 100644 index 00000000..e508b3c7 --- /dev/null +++ b/dasp_slice/src/boxed.rs @@ -0,0 +1,294 @@ +//! Items related to boxed-slice conversions. +//! +//! ### Required Features +//! +//! - When using `dasp_slice`, this module requires the **boxed** feature to be enabled. +//! - When using `dasp`, this module requires the **slice-boxed** feature to be enabled. + +#[cfg(not(feature = "std"))] +extern crate alloc; + +use dasp_frame::Frame; +use dasp_sample::Sample; + +/// Equal to `std::boxed::Box` on std, `alloc::boxed::Box` in `no_std` context. +#[cfg(not(feature = "std"))] +pub type Box = alloc::boxed::Box; +/// Equal to `std::boxed::Box` on std, `alloc::boxed::Box` in `no_std` context. +#[cfg(feature = "std")] +pub type Box = std::boxed::Box; + +// Traits +// ---------------------------------------------------------------------------- + +/// For converting a boxed slice of `Sample`s to a boxed slice of `Frame`s. +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub trait FromBoxedSampleSlice: Sized +where + S: Sample, +{ + fn from_boxed_sample_slice(slice: Box<[S]>) -> Option; +} + +/// For converting from a boxed slice of `Frame`s to a boxed slice of `Sample`s. +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub trait FromBoxedFrameSlice +where + F: Frame, +{ + fn from_boxed_frame_slice(slice: Box<[F]>) -> Self; +} + +/// For converting from a boxed slice of `Frame`s to a boxed slice of `Sample`s. +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub trait ToBoxedSampleSlice +where + S: Sample, +{ + fn to_boxed_sample_slice(self) -> Box<[S]>; +} + +/// For converting from a boxed slice of `Sample`s to a boxed slice of `Frame`s. +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub trait ToBoxedFrameSlice +where + F: Frame, +{ + fn to_boxed_frame_slice(self) -> Option>; +} + +/// For converting to and from a boxed slice of `Sample`s. +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub trait DuplexBoxedSampleSlice: FromBoxedSampleSlice + ToBoxedSampleSlice +where + S: Sample, +{ +} + +/// For converting to and from a boxed slice of `Frame`s. +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub trait DuplexBoxedFrameSlice: FromBoxedFrameSlice + ToBoxedFrameSlice +where + F: Frame, +{ +} + +/// For converting to and from a boxed slice of `Sample`s of type `S` and a slice of `Frame`s of +/// type `F`. +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub trait DuplexBoxedSlice: DuplexBoxedSampleSlice + DuplexBoxedFrameSlice +where + S: Sample, + F: Frame, +{ +} + +// Implementations +// ---------------------------------------------------------------------------- + +impl FromBoxedSampleSlice for Box<[S]> +where + S: Sample, +{ + #[inline] + fn from_boxed_sample_slice(slice: Box<[S]>) -> Option { + Some(slice) + } +} + +impl FromBoxedFrameSlice for Box<[F]> +where + F: Frame, +{ + #[inline] + fn from_boxed_frame_slice(slice: Box<[F]>) -> Self { + slice + } +} + +impl ToBoxedSampleSlice for Box<[S]> +where + S: Sample, +{ + #[inline] + fn to_boxed_sample_slice(self) -> Box<[S]> { + self + } +} + +impl ToBoxedFrameSlice for Box<[F]> +where + F: Frame, +{ + #[inline] + fn to_boxed_frame_slice(self) -> Option> { + Some(self) + } +} + +impl DuplexBoxedSampleSlice for T +where + S: Sample, + T: FromBoxedSampleSlice + ToBoxedSampleSlice, +{ +} + +impl DuplexBoxedFrameSlice for T +where + F: Frame, + T: FromBoxedFrameSlice + ToBoxedFrameSlice, +{ +} + +impl DuplexBoxedSlice for T +where + S: Sample, + F: Frame, + T: DuplexBoxedSampleSlice + DuplexBoxedFrameSlice, +{ +} + +// Free Functions +// ---------------------------------------------------------------------------- + +/// Converts the given boxed slice into a boxed slice of `Sample`s. +/// +/// This is a convenience function that wraps the `ToBoxedSampleSlice` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = vec![[0.0, 0.5], [0.0, -0.5]].into_boxed_slice(); +/// let bar = dasp_slice::to_boxed_sample_slice(foo); +/// assert_eq!(bar.into_vec(), vec![0.0, 0.5, 0.0, -0.5]); +/// } +/// ``` +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub fn to_boxed_sample_slice(slice: T) -> Box<[S]> +where + S: Sample, + T: ToBoxedSampleSlice, +{ + slice.to_boxed_sample_slice() +} + +/// Converts the given boxed slice into a boxed slice of `Frame`s. +/// +/// Returns `None` if the number of channels in a single frame `F` is not a multiple of the number +/// of samples in the given slice. +/// +/// This is a convenience function that wraps the `ToBoxedFrameSlice` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = vec![0.0, 0.5, 0.0, -0.5].into_boxed_slice(); +/// let bar: Box<[[f32; 2]]> = dasp_slice::to_boxed_frame_slice(foo).unwrap(); +/// assert_eq!(bar.into_vec(), vec![[0.0, 0.5], [0.0, -0.5]]); +/// +/// let foo = vec![0.0, 0.5, 0.0].into_boxed_slice(); +/// let bar = dasp_slice::to_boxed_frame_slice(foo); +/// assert_eq!(bar, None::>); +/// } +/// ``` +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub fn to_boxed_frame_slice(slice: T) -> Option> +where + F: Frame, + T: ToBoxedFrameSlice, +{ + slice.to_boxed_frame_slice() +} + +/// Converts the given boxed slice of `Sample`s into some slice `T`. +/// +/// Returns `None` if the number of channels in a single frame is not a multiple of the number of +/// samples in the given slice. +/// +/// This is a convenience function that wraps the `FromBoxedSampleSlice` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = vec![0.0, 0.5, 0.0, -0.5].into_boxed_slice(); +/// let bar: Box<[[f32; 2]]> = dasp_slice::from_boxed_sample_slice(foo).unwrap(); +/// assert_eq!(bar.into_vec(), vec![[0.0, 0.5], [0.0, -0.5]]); +/// } +/// ``` +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub fn from_boxed_sample_slice(slice: Box<[S]>) -> Option +where + S: Sample, + T: FromBoxedSampleSlice, +{ + T::from_boxed_sample_slice(slice) +} + +/// Converts the given boxed slice of `Frame`s into some slice `T`. +/// +/// This is a convenience function that wraps the `FromBoxedFrameSlice` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = vec![[0.0, 0.5], [0.0, -0.5]].into_boxed_slice(); +/// let bar: Box<[f32]> = dasp_slice::from_boxed_frame_slice(foo); +/// assert_eq!(bar.into_vec(), vec![0.0, 0.5, 0.0, -0.5]); +/// } +/// ``` +/// +/// ### Required Features +/// +/// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. +/// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. +pub fn from_boxed_frame_slice(slice: Box<[F]>) -> T +where + F: Frame, + T: FromBoxedFrameSlice, +{ + T::from_boxed_frame_slice(slice) +} diff --git a/dasp_slice/src/frame/fixed_size_array.rs b/dasp_slice/src/frame/fixed_size_array.rs new file mode 100644 index 00000000..3724066a --- /dev/null +++ b/dasp_slice/src/frame/fixed_size_array.rs @@ -0,0 +1,210 @@ +//! Implementations of the slice conversion traits for slices of fixed-size-array frames. + +#[cfg(feature = "boxed")] +use crate::boxed::{ + Box, FromBoxedFrameSlice, FromBoxedSampleSlice, ToBoxedFrameSlice, ToBoxedSampleSlice, +}; +use crate::{ + FromFrameSlice, FromFrameSliceMut, FromSampleSlice, FromSampleSliceMut, ToFrameSlice, + ToFrameSliceMut, ToSampleSlice, ToSampleSliceMut, +}; +use dasp_frame::Frame; +use dasp_sample::Sample; + +/// A macro for implementing all audio slice conversion traits for each fixed-size array. +macro_rules! impl_from_slice_conversions { + ($($N:expr)*) => { + $( + impl<'a, S> FromSampleSlice<'a, S> for &'a [[S; $N]] + where + S: Sample, + [S; $N]: Frame, + { + #[inline] + fn from_sample_slice(slice: &'a [S]) -> Option { + let len = slice.len(); + if len % $N == 0 { + let new_len = len / $N; + let ptr = slice.as_ptr() as *const _; + let new_slice = unsafe { + core::slice::from_raw_parts(ptr, new_len) + }; + Some(new_slice) + } else { + None + } + } + } + + impl<'a, S> FromSampleSliceMut<'a, S> for &'a mut [[S; $N]] + where + S: Sample, + [S; $N]: Frame, + { + #[inline] + fn from_sample_slice_mut(slice: &'a mut [S]) -> Option { + let len = slice.len(); + if len % $N == 0 { + let new_len = len / $N; + let ptr = slice.as_ptr() as *mut _; + let new_slice = unsafe { + core::slice::from_raw_parts_mut(ptr, new_len) + }; + Some(new_slice) + } else { + None + } + } + } + + impl<'a, S> FromFrameSlice<'a, [S; $N]> for &'a [S] + where + [S; $N]: Frame, + { + #[inline] + fn from_frame_slice(slice: &'a [[S; $N]]) -> Self { + let new_len = slice.len() * $N; + let ptr = slice.as_ptr() as *const _; + unsafe { + core::slice::from_raw_parts(ptr, new_len) + } + } + } + + impl<'a, S> FromFrameSliceMut<'a, [S; $N]> for &'a mut [S] + where + [S; $N]: Frame, + { + #[inline] + fn from_frame_slice_mut(slice: &'a mut [[S; $N]]) -> Self { + let new_len = slice.len() * $N; + let ptr = slice.as_ptr() as *mut _; + unsafe { + core::slice::from_raw_parts_mut(ptr, new_len) + } + } + } + + impl<'a, S> ToSampleSlice<'a, S> for &'a [[S; $N]] + where + S: Sample, + { + #[inline] + fn to_sample_slice(self) -> &'a [S] { + FromFrameSlice::from_frame_slice(self) + } + } + + impl<'a, S> ToSampleSliceMut<'a, S> for &'a mut [[S; $N]] + where + S: Sample, + { + #[inline] + fn to_sample_slice_mut(self) -> &'a mut [S] { + FromFrameSliceMut::from_frame_slice_mut(self) + } + } + + impl<'a, S> ToFrameSlice<'a, [S; $N]> for &'a [S] + where + S: Sample, + [S; $N]: Frame, + { + #[inline] + fn to_frame_slice(self) -> Option<&'a [[S; $N]]> { + FromSampleSlice::from_sample_slice(self) + } + } + + impl<'a, S> ToFrameSliceMut<'a, [S; $N]> for &'a mut [S] + where + S: Sample, + [S; $N]: Frame, + { + #[inline] + fn to_frame_slice_mut(self) -> Option<&'a mut [[S; $N]]> { + FromSampleSliceMut::from_sample_slice_mut(self) + } + } + + #[cfg(feature = "boxed")] + impl FromBoxedSampleSlice for Box<[[S; $N]]> + where + S: Sample, + [S; $N]: Frame, + { + #[inline] + fn from_boxed_sample_slice(mut slice: Box<[S]>) -> Option { + // First, we need a raw pointer to the slice and to make sure that the `Box` is + // forgotten so that our slice does not get deallocated. + let len = slice.len(); + let slice_ptr = &mut slice as &mut [S] as *mut [S]; + core::mem::forget(slice); + let sample_slice = unsafe { + core::slice::from_raw_parts_mut((*slice_ptr).as_mut_ptr(), len) + }; + + // Convert to our frame slice if possible. + let frame_slice = match <&mut [[S; $N]]>::from_sample_slice_mut(sample_slice) { + Some(slice) => slice, + None => return None, + }; + let ptr = frame_slice as *mut [[S; $N]]; + + // Take ownership over the slice again before returning it. + let new_slice = unsafe { + Box::from_raw(ptr) + }; + + Some(new_slice) + } + } + + #[cfg(feature = "boxed")] + impl FromBoxedFrameSlice<[S; $N]> for Box<[S]> + where + [S; $N]: Frame, + { + #[inline] + fn from_boxed_frame_slice(mut slice: Box<[[S; $N]]>) -> Self { + let new_len = slice.len() * $N; + let frame_slice_ptr = &mut slice as &mut [[S; $N]] as *mut [[S; $N]]; + core::mem::forget(slice); + let sample_slice_ptr = frame_slice_ptr as *mut [S]; + unsafe { + let ptr = (*sample_slice_ptr).as_mut_ptr(); + let sample_slice = core::slice::from_raw_parts_mut(ptr, new_len); + Box::from_raw(sample_slice as *mut _) + } + } + } + + #[cfg(feature = "boxed")] + impl ToBoxedSampleSlice for Box<[[S; $N]]> + where + S: Sample, + { + #[inline] + fn to_boxed_sample_slice(self) -> Box<[S]> { + FromBoxedFrameSlice::from_boxed_frame_slice(self) + } + } + + #[cfg(feature = "boxed")] + impl ToBoxedFrameSlice<[S; $N]> for Box<[S]> + where + S: Sample, + [S; $N]: Frame, + { + #[inline] + fn to_boxed_frame_slice(self) -> Option> { + FromBoxedSampleSlice::from_boxed_sample_slice(self) + } + } + )* + }; +} + +impl_from_slice_conversions! { + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 +} diff --git a/dasp_slice/src/frame/mod.rs b/dasp_slice/src/frame/mod.rs new file mode 100644 index 00000000..99f90f58 --- /dev/null +++ b/dasp_slice/src/frame/mod.rs @@ -0,0 +1,201 @@ +use dasp_frame::Frame; + +mod fixed_size_array; + +/// For converting from a slice of `Frame`s to a slice of `Sample`s. +pub trait FromFrameSlice<'a, F> +where + F: Frame, +{ + fn from_frame_slice(slice: &'a [F]) -> Self; +} + +/// For converting from a slice of `Frame`s to a slice of `Sample`s. +pub trait FromFrameSliceMut<'a, F> +where + F: Frame, +{ + fn from_frame_slice_mut(slice: &'a mut [F]) -> Self; +} + +/// For converting from a slice of `Sample`s to a slice of `Frame`s. +pub trait ToFrameSlice<'a, F> +where + F: Frame, +{ + fn to_frame_slice(self) -> Option<&'a [F]>; +} + +/// For converting from a mutable slice of `Sample`s to a mutable slice of `Frame`s. +pub trait ToFrameSliceMut<'a, F> +where + F: Frame, +{ + fn to_frame_slice_mut(self) -> Option<&'a mut [F]>; +} + +/// For converting to and from a slice of `Frame`s. +pub trait DuplexFrameSlice<'a, F>: FromFrameSlice<'a, F> + ToFrameSlice<'a, F> +where + F: Frame, +{ +} + +/// For converting to and from a mutable slice of `Frame`s. +pub trait DuplexFrameSliceMut<'a, F>: FromFrameSliceMut<'a, F> + ToFrameSliceMut<'a, F> +where + F: Frame, +{ +} + +impl<'a, F> FromFrameSlice<'a, F> for &'a [F] +where + F: Frame, +{ + #[inline] + fn from_frame_slice(slice: &'a [F]) -> Self { + slice + } +} + +impl<'a, F> FromFrameSliceMut<'a, F> for &'a mut [F] +where + F: Frame, +{ + #[inline] + fn from_frame_slice_mut(slice: &'a mut [F]) -> Self { + slice + } +} + +impl<'a, F> ToFrameSlice<'a, F> for &'a [F] +where + F: Frame, +{ + #[inline] + fn to_frame_slice(self) -> Option<&'a [F]> { + Some(self) + } +} + +impl<'a, F> ToFrameSliceMut<'a, F> for &'a mut [F] +where + F: Frame, +{ + #[inline] + fn to_frame_slice_mut(self) -> Option<&'a mut [F]> { + Some(self) + } +} + +impl<'a, F, T> DuplexFrameSlice<'a, F> for T +where + F: Frame, + T: FromFrameSlice<'a, F> + ToFrameSlice<'a, F>, +{ +} + +impl<'a, F, T> DuplexFrameSliceMut<'a, F> for T +where + F: Frame, + T: FromFrameSliceMut<'a, F> + ToFrameSliceMut<'a, F>, +{ +} + +/// Converts the given slice into a slice of `Frame`s. +/// +/// Returns `None` if the number of channels in a single frame `F` is not a multiple of the number +/// of samples in the given slice. +/// +/// This is a convenience function that wraps the `ToFrameSlice` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = &[0.0, 0.5, 0.0, -0.5][..]; +/// let bar = dasp_slice::to_frame_slice(foo); +/// assert_eq!(bar, Some(&[[0.0, 0.5], [0.0, -0.5]][..])); +/// +/// let foo = &[0.0, 0.5, 0.0][..]; +/// let bar = dasp_slice::to_frame_slice(foo); +/// assert_eq!(bar, None::<&[[f32; 2]]>); +/// } +/// ``` +pub fn to_frame_slice<'a, T, F>(slice: T) -> Option<&'a [F]> +where + F: Frame, + T: ToFrameSlice<'a, F>, +{ + slice.to_frame_slice() +} + +/// Converts the given mutable slice into a mutable slice of `Frame`s. +/// +/// Returns `None` if the number of channels in a single frame `F` is not a multiple of the number +/// of samples in the given slice. +/// +/// This is a convenience function that wraps the `ToFrameSliceMut` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = &mut [0.0, 0.5, 0.0, -0.5][..]; +/// let bar = dasp_slice::to_frame_slice_mut(foo); +/// assert_eq!(bar, Some(&mut [[0.0, 0.5], [0.0, -0.5]][..])); +/// +/// let foo = &mut [0.0, 0.5, 0.0][..]; +/// let bar = dasp_slice::to_frame_slice_mut(foo); +/// assert_eq!(bar, None::<&mut [[f32; 2]]>); +/// } +/// ``` +pub fn to_frame_slice_mut<'a, T, F>(slice: T) -> Option<&'a mut [F]> +where + F: Frame, + T: ToFrameSliceMut<'a, F>, +{ + slice.to_frame_slice_mut() +} + +/// Converts the given slice of `Frame`s into some slice `T`. +/// +/// This is a convenience function that wraps the `FromFrameSlice` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = &[[0.0, 0.5], [0.0, -0.5]][..]; +/// let bar: &[f32] = dasp_slice::from_frame_slice(foo); +/// assert_eq!(bar, &[0.0, 0.5, 0.0, -0.5][..]); +/// } +/// ``` +pub fn from_frame_slice<'a, T, F>(slice: &'a [F]) -> T +where + F: Frame, + T: FromFrameSlice<'a, F>, +{ + T::from_frame_slice(slice) +} + +/// Converts the given slice of mutable `Frame`s into some mutable slice `T`. +/// +/// This is a convenience function that wraps the `FromFrameSliceMut` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = &mut [[0.0, 0.5], [0.0, -0.5]][..]; +/// let bar: &mut [f32] = dasp_slice::from_frame_slice_mut(foo); +/// assert_eq!(bar, &mut [0.0, 0.5, 0.0, -0.5][..]); +/// } +/// ``` +pub fn from_frame_slice_mut<'a, T, F>(slice: &'a mut [F]) -> T +where + F: Frame, + T: FromFrameSliceMut<'a, F>, +{ + T::from_frame_slice_mut(slice) +} diff --git a/dasp_slice/src/lib.rs b/dasp_slice/src/lib.rs new file mode 100644 index 00000000..8d3264c5 --- /dev/null +++ b/dasp_slice/src/lib.rs @@ -0,0 +1,361 @@ +//! For working with slices of PCM audio data. +//! +//! Items related to conversion between slices of frames and slices of samples, particularly useful +//! for working with interleaved data. +//! +//! ### Optional Features +//! +//! - The **boxed** feature (or **slice-boxed** feature if using `dasp`) provides a suite of boxed +//! slice conversion traits and functions under the [**boxed**](./boxed/index.html) module. +//! +//! ### no_std +//! +//! If working in a `no_std` context, you can disable the default **std** feature with +//! `--no-default-features`. + +#![cfg_attr(not(feature = "std"), no_std)] + +use dasp_frame::Frame; +use dasp_sample::Sample; + +#[cfg(feature = "boxed")] +pub use boxed::{ + from_boxed_frame_slice, from_boxed_sample_slice, to_boxed_frame_slice, to_boxed_sample_slice, + DuplexBoxedFrameSlice, DuplexBoxedSampleSlice, DuplexBoxedSlice, FromBoxedFrameSlice, + FromBoxedSampleSlice, ToBoxedFrameSlice, ToBoxedSampleSlice, +}; + +pub use frame::{ + from_frame_slice, from_frame_slice_mut, to_frame_slice, to_frame_slice_mut, DuplexFrameSlice, + DuplexFrameSliceMut, FromFrameSlice, FromFrameSliceMut, ToFrameSlice, ToFrameSliceMut, +}; + +#[cfg(feature = "boxed")] +pub mod boxed; + +mod frame; + +// Slice Conversion Traits +// ---------------------------------------------------------------------------- + +/// For converting from a slice of `Sample`s to a slice of `Frame`s. +pub trait FromSampleSlice<'a, S>: Sized +where + S: Sample, +{ + fn from_sample_slice(slice: &'a [S]) -> Option; +} + +/// For converting from a mutable slice of `Sample`s to a mutable slice of `Frame`s. +pub trait FromSampleSliceMut<'a, S>: Sized +where + S: Sample, +{ + fn from_sample_slice_mut(slice: &'a mut [S]) -> Option; +} + +/// For converting from a slice of `Frame`s to a slice of `Sample`s. +pub trait ToSampleSlice<'a, S> +where + S: Sample, +{ + fn to_sample_slice(self) -> &'a [S]; +} + +/// For converting from a mutable slice of `Frame`s to a mutable slice of `Sample`s. +pub trait ToSampleSliceMut<'a, S> +where + S: Sample, +{ + fn to_sample_slice_mut(self) -> &'a mut [S]; +} + +/// For converting to and from a slice of `Sample`s. +pub trait DuplexSampleSlice<'a, S>: FromSampleSlice<'a, S> + ToSampleSlice<'a, S> +where + S: Sample, +{ +} + +/// For converting to and from a mutable slice of `Sample`s. +pub trait DuplexSampleSliceMut<'a, S>: FromSampleSliceMut<'a, S> + ToSampleSliceMut<'a, S> +where + S: Sample, +{ +} + +/// For converting to and from a slice of `Sample`s of type `S` and a slice of `Frame`s of type +/// `F`. +pub trait DuplexSlice<'a, S, F>: DuplexSampleSlice<'a, S> + DuplexFrameSlice<'a, F> +where + S: Sample, + F: Frame, +{ +} + +/// For converting to and from a mutable slice of `Sample`s of type `S` and a slice of `Frame`s of +/// type `F`. +pub trait DuplexSliceMut<'a, S, F>: + DuplexSampleSliceMut<'a, S> + DuplexFrameSliceMut<'a, F> +where + S: Sample, + F: Frame, +{ +} + +// Slice Conversion Trait Implementations +// ---------------------------------------------------------------------------- + +impl<'a, S> FromSampleSlice<'a, S> for &'a [S] +where + S: Sample, +{ + #[inline] + fn from_sample_slice(slice: &'a [S]) -> Option { + Some(slice) + } +} + +impl<'a, S> FromSampleSliceMut<'a, S> for &'a mut [S] +where + S: Sample, +{ + #[inline] + fn from_sample_slice_mut(slice: &'a mut [S]) -> Option { + Some(slice) + } +} + +impl<'a, S> ToSampleSlice<'a, S> for &'a [S] +where + S: Sample, +{ + #[inline] + fn to_sample_slice(self) -> &'a [S] { + self + } +} + +impl<'a, S> ToSampleSliceMut<'a, S> for &'a mut [S] +where + S: Sample, +{ + #[inline] + fn to_sample_slice_mut(self) -> &'a mut [S] { + self + } +} + +impl<'a, S, T> DuplexSampleSlice<'a, S> for T +where + S: Sample, + T: FromSampleSlice<'a, S> + ToSampleSlice<'a, S>, +{ +} + +impl<'a, S, T> DuplexSampleSliceMut<'a, S> for T +where + S: Sample, + T: FromSampleSliceMut<'a, S> + ToSampleSliceMut<'a, S>, +{ +} + +impl<'a, S, F, T> DuplexSlice<'a, S, F> for T +where + S: Sample, + F: Frame, + T: DuplexSampleSlice<'a, S> + DuplexFrameSlice<'a, F>, +{ +} + +impl<'a, S, F, T> DuplexSliceMut<'a, S, F> for T +where + S: Sample, + F: Frame, + T: DuplexSampleSliceMut<'a, S> + DuplexFrameSliceMut<'a, F>, +{ +} + +// Conversion Functions +// ---------------------------------------------------------------------------- + +/// Converts the given slice into a slice of `Sample`s. +/// +/// This is a convenience function that wraps the `ToSampleSlice` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = &[[0.0, 0.5], [0.0, -0.5]][..]; +/// let bar = dasp_slice::to_sample_slice(foo); +/// assert_eq!(bar, &[0.0, 0.5, 0.0, -0.5][..]); +/// } +/// ``` +pub fn to_sample_slice<'a, T, S>(slice: T) -> &'a [S] +where + S: Sample, + T: ToSampleSlice<'a, S>, +{ + slice.to_sample_slice() +} + +/// Converts the given mutable slice of `Frame`s into a mutable slice of `Sample`s. +/// +/// This is a convenience function that wraps the `ToSampleSliceMut` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = &mut [[0.0, 0.5], [0.0, -0.5]][..]; +/// let bar = dasp_slice::to_sample_slice_mut(foo); +/// assert_eq!(bar, &mut [0.0, 0.5, 0.0, -0.5][..]); +/// } +/// ``` +pub fn to_sample_slice_mut<'a, T, S>(slice: T) -> &'a mut [S] +where + S: Sample, + T: ToSampleSliceMut<'a, S>, +{ + slice.to_sample_slice_mut() +} + +/// Converts the given slice of `Sample`s into some slice `T`. +/// +/// Returns `None` if the number of channels in a single frame is not a multiple of the number of +/// samples in the given slice. +/// +/// This is a convenience function that wraps the `FromSampleSlice` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = &[0.0, 0.5, 0.0, -0.5][..]; +/// let bar: Option<&_> = dasp_slice::from_sample_slice(foo); +/// assert_eq!(bar, Some(&[[0.0, 0.5], [0.0, -0.5]][..])); +/// } +/// ``` +pub fn from_sample_slice<'a, T, S>(slice: &'a [S]) -> Option +where + S: Sample, + T: FromSampleSlice<'a, S>, +{ + T::from_sample_slice(slice) +} + +/// Converts the given mutable slice of `Sample`s into some mutable slice `T`. +/// +/// Returns `None` if the number of channels in a single frame is not a multiple of the number of +/// samples in the given slice. +/// +/// This is a convenience function that wraps the `FromSampleSliceMut` trait. +/// +/// # Examples +/// +/// ``` +/// fn main() { +/// let foo = &mut [0.0, 0.5, 0.0, -0.5][..]; +/// let bar: Option<&mut _> = dasp_slice::from_sample_slice_mut(foo); +/// assert_eq!(bar, Some(&mut [[0.0, 0.5], [0.0, -0.5]][..])); +/// } +/// ``` +pub fn from_sample_slice_mut<'a, T, S>(slice: &'a mut [S]) -> Option +where + S: Sample, + T: FromSampleSliceMut<'a, S>, +{ + T::from_sample_slice_mut(slice) +} + +///// Utility Functions + +/// Mutate every element in the slice with the given function. +#[inline] +pub fn map_in_place(a: &mut [F], mut map: M) +where + M: FnMut(F) -> F, + F: Frame, +{ + for f in a { + *f = map(*f); + } +} + +/// Sets the slice of frames at the associated `Sample`'s equilibrium value. +#[inline] +pub fn equilibrium(a: &mut [F]) +where + F: Frame, +{ + map_in_place(a, |_| F::EQUILIBRIUM) +} + +/// Mutate every frame in slice `a` while reading from each frame in slice `b` in lock-step using +/// the given function. +/// +/// **Panics** if the length of `b` is not equal to the length of `a`. +#[inline] +pub fn zip_map_in_place(a: &mut [FA], b: &[FB], zip_map: M) +where + FA: Frame, + FB: Frame, + M: FnMut(FA, FB) -> FA, +{ + assert_eq!(a.len(), b.len()); + + // We've asserted that the lengths are equal so we don't need bounds checking. + unsafe { + zip_map_in_place_unchecked(a, b, zip_map); + } +} + +/// Writes every sample in slice `b` to slice `a`. +/// +/// **Panics** if the slice lengths differ. +#[inline] +pub fn write(a: &mut [F], b: &[F]) +where + F: Frame, +{ + zip_map_in_place(a, b, |_, b| b); +} + +/// Adds every sample in slice `b` to every sample in slice `a` respectively. +#[inline] +pub fn add_in_place(a: &mut [FA], b: &[FB]) +where + FA: Frame, + FB: Frame::Signed, NumChannels = FA::NumChannels>, +{ + zip_map_in_place(a, b, |a, b| a.add_amp(b)); +} + +/// Scale the amplitude of each frame in `b` by `amp_per_channel` before summing it onto `a`. +#[inline] +pub fn add_in_place_with_amp_per_channel(a: &mut [FA], b: &[FB], amp_per_channel: A) +where + FA: Frame, + FB: Frame::Signed, NumChannels = FA::NumChannels>, + A: Frame::Float, NumChannels = FB::NumChannels>, +{ + zip_map_in_place(a, b, |af, bf| af.add_amp(bf.mul_amp(amp_per_channel))); +} + +/// Mutate every element in slice `a` while reading from each element from slice `b` in lock-step +/// using the given function. +/// +/// This function does not check that the slices are the same length and will crash horrifically on +/// index-out-of-bounds. +#[inline] +unsafe fn zip_map_in_place_unchecked(a: &mut [FA], b: &[FB], mut zip_map: M) +where + FA: Frame, + FB: Frame, + M: FnMut(FA, FB) -> FA, +{ + for i in 0..a.len() { + *a.get_unchecked_mut(i) = zip_map(*a.get_unchecked(i), *b.get_unchecked(i)); + } +} diff --git a/tests/slice.rs b/dasp_slice/tests/slice.rs similarity index 70% rename from tests/slice.rs rename to dasp_slice/tests/slice.rs index bcfd1a96..e9427087 100644 --- a/tests/slice.rs +++ b/dasp_slice/tests/slice.rs @@ -1,10 +1,8 @@ -extern crate sample; - #[test] fn test_add_slice() { let mut a = [[-0.5]; 32]; let b = [[0.5]; 32]; - sample::slice::add_in_place(&mut a, &b); + dasp_slice::add_in_place(&mut a, &b); assert_eq!([[0.0]; 32], a); } @@ -13,14 +11,14 @@ fn test_add_slice() { fn test_add_slice_panic() { let mut a = [[0.5]; 31]; let b = [[0.5]; 32]; - sample::slice::add_in_place(&mut a, &b); + dasp_slice::add_in_place(&mut a, &b); } #[test] fn test_write_slice() { let mut a = [[0.0]; 32]; let b = [[1.0]; 32]; - sample::slice::write(&mut a, &b); + dasp_slice::write(&mut a, &b); assert_eq!([[1.0]; 32], a); } @@ -29,7 +27,7 @@ fn test_write_slice() { fn test_write_slice_panic() { let mut a = [[0.0]; 31]; let b = [[1.0]; 32]; - sample::slice::write(&mut a, &b); + dasp_slice::write(&mut a, &b); } #[test] @@ -37,7 +35,7 @@ fn test_add_slice_with_amp_per_channel() { let mut a = [[0.5]; 32]; let b = [[1.0]; 32]; let amp = [0.5]; - sample::slice::add_in_place_with_amp_per_channel(&mut a, &b, amp); + dasp_slice::add_in_place_with_amp_per_channel(&mut a, &b, amp); assert_eq!([[1.0]; 32], a); } @@ -47,5 +45,5 @@ fn test_add_slice_with_amp_per_channel_panic() { let mut a = [[0.5]; 31]; let b = [[1.0]; 32]; let amp = [0.5]; - sample::slice::add_in_place_with_amp_per_channel(&mut a, &b, amp); + dasp_slice::add_in_place_with_amp_per_channel(&mut a, &b, amp); } diff --git a/dasp_window/Cargo.toml b/dasp_window/Cargo.toml new file mode 100644 index 00000000..4467cd04 --- /dev/null +++ b/dasp_window/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "dasp_window" +description = "Windowing function abstractions (e.g. hanning, rectangle) for audio PCM DSP." +version = "0.11.0" +authors = ["mitchmindtree "] +readme = "../README.md" +keywords = ["dsp", "window", "hanning", "pcm", "audio"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustaudio/dasp.git" +homepage = "https://github.com/rustaudio/dasp" +edition = "2018" + +[dependencies] +dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } + +[features] +default = ["std"] +all = ["std", "all-no-std"] +all-no-std = [ + "hanning", + "rectangle", +] +std = [ + "dasp_sample/std", +] +hanning = [] +rectangle = [] + +[package.metadata.docs.rs] +all-features = true diff --git a/dasp_window/src/hanning/mod.rs b/dasp_window/src/hanning/mod.rs new file mode 100644 index 00000000..1e5934af --- /dev/null +++ b/dasp_window/src/hanning/mod.rs @@ -0,0 +1,30 @@ +use crate::Window; +use dasp_sample::Sample; +use ops::f64::cos; + +mod ops; + +/// A type of window function, also known as the "raised cosine window". +/// +/// [Wiki entry](https://en.wikipedia.org/wiki/Window_function#Hann_.28Hanning.29_window). +/// +/// ### Required Features +/// +/// - When using `dasp_window`, this item requires the **hanning** feature to be enabled. +/// - When using `dasp`, this item requires the **window-hanning** feature to be enabled. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Hanning; + +impl Window for Hanning +where + S: Sample, +{ + type Output = S; + fn window(phase: S) -> Self::Output { + const PI_2: f64 = core::f64::consts::PI * 2.0; + let v = phase.to_float_sample().to_sample() * PI_2; + (0.5 * (1.0 - cos(v))) + .to_sample::() + .to_sample::() + } +} diff --git a/dasp_window/src/hanning/ops.rs b/dasp_window/src/hanning/ops.rs new file mode 100644 index 00000000..f37cda1f --- /dev/null +++ b/dasp_window/src/hanning/ops.rs @@ -0,0 +1,12 @@ +#![allow(dead_code)] + +pub mod f64 { + #[cfg(not(feature = "std"))] + pub fn cos(x: f64) -> f64 { + unsafe { core::intrinsics::cosf64(x) } + } + #[cfg(feature = "std")] + pub fn cos(x: f64) -> f64 { + x.cos() + } +} diff --git a/dasp_window/src/lib.rs b/dasp_window/src/lib.rs new file mode 100644 index 00000000..7aff9a80 --- /dev/null +++ b/dasp_window/src/lib.rs @@ -0,0 +1,40 @@ +//! Module for windowing over a batch of Frames. Includes default Hanning and Rectangle window +//! types. +//! +//! ### Optional Features +//! +//! - The **hanning** feature (or **window-hanning** feature if using `dasp`) provides the +//! [**Hanning**](./struct.Hanning.html) window function implementation. +//! - The **rectangle** feature (or **window-rectangle** feature if using `dasp`) provides the +//! [**Rectangle**](./struct.Rectangle.html) window function implementation. +//! +//! ### no_std +//! +//! If working in a `no_std` context, you can disable the default **std** feature with +//! `--no-default-features`. +//! +//! To enable all of the above features in a `no_std` context, enable the **all-no-std** feature. + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] + +#[cfg(feature = "hanning")] +pub use hanning::Hanning; +#[cfg(feature = "rectangle")] +pub use rectangle::Rectangle; + +#[cfg(feature = "hanning")] +mod hanning; +#[cfg(feature = "rectangle")] +mod rectangle; + +/// An abstraction supporting different types of `Window` functions. +/// +/// The type `S` represents the phase of the window, while the `Output` represents the window +/// amplitude. +pub trait Window { + /// The type used to represent the window amplitude. + type Output; + /// Returns the amplitude for the given phase, given as some `Sample` type. + fn window(phase: S) -> Self::Output; +} diff --git a/dasp_window/src/rectangle.rs b/dasp_window/src/rectangle.rs new file mode 100644 index 00000000..29cf69e1 --- /dev/null +++ b/dasp_window/src/rectangle.rs @@ -0,0 +1,23 @@ +use crate::Window; + +use dasp_sample::Sample; + +/// The simplest window type, equivalent to replacing all but *N* values of data sequence by +/// zeroes, making it appear as though the waveform suddenly turns on and off. +/// +/// ### Required Features +/// +/// - When using `dasp_window`, this item requires the **rectangle** feature to be enabled. +/// - When using `dasp`, this item requires the **window-rectangle** feature to be enabled. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Rectangle; + +impl Window for Rectangle +where + S: Sample, +{ + type Output = S; + fn window(_phase: S) -> Self::Output { + S::IDENTITY.to_sample::() + } +} diff --git a/examples/Cargo.toml b/examples/Cargo.toml new file mode 100644 index 00000000..0f7dbdf0 --- /dev/null +++ b/examples/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "examples" +version = "0.1.0" +authors = ["mitchmindtree "] +edition = "2018" + +[dev-dependencies] +anyhow = "1" +cpal = { git = "https://github.com/rustaudio/cpal", branch = "master" } +dasp = { version = "0.11", path = "../dasp", features = ["all"] } +find_folder = "0.3" +hound = "3" + +[[example]] +name = "play_wav" +path = "play_wav.rs" +[[example]] +name = "resample" +path = "resample.rs" +[[example]] +name = "synth" +path = "synth.rs" +[[example]] +name = "test" +path = "test.rs" diff --git a/examples/play_wav.rs b/examples/play_wav.rs index af72aa82..1c5d89b4 100644 --- a/examples/play_wav.rs +++ b/examples/play_wav.rs @@ -1,13 +1,13 @@ -extern crate find_folder; -extern crate hound; -extern crate portaudio as pa; -extern crate sample; +use cpal; +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use dasp::signal::{self, Signal}; +use dasp::slice::ToFrameSliceMut; -use sample::{signal, Signal, ToFrameSliceMut}; - -fn main() { +fn main() -> Result<(), anyhow::Error> { // Find and load the wav. - let assets = find_folder::Search::ParentsThenKids(5, 5).for_folder("assets").unwrap(); + let assets = find_folder::Search::ParentsThenKids(5, 5) + .for_folder("assets") + .unwrap(); let reader = hound::WavReader::open(assets.join("thumbpiano A#3.wav")).unwrap(); let spec = reader.spec(); @@ -15,38 +15,45 @@ fn main() { let samples = reader.into_samples::().filter_map(Result::ok); let mut frames = signal::from_interleaved_samples_iter(samples).until_exhausted(); - // Initialise PortAudio. - let pa = pa::PortAudio::new().unwrap(); - let ch = spec.channels as i32; - let sr = spec.sample_rate as f64; - let buffer_len = 0; // use default - let settings = pa.default_output_stream_settings::(ch, sr, buffer_len).unwrap(); + // Initialise CPAL. + let host = cpal::default_host(); + let device = host + .default_output_device() + .expect("failed to find a default output device"); + + // Create a stream config to match the wave format. + // + // NOTE: It's possible that the platform will not support the sample format, sample rate or + // channel layout of the WAV file. In these cases, you may need to convert the data read from + // the WAV file to a format compatible with one of the platform's supported stream + // configurations. + let config = cpal::StreamConfig { + channels: spec.channels, + sample_rate: cpal::SampleRate(spec.sample_rate), + }; // A channel for indicating when playback has completed. - let (complete_tx, complete_rx) = std::sync::mpsc::channel(); + let (complete_tx, complete_rx) = std::sync::mpsc::sync_channel(1); - // Define the callback which provides PortAudio the audio. - let callback = move |pa::OutputStreamCallbackArgs { buffer, .. }| { - let buffer: &mut [[i16; 2]] = buffer.to_frame_slice_mut().unwrap(); + // Create and run the CPAL stream. + let err_fn = |err| eprintln!("an error occurred on stream: {}", err); + let data_fn = move |data: &mut [i16], _info: &cpal::OutputCallbackInfo| { + let buffer: &mut [[i16; 2]] = data.to_frame_slice_mut().unwrap(); for out_frame in buffer { match frames.next() { Some(frame) => *out_frame = frame, None => { - complete_tx.send(()).unwrap(); - return pa::Complete; - }, + complete_tx.try_send(()).ok(); + *out_frame = dasp::Frame::EQUILIBRIUM; + } } } - pa::Continue }; - - // Spawn and start the output stream. - let mut stream = pa.open_non_blocking_stream(settings, callback).unwrap(); - stream.start().unwrap(); + let stream = device.build_output_stream(&config, data_fn, err_fn)?; + stream.play().unwrap(); // Block until playback completes. complete_rx.recv().unwrap(); - - stream.stop().unwrap(); - stream.close().unwrap(); + stream.pause().ok(); + Ok(()) } diff --git a/examples/resample.rs b/examples/resample.rs index 889dd824..083d16b9 100644 --- a/examples/resample.rs +++ b/examples/resample.rs @@ -1,16 +1,14 @@ // An example of using `sample` to efficiently perform decent quality sample rate conversion on a // WAV file entirely on the stack. -extern crate find_folder; -extern crate hound; -extern crate sample; - +use dasp::{interpolate::sinc::Sinc, ring_buffer, signal, Sample, Signal}; use hound::{WavReader, WavWriter}; -use sample::{interpolate, ring_buffer, signal, Sample, Signal}; fn main() { // Find and load the wav. - let assets = find_folder::Search::ParentsThenKids(5, 5).for_folder("assets").unwrap(); + let assets = find_folder::Search::ParentsThenKids(5, 5) + .for_folder("assets") + .unwrap(); let reader = WavReader::open(assets.join("two_vowels.wav")).unwrap(); // Get the wav spec and create a target with the new desired sample rate. @@ -19,12 +17,15 @@ fn main() { target.sample_rate = 10_000; // Read the interleaved samples and convert them to a signal. - let samples = reader.into_samples().filter_map(Result::ok).map(i16::to_sample::); + let samples = reader + .into_samples() + .filter_map(Result::ok) + .map(i16::to_sample::); let signal = signal::from_interleaved_samples_iter(samples); // Convert the signal's sample rate using `Sinc` interpolation. let ring_buffer = ring_buffer::Fixed::from([[0.0]; 100]); - let sinc = interpolate::Sinc::new(ring_buffer); + let sinc = Sinc::new(ring_buffer); let new_signal = signal.from_hz_to_hz(sinc, spec.sample_rate as f64, target.sample_rate as f64); // Write the result to a new file. diff --git a/examples/synth.rs b/examples/synth.rs index bf70cb8b..11a16be8 100644 --- a/examples/synth.rs +++ b/examples/synth.rs @@ -1,59 +1,82 @@ -extern crate portaudio as pa; -extern crate sample; +use cpal; +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use dasp::{signal, Sample, Signal}; +use std::sync::mpsc; -use sample::{signal, Frame, Sample, Signal, ToFrameSliceMut}; +fn main() -> Result<(), anyhow::Error> { + let host = cpal::default_host(); + let device = host + .default_output_device() + .expect("failed to find a default output device"); + let config = device.default_output_config()?; -const FRAMES_PER_BUFFER: u32 = 512; -const NUM_CHANNELS: i32 = 1; -const SAMPLE_RATE: f64 = 44_100.0; + match config.sample_format() { + cpal::SampleFormat::F32 => run::(&device, &config.into())?, + cpal::SampleFormat::I16 => run::(&device, &config.into())?, + cpal::SampleFormat::U16 => run::(&device, &config.into())?, + } -fn main() { - run().unwrap(); + Ok(()) } -fn run() -> Result<(), pa::Error> { - +fn run(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error> +where + T: cpal::Sample, +{ // Create a signal chain to play back 1 second of each oscillator at A4. - let hz = signal::rate(SAMPLE_RATE).const_hz(440.0); - let one_sec = SAMPLE_RATE as usize; - let mut waves = hz.clone() + let hz = signal::rate(config.sample_rate.0 as f64).const_hz(440.0); + let one_sec = config.sample_rate.0 as usize; + let mut synth = hz + .clone() .sine() .take(one_sec) .chain(hz.clone().saw().take(one_sec)) .chain(hz.clone().square().take(one_sec)) .chain(hz.clone().noise_simplex().take(one_sec)) .chain(signal::noise(0).take(one_sec)) - .map(|f| f.map(|s| s.to_sample::() * 0.2)); - - // Initialise PortAudio. - let pa = try!(pa::PortAudio::new()); - let settings = try!(pa.default_output_stream_settings::( - NUM_CHANNELS, - SAMPLE_RATE, - FRAMES_PER_BUFFER, - )); - - // Define the callback which provides PortAudio the audio. - let callback = move |pa::OutputStreamCallbackArgs { buffer, .. }| { - let buffer: &mut [[f32; 1]] = buffer.to_frame_slice_mut().unwrap(); - for out_frame in buffer { - match waves.next() { - Some(frame) => *out_frame = frame, - None => return pa::Complete, - } - } - pa::Continue - }; + .map(|s| s.to_sample::() * 0.2); - let mut stream = try!(pa.open_non_blocking_stream(settings, callback)); - try!(stream.start()); + // A channel for indicating when playback has completed. + let (complete_tx, complete_rx) = mpsc::sync_channel(1); - while let Ok(true) = stream.is_active() { - std::thread::sleep(std::time::Duration::from_millis(100)); - } + // Create and run the stream. + let err_fn = |err| eprintln!("an error occurred on stream: {}", err); + let channels = config.channels as usize; + let stream = device.build_output_stream( + config, + move |data: &mut [T], _: &cpal::OutputCallbackInfo| { + write_data(data, channels, &complete_tx, &mut synth) + }, + err_fn, + )?; + stream.play()?; - try!(stream.stop()); - try!(stream.close()); + // Wait for playback to complete. + complete_rx.recv().unwrap(); + stream.pause()?; Ok(()) } + +fn write_data( + output: &mut [T], + channels: usize, + complete_tx: &mpsc::SyncSender<()>, + signal: &mut dyn Iterator, +) where + T: cpal::Sample, +{ + for frame in output.chunks_mut(channels) { + let sample = match signal.next() { + None => { + complete_tx.try_send(()).ok(); + 0.0 + } + Some(sample) => sample, + }; + let value: T = cpal::Sample::from::(&sample); + for sample in frame.iter_mut() { + *sample = value; + } + } +} diff --git a/examples/test.rs b/examples/test.rs index a879266d..1c893e4c 100644 --- a/examples/test.rs +++ b/examples/test.rs @@ -1,9 +1,7 @@ //! A short example that converts an f64 sine wave to a few of the sample types available within //! the `Sample` crate, prints their values, and then converts them back to the original f64. -extern crate sample; - -use sample::Sample; +use dasp::Sample; /// An iterator that continually steps forward the phase for a signal by `0.03`. struct Iter { @@ -49,7 +47,6 @@ fn main() { sample_i32 ); - let wave_f32 = sample_f32.to_sample::(); let wave_u8 = sample_u8.to_sample::(); let wave_u16 = sample_u16.to_sample::(); diff --git a/src/envelope/detect.rs b/src/envelope/detect.rs deleted file mode 100644 index ee643a94..00000000 --- a/src/envelope/detect.rs +++ /dev/null @@ -1,195 +0,0 @@ -use {Frame, Sample}; -use core; -use peak; -use ring_buffer; -use rms; - -/// A type that can be used to detect the envelope of a signal. -#[derive(Clone, Debug)] -pub struct Detector -where - F: Frame, - D: Detect, -{ - last_env_frame: D::Output, - attack_gain: f32, - release_gain: f32, - detect: D, -} - -/// Types that may be used to detect an envelope over a signal. -pub trait Detect -where - F: Frame, -{ - /// The result of detection. - type Output: Frame; - /// Given some frame, return the detected envelope over each channel. - fn detect(&mut self, frame: F) -> Self::Output; -} - -/// A `Peak` detector, generic over the `FullWave`, `PositiveHalfWave`, `NegativeHalfWave` -/// rectifiers. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Peak { - rectifier: R, -} - -impl From for Peak { - fn from(rectifier: R) -> Self { - Peak { rectifier: rectifier } - } -} - -impl Peak { - /// A signal rectifier that produces the absolute amplitude from samples. - pub fn full_wave() -> Self { - peak::FullWave.into() - } -} - -impl Peak { - /// A signal rectifier that produces only the positive samples. - pub fn positive_half_wave() -> Self { - peak::PositiveHalfWave.into() - } -} - -impl Peak { - /// A signal rectifier that produces only the negative samples. - pub fn negative_half_wave() -> Self { - peak::NegativeHalfWave.into() - } -} - -impl Detect for Peak -where - F: Frame, - R: peak::Rectifier, -{ - type Output = R::Output; - fn detect(&mut self, frame: F) -> Self::Output { - self.rectifier.rectify(frame) - } -} - -impl Detect for rms::Rms -where - F: Frame, - S: ring_buffer::Slice - + ring_buffer::SliceMut, -{ - type Output = F::Float; - fn detect(&mut self, frame: F) -> Self::Output { - self.next(frame) - } -} - -fn calc_gain(n_frames: f32) -> f32 { - if n_frames == 0.0 { - 0.0 - } else { - ::ops::f32::powf32(core::f32::consts::E, -1.0 / n_frames) - } -} - -impl Detector> -where - F: Frame, - S: ring_buffer::Slice + ring_buffer::SliceMut, -{ -/// Construct a new **Rms** **Detector**. - pub fn rms(buffer: ring_buffer::Fixed, attack_frames: f32, release_frames: f32) -> Self { - let rms = rms::Rms::new(buffer); - Self::new(rms, attack_frames, release_frames) - } -} - -impl Detector> -where - F: Frame, - R: peak::Rectifier, -{ - /// Construct a new **Peak** **Detector** that uses the given rectifier. - pub fn peak_from_rectifier(rectifier: R, attack_frames: f32, release_frames: f32) -> Self { - let peak = rectifier.into(); - Self::new(peak, attack_frames, release_frames) - } -} - -impl Detector> -where - F: Frame, -{ - /// Construct a new full wave **Peak** **Detector**. - pub fn peak(attack_frames: f32, release_frames: f32) -> Self { - let peak = Peak::full_wave(); - Self::new(peak, attack_frames, release_frames) - } -} - -impl Detector> -where - F: Frame, -{ - /// Construct a new positive half wave **Peak** **Detector**. - pub fn peak_positive_half_wave(attack_frames: f32, release_frames: f32) -> Self { - let peak = Peak::positive_half_wave(); - Self::new(peak, attack_frames, release_frames) - } -} - -impl Detector> -where - F: Frame, -{ - /// Construct a new positive half wave **Peak** **Detector**. - pub fn peak_negative_half_wave(attack_frames: f32, release_frames: f32) -> Self { - let peak = Peak::negative_half_wave(); - Self::new(peak, attack_frames, release_frames) - } -} - -impl Detector -where - F: Frame, - D: Detect, -{ - fn new(detect: D, attack_frames: f32, release_frames: f32) -> Self { - Detector { - last_env_frame: D::Output::equilibrium(), - attack_gain: calc_gain(attack_frames), - release_gain: calc_gain(release_frames), - detect: detect, - } - } - - /// Set the **Detector**'s attack time as a number of frames. - pub fn set_attack_frames(&mut self, frames: f32) { - self.attack_gain = calc_gain(frames); - } - - /// Set the **Detector**'s release time as a number of frames. - pub fn set_release_frames(&mut self, frames: f32) { - self.release_gain = calc_gain(frames); - } - - /// Given the next input signal frame, detect and return the next envelope frame. - pub fn next(&mut self, frame: F) -> D::Output { - let Detector { - attack_gain, - release_gain, - ref mut detect, - ref mut last_env_frame, - } = *self; - - let detected_frame = detect.detect(frame); - let new_env_frame = last_env_frame.zip_map(detected_frame, |l, d| { - let gain = if l < d { attack_gain } else { release_gain }; - let diff = l.add_amp(-d.to_signed_sample()); - d.add_amp(diff.mul_amp(gain.to_sample()).to_sample()) - }); - *last_env_frame = new_env_frame; - new_env_frame - } -} diff --git a/src/envelope/mod.rs b/src/envelope/mod.rs deleted file mode 100644 index dd3cf9aa..00000000 --- a/src/envelope/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod detect; - -pub use self::detect::{Detect, Detector}; diff --git a/src/interpolate.rs b/src/interpolate.rs deleted file mode 100644 index 23d0efa9..00000000 --- a/src/interpolate.rs +++ /dev/null @@ -1,356 +0,0 @@ -//! The Interpolate module allows for conversion between various sample rates. - -use {Duplex, Frame, Sample, Signal}; -use core::f64::consts::PI; -use ring_buffer; -use ops::f64::{sin, cos}; - -/// An iterator that converts the rate at which frames are yielded from some given frame -/// Interpolator into a new type. -/// -/// Other names for `sample::interpolate::Converter` might include: -/// -/// - Sample rate converter -/// - {Up/Down}sampler -/// - Sample interpolater -/// - Sample decimator -/// -#[derive(Clone)] -pub struct Converter -where - S: Signal, - I: Interpolator, -{ - source: S, - interpolator: I, - interpolation_value: f64, - source_to_target_ratio: f64, -} - -/// Interpolator that just rounds off any values to the previous value from the source -pub struct Floor { - left: F, -} - -/// Interpolator that interpolates linearly between the previous value and the next value -pub struct Linear { - left: F, - right: F, -} - -/// Interpolator for sinc interpolation. -/// -/// Generally accepted as one of the better sample rate converters, although it uses significantly -/// more computation. -pub struct Sinc { - frames: ring_buffer::Fixed, - idx: usize, -} - -/// Types that can interpolate between two values. -/// -/// Implementations should keep track of the necessary data both before and after the current -/// frame. -pub trait Interpolator { - type Frame: Frame; - - /// Given a distance between [0.0 and 1.0) to the following sample, return the interpolated - /// value. - fn interpolate(&self, x: f64) -> Self::Frame; - - /// Called whenever the Interpolator value steps passed 1.0. - fn next_source_frame(&mut self, source_frame: Self::Frame); -} - -impl Converter -where - S: Signal, - I: Interpolator, -{ - /// Construct a new `Converter` from the source frames and the source and target sample rates - /// (in Hz). - #[inline] - pub fn from_hz_to_hz(source: S, interpolator: I, source_hz: f64, target_hz: f64) -> Self { - Self::scale_playback_hz(source, interpolator, source_hz / target_hz) - } - - /// Construct a new `Converter` from the source frames and the amount by which the current - /// ***playback*** **rate** (not sample rate) should be multiplied to reach the new playback - /// rate. - /// - /// For example, if our `source_frames` is a sine wave oscillating at a frequency of 2hz and - /// we wanted to convert it to a frequency of 3hz, the given `scale` should be `1.5`. - #[inline] - pub fn scale_playback_hz(source: S, interpolator: I, scale: f64) -> Self { - assert!( - scale > 0.0, - "We can't yield any frames at 0 times a second!" - ); - Converter { - source: source, - interpolator: interpolator, - interpolation_value: 0.0, - source_to_target_ratio: scale, - } - } - - /// Construct a new `Converter` from the source frames and the amount by which the current - /// ***sample*** **rate** (not playback rate) should be multiplied to reach the new sample - /// rate. - /// - /// If our `source_frames` are being sampled at a rate of 44_100hz and we want to - /// convert to a sample rate of 96_000hz, the given `scale` should be `96_000.0 / 44_100.0`. - /// - /// This is the same as calling `Converter::scale_playback_hz(source_frames, 1.0 / scale)`. - #[inline] - pub fn scale_sample_hz(source: S, interpolator: I, scale: f64) -> Self { - Self::scale_playback_hz(source, interpolator, 1.0 / scale) - } - - /// Update the `source_to_target_ratio` internally given the source and target hz. - /// - /// This method might be useful for changing the sample rate during playback. - #[inline] - pub fn set_hz_to_hz(&mut self, source_hz: f64, target_hz: f64) { - self.set_playback_hz_scale(source_hz / target_hz) - } - - /// Update the `source_to_target_ratio` internally given a new **playback rate** multiplier. - /// - /// This method is useful for dynamically changing rates. - #[inline] - pub fn set_playback_hz_scale(&mut self, scale: f64) { - self.source_to_target_ratio = scale; - } - - /// Update the `source_to_target_ratio` internally given a new **sample rate** multiplier. - /// - /// This method is useful for dynamically changing rates. - #[inline] - pub fn set_sample_hz_scale(&mut self, scale: f64) { - self.set_playback_hz_scale(1.0 / scale); - } - - /// Borrow the `source_frames` Interpolator from the `Converter`. - #[inline] - pub fn source(&self) -> &S { - &self.source - } - - /// Mutably borrow the `source_frames` Iterator from the `Converter`. - #[inline] - pub fn source_mut(&mut self) -> &mut S { - &mut self.source - } - - /// Drop `self` and return the internal `source_frames` Iterator. - #[inline] - pub fn into_source(self) -> S { - self.source - } -} - -impl Signal for Converter -where - S: Signal, - I: Interpolator, -{ - type Frame = S::Frame; - - fn next(&mut self) -> Self::Frame { - let Converter { - ref mut source, - ref mut interpolator, - ref mut interpolation_value, - source_to_target_ratio, - } = *self; - - // Advance frames - while *interpolation_value >= 1.0 { - interpolator.next_source_frame(source.next()); - *interpolation_value -= 1.0; - } - - let out = interpolator.interpolate(*interpolation_value); - *interpolation_value += source_to_target_ratio; - out - } - - fn is_exhausted(&self) -> bool { - self.source.is_exhausted() && self.interpolation_value >= 1.0 - } -} - -impl Floor { - /// Create a new Floor Interpolator. - pub fn new(left: F) -> Floor { - Floor { left: left } - } - - /// Consumes the first value from a given source in order to initialize itself. If the source - /// has no values at all, this will return None. - pub fn from_source(source: &mut S) -> Floor - where - F: Frame, - S: Signal, - { - let left = source.next(); - Floor { left: left } - } -} - -impl Linear { - /// Create a new Linear Interpolator. - pub fn new(left: F, right: F) -> Linear { - Linear { - left: left, - right: right, - } - } - - /// Consumes the first value from a given source to initialize itself. If the source has no - /// values, this will return None. - pub fn from_source(source: &mut S) -> Linear - where - F: Frame, - S: Signal, - { - let left = source.next(); - let right = source.next(); - Linear { - left: left, - right: right, - } - } -} - -impl Sinc { - /// Create a new **Sinc** interpolator with the given ring buffer. - /// - /// The given ring buffer should have a length twice that of the desired sinc interpolation - /// `depth`. - /// - /// The initial contents of the ring_buffer will act as padding for the interpolated signal. - /// - /// **panic!**s if the given ring buffer's length is not a multiple of `2`. - pub fn new(frames: ring_buffer::Fixed) -> Self - where - S: ring_buffer::SliceMut, - S::Element: Frame, - { - assert!(frames.len() % 2 == 0); - Sinc { - frames: frames, - idx: 0, - } - } - - fn depth(&self) -> usize - where - S: ring_buffer::Slice, - { - self.frames.len() / 2 - } -} - -impl Interpolator for Floor -where - F: Frame, - F::Sample: Duplex, -{ - type Frame = F; - - fn interpolate(&self, _x: f64) -> Self::Frame { - self.left - } - - fn next_source_frame(&mut self, source_frame: Self::Frame) { - self.left = source_frame; - } -} - -impl Interpolator for Linear -where - F: Frame, - F::Sample: Duplex, -{ - type Frame = F; - - /// Converts linearly from the previous value, using the next value to interpolate. It is - /// possible, although not advisable, to provide an x > 1.0 or < 0.0, but this will just - /// continue to be a linear ramp in one direction or another. - fn interpolate(&self, x: f64) -> Self::Frame { - self.left.zip_map(self.right, |l, r| { - let l_f = l.to_sample::(); - let r_f = r.to_sample::(); - let diff = r_f - l_f; - ((diff * x) + l_f).to_sample::<::Sample>() - }) - } - - fn next_source_frame(&mut self, source_frame: Self::Frame) { - self.left = self.right; - self.right = source_frame; - } -} - -impl Interpolator for Sinc -where - S: ring_buffer::SliceMut, - S::Element: Frame, - ::Sample: Duplex, -{ - type Frame = S::Element; - - /// Sinc interpolation - fn interpolate(&self, x: f64) -> Self::Frame { - let phil = x; - let phir = 1.0 - x; - let nl = self.idx; - let nr = self.idx + 1; - let depth = self.depth(); - - let rightmost = nl + depth; - let leftmost = nr as isize - depth as isize; - let max_depth = if rightmost >= self.frames.len() { - self.frames.len() - depth - } else if leftmost < 0 { - (depth as isize + leftmost) as usize - } else { - depth - }; - - (0..max_depth).fold(Self::Frame::equilibrium(), |mut v, n| { - v = { - let a = PI * (phil + n as f64); - let first = if a == 0.0 { 1.0 } else { sin(a) / a }; - let second = 0.5 + 0.5 * cos(a / (phil + max_depth as f64)); - v.zip_map(self.frames[nr - n], |vs, r_lag| { - vs.add_amp( - (first * second * r_lag.to_sample::()) - .to_sample::<::Sample>() - .to_signed_sample(), - ) - }) - }; - - let a = PI * (phir + n as f64); - let first = if a == 0.0 { 1.0 } else { sin(a) / a }; - let second = 0.5 + 0.5 * cos(a / (phir + max_depth as f64)); - v.zip_map(self.frames[nl + n], |vs, r_lag| { - vs.add_amp( - (first * second * r_lag.to_sample::()) - .to_sample::<::Sample>() - .to_signed_sample(), - ) - }) - }) - } - - fn next_source_frame(&mut self, source_frame: Self::Frame) { - let _old_frame = self.frames.push(source_frame); - if self.idx < self.depth() { - self.idx += 1; - } - } -} diff --git a/src/slice.rs b/src/slice.rs deleted file mode 100644 index 88f5baeb..00000000 --- a/src/slice.rs +++ /dev/null @@ -1,410 +0,0 @@ -//! This module provides various helper functions for performing operations on slices of frames. - -use {Box, Frame, Sample, ToSampleSlice, ToSampleSliceMut, ToBoxedSampleSlice, ToFrameSlice, - ToFrameSliceMut, ToBoxedFrameSlice, FromSampleSlice, FromSampleSliceMut, - FromBoxedSampleSlice, FromFrameSlice, FromFrameSliceMut, FromBoxedFrameSlice}; - - -///// Conversion Functions -///// -///// The following functions wrap the various DSP slice conversion traits for convenience. - - -/// Converts the given slice into a slice of `Frame`s. -/// -/// Returns `None` if the number of channels in a single frame `F` is not a multiple of the number -/// of samples in the given slice. -/// -/// This is a convenience function that wraps the `ToFrameSlice` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = &[0.0, 0.5, 0.0, -0.5][..]; -/// let bar = sample::slice::to_frame_slice(foo); -/// assert_eq!(bar, Some(&[[0.0, 0.5], [0.0, -0.5]][..])); -/// -/// let foo = &[0.0, 0.5, 0.0][..]; -/// let bar = sample::slice::to_frame_slice(foo); -/// assert_eq!(bar, None::<&[[f32; 2]]>); -/// } -/// ``` -pub fn to_frame_slice<'a, T, F>(slice: T) -> Option<&'a [F]> -where - F: Frame, - T: ToFrameSlice<'a, F>, -{ - slice.to_frame_slice() -} - -/// Converts the given mutable slice into a mutable slice of `Frame`s. -/// -/// Returns `None` if the number of channels in a single frame `F` is not a multiple of the number -/// of samples in the given slice. -/// -/// This is a convenience function that wraps the `ToFrameSliceMut` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = &mut [0.0, 0.5, 0.0, -0.5][..]; -/// let bar = sample::slice::to_frame_slice_mut(foo); -/// assert_eq!(bar, Some(&mut [[0.0, 0.5], [0.0, -0.5]][..])); -/// -/// let foo = &mut [0.0, 0.5, 0.0][..]; -/// let bar = sample::slice::to_frame_slice_mut(foo); -/// assert_eq!(bar, None::<&mut [[f32; 2]]>); -/// } -/// ``` -pub fn to_frame_slice_mut<'a, T, F>(slice: T) -> Option<&'a mut [F]> -where - F: Frame, - T: ToFrameSliceMut<'a, F>, -{ - slice.to_frame_slice_mut() -} - -/// Converts the given boxed slice into a boxed slice of `Frame`s. -/// -/// Returns `None` if the number of channels in a single frame `F` is not a multiple of the number -/// of samples in the given slice. -/// -/// This is a convenience function that wraps the `ToBoxedFrameSlice` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = vec![0.0, 0.5, 0.0, -0.5].into_boxed_slice(); -/// let bar: Box<[[f32; 2]]> = sample::slice::to_boxed_frame_slice(foo).unwrap(); -/// assert_eq!(bar.into_vec(), vec![[0.0, 0.5], [0.0, -0.5]]); -/// -/// let foo = vec![0.0, 0.5, 0.0].into_boxed_slice(); -/// let bar = sample::slice::to_boxed_frame_slice(foo); -/// assert_eq!(bar, None::>); -/// } -/// ``` -pub fn to_boxed_frame_slice(slice: T) -> Option> -where - F: Frame, - T: ToBoxedFrameSlice, -{ - slice.to_boxed_frame_slice() -} - -/// Converts the given slice into a slice of `Sample`s. -/// -/// This is a convenience function that wraps the `ToSampleSlice` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = &[[0.0, 0.5], [0.0, -0.5]][..]; -/// let bar = sample::slice::to_sample_slice(foo); -/// assert_eq!(bar, &[0.0, 0.5, 0.0, -0.5][..]); -/// } -/// ``` -pub fn to_sample_slice<'a, T, S>(slice: T) -> &'a [S] -where - S: Sample, - T: ToSampleSlice<'a, S>, -{ - slice.to_sample_slice() -} - -/// Converts the given mutable slice of `Frame`s into a mutable slice of `Sample`s. -/// -/// This is a convenience function that wraps the `ToSampleSliceMut` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = &mut [[0.0, 0.5], [0.0, -0.5]][..]; -/// let bar = sample::slice::to_sample_slice_mut(foo); -/// assert_eq!(bar, &mut [0.0, 0.5, 0.0, -0.5][..]); -/// } -/// ``` -pub fn to_sample_slice_mut<'a, T, S>(slice: T) -> &'a mut [S] -where - S: Sample, - T: ToSampleSliceMut<'a, S>, -{ - slice.to_sample_slice_mut() -} - -/// Converts the given boxed slice into a boxed slice of `Sample`s. -/// -/// This is a convenience function that wraps the `ToBoxedSampleSlice` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = vec![[0.0, 0.5], [0.0, -0.5]].into_boxed_slice(); -/// let bar = sample::slice::to_boxed_sample_slice(foo); -/// assert_eq!(bar.into_vec(), vec![0.0, 0.5, 0.0, -0.5]); -/// } -/// ``` -pub fn to_boxed_sample_slice(slice: T) -> Box<[S]> -where - S: Sample, - T: ToBoxedSampleSlice, -{ - slice.to_boxed_sample_slice() -} - -/// Converts the given slice of `Sample`s into some slice `T`. -/// -/// Returns `None` if the number of channels in a single frame is not a multiple of the number of -/// samples in the given slice. -/// -/// This is a convenience function that wraps the `FromSampleSlice` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = &[0.0, 0.5, 0.0, -0.5][..]; -/// let bar: Option<&_> = sample::slice::from_sample_slice(foo); -/// assert_eq!(bar, Some(&[[0.0, 0.5], [0.0, -0.5]][..])); -/// } -/// ``` -pub fn from_sample_slice<'a, T, S>(slice: &'a [S]) -> Option -where - S: Sample, - T: FromSampleSlice<'a, S>, -{ - T::from_sample_slice(slice) -} - -/// Converts the given mutable slice of `Sample`s into some mutable slice `T`. -/// -/// Returns `None` if the number of channels in a single frame is not a multiple of the number of -/// samples in the given slice. -/// -/// This is a convenience function that wraps the `FromSampleSliceMut` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = &mut [0.0, 0.5, 0.0, -0.5][..]; -/// let bar: Option<&mut _> = sample::slice::from_sample_slice_mut(foo); -/// assert_eq!(bar, Some(&mut [[0.0, 0.5], [0.0, -0.5]][..])); -/// } -/// ``` -pub fn from_sample_slice_mut<'a, T, S>(slice: &'a mut [S]) -> Option -where - S: Sample, - T: FromSampleSliceMut<'a, S>, -{ - T::from_sample_slice_mut(slice) -} - -/// Converts the given boxed slice of `Sample`s into some slice `T`. -/// -/// Returns `None` if the number of channels in a single frame is not a multiple of the number of -/// samples in the given slice. -/// -/// This is a convenience function that wraps the `FromBoxedSampleSlice` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = vec![0.0, 0.5, 0.0, -0.5].into_boxed_slice(); -/// let bar: Box<[[f32; 2]]> = sample::slice::from_boxed_sample_slice(foo).unwrap(); -/// assert_eq!(bar.into_vec(), vec![[0.0, 0.5], [0.0, -0.5]]); -/// } -/// ``` -pub fn from_boxed_sample_slice(slice: Box<[S]>) -> Option -where - S: Sample, - T: FromBoxedSampleSlice, -{ - T::from_boxed_sample_slice(slice) -} - -/// Converts the given slice of `Frame`s into some slice `T`. -/// -/// This is a convenience function that wraps the `FromFrameSlice` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = &[[0.0, 0.5], [0.0, -0.5]][..]; -/// let bar: &[f32] = sample::slice::from_frame_slice(foo); -/// assert_eq!(bar, &[0.0, 0.5, 0.0, -0.5][..]); -/// } -/// ``` -pub fn from_frame_slice<'a, T, F>(slice: &'a [F]) -> T -where - F: Frame, - T: FromFrameSlice<'a, F>, -{ - T::from_frame_slice(slice) -} - -/// Converts the given slice of mutable `Frame`s into some mutable slice `T`. -/// -/// This is a convenience function that wraps the `FromFrameSliceMut` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = &mut [[0.0, 0.5], [0.0, -0.5]][..]; -/// let bar: &mut [f32] = sample::slice::from_frame_slice_mut(foo); -/// assert_eq!(bar, &mut [0.0, 0.5, 0.0, -0.5][..]); -/// } -/// ``` -pub fn from_frame_slice_mut<'a, T, F>(slice: &'a mut [F]) -> T -where - F: Frame, - T: FromFrameSliceMut<'a, F>, -{ - T::from_frame_slice_mut(slice) -} - -/// Converts the given boxed slice of `Frame`s into some slice `T`. -/// -/// This is a convenience function that wraps the `FromBoxedFrameSlice` trait. -/// -/// # Examples -/// -/// ``` -/// extern crate sample; -/// -/// fn main() { -/// let foo = vec![[0.0, 0.5], [0.0, -0.5]].into_boxed_slice(); -/// let bar: Box<[f32]> = sample::slice::from_boxed_frame_slice(foo); -/// assert_eq!(bar.into_vec(), vec![0.0, 0.5, 0.0, -0.5]); -/// } -/// ``` -pub fn from_boxed_frame_slice(slice: Box<[F]>) -> T -where - F: Frame, - T: FromBoxedFrameSlice, -{ - T::from_boxed_frame_slice(slice) -} - - -///// Utility Functions - - -/// Mutate every element in the slice with the given function. -#[inline] -pub fn map_in_place(a: &mut [F], mut map: M) -where - M: FnMut(F) -> F, - F: Frame, -{ - for f in a { - *f = map(*f); - } -} - -/// Sets the slice of frames at the associated `Sample`'s equilibrium value. -#[inline] -pub fn equilibrium(a: &mut [F]) -where - F: Frame, -{ - map_in_place(a, |_| F::equilibrium()) -} - -/// Mutate every frame in slice `a` while reading from each frame in slice `b` in lock-step using -/// the given function. -/// -/// **Panics** if the length of `b` is not equal to the length of `a`. -#[inline] -pub fn zip_map_in_place(a: &mut [FA], b: &[FB], zip_map: M) -where - FA: Frame, - FB: Frame, - M: FnMut(FA, FB) -> FA, -{ - assert_eq!(a.len(), b.len()); - - // We've asserted that the lengths are equal so we don't need bounds checking. - unsafe { - zip_map_in_place_unchecked(a, b, zip_map); - } -} - -/// Writes every sample in slice `b` to slice `a`. -/// -/// **Panics** if the slice lengths differ. -#[inline] -pub fn write(a: &mut [F], b: &[F]) -where - F: Frame, -{ - zip_map_in_place(a, b, |_, b| b); -} - -/// Adds every sample in slice `b` to every sample in slice `a` respectively. -#[inline] -pub fn add_in_place(a: &mut [FA], b: &[FB]) -where - FA: Frame, - FB: Frame::Signed, NumChannels = FA::NumChannels>, -{ - zip_map_in_place(a, b, |a, b| a.add_amp(b)); -} - -/// Scale the amplitude of each frame in `b` by `amp_per_channel` before summing it onto `a`. -#[inline] -pub fn add_in_place_with_amp_per_channel(a: &mut [FA], b: &[FB], amp_per_channel: A) -where - FA: Frame, - FB: Frame::Signed, NumChannels = FA::NumChannels>, - A: Frame::Float, NumChannels = FB::NumChannels>, -{ - zip_map_in_place(a, b, |af, bf| af.add_amp(bf.mul_amp(amp_per_channel))); -} - -/// Mutate every element in slice `a` while reading from each element from slice `b` in lock-step -/// using the given function. -/// -/// This function does not check that the slices are the same length and will crash horrifically on -/// index-out-of-bounds. -#[inline] -unsafe fn zip_map_in_place_unchecked(a: &mut [FA], b: &[FB], mut zip_map: M) -where - FA: Frame, - FB: Frame, - M: FnMut(FA, FB) -> FA, -{ - for i in 0..a.len() { - *a.get_unchecked_mut(i) = zip_map(*a.get_unchecked(i), *b.get_unchecked(i)); - } -} diff --git a/tests/types.rs b/tests/types.rs deleted file mode 100644 index 19f0d393..00000000 --- a/tests/types.rs +++ /dev/null @@ -1,63 +0,0 @@ -extern crate sample; - -/// Expands to a unique module with a variety of tests for the given sample newtype. -/// -/// Tests include basic operations and over/underflow checks. -macro_rules! test_type { - ($T:ident, $mod_name:ident) => { - mod $mod_name { - #[test] - fn ops() { - use sample::types::$mod_name::$T; - assert_eq!($T::new(8).unwrap() + $T::new(12).unwrap(), $T::new(20).unwrap()); - assert_eq!($T::new(12).unwrap() - $T::new(4).unwrap(), $T::new(8).unwrap()); - assert_eq!($T::new(2).unwrap() * $T::new(2).unwrap(), $T::new(4).unwrap()); - assert_eq!($T::new(3).unwrap() * $T::new(3).unwrap(), $T::new(9).unwrap()); - assert_eq!($T::new(5).unwrap() * $T::new(10).unwrap(), $T::new(50).unwrap()); - assert_eq!($T::new(16).unwrap() / $T::new(8).unwrap(), $T::new(2).unwrap()); - assert_eq!($T::new(8).unwrap() % $T::new(3).unwrap(), $T::new(2).unwrap()); - } - - #[cfg(debug_assertions)] - #[test] - #[should_panic] - fn add_panic_debug() { - use sample::types::$mod_name::{self, $T}; - let _ = $mod_name::MAX + $T::new(1).unwrap(); - } - - #[cfg(debug_assertions)] - #[test] - #[should_panic] - fn sub_panic_debug() { - use sample::types::$mod_name::{self, $T}; - let _ = $mod_name::MIN - $T::new(1).unwrap(); - } - - #[cfg(debug_assertions)] - #[test] - #[should_panic] - fn mul_panic_debug() { - use sample::types::$mod_name::{self, $T}; - let _ = $mod_name::MAX * $T::new(2).unwrap(); - } - - #[cfg(not(debug_assertions))] - #[test] - fn release_wrapping() { - use sample::types::$mod_name::{self, $T}; - assert_eq!($mod_name::MIN - $T::new(1).unwrap(), $mod_name::MAX); - assert_eq!($mod_name::MAX + $T::new(1).unwrap(), $mod_name::MIN); - } - } - }; -} - -test_type!(I11, i11); -test_type!(U11, u11); -test_type!(I20, i20); -test_type!(U20, u20); -test_type!(I24, i24); -test_type!(U24, u24); -test_type!(I48, i48); -test_type!(U48, u48);