Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

1
vendor/rodio/.cargo-checksum.json vendored Normal file

File diff suppressed because one or more lines are too long

170
vendor/rodio/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,170 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
# Version 0.20.1 (2024-11-08)
### Fixed
- Builds without the `symphonia` feature did not compile
# Version 0.20.0 (2024-11-08)
### Added
- Support for *ALAC/AIFF*
- Add `automatic_gain_control` source for dynamic audio level adjustment.
- New test signal generator sources:
- `SignalGenerator` source generates a sine, triangle, square wave or sawtooth
of a given frequency and sample rate.
- `Chirp` source generates a sine wave with a linearly-increasing
frequency over a given frequency range and duration.
- `white` and `pink` generate white or pink noise, respectively. These
sources depend on the `rand` crate and are guarded with the "noise"
feature.
- Documentation for the "noise" feature has been added to `lib.rs`.
- New Fade and Crossfade sources:
- `fade_out` fades an input out using a linear gain fade.
- `linear_gain_ramp` applies a linear gain change to a sound over a
given duration. `fade_out` is implemented as a `linear_gain_ramp` and
`fade_in` has been refactored to use the `linear_gain_ramp`
implementation.
### Fixed
- `Sink.try_seek` now updates `controls.position` before returning. Calls to `Sink.get_pos`
done immediately after a seek will now return the correct value.
### Changed
- `SamplesBuffer` is now `Clone`
# Version 0.19.0 (2024-06-29)
### Added
- Adds a new source `track_position`. It keeps track of duration since the
beginning of the underlying source.
### Fixed
- Mp4a with decodable tracks after undecodable tracks now play. This matches
VLC's behaviour.
# Version 0.18.1 (2024-05-23)
### Fixed
- Seek no longer hangs if the sink is empty.
# Version 0.18.0 (2024-05-05)
### Changed
- `Source` trait is now also implemented for `Box<dyn Source>` and `&mut Source`
- `fn new_vorbis` is now also available when the `symphonia-vorbis` feature is enabled
### Added
- Adds a new method `try_seek` to all sources. It returns either an error or
seeks to the given position. A few sources are "unsupported" they return the
error `Unsupported`.
- Adds `SpatialSink::clear()` bringing it in line with `Sink`
### Fixed
- channel upscaling now follows the 'WAVEFORMATEXTENSIBLE' format and no longer
repeats the last source channel on all extra output channels.
Stereo content playing on a 5.1 speaker set will now only use the front left
and front right speaker instead of repeating the right sample on all speakers
except the front left one.
- `mp3::is_mp3()` no longer changes the position in the stream when the stream
is mp3
# Version 0.17.3 (2023-10-23)
- Build fix for `minimp3` backend.
# Version 0.17.2 (2023-10-17)
- Add `EmptyCallback` source.
- Fix index out of bounds bug.
- Use non-vulnerable `minimp3` fork.
- Add filter functions with additional q parameter.
# Version 0.17.1 (2023-02-25)
- Disable `symphonia`'s default features.
# Version 0.17.0 (2023-02-17)
- Update `cpal` to [0.15](https://github.com/RustAudio/cpal/blob/master/CHANGELOG.md#version-0150-2022-01-29).
- Default to `symphonia` for mp3 decoding.
# Version 0.16.0 (2022-09-14)
- Update `cpal` to [0.14](https://github.com/RustAudio/cpal/blob/master/CHANGELOG.md#version-0140-2022-08-22).
- Update `symphonia` to [0.5](https://github.com/pdeljanov/Symphonia/releases/tag/v0.5.1).
# Version 0.15.0 (2022-01-23)
- Remove requirement that the argument `Decoder::new` and `LoopedDecoder::new` implement `Send`.
- Add optional symphonia backend.
- `WavDecoder`'s `total_duration` now returns the total duration of the sound rather than the remaining duration.
- Add 32-bit signed in WAV decoding.
- `SineWave::new()` now takes a `f32` instead of a `u32`.
- Add `len()` method to `SpatialSink`.
# Version 0.14.0 (2021-05-21)
- Re-export `cpal` in full.
- Replace panics when calling `OutputStream::try_default`, `OutputStream::try_from_device` with new
`StreamError` variants.
- `OutputStream::try_default` will now fallback to non-default output devices if an `OutputStream`
cannot be created from the default device.
# Version 0.13.1 (2021-03-28)
- Fix panic when no `pulseaudio-alsa` was installed.
# Version 0.13.0 (2020-11-03)
- Update `cpal` to [0.13](https://github.com/RustAudio/cpal/blob/master/CHANGELOG.md#version-0130-2020-10-28).
- Add Android support.
# Version 0.12.0 (2020-10-05)
- Breaking: Update `cpal` to [0.12](https://github.com/RustAudio/cpal/blob/master/CHANGELOG.md#version-0120-2020-07-09).
- Breaking: Rework API removing global "rodio audio processing" thread & adapting to the upstream cpal API changes.
- Add new_X format specific methods to Decoder.
- Fix resampler dependency on internal `Vec::capacity` behaviour.
# Version 0.11.0 (2020-03-16)
- Update `lewton` to [0.10](https://github.com/RustAudio/lewton/blob/master/CHANGELOG.md#release-0100---january-30-2020).
- Breaking: Update `cpal` to [0.11](https://github.com/RustAudio/cpal/blob/master/CHANGELOG.md#version-0110-2019-12-11)
# Version 0.10.0 (2019-11-16)
- Removal of nalgebra in favour of own code.
- Fix a bug that switched channels when resuming after having paused.
- Attempt all supported output formats if the default format fails in `Sink::new`.
- Breaking: Update `cpal` to [0.10](https://github.com/RustAudio/cpal/blob/master/CHANGELOG.md#version-0100-2019-07-05).
# Version 0.9.0 (2019-06-08)
- Remove exclusive `&mut` borrow requirements in `Sink` & `SpatialSink` setters.
- Use `nalgebra` instead of `cgmath` for `Spatial` source.
# Version 0.8.1 (2018-09-18)
- Update `lewton` dependency to [0.9](https://github.com/RustAudio/lewton/blob/master/CHANGELOG.md#release-090---august-16-2018)
- Change license from `Apache-2.0` only to `Apache-2.0 OR MIT`
# Version 0.8.0 (2018-06-22)
- Add mp3 decoding capabilities via `minimp3`
# Version 0.7.0 (2018-04-19)
- Update `cpal` dependency to 0.8, and adopt the new naming convention
- BREAKING CHANGES:
- renamed `Endpoint` to `Device`
- split `default_endpoint()` into `default_output_device()` and `default_input_device()`
- renamed `endpoints()` to `devices()`
- introduced `output_devices()` and `input_devices()`

141
vendor/rodio/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,141 @@
# Rodio Development Guide
## Quick Start
1. Clone the repository: `git clone https://github.com/RustAudio/rodio.git`
2. Navigate to the project: `cd rodio`
3. Build the project: `cargo build`
## Project Structure
src/:
- `source/`: Audio source implementations
- `decoder/`: Audio format decoders
- `sink/`: Audio playback and mixing
- `dynamic_mixer/`: Real-time audio mixing
- `spatial/`: Spatial audio capabilities
## Coding Guidelines
- Follow [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/)
- Use `rustfmt` for formatting
- Implement `Source` trait for new audio sources
- Use `Sink` for playback management
## Common Tasks
### Adding a New Audio Source or Effect
1. Create a new file in `src/source/`
2. Implement the `Source` trait to define how audio samples are generated or modified
3. Consider implementing sources like oscillators, noise generators, or effects like amplification, filtering, or distortion
4. If your contribution creates sound you should give it a public (factory) function that constructs it. If its an effect then add a method with default implementation for it in the `Source` trait.
5. Begin with a test for your new feature (see the [Testing](#testing)). This approach ensures your PR is ready and simplifies development. Don't worry about optimizing initially; focus on functionality.
6. Once your feature works, celebrate your progress! 🎉 Open a draft PR at this stage - we're here to assist with refactoring and optimization.
7. Refactor your code, add benchmarks, and work on improving performance, especially for real-time processing in effects. Refer to the [Rust Performance Book](https://nnethercote.github.io/perf-book/introduction.html) for optimization techniques.
8. Finally add some documentation and an example. For details see [Documentation]($documentation)
9. If you're unsure about creating tests, implement your feature first, then open a PR with what you have asking for guidance. We're happy to help!
### Implementing a New Decoder
1. Add new module in `src/decoder/`
2. Implement necessary traits (e.g., `Decoder`) to handle specific audio formats
3. Focus on efficiently parsing audio file headers and decoding compressed audio data
4. Update `src/decoder/mod.rs` to integrate the new decoder
### Unit Tests
- Feel free to write temporary unit tests during development if they help you verify functionality
- These tests can be rough and don't need to be comprehensive - they're just development aids
- It's okay to include these temporary unit tests in your pull request
- We'll remove these tests before merging into the main codebase, primarily because:
- They can make refactoring more difficult as tests may break with code changes
- Rust's robust type system reduces the need for extensive unit testing compared to dynamically typed languages
### Integration Tests
When possible, add integration tests for your new features. Keep in mind:
- Integration tests do not create sound through your speakers. Instead, you write code that examines the produced *samples*.
- For new audio sources:
- Verify that samples have changed from their initial state.
- Check if samples are non-zero where appropriate.
- Look for expected patterns or characteristics in the audio data.
- Examples of integration tests:
- The `tests/wav_test.rs` test simply checks if the decoder produces nonzero samples.
- The test `seek_does_not_break_channel_order` in `tests/seek.rs` uses a beeping sound that alternates between two channels. It seeks to a point where we know only the second channel should make sound. Then we check if the first channel is silent while the second is not.
- Be aware that many aspects of audio processing are challenging to verify automatically.
- It's impossible to write a test that checks if something sounds "good". For features requiring audible verification:
- Create an example in the `examples/` directory that demonstrates the functionality.
- These examples can produce sound for manual testing.
- Document the expected behavior in the example's comments.
- We love integration tests but they can be hard to write. If you have trouble adding one its fine to leave it out. If you do add one create a new file for it in `tests/`.
- Run tests: `cargo test`
- Run examples: `cargo run --example <example_name>`
## Documentation
- Add inline documentation to all public items.
- Look at the [documenting components](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html#documenting-components) section of the rustdoc book.
- Generate docs: `cargo doc --open`
- Add an example. That could be as part of the inline documentation or a more complex scenario in `examples/`
## Contribution Workflow
1. Fork the repository on GitHub
2. Clone your fork locally: `git clone https://github.com/YOUR_USERNAME/rodio.git`
3. Create a feature branch: `git checkout -b feature/your-feature-name`
4. Make changes and add tests where applicable (Dont be afraid to ask for help)
5. Commit your changes following these guidelines: (`git commit`)
- Write clear, concise commit messages
- Limit the first line to 50 characters
- Provide a detailed description after a blank line, if necessary
- Reference relevant issue numbers (e.g., "Fixes #123")
- Separate logical changes into multiple commits
- Avoid commits with unrelated changes
Example:
```
Add spatial audio support for stereo sources
- Implement SpatialSource struct
- Add panning and distance attenuation
- Update documentation for spatial audio usage
Fixes #456
```
6. Push your changes to your fork: `git push origin feature/your-feature-name`
7. Create a pull request on GitHub
## Getting Help / Got a question?
- Open an issue on GitHub
- Ask questions in your pull request
- Open an issue for guidance/questions
- Join the Rust Audio Discord
## Useful Commands
- Format: `cargo fmt` - Automatically formats the code according to Rust style guidelines
- Lint: `cargo clippy` - Runs the Clippy linter to catch common mistakes and improve code quality
- Benchmark: `cargo bench` - Executes performance benchmarks for the project
For more detailed information, refer to the full documentation and source code.
## Useful External Resources
- [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/)
- [Rustdoc book](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html)
- [Rust Performance Book](https://nnethercote.github.io/perf-book/introduction.html)
- [Rust Audio Discord](https://discord.com/invite/8qW6q2k)
## Disclaimer
Please note that the guidelines and practices outlined in this document
are not strict rules. They are general recommendations to promote
consistency and quality in contributions.
We understand that every situation is unique and encourage contributors
to use their best judgment. If you have any doubts or questions about
how to approach a particular task or contribution, don't hesitate to
reach out to the maintainers for guidance.

1665
vendor/rodio/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

216
vendor/rodio/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,216 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "rodio"
version = "0.20.1"
build = false
exclude = [
"assets/**",
"tests/**",
]
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Audio playback library"
documentation = "https://docs.rs/rodio"
readme = "README.md"
keywords = [
"audio",
"playback",
"gamedev",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/RustAudio/rodio"
[lib]
name = "rodio"
path = "src/lib.rs"
[[example]]
name = "automatic_gain_control"
path = "examples/automatic_gain_control.rs"
[[example]]
name = "basic"
path = "examples/basic.rs"
[[example]]
name = "mix_multiple_sources"
path = "examples/mix_multiple_sources.rs"
[[example]]
name = "music_flac"
path = "examples/music_flac.rs"
[[example]]
name = "music_m4a"
path = "examples/music_m4a.rs"
required-features = [
"symphonia-isomp4",
"symphonia-aac",
]
[[example]]
name = "music_mp3"
path = "examples/music_mp3.rs"
[[example]]
name = "music_ogg"
path = "examples/music_ogg.rs"
[[example]]
name = "music_wav"
path = "examples/music_wav.rs"
[[example]]
name = "noise_generator"
path = "examples/noise_generator.rs"
required-features = ["noise"]
[[example]]
name = "reverb"
path = "examples/reverb.rs"
[[example]]
name = "seek_mp3"
path = "examples/seek_mp3.rs"
[[example]]
name = "signal_generator"
path = "examples/signal_generator.rs"
[[example]]
name = "spatial"
path = "examples/spatial.rs"
[[example]]
name = "stereo"
path = "examples/stereo.rs"
[[bench]]
name = "conversions"
path = "benches/conversions.rs"
harness = false
[[bench]]
name = "effects"
path = "benches/effects.rs"
harness = false
[[bench]]
name = "shared"
path = "benches/shared.rs"
[dependencies.atomic_float]
version = "1.1.0"
optional = true
[dependencies.claxon]
version = "0.4.2"
optional = true
[dependencies.cpal]
version = "0.15.3"
[dependencies.crossbeam-channel]
version = "0.5.8"
optional = true
[dependencies.hound]
version = "3.3.1"
optional = true
[dependencies.lewton]
version = "0.10"
optional = true
[dependencies.minimp3_fixed]
version = "0.5.4"
optional = true
[dependencies.rand]
version = "0.8.5"
features = ["small_rng"]
optional = true
[dependencies.symphonia]
version = "0.5.4"
optional = true
default-features = false
[dependencies.tracing]
version = "0.1.40"
optional = true
[dev-dependencies.approx]
version = "0.5.1"
[dev-dependencies.dasp_sample]
version = "0.11.0"
[dev-dependencies.divan]
version = "0.1.14"
[dev-dependencies.quickcheck]
version = "0.9.2"
[dev-dependencies.rstest]
version = "0.18.2"
[dev-dependencies.rstest_reuse]
version = "0.6.0"
[features]
cpal-shared-stdcxx = ["cpal/oboe-shared-stdcxx"]
default = [
"flac",
"vorbis",
"wav",
"mp3",
]
experimental = ["dep:atomic_float"]
flac = ["claxon"]
minimp3 = ["dep:minimp3_fixed"]
mp3 = ["symphonia-mp3"]
noise = ["rand"]
symphonia-aac = ["symphonia/aac"]
symphonia-aiff = [
"symphonia/aiff",
"symphonia/pcm",
]
symphonia-alac = [
"symphonia/isomp4",
"symphonia/alac",
]
symphonia-all = [
"symphonia-aac",
"symphonia-flac",
"symphonia-isomp4",
"symphonia-mp3",
"symphonia-vorbis",
"symphonia-wav",
]
symphonia-flac = ["symphonia/flac"]
symphonia-isomp4 = ["symphonia/isomp4"]
symphonia-mp3 = ["symphonia/mp3"]
symphonia-vorbis = ["symphonia/vorbis"]
symphonia-wav = [
"symphonia/wav",
"symphonia/pcm",
"symphonia/adpcm",
]
tracing = ["dep:tracing"]
vorbis = ["lewton"]
wasm-bindgen = ["cpal/wasm-bindgen"]
wav = ["hound"]

201
vendor/rodio/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 Pierre Krieger
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

23
vendor/rodio/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

43
vendor/rodio/README.md vendored Normal file
View File

@@ -0,0 +1,43 @@
# Audio playback library
[![Crates.io Version](https://img.shields.io/crates/v/rodio.svg)](https://crates.io/crates/rodio)
[![Crates.io Downloads](https://img.shields.io/crates/d/rodio.svg)](https://crates.io/crates/rodio)
[![Build Status](https://github.com/RustAudio/rodio/workflows/CI/badge.svg)](https://github.com/RustAudio/rodio/actions)
Rust playback library.
Playback is handled by [cpal](https://github.com/RustAudio/cpal). Format decoding can be handled either by [Symphonia](https://github.com/pdeljanov/Symphonia), or by format-specific decoders:
- MP3 by [minimp3](https://github.com/lieff/minimp3) (but defaults to [Symphonia](https://github.com/pdeljanov/Symphonia)).
- WAV by [hound](https://github.com/ruud-v-a/hound).
- Vorbis by [lewton](https://github.com/est31/lewton).
- FLAC by [claxon](https://github.com/ruuda/claxon).
- MP4 and AAC (both disabled by default) are handled only by [Symphonia](https://github.com/pdeljanov/Symphonia).
See [the docs](https://docs.rs/rodio/latest/rodio/#alternative-decoder-backends) for more details on backends.
# [Documentation](http://docs.rs/rodio)
[The documentation](http://docs.rs/rodio) contains an introduction to the library.
## Dependencies(Linux only)
Rodio uses `cpal` to send audio to the OS for playback. On Linux `cpal` needs the ALSA development files. These are provided as part of the libasound2-dev package on Debian and Ubuntu distributions and alsa-lib-devel on Fedora.
# Contributing
For information on how to contribute to this project, please see our [Contributing Guide](https://github.com/RustAudio/rodio/CONTRIBUTING.md).
## License
[License]: #license
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0), or
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### License of your contributions
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

21
vendor/rodio/benches/conversions.rs vendored Normal file
View File

@@ -0,0 +1,21 @@
use cpal::FromSample;
use divan::Bencher;
use rodio::Source;
mod shared;
use shared::TestSource;
fn main() {
divan::main();
}
#[divan::bench(types = [i16, u16, f32])]
fn from_i16_to<T: rodio::Sample + FromSample<i16>>(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav())
.bench_values(|source| {
source
.convert_samples::<T>()
.for_each(divan::black_box_drop)
})
}

87
vendor/rodio/benches/effects.rs vendored Normal file
View File

@@ -0,0 +1,87 @@
use std::time::Duration;
use divan::Bencher;
use rodio::Source;
mod shared;
use shared::TestSource;
fn main() {
divan::main();
}
#[divan::bench]
fn reverb(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav())
.bench_values(|source| {
source
.buffered()
.reverb(Duration::from_secs_f32(0.05), 0.3)
.for_each(divan::black_box_drop)
})
}
#[divan::bench]
fn high_pass(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav().to_f32s())
.bench_values(|source| source.high_pass(200).for_each(divan::black_box_drop))
}
#[divan::bench]
fn fade_out(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav())
.bench_values(|source| {
source
.fade_out(Duration::from_secs(5))
.for_each(divan::black_box_drop)
})
}
#[divan::bench]
fn amplify(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav().to_f32s())
.bench_values(|source| source.amplify(0.8).for_each(divan::black_box_drop))
}
#[divan::bench]
fn agc_enabled(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav().to_f32s())
.bench_values(|source| {
source
.automatic_gain_control(
1.0, // target_level
4.0, // attack_time (in seconds)
0.005, // release_time (in seconds)
5.0, // absolute_max_gain
)
.for_each(divan::black_box_drop)
})
}
#[cfg(feature = "experimental")]
#[divan::bench]
fn agc_disabled(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav().to_f32s())
.bench_values(|source| {
// Create the AGC source
let amplified_source = source.automatic_gain_control(
1.0, // target_level
4.0, // attack_time (in seconds)
0.005, // release_time (in seconds)
5.0, // absolute_max_gain
);
// Get the control handle and disable AGC
let agc_control = amplified_source.get_agc_control();
agc_control.store(false, std::sync::atomic::Ordering::Relaxed);
// Process the audio stream with AGC disabled
amplified_source.for_each(divan::black_box_drop)
})
}

83
vendor/rodio/benches/shared.rs vendored Normal file
View File

@@ -0,0 +1,83 @@
use std::io::Cursor;
use std::time::Duration;
use std::vec;
use rodio::Source;
pub struct TestSource<T> {
samples: vec::IntoIter<T>,
channels: u16,
sample_rate: u32,
total_duration: Duration,
}
impl<T> Iterator for TestSource<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.samples.next()
}
}
impl<T> ExactSizeIterator for TestSource<T> {
fn len(&self) -> usize {
self.samples.len()
}
}
impl<T: rodio::Sample> Source for TestSource<T> {
fn current_frame_len(&self) -> Option<usize> {
None // forever
}
fn channels(&self) -> u16 {
self.channels
}
fn sample_rate(&self) -> u32 {
self.sample_rate
}
fn total_duration(&self) -> Option<Duration> {
Some(self.total_duration)
}
}
impl TestSource<i16> {
pub fn music_wav() -> Self {
let data = include_bytes!("../assets/music.wav");
let cursor = Cursor::new(data);
let duration = Duration::from_secs(10);
let sound = rodio::Decoder::new(cursor)
.expect("music.wav is correctly encoded & wav is supported")
.take_duration(duration);
TestSource {
channels: sound.channels(),
sample_rate: sound.sample_rate(),
total_duration: duration,
samples: sound.into_iter().collect::<Vec<_>>().into_iter(),
}
}
#[allow(unused, reason = "not everything from shared is used in all libs")]
pub fn to_f32s(self) -> TestSource<f32> {
let TestSource {
samples,
channels,
sample_rate,
total_duration,
} = self;
let samples = samples
.map(|s| cpal::Sample::from_sample(s))
.collect::<Vec<_>>()
.into_iter();
TestSource {
samples,
channels,
sample_rate,
total_duration,
}
}
}

View File

@@ -0,0 +1,46 @@
use rodio::source::Source;
use rodio::Decoder;
use std::fs::File;
use std::io::BufReader;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::try_new(&handle).unwrap();
// Decode the sound file into a source
let file = BufReader::new(File::open("assets/music.flac").unwrap());
let source = Decoder::new(file).unwrap();
// Apply automatic gain control to the source
let agc_source = source.automatic_gain_control(1.0, 4.0, 0.005, 5.0);
// Make it so that the source checks if automatic gain control should be
// enabled or disabled every 5 milliseconds. We must clone `agc_enabled`
// or we would lose it when we move it into the periodic access.
let agc_enabled = Arc::new(AtomicBool::new(true));
let agc_enabled_clone = agc_enabled.clone();
let controlled = agc_source.periodic_access(Duration::from_millis(5), move |agc_source| {
agc_source.set_enabled(agc_enabled_clone.load(Ordering::Relaxed));
});
// Add the source now equipped with automatic gain control and controlled via
// periodic_access to the sink for playback
sink.append(controlled);
// after 5 seconds of playback disable automatic gain control using the
// shared AtomicBool `agc_enabled`. You could do this from another part
// of the program since `agc_enabled` is of type Arc<AtomicBool> which
// is freely clone-able and move-able.
//
// Note that disabling the AGC takes up to 5 millis because periodic_access
// controls the source every 5 millis.
thread::sleep(Duration::from_secs(5));
agc_enabled.store(false, Ordering::Relaxed);
// Keep the program running until playback is complete
sink.sleep_until_end();
}

36
vendor/rodio/examples/basic.rs vendored Normal file
View File

@@ -0,0 +1,36 @@
use std::io::BufReader;
use std::thread;
use std::time::Duration;
fn main() {
let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
let file = std::fs::File::open("assets/beep.wav").unwrap();
let beep1 = stream_handle.play_once(BufReader::new(file)).unwrap();
beep1.set_volume(0.2);
println!("Started beep1");
thread::sleep(Duration::from_millis(1500));
let file = std::fs::File::open("assets/beep2.wav").unwrap();
let beep2 = stream_handle.play_once(BufReader::new(file)).unwrap();
beep2.set_volume(0.3);
beep2.detach();
println!("Started beep2");
thread::sleep(Duration::from_millis(1500));
let file = std::fs::File::open("assets/beep3.ogg").unwrap();
let beep3 = stream_handle.play_once(file).unwrap();
beep3.set_volume(0.2);
println!("Started beep3");
thread::sleep(Duration::from_millis(1500));
drop(beep1);
println!("Stopped beep1");
thread::sleep(Duration::from_millis(1500));
drop(beep3);
println!("Stopped beep3");
thread::sleep(Duration::from_millis(1500));
}

View File

@@ -0,0 +1,38 @@
use rodio::source::{SineWave, Source};
use rodio::{dynamic_mixer, OutputStream, Sink};
use std::time::Duration;
fn main() {
// Construct a dynamic controller and mixer, stream_handle, and sink.
let (controller, mixer) = dynamic_mixer::mixer::<f32>(2, 44_100);
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
let sink = Sink::try_new(&stream_handle).unwrap();
// Create four unique sources. The frequencies used here correspond
// notes in the key of C and in octave 4: C4, or middle C on a piano,
// E4, G4, and A4 respectively.
let source_c = SineWave::new(261.63)
.take_duration(Duration::from_secs_f32(1.))
.amplify(0.20);
let source_e = SineWave::new(329.63)
.take_duration(Duration::from_secs_f32(1.))
.amplify(0.20);
let source_g = SineWave::new(392.0)
.take_duration(Duration::from_secs_f32(1.))
.amplify(0.20);
let source_a = SineWave::new(440.0)
.take_duration(Duration::from_secs_f32(1.))
.amplify(0.20);
// Add sources C, E, G, and A to the mixer controller.
controller.add(source_c);
controller.add(source_e);
controller.add(source_g);
controller.add(source_a);
// Append the dynamic mixer to the sink to play a C major 6th chord.
sink.append(mixer);
// Sleep the thread until sink is empty.
sink.sleep_until_end();
}

11
vendor/rodio/examples/music_flac.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
use std::io::BufReader;
fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("assets/music.flac").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
sink.sleep_until_end();
}

11
vendor/rodio/examples/music_m4a.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
use std::io::BufReader;
fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("assets/music.m4a").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
sink.sleep_until_end();
}

11
vendor/rodio/examples/music_mp3.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
use std::io::BufReader;
fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("assets/music.mp3").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
sink.sleep_until_end();
}

11
vendor/rodio/examples/music_ogg.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
use std::io::BufReader;
fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("assets/music.ogg").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
sink.sleep_until_end();
}

11
vendor/rodio/examples/music_wav.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
use std::io::BufReader;
fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("assets/music.wav").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
sink.sleep_until_end();
}

View File

@@ -0,0 +1,41 @@
//! Noise generator example. Use the "noise" feature to enable the noise generator sources.
#[cfg(feature = "noise")]
fn main() {
use rodio::source::{pink, white, Source};
use std::thread;
use std::time::Duration;
let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
let noise_duration = Duration::from_millis(1000);
let interval_duration = Duration::from_millis(1500);
stream_handle
.play_raw(
white(cpal::SampleRate(48000))
.amplify(0.1)
.take_duration(noise_duration),
)
.unwrap();
println!("Playing white noise");
thread::sleep(interval_duration);
stream_handle
.play_raw(
pink(cpal::SampleRate(48000))
.amplify(0.1)
.take_duration(noise_duration),
)
.unwrap();
println!("Playing pink noise");
thread::sleep(interval_duration);
}
#[cfg(not(feature = "noise"))]
fn main() {
println!("rodio has not been compiled with noise sources, use `--features noise` to enable this feature.");
println!("Exiting...");
}

15
vendor/rodio/examples/reverb.rs vendored Normal file
View File

@@ -0,0 +1,15 @@
use rodio::Source;
use std::io::BufReader;
use std::time::Duration;
fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("assets/music.ogg").unwrap();
let source = rodio::Decoder::new(BufReader::new(file)).unwrap();
let with_reverb = source.buffered().reverb(Duration::from_millis(40), 0.7);
sink.append(with_reverb);
sink.sleep_until_end();
}

22
vendor/rodio/examples/seek_mp3.rs vendored Normal file
View File

@@ -0,0 +1,22 @@
use std::io::BufReader;
use std::time::Duration;
fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("assets/music.mp3").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
std::thread::sleep(std::time::Duration::from_secs(2));
sink.try_seek(Duration::from_secs(0)).unwrap();
std::thread::sleep(std::time::Duration::from_secs(2));
sink.try_seek(Duration::from_secs(4)).unwrap();
sink.sleep_until_end();
// wont do anything since the sound has ended already
sink.try_seek(Duration::from_secs(5)).unwrap();
println!("seek example ended");
}

View File

@@ -0,0 +1,83 @@
//! Test signal generator example.
fn main() {
use rodio::source::{chirp, Function, SignalGenerator, Source};
use std::thread;
use std::time::Duration;
let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
let test_signal_duration = Duration::from_millis(1000);
let interval_duration = Duration::from_millis(1500);
println!("Playing 1000 Hz tone");
stream_handle
.play_raw(
SignalGenerator::new(cpal::SampleRate(48000), 1000.0, Function::Sine)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();
thread::sleep(interval_duration);
println!("Playing 10,000 Hz tone");
stream_handle
.play_raw(
SignalGenerator::new(cpal::SampleRate(48000), 10000.0, Function::Sine)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();
thread::sleep(interval_duration);
println!("Playing 440 Hz Triangle Wave");
stream_handle
.play_raw(
SignalGenerator::new(cpal::SampleRate(48000), 440.0, Function::Triangle)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();
thread::sleep(interval_duration);
println!("Playing 440 Hz Sawtooth Wave");
stream_handle
.play_raw(
SignalGenerator::new(cpal::SampleRate(48000), 440.0, Function::Sawtooth)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();
thread::sleep(interval_duration);
println!("Playing 440 Hz Square Wave");
stream_handle
.play_raw(
SignalGenerator::new(cpal::SampleRate(48000), 440.0, Function::Square)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();
thread::sleep(interval_duration);
println!("Playing 20-10000 Hz Sweep");
stream_handle
.play_raw(
chirp(
cpal::SampleRate(48000),
20.0,
10000.0,
Duration::from_secs(1),
)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();
thread::sleep(interval_duration);
}

53
vendor/rodio/examples/spatial.rs vendored Normal file
View File

@@ -0,0 +1,53 @@
use std::io::BufReader;
use std::thread;
use std::time::Duration;
use rodio::Source;
fn main() {
let iter_duration = Duration::from_secs(5);
let iter_distance = 5.;
let refresh_duration = Duration::from_millis(10);
let num_steps = iter_duration.as_secs_f32() / refresh_duration.as_secs_f32();
let step_distance = iter_distance / num_steps;
let num_steps = num_steps as u32;
let repeats = 5;
let total_duration = iter_duration * 2 * repeats;
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let mut positions = ([0., 0., 0.], [-1., 0., 0.], [1., 0., 0.]);
let sink = rodio::SpatialSink::try_new(&handle, positions.0, positions.1, positions.2).unwrap();
let file = std::fs::File::open("assets/music.ogg").unwrap();
let source = rodio::Decoder::new(BufReader::new(file))
.unwrap()
.repeat_infinite()
.take_duration(total_duration);
sink.append(source);
// A sound emitter playing the music starting at the centre gradually moves to the right
// until it stops and begins traveling to the left, it will eventually pass through the
// listener again and go to the far left.
// This is repeated 5 times.
for _ in 0..repeats {
for _ in 0..num_steps {
thread::sleep(refresh_duration);
positions.0[0] += step_distance;
sink.set_emitter_position(positions.0);
}
for _ in 0..(num_steps * 2) {
thread::sleep(refresh_duration);
positions.0[0] -= step_distance;
sink.set_emitter_position(positions.0);
}
for _ in 0..num_steps {
thread::sleep(refresh_duration);
positions.0[0] += step_distance;
sink.set_emitter_position(positions.0);
}
}
sink.sleep_until_end();
}

12
vendor/rodio/examples/stereo.rs vendored Normal file
View File

@@ -0,0 +1,12 @@
//! Plays a tone alternating between right and left ears, with right being first.
use std::io::BufReader;
fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
let sink = rodio::Sink::try_new(&handle).unwrap();
let file = std::fs::File::open("assets/RL.ogg").unwrap();
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
sink.sleep_until_end();
}

View File

@@ -0,0 +1,48 @@
<!---
When 0.20.0 is released this announcement will be posted to r/rust and the
rust user forum. Also post in rust audio discord: https://discord.gg/8qW6q2k
-->
# Announcing rodio 0.20 and call for help
Rodio is an audio playback library. It can decode audio files, synthesize new
sounds, apply effects to sounds & mix them. Rodio has been part of the Rust
ecosystem for 9 years now! 🎉.
## New release
The rodio contributors have made many improvements in the last 5 months. Rodio can now:
- Seek back and forth through sound efficiently
- Track the playback position at sample accuracy!
- Generate more signals such as chirps, white & pink noise and different
wavesforms
- Automatically adjust the gain to limit the peak volume and change in loudness
This is ignoring the many fixes and smaller additions made by the many
contributors who helped out expand rodio.
## Call for help
In its 9 years of existence Rust has changed a lot. Further more Rodio is being
used for applications beyond its original scope. To improve rodio we believe its
time for larger (breaking) changes.
### User feedback
To ensure we make the right changes we want
to know what rodio is being used for and what you all would like to use it for.
We can use any input you have but are especially looking for users who are:
- using rodio and feel some part of the API is hard to use.
- have experienced footguns/pain point
- wanted to use rodio but could not make it fit their use-case (excluding complex
game audio (best served by [kira](https://crates.io/crates/kira)) and advanced
dsp). If you disagree and think rodio can server those excluded use-case too
let us know!
The best way to leave your feedback is a short user story on our issue
[tracker](https://github.com/RustAudio/rodio/issues). If that is not your thing
any other form posted there works too!
### Architecture & API
We can use input on our planned [changes](https://github.com/RustAudio/rodio/issues/614) and how to best implement them.

201
vendor/rodio/src/buffer.rs vendored Normal file
View File

@@ -0,0 +1,201 @@
//! A simple source of samples coming from a buffer.
//!
//! The `SamplesBuffer` struct can be used to treat a list of values as a `Source`.
//!
//! # Example
//!
//! ```
//! use rodio::buffer::SamplesBuffer;
//! let _ = SamplesBuffer::new(1, 44100, vec![1i16, 2, 3, 4, 5, 6]);
//! ```
//!
use std::time::Duration;
use crate::source::SeekError;
use crate::{Sample, Source};
/// A buffer of samples treated as a source.
#[derive(Debug, Clone)]
pub struct SamplesBuffer<S> {
data: Vec<S>,
pos: usize,
channels: u16,
sample_rate: u32,
duration: Duration,
}
impl<S> SamplesBuffer<S>
where
S: Sample,
{
/// Builds a new `SamplesBuffer`.
///
/// # Panic
///
/// - Panics if the number of channels is zero.
/// - Panics if the samples rate is zero.
/// - Panics if the length of the buffer is larger than approximately 16 billion elements.
/// This is because the calculation of the duration would overflow.
///
pub fn new<D>(channels: u16, sample_rate: u32, data: D) -> SamplesBuffer<S>
where
D: Into<Vec<S>>,
{
assert!(channels != 0);
assert!(sample_rate != 0);
let data = data.into();
let duration_ns = 1_000_000_000u64.checked_mul(data.len() as u64).unwrap()
/ sample_rate as u64
/ channels as u64;
let duration = Duration::new(
duration_ns / 1_000_000_000,
(duration_ns % 1_000_000_000) as u32,
);
SamplesBuffer {
data,
pos: 0,
channels,
sample_rate,
duration,
}
}
}
impl<S> Source for SamplesBuffer<S>
where
S: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
self.channels
}
#[inline]
fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
Some(self.duration)
}
// this is fast because all the samples are in memory already
// and due to the constant sample_rate we can jump to the right
// sample directly
//
/// This jumps in memory till the sample for `pos`.
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
let curr_channel = self.pos % self.channels() as usize;
let new_pos = pos.as_secs_f32() * self.sample_rate() as f32 * self.channels() as f32;
// saturate pos at the end of the source
let new_pos = new_pos as usize;
let new_pos = new_pos.min(self.data.len());
// make sure the next sample is for the right channel
let new_pos = new_pos.next_multiple_of(self.channels() as usize);
let new_pos = new_pos - curr_channel;
self.pos = new_pos;
Ok(())
}
}
impl<S> Iterator for SamplesBuffer<S>
where
S: Sample,
{
type Item = S;
#[inline]
fn next(&mut self) -> Option<S> {
let sample = self.data.get(self.pos)?;
self.pos += 1;
Some(*sample)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.data.len(), Some(self.data.len()))
}
}
#[cfg(test)]
mod tests {
use crate::buffer::SamplesBuffer;
use crate::source::Source;
#[test]
fn basic() {
let _ = SamplesBuffer::new(1, 44100, vec![0i16, 0, 0, 0, 0, 0]);
}
#[test]
#[should_panic]
fn panic_if_zero_channels() {
SamplesBuffer::new(0, 44100, vec![0i16, 0, 0, 0, 0, 0]);
}
#[test]
#[should_panic]
fn panic_if_zero_sample_rate() {
SamplesBuffer::new(1, 0, vec![0i16, 0, 0, 0, 0, 0]);
}
#[test]
fn duration_basic() {
let buf = SamplesBuffer::new(2, 2, vec![0i16, 0, 0, 0, 0, 0]);
let dur = buf.total_duration().unwrap();
assert_eq!(dur.as_secs(), 1);
assert_eq!(dur.subsec_nanos(), 500_000_000);
}
#[test]
fn iteration() {
let mut buf = SamplesBuffer::new(1, 44100, vec![1i16, 2, 3, 4, 5, 6]);
assert_eq!(buf.next(), Some(1));
assert_eq!(buf.next(), Some(2));
assert_eq!(buf.next(), Some(3));
assert_eq!(buf.next(), Some(4));
assert_eq!(buf.next(), Some(5));
assert_eq!(buf.next(), Some(6));
assert_eq!(buf.next(), None);
}
#[cfg(test)]
mod try_seek {
use super::*;
use std::time::Duration;
#[test]
fn channel_order_stays_correct() {
const SAMPLE_RATE: u32 = 100;
const CHANNELS: u16 = 2;
let mut buf = SamplesBuffer::new(
CHANNELS,
SAMPLE_RATE,
(0..2000i16).into_iter().collect::<Vec<_>>(),
);
buf.try_seek(Duration::from_secs(5)).unwrap();
assert_eq!(
buf.next(),
Some(5i16 * SAMPLE_RATE as i16 * CHANNELS as i16)
);
assert!(buf.next().is_some_and(|s| s % 2 == 1));
assert!(buf.next().is_some_and(|s| s % 2 == 0));
buf.try_seek(Duration::from_secs(6)).unwrap();
assert!(buf.next().is_some_and(|s| s % 2 == 1),);
}
}
}

181
vendor/rodio/src/conversions/channels.rs vendored Normal file
View File

@@ -0,0 +1,181 @@
use cpal::Sample;
/// Iterator that converts from a certain channel count to another.
#[derive(Clone, Debug)]
pub struct ChannelCountConverter<I>
where
I: Iterator,
{
input: I,
from: cpal::ChannelCount,
to: cpal::ChannelCount,
sample_repeat: Option<I::Item>,
next_output_sample_pos: cpal::ChannelCount,
}
impl<I> ChannelCountConverter<I>
where
I: Iterator,
{
/// Initializes the iterator.
///
/// # Panic
///
/// Panics if `from` or `to` are equal to 0.
///
#[inline]
pub fn new(
input: I,
from: cpal::ChannelCount,
to: cpal::ChannelCount,
) -> ChannelCountConverter<I> {
assert!(from >= 1);
assert!(to >= 1);
ChannelCountConverter {
input,
from,
to,
sample_repeat: None,
next_output_sample_pos: 0,
}
}
/// Destroys this iterator and returns the underlying iterator.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
/// Get mutable access to the iterator
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
}
impl<I> Iterator for ChannelCountConverter<I>
where
I: Iterator,
I::Item: Sample,
{
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
let result = match self.next_output_sample_pos {
0 => {
// save first sample for mono -> stereo conversion
let value = self.input.next();
self.sample_repeat = value;
value
}
x if x < self.from => self.input.next(),
1 => self.sample_repeat,
_ => Some(I::Item::EQUILIBRIUM),
};
if result.is_some() {
self.next_output_sample_pos += 1;
}
if self.next_output_sample_pos == self.to {
self.next_output_sample_pos = 0;
if self.from > self.to {
for _ in self.to..self.from {
self.input.next(); // discarding extra input
}
}
}
result
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (min, max) = self.input.size_hint();
let consumed = std::cmp::min(self.from, self.next_output_sample_pos) as usize;
let calculate = |size| {
(size + consumed) / self.from as usize * self.to as usize
- self.next_output_sample_pos as usize
};
let min = calculate(min);
let max = max.map(calculate);
(min, max)
}
}
impl<I> ExactSizeIterator for ChannelCountConverter<I>
where
I: ExactSizeIterator,
I::Item: Sample,
{
}
#[cfg(test)]
mod test {
use super::ChannelCountConverter;
#[test]
fn remove_channels() {
let input = vec![1u16, 2, 3, 4, 5, 6];
let output = ChannelCountConverter::new(input.into_iter(), 3, 2).collect::<Vec<_>>();
assert_eq!(output, [1, 2, 4, 5]);
let input = vec![1u16, 2, 3, 4, 5, 6, 7, 8];
let output = ChannelCountConverter::new(input.into_iter(), 4, 1).collect::<Vec<_>>();
assert_eq!(output, [1, 5]);
}
#[test]
fn add_channels() {
let input = vec![1i16, 2, 3, 4];
let output = ChannelCountConverter::new(input.into_iter(), 1, 2).collect::<Vec<_>>();
assert_eq!(output, [1, 1, 2, 2, 3, 3, 4, 4]);
let input = vec![1i16, 2];
let output = ChannelCountConverter::new(input.into_iter(), 1, 4).collect::<Vec<_>>();
assert_eq!(output, [1, 1, 0, 0, 2, 2, 0, 0]);
let input = vec![1i16, 2, 3, 4];
let output = ChannelCountConverter::new(input.into_iter(), 2, 4).collect::<Vec<_>>();
assert_eq!(output, [1, 2, 0, 0, 3, 4, 0, 0]);
}
#[test]
fn size_hint() {
fn test(input: &[i16], from: cpal::ChannelCount, to: cpal::ChannelCount) {
let mut converter = ChannelCountConverter::new(input.iter().copied(), from, to);
let count = converter.clone().count();
for left_in_iter in (0..=count).rev() {
println!("left_in_iter = {left_in_iter}");
assert_eq!(converter.size_hint(), (left_in_iter, Some(left_in_iter)));
converter.next();
}
assert_eq!(converter.size_hint(), (0, Some(0)));
}
test(&[1i16, 2, 3], 1, 2);
test(&[1i16, 2, 3, 4], 2, 4);
test(&[1i16, 2, 3, 4], 4, 2);
test(&[1i16, 2, 3, 4, 5, 6], 3, 8);
test(&[1i16, 2, 3, 4, 5, 6, 7, 8], 4, 1);
}
#[test]
fn len_more() {
let input = vec![1i16, 2, 3, 4];
let output = ChannelCountConverter::new(input.into_iter(), 2, 3);
assert_eq!(output.len(), 6);
}
#[test]
fn len_less() {
let input = vec![1i16, 2, 3, 4];
let output = ChannelCountConverter::new(input.into_iter(), 2, 1);
assert_eq!(output.len(), 2);
}
}

17
vendor/rodio/src/conversions/mod.rs vendored Normal file
View File

@@ -0,0 +1,17 @@
/*!
This module contains function that will convert from one PCM format to another.
This includes conversion between sample formats, channels or sample rates.
*/
pub use self::channels::ChannelCountConverter;
pub use self::sample::DataConverter;
pub use self::sample::Sample;
pub use self::sample_rate::SampleRateConverter;
mod channels;
// TODO: < shouldn't be public ; there's a bug in Rust 1.4 and below that makes This
// `pub` mandatory
pub mod sample;
mod sample_rate;

180
vendor/rodio/src/conversions/sample.rs vendored Normal file
View File

@@ -0,0 +1,180 @@
use cpal::{FromSample, Sample as CpalSample};
use std::marker::PhantomData;
/// Converts the samples data type to `O`.
#[derive(Clone, Debug)]
pub struct DataConverter<I, O> {
input: I,
marker: PhantomData<O>,
}
impl<I, O> DataConverter<I, O> {
/// Builds a new converter.
#[inline]
pub fn new(input: I) -> DataConverter<I, O> {
DataConverter {
input,
marker: PhantomData,
}
}
/// Destroys this iterator and returns the underlying iterator.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
/// get mutable access to the iterator
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
}
impl<I, O> Iterator for DataConverter<I, O>
where
I: Iterator,
I::Item: Sample,
O: FromSample<I::Item> + Sample,
{
type Item = O;
#[inline]
fn next(&mut self) -> Option<O> {
self.input.next().map(|s| CpalSample::from_sample(s))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I, O> ExactSizeIterator for DataConverter<I, O>
where
I: ExactSizeIterator,
I::Item: Sample,
O: FromSample<I::Item> + Sample,
{
}
/// Represents a value of a single sample.
///
/// This trait is implemented by default on three types: `i16`, `u16` and `f32`.
///
/// - For `i16`, silence corresponds to the value `0`. The minimum and maximum amplitudes are
/// represented by `i16::min_value()` and `i16::max_value()` respectively.
/// - For `u16`, silence corresponds to the value `u16::max_value() / 2`. The minimum and maximum
/// amplitudes are represented by `0` and `u16::max_value()` respectively.
/// - For `f32`, silence corresponds to the value `0.0`. The minimum and maximum amplitudes are
/// represented by `-1.0` and `1.0` respectively.
///
/// You can implement this trait on your own type as well if you wish so.
///
pub trait Sample: CpalSample {
/// Linear interpolation between two samples.
///
/// The result should be equal to
/// `first * numerator / denominator + second * (1 - numerator / denominator)`.
fn lerp(first: Self, second: Self, numerator: u32, denominator: u32) -> Self;
/// Multiplies the value of this sample by the given amount.
fn amplify(self, value: f32) -> Self;
/// Converts the sample to an f32 value.
fn to_f32(self) -> f32;
/// Calls `saturating_add` on the sample.
fn saturating_add(self, other: Self) -> Self;
/// Returns the value corresponding to the absence of sound.
fn zero_value() -> Self;
}
impl Sample for u16 {
#[inline]
fn lerp(first: u16, second: u16, numerator: u32, denominator: u32) -> u16 {
let a = first as i32;
let b = second as i32;
let n = numerator as i32;
let d = denominator as i32;
(a + (b - a) * n / d) as u16
}
#[inline]
fn amplify(self, value: f32) -> u16 {
((self as f32) * value) as u16
}
#[inline]
fn to_f32(self) -> f32 {
// Convert u16 to f32 in the range [-1.0, 1.0]
(self as f32 - 32768.0) / 32768.0
}
#[inline]
fn saturating_add(self, other: u16) -> u16 {
self.saturating_add(other)
}
#[inline]
fn zero_value() -> u16 {
32768
}
}
impl Sample for i16 {
#[inline]
fn lerp(first: i16, second: i16, numerator: u32, denominator: u32) -> i16 {
(first as i32 + (second as i32 - first as i32) * numerator as i32 / denominator as i32)
as i16
}
#[inline]
fn amplify(self, value: f32) -> i16 {
((self as f32) * value) as i16
}
#[inline]
fn to_f32(self) -> f32 {
// Convert i16 to f32 in the range [-1.0, 1.0]
self as f32 / 32768.0
}
#[inline]
fn saturating_add(self, other: i16) -> i16 {
self.saturating_add(other)
}
#[inline]
fn zero_value() -> i16 {
0
}
}
impl Sample for f32 {
#[inline]
fn lerp(first: f32, second: f32, numerator: u32, denominator: u32) -> f32 {
first + (second - first) * numerator as f32 / denominator as f32
}
#[inline]
fn amplify(self, value: f32) -> f32 {
self * value
}
#[inline]
fn to_f32(self) -> f32 {
// f32 is already in the correct format
self
}
#[inline]
fn saturating_add(self, other: f32) -> f32 {
self + other
}
#[inline]
fn zero_value() -> f32 {
0.0
}
}

View File

@@ -0,0 +1,377 @@
use crate::conversions::Sample;
use std::mem;
/// Iterator that converts from a certain sample rate to another.
#[derive(Clone, Debug)]
pub struct SampleRateConverter<I>
where
I: Iterator,
{
/// The iterator that gives us samples.
input: I,
/// We convert chunks of `from` samples into chunks of `to` samples.
from: u32,
/// We convert chunks of `from` samples into chunks of `to` samples.
to: u32,
/// Number of channels in the stream
channels: cpal::ChannelCount,
/// One sample per channel, extracted from `input`.
current_frame: Vec<I::Item>,
/// Position of `current_sample` modulo `from`.
current_frame_pos_in_chunk: u32,
/// The samples right after `current_sample` (one per channel), extracted from `input`.
next_frame: Vec<I::Item>,
/// The position of the next sample that the iterator should return, modulo `to`.
/// This counter is incremented (modulo `to`) every time the iterator is called.
next_output_frame_pos_in_chunk: u32,
/// The buffer containing the samples waiting to be output.
output_buffer: Vec<I::Item>,
}
impl<I> SampleRateConverter<I>
where
I: Iterator,
I::Item: Sample,
{
///
///
/// # Panic
///
/// Panics if `from` or `to` are equal to 0.
///
#[inline]
pub fn new(
mut input: I,
from: cpal::SampleRate,
to: cpal::SampleRate,
num_channels: cpal::ChannelCount,
) -> SampleRateConverter<I> {
let from = from.0;
let to = to.0;
assert!(from >= 1);
assert!(to >= 1);
// finding greatest common divisor
let gcd = {
#[inline]
fn gcd(a: u32, b: u32) -> u32 {
if b == 0 {
a
} else {
gcd(b, a % b)
}
}
gcd(from, to)
};
let (first_samples, next_samples) = if from == to {
// if `from` == `to` == 1, then we just pass through
debug_assert_eq!(from, gcd);
(Vec::new(), Vec::new())
} else {
let first = input
.by_ref()
.take(num_channels as usize)
.collect::<Vec<_>>();
let next = input
.by_ref()
.take(num_channels as usize)
.collect::<Vec<_>>();
(first, next)
};
SampleRateConverter {
input,
from: from / gcd,
to: to / gcd,
channels: num_channels,
current_frame_pos_in_chunk: 0,
next_output_frame_pos_in_chunk: 0,
current_frame: first_samples,
next_frame: next_samples,
output_buffer: Vec::with_capacity(num_channels as usize - 1),
}
}
/// Destroys this iterator and returns the underlying iterator.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
/// get mutable access to the iterator
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
fn next_input_frame(&mut self) {
self.current_frame_pos_in_chunk += 1;
mem::swap(&mut self.current_frame, &mut self.next_frame);
self.next_frame.clear();
for _ in 0..self.channels {
if let Some(i) = self.input.next() {
self.next_frame.push(i);
} else {
break;
}
}
}
}
impl<I> Iterator for SampleRateConverter<I>
where
I: Iterator,
I::Item: Sample + Clone,
{
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
// the algorithm below doesn't work if `self.from == self.to`
if self.from == self.to {
debug_assert_eq!(self.from, 1);
return self.input.next();
}
// Short circuit if there are some samples waiting.
if !self.output_buffer.is_empty() {
return Some(self.output_buffer.remove(0));
}
// The frame we are going to return from this function will be a linear interpolation
// between `self.current_frame` and `self.next_frame`.
if self.next_output_frame_pos_in_chunk == self.to {
// If we jump to the next frame, we reset the whole state.
self.next_output_frame_pos_in_chunk = 0;
self.next_input_frame();
while self.current_frame_pos_in_chunk != self.from {
self.next_input_frame();
}
self.current_frame_pos_in_chunk = 0;
} else {
// Finding the position of the first sample of the linear interpolation.
let req_left_sample =
(self.from * self.next_output_frame_pos_in_chunk / self.to) % self.from;
// Advancing `self.current_frame`, `self.next_frame` and
// `self.current_frame_pos_in_chunk` until the latter variable
// matches `req_left_sample`.
while self.current_frame_pos_in_chunk != req_left_sample {
self.next_input_frame();
debug_assert!(self.current_frame_pos_in_chunk < self.from);
}
}
// Merging `self.current_frame` and `self.next_frame` into `self.output_buffer`.
// Note that `self.output_buffer` can be truncated if there is not enough data in
// `self.next_frame`.
let mut result = None;
let numerator = (self.from * self.next_output_frame_pos_in_chunk) % self.to;
for (off, (cur, next)) in self
.current_frame
.iter()
.zip(self.next_frame.iter())
.enumerate()
{
let sample = Sample::lerp(*cur, *next, numerator, self.to);
if off == 0 {
result = Some(sample);
} else {
self.output_buffer.push(sample);
}
}
// Incrementing the counter for the next iteration.
self.next_output_frame_pos_in_chunk += 1;
if result.is_some() {
result
} else {
// draining `self.current_frame`
if !self.current_frame.is_empty() {
let r = Some(self.current_frame.remove(0));
mem::swap(&mut self.output_buffer, &mut self.current_frame);
self.current_frame.clear();
r
} else {
None
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let apply = |samples: usize| {
// `samples_after_chunk` will contain the number of samples remaining after the chunk
// currently being processed
let samples_after_chunk = samples;
// adding the samples of the next chunk that may have already been read
let samples_after_chunk = if self.current_frame_pos_in_chunk == self.from - 1 {
samples_after_chunk + self.next_frame.len()
} else {
samples_after_chunk
};
// removing the samples of the current chunk that have not yet been read
let samples_after_chunk = samples_after_chunk.saturating_sub(
self.from
.saturating_sub(self.current_frame_pos_in_chunk + 2) as usize
* usize::from(self.channels),
);
// calculating the number of samples after the transformation
// TODO: this is wrong here \|/
let samples_after_chunk = samples_after_chunk * self.to as usize / self.from as usize;
// `samples_current_chunk` will contain the number of samples remaining to be output
// for the chunk currently being processed
let samples_current_chunk = (self.to - self.next_output_frame_pos_in_chunk) as usize
* usize::from(self.channels);
samples_current_chunk + samples_after_chunk + self.output_buffer.len()
};
if self.from == self.to {
self.input.size_hint()
} else {
let (min, max) = self.input.size_hint();
(apply(min), max.map(apply))
}
}
}
impl<I> ExactSizeIterator for SampleRateConverter<I>
where
I: ExactSizeIterator,
I::Item: Sample + Clone,
{
}
#[cfg(test)]
mod test {
use super::SampleRateConverter;
use core::time::Duration;
use cpal::SampleRate;
use quickcheck::quickcheck;
// TODO: Remove once cpal 0.12.2 is released and the dependency is updated
// (cpal#483 implemented ops::Mul on SampleRate)
const fn multiply_rate(r: SampleRate, k: u32) -> SampleRate {
SampleRate(k * r.0)
}
quickcheck! {
/// Check that resampling an empty input produces no output.
fn empty(from: u32, to: u32, n: u16) -> () {
let from = if from == 0 { return; } else { SampleRate(from) };
let to = if to == 0 { return; } else { SampleRate(to) };
if n == 0 { return; }
let input: Vec<u16> = Vec::new();
let output =
SampleRateConverter::new(input.into_iter(), from, to, n)
.collect::<Vec<_>>();
assert_eq!(output, []);
}
/// Check that resampling to the same rate does not change the signal.
fn identity(from: u32, n: u16, input: Vec<u16>) -> () {
let from = if from == 0 { return; } else { SampleRate(from) };
if n == 0 { return; }
let output =
SampleRateConverter::new(input.clone().into_iter(), from, from, n)
.collect::<Vec<_>>();
assert_eq!(input, output);
}
/// Check that dividing the sample rate by k (integer) is the same as
/// dropping a sample from each channel.
fn divide_sample_rate(to: u32, k: u32, input: Vec<u16>, n: u16) -> () {
let to = if to == 0 { return; } else { SampleRate(to) };
let from = multiply_rate(to, k);
if k == 0 || n == 0 { return; }
// Truncate the input, so it contains an integer number of frames.
let input = {
let ns = n as usize;
let mut i = input;
i.truncate(ns * (i.len() / ns));
i
};
let output =
SampleRateConverter::new(input.clone().into_iter(), from, to, n)
.collect::<Vec<_>>();
assert_eq!(input.chunks_exact(n.into())
.step_by(k as usize).collect::<Vec<_>>().concat(),
output)
}
/// Check that, after multiplying the sample rate by k, every k-th
/// sample in the output matches exactly with the input.
fn multiply_sample_rate(from: u32, k: u32, input: Vec<u16>, n: u16) -> () {
let from = if from == 0 { return; } else { SampleRate(from) };
let to = multiply_rate(from, k);
if k == 0 || n == 0 { return; }
// Truncate the input, so it contains an integer number of frames.
let input = {
let ns = n as usize;
let mut i = input;
i.truncate(ns * (i.len() / ns));
i
};
let output =
SampleRateConverter::new(input.clone().into_iter(), from, to, n)
.collect::<Vec<_>>();
assert_eq!(input,
output.chunks_exact(n.into())
.step_by(k as usize).collect::<Vec<_>>().concat()
)
}
#[ignore]
/// Check that resampling does not change the audio duration,
/// except by a negligible amount (± 1ms). Reproduces #316.
/// Ignored, pending a bug fix.
fn preserve_durations(d: Duration, freq: f32, to: u32) -> () {
use crate::source::{SineWave, Source};
let to = if to == 0 { return; } else { SampleRate(to) };
let source = SineWave::new(freq).take_duration(d);
let from = SampleRate(source.sample_rate());
let resampled =
SampleRateConverter::new(source, from, to, 1);
let duration =
Duration::from_secs_f32(resampled.count() as f32 / to.0 as f32);
let delta = if d < duration { duration - d } else { d - duration };
assert!(delta < Duration::from_millis(1),
"Resampled duration ({:?}) is not close to original ({:?}); Δ = {:?}",
duration, d, delta);
}
}
#[test]
fn upsample() {
let input = vec![2u16, 16, 4, 18, 6, 20, 8, 22];
let output =
SampleRateConverter::new(input.into_iter(), SampleRate(2000), SampleRate(3000), 2);
assert_eq!(output.len(), 12);
let output = output.collect::<Vec<_>>();
assert_eq!(output, [2, 16, 3, 17, 4, 18, 6, 20, 7, 21, 8, 22]);
}
}

144
vendor/rodio/src/decoder/flac.rs vendored Normal file
View File

@@ -0,0 +1,144 @@
use std::cmp::Ordering;
use std::io::{Read, Seek, SeekFrom};
use std::mem;
use std::time::Duration;
use crate::source::SeekError;
use crate::Source;
use claxon::FlacReader;
/// Decoder for the Flac format.
pub struct FlacDecoder<R>
where
R: Read + Seek,
{
reader: FlacReader<R>,
current_block: Vec<i32>,
current_block_channel_len: usize,
current_block_off: usize,
bits_per_sample: u32,
sample_rate: u32,
channels: u16,
samples: Option<u64>,
}
impl<R> FlacDecoder<R>
where
R: Read + Seek,
{
/// Attempts to decode the data as Flac.
pub fn new(mut data: R) -> Result<FlacDecoder<R>, R> {
if !is_flac(data.by_ref()) {
return Err(data);
}
let reader = FlacReader::new(data).unwrap();
let spec = reader.streaminfo();
Ok(FlacDecoder {
reader,
current_block: Vec::with_capacity(
spec.max_block_size as usize * spec.channels as usize,
),
current_block_channel_len: 1,
current_block_off: 0,
bits_per_sample: spec.bits_per_sample,
sample_rate: spec.sample_rate,
channels: spec.channels as u16,
samples: spec.samples,
})
}
pub fn into_inner(self) -> R {
self.reader.into_inner()
}
}
impl<R> Source for FlacDecoder<R>
where
R: Read + Seek,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
self.channels
}
#[inline]
fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
// `samples` in FLAC means "inter-channel samples" aka frames
// so we do not divide by `self.channels` here.
self.samples
.map(|s| Duration::from_micros(s * 1_000_000 / self.sample_rate as u64))
}
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Err(SeekError::NotSupported {
underlying_source: std::any::type_name::<Self>(),
})
}
}
impl<R> Iterator for FlacDecoder<R>
where
R: Read + Seek,
{
type Item = i16;
#[inline]
fn next(&mut self) -> Option<i16> {
loop {
if self.current_block_off < self.current_block.len() {
// Read from current block.
let real_offset = (self.current_block_off % self.channels as usize)
* self.current_block_channel_len
+ self.current_block_off / self.channels as usize;
let raw_val = self.current_block[real_offset];
self.current_block_off += 1;
let real_val = match self.bits_per_sample.cmp(&16) {
Ordering::Less => (raw_val << (16 - self.bits_per_sample)) as i16,
Ordering::Equal => raw_val as i16,
Ordering::Greater => (raw_val >> (self.bits_per_sample - 16)) as i16,
};
return Some(real_val);
}
// Load the next block.
self.current_block_off = 0;
let buffer = mem::take(&mut self.current_block);
match self.reader.blocks().read_next_or_eof(buffer) {
Ok(Some(block)) => {
self.current_block_channel_len = (block.len() / block.channels()) as usize;
self.current_block = block.into_buffer();
}
_ => return None,
}
}
}
}
/// Returns true if the stream contains Flac data, then resets it to where it was.
fn is_flac<R>(mut data: R) -> bool
where
R: Read + Seek,
{
let stream_pos = data.stream_position().unwrap();
if FlacReader::new(data.by_ref()).is_err() {
data.seek(SeekFrom::Start(stream_pos)).unwrap();
return false;
}
data.seek(SeekFrom::Start(stream_pos)).unwrap();
true
}

585
vendor/rodio/src/decoder/mod.rs vendored Normal file
View File

@@ -0,0 +1,585 @@
//! Decodes samples from an audio file.
use std::error::Error;
use std::fmt;
#[allow(unused_imports)]
use std::io::{Read, Seek, SeekFrom};
use std::mem;
use std::str::FromStr;
use std::time::Duration;
use crate::source::SeekError;
use crate::Source;
#[cfg(feature = "symphonia")]
use self::read_seek_source::ReadSeekSource;
#[cfg(feature = "symphonia")]
use ::symphonia::core::io::{MediaSource, MediaSourceStream};
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
mod flac;
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
mod mp3;
#[cfg(feature = "symphonia")]
mod read_seek_source;
#[cfg(feature = "symphonia")]
/// Symphonia decoders types
pub mod symphonia;
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
mod vorbis;
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
mod wav;
/// Source of audio samples from decoding a file.
///
/// Supports MP3, WAV, Vorbis and Flac.
pub struct Decoder<R>(DecoderImpl<R>)
where
R: Read + Seek;
/// Source of audio samples from decoding a file that never ends. When the
/// end of the file is reached the decoder starts again from the beginning.
///
/// Supports MP3, WAV, Vorbis and Flac.
pub struct LoopedDecoder<R>(DecoderImpl<R>)
where
R: Read + Seek;
// Cannot really reduce the size of the VorbisDecoder. There are not any
// arrays just a lot of struct fields.
#[allow(clippy::large_enum_variant)]
enum DecoderImpl<R>
where
R: Read + Seek,
{
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
Wav(wav::WavDecoder<R>),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Vorbis(vorbis::VorbisDecoder<R>),
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
Flac(flac::FlacDecoder<R>),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
Mp3(mp3::Mp3Decoder<R>),
#[cfg(feature = "symphonia")]
Symphonia(symphonia::SymphoniaDecoder),
None(::std::marker::PhantomData<R>),
}
impl<R: Read + Seek> DecoderImpl<R> {
#[inline]
fn next(&mut self) -> Option<i16> {
match self {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.next(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.next(),
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.next(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.next(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source) => source.next(),
DecoderImpl::None(_) => None,
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.size_hint(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.size_hint(),
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.size_hint(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.size_hint(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source) => source.size_hint(),
DecoderImpl::None(_) => (0, None),
}
}
#[inline]
fn current_frame_len(&self) -> Option<usize> {
match self {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.current_frame_len(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.current_frame_len(),
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.current_frame_len(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.current_frame_len(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source) => source.current_frame_len(),
DecoderImpl::None(_) => Some(0),
}
}
#[inline]
fn channels(&self) -> u16 {
match self {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.channels(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.channels(),
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.channels(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.channels(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source) => source.channels(),
DecoderImpl::None(_) => 0,
}
}
#[inline]
fn sample_rate(&self) -> u32 {
match self {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.sample_rate(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.sample_rate(),
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.sample_rate(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.sample_rate(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source) => source.sample_rate(),
DecoderImpl::None(_) => 1,
}
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
match self {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.total_duration(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.total_duration(),
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.total_duration(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.total_duration(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source) => source.total_duration(),
DecoderImpl::None(_) => Some(Duration::default()),
}
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
match self {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.try_seek(pos),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.try_seek(pos),
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.try_seek(pos),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.try_seek(pos),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source) => source.try_seek(pos),
DecoderImpl::None(_) => Err(SeekError::NotSupported {
underlying_source: "DecoderImpl::None",
}),
}
}
}
impl<R> Decoder<R>
where
R: Read + Seek + Send + Sync + 'static,
{
/// Builds a new decoder.
///
/// Attempts to automatically detect the format of the source of data.
#[allow(unused_variables)]
pub fn new(data: R) -> Result<Decoder<R>, DecoderError> {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
let data = match wav::WavDecoder::new(data) {
Err(data) => data,
Ok(decoder) => {
return Ok(Decoder(DecoderImpl::Wav(decoder)));
}
};
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
let data = match flac::FlacDecoder::new(data) {
Err(data) => data,
Ok(decoder) => {
return Ok(Decoder(DecoderImpl::Flac(decoder)));
}
};
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
let data = match vorbis::VorbisDecoder::new(data) {
Err(data) => data,
Ok(decoder) => {
return Ok(Decoder(DecoderImpl::Vorbis(decoder)));
}
};
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
let data = match mp3::Mp3Decoder::new(data) {
Err(data) => data,
Ok(decoder) => {
return Ok(Decoder(DecoderImpl::Mp3(decoder)));
}
};
#[cfg(feature = "symphonia")]
{
let mss = MediaSourceStream::new(
Box::new(ReadSeekSource::new(data)) as Box<dyn MediaSource>,
Default::default(),
);
match symphonia::SymphoniaDecoder::new(mss, None) {
Err(e) => Err(e),
Ok(decoder) => Ok(Decoder(DecoderImpl::Symphonia(decoder))),
}
}
#[cfg(not(feature = "symphonia"))]
Err(DecoderError::UnrecognizedFormat)
}
/// Builds a new looped decoder.
///
/// Attempts to automatically detect the format of the source of data.
pub fn new_looped(data: R) -> Result<LoopedDecoder<R>, DecoderError> {
Self::new(data).map(LoopedDecoder::new)
}
/// Builds a new decoder from wav data.
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
pub fn new_wav(data: R) -> Result<Decoder<R>, DecoderError> {
match wav::WavDecoder::new(data) {
Err(_) => Err(DecoderError::UnrecognizedFormat),
Ok(decoder) => Ok(Decoder(DecoderImpl::Wav(decoder))),
}
}
/// Builds a new decoder from wav data.
#[cfg(feature = "symphonia-wav")]
pub fn new_wav(data: R) -> Result<Decoder<R>, DecoderError> {
Decoder::new_symphonia(data, "wav")
}
/// Builds a new decoder from flac data.
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
pub fn new_flac(data: R) -> Result<Decoder<R>, DecoderError> {
match flac::FlacDecoder::new(data) {
Err(_) => Err(DecoderError::UnrecognizedFormat),
Ok(decoder) => Ok(Decoder(DecoderImpl::Flac(decoder))),
}
}
/// Builds a new decoder from flac data.
#[cfg(feature = "symphonia-flac")]
pub fn new_flac(data: R) -> Result<Decoder<R>, DecoderError> {
Decoder::new_symphonia(data, "flac")
}
/// Builds a new decoder from vorbis data.
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
pub fn new_vorbis(data: R) -> Result<Decoder<R>, DecoderError> {
match vorbis::VorbisDecoder::new(data) {
Err(_) => Err(DecoderError::UnrecognizedFormat),
Ok(decoder) => Ok(Decoder(DecoderImpl::Vorbis(decoder))),
}
}
/// Builds a new decoder from vorbis data.
#[cfg(feature = "symphonia-vorbis")]
pub fn new_vorbis(data: R) -> Result<Decoder<R>, DecoderError> {
Decoder::new_symphonia(data, "ogg")
}
/// Builds a new decoder from mp3 data.
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
pub fn new_mp3(data: R) -> Result<Decoder<R>, DecoderError> {
match mp3::Mp3Decoder::new(data) {
Err(_) => Err(DecoderError::UnrecognizedFormat),
Ok(decoder) => Ok(Decoder(DecoderImpl::Mp3(decoder))),
}
}
/// Builds a new decoder from mp3 data.
#[cfg(feature = "symphonia-mp3")]
pub fn new_mp3(data: R) -> Result<Decoder<R>, DecoderError> {
Decoder::new_symphonia(data, "mp3")
}
/// Builds a new decoder from aac data.
#[cfg(feature = "symphonia-aac")]
pub fn new_aac(data: R) -> Result<Decoder<R>, DecoderError> {
Decoder::new_symphonia(data, "aac")
}
/// Builds a new decoder from mp4 data.
#[cfg(feature = "symphonia-isomp4")]
pub fn new_mp4(data: R, hint: Mp4Type) -> Result<Decoder<R>, DecoderError> {
Decoder::new_symphonia(data, &hint.to_string())
}
#[cfg(feature = "symphonia")]
fn new_symphonia(data: R, hint: &str) -> Result<Decoder<R>, DecoderError> {
let mss = MediaSourceStream::new(
Box::new(ReadSeekSource::new(data)) as Box<dyn MediaSource>,
Default::default(),
);
match symphonia::SymphoniaDecoder::new(mss, Some(hint)) {
Err(e) => Err(e),
Ok(decoder) => Ok(Decoder(DecoderImpl::Symphonia(decoder))),
}
}
}
#[allow(missing_docs)] // Reason: will be removed, see: #612
#[derive(Debug)]
pub enum Mp4Type {
Mp4,
M4a,
M4p,
M4b,
M4r,
M4v,
Mov,
}
impl FromStr for Mp4Type {
type Err = String;
fn from_str(input: &str) -> Result<Mp4Type, Self::Err> {
match &input.to_lowercase()[..] {
"mp4" => Ok(Mp4Type::Mp4),
"m4a" => Ok(Mp4Type::M4a),
"m4p" => Ok(Mp4Type::M4p),
"m4b" => Ok(Mp4Type::M4b),
"m4r" => Ok(Mp4Type::M4r),
"m4v" => Ok(Mp4Type::M4v),
"mov" => Ok(Mp4Type::Mov),
_ => Err(format!("{input} is not a valid mp4 extension")),
}
}
}
impl fmt::Display for Mp4Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let text = match self {
Mp4Type::Mp4 => "mp4",
Mp4Type::M4a => "m4a",
Mp4Type::M4p => "m4p",
Mp4Type::M4b => "m4b",
Mp4Type::M4r => "m4r",
Mp4Type::M4v => "m4v",
Mp4Type::Mov => "mov",
};
write!(f, "{text}")
}
}
impl<R> LoopedDecoder<R>
where
R: Read + Seek,
{
fn new(decoder: Decoder<R>) -> LoopedDecoder<R> {
Self(decoder.0)
}
}
impl<R> Iterator for Decoder<R>
where
R: Read + Seek,
{
type Item = i16;
#[inline]
fn next(&mut self) -> Option<i16> {
self.0.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<R> Source for Decoder<R>
where
R: Read + Seek,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.0.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.0.channels()
}
fn sample_rate(&self) -> u32 {
self.0.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.0.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.0.try_seek(pos)
}
}
impl<R> Iterator for LoopedDecoder<R>
where
R: Read + Seek,
{
type Item = i16;
#[inline]
fn next(&mut self) -> Option<i16> {
if let Some(sample) = self.0.next() {
Some(sample)
} else {
let decoder = mem::replace(&mut self.0, DecoderImpl::None(Default::default()));
let (decoder, sample) = match decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => {
let mut reader = source.into_inner();
reader.seek(SeekFrom::Start(0)).ok()?;
let mut source = wav::WavDecoder::new(reader).ok()?;
let sample = source.next();
(DecoderImpl::Wav(source), sample)
}
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => {
use lewton::inside_ogg::OggStreamReader;
let mut reader = source.into_inner().into_inner();
reader.seek_bytes(SeekFrom::Start(0)).ok()?;
let mut source = vorbis::VorbisDecoder::from_stream_reader(
OggStreamReader::from_ogg_reader(reader).ok()?,
);
let sample = source.next();
(DecoderImpl::Vorbis(source), sample)
}
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => {
let mut reader = source.into_inner();
reader.seek(SeekFrom::Start(0)).ok()?;
let mut source = flac::FlacDecoder::new(reader).ok()?;
let sample = source.next();
(DecoderImpl::Flac(source), sample)
}
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => {
let mut reader = source.into_inner();
reader.seek(SeekFrom::Start(0)).ok()?;
let mut source = mp3::Mp3Decoder::new(reader).ok()?;
let sample = source.next();
(DecoderImpl::Mp3(source), sample)
}
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source) => {
let mut reader = source.into_inner();
reader.seek(SeekFrom::Start(0)).ok()?;
let mut source = symphonia::SymphoniaDecoder::new(reader, None).ok()?;
let sample = source.next();
(DecoderImpl::Symphonia(source), sample)
}
none @ DecoderImpl::None(_) => (none, None),
};
self.0 = decoder;
sample
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<R> Source for LoopedDecoder<R>
where
R: Read + Seek,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.0.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.0.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.0.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.0.try_seek(pos)
}
}
/// Error that can happen when creating a decoder.
#[derive(Debug, Clone)]
pub enum DecoderError {
/// The format of the data has not been recognized.
UnrecognizedFormat,
/// An IO error occurred while reading, writing, or seeking the stream.
#[cfg(feature = "symphonia")]
IoError(String),
/// The stream contained malformed data and could not be decoded or demuxed.
#[cfg(feature = "symphonia")]
DecodeError(&'static str),
/// A default or user-defined limit was reached while decoding or demuxing the stream. Limits
/// are used to prevent denial-of-service attacks from malicious streams.
#[cfg(feature = "symphonia")]
LimitError(&'static str),
/// The demuxer or decoder needs to be reset before continuing.
#[cfg(feature = "symphonia")]
ResetRequired,
/// No streams were found by the decoder
#[cfg(feature = "symphonia")]
NoStreams,
}
impl fmt::Display for DecoderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let text = match self {
DecoderError::UnrecognizedFormat => "Unrecognized format",
#[cfg(feature = "symphonia")]
DecoderError::IoError(msg) => &msg[..],
#[cfg(feature = "symphonia")]
DecoderError::DecodeError(msg) => msg,
#[cfg(feature = "symphonia")]
DecoderError::LimitError(msg) => msg,
#[cfg(feature = "symphonia")]
DecoderError::ResetRequired => "Reset required",
#[cfg(feature = "symphonia")]
DecoderError::NoStreams => "No streams",
};
write!(f, "{text}")
}
}
impl Error for DecoderError {}

125
vendor/rodio/src/decoder/mp3.rs vendored Normal file
View File

@@ -0,0 +1,125 @@
use std::io::{Read, Seek, SeekFrom};
use std::time::Duration;
use crate::source::SeekError;
use crate::Source;
use minimp3::Decoder;
use minimp3::Frame;
use minimp3_fixed as minimp3;
pub struct Mp3Decoder<R>
where
R: Read + Seek,
{
// decoder: SeekDecoder<R>,
decoder: Decoder<R>,
current_frame: Frame,
current_frame_offset: usize,
}
impl<R> Mp3Decoder<R>
where
R: Read + Seek,
{
pub fn new(mut data: R) -> Result<Self, R> {
if !is_mp3(data.by_ref()) {
return Err(data);
}
// let mut decoder = SeekDecoder::new(data)
let mut decoder = Decoder::new(data);
// parameters are correct and minimp3 is used correctly
// thus if we crash here one of these invariants is broken:
// .expect("should be able to allocate memory, perform IO");
// let current_frame = decoder.decode_frame()
let current_frame = decoder.next_frame()
// the reader makes enough data available therefore
// if we crash here the invariant broken is:
.expect("data should not corrupt");
Ok(Mp3Decoder {
decoder,
current_frame,
current_frame_offset: 0,
})
}
pub fn into_inner(self) -> R {
self.decoder.into_inner()
}
}
impl<R> Source for Mp3Decoder<R>
where
R: Read + Seek,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
Some(self.current_frame.data.len())
}
#[inline]
fn channels(&self) -> u16 {
self.current_frame.channels as _
}
#[inline]
fn sample_rate(&self) -> u32 {
self.current_frame.sample_rate as _
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
// TODO waiting for PR in minimp3_fixed or minimp3
// let pos = (pos.as_secs_f32() * self.sample_rate() as f32) as u64;
// // do not trigger a sample_rate, channels and frame len update
// // as the seek only takes effect after the current frame is done
// self.decoder.seek_samples(pos)?;
// Ok(())
Err(SeekError::NotSupported {
underlying_source: std::any::type_name::<Self>(),
})
}
}
impl<R> Iterator for Mp3Decoder<R>
where
R: Read + Seek,
{
type Item = i16;
fn next(&mut self) -> Option<i16> {
if self.current_frame_offset == self.current_frame_len().unwrap() {
if let Ok(frame) = self.decoder.next_frame() {
// if let Ok(frame) = self.decoder.decode_frame() {
self.current_frame = frame;
self.current_frame_offset = 0;
} else {
return None;
}
}
let v = self.current_frame.data[self.current_frame_offset];
self.current_frame_offset += 1;
Some(v)
}
}
/// Returns true if the stream contains mp3 data, then resets it to where it was.
fn is_mp3<R>(mut data: R) -> bool
where
R: Read + Seek,
{
let stream_pos = data.seek(SeekFrom::Current(0)).unwrap();
let mut decoder = Decoder::new(data.by_ref());
let ok = decoder.next_frame().is_ok();
data.seek(SeekFrom::Start(stream_pos)).unwrap();
ok
}

View File

@@ -0,0 +1,37 @@
use std::io::{Read, Result, Seek, SeekFrom};
use symphonia::core::io::MediaSource;
pub struct ReadSeekSource<T: Read + Seek + Send + Sync> {
inner: T,
}
impl<T: Read + Seek + Send + Sync> ReadSeekSource<T> {
/// Instantiates a new `ReadSeekSource<T>` by taking ownership and wrapping the provided
/// `Read + Seek`er.
pub fn new(inner: T) -> Self {
ReadSeekSource { inner }
}
}
impl<T: Read + Seek + Send + Sync> MediaSource for ReadSeekSource<T> {
fn is_seekable(&self) -> bool {
true
}
fn byte_len(&self) -> Option<u64> {
None
}
}
impl<T: Read + Seek + Send + Sync> Read for ReadSeekSource<T> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.inner.read(buf)
}
}
impl<T: Read + Seek + Send + Sync> Seek for ReadSeekSource<T> {
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
self.inner.seek(pos)
}
}

333
vendor/rodio/src/decoder/symphonia.rs vendored Normal file
View File

@@ -0,0 +1,333 @@
use core::fmt;
use core::time::Duration;
use symphonia::{
core::{
audio::{AudioBufferRef, SampleBuffer, SignalSpec},
codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL},
errors::Error,
formats::{FormatOptions, FormatReader, SeekedTo},
io::MediaSourceStream,
meta::MetadataOptions,
probe::Hint,
units::{self, Time},
},
default::get_probe,
};
use crate::{source, Source};
use super::DecoderError;
// Decoder errors are not considered fatal.
// The correct action is to just get a new packet and try again.
// But a decode error in more than 3 consecutive packets is fatal.
const MAX_DECODE_RETRIES: usize = 3;
pub(crate) struct SymphoniaDecoder {
decoder: Box<dyn Decoder>,
current_frame_offset: usize,
format: Box<dyn FormatReader>,
total_duration: Option<Time>,
buffer: SampleBuffer<i16>,
spec: SignalSpec,
}
impl SymphoniaDecoder {
pub(crate) fn new(
mss: MediaSourceStream,
extension: Option<&str>,
) -> Result<Self, DecoderError> {
match SymphoniaDecoder::init(mss, extension) {
Err(e) => match e {
Error::IoError(e) => Err(DecoderError::IoError(e.to_string())),
Error::DecodeError(e) => Err(DecoderError::DecodeError(e)),
Error::SeekError(_) => {
unreachable!("Seek errors should not occur during initialization")
}
Error::Unsupported(_) => Err(DecoderError::UnrecognizedFormat),
Error::LimitError(e) => Err(DecoderError::LimitError(e)),
Error::ResetRequired => Err(DecoderError::ResetRequired),
},
Ok(Some(decoder)) => Ok(decoder),
Ok(None) => Err(DecoderError::NoStreams),
}
}
pub(crate) fn into_inner(self) -> MediaSourceStream {
self.format.into_inner()
}
fn init(
mss: MediaSourceStream,
extension: Option<&str>,
) -> symphonia::core::errors::Result<Option<SymphoniaDecoder>> {
let mut hint = Hint::new();
if let Some(ext) = extension {
hint.with_extension(ext);
}
let format_opts: FormatOptions = FormatOptions {
enable_gapless: true,
..Default::default()
};
let metadata_opts: MetadataOptions = Default::default();
let mut probed = get_probe().format(&hint, mss, &format_opts, &metadata_opts)?;
let stream = match probed.format.default_track() {
Some(stream) => stream,
None => return Ok(None),
};
// Select the first supported track
let track_id = probed
.format
.tracks()
.iter()
.find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
.ok_or(symphonia::core::errors::Error::Unsupported(
"No track with supported codec",
))?
.id;
let track = probed
.format
.tracks()
.iter()
.find(|track| track.id == track_id)
.unwrap();
let mut decoder = symphonia::default::get_codecs()
.make(&track.codec_params, &DecoderOptions::default())?;
let total_duration = stream
.codec_params
.time_base
.zip(stream.codec_params.n_frames)
.map(|(base, frames)| base.calc_time(frames));
let mut decode_errors: usize = 0;
let decoded = loop {
let current_frame = match probed.format.next_packet() {
Ok(packet) => packet,
Err(Error::IoError(_)) => break decoder.last_decoded(),
Err(e) => return Err(e),
};
// If the packet does not belong to the selected track, skip over it
if current_frame.track_id() != track_id {
continue;
}
match decoder.decode(&current_frame) {
Ok(decoded) => break decoded,
Err(e) => match e {
Error::DecodeError(_) => {
decode_errors += 1;
if decode_errors > MAX_DECODE_RETRIES {
return Err(e);
} else {
continue;
}
}
_ => return Err(e),
},
}
};
let spec = decoded.spec().to_owned();
let buffer = SymphoniaDecoder::get_buffer(decoded, &spec);
Ok(Some(SymphoniaDecoder {
decoder,
current_frame_offset: 0,
format: probed.format,
total_duration,
buffer,
spec,
}))
}
#[inline]
fn get_buffer(decoded: AudioBufferRef, spec: &SignalSpec) -> SampleBuffer<i16> {
let duration = units::Duration::from(decoded.capacity() as u64);
let mut buffer = SampleBuffer::<i16>::new(duration, *spec);
buffer.copy_interleaved_ref(decoded);
buffer
}
}
impl Source for SymphoniaDecoder {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
Some(self.buffer.samples().len())
}
#[inline]
fn channels(&self) -> u16 {
self.spec.channels.count() as u16
}
#[inline]
fn sample_rate(&self) -> u32 {
self.spec.rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.total_duration
.map(|Time { seconds, frac }| Duration::new(seconds, (1f64 / frac) as u32))
}
fn try_seek(&mut self, pos: Duration) -> Result<(), source::SeekError> {
use symphonia::core::formats::{SeekMode, SeekTo};
let seek_beyond_end = self
.total_duration()
.is_some_and(|dur| dur.saturating_sub(pos).as_millis() < 1);
let time = if seek_beyond_end {
let time = self.total_duration.expect("if guarantees this is Some");
skip_back_a_tiny_bit(time) // some decoders can only seek to just before the end
} else {
pos.as_secs_f64().into()
};
// make sure the next sample is for the right channel
let to_skip = self.current_frame_offset % self.channels() as usize;
let seek_res = self
.format
.seek(
SeekMode::Accurate,
SeekTo::Time {
time,
track_id: None,
},
)
.map_err(SeekError::BaseSeek)?;
self.refine_position(seek_res)?;
self.current_frame_offset += to_skip;
Ok(())
}
}
/// Error returned when the try_seek implementation of the symphonia decoder fails.
#[derive(Debug)]
pub enum SeekError {
/// Could not get next packet while refining seek position
Refining(symphonia::core::errors::Error),
/// Format reader failed to seek
BaseSeek(symphonia::core::errors::Error),
/// Decoding failed retrying on the next packet failed
Retrying(symphonia::core::errors::Error),
/// Decoding failed on multiple consecutive packets
Decoding(symphonia::core::errors::Error),
}
impl fmt::Display for SeekError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SeekError::Refining(err) => {
write!(
f,
"Could not get next packet while refining seek position: {:?}",
err
)
}
SeekError::BaseSeek(err) => {
write!(f, "Format reader failed to seek: {:?}", err)
}
SeekError::Retrying(err) => {
write!(
f,
"Decoding failed retrying on the next packet failed: {:?}",
err
)
}
SeekError::Decoding(err) => {
write!(
f,
"Decoding failed on multiple consecutive packets: {:?}",
err
)
}
}
}
}
impl std::error::Error for SeekError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
SeekError::Refining(err) => Some(err),
SeekError::BaseSeek(err) => Some(err),
SeekError::Retrying(err) => Some(err),
SeekError::Decoding(err) => Some(err),
}
}
}
impl SymphoniaDecoder {
/// Note frame offset must be set after
fn refine_position(&mut self, seek_res: SeekedTo) -> Result<(), source::SeekError> {
let mut samples_to_pass = seek_res.required_ts - seek_res.actual_ts;
let packet = loop {
let candidate = self.format.next_packet().map_err(SeekError::Refining)?;
if candidate.dur() > samples_to_pass {
break candidate;
} else {
samples_to_pass -= candidate.dur();
}
};
let mut decoded = self.decoder.decode(&packet);
for _ in 0..MAX_DECODE_RETRIES {
if decoded.is_err() {
let packet = self.format.next_packet().map_err(SeekError::Retrying)?;
decoded = self.decoder.decode(&packet);
}
}
let decoded = decoded.map_err(SeekError::Decoding)?;
decoded.spec().clone_into(&mut self.spec);
self.buffer = SymphoniaDecoder::get_buffer(decoded, &self.spec);
self.current_frame_offset = samples_to_pass as usize * self.channels() as usize;
Ok(())
}
}
fn skip_back_a_tiny_bit(
Time {
mut seconds,
mut frac,
}: Time,
) -> Time {
frac -= 0.0001;
if frac < 0.0 {
seconds = seconds.saturating_sub(1);
frac = 1.0 - frac;
}
Time { seconds, frac }
}
impl Iterator for SymphoniaDecoder {
type Item = i16;
#[inline]
fn next(&mut self) -> Option<i16> {
if self.current_frame_offset >= self.buffer.len() {
let packet = self.format.next_packet().ok()?;
let mut decoded = self.decoder.decode(&packet);
for _ in 0..MAX_DECODE_RETRIES {
if decoded.is_err() {
let packet = self.format.next_packet().ok()?;
decoded = self.decoder.decode(&packet);
}
}
let decoded = decoded.ok()?;
decoded.spec().clone_into(&mut self.spec);
self.buffer = SymphoniaDecoder::get_buffer(decoded, &self.spec);
self.current_frame_offset = 0;
}
let sample = *self.buffer.samples().get(self.current_frame_offset)?;
self.current_frame_offset += 1;
Some(sample)
}
}

144
vendor/rodio/src/decoder/vorbis.rs vendored Normal file
View File

@@ -0,0 +1,144 @@
use std::io::{Read, Seek, SeekFrom};
use std::time::Duration;
use crate::source::SeekError;
use crate::Source;
use lewton::inside_ogg::OggStreamReader;
/// Decoder for an OGG file that contains Vorbis sound format.
pub struct VorbisDecoder<R>
where
R: Read + Seek,
{
stream_reader: OggStreamReader<R>,
current_data: Vec<i16>,
next: usize,
}
impl<R> VorbisDecoder<R>
where
R: Read + Seek,
{
/// Attempts to decode the data as ogg/vorbis.
pub fn new(mut data: R) -> Result<VorbisDecoder<R>, R> {
if !is_vorbis(data.by_ref()) {
return Err(data);
}
let stream_reader = OggStreamReader::new(data).unwrap();
Ok(Self::from_stream_reader(stream_reader))
}
pub fn from_stream_reader(mut stream_reader: OggStreamReader<R>) -> Self {
let mut data = match stream_reader.read_dec_packet_itl() {
Ok(Some(d)) => d,
_ => Vec::new(),
};
// The first packet is always empty, therefore
// we need to read the second frame to get some data
if let Ok(Some(mut d)) = stream_reader.read_dec_packet_itl() {
data.append(&mut d);
}
VorbisDecoder {
stream_reader,
current_data: data,
next: 0,
}
}
pub fn into_inner(self) -> OggStreamReader<R> {
self.stream_reader
}
}
impl<R> Source for VorbisDecoder<R>
where
R: Read + Seek,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
Some(self.current_data.len())
}
#[inline]
fn channels(&self) -> u16 {
self.stream_reader.ident_hdr.audio_channels as u16
}
#[inline]
fn sample_rate(&self) -> u32 {
self.stream_reader.ident_hdr.audio_sample_rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
/// seek is broken, https://github.com/RustAudio/lewton/issues/73.
// We could work around it by:
// - using unsafe to create an instance of Self
// - use mem::swap to turn the &mut self into a mut self
// - take out the underlying Read+Seek
// - make a new self and seek
//
// If this issue is fixed use the implementation in
// commit: 3bafe32388b4eb7a48c6701e6c65044dc8c555e6
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Err(SeekError::NotSupported {
underlying_source: std::any::type_name::<Self>(),
})
}
}
impl<R> Iterator for VorbisDecoder<R>
where
R: Read + Seek,
{
type Item = i16;
#[inline]
fn next(&mut self) -> Option<i16> {
if let Some(sample) = self.current_data.get(self.next).copied() {
self.next += 1;
if self.current_data.is_empty() {
if let Ok(Some(data)) = self.stream_reader.read_dec_packet_itl() {
self.current_data = data;
self.next = 0;
}
}
Some(sample)
} else {
if let Ok(Some(data)) = self.stream_reader.read_dec_packet_itl() {
self.current_data = data;
self.next = 0;
}
let sample = self.current_data.get(self.next).copied();
self.next += 1;
sample
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.current_data.len(), None)
}
}
/// Returns true if the stream contains Vorbis data, then resets it to where it was.
fn is_vorbis<R>(mut data: R) -> bool
where
R: Read + Seek,
{
let stream_pos = data.stream_position().unwrap();
if OggStreamReader::new(data.by_ref()).is_err() {
data.seek(SeekFrom::Start(stream_pos)).unwrap();
return false;
}
data.seek(SeekFrom::Start(stream_pos)).unwrap();
true
}

221
vendor/rodio/src/decoder/wav.rs vendored Normal file
View File

@@ -0,0 +1,221 @@
use std::io::{Read, Seek, SeekFrom};
use std::time::Duration;
use crate::source::SeekError;
use crate::Source;
use hound::{SampleFormat, WavReader};
/// Decoder for the WAV format.
pub struct WavDecoder<R>
where
R: Read + Seek,
{
reader: SamplesIterator<R>,
total_duration: Duration,
sample_rate: u32,
channels: u16,
}
impl<R> WavDecoder<R>
where
R: Read + Seek,
{
/// Attempts to decode the data as WAV.
pub fn new(mut data: R) -> Result<WavDecoder<R>, R> {
if !is_wave(data.by_ref()) {
return Err(data);
}
let reader = WavReader::new(data).unwrap();
let spec = reader.spec();
let len = reader.len() as u64;
let reader = SamplesIterator {
reader,
samples_read: 0,
};
let sample_rate = spec.sample_rate;
let channels = spec.channels;
let total_duration =
Duration::from_micros((1_000_000 * len) / (sample_rate as u64 * channels as u64));
Ok(WavDecoder {
reader,
total_duration,
sample_rate,
channels,
})
}
pub fn into_inner(self) -> R {
self.reader.reader.into_inner()
}
}
struct SamplesIterator<R>
where
R: Read + Seek,
{
reader: WavReader<R>,
samples_read: u32, // wav header is u32 so this suffices
}
impl<R> Iterator for SamplesIterator<R>
where
R: Read + Seek,
{
type Item = i16;
#[inline]
fn next(&mut self) -> Option<i16> {
let spec = self.reader.spec();
match (spec.sample_format, spec.bits_per_sample) {
(SampleFormat::Float, 32) => self.reader.samples().next().map(|value| {
self.samples_read += 1;
f32_to_i16(value.unwrap_or(0.0))
}),
(SampleFormat::Int, 8) => self.reader.samples().next().map(|value| {
self.samples_read += 1;
i8_to_i16(value.unwrap_or(0))
}),
(SampleFormat::Int, 16) => self.reader.samples().next().map(|value| {
self.samples_read += 1;
value.unwrap_or(0)
}),
(SampleFormat::Int, 24) => self.reader.samples().next().map(|value| {
self.samples_read += 1;
i24_to_i16(value.unwrap_or(0))
}),
(SampleFormat::Int, 32) => self.reader.samples().next().map(|value| {
self.samples_read += 1;
i32_to_i16(value.unwrap_or(0))
}),
(sample_format, bits_per_sample) => {
panic!("Unimplemented wav spec: {sample_format:?}, {bits_per_sample}")
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = (self.reader.len() - self.samples_read) as usize;
(len, Some(len))
}
}
impl<R> ExactSizeIterator for SamplesIterator<R> where R: Read + Seek {}
impl<R> Source for WavDecoder<R>
where
R: Read + Seek,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
self.channels
}
#[inline]
fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
Some(self.total_duration)
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
let file_len = self.reader.reader.duration();
let new_pos = pos.as_secs_f32() * self.sample_rate() as f32;
let new_pos = new_pos as u32;
let new_pos = new_pos.min(file_len); // saturate pos at the end of the source
// make sure the next sample is for the right channel
let to_skip = self.reader.samples_read % self.channels() as u32;
self.reader
.reader
.seek(new_pos)
.map_err(SeekError::HoundDecoder)?;
self.reader.samples_read = new_pos * self.channels() as u32;
for _ in 0..to_skip {
self.next();
}
Ok(())
}
}
impl<R> Iterator for WavDecoder<R>
where
R: Read + Seek,
{
type Item = i16;
#[inline]
fn next(&mut self) -> Option<i16> {
self.reader.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.reader.size_hint()
}
}
impl<R> ExactSizeIterator for WavDecoder<R> where R: Read + Seek {}
/// Returns true if the stream contains WAV data, then resets it to where it was.
fn is_wave<R>(mut data: R) -> bool
where
R: Read + Seek,
{
let stream_pos = data.stream_position().unwrap();
if WavReader::new(data.by_ref()).is_err() {
data.seek(SeekFrom::Start(stream_pos)).unwrap();
return false;
}
data.seek(SeekFrom::Start(stream_pos)).unwrap();
true
}
/// Returns a 32 bit WAV float as an i16. WAV floats are typically in the range of
/// [-1.0, 1.0] while i16s are in the range [-32768, 32767]. Note that this
/// function definitely causes precision loss but hopefully this isn't too
/// audiable when actually playing?
fn f32_to_i16(f: f32) -> i16 {
// prefer to clip the input rather than be excessively loud.
(f.clamp(-1.0, 1.0) * i16::MAX as f32) as i16
}
/// Returns an 8-bit WAV int as an i16. This scales the sample value by a factor
/// of 256.
fn i8_to_i16(i: i8) -> i16 {
i as i16 * 256
}
/// Returns a 24 bit WAV int as an i16. Note that this is a 24 bit integer, not a
/// 32 bit one. 24 bit ints are in the range [8,388,608, 8,388,607] while i16s
/// are in the range [-32768, 32767]. Note that this function definitely causes
/// precision loss but hopefully this isn't too audiable when actually playing?
fn i24_to_i16(i: i32) -> i16 {
(i >> 8) as i16
}
/// Returns a 32 bit WAV int as an i16. 32 bit ints are in the range
/// [-2,147,483,648, 2,147,483,647] while i16s are in the range [-32768, 32767].
/// Note that this function definitely causes precision loss but hopefully this
/// isn't too audiable when actually playing?
fn i32_to_i16(i: i32) -> i16 {
(i >> 16) as i16
}

303
vendor/rodio/src/dynamic_mixer.rs vendored Normal file
View File

@@ -0,0 +1,303 @@
//! Mixer that plays multiple sounds at the same time.
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use crate::source::{SeekError, Source, UniformSourceIterator};
use crate::Sample;
/// Builds a new mixer.
///
/// You can choose the characteristics of the output thanks to this constructor. All the sounds
/// added to the mixer will be converted to these values.
///
/// After creating a mixer, you can add new sounds with the controller.
pub fn mixer<S>(
channels: u16,
sample_rate: u32,
) -> (Arc<DynamicMixerController<S>>, DynamicMixer<S>)
where
S: Sample + Send + 'static,
{
let input = Arc::new(DynamicMixerController {
has_pending: AtomicBool::new(false),
pending_sources: Mutex::new(Vec::new()),
channels,
sample_rate,
});
let output = DynamicMixer {
current_sources: Vec::with_capacity(16),
input: input.clone(),
sample_count: 0,
still_pending: vec![],
still_current: vec![],
};
(input, output)
}
/// The input of the mixer.
pub struct DynamicMixerController<S> {
has_pending: AtomicBool,
pending_sources: Mutex<Vec<Box<dyn Source<Item = S> + Send>>>,
channels: u16,
sample_rate: u32,
}
impl<S> DynamicMixerController<S>
where
S: Sample + Send + 'static,
{
/// Adds a new source to mix to the existing ones.
#[inline]
pub fn add<T>(&self, source: T)
where
T: Source<Item = S> + Send + 'static,
{
let uniform_source = UniformSourceIterator::new(source, self.channels, self.sample_rate);
self.pending_sources
.lock()
.unwrap()
.push(Box::new(uniform_source) as Box<_>);
self.has_pending.store(true, Ordering::SeqCst); // TODO: can we relax this ordering?
}
}
/// The output of the mixer. Implements `Source`.
pub struct DynamicMixer<S> {
// The current iterator that produces samples.
current_sources: Vec<Box<dyn Source<Item = S> + Send>>,
// The pending sounds.
input: Arc<DynamicMixerController<S>>,
// The number of samples produced so far.
sample_count: usize,
// A temporary vec used in start_pending_sources.
still_pending: Vec<Box<dyn Source<Item = S> + Send>>,
// A temporary vec used in sum_current_sources.
still_current: Vec<Box<dyn Source<Item = S> + Send>>,
}
impl<S> Source for DynamicMixer<S>
where
S: Sample + Send + 'static,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Err(SeekError::NotSupported {
underlying_source: std::any::type_name::<Self>(),
})
// uncomment when #510 is implemented (query position of playback)
// let mut org_positions = Vec::with_capacity(self.current_sources.len());
// let mut encounterd_err = None;
//
// for source in &mut self.current_sources {
// let pos = /* source.playback_pos() */ todo!();
// if let Err(e) = source.try_seek(pos) {
// encounterd_err = Some(e);
// break;
// } else {
// // store pos in case we need to roll back
// org_positions.push(pos);
// }
// }
//
// if let Some(e) = encounterd_err {
// // rollback seeks that happend before err
// for (pos, source) in org_positions
// .into_iter()
// .zip(self.current_sources.iter_mut())
// {
// source.try_seek(pos)?;
// }
// Err(e)
// } else {
// Ok(())
// }
}
}
impl<S> Iterator for DynamicMixer<S>
where
S: Sample + Send + 'static,
{
type Item = S;
#[inline]
fn next(&mut self) -> Option<S> {
if self.input.has_pending.load(Ordering::SeqCst) {
self.start_pending_sources();
}
self.sample_count += 1;
let sum = self.sum_current_sources();
if self.current_sources.is_empty() {
None
} else {
Some(sum)
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(0, None)
}
}
impl<S> DynamicMixer<S>
where
S: Sample + Send + 'static,
{
// Samples from the #next() function are interlaced for each of the channels.
// We need to ensure we start playing sources so that their samples are
// in-step with the modulo of the samples produced so far. Otherwise, the
// sound will play on the wrong channels, e.g. left / right will be reversed.
fn start_pending_sources(&mut self) {
let mut pending = self.input.pending_sources.lock().unwrap(); // TODO: relax ordering?
for source in pending.drain(..) {
let in_step = self.sample_count % source.channels() as usize == 0;
if in_step {
self.current_sources.push(source);
} else {
self.still_pending.push(source);
}
}
std::mem::swap(&mut self.still_pending, &mut pending);
let has_pending = !pending.is_empty();
self.input.has_pending.store(has_pending, Ordering::SeqCst); // TODO: relax ordering?
}
fn sum_current_sources(&mut self) -> S {
let mut sum = S::zero_value();
for mut source in self.current_sources.drain(..) {
if let Some(value) = source.next() {
sum = sum.saturating_add(value);
self.still_current.push(source);
}
}
std::mem::swap(&mut self.still_current, &mut self.current_sources);
sum
}
}
#[cfg(test)]
mod tests {
use crate::buffer::SamplesBuffer;
use crate::dynamic_mixer;
use crate::source::Source;
#[test]
fn basic() {
let (tx, mut rx) = dynamic_mixer::mixer(1, 48000);
tx.add(SamplesBuffer::new(1, 48000, vec![10i16, -10, 10, -10]));
tx.add(SamplesBuffer::new(1, 48000, vec![5i16, 5, 5, 5]));
assert_eq!(rx.channels(), 1);
assert_eq!(rx.sample_rate(), 48000);
assert_eq!(rx.next(), Some(15));
assert_eq!(rx.next(), Some(-5));
assert_eq!(rx.next(), Some(15));
assert_eq!(rx.next(), Some(-5));
assert_eq!(rx.next(), None);
}
#[test]
fn channels_conv() {
let (tx, mut rx) = dynamic_mixer::mixer(2, 48000);
tx.add(SamplesBuffer::new(1, 48000, vec![10i16, -10, 10, -10]));
tx.add(SamplesBuffer::new(1, 48000, vec![5i16, 5, 5, 5]));
assert_eq!(rx.channels(), 2);
assert_eq!(rx.sample_rate(), 48000);
assert_eq!(rx.next(), Some(15));
assert_eq!(rx.next(), Some(15));
assert_eq!(rx.next(), Some(-5));
assert_eq!(rx.next(), Some(-5));
assert_eq!(rx.next(), Some(15));
assert_eq!(rx.next(), Some(15));
assert_eq!(rx.next(), Some(-5));
assert_eq!(rx.next(), Some(-5));
assert_eq!(rx.next(), None);
}
#[test]
fn rate_conv() {
let (tx, mut rx) = dynamic_mixer::mixer(1, 96000);
tx.add(SamplesBuffer::new(1, 48000, vec![10i16, -10, 10, -10]));
tx.add(SamplesBuffer::new(1, 48000, vec![5i16, 5, 5, 5]));
assert_eq!(rx.channels(), 1);
assert_eq!(rx.sample_rate(), 96000);
assert_eq!(rx.next(), Some(15));
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), Some(-5));
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), Some(15));
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), Some(-5));
assert_eq!(rx.next(), None);
}
#[test]
fn start_afterwards() {
let (tx, mut rx) = dynamic_mixer::mixer(1, 48000);
tx.add(SamplesBuffer::new(1, 48000, vec![10i16, -10, 10, -10]));
assert_eq!(rx.next(), Some(10));
assert_eq!(rx.next(), Some(-10));
tx.add(SamplesBuffer::new(1, 48000, vec![5i16, 5, 6, 6, 7, 7, 7]));
assert_eq!(rx.next(), Some(15));
assert_eq!(rx.next(), Some(-5));
assert_eq!(rx.next(), Some(6));
assert_eq!(rx.next(), Some(6));
tx.add(SamplesBuffer::new(1, 48000, vec![2i16]));
assert_eq!(rx.next(), Some(9));
assert_eq!(rx.next(), Some(7));
assert_eq!(rx.next(), Some(7));
assert_eq!(rx.next(), None);
}
}

