Skip to content

Commit a0b8f73

Browse files
committed
add seeking and deduplicate
1 parent 2969650 commit a0b8f73

10 files changed

Lines changed: 87 additions & 131 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
### Added
1313

14+
- Added `FixedSource` which mirrors `Source` but does not allow the sample rate or
15+
channel count to change.
16+
- Added `ConstSource` which is like `FixedSource` except the sample rate and
17+
channel count are fixed at compile time.
1418
- Added `Skippable::skipped` function to check if the inner source was skipped.
1519
- All sources now implement `ExactSizeIterator` when their inner source does.
1620
- All sources now implement `Iterator::size_hint()`.
@@ -21,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2125

2226
### Changed
2327

28+
- Breaking: `Microphone` now implements `FixedSource`
2429
- Breaking: `Done` now calls a callback instead of decrementing an `Arc<AtomicUsize>`.
2530
- Updated `cpal` to v0.18.
2631
- Clarified `Source::current_span_len()` documentation to specify it returns total span length.

‎examples/microphone.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use inquire::Select;
22
use rodio::microphone::{self, MicrophoneBuilder};
3-
use rodio::Source;
3+
use rodio::FixedSource;
44
use std::error::Error;
55
use std::thread;
66
use std::time::Duration;

‎src/const_source.rs‎

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,33 +58,33 @@ pub trait ConstSource<const SR: u32, const CH: u16>: Iterator<Item = Sample> {
5858
IntoDynamicSource { inner: self }
5959
}
6060

61-
/// Use this const source as if it's a fixed source which is generally
62-
/// easier to work with since it drops the generics. The same effects are
63-
/// available for both.
64-
///
65-
/// # Example
66-
///
67-
/// ```rust
68-
/// # struct CustomEffect<S: FixedSource>(S);
69-
/// # use rodio::{FixedSource, ConstSource};
70-
/// # use rodio::generators::const_source;
71-
///
72-
/// // Note custom effect can only wrap a FixedSource
73-
/// fn apply_custom_effect<S: FixedSource>(source: S) -> CustomEffect<S> {
74-
/// CustomEffect(source)
75-
/// }
76-
///
77-
/// let source = const_source::Silence::<44100>::new();
78-
/// let source = source.into_fixed_source();
79-
/// apply_custom_effect(source);
80-
/// ```
81-
fn into_fixed_source(self) -> IntoFixedSource<SR, CH, Self>
82-
where
83-
Self: Sized,
84-
{
85-
IntoFixedSource { inner: self }
86-
}
87-
61+
/// Use this const source as if it's a fixed source which is generally
62+
/// easier to work with since it drops the generics. The same effects are
63+
/// available for both.
64+
///
65+
/// # Example
66+
///
67+
/// ```rust
68+
/// # struct CustomEffect<S: FixedSource>(S);
69+
/// # use rodio::{FixedSource, ConstSource};
70+
/// # use rodio::generators::const_source;
71+
///
72+
/// // Note custom effect can only wrap a FixedSource
73+
/// fn apply_custom_effect<S: FixedSource>(source: S) -> CustomEffect<S> {
74+
/// CustomEffect(source)
75+
/// }
76+
///
77+
/// let source = const_source::Silence::<44100>::new();
78+
/// let source = source.into_fixed_source();
79+
/// apply_custom_effect(source);
80+
/// ```
81+
fn into_fixed_source(self) -> IntoFixedSource<SR, CH, Self>
82+
where
83+
Self: Sized,
84+
{
85+
IntoFixedSource { inner: self }
86+
}
87+
8888
#[doc = include_str!("docs/collect_into_buffer.md")]
8989
fn collect_into_buffer(self) -> SamplesBuffer<SR, CH>
9090
where

‎src/fixed_source.rs‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! channel count.
33
use std::time::Duration;
44

5+
use crate::source::SeekError;
56
use crate::{ChannelCount, ConstSource, Sample, SampleRate};
67

78
mod buffer;
@@ -23,6 +24,14 @@ pub trait FixedSource: Iterator<Item = Sample> {
2324
/// `None` indicates at the same time "infinite" or "unknown".
2425
fn total_duration(&self) -> Option<Duration>;
2526

27+
#[allow(unused_variables)]
28+
#[doc = include_str!("docs/try_seek.md")]
29+
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
30+
Err(SeekError::NotSupported {
31+
underlying_source: std::any::type_name::<Self>(),
32+
})
33+
}
34+
2635
/// Tries to convert from a fixed source to a const one assuming
2736
/// the parameters already match. If they do not this returns an error.
2837
///
@@ -56,6 +65,18 @@ pub trait FixedSource: Iterator<Item = Sample> {
5665
IntoDynamicSource(self)
5766
}
5867

