use {Duplex, Frame, Sample};
#[derive(Clone)]
pub struct Converter<I>
where I: Iterator,
I::Item: Frame,
{
source_frames: I,
source_to_target_ratio: f64,
source_window_left: Option<I::Item>,
source_window_right: Option<I::Item>,
source_interpolation: f64,
}
impl<I> Converter<I>
where I: Iterator,
I::Item: Frame,
{
#[inline]
pub fn from_hz_to_hz(source_frames: I, source_hz: f64, target_hz: f64) -> Self {
Self::scale_playback_hz(source_frames, source_hz / target_hz)
}
#[inline]
pub fn scale_playback_hz(source_frames: I, scale: f64) -> Self {
assert!(scale > 0.0, "We can't yield any frames at 0 times a second!");
Converter {
source_frames: source_frames,
source_to_target_ratio: scale,
source_interpolation: 0.0,
source_window_left: None,
source_window_right: None,
}
}
#[inline]
pub fn scale_sample_hz(source_frames: I, scale: f64) -> Self {
Self::scale_playback_hz(source_frames, 1.0 / scale)
}
#[inline]
pub fn set_hz_to_hz(&mut self, source_hz: f64, target_hz: f64) {
self.set_playback_hz_scale(source_hz / target_hz)
}
#[inline]
pub fn set_playback_hz_scale(&mut self, scale: f64) {
self.source_to_target_ratio = scale;
}
#[inline]
pub fn set_sample_hz_scale(&mut self, scale: f64) {
self.set_playback_hz_scale(1.0 / scale);
}
#[inline]
pub fn source(&self) -> &I {
&self.source_frames
}
#[inline]
pub fn source_mut(&mut self) -> &I {
&mut self.source_frames
}
#[inline]
pub fn into_source(self) -> I {
self.source_frames
}
#[inline]
pub fn next_frame(&mut self) -> Option<I::Item>
where <I::Item as Frame>::Sample: Duplex<f64>,
{
let Converter {
ref mut source_frames,
source_to_target_ratio,
ref mut source_interpolation,
ref mut source_window_left,
ref mut source_window_right,
} = *self;
let mut left_frame = match *source_window_left {
Some(frame) => frame,
None => match source_frames.next() {
Some(frame) => {
*source_window_left = Some(frame);
*source_interpolation += source_to_target_ratio;
return *source_window_left;
},
None => return None,
},
};
let mut right_frame = match *source_window_right {
Some(frame) => frame,
None => match source_frames.next() {
Some(frame) => frame,
None => return None,
},
};
while *source_interpolation > 1.0 {
left_frame = right_frame;
right_frame = match source_frames.next() {
Some(frame) => frame,
None => return None,
};
*source_interpolation -= 1.0;
}
let target_frame = left_frame.zip_map(right_frame, |current, next| {
let current_f = current.to_sample::<f64>();
let next_f = next.to_sample::<f64>();
let diff = next_f - current_f;
let amp = current_f + diff * *source_interpolation;
amp.to_sample::<<I::Item as Frame>::Sample>()
});
*source_window_left = Some(left_frame);
*source_window_right = Some(right_frame);
*source_interpolation += source_to_target_ratio;
Some(target_frame)
}
}
impl<I> Iterator for Converter<I>
where I: Iterator,
I::Item: Frame,
<I::Item as Frame>::Sample: Duplex<f64>,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.next_frame()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len_multiplier = self.source_to_target_ratio / 1.0;
let (source_lower, source_upper) = self.source_frames.size_hint();
let lower = (source_lower as f64 * len_multiplier) as usize;
let upper = source_upper.map(|upper| (upper as f64 * len_multiplier) as usize);
(lower, upper)
}
}