152
vendor/rodio/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,152 @@
//! Audio playback library.
//!
//! The main concept of this library is the [`Source`] trait, which
//! represents a sound (streaming or not). In order to play a sound, there are three steps:
//!
//! - Create an object that represents the streaming sound. It can be a sine wave, a buffer, a
//! [`decoder`], etc. or even your own type that implements the [`Source`] trait.
//! - Get an output stream handle to a physical device. For example, get a stream to the system's
//! default sound device with [`OutputStream::try_default()`]
//! - Call [`.play_raw(source)`](OutputStreamHandle::play_raw) on the output stream handle.
//!
//! The [`play_raw`](OutputStreamHandle::play_raw) function expects the source to produce [`f32`]s,
//! which may not be the case. If you get a compilation error, try calling
//! [`.convert_samples()`](Source::convert_samples) on the source to fix it.
//!
//! For example, here is how you would play an audio file:
//!
//! ```no_run
//! use std::fs::File;
//! use std::io::BufReader;
//! use rodio::{Decoder, OutputStream, source::Source};
//!
//! // Get an output stream handle to the default physical sound device.
//! // Note that no sound will be played if _stream is dropped
//! let (_stream, stream_handle) = OutputStream::try_default().unwrap();
//! // Load a sound from a file, using a path relative to Cargo.toml
//! let file = BufReader::new(File::open("examples/music.ogg").unwrap());
//! // Decode that sound file into a source
//! let source = Decoder::new(file).unwrap();
//! // Play the sound directly on the device
//! stream_handle.play_raw(source.convert_samples());
//!
//! // The sound plays in a separate audio thread,
//! // so we need to keep the main thread alive while it's playing.
//! std::thread::sleep(std::time::Duration::from_secs(5));
//! ```
//!
//! ## Sink
//!
//! In order to make it easier to control the playback, the rodio library also provides a type
//! named [`Sink`] which represents an audio track.
//!
//! Instead of playing the sound with [`play_raw`](OutputStreamHandle::play_raw), you can add it to
//! a [`Sink`] instead.
//!
//! - Get a [`Sink`] to the output stream, and [`.append()`](Sink::append) your sound to it.
//!
//! ```no_run
//! use std::fs::File;
//! use std::io::BufReader;
//! use std::time::Duration;
//! use rodio::{Decoder, OutputStream, Sink};
//! use rodio::source::{SineWave, Source};
//!
//! // _stream must live as long as the sink
//! let (_stream, stream_handle) = OutputStream::try_default().unwrap();
//! let sink = Sink::try_new(&stream_handle).unwrap();
//!
//! // Add a dummy source of the sake of the example.
//! let source = SineWave::new(440.0).take_duration(Duration::from_secs_f32(0.25)).amplify(0.20);
//! sink.append(source);
//!
//! // The sound plays in a separate thread. This call will block the current thread until the sink
//! // has finished playing all its queued sounds.
//! sink.sleep_until_end();
//! ```
//!
//! The [`append`](Sink::append) method will add the sound at the end of the
//! sink. It will be played when all the previous sounds have been played. If you want multiple
//! sounds to play simultaneously, you should create multiple [`Sink`]s.
//!
//! The [`Sink`] type also provides utilities such as playing/pausing or controlling the volume.
//!
//! **Please note that the [`Sink`] requires the [`OutputStream`], make sure that the OutputStream is not dropped before the sink.**
//!
//! ## Filters
//!
//! The [`Source`] trait provides various filters, similar to the standard [`Iterator`] trait.
//!
//! Example:
//!
//! ```
//! use rodio::Source;
//! use std::time::Duration;
//!
//! // Repeats the first five seconds of the sound forever.
//! # let source = rodio::source::SineWave::new(440.0);
//! let source = source.take_duration(Duration::from_secs(5)).repeat_infinite();
//! ```
//!
//! ## Alternative Decoder Backends
//!
//! [Symphonia](https://github.com/pdeljanov/Symphonia) is an alternative decoder library that can be used in place
//! of many of the default backends.
//! Currently, the main benefit is that Symphonia is the only backend that supports M4A and AAC,
//! but it may be used to implement additional optional functionality in the future.
//!
//! To use, enable either the `symphonia-all` feature to enable all Symphonia codecs
//! or enable specific codecs using one of the `symphonia-{codec name}` features.
//! If you enable one or more of the Symphonia codecs, you may want to set `default-features = false` in order
//! to avoid adding extra crates to your binary.
//! See the [available feature flags](https://docs.rs/crate/rodio/latest/features) for all options.
//!
//! ## Optional Features
//!
//! Rodio provides several optional features that are guarded with feature gates.
//!
//! ### Feature "tracing"
//!
//! The "tracing" feature replaces the print to stderr when a stream error happens with a
//! recording an error event with tracing.
//!
//! ### Feature "Noise"
//!
//! The "noise" feature adds support for white and pink noise sources. This feature requires the
//! "rand" crate.
//!
//! ## How it works under the hood
//!
//! Rodio spawns a background thread that is dedicated to reading from the sources and sending
//! the output to the device. Whenever you give up ownership of a [`Source`] in order to play it,
//! it is sent to this background thread where it will be read by rodio.
//!
//! All the sounds are mixed together by rodio before being sent to the operating system or the
//! hardware. Therefore there is no restriction on the number of sounds that play simultaneously or
//! the number of sinks that can be created (except for the fact that creating too many will slow
//! down your program).
#![cfg_attr(test, deny(missing_docs))]
pub use cpal::{
self, traits::DeviceTrait, Device, Devices, DevicesError, InputDevices, OutputDevices,
SupportedStreamConfig,
};
mod conversions;
mod sink;
mod spatial_sink;
mod stream;
pub mod buffer;
pub mod decoder;
pub mod dynamic_mixer;
pub mod queue;
pub mod source;
pub mod static_buffer;
pub use crate::conversions::Sample;
pub use crate::decoder::Decoder;
pub use crate::sink::Sink;
pub use crate::source::Source;
pub use crate::spatial_sink::SpatialSink;
pub use crate::stream::{OutputStream, OutputStreamHandle, PlayError, StreamError};

