zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit 2714c5952da83932b5122d6dabe0e28964f84f26
parent 9b381cc5ce72caaffe4b73626e2304221ea235c4
Author: Jonathan Moore Liles <j.liles@unix.net>
Date:   Thu, 10 Dec 2020 19:38:46 -0800

SVFilter: Use Value_Smoothing_Filter to smooth cutoff frequency changes.

Also, add include guard to Value_Smoothing_Filter.
Also, cleanup some unused properties.

Diffstat:
Msrc/DSP/AnalogFilter.cpp | 13+------------
Msrc/DSP/AnalogFilter.h | 7-------
Msrc/DSP/SVFilter.cpp | 87+++++++++++++++++++++++--------------------------------------------------------
Msrc/DSP/SVFilter.h | 12+++++-------
Msrc/DSP/Value_Smoothing_Filter.cpp | 7+++++++
Msrc/DSP/Value_Smoothing_Filter.h | 10++++++++++
Msrc/Misc/Master.cpp | 6+++---
7 files changed, 51 insertions(+), 91 deletions(-)

diff --git a/src/DSP/AnalogFilter.cpp b/src/DSP/AnalogFilter.cpp @@ -32,18 +32,14 @@ AnalogFilter::AnalogFilter(unsigned char Ftype, stages(Fstages), freq(Ffreq), q(Fq), - gain(1.0), - abovenq(false), - oldabovenq(false) + gain(1.0) { for(int i = 0; i < 3; ++i) coeff.c[i] = coeff.d[i] = oldCoeff.c[i] = oldCoeff.d[i] = 0.0f; if(stages >= MAX_FILTER_STAGES) stages = MAX_FILTER_STAGES; cleanup(); - firsttime = false; setfreq_and_q(Ffreq, Fq); - firsttime = true; coeff.d[0] = 0; //this is not used outgain = 1.0f; freq_smoothing.sample_rate(samplerate_f); @@ -62,7 +58,6 @@ void AnalogFilter::cleanup() history[i].y2 = 0.0f; oldHistory[i] = history[i]; } - needsinterpolation = false; } AnalogFilter::Coeff AnalogFilter::computeCoeff(int type, float cutoff, float q, @@ -277,14 +272,8 @@ void AnalogFilter::setfreq(float frequency) if(rap < 1.0f) rap = 1.0f / rap; - oldabovenq = abovenq; - abovenq = frequency > (halfsamplerate_f - 500.0f); - - bool nyquistthresh = (abovenq ^ oldabovenq); - freq = frequency; computefiltercoefs(); - firsttime = false; } void AnalogFilter::setfreq_and_q(float frequency, float q_) diff --git a/src/DSP/AnalogFilter.h b/src/DSP/AnalogFilter.h @@ -74,13 +74,6 @@ class AnalogFilter:public Filter int order; //the order of the filter (number of poles) - bool needsinterpolation, //Interpolation between coeff changes - firsttime; //First Iteration of filter - bool abovenq, //if the frequency is above the nyquist - oldabovenq; //if the last time was above nyquist - //(used to see if it needs interpolation) - - Value_Smoothing_Filter freq_smoothing; /* for smoothing freq modulations to avoid zipper effect */ }; diff --git a/src/DSP/SVFilter.cpp b/src/DSP/SVFilter.cpp @@ -26,12 +26,6 @@ namespace zyn { -enum FilterInterpolationType { - INTERPOLATE_EXTREME = 0x01, - INTERPOLATE_NON_ZERO, - INTERPOLATE_NONE -}; - SVFilter::SVFilter(unsigned char Ftype, float Ffreq, float Fq, unsigned char Fstages, unsigned int srate, int bufsize) :Filter(srate, bufsize), @@ -39,15 +33,15 @@ SVFilter::SVFilter(unsigned char Ftype, float Ffreq, float Fq, stages(Fstages), freq(Ffreq), q(Fq), - gain(1.0f), - needsinterpolation(INTERPOLATE_NONE), - firsttime(true) + gain(1.0f) { if(stages >= MAX_FILTER_STAGES) stages = MAX_FILTER_STAGES; outgain = 1.0f; cleanup(); setfreq_and_q(Ffreq, Fq); + freq_smoothing.reset(Ffreq); + freq_smoothing.sample_rate(bufsize); } SVFilter::~SVFilter() @@ -57,8 +51,6 @@ void SVFilter::cleanup() { for(int i = 0; i < MAX_FILTER_STAGES + 1; ++i) st[i].low = st[i].high = st[i].band = st[i].notch = 0.0f; - oldabovenq = false; - abovenq = false; } SVFilter::response::response(float b0, float b1, float b2, @@ -127,26 +119,8 @@ void SVFilter::setfreq(float frequency) if(rap < 1.0f) rap = 1.0f / rap; - oldabovenq = abovenq; - abovenq = frequency > (samplerate_f / 2 - 500.0f); - - bool nyquistthresh = (abovenq ^ oldabovenq); - - //if the frequency is changed fast, it needs interpolation - if((rap > 3.0f) || nyquistthresh) { //(now, filter and coefficients backup) - if(!firsttime) - needsinterpolation = INTERPOLATE_EXTREME; - ipar = par; - } else if(rap != 1.0) { - if (!firsttime) - needsinterpolation = INTERPOLATE_NON_ZERO; - ipar = par; - } else { - needsinterpolation = INTERPOLATE_NONE; - } freq = frequency; computefiltercoefs(); - firsttime = false; } void SVFilter::setfreq_and_q(float frequency, float q_) @@ -204,21 +178,6 @@ float *SVFilter::getfilteroutfortype(SVFilter::fstage &x) { return out; } -void SVFilter::singlefilterout_with_par_interpolation(float *smp, fstage &x, parameters &par1, parameters &par2) -{ - float *out = getfilteroutfortype(x); - for(int i = 0; i < buffersize; ++i) { - float p = i / buffersize_f; - float f = par1.f + (par2.f - par1.f) * p; - float q = par1.q + (par2.q - par1.q) * p; - float q_sqrt = sqrtf(q); - x.low = x.low + f * x.band; - x.high = q_sqrt * smp[i] - x.low - q * x.band; - x.band = f * x.high + x.band; - x.notch = x.high + x.low; - smp[i] = *out; - } -} // simplifying the responses // xl = xl*z(-1) + pf*xb*z(-1) @@ -232,7 +191,7 @@ void SVFilter::singlefilterout_with_par_interpolation(float *smp, fstage &x, par -void SVFilter::singlefilterout(float *smp, SVFilter::fstage &x, SVFilter::parameters &par) +void SVFilter::singlefilterout(float *smp, SVFilter::fstage &x, SVFilter::parameters &par, int buffersize ) { float *out = getfilteroutfortype(x); for(int i = 0; i < buffersize; ++i) { @@ -246,24 +205,28 @@ void SVFilter::singlefilterout(float *smp, SVFilter::fstage &x, SVFilter::parame void SVFilter::filterout(float *smp) { - if (needsinterpolation == INTERPOLATE_EXTREME) { - float ismp[buffersize]; - for(int i = 0; i < stages + 1; ++i) - singlefilterout(smp, st[i], par); - memcpy(ismp, smp, bufferbytes); - for(int i = 0; i < stages + 1; ++i) - singlefilterout(ismp, st[i], ipar); - for(int i = 0; i < buffersize; ++i) { - float x = i / buffersize_f; - smp[i] = ismp[i] * (1.0f - x) + smp[i] * x; - } - } else if (needsinterpolation == INTERPOLATE_NON_ZERO) { - for(int i = 0; i < stages + 1; ++i) - singlefilterout_with_par_interpolation(smp, st[i], ipar, par); - } else { - for(int i = 0; i < stages + 1; ++i) - singlefilterout(smp, st[i], par); + assert((buffersize % 8) == 0); + + float freqbuf[buffersize]; + + if ( freq_smoothing.apply( freqbuf, buffersize, freq ) ) + { + /* 8 sample chunks seems to work OK for AnalogFilter, so do that here too. */ + for ( int i = 0; i < buffersize; i += 8 ) + { + freq = freqbuf[i]; + computefiltercoefs(); + + for(int j = 0; j < stages + 1; ++j) + singlefilterout(smp + i, st[j], par, 8 ); + } + + freq = freqbuf[buffersize - 1]; + computefiltercoefs(); } + else + for(int i = 0; i < stages + 1; ++i) + singlefilterout(smp, st[i], par, buffersize ); for(int i = 0; i < buffersize; ++i) smp[i] *= outgain; diff --git a/src/DSP/SVFilter.h b/src/DSP/SVFilter.h @@ -16,6 +16,7 @@ #include "../globals.h" #include "Filter.h" +#include "Value_Smoothing_Filter.h" namespace zyn { @@ -57,19 +58,16 @@ class SVFilter:public Filter } par, ipar; float *getfilteroutfortype(SVFilter::fstage &x); - void singlefilterout(float *smp, fstage &x, parameters &par); - void singlefilterout_with_par_interpolation(float *smp, fstage &x, parameters &par1, parameters &par2); - void computefiltercoefs(void); + void singlefilterout(float *smp, fstage &x, parameters &par, int buffersize ); + + void computefiltercoefs(void); int type; // The type of the filter (LPF1,HPF1,LPF2,HPF2...) int stages; // how many times the filter is applied (0->1,1->2,etc.) float freq; // Frequency given in Hz float q; // Q factor (resonance or Q factor) float gain; // the gain of the filter (if are shelf/peak) filters - bool abovenq, //if the frequency is above the nyquist - oldabovenq; - int needsinterpolation; - bool firsttime; + Value_Smoothing_Filter freq_smoothing; }; } diff --git a/src/DSP/Value_Smoothing_Filter.cpp b/src/DSP/Value_Smoothing_Filter.cpp @@ -32,6 +32,13 @@ Value_Smoothing_Filter::sample_rate ( nframes_t n ) bool Value_Smoothing_Filter::apply( sample_t * __restrict__ dst, nframes_t nframes, float gt ) { + if ( _reset_on_next_apply ) + { + reset( gt ); + _reset_on_next_apply = false; + return false; + } + if ( target_reached(gt) ) return false; diff --git a/src/DSP/Value_Smoothing_Filter.h b/src/DSP/Value_Smoothing_Filter.h @@ -17,6 +17,9 @@ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*******************************************************************************/ +#ifndef VALUE_SMOOTHING_FILTER_H +#define VALUE_SMOOTHING_FILTER_H + typedef unsigned long nframes_t; typedef float sample_t; @@ -26,14 +29,20 @@ class Value_Smoothing_Filter float _cutoff; + bool _reset_on_next_apply; + public: Value_Smoothing_Filter ( ) { g1 = g2 = 0; _cutoff = 10.0f; + _reset_on_next_apply = false; } + + void reset_on_next_apply ( bool v ) { _reset_on_next_apply = v; } + void cutoff ( float v ) { _cutoff = v; } void reset ( float v ) { g2 = g1 = v; } @@ -45,3 +54,4 @@ public: bool apply( sample_t * __restrict__ dst, nframes_t nframes, float gt ); }; +#endif diff --git a/src/Misc/Master.cpp b/src/Misc/Master.cpp @@ -785,13 +785,13 @@ Master::Master(const SYNTH_T &synth_, Config* config) config->cfg.Interpolation, &microtonal, fft, &watcher, (ss+"/part"+npart+"/").c_str); smoothing_part_l[npart].sample_rate( synth.samplerate ); - smoothing_part_l[npart].reset( 1.0f ); + smoothing_part_l[npart].reset_on_next_apply( true ); /* necessary to make CI tests happy, otherwise of no practical use */ smoothing_part_r[npart].sample_rate( synth.samplerate ); - smoothing_part_r[npart].reset( 0.992126f ); /* curious default, must be a bug in panning formula. */ + smoothing_part_r[npart].reset_on_next_apply( true ); /* necessary to make CI tests happy, otherwise of no practical use */ } smoothing.sample_rate( synth.samplerate ); - smoothing.reset( 0.464159f ); /* master volume has this odd default for some reason, just go with it. */ + smoothing.reset_on_next_apply( true ); /* necessary to make CI tests happy, otherwise of no practical use */ //Insertion Effects init for(int nefx = 0; nefx < NUM_INS_EFX; ++nefx)