├── .github └── workflows │ └── dasp.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── assets ├── deps-graph.png ├── thumbpiano A#3.wav └── two_vowels.wav ├── dasp ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ └── lib.rs ├── dasp_envelope ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ ├── detect │ ├── mod.rs │ ├── ops.rs │ ├── peak.rs │ └── rms.rs │ └── lib.rs ├── dasp_frame ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ └── lib.rs ├── dasp_graph ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── src │ ├── buffer.rs │ ├── lib.rs │ └── node │ │ ├── boxed.rs │ │ ├── delay.rs │ │ ├── graph.rs │ │ ├── mod.rs │ │ ├── pass.rs │ │ ├── signal.rs │ │ └── sum.rs └── tests │ ├── graph_send.rs │ ├── graph_types.rs │ └── sum.rs ├── dasp_interpolate ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ ├── floor.rs │ ├── lib.rs │ ├── linear.rs │ └── sinc │ ├── mod.rs │ └── ops.rs ├── dasp_peak ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ └── lib.rs ├── dasp_ring_buffer ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── src │ └── lib.rs └── tests │ └── ring_buffer.rs ├── dasp_rms ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ └── lib.rs ├── dasp_sample ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── src │ ├── conv.rs │ ├── lib.rs │ ├── ops.rs │ └── types.rs └── tests │ ├── conv.rs │ └── types.rs ├── dasp_signal ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── src │ ├── boxed.rs │ ├── bus.rs │ ├── envelope.rs │ ├── interpolate.rs │ ├── lib.rs │ ├── ops.rs │ ├── rms.rs │ └── window │ │ ├── hann.rs │ │ ├── mod.rs │ │ └── rectangle.rs └── tests │ ├── interpolate.rs │ ├── signal.rs │ └── window.rs ├── dasp_slice ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── src │ ├── boxed.rs │ ├── frame │ │ ├── fixed_size_array.rs │ │ └── mod.rs │ └── lib.rs └── tests │ └── slice.rs ├── dasp_window ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ ├── hann │ ├── mod.rs │ └── ops.rs │ ├── lib.rs │ └── rectangle.rs └── examples ├── Cargo.toml ├── play_wav.rs ├── resample.rs ├── synth.rs └── test.rs /.github/workflows/dasp.yml: -------------------------------------------------------------------------------- 1 | name: dasp 2 | on: [push, pull_request] 3 | jobs: 4 | cargo-fmt-check: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Install stable 9 | uses: actions-rs/toolchain@v1 10 | with: 11 | profile: minimal 12 | toolchain: stable 13 | override: true 14 | components: rustfmt 15 | - name: cargo fmt check 16 | uses: actions-rs/cargo@v1 17 | with: 18 | command: fmt 19 | args: --all -- --check 20 | 21 | cargo-test: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - name: Update apt 26 | run: sudo apt update 27 | - name: Install alsa 28 | run: sudo apt-get install libasound2-dev 29 | - name: Install stable 30 | uses: actions-rs/toolchain@v1 31 | with: 32 | profile: minimal 33 | toolchain: stable 34 | override: true 35 | - name: cargo test 36 | uses: actions-rs/cargo@v1 37 | with: 38 | command: test 39 | args: --all --verbose 40 | 41 | cargo-test-no-default-features: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - uses: actions/checkout@v2 45 | - name: Update apt 46 | run: sudo apt update 47 | - name: Install alsa 48 | run: sudo apt-get install libasound2-dev 49 | - name: Install stable 50 | uses: actions-rs/toolchain@v1 51 | with: 52 | profile: minimal 53 | toolchain: nightly 54 | override: true 55 | # Can't do `--no-default-features` for all pkgs, so do them one by one. 56 | - name: cargo test dasp (no default features) 57 | uses: actions-rs/cargo@v1 58 | with: 59 | command: test 60 | args: --manifest-path dasp/Cargo.toml --no-default-features --verbose 61 | - name: cargo test dasp_envelope (no default features) 62 | uses: actions-rs/cargo@v1 63 | with: 64 | command: test 65 | args: --manifest-path dasp_envelope/Cargo.toml --no-default-features --verbose 66 | - name: cargo test dasp_frame (no default features) 67 | uses: actions-rs/cargo@v1 68 | with: 69 | command: test 70 | args: --manifest-path dasp_frame/Cargo.toml --no-default-features --verbose 71 | - name: cargo test dasp_interpolate (no default features) 72 | uses: actions-rs/cargo@v1 73 | with: 74 | command: test 75 | args: --manifest-path dasp_interpolate/Cargo.toml --no-default-features --verbose 76 | - name: cargo test dasp_peak (no default features) 77 | uses: actions-rs/cargo@v1 78 | with: 79 | command: test 80 | args: --manifest-path dasp_peak/Cargo.toml --no-default-features --verbose 81 | - name: cargo test dasp_ring_buffer (no default features) 82 | uses: actions-rs/cargo@v1 83 | with: 84 | command: test 85 | args: --manifest-path dasp_ring_buffer/Cargo.toml --no-default-features --verbose 86 | - name: cargo test dasp_rms (no default features) 87 | uses: actions-rs/cargo@v1 88 | with: 89 | command: test 90 | args: --manifest-path dasp_rms/Cargo.toml --no-default-features --verbose 91 | - name: cargo test dasp_sample (no default features) 92 | uses: actions-rs/cargo@v1 93 | with: 94 | command: test 95 | args: --manifest-path dasp_sample/Cargo.toml --no-default-features --verbose 96 | - name: cargo test dasp_signal (no default features) 97 | uses: actions-rs/cargo@v1 98 | with: 99 | command: test 100 | args: --manifest-path dasp_signal/Cargo.toml --no-default-features --verbose 101 | - name: cargo test dasp_slice (no default features) 102 | uses: actions-rs/cargo@v1 103 | with: 104 | command: test 105 | args: --manifest-path dasp_slice/Cargo.toml --no-default-features --verbose 106 | - name: cargo test dasp_window (no default features) 107 | uses: actions-rs/cargo@v1 108 | with: 109 | command: test 110 | args: --manifest-path dasp_window/Cargo.toml --no-default-features --verbose 111 | - name: cargo test dasp_graph (no default features) 112 | uses: actions-rs/cargo@v1 113 | with: 114 | command: test 115 | args: --manifest-path dasp_graph/Cargo.toml --no-default-features --verbose 116 | 117 | cargo-test-all-features: 118 | runs-on: ubuntu-latest 119 | steps: 120 | - uses: actions/checkout@v2 121 | - name: Update apt 122 | run: sudo apt update 123 | - name: Install alsa 124 | run: sudo apt-get install libasound2-dev 125 | - name: Install stable 126 | uses: actions-rs/toolchain@v1 127 | with: 128 | profile: minimal 129 | toolchain: stable 130 | override: true 131 | - name: cargo test (all features) 132 | uses: actions-rs/cargo@v1 133 | with: 134 | command: test 135 | args: --all --all-features --verbose 136 | 137 | cargo-test-all-features-no-std: 138 | runs-on: ubuntu-latest 139 | steps: 140 | - uses: actions/checkout@v2 141 | - name: Update apt 142 | run: sudo apt update 143 | - name: Install alsa 144 | run: sudo apt-get install libasound2-dev 145 | - name: Install stable 146 | uses: actions-rs/toolchain@v1 147 | with: 148 | profile: minimal 149 | toolchain: nightly 150 | override: true 151 | # Can't do `--no-default-features` or `--features` for all pkgs, so do them one by one. 152 | - name: cargo test dasp (all features no std) 153 | uses: actions-rs/cargo@v1 154 | with: 155 | command: test 156 | args: --manifest-path dasp/Cargo.toml --no-default-features --features "all-no-std" --verbose 157 | - name: cargo test dasp_signal (all features no std) 158 | uses: actions-rs/cargo@v1 159 | with: 160 | command: test 161 | args: --manifest-path dasp_signal/Cargo.toml --no-default-features --features "all-no-std" --verbose 162 | - name: cargo test dasp_slice (all features no std) 163 | uses: actions-rs/cargo@v1 164 | with: 165 | command: test 166 | args: --manifest-path dasp_slice/Cargo.toml --no-default-features --features "all-no-std" --verbose 167 | - name: cargo test dasp_interpolate (all features no std) 168 | uses: actions-rs/cargo@v1 169 | with: 170 | command: test 171 | args: --manifest-path dasp_interpolate/Cargo.toml --no-default-features --features "all-no-std" --verbose 172 | - name: cargo test window (all features no std) 173 | uses: actions-rs/cargo@v1 174 | with: 175 | command: test 176 | args: --manifest-path dasp_window/Cargo.toml --no-default-features --features "all-no-std" --verbose 177 | - name: cargo test dasp_envelope (all features no std) 178 | uses: actions-rs/cargo@v1 179 | with: 180 | command: test 181 | args: --manifest-path dasp_envelope/Cargo.toml --no-default-features --features "all-no-std" --verbose 182 | 183 | cargo-doc: 184 | runs-on: ubuntu-latest 185 | steps: 186 | - uses: actions/checkout@v2 187 | - name: Update apt 188 | run: sudo apt update 189 | - name: Install alsa 190 | run: sudo apt-get install libasound2-dev 191 | - name: Install stable 192 | uses: actions-rs/toolchain@v1 193 | with: 194 | profile: minimal 195 | toolchain: stable 196 | override: true 197 | - name: cargo doc 198 | uses: actions-rs/cargo@v1 199 | with: 200 | command: doc 201 | args: --all --all-features --verbose 202 | 203 | cargo-publish: 204 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 205 | env: 206 | CRATESIO_TOKEN: ${{ secrets.CRATESIO_TOKEN }} 207 | runs-on: ubuntu-latest 208 | steps: 209 | - uses: actions/checkout@v2 210 | - name: Update apt 211 | run: sudo apt update 212 | - name: Install alsa dev tools 213 | run: sudo apt-get install libasound2-dev 214 | - name: Install stable 215 | uses: actions-rs/toolchain@v1 216 | with: 217 | profile: minimal 218 | toolchain: stable 219 | override: true 220 | - name: cargo publish dasp_sample 221 | continue-on-error: true 222 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_sample/Cargo.toml 223 | - name: wait for crates.io 224 | run: sleep 5 225 | - name: cargo publish dasp_frame 226 | continue-on-error: true 227 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_frame/Cargo.toml 228 | - name: wait for crates.io 229 | run: sleep 5 230 | - name: cargo publish dasp_slice 231 | continue-on-error: true 232 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_slice/Cargo.toml 233 | - name: wait for crates.io 234 | run: sleep 5 235 | - name: cargo publish dasp_ring_buffer 236 | continue-on-error: true 237 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_ring_buffer/Cargo.toml 238 | - name: wait for crates.io 239 | run: sleep 5 240 | - name: cargo publish dasp_peak 241 | continue-on-error: true 242 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_peak/Cargo.toml 243 | - name: wait for crates.io 244 | run: sleep 5 245 | - name: cargo publish dasp_rms 246 | continue-on-error: true 247 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_rms/Cargo.toml 248 | - name: wait for crates.io 249 | run: sleep 5 250 | - name: cargo publish dasp_interpolate 251 | continue-on-error: true 252 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_interpolate/Cargo.toml 253 | - name: wait for crates.io 254 | run: sleep 5 255 | - name: cargo publish dasp_window 256 | continue-on-error: true 257 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_window/Cargo.toml 258 | - name: wait for crates.io 259 | run: sleep 5 260 | - name: cargo publish dasp_envelope 261 | continue-on-error: true 262 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_envelope/Cargo.toml 263 | - name: wait for crates.io 264 | run: sleep 5 265 | - name: cargo publish dasp_signal 266 | continue-on-error: true 267 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_signal/Cargo.toml 268 | - name: wait for crates.io 269 | run: sleep 5 270 | - name: cargo publish dasp_graph 271 | continue-on-error: true 272 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp_graph/Cargo.toml 273 | - name: wait for crates.io 274 | run: sleep 5 275 | - name: cargo publish dasp 276 | continue-on-error: true 277 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path dasp/Cargo.toml 278 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | assets/two_vowels_10k.wav 2 | target/ 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Unreleased 2 | 3 | - Renamed `window-hanning` to `window-hann` 4 | - Made `IntoInterleavedSamples` and `IntoInterleavedSamplesIterator` stop 5 | yielding samples when the underlying signal gets exhausted. This is a breaking 6 | change. The return type of the `IntoInterleavedSamples#next_sample` method was 7 | modified. 8 | 9 | --- 10 | 11 | # 0.11.0 (2020-05-29) 12 | 13 | - Refactor the `sample` crate into a modular collection of crates under `dasp`. 14 | - Rename repository from `sample` to `dasp`, where `dasp` stands for digital 15 | audio signal processing. 16 | - Add a suite of useful feature gates: 17 | - Add `std` to all crates. Can be disabled in order to use `no_std`. 18 | - Add a `all-features-no-std` feature to `dasp`, `dasp_envelope`, 19 | `dasp_interpolate`, `dasp_signal`, `dasp_slice` and `dasp_window`. Enables 20 | all features within a `no_std` context. 21 | - `dasp_envelope` crate: 22 | - `peak` - enables peak detector implementation. 23 | - `rms` - enables RMS detector implementation. 24 | - `dasp_interpolate` crate: 25 | - `floor` - enables `Floor` `Interpolate` implementation. 26 | - `linear` - enables `Linear` `Interpolate` implementation. 27 | - `sinc` - enables `Sinc` `Interpolate` implementation. 28 | - `dasp_signal` crate: 29 | - `boxed` - enables `Signal` implementation for `Box`. 30 | - `bus` - enables `SignalBus` trait. 31 | - `envelope` - enables `SignalEnvelope` trait. 32 | - `rms` - enables `SignalRms` trait. 33 | - `window` - enables `signal::window` module. 34 | - `window-hanning` - enables *hanning* window constructor. 35 | - `window-rectangle` - enables *rectangle* window constructor. 36 | - `dasp_slice` crate: 37 | - `boxed` - enables conversions between boxed slices. 38 | - The `dasp` crate has a feature for each of the above. 39 | - Make **Window** trait generic over its phase and amplitude type. Update the 40 | `dasp_signal::window` module items accordingly. 41 | - Remove unsafe uninitialized ring buffer constructors. 42 | - Remove `equilibrium()` and `identity()` constructors from `Sample` and `Frame` 43 | traitsin favour of `EQUILIBRIUM` and `IDENTITY` associated consts. 44 | - Remove `Frame::n_channels` function in favour of `Frame::CHANNELS` associated 45 | const. 46 | - Add implementation of `Frame` for all primitive `Sample` types where each are 47 | assumed to represent a frame of a monophonic signal. This greatly simplifies 48 | working with monophonic signal sources as demonstrated in the updated 49 | `dasp_signal` crate. 50 | 51 | --- 52 | 53 | *CHANGELOG begins...* 54 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "dasp", 4 | "dasp_envelope", 5 | "dasp_frame", 6 | "dasp_graph", 7 | "dasp_interpolate", 8 | "dasp_peak", 9 | "dasp_ring_buffer", 10 | "dasp_rms", 11 | "dasp_sample", 12 | "dasp_signal", 13 | "dasp_slice", 14 | "dasp_window", 15 | "examples", 16 | ] 17 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Copyright 2016 RustAudio Developers 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 RustAudio Developers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/deps-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustAudio/dasp/567f1ae076480f244206854424b80fd50a246432/assets/deps-graph.png -------------------------------------------------------------------------------- /assets/thumbpiano A#3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustAudio/dasp/567f1ae076480f244206854424b80fd50a246432/assets/thumbpiano A#3.wav -------------------------------------------------------------------------------- /assets/two_vowels.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustAudio/dasp/567f1ae076480f244206854424b80fd50a246432/assets/two_vowels.wav -------------------------------------------------------------------------------- /dasp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp" 3 | description = "A crate providing the fundamentals for working with audio PCM DSP." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["dsp", "bit-depth", "rate", "pcm", "audio"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | dasp_envelope = { version = "0.11", path = "../dasp_envelope", default-features = false, optional = true } 15 | dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } 16 | dasp_graph = { version = "0.11", path = "../dasp_graph", default-features = false, optional = true } 17 | dasp_interpolate = { version = "0.11", path = "../dasp_interpolate", default-features = false, optional = true } 18 | dasp_peak = { version = "0.11", path = "../dasp_peak", default-features = false, optional = true } 19 | dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false, optional = true } 20 | dasp_rms = { version = "0.11", path = "../dasp_rms", default-features = false, optional = true } 21 | dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } 22 | dasp_signal = { version = "0.11", path = "../dasp_signal", default-features = false, optional = true } 23 | dasp_slice = { version = "0.11", path = "../dasp_slice", default-features = false, optional = true } 24 | dasp_window = { version = "0.11", path = "../dasp_window", default-features = false, optional = true } 25 | 26 | [features] 27 | default = ["std"] 28 | all = [ 29 | "std", 30 | "all-no-std", 31 | # TODO: Move these into `all-no-std` once `dasp_graph` gains `no_std` support. 32 | "graph", 33 | "graph-all-nodes", 34 | ] 35 | all-no-std = [ 36 | "envelope", 37 | "envelope-peak", 38 | "envelope-rms", 39 | "interpolate", 40 | "interpolate-floor", 41 | "interpolate-linear", 42 | "interpolate-sinc", 43 | "peak", 44 | "ring_buffer", 45 | "rms", 46 | "signal", 47 | "signal-boxed", 48 | "signal-bus", 49 | "signal-envelope", 50 | "signal-rms", 51 | "signal-window", 52 | "signal-window-hann", 53 | "signal-window-rectangle", 54 | "slice", 55 | "slice-boxed", 56 | "window", 57 | "window-hann", 58 | "window-rectangle", 59 | ] 60 | std = [ 61 | "dasp_envelope/std", 62 | "dasp_frame/std", 63 | "dasp_interpolate/std", 64 | "dasp_peak/std", 65 | "dasp_ring_buffer/std", 66 | "dasp_rms/std", 67 | "dasp_sample/std", 68 | "dasp_signal/std", 69 | "dasp_slice/std", 70 | "dasp_window/std", 71 | ] 72 | envelope = ["dasp_envelope"] 73 | envelope-peak = ["dasp_envelope/peak"] 74 | envelope-rms = ["dasp_envelope/rms"] 75 | graph = ["dasp_graph"] 76 | graph-all-nodes = ["dasp_graph/all-nodes"] 77 | graph-node-boxed = ["dasp_graph/node-boxed"] 78 | graph-node-delay = ["dasp_graph/node-delay"] 79 | graph-node-graph = ["dasp_graph/node-graph"] 80 | graph-node-pass = ["dasp_graph/node-pass"] 81 | graph-node-sum = ["dasp_graph/node-sum"] 82 | interpolate = ["dasp_interpolate"] 83 | interpolate-floor = ["dasp_interpolate/floor"] 84 | interpolate-linear = ["dasp_interpolate/linear"] 85 | interpolate-sinc = ["dasp_interpolate/sinc"] 86 | peak = ["dasp_peak"] 87 | ring_buffer = ["dasp_ring_buffer"] 88 | rms = ["dasp_rms"] 89 | signal = ["dasp_signal"] 90 | signal-boxed = ["dasp_signal/boxed"] 91 | signal-bus = ["dasp_signal/bus"] 92 | signal-envelope = ["dasp_signal/envelope", "envelope"] 93 | signal-rms = ["dasp_signal/rms", "rms"] 94 | signal-window = ["dasp_signal/window", "window"] 95 | signal-window-hann = ["dasp_signal/window-hann", "window-hann"] 96 | signal-window-rectangle = ["dasp_signal/window-rectangle", "window-rectangle"] 97 | slice = ["dasp_slice"] 98 | slice-boxed = ["dasp_slice/boxed"] 99 | window = ["dasp_window"] 100 | window-hann = ["dasp_window/hann"] 101 | window-rectangle = ["dasp_window/rectangle"] 102 | 103 | [package.metadata.docs.rs] 104 | all-features = true 105 | -------------------------------------------------------------------------------- /dasp/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! **dasp** (formerly known as ***sample***) is a suite of crates providing the fundamentals for 2 | //! working with pulse-code modulation **digital audio signal processing**. In other words, 3 | //! **dasp** provides a suite of low-level, high-performance tools including types, traits and 4 | //! functions for working with digital audio signals. 5 | //! 6 | //! Each of the **dasp** crates are re-exported under their respective 7 | //! [modules](file:///home/mindtree/programming/rust/dasp/target/doc/dasp/index.html#modules). 8 | //! 9 | //! ## Highlights 10 | //! 11 | //! The following are some of the more interesting items within the dasp collection: 12 | //! 13 | //! - Use the [**Sample** trait](./trait.Sample.html) to remain generic across bit-depth. 14 | //! - Use the [**Frame** trait](./frame/trait.Frame.html) to remain generic over channel layout. 15 | //! - Use the [**Signal** trait](./signal/trait.Signal.html) for working with **Iterators** that 16 | //! yield **Frames**. 17 | //! - See the [**signal** module](./signal/index.html) for a collection of interesting signal 18 | //! constructors (e.g. `sine`, `noise`, `from_iter`, etc). 19 | //! - Use the [**slice** module](./slice/index.html) for working with slices of **Samples** and 20 | //! **Frames**. 21 | //! - See the [**sample::types** module](./sample/types/index.html) for provided custom sample 22 | //! types. 23 | //! - See the [**Converter** type](./signal/interpolate/struct.Converter.html) for sample rate 24 | //! conversion and scaling. 25 | //! - See the [**ring_buffer** module](./ring_buffer/index.html) for fast FIFO queue options. 26 | //! - See the [**graph** module](./graph/index.html) for working with dynamic audio graphs. 27 | //! 28 | //! ## Optional Features 29 | //! 30 | //! By default, only the **sample** and **frame** modules and their respective traits are included 31 | //! within this crate. You may pick and choose between the following features for additional 32 | //! functionality. 33 | //! 34 | //! - The **all** feature enables all of the following features. 35 | //! - The **std** feature enables the std library. This is enabled by default. 36 | //! - The **all-no-std** feature enables all of the following features (without std). 37 | //! 38 | //! The following features map to each of the sub-crates and their respective features. 39 | //! 40 | //! - The **envelope** feature enables the `dasp_envelope` crate via the 41 | //! [envelope](./envelope/index.html) module. 42 | //! - The **envelope-peak** feature enables peak envelope detection. 43 | //! - The **envelope-rms** feature enables RMS envelope detection. 44 | //! - The **graph** feature enables the `dasp_graph` crate via the [graph](./graph/index.html) 45 | //! module. 46 | //! - The **node-boxed** feature provides a `Node` implementation for `Box`. 47 | //! - The **node-delay** feature provides a simple multi-channel `Delay` node. 48 | //! - The **node-graph** feature provides an implementation of `Node` for a type that encapsulates 49 | //! another `dasp` graph type. 50 | //! - The **node-pass** feature provides a `Pass` node that simply passes audio from its 51 | //! inputs to its outputs. 52 | //! - The **node-signal** feature provides an implementation of `Node` for `dyn Signal`. 53 | //! - The **node-sum** feature provides `Sum` and `SumBuffers` `Node` implementations. 54 | //! - The **interpolate** feature enables the `dasp_interpolate` crate via the 55 | //! [interpolate](./interpolate/index.html) module. 56 | //! - The **interpolate-floor** feature enables a floor interpolation implementation. 57 | //! - The **interpolate-linear** feature enables a linear interpolation implementation. 58 | //! - The **interpolate-sinc** feature enables a sinc interpolation implementation. 59 | //! - The **peak** feature enables the `dasp_peak` crate via the [peak](./peak/index.html) module. 60 | //! - The **ring_buffer** feature enables the `dasp_ring_buffer` crate via the 61 | //! [ring_buffer](./peak/index.html) module. 62 | //! - The **rms** feature enables the `dasp_rms` crate via the [rms](./rms/index.html) module. 63 | //! - The **signal** feature enables the `dasp_signal` crate via the [signal](./signal/index.html) 64 | //! module. 65 | //! - The **signal-boxed** feature enables an implementation of **Signal** for `Box`. 67 | //! - The **signal-bus** feature enables the [**SignalBus**](./signal/bus/trait.SignalBus.html) 68 | //! trait. 69 | //! - The **signal-envelope** feature enables the 70 | //! [**SignalEnvelope**](./signal/envelope/trait.SignalEnvelope.html) trait. 71 | //! - The **signal-rms** feature enables the [**SignalRms**](./signal/rms/trait.SignalRms.html) 72 | //! trait. 73 | //! - The **signal-window** feature enables the 74 | //! [**signal::window**](./signal/window/index.html) module. 75 | //! - The **signal-window-hann** enables the 76 | //! [**signal::window::hann**](./signal/window/fn.hann.html) window constructor. 77 | //! - The **signal-window-rectangle** enables the 78 | //! [**signal::window::rectangle**](./signal/window/fn.rectangle.html) window constructor. 79 | //! - The **slice** feature enables the `dasp_slice` crate via the [slice](./slice/index.html) 80 | //! module. 81 | //! - The **slice-boxed** feature enables boxed slice conversion traits and functions. 82 | //! - The **window** feature enables the `dasp_window` crate via the [window](./window/index.html) 83 | //! module. 84 | //! - The **window-hann** feature enables the [**Hann**](./window/struct.Hann.html) 85 | //! window implementation. 86 | //! - The **window-rectangle** feature enables the 87 | //! [**Rectangle**](./window/struct.Rectangle.html) window implementation. 88 | //! 89 | //! You can also enable all of the above features with the `--all-features` flag. 90 | //! 91 | //! ### no_std 92 | //! 93 | //! If working in a `no_std` context, you can disable the default **std** feature with 94 | //! `--no-default-features`. 95 | //! 96 | //! To enable all of the above features in a `no_std` context, enable the **all-no-std** feature. 97 | //! 98 | //! *Note: The **graph** module is currently only available with the **std** feature enabled. 99 | //! Adding support for `no_std` is pending the addition of support for `no_std` in petgraph. See 100 | //! [this PR](https://github.com/petgraph/petgraph/pull/238). 101 | 102 | #![cfg_attr(not(feature = "std"), no_std)] 103 | 104 | #[cfg(feature = "envelope")] 105 | #[doc(inline)] 106 | pub use dasp_envelope as envelope; 107 | #[doc(inline)] 108 | pub use dasp_frame::{self as frame, Frame}; 109 | // TODO: Remove `std` requirement once `dasp_graph` gains `no_std` support. 110 | #[cfg(all(feature = "graph", feature = "std"))] 111 | #[doc(inline)] 112 | pub use dasp_graph as graph; 113 | #[cfg(feature = "interpolate")] 114 | #[doc(inline)] 115 | pub use dasp_interpolate as interpolate; 116 | #[cfg(feature = "peak")] 117 | #[doc(inline)] 118 | pub use dasp_peak as peak; 119 | #[cfg(feature = "ring_buffer")] 120 | #[doc(inline)] 121 | pub use dasp_ring_buffer as ring_buffer; 122 | #[cfg(feature = "rms")] 123 | #[doc(inline)] 124 | pub use dasp_rms as rms; 125 | #[doc(inline)] 126 | pub use dasp_sample::{self as sample, Sample}; 127 | #[cfg(feature = "signal")] 128 | #[doc(inline)] 129 | pub use dasp_signal::{self as signal, Signal}; 130 | #[cfg(feature = "slice")] 131 | #[doc(inline)] 132 | pub use dasp_slice as slice; 133 | #[cfg(feature = "window")] 134 | #[doc(inline)] 135 | pub use dasp_window as window; 136 | -------------------------------------------------------------------------------- /dasp_envelope/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_envelope" 3 | version = "0.11.0" 4 | description = "Audio PCM DSP envelope detection with peak and RMS implementations." 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["envelope", "detector", "follower", "peak", "rms"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } 15 | dasp_peak = { version = "0.11", path = "../dasp_peak", default-features = false, optional = true } 16 | dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false } 17 | dasp_rms = { version = "0.11", path = "../dasp_rms", default-features = false, optional = true } 18 | dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } 19 | 20 | [features] 21 | default = ["std"] 22 | all = ["std", "all-no-std"] 23 | all-no-std = [ 24 | "peak", 25 | "rms", 26 | ] 27 | std = [ 28 | "dasp_frame/std", 29 | "dasp_peak/std", 30 | "dasp_ring_buffer/std", 31 | "dasp_rms/std", 32 | "dasp_sample/std", 33 | ] 34 | peak = ["dasp_peak"] 35 | rms = ["dasp_rms"] 36 | 37 | [package.metadata.docs.rs] 38 | all-features = true 39 | -------------------------------------------------------------------------------- /dasp_envelope/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_envelope/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_envelope/src/detect/mod.rs: -------------------------------------------------------------------------------- 1 | use dasp_frame::Frame; 2 | use dasp_sample::Sample; 3 | use ops::f32::powf32; 4 | 5 | #[cfg(feature = "peak")] 6 | pub use self::peak::Peak; 7 | 8 | mod ops; 9 | #[cfg(feature = "peak")] 10 | mod peak; 11 | #[cfg(feature = "rms")] 12 | mod rms; 13 | 14 | /// A type that can be used to detect the envelope of a signal. 15 | #[derive(Clone, Debug)] 16 | pub struct Detector 17 | where 18 | F: Frame, 19 | D: Detect, 20 | { 21 | last_env_frame: D::Output, 22 | attack_gain: f32, 23 | release_gain: f32, 24 | detect: D, 25 | } 26 | 27 | /// Types that may be used to detect an envelope over a signal. 28 | pub trait Detect 29 | where 30 | F: Frame, 31 | { 32 | /// The result of detection. 33 | type Output: Frame; 34 | /// Given some frame, return the detected envelope over each channel. 35 | fn detect(&mut self, frame: F) -> Self::Output; 36 | } 37 | 38 | fn calc_gain(n_frames: f32) -> f32 { 39 | if n_frames == 0.0 { 40 | 0.0 41 | } else { 42 | powf32(core::f32::consts::E, -1.0 / n_frames) 43 | } 44 | } 45 | 46 | impl Detector 47 | where 48 | F: Frame, 49 | D: Detect, 50 | { 51 | /// Construct a **Detector** with the given **Detect** implementation. 52 | pub fn new(detect: D, attack_frames: f32, release_frames: f32) -> Self { 53 | Detector { 54 | last_env_frame: D::Output::EQUILIBRIUM, 55 | attack_gain: calc_gain(attack_frames), 56 | release_gain: calc_gain(release_frames), 57 | detect: detect, 58 | } 59 | } 60 | 61 | /// Set the **Detector**'s attack time as a number of frames. 62 | pub fn set_attack_frames(&mut self, frames: f32) { 63 | self.attack_gain = calc_gain(frames); 64 | } 65 | 66 | /// Set the **Detector**'s release time as a number of frames. 67 | pub fn set_release_frames(&mut self, frames: f32) { 68 | self.release_gain = calc_gain(frames); 69 | } 70 | 71 | /// Given the next input signal frame, detect and return the next envelope frame. 72 | pub fn next(&mut self, frame: F) -> D::Output { 73 | let Detector { 74 | attack_gain, 75 | release_gain, 76 | ref mut detect, 77 | ref mut last_env_frame, 78 | } = *self; 79 | 80 | let detected_frame = detect.detect(frame); 81 | let new_env_frame = last_env_frame.zip_map(detected_frame, |l, d| { 82 | let gain = if l < d { attack_gain } else { release_gain }; 83 | let diff = l.add_amp(-d.to_signed_sample()); 84 | d.add_amp(diff.mul_amp(gain.to_sample()).to_sample()) 85 | }); 86 | *last_env_frame = new_env_frame; 87 | new_env_frame 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /dasp_envelope/src/detect/ops.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | pub mod f32 { 4 | #[cfg(feature = "std")] 5 | pub fn powf32(a: f32, b: f32) -> f32 { 6 | a.powf(b) 7 | } 8 | #[cfg(not(feature = "std"))] 9 | pub fn powf32(a: f32, b: f32) -> f32 { 10 | unsafe { core::intrinsics::powf32(a, b) } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dasp_envelope/src/detect/peak.rs: -------------------------------------------------------------------------------- 1 | //! Peak detector implementations. 2 | //! 3 | //! ### Required Features 4 | //! 5 | //! - When using `dasp_envelope`, this module requires the **peak** feature to be enabled. 6 | //! - When using `dasp`, this module requires the **envelope-peak** feature to be enabled. 7 | 8 | use crate::{Detect, Detector}; 9 | use dasp_frame::Frame; 10 | use dasp_peak as peak; 11 | 12 | /// A `Peak` detector, generic over the `FullWave`, `PositiveHalfWave`, `NegativeHalfWave` 13 | /// rectifiers. 14 | /// 15 | /// ### Required Features 16 | /// 17 | /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. 18 | /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. 19 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 20 | pub struct Peak { 21 | rectifier: R, 22 | } 23 | 24 | impl Peak { 25 | /// A signal rectifier that produces the absolute amplitude from samples. 26 | /// 27 | /// ### Required Features 28 | /// 29 | /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. 30 | /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. 31 | pub fn full_wave() -> Self { 32 | peak::FullWave.into() 33 | } 34 | } 35 | 36 | impl Peak { 37 | /// A signal rectifier that produces only the positive samples. 38 | /// 39 | /// ### Required Features 40 | /// 41 | /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. 42 | /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. 43 | pub fn positive_half_wave() -> Self { 44 | peak::PositiveHalfWave.into() 45 | } 46 | } 47 | 48 | impl Peak { 49 | /// A signal rectifier that produces only the negative samples. 50 | /// 51 | /// ### Required Features 52 | /// 53 | /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. 54 | /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. 55 | pub fn negative_half_wave() -> Self { 56 | peak::NegativeHalfWave.into() 57 | } 58 | } 59 | 60 | impl Detector> 61 | where 62 | F: Frame, 63 | R: peak::Rectifier, 64 | { 65 | /// Construct a new **Peak** **Detector** that uses the given rectifier. 66 | /// 67 | /// ### Required Features 68 | /// 69 | /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. 70 | /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. 71 | pub fn peak_from_rectifier(rectifier: R, attack_frames: f32, release_frames: f32) -> Self { 72 | let peak = rectifier.into(); 73 | Self::new(peak, attack_frames, release_frames) 74 | } 75 | } 76 | 77 | impl Detector> 78 | where 79 | F: Frame, 80 | { 81 | /// Construct a new full wave **Peak** **Detector**. 82 | /// 83 | /// ### Required Features 84 | /// 85 | /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. 86 | /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. 87 | pub fn peak(attack_frames: f32, release_frames: f32) -> Self { 88 | let peak = Peak::full_wave(); 89 | Self::new(peak, attack_frames, release_frames) 90 | } 91 | } 92 | 93 | impl Detector> 94 | where 95 | F: Frame, 96 | { 97 | /// Construct a new positive half wave **Peak** **Detector**. 98 | /// 99 | /// ### Required Features 100 | /// 101 | /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. 102 | /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. 103 | pub fn peak_positive_half_wave(attack_frames: f32, release_frames: f32) -> Self { 104 | let peak = Peak::positive_half_wave(); 105 | Self::new(peak, attack_frames, release_frames) 106 | } 107 | } 108 | 109 | impl Detector> 110 | where 111 | F: Frame, 112 | { 113 | /// Construct a new positive half wave **Peak** **Detector**. 114 | /// 115 | /// ### Required Features 116 | /// 117 | /// - When using `dasp_envelope`, this item requires the **peak** feature to be enabled. 118 | /// - When using `dasp`, this item requires the **envelope-peak** feature to be enabled. 119 | pub fn peak_negative_half_wave(attack_frames: f32, release_frames: f32) -> Self { 120 | let peak = Peak::negative_half_wave(); 121 | Self::new(peak, attack_frames, release_frames) 122 | } 123 | } 124 | 125 | impl Detect for Peak 126 | where 127 | F: Frame, 128 | R: peak::Rectifier, 129 | { 130 | type Output = R::Output; 131 | fn detect(&mut self, frame: F) -> Self::Output { 132 | self.rectifier.rectify(frame) 133 | } 134 | } 135 | 136 | impl From for Peak { 137 | fn from(rectifier: R) -> Self { 138 | Peak { 139 | rectifier: rectifier, 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /dasp_envelope/src/detect/rms.rs: -------------------------------------------------------------------------------- 1 | use crate::{Detect, Detector}; 2 | use dasp_frame::Frame; 3 | use dasp_ring_buffer as ring_buffer; 4 | use dasp_rms as rms; 5 | 6 | impl Detect for rms::Rms 7 | where 8 | F: Frame, 9 | S: ring_buffer::Slice + ring_buffer::SliceMut, 10 | { 11 | type Output = F::Float; 12 | fn detect(&mut self, frame: F) -> Self::Output { 13 | self.next(frame) 14 | } 15 | } 16 | 17 | impl Detector> 18 | where 19 | F: Frame, 20 | S: ring_buffer::Slice + ring_buffer::SliceMut, 21 | { 22 | /// Construct a new **Rms** **Detector**. 23 | /// 24 | /// ### Required Features 25 | /// 26 | /// - When using `dasp_envelope`, this item requires the **rms** feature to be enabled. 27 | /// - When using `dasp`, this item requires the **envelope-rms** feature to be enabled. 28 | pub fn rms(buffer: ring_buffer::Fixed, attack_frames: f32, release_frames: f32) -> Self { 29 | let rms = rms::Rms::new(buffer); 30 | Self::new(rms, attack_frames, release_frames) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /dasp_envelope/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An abstraction supporting different kinds of envelope detection. 2 | //! 3 | //! - The [**Detect**](./trait.Detect.html) trait provides an abstraction for generalising over 4 | //! types of envelope detection. 5 | //! - The [**Detector**](./struct.Detector.html) type allows for applying a **Detect** 6 | //! implementation in order to detect the envelope of a signal. 7 | //! 8 | //! See the `dasp_signal` crate (or `dasp::signal` module) **SignalWindow** trait for a convenient 9 | //! way to detect envelopes over arbitrary signals. 10 | //! 11 | //! ### Optional Features 12 | //! 13 | //! - The **peak** feature (or **envelope-peak** feature if using `dasp`) provides a peak envelope 14 | //! detection implementation. 15 | //! - The **rms** feature (or **envelope-rms** feature if using `dasp`) provides an RMS envelope 16 | //! detection implementation. 17 | //! 18 | //! ### no_std 19 | //! 20 | //! If working in a `no_std` context, you can disable the default **std** feature with 21 | //! `--no-default-features`. 22 | //! 23 | //! To enable all of the above features in a `no_std` context, enable the **all-no-std** feature. 24 | 25 | #![cfg_attr(not(feature = "std"), no_std)] 26 | #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] 27 | 28 | pub mod detect; 29 | 30 | pub use self::detect::{Detect, Detector}; 31 | -------------------------------------------------------------------------------- /dasp_frame/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_frame" 3 | description = "An abstraction for audio PCM DSP frames, along with useful conversions and operations." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["dsp", "frame", "channel", "pcm", "audio"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } 15 | 16 | [features] 17 | default = ["std"] 18 | std = ["dasp_sample/std"] 19 | 20 | [package.metadata.docs.rs] 21 | all-features = true 22 | -------------------------------------------------------------------------------- /dasp_frame/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_frame/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_graph/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_graph" 3 | description = "A digital audio signal processing graph." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["dsp", "audio", "graph", "pcm", "audio"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [features] 14 | default = ["all-nodes"] 15 | all-nodes = ["node-boxed", "node-delay", "node-graph", "node-pass", "node-signal", "node-sum"] 16 | node-boxed = [] 17 | node-delay = ["dasp_ring_buffer"] 18 | node-graph = [] 19 | node-pass = [] 20 | node-signal = ["dasp_frame", "dasp_signal"] 21 | node-sum = ["dasp_slice"] 22 | 23 | [dependencies] 24 | dasp_frame = { version = "0.11", default-features = false, features = ["std"], optional = true } 25 | dasp_ring_buffer = { version = "0.11", default-features = false, features = ["std"], optional = true } 26 | dasp_signal = { version = "0.11", default-features = false, features = ["std"], optional = true } 27 | dasp_slice = { version = "0.11", default-features = false, features = ["std"], optional = true } 28 | petgraph = { version = "0.5", default-features = false } 29 | 30 | [dev-dependencies] 31 | petgraph = { version = "0.5", features = ["stable_graph"] } 32 | -------------------------------------------------------------------------------- /dasp_graph/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_graph/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_graph/src/buffer.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use core::ops::{Deref, DerefMut}; 3 | 4 | /// The fixed-size buffer used for processing the graph. 5 | #[derive(Clone)] 6 | pub struct Buffer { 7 | data: [f32; Self::LEN], 8 | } 9 | 10 | impl Buffer { 11 | /// The fixed length of the **Buffer** type. 12 | pub const LEN: usize = 64; 13 | /// A silent **Buffer**. 14 | pub const SILENT: Self = Buffer { 15 | data: [0.0; Self::LEN], 16 | }; 17 | 18 | /// Short-hand for writing silence to the whole buffer. 19 | pub fn silence(&mut self) { 20 | self.data.copy_from_slice(&Self::SILENT) 21 | } 22 | } 23 | 24 | impl Default for Buffer { 25 | fn default() -> Self { 26 | Self::SILENT 27 | } 28 | } 29 | 30 | impl From<[f32; Self::LEN]> for Buffer { 31 | fn from(data: [f32; Self::LEN]) -> Self { 32 | Buffer { data } 33 | } 34 | } 35 | 36 | impl fmt::Debug for Buffer { 37 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 38 | fmt::Debug::fmt(&self.data[..], f) 39 | } 40 | } 41 | 42 | impl PartialEq for Buffer { 43 | fn eq(&self, other: &Self) -> bool { 44 | self[..] == other[..] 45 | } 46 | } 47 | 48 | impl Deref for Buffer { 49 | type Target = [f32]; 50 | fn deref(&self) -> &Self::Target { 51 | &self.data[..] 52 | } 53 | } 54 | 55 | impl DerefMut for Buffer { 56 | fn deref_mut(&mut self) -> &mut Self::Target { 57 | &mut self.data[..] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /dasp_graph/src/node/boxed.rs: -------------------------------------------------------------------------------- 1 | use crate::{Buffer, Input, Node}; 2 | use core::fmt; 3 | use core::ops::{Deref, DerefMut}; 4 | 5 | /// A wrapper around a `Box`. 6 | /// 7 | /// Provides the necessary `Sized` implementation to allow for compatibility with the graph process 8 | /// function. 9 | pub struct BoxedNode(pub Box); 10 | 11 | /// A wrapper around a `Box`. 12 | /// 13 | /// Provides the necessary `Sized` implementation to allow for compatibility with the graph process 14 | /// function. 15 | /// 16 | /// Useful when the ability to send nodes from one thread to another is required. E.g. this is 17 | /// common when initialising nodes or the audio graph itself on one thread before sending them to 18 | /// the audio thread. 19 | pub struct BoxedNodeSend(pub Box); 20 | 21 | impl BoxedNode { 22 | /// Create a new `BoxedNode` around the given `node`. 23 | /// 24 | /// This is short-hand for `BoxedNode::from(Box::new(node))`. 25 | pub fn new(node: T) -> Self 26 | where 27 | T: 'static + Node, 28 | { 29 | Self::from(Box::new(node)) 30 | } 31 | } 32 | 33 | impl BoxedNodeSend { 34 | /// Create a new `BoxedNode` around the given `node`. 35 | /// 36 | /// This is short-hand for `BoxedNode::from(Box::new(node))`. 37 | pub fn new(node: T) -> Self 38 | where 39 | T: 'static + Node + Send, 40 | { 41 | Self::from(Box::new(node)) 42 | } 43 | } 44 | 45 | impl Node for BoxedNode { 46 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 47 | self.0.process(inputs, output) 48 | } 49 | } 50 | 51 | impl Node for BoxedNodeSend { 52 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 53 | self.0.process(inputs, output) 54 | } 55 | } 56 | 57 | impl From> for BoxedNode 58 | where 59 | T: 'static + Node, 60 | { 61 | fn from(n: Box) -> Self { 62 | BoxedNode(n as Box) 63 | } 64 | } 65 | 66 | impl From> for BoxedNodeSend 67 | where 68 | T: 'static + Node + Send, 69 | { 70 | fn from(n: Box) -> Self { 71 | BoxedNodeSend(n as Box) 72 | } 73 | } 74 | 75 | impl From for Box { 76 | fn from(val: BoxedNode) -> Self { 77 | val.0 78 | } 79 | } 80 | 81 | impl From for Box { 82 | fn from(val: BoxedNodeSend) -> Self { 83 | val.0 84 | } 85 | } 86 | 87 | impl fmt::Debug for BoxedNode { 88 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 89 | f.debug_struct("BoxedNode").finish() 90 | } 91 | } 92 | 93 | impl fmt::Debug for BoxedNodeSend { 94 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 95 | f.debug_struct("BoxedNodeSend").finish() 96 | } 97 | } 98 | 99 | impl Deref for BoxedNode { 100 | type Target = Box; 101 | fn deref(&self) -> &Self::Target { 102 | &self.0 103 | } 104 | } 105 | 106 | impl Deref for BoxedNodeSend { 107 | type Target = Box; 108 | fn deref(&self) -> &Self::Target { 109 | &self.0 110 | } 111 | } 112 | 113 | impl DerefMut for BoxedNode { 114 | fn deref_mut(&mut self) -> &mut Self::Target { 115 | &mut self.0 116 | } 117 | } 118 | 119 | impl DerefMut for BoxedNodeSend { 120 | fn deref_mut(&mut self) -> &mut Self::Target { 121 | &mut self.0 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /dasp_graph/src/node/delay.rs: -------------------------------------------------------------------------------- 1 | use crate::{Buffer, Input, Node}; 2 | use dasp_ring_buffer as ring_buffer; 3 | 4 | /// A delay node, where the delay duration for each channel is equal to the length of the inner 5 | /// ring buffer associated with that channel. 6 | /// 7 | /// Assumes that there is one input node, and that the number of input buffers, output buffers and 8 | /// ring buffers all match. 9 | #[derive(Clone, Debug, PartialEq)] 10 | pub struct Delay(pub Vec>); 11 | 12 | impl Node for Delay 13 | where 14 | S: ring_buffer::SliceMut, 15 | { 16 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 17 | // Retrieve the single input, ignore any others. 18 | let input = match inputs.first() { 19 | Some(input) => input, 20 | None => return, 21 | }; 22 | 23 | // Apply the delay across each channel. 24 | for ((ring_buf, in_buf), out_buf) in self.0.iter_mut().zip(input.buffers()).zip(output) { 25 | for (i, out) in out_buf.iter_mut().enumerate() { 26 | *out = ring_buf.push(in_buf[i]); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /dasp_graph/src/node/graph.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of `Node` for a graph of nodes. 2 | //! 3 | //! Allows for nesting subgraphs within nodes of a graph. 4 | 5 | use crate::{Buffer, Input, Node, NodeData, Processor}; 6 | use core::marker::PhantomData; 7 | use petgraph::data::DataMapMut; 8 | use petgraph::visit::{Data, GraphBase, IntoNeighborsDirected, Visitable}; 9 | 10 | pub struct GraphNode 11 | where 12 | G: Visitable, 13 | { 14 | pub processor: Processor, 15 | pub graph: G, 16 | pub input_nodes: Vec, 17 | pub output_node: G::NodeId, 18 | pub node_type: PhantomData, 19 | } 20 | 21 | impl Node for GraphNode 22 | where 23 | G: Data> + DataMapMut + Visitable, 24 | for<'a> &'a G: GraphBase + IntoNeighborsDirected, 25 | T: Node, 26 | { 27 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 28 | let GraphNode { 29 | ref mut processor, 30 | ref mut graph, 31 | ref input_nodes, 32 | output_node, 33 | .. 34 | } = *self; 35 | 36 | // Write the input buffers to the input nodes. 37 | for (input, &in_n) in inputs.iter().zip(input_nodes) { 38 | let in_node_bufs = &mut graph 39 | .node_weight_mut(in_n) 40 | .expect("no node for graph node's input node ID") 41 | .buffers; 42 | for (in_node_buf, in_buf) in in_node_bufs.iter_mut().zip(input.buffers()) { 43 | in_node_buf.copy_from_slice(in_buf); 44 | } 45 | } 46 | 47 | // Process the graph. 48 | processor.process(graph, output_node); 49 | 50 | // Write the output node buffers to the output buffers. 51 | let out_node_bufs = &mut graph 52 | .node_weight_mut(output_node) 53 | .expect("no node for graph node's output node ID") 54 | .buffers; 55 | for (out_buf, out_node_buf) in output.iter_mut().zip(out_node_bufs) { 56 | out_buf.copy_from_slice(out_node_buf); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /dasp_graph/src/node/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::buffer::Buffer; 2 | use core::fmt; 3 | 4 | #[cfg(feature = "node-boxed")] 5 | pub use boxed::{BoxedNode, BoxedNodeSend}; 6 | #[cfg(feature = "node-delay")] 7 | pub use delay::Delay; 8 | #[cfg(feature = "node-graph")] 9 | pub use graph::GraphNode; 10 | #[cfg(feature = "node-pass")] 11 | pub use pass::Pass; 12 | #[cfg(feature = "node-sum")] 13 | pub use sum::{Sum, SumBuffers}; 14 | 15 | #[cfg(feature = "node-boxed")] 16 | mod boxed; 17 | #[cfg(feature = "node-delay")] 18 | mod delay; 19 | #[cfg(feature = "node-graph")] 20 | mod graph; 21 | #[cfg(feature = "node-pass")] 22 | mod pass; 23 | #[cfg(feature = "node-signal")] 24 | mod signal; 25 | #[cfg(feature = "node-sum")] 26 | mod sum; 27 | 28 | /// The `Node` type used within a dasp graph must implement this trait. 29 | /// 30 | /// The implementation describes how audio is processed from its inputs to outputs. 31 | /// 32 | /// - Audio **sources** or **inputs** may simply ignore the `inputs` field and write their source 33 | /// data directly to the `output` buffers. 34 | /// - Audio **processors**, **effects** or **sinks** may read from their `inputs`, apply some 35 | /// custom processing and write the result to their `output` buffers. 36 | /// 37 | /// Multiple `Node` implementations are provided and can be enabled or disabled via [their 38 | /// associated features](../index.html#optional-features). 39 | /// 40 | /// # Example 41 | /// 42 | /// The following demonstrates how to implement a simple node that sums each of its inputs onto the 43 | /// output. 44 | /// 45 | /// ```rust 46 | /// use dasp_graph::{Buffer, Input, Node}; 47 | /// 48 | /// // Our new `Node` type. 49 | /// pub struct Sum; 50 | /// 51 | /// // Implement the `Node` trait for our new type. 52 | /// # #[cfg(feature = "dasp_slice")] 53 | /// impl Node for Sum { 54 | /// fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 55 | /// // Fill the output with silence. 56 | /// for out_buffer in output.iter_mut() { 57 | /// out_buffer.silence(); 58 | /// } 59 | /// // Sum the inputs onto the output. 60 | /// for (channel, out_buffer) in output.iter_mut().enumerate() { 61 | /// for input in inputs { 62 | /// let in_buffers = input.buffers(); 63 | /// if let Some(in_buffer) = in_buffers.get(channel) { 64 | /// dasp_slice::add_in_place(out_buffer, in_buffer); 65 | /// } 66 | /// } 67 | /// } 68 | /// } 69 | /// } 70 | /// ``` 71 | pub trait Node { 72 | /// Process some audio given a list of the node's `inputs` and write the result to the `output` 73 | /// buffers. 74 | /// 75 | /// `inputs` represents a list of all nodes with direct edges toward this node. Each 76 | /// [`Input`](./struct.Input.html) within the list can providee a reference to the output 77 | /// buffers of their corresponding node. 78 | /// 79 | /// The `inputs` may be ignored if the implementation is for a source node. Alternatively, if 80 | /// the `Node` only supports a specific number of `input`s, it is up to the user to decide how 81 | /// they wish to enforce this or provide feedback at the time of graph and edge creation. 82 | /// 83 | /// This `process` method is called by the [`Processor`](../struct.Processor.html) as it 84 | /// traverses the graph during audio rendering. 85 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]); 86 | } 87 | 88 | /// A reference to another node that is an input to the current node. 89 | /// 90 | /// *TODO: It may be useful to provide some information that can uniquely identify the input node. 91 | /// This could be useful to allow to distinguish between side-chained and regular inputs for 92 | /// example.* 93 | pub struct Input { 94 | buffers_ptr: *const Buffer, 95 | buffers_len: usize, 96 | } 97 | 98 | impl Input { 99 | // Constructor solely for use within the graph `process` function. 100 | pub(crate) fn new(slice: &[Buffer]) -> Self { 101 | let buffers_ptr = slice.as_ptr(); 102 | let buffers_len = slice.len(); 103 | Input { 104 | buffers_ptr, 105 | buffers_len, 106 | } 107 | } 108 | 109 | /// A reference to the buffers of the input node. 110 | pub fn buffers(&self) -> &[Buffer] { 111 | // As we know that an `Input` can only be constructed during a call to the graph `process` 112 | // function, we can be sure that our slice is still valid as long as the input itself is 113 | // alive. 114 | unsafe { std::slice::from_raw_parts(self.buffers_ptr, self.buffers_len) } 115 | } 116 | } 117 | 118 | // Inputs can only be created by the `dasp_graph::process` implementation and only ever live as 119 | // long as the lifetime of the call to the function. Thus, it's safe to implement this so that 120 | // `Send` closures can be stored within the graph and sent between threads. 121 | unsafe impl Send for Input {} 122 | 123 | impl fmt::Debug for Input { 124 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 125 | fmt::Debug::fmt(self.buffers(), f) 126 | } 127 | } 128 | 129 | impl Node for &mut T 130 | where 131 | T: Node + ?Sized, 132 | { 133 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 134 | (**self).process(inputs, output) 135 | } 136 | } 137 | 138 | impl Node for Box 139 | where 140 | T: Node + ?Sized, 141 | { 142 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 143 | (**self).process(inputs, output) 144 | } 145 | } 146 | 147 | impl Node for dyn Fn(&[Input], &mut [Buffer]) { 148 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 149 | (*self)(inputs, output) 150 | } 151 | } 152 | 153 | impl Node for dyn FnMut(&[Input], &mut [Buffer]) { 154 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 155 | (*self)(inputs, output) 156 | } 157 | } 158 | 159 | impl Node for fn(&[Input], &mut [Buffer]) { 160 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 161 | (*self)(inputs, output) 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /dasp_graph/src/node/pass.rs: -------------------------------------------------------------------------------- 1 | use crate::{Buffer, Input, Node}; 2 | 3 | /// A simple node that passes an input directly to the output. 4 | /// 5 | /// Works by mem-copying each buffer of the first input to each buffer of the output respectively. 6 | /// 7 | /// This can be useful as an intermediary node when feeding the output of a node back into one of 8 | /// its inputs. It can also be useful for discarding excess input channels by having a `Pass` with 9 | /// less output buffers than its input. 10 | #[derive(Clone, Debug, PartialEq)] 11 | pub struct Pass; 12 | 13 | impl Node for Pass { 14 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 15 | let input = match inputs.first() { 16 | None => return, 17 | Some(input) => input, 18 | }; 19 | for (out_buf, in_buf) in output.iter_mut().zip(input.buffers()) { 20 | out_buf.copy_from_slice(in_buf); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /dasp_graph/src/node/signal.rs: -------------------------------------------------------------------------------- 1 | use crate::{Buffer, Input, Node}; 2 | use dasp_frame::Frame; 3 | use dasp_signal::Signal; 4 | 5 | impl Node for dyn Signal 6 | where 7 | F: Frame, 8 | { 9 | fn process(&mut self, _inputs: &[Input], output: &mut [Buffer]) { 10 | for ix in 0..Buffer::LEN { 11 | let frame = self.next(); 12 | for (ch, out) in output.iter_mut().enumerate().take(F::CHANNELS) { 13 | // Safe, as ch never exceeds min(F::CHANNELS, output.len()). 14 | out[ix] = unsafe { *frame.channel_unchecked(ch) }; 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /dasp_graph/src/node/sum.rs: -------------------------------------------------------------------------------- 1 | use crate::{Buffer, Input, Node}; 2 | 3 | /// A stateless node that sums each of the inputs onto the output. 4 | /// 5 | /// Assumes that the number of buffers per input is equal to the number of output buffers. 6 | #[derive(Clone, Debug, PartialEq)] 7 | pub struct Sum; 8 | 9 | /// A stateless node that sums all of the buffers of all of the inputs onto each of the output 10 | /// buffers. 11 | /// 12 | /// E.g. Given two inputs with three buffers each, all 6 input buffers will be summed onto the 13 | /// first output buffer. If there is more than one output buffer, the result is copied to the 14 | /// remaining output buffers. 15 | /// 16 | /// After a call to `Node::process`, each of the output buffers will always have the same contents. 17 | /// 18 | /// Common use cases: 19 | /// 20 | /// - Summing multiple input channels down to a single output channel. 21 | /// - Writing a single input channel to multiple output channels. 22 | #[derive(Clone, Debug, PartialEq)] 23 | pub struct SumBuffers; 24 | 25 | impl Node for Sum { 26 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 27 | // Fill the output with silence. 28 | for out_buffer in output.iter_mut() { 29 | out_buffer.silence(); 30 | } 31 | // Sum the inputs onto the output. 32 | for (channel, out_buffer) in output.iter_mut().enumerate() { 33 | for input in inputs { 34 | let in_buffers = input.buffers(); 35 | if let Some(in_buffer) = in_buffers.get(channel) { 36 | dasp_slice::add_in_place(out_buffer, in_buffer); 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | impl Node for SumBuffers { 44 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 45 | // Get the first output buffer. 46 | let mut out_buffers = output.iter_mut(); 47 | let out_buffer_first = match out_buffers.next() { 48 | None => return, 49 | Some(buffer) => buffer, 50 | }; 51 | // Fill it with silence. 52 | out_buffer_first.silence(); 53 | // Sum all input buffers onto the first output buffer. 54 | for input in inputs { 55 | for in_buffer in input.buffers() { 56 | dasp_slice::add_in_place(out_buffer_first, in_buffer); 57 | } 58 | } 59 | // Write the first output buffer to the rest. 60 | for out_buffer in out_buffers { 61 | out_buffer.copy_from_slice(out_buffer_first); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /dasp_graph/tests/graph_send.rs: -------------------------------------------------------------------------------- 1 | //! Small test to make sure `Processor` and graph types stay `Send`. 2 | //! 3 | //! We only need to know they compile. 4 | 5 | #![cfg(feature = "node-boxed")] 6 | #![allow(unreachable_code, unused_variables)] 7 | 8 | use dasp_graph::{BoxedNodeSend, NodeData}; 9 | use petgraph::visit::GraphBase; 10 | 11 | #[test] 12 | #[should_panic] 13 | fn test_graph_send() { 14 | type Graph = petgraph::Graph, (), petgraph::Directed, u32>; 15 | type Processor = dasp_graph::Processor; 16 | let mut g: Graph = unimplemented!(); 17 | let mut p: Processor = unimplemented!(); 18 | let n: ::NodeId = unimplemented!(); 19 | 20 | std::thread::spawn(move || { 21 | p.process(&mut g, n); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /dasp_graph/tests/graph_types.rs: -------------------------------------------------------------------------------- 1 | //! Check that we properly support the major petgraph types. 2 | //! 3 | //! We only need to know they compile. 4 | 5 | #![cfg(feature = "node-boxed")] 6 | #![allow(unreachable_code, unused_variables)] 7 | 8 | use dasp_graph::{BoxedNode, NodeData}; 9 | use petgraph::visit::GraphBase; 10 | 11 | #[test] 12 | #[should_panic] 13 | fn test_graph() { 14 | type Graph = petgraph::Graph, (), petgraph::Directed, u32>; 15 | type Processor = dasp_graph::Processor; 16 | let mut g: Graph = unimplemented!(); 17 | let mut p: Processor = unimplemented!(); 18 | let n: ::NodeId = unimplemented!(); 19 | p.process(&mut g, n); 20 | } 21 | 22 | #[test] 23 | #[should_panic] 24 | fn test_stable_graph() { 25 | type Graph = 26 | petgraph::stable_graph::StableGraph, (), petgraph::Directed, u32>; 27 | type Processor = dasp_graph::Processor; 28 | let mut g: Graph = unimplemented!(); 29 | let mut p: Processor = unimplemented!(); 30 | let n: ::NodeId = unimplemented!(); 31 | p.process(&mut g, n); 32 | } 33 | -------------------------------------------------------------------------------- /dasp_graph/tests/sum.rs: -------------------------------------------------------------------------------- 1 | #![cfg(all(feature = "node-boxed", feature = "node-sum"))] 2 | 3 | use dasp_graph::{node, Buffer, Input, Node, NodeData}; 4 | 5 | type BoxedNode = dasp_graph::BoxedNode; 6 | 7 | // A simple source node that just writes `0.1` to the output. We'll use this to test the sum node. 8 | fn src_node(_inputs: &[Input], output: &mut [Buffer]) { 9 | for o in output { 10 | o.iter_mut().for_each(|s| *s = 0.1); 11 | } 12 | } 13 | 14 | #[test] 15 | fn test_sum() { 16 | // The type of graph to use for this test. 17 | type Graph = petgraph::Graph, (), petgraph::Directed, u32>; 18 | type Processor = dasp_graph::Processor; 19 | 20 | // Create a graph and a processor. 21 | let max_nodes = 6; 22 | let max_edges = 5; 23 | let mut g = Graph::with_capacity(max_nodes, max_edges); 24 | let mut p = Processor::with_capacity(max_nodes); 25 | 26 | // Create and add the nodes to the graph. 27 | let src_node_ptr = src_node as fn(&[Input], &mut [Buffer]); 28 | let src_a = g.add_node(NodeData::new1(BoxedNode::new(src_node_ptr))); 29 | let src_b = g.add_node(NodeData::new1(BoxedNode::new(src_node_ptr))); 30 | let sum = g.add_node(NodeData::new1(BoxedNode::new(node::Sum))); 31 | 32 | // Plug the source nodes into the sum node. 33 | g.add_edge(src_a, sum, ()); 34 | g.add_edge(src_b, sum, ()); 35 | 36 | // Process the graph from the sum node. 37 | p.process(&mut g, sum); 38 | 39 | // Check that `sum` actually contains the sum. 40 | let expected = Buffer::from([0.2; Buffer::LEN]); 41 | assert_eq!(&g[sum].buffers[..], &[expected][..]); 42 | 43 | // Plug in some more sources. 44 | let src_c = g.add_node(NodeData::new1(BoxedNode::new(src_node_ptr))); 45 | let src_d = g.add_node(NodeData::new1(BoxedNode::new(src_node_ptr))); 46 | let src_e = g.add_node(NodeData::new1(BoxedNode::new(src_node_ptr))); 47 | g.add_edge(src_c, sum, ()); 48 | g.add_edge(src_d, sum, ()); 49 | g.add_edge(src_e, sum, ()); 50 | 51 | // Check that the result is consistent. 52 | p.process(&mut g, sum); 53 | let expected = Buffer::from([0.5; Buffer::LEN]); 54 | assert_eq!(&g[sum].buffers[..], &[expected][..]); 55 | } 56 | 57 | #[test] 58 | fn test_sum2() { 59 | // The type of graph to use for this test. 60 | type Graph = petgraph::Graph, (), petgraph::Directed, u32>; 61 | type Processor = dasp_graph::Processor; 62 | 63 | // Create a graph and a processor. 64 | let mut g = Graph::new(); 65 | let mut p = Processor::with_capacity(g.node_count()); 66 | 67 | // Create a small tree where we first sum a and b, then sum the result with c. 68 | // This time, using two buffers (channels) per node. 69 | let src_node_ptr = src_node as fn(&[Input], &mut [Buffer]); 70 | let src_a = g.add_node(NodeData::new2(BoxedNode::new(src_node_ptr))); 71 | let src_b = g.add_node(NodeData::new2(BoxedNode::new(src_node_ptr))); 72 | let src_c = g.add_node(NodeData::new2(BoxedNode::new(src_node_ptr))); 73 | let sum_a_b = g.add_node(NodeData::new2(BoxedNode::new(node::Sum))); 74 | let sum_ab_c = g.add_node(NodeData::new2(BoxedNode::new(node::Sum))); 75 | g.add_edge(src_a, sum_a_b, ()); 76 | g.add_edge(src_b, sum_a_b, ()); 77 | g.add_edge(sum_a_b, sum_ab_c, ()); 78 | g.add_edge(src_c, sum_ab_c, ()); 79 | 80 | // Process the graph. 81 | p.process(&mut g, sum_ab_c); 82 | 83 | // sum_a_b should be 0.2. 84 | let expected = vec![Buffer::from([0.2; Buffer::LEN]); 2]; 85 | assert_eq!(&g[sum_a_b].buffers[..], &expected[..]); 86 | // sum_ab_c should be 0.3. 87 | let expected = vec![Buffer::from([0.3; Buffer::LEN]); 2]; 88 | assert_eq!(&g[sum_ab_c].buffers[..], &expected[..]); 89 | } 90 | 91 | #[test] 92 | fn test_sum_unboxed() { 93 | // Prove to ourselves we also support unboxed node types with a custom node type. 94 | enum TestNode { 95 | SourceFnPtr(fn(&[Input], &mut [Buffer])), 96 | Sum(node::Sum), 97 | } 98 | 99 | impl Node for TestNode { 100 | fn process(&mut self, inputs: &[Input], output: &mut [Buffer]) { 101 | match *self { 102 | TestNode::SourceFnPtr(ref mut f) => (*f)(inputs, output), 103 | TestNode::Sum(ref mut sum) => sum.process(inputs, output), 104 | } 105 | } 106 | } 107 | 108 | // The type of graph to use for this test. 109 | type Graph = petgraph::Graph, (), petgraph::Directed, u32>; 110 | type Processor = dasp_graph::Processor; 111 | 112 | // Create a graph and a processor. 113 | let mut g = Graph::new(); 114 | let mut p = Processor::with_capacity(g.node_count()); 115 | 116 | // Add two source nodes and a sum node. 117 | let src_node_ptr = src_node as _; 118 | let src_a = g.add_node(NodeData::new1(TestNode::SourceFnPtr(src_node_ptr))); 119 | let src_b = g.add_node(NodeData::new1(TestNode::SourceFnPtr(src_node_ptr))); 120 | let sum = g.add_node(NodeData::new1(TestNode::Sum(node::Sum))); 121 | 122 | // Plug the source nodes into the sum node. 123 | g.add_edge(src_a, sum, ()); 124 | g.add_edge(src_b, sum, ()); 125 | 126 | // Process the graph from the sum node. 127 | p.process(&mut g, sum); 128 | 129 | // Check that `sum` actually contains the sum. 130 | let expected = Buffer::from([0.2; Buffer::LEN]); 131 | assert_eq!(&g[sum].buffers[..], &[expected][..]); 132 | } 133 | -------------------------------------------------------------------------------- /dasp_interpolate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_interpolate" 3 | description = "An abstraction for audio PCM DSP rate interpolation, including floor, linear and sinc." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["dsp", "interpolate", "sample", "rate", "pcm"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } 15 | dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false } 16 | dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } 17 | 18 | [features] 19 | default = ["std"] 20 | all = ["std", "all-no-std"] 21 | all-no-std = [ 22 | "floor", 23 | "linear", 24 | "sinc", 25 | ] 26 | std = [ 27 | "dasp_frame/std", 28 | "dasp_ring_buffer/std", 29 | "dasp_sample/std", 30 | ] 31 | floor = [] 32 | linear = [] 33 | sinc = [] 34 | 35 | [package.metadata.docs.rs] 36 | all-features = true 37 | -------------------------------------------------------------------------------- /dasp_interpolate/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_interpolate/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_interpolate/src/floor.rs: -------------------------------------------------------------------------------- 1 | //! A floor interpolator implementation. 2 | //! 3 | //! ### Required Features 4 | //! 5 | //! - When using `dasp_interpolate`, this module requires the **floor** feature to be enabled. 6 | //! - When using `dasp`, this module requires the **interpolate-floor** feature to be enabled. 7 | 8 | use crate::Interpolator; 9 | use dasp_frame::Frame; 10 | use dasp_sample::Duplex; 11 | 12 | /// Interpolator that rounds off any values to the previous value from the source. 13 | /// 14 | /// ### Required Features 15 | /// 16 | /// - When using `dasp_interpolate`, this item requires the **floor** feature to be enabled. 17 | /// - When using `dasp`, this item requires the **interpolate-floor** feature to be enabled. 18 | pub struct Floor { 19 | left: F, 20 | } 21 | 22 | impl Floor { 23 | /// Create a new Floor Interpolator. 24 | /// 25 | /// ### Required Features 26 | /// 27 | /// - When using `dasp_interpolate`, this item requires the **floor** feature to be enabled. 28 | /// - When using `dasp`, this item requires the **interpolate-floor** feature to be enabled. 29 | pub fn new(left: F) -> Floor { 30 | Floor { left: left } 31 | } 32 | } 33 | 34 | impl Interpolator for Floor 35 | where 36 | F: Frame, 37 | F::Sample: Duplex, 38 | { 39 | type Frame = F; 40 | 41 | fn interpolate(&self, _x: f64) -> Self::Frame { 42 | self.left 43 | } 44 | 45 | fn next_source_frame(&mut self, source_frame: Self::Frame) { 46 | self.left = source_frame; 47 | } 48 | 49 | fn reset(&mut self) { 50 | self.left = Self::Frame::EQUILIBRIUM; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /dasp_interpolate/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An abstraction for sample/frame rate interpolation. 2 | //! 3 | //! The [**Interpolator**](./trait.Interpolator.html) trait provides an abstraction over different 4 | //! types of rate interpolation. 5 | //! 6 | //! See the `dasp_signal` crate (or `dasp::signal` module) **Converter** type for a convenient way 7 | //! to interpolate the rate of arbitrary signals. 8 | //! 9 | //! ### Optional Features 10 | //! 11 | //! - The **floor** feature (or **interpolate-floor** feature if using `dasp`) provides a floor 12 | //! interpolator implementation. 13 | //! - The **linear** feature (or **interpolate-linear** feature if using `dasp`) provides a linear 14 | //! interpolator implementation. 15 | //! - The **sinc** feature (or **interpolate-sinc** feature if using `dasp`) provides a sinc 16 | //! interpolator implementation. 17 | //! 18 | //! ### no_std 19 | //! 20 | //! If working in a `no_std` context, you can disable the default **std** feature with 21 | //! `--no-default-features`. 22 | //! 23 | //! To enable all of the above features in a `no_std` context, enable the **all-no-std** feature. 24 | 25 | #![cfg_attr(not(feature = "std"), no_std)] 26 | #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] 27 | 28 | use dasp_frame::Frame; 29 | 30 | #[cfg(feature = "floor")] 31 | pub mod floor; 32 | #[cfg(feature = "linear")] 33 | pub mod linear; 34 | #[cfg(feature = "sinc")] 35 | pub mod sinc; 36 | 37 | /// Types that can interpolate between two values. 38 | /// 39 | /// Implementations should keep track of the necessary data both before and after the current 40 | /// frame. 41 | pub trait Interpolator { 42 | /// The type of frame over which the interpolate may operate. 43 | type Frame: Frame; 44 | 45 | /// Given a distance between [0.0 and 1.0) toward the following sample, return the interpolated 46 | /// value. 47 | fn interpolate(&self, x: f64) -> Self::Frame; 48 | 49 | /// To be called whenever the Interpolator value steps passed 1.0. 50 | fn next_source_frame(&mut self, source_frame: Self::Frame); 51 | 52 | /// Resets the state of the interpolator. 53 | /// 54 | /// Call this when there's a break in the continuity of the input data stream. 55 | fn reset(&mut self); 56 | } 57 | -------------------------------------------------------------------------------- /dasp_interpolate/src/linear.rs: -------------------------------------------------------------------------------- 1 | //! A linear interpolator implementation. 2 | //! 3 | //! ### Required Features 4 | //! 5 | //! - When using `dasp_interpolate`, this module requires the **linear** feature to be enabled. 6 | //! - When using `dasp`, this module requires the **interpolate-linear** feature to be enabled. 7 | 8 | use crate::Interpolator; 9 | use dasp_frame::Frame; 10 | use dasp_sample::{Duplex, Sample}; 11 | 12 | /// Interpolator that interpolates linearly between the previous value and the next value 13 | /// 14 | /// ### Required Features 15 | /// 16 | /// - When using `dasp_interpolate`, this item requires the **linear** feature to be enabled. 17 | /// - When using `dasp`, this item requires the **interpolate-linear** feature to be enabled. 18 | pub struct Linear { 19 | left: F, 20 | right: F, 21 | } 22 | 23 | impl Linear { 24 | /// Create a new Linear Interpolator, where `left` and `right` are the first two frames to be 25 | /// interpolated. 26 | /// 27 | /// ### Required Features 28 | /// 29 | /// - When using `dasp_interpolate`, this item requires the **linear** feature to be enabled. 30 | /// - When using `dasp`, this item requires the **interpolate-linear** feature to be enabled. 31 | pub fn new(left: F, right: F) -> Linear { 32 | Linear { 33 | left: left, 34 | right: right, 35 | } 36 | } 37 | } 38 | 39 | impl Interpolator for Linear 40 | where 41 | F: Frame, 42 | F::Sample: Duplex, 43 | { 44 | type Frame = F; 45 | 46 | /// Converts linearly from the previous value, using the next value to interpolate. It is 47 | /// possible, although not advisable, to provide an x > 1.0 or < 0.0, but this will just 48 | /// continue to be a linear ramp in one direction or another. 49 | fn interpolate(&self, x: f64) -> Self::Frame { 50 | self.left.zip_map(self.right, |l, r| { 51 | let l_f = l.to_sample::(); 52 | let r_f = r.to_sample::(); 53 | let diff = r_f - l_f; 54 | ((diff * x) + l_f).to_sample::<::Sample>() 55 | }) 56 | } 57 | 58 | fn next_source_frame(&mut self, source_frame: Self::Frame) { 59 | self.left = self.right; 60 | self.right = source_frame; 61 | } 62 | 63 | fn reset(&mut self) { 64 | self.left = Self::Frame::EQUILIBRIUM; 65 | self.right = Self::Frame::EQUILIBRIUM; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /dasp_interpolate/src/sinc/mod.rs: -------------------------------------------------------------------------------- 1 | //! A sinc interpolator implementation. 2 | //! 3 | //! ### Required Features 4 | //! 5 | //! - When using `dasp_interpolate`, this module requires the **sinc** feature to be enabled. 6 | //! - When using `dasp`, this module requires the **interpolate-sinc** feature to be enabled. 7 | 8 | use crate::Interpolator; 9 | use core::f64::consts::PI; 10 | use dasp_frame::Frame; 11 | use dasp_ring_buffer as ring_buffer; 12 | use dasp_sample::{Duplex, Sample}; 13 | use ops::f64::{cos, sin}; 14 | 15 | mod ops; 16 | 17 | /// Interpolator for sinc interpolation. 18 | /// 19 | /// Generally accepted as one of the better sample rate converters, although it uses significantly 20 | /// more computation. 21 | /// 22 | /// ### Required Features 23 | /// 24 | /// - When using `dasp_interpolate`, this item requires the **sinc** feature to be enabled. 25 | /// - When using `dasp`, this item requires the **interpolate-sinc** feature to be enabled. 26 | pub struct Sinc { 27 | frames: ring_buffer::Fixed, 28 | idx: usize, 29 | } 30 | 31 | impl Sinc { 32 | /// Create a new **Sinc** interpolator with the given ring buffer. 33 | /// 34 | /// The given ring buffer should have a length twice that of the desired sinc interpolation 35 | /// `depth`. 36 | /// 37 | /// The initial contents of the ring_buffer will act as padding for the interpolated signal. 38 | /// 39 | /// **panic!**s if the given ring buffer's length is not a multiple of `2`. 40 | /// 41 | /// ### Required Features 42 | /// 43 | /// - When using `dasp_interpolate`, this item requires the **sinc** feature to be enabled. 44 | /// - When using `dasp`, this item requires the **interpolate-sinc** feature to be enabled. 45 | pub fn new(frames: ring_buffer::Fixed) -> Self 46 | where 47 | S: ring_buffer::SliceMut, 48 | S::Element: Frame, 49 | { 50 | assert!(frames.len() % 2 == 0); 51 | Sinc { 52 | frames: frames, 53 | idx: 0, 54 | } 55 | } 56 | 57 | fn depth(&self) -> usize 58 | where 59 | S: ring_buffer::Slice, 60 | { 61 | self.frames.len() / 2 62 | } 63 | } 64 | 65 | impl Interpolator for Sinc 66 | where 67 | S: ring_buffer::SliceMut, 68 | S::Element: Frame, 69 | ::Sample: Duplex, 70 | { 71 | type Frame = S::Element; 72 | 73 | /// Sinc interpolation 74 | fn interpolate(&self, x: f64) -> Self::Frame { 75 | let phil = x; 76 | let phir = 1.0 - x; 77 | let nl = self.idx; 78 | let nr = self.idx + 1; 79 | let depth = self.depth(); 80 | 81 | let rightmost = nl + depth; 82 | let leftmost = nr as isize - depth as isize; 83 | let max_depth = if rightmost >= self.frames.len() { 84 | self.frames.len() - depth 85 | } else if leftmost < 0 { 86 | (depth as isize + leftmost) as usize 87 | } else { 88 | depth 89 | }; 90 | 91 | (0..max_depth).fold(Self::Frame::EQUILIBRIUM, |mut v, n| { 92 | v = { 93 | let a = PI * (phil + n as f64); 94 | let first = if a == 0.0 { 1.0 } else { sin(a) / a }; 95 | let second = 0.5 + 0.5 * cos(a / depth as f64); 96 | v.zip_map(self.frames[nl - n], |vs, r_lag| { 97 | vs.add_amp( 98 | (first * second * r_lag.to_sample::()) 99 | .to_sample::<::Sample>() 100 | .to_signed_sample(), 101 | ) 102 | }) 103 | }; 104 | 105 | let a = PI * (phir + n as f64); 106 | let first = if a == 0.0 { 1.0 } else { sin(a) / a }; 107 | let second = 0.5 + 0.5 * cos(a / depth as f64); 108 | v.zip_map(self.frames[nr + n], |vs, r_lag| { 109 | vs.add_amp( 110 | (first * second * r_lag.to_sample::()) 111 | .to_sample::<::Sample>() 112 | .to_signed_sample(), 113 | ) 114 | }) 115 | }) 116 | } 117 | 118 | fn next_source_frame(&mut self, source_frame: Self::Frame) { 119 | let _old_frame = self.frames.push(source_frame); 120 | if self.idx < self.depth() { 121 | self.idx += 1; 122 | } 123 | } 124 | 125 | fn reset(&mut self) { 126 | self.idx = 0; 127 | self.frames.set_first(0); 128 | for frame in self.frames.iter_mut() { 129 | *frame = Self::Frame::EQUILIBRIUM; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /dasp_interpolate/src/sinc/ops.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | pub mod f64 { 4 | #[cfg(not(feature = "std"))] 5 | pub fn sin(x: f64) -> f64 { 6 | unsafe { core::intrinsics::sinf64(x) } 7 | } 8 | #[cfg(feature = "std")] 9 | pub fn sin(x: f64) -> f64 { 10 | x.sin() 11 | } 12 | 13 | #[cfg(not(feature = "std"))] 14 | pub fn cos(x: f64) -> f64 { 15 | unsafe { core::intrinsics::cosf64(x) } 16 | } 17 | #[cfg(feature = "std")] 18 | pub fn cos(x: f64) -> f64 { 19 | x.cos() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dasp_peak/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_peak" 3 | description = "A DSP peak detection library generic over the rectifier." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["peak", "rectifier", "full", "half", "wave"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } 15 | dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } 16 | 17 | [features] 18 | default = ["std"] 19 | std = ["dasp_frame/std", "dasp_sample/std"] 20 | 21 | [package.metadata.docs.rs] 22 | all-features = true 23 | -------------------------------------------------------------------------------- /dasp_peak/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_peak/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_peak/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Peak envelope detection over a signal. 2 | 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | 5 | use dasp_frame::Frame; 6 | use dasp_sample::Sample; 7 | 8 | /// Types that may be used to rectify a signal of frames `F` for a `Peak` detector. 9 | pub trait Rectifier 10 | where 11 | F: Frame, 12 | { 13 | /// Frames that can be detected. 14 | type Output: Frame; 15 | /// Rectify the given frame. 16 | fn rectify(&mut self, frame: F) -> Self::Output; 17 | } 18 | 19 | /// A signal rectifier that produces the absolute amplitude from samples. 20 | #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] 21 | pub struct FullWave; 22 | /// A signal rectifier that produces only the positive samples. 23 | #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] 24 | pub struct PositiveHalfWave; 25 | /// A signal rectifier that produces only the negative samples. 26 | #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] 27 | pub struct NegativeHalfWave; 28 | 29 | impl Rectifier for FullWave 30 | where 31 | F: Frame, 32 | { 33 | type Output = F::Signed; 34 | fn rectify(&mut self, frame: F) -> Self::Output { 35 | full_wave(frame) 36 | } 37 | } 38 | 39 | impl Rectifier for PositiveHalfWave 40 | where 41 | F: Frame, 42 | { 43 | type Output = F; 44 | fn rectify(&mut self, frame: F) -> Self::Output { 45 | positive_half_wave(frame) 46 | } 47 | } 48 | 49 | impl Rectifier for NegativeHalfWave 50 | where 51 | F: Frame, 52 | { 53 | type Output = F; 54 | fn rectify(&mut self, frame: F) -> Self::Output { 55 | negative_half_wave(frame) 56 | } 57 | } 58 | 59 | /// A signal rectifier that produces the absolute amplitude from samples. 60 | pub fn full_wave(frame: F) -> F::Signed 61 | where 62 | F: Frame, 63 | { 64 | frame.map(|s| { 65 | let signed = s.to_signed_sample(); 66 | if signed < Sample::EQUILIBRIUM { 67 | -signed 68 | } else { 69 | signed 70 | } 71 | }) 72 | } 73 | 74 | /// A signal rectifier that produces only the positive samples. 75 | pub fn positive_half_wave(frame: F) -> F 76 | where 77 | F: Frame, 78 | { 79 | frame.map(|s| { 80 | if s < Sample::EQUILIBRIUM { 81 | Sample::EQUILIBRIUM 82 | } else { 83 | s 84 | } 85 | }) 86 | } 87 | 88 | /// A signal rectifier that produces only the negative samples. 89 | pub fn negative_half_wave(frame: F) -> F 90 | where 91 | F: Frame, 92 | { 93 | frame.map(|s| { 94 | if s > Sample::EQUILIBRIUM { 95 | Sample::EQUILIBRIUM 96 | } else { 97 | s 98 | } 99 | }) 100 | } 101 | -------------------------------------------------------------------------------- /dasp_ring_buffer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_ring_buffer" 3 | description = "Simple fixed and bounded ring buffers for audio PCM DSP." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["ring", "buffer", "dsp", "pcm", "audio"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [features] 14 | default = ["std"] 15 | std = [] 16 | 17 | [package.metadata.docs.rs] 18 | all-features = true 19 | -------------------------------------------------------------------------------- /dasp_ring_buffer/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_ring_buffer/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_ring_buffer/tests/ring_buffer.rs: -------------------------------------------------------------------------------- 1 | use dasp_ring_buffer as ring_buffer; 2 | 3 | #[test] 4 | fn test_bounded_boxed_slice() { 5 | let mut rb = ring_buffer::Bounded::from(vec![0; 3].into_boxed_slice()); 6 | assert_eq!(rb.push(1), None); 7 | assert_eq!(rb.push(2), None); 8 | assert_eq!(rb.push(3), None); 9 | assert_eq!(rb.push(4), Some(1)); 10 | } 11 | 12 | #[test] 13 | fn test_bounded_array() { 14 | let mut rb = ring_buffer::Bounded::from([0i32; 3]); 15 | assert_eq!(rb.push(1), None); 16 | assert_eq!(rb.push(2), None); 17 | assert_eq!(rb.push(3), None); 18 | assert_eq!(rb.push(4), Some(1)); 19 | } 20 | 21 | #[test] 22 | #[should_panic] 23 | fn text_bounded_from_empty_vec() { 24 | ring_buffer::Bounded::from(Vec::::new()); 25 | } 26 | 27 | #[test] 28 | fn test_bounded_from_vec() { 29 | let mut rb = ring_buffer::Bounded::from(vec![1, 2, 3]); 30 | assert_eq!(rb.push(4), None); 31 | assert_eq!(rb.push(5), None); 32 | assert_eq!(rb.push(6), None); 33 | assert_eq!(rb.push(7), Some(4)); 34 | } 35 | 36 | #[test] 37 | #[should_panic] 38 | fn test_bounded_get_out_of_range() { 39 | let rb = ring_buffer::Bounded::from([0i32; 3]); 40 | let _ = rb[0]; 41 | } 42 | -------------------------------------------------------------------------------- /dasp_rms/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_rms" 3 | description = "RMS detection with configurable window for audio PCM DSP." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["dsp", "rms", "envelope", "pcm", "audio"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } 15 | dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false } 16 | dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } 17 | 18 | [features] 19 | default = ["std"] 20 | std = ["dasp_frame/std", "dasp_ring_buffer/std", "dasp_sample/std"] 21 | 22 | [package.metadata.docs.rs] 23 | all-features = true 24 | -------------------------------------------------------------------------------- /dasp_rms/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_rms/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_rms/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Root mean square calculation over a signal. 2 | //! 3 | //! The primary type of interest in this module is the [**Rms**](./struct.Rms). 4 | 5 | #![cfg_attr(not(feature = "std"), no_std)] 6 | 7 | use core::fmt; 8 | use core::marker::PhantomData; 9 | use dasp_frame::Frame; 10 | use dasp_ring_buffer as ring_buffer; 11 | use dasp_sample::{FloatSample, Sample}; 12 | 13 | /// Iteratively extracts the RMS (root mean square) envelope from a window over a signal of sample 14 | /// `Frame`s. 15 | #[derive(Clone)] 16 | pub struct Rms 17 | where 18 | F: Frame, 19 | S: ring_buffer::Slice, 20 | { 21 | /// The type of `Frame`s for which the RMS will be calculated. 22 | frame: PhantomData, 23 | /// The ringbuffer of frame sample squares (i.e. `sample * sample`) used to calculate the RMS 24 | /// per frame. 25 | /// 26 | /// When a new frame is received, the **Rms** pops the front sample_square and adds the new 27 | /// sample_square to the back. 28 | window: ring_buffer::Fixed, 29 | /// The sum total of all sample_squares currently within the **Rms**'s `window` ring buffer. 30 | square_sum: F::Float, 31 | } 32 | 33 | impl Rms 34 | where 35 | F: Frame, 36 | S: ring_buffer::Slice, 37 | { 38 | /// Construct a new **Rms** that uses the given ring buffer as its window. 39 | /// 40 | /// The window size of the **Rms** is equal to the length of the given ring buffer. 41 | /// 42 | /// ``` 43 | /// use dasp_ring_buffer as ring_buffer; 44 | /// use dasp_rms::Rms; 45 | /// 46 | /// fn main() { 47 | /// let window = ring_buffer::Fixed::from([[0.0]; 4]); 48 | /// let mut rms = Rms::new(window); 49 | /// rms.next([0.5]); 50 | /// } 51 | /// ``` 52 | pub fn new(ring_buffer: ring_buffer::Fixed) -> Self { 53 | Rms { 54 | frame: PhantomData, 55 | window: ring_buffer, 56 | square_sum: Frame::EQUILIBRIUM, 57 | } 58 | } 59 | 60 | /// Zeroes the square_sum and the buffer of the `window`. 61 | /// 62 | /// ``` 63 | /// use dasp_ring_buffer as ring_buffer; 64 | /// use dasp_rms::Rms; 65 | /// 66 | /// fn main() { 67 | /// let window = ring_buffer::Fixed::from([[0.0]; 4]); 68 | /// let mut rms = Rms::new(window); 69 | /// rms.next([0.6]); 70 | /// rms.next([0.9]); 71 | /// rms.reset(); 72 | /// assert_eq!(rms.current(), [0.0]); 73 | /// } 74 | /// ``` 75 | pub fn reset(&mut self) 76 | where 77 | S: ring_buffer::SliceMut, 78 | { 79 | for sample_square in self.window.iter_mut() { 80 | *sample_square = Frame::EQUILIBRIUM; 81 | } 82 | self.square_sum = Frame::EQUILIBRIUM; 83 | } 84 | 85 | /// The length of the window as a number of frames. 86 | /// 87 | /// ``` 88 | /// use dasp_ring_buffer as ring_buffer; 89 | /// use dasp_rms::Rms; 90 | /// 91 | /// fn main() { 92 | /// let window = ring_buffer::Fixed::from([[0.0]; 4]); 93 | /// let mut rms = Rms::new(window); 94 | /// assert_eq!(rms.window_frames(), 4); 95 | /// rms.next([0.5]); 96 | /// assert_eq!(rms.window_frames(), 4); 97 | /// } 98 | /// ``` 99 | #[inline] 100 | pub fn window_frames(&self) -> usize { 101 | self.window.len() 102 | } 103 | 104 | /// The next RMS given the new frame in the sequence. 105 | /// 106 | /// The **Rms** pops its front frame and adds the new frame to the back. 107 | /// 108 | /// The yielded RMS is the RMS of all frame squares in the `window` after the new frame is 109 | /// added. 110 | /// 111 | /// This method uses `Rms::next_squared` internally and then calculates the square root. 112 | /// 113 | /// ``` 114 | /// use dasp_ring_buffer as ring_buffer; 115 | /// use dasp_rms::Rms; 116 | /// 117 | /// fn main() { 118 | /// let window = ring_buffer::Fixed::from([[0.0]; 4]); 119 | /// let mut rms = Rms::new(window); 120 | /// assert_eq!(rms.next([1.0]), [0.5]); 121 | /// assert_eq!(rms.next([-1.0]), [0.7071067811865476]); 122 | /// assert_eq!(rms.next([1.0]), [0.8660254037844386]); 123 | /// assert_eq!(rms.next([-1.0]), [1.0]); 124 | /// } 125 | /// ``` 126 | #[inline] 127 | pub fn next(&mut self, new_frame: F) -> F::Float 128 | where 129 | S: ring_buffer::SliceMut, 130 | { 131 | self.next_squared(new_frame).map(|s| s.sample_sqrt()) 132 | } 133 | 134 | /// The same as **Rms::next**, but does not calculate the final square root required to 135 | /// determine the RMS. 136 | #[inline] 137 | pub fn next_squared(&mut self, new_frame: F) -> F::Float 138 | where 139 | S: ring_buffer::SliceMut, 140 | { 141 | // Determine the square of the new frame. 142 | let new_frame_square = new_frame.to_float_frame().map(|s| s * s); 143 | // Push back the new frame_square. 144 | let removed_frame_square = self.window.push(new_frame_square); 145 | // Add the new frame square and subtract the removed frame square. 146 | self.square_sum = 147 | self.square_sum 148 | .add_amp(new_frame_square) 149 | .zip_map(removed_frame_square, |s, r| { 150 | let diff = s - r; 151 | // Don't let floating point rounding errors put us below 0.0. 152 | if diff < Sample::EQUILIBRIUM { 153 | Sample::EQUILIBRIUM 154 | } else { 155 | diff 156 | } 157 | }); 158 | self.calc_rms_squared() 159 | } 160 | 161 | /// Consumes the **Rms** and returns its inner ring buffer of squared frames along with a frame 162 | /// representing the sum of all frame squares contained within the ring buffer. 163 | pub fn into_parts(self) -> (ring_buffer::Fixed, S::Element) { 164 | let Rms { 165 | window, square_sum, .. 166 | } = self; 167 | (window, square_sum) 168 | } 169 | 170 | /// Calculates the RMS of all frames currently stored within the inner window. 171 | /// 172 | /// ``` 173 | /// use dasp_ring_buffer as ring_buffer; 174 | /// use dasp_rms::Rms; 175 | /// 176 | /// fn main() { 177 | /// let window = ring_buffer::Fixed::from([[0.0]; 4]); 178 | /// let mut rms = Rms::new(window); 179 | /// assert_eq!(rms.current(), [0.0]); 180 | /// rms.next([1.0]); 181 | /// rms.next([1.0]); 182 | /// rms.next([1.0]); 183 | /// rms.next([1.0]); 184 | /// assert_eq!(rms.current(), [1.0]); 185 | /// } 186 | /// ``` 187 | pub fn current(&self) -> F::Float { 188 | self.calc_rms_squared().map(|s| s.sample_sqrt()) 189 | } 190 | 191 | fn calc_rms_squared(&self) -> F::Float { 192 | let num_frames_f = Sample::from_sample(self.window.len() as f32); 193 | self.square_sum.map(|s| s / num_frames_f) 194 | } 195 | } 196 | 197 | impl fmt::Debug for Rms 198 | where 199 | F: Frame, 200 | F::Float: fmt::Debug, 201 | S: fmt::Debug + ring_buffer::Slice, 202 | { 203 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 204 | f.debug_struct("Rms") 205 | .field("frame", &self.frame) 206 | .field("window", &self.window) 207 | .field("square_sum", &self.square_sum) 208 | .finish() 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /dasp_sample/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_sample" 3 | description = "An abstraction for audio PCM DSP samples, along with useful conversions and operations." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["dsp", "bit-depth", "sample", "pcm", "audio"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/sample.git" 10 | homepage = "https://github.com/rustaudio/sample" 11 | edition = "2018" 12 | 13 | [features] 14 | default = ["std"] 15 | std = [] 16 | 17 | [package.metadata.docs.rs] 18 | all-features = true 19 | -------------------------------------------------------------------------------- /dasp_sample/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_sample/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_sample/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Use the [**Sample**](./trait.Sample.html) trait to remain generic over sample types, easily 2 | //! access sample type conversions, apply basic audio operations and more. 3 | //! 4 | //! The **Sample** trait is the core abstraction throughout dasp on which most other abstractions 5 | //! are based. 6 | 7 | #![cfg_attr(not(feature = "std"), no_std)] 8 | 9 | #[cfg(not(feature = "std"))] 10 | extern crate alloc; 11 | 12 | pub use conv::{Duplex, FromSample, ToSample}; 13 | pub use types::{I24, I48, U24, U48}; 14 | 15 | pub mod conv; 16 | mod ops; 17 | pub mod types; 18 | 19 | /// A trait for working generically across different **Sample** format types. 20 | /// 21 | /// Provides methods for converting to and from any type that implements the 22 | /// [`FromSample`](./trait.FromSample.html) trait and provides methods for performing signal 23 | /// amplitude addition and multiplication. 24 | /// 25 | /// # Example 26 | /// 27 | /// ```rust 28 | /// use dasp_sample::{I24, Sample}; 29 | /// 30 | /// fn main() { 31 | /// assert_eq!((-1.0).to_sample::(), 0); 32 | /// assert_eq!(0.0.to_sample::(), 128); 33 | /// assert_eq!(0i32.to_sample::(), 2_147_483_648); 34 | /// assert_eq!(I24::new(0).unwrap(), Sample::from_sample(0.0)); 35 | /// assert_eq!(0.0, Sample::EQUILIBRIUM); 36 | /// } 37 | /// ``` 38 | pub trait Sample: Copy + Clone + PartialOrd + PartialEq { 39 | /// When summing two samples of a signal together, it is necessary for both samples to be 40 | /// represented in some signed format. This associated `Addition` type represents the format to 41 | /// which `Self` should be converted for optimal `Addition` performance. 42 | /// 43 | /// For example, u32's optimal `Addition` type would be i32, u8's would be i8, f32's would be 44 | /// f32, etc. 45 | /// 46 | /// Specifying this as an associated type allows us to automatically determine the optimal, 47 | /// lossless Addition format type for summing any two unique `Sample` types together. 48 | /// 49 | /// As a user of the `sample` crate, you will never need to be concerned with this type unless 50 | /// you are defining your own unique `Sample` type(s). 51 | type Signed: SignedSample + Duplex; 52 | 53 | /// When multiplying two samples of a signal together, it is necessary for both samples to be 54 | /// represented in some signed, floating-point format. This associated `Multiplication` type 55 | /// represents the format to which `Self` should be converted for optimal `Multiplication` 56 | /// performance. 57 | /// 58 | /// For example, u32's optimal `Multiplication` type would be f32, u64's would be f64, i8's 59 | /// would be f32, etc. 60 | /// 61 | /// Specifying this as an associated type allows us to automatically determine the optimal, 62 | /// lossless Multiplication format type for multiplying any two unique `Sample` types together. 63 | /// 64 | /// As a user of the `sample` crate, you will never need to be concerned with this type unless 65 | /// you are defining your own unique `Sample` type(s). 66 | type Float: FloatSample + Duplex; 67 | 68 | /// The equilibrium value for the wave that this `Sample` type represents. This is normally the 69 | /// value that is equal distance from both the min and max ranges of the sample. 70 | /// 71 | /// # Example 72 | /// 73 | /// ```rust 74 | /// use dasp_sample::Sample; 75 | /// 76 | /// fn main() { 77 | /// assert_eq!(0.0, f32::EQUILIBRIUM); 78 | /// assert_eq!(0, i32::EQUILIBRIUM); 79 | /// assert_eq!(128, u8::EQUILIBRIUM); 80 | /// assert_eq!(32_768_u16, Sample::EQUILIBRIUM); 81 | /// } 82 | /// ``` 83 | /// 84 | /// **Note:** This will likely be changed to an "associated const" if the feature lands. 85 | const EQUILIBRIUM: Self; 86 | 87 | /// The multiplicative identity of the signal. 88 | /// 89 | /// In other words: A value which when used to scale/multiply the amplitude or frequency of a 90 | /// signal, returns the same signal. 91 | /// 92 | /// This is useful as a default, non-affecting amplitude or frequency multiplier. 93 | /// 94 | /// # Example 95 | /// 96 | /// ```rust 97 | /// use dasp_sample::{Sample, U48}; 98 | /// 99 | /// fn main() { 100 | /// assert_eq!(1.0, f32::IDENTITY); 101 | /// assert_eq!(1.0, i8::IDENTITY); 102 | /// assert_eq!(1.0, u8::IDENTITY); 103 | /// assert_eq!(1.0, U48::IDENTITY); 104 | /// } 105 | /// ``` 106 | const IDENTITY: Self::Float = ::IDENTITY; 107 | 108 | /// Convert `self` to any type that implements `FromSample`. 109 | /// 110 | /// Find more details on type-specific conversion ranges and caveats in the `conv` module. 111 | /// 112 | /// # Example 113 | /// 114 | /// ```rust 115 | /// use dasp_sample::Sample; 116 | /// 117 | /// fn main() { 118 | /// assert_eq!(0.0.to_sample::(), 0); 119 | /// assert_eq!(0.0.to_sample::(), 128); 120 | /// assert_eq!((-1.0).to_sample::(), 0); 121 | /// } 122 | /// ``` 123 | #[inline] 124 | fn to_sample(self) -> S 125 | where 126 | Self: ToSample, 127 | { 128 | self.to_sample_() 129 | } 130 | 131 | /// Create a `Self` from any type that implements `ToSample`. 132 | /// 133 | /// Find more details on type-specific conversion ranges and caveats in the `conv` module. 134 | /// 135 | /// # Example 136 | /// 137 | /// ```rust 138 | /// use dasp_sample::{Sample, I24}; 139 | /// 140 | /// fn main() { 141 | /// assert_eq!(f32::from_sample(128_u8), 0.0); 142 | /// assert_eq!(i8::from_sample(-1.0), -128); 143 | /// assert_eq!(I24::from_sample(0.0), I24::new(0).unwrap()); 144 | /// } 145 | /// ``` 146 | 147 | #[inline] 148 | fn from_sample(s: S) -> Self 149 | where 150 | Self: FromSample, 151 | { 152 | FromSample::from_sample_(s) 153 | } 154 | 155 | /// Converts `self` to the equivalent `Sample` in the associated `Signed` format. 156 | /// 157 | /// This is a simple wrapper around `Sample::to_sample` which may provide extra convenience in 158 | /// some cases, particularly for assisting type inference. 159 | /// 160 | /// # Example 161 | /// 162 | /// ```rust 163 | /// use dasp_sample::Sample; 164 | /// 165 | /// fn main() { 166 | /// assert_eq!(128_u8.to_signed_sample(), 0i8); 167 | /// } 168 | /// ``` 169 | fn to_signed_sample(self) -> Self::Signed { 170 | self.to_sample() 171 | } 172 | 173 | /// Converts `self` to the equivalent `Sample` in the associated `Float` format. 174 | /// 175 | /// This is a simple wrapper around `Sample::to_sample` which may provide extra convenience in 176 | /// some cases, particularly for assisting type inference. 177 | /// 178 | /// # Example 179 | /// 180 | /// ```rust 181 | /// use dasp_sample::Sample; 182 | /// 183 | /// fn main() { 184 | /// assert_eq!(128_u8.to_float_sample(), 0.0); 185 | /// } 186 | /// ``` 187 | fn to_float_sample(self) -> Self::Float { 188 | self.to_sample() 189 | } 190 | 191 | /// Adds (or "offsets") the amplitude of the `Sample` by the given signed amplitude. 192 | /// 193 | /// `Self` will be converted to `Self::Signed`, the addition will occur and then the result 194 | /// will be converted back to `Self`. These conversions allow us to correctly handle the 195 | /// addition of unsigned signal formats. 196 | /// 197 | /// # Example 198 | /// 199 | /// ```rust 200 | /// use dasp_sample::Sample; 201 | /// 202 | /// fn main() { 203 | /// assert_eq!(0.25.add_amp(0.5), 0.75); 204 | /// assert_eq!(192u8.add_amp(-128), 64); 205 | /// } 206 | /// ``` 207 | #[inline] 208 | fn add_amp(self, amp: Self::Signed) -> Self { 209 | let self_s = self.to_signed_sample(); 210 | (self_s + amp).to_sample() 211 | } 212 | 213 | /// Multiplies (or "scales") the amplitude of the `Sample` by the given float amplitude. 214 | /// 215 | /// - `amp` > 1.0 amplifies the sample. 216 | /// - `amp` < 1.0 attenuates the sample. 217 | /// - `amp` == 1.0 yields the same sample. 218 | /// - `amp` == 0.0 yields the `Sample::EQUILIBRIUM`. 219 | /// 220 | /// `Self` will be converted to `Self::Float`, the multiplication will occur and then the 221 | /// result will be converted back to `Self`. These conversions allow us to correctly handle the 222 | /// multiplication of integral signal formats. 223 | /// 224 | /// # Example 225 | /// 226 | /// ```rust 227 | /// use dasp_sample::Sample; 228 | /// 229 | /// fn main() { 230 | /// assert_eq!(64_i8.mul_amp(0.5), 32); 231 | /// assert_eq!(0.5.mul_amp(-2.0), -1.0); 232 | /// assert_eq!(64_u8.mul_amp(0.0), 128); 233 | /// } 234 | /// ``` 235 | #[inline] 236 | fn mul_amp(self, amp: Self::Float) -> Self { 237 | let self_f = self.to_float_sample(); 238 | (self_f * amp).to_sample() 239 | } 240 | } 241 | 242 | /// A macro used to simplify the implementation of `Sample`. 243 | macro_rules! impl_sample { 244 | ($($T:ty: 245 | Signed: $Addition:ty, 246 | Float: $Modulation:ty, 247 | EQUILIBRIUM: $EQUILIBRIUM:expr),*) => 248 | { 249 | $( 250 | impl Sample for $T { 251 | type Signed = $Addition; 252 | type Float = $Modulation; 253 | const EQUILIBRIUM: Self = $EQUILIBRIUM; 254 | } 255 | )* 256 | } 257 | } 258 | 259 | // Expands to `Sample` implementations for all of the following types. 260 | impl_sample! { 261 | i8: Signed: i8, Float: f32, EQUILIBRIUM: 0, 262 | i16: Signed: i16, Float: f32, EQUILIBRIUM: 0, 263 | I24: Signed: I24, Float: f32, EQUILIBRIUM: types::i24::EQUILIBRIUM, 264 | i32: Signed: i32, Float: f32, EQUILIBRIUM: 0, 265 | I48: Signed: I48, Float: f64, EQUILIBRIUM: types::i48::EQUILIBRIUM, 266 | i64: Signed: i64, Float: f64, EQUILIBRIUM: 0, 267 | u8: Signed: i8, Float: f32, EQUILIBRIUM: 128, 268 | u16: Signed: i16, Float: f32, EQUILIBRIUM: 32_768, 269 | U24: Signed: i32, Float: f32, EQUILIBRIUM: types::u24::EQUILIBRIUM, 270 | u32: Signed: i32, Float: f32, EQUILIBRIUM: 2_147_483_648, 271 | U48: Signed: i64, Float: f64, EQUILIBRIUM: types::u48::EQUILIBRIUM, 272 | u64: Signed: i64, Float: f64, EQUILIBRIUM: 9_223_372_036_854_775_808, 273 | f32: Signed: f32, Float: f32, EQUILIBRIUM: 0.0, 274 | f64: Signed: f64, Float: f64, EQUILIBRIUM: 0.0 275 | } 276 | 277 | /// Integral and floating-point **Sample** format types whose equilibrium is at 0. 278 | /// 279 | /// **Sample**s often need to be converted to some mutual **SignedSample** type for signal 280 | /// addition. 281 | pub trait SignedSample: 282 | Sample 283 | + core::ops::Add 284 | + core::ops::Sub 285 | + core::ops::Neg 286 | { 287 | } 288 | macro_rules! impl_signed_sample { ($($T:ty)*) => { $( impl SignedSample for $T {} )* } } 289 | impl_signed_sample!(i8 i16 I24 i32 I48 i64 f32 f64); 290 | 291 | /// Sample format types represented as floating point numbers. 292 | /// 293 | /// **Sample**s often need to be converted to some mutual **FloatSample** type for signal scaling 294 | /// and modulation. 295 | pub trait FloatSample: 296 | Sample 297 | + SignedSample 298 | + core::ops::Mul 299 | + core::ops::Div 300 | + Duplex 301 | + Duplex 302 | { 303 | /// Represents the multiplicative identity of the floating point signal. 304 | const IDENTITY: Self; 305 | /// Calculate the square root of `Self`. 306 | fn sample_sqrt(self) -> Self; 307 | } 308 | 309 | impl FloatSample for f32 { 310 | const IDENTITY: Self = 1.0; 311 | #[inline] 312 | fn sample_sqrt(self) -> Self { 313 | ops::f32::sqrt(self) 314 | } 315 | } 316 | 317 | impl FloatSample for f64 { 318 | const IDENTITY: Self = 1.0; 319 | #[inline] 320 | fn sample_sqrt(self) -> Self { 321 | ops::f64::sqrt(self) 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /dasp_sample/src/ops.rs: -------------------------------------------------------------------------------- 1 | pub mod f32 { 2 | #[allow(unused_imports)] 3 | use core; 4 | 5 | #[cfg(not(feature = "std"))] 6 | pub fn sqrt(x: f32) -> f32 { 7 | if x >= 0.0 { 8 | f32::from_bits((x.to_bits() + 0x3f80_0000) >> 1) 9 | } else { 10 | f32::NAN 11 | } 12 | } 13 | #[cfg(feature = "std")] 14 | pub fn sqrt(x: f32) -> f32 { 15 | x.sqrt() 16 | } 17 | } 18 | 19 | pub mod f64 { 20 | #[allow(unused_imports)] 21 | use core; 22 | 23 | #[cfg(not(feature = "std"))] 24 | pub fn sqrt(x: f64) -> f64 { 25 | if x >= 0.0 { 26 | f64::from_bits((x.to_bits() + 0x3f80_0000) >> 1) 27 | } else { 28 | f64::NAN 29 | } 30 | } 31 | #[cfg(feature = "std")] 32 | pub fn sqrt(x: f64) -> f64 { 33 | x.sqrt() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /dasp_sample/src/types.rs: -------------------------------------------------------------------------------- 1 | //! A collection of custom, non-std **Sample** types. 2 | 3 | pub use self::i11::I11; 4 | pub use self::i20::I20; 5 | pub use self::i24::I24; 6 | pub use self::i48::I48; 7 | pub use self::u11::U11; 8 | pub use self::u20::U20; 9 | pub use self::u24::U24; 10 | pub use self::u48::U48; 11 | 12 | macro_rules! impl_from { 13 | ($T:ident: $Rep:ident from {$U:ident : $URep:ty}) => { 14 | impl From<$U> for $T { 15 | #[inline] 16 | fn from(other: $U) -> Self { 17 | $T(other.inner() as $Rep) 18 | } 19 | } 20 | }; 21 | ($T:ident: $Rep:ident from $U:ident) => { 22 | impl From<$U> for $T { 23 | #[inline] 24 | fn from(other: $U) -> Self { 25 | $T(other as $Rep) 26 | } 27 | } 28 | }; 29 | } 30 | 31 | macro_rules! impl_froms { 32 | ($T:ident: $Rep:ident, {$U:ident : $URep:ty}, $($rest:tt)*) => { 33 | impl_from!($T: $Rep from {$U: $URep}); 34 | impl_froms!($T: $Rep, $($rest)*); 35 | }; 36 | ($T:ident: $Rep:ident, {$U:ident : $URep:ty}) => { 37 | impl_from!($T: $Rep from {$U: $URep}); 38 | }; 39 | ($T:ident: $Rep:ident, $U:ident, $($rest:tt)*) => { 40 | impl_from!($T: $Rep from $U); 41 | impl_froms!($T: $Rep, $($rest)*); 42 | }; 43 | ($T:ident: $Rep:ident, $U:ident) => { 44 | impl_from!($T: $Rep from $U); 45 | }; 46 | ($T:ident: $Rep:ident,) => {}; 47 | } 48 | 49 | macro_rules! impl_neg { 50 | ($T:ident) => { 51 | impl ::core::ops::Neg for $T { 52 | type Output = $T; 53 | #[inline] 54 | fn neg(self) -> $T { 55 | $T(-self.0) 56 | } 57 | } 58 | }; 59 | } 60 | 61 | macro_rules! new_sample_type { 62 | ($T:ident: $Rep:ident, eq: $EQ:expr, min: $MIN:expr, max: $MAX:expr, total: $TOTAL:expr, from: $($rest:tt)*) => { 63 | pub const MIN: $T = $T($MIN); 64 | pub const MAX: $T = $T($MAX); 65 | pub const EQUILIBRIUM: $T = $T($EQ); 66 | const MIN_REP: $Rep = $MIN; 67 | const MAX_REP: $Rep = $MAX; 68 | const TOTAL: $Rep = $TOTAL; 69 | 70 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)] 71 | pub struct $T($Rep); 72 | 73 | impl From<$Rep> for $T { 74 | #[inline] 75 | fn from(val: $Rep) -> Self { 76 | $T(val).wrap_overflow() 77 | } 78 | } 79 | 80 | impl $T { 81 | /// Construct a new sample if the given value is within range. 82 | /// 83 | /// Returns `None` if `val` is out of range. 84 | #[inline] 85 | pub fn new(val: $Rep) -> Option { 86 | if val > MAX_REP || val < MIN_REP { 87 | None 88 | } else { 89 | Some($T(val)) 90 | } 91 | } 92 | 93 | /// Constructs a new sample without checking for overflowing. 94 | /// 95 | /// This should *only* be used if the user can guarantee the sample will be within 96 | /// range and they require the extra performance. 97 | /// 98 | /// If this function is used, the sample crate can't guarantee that the returned sample 99 | /// or any interacting samples will remain within their MIN and MAX bounds. 100 | pub fn new_unchecked(s: $Rep) -> Self { 101 | $T(s) 102 | } 103 | 104 | /// Return the internal value used to represent the sample type. 105 | #[inline] 106 | pub fn inner(self) -> $Rep { 107 | self.0 108 | } 109 | 110 | /// Wraps self once in the case that self has overflowed. 111 | #[inline] 112 | fn wrap_overflow_once(self) -> Self { 113 | if self.0 > MAX_REP { $T(self.0 - TOTAL) } 114 | else if self.0 < MIN_REP { $T(self.0 + TOTAL) } 115 | else { self } 116 | } 117 | 118 | /// Wraps self in the case that self has overflowed. 119 | #[inline] 120 | fn wrap_overflow(mut self) -> Self { 121 | while self.0 > MAX_REP { 122 | self.0 -= TOTAL; 123 | } 124 | while self.0 < MIN_REP { 125 | self.0 += TOTAL; 126 | } 127 | self 128 | } 129 | } 130 | 131 | impl ::core::ops::Add<$T> for $T { 132 | type Output = $T; 133 | #[inline] 134 | fn add(self, other: Self) -> Self { 135 | if cfg!(debug_assertions) { 136 | $T::new(self.0 + other.0).expect("arithmetic operation overflowed") 137 | } else { 138 | $T(self.0 + other.0).wrap_overflow_once() 139 | } 140 | } 141 | } 142 | 143 | impl ::core::ops::Sub<$T> for $T { 144 | type Output = $T; 145 | #[inline] 146 | fn sub(self, other: Self) -> Self { 147 | if cfg!(debug_assertions) { 148 | $T::new(self.0 - other.0).expect("arithmetic operation overflowed") 149 | } else { 150 | $T(self.0 - other.0).wrap_overflow_once() 151 | } 152 | } 153 | } 154 | 155 | impl ::core::ops::Mul<$T> for $T { 156 | type Output = $T; 157 | #[inline] 158 | fn mul(self, other: Self) -> Self { 159 | if cfg!(debug_assertions) { 160 | $T::new(self.0 * other.0).expect("arithmetic operation overflowed") 161 | } else { 162 | $T::from(self.0 * other.0) 163 | } 164 | } 165 | } 166 | 167 | impl ::core::ops::Div<$T> for $T { 168 | type Output = $T; 169 | #[inline] 170 | fn div(self, other: Self) -> Self { 171 | $T(self.0 / other.0) 172 | } 173 | } 174 | 175 | impl ::core::ops::Not for $T { 176 | type Output = $T; 177 | #[inline] 178 | fn not(self) -> $T { 179 | $T(!self.0) 180 | } 181 | } 182 | 183 | impl ::core::ops::Rem<$T> for $T { 184 | type Output = $T; 185 | #[inline] 186 | fn rem(self, other: Self) -> Self { 187 | $T(self.0 % other.0) 188 | } 189 | } 190 | 191 | impl ::core::ops::Shl<$T> for $T { 192 | type Output = $T; 193 | #[inline] 194 | fn shl(self, other: Self) -> Self { 195 | // TODO: Needs review 196 | $T(self.0 << other.0) 197 | } 198 | } 199 | 200 | impl ::core::ops::Shr<$T> for $T { 201 | type Output = $T; 202 | #[inline] 203 | fn shr(self, other: Self) -> Self { 204 | // TODO: Needs review 205 | $T(self.0 >> other.0) 206 | } 207 | } 208 | 209 | impl ::core::ops::BitAnd<$T> for $T { 210 | type Output = $T; 211 | #[inline] 212 | fn bitand(self, other: Self) -> Self { 213 | $T(self.0 & other.0) 214 | } 215 | } 216 | 217 | impl ::core::ops::BitOr<$T> for $T { 218 | type Output = $T; 219 | #[inline] 220 | fn bitor(self, other: Self) -> Self { 221 | $T(self.0 | other.0) 222 | } 223 | } 224 | 225 | impl ::core::ops::BitXor<$T> for $T { 226 | type Output = $T; 227 | #[inline] 228 | fn bitxor(self, other: Self) -> Self { 229 | $T(self.0 ^ other.0) 230 | } 231 | } 232 | 233 | impl_froms!($T: $Rep, $($rest)*); 234 | }; 235 | } 236 | 237 | pub mod i11 { 238 | new_sample_type!(I11: i16, eq: 0, min: -1024, max: 1023, total: 2048, 239 | from: i8, u8); 240 | impl_neg!(I11); 241 | } 242 | 243 | pub mod i20 { 244 | use super::{I11, U11}; 245 | new_sample_type!(I20: i32, eq: 0, min: -524_288, max: 524_287, total: 1_048_576, 246 | from: i8, {I11:i16}, i16, u8, {U11:i16}, u16); 247 | } 248 | 249 | pub mod i24 { 250 | use super::{I20, U20}; 251 | new_sample_type!(I24: i32, eq: 0, min: -8_388_608, max: 8_388_607, total: 16_777_216, 252 | from: i8, i16, {I20:i32}, u8, u16, {U20:i32}); 253 | impl_neg!(I24); 254 | } 255 | 256 | pub mod i48 { 257 | use super::{I20, I24, U20, U24}; 258 | 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, 259 | from: i8, i16, {I20:i32}, {I24:i32}, i32, u8, u16, {U20:i32}, {U24:i32}, u32); 260 | impl_neg!(I48); 261 | } 262 | 263 | pub mod u11 { 264 | new_sample_type!(U11: i16, eq: 1024, min: 0, max: 2047, total: 2048, 265 | from: u8); 266 | impl_neg!(U11); 267 | } 268 | 269 | pub mod u20 { 270 | new_sample_type!(U20: i32, eq: 524_288, min: 0, max: 1_048_575, total: 1_048_576, 271 | from: u8, u16); 272 | } 273 | 274 | pub mod u24 { 275 | use super::U20; 276 | new_sample_type!(U24: i32, eq: 8_388_608, min: 0, max: 16_777_215, total: 16_777_216, 277 | from: u8, u16, {U20:i32}); 278 | } 279 | 280 | pub mod u48 { 281 | use super::{U20, U24}; 282 | 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, 283 | from: u8, u16, {U20:i32}, {U24:i32}, u32); 284 | } 285 | -------------------------------------------------------------------------------- /dasp_sample/tests/types.rs: -------------------------------------------------------------------------------- 1 | /// Expands to a unique module with a variety of tests for the given sample newtype. 2 | /// 3 | /// Tests include basic operations and over/underflow checks. 4 | macro_rules! test_type { 5 | ($T:ident, $mod_name:ident) => { 6 | mod $mod_name { 7 | #[test] 8 | fn ops() { 9 | use dasp_sample::types::$mod_name::$T; 10 | assert_eq!( 11 | $T::new(8).unwrap() + $T::new(12).unwrap(), 12 | $T::new(20).unwrap() 13 | ); 14 | assert_eq!( 15 | $T::new(12).unwrap() - $T::new(4).unwrap(), 16 | $T::new(8).unwrap() 17 | ); 18 | assert_eq!( 19 | $T::new(2).unwrap() * $T::new(2).unwrap(), 20 | $T::new(4).unwrap() 21 | ); 22 | assert_eq!( 23 | $T::new(3).unwrap() * $T::new(3).unwrap(), 24 | $T::new(9).unwrap() 25 | ); 26 | assert_eq!( 27 | $T::new(5).unwrap() * $T::new(10).unwrap(), 28 | $T::new(50).unwrap() 29 | ); 30 | assert_eq!( 31 | $T::new(16).unwrap() / $T::new(8).unwrap(), 32 | $T::new(2).unwrap() 33 | ); 34 | assert_eq!( 35 | $T::new(8).unwrap() % $T::new(3).unwrap(), 36 | $T::new(2).unwrap() 37 | ); 38 | } 39 | 40 | #[cfg(debug_assertions)] 41 | #[test] 42 | #[should_panic] 43 | fn add_panic_debug() { 44 | use dasp_sample::types::$mod_name::{self, $T}; 45 | let _ = $mod_name::MAX + $T::new(1).unwrap(); 46 | } 47 | 48 | #[cfg(debug_assertions)] 49 | #[test] 50 | #[should_panic] 51 | fn sub_panic_debug() { 52 | use dasp_sample::types::$mod_name::{self, $T}; 53 | let _ = $mod_name::MIN - $T::new(1).unwrap(); 54 | } 55 | 56 | #[cfg(debug_assertions)] 57 | #[test] 58 | #[should_panic] 59 | fn mul_panic_debug() { 60 | use dasp_sample::types::$mod_name::{self, $T}; 61 | let _ = $mod_name::MAX * $T::new(2).unwrap(); 62 | } 63 | 64 | #[cfg(not(debug_assertions))] 65 | #[test] 66 | fn release_wrapping() { 67 | use dasp_sample::types::$mod_name::{self, $T}; 68 | assert_eq!($mod_name::MIN - $T::new(1).unwrap(), $mod_name::MAX); 69 | assert_eq!($mod_name::MAX + $T::new(1).unwrap(), $mod_name::MIN); 70 | } 71 | } 72 | }; 73 | } 74 | 75 | test_type!(I11, i11); 76 | test_type!(U11, u11); 77 | test_type!(I20, i20); 78 | test_type!(U20, u20); 79 | test_type!(I24, i24); 80 | test_type!(U24, u24); 81 | test_type!(I48, i48); 82 | test_type!(U48, u48); 83 | -------------------------------------------------------------------------------- /dasp_signal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_signal" 3 | description = "An iterator-like API for audio PCM DSP streams." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["dsp", "signal", "rate", "pcm", "audio"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | dasp_envelope = { version = "0.11", path = "../dasp_envelope", default-features = false, optional = true } 15 | dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } 16 | dasp_interpolate = { version = "0.11", path = "../dasp_interpolate", default-features = false } 17 | dasp_peak = { version = "0.11", path = "../dasp_peak", default-features = false } 18 | dasp_ring_buffer = { version = "0.11", path = "../dasp_ring_buffer", default-features = false } 19 | dasp_rms = { version = "0.11", path = "../dasp_rms", default-features = false, optional = true } 20 | dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } 21 | dasp_window = { version = "0.11", path = "../dasp_window", default-features = false, optional = true } 22 | 23 | [dev-dependencies] 24 | dasp_envelope = { version = "0.11", path = "../dasp_envelope", default-features = false, features = ["peak"] } 25 | dasp_interpolate = { version = "0.11", path = "../dasp_interpolate", default-features = false, features = ["floor", "linear", "sinc"] } 26 | dasp_window = { version = "0.11", path = "../dasp_window", default-features = false, features = ["hann"] } 27 | 28 | [features] 29 | default = ["std"] 30 | all = ["std", "all-no-std"] 31 | all-no-std = [ 32 | "boxed", 33 | "bus", 34 | "envelope", 35 | "rms", 36 | "window", 37 | "window-hann", 38 | "window-rectangle", 39 | ] 40 | std = [ 41 | "dasp_envelope/std", 42 | "dasp_frame/std", 43 | "dasp_interpolate/std", 44 | "dasp_peak/std", 45 | "dasp_ring_buffer/std", 46 | "dasp_rms/std", 47 | "dasp_sample/std", 48 | "dasp_window/std", 49 | ] 50 | boxed = [] 51 | bus = [] 52 | envelope = ["dasp_envelope"] 53 | rms = ["dasp_rms"] 54 | window = ["dasp_window"] 55 | window-hann = ["dasp_window/hann"] 56 | window-rectangle = ["dasp_window/rectangle"] 57 | 58 | [package.metadata.docs.rs] 59 | all-features = true 60 | -------------------------------------------------------------------------------- /dasp_signal/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_signal/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_signal/src/boxed.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "std"))] 2 | type Box = alloc::boxed::Box; 3 | #[cfg(feature = "std")] 4 | type Box = std::boxed::Box; 5 | 6 | impl Signal for Box 7 | where 8 | S: Signal + ?Sized, 9 | { 10 | type Frame = S::Frame; 11 | #[inline] 12 | fn next(&mut self) -> Self::Frame { 13 | (**self).next() 14 | } 15 | 16 | #[inline] 17 | fn is_exhausted(&self) -> bool { 18 | (**self).is_exhausted() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dasp_signal/src/bus.rs: -------------------------------------------------------------------------------- 1 | //! An extension to the **Signal** trait that enables multiple signal outputs. 2 | //! 3 | //! ### Required Features 4 | //! 5 | //! - When using `dasp_signal`, this item requires the **bus** feature to be enabled. 6 | //! - When using `dasp`, this item requires the **signal-bus** feature to be enabled. 7 | 8 | use crate::{Rc, Signal}; 9 | 10 | #[cfg(not(feature = "std"))] 11 | type BTreeMap = alloc::collections::btree_map::BTreeMap; 12 | #[cfg(feature = "std")] 13 | type BTreeMap = std::collections::BTreeMap; 14 | 15 | #[cfg(not(feature = "std"))] 16 | type VecDeque = alloc::collections::vec_deque::VecDeque; 17 | #[cfg(feature = "std")] 18 | type VecDeque = std::collections::vec_deque::VecDeque; 19 | 20 | /// An extension to the **Signal** trait that enables multiple signal outputs. 21 | /// 22 | /// ### Required Features 23 | /// 24 | /// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. 25 | /// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. 26 | pub trait SignalBus: Signal { 27 | /// Moves the `Signal` into a `Bus` from which its output may be divided into multiple other 28 | /// `Signal`s in the form of `Output`s. 29 | /// 30 | /// This method allows to create more complex directed acyclic graph structures that 31 | /// incorporate concepts like sends, side-chaining, etc, rather than being restricted to tree 32 | /// structures where signals can only ever be joined but never divided. 33 | /// 34 | /// Note: When using multiple `Output`s in this fashion, you will need to be sure to pull the 35 | /// frames from each `Output` in sync (whether per frame or per buffer). This is because when 36 | /// output A requests `Frame`s before output B, those frames must remain available for output 37 | /// B and in turn must be stored in an intermediary ring buffer. 38 | /// 39 | /// # Example 40 | /// 41 | /// ```rust 42 | /// use dasp_signal::{self as signal, Signal}; 43 | /// use dasp_signal::bus::SignalBus; 44 | /// 45 | /// fn main() { 46 | /// let frames = [[0.1], [0.2], [0.3], [0.4], [0.5], [0.6]]; 47 | /// let signal = signal::from_iter(frames.iter().cloned()); 48 | /// let bus = signal.bus(); 49 | /// let mut a = bus.send(); 50 | /// let mut b = bus.send(); 51 | /// assert_eq!(a.by_ref().take(3).collect::>(), vec![[0.1], [0.2], [0.3]]); 52 | /// assert_eq!(b.by_ref().take(3).collect::>(), vec![[0.1], [0.2], [0.3]]); 53 | /// 54 | /// let c = bus.send(); 55 | /// assert_eq!(c.take(3).collect::>(), vec![[0.4], [0.5], [0.6]]); 56 | /// assert_eq!(b.take(3).collect::>(), vec![[0.4], [0.5], [0.6]]); 57 | /// assert_eq!(a.take(3).collect::>(), vec![[0.4], [0.5], [0.6]]); 58 | /// } 59 | /// ``` 60 | /// 61 | /// ### Required Features 62 | /// 63 | /// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. 64 | /// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. 65 | fn bus(self) -> Bus 66 | where 67 | Self: Sized, 68 | { 69 | Bus::new(self, BTreeMap::new()) 70 | } 71 | } 72 | 73 | /// The data shared between each `Output`. 74 | struct SharedNode 75 | where 76 | S: Signal, 77 | { 78 | signal: S, 79 | // The buffer of frames that have not yet been consumed by all outputs. 80 | buffer: VecDeque, 81 | // The number of frames in `buffer` that have already been read for each output. 82 | frames_read: BTreeMap, 83 | // The next output key. 84 | next_key: usize, 85 | } 86 | 87 | /// A type which allows for `send`ing a single `Signal` to multiple outputs. 88 | /// 89 | /// ### Required Features 90 | /// 91 | /// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. 92 | /// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. 93 | pub struct Bus 94 | where 95 | S: Signal, 96 | { 97 | node: Rc>>, 98 | } 99 | 100 | /// An output node to which some signal `S` is `Output`ing its frames. 101 | /// 102 | /// It may be more accurate to say that the `Output` "pull"s frames from the signal. 103 | /// 104 | /// ### Required Features 105 | /// 106 | /// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. 107 | /// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. 108 | pub struct Output 109 | where 110 | S: Signal, 111 | { 112 | key: usize, 113 | node: Rc>>, 114 | } 115 | 116 | impl Bus 117 | where 118 | S: Signal, 119 | { 120 | fn new(signal: S, frames_read: BTreeMap) -> Self { 121 | Bus { 122 | node: Rc::new(core::cell::RefCell::new(SharedNode { 123 | signal: signal, 124 | buffer: VecDeque::new(), 125 | frames_read: frames_read, 126 | next_key: 0, 127 | })), 128 | } 129 | } 130 | 131 | /// Produce a new Output node to which the signal `S` will output its frames. 132 | /// 133 | /// ### Required Features 134 | /// 135 | /// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. 136 | /// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. 137 | #[inline] 138 | pub fn send(&self) -> Output { 139 | let mut node = self.node.borrow_mut(); 140 | 141 | // Get the key and increment for the next output. 142 | let key = node.next_key; 143 | node.next_key = node.next_key.wrapping_add(1); 144 | 145 | // Insert the number of frames read by the new output. 146 | let num_frames = node.buffer.len(); 147 | node.frames_read.insert(key, num_frames); 148 | 149 | Output { 150 | key: key, 151 | node: self.node.clone(), 152 | } 153 | } 154 | } 155 | 156 | impl SharedNode 157 | where 158 | S: Signal, 159 | { 160 | // Requests the next frame for the `Output` at the given key. 161 | // 162 | // If there are no frames pending for the output, a new frame will be requested from the 163 | // signal and appended to the ring buffer to be received by the other outputs. 164 | fn next_frame(&mut self, key: usize) -> S::Frame { 165 | let num_frames = self.buffer.len(); 166 | let frames_read = self 167 | .frames_read 168 | .remove(&key) 169 | .expect("no frames_read for Output"); 170 | 171 | let frame = if frames_read < num_frames { 172 | self.buffer[frames_read] 173 | } else { 174 | let frame = self.signal.next(); 175 | self.buffer.push_back(frame); 176 | frame 177 | }; 178 | 179 | // If the number of frames read by this output is the lowest, then we can pop the frame 180 | // from the front. 181 | let least_frames_read = !self 182 | .frames_read 183 | .values() 184 | .any(|&other_frames_read| other_frames_read <= frames_read); 185 | 186 | // If this output had read the least number of frames, pop the front frame and decrement 187 | // the frames read counters for each of the other outputs. 188 | let new_frames_read = if least_frames_read { 189 | self.buffer.pop_front(); 190 | for other_frames_read in self.frames_read.values_mut() { 191 | *other_frames_read -= 1; 192 | } 193 | frames_read 194 | } else { 195 | frames_read + 1 196 | }; 197 | 198 | self.frames_read.insert(key, new_frames_read); 199 | 200 | frame 201 | } 202 | 203 | #[inline] 204 | fn pending_frames(&self, key: usize) -> usize { 205 | self.buffer.len() - self.frames_read[&key] 206 | } 207 | 208 | // Drop the given output from the `Bus`. 209 | // 210 | // Called by the `Output::drop` implementation. 211 | fn drop_output(&mut self, key: usize) { 212 | self.frames_read.remove(&key); 213 | let least_frames_read = self 214 | .frames_read 215 | .values() 216 | .fold(self.buffer.len(), |a, &b| core::cmp::min(a, b)); 217 | if least_frames_read > 0 { 218 | for frames_read in self.frames_read.values_mut() { 219 | *frames_read -= least_frames_read; 220 | } 221 | for _ in 0..least_frames_read { 222 | self.buffer.pop_front(); 223 | } 224 | } 225 | } 226 | } 227 | 228 | impl Output 229 | where 230 | S: Signal, 231 | { 232 | /// The number of frames that have been requested from the `Signal` `S` by some other `Output` 233 | /// that have not yet been requested by this `Output`. 234 | /// 235 | /// This is useful when using an `Output` to "monitor" some signal, allowing the user to drain 236 | /// only frames that have already been requested by some other `Output`. 237 | /// 238 | /// # Example 239 | /// 240 | /// ``` 241 | /// use dasp_signal::{self as signal, Signal}; 242 | /// use dasp_signal::bus::SignalBus; 243 | /// 244 | /// fn main() { 245 | /// let frames = [[0.1], [0.2], [0.3]]; 246 | /// let bus = signal::from_iter(frames.iter().cloned()).bus(); 247 | /// let signal = bus.send(); 248 | /// let mut monitor = bus.send(); 249 | /// assert_eq!(signal.take(3).collect::>(), vec![[0.1], [0.2], [0.3]]); 250 | /// assert_eq!(monitor.pending_frames(), 3); 251 | /// assert_eq!(monitor.next(), [0.1]); 252 | /// assert_eq!(monitor.pending_frames(), 2); 253 | /// } 254 | /// ``` 255 | /// 256 | /// ### Required Features 257 | /// 258 | /// - When using `dasp_signal`, this item requires the **bus** feature to be enabled. 259 | /// - When using `dasp`, this item requires the **signal-bus** feature to be enabled. 260 | #[inline] 261 | pub fn pending_frames(&self) -> usize { 262 | self.node.borrow().pending_frames(self.key) 263 | } 264 | } 265 | 266 | impl SignalBus for T where T: Signal {} 267 | 268 | impl Signal for Output 269 | where 270 | S: Signal, 271 | { 272 | type Frame = S::Frame; 273 | 274 | #[inline] 275 | fn next(&mut self) -> Self::Frame { 276 | self.node.borrow_mut().next_frame(self.key) 277 | } 278 | 279 | #[inline] 280 | fn is_exhausted(&self) -> bool { 281 | let node = self.node.borrow(); 282 | node.pending_frames(self.key) == 0 && node.signal.is_exhausted() 283 | } 284 | } 285 | 286 | impl Drop for Output 287 | where 288 | S: Signal, 289 | { 290 | fn drop(&mut self) { 291 | self.node.borrow_mut().drop_output(self.key) 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /dasp_signal/src/envelope.rs: -------------------------------------------------------------------------------- 1 | //! An extension to the **Signal** trait that enables envelope detection. 2 | //! 3 | //! ### Required Features 4 | //! 5 | //! - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. 6 | //! - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. 7 | 8 | use crate::Signal; 9 | use dasp_envelope as envelope; 10 | 11 | /// An extension to the **Signal** trait that enables envelope detection. 12 | /// 13 | /// ### Required Features 14 | /// 15 | /// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. 16 | /// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. 17 | pub trait SignalEnvelope: Signal { 18 | /// An adaptor that detects and yields the envelope of the signal. 19 | /// 20 | /// # Example 21 | /// 22 | /// ``` 23 | /// use dasp_envelope as envelope; 24 | /// use dasp_signal::{self as signal, Signal}; 25 | /// use dasp_signal::envelope::SignalEnvelope; 26 | /// 27 | /// fn main() { 28 | /// let signal = signal::rate(4.0).const_hz(1.0).sine(); 29 | /// let attack = 1.0; 30 | /// let release = 1.0; 31 | /// let detector = envelope::Detector::peak(attack, release); 32 | /// let mut envelope = signal.detect_envelope(detector); 33 | /// assert_eq!( 34 | /// envelope.take(4).collect::>(), 35 | /// vec![0.0, 0.6321205496788025, 0.23254416035257117, 0.7176687675647109] 36 | /// ); 37 | /// } 38 | /// ``` 39 | /// 40 | /// ### Required Features 41 | /// 42 | /// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. 43 | /// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. 44 | fn detect_envelope( 45 | self, 46 | detector: envelope::Detector, 47 | ) -> DetectEnvelope 48 | where 49 | Self: Sized, 50 | D: envelope::Detect, 51 | { 52 | DetectEnvelope { 53 | signal: self, 54 | detector: detector, 55 | } 56 | } 57 | } 58 | 59 | /// An adaptor that detects and yields the envelope of the signal. 60 | /// 61 | /// ### Required Features 62 | /// 63 | /// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. 64 | /// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. 65 | #[derive(Clone)] 66 | pub struct DetectEnvelope 67 | where 68 | S: Signal, 69 | D: envelope::Detect, 70 | { 71 | signal: S, 72 | detector: envelope::Detector, 73 | } 74 | 75 | impl DetectEnvelope 76 | where 77 | S: Signal, 78 | D: envelope::Detect, 79 | { 80 | /// Set the **Detector**'s attack time as a number of frames. 81 | /// 82 | /// ### Required Features 83 | /// 84 | /// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. 85 | /// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. 86 | pub fn set_attack_frames(&mut self, frames: f32) { 87 | self.detector.set_attack_frames(frames); 88 | } 89 | 90 | /// Set the **Detector**'s release time as a number of frames. 91 | /// 92 | /// ### Required Features 93 | /// 94 | /// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. 95 | /// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. 96 | pub fn set_release_frames(&mut self, frames: f32) { 97 | self.detector.set_release_frames(frames); 98 | } 99 | 100 | /// Consumes `Self` and returns the inner signal `S` and `Detector`. 101 | /// 102 | /// ### Required Features 103 | /// 104 | /// - When using `dasp_signal`, this item requires the **envelope** feature to be enabled. 105 | /// - When using `dasp`, this item requires the **signal-envelope** feature to be enabled. 106 | pub fn into_parts(self) -> (S, envelope::Detector) { 107 | let DetectEnvelope { signal, detector } = self; 108 | (signal, detector) 109 | } 110 | } 111 | 112 | impl Signal for DetectEnvelope 113 | where 114 | S: Signal, 115 | D: envelope::Detect, 116 | { 117 | type Frame = D::Output; 118 | fn next(&mut self) -> Self::Frame { 119 | self.detector.next(self.signal.next()) 120 | } 121 | 122 | fn is_exhausted(&self) -> bool { 123 | self.signal.is_exhausted() 124 | } 125 | } 126 | 127 | impl SignalEnvelope for T where T: Signal {} 128 | -------------------------------------------------------------------------------- /dasp_signal/src/interpolate.rs: -------------------------------------------------------------------------------- 1 | //! The [**Converter**](./struct.Converter.html) type for interpolating the rate of a signal. 2 | 3 | use crate::Signal; 4 | use dasp_interpolate::Interpolator; 5 | 6 | /// A signal type that converts the rate at which frames are yielded from some source signal to 7 | /// some target rate. 8 | /// 9 | /// Other names for `sample::interpolate::Converter` might include: 10 | /// 11 | /// - Sample rate converter. 12 | /// - {Up/Down}sampler. 13 | /// - Sample interpolater. 14 | /// - Sample decimator. 15 | #[derive(Clone)] 16 | pub struct Converter 17 | where 18 | S: Signal, 19 | I: Interpolator, 20 | { 21 | source: S, 22 | interpolator: I, 23 | interpolation_value: f64, 24 | source_to_target_ratio: f64, 25 | } 26 | 27 | impl Converter 28 | where 29 | S: Signal, 30 | I: Interpolator, 31 | { 32 | /// Construct a new `Converter` from the source frames and the source and target sample rates 33 | /// (in Hz). 34 | #[inline] 35 | pub fn from_hz_to_hz(source: S, interpolator: I, source_hz: f64, target_hz: f64) -> Self { 36 | Self::scale_playback_hz(source, interpolator, source_hz / target_hz) 37 | } 38 | 39 | /// Construct a new `Converter` from the source frames and the amount by which the current 40 | /// ***playback*** **rate** (not sample rate) should be multiplied to reach the new playback 41 | /// rate. 42 | /// 43 | /// For example, if our `source_frames` is a sine wave oscillating at a frequency of 2hz and 44 | /// we wanted to convert it to a frequency of 3hz, the given `scale` should be `1.5`. 45 | #[inline] 46 | pub fn scale_playback_hz(source: S, interpolator: I, scale: f64) -> Self { 47 | assert!( 48 | scale > 0.0, 49 | "We can't yield any frames at 0 times a second!" 50 | ); 51 | Converter { 52 | source: source, 53 | interpolator: interpolator, 54 | interpolation_value: 0.0, 55 | source_to_target_ratio: scale, 56 | } 57 | } 58 | 59 | /// Construct a new `Converter` from the source frames and the amount by which the current 60 | /// ***sample*** **rate** (not playback rate) should be multiplied to reach the new sample 61 | /// rate. 62 | /// 63 | /// If our `source_frames` are being sampled at a rate of 44_100hz and we want to 64 | /// convert to a sample rate of 96_000hz, the given `scale` should be `96_000.0 / 44_100.0`. 65 | /// 66 | /// This is the same as calling `Converter::scale_playback_hz(source_frames, 1.0 / scale)`. 67 | #[inline] 68 | pub fn scale_sample_hz(source: S, interpolator: I, scale: f64) -> Self { 69 | Self::scale_playback_hz(source, interpolator, 1.0 / scale) 70 | } 71 | 72 | /// Update the `source_to_target_ratio` internally given the source and target hz. 73 | /// 74 | /// This method might be useful for changing the sample rate during playback. 75 | #[inline] 76 | pub fn set_hz_to_hz(&mut self, source_hz: f64, target_hz: f64) { 77 | self.set_playback_hz_scale(source_hz / target_hz) 78 | } 79 | 80 | /// Update the `source_to_target_ratio` internally given a new **playback rate** multiplier. 81 | /// 82 | /// This method is useful for dynamically changing rates. 83 | #[inline] 84 | pub fn set_playback_hz_scale(&mut self, scale: f64) { 85 | self.source_to_target_ratio = scale; 86 | } 87 | 88 | /// Update the `source_to_target_ratio` internally given a new **sample rate** multiplier. 89 | /// 90 | /// This method is useful for dynamically changing rates. 91 | #[inline] 92 | pub fn set_sample_hz_scale(&mut self, scale: f64) { 93 | self.set_playback_hz_scale(1.0 / scale); 94 | } 95 | 96 | /// Borrow the `source_frames` Interpolator from the `Converter`. 97 | #[inline] 98 | pub fn source(&self) -> &S { 99 | &self.source 100 | } 101 | 102 | /// Mutably borrow the `source_frames` Iterator from the `Converter`. 103 | #[inline] 104 | pub fn source_mut(&mut self) -> &mut S { 105 | &mut self.source 106 | } 107 | 108 | /// Drop `self` and return the internal `source_frames` Iterator. 109 | #[inline] 110 | pub fn into_source(self) -> S { 111 | self.source 112 | } 113 | } 114 | 115 | impl Signal for Converter 116 | where 117 | S: Signal, 118 | I: Interpolator, 119 | { 120 | type Frame = S::Frame; 121 | 122 | fn next(&mut self) -> Self::Frame { 123 | let Converter { 124 | ref mut source, 125 | ref mut interpolator, 126 | ref mut interpolation_value, 127 | source_to_target_ratio, 128 | } = *self; 129 | 130 | // Advance frames 131 | while *interpolation_value >= 1.0 { 132 | interpolator.next_source_frame(source.next()); 133 | *interpolation_value -= 1.0; 134 | } 135 | 136 | let out = interpolator.interpolate(*interpolation_value); 137 | *interpolation_value += source_to_target_ratio; 138 | out 139 | } 140 | 141 | fn is_exhausted(&self) -> bool { 142 | self.source.is_exhausted() && self.interpolation_value >= 1.0 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /dasp_signal/src/ops.rs: -------------------------------------------------------------------------------- 1 | pub mod f64 { 2 | #[allow(unused_imports)] 3 | use core; 4 | 5 | #[cfg(not(feature = "std"))] 6 | pub fn floor(x: f64) -> f64 { 7 | unsafe { core::intrinsics::floorf64(x) } 8 | } 9 | #[cfg(feature = "std")] 10 | pub fn floor(x: f64) -> f64 { 11 | x.floor() 12 | } 13 | 14 | #[cfg(not(feature = "std"))] 15 | #[allow(dead_code)] 16 | pub fn ceil(x: f64) -> f64 { 17 | unsafe { core::intrinsics::ceilf64(x) } 18 | } 19 | #[cfg(feature = "std")] 20 | #[allow(dead_code)] 21 | pub fn ceil(x: f64) -> f64 { 22 | x.ceil() 23 | } 24 | 25 | #[cfg(not(feature = "std"))] 26 | pub fn sin(x: f64) -> f64 { 27 | unsafe { core::intrinsics::sinf64(x) } 28 | } 29 | #[cfg(feature = "std")] 30 | pub fn sin(x: f64) -> f64 { 31 | x.sin() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /dasp_signal/src/rms.rs: -------------------------------------------------------------------------------- 1 | //! An extension to the **Signal** trait that monitors the RMS of a signal. 2 | //! 3 | //! ### Required Features 4 | //! 5 | //! - When using `dasp_signal`, this module requires the **rms** feature to be enabled. 6 | //! - When using `dasp`, this module requires the **signal-rms** feature to be enabled. 7 | 8 | use crate::Signal; 9 | use dasp_frame::Frame; 10 | use dasp_ring_buffer as ring_buffer; 11 | use dasp_rms as rms; 12 | 13 | /// An extension to the **Signal** trait that monitors the RMS of a signal. 14 | /// 15 | /// ### Required Features 16 | /// 17 | /// - When using `dasp_signal`, this item requires the **rms** feature to be enabled. 18 | /// - When using `dasp`, this item requires the **signal-rms** feature to be enabled. 19 | pub trait SignalRms: Signal { 20 | /// An adaptor that yields the RMS of the signal. 21 | /// 22 | /// The window size of the RMS detector is equal to the given ring buffer length. 23 | /// 24 | /// # Example 25 | /// 26 | /// ``` 27 | /// use dasp_ring_buffer as ring_buffer; 28 | /// use dasp_signal::{self as signal, Signal}; 29 | /// use dasp_signal::rms::SignalRms; 30 | /// 31 | /// fn main() { 32 | /// let frames = [[0.9], [-0.8], [0.6], [-0.9]]; 33 | /// let signal = signal::from_iter(frames.iter().cloned()); 34 | /// let ring_buffer = ring_buffer::Fixed::from([[0.0]; 2]); 35 | /// let mut rms_signal = signal.rms(ring_buffer); 36 | /// assert_eq!( 37 | /// [rms_signal.next(), rms_signal.next(), rms_signal.next()], 38 | /// [[0.6363961030678927], [0.8514693182963201], [0.7071067811865476]] 39 | /// ); 40 | /// } 41 | /// ``` 42 | /// 43 | /// ### Required Features 44 | /// 45 | /// - When using `dasp_signal`, this item requires the **rms** feature to be enabled. 46 | /// - When using `dasp`, this item requires the **signal-rms** feature to be enabled. 47 | fn rms(self, ring_buffer: ring_buffer::Fixed) -> Rms 48 | where 49 | Self: Sized, 50 | S: ring_buffer::Slice::Float> + ring_buffer::SliceMut, 51 | { 52 | Rms { 53 | signal: self, 54 | rms: rms::Rms::new(ring_buffer), 55 | } 56 | } 57 | } 58 | 59 | /// An adaptor that yields the RMS of the signal. 60 | /// 61 | /// The window size of the RMS detector is equal to the given ring buffer length. 62 | /// 63 | /// ### Required Features 64 | /// 65 | /// - When using `dasp_signal`, this item requires the **rms** feature to be enabled. 66 | /// - When using `dasp`, this item requires the **signal-rms** feature to be enabled. 67 | #[derive(Clone)] 68 | pub struct Rms 69 | where 70 | S: Signal, 71 | D: ring_buffer::Slice::Float>, 72 | { 73 | signal: S, 74 | rms: rms::Rms, 75 | } 76 | 77 | impl Rms 78 | where 79 | S: Signal, 80 | D: ring_buffer::Slice::Float> + ring_buffer::SliceMut, 81 | { 82 | /// The same as `Signal::next` but does not calculate the final square root required to 83 | /// determine the RMS. 84 | /// 85 | /// ### Required Features 86 | /// 87 | /// - When using `dasp_signal`, this item requires the **rms** feature to be enabled. 88 | /// - When using `dasp`, this item requires the **signal-rms** feature to be enabled. 89 | pub fn next_squared(&mut self) -> ::Frame { 90 | self.rms.next_squared(self.signal.next()) 91 | } 92 | 93 | /// Consumes the `Rms` signal and returns its inner signal `S` and `Rms` detector. 94 | /// 95 | /// ### Required Features 96 | /// 97 | /// - When using `dasp_signal`, this item requires the **rms** feature to be enabled. 98 | /// - When using `dasp`, this item requires the **signal-rms** feature to be enabled. 99 | pub fn into_parts(self) -> (S, rms::Rms) { 100 | let Rms { signal, rms } = self; 101 | (signal, rms) 102 | } 103 | } 104 | 105 | impl Signal for Rms 106 | where 107 | S: Signal, 108 | D: ring_buffer::Slice::Float> + ring_buffer::SliceMut, 109 | { 110 | type Frame = ::Float; 111 | fn next(&mut self) -> Self::Frame { 112 | self.rms.next(self.signal.next()) 113 | } 114 | 115 | fn is_exhausted(&self) -> bool { 116 | self.signal.is_exhausted() 117 | } 118 | } 119 | 120 | impl SignalRms for T where T: Signal {} 121 | -------------------------------------------------------------------------------- /dasp_signal/src/window/hann.rs: -------------------------------------------------------------------------------- 1 | use super::{Window, Windower}; 2 | use dasp_frame::Frame; 3 | use dasp_window::Hann; 4 | 5 | impl<'a, F> Windower<'a, F, Hann> 6 | where 7 | F: 'a + Frame, 8 | { 9 | /// Constructor for a `Windower` using the `Hann` window function. 10 | /// 11 | /// ### Required Features 12 | /// 13 | /// - When using `dasp_signal`, this item requires the **window-hann** feature to be enabled. 14 | /// - When using `dasp`, this item requires the **signal-window-hann** feature to be enabled. 15 | pub fn hann(frames: &'a [F], bin: usize, hop: usize) -> Self { 16 | Windower::new(frames, bin, hop) 17 | } 18 | } 19 | 20 | /// A helper function for constructing a `Window` that uses a `Hann` `Type` function. 21 | /// 22 | /// ### Required Features 23 | /// 24 | /// - When using `dasp_signal`, this item requires the **window-hann** feature to be enabled. 25 | /// - When using `dasp`, this item requires the **signal-window-hann** feature to be enabled. 26 | pub fn hann(num_frames: usize) -> Window 27 | where 28 | F: Frame, 29 | { 30 | Window::new(num_frames) 31 | } 32 | -------------------------------------------------------------------------------- /dasp_signal/src/window/mod.rs: -------------------------------------------------------------------------------- 1 | //! Items to ease the application of windowing functions to signals. 2 | 3 | use crate::{ConstHz, FromIterator, Phase, Signal}; 4 | use core::marker::PhantomData; 5 | use dasp_frame::Frame; 6 | use dasp_sample::Sample; 7 | use dasp_window::Window as WindowType; 8 | 9 | #[cfg(feature = "window-hann")] 10 | pub use hann::hann; 11 | #[cfg(feature = "window-rectangle")] 12 | pub use rectangle::rectangle; 13 | 14 | #[cfg(feature = "window-hann")] 15 | mod hann; 16 | #[cfg(feature = "window-rectangle")] 17 | mod rectangle; 18 | 19 | /// A `Signal` type that for every yielded `phase`, yields the amplitude across the `window::Type` 20 | /// for that phase. 21 | /// 22 | /// ### Required Features 23 | /// 24 | /// - When using `dasp_signal`, this item requires the **window** feature to be enabled. 25 | /// - When using `dasp`, this item requires the **signal-window** feature to be enabled. 26 | #[derive(Clone)] 27 | pub struct Window 28 | where 29 | F: Frame, 30 | W: WindowType, 31 | { 32 | /// Yields phase stepped at a constant rate to be passed to the window function `W`. 33 | pub phase: Phase, 34 | marker: PhantomData<(F, W)>, 35 | } 36 | 37 | /// Takes a long slice of frames and yields `Windowed` chunks of size `bin` once every `hop` frames. 38 | /// 39 | /// ### Required Features 40 | /// 41 | /// - When using `dasp_signal`, this item requires the **window** feature to be enabled. 42 | /// - When using `dasp`, this item requires the **signal-window** feature to be enabled. 43 | #[derive(Clone)] 44 | pub struct Windower<'a, F, W> 45 | where 46 | F: 'a + Frame, 47 | W: WindowType, 48 | { 49 | /// The size of each `Windowed` chunk to be yielded. 50 | pub bin: usize, 51 | /// The step size over `frames` for the start of each new `Windowed` chunk. 52 | pub hop: usize, 53 | /// The beginning of the remaining slice to be yielded by the `Windower`. 54 | pub frames: &'a [F], 55 | wttype: PhantomData, 56 | } 57 | 58 | /// An Iterator that multiplies a Signal with a Window. 59 | /// 60 | /// Returns `None` once the `Window` has been exhausted. 61 | /// 62 | /// ### Required Features 63 | /// 64 | /// - When using `dasp_signal`, this item requires the **window** feature to be enabled. 65 | /// - When using `dasp`, this item requires the **signal-window** feature to be enabled. 66 | #[derive(Clone)] 67 | pub struct Windowed 68 | where 69 | S: Signal, 70 | W: WindowType, 71 | { 72 | signal: S, 73 | window: Window<::Float, W>, 74 | } 75 | 76 | impl Window 77 | where 78 | F: Frame, 79 | W: WindowType, 80 | { 81 | /// Construct a new `Window` with the given length as a number of frames. 82 | /// 83 | /// ### Required Features 84 | /// 85 | /// - When using `dasp_signal`, this item requires the **window** feature to be enabled. 86 | /// - When using `dasp`, this item requires the **signal-window** feature to be enabled. 87 | pub fn new(len: usize) -> Self { 88 | let step = crate::rate(len as f64 - 1.0).const_hz(1.0); 89 | Window { 90 | phase: crate::phase(step), 91 | marker: PhantomData, 92 | } 93 | } 94 | } 95 | 96 | impl<'a, F, W> Windower<'a, F, W> 97 | where 98 | F: 'a + Frame, 99 | W: WindowType, 100 | { 101 | /// Constructor for a new `Windower` iterator. 102 | /// 103 | /// ### Required Features 104 | /// 105 | /// - When using `dasp_signal`, this item requires the **window** feature to be enabled. 106 | /// - When using `dasp`, this item requires the **signal-window** feature to be enabled. 107 | pub fn new(frames: &'a [F], bin: usize, hop: usize) -> Self { 108 | Windower { 109 | bin: bin, 110 | hop: hop, 111 | frames: frames, 112 | wttype: PhantomData, 113 | } 114 | } 115 | } 116 | 117 | impl Iterator for Window 118 | where 119 | F: Frame, 120 | W: WindowType, 121 | { 122 | type Item = F; 123 | 124 | fn next(&mut self) -> Option { 125 | let v = W::window(self.phase.next_phase()); 126 | let v_f: ::Float = v.to_sample(); 127 | Some(F::from_fn(|_| v_f.to_sample::())) 128 | } 129 | } 130 | 131 | impl<'a, F, W> Iterator for Windower<'a, F, W> 132 | where 133 | F: 'a + Frame, 134 | W: WindowType, 135 | { 136 | type Item = Windowed>>, W>; 137 | 138 | fn next(&mut self) -> Option { 139 | let num_frames = self.frames.len(); 140 | if self.bin <= num_frames { 141 | let frames = &self.frames[..self.bin]; 142 | let window = Window::new(self.bin); 143 | self.frames = if self.hop < num_frames { 144 | &self.frames[self.hop..] 145 | } else { 146 | &[] 147 | }; 148 | Some(Windowed { 149 | signal: crate::from_iter(frames.iter().cloned()), 150 | window: window, 151 | }) 152 | } else { 153 | None 154 | } 155 | } 156 | 157 | fn size_hint(&self) -> (usize, Option) { 158 | let num_frames = self.frames.len(); 159 | // Must have at least `bin` number of frames left to iterate at all. 160 | if self.bin < num_frames { 161 | // If the hop size is 0, we'll iterate forever. 162 | if self.hop == 0 { 163 | return (core::usize::MAX, None); 164 | } 165 | // Otherwise we can determine exactly how many iterations remain. 166 | let remaining_hop_frames = self.frames.len() - self.bin; 167 | let remaining_iterations = remaining_hop_frames / self.hop; 168 | (remaining_iterations, Some(remaining_iterations)) 169 | } else { 170 | (0, Some(0)) 171 | } 172 | } 173 | } 174 | 175 | impl Iterator for Windowed 176 | where 177 | S: Signal, 178 | W: WindowType, 179 | { 180 | type Item = S::Frame; 181 | fn next(&mut self) -> Option { 182 | self.window.next().map(|w_f| { 183 | let s_f = self.signal.next(); 184 | s_f.mul_amp(w_f) 185 | }) 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /dasp_signal/src/window/rectangle.rs: -------------------------------------------------------------------------------- 1 | use super::{Window, Windower}; 2 | use dasp_frame::Frame; 3 | use dasp_window::Rectangle; 4 | 5 | impl<'a, F> Windower<'a, F, Rectangle> 6 | where 7 | F: 'a + Frame, 8 | { 9 | /// Constructor for a `Windower` using the `Rectangle` window function. 10 | /// 11 | /// ### Required Features 12 | /// 13 | /// - When using `dasp_signal`, this item requires the **window-rectangle** feature to be enabled. 14 | /// - When using `dasp`, this item requires the **signal-window-rectangle** feature to be enabled. 15 | pub fn rectangle(frames: &'a [F], bin: usize, hop: usize) -> Self { 16 | Windower::new(frames, bin, hop) 17 | } 18 | } 19 | 20 | /// A helper function for constructing a `Window` that uses a `Rectangle` `Type` function. 21 | /// 22 | /// ### Required Features 23 | /// 24 | /// - When using `dasp_signal`, this item requires the **window-rectangle** feature to be enabled. 25 | /// - When using `dasp`, this item requires the **signal-window-rectangle** feature to be enabled. 26 | pub fn rectangle(num_frames: usize) -> Window 27 | where 28 | F: Frame, 29 | { 30 | Window::new(num_frames) 31 | } 32 | -------------------------------------------------------------------------------- /dasp_signal/tests/interpolate.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the `Converter` and `Interpolator` traits 2 | 3 | use dasp_interpolate::{floor::Floor, linear::Linear, sinc::Sinc}; 4 | use dasp_ring_buffer as ring_buffer; 5 | use dasp_signal::{self as signal, interpolate::Converter, Signal}; 6 | 7 | #[test] 8 | fn test_floor_converter() { 9 | let frames: [f64; 3] = [0.0, 1.0, 2.0]; 10 | let mut source = signal::from_iter(frames.iter().cloned()); 11 | let interp = Floor::new(source.next()); 12 | let mut conv = Converter::scale_playback_hz(source, interp, 0.5); 13 | 14 | assert_eq!(conv.next(), 0.0); 15 | assert_eq!(conv.next(), 0.0); 16 | assert_eq!(conv.next(), 1.0); 17 | assert_eq!(conv.next(), 1.0); 18 | // It may seem odd that we are emitting two values, but consider this: no matter what the next 19 | // value would be, Floor would always yield the same frame until we hit an interpolation_value 20 | // of 1.0 and had to advance the frame. We don't know what the future holds, so we should 21 | // continue yielding frames. 22 | assert_eq!(conv.next(), 2.0); 23 | assert_eq!(conv.next(), 2.0); 24 | } 25 | 26 | #[test] 27 | fn test_linear_converter() { 28 | let frames: [f64; 3] = [0.0, 1.0, 2.0]; 29 | let mut source = signal::from_iter(frames.iter().cloned()); 30 | let a = source.next(); 31 | let b = source.next(); 32 | let interp = Linear::new(a, b); 33 | let mut conv = Converter::scale_playback_hz(source, interp, 0.5); 34 | 35 | assert_eq!(conv.next(), 0.0); 36 | assert_eq!(conv.next(), 0.5); 37 | assert_eq!(conv.next(), 1.0); 38 | assert_eq!(conv.next(), 1.5); 39 | assert_eq!(conv.next(), 2.0); 40 | // There's nothing else here to interpolate toward, but we do want to ensure that we're 41 | // emitting the correct number of frames. 42 | assert_eq!(conv.next(), 1.0); 43 | } 44 | 45 | #[test] 46 | fn test_scale_playback_rate() { 47 | // Scale the playback rate by `0.5` 48 | let foo = [0.0, 1.0, 0.0, -1.0]; 49 | let mut source = signal::from_iter(foo.iter().cloned()); 50 | let a = source.next(); 51 | let b = source.next(); 52 | let interp = Linear::new(a, b); 53 | let frames: Vec<_> = source.scale_hz(interp, 0.5).take(8).collect(); 54 | assert_eq!( 55 | &frames[..], 56 | &[0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -0.5][..] 57 | ); 58 | } 59 | 60 | #[test] 61 | fn test_sinc() { 62 | let foo = [0f64, 1.0, 0.0, -1.0]; 63 | let source = signal::from_iter(foo.iter().cloned()); 64 | 65 | let frames = ring_buffer::Fixed::from(vec![0.0; 50]); 66 | let interp = Sinc::new(frames); 67 | let resampled = source.from_hz_to_hz(interp, 44100.0, 11025.0); 68 | 69 | assert_eq!( 70 | resampled.until_exhausted().find(|sample| sample.is_nan()), 71 | None 72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /dasp_signal/tests/signal.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the `Signal` trait. 2 | 3 | use dasp_signal::{self as signal, Signal}; 4 | 5 | #[test] 6 | fn test_equilibrium() { 7 | let equilibrium: Vec = signal::equilibrium().take(4).collect(); 8 | assert_eq!(equilibrium, vec![0, 0, 0, 0]); 9 | } 10 | 11 | #[test] 12 | fn test_scale_amp() { 13 | let foo = [0.5, 0.8, -0.4, -0.2]; 14 | let amp = 0.5; 15 | let amp_scaled: Vec<_> = signal::from_iter(foo.iter().cloned()) 16 | .scale_amp(amp) 17 | .take(4) 18 | .collect(); 19 | assert_eq!(amp_scaled, vec![0.25, 0.4, -0.2, -0.1]); 20 | } 21 | 22 | #[test] 23 | fn test_offset_amp() { 24 | let foo = [0.5, 0.9, -0.4, -0.2]; 25 | let amp = -0.5; 26 | let amp_offset: Vec<_> = signal::from_iter(foo.iter().cloned()) 27 | .offset_amp(amp) 28 | .take(4) 29 | .collect(); 30 | assert_eq!(amp_offset, vec![0.0, 0.4, -0.9, -0.7]); 31 | } 32 | -------------------------------------------------------------------------------- /dasp_signal/tests/window.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "window")] 2 | 3 | use dasp_frame::Frame; 4 | use dasp_signal::window::{self, Windower}; 5 | 6 | #[cfg(feature = "window-hann")] 7 | #[test] 8 | fn test_window_at_phase() { 9 | let window = window::hann::(9); 10 | let expected = [ 11 | 0.0, 0.1464, 0.5000, 0.8536, 1., 0.8536, 0.5000, 0.1464, 0., 0.1464, 12 | ]; 13 | for (r, e) in window.zip(&expected) { 14 | println!("Expected: {}\t\tFound: {}", e, r); 15 | assert!((r - e).abs() < 0.001); 16 | } 17 | } 18 | 19 | #[test] 20 | fn test_windower() { 21 | let data = [0.1f64, 0.1, 0.2, 0.2, 0.3, 0.3, 0.4, 0.4]; 22 | let expected = [ 23 | [0.1f64, 0.1], 24 | [0.1, 0.2], 25 | [0.2, 0.2], 26 | [0.2, 0.3], 27 | [0.3, 0.3], 28 | [0.3, 0.4], 29 | [0.4, 0.4], 30 | ]; 31 | let windower = Windower::rectangle(&data, 2, 1); 32 | for (chunk, expected_chunk) in windower.zip(&expected) { 33 | for (r, e) in chunk.zip(expected_chunk.iter()) { 34 | for (r_chan, e_chan) in r.channels().zip(e.channels()) { 35 | println!("Expected: {}\t\tFound: {}", e_chan, r_chan); 36 | assert!((r_chan - e_chan).abs() < 0.001); 37 | } 38 | } 39 | } 40 | } 41 | 42 | #[cfg(feature = "window-hann")] 43 | #[test] 44 | fn test_window_size() { 45 | let v = [1f32; 16]; 46 | let windows: Vec> = Windower::hann(&v, 8, 4) 47 | .map(|i| i.take(8).collect::>()) 48 | .take(3) 49 | .collect(); 50 | assert_eq!(windows.len(), 3); 51 | } 52 | -------------------------------------------------------------------------------- /dasp_slice/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_slice" 3 | description = "Conversions and operations for slices of audio PCM DSP samples and frames." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["dsp", "bit-depth", "rate", "pcm", "audio"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } 15 | dasp_frame = { version = "0.11", path = "../dasp_frame", default-features = false } 16 | 17 | [features] 18 | default = ["std"] 19 | all = ["std", "all-no-std"] 20 | all-no-std = [ 21 | "boxed", 22 | ] 23 | std = [ 24 | "dasp_sample/std", 25 | "dasp_frame/std", 26 | ] 27 | boxed = [] 28 | 29 | [package.metadata.docs.rs] 30 | all-features = true 31 | -------------------------------------------------------------------------------- /dasp_slice/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_slice/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_slice/src/boxed.rs: -------------------------------------------------------------------------------- 1 | //! Items related to boxed-slice conversions. 2 | //! 3 | //! ### Required Features 4 | //! 5 | //! - When using `dasp_slice`, this module requires the **boxed** feature to be enabled. 6 | //! - When using `dasp`, this module requires the **slice-boxed** feature to be enabled. 7 | 8 | #[cfg(not(feature = "std"))] 9 | extern crate alloc; 10 | 11 | use dasp_frame::Frame; 12 | use dasp_sample::Sample; 13 | 14 | /// Equal to `std::boxed::Box` on std, `alloc::boxed::Box` in `no_std` context. 15 | #[cfg(not(feature = "std"))] 16 | pub type Box = alloc::boxed::Box; 17 | /// Equal to `std::boxed::Box` on std, `alloc::boxed::Box` in `no_std` context. 18 | #[cfg(feature = "std")] 19 | pub type Box = std::boxed::Box; 20 | 21 | // Traits 22 | // ---------------------------------------------------------------------------- 23 | 24 | /// For converting a boxed slice of `Sample`s to a boxed slice of `Frame`s. 25 | /// 26 | /// ### Required Features 27 | /// 28 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 29 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 30 | pub trait FromBoxedSampleSlice: Sized 31 | where 32 | S: Sample, 33 | { 34 | fn from_boxed_sample_slice(slice: Box<[S]>) -> Option; 35 | } 36 | 37 | /// For converting from a boxed slice of `Frame`s to a boxed slice of `Sample`s. 38 | /// 39 | /// ### Required Features 40 | /// 41 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 42 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 43 | pub trait FromBoxedFrameSlice 44 | where 45 | F: Frame, 46 | { 47 | fn from_boxed_frame_slice(slice: Box<[F]>) -> Self; 48 | } 49 | 50 | /// For converting from a boxed slice of `Frame`s to a boxed slice of `Sample`s. 51 | /// 52 | /// ### Required Features 53 | /// 54 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 55 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 56 | pub trait ToBoxedSampleSlice 57 | where 58 | S: Sample, 59 | { 60 | fn to_boxed_sample_slice(self) -> Box<[S]>; 61 | } 62 | 63 | /// For converting from a boxed slice of `Sample`s to a boxed slice of `Frame`s. 64 | /// 65 | /// ### Required Features 66 | /// 67 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 68 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 69 | pub trait ToBoxedFrameSlice 70 | where 71 | F: Frame, 72 | { 73 | fn to_boxed_frame_slice(self) -> Option>; 74 | } 75 | 76 | /// For converting to and from a boxed slice of `Sample`s. 77 | /// 78 | /// ### Required Features 79 | /// 80 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 81 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 82 | pub trait DuplexBoxedSampleSlice: FromBoxedSampleSlice + ToBoxedSampleSlice 83 | where 84 | S: Sample, 85 | { 86 | } 87 | 88 | /// For converting to and from a boxed slice of `Frame`s. 89 | /// 90 | /// ### Required Features 91 | /// 92 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 93 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 94 | pub trait DuplexBoxedFrameSlice: FromBoxedFrameSlice + ToBoxedFrameSlice 95 | where 96 | F: Frame, 97 | { 98 | } 99 | 100 | /// For converting to and from a boxed slice of `Sample`s of type `S` and a slice of `Frame`s of 101 | /// type `F`. 102 | /// 103 | /// ### Required Features 104 | /// 105 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 106 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 107 | pub trait DuplexBoxedSlice: DuplexBoxedSampleSlice + DuplexBoxedFrameSlice 108 | where 109 | S: Sample, 110 | F: Frame, 111 | { 112 | } 113 | 114 | // Implementations 115 | // ---------------------------------------------------------------------------- 116 | 117 | impl FromBoxedSampleSlice for Box<[S]> 118 | where 119 | S: Sample, 120 | { 121 | #[inline] 122 | fn from_boxed_sample_slice(slice: Box<[S]>) -> Option { 123 | Some(slice) 124 | } 125 | } 126 | 127 | impl FromBoxedFrameSlice for Box<[F]> 128 | where 129 | F: Frame, 130 | { 131 | #[inline] 132 | fn from_boxed_frame_slice(slice: Box<[F]>) -> Self { 133 | slice 134 | } 135 | } 136 | 137 | impl ToBoxedSampleSlice for Box<[S]> 138 | where 139 | S: Sample, 140 | { 141 | #[inline] 142 | fn to_boxed_sample_slice(self) -> Box<[S]> { 143 | self 144 | } 145 | } 146 | 147 | impl ToBoxedFrameSlice for Box<[F]> 148 | where 149 | F: Frame, 150 | { 151 | #[inline] 152 | fn to_boxed_frame_slice(self) -> Option> { 153 | Some(self) 154 | } 155 | } 156 | 157 | impl DuplexBoxedSampleSlice for T 158 | where 159 | S: Sample, 160 | T: FromBoxedSampleSlice + ToBoxedSampleSlice, 161 | { 162 | } 163 | 164 | impl DuplexBoxedFrameSlice for T 165 | where 166 | F: Frame, 167 | T: FromBoxedFrameSlice + ToBoxedFrameSlice, 168 | { 169 | } 170 | 171 | impl DuplexBoxedSlice for T 172 | where 173 | S: Sample, 174 | F: Frame, 175 | T: DuplexBoxedSampleSlice + DuplexBoxedFrameSlice, 176 | { 177 | } 178 | 179 | // Free Functions 180 | // ---------------------------------------------------------------------------- 181 | 182 | /// Converts the given boxed slice into a boxed slice of `Sample`s. 183 | /// 184 | /// This is a convenience function that wraps the `ToBoxedSampleSlice` trait. 185 | /// 186 | /// # Examples 187 | /// 188 | /// ``` 189 | /// fn main() { 190 | /// let foo = vec![[0.0, 0.5], [0.0, -0.5]].into_boxed_slice(); 191 | /// let bar = dasp_slice::to_boxed_sample_slice(foo); 192 | /// assert_eq!(bar.into_vec(), vec![0.0, 0.5, 0.0, -0.5]); 193 | /// } 194 | /// ``` 195 | /// 196 | /// ### Required Features 197 | /// 198 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 199 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 200 | pub fn to_boxed_sample_slice(slice: T) -> Box<[S]> 201 | where 202 | S: Sample, 203 | T: ToBoxedSampleSlice, 204 | { 205 | slice.to_boxed_sample_slice() 206 | } 207 | 208 | /// Converts the given boxed slice into a boxed slice of `Frame`s. 209 | /// 210 | /// Returns `None` if the number of channels in a single frame `F` is not a multiple of the number 211 | /// of samples in the given slice. 212 | /// 213 | /// This is a convenience function that wraps the `ToBoxedFrameSlice` trait. 214 | /// 215 | /// # Examples 216 | /// 217 | /// ``` 218 | /// fn main() { 219 | /// let foo = vec![0.0, 0.5, 0.0, -0.5].into_boxed_slice(); 220 | /// let bar: Box<[[f32; 2]]> = dasp_slice::to_boxed_frame_slice(foo).unwrap(); 221 | /// assert_eq!(bar.into_vec(), vec![[0.0, 0.5], [0.0, -0.5]]); 222 | /// 223 | /// let foo = vec![0.0, 0.5, 0.0].into_boxed_slice(); 224 | /// let bar = dasp_slice::to_boxed_frame_slice(foo); 225 | /// assert_eq!(bar, None::>); 226 | /// } 227 | /// ``` 228 | /// 229 | /// ### Required Features 230 | /// 231 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 232 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 233 | pub fn to_boxed_frame_slice(slice: T) -> Option> 234 | where 235 | F: Frame, 236 | T: ToBoxedFrameSlice, 237 | { 238 | slice.to_boxed_frame_slice() 239 | } 240 | 241 | /// Converts the given boxed slice of `Sample`s into some slice `T`. 242 | /// 243 | /// Returns `None` if the number of channels in a single frame is not a multiple of the number of 244 | /// samples in the given slice. 245 | /// 246 | /// This is a convenience function that wraps the `FromBoxedSampleSlice` trait. 247 | /// 248 | /// # Examples 249 | /// 250 | /// ``` 251 | /// fn main() { 252 | /// let foo = vec![0.0, 0.5, 0.0, -0.5].into_boxed_slice(); 253 | /// let bar: Box<[[f32; 2]]> = dasp_slice::from_boxed_sample_slice(foo).unwrap(); 254 | /// assert_eq!(bar.into_vec(), vec![[0.0, 0.5], [0.0, -0.5]]); 255 | /// } 256 | /// ``` 257 | /// 258 | /// ### Required Features 259 | /// 260 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 261 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 262 | pub fn from_boxed_sample_slice(slice: Box<[S]>) -> Option 263 | where 264 | S: Sample, 265 | T: FromBoxedSampleSlice, 266 | { 267 | T::from_boxed_sample_slice(slice) 268 | } 269 | 270 | /// Converts the given boxed slice of `Frame`s into some slice `T`. 271 | /// 272 | /// This is a convenience function that wraps the `FromBoxedFrameSlice` trait. 273 | /// 274 | /// # Examples 275 | /// 276 | /// ``` 277 | /// fn main() { 278 | /// let foo = vec![[0.0, 0.5], [0.0, -0.5]].into_boxed_slice(); 279 | /// let bar: Box<[f32]> = dasp_slice::from_boxed_frame_slice(foo); 280 | /// assert_eq!(bar.into_vec(), vec![0.0, 0.5, 0.0, -0.5]); 281 | /// } 282 | /// ``` 283 | /// 284 | /// ### Required Features 285 | /// 286 | /// - When using `dasp_slice`, this item requires the **boxed** feature to be enabled. 287 | /// - When using `dasp`, this item requires the **slice-boxed** feature to be enabled. 288 | pub fn from_boxed_frame_slice(slice: Box<[F]>) -> T 289 | where 290 | F: Frame, 291 | T: FromBoxedFrameSlice, 292 | { 293 | T::from_boxed_frame_slice(slice) 294 | } 295 | -------------------------------------------------------------------------------- /dasp_slice/src/frame/fixed_size_array.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the slice conversion traits for slices of fixed-size-array frames. 2 | 3 | #[cfg(feature = "boxed")] 4 | use crate::boxed::{ 5 | Box, FromBoxedFrameSlice, FromBoxedSampleSlice, ToBoxedFrameSlice, ToBoxedSampleSlice, 6 | }; 7 | use crate::{ 8 | FromFrameSlice, FromFrameSliceMut, FromSampleSlice, FromSampleSliceMut, ToFrameSlice, 9 | ToFrameSliceMut, ToSampleSlice, ToSampleSliceMut, 10 | }; 11 | use dasp_frame::Frame; 12 | use dasp_sample::Sample; 13 | 14 | /// A macro for implementing all audio slice conversion traits for each fixed-size array. 15 | macro_rules! impl_from_slice_conversions { 16 | ($($N:expr)*) => { 17 | $( 18 | impl<'a, S> FromSampleSlice<'a, S> for &'a [[S; $N]] 19 | where 20 | S: Sample, 21 | [S; $N]: Frame, 22 | { 23 | #[inline] 24 | fn from_sample_slice(slice: &'a [S]) -> Option { 25 | let len = slice.len(); 26 | if len % $N == 0 { 27 | let new_len = len / $N; 28 | let ptr = slice.as_ptr() as *const _; 29 | let new_slice = unsafe { 30 | core::slice::from_raw_parts(ptr, new_len) 31 | }; 32 | Some(new_slice) 33 | } else { 34 | None 35 | } 36 | } 37 | } 38 | 39 | impl<'a, S> FromSampleSliceMut<'a, S> for &'a mut [[S; $N]] 40 | where 41 | S: Sample, 42 | [S; $N]: Frame, 43 | { 44 | #[inline] 45 | fn from_sample_slice_mut(slice: &'a mut [S]) -> Option { 46 | let len = slice.len(); 47 | if len % $N == 0 { 48 | let new_len = len / $N; 49 | let ptr = slice.as_ptr() as *mut _; 50 | let new_slice = unsafe { 51 | core::slice::from_raw_parts_mut(ptr, new_len) 52 | }; 53 | Some(new_slice) 54 | } else { 55 | None 56 | } 57 | } 58 | } 59 | 60 | impl<'a, S> FromFrameSlice<'a, [S; $N]> for &'a [S] 61 | where 62 | [S; $N]: Frame, 63 | { 64 | #[inline] 65 | fn from_frame_slice(slice: &'a [[S; $N]]) -> Self { 66 | let new_len = slice.len() * $N; 67 | let ptr = slice.as_ptr() as *const _; 68 | unsafe { 69 | core::slice::from_raw_parts(ptr, new_len) 70 | } 71 | } 72 | } 73 | 74 | impl<'a, S> FromFrameSliceMut<'a, [S; $N]> for &'a mut [S] 75 | where 76 | [S; $N]: Frame, 77 | { 78 | #[inline] 79 | fn from_frame_slice_mut(slice: &'a mut [[S; $N]]) -> Self { 80 | let new_len = slice.len() * $N; 81 | let ptr = slice.as_ptr() as *mut _; 82 | unsafe { 83 | core::slice::from_raw_parts_mut(ptr, new_len) 84 | } 85 | } 86 | } 87 | 88 | impl<'a, S> ToSampleSlice<'a, S> for &'a [[S; $N]] 89 | where 90 | S: Sample, 91 | { 92 | #[inline] 93 | fn to_sample_slice(self) -> &'a [S] { 94 | FromFrameSlice::from_frame_slice(self) 95 | } 96 | } 97 | 98 | impl<'a, S> ToSampleSliceMut<'a, S> for &'a mut [[S; $N]] 99 | where 100 | S: Sample, 101 | { 102 | #[inline] 103 | fn to_sample_slice_mut(self) -> &'a mut [S] { 104 | FromFrameSliceMut::from_frame_slice_mut(self) 105 | } 106 | } 107 | 108 | impl<'a, S> ToFrameSlice<'a, [S; $N]> for &'a [S] 109 | where 110 | S: Sample, 111 | [S; $N]: Frame, 112 | { 113 | #[inline] 114 | fn to_frame_slice(self) -> Option<&'a [[S; $N]]> { 115 | FromSampleSlice::from_sample_slice(self) 116 | } 117 | } 118 | 119 | impl<'a, S> ToFrameSliceMut<'a, [S; $N]> for &'a mut [S] 120 | where 121 | S: Sample, 122 | [S; $N]: Frame, 123 | { 124 | #[inline] 125 | fn to_frame_slice_mut(self) -> Option<&'a mut [[S; $N]]> { 126 | FromSampleSliceMut::from_sample_slice_mut(self) 127 | } 128 | } 129 | 130 | #[cfg(feature = "boxed")] 131 | impl FromBoxedSampleSlice for Box<[[S; $N]]> 132 | where 133 | S: Sample, 134 | [S; $N]: Frame, 135 | { 136 | #[inline] 137 | fn from_boxed_sample_slice(mut slice: Box<[S]>) -> Option { 138 | // First, we need a raw pointer to the slice and to make sure that the `Box` is 139 | // forgotten so that our slice does not get deallocated. 140 | let len = slice.len(); 141 | let slice_ptr = &mut slice as &mut [S] as *mut [S]; 142 | core::mem::forget(slice); 143 | let sample_slice = unsafe { 144 | core::slice::from_raw_parts_mut((*slice_ptr).as_mut_ptr(), len) 145 | }; 146 | 147 | // Convert to our frame slice if possible. 148 | let frame_slice = match <&mut [[S; $N]]>::from_sample_slice_mut(sample_slice) { 149 | Some(slice) => slice, 150 | None => return None, 151 | }; 152 | let ptr = frame_slice as *mut [[S; $N]]; 153 | 154 | // Take ownership over the slice again before returning it. 155 | let new_slice = unsafe { 156 | Box::from_raw(ptr) 157 | }; 158 | 159 | Some(new_slice) 160 | } 161 | } 162 | 163 | #[cfg(feature = "boxed")] 164 | impl FromBoxedFrameSlice<[S; $N]> for Box<[S]> 165 | where 166 | [S; $N]: Frame, 167 | { 168 | #[inline] 169 | fn from_boxed_frame_slice(mut slice: Box<[[S; $N]]>) -> Self { 170 | let new_len = slice.len() * $N; 171 | let frame_slice_ptr = &mut slice as &mut [[S; $N]] as *mut [[S; $N]]; 172 | core::mem::forget(slice); 173 | let sample_slice_ptr = frame_slice_ptr as *mut [S]; 174 | unsafe { 175 | let ptr = (*sample_slice_ptr).as_mut_ptr(); 176 | let sample_slice = core::slice::from_raw_parts_mut(ptr, new_len); 177 | Box::from_raw(sample_slice as *mut _) 178 | } 179 | } 180 | } 181 | 182 | #[cfg(feature = "boxed")] 183 | impl ToBoxedSampleSlice for Box<[[S; $N]]> 184 | where 185 | S: Sample, 186 | { 187 | #[inline] 188 | fn to_boxed_sample_slice(self) -> Box<[S]> { 189 | FromBoxedFrameSlice::from_boxed_frame_slice(self) 190 | } 191 | } 192 | 193 | #[cfg(feature = "boxed")] 194 | impl ToBoxedFrameSlice<[S; $N]> for Box<[S]> 195 | where 196 | S: Sample, 197 | [S; $N]: Frame, 198 | { 199 | #[inline] 200 | fn to_boxed_frame_slice(self) -> Option> { 201 | FromBoxedSampleSlice::from_boxed_sample_slice(self) 202 | } 203 | } 204 | )* 205 | }; 206 | } 207 | 208 | impl_from_slice_conversions! { 209 | 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 210 | } 211 | -------------------------------------------------------------------------------- /dasp_slice/src/frame/mod.rs: -------------------------------------------------------------------------------- 1 | use dasp_frame::Frame; 2 | 3 | mod fixed_size_array; 4 | 5 | /// For converting from a slice of `Frame`s to a slice of `Sample`s. 6 | pub trait FromFrameSlice<'a, F> 7 | where 8 | F: Frame, 9 | { 10 | fn from_frame_slice(slice: &'a [F]) -> Self; 11 | } 12 | 13 | /// For converting from a slice of `Frame`s to a slice of `Sample`s. 14 | pub trait FromFrameSliceMut<'a, F> 15 | where 16 | F: Frame, 17 | { 18 | fn from_frame_slice_mut(slice: &'a mut [F]) -> Self; 19 | } 20 | 21 | /// For converting from a slice of `Sample`s to a slice of `Frame`s. 22 | pub trait ToFrameSlice<'a, F> 23 | where 24 | F: Frame, 25 | { 26 | fn to_frame_slice(self) -> Option<&'a [F]>; 27 | } 28 | 29 | /// For converting from a mutable slice of `Sample`s to a mutable slice of `Frame`s. 30 | pub trait ToFrameSliceMut<'a, F> 31 | where 32 | F: Frame, 33 | { 34 | fn to_frame_slice_mut(self) -> Option<&'a mut [F]>; 35 | } 36 | 37 | /// For converting to and from a slice of `Frame`s. 38 | pub trait DuplexFrameSlice<'a, F>: FromFrameSlice<'a, F> + ToFrameSlice<'a, F> 39 | where 40 | F: Frame, 41 | { 42 | } 43 | 44 | /// For converting to and from a mutable slice of `Frame`s. 45 | pub trait DuplexFrameSliceMut<'a, F>: FromFrameSliceMut<'a, F> + ToFrameSliceMut<'a, F> 46 | where 47 | F: Frame, 48 | { 49 | } 50 | 51 | impl<'a, F> FromFrameSlice<'a, F> for &'a [F] 52 | where 53 | F: Frame, 54 | { 55 | #[inline] 56 | fn from_frame_slice(slice: &'a [F]) -> Self { 57 | slice 58 | } 59 | } 60 | 61 | impl<'a, F> FromFrameSliceMut<'a, F> for &'a mut [F] 62 | where 63 | F: Frame, 64 | { 65 | #[inline] 66 | fn from_frame_slice_mut(slice: &'a mut [F]) -> Self { 67 | slice 68 | } 69 | } 70 | 71 | impl<'a, F> ToFrameSlice<'a, F> for &'a [F] 72 | where 73 | F: Frame, 74 | { 75 | #[inline] 76 | fn to_frame_slice(self) -> Option<&'a [F]> { 77 | Some(self) 78 | } 79 | } 80 | 81 | impl<'a, F> ToFrameSliceMut<'a, F> for &'a mut [F] 82 | where 83 | F: Frame, 84 | { 85 | #[inline] 86 | fn to_frame_slice_mut(self) -> Option<&'a mut [F]> { 87 | Some(self) 88 | } 89 | } 90 | 91 | impl<'a, F, T> DuplexFrameSlice<'a, F> for T 92 | where 93 | F: Frame, 94 | T: FromFrameSlice<'a, F> + ToFrameSlice<'a, F>, 95 | { 96 | } 97 | 98 | impl<'a, F, T> DuplexFrameSliceMut<'a, F> for T 99 | where 100 | F: Frame, 101 | T: FromFrameSliceMut<'a, F> + ToFrameSliceMut<'a, F>, 102 | { 103 | } 104 | 105 | /// Converts the given slice into a slice of `Frame`s. 106 | /// 107 | /// Returns `None` if the number of channels in a single frame `F` is not a multiple of the number 108 | /// of samples in the given slice. 109 | /// 110 | /// This is a convenience function that wraps the `ToFrameSlice` trait. 111 | /// 112 | /// # Examples 113 | /// 114 | /// ``` 115 | /// fn main() { 116 | /// let foo = &[0.0, 0.5, 0.0, -0.5][..]; 117 | /// let bar = dasp_slice::to_frame_slice(foo); 118 | /// assert_eq!(bar, Some(&[[0.0, 0.5], [0.0, -0.5]][..])); 119 | /// 120 | /// let foo = &[0.0, 0.5, 0.0][..]; 121 | /// let bar = dasp_slice::to_frame_slice(foo); 122 | /// assert_eq!(bar, None::<&[[f32; 2]]>); 123 | /// } 124 | /// ``` 125 | pub fn to_frame_slice<'a, T, F>(slice: T) -> Option<&'a [F]> 126 | where 127 | F: Frame, 128 | T: ToFrameSlice<'a, F>, 129 | { 130 | slice.to_frame_slice() 131 | } 132 | 133 | /// Converts the given mutable slice into a mutable slice of `Frame`s. 134 | /// 135 | /// Returns `None` if the number of channels in a single frame `F` is not a multiple of the number 136 | /// of samples in the given slice. 137 | /// 138 | /// This is a convenience function that wraps the `ToFrameSliceMut` trait. 139 | /// 140 | /// # Examples 141 | /// 142 | /// ``` 143 | /// fn main() { 144 | /// let foo = &mut [0.0, 0.5, 0.0, -0.5][..]; 145 | /// let bar = dasp_slice::to_frame_slice_mut(foo); 146 | /// assert_eq!(bar, Some(&mut [[0.0, 0.5], [0.0, -0.5]][..])); 147 | /// 148 | /// let foo = &mut [0.0, 0.5, 0.0][..]; 149 | /// let bar = dasp_slice::to_frame_slice_mut(foo); 150 | /// assert_eq!(bar, None::<&mut [[f32; 2]]>); 151 | /// } 152 | /// ``` 153 | pub fn to_frame_slice_mut<'a, T, F>(slice: T) -> Option<&'a mut [F]> 154 | where 155 | F: Frame, 156 | T: ToFrameSliceMut<'a, F>, 157 | { 158 | slice.to_frame_slice_mut() 159 | } 160 | 161 | /// Converts the given slice of `Frame`s into some slice `T`. 162 | /// 163 | /// This is a convenience function that wraps the `FromFrameSlice` trait. 164 | /// 165 | /// # Examples 166 | /// 167 | /// ``` 168 | /// fn main() { 169 | /// let foo = &[[0.0, 0.5], [0.0, -0.5]][..]; 170 | /// let bar: &[f32] = dasp_slice::from_frame_slice(foo); 171 | /// assert_eq!(bar, &[0.0, 0.5, 0.0, -0.5][..]); 172 | /// } 173 | /// ``` 174 | pub fn from_frame_slice<'a, T, F>(slice: &'a [F]) -> T 175 | where 176 | F: Frame, 177 | T: FromFrameSlice<'a, F>, 178 | { 179 | T::from_frame_slice(slice) 180 | } 181 | 182 | /// Converts the given slice of mutable `Frame`s into some mutable slice `T`. 183 | /// 184 | /// This is a convenience function that wraps the `FromFrameSliceMut` trait. 185 | /// 186 | /// # Examples 187 | /// 188 | /// ``` 189 | /// fn main() { 190 | /// let foo = &mut [[0.0, 0.5], [0.0, -0.5]][..]; 191 | /// let bar: &mut [f32] = dasp_slice::from_frame_slice_mut(foo); 192 | /// assert_eq!(bar, &mut [0.0, 0.5, 0.0, -0.5][..]); 193 | /// } 194 | /// ``` 195 | pub fn from_frame_slice_mut<'a, T, F>(slice: &'a mut [F]) -> T 196 | where 197 | F: Frame, 198 | T: FromFrameSliceMut<'a, F>, 199 | { 200 | T::from_frame_slice_mut(slice) 201 | } 202 | -------------------------------------------------------------------------------- /dasp_slice/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! For working with slices of PCM audio data. 2 | //! 3 | //! Items related to conversion between slices of frames and slices of samples, particularly useful 4 | //! for working with interleaved data. 5 | //! 6 | //! ### Optional Features 7 | //! 8 | //! - The **boxed** feature (or **slice-boxed** feature if using `dasp`) provides a suite of boxed 9 | //! slice conversion traits and functions under the [**boxed**](./boxed/index.html) module. 10 | //! 11 | //! ### no_std 12 | //! 13 | //! If working in a `no_std` context, you can disable the default **std** feature with 14 | //! `--no-default-features`. 15 | 16 | #![cfg_attr(not(feature = "std"), no_std)] 17 | 18 | use dasp_frame::Frame; 19 | use dasp_sample::Sample; 20 | 21 | #[cfg(feature = "boxed")] 22 | pub use boxed::{ 23 | from_boxed_frame_slice, from_boxed_sample_slice, to_boxed_frame_slice, to_boxed_sample_slice, 24 | DuplexBoxedFrameSlice, DuplexBoxedSampleSlice, DuplexBoxedSlice, FromBoxedFrameSlice, 25 | FromBoxedSampleSlice, ToBoxedFrameSlice, ToBoxedSampleSlice, 26 | }; 27 | 28 | pub use frame::{ 29 | from_frame_slice, from_frame_slice_mut, to_frame_slice, to_frame_slice_mut, DuplexFrameSlice, 30 | DuplexFrameSliceMut, FromFrameSlice, FromFrameSliceMut, ToFrameSlice, ToFrameSliceMut, 31 | }; 32 | 33 | #[cfg(feature = "boxed")] 34 | pub mod boxed; 35 | 36 | mod frame; 37 | 38 | // Slice Conversion Traits 39 | // ---------------------------------------------------------------------------- 40 | 41 | /// For converting from a slice of `Sample`s to a slice of `Frame`s. 42 | pub trait FromSampleSlice<'a, S>: Sized 43 | where 44 | S: Sample, 45 | { 46 | fn from_sample_slice(slice: &'a [S]) -> Option; 47 | } 48 | 49 | /// For converting from a mutable slice of `Sample`s to a mutable slice of `Frame`s. 50 | pub trait FromSampleSliceMut<'a, S>: Sized 51 | where 52 | S: Sample, 53 | { 54 | fn from_sample_slice_mut(slice: &'a mut [S]) -> Option; 55 | } 56 | 57 | /// For converting from a slice of `Frame`s to a slice of `Sample`s. 58 | pub trait ToSampleSlice<'a, S> 59 | where 60 | S: Sample, 61 | { 62 | fn to_sample_slice(self) -> &'a [S]; 63 | } 64 | 65 | /// For converting from a mutable slice of `Frame`s to a mutable slice of `Sample`s. 66 | pub trait ToSampleSliceMut<'a, S> 67 | where 68 | S: Sample, 69 | { 70 | fn to_sample_slice_mut(self) -> &'a mut [S]; 71 | } 72 | 73 | /// For converting to and from a slice of `Sample`s. 74 | pub trait DuplexSampleSlice<'a, S>: FromSampleSlice<'a, S> + ToSampleSlice<'a, S> 75 | where 76 | S: Sample, 77 | { 78 | } 79 | 80 | /// For converting to and from a mutable slice of `Sample`s. 81 | pub trait DuplexSampleSliceMut<'a, S>: FromSampleSliceMut<'a, S> + ToSampleSliceMut<'a, S> 82 | where 83 | S: Sample, 84 | { 85 | } 86 | 87 | /// For converting to and from a slice of `Sample`s of type `S` and a slice of `Frame`s of type 88 | /// `F`. 89 | pub trait DuplexSlice<'a, S, F>: DuplexSampleSlice<'a, S> + DuplexFrameSlice<'a, F> 90 | where 91 | S: Sample, 92 | F: Frame, 93 | { 94 | } 95 | 96 | /// For converting to and from a mutable slice of `Sample`s of type `S` and a slice of `Frame`s of 97 | /// type `F`. 98 | pub trait DuplexSliceMut<'a, S, F>: 99 | DuplexSampleSliceMut<'a, S> + DuplexFrameSliceMut<'a, F> 100 | where 101 | S: Sample, 102 | F: Frame, 103 | { 104 | } 105 | 106 | // Slice Conversion Trait Implementations 107 | // ---------------------------------------------------------------------------- 108 | 109 | impl<'a, S> FromSampleSlice<'a, S> for &'a [S] 110 | where 111 | S: Sample, 112 | { 113 | #[inline] 114 | fn from_sample_slice(slice: &'a [S]) -> Option { 115 | Some(slice) 116 | } 117 | } 118 | 119 | impl<'a, S> FromSampleSliceMut<'a, S> for &'a mut [S] 120 | where 121 | S: Sample, 122 | { 123 | #[inline] 124 | fn from_sample_slice_mut(slice: &'a mut [S]) -> Option { 125 | Some(slice) 126 | } 127 | } 128 | 129 | impl<'a, S> ToSampleSlice<'a, S> for &'a [S] 130 | where 131 | S: Sample, 132 | { 133 | #[inline] 134 | fn to_sample_slice(self) -> &'a [S] { 135 | self 136 | } 137 | } 138 | 139 | impl<'a, S> ToSampleSliceMut<'a, S> for &'a mut [S] 140 | where 141 | S: Sample, 142 | { 143 | #[inline] 144 | fn to_sample_slice_mut(self) -> &'a mut [S] { 145 | self 146 | } 147 | } 148 | 149 | impl<'a, S, T> DuplexSampleSlice<'a, S> for T 150 | where 151 | S: Sample, 152 | T: FromSampleSlice<'a, S> + ToSampleSlice<'a, S>, 153 | { 154 | } 155 | 156 | impl<'a, S, T> DuplexSampleSliceMut<'a, S> for T 157 | where 158 | S: Sample, 159 | T: FromSampleSliceMut<'a, S> + ToSampleSliceMut<'a, S>, 160 | { 161 | } 162 | 163 | impl<'a, S, F, T> DuplexSlice<'a, S, F> for T 164 | where 165 | S: Sample, 166 | F: Frame, 167 | T: DuplexSampleSlice<'a, S> + DuplexFrameSlice<'a, F>, 168 | { 169 | } 170 | 171 | impl<'a, S, F, T> DuplexSliceMut<'a, S, F> for T 172 | where 173 | S: Sample, 174 | F: Frame, 175 | T: DuplexSampleSliceMut<'a, S> + DuplexFrameSliceMut<'a, F>, 176 | { 177 | } 178 | 179 | // Conversion Functions 180 | // ---------------------------------------------------------------------------- 181 | 182 | /// Converts the given slice into a slice of `Sample`s. 183 | /// 184 | /// This is a convenience function that wraps the `ToSampleSlice` trait. 185 | /// 186 | /// # Examples 187 | /// 188 | /// ``` 189 | /// fn main() { 190 | /// let foo = &[[0.0, 0.5], [0.0, -0.5]][..]; 191 | /// let bar = dasp_slice::to_sample_slice(foo); 192 | /// assert_eq!(bar, &[0.0, 0.5, 0.0, -0.5][..]); 193 | /// } 194 | /// ``` 195 | pub fn to_sample_slice<'a, T, S>(slice: T) -> &'a [S] 196 | where 197 | S: Sample, 198 | T: ToSampleSlice<'a, S>, 199 | { 200 | slice.to_sample_slice() 201 | } 202 | 203 | /// Converts the given mutable slice of `Frame`s into a mutable slice of `Sample`s. 204 | /// 205 | /// This is a convenience function that wraps the `ToSampleSliceMut` trait. 206 | /// 207 | /// # Examples 208 | /// 209 | /// ``` 210 | /// fn main() { 211 | /// let foo = &mut [[0.0, 0.5], [0.0, -0.5]][..]; 212 | /// let bar = dasp_slice::to_sample_slice_mut(foo); 213 | /// assert_eq!(bar, &mut [0.0, 0.5, 0.0, -0.5][..]); 214 | /// } 215 | /// ``` 216 | pub fn to_sample_slice_mut<'a, T, S>(slice: T) -> &'a mut [S] 217 | where 218 | S: Sample, 219 | T: ToSampleSliceMut<'a, S>, 220 | { 221 | slice.to_sample_slice_mut() 222 | } 223 | 224 | /// Converts the given slice of `Sample`s into some slice `T`. 225 | /// 226 | /// Returns `None` if the number of channels in a single frame is not a multiple of the number of 227 | /// samples in the given slice. 228 | /// 229 | /// This is a convenience function that wraps the `FromSampleSlice` trait. 230 | /// 231 | /// # Examples 232 | /// 233 | /// ``` 234 | /// fn main() { 235 | /// let foo = &[0.0, 0.5, 0.0, -0.5][..]; 236 | /// let bar: Option<&_> = dasp_slice::from_sample_slice(foo); 237 | /// assert_eq!(bar, Some(&[[0.0, 0.5], [0.0, -0.5]][..])); 238 | /// } 239 | /// ``` 240 | pub fn from_sample_slice<'a, T, S>(slice: &'a [S]) -> Option 241 | where 242 | S: Sample, 243 | T: FromSampleSlice<'a, S>, 244 | { 245 | T::from_sample_slice(slice) 246 | } 247 | 248 | /// Converts the given mutable slice of `Sample`s into some mutable slice `T`. 249 | /// 250 | /// Returns `None` if the number of channels in a single frame is not a multiple of the number of 251 | /// samples in the given slice. 252 | /// 253 | /// This is a convenience function that wraps the `FromSampleSliceMut` trait. 254 | /// 255 | /// # Examples 256 | /// 257 | /// ``` 258 | /// fn main() { 259 | /// let foo = &mut [0.0, 0.5, 0.0, -0.5][..]; 260 | /// let bar: Option<&mut _> = dasp_slice::from_sample_slice_mut(foo); 261 | /// assert_eq!(bar, Some(&mut [[0.0, 0.5], [0.0, -0.5]][..])); 262 | /// } 263 | /// ``` 264 | pub fn from_sample_slice_mut<'a, T, S>(slice: &'a mut [S]) -> Option 265 | where 266 | S: Sample, 267 | T: FromSampleSliceMut<'a, S>, 268 | { 269 | T::from_sample_slice_mut(slice) 270 | } 271 | 272 | ///// Utility Functions 273 | 274 | /// Mutate every element in the slice with the given function. 275 | #[inline] 276 | pub fn map_in_place(a: &mut [F], mut map: M) 277 | where 278 | M: FnMut(F) -> F, 279 | F: Frame, 280 | { 281 | for f in a { 282 | *f = map(*f); 283 | } 284 | } 285 | 286 | /// Sets the slice of frames at the associated `Sample`'s equilibrium value. 287 | #[inline] 288 | pub fn equilibrium(a: &mut [F]) 289 | where 290 | F: Frame, 291 | { 292 | map_in_place(a, |_| F::EQUILIBRIUM) 293 | } 294 | 295 | /// Mutate every frame in slice `a` while reading from each frame in slice `b` in lock-step using 296 | /// the given function. 297 | /// 298 | /// **Panics** if the length of `b` is not equal to the length of `a`. 299 | #[inline] 300 | pub fn zip_map_in_place(a: &mut [FA], b: &[FB], zip_map: M) 301 | where 302 | FA: Frame, 303 | FB: Frame, 304 | M: FnMut(FA, FB) -> FA, 305 | { 306 | assert_eq!(a.len(), b.len()); 307 | 308 | // We've asserted that the lengths are equal so we don't need bounds checking. 309 | unsafe { 310 | zip_map_in_place_unchecked(a, b, zip_map); 311 | } 312 | } 313 | 314 | /// Writes every sample in slice `b` to slice `a`. 315 | /// 316 | /// **Panics** if the slice lengths differ. 317 | #[inline] 318 | pub fn write(a: &mut [F], b: &[F]) 319 | where 320 | F: Frame, 321 | { 322 | zip_map_in_place(a, b, |_, b| b); 323 | } 324 | 325 | /// Adds every sample in slice `b` to every sample in slice `a` respectively. 326 | #[inline] 327 | pub fn add_in_place(a: &mut [FA], b: &[FB]) 328 | where 329 | FA: Frame, 330 | FB: Frame::Signed, NumChannels = FA::NumChannels>, 331 | { 332 | zip_map_in_place(a, b, |a, b| a.add_amp(b)); 333 | } 334 | 335 | /// Scale the amplitude of each frame in `b` by `amp_per_channel` before summing it onto `a`. 336 | #[inline] 337 | pub fn add_in_place_with_amp_per_channel(a: &mut [FA], b: &[FB], amp_per_channel: A) 338 | where 339 | FA: Frame, 340 | FB: Frame::Signed, NumChannels = FA::NumChannels>, 341 | A: Frame::Float, NumChannels = FB::NumChannels>, 342 | { 343 | zip_map_in_place(a, b, |af, bf| af.add_amp(bf.mul_amp(amp_per_channel))); 344 | } 345 | 346 | /// Mutate every element in slice `a` while reading from each element from slice `b` in lock-step 347 | /// using the given function. 348 | /// 349 | /// This function does not check that the slices are the same length and will crash horrifically on 350 | /// index-out-of-bounds. 351 | #[inline] 352 | unsafe fn zip_map_in_place_unchecked(a: &mut [FA], b: &[FB], mut zip_map: M) 353 | where 354 | FA: Frame, 355 | FB: Frame, 356 | M: FnMut(FA, FB) -> FA, 357 | { 358 | for i in 0..a.len() { 359 | *a.get_unchecked_mut(i) = zip_map(*a.get_unchecked(i), *b.get_unchecked(i)); 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /dasp_slice/tests/slice.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_add_slice() { 3 | let mut a = [[-0.5]; 32]; 4 | let b = [[0.5]; 32]; 5 | dasp_slice::add_in_place(&mut a, &b); 6 | assert_eq!([[0.0]; 32], a); 7 | } 8 | 9 | #[test] 10 | #[should_panic] 11 | fn test_add_slice_panic() { 12 | let mut a = [[0.5]; 31]; 13 | let b = [[0.5]; 32]; 14 | dasp_slice::add_in_place(&mut a, &b); 15 | } 16 | 17 | #[test] 18 | fn test_write_slice() { 19 | let mut a = [[0.0]; 32]; 20 | let b = [[1.0]; 32]; 21 | dasp_slice::write(&mut a, &b); 22 | assert_eq!([[1.0]; 32], a); 23 | } 24 | 25 | #[test] 26 | #[should_panic] 27 | fn test_write_slice_panic() { 28 | let mut a = [[0.0]; 31]; 29 | let b = [[1.0]; 32]; 30 | dasp_slice::write(&mut a, &b); 31 | } 32 | 33 | #[test] 34 | fn test_add_slice_with_amp_per_channel() { 35 | let mut a = [[0.5]; 32]; 36 | let b = [[1.0]; 32]; 37 | let amp = [0.5]; 38 | dasp_slice::add_in_place_with_amp_per_channel(&mut a, &b, amp); 39 | assert_eq!([[1.0]; 32], a); 40 | } 41 | 42 | #[test] 43 | #[should_panic] 44 | fn test_add_slice_with_amp_per_channel_panic() { 45 | let mut a = [[0.5]; 31]; 46 | let b = [[1.0]; 32]; 47 | let amp = [0.5]; 48 | dasp_slice::add_in_place_with_amp_per_channel(&mut a, &b, amp); 49 | } 50 | -------------------------------------------------------------------------------- /dasp_window/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dasp_window" 3 | description = "Windowing function abstractions (e.g. hann, rectangle) for audio PCM DSP." 4 | version = "0.11.0" 5 | authors = ["mitchmindtree "] 6 | readme = "../README.md" 7 | keywords = ["dsp", "window", "hann", "pcm", "audio"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/rustaudio/dasp.git" 10 | homepage = "https://github.com/rustaudio/dasp" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | dasp_sample = { version = "0.11", path = "../dasp_sample", default-features = false } 15 | 16 | [features] 17 | default = ["std"] 18 | all = ["std", "all-no-std"] 19 | all-no-std = [ 20 | "hann", 21 | "rectangle", 22 | ] 23 | std = [ 24 | "dasp_sample/std", 25 | ] 26 | hann = [] 27 | rectangle = [] 28 | 29 | [package.metadata.docs.rs] 30 | all-features = true 31 | -------------------------------------------------------------------------------- /dasp_window/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /dasp_window/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /dasp_window/src/hann/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::Window; 2 | use dasp_sample::Sample; 3 | use ops::f64::cos; 4 | 5 | mod ops; 6 | 7 | /// A type of window function, also known as the "raised cosine window". 8 | /// 9 | /// [Wiki entry](https://en.wikipedia.org/wiki/Hann_function). 10 | /// 11 | /// ### Required Features 12 | /// 13 | /// - When using `dasp_window`, this item requires the **hann** feature to be enabled. 14 | /// - When using `dasp`, this item requires the **window-hann** feature to be enabled. 15 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 16 | pub struct Hann; 17 | 18 | impl Window for Hann 19 | where 20 | S: Sample, 21 | { 22 | type Output = S; 23 | fn window(phase: S) -> Self::Output { 24 | const PI_2: f64 = core::f64::consts::PI * 2.0; 25 | let v = phase.to_float_sample().to_sample::() * PI_2; 26 | (0.5 * (1.0 - cos(v))) 27 | .to_sample::() 28 | .to_sample::() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /dasp_window/src/hann/ops.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | pub mod f64 { 4 | #[cfg(not(feature = "std"))] 5 | pub fn cos(x: f64) -> f64 { 6 | unsafe { core::intrinsics::cosf64(x) } 7 | } 8 | #[cfg(feature = "std")] 9 | pub fn cos(x: f64) -> f64 { 10 | x.cos() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dasp_window/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Module for windowing over a batch of Frames. Includes default Hann and Rectangle window 2 | //! types. 3 | //! 4 | //! ### Optional Features 5 | //! 6 | //! - The **hann** feature (or **window-hann** feature if using `dasp`) provides the 7 | //! [**Hann**](./struct.Hann.html) window function implementation. 8 | //! - The **rectangle** feature (or **window-rectangle** feature if using `dasp`) provides the 9 | //! [**Rectangle**](./struct.Rectangle.html) window function implementation. 10 | //! 11 | //! ### no_std 12 | //! 13 | //! If working in a `no_std` context, you can disable the default **std** feature with 14 | //! `--no-default-features`. 15 | //! 16 | //! To enable all of the above features in a `no_std` context, enable the **all-no-std** feature. 17 | 18 | #![cfg_attr(not(feature = "std"), no_std)] 19 | #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] 20 | 21 | #[cfg(feature = "hann")] 22 | pub use hann::Hann; 23 | #[cfg(feature = "rectangle")] 24 | pub use rectangle::Rectangle; 25 | 26 | #[cfg(feature = "hann")] 27 | mod hann; 28 | #[cfg(feature = "rectangle")] 29 | mod rectangle; 30 | 31 | /// An abstraction supporting different types of `Window` functions. 32 | /// 33 | /// The type `S` represents the phase of the window, while the `Output` represents the window 34 | /// amplitude. 35 | pub trait Window { 36 | /// The type used to represent the window amplitude. 37 | type Output; 38 | /// Returns the amplitude for the given phase, given as some `Sample` type. 39 | fn window(phase: S) -> Self::Output; 40 | } 41 | -------------------------------------------------------------------------------- /dasp_window/src/rectangle.rs: -------------------------------------------------------------------------------- 1 | use crate::Window; 2 | 3 | use dasp_sample::Sample; 4 | 5 | /// The simplest window type, equivalent to replacing all but *N* values of data sequence by 6 | /// zeroes, making it appear as though the waveform suddenly turns on and off. 7 | /// 8 | /// ### Required Features 9 | /// 10 | /// - When using `dasp_window`, this item requires the **rectangle** feature to be enabled. 11 | /// - When using `dasp`, this item requires the **window-rectangle** feature to be enabled. 12 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 13 | pub struct Rectangle; 14 | 15 | impl Window for Rectangle 16 | where 17 | S: Sample, 18 | { 19 | type Output = S; 20 | fn window(_phase: S) -> Self::Output { 21 | S::IDENTITY.to_sample::() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples" 3 | version = "0.1.0" 4 | authors = ["mitchmindtree "] 5 | edition = "2018" 6 | 7 | [dev-dependencies] 8 | anyhow = "1" 9 | cpal = "0.12" 10 | dasp = { version = "0.11", path = "../dasp", features = ["all"] } 11 | find_folder = "0.3" 12 | hound = "3" 13 | 14 | [[example]] 15 | name = "play_wav" 16 | path = "play_wav.rs" 17 | [[example]] 18 | name = "resample" 19 | path = "resample.rs" 20 | [[example]] 21 | name = "synth" 22 | path = "synth.rs" 23 | [[example]] 24 | name = "test" 25 | path = "test.rs" 26 | -------------------------------------------------------------------------------- /examples/play_wav.rs: -------------------------------------------------------------------------------- 1 | use cpal; 2 | use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; 3 | use dasp::signal::{self, Signal}; 4 | use dasp::slice::ToFrameSliceMut; 5 | 6 | fn main() -> Result<(), anyhow::Error> { 7 | // Find and load the wav. 8 | let assets = find_folder::Search::ParentsThenKids(5, 5) 9 | .for_folder("assets") 10 | .unwrap(); 11 | let reader = hound::WavReader::open(assets.join("thumbpiano A#3.wav")).unwrap(); 12 | let spec = reader.spec(); 13 | 14 | // Read the interleaved samples and convert them to a signal. 15 | let samples = reader.into_samples::().filter_map(Result::ok); 16 | let mut frames = signal::from_interleaved_samples_iter(samples).until_exhausted(); 17 | 18 | // Initialise CPAL. 19 | let host = cpal::default_host(); 20 | let device = host 21 | .default_output_device() 22 | .expect("failed to find a default output device"); 23 | 24 | // Create a stream config to match the wave format. 25 | // 26 | // NOTE: It's possible that the platform will not support the sample format, sample rate or 27 | // channel layout of the WAV file. In these cases, you may need to convert the data read from 28 | // the WAV file to a format compatible with one of the platform's supported stream 29 | // configurations. 30 | let config = cpal::StreamConfig { 31 | channels: spec.channels, 32 | sample_rate: cpal::SampleRate(spec.sample_rate), 33 | buffer_size: cpal::BufferSize::Default, 34 | }; 35 | 36 | // A channel for indicating when playback has completed. 37 | let (complete_tx, complete_rx) = std::sync::mpsc::sync_channel(1); 38 | 39 | // Create and run the CPAL stream. 40 | let err_fn = |err| eprintln!("an error occurred on stream: {}", err); 41 | let data_fn = move |data: &mut [i16], _info: &cpal::OutputCallbackInfo| { 42 | let buffer: &mut [[i16; 2]] = data.to_frame_slice_mut().unwrap(); 43 | for out_frame in buffer { 44 | match frames.next() { 45 | Some(frame) => *out_frame = frame, 46 | None => { 47 | complete_tx.try_send(()).ok(); 48 | *out_frame = dasp::Frame::EQUILIBRIUM; 49 | } 50 | } 51 | } 52 | }; 53 | let stream = device.build_output_stream(&config, data_fn, err_fn)?; 54 | stream.play().unwrap(); 55 | 56 | // Block until playback completes. 57 | complete_rx.recv().unwrap(); 58 | stream.pause().ok(); 59 | Ok(()) 60 | } 61 | -------------------------------------------------------------------------------- /examples/resample.rs: -------------------------------------------------------------------------------- 1 | // An example of using `sample` to efficiently perform decent quality sample rate conversion on a 2 | // WAV file entirely on the stack. 3 | 4 | use dasp::{interpolate::sinc::Sinc, ring_buffer, signal, Sample, Signal}; 5 | use hound::{WavReader, WavWriter}; 6 | 7 | fn main() { 8 | // Find and load the wav. 9 | let assets = find_folder::Search::ParentsThenKids(5, 5) 10 | .for_folder("assets") 11 | .unwrap(); 12 | let reader = WavReader::open(assets.join("two_vowels.wav")).unwrap(); 13 | 14 | // Get the wav spec and create a target with the new desired sample rate. 15 | let spec = reader.spec(); 16 | let mut target = spec; 17 | target.sample_rate = 10_000; 18 | 19 | // Read the interleaved samples and convert them to a signal. 20 | let samples = reader 21 | .into_samples() 22 | .filter_map(Result::ok) 23 | .map(i16::to_sample::); 24 | let signal = signal::from_interleaved_samples_iter(samples); 25 | 26 | // Convert the signal's sample rate using `Sinc` interpolation. 27 | let ring_buffer = ring_buffer::Fixed::from([[0.0]; 100]); 28 | let sinc = Sinc::new(ring_buffer); 29 | let new_signal = signal.from_hz_to_hz(sinc, spec.sample_rate as f64, target.sample_rate as f64); 30 | 31 | // Write the result to a new file. 32 | let mut writer = WavWriter::create(assets.join("two_vowels_10k.wav"), target).unwrap(); 33 | for frame in new_signal.until_exhausted() { 34 | writer.write_sample(frame[0].to_sample::()).unwrap(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/synth.rs: -------------------------------------------------------------------------------- 1 | use cpal; 2 | use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; 3 | use dasp::{signal, Sample, Signal}; 4 | use std::sync::mpsc; 5 | 6 | fn main() -> Result<(), anyhow::Error> { 7 | let host = cpal::default_host(); 8 | let device = host 9 | .default_output_device() 10 | .expect("failed to find a default output device"); 11 | let config = device.default_output_config()?; 12 | 13 | match config.sample_format() { 14 | cpal::SampleFormat::F32 => run::(&device, &config.into())?, 15 | cpal::SampleFormat::I16 => run::(&device, &config.into())?, 16 | cpal::SampleFormat::U16 => run::(&device, &config.into())?, 17 | } 18 | 19 | Ok(()) 20 | } 21 | 22 | fn run(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error> 23 | where 24 | T: cpal::Sample, 25 | { 26 | // Create a signal chain to play back 1 second of each oscillator at A4. 27 | let hz = signal::rate(config.sample_rate.0 as f64).const_hz(440.0); 28 | let one_sec = config.sample_rate.0 as usize; 29 | let mut synth = hz 30 | .clone() 31 | .sine() 32 | .take(one_sec) 33 | .chain(hz.clone().saw().take(one_sec)) 34 | .chain(hz.clone().square().take(one_sec)) 35 | .chain(hz.clone().noise_simplex().take(one_sec)) 36 | .chain(signal::noise(0).take(one_sec)) 37 | .map(|s| s.to_sample::() * 0.2); 38 | 39 | // A channel for indicating when playback has completed. 40 | let (complete_tx, complete_rx) = mpsc::sync_channel(1); 41 | 42 | // Create and run the stream. 43 | let err_fn = |err| eprintln!("an error occurred on stream: {}", err); 44 | let channels = config.channels as usize; 45 | let stream = device.build_output_stream( 46 | config, 47 | move |data: &mut [T], _: &cpal::OutputCallbackInfo| { 48 | write_data(data, channels, &complete_tx, &mut synth) 49 | }, 50 | err_fn, 51 | )?; 52 | stream.play()?; 53 | 54 | // Wait for playback to complete. 55 | complete_rx.recv().unwrap(); 56 | stream.pause()?; 57 | 58 | Ok(()) 59 | } 60 | 61 | fn write_data( 62 | output: &mut [T], 63 | channels: usize, 64 | complete_tx: &mpsc::SyncSender<()>, 65 | signal: &mut dyn Iterator, 66 | ) where 67 | T: cpal::Sample, 68 | { 69 | for frame in output.chunks_mut(channels) { 70 | let sample = match signal.next() { 71 | None => { 72 | complete_tx.try_send(()).ok(); 73 | 0.0 74 | } 75 | Some(sample) => sample, 76 | }; 77 | let value: T = cpal::Sample::from::(&sample); 78 | for sample in frame.iter_mut() { 79 | *sample = value; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /examples/test.rs: -------------------------------------------------------------------------------- 1 | //! A short example that converts an f64 sine wave to a few of the sample types available within 2 | //! the `Sample` crate, prints their values, and then converts them back to the original f64. 3 | 4 | use dasp::Sample; 5 | 6 | /// An iterator that continually steps forward the phase for a signal by `0.03`. 7 | struct Iter { 8 | value: f64, 9 | } 10 | 11 | impl Iterator for Iter { 12 | type Item = f64; 13 | fn next(&mut self) -> Option { 14 | self.value += 0.03; 15 | Some(self.value) 16 | } 17 | } 18 | 19 | fn main() { 20 | for phase in (Iter { value: 0.0 }).take(50) { 21 | // Return a sine wave for the given phase. 22 | fn sine_wave(phase: f64) -> f64 { 23 | use std::f64::consts::PI; 24 | (phase * PI * 2.0).sin() 25 | } 26 | 27 | let wave = sine_wave(phase); 28 | 29 | println!("Wave {}", wave); 30 | 31 | let sample_f32 = f32::from_sample(wave); 32 | let sample_u8 = u8::from_sample(wave); 33 | let sample_u16 = u16::from_sample(wave); 34 | let sample_u32 = u32::from_sample(wave); 35 | let sample_i8 = i8::from_sample(wave); 36 | let sample_i16 = i16::from_sample(wave); 37 | let sample_i32 = i32::from_sample(wave); 38 | 39 | println!( 40 | "\tFrom wave to sample -> f32: {:<10} u8: {:<10} u16: {:<10} u32: {:<10} i8: {:<10} i16: {:<10} i32: {:<10}", 41 | sample_f32, 42 | sample_u8, 43 | sample_u16, 44 | sample_u32, 45 | sample_i8, 46 | sample_i16, 47 | sample_i32 48 | ); 49 | 50 | let wave_f32 = sample_f32.to_sample::(); 51 | let wave_u8 = sample_u8.to_sample::(); 52 | let wave_u16 = sample_u16.to_sample::(); 53 | let wave_u32 = sample_u32.to_sample::(); 54 | let wave_i8 = sample_i8.to_sample::(); 55 | let wave_i16 = sample_i16.to_sample::(); 56 | let wave_i32 = sample_i32.to_sample::(); 57 | 58 | println!( 59 | "\tFrom sample to wave -> f32: {:<10} u8: {:<10} u16: {:<10} u32: {:<10} i8: {:<10} i16: {:<10} i32: {:<10}\n", 60 | wave_f32, 61 | wave_u8, 62 | wave_u16, 63 | wave_u32, 64 | wave_i8, 65 | wave_i16, 66 | wave_i32 67 | ); 68 | 69 | // Check that the conversion back to wave is the same as the original wave (with some 70 | // headroom for resolution/rounding error). 71 | let headroom = 0.05; 72 | assert!((wave_f32 - wave).abs() < headroom, "{}", wave_f32); 73 | assert!((wave_u8 - wave).abs() < headroom, "{}", wave_u8); 74 | assert!((wave_u16 - wave).abs() < headroom, "{}", wave_u16); 75 | assert!((wave_u32 - wave).abs() < headroom, "{}", wave_u32); 76 | assert!((wave_i8 - wave).abs() < headroom, "{}", wave_i8); 77 | assert!((wave_i16 - wave).abs() < headroom, "{}", wave_i16); 78 | assert!((wave_i32 - wave).abs() < headroom, "{}", wave_i32); 79 | } 80 | } 81 | --------------------------------------------------------------------------------