implemented moog filter and started to test it

This commit is contained in:
Weird Constructor 2021-07-18 08:18:32 +02:00
parent 1a5dfb4b50
commit c4495d6039
4 changed files with 156 additions and 3 deletions

View file

@ -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

View file

@ -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)

View file

@ -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
});
},
_ => {},
}

View file

@ -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)
]);
}