Implement the smoothing interval on Diopser
This commit is contained in:
parent
0bd0ea6af2
commit
f399b30dba
1 changed files with 48 additions and 20 deletions
|
@ -31,10 +31,10 @@ mod filter;
|
||||||
/// how many filters are actually active.
|
/// how many filters are actually active.
|
||||||
const MAX_NUM_FILTERS: usize = 512;
|
const MAX_NUM_FILTERS: usize = 512;
|
||||||
/// The minimum step size for smoothing the filter parmaeters.
|
/// The minimum step size for smoothing the filter parmaeters.
|
||||||
const MIN_AUTOMATION_STEP_SIZE: usize = 1;
|
const MIN_AUTOMATION_STEP_SIZE: u32 = 1;
|
||||||
/// The maximum step size for smoothing the filter parameters. Updating these parameters can be
|
/// The maximum step size for smoothing the filter parameters. Updating these parameters can be
|
||||||
/// expensive, so updating them in larger steps can be useful.
|
/// expensive, so updating them in larger steps can be useful.
|
||||||
const MAX_AUTOMATION_STEP_SIZE: usize = 512;
|
const MAX_AUTOMATION_STEP_SIZE: u32 = 512;
|
||||||
|
|
||||||
// An incomplete list of unported features includes:
|
// An incomplete list of unported features includes:
|
||||||
// - Actually add the parameters
|
// - Actually add the parameters
|
||||||
|
@ -54,6 +54,12 @@ struct Diopser {
|
||||||
/// updated. For the regular filter parameters we can look at the smoothers, but this is needed
|
/// updated. For the regular filter parameters we can look at the smoothers, but this is needed
|
||||||
/// when changing the number of active filters.
|
/// when changing the number of active filters.
|
||||||
should_update_filters: Arc<AtomicBool>,
|
should_update_filters: Arc<AtomicBool>,
|
||||||
|
/// If this is 1 and any of the filter parameters are still smoothing, thenn the filter
|
||||||
|
/// coefficients should be recalculated on the next sample. After that, this gets reset to
|
||||||
|
/// `unnormalize_automation_precision(self.params.automation_precision.value)`. This is to
|
||||||
|
/// reduce the DSP load of automation parameters. It can also cause some fun sounding glitchy
|
||||||
|
/// effects when the precision is low.
|
||||||
|
next_filter_smoothing_in: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Some combinations of parameters can cause really loud resonance. We should limit the
|
// TODO: Some combinations of parameters can cause really loud resonance. We should limit the
|
||||||
|
@ -94,6 +100,7 @@ impl Default for Diopser {
|
||||||
|
|
||||||
filters: Vec::new(),
|
filters: Vec::new(),
|
||||||
should_update_filters,
|
should_update_filters,
|
||||||
|
next_filter_smoothing_in: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,18 +206,14 @@ impl Plugin for Diopser {
|
||||||
buffer: &mut Buffer,
|
buffer: &mut Buffer,
|
||||||
_context: &mut impl ProcessContext,
|
_context: &mut impl ProcessContext,
|
||||||
) -> ProcessStatus {
|
) -> ProcessStatus {
|
||||||
for mut channel_samples in buffer.iter_mut() {
|
|
||||||
// Since this is an expensive operation, only update the filters when it's actually
|
// Since this is an expensive operation, only update the filters when it's actually
|
||||||
// necessary
|
// necessary, and allow smoothing only every n samples using the automation precision
|
||||||
let should_update_filters = self
|
// parameter
|
||||||
.should_update_filters
|
let smoothing_interval =
|
||||||
.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed)
|
unnormalize_automation_precision(self.params.automation_precision.value);
|
||||||
.is_ok()
|
|
||||||
|| self.params.filter_frequency.smoothed.is_smoothing()
|
for mut channel_samples in buffer.iter_mut() {
|
||||||
|| self.params.filter_resonance.smoothed.is_smoothing();
|
self.maybe_update_filters(smoothing_interval);
|
||||||
if should_update_filters {
|
|
||||||
self.update_filters();
|
|
||||||
}
|
|
||||||
|
|
||||||
// We get better cache locality by iterating over the filters and then over the channels
|
// We get better cache locality by iterating over the filters and then over the channels
|
||||||
for filter_idx in 0..self.params.filter_stages.value as usize {
|
for filter_idx in 0..self.params.filter_stages.value as usize {
|
||||||
|
@ -227,11 +230,37 @@ impl Plugin for Diopser {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Diopser {
|
impl Diopser {
|
||||||
fn update_filters(&mut self) {
|
/// Check if the filters need to be updated beased on [Self::should_update_filters] and the
|
||||||
|
/// smoothing interval, and update them as needed.
|
||||||
|
fn maybe_update_filters(&mut self, smoothing_interval: u32) {
|
||||||
|
let should_update_filters = self
|
||||||
|
.should_update_filters
|
||||||
|
.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed)
|
||||||
|
.is_ok()
|
||||||
|
|| ((self.params.filter_frequency.smoothed.is_smoothing()
|
||||||
|
|| self.params.filter_resonance.smoothed.is_smoothing())
|
||||||
|
&& self.next_filter_smoothing_in <= 1);
|
||||||
|
if should_update_filters {
|
||||||
|
self.update_filters(smoothing_interval);
|
||||||
|
self.next_filter_smoothing_in = smoothing_interval as i32;
|
||||||
|
} else {
|
||||||
|
self.next_filter_smoothing_in -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recompute the filter coefficients based on the smoothed paraetersm. We can skip forwardq in
|
||||||
|
/// larger steps to reduce the DSP load.
|
||||||
|
fn update_filters(&mut self, smoothing_interval: u32) {
|
||||||
let coefficients = filter::BiquadCoefficients::allpass(
|
let coefficients = filter::BiquadCoefficients::allpass(
|
||||||
self.sample_rate,
|
self.sample_rate,
|
||||||
self.params.filter_frequency.smoothed.next(),
|
self.params
|
||||||
self.params.filter_resonance.smoothed.next(),
|
.filter_frequency
|
||||||
|
.smoothed
|
||||||
|
.next_step(smoothing_interval),
|
||||||
|
self.params
|
||||||
|
.filter_resonance
|
||||||
|
.smoothed
|
||||||
|
.next_step(smoothing_interval),
|
||||||
);
|
);
|
||||||
for channel in self.filters.iter_mut() {
|
for channel in self.filters.iter_mut() {
|
||||||
for filter in channel
|
for filter in channel
|
||||||
|
@ -244,15 +273,14 @@ impl Diopser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize_automation_precision(step_size: usize) -> f32 {
|
fn normalize_automation_precision(step_size: u32) -> f32 {
|
||||||
(MAX_AUTOMATION_STEP_SIZE - step_size) as f32
|
(MAX_AUTOMATION_STEP_SIZE - step_size) as f32
|
||||||
/ (MAX_AUTOMATION_STEP_SIZE - MIN_AUTOMATION_STEP_SIZE) as f32
|
/ (MAX_AUTOMATION_STEP_SIZE - MIN_AUTOMATION_STEP_SIZE) as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unnormalize_automation_precision(normalized: f32) -> usize {
|
fn unnormalize_automation_precision(normalized: f32) -> u32 {
|
||||||
MAX_AUTOMATION_STEP_SIZE
|
MAX_AUTOMATION_STEP_SIZE
|
||||||
- (normalized * (MAX_AUTOMATION_STEP_SIZE - MIN_AUTOMATION_STEP_SIZE) as f32).round()
|
- (normalized * (MAX_AUTOMATION_STEP_SIZE - MIN_AUTOMATION_STEP_SIZE) as f32).round() as u32
|
||||||
as usize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vst3Plugin for Diopser {
|
impl Vst3Plugin for Diopser {
|
||||||
|
|
Loading…
Add table
Reference in a new issue