319
vendor/rodio/src/queue.rs vendored Normal file
View File

@@ -0,0 +1,319 @@
//! Queue that plays sounds one after the other.
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use crate::source::{Empty, SeekError, Source, Zero};
use crate::Sample;
#[cfg(feature = "crossbeam-channel")]
use crossbeam_channel::{unbounded as channel, Receiver, Sender};
#[cfg(not(feature = "crossbeam-channel"))]
use std::sync::mpsc::{channel, Receiver, Sender};
/// Builds a new queue. It consists of an input and an output.
///
/// The input can be used to add sounds to the end of the queue, while the output implements
/// `Source` and plays the sounds.
///
/// The parameter indicates how the queue should behave if the queue becomes empty:
///
/// - If you pass `true`, then the queue is infinite and will play a silence instead until you add
/// a new sound.
/// - If you pass `false`, then the queue will report that it has finished playing.
///
pub fn queue<S>(keep_alive_if_empty: bool) -> (Arc<SourcesQueueInput<S>>, SourcesQueueOutput<S>)
where
S: Sample + Send + 'static,
{
let input = Arc::new(SourcesQueueInput {
next_sounds: Mutex::new(Vec::new()),
keep_alive_if_empty: AtomicBool::new(keep_alive_if_empty),
});
let output = SourcesQueueOutput {
current: Box::new(Empty::<S>::new()) as Box<_>,
signal_after_end: None,
input: input.clone(),
};
(input, output)
}
// TODO: consider reimplementing this with `from_factory`
type Sound<S> = Box<dyn Source<Item = S> + Send>;
type SignalDone = Option<Sender<()>>;
/// The input of the queue.
pub struct SourcesQueueInput<S> {
next_sounds: Mutex<Vec<(Sound<S>, SignalDone)>>,
// See constructor.
keep_alive_if_empty: AtomicBool,
}
impl<S> SourcesQueueInput<S>
where
S: Sample + Send + 'static,
{
/// Adds a new source to the end of the queue.
#[inline]
pub fn append<T>(&self, source: T)
where
T: Source<Item = S> + Send + 'static,
{
self.next_sounds
.lock()
.unwrap()
.push((Box::new(source) as Box<_>, None));
}
/// Adds a new source to the end of the queue.
///
/// The `Receiver` will be signalled when the sound has finished playing.
///
/// Enable the feature flag `crossbeam-channel` in rodio to use a `crossbeam_channel::Receiver` instead.
#[inline]
pub fn append_with_signal<T>(&self, source: T) -> Receiver<()>
where
T: Source<Item = S> + Send + 'static,
{
let (tx, rx) = channel();
self.next_sounds
.lock()
.unwrap()
.push((Box::new(source) as Box<_>, Some(tx)));
rx
}
/// Sets whether the queue stays alive if there's no more sound to play.
///
/// See also the constructor.
pub fn set_keep_alive_if_empty(&self, keep_alive_if_empty: bool) {
self.keep_alive_if_empty
.store(keep_alive_if_empty, Ordering::Release);
}
/// Removes all the sounds from the queue. Returns the number of sounds cleared.
pub fn clear(&self) -> usize {
let mut sounds = self.next_sounds.lock().unwrap();
let len = sounds.len();
sounds.clear();
len
}
}
/// The output of the queue. Implements `Source`.
pub struct SourcesQueueOutput<S> {
// The current iterator that produces samples.
current: Box<dyn Source<Item = S> + Send>,
// Signal this sender before picking from `next`.
signal_after_end: Option<Sender<()>>,
// The next sounds.
input: Arc<SourcesQueueInput<S>>,
}
const THRESHOLD: usize = 512;
impl<S> Source for SourcesQueueOutput<S>
where
S: Sample + Send + 'static,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
// This function is non-trivial because the boundary between two sounds in the queue should
// be a frame boundary as well.
//
// The current sound is free to return `None` for `current_frame_len()`, in which case
// we *should* return the number of samples remaining the current sound.
// This can be estimated with `size_hint()`.
//
// If the `size_hint` is `None` as well, we are in the worst case scenario. To handle this
// situation we force a frame to have a maximum number of samples indicate by this
// constant.
// Try the current `current_frame_len`.
if let Some(val) = self.current.current_frame_len() {
if val != 0 {
return Some(val);
} else if self.input.keep_alive_if_empty.load(Ordering::Acquire)
&& self.input.next_sounds.lock().unwrap().is_empty()
{
// The next source will be a filler silence which will have the length of `THRESHOLD`
return Some(THRESHOLD);
}
}
// Try the size hint.
let (lower_bound, _) = self.current.size_hint();
// The iterator default implementation just returns 0.
// That's a problematic value, so skip it.
if lower_bound > 0 {
return Some(lower_bound);
}
// Otherwise we use the constant value.
Some(THRESHOLD)
}
#[inline]
fn channels(&self) -> u16 {
self.current.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.current.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
/// Only seeks within the current source.
// We can not go back to previous sources. We could implement seek such
// that it advances the queue if the position is beyond the current song.
//
// We would then however need to enable seeking backwards across sources too.
// That no longer seems in line with the queue behaviour.
//
// A final pain point is that we would need the total duration for the
// next few songs.
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.current.try_seek(pos)
}
}
impl<S> Iterator for SourcesQueueOutput<S>
where
S: Sample + Send + 'static,
{
type Item = S;
#[inline]
fn next(&mut self) -> Option<S> {
loop {
// Basic situation that will happen most of the time.
if let Some(sample) = self.current.next() {
return Some(sample);
}
// Since `self.current` has finished, we need to pick the next sound.
// In order to avoid inlining this expensive operation, the code is in another function.
if self.go_next().is_err() {
return None;
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.current.size_hint().0, None)
}
}
impl<S> SourcesQueueOutput<S>
where
S: Sample + Send + 'static,
{
// Called when `current` is empty and we must jump to the next element.
// Returns `Ok` if the sound should continue playing, or an error if it should stop.
//
// This method is separate so that it is not inlined.
fn go_next(&mut self) -> Result<(), ()> {
if let Some(signal_after_end) = self.signal_after_end.take() {
let _ = signal_after_end.send(());
}
let (next, signal_after_end) = {
let mut next = self.input.next_sounds.lock().unwrap();
if next.len() == 0 {
let silence = Box::new(Zero::<S>::new_samples(1, 44100, THRESHOLD)) as Box<_>;
if self.input.keep_alive_if_empty.load(Ordering::Acquire) {
// Play a short silence in order to avoid spinlocking.
(silence, None)
} else {
return Err(());
}
} else {
next.remove(0)
}
};
self.current = next;
self.signal_after_end = signal_after_end;
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::buffer::SamplesBuffer;
use crate::queue;
use crate::source::Source;
#[test]
#[ignore] // FIXME: samples rate and channel not updated immediately after transition
fn basic() {
let (tx, mut rx) = queue::queue(false);
tx.append(SamplesBuffer::new(1, 48000, vec![10i16, -10, 10, -10]));
tx.append(SamplesBuffer::new(2, 96000, vec![5i16, 5, 5, 5]));
assert_eq!(rx.channels(), 1);
assert_eq!(rx.sample_rate(), 48000);
assert_eq!(rx.next(), Some(10));
assert_eq!(rx.next(), Some(-10));
assert_eq!(rx.next(), Some(10));
assert_eq!(rx.next(), Some(-10));
assert_eq!(rx.channels(), 2);
assert_eq!(rx.sample_rate(), 96000);
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), None);
}
#[test]
fn immediate_end() {
let (_, mut rx) = queue::queue::<i16>(false);
assert_eq!(rx.next(), None);
}
#[test]
fn keep_alive() {
let (tx, mut rx) = queue::queue(true);
tx.append(SamplesBuffer::new(1, 48000, vec![10i16, -10, 10, -10]));
assert_eq!(rx.next(), Some(10));
assert_eq!(rx.next(), Some(-10));
assert_eq!(rx.next(), Some(10));
assert_eq!(rx.next(), Some(-10));
for _ in 0..100000 {
assert_eq!(rx.next(), Some(0));
}
}
#[test]
#[ignore] // TODO: not yet implemented
fn no_delay_when_added() {
let (tx, mut rx) = queue::queue(true);
for _ in 0..500 {
assert_eq!(rx.next(), Some(0));
}
tx.append(SamplesBuffer::new(1, 48000, vec![10i16, -10, 10, -10]));
assert_eq!(rx.next(), Some(10));
assert_eq!(rx.next(), Some(-10));
assert_eq!(rx.next(), Some(10));
assert_eq!(rx.next(), Some(-10));
}
}