68+
#[doc = include_str!("docs/collect_into_buffer.md")]
69+
fn collect_into_buffer(self) -> SamplesBuffer
70+
where
71+
Self: Sized,
72+
{
73+
SamplesBuffer::new(
74+
self.channels(),
75+
self.sample_rate(),
76+
self.collect::<Vec<_>>(),
77+
)
78+
}
79+
5980
/// Add another source to play directly after this one.
6081
///
6182
/// # Example
@@ -106,6 +127,14 @@ where
106127
inner: std::marker::PhantomData<S>,
107128
}
108129

130+
impl<S> Placeholder<S>
131+
where
132+
S: FixedSource,
133+
{
134+
/// placeholder
135+
pub fn record(&self) {}
136+
}
137+
109138
/// A [`ConstSource`] adapted from a [`FixedSource`].
110139
pub struct IntoConstSource<const SR: u32, const CH: u16, S: FixedSource>(S);
111140

‎src/fixed_source/buffer.rs‎

Lines changed: 4 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::sync::Arc;
22
use std::time::Duration;
33

4+
use crate::source::SeekError;
45
use crate::FixedSource;
56
use crate::{ChannelCount, Sample, SampleRate};
67

@@ -19,7 +20,7 @@ impl SamplesBuffer {
1920
where
2021
D: Into<Vec<Sample>>,
2122
{
22-
let data: Arc<[f32]> = data.into().into();
23+
let data: Arc<[Sample]> = data.into().into();
2324
SamplesBuffer {
2425
data,
2526
pos: 0,
@@ -38,52 +39,9 @@ impl FixedSource for SamplesBuffer {
3839
fn sample_rate(&self) -> SampleRate {
3940
self.sample_rate
4041
}
41-
/// # Panics
42-
/// If the length of the buffer is larger than approximately 16 billion elements.
43-
/// This is because the calculation of the duration would overflow.
44-
#[inline]
45-
fn total_duration(&self) -> Option<Duration> {
46-
let duration_ns = 1_000_000_000u64
47-
.checked_mul(self.data.len() as u64)
48-
.unwrap()
49-
/ self.sample_rate.get() as u64
50-
/ self.channels.get() as u64;
51-
let duration = Duration::new(
52-
duration_ns / 1_000_000_000,
53-
(duration_ns % 1_000_000_000) as u32,
54-
);
55-
56-
Some(duration)
57-
}
58-
// /// This jumps in memory till the sample for `pos`.
59-
// #[inline]
60-
// fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
61-
// // This is fast because all the samples are in memory already
62-
// // and due to the constant sample_rate we can jump to the right
63-
// // sample directly.
64-
// let curr_channel = self.pos % self.channels() as usize;
65-
// let new_pos = pos.as_secs_f32() * self.sample_rate() as f32 * self.channels() as f32;
66-
// // saturate pos at the end of the source
67-
// let new_pos = new_pos as usize;
68-
// let new_pos = new_pos.min(self.data.len());
69-
// // make sure the next sample is for the right channel
70-
// let new_pos = new_pos.next_multiple_of(self.channels() as usize);
71-
// let new_pos = new_pos - curr_channel;
72-
// self.pos = new_pos;
73-
// Ok(())
74-
// }
42+
crate::common::source::buffer::source_impl! {}
7543
}
7644

7745
impl Iterator for SamplesBuffer {
78-
type Item = Sample;
79-
#[inline]
80-
fn next(&mut self) -> Option<Self::Item> {
81-
let sample = self.data.get(self.pos)?;
82-
self.pos += 1;
83-
Some(*sample)
84-
}
85-
#[inline]
86-
fn size_hint(&self) -> (usize, Option<usize>) {
87-
(self.data.len(), Some(self.data.len()))
88-
}
46+
crate::common::source::buffer::iter_impl! {}
8947
}

‎src/fixed_source/chain.rs‎

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use crate::{ChannelCount, SampleRate};
77
/// Two chained sources, the one played after the other.
88
#[derive(Clone)]
99
pub struct SourceChain<S1, S2> {
10-
inner: S1,
11-
next: S2,
10+
first: S1,
11+
second: S2,
1212
playing_inner: bool,
1313
}
1414

@@ -43,44 +43,19 @@ impl<S1: FixedSource, S2: FixedSource> SourceChain<S1, S2> {
4343
})
4444
} else {
4545
Ok(SourceChain {
46-
inner: s1,
47-
next: s2,
46+
first: s1,
47+
second: s2,
4848
playing_inner: true,
4949
})
5050
}
5151
}
5252
}
5353

