Skip to content

Commit 54369c6

Browse files
committed
Builder and sample rate
1 parent 32f439d commit 54369c6

File tree

3 files changed

+217
-26
lines changed

3 files changed

+217
-26
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub trait Convert {
6666
pub enum Error {
6767
UnsupportedRatio,
6868
InvalidParam,
69+
NotEnoughParam,
6970
}
7071

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

src/sinc.rs

Lines changed: 180 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,40 @@ impl Manager {
252252
})
253253
}
254254

255+
fn new_internal(ratio: Rational64, atten: f64, quan: u32, trans_width: f64) -> Result<Self> {
256+
if !supported_ratio(ratio) {
257+
return Err(Error::UnsupportedRatio);
258+
}
259+
if !(MIN_ATTEN..=MAX_ATTEN).contains(&atten)
260+
|| !(MIN_QUAN..=MAX_QUAN).contains(&quan)
261+
|| !(0.01..=1.0).contains(&trans_width)
262+
{
263+
return Err(Error::InvalidParam);
264+
}
265+
let kaiser_beta = calc_kaiser_beta(atten);
266+
let fratio = *ratio.numer() as f64 / *ratio.denom() as f64;
267+
let order = calc_order(fratio, atten, trans_width);
268+
let cutoff = fratio.min(1.0) * (1.0 - 0.5 * trans_width);
269+
Self::with_raw_internal(ratio, quan, order, kaiser_beta, cutoff)
270+
}
271+
272+
fn with_order_internal(ratio: Rational64, atten: f64, quan: u32, order: u32) -> Result<Self> {
273+
if !supported_ratio(ratio) {
274+
return Err(Error::UnsupportedRatio);
275+
}
276+
if !(MIN_ATTEN..=MAX_ATTEN).contains(&atten)
277+
|| !(MIN_QUAN..=MAX_QUAN).contains(&quan)
278+
|| !(MIN_ORDER..=MAX_ORDER).contains(&order)
279+
{
280+
return Err(Error::InvalidParam);
281+
}
282+
let fratio = *ratio.numer() as f64 / *ratio.denom() as f64;
283+
let kaiser_beta = calc_kaiser_beta(atten);
284+
let trans_width = calc_trans_width(fratio, atten, order);
285+
let cutoff = fratio.min(1.0) * (1.0 - 0.5 * trans_width);
286+
Self::with_raw_internal(ratio, quan, order, kaiser_beta, cutoff)
287+
}
288+
255289
/// Create a `Manager` with raw parameters, that means all of these should
256290
/// be calculated in advance.
257291
///
@@ -281,20 +315,8 @@ impl Manager {
281315
/// - trans_width: the transition band width in `[0.01, 1.0]`
282316
#[inline]
283317
pub fn new(ratio: f64, atten: f64, quan: u32, trans_width: f64) -> Result<Self> {
284-
let ratio_i64 = Rational64::approximate_float(ratio).unwrap_or_default();
285-
if !supported_ratio(ratio_i64) {
286-
return Err(Error::UnsupportedRatio);
287-
}
288-
if !(MIN_ATTEN..=MAX_ATTEN).contains(&atten)
289-
|| !(MIN_QUAN..=MAX_QUAN).contains(&quan)
290-
|| !(0.01..=1.0).contains(&trans_width)
291-
{
292-
return Err(Error::InvalidParam);
293-
}
294-
let kaiser_beta = calc_kaiser_beta(atten);
295-
let order = calc_order(ratio, atten, trans_width);
296-
let cutoff = ratio.min(1.0) * (1.0 - 0.5 * trans_width);
297-
Self::with_raw_internal(ratio_i64, quan, order, kaiser_beta, cutoff)
318+
let ratio = Rational64::approximate_float(ratio).unwrap_or_default();
319+
Self::new_internal(ratio, atten, quan, trans_width)
298320
}
299321

300322
/// Create a `Manager` with attenuation, quantify and order
@@ -307,20 +329,28 @@ impl Manager {
307329
/// - order: `[1, 2048]`
308330
#[inline]
309331
pub fn with_order(ratio: f64, atten: f64, quan: u32, order: u32) -> Result<Self> {
310-
let ratio_i64 = Rational64::approximate_float(ratio).unwrap_or_default();
311-
if !supported_ratio(ratio_i64) {
312-
return Err(Error::UnsupportedRatio);
313-
}
314-
if !(MIN_ATTEN..=MAX_ATTEN).contains(&atten)
315-
|| !(MIN_QUAN..=MAX_QUAN).contains(&quan)
316-
|| !(MIN_ORDER..=MAX_ORDER).contains(&order)
317-
{
332+
let ratio = Rational64::approximate_float(ratio).unwrap_or_default();
333+
Self::with_order_internal(ratio, atten, quan, order)
334+
}
335+
336+
#[inline]
337+
pub fn with_sample_rate(
338+
old_sr: u32,
339+
new_sr: u32,
340+
atten: f64,
341+
quan: u32,
342+
pass_freq: u32,
343+
) -> Result<Self> {
344+
if old_sr == 0 || new_sr == 0 {
318345
return Err(Error::InvalidParam);
319346
}
320-
let kaiser_beta = calc_kaiser_beta(atten);
321-
let trans_width = calc_trans_width(ratio, atten, order);
322-
let cutoff = ratio.min(1.0) * (1.0 - 0.5 * trans_width);
323-
Self::with_raw_internal(ratio_i64, quan, order, kaiser_beta, cutoff)
347+
let ratio = Rational64::new(new_sr.into(), old_sr.into());
348+
if !supported_ratio(ratio) {
349+
return Err(Error::UnsupportedRatio);
350+
}
351+
let min_sr = new_sr.min(old_sr);
352+
let trans_width = min_sr.saturating_sub(pass_freq.saturating_mul(2)) as f64 / min_sr as f64;
353+
Self::new_internal(ratio, atten, quan, trans_width)
324354
}
325355

326356
/// Create a `Converter` which actually implement the interpolation.
@@ -345,6 +375,118 @@ impl Manager {
345375
pub fn order(&self) -> u32 {
346376
self.order
347377
}
378+
379+
#[inline]
380+
pub fn builder() -> Builder {
381+
Builder::default()
382+
}
383+
}
384+
385+
#[derive(Default)]
386+
pub struct Builder {
387+
ratio: Option<Rational64>,
388+
order: Option<u32>,
389+
quan: Option<u32>,
390+
kaiser_beta: Option<f64>,
391+
cutoff: Option<f64>,
392+
atten: Option<f64>,
393+
trans_width: Option<f64>,
394+
old_sr: Option<u32>,
395+
new_sr: Option<u32>,
396+
pass_freq: Option<u32>,
397+
}
398+
399+
impl Builder {
400+
pub fn ratio(mut self, ratio: f64) -> Self {
401+
self.ratio = Some(Rational64::approximate_float(ratio).unwrap_or_default());
402+
self
403+
}
404+
405+
pub fn sample_rate(mut self, old_sr: u32, new_sr: u32) -> Self {
406+
self.old_sr = Some(old_sr);
407+
self.new_sr = Some(new_sr);
408+
self
409+
}
410+
411+
pub fn quantify(mut self, quan: u32) -> Self {
412+
self.quan = Some(quan);
413+
self
414+
}
415+
416+
pub fn order(mut self, order: u32) -> Self {
417+
self.order = Some(order);
418+
self
419+
}
420+
421+
pub fn kaiser_beta<B: Into<f64>>(mut self, beta: B) -> Self {
422+
self.kaiser_beta = Some(beta.into());
423+
self
424+
}
425+
426+
pub fn cutoff(mut self, cutoff: f64) -> Self {
427+
self.cutoff = Some(cutoff);
428+
self
429+
}
430+
431+
pub fn attenuation<A: Into<f64>>(mut self, atten: A) -> Self {
432+
self.atten = Some(atten.into());
433+
self
434+
}
435+
436+
pub fn trans_width(mut self, width: f64) -> Self {
437+
self.trans_width = Some(width);
438+
self
439+
}
440+
441+
pub fn pass_width(mut self, width: f64) -> Self {
442+
self.trans_width = Some(1.0 - width);
443+
self
444+
}
445+
446+
pub fn pass_freq(mut self, freq: u32) -> Self {
447+
self.pass_freq = Some(freq);
448+
self
449+
}
450+
451+
pub fn build(self) -> Result<Manager> {
452+
let (ratio, quan) = match (self.ratio, self.quan, self.old_sr, self.new_sr) {
453+
(Some(ratio), Some(quan), _, _) => (ratio, quan),
454+
(_, Some(quan), Some(old_sr), Some(new_sr)) => {
455+
if old_sr == 0 || new_sr == 0 {
456+
return Err(Error::InvalidParam);
457+
}
458+
(Rational64::new(new_sr.into(), old_sr.into()), quan)
459+
}
460+
_ => return Err(Error::NotEnoughParam),
461+
};
462+
if !supported_ratio(ratio) {
463+
return Err(Error::UnsupportedRatio);
464+
}
465+
match (
466+
self.order,
467+
self.kaiser_beta,
468+
self.cutoff,
469+
self.atten,
470+
self.trans_width,
471+
self.old_sr,
472+
self.new_sr,
473+
self.pass_freq,
474+
) {
475+
(Some(order), Some(kaiser_beta), Some(cutoff), _, _, _, _, _) => {
476+
Manager::with_raw_internal(ratio, quan, order, kaiser_beta, cutoff)
477+
}
478+
(_, _, _, Some(atten), Some(trans_width), _, _, _) => {
479+
Manager::new_internal(ratio, atten, quan, trans_width)
480+
}
481+
(Some(order), _, _, Some(atten), _, _, _, _) => {
482+
Manager::with_order_internal(ratio, atten, quan, order)
483+
}
484+
(_, _, _, Some(atten), _, Some(old_sr), Some(new_sr), Some(pass_freq)) => {
485+
Manager::with_sample_rate(old_sr, new_sr, atten, quan, pass_freq)
486+
}
487+
_ => Err(Error::NotEnoughParam),
488+
}
489+
}
348490
}
349491

350492
#[cfg(test)]
@@ -380,4 +522,16 @@ mod tests {
380522
assert!(Manager::with_order(2.0, 12.0, 32, 32).is_ok());
381523
assert!(Manager::with_order(2.0, 11.9, 32, 32).is_err());
382524
}
525+
526+
#[test]
527+
fn test_builder() {
528+
assert!(Manager::builder().build().is_err());
529+
let manager = Manager::builder()
530+
.sample_rate(44100, 48000)
531+
.quantify(32)
532+
.attenuation(72)
533+
.pass_freq(20000)
534+
.build();
535+
assert!(manager.is_ok());
536+
}
383537
}

tests/sinc.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ impl Src {
3232
.unwrap(),
3333
}
3434
}
35+
36+
fn new_by_builder(sr_old: u32, sr_new: u32, builder: sinc::Builder) -> Self {
37+
Self {
38+
sr_old,
39+
sr_new,
40+
manager: builder.build().unwrap(),
41+
}
42+
}
3543
}
3644