449
vendor/rodio/src/sink.rs vendored Normal file
View File

@@ -0,0 +1,449 @@
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
#[cfg(feature = "crossbeam-channel")]
use crossbeam_channel::{Receiver, Sender};
#[cfg(not(feature = "crossbeam-channel"))]
use std::sync::mpsc::{Receiver, Sender};
use crate::source::SeekError;
use crate::stream::{OutputStreamHandle, PlayError};
use crate::{queue, source::Done, Sample, Source};
use cpal::FromSample;
/// Handle to a device that outputs sounds.
///
/// Dropping the `Sink` stops all sounds. You can use `detach` if you want the sounds to continue
/// playing.
pub struct Sink {
queue_tx: Arc<queue::SourcesQueueInput<f32>>,
sleep_until_end: Mutex<Option<Receiver<()>>>,
controls: Arc<Controls>,
sound_count: Arc<AtomicUsize>,
detached: bool,
}
struct SeekOrder {
pos: Duration,
feedback: Sender<Result<(), SeekError>>,
}
impl SeekOrder {
fn new(pos: Duration) -> (Self, Receiver<Result<(), SeekError>>) {
#[cfg(not(feature = "crossbeam-channel"))]
let (tx, rx) = {
use std::sync::mpsc;
mpsc::channel()
};
#[cfg(feature = "crossbeam-channel")]
let (tx, rx) = {
use crossbeam_channel::bounded;
bounded(1)
};
(Self { pos, feedback: tx }, rx)
}
fn attempt<S>(self, maybe_seekable: &mut S)
where
S: Source,
S::Item: Sample + Send,
{
let res = maybe_seekable.try_seek(self.pos);
let _ignore_receiver_dropped = self.feedback.send(res);
}
}
struct Controls {
pause: AtomicBool,
volume: Mutex<f32>,
stopped: AtomicBool,
speed: Mutex<f32>,
to_clear: Mutex<u32>,
seek: Mutex<Option<SeekOrder>>,
position: Mutex<Duration>,
}
impl Sink {
/// Builds a new `Sink`, beginning playback on a stream.
#[inline]
pub fn try_new(stream: &OutputStreamHandle) -> Result<Sink, PlayError> {
let (sink, queue_rx) = Sink::new_idle();
stream.play_raw(queue_rx)?;
Ok(sink)
}
/// Builds a new `Sink`.
#[inline]
pub fn new_idle() -> (Sink, queue::SourcesQueueOutput<f32>) {
let (queue_tx, queue_rx) = queue::queue(true);
let sink = Sink {
queue_tx,
sleep_until_end: Mutex::new(None),
controls: Arc::new(Controls {
pause: AtomicBool::new(false),
volume: Mutex::new(1.0),
stopped: AtomicBool::new(false),
speed: Mutex::new(1.0),
to_clear: Mutex::new(0),
seek: Mutex::new(None),
position: Mutex::new(Duration::ZERO),
}),
sound_count: Arc::new(AtomicUsize::new(0)),
detached: false,
};
(sink, queue_rx)
}
/// Appends a sound to the queue of sounds to play.
#[inline]
pub fn append<S>(&self, source: S)
where
S: Source + Send + 'static,
f32: FromSample<S::Item>,
S::Item: Sample + Send,
{
// Wait for queue to flush then resume stopped playback
if self.controls.stopped.load(Ordering::SeqCst) {
if self.sound_count.load(Ordering::SeqCst) > 0 {
self.sleep_until_end();
}
self.controls.stopped.store(false, Ordering::SeqCst);
}
let controls = self.controls.clone();
let start_played = AtomicBool::new(false);
let source = source
.speed(1.0)
// must be placed before pausable but after speed & delay
.track_position()
.pausable(false)
.amplify(1.0)
.skippable()
.stoppable()
// if you change the duration update the docs for try_seek!
.periodic_access(Duration::from_millis(5), move |src| {
if controls.stopped.load(Ordering::SeqCst) {
src.stop();
*controls.position.lock().unwrap() = Duration::ZERO;
}
{
let mut to_clear = controls.to_clear.lock().unwrap();
if *to_clear > 0 {
src.inner_mut().skip();
*to_clear -= 1;
*controls.position.lock().unwrap() = Duration::ZERO;
} else {
*controls.position.lock().unwrap() = src.inner().inner().inner().inner().get_pos();
}
}
let amp = src.inner_mut().inner_mut();
amp.set_factor(*controls.volume.lock().unwrap());
amp.inner_mut()
.set_paused(controls.pause.load(Ordering::SeqCst));
amp.inner_mut()
.inner_mut()
.inner_mut()
.set_factor(*controls.speed.lock().unwrap());
if let Some(seek) = controls.seek.lock().unwrap().take() {
seek.attempt(amp)
}
start_played.store(true, Ordering::SeqCst);
})
.convert_samples();
self.sound_count.fetch_add(1, Ordering::Relaxed);
let source = Done::new(source, self.sound_count.clone());
*self.sleep_until_end.lock().unwrap() = Some(self.queue_tx.append_with_signal(source));
}
/// Gets the volume of the sound.
///
/// The value `1.0` is the "normal" volume (unfiltered input). Any value other than 1.0 will
/// multiply each sample by this value.
#[inline]
pub fn volume(&self) -> f32 {
*self.controls.volume.lock().unwrap()
}
/// Changes the volume of the sound.
///
/// The value `1.0` is the "normal" volume (unfiltered input). Any value other than `1.0` will
/// multiply each sample by this value.
#[inline]
pub fn set_volume(&self, value: f32) {
*self.controls.volume.lock().unwrap() = value;
}
/// Changes the play speed of the sound. Does not adjust the samples, only the playback speed.
///
/// # Note:
/// 1. **Increasing the speed will increase the pitch by the same factor**
/// - If you set the speed to 0.5 this will halve the frequency of the sound
/// lowering its pitch.
/// - If you set the speed to 2 the frequency will double raising the
/// pitch of the sound.
/// 2. **Change in the speed affect the total duration inversely**
/// - If you set the speed to 0.5, the total duration will be twice as long.
/// - If you set the speed to 2 the total duration will be halve of what it
/// was.
///
/// See [`Speed`] for details
#[inline]
pub fn speed(&self) -> f32 {
*self.controls.speed.lock().unwrap()
}
/// Changes the speed of the sound.
///
/// The value `1.0` is the "normal" speed (unfiltered input). Any value other than `1.0` will
/// change the play speed of the sound.
///
/// #### Note:
/// 1. **Increasing the speed would also increase the pitch by the same factor**
/// - If you increased set the speed to 0.5, the frequency would be slower (0.5x the original frequency) .
/// - Also if you set the speed to 1.5 the frequency would be faster ( 1.5x the original frequency).
/// 2. **Change in the speed would affect your total duration inversely**
/// - if you set the speed by 0.5, your total duration would be (2x the original total duration) longer.
/// - Also if you set the speed to 2 the total duration would be (0.5 the original total_duration) shorter
#[inline]
pub fn set_speed(&self, value: f32) {
*self.controls.speed.lock().unwrap() = value;
}
/// Resumes playback of a paused sink.
///
/// No effect if not paused.
#[inline]
pub fn play(&self) {
self.controls.pause.store(false, Ordering::SeqCst);
}
// There is no `can_seek()` method as it is impossible to use correctly. Between
// checking if a source supports seeking and actually seeking the sink can
// switch to a new source.
/// Attempts to seek to a given position in the current source.
///
/// This blocks between 0 and ~5 milliseconds.
///
/// As long as the duration of the source is known, seek is guaranteed to saturate
/// at the end of the source. For example given a source that reports a total duration
/// of 42 seconds calling `try_seek()` with 60 seconds as argument will seek to
/// 42 seconds.
///
/// # Errors
/// This function will return [`SeekError::NotSupported`] if one of the underlying
/// sources does not support seeking.
///
/// It will return an error if an implementation ran
/// into one during the seek.
///
/// When seeking beyond the end of a source this
/// function might return an error if the duration of the source is not known.
pub fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
let (order, feedback) = SeekOrder::new(pos);
*self.controls.seek.lock().unwrap() = Some(order);
if self.sound_count.load(Ordering::Acquire) == 0 {
// No sound is playing, seek will not be performed
return Ok(());
}
match feedback.recv() {
Ok(seek_res) => {
*self.controls.position.lock().unwrap() = pos;
seek_res
}
// The feedback channel closed. Probably another SeekOrder was set
// invalidating this one and closing the feedback channel
// ... or the audio thread panicked.
Err(_) => Ok(()),
}
}
/// Pauses playback of this sink.
///
/// No effect if already paused.
///
/// A paused sink can be resumed with `play()`.
pub fn pause(&self) {
self.controls.pause.store(true, Ordering::SeqCst);
}
/// Gets if a sink is paused
///
/// Sinks can be paused and resumed using `pause()` and `play()`. This returns `true` if the
/// sink is paused.
pub fn is_paused(&self) -> bool {
self.controls.pause.load(Ordering::SeqCst)
}
/// Removes all currently loaded `Source`s from the `Sink`, and pauses it.
///
/// See `pause()` for information about pausing a `Sink`.
pub fn clear(&self) {
let len = self.sound_count.load(Ordering::SeqCst) as u32;
*self.controls.to_clear.lock().unwrap() = len;
self.sleep_until_end();
self.pause();
}
/// Skips to the next `Source` in the `Sink`
///
/// If there are more `Source`s appended to the `Sink` at the time,
/// it will play the next one. Otherwise, the `Sink` will finish as if
/// it had finished playing a `Source` all the way through.
pub fn skip_one(&self) {
let len = self.sound_count.load(Ordering::SeqCst) as u32;
let mut to_clear = self.controls.to_clear.lock().unwrap();
if len > *to_clear {
*to_clear += 1;
}
}
/// Stops the sink by emptying the queue.
#[inline]
pub fn stop(&self) {
self.controls.stopped.store(true, Ordering::SeqCst);
}
/// Destroys the sink without stopping the sounds that are still playing.
#[inline]
pub fn detach(mut self) {
self.detached = true;
}
/// Sleeps the current thread until the sound ends.
#[inline]
pub fn sleep_until_end(&self) {
if let Some(sleep_until_end) = self.sleep_until_end.lock().unwrap().take() {
let _ = sleep_until_end.recv();
}
}
/// Returns true if this sink has no more sounds to play.
#[inline]
pub fn empty(&self) -> bool {
self.len() == 0
}
/// Returns the number of sounds currently in the queue.
#[allow(clippy::len_without_is_empty)]
#[inline]
pub fn len(&self) -> usize {
self.sound_count.load(Ordering::Relaxed)
}
/// Returns the position of the sound that's being played.
///
/// This takes into account any speedup or delay applied.
///
/// Example: if you apply a speedup of *2* to an mp3 decoder source and
/// [`get_pos()`](Sink::get_pos) returns *5s* then the position in the mp3
/// recording is *10s* from its start.
#[inline]
pub fn get_pos(&self) -> Duration {
*self.controls.position.lock().unwrap()
}
}
impl Drop for Sink {
#[inline]
fn drop(&mut self) {
self.queue_tx.set_keep_alive_if_empty(false);
if !self.detached {
self.controls.stopped.store(true, Ordering::Relaxed);
}
}
}
#[cfg(test)]
mod tests {
use crate::buffer::SamplesBuffer;
use crate::{Sink, Source};
use std::sync::atomic::Ordering;
#[test]
fn test_pause_and_stop() {
let (sink, mut queue_rx) = Sink::new_idle();
// assert_eq!(queue_rx.next(), Some(0.0));
let v = vec![10i16, -10, 20, -20, 30, -30];
// Low rate to ensure immediate control.
sink.append(SamplesBuffer::new(1, 1, v.clone()));
let mut src = SamplesBuffer::new(1, 1, v).convert_samples();
assert_eq!(queue_rx.next(), src.next());
assert_eq!(queue_rx.next(), src.next());
sink.pause();
assert_eq!(queue_rx.next(), Some(0.0));
sink.play();
assert_eq!(queue_rx.next(), src.next());
assert_eq!(queue_rx.next(), src.next());
sink.stop();
assert_eq!(queue_rx.next(), Some(0.0));
assert_eq!(sink.empty(), true);
}
#[test]
fn test_stop_and_start() {
let (sink, mut queue_rx) = Sink::new_idle();
let v = vec![10i16, -10, 20, -20, 30, -30];
sink.append(SamplesBuffer::new(1, 1, v.clone()));
let mut src = SamplesBuffer::new(1, 1, v.clone()).convert_samples();
assert_eq!(queue_rx.next(), src.next());
assert_eq!(queue_rx.next(), src.next());
sink.stop();
assert!(sink.controls.stopped.load(Ordering::SeqCst));
assert_eq!(queue_rx.next(), Some(0.0));
src = SamplesBuffer::new(1, 1, v.clone()).convert_samples();
sink.append(SamplesBuffer::new(1, 1, v));
assert!(!sink.controls.stopped.load(Ordering::SeqCst));
// Flush silence
let mut queue_rx = queue_rx.skip_while(|v| *v == 0.0);
assert_eq!(queue_rx.next(), src.next());
assert_eq!(queue_rx.next(), src.next());
}
#[test]
fn test_volume() {
let (sink, mut queue_rx) = Sink::new_idle();
let v = vec![10i16, -10, 20, -20, 30, -30];
// High rate to avoid immediate control.
sink.append(SamplesBuffer::new(2, 44100, v.clone()));
let src = SamplesBuffer::new(2, 44100, v.clone()).convert_samples();
let mut src = src.amplify(0.5);
sink.set_volume(0.5);
for _ in 0..v.len() {
assert_eq!(queue_rx.next(), src.next());
}
}
}

490
vendor/rodio/src/source/agc.rs vendored Normal file
View File