54+
pub use crate::common::source::chain::ChainSeekError;
5455
impl<S1: FixedSource, S2: FixedSource> FixedSource for SourceChain<S1, S2> {
55-
fn channels(&self) -> ChannelCount {
56-
self.inner.channels()
57-
}
58-
59-
fn sample_rate(&self) -> SampleRate {
60-
self.inner.sample_rate()
61-
}
62-
63-
fn total_duration(&self) -> Option<std::time::Duration> {
64-
self.inner
65-
.total_duration()
66-
.and_then(|d| self.next.total_duration().map(|d2| d2 + d))
67-
}
56+
crate::common::source::chain::source_impl! {}
6857
}
6958

7059
impl<S1: FixedSource, S2: FixedSource> Iterator for SourceChain<S1, S2> {
71-
type Item = Sample;
72-
73-
fn next(&mut self) -> Option<Self::Item> {
74-
if self.playing_inner {
75-
match self.inner.next() {
76-
Some(sample) => Some(sample),
77-
None => {
78-
self.playing_inner = false;
79-
self.next.next()
80-
}
81-
}
82-
} else {
83-
self.next.next()
84-
}
85-
}
60+
crate::common::source::chain::iter_impl! {}
8661
}

‎src/generators/silence.rs‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
pub mod fixed_source {
2+
use std::time::Duration;
3+
4+
use crate::source::SeekError;
5+
use crate::{nz, FixedSource};
26
use crate::{ChannelCount, SampleRate};
3-
use crate::{FixedSource, nz};
47

58
/// A source producing an infinite amount of Silence. Like all generators you
69
/// probably want to limit the duration of this source.
@@ -46,6 +49,11 @@ pub mod fixed_source {
4649
fn total_duration(&self) -> Option<std::time::Duration> {
4750
None
4851
}
52+
53+
/// This does nothing since all silence is equal :3
54+
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
55+
Ok(())
56+
}
4957
}
5058

5159
impl Iterator for Silence {
@@ -102,7 +110,7 @@ pub mod const_source {
102110
}
103111

104112
impl<const SR: u32> ConstSource<SR, 1> for Silence<SR> {
105-
fn total_duration(&self) -> Option<std::time::Duration> {
113+
fn total_duration(&self) -> Option<Duration> {
106114
None
107115
}
108116

‎src/lib.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,10 @@ pub mod stream;
224224
mod wav_output;
225225

226226
pub mod buffer;
227-
pub mod const_source;
228227
pub mod conversions;
229228
pub mod decoder;
230229

230+
pub mod const_source;
231231
pub mod fixed_source;
232232

233233
pub mod generators;

‎src/microphone.rs‎

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//!
55
//! ```no_run
66
//! use rodio::microphone::MicrophoneBuilder;
7-
//! use rodio::Source;
7+
//! use rodio::FixedSource;
88
//! use std::time::Duration;
99
//!
1010
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -106,7 +106,7 @@ use std::time::Duration;
106106

107107
use crate::common::assert_error_traits;
108108
use crate::conversions::SampleTypeConverter;
109-
use crate::{Sample, Source};
109+
use crate::Sample;
110110

111111
mod builder;
112112
mod config;
@@ -188,25 +188,6 @@ pub struct Microphone {
188188
config: InputConfig,
189189
}
190190

191-
impl Source for Microphone {
192-
fn current_span_len(&self) -> Option<usize> {
193-
None
194-
}
195-
196-
fn channels(&self) -> crate::ChannelCount {
197-
self.config.channel_count
198-
}
199-
200-
fn sample_rate(&self) -> crate::SampleRate {
201-
self.config.sample_rate
202-
}
203-
204-
fn total_duration(&self) -> Option<std::time::Duration> {
205-
None
206-
}
207-
}
208-
209-
#[cfg(feature = "experimental")]
210191
impl crate::FixedSource for Microphone {
211192
fn channels(&self) -> crate::ChannelCount {
212193
self.config.channel_count

‎src/microphone/builder.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ where
538538
/// # Example
539539
/// ```no_run
540540
/// # use rodio::microphone::MicrophoneBuilder;
541-
/// # use rodio::Source;
541+
/// # use rodio::FixedSource;
542542
/// # use std::time::Duration;
543543
/// let mic = MicrophoneBuilder::new()
544544
/// .default_device()?

0 commit comments

Comments
 (0)