use super::{Convert, ConvertMode, Ratio, Rational, Result, convert_with, output_len};
enum State {
First,
Normal,
Suspend,
}
pub(crate) struct FloatConverter {
state: State,
last_in: [f64; 2],
step: f64,
pos: f64,
}
pub(crate) struct RationalConverter {
state: State,
last_in: [f64; 2],
numer: usize,
denom: usize,
pos: usize,
recip: f64,
}
pub(crate) struct RationalFastConverter {
state: State,
last_in: [f64; 2],
numer: usize,
denom: usize,
pos: usize,
coef: Vec<f64>,
}
enum ConverterKind {
Float(FloatConverter),
Rational(RationalConverter),
RationalFast(RationalFastConverter),
}
pub struct Converter {
inner: ConverterKind,
}
impl FloatConverter {
fn new(step: f64) -> Self {
Self {
step,
pos: 0.0,
last_in: [0.0; 2],
state: State::First,
}
}
}
impl Convert for FloatConverter {
fn next_sample<I>(&mut self, iter: &mut I) -> Option<f64>
where
I: Iterator<Item = f64>,
Self: Sized,
{
loop {
match self.state {
State::First => {
let s = iter.next()?;
self.last_in[1] = s;
self.pos = 1.0;
self.state = State::Normal;
}
State::Normal => {
while self.pos >= 1.0 {
self.pos -= 1.0;
self.last_in[0] = self.last_in[1];
if let Some(s) = iter.next() {
self.last_in[1] = s;
} else {
self.state = State::Suspend;
return None;
}
}
let coef = self.pos;
let interp = self.last_in[0] + (self.last_in[1] - self.last_in[0]) * coef;
self.pos += self.step;
return Some(interp);
}
State::Suspend => {
let s = iter.next()?;
self.last_in[1] = s;
self.state = State::Normal;
}
}
}
}
}
impl RationalConverter {
fn new(step: Rational) -> Self {
let numer = *step.numer() as usize;
let denom = *step.denom() as usize;
Self {
state: State::First,
last_in: [0.0; 2],
numer,
denom,
pos: 0,
recip: (denom as f64).recip(),
}
}
}
impl Convert for RationalConverter {
fn next_sample<I>(&mut self, iter: &mut I) -> Option<f64>
where
I: Iterator<Item = f64>,
{
loop {
match self.state {
State::First => {
let s = iter.next()?;
self.last_in[1] = s;
self.pos = self.numer;
self.state = State::Normal;
}
State::Normal => {
while self.pos >= self.denom {
self.pos -= self.denom;
self.last_in[0] = self.last_in[1];
if let Some(s) = iter.next() {
self.last_in[1] = s;
} else {
self.state = State::Suspend;
return None;
}
}
let coef = self.pos as f64 * self.recip;
let interp = self.last_in[0] + (self.last_in[1] - self.last_in[0]) * coef;
self.pos += self.numer;
return Some(interp);
}
State::Suspend => {
let s = iter.next()?;
self.last_in[1] = s;
self.state = State::Normal;
}
}
}
}
}
impl RationalFastConverter {
fn new(step: Rational) -> Self {
let numer = *step.numer() as usize;
let denom = *step.denom() as usize;
let coef = (0..denom).map(|i| i as f64 / denom as f64).collect();
Self {
numer,
denom,
pos: 0,
coef,
last_in: [0.0; 2],
state: State::First,
}
}
}
impl Convert for RationalFastConverter {
fn next_sample<I>(&mut self, iter: &mut I) -> Option<f64>
where
I: Iterator<Item = f64>,
{
loop {
match self.state {
State::First => {
let s = iter.next()?;
self.last_in[1] = s;
self.pos = self.numer;
self.state = State::Normal;
}
State::Normal => {
while self.pos >= self.denom {
self.pos -= self.denom;
self.last_in[0] = self.last_in[1];
if let Some(s) = iter.next() {
self.last_in[1] = s;
} else {
self.state = State::Suspend;
return None;
}
}
let coef = self.coef[self.pos];
let interp = self.last_in[0] + (self.last_in[1] - self.last_in[0]) * coef;
self.pos += self.numer;
return Some(interp);
}
State::Suspend => {
let s = iter.next()?;
self.last_in[1] = s;
self.state = State::Normal;
}
}
}
}
}
impl Converter {
fn delay_empty(&self) -> bool {
match &self.inner {
ConverterKind::Float(c) => {
matches!(c.state, State::First) || (c.last_in[0] == 0.0 && c.last_in[1] == 0.0)
}
ConverterKind::Rational(c) => {
matches!(c.state, State::First) || (c.last_in[0] == 0.0 && c.last_in[1] == 0.0)
}
ConverterKind::RationalFast(c) => {
matches!(c.state, State::First) || (c.last_in[0] == 0.0 && c.last_in[1] == 0.0)
}
}
}
fn new(ratio: Ratio) -> Self {
let inner = match ratio {
Ratio::Float(ratio) => ConverterKind::Float(FloatConverter::new(ratio.recip())),
Ratio::Rational(ratio) => {
if *ratio.numer() <= 16384 {
ConverterKind::RationalFast(RationalFastConverter::new(ratio.recip()))
} else {
ConverterKind::Rational(RationalConverter::new(ratio.recip()))
}
}
};
Self { inner }
}
}
impl Convert for Converter {
#[inline]
fn next_sample<I>(&mut self, iter: &mut I) -> Option<f64>
where
I: Iterator<Item = f64>,
Self: Sized,
{
match &mut self.inner {
ConverterKind::Float(converter) => converter.next_sample(iter),
ConverterKind::Rational(converter) => converter.next_sample(iter),
ConverterKind::RationalFast(converter) => converter.next_sample(iter),
}
}
fn flush(&mut self, output: &mut [f64]) -> usize
where
Self: Sized,
{
if self.delay_empty() {
return 0;
}
let mut zeros = std::iter::repeat(0.0);
let mut produced = 0;
while produced < output.len() {
match self.next_sample(&mut zeros) {
Some(sample) => {
output[produced] = sample;
produced += 1;
if self.delay_empty() {
break;
}
}
None => break,
}
}
produced
}
}
#[derive(Clone, Copy)]
pub struct Manager {
ratio: Ratio,
}
impl Manager {
#[inline]
pub fn new(ratio: f64) -> Result<Self> {
let ratio = Ratio::try_from_float(ratio)?;
Ok(Self { ratio })
}
#[inline]
pub fn with_sample_rate(old_sr: u32, new_sr: u32) -> Result<Self> {
let ratio = Ratio::try_from_integers(new_sr, old_sr)?;
Ok(Self { ratio })
}
#[inline]
pub fn converter(&self) -> Converter {
Converter::new(self.ratio)
}
#[inline]
pub fn ratio(&self) -> f64 {
self.ratio.as_float()
}
#[inline]
pub fn ratio_parts(&self) -> Option<(i64, i64)> {
self.ratio.parts()
}
#[inline]
pub fn mode(&self) -> ConvertMode {
self.ratio.linear_mode()
}
#[inline]
pub fn latency(&self) -> usize {
0
}
#[inline]
pub fn output_len(&self, input_len: usize) -> usize {
output_len(self.ratio(), input_len)
}
pub fn convert(&self, input: &[f64]) -> Vec<f64> {
convert_with(self.converter(), self.latency(), self.ratio(), input)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_manager_ok() {
let ratio_ok = vec![0.0625, 0.063, 1.0, 15.9, 16.0, 0.123456];
for ratio in ratio_ok {
assert!(Manager::new(ratio).is_ok());
}
}
#[test]
fn test_manager_err() {
let ratio_err = vec![
-1.0,
0.0,
0.0624,
16.01,
f64::INFINITY,
f64::NEG_INFINITY,
f64::NAN,
];
for ratio in ratio_err {
assert!(Manager::new(ratio).is_err());
}
}
#[test]
fn test_mode_and_sample_rate() {
let m = Manager::with_sample_rate(44100, 48000).unwrap();
assert_eq!(m.mode(), ConvertMode::RationalFast);
assert_eq!(m.ratio_parts(), Some((160, 147)));
let two = Manager::new(2.0).unwrap();
assert_eq!(two.mode(), ConvertMode::RationalFast);
let generic = Manager::with_sample_rate(19999, 20000).unwrap();
assert_eq!(generic.mode(), ConvertMode::Rational);
let pi = Manager::new(std::f64::consts::PI).unwrap();
assert_eq!(pi.mode(), ConvertMode::Float);
assert_eq!(pi.ratio_parts(), None);
}
}