@@ -0,0 +1,490 @@
//
// Automatic Gain Control (AGC) Algorithm
// Designed by @UnknownSuperficialNight
//
// Features:
// • Adaptive peak detection
// • RMS-based level estimation
// • Asymmetric attack/release
// • RMS-based general adjustments with peak limiting
//
// Optimized for smooth and responsive gain control
//
// Crafted with love. Enjoy! :)
//
use super::SeekError;
use crate::{Sample, Source};
#[cfg(feature = "experimental")]
use atomic_float::AtomicF32;
#[cfg(feature = "experimental")]
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(feature = "experimental")]
use std::sync::Arc;
use std::time::Duration;
#[cfg(feature = "tracing")]
use tracing;
/// Ensures `RMS_WINDOW_SIZE` is a power of two
const fn power_of_two(n: usize) -> usize {
assert!(
n.is_power_of_two(),
"RMS_WINDOW_SIZE must be a power of two"
);
n
}
/// Size of the circular buffer used for RMS calculation.
/// A larger size provides more stable RMS values but increases latency.
const RMS_WINDOW_SIZE: usize = power_of_two(8192);
#[cfg(feature = "experimental")]
/// Automatic Gain Control filter for maintaining consistent output levels.
///
/// This struct implements an AGC algorithm that dynamically adjusts audio levels
/// based on both **peak** and **RMS** (Root Mean Square) measurements.
#[derive(Clone, Debug)]
pub struct AutomaticGainControl<I> {
input: I,
target_level: Arc<AtomicF32>,
absolute_max_gain: Arc<AtomicF32>,
current_gain: f32,
attack_coeff: Arc<AtomicF32>,
release_coeff: Arc<AtomicF32>,
min_attack_coeff: f32,
peak_level: f32,
rms_window: CircularBuffer,
is_enabled: Arc<AtomicBool>,
}
#[cfg(not(feature = "experimental"))]
/// Automatic Gain Control filter for maintaining consistent output levels.
///
/// This struct implements an AGC algorithm that dynamically adjusts audio levels
/// based on both **peak** and **RMS** (Root Mean Square) measurements.
#[derive(Clone, Debug)]
pub struct AutomaticGainControl<I> {
input: I,
target_level: f32,
absolute_max_gain: f32,
current_gain: f32,
attack_coeff: f32,
release_coeff: f32,
min_attack_coeff: f32,
peak_level: f32,
rms_window: CircularBuffer,
is_enabled: bool,
}
/// A circular buffer for efficient RMS calculation over a sliding window.
///
/// This structure allows for constant-time updates and mean calculations,
/// which is crucial for real-time audio processing.
#[derive(Clone, Debug)]
struct CircularBuffer {
buffer: Box<[f32; RMS_WINDOW_SIZE]>,
sum: f32,
index: usize,
}
impl CircularBuffer {
/// Creates a new `CircularBuffer` with a fixed size determined at compile time.
#[inline]
fn new() -> Self {
CircularBuffer {
buffer: Box::new([0.0; RMS_WINDOW_SIZE]),
sum: 0.0,
index: 0,
}
}
/// Pushes a new value into the buffer and returns the old value.
///
/// This method maintains a running sum for efficient mean calculation.
#[inline]
fn push(&mut self, value: f32) -> f32 {
let old_value = self.buffer[self.index];
// Update the sum by first subtracting the old value and then adding the new value; this is more accurate.
self.sum = self.sum - old_value + value;
self.buffer[self.index] = value;
// Use bitwise AND for efficient index wrapping since RMS_WINDOW_SIZE is a power of two.
self.index = (self.index + 1) & (RMS_WINDOW_SIZE - 1);
old_value
}
/// Calculates the mean of all values in the buffer.
///
/// This operation is `O(1)` due to the maintained running sum.
#[inline]
fn mean(&self) -> f32 {
self.sum / RMS_WINDOW_SIZE as f32
}
}
/// Constructs an `AutomaticGainControl` object with specified parameters.
///
/// # Arguments
///
/// * `input` - The input audio source
/// * `target_level` - The desired output level
/// * `attack_time` - Time constant for gain increase
/// * `release_time` - Time constant for gain decrease
/// * `absolute_max_gain` - Maximum allowable gain
#[inline]
pub(crate) fn automatic_gain_control<I>(
input: I,
target_level: f32,
attack_time: f32,
release_time: f32,
absolute_max_gain: f32,
) -> AutomaticGainControl<I>
where
I: Source,
I::Item: Sample,
{
let sample_rate = input.sample_rate();
let attack_coeff = (-1.0 / (attack_time * sample_rate as f32)).exp();
let release_coeff = (-1.0 / (release_time * sample_rate as f32)).exp();
#[cfg(feature = "experimental")]
{
AutomaticGainControl {
input,
target_level: Arc::new(AtomicF32::new(target_level)),
absolute_max_gain: Arc::new(AtomicF32::new(absolute_max_gain)),
current_gain: 1.0,
attack_coeff: Arc::new(AtomicF32::new(attack_coeff)),
release_coeff: Arc::new(AtomicF32::new(release_coeff)),
min_attack_coeff: release_time,
peak_level: 0.0,
rms_window: CircularBuffer::new(),
is_enabled: Arc::new(AtomicBool::new(true)),
}
}
#[cfg(not(feature = "experimental"))]
{
AutomaticGainControl {
input,
target_level,
absolute_max_gain,
current_gain: 1.0,
attack_coeff,
release_coeff,
min_attack_coeff: release_time,
peak_level: 0.0,
rms_window: CircularBuffer::new(),
is_enabled: true,
}
}
}
impl<I> AutomaticGainControl<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn target_level(&self) -> f32 {
#[cfg(feature = "experimental")]
{
self.target_level.load(Ordering::Relaxed)
}
#[cfg(not(feature = "experimental"))]
{
self.target_level
}
}
#[inline]
fn absolute_max_gain(&self) -> f32 {
#[cfg(feature = "experimental")]
{
self.absolute_max_gain.load(Ordering::Relaxed)
}
#[cfg(not(feature = "experimental"))]
{
self.absolute_max_gain
}
}
#[inline]
fn attack_coeff(&self) -> f32 {
#[cfg(feature = "experimental")]
{
self.attack_coeff.load(Ordering::Relaxed)
}
#[cfg(not(feature = "experimental"))]
{
self.attack_coeff
}
}
#[inline]
fn release_coeff(&self) -> f32 {
#[cfg(feature = "experimental")]
{
self.release_coeff.load(Ordering::Relaxed)
}
#[cfg(not(feature = "experimental"))]
{
self.release_coeff
}
}
#[inline]
fn is_enabled(&self) -> bool {
#[cfg(feature = "experimental")]
{
self.is_enabled.load(Ordering::Relaxed)
}
#[cfg(not(feature = "experimental"))]
{
self.is_enabled
}
}
#[cfg(feature = "experimental")]
/// Access the target output level for real-time adjustment.
///
/// Use this to dynamically modify the AGC's target level while audio is processing.
/// Adjust this value to control the overall output amplitude of the processed signal.
#[inline]
pub fn get_target_level(&self) -> Arc<AtomicF32> {
Arc::clone(&self.target_level)
}
#[cfg(feature = "experimental")]
/// Access the maximum gain limit for real-time adjustment.
///
/// Use this to dynamically modify the AGC's maximum allowable gain during runtime.
/// Adjusting this value helps prevent excessive amplification in low-level signals.
#[inline]
pub fn get_absolute_max_gain(&self) -> Arc<AtomicF32> {
Arc::clone(&self.absolute_max_gain)
}
#[cfg(feature = "experimental")]
/// Access the attack coefficient for real-time adjustment.
///
/// Use this to dynamically modify how quickly the AGC responds to level increases.
/// Smaller values result in faster response, larger values in slower response.
/// Adjust during runtime to fine-tune AGC behavior for different audio content.
#[inline]
pub fn get_attack_coeff(&self) -> Arc<AtomicF32> {
Arc::clone(&self.attack_coeff)
}
#[cfg(feature = "experimental")]
/// Access the release coefficient for real-time adjustment.
///
/// Use this to dynamically modify how quickly the AGC responds to level decreases.
/// Smaller values result in faster response, larger values in slower response.
/// Adjust during runtime to optimize AGC behavior for varying audio dynamics.
#[inline]
pub fn get_release_coeff(&self) -> Arc<AtomicF32> {
Arc::clone(&self.release_coeff)
}
#[cfg(feature = "experimental")]
/// Access the AGC on/off control for real-time adjustment.
///
/// Use this to dynamically enable or disable AGC processing during runtime.
/// Useful for comparing processed and unprocessed audio or for disabling/enabling AGC at runtime.
#[inline]
pub fn get_agc_control(&self) -> Arc<AtomicBool> {
Arc::clone(&self.is_enabled)
}
#[cfg(not(feature = "experimental"))]
/// Enable or disable AGC processing.
///
/// Use this to enable or disable AGC processing.
/// Useful for comparing processed and unprocessed audio or for disabling/enabling AGC.
#[inline]
pub fn set_enabled(&mut self, enabled: bool) {
self.is_enabled = enabled;
}
/// Updates the peak level with an adaptive attack coefficient
///
/// This method adjusts the peak level using a variable attack coefficient.
/// It responds faster to sudden increases in signal level by using a
/// minimum attack coefficient of `min_attack_coeff` when the sample value exceeds the
/// current peak level. This adaptive behavior helps capture transients
/// more accurately while maintaining smoother behavior for gradual changes.
#[inline]
fn update_peak_level(&mut self, sample_value: f32) {
let attack_coeff = if sample_value > self.peak_level {
self.attack_coeff().min(self.min_attack_coeff) // User-defined attack time limited via release_time
} else {
self.release_coeff()
};
self.peak_level = attack_coeff * self.peak_level + (1.0 - attack_coeff) * sample_value;
}
/// Updates the RMS (Root Mean Square) level using a circular buffer approach.
/// This method calculates a moving average of the squared input samples,
/// providing a measure of the signal's average power over time.
#[inline]
fn update_rms(&mut self, sample_value: f32) -> f32 {
let squared_sample = sample_value * sample_value;
self.rms_window.push(squared_sample);
self.rms_window.mean().sqrt()
}
/// Calculate gain adjustments based on peak levels
/// This method determines the appropriate gain level to apply to the audio
/// signal, considering the peak level.
/// The peak level helps prevent sudden spikes in the output signal.
#[inline]
fn calculate_peak_gain(&self) -> f32 {
if self.peak_level > 0.0 {
(self.target_level() / self.peak_level).min(self.absolute_max_gain())
} else {
self.absolute_max_gain()
}
}
#[inline]
fn process_sample(&mut self, sample: I::Item) -> I::Item {
// Convert the sample to its absolute float value for level calculations
let sample_value = sample.to_f32().abs();
// Dynamically adjust peak level using an adaptive attack coefficient
self.update_peak_level(sample_value);
// Calculate the current RMS (Root Mean Square) level using a sliding window approach
let rms = self.update_rms(sample_value);
// Compute the gain adjustment required to reach the target level based on RMS
let rms_gain = if rms > 0.0 {
self.target_level() / rms
} else {
self.absolute_max_gain() // Default to max gain if RMS is zero
};
// Calculate the peak limiting gain
let peak_gain = self.calculate_peak_gain();
// Use RMS for general adjustments, but limit by peak gain to prevent clipping
let desired_gain = rms_gain.min(peak_gain);
// Adaptive attack/release speed for AGC (Automatic Gain Control)
//
// This mechanism implements an asymmetric approach to gain adjustment:
// 1. **Slow increase**: Prevents abrupt amplification of noise during quiet periods.
// 2. **Fast decrease**: Rapidly attenuates sudden loud signals to avoid distortion.
//
// The asymmetry is crucial because:
// - Gradual gain increases sound more natural and less noticeable to listeners.
// - Quick gain reductions are necessary to prevent clipping and maintain audio quality.
//
// This approach addresses several challenges associated with high attack times:
// 1. **Slow response**: With a high attack time, the AGC responds very slowly to changes in input level.
// This means it takes longer for the gain to adjust to new signal levels.
// 2. **Initial gain calculation**: When the audio starts or after a period of silence, the initial gain
// calculation might result in a very high gain value, especially if the input signal starts quietly.
// 3. **Overshooting**: As the gain slowly increases (due to the high attack time), it might overshoot
// the desired level, causing the signal to become too loud.
// 4. **Overcorrection**: The AGC then tries to correct this by reducing the gain, but due to the slow response,
// it might reduce the gain too much, causing the sound to drop to near-zero levels.
// 5. **Slow recovery**: Again, due to the high attack time, it takes a while for the gain to increase
// back to the appropriate level.
//
// By using a faster release time for decreasing gain, we can mitigate these issues and provide
// more responsive control over sudden level increases while maintaining smooth gain increases.
let attack_speed = if desired_gain > self.current_gain {
self.attack_coeff()
} else {
self.release_coeff()
};
// Gradually adjust the current gain towards the desired gain for smooth transitions
self.current_gain = self.current_gain * attack_speed + desired_gain * (1.0 - attack_speed);
// Ensure the calculated gain stays within the defined operational range
self.current_gain = self.current_gain.clamp(0.1, self.absolute_max_gain());
// Output current gain value for developers to fine tune their inputs to automatic_gain_control
#[cfg(feature = "tracing")]
tracing::debug!("AGC gain: {}", self.current_gain,);
// Apply the computed gain to the input sample and return the result
sample.amplify(self.current_gain)
}
/// Returns a mutable reference to the inner source.
pub fn inner(&self) -> &I {
&self.input
}
/// Returns the inner source.
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
}
impl<I> Iterator for AutomaticGainControl<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
self.input.next().map(|sample| {
if self.is_enabled() {
self.process_sample(sample)
} else {
sample
}
})
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> ExactSizeIterator for AutomaticGainControl<I>
where
I: Source + ExactSizeIterator,
I::Item: Sample,
{
}
impl<I> Source for AutomaticGainControl<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}

103
vendor/rodio/src/source/amplify.rs vendored Normal file
View File

@@ -0,0 +1,103 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Internal function that builds a `Amplify` object.
pub fn amplify<I>(input: I, factor: f32) -> Amplify<I>
where
I: Source,
I::Item: Sample,
{
Amplify { input, factor }
}
/// Filter that modifies each sample by a given value.
#[derive(Clone, Debug)]
pub struct Amplify<I> {
input: I,
factor: f32,
}
impl<I> Amplify<I> {
/// Modifies the amplification factor.
#[inline]
pub fn set_factor(&mut self, factor: f32) {
self.factor = factor;
}
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Iterator for Amplify<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
self.input.next().map(|value| value.amplify(self.factor))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> ExactSizeIterator for Amplify<I>
where
I: Source + ExactSizeIterator,
I::Item: Sample,
{
}
impl<I> Source for Amplify<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}

252
vendor/rodio/src/source/blt.rs vendored Normal file
View File

@@ -0,0 +1,252 @@
use std::f32::consts::PI;
use std::time::Duration;
use crate::Source;
use super::SeekError;
// Implemented following http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
/// Internal function that builds a `BltFilter` object.
pub fn low_pass<I>(input: I, freq: u32) -> BltFilter<I>
where
I: Source<Item = f32>,
{
low_pass_with_q(input, freq, 0.5)
}
pub fn high_pass<I>(input: I, freq: u32) -> BltFilter<I>
where
I: Source<Item = f32>,
{
high_pass_with_q(input, freq, 0.5)
}
/// Same as low_pass but allows the q value (bandwidth) to be changed
pub fn low_pass_with_q<I>(input: I, freq: u32, q: f32) -> BltFilter<I>
where
I: Source<Item = f32>,
{
BltFilter {
input,
formula: BltFormula::LowPass { freq, q },
applier: None,
x_n1: 0.0,
x_n2: 0.0,
y_n1: 0.0,
y_n2: 0.0,
}
}
/// Same as high_pass but allows the q value (bandwidth) to be changed
pub fn high_pass_with_q<I>(input: I, freq: u32, q: f32) -> BltFilter<I>
where
I: Source<Item = f32>,
{
BltFilter {
input,
formula: BltFormula::HighPass { freq, q },
applier: None,
x_n1: 0.0,
x_n2: 0.0,
y_n1: 0.0,
y_n2: 0.0,
}
}
/// This applies an audio filter, it can be a high or low pass filter.
#[derive(Clone, Debug)]
pub struct BltFilter<I> {
input: I,
formula: BltFormula,
applier: Option<BltApplier>,
x_n1: f32,
x_n2: f32,
y_n1: f32,
y_n2: f32,
}
impl<I> BltFilter<I> {
/// Modifies this filter so that it becomes a low-pass filter.
pub fn to_low_pass(&mut self, freq: u32) {
self.to_low_pass_with_q(freq, 0.5);
}
/// Modifies this filter so that it becomes a high-pass filter
pub fn to_high_pass(&mut self, freq: u32) {
self.to_high_pass_with_q(freq, 0.5);
}
/// Same as to_low_pass but allows the q value (bandwidth) to be changed
pub fn to_low_pass_with_q(&mut self, freq: u32, q: f32) {
self.formula = BltFormula::LowPass { freq, q };
self.applier = None;
}
/// Same as to_high_pass but allows the q value (bandwidth) to be changed
pub fn to_high_pass_with_q(&mut self, freq: u32, q: f32) {
self.formula = BltFormula::HighPass { freq, q };
self.applier = None;
}
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Iterator for BltFilter<I>
where
I: Source<Item = f32>,
{
type Item = f32;
#[inline]
fn next(&mut self) -> Option<f32> {
let last_in_frame = self.input.current_frame_len() == Some(1);
if self.applier.is_none() {
self.applier = Some(self.formula.to_applier(self.input.sample_rate()));
}
let sample = match self.input.next() {
None => return None,
Some(s) => s,
};
let result = self
.applier
.as_ref()
.unwrap()
.apply(sample, self.x_n1, self.x_n2, self.y_n1, self.y_n2);
self.y_n2 = self.y_n1;
self.x_n2 = self.x_n1;
self.y_n1 = result;
self.x_n1 = sample;
if last_in_frame {
self.applier = None;
}
Some(result)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> ExactSizeIterator for BltFilter<I> where I: Source<Item = f32> + ExactSizeIterator {}
impl<I> Source for BltFilter<I>
where
I: Source<Item = f32>,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}
#[derive(Clone, Debug)]
enum BltFormula {
LowPass { freq: u32, q: f32 },
HighPass { freq: u32, q: f32 },
}
impl BltFormula {
fn to_applier(&self, sampling_frequency: u32) -> BltApplier {
match *self {
BltFormula::LowPass { freq, q } => {
let w0 = 2.0 * PI * freq as f32 / sampling_frequency as f32;
let alpha = w0.sin() / (2.0 * q);
let b1 = 1.0 - w0.cos();
let b0 = b1 / 2.0;
let b2 = b0;
let a0 = 1.0 + alpha;
let a1 = -2.0 * w0.cos();
let a2 = 1.0 - alpha;
BltApplier {
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
a1: a1 / a0,
a2: a2 / a0,
}
}
BltFormula::HighPass { freq, q } => {
let w0 = 2.0 * PI * freq as f32 / sampling_frequency as f32;
let cos_w0 = w0.cos();
let alpha = w0.sin() / (2.0 * q);
let b0 = (1.0 + cos_w0) / 2.0;
let b1 = -1.0 - cos_w0;
let b2 = b0;
let a0 = 1.0 + alpha;
let a1 = -2.0 * cos_w0;
let a2 = 1.0 - alpha;
BltApplier {
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0,
a1: a1 / a0,
a2: a2 / a0,
}
}
}
}
}
#[derive(Clone, Debug)]
struct BltApplier {
b0: f32,
b1: f32,
b2: f32,
a1: f32,
a2: f32,
}
impl BltApplier {
#[inline]
fn apply(&self, x_n: f32, x_n1: f32, x_n2: f32, y_n1: f32, y_n2: f32) -> f32 {
self.b0 * x_n + self.b1 * x_n1 + self.b2 * x_n2 - self.a1 * y_n1 - self.a2 * y_n2
}
}

268
vendor/rodio/src/source/buffered.rs vendored Normal file
View File

@@ -0,0 +1,268 @@
use std::cmp;
use std::mem;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Internal function that builds a `Buffered` object.
#[inline]
pub fn buffered<I>(input: I) -> Buffered<I>
where
I: Source,
I::Item: Sample,
{
let total_duration = input.total_duration();
let first_frame = extract(input);
Buffered {
current_frame: first_frame,
position_in_frame: 0,
total_duration,
}
}
/// Iterator that at the same time extracts data from the iterator and stores it in a buffer.
pub struct Buffered<I>
where
I: Source,
I::Item: Sample,
{
/// Immutable reference to the next frame of data. Cannot be `Frame::Input`.
current_frame: Arc<Frame<I>>,
/// The position in number of samples of this iterator inside `current_frame`.
position_in_frame: usize,
/// Obtained once at creation and never modified again.
total_duration: Option<Duration>,
}
enum Frame<I>
where
I: Source,
I::Item: Sample,
{
/// Data that has already been extracted from the iterator. Also contains a pointer to the
/// next frame.
Data(FrameData<I>),
/// No more data.
End,
/// Unextracted data. The `Option` should never be `None` and is only here for easier data
/// processing.
Input(Mutex<Option<I>>),
}
struct FrameData<I>
where
I: Source,
I::Item: Sample,
{
data: Vec<I::Item>,
channels: u16,
rate: u32,
next: Mutex<Arc<Frame<I>>>,
}
impl<I> Drop for FrameData<I>
where
I: Source,
I::Item: Sample,
{
fn drop(&mut self) {
// This is necessary to prevent stack overflows deallocating long chains of the mutually
// recursive `Frame` and `FrameData` types. This iteratively traverses as much of the
// chain as needs to be deallocated, and repeatedly "pops" the head off the list. This
// solves the problem, as when the time comes to actually deallocate the `FrameData`,
// the `next` field will contain a `Frame::End`, or an `Arc` with additional references,
// so the depth of recursive drops will be bounded.
while let Ok(arc_next) = self.next.get_mut() {
if let Some(next_ref) = Arc::get_mut(arc_next) {
// This allows us to own the next Frame.
let next = mem::replace(next_ref, Frame::End);
if let Frame::Data(next_data) = next {
// Swap the current FrameData with the next one, allowing the current one
// to go out of scope.
*self = next_data;
} else {
break;
}
} else {
break;
}
}
}
}
/// Builds a frame from the input iterator.
fn extract<I>(mut input: I) -> Arc<Frame<I>>
where
I: Source,
I::Item: Sample,
{
let frame_len = input.current_frame_len();
if frame_len == Some(0) {
return Arc::new(Frame::End);
}
let channels = input.channels();
let rate = input.sample_rate();
let data: Vec<I::Item> = input
.by_ref()
.take(cmp::min(frame_len.unwrap_or(32768), 32768))
.collect();
if data.is_empty() {
return Arc::new(Frame::End);
}
Arc::new(Frame::Data(FrameData {
data,
channels,
rate,
next: Mutex::new(Arc::new(Frame::Input(Mutex::new(Some(input))))),
}))
}
impl<I> Buffered<I>
where
I: Source,
I::Item: Sample,
{
/// Advances to the next frame.
fn next_frame(&mut self) {
let next_frame = {
let mut next_frame_ptr = match &*self.current_frame {
Frame::Data(FrameData { next, .. }) => next.lock().unwrap(),
_ => unreachable!(),
};
let next_frame = match &**next_frame_ptr {
Frame::Data(_) => next_frame_ptr.clone(),
Frame::End => next_frame_ptr.clone(),
Frame::Input(input) => {
let input = input.lock().unwrap().take().unwrap();
extract(input)
}
};
*next_frame_ptr = next_frame.clone();
next_frame
};
self.current_frame = next_frame;
self.position_in_frame = 0;
}
}
impl<I> Iterator for Buffered<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
let current_sample;
let advance_frame;
match &*self.current_frame {
Frame::Data(FrameData { data, .. }) => {
current_sample = Some(data[self.position_in_frame]);
self.position_in_frame += 1;
advance_frame = self.position_in_frame >= data.len();
}
Frame::End => {
current_sample = None;
advance_frame = false;
}
Frame::Input(_) => unreachable!(),
};
if advance_frame {
self.next_frame();
}
current_sample
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
// TODO:
(0, None)
}
}
// TODO: uncomment when `size_hint` is fixed
/*impl<I> ExactSizeIterator for Amplify<I> where I: Source + ExactSizeIterator, I::Item: Sample {
}*/
impl<I> Source for Buffered<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
match &*self.current_frame {
Frame::Data(FrameData { data, .. }) => Some(data.len() - self.position_in_frame),
Frame::End => Some(0),
Frame::Input(_) => unreachable!(),
}
}
#[inline]
fn channels(&self) -> u16 {
match *self.current_frame {
Frame::Data(FrameData { channels, .. }) => channels,
Frame::End => 1,
Frame::Input(_) => unreachable!(),
}
}
#[inline]
fn sample_rate(&self) -> u32 {
match *self.current_frame {
Frame::Data(FrameData { rate, .. }) => rate,
Frame::End => 44100,
Frame::Input(_) => unreachable!(),
}
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.total_duration
}
/// Can not support seek, in the end state we lose the underlying source
/// which makes seeking back impossible.
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Err(SeekError::NotSupported {
underlying_source: std::any::type_name::<Self>(),
})
}
}
impl<I> Clone for Buffered<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn clone(&self) -> Buffered<I> {
Buffered {
current_frame: self.current_frame.clone(),
position_in_frame: self.position_in_frame,
total_duration: self.total_duration,
}
}
}

View File

@@ -0,0 +1,150 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Combines channels in input into a single mono source, then plays that mono sound
/// to each channel at the volume given for that channel.
#[derive(Clone, Debug)]
pub struct ChannelVolume<I>
where
I: Source,
I::Item: Sample,
{
input: I,
// Channel number is used as index for amplification value.
channel_volumes: Vec<f32>,
// Current listener being processed.
current_channel: usize,
current_sample: Option<I::Item>,
}
impl<I> ChannelVolume<I>
where
I: Source,
I::Item: Sample,
{
/// Wrap the input source and make it mono. Play that mono sound to each
/// channel at the volume set by the user. The volume can be changed using
/// [`ChannelVolume::set_volume`].
pub fn new(mut input: I, channel_volumes: Vec<f32>) -> ChannelVolume<I>
where
I: Source,
I::Item: Sample,
{
let mut sample = None;
for _ in 0..input.channels() {
if let Some(s) = input.next() {
sample = Some(
sample
.get_or_insert_with(I::Item::zero_value)
.saturating_add(s),
);
}
}
ChannelVolume {
input,
channel_volumes,
current_channel: 0,
current_sample: sample,
}
}
/// Sets the volume for a given channel number. Will panic if channel number
/// is invalid.
pub fn set_volume(&mut self, channel: usize, volume: f32) {
self.channel_volumes[channel] = volume;
}
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Iterator for ChannelVolume<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
let ret = self
.current_sample
.map(|sample| sample.amplify(self.channel_volumes[self.current_channel]));
self.current_channel += 1;
if self.current_channel >= self.channel_volumes.len() {
self.current_channel = 0;
self.current_sample = None;
for _ in 0..self.input.channels() {
if let Some(s) = self.input.next() {
self.current_sample = Some(
self.current_sample
.get_or_insert_with(I::Item::zero_value)
.saturating_add(s),
);
}
}
}
ret
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> ExactSizeIterator for ChannelVolume<I>
where
I: Source + ExactSizeIterator,
I::Item: Sample,
{
}
impl<I> Source for ChannelVolume<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.channel_volumes.len() as u16
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}

76
vendor/rodio/src/source/chirp.rs vendored Normal file
View File

@@ -0,0 +1,76 @@
//! Chirp/sweep source.
use std::{f32::consts::TAU, time::Duration};
use crate::Source;
/// Convenience function to create a new `Chirp` source.
#[inline]
pub fn chirp(
sample_rate: cpal::SampleRate,
start_frequency: f32,
end_frequency: f32,
duration: Duration,
) -> Chirp {
Chirp::new(sample_rate, start_frequency, end_frequency, duration)
}
/// Generate a sine wave with an instantaneous frequency that changes/sweeps linearly over time.
/// At the end of the chirp, once the `end_frequency` is reached, the source is exhausted.
#[derive(Clone, Debug)]
pub struct Chirp {
start_frequency: f32,
end_frequency: f32,
sample_rate: cpal::SampleRate,
total_samples: u64,
elapsed_samples: u64,
}
impl Chirp {
fn new(
sample_rate: cpal::SampleRate,
start_frequency: f32,
end_frequency: f32,
duration: Duration,
) -> Self {
Self {
sample_rate,
start_frequency,
end_frequency,
total_samples: (duration.as_secs_f64() * (sample_rate.0 as f64)) as u64,
elapsed_samples: 0,
}
}
}
impl Iterator for Chirp {
type Item = f32;
fn next(&mut self) -> Option<Self::Item> {
let i = self.elapsed_samples;
let ratio = self.elapsed_samples as f32 / self.total_samples as f32;
self.elapsed_samples += 1;
let freq = self.start_frequency * (1.0 - ratio) + self.end_frequency * ratio;
let t = (i as f32 / self.sample_rate() as f32) * TAU * freq;
Some(t.sin())
}
}
impl Source for Chirp {
fn current_frame_len(&self) -> Option<usize> {
None
}
fn channels(&self) -> u16 {
1
}
fn sample_rate(&self) -> u32 {
self.sample_rate.0
}
fn total_duration(&self) -> Option<Duration> {
let secs: f64 = self.total_samples as f64 / self.sample_rate.0 as f64;
Some(Duration::new(1, 0).mul_f64(secs))
}
}

79
vendor/rodio/src/source/crossfade.rs vendored Normal file
View File

@@ -0,0 +1,79 @@
use std::time::Duration;
use cpal::FromSample;
use crate::source::{FadeIn, Mix, TakeDuration};
use crate::{Sample, Source};
/// Mixes one sound fading out with another sound fading in for the given
/// duration.
///
/// Only the crossfaded portion (beginning of fadeout, beginning of fadein) is
/// returned.
pub fn crossfade<I1, I2>(
input_fadeout: I1,
input_fadein: I2,
duration: Duration,
) -> Crossfade<I1, I2>
where
I1: Source,
I2: Source,
I1::Item: FromSample<I2::Item> + Sample,
I2::Item: Sample,
{
let mut input_fadeout = input_fadeout.take_duration(duration);
input_fadeout.set_filter_fadeout();
let input_fadein = input_fadein.take_duration(duration).fade_in(duration);
input_fadeout.mix(input_fadein)
}
/// Mixes one sound fading out with another sound fading in for the given
/// duration.
///
/// Only the crossfaded portion (beginning of fadeout, beginning of fadein) is
/// covered.
pub type Crossfade<I1, I2> = Mix<TakeDuration<I1>, FadeIn<TakeDuration<I2>>>;
#[cfg(test)]
mod tests {
use super::*;
use crate::buffer::SamplesBuffer;
fn dummysource(length: u8) -> SamplesBuffer<f32> {
let data: Vec<f32> = (1..=length).map(f32::from).collect();
SamplesBuffer::new(1, 1, data)
}
#[test]
fn test_crossfade_with_self() {
let source1 = dummysource(10);
let source2 = dummysource(10);
let mut mixed = crossfade(
source1,
source2,
Duration::from_secs(5) + Duration::from_nanos(1),
);
assert_eq!(mixed.next(), Some(1.0));
assert_eq!(mixed.next(), Some(2.0));
assert_eq!(mixed.next(), Some(3.0));
assert_eq!(mixed.next(), Some(4.0));
assert_eq!(mixed.next(), Some(5.0));
assert_eq!(mixed.next(), None);
}
#[test]
fn test_crossfade() {
let source1 = dummysource(10);
let source2 = dummysource(10).amplify(0.0);
let mut mixed = crossfade(
source1,
source2,
Duration::from_secs(5) + Duration::from_nanos(1),
);
assert_eq!(mixed.next(), Some(1.0 * 1.0));
assert_eq!(mixed.next(), Some(2.0 * 0.8));
assert_eq!(mixed.next(), Some(3.0 * 0.6));
assert_eq!(mixed.next(), Some(4.0 * 0.4));
assert_eq!(mixed.next(), Some(5.0 * 0.2));
assert_eq!(mixed.next(), None);
}
}

138
vendor/rodio/src/source/delay.rs vendored Normal file
View File

@@ -0,0 +1,138 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
fn remaining_samples(until_playback: Duration, sample_rate: u32, channels: u16) -> usize {
let ns = until_playback.as_secs() * 1_000_000_000 + until_playback.subsec_nanos() as u64;
let samples = ns * channels as u64 * sample_rate as u64 / 1_000_000_000;
samples as usize
}
/// Internal function that builds a `Delay` object.
pub fn delay<I>(input: I, duration: Duration) -> Delay<I>
where
I: Source,
I::Item: Sample,
{
Delay {
remaining_samples: remaining_samples(duration, input.sample_rate(), input.channels()),
requested_duration: duration,
input,
}
}
/// A source that delays the given source by a certain amount.
#[derive(Clone, Debug)]
pub struct Delay<I> {
input: I,
remaining_samples: usize,
requested_duration: Duration,
}
impl<I> Delay<I>
where
I: Source,
I::Item: Sample,
{
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Iterator for Delay<I>
where
I: Source,
I::Item: Sample,
{
type Item = <I as Iterator>::Item;
#[inline]
fn next(&mut self) -> Option<<I as Iterator>::Item> {
if self.remaining_samples >= 1 {
self.remaining_samples -= 1;
Some(Sample::zero_value())
} else {
self.input.next()
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (min, max) = self.input.size_hint();
(
min + self.remaining_samples,
max.map(|v| v + self.remaining_samples),
)
}
}
impl<I> Source for Delay<I>
where
I: Iterator + Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input
.current_frame_len()
.map(|val| val + self.remaining_samples)
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input
.total_duration()
.map(|val| val + self.requested_duration)
}
/// Pos is seen from the perspective of the api user.
///
/// # Example
///
/// ```ignore
/// use std::time::Duration;
///
/// let mut source = inner_source.delay(Duration::from_secs(10));
/// source.try_seek(Duration::from_secs(15));
///
/// // inner_source is now at pos: Duration::from_secs(5);
/// ```
///
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
if pos < self.requested_duration {
self.input.try_seek(Duration::ZERO)?;
let until_playback = self.requested_duration - pos;
self.remaining_samples =
remaining_samples(until_playback, self.sample_rate(), self.channels());
}
let compensated_for_delay = pos.saturating_sub(self.requested_duration);
self.input.try_seek(compensated_for_delay)
}
}

99
vendor/rodio/src/source/done.rs vendored Normal file
View File

@@ -0,0 +1,99 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// When the inner source is empty this decrements a `AtomicUsize`.
#[derive(Debug, Clone)]
pub struct Done<I> {
input: I,
signal: Arc<AtomicUsize>,
signal_sent: bool,
}
impl<I> Done<I> {
/// When the inner source is empty the AtomicUsize passed in is decremented.
/// If it was zero it will overflow negatively.
#[inline]
pub fn new(input: I, signal: Arc<AtomicUsize>) -> Done<I> {
Done {
input,
signal,
signal_sent: false,
}
}
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I: Source> Iterator for Done<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
let next = self.input.next();
if !self.signal_sent && next.is_none() {
self.signal.fetch_sub(1, Ordering::Relaxed);
self.signal_sent = true;
}
next
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> Source for Done<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}

67
vendor/rodio/src/source/empty.rs vendored Normal file
View File

@@ -0,0 +1,67 @@
use std::marker::PhantomData;
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// An empty source.
#[derive(Debug, Copy, Clone)]
pub struct Empty<S>(PhantomData<S>);
impl<S> Default for Empty<S> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<S> Empty<S> {
/// An empty source that immediately ends without ever returning a sample to
/// play
#[inline]
pub fn new() -> Empty<S> {
Empty(PhantomData)
}
}
impl<S> Iterator for Empty<S> {
type Item = S;
#[inline]
fn next(&mut self) -> Option<S> {
None
}
}
impl<S> Source for Empty<S>
where
S: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
1
}
#[inline]
fn sample_rate(&self) -> u32 {
48000
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
Some(Duration::new(0, 0))
}
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Err(SeekError::NotSupported {
underlying_source: std::any::type_name::<Self>(),
})
}
}

View File

@@ -0,0 +1,70 @@
use std::marker::PhantomData;
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// An empty source which executes a callback function
pub struct EmptyCallback<S> {
#[allow(missing_docs)] // See: https://github.com/RustAudio/rodio/issues/615
pub phantom_data: PhantomData<S>,
#[allow(missing_docs)] // See: https://github.com/RustAudio/rodio/issues/615
pub callback: Box<dyn Send + Fn()>,
}
impl<S> EmptyCallback<S> {
#[inline]
/// Create an empty source which executes a callback function.
/// Example use-case:
///
/// Detect and do something when the source before this one has ended.
pub fn new(callback: Box<dyn Send + Fn()>) -> EmptyCallback<S> {
EmptyCallback {
phantom_data: PhantomData,
callback,
}
}
}
impl<S> Iterator for EmptyCallback<S> {
type Item = S;
#[inline]
fn next(&mut self) -> Option<S> {
(self.callback)();
None
}
}
impl<S> Source for EmptyCallback<S>
where
S: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
1
}
#[inline]
fn sample_rate(&self) -> u32 {
48000
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
Some(Duration::new(0, 0))
}
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Err(SeekError::NotSupported {
underlying_source: std::any::type_name::<Self>(),
})
}
}

102
vendor/rodio/src/source/fadein.rs vendored Normal file
View File

@@ -0,0 +1,102 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::{linear_ramp::linear_gain_ramp, LinearGainRamp, SeekError};
/// Internal function that builds a `FadeIn` object.
pub fn fadein<I>(input: I, duration: Duration) -> FadeIn<I>
where
I: Source,
I::Item: Sample,
{
FadeIn {
input: linear_gain_ramp(input, duration, 0.0f32, 1.0f32, false),
}
}
/// Filter that modifies raises the volume from silence over a time period.
#[derive(Clone, Debug)]
pub struct FadeIn<I> {
input: LinearGainRamp<I>,
}
impl<I> FadeIn<I>
where
I: Source,
I::Item: Sample,
{
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
self.input.inner()
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
self.input.inner_mut()
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input.into_inner()
}
}
impl<I> Iterator for FadeIn<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
self.input.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> ExactSizeIterator for FadeIn<I>
where
I: Source + ExactSizeIterator,
I::Item: Sample,
{
}
impl<I> Source for FadeIn<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.inner().current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.inner().channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.inner().sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.inner().total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.inner_mut().try_seek(pos)
}
}

