implemented moog filter and started to test it
This commit is contained in:
parent
1a5dfb4b50
commit
c4495d6039
4 changed files with 156 additions and 3 deletions
|
@ -918,7 +918,7 @@ const FILTER_OVERSAMPLE_HAL_CHAMBERLIN : usize = 2;
|
|||
// https://www.earlevel.com/main/2003/03/02/the-digital-state-variable-filter/
|
||||
// Inspired by SynthV1 by Rui Nuno Capela, under the terms of
|
||||
// GPLv2 or any later:
|
||||
/// Process a HAL Chamberlin filter with two delays/state variables.
|
||||
/// Process a HAL Chamberlin filter with two delays/state variables that is 12dB.
|
||||
/// The filter does internal oversampling with very simple decimation to
|
||||
/// rise the stability for cutoff frequency up to 16kHz.
|
||||
///
|
||||
|
@ -974,7 +974,7 @@ pub fn process_hal_chamberlin_svf(
|
|||
(high, notch)
|
||||
}
|
||||
|
||||
/// This function processes a Simper SVF. It's a much newer algorithm
|
||||
/// This function processes a Simper SVF with 12dB. It's a much newer algorithm
|
||||
/// for filtering and provides easy to calculate multiple outputs.
|
||||
///
|
||||
/// * `input` - Input sample.
|
||||
|
@ -1042,6 +1042,44 @@ pub fn process_simper_svf(
|
|||
(v2, v1, input - k * v1 - v2)
|
||||
}
|
||||
|
||||
/// This function implements a simple Stilson/Moog filter with 24dB.
|
||||
/// It provides multiple outputs for low, high and band pass and a notch
|
||||
/// output.
|
||||
///
|
||||
// Stilson/Moog implementation partly translated from SynthV1 by rncbc
|
||||
// https://github.com/rncbc/synthv1/blob/master/src/synthv1_filter.h#L103
|
||||
// under GPLv2 or any later.
|
||||
#[inline]
|
||||
pub fn process_stilson_moog(
|
||||
input: f32, freq: f32, res: f32, israte: f32,
|
||||
b0: &mut f32, b1: &mut f32, b2: &mut f32, b3: &mut f32, b4: &mut f32
|
||||
) -> (f32, f32, f32, f32) {
|
||||
// let cutoff = freq * israte;
|
||||
// let cutoff = (std::f32::consts::PI * freq * israte).tan();
|
||||
let cutoff = 2.0 * freq * israte;
|
||||
|
||||
let c = 1.0 - cutoff;
|
||||
let p = cutoff + 0.8 * cutoff * c;
|
||||
let f = p + p - 1.0;
|
||||
// let f = 2.0 * cutoff.sin() - 1.0;
|
||||
let q = res * (1.0 + 0.5 * c * (1.0 - c + 5.6 * c * c));
|
||||
|
||||
let inp = input - q * *b4;
|
||||
let t1 = *b1; *b1 = (inp + *b0) * p - *b1 * f;
|
||||
let t2 = *b2; *b2 = (*b1 + t1) * p - *b2 * f;
|
||||
let t1 = *b3; *b3 = (*b2 + t2) * p - *b3 * f;
|
||||
*b4 = (*b3 + t1) * p - *b4 * f;
|
||||
|
||||
*b4 = *b4 - *b4 * *b4 * *b4 * 0.166667; // clipping
|
||||
|
||||
*b0 = inp;
|
||||
|
||||
let band = 3.0 * (*b3 - *b4);
|
||||
|
||||
// low, band, high, notch
|
||||
(*b4, band, inp - *b4, band - inp)
|
||||
}
|
||||
|
||||
// translated from Odin 2 Synthesizer Plugin
|
||||
// Copyright (C) 2020 TheWaveWarden
|
||||
// under GPLv3 or any later
|
||||
|
|
|
@ -583,7 +583,7 @@ macro_rules! node_list {
|
|||
(0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
||||
(1 freq n_pit d_pit r_fq f_freq stp_d -1.0, 0.5647131, 1000.0)
|
||||
(2 res n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5)
|
||||
{3 0 ftype setting(8) fa_sfilter_type 0 12}
|
||||
{3 0 ftype setting(8) fa_sfilter_type 0 16}
|
||||
[0 sig],
|
||||
test => Test UIType::Generic UICategory::IOUtil
|
||||
(0 f n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5)
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::dsp::helpers::{
|
|||
process_1pole_tpt_highpass,
|
||||
process_hal_chamberlin_svf,
|
||||
process_simper_svf,
|
||||
process_stilson_moog,
|
||||
};
|
||||
|
||||
#[macro_export]
|
||||
|
@ -30,6 +31,10 @@ macro_rules! fa_sfilter_type { ($formatter: expr, $v: expr, $denorm_v: expr) =>
|
|||
10 => "BP 12s",
|
||||
11 => "NO 12s",
|
||||
12 => "PK 12s",
|
||||
13 => "LP 24m",
|
||||
14 => "HP 24m",
|
||||
15 => "BP 24m",
|
||||
16 => "NO 24m",
|
||||
_ => "?",
|
||||
};
|
||||
write!($formatter, "{}", s)
|
||||
|
@ -43,6 +48,7 @@ pub struct SFilter {
|
|||
y: f32,
|
||||
k: f32,
|
||||
h: f32,
|
||||
m: f32,
|
||||
otype: i8,
|
||||
}
|
||||
|
||||
|
@ -54,6 +60,7 @@ impl SFilter {
|
|||
y: 0.0,
|
||||
k: 0.0,
|
||||
h: 0.0,
|
||||
m: 0.0,
|
||||
otype: -1,
|
||||
}
|
||||
}
|
||||
|
@ -219,6 +226,7 @@ impl DspNode for SFilter {
|
|||
self.y = 0.0;
|
||||
self.k = 0.0;
|
||||
self.h = 0.0;
|
||||
self.m = 0.0;
|
||||
self.otype = -1;
|
||||
}
|
||||
|
||||
|
@ -244,6 +252,7 @@ impl DspNode for SFilter {
|
|||
self.z = 0.0;
|
||||
self.k = 0.0;
|
||||
self.h = 0.0;
|
||||
self.m = 0.0;
|
||||
self.otype = ftype;
|
||||
}
|
||||
|
||||
|
@ -366,6 +375,18 @@ impl DspNode for SFilter {
|
|||
low - high
|
||||
});
|
||||
},
|
||||
13 => { // Stilson/Moog Low Pass
|
||||
process_filter_fun32!(
|
||||
ctx.nframes(), inp, out, freq, res, 1.0, input, 22000.0, {
|
||||
let (low, _band, _high, _notch) =
|
||||
process_stilson_moog(
|
||||
input, freq, res, self.israte,
|
||||
&mut self.z, &mut self.y, &mut self.k,
|
||||
&mut self.h, &mut self.m);
|
||||
|
||||
low
|
||||
});
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
|
|
@ -995,3 +995,97 @@ fn check_node_sfilter_simpersvf_peak() {
|
|||
(0, 164), (10, 40), (100, 16), (1000, 20), (4000, 16), (12000, 20)
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_node_sfilter_moog_lowpass() {
|
||||
let (mut matrix, mut node_exec) = setup_sfilter_matrix();
|
||||
|
||||
// Low Pass Stilson/Moog @ 1000Hz RES=1.0
|
||||
let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 13, 1000.0, 1.0);
|
||||
assert_eq!(
|
||||
avg_fft_freqs(4.0, &[
|
||||
100, 200, 400, 500, 700, 900, 1000, 1100, 1200,
|
||||
1500, 2000, 3000, 4000, 6000, 12000
|
||||
], &fft[..]), vec![
|
||||
(0, 0), (100, 4), (200, 4), (400, 0), (500, 4), (700, 4),
|
||||
(900, 12), (1000, 36), (1100, 80), (1200, 8),
|
||||
(1500, 0), (2000, 0), (3000, 0), (4000, 0), (6000, 0)
|
||||
]);
|
||||
|
||||
// Low Pass Stilson/Moog @ 1000Hz RES=0.5
|
||||
let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 13, 1000.0, 0.5);
|
||||
assert_eq!(
|
||||
avg_fft_freqs(4.0, &[
|
||||
100, 200, 400, 500, 700, 900, 1000, 1100, 1200,
|
||||
1500, 2000, 3000, 4000, 6000, 12000
|
||||
], &fft[..]), vec![
|
||||
(0, 4), (100, 4), (200, 4), (400, 4), (500, 8),
|
||||
(700, 12), (900, 16), (1000, 12), (1100, 8), (1200, 4),
|
||||
(1500, 0), (2000, 0), (3000, 0), (4000, 0), (6000, 0)
|
||||
]);
|
||||
|
||||
// Low Pass Stilson/Moog @ 1000Hz RES=0.0
|
||||
let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 13, 1000.0, 0.0);
|
||||
assert_eq!(
|
||||
avg_fft_freqs(4.0, &[
|
||||
100, 200, 400, 500, 700, 900, 1000, 1100, 1200,
|
||||
1500, 2000, 3000, 4000, 6000, 12000
|
||||
], &fft[..]), vec![
|
||||
(0, 16), (100, 16), (200, 16), (400, 4), (500, 12), (700, 8),
|
||||
(900, 4), (1000, 4), (1100, 0), (1200, 0), (1500, 0), (2000, 0),
|
||||
(3000, 0), (4000, 0), (6000, 0)
|
||||
]);
|
||||
|
||||
// Low Pass Stilson/Moog @ 4000Hz RES=1.0
|
||||
let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 13, 4000.0, 1.0);
|
||||
assert_eq!(
|
||||
avg_fft_freqs(4.0, &[
|
||||
100, 500, 1000, 2000, 3500, 4000, 5000, 6000, 8000, 12000
|
||||
], &fft[..]), vec![
|
||||
(0, 24), (100, 16), (500, 20), (1000, 20), (2000, 36), (3500, 332),
|
||||
(4000, 164), (5000, 20), (6000, 8), (8000, 0)
|
||||
]);
|
||||
|
||||
// Low Pass Stilson/Moog @ 4000Hz RES=0.0
|
||||
let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 13, 4000.0, 0.0);
|
||||
assert_eq!(
|
||||
avg_fft_freqs(4.0, &[
|
||||
100, 500, 1000, 2000, 3500, 4000, 5000, 6000, 8000, 12000
|
||||
], &fft[..]), vec![
|
||||
(0, 20), (100, 12), (500, 16), (1000, 16), (2000, 12), (3500, 8),
|
||||
(4000, 8), (5000, 4), (6000, 4), (8000, 0)
|
||||
]);
|
||||
|
||||
// Low Pass Stilson/Moog @ 22050Hz RES=0.0
|
||||
let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 13, 22050.0, 0.0);
|
||||
assert_eq!(
|
||||
avg_fft_freqs(8.0, &[100, 1000, 4000, 12000, 16000, 20000, 22050, 22051], &fft[..]), vec![
|
||||
(0, 16), (100, 16), (1000, 16), (4000, 16), (12000, 16),
|
||||
(16000, 16), (20000, 16), (22050, 0)
|
||||
]);
|
||||
|
||||
// Low Pass Stilson/Moog @ 22050Hz RES=1.0
|
||||
let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 13, 22050.0, 1.0);
|
||||
assert_eq!(
|
||||
avg_fft_freqs(8.0, &[100, 1000, 4000, 12000, 16000, 20000, 22050, 22051], &fft[..]), vec![
|
||||
(0, 8), (100, 16), (1000, 16), (4000, 16), (12000, 16),
|
||||
(16000, 16), (20000, 16), (22050, 0)
|
||||
]);
|
||||
|
||||
// Low Pass Stilson/Moog @ 0Hz RES=0.0
|
||||
let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 13, 0.0, 0.0);
|
||||
assert_eq!(
|
||||
avg_fft_freqs(4.0, &[10, 100, 1000, 4000, 12000, 22050, 22051], &fft[..]), vec![
|
||||
(0, 0), (10, 0), (100, 0), (1000, 0), (4000, 0), (12000, 0),
|
||||
(22050, 0)
|
||||
]);
|
||||
|
||||
// Low Pass Stilson/Moog @ 0Hz RES=1.0
|
||||
let fft = fft_with_freq_res_type(&mut matrix, &mut node_exec, 13, 0.0, 1.0);
|
||||
assert_eq!(
|
||||
avg_fft_freqs(4.0, &[1, 5, 10, 100, 1000, 4000, 12000, 22050, 22051], &fft[..]), vec![
|
||||
(0, 68), (1, 0), (5, 0), (10, 4), (100, 0), (1000, 0),
|
||||
(4000, 0), (12000, 0), (22050, 0)
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue