use alloc::{vec, vec::Vec};
use crate::{Radix, SampleRate, SampleRateFamily};
#[derive(Debug, Clone)]
pub(crate) struct ConversionConfig {
pub(crate) base_fft_size_in: usize,
pub(crate) base_fft_size_out: usize,
pub(crate) base_factors_in: Vec<Radix>,
pub(crate) base_factors_out: Vec<Radix>,
}
impl ConversionConfig {
pub(crate) fn from_sample_rates(
input_rate: SampleRate,
output_rate: SampleRate,
) -> ConversionConfig {
let input_family = input_rate.family();
let output_family = output_rate.family();
let input_multiplier = input_rate.family_multiplier() as usize;
let output_multiplier = output_rate.family_multiplier() as usize;
let base_config = match (input_family, output_family) {
(SampleRateFamily::Hz48000, SampleRateFamily::Hz48000)
| (SampleRateFamily::Hz22050, SampleRateFamily::Hz22050)
| (SampleRateFamily::Hz16000, SampleRateFamily::Hz16000) => ConversionConfig {
base_fft_size_in: 2,
base_fft_size_out: 2,
base_factors_in: vec![Radix::Factor2],
base_factors_out: vec![Radix::Factor2],
},
(SampleRateFamily::Hz22050, SampleRateFamily::Hz48000) => ConversionConfig {
base_fft_size_in: 588,
base_fft_size_out: 1280,
base_factors_in: vec![
Radix::Factor3,
Radix::Factor4,
Radix::Factor7,
Radix::Factor7,
],
base_factors_out: vec![
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor5,
],
},
(SampleRateFamily::Hz48000, SampleRateFamily::Hz22050) => ConversionConfig {
base_fft_size_in: 1280,
base_fft_size_out: 588,
base_factors_in: vec![
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor5,
],
base_factors_out: vec![
Radix::Factor3,
Radix::Factor4,
Radix::Factor7,
Radix::Factor7,
],
},
(SampleRateFamily::Hz16000, SampleRateFamily::Hz48000) => ConversionConfig {
base_fft_size_in: 64,
base_fft_size_out: 192,
base_factors_in: vec![Radix::Factor2; 6],
base_factors_out: vec![
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor3,
],
},
(SampleRateFamily::Hz48000, SampleRateFamily::Hz16000) => ConversionConfig {
base_fft_size_in: 192,
base_fft_size_out: 64,
base_factors_in: vec![
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor3,
],
base_factors_out: vec![Radix::Factor2; 6],
},
(SampleRateFamily::Hz16000, SampleRateFamily::Hz22050) => ConversionConfig {
base_fft_size_in: 640,
base_fft_size_out: 882,
base_factors_in: vec![
Radix::Factor2,
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor5,
],
base_factors_out: vec![
Radix::Factor2,
Radix::Factor3,
Radix::Factor3,
Radix::Factor7,
Radix::Factor7,
],
},
(SampleRateFamily::Hz22050, SampleRateFamily::Hz16000) => ConversionConfig {
base_fft_size_in: 882,
base_fft_size_out: 640,
base_factors_in: vec![
Radix::Factor2,
Radix::Factor3,
Radix::Factor3,
Radix::Factor7,
Radix::Factor7,
],
base_factors_out: vec![
Radix::Factor2,
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor5,
],
},
};
let scaled_fft_size_in = base_config.base_fft_size_in * input_multiplier;
let scaled_fft_size_out = base_config.base_fft_size_out * output_multiplier;
let input_multiplier_factors = Self::decompose_multiplier(input_multiplier);
let output_multiplier_factors = Self::decompose_multiplier(output_multiplier);
let mut scaled_factors_in = base_config.base_factors_in.clone();
scaled_factors_in.extend(input_multiplier_factors);
let mut scaled_factors_out = base_config.base_factors_out.clone();
scaled_factors_out.extend(output_multiplier_factors);
ConversionConfig {
base_fft_size_in: scaled_fft_size_in,
base_fft_size_out: scaled_fft_size_out,
base_factors_in: scaled_factors_in,
base_factors_out: scaled_factors_out,
}
}
fn decompose_multiplier(multiplier: usize) -> Vec<Radix> {
if multiplier == 1 {
return Vec::new();
}
assert!(multiplier.is_power_of_two());
let num_bits = multiplier.trailing_zeros() as usize;
let num_factor8 = num_bits / 3;
let remainder = num_bits % 3;
let mut factors = vec![Radix::Factor8; num_factor8];
match remainder {
0 => {} 1 => factors.push(Radix::Factor2),
2 => factors.push(Radix::Factor4),
_ => unreachable!(),
}
factors
}
pub(crate) fn scale_for_throughput(&self) -> (usize, Vec<Radix>, usize, Vec<Radix>) {
const TARGET_INPUT_SAMPLES: usize = 512;
#[cfg(not(feature = "no_std"))]
let multiplier = (TARGET_INPUT_SAMPLES as f32 / self.base_fft_size_in as f32)
.ceil()
.max(1.0) as usize;
#[cfg(feature = "no_std")]
let multiplier = libm::ceilf(TARGET_INPUT_SAMPLES as f32 / self.base_fft_size_in as f32)
.max(1.0) as usize;
let multiplier = multiplier.next_power_of_two();
let scaled_fft_size_in = self.base_fft_size_in * multiplier;
let scaled_fft_size_out = self.base_fft_size_out * multiplier;
let scaling_factors_in = Self::decompose_multiplier(multiplier);
let scaling_factors_out = Self::decompose_multiplier(multiplier);
let mut factors_in = self.base_factors_in.clone();
factors_in.extend(scaling_factors_in);
let mut factors_out = self.base_factors_out.clone();
factors_out.extend(scaling_factors_out);
(
scaled_fft_size_in,
factors_in,
scaled_fft_size_out,
factors_out,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_conversion_config_48000_to_96000() {
let config = ConversionConfig::from_sample_rates(SampleRate::Hz48000, SampleRate::Hz96000);
assert_eq!(config.base_fft_size_in, 2);
assert_eq!(config.base_fft_size_out, 4);
}
#[test]
fn test_conversion_config_48000_to_192000() {
let config = ConversionConfig::from_sample_rates(SampleRate::Hz48000, SampleRate::Hz192000);
assert_eq!(config.base_fft_size_in, 2);
assert_eq!(config.base_fft_size_out, 8);
}
#[test]
fn test_conversion_config_22050_to_48000() {
let config = ConversionConfig::from_sample_rates(SampleRate::Hz22050, SampleRate::Hz48000);
assert_eq!(config.base_fft_size_in, 588);
assert_eq!(config.base_fft_size_out, 1280);
}
#[test]
fn test_conversion_config_16000_to_48000() {
let config = ConversionConfig::from_sample_rates(SampleRate::Hz16000, SampleRate::Hz48000);
assert_eq!(config.base_fft_size_in, 64);
assert_eq!(config.base_fft_size_out, 192);
}
#[test]
fn test_conversion_config_16000_to_44100() {
let config = ConversionConfig::from_sample_rates(SampleRate::Hz16000, SampleRate::Hz44100);
assert_eq!(config.base_fft_size_in, 640);
assert_eq!(config.base_fft_size_out, 1764);
}
#[test]
fn test_conversion_config_44100_to_48000() {
let config = ConversionConfig::from_sample_rates(SampleRate::Hz44100, SampleRate::Hz48000);
assert_eq!(config.base_fft_size_in, 1176);
assert_eq!(config.base_fft_size_out, 1280);
assert_eq!(
config.base_factors_in,
vec![
Radix::Factor3,
Radix::Factor4,
Radix::Factor7,
Radix::Factor7,
Radix::Factor2
]
);
assert_eq!(
config.base_factors_out,
vec![
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor5
]
);
}
#[test]
fn test_conversion_config_44100_to_96000() {
let config = ConversionConfig::from_sample_rates(SampleRate::Hz44100, SampleRate::Hz96000);
assert_eq!(config.base_fft_size_in, 1176);
assert_eq!(config.base_fft_size_out, 2560);
assert_eq!(
config.base_factors_in,
vec![
Radix::Factor3,
Radix::Factor4,
Radix::Factor7,
Radix::Factor7,
Radix::Factor2
]
);
assert_eq!(
config.base_factors_out,
vec![
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor5,
Radix::Factor2
]
);
}
#[test]
fn test_prefer_factor4_for_mixed_radix() {
let config = ConversionConfig {
base_fft_size_in: 588,
base_fft_size_out: 1280,
base_factors_in: vec![
Radix::Factor3,
Radix::Factor4,
Radix::Factor7,
Radix::Factor7,
],
base_factors_out: vec![
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor5,
],
};
let (size_in, factors_in, size_out, factors_out) = config.scale_for_throughput();
assert_eq!(size_in, 588);
assert_eq!(size_out, 1280);
assert_eq!(
factors_in,
vec![
Radix::Factor3,
Radix::Factor4,
Radix::Factor7,
Radix::Factor7
]
);
assert_eq!(
factors_out,
vec![
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor5
]
);
}
#[test]
fn test_throughput_scaling() {
let config = ConversionConfig {
base_fft_size_in: 588,
base_fft_size_out: 1280,
base_factors_in: vec![
Radix::Factor3,
Radix::Factor4,
Radix::Factor7,
Radix::Factor7,
],
base_factors_out: vec![
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor5,
],
};
let (input, factors_in, output, factors_out) = config.scale_for_throughput();
assert_eq!(input, 588);
assert_eq!(output, 1280);
assert_eq!(
factors_in,
vec![
Radix::Factor3,
Radix::Factor4,
Radix::Factor7,
Radix::Factor7
]
);
assert_eq!(
factors_out,
vec![
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor4,
Radix::Factor5
]
);
}
}