102
vendor/rodio/src/source/fadeout.rs vendored Normal file
View File

@@ -0,0 +1,102 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::{linear_ramp::linear_gain_ramp, LinearGainRamp, SeekError};
/// Internal function that builds a `FadeOut` object.
pub fn fadeout<I>(input: I, duration: Duration) -> FadeOut<I>
where
I: Source,
I::Item: Sample,
{
FadeOut {
input: linear_gain_ramp(input, duration, 1.0f32, 0.0f32, true),
}
}
/// Filter that modifies lowers the volume to silence over a time period.
#[derive(Clone, Debug)]
pub struct FadeOut<I> {
input: LinearGainRamp<I>,
}
impl<I> FadeOut<I>
where
I: Source,
I::Item: Sample,
{
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
self.input.inner()
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
self.input.inner_mut()
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input.into_inner()
}
}
impl<I> Iterator for FadeOut<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
self.input.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> ExactSizeIterator for FadeOut<I>
where
I: Source + ExactSizeIterator,
I::Item: Sample,
{
}
impl<I> Source for FadeOut<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.inner().current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.inner().channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.inner().sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.inner().total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.inner_mut().try_seek(pos)
}
}

37
vendor/rodio/src/source/from_factory.rs vendored Normal file
View File

@@ -0,0 +1,37 @@
use crate::source::{from_iter, FromIter};
/// Builds a source that chains sources built from a factory.
///
/// The `factory` parameter is a function that produces a source. The source is then played.
/// Whenever the source ends, `factory` is called again in order to produce the source that is
/// played next.
///
/// If the `factory` closure returns `None`, then the sound ends.
pub fn from_factory<F, S>(factory: F) -> FromIter<FromFactoryIter<F>>
where
F: FnMut() -> Option<S>,
{
from_iter(FromFactoryIter { factory })
}
/// Internal type used by `from_factory`.
pub struct FromFactoryIter<F> {
factory: F,
}
impl<F, S> Iterator for FromFactoryIter<F>
where
F: FnMut() -> Option<S>,
{
type Item = S;
#[inline]
fn next(&mut self) -> Option<S> {
(self.factory)()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(0, None)
}
}

183
vendor/rodio/src/source/from_iter.rs vendored Normal file
View File

@@ -0,0 +1,183 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Builds a source that chains sources provided by an iterator.
///
/// The `iterator` parameter is an iterator that produces a source. The source is then played.
/// Whenever the source ends, the `iterator` is used again in order to produce the source that is
/// played next.
///
/// If the `iterator` produces `None`, then the sound ends.
pub fn from_iter<I>(iterator: I) -> FromIter<I::IntoIter>
where
I: IntoIterator,
{
let mut iterator = iterator.into_iter();
let first_source = iterator.next();
FromIter {
iterator,
current_source: first_source,
}
}
/// A source that chains sources provided by an iterator.
#[derive(Clone)]
pub struct FromIter<I>
where
I: Iterator,
{
// The iterator that provides sources.
iterator: I,
// Is only ever `None` if the first element of the iterator is `None`.
current_source: Option<I::Item>,
}
impl<I> Iterator for FromIter<I>
where
I: Iterator,
I::Item: Iterator + Source,
<I::Item as Iterator>::Item: Sample,
{
type Item = <I::Item as Iterator>::Item;
#[inline]
fn next(&mut self) -> Option<<I::Item as Iterator>::Item> {
loop {
if let Some(src) = &mut self.current_source {
if let Some(value) = src.next() {
return Some(value);
}
}
if let Some(src) = self.iterator.next() {
self.current_source = Some(src);
} else {
return None;
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if let Some(cur) = &self.current_source {
(cur.size_hint().0, None)
} else {
(0, None)
}
}
}
impl<I> Source for FromIter<I>
where
I: Iterator,
I::Item: Iterator + Source,
<I::Item as Iterator>::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
// This function is non-trivial because the boundary between the current source and the
// next must be a frame boundary as well.
//
// The current sound is free to return `None` for `current_frame_len()`, in which case
// we *should* return the number of samples remaining the current sound.
// This can be estimated with `size_hint()`.
//
// If the `size_hint` is `None` as well, we are in the worst case scenario. To handle this
// situation we force a frame to have a maximum number of samples indicate by this
// constant.
const THRESHOLD: usize = 10240;
// Try the current `current_frame_len`.
if let Some(src) = &self.current_source {
if let Some(val) = src.current_frame_len() {
if val != 0 {
return Some(val);
}
}
}
// Try the size hint.
if let Some(src) = &self.current_source {
if let Some(val) = src.size_hint().1 {
if val < THRESHOLD && val != 0 {
return Some(val);
}
}
}
// Otherwise we use the constant value.
Some(THRESHOLD)
}
#[inline]
fn channels(&self) -> u16 {
if let Some(src) = &self.current_source {
src.channels()
} else {
// Dummy value that only happens if the iterator was empty.
2
}
}
#[inline]
fn sample_rate(&self) -> u32 {
if let Some(src) = &self.current_source {
src.sample_rate()
} else {
// Dummy value that only happens if the iterator was empty.
44100
}
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
if let Some(source) = self.current_source.as_mut() {
source.try_seek(pos)
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use crate::buffer::SamplesBuffer;
use crate::source::{from_iter, Source};
#[test]
fn basic() {
let mut rx = from_iter((0..2).map(|n| {
if n == 0 {
SamplesBuffer::new(1, 48000, vec![10i16, -10, 10, -10])
} else if n == 1 {
SamplesBuffer::new(2, 96000, vec![5i16, 5, 5, 5])
} else {
unreachable!()
}
}));
assert_eq!(rx.channels(), 1);
assert_eq!(rx.sample_rate(), 48000);
assert_eq!(rx.next(), Some(10));
assert_eq!(rx.next(), Some(-10));
assert_eq!(rx.next(), Some(10));
assert_eq!(rx.next(), Some(-10));
/*assert_eq!(rx.channels(), 2);
assert_eq!(rx.sample_rate(), 96000);*/
// FIXME: not working
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), Some(5));
assert_eq!(rx.next(), None);
}
}

238
vendor/rodio/src/source/linear_ramp.rs vendored Normal file
View File

@@ -0,0 +1,238 @@
use std::time::Duration;
use super::SeekError;
use crate::{Sample, Source};
/// Internal function that builds a `LinearRamp` object.
pub fn linear_gain_ramp<I>(
input: I,
duration: Duration,
start_gain: f32,
end_gain: f32,
clamp_end: bool,
) -> LinearGainRamp<I>
where
I: Source,
I::Item: Sample,
{
let duration_nanos = duration.as_nanos() as f32;
assert!(duration_nanos > 0.0f32);
LinearGainRamp {
input,
elapsed_ns: 0.0f32,
total_ns: duration_nanos,
start_gain,
end_gain,
clamp_end,
sample_idx: 0u64,
}
}
/// Filter that adds a linear gain ramp to the source over a given time range.
#[derive(Clone, Debug)]
pub struct LinearGainRamp<I> {
input: I,
elapsed_ns: f32,
total_ns: f32,
start_gain: f32,
end_gain: f32,
clamp_end: bool,
sample_idx: u64,
}
impl<I> LinearGainRamp<I>
where
I: Source,
I::Item: Sample,
{
/// Returns a reference to the innner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Iterator for LinearGainRamp<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
let factor: f32;
let remaining_ns = self.total_ns - self.elapsed_ns;
if remaining_ns < 0.0 {
if self.clamp_end {
factor = self.end_gain;
} else {
factor = 1.0f32;
}
} else {
self.sample_idx += 1;
let p = self.elapsed_ns / self.total_ns;
factor = self.start_gain * (1.0f32 - p) + self.end_gain * p;
}
if self.sample_idx % (self.channels() as u64) == 0 {
self.elapsed_ns += 1000000000.0 / (self.input.sample_rate() as f32);
}
self.input.next().map(|value| value.amplify(factor))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> ExactSizeIterator for LinearGainRamp<I>
where
I: Source + ExactSizeIterator,
I::Item: Sample,
{
}
impl<I> Source for LinearGainRamp<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.elapsed_ns = pos.as_nanos() as f32;
self.input.try_seek(pos)
}
}
#[cfg(test)]
mod tests {
use approx::assert_abs_diff_eq;
use super::*;
use crate::buffer::SamplesBuffer;
/// Create a SamplesBuffer of identical samples with value `value`.
/// Returned buffer is one channel and has a sample rate of 1 hz.
fn const_source(length: u8, value: f32) -> SamplesBuffer<f32> {
let data: Vec<f32> = (1..=length).map(|_| value).collect();
SamplesBuffer::new(1, 1, data)
}
/// Create a SamplesBuffer of repeating sample values from `values`.
fn cycle_source(length: u8, values: Vec<f32>) -> SamplesBuffer<f32> {
let data: Vec<f32> = (1..=length)
.enumerate()
.map(|(i, _)| values[i % values.len()])
.collect();
SamplesBuffer::new(1, 1, data)
}
#[test]
fn test_linear_ramp() {
let source1 = const_source(10, 1.0f32);
let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 1.0, true);
assert_eq!(faded.next(), Some(0.0));
assert_eq!(faded.next(), Some(0.25));
assert_eq!(faded.next(), Some(0.5));
assert_eq!(faded.next(), Some(0.75));
assert_eq!(faded.next(), Some(1.0));
assert_eq!(faded.next(), Some(1.0));
assert_eq!(faded.next(), Some(1.0));
assert_eq!(faded.next(), Some(1.0));
assert_eq!(faded.next(), Some(1.0));
assert_eq!(faded.next(), Some(1.0));
assert_eq!(faded.next(), None);
}
#[test]
fn test_linear_ramp_clamped() {
let source1 = const_source(10, 1.0f32);
let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 0.5, true);
assert_eq!(faded.next(), Some(0.0)); // fading in...
assert_eq!(faded.next(), Some(0.125));
assert_eq!(faded.next(), Some(0.25));
assert_eq!(faded.next(), Some(0.375));
assert_eq!(faded.next(), Some(0.5)); // fade is done
assert_eq!(faded.next(), Some(0.5));
assert_eq!(faded.next(), Some(0.5));
assert_eq!(faded.next(), Some(0.5));
assert_eq!(faded.next(), Some(0.5));
assert_eq!(faded.next(), Some(0.5));
assert_eq!(faded.next(), None);
}
#[test]
fn test_linear_ramp_seek() {
let source1 = cycle_source(20, vec![0.0f32, 0.4f32, 0.8f32]);
let mut faded = linear_gain_ramp(source1, Duration::from_secs(10), 0.0, 1.0, true);
assert_abs_diff_eq!(faded.next().unwrap(), 0.0); // source value 0
assert_abs_diff_eq!(faded.next().unwrap(), 0.04); // source value 0.4, ramp gain 0.1
assert_abs_diff_eq!(faded.next().unwrap(), 0.16); // source value 0.8, ramp gain 0.2
if let Ok(_result) = faded.try_seek(Duration::from_secs(5)) {
assert_abs_diff_eq!(faded.next().unwrap(), 0.40); // source value 0.8, ramp gain 0.5
assert_abs_diff_eq!(faded.next().unwrap(), 0.0); // source value 0, ramp gain 0.6
assert_abs_diff_eq!(faded.next().unwrap(), 0.28); // source value 0.4. ramp gain 0.7
} else {
panic!("try_seek() failed!");
}
if let Ok(_result) = faded.try_seek(Duration::from_secs(0)) {
assert_abs_diff_eq!(faded.next().unwrap(), 0.0); // source value 0, ramp gain 0.0
assert_abs_diff_eq!(faded.next().unwrap(), 0.04); // source value 0.4, ramp gain 0.1
assert_abs_diff_eq!(faded.next().unwrap(), 0.16); // source value 0.8. ramp gain 0.2
} else {
panic!("try_seek() failed!");
}
if let Ok(_result) = faded.try_seek(Duration::from_secs(10)) {
assert_abs_diff_eq!(faded.next().unwrap(), 0.4); // source value 0.4, ramp gain 1.0
assert_abs_diff_eq!(faded.next().unwrap(), 0.8); // source value 0.8, ramp gain 1.0
assert_abs_diff_eq!(faded.next().unwrap(), 0.0); // source value 0. ramp gain 1.0
} else {
panic!("try_seek() failed!");
}
}
}

144
vendor/rodio/src/source/mix.rs vendored Normal file
View File

@@ -0,0 +1,144 @@
use std::cmp;
use std::time::Duration;
use crate::source::uniform::UniformSourceIterator;
use crate::source::SeekError;
use crate::{Sample, Source};
use cpal::{FromSample, Sample as CpalSample};
/// Internal function that builds a `Mix` object.
pub fn mix<I1, I2>(input1: I1, input2: I2) -> Mix<I1, I2>
where
I1: Source,
I1::Item: FromSample<I2::Item> + Sample,
I2: Source,
I2::Item: Sample,
{
let channels = input1.channels();
let rate = input1.sample_rate();
Mix {
input1: UniformSourceIterator::new(input1, channels, rate),
input2: UniformSourceIterator::new(input2, channels, rate),
}
}
/// Filter that modifies each sample by a given value.
#[derive(Clone)]
pub struct Mix<I1, I2>
where
I1: Source,
I1::Item: FromSample<I2::Item> + Sample,
I2: Source,
I2::Item: Sample,
{
input1: UniformSourceIterator<I1, I1::Item>,
input2: UniformSourceIterator<I2, I2::Item>,
}
impl<I1, I2> Iterator for Mix<I1, I2>
where
I1: Source,
I1::Item: FromSample<I2::Item> + Sample,
I2: Source,
I2::Item: Sample,
{
type Item = I1::Item;
#[inline]
fn next(&mut self) -> Option<I1::Item> {
let s1 = self.input1.next();
let s2 = self.input2.next();
match (s1, s2) {
(Some(s1), Some(s2)) => Some(s1.saturating_add(CpalSample::from_sample(s2))),
(Some(s1), None) => Some(s1),
(None, Some(s2)) => Some(CpalSample::from_sample(s2)),
(None, None) => None,
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let s1 = self.input1.size_hint();
let s2 = self.input2.size_hint();
let min = cmp::max(s1.0, s2.0);
let max = match (s1.1, s2.1) {
(Some(s1), Some(s2)) => Some(cmp::max(s1, s2)),
_ => None,
};
(min, max)
}
}
impl<I1, I2> ExactSizeIterator for Mix<I1, I2>
where
I1: Source + ExactSizeIterator,
I1::Item: FromSample<I2::Item> + Sample,
I2: Source + ExactSizeIterator,
I2::Item: Sample,
{
}
impl<I1, I2> Source for Mix<I1, I2>
where
I1: Source,
I1::Item: FromSample<I2::Item> + Sample,
I2: Source,
I2::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
let f1 = self.input1.current_frame_len();
let f2 = self.input2.current_frame_len();
match (f1, f2) {
(Some(f1), Some(f2)) => Some(cmp::min(f1, f2)),
_ => None,
}
}
#[inline]
fn channels(&self) -> u16 {
self.input1.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input1.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
let f1 = self.input1.total_duration();
let f2 = self.input2.total_duration();
match (f1, f2) {
(Some(f1), Some(f2)) => Some(cmp::max(f1, f2)),
_ => None,
}
}
/// Will only attempt a seek if both underlying sources support seek.
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Err(SeekError::NotSupported {
underlying_source: std::any::type_name::<Self>(),
})
// uncomment when #510 is implemented (query position of playback)
// TODO use source_intact to check if rollback makes sense
// let org_pos = self.input1.playback_pos();
// self.input1.try_seek(pos)?;
//
// let res = self.input2.try_seek(pos);
// if res.is_err() { // rollback seek in input1
// self.input1.try_seek(org_pos)?;
// }
//
// res
}
}

697
vendor/rodio/src/source/mod.rs vendored Normal file
View File

@@ -0,0 +1,697 @@
//! Sources of sound and various filters.
use core::fmt;
use core::time::Duration;
use cpal::FromSample;
use crate::Sample;
pub use self::agc::AutomaticGainControl;
pub use self::amplify::Amplify;
pub use self::blt::BltFilter;
pub use self::buffered::Buffered;
pub use self::channel_volume::ChannelVolume;
pub use self::chirp::{chirp, Chirp};
pub use self::crossfade::Crossfade;
pub use self::delay::Delay;
pub use self::done::Done;
pub use self::empty::Empty;
pub use self::empty_callback::EmptyCallback;
pub use self::fadein::FadeIn;
pub use self::fadeout::FadeOut;
pub use self::from_factory::{from_factory, FromFactoryIter};
pub use self::from_iter::{from_iter, FromIter};
pub use self::linear_ramp::LinearGainRamp;
pub use self::mix::Mix;
pub use self::pausable::Pausable;
pub use self::periodic::PeriodicAccess;
pub use self::position::TrackPosition;
pub use self::repeat::Repeat;
pub use self::samples_converter::SamplesConverter;
pub use self::signal_generator::{Function, SignalGenerator};
pub use self::sine::SineWave;
pub use self::skip::SkipDuration;
pub use self::skippable::Skippable;
pub use self::spatial::Spatial;
pub use self::speed::Speed;
pub use self::stoppable::Stoppable;
pub use self::take::TakeDuration;
pub use self::uniform::UniformSourceIterator;
pub use self::zero::Zero;
mod agc;
mod amplify;
mod blt;
mod buffered;
mod channel_volume;
mod chirp;
mod crossfade;
mod delay;
mod done;
mod empty;
mod empty_callback;
mod fadein;
mod fadeout;
mod from_factory;
mod from_iter;
mod linear_ramp;
mod mix;
mod pausable;
mod periodic;
mod position;
mod repeat;
mod samples_converter;
mod signal_generator;
mod sine;
mod skip;
mod skippable;
mod spatial;
mod speed;
mod stoppable;
mod take;
mod uniform;
mod zero;
#[cfg(feature = "noise")]
mod noise;
#[cfg(feature = "noise")]
pub use self::noise::{pink, white, PinkNoise, WhiteNoise};
/// A source of samples.
///
/// # A quick lesson about sounds
///
/// ## Sampling
///
/// A sound is a vibration that propagates through air and reaches your ears. This vibration can
/// be represented as an analog signal.
///
/// In order to store this signal in the computer's memory or on the disk, we perform what is
/// called *sampling*. The consists in choosing an interval of time (for example 20µs) and reading
/// the amplitude of the signal at each interval (for example, if the interval is 20µs we read the
/// amplitude every 20µs). By doing so we obtain a list of numerical values, each value being
/// called a *sample*.
///
/// Therefore a sound can be represented in memory by a frequency and a list of samples. The
/// frequency is expressed in hertz and corresponds to the number of samples that have been
/// read per second. For example if we read one sample every 20µs, the frequency would be
/// 50000 Hz. In reality, common values for the frequency are 44100, 48000 and 96000.
///
/// ## Channels
///
/// But a frequency and a list of values only represent one signal. When you listen to a sound,
/// your left and right ears don't receive exactly the same signal. In order to handle this,
/// we usually record not one but two different signals: one for the left ear and one for the right
/// ear. We say that such a sound has two *channels*.
///
/// Sometimes sounds even have five or six channels, each corresponding to a location around the
/// head of the listener.
///
/// The standard in audio manipulation is to *interleave* the multiple channels. In other words,
/// in a sound with two channels the list of samples contains the first sample of the first
/// channel, then the first sample of the second channel, then the second sample of the first
/// channel, then the second sample of the second channel, and so on. The same applies if you have
/// more than two channels. The rodio library only supports this schema.
///
/// Therefore in order to represent a sound in memory in fact we need three characteristics: the
/// frequency, the number of channels, and the list of samples.
///
/// ## The `Source` trait
///
/// A Rust object that represents a sound should implement the `Source` trait.
///
/// The three characteristics that describe a sound are provided through this trait:
///
/// - The number of channels can be retrieved with `channels`.
/// - The frequency can be retrieved with `sample_rate`.
/// - The list of values can be retrieved by iterating on the source. The `Source` trait requires
/// that the `Iterator` trait be implemented as well. When a `Source` returns None the
/// sound has ended.
///
/// # Frames
///
/// The samples rate and number of channels of some sound sources can change by itself from time
/// to time.
///
/// > **Note**: As a basic example, if you play two audio files one after the other and treat the
/// > whole as a single source, then the channels and samples rate of that source may change at the
/// > transition between the two files.
///
/// However, for optimization purposes rodio supposes that the number of channels and the frequency
/// stay the same for long periods of time and avoids calling `channels()` and
/// `sample_rate` too frequently.
///
/// In order to properly handle this situation, the `current_frame_len()` method should return
/// the number of samples that remain in the iterator before the samples rate and number of
/// channels can potentially change.
///
pub trait Source: Iterator
where
Self::Item: Sample,
{
/// Returns the number of samples before the current frame ends. `None` means "infinite" or
/// "until the sound ends".
/// Should never return 0 unless there's no more data.
///
/// After the engine has finished reading the specified number of samples, it will check
/// whether the value of `channels()` and/or `sample_rate()` have changed.
fn current_frame_len(&self) -> Option<usize>;
/// Returns the number of channels. Channels are always interleaved.
fn channels(&self) -> u16;
/// Returns the rate at which the source should be played. In number of samples per second.
fn sample_rate(&self) -> u32;
/// Returns the total duration of this source, if known.
///
/// `None` indicates at the same time "infinite" or "unknown".
fn total_duration(&self) -> Option<Duration>;
/// Stores the source in a buffer in addition to returning it. This iterator can be cloned.
#[inline]
fn buffered(self) -> Buffered<Self>
where
Self: Sized,
{
buffered::buffered(self)
}
/// Mixes this source with another one.
#[inline]
fn mix<S>(self, other: S) -> Mix<Self, S>
where
Self: Sized,
Self::Item: FromSample<S::Item>,
S: Source,
S::Item: Sample,
{
mix::mix(self, other)
}
/// Repeats this source forever.
///
/// Note that this works by storing the data in a buffer, so the amount of memory used is
/// proportional to the size of the sound.
#[inline]
fn repeat_infinite(self) -> Repeat<Self>
where
Self: Sized,
{
repeat::repeat(self)
}
/// Takes a certain duration of this source and then stops.
#[inline]
fn take_duration(self, duration: Duration) -> TakeDuration<Self>
where
Self: Sized,
{
take::take_duration(self, duration)
}
/// Delays the sound by a certain duration.
///
/// The rate and channels of the silence will use the same format as the first frame of the
/// source.
#[inline]
fn delay(self, duration: Duration) -> Delay<Self>
where
Self: Sized,
{
delay::delay(self, duration)
}
/// Immediately skips a certain duration of this source.
///
/// If the specified duration is longer than the source itself, `skip_duration` will skip to the end of the source.
#[inline]
fn skip_duration(self, duration: Duration) -> SkipDuration<Self>
where
Self: Sized,
{
skip::skip_duration(self, duration)
}
/// Amplifies the sound by the given value.
#[inline]
fn amplify(self, value: f32) -> Amplify<Self>
where
Self: Sized,
{
amplify::amplify(self, value)
}
/// Applies automatic gain control to the sound.
///
/// Automatic Gain Control (AGC) adjusts the amplitude of the audio signal
/// to maintain a consistent output level.
///
/// # Parameters
///
/// `target_level`:
/// **TL;DR**: Desired output level. 1.0 = original level, > 1.0 amplifies, < 1.0 reduces.
///
/// The desired output level, where 1.0 represents the original sound level.
/// Values above 1.0 will amplify the sound, while values below 1.0 will lower it.
/// For example, a target_level of 1.4 means that at normal sound levels, the AGC
/// will aim to increase the gain by a factor of 1.4, resulting in a minimum 40% amplification.
/// A recommended level is `1.0`, which maintains the original sound level.
///
/// `attack_time`:
/// **TL;DR**: Response time for volume increases. Shorter = faster but may cause abrupt changes. **Recommended: `4.0` seconds**.
///
/// The time (in seconds) for the AGC to respond to input level increases.
/// Shorter times mean faster response but may cause abrupt changes. Longer times result
/// in smoother transitions but slower reactions to sudden volume changes. Too short can
/// lead to overreaction to peaks, causing unnecessary adjustments. Too long can make the
/// AGC miss important volume changes or react too slowly to sudden loud passages. Very
/// high values might result in excessively loud output or sluggish response, as the AGC's
/// adjustment speed is limited by the attack time. Balance is key for optimal performance.
/// A recommended attack_time of `4.0` seconds provides a sweet spot for most applications.
///
/// `release_time`:
/// **TL;DR**: Response time for volume decreases. Shorter = faster gain reduction. **Recommended: `0.005` seconds**.
///
/// The time (in seconds) for the AGC to respond to input level decreases.
/// This parameter controls how quickly the gain is reduced when the signal level drops.
/// Shorter release times result in faster gain reduction, which can be useful for quick
/// adaptation to quieter passages but may lead to pumping effects. Longer release times
/// provide smoother transitions but may be slower to respond to sudden decreases in volume.
/// However, if the release_time is too high, the AGC may not be able to lower the gain
/// quickly enough, potentially leading to clipping and distorted sound before it can adjust.
/// Finding the right balance is crucial for maintaining natural-sounding dynamics and
/// preventing distortion. A recommended release_time of `0.005` seconds often works well for
/// general use, providing a good balance between responsiveness and smooth transitions.
///
/// `absolute_max_gain`:
/// **TL;DR**: Maximum allowed gain. Prevents over-amplification. **Recommended: `5.0`**.
///
/// The maximum gain that can be applied to the signal.
/// This parameter acts as a safeguard against excessive amplification of quiet signals
/// or background noise. It establishes an upper boundary for the AGC's signal boost,
/// effectively preventing distortion or overamplification of low-level sounds.
/// This is crucial for maintaining audio quality and preventing unexpected volume spikes.
/// A recommended value for `absolute_max_gain` is `5`, which provides a good balance between
/// amplification capability and protection against distortion in most scenarios.
///
/// Use `get_agc_control` to obtain a handle for real-time enabling/disabling of the AGC.
///
/// # Example (Quick start)
///
/// ```rust
/// // Apply Automatic Gain Control to the source (AGC is on by default)
/// let agc_source = source.automatic_gain_control(1.0, 4.0, 0.005, 5.0);
///
/// // Get a handle to control the AGC's enabled state (optional)
/// let agc_control = agc_source.get_agc_control();
///
/// // You can toggle AGC on/off at any time (optional)
/// agc_control.store(false, std::sync::atomic::Ordering::Relaxed);
///
/// // Add the AGC-controlled source to the sink
/// sink.append(agc_source);
///
/// // Note: Using agc_control is optional. If you don't need to toggle AGC,
/// // you can simply use the agc_source directly without getting agc_control.
/// ```
#[inline]
fn automatic_gain_control(
self,
target_level: f32,
attack_time: f32,
release_time: f32,
absolute_max_gain: f32,
) -> AutomaticGainControl<Self>
where
Self: Sized,
{
// Added Limits to prevent the AGC from blowing up. ;)
const MIN_ATTACK_TIME: f32 = 10.0;
const MIN_RELEASE_TIME: f32 = 10.0;
let attack_time = attack_time.min(MIN_ATTACK_TIME);
let release_time = release_time.min(MIN_RELEASE_TIME);
agc::automatic_gain_control(
self,
target_level,
attack_time,
release_time,
absolute_max_gain,
)
}
/// Mixes this sound fading out with another sound fading in for the given duration.
///
/// Only the crossfaded portion (beginning of self, beginning of other) is returned.
#[inline]
fn take_crossfade_with<S: Source>(self, other: S, duration: Duration) -> Crossfade<Self, S>
where
Self: Sized,
Self::Item: FromSample<S::Item>,
<S as Iterator>::Item: Sample,
{
crossfade::crossfade(self, other, duration)
}
/// Fades in the sound.
#[inline]
fn fade_in(self, duration: Duration) -> FadeIn<Self>
where
Self: Sized,
{
fadein::fadein(self, duration)
}
/// Fades out the sound.
#[inline]
fn fade_out(self, duration: Duration) -> FadeOut<Self>
where
Self: Sized,
{
fadeout::fadeout(self, duration)
}
/// Applies a linear gain ramp to the sound.
///
/// If `clamp_end` is `true`, all samples subsequent to the end of the ramp
/// will be scaled by the `end_value`. If `clamp_end` is `false`, all
/// subsequent samples will not have any scaling applied.
#[inline]
fn linear_gain_ramp(
self,
duration: Duration,
start_value: f32,
end_value: f32,
clamp_end: bool,
) -> LinearGainRamp<Self>
where
Self: Sized,
{
linear_ramp::linear_gain_ramp(self, duration, start_value, end_value, clamp_end)
}
/// Calls the `access` closure on `Self` the first time the source is iterated and every
/// time `period` elapses.
///
/// Later changes in either `sample_rate()` or `channels_count()` won't be reflected in
/// the rate of access.
///
/// The rate is based on playback speed, so both the following will call `access` when the
/// same samples are reached:
/// `periodic_access(Duration::from_secs(1), ...).speed(2.0)`
/// `speed(2.0).periodic_access(Duration::from_secs(2), ...)`
#[inline]
fn periodic_access<F>(self, period: Duration, access: F) -> PeriodicAccess<Self, F>
where
Self: Sized,
F: FnMut(&mut Self),
{
periodic::periodic(self, period, access)
}
/// Changes the play speed of the sound. Does not adjust the samples, only the playback speed.
///
/// # Note:
/// 1. **Increasing the speed will increase the pitch by the same factor**
/// - If you set the speed to 0.5 this will halve the frequency of the sound
/// lowering its pitch.
/// - If you set the speed to 2 the frequency will double raising the
/// pitch of the sound.
/// 2. **Change in the speed affect the total duration inversely**
/// - If you set the speed to 0.5, the total duration will be twice as long.
/// - If you set the speed to 2 the total duration will be halve of what it
/// was.
///
/// See [`Speed`] for details
#[inline]
fn speed(self, ratio: f32) -> Speed<Self>
where
Self: Sized,
{
speed::speed(self, ratio)
}
/// Adds a basic reverb effect.
///
/// This function requires the source to implement `Clone`. This can be done by using
/// `buffered()`.
///
/// # Example
///
/// ```ignore
/// use std::time::Duration;
///
/// let source = source.buffered().reverb(Duration::from_millis(100), 0.7);
/// ```
#[inline]
fn reverb(self, duration: Duration, amplitude: f32) -> Mix<Self, Delay<Amplify<Self>>>
where
Self: Sized + Clone,
{
let echo = self.clone().amplify(amplitude).delay(duration);
self.mix(echo)
}
/// Converts the samples of this source to another type.
#[inline]
fn convert_samples<D>(self) -> SamplesConverter<Self, D>
where
Self: Sized,
D: Sample,
{
SamplesConverter::new(self)
}
/// Makes the sound pausable.
// TODO: add example
#[inline]
fn pausable(self, initially_paused: bool) -> Pausable<Self>
where
Self: Sized,
{
pausable::pausable(self, initially_paused)
}
/// Makes the sound stoppable.
// TODO: add example
#[inline]
fn stoppable(self) -> Stoppable<Self>
where
Self: Sized,
{
stoppable::stoppable(self)
}
/// Adds a method [`Skippable::skip`] for skipping this source. Skipping
/// makes Source::next() return None. Which in turn makes the Sink skip to
/// the next source.
fn skippable(self) -> Skippable<Self>
where
Self: Sized,
{
skippable::skippable(self)
}
/// Start tracking the elapsed duration since the start of the underlying
/// source.
///
/// If a speedup and or delay is applied after this that will not be reflected
/// in the position returned by [`get_pos`](TrackPosition::get_pos).
///
/// This can get confusing when using [`get_pos()`](TrackPosition::get_pos)
/// together with [`Source::try_seek()`] as the latter does take all
/// speedup's and delay's into account. Its recommended therefore to apply
/// track_position after speedup's and delay's.
fn track_position(self) -> TrackPosition<Self>
where
Self: Sized,
{
position::track_position(self)
}
/// Applies a low-pass filter to the source.
/// **Warning**: Probably buggy.
#[inline]
fn low_pass(self, freq: u32) -> BltFilter<Self>
where
Self: Sized,
Self: Source<Item = f32>,
{
blt::low_pass(self, freq)
}
/// Applies a high-pass filter to the source.
#[inline]
fn high_pass(self, freq: u32) -> BltFilter<Self>
where
Self: Sized,
Self: Source<Item = f32>,
{
blt::high_pass(self, freq)
}
/// Applies a low-pass filter to the source while allowing the q (bandwidth) to be changed.
#[inline]
fn low_pass_with_q(self, freq: u32, q: f32) -> BltFilter<Self>
where
Self: Sized,
Self: Source<Item = f32>,
{
blt::low_pass_with_q(self, freq, q)
}
/// Applies a high-pass filter to the source while allowing the q (bandwidth) to be changed.
#[inline]
fn high_pass_with_q(self, freq: u32, q: f32) -> BltFilter<Self>
where
Self: Sized,
Self: Source<Item = f32>,
{
blt::high_pass_with_q(self, freq, q)
}
// There is no `can_seek()` method as it is impossible to use correctly. Between
// checking if a source supports seeking and actually seeking the sink can
// switch to a new source.
/// Attempts to seek to a given position in the current source.
///
/// As long as the duration of the source is known seek is guaranteed to saturate
/// at the end of the source. For example given a source that reports a total duration
/// of 42 seconds calling `try_seek()` with 60 seconds as argument will seek to
/// 42 seconds.
///
/// # Errors
/// This function will return [`SeekError::NotSupported`] if one of the underlying
/// sources does not support seeking.
///
/// It will return an error if an implementation ran
/// into one during the seek.
///
/// Seeking beyond the end of a source might return an error if the total duration of
/// the source is not known.
#[allow(unused_variables)]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
Err(SeekError::NotSupported {
underlying_source: std::any::type_name::<Self>(),
})
}
}
// We might add decoders requiring new error types, without non_exhaustive
// this would break users builds
/// Occurs when try_seek fails because the underlying decoder has an error or
/// does not support seeking.
#[non_exhaustive]
#[derive(Debug)]
pub enum SeekError {
/// One of the underlying sources does not support seeking
NotSupported {
/// The source that did not support seek
underlying_source: &'static str,
},
#[cfg(feature = "symphonia")]
/// The symphonia decoder ran into an issue
SymphoniaDecoder(crate::decoder::symphonia::SeekError),
#[cfg(feature = "wav")]
/// The hound (wav) decoder ran into an issue
HoundDecoder(std::io::Error),
// Prefer adding an enum variant to using this. Its meant for end users their
// own try_seek implementations
/// Any other error probably in a custom Source
Other(Box<dyn std::error::Error + Send>),
}
impl fmt::Display for SeekError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SeekError::NotSupported { underlying_source } => {
write!(
f,
"Seeking is not supported by source: {}",
underlying_source
)
}
#[cfg(feature = "symphonia")]
SeekError::SymphoniaDecoder(err) => write!(f, "Error seeking: {}", err),
#[cfg(feature = "wav")]
SeekError::HoundDecoder(err) => write!(f, "Error seeking in wav source: {}", err),
SeekError::Other(_) => write!(f, "An error occurred"),
}
}
}
impl std::error::Error for SeekError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
SeekError::NotSupported { .. } => None,
#[cfg(feature = "symphonia")]
SeekError::SymphoniaDecoder(err) => Some(err),
#[cfg(feature = "wav")]
SeekError::HoundDecoder(err) => Some(err),
SeekError::Other(err) => Some(err.as_ref()),
}
}
}
#[cfg(feature = "symphonia")]
impl From<crate::decoder::symphonia::SeekError> for SeekError {
fn from(source: crate::decoder::symphonia::SeekError) -> Self {
SeekError::SymphoniaDecoder(source)
}
}
impl SeekError {
/// Will the source remain playing at its position before the seek or is it
/// broken?
pub fn source_intact(&self) -> bool {
match self {
SeekError::NotSupported { .. } => true,
#[cfg(feature = "symphonia")]
SeekError::SymphoniaDecoder(_) => false,
#[cfg(feature = "wav")]
SeekError::HoundDecoder(_) => false,
SeekError::Other(_) => false,
}
}
}
macro_rules! source_pointer_impl {
($($sig:tt)+) => {
impl $($sig)+ {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
(**self).current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
(**self).channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
(**self).sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
(**self).total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
(**self).try_seek(pos)
}
}
};
}
source_pointer_impl!(<S> Source for Box<dyn Source<Item = S>> where S: Sample,);
source_pointer_impl!(<S> Source for Box<dyn Source<Item = S> + Send> where S: Sample,);
source_pointer_impl!(<S> Source for Box<dyn Source<Item = S> + Send + Sync> where S: Sample,);
source_pointer_impl!(<'a, S, C> Source for &'a mut C where S: Sample, C: Source<Item = S>,);

