From bb9e2fb9ed49e68e55ae9e14caea7ea21831d30b Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 1 May 2017 13:15:20 +0200 Subject: [PATCH] Add source::from_factory --- src/queue.rs | 2 + src/source/from_factory.rs | 147 +++++++++++++++++++++++++++++++++++++ src/source/mod.rs | 2 + 3 files changed, 151 insertions(+) create mode 100644 src/source/from_factory.rs diff --git a/src/queue.rs b/src/queue.rs index b069908..2a322cb 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -44,6 +44,8 @@ pub fn queue(keep_alive_if_empty: bool) (input, output) } +// TODO: consider reimplementing this with `from_factory` + /// The input of the queue. pub struct SourcesQueueInput { next_sounds: Mutex + Send>, Option>)>>, diff --git a/src/source/from_factory.rs b/src/source/from_factory.rs new file mode 100644 index 0000000..b19a67c --- /dev/null +++ b/src/source/from_factory.rs @@ -0,0 +1,147 @@ +use std::time::Duration; + +use Sample; +use Source; + +/// 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(mut factory: F) -> FromFactory + where F: FnMut() -> Option +{ + let first_source = factory().expect("The factory returned an empty source"); // TODO: meh + + FromFactory { + factory: factory, + current_source: first_source, + } +} + +/// A source that chains sources built from a factory. +#[derive(Clone)] +pub struct FromFactory { + factory: F, + current_source: S, +} + +impl Iterator for FromFactory + where F: FnMut() -> Option, + S: Source, + S::Item: Sample +{ + type Item = ::Item; + + #[inline] + fn next(&mut self) -> Option<::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; + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.current_source.size_hint().0, None) + } +} + +impl Source for FromFactory + where F: FnMut() -> Option, + S: Iterator + Source, + S::Item: Sample +{ + #[inline] + fn current_frame_len(&self) -> Option { + // 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 { + 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); + } +} diff --git a/src/source/mod.rs b/src/source/mod.rs index 9d6dc31..4e78b1f 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -9,6 +9,7 @@ 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::mix::Mix; pub use self::pausable::Pausable; pub use self::repeat::Repeat; @@ -26,6 +27,7 @@ mod buffered; mod delay; mod empty; mod fadein; +mod from_factory; mod mix; mod pausable; mod repeat;