diff --git a/src/dsp/helpers.rs b/src/dsp/helpers.rs index 7e9e70c..ff9d454 100644 --- a/src/dsp/helpers.rs +++ b/src/dsp/helpers.rs @@ -721,11 +721,10 @@ pub fn process_1pole_lowpass(input: f64, freq: f64, israte: f64, z: &mut f64) -> pub fn process_1pole_highpass(input: f64, freq: f64, israte: f64, z: &mut f64, y: &mut f64) -> f64 { let b = (-std::f64::consts::TAU * freq * israte).exp(); let a = (1.0 + b) / 2.0; - let a1 = - a; let v = a * input - + a1 * *z + - a * *z + b * *y; *y = v; *z = input; @@ -737,10 +736,10 @@ pub fn process_1pole_highpass(input: f64, freq: f64, israte: f64, z: &mut f64, y // (page 5) #[inline] pub fn process_1pole_tpt_lowpass(input: f64, freq: f64, israte: f64, z: &mut f64) -> f64 { - let g = (std::f64::consts::PI * freq * israte).tan(); - let a1 = g / (1.0 + g); + let g = (std::f64::consts::PI * freq * israte).tan(); + let a = g / (1.0 + g); - let v1 = a1 * (input - *z); + let v1 = a * (input - *z); let v2 = v1 + *z; *z = v2 + v1; diff --git a/src/dsp/node_sfilter.rs b/src/dsp/node_sfilter.rs index 9772b32..5a1da9c 100644 --- a/src/dsp/node_sfilter.rs +++ b/src/dsp/node_sfilter.rs @@ -89,7 +89,7 @@ impl DspNode for SFilter { for frame in 0..ctx.nframes() { let input = inp.read(frame) as f64; let freq = denorm::SFilter::freq(freq, frame) as f64; - let freq = freq.clamp(10.0, 22050.0); + let freq = freq.clamp(1.0, 22000.0); out.write(frame, process_1pole_lowpass( input, freq, self.israte, &mut self.z) @@ -100,7 +100,7 @@ impl DspNode for SFilter { for frame in 0..ctx.nframes() { let input = inp.read(frame) as f64; let freq = denorm::SFilter::freq(freq, frame) as f64; - let freq = freq.clamp(10.0, 18000.0); + let freq = freq.clamp(1.0, 22000.0); out.write(frame, process_1pole_tpt_lowpass( input, freq, self.israte, &mut self.z) @@ -111,7 +111,7 @@ impl DspNode for SFilter { for frame in 0..ctx.nframes() { let input = inp.read(frame) as f64; let freq = denorm::SFilter::freq(freq, frame) as f64; - let freq = freq.clamp(10.0, 22050.0); + let freq = freq.clamp(1.0, 22000.0); out.write(frame, process_1pole_highpass( input, freq, self.israte, &mut self.z, &mut self.y) @@ -122,7 +122,7 @@ impl DspNode for SFilter { for frame in 0..ctx.nframes() { let input = inp.read(frame) as f64; let freq = denorm::SFilter::freq(freq, frame) as f64; - let freq = freq.clamp(10.0, 18000.0); + let freq = freq.clamp(1.0, 22000.0); out.write(frame, process_1pole_tpt_highpass( input, freq, self.israte, &mut self.z) diff --git a/tests/common/mod.rs b/tests/common/mod.rs index df39c46..5c92124 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,6 +1,7 @@ pub use hexodsp::matrix::*; pub use hexodsp::nodes::new_node_engine; pub use hexodsp::dsp::*; +pub use hexodsp::NodeExecutor; use hound; //use num_complex::Complex; @@ -248,6 +249,20 @@ pub fn pset_d(matrix: &mut Matrix, nid: NodeId, parm: &str, v_denorm: f32) { matrix.set_param(p, SAtom::param(p.norm(v_denorm))); } +#[allow(unused)] +pub fn pset_n_wait(matrix: &mut Matrix, ne: &mut NodeExecutor, nid: NodeId, parm: &str, v_norm: f32) { + let p = nid.inp_param(parm).unwrap(); + matrix.set_param(p, SAtom::param(v_norm)); + run_for_ms(ne, 15.0); +} + +#[allow(unused)] +pub fn pset_d_wait(matrix: &mut Matrix, ne: &mut NodeExecutor, nid: NodeId, parm: &str, v_denorm: f32) { + let p = nid.inp_param(parm).unwrap(); + matrix.set_param(p, SAtom::param(p.norm(v_denorm))); + run_for_ms(ne, 15.0); +} + #[allow(dead_code)] pub fn save_wav(name: &str, buf: &[f32]) { let spec = hound::WavSpec { @@ -407,6 +422,90 @@ pub fn run_and_get_fft4096( fft_thres_at_ms(&mut out_l[..], FFT::F4096, thres, offs_ms) } +#[allow(unused)] +pub fn calc_exp_avg_buckets4096(fft: &[(u16, u32)]) -> Vec<(u16, u32)> { + let mut avg = vec![]; + let mut last_n = [0; 256]; + let mut p = 0; + let mut cur_len = 2; + + for (i, (fq, lvl)) in fft.iter().enumerate() { + last_n[p] = *lvl; + p += 1; + if p >= cur_len { + avg.push(( + *fq, + last_n + .iter() + .take(cur_len) + .map(|x| *x) + .sum::() + / (cur_len as u32))); + p = 0; + } + + if i % 16 == 0 { + cur_len += 2; + if cur_len > last_n.len() { + cur_len = last_n.len(); + } + //d// println!("len={}", cur_len); + } + } + + avg +} + +#[allow(unused)] +pub fn avg_fft_freqs(round_by: f32, ranges: &[u16], fft: &[(u16, u32)]) -> Vec<(u16, u32)> { + let mut from = 0; + let mut out = vec![]; + for rng in ranges.iter() { + out.push( + (from, + ((avg_fft_range(from, *rng, fft) + / round_by) + .floor() * round_by) + as u32)); + from = *rng; + } + + out +} + +#[allow(unused)] +pub fn avg_fft_range(from_freq: u16, to_freq: u16, fft: &[(u16, u32)]) -> f32 { + let mut count = 0; + let mut sum = 0; + for (fq, lvl) in fft.iter() { + if from_freq <= *fq && *fq < to_freq { + sum += *lvl; + count += 1; + } + } + + sum as f32 / count as f32 +} + + +#[allow(unused)] +pub fn run_and_get_fft512( + node_exec: &mut hexodsp::nodes::NodeExecutor, + thres: u32, + offs_ms: f32) -> Vec<(u16, u32)> +{ + let min_samples_for_fft = 512.0; + let offs_samples = (offs_ms * (SAMPLE_RATE / 1000.0)).ceil(); + let min_len_samples = + offs_samples + // 2.0 * for safety margin + + 2.0 * min_samples_for_fft; + let run_len_s = min_len_samples / SAMPLE_RATE; + let (mut out_l, _out_r) = run_no_input(node_exec, run_len_s); + fft_thres_at_ms(&mut out_l[..], FFT::F512, thres, offs_ms) +} + + #[allow(unused)] pub fn run_and_get_fft4096_now( node_exec: &mut hexodsp::nodes::NodeExecutor, diff --git a/tests/node_sfilter.rs b/tests/node_sfilter.rs index 332287b..1c77193 100644 --- a/tests/node_sfilter.rs +++ b/tests/node_sfilter.rs @@ -1,8 +1,7 @@ mod common; use common::*; -#[test] -fn check_node_sfilter_compare() { +fn setup_sfilter_matrix() -> (Matrix, NodeExecutor) { let (node_conf, mut node_exec) = new_node_engine(); let mut matrix = Matrix::new(node_conf, 3, 3); @@ -20,52 +19,205 @@ fn check_node_sfilter_compare() { .out(None, None, noise.out("sig"))); matrix.place(1, 2, Cell::empty(out) .input(out.inp("ch2"), None, None)); - pset_d(&mut matrix, sf, "freq", 440.0); matrix.sync().unwrap(); - let ta = std::time::Instant::now(); - let (out_l, out_r) = run_for_ms(&mut node_exec, 10000.0); - let ta = std::time::Instant::now().duration_since(ta); - println!("ta1 Elapsed: {:?}", ta); - - save_wav("check_noise_sfilt_in.wav", &out_r); - save_wav("check_noise_sfilt1.wav", &out_l); - -// let fft = run_and_get_fft4096(&mut node_exec, 5, 1000.0); -// for (fq, lvl) in fft { -// println!("{:5}: {}", fq, lvl); -// } - - pset_s(&mut matrix, sf, "ftype", 1); - - let ta = std::time::Instant::now(); - let (out_l, _) = run_for_ms(&mut node_exec, 10000.0); - save_wav("check_noise_sfilt2.wav", &out_l); - let ta = std::time::Instant::now().duration_since(ta); - println!("ta2 Elapsed: {:?}", ta); - - pset_s(&mut matrix, sf, "ftype", 2); - - let ta = std::time::Instant::now(); - let (out_l, _) = run_for_ms(&mut node_exec, 10000.0); - save_wav("check_noise_sfilt3.wav", &out_l); - let ta = std::time::Instant::now().duration_since(ta); - println!("ta3 Elapsed: {:?}", ta); - - pset_s(&mut matrix, sf, "ftype", 3); - - let ta = std::time::Instant::now(); - let (out_l, _) = run_for_ms(&mut node_exec, 10000.0); - save_wav("check_noise_sfilt4.wav", &out_l); - let ta = std::time::Instant::now().duration_since(ta); - println!("ta4 Elapsed: {:?}", ta); - -// let fft = run_and_get_fft4096(&mut node_exec, 5, 1000.0); -// for (fq, lvl) in fft { -// println!("{:5}: {}", fq, lvl); -// } - -// let fft = run_and_get_fft4096(&mut node_exec, 50, 1000.0); -// assert!(fft.len() == 0); - assert!(false); + (matrix, node_exec) +} + +fn fft_with_freq_res_type( + matrix: &mut Matrix, + node_exec: &mut NodeExecutor, + ftype: i64, freq: f32, _res: f32) -> Vec<(u16, u32)> +{ + let sf = NodeId::SFilter(0); + pset_d_wait(matrix, node_exec, sf, "freq", freq); +// pset_d_wait(&mut matrix, &mut node_exec, sf, "freq", freq); + pset_s(matrix, sf, "ftype", ftype); + run_and_get_fft4096(node_exec, 0, 1000.0) +} + +#[test] +fn check_node_sfilter_lowpass() { + let (mut matrix, mut node_exec) = setup_sfilter_matrix(); + + // Low Pass @ 1000Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 0, 1000.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 100, 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 8000, 12000, 16000, + ], &fft[..]), vec![ + (0, 16), (100, 24), (250, 16), (500, 12), (750, 12), (1000, 12), + (1500, 8), (2000, 4), (3000, 4), (4000, 0), (8000, 0), (12000, 0), + ]); + + // Low Pass @ 4000Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 0, 4000.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 100, 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 8000, 12000, 16000, + ], &fft[..]), vec![ + (0, 16), (100, 20), (250, 16), (500, 12), (750, 20), (1000, 16), + (1500, 16), (2000, 16), (3000, 12), (4000, 8), (8000, 4), (12000, 4), + ]); + + // Low Pass @ 22050Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 0, 22050.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 100, 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 8000, 12000, 16000, + 22050, + ], &fft[..]), vec![ + (0, 16), (100, 20), (250, 12), (500, 16), (750, 16), (1000, 16), + (1500, 16), (2000, 16), (3000, 20), (4000, 16), (8000, 16), (12000, 16), + (16000, 16), + ]); + + // Low Pass @ 0Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 0, 0.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 500, 1000, 1500, 2000, 8000, 12000, 16000, 20000 + ], &fft[..]), vec![ + (0, 0), (500, 0), (1000, 0), + (1500, 0), (2000, 0), (8000, 0), (12000, 0), (16000, 0), + ]); +} + +#[test] +fn check_node_sfilter_lowpass_tpt() { + let (mut matrix, mut node_exec) = setup_sfilter_matrix(); + + // Low Pass TPT @ 1000Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 1, 1000.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 100, 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 8000, 12000, 16000, + ], &fft[..]), vec![ + (0, 16), (100, 24), (250, 16), (500, 12), (750, 12), (1000, 12), + (1500, 8), (2000, 4), (3000, 4), (4000, 0), (8000, 0), (12000, 0), + ]); + + // Low Pass TPT @ 4000Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 1, 4000.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 100, 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 8000, 12000, 16000, + ], &fft[..]), vec![ + (0, 16), (100, 20), (250, 16), (500, 12), (750, 20), (1000, 16), + (1500, 16), (2000, 16), (3000, 12), (4000, 8), (8000, 4), (12000, 0), + ]); + + // Low Pass TPT @ 22050Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 1, 22050.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 100, 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 8000, 12000, 16000, + 22050, + ], &fft[..]), vec![ + (0, 16), (100, 20), (250, 12), (500, 16), (750, 16), (1000, 16), + (1500, 16), (2000, 16), (3000, 20), (4000, 16), (8000, 16), (12000, 16), + (16000, 16), + ]); + + // Low Pass TPT @ 0Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 1, 0.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 500, 1000, 1500, 2000, 8000, 12000, 16000, 20000 + ], &fft[..]), vec![ + (0, 0), (500, 0), (1000, 0), + (1500, 0), (2000, 0), (8000, 0), (12000, 0), (16000, 0), + ]); +} + +#[test] +fn check_node_sfilter_highpass() { + let (mut matrix, mut node_exec) = setup_sfilter_matrix(); + + // High Pass @ 1000Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 2, 1000.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 100, 250, 500, 750, 1000, 1500, 2000, 3000, 8000, 12000 + ], &fft[..]), vec![ + (0, 0), (100, 4), (250, 4), (500, 8), (750, 8), (1000, 16), + (1500, 16), (2000, 16), (3000, 16), (8000, 16), + ]); + + // High Pass @ 4000Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 2, 4000.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 100, 250, 500, 750, 1000, 1500, 2000, 3000, 8000, 12000 + ], &fft[..]), vec![ + (0, 0), (100, 0), (250, 0), (500, 0), (750, 4), (1000, 4), + (1500, 4), (2000, 8), (3000, 12), (8000, 16), + ]); + + // High Pass @ 22050Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 2, 22050.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 500, 1000, 1500, 2000, 8000, 12000, 16000, 20000 + ], &fft[..]), vec![ + (0, 0), (500, 0), (1000, 0), + (1500, 0), (2000, 4), (8000, 12), (12000, 12), (16000, 16), + ]); + + // High Pass @ 0Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 2, 0.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 10, 50, 100, 250, 500, 750, 1000, 1500, 2000, 3000, 8000, 12000 + ], &fft[..]), vec![ + (0, 12), (10, 24), (50, 24), (100, 20), (250, 12), + (500, 16), (750, 20), (1000, 16), (1500, 16), (2000, 16), + (3000, 20), (8000, 16), + ]); +} + +#[test] +fn check_node_sfilter_highpass_tpt() { + let (mut matrix, mut node_exec) = setup_sfilter_matrix(); + + // High Pass TPT @ 1000Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 3, 1000.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 100, 250, 500, 750, 1000, 1500, 2000, 3000, 8000, 12000 + ], &fft[..]), vec![ + (0, 0), (100, 0), (250, 4), (500, 8), (750, 8), (1000, 16), + (1500, 16), (2000, 16), (3000, 16), (8000, 16), + ]); + + // High Pass TPT @ 4000Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 3, 4000.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 100, 250, 500, 750, 1000, 1500, 2000, 3000, 8000, 12000 + ], &fft[..]), vec![ + (0, 0), (100, 0), (250, 0), (500, 0), (750, 4), (1000, 4), + (1500, 4), (2000, 8), (3000, 12), (8000, 16), + ]); + + // High Pass TPT @ 22050Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 3, 22050.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 500, 1000, 1500, 2000, 8000, 12000, 16000, 20000 + ], &fft[..]), vec![ + (0, 0), (500, 0), (1000, 0), + (1500, 0), (2000, 0), (8000, 0), (12000, 0), (16000, 0), + ]); + + // High Pass TPT @ 0Hz + let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 3, 0.0, 0.0); + assert_eq!( + avg_fft_freqs(4.0, &[ + 10, 50, 100, 250, 500, 750, 1000, 1500, 2000, 3000, 8000, 12000 + ], &fft[..]), vec![ + (0, 12), (10, 24), (50, 24), (100, 20), (250, 12), + (500, 16), (750, 20), (1000, 16), (1500, 16), (2000, 16), + (3000, 20), (8000, 16), + ]); }