Skip to content

Commit

Permalink
Use Result, bound check and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
DrPeaboss committed Oct 15, 2024
1 parent 2735d0b commit f427aa5
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 42 deletions.
10 changes: 6 additions & 4 deletions benches/bench1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,15 @@ const TRANS48K: f64 = 4000.0 / 24000.0;

#[divan::bench(args=[Conv::C44k48k, Conv::C44k96k, Conv::C48k44k, Conv::C48k96k, Conv::C96k44k, Conv::C96k48k])]
fn init_a120(conv: &Conv) -> Manager {
match conv {
let m = match conv {
Conv::C44k48k => Manager::new(R44K48K, 120.0, 512, TRANS44K),
Conv::C44k96k => Manager::new(R44K96K, 120.0, 512, TRANS44K),
Conv::C48k44k => Manager::new(R48K44K, 120.0, 512, TRANS44K),
Conv::C48k96k => Manager::new(R48K96K, 120.0, 512, TRANS48K),
Conv::C96k44k => Manager::new(R96K44K, 120.0, 512, TRANS44K),
Conv::C96k48k => Manager::new(R96K48K, 120.0, 512, TRANS48K),
}
};
m.unwrap()
}

#[divan::bench(
Expand All @@ -74,14 +75,15 @@ fn proc_a120_10ms(bencher: divan::Bencher, conv: &Conv) {

#[divan::bench(args=[Conv::C44k48k, Conv::C44k96k, Conv::C48k44k, Conv::C48k96k, Conv::C96k44k, Conv::C96k48k])]
fn init_a144(conv: &Conv) -> Manager {
match conv {
let m = match conv {
Conv::C44k48k => Manager::new(R44K48K, 144.0, 2048, TRANS44K),
Conv::C44k96k => Manager::new(R44K96K, 144.0, 2048, TRANS44K),
Conv::C48k44k => Manager::new(R48K44K, 144.0, 2048, TRANS44K),
Conv::C48k96k => Manager::new(R48K96K, 144.0, 2048, TRANS48K),
Conv::C96k44k => Manager::new(R96K44K, 144.0, 2048, TRANS44K),
Conv::C96k48k => Manager::new(R96K48K, 144.0, 2048, TRANS48K),
}
};
m.unwrap()
}

#[divan::bench(
Expand Down
2 changes: 1 addition & 1 deletion examples/e1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use simple_src::{linear, Convert};
fn main() {
let samples1 = [1.0, 2.0, 3.0, 4.0];
let samples2 = [5.0, 6.0, 7.0, 8.0];
let manager = linear::Manager::new(2.0);
let manager = linear::Manager::new(2.0).unwrap();
let mut cvtr = manager.converter();
for s in cvtr.process(samples1.into_iter()) {
println!("{s}");
Expand Down
2 changes: 1 addition & 1 deletion examples/perf1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use simple_src::{sinc, Convert};
// cargo flamegraph --profile perf --example perf1
fn main() {
let now = std::time::Instant::now();
let manager = sinc::Manager::new(48000.0 / 44100.0, 150.0, 2048, 2050.0 / 22050.0);
let manager = sinc::Manager::new(48000.0 / 44100.0, 150.0, 2048, 2050.0 / 22050.0).unwrap();
println!("{:?}", now.elapsed());
let now = std::time::Instant::now();
let iter = (0..).map(|x| x as f64).into_iter();
Expand Down
2 changes: 1 addition & 1 deletion examples/two_channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn convert_to_48k() {
sample_format: hound::SampleFormat::Int,
};
let mut writer = hound::WavWriter::create(TARGET_FILE, spec).unwrap();
let manager = sinc::Manager::new(48000.0 / 44100.0, 110.0, 256, 2050.0 / 22050.0);
let manager = sinc::Manager::new(48000.0 / 44100.0, 110.0, 256, 2050.0 / 22050.0).unwrap();
let latency = manager.latency();
let mut converter1 = manager.converter();
let mut converter2 = manager.converter();
Expand Down
23 changes: 16 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//! use simple_src::{sinc, Convert};
//!
//! let samples = vec![1.0, 2.0, 3.0, 4.0];
//! let manager = sinc::Manager::new(2.0, 48.0, 8, 0.1);
//! let manager = sinc::Manager::new(2.0, 48.0, 8, 0.1).unwrap();
//! let mut converter = manager.converter();
//! for s in converter.process(samples.into_iter()) {
//! println!("{s}");
Expand Down Expand Up @@ -62,6 +62,14 @@ pub trait Convert {
}
}

#[derive(Debug)]
pub enum Error {
InvalidRatio,
InvalidParam,
}

pub type Result<T> = std::result::Result<T, Error>;

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -73,10 +81,11 @@ mod tests {
#[allow(dead_code)]
pub fn new(a: i32) -> Box<dyn Convert> {
if a == 0 {
Box::new(linear::Converter::new(0.5))
let manager = linear::Manager::new(2.0).unwrap();
Box::new(manager.converter())
} else {
let filter = std::sync::Arc::new(vec![]);
Box::new(sinc::Converter::new(0.5, 128, 128, filter))
let manager = sinc::Manager::new(2.0, 48.0, 8, 0.2).unwrap();
Box::new(manager.converter())
}
}
}
Expand All @@ -85,7 +94,7 @@ mod tests {
#[ignore = "display only"]
fn test1() {
let samples = vec![1.0, 2.0, 3.0, 4.0];
let manager = linear::Manager::new(2.0);
let manager = linear::Manager::new(2.0).unwrap();
let mut cvtr = manager.converter();
for s in cvtr.process(samples.into_iter()) {
println!("sample = {s}");
Expand All @@ -96,7 +105,7 @@ mod tests {
#[ignore = "display only"]
fn test2() {
let samples = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let manager = sinc::Manager::with_raw(2.0, 16, 4, 5.0, 1.0);
let manager = sinc::Manager::with_raw(2.0, 16, 4, 5.0, 1.0).unwrap();
for s in manager
.converter()
.process(samples.into_iter())
Expand All @@ -110,7 +119,7 @@ mod tests {
#[ignore = "display only"]
fn test3() {
let samples = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let manager = sinc::Manager::with_order(2.0, 30.0, 16, 4);
let manager = sinc::Manager::with_order(2.0, 30.0, 16, 4).unwrap();
for s in manager
.converter()
.process(samples.into_iter())
Expand Down
41 changes: 37 additions & 4 deletions src/linear.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Linear converter

use super::Convert;
use super::{Convert, Error, Result};

enum State {
First,
Expand All @@ -17,7 +17,7 @@ pub struct Converter {

impl Converter {
#[inline]
pub fn new(step: f64) -> Self {
fn new(step: f64) -> Self {
Self {
step,
pos: 0.0,
Expand Down Expand Up @@ -79,12 +79,45 @@ pub struct Manager {

impl Manager {
#[inline]
pub fn new(ratio: f64) -> Self {
Self { ratio }
pub fn new(ratio: f64) -> Result<Self> {
if ratio >= 0.01 && ratio <= 100.0 {
Ok(Self { ratio })
} else {
Err(Error::InvalidRatio)
}
}

#[inline]
pub fn converter(&self) -> Converter {
Converter::new(self.ratio.recip())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_manager_ok() {
let ratio_ok = vec![0.01, 1.0, 10.0, 99.99, 100.0];
for ratio in ratio_ok {
assert!(Manager::new(ratio).is_ok());
}
}

#[test]
fn test_manager_err() {
let ratio_err = vec![
-1.0,
0.0,
100.01,
1000.0,
f64::INFINITY,
f64::NEG_INFINITY,
f64::NAN,
];
for ratio in ratio_err {
assert!(Manager::new(ratio).is_err());
}
}
}
115 changes: 99 additions & 16 deletions src/sinc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::collections::VecDeque;
use std::f64::consts::PI;
use std::sync::Arc;

use super::Convert;
use super::{Convert, Error, Result};

#[inline]
fn sinc_c(x: f64, cutoff: f64) -> f64 {
Expand Down Expand Up @@ -93,7 +93,7 @@ pub struct Converter {

impl Converter {
#[inline]
pub fn new(step: f64, order: u32, quan: u32, filter: Arc<Vec<f64>>) -> Self {
fn new(step: f64, order: u32, quan: u32, filter: Arc<Vec<f64>>) -> Self {
let taps = (order + 1) as usize;
let mut buf = VecDeque::with_capacity(taps);
buf.extend(std::iter::repeat(0.0).take(taps));
Expand Down Expand Up @@ -178,33 +178,65 @@ impl Manager {
/// Create a `Manager` with raw parameters, that means all of these should
/// be calculated in advance.
///
/// - ratio: the conversion ratio, fs_new / fs_old
/// - quan: the quantify number, usually power of 2
/// - order: the order of interpolation FIR filter
/// - kaiser_beta: the beta parameter of kaiser window method
/// - cutoff: the cutoff of FIR filter, according to target sample rate
pub fn with_raw(ratio: f64, quan: u32, order: u32, kaiser_beta: f64, cutoff: f64) -> Self {
/// - ratio: the conversion ratio, fs_new / fs_old, support [0.1, 100.0]
/// - quan: the quantify number, usually power of 2, support [1, 16384]
/// - order: the order of interpolation FIR filter, support [1, 2048]
/// - kaiser_beta: the beta parameter of kaiser window method, support [0.0, 20.0]
/// - cutoff: the cutoff of FIR filter, according to target sample rate, in [0.01, 1.0]
pub fn with_raw(
ratio: f64,
quan: u32,
order: u32,
kaiser_beta: f64,
cutoff: f64,
) -> Result<Self> {
if ratio < 0.01 || ratio > 100.0 {
return Err(Error::InvalidRatio);
}
if quan == 0
|| quan > 16384
|| order == 0
|| order > 2048
|| kaiser_beta < 0.0
|| kaiser_beta > 20.0
|| cutoff < 0.01
|| cutoff > 1.0
{
return Err(Error::InvalidParam);
}
let filter = generate_filter_table(quan, order, kaiser_beta, cutoff);
let latency = (ratio * order as f64 * 0.5).round() as usize;
Self {
Ok(Self {
ratio,
order,
quan,
latency,
filter: Arc::new(filter),
}
})
}

/// Create a `Manager` with attenuation, quantify and transition band width.
///
/// That means the order will be calculated.
///
/// - ratio: the conversion ratio, fs_new / fs_old
/// - atten: the attenuation in dB
/// - quan: the quantify number, usually power of 2
/// - trans_width: the transition band width in (0,1)
/// - ratio: the conversion ratio, fs_new / fs_old, support [0.1, 100.0]
/// - atten: the attenuation in dB, support [12.0, 180.0]
/// - quan: the quantify number, usually power of 2, support [1, 16384]
/// - trans_width: the transition band width in [0.01, 1.0]
#[inline]
pub fn new(ratio: f64, atten: f64, quan: u32, trans_width: f64) -> Self {
pub fn new(ratio: f64, atten: f64, quan: u32, trans_width: f64) -> Result<Self> {
if ratio < 0.01 || ratio > 100.0 {
return Err(Error::InvalidRatio);
}
if atten < 12.0
|| atten > 180.0
|| quan == 0
|| quan > 16384
|| trans_width < 0.01
|| trans_width > 1.0
{
return Err(Error::InvalidParam);
}
let kaiser_beta = calc_kaiser_beta(atten);
let order = calc_order(ratio, atten, trans_width);
let cutoff = ratio.min(1.0) * (1.0 - 0.5 * trans_width);
Expand All @@ -214,8 +246,20 @@ impl Manager {
/// Create a `Manager` with attenuation, quantify and order
///
/// That means the transition band will be calculated.
///
/// - ratio: [0.1, 100.0]
/// - atten: [12.0, 180.0]
/// - quan: [1, 16384]
/// - order: [1, 2048]
#[inline]
pub fn with_order(ratio: f64, atten: f64, quan: u32, order: u32) -> Self {
pub fn with_order(ratio: f64, atten: f64, quan: u32, order: u32) -> Result<Self> {
if ratio < 0.01 || ratio > 100.0 {
return Err(Error::InvalidRatio);
}
if atten < 12.0 || atten > 180.0 || quan == 0 || quan > 16384 || order == 0 || order > 2048
{
return Err(Error::InvalidParam);
}
let kaiser_beta = calc_kaiser_beta(atten);
let trans_width = calc_trans_width(ratio, atten, order);
let cutoff = ratio.min(1.0) * (1.0 - 0.5 * trans_width);
Expand Down Expand Up @@ -245,3 +289,42 @@ impl Manager {
self.order
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_manager_with_raw() {
assert!(Manager::with_raw(2.0, 48, 32, 5.0, 0.8).is_ok());
assert!(Manager::with_raw(0.01, 48, 32, 5.0, 0.8).is_ok());
assert!(Manager::with_raw(100.0, 48, 32, 5.0, 0.8).is_ok());
assert!(Manager::with_raw(0.009, 48, 32, 5.0, 0.8).is_err());
assert!(Manager::with_raw(100.1, 48, 32, 5.0, 0.8).is_err());
assert!(Manager::with_raw(2.0, 0, 32, 5.0, 0.8).is_err());
assert!(Manager::with_raw(2.0, 48, 0, 5.0, 0.8).is_err());
assert!(Manager::with_raw(2.0, 48, 32, 5.0, -0.1).is_err());
assert!(Manager::with_raw(2.0, 48, 32, 5.0, 1.1).is_err());
assert!(Manager::with_raw(2.0, 48, 32, -0.1, 0.8).is_err());
}

#[test]
fn test_manager_new() {
assert!(Manager::new(2.0, 96.0, 128, 0.1).is_ok());
assert!(Manager::new(2.0, 96.0, 0, 0.1).is_err());
assert!(Manager::new(2.0, 96.0, 128, 0.0).is_err());
assert!(Manager::new(2.0, 96.0, 128, 1.1).is_err());
assert!(Manager::new(2.0, 4.0, 128, 0.1).is_err());
assert!(Manager::new(2.0, 8.0, 128, 0.1).is_err());
assert!(Manager::new(2.0, 8.1, 128, 0.1).is_ok());
}

#[test]
fn test_manager_with_order() {
assert!(Manager::with_order(2.0, 96.0, 128, 128).is_ok());
assert!(Manager::with_order(2.0, 96.0, 128, 0).is_err());
assert!(Manager::with_order(2.0, 96.0, 0, 128).is_err());
assert!(Manager::with_order(2.0, 8.0, 128, 128).is_err());
assert!(Manager::with_order(2.0, 8.1, 128, 128).is_ok());
}
}
3 changes: 2 additions & 1 deletion tests/linear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ fn convert(file_prefix: &str, sr_old: u32, sr_new: u32) {
.map(|s| s.unwrap() as f64)
.chain(std::iter::repeat(0.0));
Manager::new(ratio)
.unwrap()
.converter()
.process(in_iter)
.take(out_duration)
Expand All @@ -45,7 +46,7 @@ fn tlinear() {
#[test]
#[ignore = "display only"]
fn tmultithread() {
let manager = Manager::new(2.0);
let manager = Manager::new(2.0).unwrap();
let h1 = std::thread::spawn(move || {
let mut converter = manager.converter();
let samples = (0..10).map(|x| x as f64);
Expand Down
Loading

0 comments on commit f427aa5

Please sign in to comment.