Skip to content

Commit 0c46eeb

Browse files
committed
Support rational ratio
1 parent 01070ce commit 0c46eeb

File tree

3 files changed

+73
-58
lines changed

3 files changed

+73
-58
lines changed

src/lib.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,23 @@ pub trait Convert {
6262
}
6363
}
6464

65-
const MIN_RATIO: f64 = 0.01;
66-
const MAX_RATIO: f64 = 100.0;
67-
6865
#[derive(Debug)]
6966
pub enum Error {
70-
InvalidRatio,
67+
UnsupportedRatio,
7168
InvalidParam,
7269
}
7370

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

73+
use num_rational::Rational64;
74+
75+
fn supported_ratio(ratio: Rational64) -> bool {
76+
ratio > Rational64::default()
77+
&& *ratio.numer() <= 1024
78+
&& ratio.ceil().to_integer() <= 16
79+
&& ratio.recip().ceil().to_integer() <= 16
80+
}
81+
7682
#[cfg(test)]
7783
mod tests {
7884
use super::*;

src/linear.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
use num_rational::Rational64;
44

5+
use crate::supported_ratio;
6+
57
use super::{Convert, Error, Result};
68

79
enum State {
@@ -21,10 +23,9 @@ pub struct Converter {
2123

2224
impl Converter {
2325
#[inline]
24-
fn new(step: f64) -> Self {
25-
let rstep = Rational64::approximate_float(step).unwrap();
26-
let numer = *rstep.numer() as usize;
27-
let denom = *rstep.denom() as usize;
26+
fn new(step: Rational64) -> Self {
27+
let numer = *step.numer() as usize;
28+
let denom = *step.denom() as usize;
2829
let mut coefs = Vec::with_capacity(denom);
2930
for i in 0..denom {
3031
coefs.push(i as f64 / denom as f64);
@@ -86,20 +87,19 @@ impl Convert for Converter {
8687
}
8788
}
8889

89-
use super::{MAX_RATIO, MIN_RATIO};
90-
9190
#[derive(Clone, Copy)]
9291
pub struct Manager {
93-
ratio: f64,
92+
ratio: Rational64,
9493
}
9594

9695
impl Manager {
9796
#[inline]
9897
pub fn new(ratio: f64) -> Result<Self> {
99-
if (MIN_RATIO..=MAX_RATIO).contains(&ratio) {
98+
let ratio = Rational64::approximate_float(ratio).unwrap_or_default();
99+
if supported_ratio(ratio) {
100100
Ok(Self { ratio })
101101
} else {
102-
Err(Error::InvalidRatio)
102+
Err(Error::UnsupportedRatio)
103103
}
104104
}
105105

@@ -115,7 +115,7 @@ mod tests {
115115

116116
#[test]
117117
fn test_manager_ok() {
118-
let ratio_ok = vec![0.01, 1.0, 10.0, 99.99, 100.0];
118+
let ratio_ok = vec![0.0625, 0.063, 1.0, 15.9, 16.0];
119119
for ratio in ratio_ok {
120120
assert!(Manager::new(ratio).is_ok());
121121
}
@@ -126,8 +126,9 @@ mod tests {
126126
let ratio_err = vec![
127127
-1.0,
128128
0.0,
129-
100.01,
130-
1000.0,
129+
0.0624,
130+
16.01,
131+
0.123456,
131132
f64::INFINITY,
132133
f64::NEG_INFINITY,
133134
f64::NAN,

src/sinc.rs

Lines changed: 50 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use std::sync::Arc;
66

77
use num_rational::Rational64;
88

9+
use crate::supported_ratio;
10+
911
use super::{Convert, Error, Result};
1012

1113
#[inline]
@@ -97,10 +99,9 @@ pub struct Converter {
9799

98100
impl Converter {
99101
#[inline]
100-
fn new(step: f64, order: u32, quan: u32, filter: Arc<Vec<f64>>) -> Self {
101-
let rstep = Rational64::approximate_float(step).unwrap();
102-
let numer = *rstep.numer() as usize;
103-
let denom = *rstep.denom() as usize;
102+
fn new(step: Rational64, order: u32, quan: u32, filter: Arc<Vec<f64>>) -> Self {
103+
let numer = *step.numer() as usize;
104+
let denom = *step.denom() as usize;
104105
let mut coefs = Vec::with_capacity(denom);
105106
for i in 0..denom {
106107
coefs.push(i as f64 / denom as f64);
@@ -178,54 +179,43 @@ impl Convert for Converter {
178179
}
179180
}
180181

181-
use super::{MAX_RATIO, MIN_RATIO};
182-
183182
const MIN_ORDER: u32 = 1;
184183
const MAX_ORDER: u32 = 2048;
185184
const MIN_QUAN: u32 = 1;
186185
const MAX_QUAN: u32 = 16384;
187-
const MIN_CUTOFF: f64 = 0.01;
188-
const MAX_CUTOFF: f64 = 1.0;
189186
const MIN_ATTEN: f64 = 12.0;
190187
const MAX_ATTEN: f64 = 180.0;
191188

192189
#[derive(Clone)]
193190
pub struct Manager {
194-
ratio: f64,
191+
ratio: Rational64,
195192
order: u32,
196193
quan: u32,
197194
latency: usize,
198195
filter: Arc<Vec<f64>>,
199196
}
200197

201198
impl Manager {
202-
/// Create a `Manager` with raw parameters, that means all of these should
203-
/// be calculated in advance.
204-
///
205-
/// - ratio: the conversion ratio, fs_new / fs_old, support [0.1, 100.0]
206-
/// - quan: the quantify number, usually power of 2, support [1, 16384]
207-
/// - order: the order of interpolation FIR filter, support [1, 2048]
208-
/// - kaiser_beta: the beta parameter of kaiser window method, support [0.0, 20.0]
209-
/// - cutoff: the cutoff of FIR filter, according to target sample rate, in [0.01, 1.0]
210-
pub fn with_raw(
211-
ratio: f64,
199+
fn with_raw_internal(
200+
ratio: Rational64,
212201
quan: u32,
213202
order: u32,
214203
kaiser_beta: f64,
215204
cutoff: f64,
216205
) -> Result<Self> {
217-
if !(MIN_RATIO..=MAX_RATIO).contains(&ratio) {
218-
return Err(Error::InvalidRatio);
206+
if !supported_ratio(ratio) {
207+
return Err(Error::UnsupportedRatio);
219208
}
220209
if !(MIN_QUAN..=MAX_QUAN).contains(&quan)
221210
|| !(MIN_ORDER..=MAX_ORDER).contains(&order)
222211
|| !(0.0..=20.0).contains(&kaiser_beta)
223-
|| !(MIN_CUTOFF..=MAX_CUTOFF).contains(&cutoff)
212+
|| !(0.01..=1.0).contains(&cutoff)
224213
{
225214
return Err(Error::InvalidParam);
226215
}
227216
let filter = generate_filter_table(quan, order, kaiser_beta, cutoff);
228-
let latency = (ratio * order as f64 * 0.5).round() as usize;
217+
let fratio = *ratio.numer() as f64 / *ratio.denom() as f64;
218+
let latency = (fratio * order as f64 * 0.5).round() as usize;
229219
Ok(Self {
230220
ratio,
231221
order,
@@ -235,18 +225,38 @@ impl Manager {
235225
})
236226
}
237227

228+
/// Create a `Manager` with raw parameters, that means all of these should
229+
/// be calculated in advance.
230+
///
231+
/// - ratio: the conversion ratio, fs_new / fs_old, support `[0.1, 10.0]`
232+
/// - quan: the quantify number, usually power of 2, support `[1, 16384]`
233+
/// - order: the order of interpolation FIR filter, support `[1, 2048]`
234+
/// - kaiser_beta: the beta parameter of kaiser window method, support `[0.0, 20.0]`
235+
/// - cutoff: the cutoff of FIR filter, according to target sample rate, in `[0.01, 1.0]`
236+
pub fn with_raw(
237+
ratio: f64,
238+
quan: u32,
239+
order: u32,
240+
kaiser_beta: f64,
241+
cutoff: f64,
242+
) -> Result<Self> {
243+
let ratio = Rational64::approximate_float(ratio).unwrap_or_default();
244+
Self::with_raw_internal(ratio, quan, order, kaiser_beta, cutoff)
245+
}
246+
238247
/// Create a `Manager` with attenuation, quantify and transition band width.
239248
///
240249
/// That means the order will be calculated.
241250
///
242-
/// - ratio: the conversion ratio, fs_new / fs_old, support [0.1, 100.0]
243-
/// - atten: the attenuation in dB, support [12.0, 180.0]
244-
/// - quan: the quantify number, usually power of 2, support [1, 16384]
245-
/// - trans_width: the transition band width in [0.01, 1.0]
251+
/// - ratio: the conversion ratio, fs_new / fs_old, support `[0.1, 10.0]`
252+
/// - atten: the attenuation in dB, support `[12.0, 180.0]`
253+
/// - quan: the quantify number, usually power of 2, support `[1, 16384]`
254+
/// - trans_width: the transition band width in `[0.01, 1.0]`
246255
#[inline]
247256
pub fn new(ratio: f64, atten: f64, quan: u32, trans_width: f64) -> Result<Self> {
248-
if !(MIN_RATIO..=MAX_RATIO).contains(&ratio) {
249-
return Err(Error::InvalidRatio);
257+
let ratio_i64 = Rational64::approximate_float(ratio).unwrap_or_default();
258+
if !supported_ratio(ratio_i64) {
259+
return Err(Error::UnsupportedRatio);
250260
}
251261
if !(MIN_ATTEN..=MAX_ATTEN).contains(&atten)
252262
|| !(MIN_QUAN..=MAX_QUAN).contains(&quan)
@@ -257,21 +267,22 @@ impl Manager {
257267
let kaiser_beta = calc_kaiser_beta(atten);
258268
let order = calc_order(ratio, atten, trans_width);
259269
let cutoff = ratio.min(1.0) * (1.0 - 0.5 * trans_width);
260-
Self::with_raw(ratio, quan, order, kaiser_beta, cutoff)
270+
Self::with_raw_internal(ratio_i64, quan, order, kaiser_beta, cutoff)
261271
}
262272

263273
/// Create a `Manager` with attenuation, quantify and order
264274
///
265275
/// That means the transition band will be calculated.
266276
///
267-
/// - ratio: [0.1, 100.0]
268-
/// - atten: [12.0, 180.0]
269-
/// - quan: [1, 16384]
270-
/// - order: [1, 2048]
277+
/// - ratio: `[0.1, 10.0]`
278+
/// - atten: `[12.0, 180.0]`
279+
/// - quan: `[1, 16384]`
280+
/// - order: `[1, 2048]`
271281
#[inline]
272282
pub fn with_order(ratio: f64, atten: f64, quan: u32, order: u32) -> Result<Self> {
273-
if !(MIN_RATIO..=MAX_RATIO).contains(&ratio) {
274-
return Err(Error::InvalidRatio);
283+
let ratio_i64 = Rational64::approximate_float(ratio).unwrap_or_default();
284+
if !supported_ratio(ratio_i64) {
285+
return Err(Error::UnsupportedRatio);
275286
}
276287
if !(MIN_ATTEN..=MAX_ATTEN).contains(&atten)
277288
|| !(MIN_QUAN..=MAX_QUAN).contains(&quan)
@@ -282,7 +293,7 @@ impl Manager {
282293
let kaiser_beta = calc_kaiser_beta(atten);
283294
let trans_width = calc_trans_width(ratio, atten, order);
284295
let cutoff = ratio.min(1.0) * (1.0 - 0.5 * trans_width);
285-
Self::with_raw(ratio, quan, order, kaiser_beta, cutoff)
296+
Self::with_raw_internal(ratio_i64, quan, order, kaiser_beta, cutoff)
286297
}
287298

288299
/// Create a `Converter` which actually implement the interpolation.
@@ -316,15 +327,12 @@ mod tests {
316327
#[test]
317328
fn test_manager_with_raw() {
318329
assert!(Manager::with_raw(2.0, 32, 32, 5.0, 0.8).is_ok());
319-
assert!(Manager::with_raw(0.01, 32, 32, 5.0, 0.8).is_ok());
320-
assert!(Manager::with_raw(100.0, 32, 32, 5.0, 0.8).is_ok());
321-
assert!(Manager::with_raw(0.009, 32, 32, 5.0, 0.8).is_err());
322-
assert!(Manager::with_raw(100.1, 32, 32, 5.0, 0.8).is_err());
323330
assert!(Manager::with_raw(2.0, 0, 32, 5.0, 0.8).is_err());
324331
assert!(Manager::with_raw(2.0, 32, 0, 5.0, 0.8).is_err());
325-
assert!(Manager::with_raw(2.0, 32, 32, 5.0, -0.1).is_err());
332+
assert!(Manager::with_raw(2.0, 32, 32, 5.0, 0.0).is_err());
326333
assert!(Manager::with_raw(2.0, 32, 32, 5.0, 1.1).is_err());
327334
assert!(Manager::with_raw(2.0, 32, 32, -0.1, 0.8).is_err());
335+
assert!(Manager::with_raw(2.0, 32, 32, 20.1, 0.8).is_err());
328336
}
329337

330338
#[test]

0 commit comments

Comments
 (0)