use std::time::Duration;
use crate::{Sample, Source};
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,
}
}
#[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;
#[derive(Clone, Debug)]
pub struct TakeDuration<I> {
input: I,
remaining_duration: Duration,
requested_duration: Duration,
filter: Option<DurationFilter>,
current_frame_len: Option<usize>,
duration_per_sample: Duration,
}
impl<I> TakeDuration<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
fn get_duration_per_sample(input: &I) -> Duration {
let ns = NANOS_PER_SEC / (input.sample_rate() as u64 * input.channels() as u64);
Duration::new(0, ns as u32)
}
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
#[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();
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
}
}
}
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
}
}
}