158
vendor/rodio/src/source/noise.rs vendored Normal file
View File

@@ -0,0 +1,158 @@
//! Noise sources.
//!
//!
use crate::Source;
use super::SeekError;
use rand::{rngs::SmallRng, RngCore, SeedableRng};
/// Convenience function to create a new `WhiteNoise` noise source.
#[inline]
pub fn white(sample_rate: cpal::SampleRate) -> WhiteNoise {
WhiteNoise::new(sample_rate)
}
/// Convenience function to create a new `PinkNoise` noise source.
#[inline]
pub fn pink(sample_rate: cpal::SampleRate) -> PinkNoise {
PinkNoise::new(sample_rate)
}
/// Generates an infinite stream of random samples in [-1.0, 1.0]. This source generates random
/// samples as provided by the `rand::rngs::SmallRng` randomness source.
#[derive(Clone, Debug)]
pub struct WhiteNoise {
sample_rate: cpal::SampleRate,
rng: SmallRng,
}
impl WhiteNoise {
/// Create a new white noise generator, seeding the RNG with `seed`.
pub fn new_with_seed(sample_rate: cpal::SampleRate, seed: u64) -> Self {
Self {
sample_rate,
rng: SmallRng::seed_from_u64(seed),
}
}
/// Create a new white noise generator, seeding the RNG with system entropy.
pub fn new(sample_rate: cpal::SampleRate) -> Self {
Self {
sample_rate,
rng: SmallRng::from_entropy(),
}
}
}
impl Iterator for WhiteNoise {
type Item = f32;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let rand = self.rng.next_u32() as f32 / u32::MAX as f32;
let scaled = rand * 2.0 - 1.0;
Some(scaled)
}
}
impl Source for WhiteNoise {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
1
}
#[inline]
fn sample_rate(&self) -> u32 {
self.sample_rate.0
}
#[inline]
fn total_duration(&self) -> Option<std::time::Duration> {
None
}
#[inline]
fn try_seek(&mut self, _: std::time::Duration) -> Result<(), SeekError> {
// Does nothing, should do nothing
Ok(())
}
}
/// Generates an infinite stream of pink noise samples in [-1.0, 1.0].
///
/// The output of the source is the result of taking the output of the `WhiteNoise` source and
/// filtering it according to a weighted-sum of seven FIR filters after [Paul Kellett's
/// method][pk_method] from *musicdsp.org*.
///
/// [pk_method]: https://www.musicdsp.org/en/latest/Filters/76-pink-noise-filter.html
pub struct PinkNoise {
white_noise: WhiteNoise,
b: [f32; 7],
}
impl PinkNoise {
pub fn new(sample_rate: cpal::SampleRate) -> Self {
Self {
white_noise: WhiteNoise::new(sample_rate),
b: [0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32],
}
}
}
impl Iterator for PinkNoise {
type Item = f32;
fn next(&mut self) -> Option<Self::Item> {
let white = self.white_noise.next().unwrap();
self.b[0] = 0.99886 * self.b[0] + white * 0.0555179;
self.b[1] = 0.99332 * self.b[1] + white * 0.0750759;
self.b[2] = 0.969 * self.b[2] + white * 0.153852;
self.b[3] = 0.8665 * self.b[3] + white * 0.3104856;
self.b[4] = 0.550 * self.b[4] + white * 0.5329522;
self.b[5] = -0.7616 * self.b[5] - white * 0.016898;
let pink = self.b[0]
+ self.b[1]
+ self.b[2]
+ self.b[3]
+ self.b[4]
+ self.b[5]
+ self.b[6]
+ white * 0.5362;
self.b[6] = white * 0.115926;
Some(pink)
}
}
impl Source for PinkNoise {
fn current_frame_len(&self) -> Option<usize> {
None
}
fn channels(&self) -> u16 {
1
}
fn sample_rate(&self) -> u32 {
self.white_noise.sample_rate()
}
fn total_duration(&self) -> Option<std::time::Duration> {
None
}
#[inline]
fn try_seek(&mut self, _: std::time::Duration) -> Result<(), SeekError> {
// Does nothing, should do nothing
Ok(())
}
}

131
vendor/rodio/src/source/pausable.rs vendored Normal file
View File

@@ -0,0 +1,131 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Builds a `Pausable` object.
pub fn pausable<I>(source: I, paused: bool) -> Pausable<I>
where
I: Source,
I::Item: Sample,
{
let paused_channels = if paused {
Some(source.channels())
} else {
None
};
Pausable {
input: source,
paused_channels,
remaining_paused_samples: 0,
}
}
/// Wraps a source and makes it pausable by calling [`Pausable::set_paused`] on
/// this object. When the source is paused it returns zero value samples.
///
/// You can usually still use this from another source wrapping this one by
/// calling `inner_mut` on it. Similarly this provides [`Pausable::inner`] and
/// mutable/destructing variants for accessing the underlying source.
#[derive(Clone, Debug)]
pub struct Pausable<I> {
input: I,
paused_channels: Option<u16>,
remaining_paused_samples: u16,
}
impl<I> Pausable<I>
where
I: Source,
I::Item: Sample,
{
/// Sets whether the filter applies.
///
/// If set to true, the inner sound stops playing and no samples are processed from it.
#[inline]
pub fn set_paused(&mut self, paused: bool) {
match (self.paused_channels, paused) {
(None, true) => self.paused_channels = Some(self.input.channels()),
(Some(_), false) => self.paused_channels = None,
_ => (),
}
}
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Iterator for Pausable<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
if self.remaining_paused_samples > 0 {
self.remaining_paused_samples -= 1;
return Some(I::Item::zero_value());
}
if let Some(paused_channels) = self.paused_channels {
self.remaining_paused_samples = paused_channels - 1;
return Some(I::Item::zero_value());
}
self.input.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> Source for Pausable<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}

174
vendor/rodio/src/source/periodic.rs vendored Normal file
View File

@@ -0,0 +1,174 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Internal function that builds a `PeriodicAccess` object.
pub fn periodic<I, F>(source: I, period: Duration, modifier: F) -> PeriodicAccess<I, F>
where
I: Source,
I::Item: Sample,
{
// TODO: handle the fact that the samples rate can change
// TODO: generally, just wrong
let update_ms = period.as_secs() as u32 * 1_000 + period.subsec_millis();
let update_frequency = (update_ms * source.sample_rate()) / 1000 * source.channels() as u32;
PeriodicAccess {
input: source,
modifier,
// Can overflow when subtracting if this is 0
update_frequency: if update_frequency == 0 {
1
} else {
update_frequency
},
samples_until_update: 1,
}
}
/// Calls a function on a source every time a period elapsed.
#[derive(Clone, Debug)]
pub struct PeriodicAccess<I, F> {
// The inner source.
input: I,
// Closure that gets access to `inner`.
modifier: F,
// The frequency with which local_volume should be updated by remote_volume
update_frequency: u32,
// How many samples remain until it is time to update local_volume with remote_volume.
samples_until_update: u32,
}
impl<I, F> PeriodicAccess<I, F>
where
I: Source,
I::Item: Sample,
F: FnMut(&mut I),
{
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I, F> Iterator for PeriodicAccess<I, F>
where
I: Source,
I::Item: Sample,
F: FnMut(&mut I),
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
self.samples_until_update -= 1;
if self.samples_until_update == 0 {
(self.modifier)(&mut self.input);
self.samples_until_update = self.update_frequency;
}
self.input.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I, F> Source for PeriodicAccess<I, F>
where
I: Source,
I::Item: Sample,
F: FnMut(&mut I),
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::time::Duration;
use crate::buffer::SamplesBuffer;
use crate::source::Source;
#[test]
fn stereo_access() {
// Stereo, 1Hz audio buffer
let inner = SamplesBuffer::new(2, 1, vec![10i16, -10, 10, -10, 20, -20]);
let cnt = RefCell::new(0);
let mut source = inner.periodic_access(Duration::from_millis(1000), |_src| {
*cnt.borrow_mut() += 1;
});
assert_eq!(*cnt.borrow(), 0);
// Always called on first access!
assert_eq!(source.next(), Some(10));
assert_eq!(*cnt.borrow(), 1);
// Called every 1 second afterwards
assert_eq!(source.next(), Some(-10));
assert_eq!(*cnt.borrow(), 1);
assert_eq!(source.next(), Some(10));
assert_eq!(*cnt.borrow(), 2);
assert_eq!(source.next(), Some(-10));
assert_eq!(*cnt.borrow(), 2);
assert_eq!(source.next(), Some(20));
assert_eq!(*cnt.borrow(), 3);
assert_eq!(source.next(), Some(-20));
assert_eq!(*cnt.borrow(), 3);
}
#[test]
fn fast_access_overflow() {
// 1hz is lower than 0.5 samples per 5ms
let inner = SamplesBuffer::new(1, 1, vec![10i16, -10, 10, -10, 20, -20]);
let mut source = inner.periodic_access(Duration::from_millis(5), |_src| {});
source.next();
source.next(); // Would overflow here.
}
}

199
vendor/rodio/src/source/position.rs vendored Normal file
View File

@@ -0,0 +1,199 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Internal function that builds a `TrackPosition` object. See trait docs for
/// details
pub fn track_position<I>(source: I) -> TrackPosition<I> {
TrackPosition {
input: source,
samples_counted: 0,
offset_duration: 0.0,
current_frame_sample_rate: 0,
current_frame_channels: 0,
current_frame_len: None,
}
}
/// Tracks the elapsed duration since the start of the underlying source.
#[derive(Debug)]
pub struct TrackPosition<I> {
input: I,
samples_counted: usize,
offset_duration: f64,
current_frame_sample_rate: u32,
current_frame_channels: u16,
current_frame_len: Option<usize>,
}
impl<I> TrackPosition<I> {
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> TrackPosition<I>
where
I: Source,
I::Item: Sample,
{
/// Returns the position of the underlying source relative to its start.
///
/// If a speedup and or delay is applied after applying a
/// [`Source::track_position`] it will not be reflected in the position
/// returned by [`get_pos`](TrackPosition::get_pos).
///
/// This can get confusing when using [`get_pos()`](TrackPosition::get_pos)
/// together with [`Source::try_seek()`] as the the latter does take all
/// speedup's and delay's into account. Its recommended therefore to apply
/// track_position after speedup's and delay's.
#[inline]
pub fn get_pos(&self) -> Duration {
let seconds = self.samples_counted as f64
/ self.input.sample_rate() as f64
/ self.input.channels() as f64
+ self.offset_duration;
Duration::from_secs_f64(seconds)
}
#[inline]
fn set_current_frame(&mut self) {
self.current_frame_len = self.current_frame_len();
self.current_frame_sample_rate = self.sample_rate();
self.current_frame_channels = self.channels();
}
}
impl<I> Iterator for TrackPosition<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
// This should only be executed once at the first call to next.
if self.current_frame_len.is_none() {
self.set_current_frame();
}
let item = self.input.next();
if item.is_some() {
self.samples_counted += 1;
// At the end of a frame add the duration of this frame to
// offset_duration and start collecting samples again.
if Some(self.samples_counted) == self.current_frame_len() {
self.offset_duration += self.samples_counted as f64
/ self.current_frame_sample_rate as f64
/ self.current_frame_channels as f64;
// Reset.
self.samples_counted = 0;
self.set_current_frame();
};
};
item
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> Source for TrackPosition<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
let result = self.input.try_seek(pos);
if result.is_ok() {
self.offset_duration = pos.as_secs_f64();
// This assumes that the seek implementation of the codec always
// starts again at the beginning of a frame. Which is the case with
// symphonia.
self.samples_counted = 0;
}
result
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use crate::buffer::SamplesBuffer;
use crate::source::Source;
#[test]
fn test_position() {
let inner = SamplesBuffer::new(1, 1, vec![10i16, -10, 10, -10, 20, -20]);
let mut source = inner.track_position();
assert_eq!(source.get_pos().as_secs_f32(), 0.0);
source.next();
assert_eq!(source.get_pos().as_secs_f32(), 1.0);
source.next();
assert_eq!(source.get_pos().as_secs_f32(), 2.0);
assert_eq!(source.try_seek(Duration::new(1, 0)).is_ok(), true);
assert_eq!(source.get_pos().as_secs_f32(), 1.0);
}
#[test]
fn test_position_in_presence_of_speedup() {
let inner = SamplesBuffer::new(1, 1, vec![10i16, -10, 10, -10, 20, -20]);
let mut source = inner.speed(2.0).track_position();
assert_eq!(source.get_pos().as_secs_f32(), 0.0);
source.next();
assert_eq!(source.get_pos().as_secs_f32(), 0.5);
source.next();
assert_eq!(source.get_pos().as_secs_f32(), 1.0);
assert_eq!(source.try_seek(Duration::new(1, 0)).is_ok(), true);
assert_eq!(source.get_pos().as_secs_f32(), 1.0);
}
}

108
vendor/rodio/src/source/repeat.rs vendored Normal file
View File

@@ -0,0 +1,108 @@
use std::time::Duration;
use crate::source::buffered::Buffered;
use crate::{Sample, Source};
use super::SeekError;
/// Internal function that builds a `Repeat` object.
pub fn repeat<I>(input: I) -> Repeat<I>
where
I: Source,
I::Item: Sample,
{
let input = input.buffered();
Repeat {
inner: input.clone(),
next: input,
}
}
/// A source that repeats the given source.
pub struct Repeat<I>
where
I: Source,
I::Item: Sample,
{
inner: Buffered<I>,
next: Buffered<I>,
}
impl<I> Iterator for Repeat<I>
where
I: Source,
I::Item: Sample,
{
type Item = <I as Iterator>::Item;
#[inline]
fn next(&mut self) -> Option<<I as Iterator>::Item> {
if let Some(value) = self.inner.next() {
return Some(value);
}
self.inner = self.next.clone();
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
// infinite
(0, None)
}
}
impl<I> Source for Repeat<I>
where
I: Iterator + Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
match self.inner.current_frame_len() {
Some(0) => self.next.current_frame_len(),
a => a,
}
}
#[inline]
fn channels(&self) -> u16 {
match self.inner.current_frame_len() {
Some(0) => self.next.channels(),
_ => self.inner.channels(),
}
}
#[inline]
fn sample_rate(&self) -> u32 {
match self.inner.current_frame_len() {
Some(0) => self.next.sample_rate(),
_ => self.inner.sample_rate(),
}
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.inner.try_seek(pos)
}
}
impl<I> Clone for Repeat<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn clone(&self) -> Repeat<I> {
Repeat {
inner: self.inner.clone(),
next: self.next.clone(),
}
}
}

View File

@@ -0,0 +1,104 @@
use std::marker::PhantomData;
use std::time::Duration;
use crate::{Sample, Source};
use cpal::{FromSample, Sample as CpalSample};
use super::SeekError;
/// Wrap the input and lazily converts the samples it provides to the type
/// specified by the generic parameter D
#[derive(Clone)]
pub struct SamplesConverter<I, D> {
inner: I,
dest: PhantomData<D>,
}
impl<I, D> SamplesConverter<I, D> {
/// Wrap the input and lazily converts the samples it provides to the type
/// specified by the generic parameter D
#[inline]
pub fn new(input: I) -> SamplesConverter<I, D> {
SamplesConverter {
inner: input,
dest: PhantomData,
}
}
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.inner
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.inner
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.inner
}
}
impl<I, D> Iterator for SamplesConverter<I, D>
where
I: Source,
I::Item: Sample,
D: FromSample<I::Item> + Sample,
{
type Item = D;
#[inline]
fn next(&mut self) -> Option<D> {
self.inner.next().map(|s| CpalSample::from_sample(s))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<I, D> ExactSizeIterator for SamplesConverter<I, D>
where
I: Source + ExactSizeIterator,
I::Item: Sample,
D: FromSample<I::Item> + Sample,
{
}
impl<I, D> Source for SamplesConverter<I, D>
where
I: Source,
I::Item: Sample,
D: FromSample<I::Item> + Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.inner.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.inner.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.inner.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.inner.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.inner.try_seek(pos)
}
}

View File

@@ -0,0 +1,185 @@
//! Generator sources for various periodic test waveforms.
//!
//! This module provides several periodic, deterministic waveforms for testing other sources and
//! for simple additive sound synthesis. Every source is monoaural and in the codomain [-1.0f32,
//! 1.0f32].
//!
//! # Example
//!
//! ```
//! use rodio::source::{SignalGenerator,Function};
//!
//! let tone = SignalGenerator::new(cpal::SampleRate(48000), 440.0, Function::Sine);
//! ```
use std::f32::consts::TAU;
use std::time::Duration;
use super::SeekError;
use crate::Source;
/// Waveform functions.
#[derive(Clone, Debug)]
pub enum Function {
/// A sinusoidal waveform.
Sine,
/// A triangle waveform.
Triangle,
/// A square wave, rising edge at t=0.
Square,
/// A rising sawtooth wave.
Sawtooth,
}
impl Function {
/// Create a single sample for the given waveform
#[inline]
fn render(&self, i: u64, period: f32) -> f32 {
let cycle_pos: f32 = i as f32 / period;
match self {
Self::Sine => (TAU * cycle_pos).sin(),
Self::Triangle => 4.0f32 * (cycle_pos - (cycle_pos + 0.5f32).floor()).abs() - 1f32,
Self::Square => {
if cycle_pos % 1.0f32 < 0.5f32 {
1.0f32
} else {
-1.0f32
}
}
Self::Sawtooth => 2.0f32 * (cycle_pos - (cycle_pos + 0.5f32).floor()),
}
}
}
/// An infinite source that produces one of a selection of test waveforms.
#[derive(Clone, Debug)]
pub struct SignalGenerator {
sample_rate: cpal::SampleRate,
period: f32,
function: Function,
i: u64,
}
impl SignalGenerator {
/// Create a new `TestWaveform` object that generates an endless waveform
/// `f`.
///
/// # Panics
///
/// Will panic if `frequency` is equal to zero.
#[inline]
pub fn new(sample_rate: cpal::SampleRate, frequency: f32, f: Function) -> SignalGenerator {
assert!(frequency != 0.0, "frequency must be greater than zero");
let period = sample_rate.0 as f32 / frequency;
SignalGenerator {
sample_rate,
period,
function: f,
i: 0,
}
}
}
impl Iterator for SignalGenerator {
type Item = f32;
#[inline]
fn next(&mut self) -> Option<f32> {
let val = Some(self.function.render(self.i, self.period));
self.i += 1;
val
}
}
impl Source for SignalGenerator {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
1
}
#[inline]
fn sample_rate(&self) -> u32 {
self.sample_rate.0
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
#[inline]
fn try_seek(&mut self, duration: Duration) -> Result<(), SeekError> {
self.i = (self.sample_rate.0 as f32 * duration.as_secs_f32()) as u64;
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::source::{Function, SignalGenerator};
use approx::assert_abs_diff_eq;
#[test]
fn square() {
let mut wf = SignalGenerator::new(cpal::SampleRate(2000), 500.0f32, Function::Square);
assert_eq!(wf.next(), Some(1.0f32));
assert_eq!(wf.next(), Some(1.0f32));
assert_eq!(wf.next(), Some(-1.0f32));
assert_eq!(wf.next(), Some(-1.0f32));
assert_eq!(wf.next(), Some(1.0f32));
assert_eq!(wf.next(), Some(1.0f32));
assert_eq!(wf.next(), Some(-1.0f32));
assert_eq!(wf.next(), Some(-1.0f32));
}
#[test]
fn triangle() {
let mut wf = SignalGenerator::new(cpal::SampleRate(8000), 1000.0f32, Function::Triangle);
assert_eq!(wf.next(), Some(-1.0f32));
assert_eq!(wf.next(), Some(-0.5f32));
assert_eq!(wf.next(), Some(0.0f32));
assert_eq!(wf.next(), Some(0.5f32));
assert_eq!(wf.next(), Some(1.0f32));
assert_eq!(wf.next(), Some(0.5f32));
assert_eq!(wf.next(), Some(0.0f32));
assert_eq!(wf.next(), Some(-0.5f32));
assert_eq!(wf.next(), Some(-1.0f32));
assert_eq!(wf.next(), Some(-0.5f32));
assert_eq!(wf.next(), Some(0.0f32));
assert_eq!(wf.next(), Some(0.5f32));
assert_eq!(wf.next(), Some(1.0f32));
assert_eq!(wf.next(), Some(0.5f32));
assert_eq!(wf.next(), Some(0.0f32));
assert_eq!(wf.next(), Some(-0.5f32));
}
#[test]
fn saw() {
let mut wf = SignalGenerator::new(cpal::SampleRate(200), 50.0f32, Function::Sawtooth);
assert_eq!(wf.next(), Some(0.0f32));
assert_eq!(wf.next(), Some(0.5f32));
assert_eq!(wf.next(), Some(-1.0f32));
assert_eq!(wf.next(), Some(-0.5f32));
assert_eq!(wf.next(), Some(0.0f32));
assert_eq!(wf.next(), Some(0.5f32));
assert_eq!(wf.next(), Some(-1.0f32));
}
#[test]
fn sine() {
let mut wf = SignalGenerator::new(cpal::SampleRate(1000), 100f32, Function::Sine);
assert_abs_diff_eq!(wf.next().unwrap(), 0.0f32);
assert_abs_diff_eq!(wf.next().unwrap(), 0.58778525f32);
assert_abs_diff_eq!(wf.next().unwrap(), 0.95105652f32);
assert_abs_diff_eq!(wf.next().unwrap(), 0.95105652f32);
assert_abs_diff_eq!(wf.next().unwrap(), 0.58778525f32);
assert_abs_diff_eq!(wf.next().unwrap(), 0.0f32);
assert_abs_diff_eq!(wf.next().unwrap(), -0.58778554f32);
}
}

66
vendor/rodio/src/source/sine.rs vendored Normal file
View File

@@ -0,0 +1,66 @@
use std::time::Duration;
use crate::source::{Function, SignalGenerator};
use crate::Source;
use super::SeekError;
/// An infinite source that produces a sine.
///
/// Always has a rate of 48kHz and one channel.
#[derive(Clone, Debug)]
pub struct SineWave {
test_sine: SignalGenerator,
}
impl SineWave {
const SAMPLE_RATE: u32 = 48000;
/// The frequency of the sine.
#[inline]
pub fn new(freq: f32) -> SineWave {
let sr = cpal::SampleRate(Self::SAMPLE_RATE);
SineWave {
test_sine: SignalGenerator::new(sr, freq, Function::Sine),
}
}
}
impl Iterator for SineWave {
type Item = f32;
#[inline]
fn next(&mut self) -> Option<f32> {
self.test_sine.next()
}
}
impl Source for SineWave {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
1
}
#[inline]
fn sample_rate(&self) -> u32 {
Self::SAMPLE_RATE
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
/// `try_seek()` does nothing on the sine generator. If you need to
/// generate a sine tone with a precise phase or sample offset, consider
/// using `skip::skip_samples()`.
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Ok(())
}
}

259
vendor/rodio/src/source/skip.rs vendored Normal file
View File

@@ -0,0 +1,259 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
const NS_PER_SECOND: u128 = 1_000_000_000;
/// Internal function that builds a `SkipDuration` object.
pub fn skip_duration<I>(mut input: I, duration: Duration) -> SkipDuration<I>
where
I: Source,
I::Item: Sample,
{
do_skip_duration(&mut input, duration);
SkipDuration {
input,
skipped_duration: duration,
}
}
/// Skips specified `duration` of the given `input` source from it's current position.
fn do_skip_duration<I>(input: &mut I, mut duration: Duration)
where
I: Source,
I::Item: Sample,
{
while duration > Duration::new(0, 0) {
if input.current_frame_len().is_none() {
// Sample rate and the amount of channels will be the same till the end.
do_skip_duration_unchecked(input, duration);
return;
}
// .unwrap() safety: if `current_frame_len()` is None, the body of the `if` statement
// above returns before we get here.
let frame_len: usize = input.current_frame_len().unwrap();
// If frame_len is zero, then there is no more data to skip. Instead
// just bail out.
if frame_len == 0 {
return;
}
let ns_per_sample: u128 =
NS_PER_SECOND / input.sample_rate() as u128 / input.channels() as u128;
// Check if we need to skip only part of the current frame.
if frame_len as u128 * ns_per_sample > duration.as_nanos() {
skip_samples(input, (duration.as_nanos() / ns_per_sample) as usize);
return;
}
skip_samples(input, frame_len);
duration -= Duration::from_nanos((frame_len * ns_per_sample as usize) as u64);
}
}
/// Skips specified `duration` from the `input` source assuming that sample rate
/// and amount of channels are not changing.
fn do_skip_duration_unchecked<I>(input: &mut I, duration: Duration)
where
I: Source,
I::Item: Sample,
{
let samples_per_channel: u128 =
duration.as_nanos() * input.sample_rate() as u128 / NS_PER_SECOND;
let samples_to_skip: u128 = samples_per_channel * input.channels() as u128;
skip_samples(input, samples_to_skip as usize);
}
/// Skips `n` samples from the given `input` source.
fn skip_samples<I>(input: &mut I, n: usize)
where
I: Source,
I::Item: Sample,
{
for _ in 0..n {
if input.next().is_none() {
break;
}
}
}
/// A source that skips specified duration of the given source from it's current position.
#[derive(Clone, Debug)]
pub struct SkipDuration<I> {
input: I,
skipped_duration: Duration,
}
impl<I> SkipDuration<I>
where
I: Source,
I::Item: Sample,
{
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Iterator for SkipDuration<I>
where
I: Source,
I::Item: Sample,
{
type Item = <I as Iterator>::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.input.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> Source for SkipDuration<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration().map(|val| {
val.checked_sub(self.skipped_duration)
.unwrap_or_else(|| Duration::from_secs(0))
})
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use crate::buffer::SamplesBuffer;
use crate::source::Source;
fn test_skip_duration_samples_left(
channels: u16,
sample_rate: u32,
seconds: u32,
seconds_to_skip: u32,
) {
let data: Vec<f32> = (1..=(sample_rate * channels as u32 * seconds))
.map(|_| 0f32)
.collect();
let test_buffer = SamplesBuffer::new(channels, sample_rate, data);
let seconds_left = seconds.saturating_sub(seconds_to_skip);
let samples_left_expected = (sample_rate * channels as u32 * seconds_left) as usize;
let samples_left = test_buffer
.skip_duration(Duration::from_secs(seconds_to_skip as u64))
.count();
assert_eq!(samples_left, samples_left_expected);
}
macro_rules! skip_duration_test_block {
($(channels: $ch:expr, sample rate: $sr:expr, seconds: $sec:expr, seconds to skip: $sec_to_skip:expr;)+) => {
$(
test_skip_duration_samples_left($ch, $sr, $sec, $sec_to_skip);
)+
}
}
#[test]
fn skip_duration_shorter_than_source() {
skip_duration_test_block! {
channels: 1, sample rate: 44100, seconds: 5, seconds to skip: 3;
channels: 1, sample rate: 96000, seconds: 5, seconds to skip: 3;
channels: 2, sample rate: 44100, seconds: 5, seconds to skip: 3;
channels: 2, sample rate: 96000, seconds: 5, seconds to skip: 3;
channels: 4, sample rate: 44100, seconds: 5, seconds to skip: 3;
channels: 4, sample rate: 96000, seconds: 5, seconds to skip: 3;
}
}
#[test]
fn skip_duration_zero_duration() {
skip_duration_test_block! {
channels: 1, sample rate: 44100, seconds: 5, seconds to skip: 0;
channels: 1, sample rate: 96000, seconds: 5, seconds to skip: 0;
channels: 2, sample rate: 44100, seconds: 5, seconds to skip: 0;
channels: 2, sample rate: 96000, seconds: 5, seconds to skip: 0;
channels: 4, sample rate: 44100, seconds: 5, seconds to skip: 0;
channels: 4, sample rate: 96000, seconds: 5, seconds to skip: 0;
}
}
#[test]
fn skip_duration_longer_than_source() {
skip_duration_test_block! {
channels: 1, sample rate: 44100, seconds: 1, seconds to skip: 5;
channels: 1, sample rate: 96000, seconds: 10, seconds to skip: 11;
channels: 2, sample rate: 44100, seconds: 1, seconds to skip: 5;
channels: 2, sample rate: 96000, seconds: 10, seconds to skip: 11;
channels: 4, sample rate: 44100, seconds: 1, seconds to skip: 5;
channels: 4, sample rate: 96000, seconds: 10, seconds to skip: 11;
}
}
#[test]
fn skip_duration_equal_to_source_length() {
skip_duration_test_block! {
channels: 1, sample rate: 44100, seconds: 1, seconds to skip: 1;
channels: 1, sample rate: 96000, seconds: 10, seconds to skip: 10;
channels: 2, sample rate: 44100, seconds: 1, seconds to skip: 1;
channels: 2, sample rate: 96000, seconds: 10, seconds to skip: 10;
channels: 4, sample rate: 44100, seconds: 1, seconds to skip: 1;
channels: 4, sample rate: 96000, seconds: 10, seconds to skip: 10;
}
}
}

104
vendor/rodio/src/source/skippable.rs vendored Normal file
View File

@@ -0,0 +1,104 @@
use std::time::Duration;
use crate::Sample;
use crate::Source;
use super::SeekError;
/// Wrap the source in a skippable. It allows ending the current source early by
/// calling [`Skippable::skip`]. If this source is in a queue such as the Sink
/// ending the source early is equal to skipping the source.
pub fn skippable<I>(source: I) -> Skippable<I> {
Skippable {
input: source,
do_skip: false,
}
}
/// Wrap the source in a skippable. It allows ending the current source early by
/// calling [`Skippable::skip`]. If this source is in a queue such as the Sink
/// ending the source early is equal to skipping the source.
#[derive(Clone, Debug)]
pub struct Skippable<I> {
input: I,
do_skip: bool,
}
impl<I> Skippable<I> {
/// Skips the current source
#[inline]
pub fn skip(&mut self) {
self.do_skip = true;
}
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Iterator for Skippable<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
if self.do_skip {
None
} else {
self.input.next()
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> Source for Skippable<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}

129
vendor/rodio/src/source/spatial.rs vendored Normal file
View File

@@ -0,0 +1,129 @@
use std::time::Duration;
use crate::source::ChannelVolume;
use crate::{Sample, Source};
use super::SeekError;
/// A simple spatial audio source. The underlying source is transformed to Mono
/// and then played in stereo. The left and right channel's volume are amplified
/// differently depending on the distance of the left and right ear to the source.
#[derive(Clone)]
pub struct Spatial<I>
where
I: Source,
I::Item: Sample,
{
input: ChannelVolume<I>,
}
fn dist_sq(a: [f32; 3], b: [f32; 3]) -> f32 {
a.iter()
.zip(b.iter())
.map(|(a, b)| (a - b) * (a - b))
.sum::<f32>()
}
impl<I> Spatial<I>
where
I: Source,
I::Item: Sample,
{
/// Builds a new `SpatialSink`, beginning playback on a stream.
pub fn new(
input: I,
emitter_position: [f32; 3],
left_ear: [f32; 3],
right_ear: [f32; 3],
) -> Spatial<I>
where
I: Source,
I::Item: Sample,
{
let mut ret = Spatial {
input: ChannelVolume::new(input, vec![0.0, 0.0]),
};
ret.set_positions(emitter_position, left_ear, right_ear);
ret
}
/// Sets the position of the emitter and ears in the 3D world.
pub fn set_positions(
&mut self,
emitter_pos: [f32; 3],
left_ear: [f32; 3],
right_ear: [f32; 3],
) {
debug_assert!(left_ear != right_ear);
let left_dist_sq = dist_sq(left_ear, emitter_pos);
let right_dist_sq = dist_sq(right_ear, emitter_pos);
let max_diff = dist_sq(left_ear, right_ear).sqrt();
let left_dist = left_dist_sq.sqrt();
let right_dist = right_dist_sq.sqrt();
let left_diff_modifier = (((left_dist - right_dist) / max_diff + 1.0) / 4.0 + 0.5).min(1.0);
let right_diff_modifier =
(((right_dist - left_dist) / max_diff + 1.0) / 4.0 + 0.5).min(1.0);
let left_dist_modifier = (1.0 / left_dist_sq).min(1.0);
let right_dist_modifier = (1.0 / right_dist_sq).min(1.0);
self.input
.set_volume(0, left_diff_modifier * left_dist_modifier);
self.input
.set_volume(1, right_diff_modifier * right_dist_modifier);
}
}
impl<I> Iterator for Spatial<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
self.input.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> ExactSizeIterator for Spatial<I>
where
I: Source + ExactSizeIterator,
I::Item: Sample,
{
}
impl<I> Source for Spatial<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}

149
vendor/rodio/src/source/speed.rs vendored Normal file
View File

@@ -0,0 +1,149 @@
//! Playback Speed control Module.
//!
//! The main concept of this module is the [`Speed`] struct, which
//! encapsulates playback speed controls of the current sink.
//!
//! In order to speed up a sink, the speed struct:
//! - Increases the current sample rate by the given factor
//! - Updates the total duration function to cover for the new factor by dividing by the factor
//! - Updates the try_seek function by multiplying the audio position by the factor
//!
//! To speed up a source from sink all you need to do is call the `set_speed(factor: f32)` function
//! For example, here is how you speed up your sound by using sink or playing raw
//!
//! ```no_run
//!# use std::fs::File;
//!# use std::io::BufReader;
//!# use rodio::{Decoder, Sink, OutputStream, source::{Source, SineWave}};
//!
//! // Get an output stream handle to the default physical sound device.
//! // Note that no sound will be played if _stream is dropped
//! let (_stream, stream_handle) = OutputStream::try_default().unwrap();
//! // Load a sound from a file, using a path relative to Cargo.toml
//! let file = BufReader::new(File::open("examples/music.ogg").unwrap());
//! // Decode that sound file into a source
//! let source = Decoder::new(file).unwrap();
//! // Play the sound directly on the device 2x faster
//! stream_handle.play_raw(source.convert_samples().speed(2.0));
//! std::thread::sleep(std::time::Duration::from_secs(5));
//! ```
//! here is how you would do it using the sink
//! ```
//! let source = SineWave::new(440.0)
//! .take_duration(Duration::from_secs_f32(20.25))
//! .amplify(0.20);
//!
//! let sink = Sink::try_new(&stream_handle)?;
//! sink.set_speed(2.0);
//! sink.append(source);
//! std::thread::sleep(std::time::Duration::from_secs(5));
//! ```
//! Notice the increase in pitch as the factor increases
//!
//! Since the samples are played faster the audio wave get shorter increasing their frequencies
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Internal function that builds a `Speed` object.
pub fn speed<I>(input: I, factor: f32) -> Speed<I> {
Speed { input, factor }
}
/// Filter that modifies each sample by a given value.
#[derive(Clone, Debug)]
pub struct Speed<I> {
input: I,
factor: f32,
}
impl<I> Speed<I>
where
I: Source,
I::Item: Sample,
{
/// Modifies the speed factor.
#[inline]
pub fn set_factor(&mut self, factor: f32) {
self.factor = factor;
}
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Iterator for Speed<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
self.input.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> ExactSizeIterator for Speed<I>
where
I: Source + ExactSizeIterator,
I::Item: Sample,
{
}
impl<I> Source for Speed<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
(self.input.sample_rate() as f32 * self.factor) as u32
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration().map(|d| d.div_f32(self.factor))
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
let pos_accounting_for_speedup = pos.mul_f32(self.factor);
self.input.try_seek(pos_accounting_for_speedup)
}
}

