perf(rodio): reduce control polling during playback

This commit is contained in:
vicrodh committed 2026-01-13 00:27:03 -06:00
1 parent 3c1c598e34
commit 1e6a8b1aee
70 files changed
+9378

No files matched your search

+1
View File
@@ -0,0 +1 @@
{"v":1}
+6
View File
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "27f2b424060ff4a977285ff83ecd6c076205c121"
},
"path_in_vcs": ""
}
+74
View File
@@ -0,0 +1,74 @@
name: CI
on:
pull_request:
push:
branches: [main, master]
env:
RUSTFLAGS: "-C debuginfo=0 -D warnings"
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
jobs:
tests:
name: Tests
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.toolchain == 'nightly' }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
toolchain: [stable, beta, nightly]
include:
- os: macos-latest
MACOS: true
- os: windows-latest
- os: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: install linux deps
run: |
sudo apt update
sudo apt install -y --no-install-recommends libasound2-dev pkg-config
if: contains(matrix.os, 'ubuntu')
- name: install ${{ matrix.toolchain }} toolchain
id: install_toolchain
run: rustup toolchain install ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: cargo clippy -- -D warnings
if: matrix.toolchain == 'stable' && matrix.os == 'ubuntu-latest'
- run: |
rustup component add rustfmt
cargo fmt --all -- --check
if: matrix.toolchain == 'stable' && matrix.os == 'ubuntu-latest'
- run: cargo test --all-targets
- run: cargo test --features=symphonia-all --all-targets
cargo-publish:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
env:
CRATESIO_TOKEN: ${{ secrets.CRATESIO_TOKEN }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Update apt
run: sudo apt update
- name: Install alsa
run: sudo apt install -y --no-install-recommends libasound2-dev pkg-config
- name: Run cargo publish for rodio
continue-on-error: true
run: |
RODIO_TMP=$(mktemp /tmp/rodioXXX.txt) || echo "::error::mktemp error"
echo "RODIO_TMP=$RODIO_TMP" >> $GITHUB_ENV
cargo publish --token $CRATESIO_TOKEN 2> $RODIO_TMP
- name: Check if rodio is already published
run: |
empty=0
RODIO_TMP="${{ env.RODIO_TMP }}"
grep -q '[^[:space:]]' < $RODIO_TMP || empty=1
[ $empty -eq 0 ] && cat $RODIO_TMP
[ $empty -eq 1 ] || grep -q "is already uploaded" < $RODIO_TMP
+2
View File
@@ -0,0 +1,2 @@
target
Cargo.lock
+2
View File
@@ -0,0 +1,2 @@
reorder_imports = true
use_try_shorthand = true
+137
View File
@@ -0,0 +1,137 @@
# 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.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()`
+109
View File
@@ -0,0 +1,109 @@
# 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.19.0"
exclude = [
"assets/**",
"tests/**",
]
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"
[[example]]
name = "music_m4a"
required-features = [
"symphonia-isomp4",
"symphonia-aac",
]
[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.symphonia]
version = "0.5.4"
optional = true
default-features = false
[dependencies.thiserror]
version = "1.0.49"
[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",
]
flac = ["claxon"]
minimp3 = ["dep:minimp3_fixed"]
mp3 = ["symphonia-mp3"]
symphonia-aac = ["symphonia/aac"]
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",
]
vorbis = ["lewton"]
wasm-bindgen = ["cpal/wasm-bindgen"]
wav = ["hound"]
+47
View File
@@ -0,0 +1,47 @@
[package]
name = "rodio"
version = "0.19.0"
license = "MIT OR Apache-2.0"
description = "Audio playback library"
keywords = ["audio", "playback", "gamedev"]
repository = "https://github.com/RustAudio/rodio"
documentation = "https://docs.rs/rodio"
exclude = ["assets/**", "tests/**"]
edition = "2021"
[dependencies]
cpal = "0.15.3"
claxon = { version = "0.4.2", optional = true }
hound = { version = "3.3.1", optional = true }
lewton = { version = "0.10", optional = true }
minimp3_fixed = { version = "0.5.4", optional = true}
symphonia = { version = "0.5.4", optional = true, default-features = false }
crossbeam-channel = { version = "0.5.8", optional = true }
thiserror = "1.0.49"
[features]
default = ["flac", "vorbis", "wav", "mp3"]
flac = ["claxon"]
vorbis = ["lewton"]
wav = ["hound"]
mp3 = ["symphonia-mp3"]
minimp3 = ["dep:minimp3_fixed"]
wasm-bindgen = ["cpal/wasm-bindgen"]
cpal-shared-stdcxx = ["cpal/oboe-shared-stdcxx"]
symphonia-aac = ["symphonia/aac"]
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"]
[dev-dependencies]
quickcheck = "0.9.2"
rstest = "0.18.2"
rstest_reuse = "0.6.0"
[[example]]
name = "music_m4a"
required-features = ["symphonia-isomp4", "symphonia-aac"]
+201
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
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.
+35
View File
@@ -0,0 +1,35 @@
# 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.
## 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.
+36
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));
}
+38
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
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
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
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
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
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();
}
+15
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
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");
}
+53
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
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();
}
+200
View File
@@ -0,0 +1,200 @@
//! 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.
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
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
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;
+159
View File
@@ -0,0 +1,159 @@
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;
/// 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 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 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 saturating_add(self, other: f32) -> f32 {
self + other
}
#[inline]
fn zero_value() -> f32 {
0.0
}
}
+377
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
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
}
+575
View File
@@ -0,0 +1,575 @@
//! 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")]
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;
pub struct LoopedDecoder<R>(DecoderImpl<R>)
where
R: Read + Seek;
// can not 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)
}
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))),
}
}
}
#[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!("{} is not a valid mp4 extension", input)),
}
}
}
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
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
}
+37
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)
}
}
+291
View File
@@ -0,0 +1,291 @@
use std::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(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum SeekError {
#[error("Could not get next packet while refining seek position: {0:?}")]
Refining(symphonia::core::errors::Error),
#[error("Format reader failed to seek: {0:?}")]
BaseSeek(symphonia::core::errors::Error),
#[error("Decoding failed retrying on the next packet failed: {0:?}")]
Retrying(symphonia::core::errors::Error),
#[error("Decoding failed on multiple consecutive packets: {0:?}")]
Decoding(symphonia::core::errors::Error),
}
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
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
}
+222
View File
@@ -0,0 +1,222 @@
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
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);
}
}
+137
View File
@@ -0,0 +1,137 @@
//! 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
//! 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.
//!
//! ## 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
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));
}
}
+428
View File
@@ -0,0 +1,428 @@
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(20), 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;
}
/// Gets 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 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.
#[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) => 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());
}
}
}
+103
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)
}
}
+251
View File
@@ -0,0 +1,251 @@
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,
}
}
#[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
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,
}
}
}
+148
View File
@@ -0,0 +1,148 @@
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,
{
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
/// was 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> {
// return value
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)
}
}
+69
View File
@@ -0,0 +1,69 @@
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)
}
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() {
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);
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
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)
}
}
+97
View File
@@ -0,0 +1,97 @@
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 an `AtomicUsize`.
#[derive(Debug, Clone)]
pub struct Done<I> {
input: I,
signal: Arc<AtomicUsize>,
signal_sent: bool,
}
impl<I> Done<I> {
#[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)
}
}
+65
View File
@@ -0,0 +1,65 @@
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> {
#[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>(),
})
}
}
+64
View File
@@ -0,0 +1,64 @@
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> {
pub phantom_data: PhantomData<S>,
pub callback: Box<dyn Send + Fn()>,
}
impl<S> EmptyCallback<S> {
#[inline]
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>(),
})
}
}
+115
View File
@@ -0,0 +1,115 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Internal function that builds a `FadeIn` object.
pub fn fadein<I>(input: I, duration: Duration) -> FadeIn<I>
where
I: Source,
I::Item: Sample,
{
let duration = duration.as_secs() * 1000000000 + duration.subsec_nanos() as u64;
FadeIn {
input,
remaining_ns: duration as f32,
total_ns: duration as f32,
}
}
/// Filter that modifies raises the volume from silence over a time period.
#[derive(Clone, Debug)]
pub struct FadeIn<I> {
input: I,
remaining_ns: f32,
total_ns: f32,
}
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
}
/// 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 FadeIn<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
if self.remaining_ns <= 0.0 {
return self.input.next();
}
let factor = 1.0 - self.remaining_ns / self.total_ns;
self.remaining_ns -=
1000000000.0 / (self.input.sample_rate() as f32 * self.channels() 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 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.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)
}
}
+37
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
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);
}
}
+144
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
}
}
+491
View File
@@ -0,0 +1,491 @@
//! Sources of sound and various filters.
use std::time::Duration;
use cpal::FromSample;
use crate::Sample;
pub use self::amplify::Amplify;
pub use self::blt::BltFilter;
pub use self::buffered::Buffered;
pub use self::channel_volume::ChannelVolume;
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::from_factory::{from_factory, FromFactoryIter};
pub use self::from_iter::{from_iter, FromIter};
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::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 amplify;
mod blt;
mod buffered;
mod channel_volume;
mod crossfade;
mod delay;
mod done;
mod empty;
mod empty_callback;
mod fadein;
mod from_factory;
mod from_iter;
mod mix;
mod pausable;
mod periodic;
mod position;
mod repeat;
mod samples_converter;
mod sine;
mod skip;
mod skippable;
mod spatial;
mod speed;
mod stoppable;
mod take;
mod uniform;
mod zero;
/// 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.
///
/// # 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)
}
/// 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)
}
/// 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 play speed.
#[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)
}
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 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
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum SeekError {
#[error("Streaming is not supported by source: {underlying_source}")]
NotSupported { underlying_source: &'static str },
#[cfg(feature = "symphonia")]
#[error("Error seeking: {0}")]
SymphoniaDecoder(#[from] crate::decoder::symphonia::SeekError),
#[cfg(feature = "wav")]
#[error("Error seeking in wav source: {0}")]
HoundDecoder(std::io::Error),
#[error("An error occurred")]
Other(Box<dyn std::error::Error + Send>),
}
impl SeekError {
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>,);
+125
View File
@@ -0,0 +1,125 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Internal function that 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,
}
}
#[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
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.
}
}
+198
View File
@@ -0,0 +1,198 @@
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,
}
}
#[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
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(),
}
}
}
+105
View File
@@ -0,0 +1,105 @@
use std::marker::PhantomData;
use std::time::Duration;
use crate::{Sample, Source};
use cpal::{FromSample, Sample as CpalSample};
use super::SeekError;
/// An iterator that reads from a `Source` and converts the samples to a specific 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 SamplesConverter<I, D> {
inner: I,
dest: PhantomData<D>,
}
impl<I, D> SamplesConverter<I, 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)
}
}
+69
View File
@@ -0,0 +1,69 @@
use std::f32::consts::PI;
use std::time::Duration;
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 {
freq: f32,
num_sample: usize,
}
impl SineWave {
/// The frequency of the sine.
#[inline]
pub fn new(freq: f32) -> SineWave {
SineWave {
freq,
num_sample: 0,
}
}
}
impl Iterator for SineWave {
type Item = f32;
#[inline]
fn next(&mut self) -> Option<f32> {
self.num_sample = self.num_sample.wrapping_add(1);
let value = 2.0 * PI * self.freq * self.num_sample as f32 / 48000.0;
Some(value.sin())
}
}
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 {
48000
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
#[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
// This is a constant sound, normal seeking would not have any effect.
// While changing the phase of the sine wave could change how it sounds in
// combination with another sound (beating) such precision is not the intend
// of seeking
Ok(())
}
}
+259
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;
}
}
}
+99
View File
@@ -0,0 +1,99 @@
use std::time::Duration;
use crate::Sample;
use crate::Source;
use super::SeekError;
/// Internal function that builds a `Skippable` object.
pub fn skippable<I>(source: I) -> Skippable<I> {
Skippable {
input: source,
do_skip: false,
}
}
#[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)
}
}
+127
View File
@@ -0,0 +1,127 @@
use std::time::Duration;
use crate::source::ChannelVolume;
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)]
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,
{
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)
}
}
+109
View File
@@ -0,0 +1,109 @@
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.mul_f32(self.factor))
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
/* TODO: This might be wrong, I do not know how speed achieves its speedup
* so I can not reason about the correctness.
* <dvdsk noreply@davidsk.dev> */
// even after 24 hours of playback f32 has enough precision
let pos_accounting_for_speedup = pos.mul_f32(self.factor);
self.input.try_seek(pos_accounting_for_speedup)
}
}
+98
View File
@@ -0,0 +1,98 @@
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// Internal function that builds a `Stoppable` object.
pub fn stoppable<I>(source: I) -> Stoppable<I> {
Stoppable {
input: source,
stopped: false,
}
}
#[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)
}
}
+186
View File
@@ -0,0 +1,186 @@
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
}
pub fn set_filter_fadeout(&mut self) {
self.filter = Some(DurationFilter::FadeOut);
}
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)
}
}
+210
View File
@@ -0,0 +1,210 @@
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 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,
{
#[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 {}
+87
View File
@@ -0,0 +1,87 @@
use std::marker::PhantomData;
use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
/// An infinite source that produces zero.
#[derive(Clone, Debug)]
pub struct Zero<S> {
channels: u16,
sample_rate: u32,
num_samples: Option<usize>,
marker: PhantomData<S>,
}
impl<S> Zero<S> {
#[inline]
pub fn new(channels: u16, sample_rate: u32) -> Zero<S> {
Zero {
channels,
sample_rate,
num_samples: None,
marker: PhantomData,
}
}
#[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(())
}
}
+210
View File
@@ -0,0 +1,210 @@
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};
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);
}
/// Gets 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 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
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);
}
}
+358
View File
@@ -0,0 +1,358 @@
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()?;
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()?;
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,
}
}
}
#[derive(Debug)]
pub enum StreamError {
PlayStreamError(cpal::PlayStreamError),
DefaultStreamConfigError(cpal::DefaultStreamConfigError),
BuildStreamError(cpal::BuildStreamError),
SupportedStreamConfigsError(cpal::SupportedStreamConfigsError),
NoDevice,
}
impl From<cpal::DefaultStreamConfigError> for StreamError {
fn from(err: cpal::DefaultStreamConfigError) -> Self {
Self::DefaultStreamConfigError(err)
}
}
impl From<cpal::SupportedStreamConfigsError> for StreamError {
fn from(err: cpal::SupportedStreamConfigsError) -> Self {
Self::SupportedStreamConfigsError(err)
}
}
impl From<cpal::BuildStreamError> for StreamError {
fn from(err: cpal::BuildStreamError) -> Self {
Self::BuildStreamError(err)
}
}
impl From<cpal::PlayStreamError> for StreamError {
fn from(err: cpal::PlayStreamError) -> Self {
Self::PlayStreamError(err)
}
}
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| 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()?.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
}))
}