3745
fn convert(file_prefix: &str, src: &Src, remark: &str) {
@@ -333,6 +341,34 @@ fn ta150_1_192k_down_order() {
333341
println!("order of 192k to 48k a150 is {}", src.manager.order());
334342
}
335343

344+
#[test]
345+
#[ignore = "slow"]
346+
// cargo test -r --test sinc -- --ignored --exact --show-output ta96_1
347+
fn ta96_1() {
348+
cwd();
349+
let remark = "a96_1";
350+
let builder = sinc::Builder::default()
351+
.sample_rate(44100, 48000)
352+
.attenuation(96)
353+
.quantify(128)
354+
.pass_freq(20000);
355+
let src = Src::new_by_builder(44100, 48000, builder);
356+
println!("order of 44k to 48k {remark} is {}", src.manager.order());
357+
convert("beep", &src, remark);
358+
convert("sweep", &src, remark);
359+
impulse(&src, remark);
360+
let builder = sinc::Builder::default()
361+
.sample_rate(48000, 44100)
362+
.attenuation(96)
363+
.quantify(128)
364+
.pass_freq(20000);
365+
let src = Src::new_by_builder(48000, 44100, builder);
366+
println!("order of 48k to 44k {remark} is {}", src.manager.order());
367+
convert("beep", &src, remark);
368+
convert("sweep", &src, remark);
369+
impulse(&src, remark);
370+
}
371+
336372
#[test]
337373
#[ignore = "display only"]
338374
fn tmultithread() {

0 commit comments

Comments
 (0)