99
vendor/rodio/src/source/stoppable.rs vendored Normal file
View File

@@ -0,0 +1,99 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// This is the same as [`skippable`](crate::source::skippable) see its docs
pub fn stoppable<I>(source: I) -> Stoppable<I> {
Stoppable {
input: source,
stopped: false,
}
}
/// This is the same as [`Skippable`](crate::source::Skippable) see its docs
#[derive(Clone, Debug)]
pub struct Stoppable<I> {
input: I,
stopped: bool,
}
impl<I> Stoppable<I> {
/// Stops the sound.
#[inline]
pub fn stop(&mut self) {
self.stopped = true;
}
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> Iterator for Stoppable<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
if self.stopped {
None
} else {
self.input.next()
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.input.size_hint()
}
}
impl<I> Source for Stoppable<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.input.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}

189
vendor/rodio/src/source/take.rs vendored Normal file
View File

@@ -0,0 +1,189 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Internal function that builds a `TakeDuration` object.
pub fn take_duration<I>(input: I, duration: Duration) -> TakeDuration<I>
where
I: Source,
I::Item: Sample,
{
TakeDuration {
current_frame_len: input.current_frame_len(),
duration_per_sample: TakeDuration::get_duration_per_sample(&input),
input,
remaining_duration: duration,
requested_duration: duration,
filter: None,
}
}
/// A filter that can be applied to a `TakeDuration`.
#[derive(Clone, Debug)]
enum DurationFilter {
FadeOut,
}
impl DurationFilter {
fn apply<I: Iterator>(
&self,
sample: <I as Iterator>::Item,
parent: &TakeDuration<I>,
) -> <I as Iterator>::Item
where
I::Item: Sample,
{
use self::DurationFilter::*;
match self {
FadeOut => {
let remaining = parent.remaining_duration.as_millis() as f32;
let total = parent.requested_duration.as_millis() as f32;
sample.amplify(remaining / total)
}
}
}
}
const NANOS_PER_SEC: u64 = 1_000_000_000;
/// A source that truncates the given source to a certain duration.
#[derive(Clone, Debug)]
pub struct TakeDuration<I> {
input: I,
remaining_duration: Duration,
requested_duration: Duration,
filter: Option<DurationFilter>,
// Remaining samples in current frame.
current_frame_len: Option<usize>,
// Only updated when the current frame len is exhausted.
duration_per_sample: Duration,
}
impl<I> TakeDuration<I>
where
I: Source,
I::Item: Sample,
{
/// Returns the duration elapsed for each sample extracted.
#[inline]
fn get_duration_per_sample(input: &I) -> Duration {
let ns = NANOS_PER_SEC / (input.sample_rate() as u64 * input.channels() as u64);
// \|/ the maximum value of `ns` is one billion, so this can't fail
Duration::new(0, ns as u32)
}
/// Returns a reference to the inner source.
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
/// Returns a mutable reference to the inner source.
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
/// Returns the inner source.
#[inline]
pub fn into_inner(self) -> I {
self.input
}
/// Make the truncated source end with a FadeOut. The fadeout covers the
/// entire length of the take source.
pub fn set_filter_fadeout(&mut self) {
self.filter = Some(DurationFilter::FadeOut);
}
/// Remove any filter set.
pub fn clear_filter(&mut self) {
self.filter = None;
}
}
impl<I> Iterator for TakeDuration<I>
where
I: Source,
I::Item: Sample,
{
type Item = <I as Iterator>::Item;
fn next(&mut self) -> Option<<I as Iterator>::Item> {
if let Some(frame_len) = self.current_frame_len.take() {
if frame_len > 0 {
self.current_frame_len = Some(frame_len - 1);
} else {
self.current_frame_len = self.input.current_frame_len();
// Sample rate might have changed
self.duration_per_sample = Self::get_duration_per_sample(&self.input);
}
}
if self.remaining_duration <= self.duration_per_sample {
None
} else if let Some(sample) = self.input.next() {
let sample = match &self.filter {
Some(filter) => filter.apply(sample, self),
None => sample,
};
self.remaining_duration -= self.duration_per_sample;
Some(sample)
} else {
None
}
}
// TODO: size_hint
}
impl<I> Source for TakeDuration<I>
where
I: Iterator + Source,
I::Item: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
let remaining_nanos = self.remaining_duration.as_secs() * NANOS_PER_SEC
+ self.remaining_duration.subsec_nanos() as u64;
let nanos_per_sample = self.duration_per_sample.as_secs() * NANOS_PER_SEC
+ self.duration_per_sample.subsec_nanos() as u64;
let remaining_samples = (remaining_nanos / nanos_per_sample) as usize;
self.input
.current_frame_len()
.filter(|value| *value < remaining_samples)
.or(Some(remaining_samples))
}
#[inline]
fn channels(&self) -> u16 {
self.input.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.input.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
if let Some(duration) = self.input.total_duration() {
if duration < self.requested_duration {
Some(duration)
} else {
Some(self.requested_duration)
}
} else {
None
}
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.input.try_seek(pos)
}
}

212
vendor/rodio/src/source/uniform.rs vendored Normal file
View File

@@ -0,0 +1,212 @@
use std::cmp;
use std::time::Duration;
use cpal::FromSample;
use crate::conversions::{ChannelCountConverter, DataConverter, SampleRateConverter};
use crate::{Sample, Source};
use super::SeekError;
/// An iterator that reads from a `Source` and converts the samples to a
/// specific type, sample-rate and channels count.
///
/// It implements `Source` as well, but all the data is guaranteed to be in a
/// single frame whose channels and samples rate have been passed to `new`.
#[derive(Clone)]
pub struct UniformSourceIterator<I, D>
where
I: Source,
I::Item: Sample,
D: Sample,
{
inner: Option<DataConverter<ChannelCountConverter<SampleRateConverter<Take<I>>>, D>>,
target_channels: u16,
target_sample_rate: u32,
total_duration: Option<Duration>,
}
impl<I, D> UniformSourceIterator<I, D>
where
I: Source,
I::Item: Sample,
D: Sample,
{
/// Wrap a `Source` and lazily convert its samples to a specific type,
/// sample-rate and channels count.
#[inline]
pub fn new(
input: I,
target_channels: u16,
target_sample_rate: u32,
) -> UniformSourceIterator<I, D> {
let total_duration = input.total_duration();
let input = UniformSourceIterator::bootstrap(input, target_channels, target_sample_rate);
UniformSourceIterator {
inner: Some(input),
target_channels,
target_sample_rate,
total_duration,
}
}
#[inline]
fn bootstrap(
input: I,
target_channels: u16,
target_sample_rate: u32,
) -> DataConverter<ChannelCountConverter<SampleRateConverter<Take<I>>>, D> {
// Limit the frame length to something reasonable
let frame_len = input.current_frame_len().map(|x| x.min(32768));
let from_channels = input.channels();
let from_sample_rate = input.sample_rate();
let input = Take {
iter: input,
n: frame_len,
};
let input = SampleRateConverter::new(
input,
cpal::SampleRate(from_sample_rate),
cpal::SampleRate(target_sample_rate),
from_channels,
);
let input = ChannelCountConverter::new(input, from_channels, target_channels);
DataConverter::new(input)
}
}
impl<I, D> Iterator for UniformSourceIterator<I, D>
where
I: Source,
I::Item: Sample,
D: FromSample<I::Item> + Sample,
{
type Item = D;
#[inline]
fn next(&mut self) -> Option<D> {
if let Some(value) = self.inner.as_mut().unwrap().next() {
return Some(value);
}
let input = self
.inner
.take()
.unwrap()
.into_inner()
.into_inner()
.into_inner()
.iter;
let mut input =
UniformSourceIterator::bootstrap(input, self.target_channels, self.target_sample_rate);
let value = input.next();
self.inner = Some(input);
value
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.inner.as_ref().unwrap().size_hint().0, None)
}
}
impl<I, D> Source for UniformSourceIterator<I, D>
where
I: Iterator + Source,
I::Item: Sample,
D: FromSample<I::Item> + Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
self.target_channels
}
#[inline]
fn sample_rate(&self) -> u32 {
self.target_sample_rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.total_duration
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
if let Some(input) = self.inner.as_mut() {
input
.inner_mut()
.inner_mut()
.inner_mut()
.inner_mut()
.try_seek(pos)
} else {
Ok(())
}
}
}
#[derive(Clone, Debug)]
struct Take<I> {
iter: I,
n: Option<usize>,
}
impl<I> Take<I> {
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.iter
}
}
impl<I> Iterator for Take<I>
where
I: Iterator,
{
type Item = <I as Iterator>::Item;
#[inline]
fn next(&mut self) -> Option<<I as Iterator>::Item> {
if let Some(n) = &mut self.n {
if *n != 0 {
*n -= 1;
self.iter.next()
} else {
None
}
} else {
self.iter.next()
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if let Some(n) = self.n {
let (lower, upper) = self.iter.size_hint();
let lower = cmp::min(lower, n);
let upper = match upper {
Some(x) if x < n => Some(x),
_ => Some(n),
};
(lower, upper)
} else {
self.iter.size_hint()
}
}
}
impl<I> ExactSizeIterator for Take<I> where I: ExactSizeIterator {}

91
vendor/rodio/src/source/zero.rs vendored Normal file
View File

@@ -0,0 +1,91 @@
use std::marker::PhantomData;
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// An source that produces samples with value zero (silence). Depending on if
/// it where created with [`Zero::new`] or [`Zero::new_samples`] it can be never
/// ending or finite.
#[derive(Clone, Debug)]
pub struct Zero<S> {
channels: u16,
sample_rate: u32,
num_samples: Option<usize>,
marker: PhantomData<S>,
}
impl<S> Zero<S> {
/// Create a new source that never ends and produces total silence.
#[inline]
pub fn new(channels: u16, sample_rate: u32) -> Zero<S> {
Zero {
channels,
sample_rate,
num_samples: None,
marker: PhantomData,
}
}
/// Create a new source that never ends and produces total silence.
#[inline]
pub fn new_samples(channels: u16, sample_rate: u32, num_samples: usize) -> Zero<S> {
Zero {
channels,
sample_rate,
num_samples: Some(num_samples),
marker: PhantomData,
}
}
}
impl<S> Iterator for Zero<S>
where
S: Sample,
{
type Item = S;
#[inline]
fn next(&mut self) -> Option<S> {
if let Some(num_samples) = self.num_samples {
if num_samples > 0 {
self.num_samples = Some(num_samples - 1);
Some(S::zero_value())
} else {
None
}
} else {
Some(S::zero_value())
}
}
}
impl<S> Source for Zero<S>
where
S: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.num_samples
}
#[inline]
fn channels(&self) -> u16 {
self.channels
}
#[inline]
fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Ok(())
}
}

223
vendor/rodio/src/spatial_sink.rs vendored Normal file
View File

@@ -0,0 +1,223 @@
use std::f32;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use cpal::FromSample;
use crate::source::{SeekError, Spatial};
use crate::stream::{OutputStreamHandle, PlayError};
use crate::{Sample, Sink, Source};
/// A sink that allows changing the position of the source and the listeners
/// ears while playing. The sources played are then transformed to give a simple
/// spatial effect. See [`Spatial`] for details.
pub struct SpatialSink {
sink: Sink,
positions: Arc<Mutex<SoundPositions>>,
}
struct SoundPositions {
emitter_position: [f32; 3],
left_ear: [f32; 3],
right_ear: [f32; 3],
}
impl SpatialSink {
/// Builds a new `SpatialSink`.
pub fn try_new(
stream: &OutputStreamHandle,
emitter_position: [f32; 3],
left_ear: [f32; 3],
right_ear: [f32; 3],
) -> Result<SpatialSink, PlayError> {
Ok(SpatialSink {
sink: Sink::try_new(stream)?,
positions: Arc::new(Mutex::new(SoundPositions {
emitter_position,
left_ear,
right_ear,
})),
})
}
/// Sets the position of the sound emitter in 3 dimensional space.
pub fn set_emitter_position(&self, pos: [f32; 3]) {
self.positions.lock().unwrap().emitter_position = pos;
}
/// Sets the position of the left ear in 3 dimensional space.
pub fn set_left_ear_position(&self, pos: [f32; 3]) {
self.positions.lock().unwrap().left_ear = pos;
}
/// Sets the position of the right ear in 3 dimensional space.
pub fn set_right_ear_position(&self, pos: [f32; 3]) {
self.positions.lock().unwrap().right_ear = pos;
}
/// Appends a sound to the queue of sounds to play.
#[inline]
pub fn append<S>(&self, source: S)
where
S: Source + Send + 'static,
f32: FromSample<S::Item>,
S::Item: Sample + Send,
{
let positions = self.positions.clone();
let pos_lock = self.positions.lock().unwrap();
let source = Spatial::new(
source,
pos_lock.emitter_position,
pos_lock.left_ear,
pos_lock.right_ear,
)
.periodic_access(Duration::from_millis(10), move |i| {
let pos = positions.lock().unwrap();
i.set_positions(pos.emitter_position, pos.left_ear, pos.right_ear);
});
self.sink.append(source);
}
// Gets the volume of the sound.
///
/// The value `1.0` is the "normal" volume (unfiltered input). Any value other than 1.0 will
/// multiply each sample by this value.
#[inline]
pub fn volume(&self) -> f32 {
self.sink.volume()
}
/// Changes the volume of the sound.
///
/// The value `1.0` is the "normal" volume (unfiltered input). Any value other than 1.0 will
/// multiply each sample by this value.
#[inline]
pub fn set_volume(&self, value: f32) {
self.sink.set_volume(value);
}
/// Changes the play speed of the sound. Does not adjust the samples, only the playback speed.
///
/// # Note:
/// 1. **Increasing the speed will increase the pitch by the same factor**
/// - If you set the speed to 0.5 this will halve the frequency of the sound
/// lowering its pitch.
/// - If you set the speed to 2 the frequency will double raising the
/// pitch of the sound.
/// 2. **Change in the speed affect the total duration inversely**
/// - If you set the speed to 0.5, the total duration will be twice as long.
/// - If you set the speed to 2 the total duration will be halve of what it
/// was.
///
/// See [`Speed`] for details
#[inline]
pub fn speed(&self) -> f32 {
self.sink.speed()
}
/// Changes the speed of the sound.
///
/// The value `1.0` is the "normal" speed (unfiltered input). Any value other than `1.0` will
/// change the play speed of the sound.
#[inline]
pub fn set_speed(&self, value: f32) {
self.sink.set_speed(value)
}
/// Resumes playback of a paused sound.
///
/// No effect if not paused.
#[inline]
pub fn play(&self) {
self.sink.play();
}
/// Pauses playback of this sink.
///
/// No effect if already paused.
///
/// A paused sound can be resumed with `play()`.
pub fn pause(&self) {
self.sink.pause();
}
/// Gets if a sound is paused
///
/// Sounds can be paused and resumed using pause() and play(). This gets if a sound is paused.
pub fn is_paused(&self) -> bool {
self.sink.is_paused()
}
/// Removes all currently loaded `Source`s from the `SpatialSink` and pauses it.
///
/// See `pause()` for information about pausing a `Sink`.
#[inline]
pub fn clear(&self) {
self.sink.clear();
}
/// Stops the sink by emptying the queue.
#[inline]
pub fn stop(&self) {
self.sink.stop()
}
/// Destroys the sink without stopping the sounds that are still playing.
#[inline]
pub fn detach(self) {
self.sink.detach();
}
/// Sleeps the current thread until the sound ends.
#[inline]
pub fn sleep_until_end(&self) {
self.sink.sleep_until_end();
}
/// Returns true if this sink has no more sounds to play.
#[inline]
pub fn empty(&self) -> bool {
self.sink.empty()
}
/// Returns the number of sounds currently in the queue.
#[allow(clippy::len_without_is_empty)]
#[inline]
pub fn len(&self) -> usize {
self.sink.len()
}
/// Attempts to seek to a given position in the current source.
///
/// This blocks between 0 and ~5 milliseconds.
///
/// As long as the duration of the source is known seek is guaranteed to saturate
/// at the end of the source. For example given a source that reports a total duration
/// of 42 seconds calling `try_seek()` with 60 seconds as argument will seek to
/// 42 seconds.
///
/// # Errors
/// This function will return [`SeekError::NotSupported`] if one of the underlying
/// sources does not support seeking.
///
/// It will return an error if an implementation ran
/// into one during the seek.
///
/// When seeking beyond the end of a source this
/// function might return an error if the duration of the source is not known.
pub fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
self.sink.try_seek(pos)
}
/// Returns the position of the sound that's being played.
///
/// This takes into account any speedup or delay applied.
///
/// Example: if you apply a speedup of *2* to an mp3 decoder source and
/// [`get_pos()`](Sink::get_pos) returns *5s* then the position in the mp3
/// recording is *10s* from its start.
#[inline]
pub fn get_pos(&self) -> Duration {
self.sink.get_pos()
}
}

155
vendor/rodio/src/static_buffer.rs vendored Normal file
View File

@@ -0,0 +1,155 @@
//! A simple source of samples coming from a static buffer.
//!
//! The `StaticSamplesBuffer` struct can be used to treat a list of values as a `Source`.
//!
//! # Example
//!
//! ```
//! use rodio::static_buffer::StaticSamplesBuffer;
//! let _ = StaticSamplesBuffer::new(1, 44100, &[1i16, 2, 3, 4, 5, 6]);
//! ```
//!
use std::slice::Iter as SliceIter;
use std::time::Duration;
use crate::source::SeekError;
use crate::{Sample, Source};
/// A buffer of samples treated as a source.
#[derive(Clone)]
pub struct StaticSamplesBuffer<S>
where
S: 'static,
{
data: SliceIter<'static, S>,
channels: u16,
sample_rate: u32,
duration: Duration,
}
impl<S> StaticSamplesBuffer<S>
where
S: Sample,
{
/// Builds a new `StaticSamplesBuffer`.
///
/// # Panic
///
/// - Panics if the number of channels is zero.
/// - Panics if the samples rate is zero.
/// - Panics if the length of the buffer is larger than approximately 16 billion elements.
/// This is because the calculation of the duration would overflow.
///
pub fn new(channels: u16, sample_rate: u32, data: &'static [S]) -> StaticSamplesBuffer<S> {
assert!(channels != 0);
assert!(sample_rate != 0);
let duration_ns = 1_000_000_000u64.checked_mul(data.len() as u64).unwrap()
/ sample_rate as u64
/ channels as u64;
let duration = Duration::new(
duration_ns / 1_000_000_000,
(duration_ns % 1_000_000_000) as u32,
);
StaticSamplesBuffer {
data: data.iter(),
channels,
sample_rate,
duration,
}
}
}
impl<S> Source for StaticSamplesBuffer<S>
where
S: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
self.channels
}
#[inline]
fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
Some(self.duration)
}
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Err(SeekError::NotSupported {
underlying_source: std::any::type_name::<Self>(),
})
}
}
impl<S> Iterator for StaticSamplesBuffer<S>
where
S: Sample + Clone,
{
type Item = S;
#[inline]
fn next(&mut self) -> Option<S> {
self.data.next().cloned()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.data.size_hint()
}
}
#[cfg(test)]
mod tests {
use crate::source::Source;
use crate::static_buffer::StaticSamplesBuffer;
#[test]
fn basic() {
let _ = StaticSamplesBuffer::new(1, 44100, &[0i16, 0, 0, 0, 0, 0]);
}
#[test]
#[should_panic]
fn panic_if_zero_channels() {
StaticSamplesBuffer::new(0, 44100, &[0i16, 0, 0, 0, 0, 0]);
}
#[test]
#[should_panic]
fn panic_if_zero_sample_rate() {
StaticSamplesBuffer::new(1, 0, &[0i16, 0, 0, 0, 0, 0]);
}
#[test]
fn duration_basic() {
let buf = StaticSamplesBuffer::new(2, 2, &[0i16, 0, 0, 0, 0, 0]);
let dur = buf.total_duration().unwrap();
assert_eq!(dur.as_secs(), 1);
assert_eq!(dur.subsec_nanos(), 500_000_000);
}
#[test]
fn iteration() {
let mut buf = StaticSamplesBuffer::new(1, 44100, &[1i16, 2, 3, 4, 5, 6]);
assert_eq!(buf.next(), Some(1));
assert_eq!(buf.next(), Some(2));
assert_eq!(buf.next(), Some(3));
assert_eq!(buf.next(), Some(4));
assert_eq!(buf.next(), Some(5));
assert_eq!(buf.next(), Some(6));
assert_eq!(buf.next(), None);
}
}

353
vendor/rodio/src/stream.rs vendored Normal file
View File

@@ -0,0 +1,353 @@
use std::io::{Read, Seek};
use std::sync::{Arc, Weak};
use std::{error, fmt};
use crate::decoder;
use crate::dynamic_mixer::{self, DynamicMixerController};
use crate::sink::Sink;
use crate::source::Source;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::{Sample, SupportedStreamConfig};
/// `cpal::Stream` container. Also see the more useful `OutputStreamHandle`.
///
/// If this is dropped playback will end & attached `OutputStreamHandle`s will no longer work.
pub struct OutputStream {
mixer: Arc<DynamicMixerController<f32>>,
_stream: cpal::Stream,
}
/// More flexible handle to a `OutputStream` that provides playback.
#[derive(Clone)]
pub struct OutputStreamHandle {
mixer: Weak<DynamicMixerController<f32>>,
}
impl OutputStream {
/// Returns a new stream & handle using the given output device and the default output
/// configuration.
pub fn try_from_device(
device: &cpal::Device,
) -> Result<(Self, OutputStreamHandle), StreamError> {
let default_config = device
.default_output_config()
.map_err(StreamError::DefaultStreamConfigError)?;
OutputStream::try_from_device_config(device, default_config)
}
/// Returns a new stream & handle using the given device and stream config.
///
/// If the supplied `SupportedStreamConfig` is invalid for the device this function will
/// fail to create an output stream and instead return a `StreamError`
pub fn try_from_device_config(
device: &cpal::Device,
config: SupportedStreamConfig,
) -> Result<(Self, OutputStreamHandle), StreamError> {
let (mixer, _stream) = device.try_new_output_stream_config(config)?;
_stream.play().map_err(StreamError::PlayStreamError)?;
let out = Self { mixer, _stream };
let handle = OutputStreamHandle {
mixer: Arc::downgrade(&out.mixer),
};
Ok((out, handle))
}
/// Return a new stream & handle using the default output device.
///
/// On failure will fallback to trying any non-default output devices.
pub fn try_default() -> Result<(Self, OutputStreamHandle), StreamError> {
let default_device = cpal::default_host()
.default_output_device()
.ok_or(StreamError::NoDevice)?;
let default_stream = Self::try_from_device(&default_device);
default_stream.or_else(|original_err| {
// default device didn't work, try other ones
let mut devices = match cpal::default_host().output_devices() {
Ok(d) => d,
Err(_) => return Err(original_err),
};
devices
.find_map(|d| Self::try_from_device(&d).ok())
.ok_or(original_err)
})
}
}
impl OutputStreamHandle {
/// Plays a source with a device until it ends.
pub fn play_raw<S>(&self, source: S) -> Result<(), PlayError>
where
S: Source<Item = f32> + Send + 'static,
{
let mixer = self.mixer.upgrade().ok_or(PlayError::NoDevice)?;
mixer.add(source);
Ok(())
}
/// Plays a sound once. Returns a `Sink` that can be used to control the sound.
pub fn play_once<R>(&self, input: R) -> Result<Sink, PlayError>
where
R: Read + Seek + Send + Sync + 'static,
{
let input = decoder::Decoder::new(input)?;
let sink = Sink::try_new(self)?;
sink.append(input);
Ok(sink)
}
}
/// An error occurred while attempting to play a sound.
#[derive(Debug)]
pub enum PlayError {
/// Attempting to decode the audio failed.
DecoderError(decoder::DecoderError),
/// The output device was lost.
NoDevice,
}
impl From<decoder::DecoderError> for PlayError {
fn from(err: decoder::DecoderError) -> Self {
Self::DecoderError(err)
}
}
impl fmt::Display for PlayError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DecoderError(e) => e.fmt(f),
Self::NoDevice => write!(f, "NoDevice"),
}
}
}
impl error::Error for PlayError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::DecoderError(e) => Some(e),
Self::NoDevice => None,
}
}
}
/// Errors that might occur when interfacing with audio output.
#[derive(Debug)]
pub enum StreamError {
/// Could not start playing the stream, see [cpal::PlayStreamError] for
/// details.
PlayStreamError(cpal::PlayStreamError),
/// Failed to get the stream config for device the given device. See
/// [cpal::DefaultStreamConfigError] for details
DefaultStreamConfigError(cpal::DefaultStreamConfigError),
/// Error opening stream with OS. See [cpal::BuildStreamError] for details
BuildStreamError(cpal::BuildStreamError),
/// Could not list supported stream configs for device. Maybe it
/// disconnected, for details see: [cpal::SupportedStreamConfigsError].
SupportedStreamConfigsError(cpal::SupportedStreamConfigsError),
/// Could not find any output device
NoDevice,
}
impl fmt::Display for StreamError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::PlayStreamError(e) => e.fmt(f),
Self::BuildStreamError(e) => e.fmt(f),
Self::DefaultStreamConfigError(e) => e.fmt(f),
Self::SupportedStreamConfigsError(e) => e.fmt(f),
Self::NoDevice => write!(f, "NoDevice"),
}
}
}
impl error::Error for StreamError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::PlayStreamError(e) => Some(e),
Self::BuildStreamError(e) => Some(e),
Self::DefaultStreamConfigError(e) => Some(e),
Self::SupportedStreamConfigsError(e) => Some(e),
Self::NoDevice => None,
}
}
}
/// Extensions to `cpal::Device`
pub(crate) trait CpalDeviceExt {
fn new_output_stream_with_format(
&self,
format: cpal::SupportedStreamConfig,
) -> Result<(Arc<DynamicMixerController<f32>>, cpal::Stream), cpal::BuildStreamError>;
fn try_new_output_stream_config(
&self,
config: cpal::SupportedStreamConfig,
) -> Result<(Arc<DynamicMixerController<f32>>, cpal::Stream), StreamError>;
}
impl CpalDeviceExt for cpal::Device {
fn new_output_stream_with_format(
&self,
format: cpal::SupportedStreamConfig,
) -> Result<(Arc<DynamicMixerController<f32>>, cpal::Stream), cpal::BuildStreamError> {
let (mixer_tx, mut mixer_rx) =
dynamic_mixer::mixer::<f32>(format.channels(), format.sample_rate().0);
let error_callback = |err| {
#[cfg(feature = "tracing")]
tracing::error!("an error occurred on output stream: {err}");
#[cfg(not(feature = "tracing"))]
eprintln!("an error occurred on output stream: {err}");
};
match format.sample_format() {
cpal::SampleFormat::F32 => self.build_output_stream::<f32, _, _>(
&format.config(),
move |data, _| {
data.iter_mut()
.for_each(|d| *d = mixer_rx.next().unwrap_or(0f32))
},
error_callback,
None,
),
cpal::SampleFormat::F64 => self.build_output_stream::<f64, _, _>(
&format.config(),
move |data, _| {
data.iter_mut()
.for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0f64))
},
error_callback,
None,
),
cpal::SampleFormat::I8 => self.build_output_stream::<i8, _, _>(
&format.config(),
move |data, _| {
data.iter_mut()
.for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i8))
},
error_callback,
None,
),
cpal::SampleFormat::I16 => self.build_output_stream::<i16, _, _>(
&format.config(),
move |data, _| {
data.iter_mut()
.for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i16))
},
error_callback,
None,
),
cpal::SampleFormat::I32 => self.build_output_stream::<i32, _, _>(
&format.config(),
move |data, _| {
data.iter_mut()
.for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i32))
},
error_callback,
None,
),
cpal::SampleFormat::I64 => self.build_output_stream::<i64, _, _>(
&format.config(),
move |data, _| {
data.iter_mut()
.for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i64))
},
error_callback,
None,
),
cpal::SampleFormat::U8 => self.build_output_stream::<u8, _, _>(
&format.config(),
move |data, _| {
data.iter_mut().for_each(|d| {
*d = mixer_rx
.next()
.map(Sample::from_sample)
.unwrap_or(u8::MAX / 2)
})
},
error_callback,
None,
),
cpal::SampleFormat::U16 => self.build_output_stream::<u16, _, _>(
&format.config(),
move |data, _| {
data.iter_mut().for_each(|d| {
*d = mixer_rx
.next()
.map(Sample::from_sample)
.unwrap_or(u16::MAX / 2)
})
},
error_callback,
None,
),
cpal::SampleFormat::U32 => self.build_output_stream::<u32, _, _>(
&format.config(),
move |data, _| {
data.iter_mut().for_each(|d| {
*d = mixer_rx
.next()
.map(Sample::from_sample)
.unwrap_or(u32::MAX / 2)
})
},
error_callback,
None,
),
cpal::SampleFormat::U64 => self.build_output_stream::<u64, _, _>(
&format.config(),
move |data, _| {
data.iter_mut().for_each(|d| {
*d = mixer_rx
.next()
.map(Sample::from_sample)
.unwrap_or(u64::MAX / 2)
})
},
error_callback,
None,
),
_ => return Err(cpal::BuildStreamError::StreamConfigNotSupported),
}
.map(|stream| (mixer_tx, stream))
}
fn try_new_output_stream_config(
&self,
config: SupportedStreamConfig,
) -> Result<(Arc<DynamicMixerController<f32>>, cpal::Stream), StreamError> {
self.new_output_stream_with_format(config).or_else(|err| {
// look through all supported formats to see if another works
supported_output_formats(self)?
.find_map(|format| self.new_output_stream_with_format(format).ok())
// return original error if nothing works
.ok_or(StreamError::BuildStreamError(err))
})
}
}
/// All the supported output formats with sample rates
fn supported_output_formats(
device: &cpal::Device,
) -> Result<impl Iterator<Item = cpal::SupportedStreamConfig>, StreamError> {
const HZ_44100: cpal::SampleRate = cpal::SampleRate(44_100);
let mut supported: Vec<_> = device
.supported_output_configs()
.map_err(StreamError::SupportedStreamConfigsError)?
.collect();
supported.sort_by(|a, b| b.cmp_default_heuristics(a));
Ok(supported.into_iter().flat_map(|sf| {
let max_rate = sf.max_sample_rate();
let min_rate = sf.min_sample_rate();
let mut formats = vec![sf.with_max_sample_rate()];
if HZ_44100 < max_rate && HZ_44100 > min_rate {
formats.push(sf.with_sample_rate(HZ_44100))
}
formats.push(sf.with_sample_rate(min_rate));
formats
}))
}