mirror of
https://github.com/RustAudio/rodio
synced 2025-01-05 16:28:44 +00:00
Add Source::from_iter
This commit is contained in:
parent
138b746d12
commit
a0ea1ce433
3 changed files with 183 additions and 123 deletions
|
@ -1,7 +1,5 @@
|
||||||
use std::time::Duration;
|
use source::from_iter;
|
||||||
|
use source::FromIter;
|
||||||
use Sample;
|
|
||||||
use Source;
|
|
||||||
|
|
||||||
/// Builds a source that chains sources built from a factory.
|
/// Builds a source that chains sources built from a factory.
|
||||||
///
|
///
|
||||||
|
@ -10,138 +8,31 @@ use Source;
|
||||||
/// played next.
|
/// played next.
|
||||||
///
|
///
|
||||||
/// If the `factory` closure returns `None`, then the sound ends.
|
/// If the `factory` closure returns `None`, then the sound ends.
|
||||||
pub fn from_factory<F, S>(mut factory: F) -> FromFactory<F, S>
|
pub fn from_factory<F, S>(factory: F) -> FromIter<FromFactoryIter<F>>
|
||||||
where F: FnMut() -> Option<S>
|
where F: FnMut() -> Option<S>
|
||||||
{
|
{
|
||||||
let first_source = factory().expect("The factory returned an empty source"); // TODO: meh
|
from_iter(FromFactoryIter {
|
||||||
|
|
||||||
FromFactory {
|
|
||||||
factory: factory,
|
factory: factory,
|
||||||
current_source: first_source,
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A source that chains sources built from a factory.
|
/// Internal type used by `from_factory`.
|
||||||
#[derive(Clone)]
|
pub struct FromFactoryIter<F> {
|
||||||
pub struct FromFactory<F, S> {
|
|
||||||
factory: F,
|
factory: F,
|
||||||
current_source: S,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, S> Iterator for FromFactory<F, S>
|
impl<F, S> Iterator for FromFactoryIter<F>
|
||||||
where F: FnMut() -> Option<S>,
|
where F: FnMut() -> Option<S>
|
||||||
S: Source,
|
|
||||||
S::Item: Sample
|
|
||||||
{
|
{
|
||||||
type Item = <S as Iterator>::Item;
|
type Item = S;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<<S as Iterator>::Item> {
|
fn next(&mut self) -> Option<S> {
|
||||||
loop {
|
(self.factory)()
|
||||||
if let Some(value) = self.current_source.next() {
|
|
||||||
return Some(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(src) = (self.factory)() {
|
|
||||||
self.current_source = src;
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
(self.current_source.size_hint().0, None)
|
(0, None)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F, S> Source for FromFactory<F, S>
|
|
||||||
where F: FnMut() -> Option<S>,
|
|
||||||
S: Iterator + Source,
|
|
||||||
S::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(val) = self.current_source.current_frame_len() {
|
|
||||||
if val != 0 {
|
|
||||||
return Some(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try the size hint.
|
|
||||||
if let Some(val) = self.current_source.size_hint().1 {
|
|
||||||
if val < THRESHOLD && val != 0 {
|
|
||||||
return Some(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise we use the constant value.
|
|
||||||
Some(THRESHOLD)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn channels(&self) -> u16 {
|
|
||||||
self.current_source.channels()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn samples_rate(&self) -> u32 {
|
|
||||||
self.current_source.samples_rate()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use buffer::SamplesBuffer;
|
|
||||||
use source::from_factory;
|
|
||||||
use source::Source;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn basic() {
|
|
||||||
let mut n = 0;
|
|
||||||
let mut rx = from_factory(move || {
|
|
||||||
if n == 0 {
|
|
||||||
n = 1;
|
|
||||||
Some(SamplesBuffer::new(1, 48000, vec![10i16, -10, 10, -10]))
|
|
||||||
} else if n == 1 {
|
|
||||||
n = 2;
|
|
||||||
Some(SamplesBuffer::new(2, 96000, vec![5i16, 5, 5, 5]))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(rx.channels(), 1);
|
|
||||||
assert_eq!(rx.samples_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.samples_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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
167
src/source/from_iter.rs
Normal file
167
src/source/from_iter.rs
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use Sample;
|
||||||
|
use Source;
|
||||||
|
|
||||||
|
/// 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: 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(ref mut src) = 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(ref 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(ref 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(ref 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(ref src) = self.current_source {
|
||||||
|
src.channels()
|
||||||
|
} else {
|
||||||
|
// Dummy value that only happens if the iterator was empty.
|
||||||
|
2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn samples_rate(&self) -> u32 {
|
||||||
|
if let Some(ref src) = self.current_source {
|
||||||
|
src.samples_rate()
|
||||||
|
} else {
|
||||||
|
// Dummy value that only happens if the iterator was empty.
|
||||||
|
44100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use buffer::SamplesBuffer;
|
||||||
|
use source::from_iter;
|
||||||
|
use source::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.samples_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.samples_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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,8 @@ pub use self::buffered::Buffered;
|
||||||
pub use self::delay::Delay;
|
pub use self::delay::Delay;
|
||||||
pub use self::empty::Empty;
|
pub use self::empty::Empty;
|
||||||
pub use self::fadein::FadeIn;
|
pub use self::fadein::FadeIn;
|
||||||
pub use self::from_factory::{from_factory, FromFactory};
|
pub use self::from_factory::{from_factory, FromFactoryIter};
|
||||||
|
pub use self::from_iter::{from_iter, FromIter};
|
||||||
pub use self::mix::Mix;
|
pub use self::mix::Mix;
|
||||||
pub use self::pausable::Pausable;
|
pub use self::pausable::Pausable;
|
||||||
pub use self::periodic::PeriodicAccess;
|
pub use self::periodic::PeriodicAccess;
|
||||||
|
@ -28,6 +29,7 @@ mod delay;
|
||||||
mod empty;
|
mod empty;
|
||||||
mod fadein;
|
mod fadein;
|
||||||
mod from_factory;
|
mod from_factory;
|
||||||
|
mod from_iter;
|
||||||
mod mix;
|
mod mix;
|
||||||
mod pausable;
|
mod pausable;
|
||||||
mod periodic;
|
mod periodic;
|
||||||
|
|
Loading…
Reference in a new issue