Add Source::from_iter

This commit is contained in:
Pierre Krieger 2017-05-01 21:18:00 +02:00
parent 138b746d12
commit a0ea1ce433
3 changed files with 183 additions and 123 deletions

View file

@ -1,7 +1,5 @@
use std::time::Duration;
use Sample;
use Source;
use source::from_iter;
use source::FromIter;
/// Builds a source that chains sources built from a factory.
///
@ -10,138 +8,31 @@ use Source;
/// played next.
///
/// 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>
{
let first_source = factory().expect("The factory returned an empty source"); // TODO: meh
FromFactory {
from_iter(FromFactoryIter {
factory: factory,
current_source: first_source,
}
})
}
/// A source that chains sources built from a factory.
#[derive(Clone)]
pub struct FromFactory<F, S> {
/// Internal type used by `from_factory`.
pub struct FromFactoryIter<F> {
factory: F,
current_source: S,
}
impl<F, S> Iterator for FromFactory<F, S>
where F: FnMut() -> Option<S>,
S: Source,
S::Item: Sample
impl<F, S> Iterator for FromFactoryIter<F>
where F: FnMut() -> Option<S>
{
type Item = <S as Iterator>::Item;
type Item = S;
#[inline]
fn next(&mut self) -> Option<<S as Iterator>::Item> {
loop {
if let Some(value) = self.current_source.next() {
return Some(value);
}
if let Some(src) = (self.factory)() {
self.current_source = src;
} else {
return None;
}
}
fn next(&mut self) -> Option<S> {
(self.factory)()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.current_source.size_hint().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);
(0, None)
}
}

167
src/source/from_iter.rs Normal file
View 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);
}
}

View file

@ -9,7 +9,8 @@ pub use self::buffered::Buffered;
pub use self::delay::Delay;
pub use self::empty::Empty;
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::pausable::Pausable;
pub use self::periodic::PeriodicAccess;
@ -28,6 +29,7 @@ mod delay;
mod empty;
mod fadein;
mod from_factory;
mod from_iter;
mod mix;
mod pausable;
mod periodic;