zynaddsubfx

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

commit b8473570fdc3cb1f9234bd5bca2132e691ca6c0f
parent 2c8937521bf8566e04b9e31e2d54a049c9722f7d
Author: friedolino78 <[email protected]>
Date:   Wed,  1 Sep 2021 14:09:48 +0200

add comb filter (#126)

* add comb filter

Co-authored-by: Friedolino <[email protected]>
Diffstat:
Msrc/DSP/CMakeLists.txt | 1+
Asrc/DSP/CombFilter.cpp | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/DSP/CombFilter.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/DSP/Filter.cpp | 5+++++
Msrc/Params/FilterParams.cpp | 5++++-
Msrc/Params/FilterParams.h | 2+-
Msrc/Synth/ModFilter.cpp | 10++++++++++
Msrc/Synth/ModFilter.h | 1+
Msrc/globals.h | 1+
9 files changed, 198 insertions(+), 2 deletions(-)

diff --git a/src/DSP/CMakeLists.txt b/src/DSP/CMakeLists.txt @@ -5,6 +5,7 @@ set(zynaddsubfx_dsp_SRCS DSP/FormantFilter.cpp DSP/SVFilter.cpp DSP/MoogFilter.cpp + DSP/CombFilter.cpp DSP/Unison.cpp DSP/Value_Smoothing_Filter.cpp PARENT_SCOPE diff --git a/src/DSP/CombFilter.cpp b/src/DSP/CombFilter.cpp @@ -0,0 +1,119 @@ +#include <cassert> +#include <cstdio> +#include <cmath> +#include <stdio.h> + +#include "../Misc/Allocator.h" +#include "../Misc/Util.h" +#include "CombFilter.h" + +// theory from `Introduction to Digital Filters with Audio Applications'', by Julius O. Smith III, (September 2007 Edition). +// https://www.dsprelated.com/freebooks/filters/Analysis_Digital_Comb_Filter.html + +namespace zyn{ + +CombFilter::CombFilter(Allocator *alloc, unsigned char Ftype, float Ffreq, float Fq, + unsigned int srate, int bufsize) + :Filter(srate, bufsize), gain(1.0f), type(Ftype), memory(*alloc) +{ + //worst case: looking back from smps[0] at 25Hz using higher order interpolation + mem_size = (int)ceilf((float)samplerate/25.0) + buffersize + 2; // 2178 at 48000Hz and 256Samples + input = (float*)memory.alloc_mem(mem_size*sizeof(float)); + output = (float*)memory.alloc_mem(mem_size*sizeof(float)); + memset(input, 0, mem_size*sizeof(float)); + memset(output, 0, mem_size*sizeof(float)); + + setfreq_and_q(Ffreq, Fq); + settype(type); +} + +CombFilter::~CombFilter(void) +{ + memory.dealloc(input); + memory.dealloc(output); +} + + +inline float CombFilter::tanhX(const float x) +{ + // Pade approximation of tanh(x) bound to [-1 .. +1] + // https://mathr.co.uk/blog/2017-09-06_approximating_hyperbolic_tangent.html + float x2 = x*x; + return (x*(105.0f+10.0f*x2)/(105.0f+(45.0f+x2)*x2)); // +} + +inline float CombFilter::sampleLerp(float *smp, float pos) { + int poshi = (int)pos; // integer part (pos >= 0) + float poslo = pos - (float) poshi; // decimal part + // linear interpolation between samples + return smp[poshi] + poslo * (smp[poshi+1]-smp[poshi]); +} + +void CombFilter::filterout(float *smp) +{ + // shift the buffer content to the left + memmove(&input[0], &input[buffersize], (mem_size-buffersize)*sizeof(float)); + // copy new input samples to the right end of the buffer + memcpy(&input[mem_size-buffersize], smp, buffersize*sizeof(float)); + for (int i = 0; i < buffersize; i ++) + { + // calculate the feedback sample positions in the buffer + float pos = float(mem_size-buffersize+i)-delay; + // add the fwd and bwd feedback samples to current sample + smp[i] = smp[i]*gain + tanhX( + gainfwd * sampleLerp( input, pos) - + gainbwd * sampleLerp(output, pos)); + // copy new sample to output buffer + output[mem_size-buffersize+i] = smp[i]; + // apply output gain + smp[i] *= outgain; + } + // shift the buffer content one buffersize to the left + memmove(&output[0], &output[buffersize], (mem_size-buffersize)*sizeof(float)); +} + +void CombFilter::setfreq_and_q(float freq, float q) +{ + setfreq(freq); + setq(q); +} + +void CombFilter::setfreq(float freq) +{ + float ff = limit(freq, 25.0f, 40000.0f); + delay = ((float)samplerate)/ff; +} + +void CombFilter::setq(float q_) +{ + q = cbrtf(0.0015f*q_); + settype(type); +} + +void CombFilter::setgain(float dBgain) +{ + gain = dB2rap(dBgain); +} + +void CombFilter::settype(unsigned char type_) +{ + type = type_; + switch (type) + { + case 0: + default: + gainfwd = 0.0f; + gainbwd = q; + break; + case 1: + gainfwd = q; + gainbwd = 0.0f; + break; + case 2: + gainfwd = q; + gainbwd = q; + break; + } +} + +}; diff --git a/src/DSP/CombFilter.h b/src/DSP/CombFilter.h @@ -0,0 +1,56 @@ +/* + ZynAddSubFX - a software synthesizer + + CombFilter.h - Several analog filters + Copyright (C) 2021-2021 Michael Kirchner + Author: Michael Kirchner + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. +*/ + +#pragma once +#include "Filter.h" +#include "Value_Smoothing_Filter.h" + +namespace zyn { + +class CombFilter:public Filter +{ + public: + //! @param Fq resonance, range [0.1,1000], logscale + CombFilter(Allocator *alloc, unsigned char Ftype, float Ffreq, float Fq, + unsigned int srate, int bufsize); + ~CombFilter() override; + void filterout(float *smp) override; + void setfreq(float freq) override; + void setfreq_and_q(float freq, float q_) override; + void setq(float q) override; + void setgain(float dBgain) override; + void settype(unsigned char type); + + private: + + float* input; + float* output; + float gain; + float q; + unsigned char type; + + float step(float x); + + float tanhX(const float x); + float sampleLerp(float *smp, float pos); + + float gainfwd; + float gainbwd; + float delay; + + Allocator &memory; + int mem_size; + +}; + +} diff --git a/src/DSP/Filter.cpp b/src/DSP/Filter.cpp @@ -20,6 +20,7 @@ #include "FormantFilter.h" #include "SVFilter.h" #include "MoogFilter.h" +#include "CombFilter.h" #include "../Params/FilterParams.h" #include "../Misc/Allocator.h" @@ -57,6 +58,10 @@ Filter *Filter::generate(Allocator &memory, const FilterParams *pars, filter = memory.alloc<MoogFilter>(Ftype, 1000.0f, pars->getq(), srate, bufsize); filter->setgain(pars->getgain()); break; + case 4: + filter = memory.alloc<CombFilter>(&memory, Ftype, 1000.0f, pars->getq(), srate, bufsize); + filter->outgain = dB2rap(pars->getgain()); + break; default: filter = memory.alloc<AnalogFilter>(Ftype, 1000.0f, pars->getq(), Fstages, srate, bufsize); if((Ftype >= 6) && (Ftype <= 8)) diff --git a/src/Params/FilterParams.cpp b/src/Params/FilterParams.cpp @@ -69,7 +69,7 @@ const rtosc::Ports FilterParams::ports = { rOptions(ad_global_filter, ad_voice_filter, sub_filter, in_effect), "location of the filter"), rOption(Pcategory, rShort("class"), - rOptions(analog, formant, st.var., moog), rDefault(analog), + rOptions(analog, formant, st.var., moog, comb), rDefault(analog), "Class of filter"), rOption(Ptype, rShort("type"), rOptions(LP1, HP1, LP2, HP2, BP, notch, peak, l.shelf, h.shelf), @@ -134,6 +134,9 @@ const rtosc::Ports FilterParams::ports = { {"type-moog::i", rProp(parameter) rShort("type") rOptions(HP, BP, LP) rDoc("Filter Type"), 0, rOptionCb(Ptype)}, + {"type-comb::i", rProp(parameter) rShort("type") + rOptions(BWD, FWD, both) + rDoc("Comb Filter Type"), 0, rOptionCb(Ptype)}, //UI reader {"Pvowels:", rDoc("Get Formant Vowels"), NULL, [](const char *, RtData &d) { diff --git a/src/Params/FilterParams.h b/src/Params/FilterParams.h @@ -52,7 +52,7 @@ class FilterParams:public PresetsArray float getfreqtracking(float notefreq) const ; float getgain() const ; - unsigned Pcategory:2; //!< Filter category (Analog/Formant/StVar) + unsigned Pcategory:4; //!< Filter category (Analog/Formant/StVar/Moog/Comb) unsigned Ptype:8; //!< Filter type (for analog lpf,hpf,bpf..) unsigned Pstages:8; //!< filter stages+1 float basefreq; //!< Base cutoff frequency (Hz) diff --git a/src/Synth/ModFilter.cpp b/src/Synth/ModFilter.cpp @@ -20,6 +20,7 @@ #include "../DSP/AnalogFilter.h" #include "../DSP/FormantFilter.h" #include "../DSP/MoogFilter.h" +#include "../DSP/CombFilter.h" #include <cassert> namespace zyn { @@ -127,6 +128,8 @@ static int current_category(Filter *f) return 2; else if(dynamic_cast<MoogFilter*>(f)) return 3; + else if(dynamic_cast<CombFilter*>(f)) + return 4; assert(false); return -1; @@ -151,6 +154,8 @@ void ModFilter::paramUpdate(Filter *&f) anParamUpdate(*an); else if(auto *mg = dynamic_cast<MoogFilter*>(f)) mgParamUpdate(*mg); + else if(auto *cb = dynamic_cast<CombFilter*>(f)) + cbParamUpdate(*cb); } void ModFilter::svParamUpdate(SVFilter &sv) @@ -172,4 +177,9 @@ void ModFilter::mgParamUpdate(MoogFilter &mg) mg.setgain(pars.getgain()); } +void ModFilter::cbParamUpdate(CombFilter &cb) +{ + cb.settype(pars.Ptype); + cb.setgain(pars.getgain()); +} } diff --git a/src/Synth/ModFilter.h b/src/Synth/ModFilter.h @@ -46,6 +46,7 @@ class ModFilter void svParamUpdate(SVFilter &sv); void anParamUpdate(AnalogFilter &an); void mgParamUpdate(MoogFilter &mg); + void cbParamUpdate(CombFilter &cb); const FilterParams &pars; //Parameters to Pull Updates From diff --git a/src/globals.h b/src/globals.h @@ -68,6 +68,7 @@ class Part; class Filter; class AnalogFilter; class MoogFilter; +class CombFilter; class SVFilter; class FormantFilter; class ModFilter;