zynaddsubfx

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

commit a03672f4b6901b11f7335414e131377f4b94bbfd
parent ab88ac8c8073bd0c23beffaab62a012a6ec816d7
Author: Johannes Lorenz <[email protected]>
Date:   Tue, 24 Dec 2019 22:39:12 +0100

Merge branch 'master' of github.com:zynaddsubfx/zynaddsubfx

Diffstat:
Msrc/Effects/Distorsion.cpp | 29++++++++++++++++++++++-------
Msrc/Effects/Distorsion.h | 2++
Msrc/Misc/WaveShapeSmps.cpp | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/Misc/WaveShapeSmps.h | 8+++++++-
Msrc/Synth/OscilGen.cpp | 2+-
Msrc/UI/EffUI.fl | 54+++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/UI/OscilGenUI.fl | 12++++++++++++
7 files changed, 232 insertions(+), 30 deletions(-)

diff --git a/src/Effects/Distorsion.cpp b/src/Effects/Distorsion.cpp @@ -48,7 +48,8 @@ rtosc::Ports Distorsion::ports = { rEffParOpt(Ptype, 5, rShort("type"), rOptions(Arctangent, Asymmetric, Pow, Sine, Quantisize, Zigzag, Limiter, Upper Limiter, Lower Limiter, - Inverse Limiter, Clip, Asym2, Pow2, sigmoid), + Inverse Limiter, Clip, Asym2, Pow2, Sigmoid, Tanh, + Cubic, Square), rPresets(Arctangent, Asymmetric, Zigzag, Asymmetric, Pow, Quantisize), "Distortion Shape"), @@ -61,6 +62,10 @@ rtosc::Ports Distorsion::ports = { rPresets(false, false, true, true, false, true), "Stereo"), rEffParTF(Pprefiltering, 10, rShort("p.filt"), rDefault(false), "Filtering before/after non-linearity"), + rEffPar(Pfuncpar, 11, rShort("shape"), rDefault(32), + rLinear(0, 127), "Shape of the wave shaping function"), + rEffPar(Poffset, 12, rShort("offset"), rDefault(64), + rLinear(0, 127), "Input DC Offset"), {"waveform:", 0, 0, [](const char *, rtosc::RtData &d) { Distorsion &dd = *(Distorsion*)d.obj; @@ -72,7 +77,7 @@ rtosc::Ports Distorsion::ports = { buffer[i] = 2*(i/128.0)-1; waveShapeSmps(sizeof(buffer)/sizeof(buffer[0]), buffer, - dd.Ptype + 1, dd.Pdrive); + dd.Ptype + 1, dd.Pdrive, dd.Poffset, dd.Pfuncpar); for(int i=0; i<128; ++i) { arg_str[i] = 'f'; @@ -96,7 +101,9 @@ Distorsion::Distorsion(EffectParams pars) Plpf(127), Phpf(0), Pstereo(0), - Pprefiltering(0) + Pprefiltering(0), + Pfuncpar(32), + Poffset(64) { lpfl = memory.alloc<AnalogFilter>(2, 22000, 1, 0, pars.srate, pars.bufsize); lpfr = memory.alloc<AnalogFilter>(2, 22000, 1, 0, pars.srate, pars.bufsize); @@ -155,9 +162,9 @@ void Distorsion::out(const Stereo<float *> &smp) if(Pprefiltering) applyfilters(efxoutl, efxoutr); - waveShapeSmps(buffersize, efxoutl, Ptype + 1, Pdrive); + waveShapeSmps(buffersize, efxoutl, Ptype + 1, Pdrive, Poffset, Pfuncpar); if(Pstereo) - waveShapeSmps(buffersize, efxoutr, Ptype + 1, Pdrive); + waveShapeSmps(buffersize, efxoutr, Ptype + 1, Pdrive, Poffset, Pfuncpar); if(!Pprefiltering) applyfilters(efxoutl, efxoutr); @@ -261,8 +268,8 @@ void Distorsion::changepar(int npar, unsigned char value) Plevel = value; break; case 5: - if(value > 13) - Ptype = 13; //this must be increased if more distorsion types are added + if(value > 16) + Ptype = 16; //this must be increased if more distorsion types are added else Ptype = value; break; @@ -284,6 +291,12 @@ void Distorsion::changepar(int npar, unsigned char value) case 10: Pprefiltering = value; break; + case 11: + Pfuncpar = value; + break; + case 12: + Poffset = value; + break; } } @@ -301,6 +314,8 @@ unsigned char Distorsion::getpar(int npar) const case 8: return Phpf; case 9: return Pstereo; case 10: return Pprefiltering; + case 11: return Pfuncpar; + case 12: return Poffset; default: return 0; //in case of bogus parameter number } } diff --git a/src/Effects/Distorsion.h b/src/Effects/Distorsion.h @@ -43,6 +43,8 @@ class Distorsion:public Effect unsigned char Phpf; //highpass filter unsigned char Pstereo; //0=mono, 1=stereo unsigned char Pprefiltering; //if you want to do the filtering before the distorsion + unsigned char Pfuncpar; //for parametric functions + unsigned char Poffset; //the input offset void setvolume(unsigned char _Pvolume); void setlpf(unsigned char _Plpf); diff --git a/src/Misc/WaveShapeSmps.cpp b/src/Misc/WaveShapeSmps.cpp @@ -16,20 +16,75 @@ namespace zyn { +float polyblampres(float smp, float ws, float dMax) +{ + // Formula from: Esqueda, Välimäki, Bilbao (2015): ALIASING REDUCTION IN SOFT-CLIPPING ALGORITHMS + // http://dafx16.vutbr.cz/dafxpapers/18-DAFx-16_paper_33-PN.pdf pg 123, table 1 + // Four-point polyBLAMP residual: + // [−2T, T] d^5/120 + // [−T, 0] −d^5/40 + d^4/24 + d^3/12 + d^2/12 + d/24 + 1/120 + // [0, T] d^5/40 − d^4/12 + d^2/3 − d/2 + 7/30 + // [T, 2T] −d^5/120 + d^4/24 − d^3/12 + d^2/12 − d/24 + 1/120 + + float dist = fabs(smp) - ws; + float res, d1, d2, d3, d4, d5; + if (fabs(dist) < dMax) { + if (dist < -dMax/2.0f) { + d1 = (dist + dMax)/dMax*2; // [-dMax ... -dMax/2] -> [0 ... 1] + res = powf(d1, 5.0f) / 120.0f; + } + else if ( dist < 0.0) { + d1 = (dist + dMax/2)/dMax*2; // [-dMax/2 ... 0] -> [0 ... 1] + d2 = d1*d1; + d3 = d2*d1; + d4 = d3*d1; + d5 = d4*d1; + res = (-d5/40.0f) + (d4/24.0f) + (d3/12.0f) + (d2/12.0f) + (d1/24.0f) + (1.0f/120.0f); + } + else if ( dist < dMax/2.0) { + d1 = (dist)/dMax*2; //[0 ... dMax/2] -> [0 ... 1] + d2 = d1*d1; + d4 = d2*d2; + d5 = d4*d1; + res = (d5/40.0f) - (d4/12.0f) + (d2/3.0f) - (d1/2.0f) + (7.0f/30.0f); + } + else { + d1 = (dist - dMax/2.0)/dMax*2; //[dMax/2 ... dMax] -> [0 ... 1] + d2 = d1*d1; + d3 = d2*d1; + d4 = d3*d1; + d5 = d4*d1; + res = (-d5/120.0f) + (d4/24.0f) - (d3/12.0f) + (d2/12.0f) - (d1/24.0f) + (1.0f/120.0f); + } + } + else + res = 0; + + return res*dMax/2; + +} + void waveShapeSmps(int n, float *smps, unsigned char type, - unsigned char drive) + unsigned char drive, + unsigned char offset, + unsigned char funcpar) { int i; float ws = drive / 127.0f; + float par = funcpar / 127.0f; + float offs = (offset - 64.0f) / 64.0f; float tmpv; switch(type) { case 1: ws = powf(10, ws * ws * 3.0f) - 1.0f + 0.001f; //Arctangent - for(i = 0; i < n; ++i) + for(i = 0; i < n; ++i) { + smps[i] += offs; smps[i] = atanf(smps[i] * ws) / atanf(ws); + smps[i] -= offs; + } break; case 2: ws = ws * ws * 32.0f + 0.0001f; //Asymmetric @@ -79,16 +134,26 @@ void waveShapeSmps(int n, break; case 7: ws = powf(2.0f, -ws * ws * 8.0f); //Limiter + par = par/4; + if (par > ws - 0.01) par = ws - 0.01; for(i = 0; i < n; ++i) { - float tmp = smps[i]; - if(fabs(tmp) > ws) { - if(tmp >= 0.0f) - smps[i] = 1.0f; - else - smps[i] = -1.0f; - } + // add the offset: x = smps[i] + offs + smps[i] += offs; + float res = polyblampres(smps[i], ws, par); + // now apply the polyblamped limiter: y = f(x) + if (smps[i]>=0) + smps[i] = ( smps[i] > ws ? ws-res : smps[i]-res ); + else + smps[i] = ( smps[i] < -ws ? -ws+res : smps[i]+res ); + // and substract the polyblamp-limited offset again: smps[i] = y - f(offs) + res = polyblampres(offs, ws, par); + if (offs>=0) + smps[i] -= ( offs >= ws ? ws-res : offs-res ); else - smps[i] /= ws; + smps[i] -= ( offs <= -ws ? -ws+res : offs+res ); + // divide through the drive factor: prevents limited signals to get low + smps[i] /= ws; + } break; case 8: @@ -111,16 +176,15 @@ void waveShapeSmps(int n, break; case 10: ws = (powf(2.0f, ws * 6.0f) - 1.0f) / powf(2.0f, 6.0f); //Inverse Limiter + if (par > ws - 0.01) par = ws - 0.01; for(i = 0; i < n; ++i) { - float tmp = smps[i]; - if(fabs(tmp) > ws) { - if(tmp >= 0.0f) - smps[i] = tmp - ws; - else - smps[i] = tmp + ws; - } + smps[i] += offs; + float res = polyblampres(smps[i], ws, par); + if (smps[i]>=0) + smps[i] = ( smps[i] > ws ? smps[i]-ws+res : res ); else - smps[i] = 0; + smps[i] = ( smps[i] < -ws ? smps[i]+ws-res : -res ); + smps[i] -= offs; } break; case 11: @@ -168,6 +232,8 @@ void waveShapeSmps(int n, else tmpv = 0.5f - 1.0f / (expf(ws) + 1.0f); for(i = 0; i < n; ++i) { + smps[i] += offs; //add offset + // calculate sigmoid function float tmp = smps[i] * ws; if(tmp < -10.0f) tmp = -10.0f; @@ -175,7 +241,60 @@ void waveShapeSmps(int n, if(tmp > 10.0f) tmp = 10.0f; tmp = 0.5f - 1.0f / (expf(tmp) + 1.0f); + // calculate the same for offset value + float tmpo = offs * ws; + if(tmpo < -10.0f) + tmpo = -10.0f; + else + if(tmpo > 10.0f) + tmpo = 10.0f; + tmpo = 0.5f - 1.0f / (expf(tmpo) + 1.0f); + smps[i] = tmp / tmpv; + smps[i] -= tmpo / tmpv; // substract offset + } + break; + case 15: + // f(x) = x / ((1+|x|^n)^(1/n)) // tanh approximation for n=2.5 + // Formula from: Yeh, Abel, Smith (2007): SIMPLIFIED, PHYSICALLY-INFORMED MODELS OF DISTORTION AND OVERDRIVE GUITAR EFFECTS PEDALS + par = (20.0f) * par * par + (0.1f) * par + 1.0f; //Pfunpar=32 -> n=2.5 + ws = ws * ws * 35.0f + 1.0f; + for(i = 0; i < n; ++i) { + smps[i] *= ws;// multiply signal to drive it in the saturation of the function + smps[i] += offs; // add dc offset + smps[i] = smps[i] / powf(1+powf(fabs(smps[i]), par), 1/par); + smps[i] -= offs / powf(1+powf(fabs(offs), par), 1/par); + } + break; + case 16: + // f(x) = 1.5 * (x-(x^3/3)) + // Formula from: https://ccrma.stanford.edu/~jos/pasp/Soft_Clipping.html + // modified with factor 1.5 to go through [1,1] and [-1,-1] + ws = powf(ws, 3.5f) * 20.0f + 1.0f; //cubic soft limiter + for(i = 0; i < n; ++i) { + smps[i] *= ws; // multiply signal to drive it in the saturation of the function + smps[i] += offs; // add dc offset + if(fabs(smps[i]) < 1.0f) + smps[i] = 1.5 * (smps[i] - (powf(smps[i], 3.0) / 3.0) ); + else + smps[i] = (smps[i] > 0 ? 1.0f : -1.0f); + //substract offset with distorsion function applied + smps[i] -= 1.5 * (offs - (powf(offs, 3.0) / 3.0)); + } + break; + case 17: + // f(x) = x*(2-abs(x)) + // Formula of 16 changed to square but still going through [1,1] and [-1,-1] + ws = ws * ws * ws * 20.0f + 1.0f; //square soft limiter + for(i = 0; i < n; ++i) { + smps[i] *= ws; // multiply signal to drive it in the saturation of the function + smps[i] += offs; // add dc offset + if(fabs(smps[i]) < 1.0f) + smps[i] = smps[i]*(2-fabs(smps[i])); + else + smps[i] = (smps[i] > 0 ? 1.0f : -1.0f); + //substract offset with distorsion function applied + smps[i] -= offs*(2-fabs(offs)); } break; } diff --git a/src/Misc/WaveShapeSmps.h b/src/Misc/WaveShapeSmps.h @@ -19,8 +19,14 @@ namespace zyn { void waveShapeSmps(int n, float *smps, unsigned char type, - unsigned char drive); + unsigned char drive, + unsigned char offset = 64, + unsigned char funcpar = 0); +//calculate the polyblamp residual value (called by waveshape function) +float polyblampres(float smp, + float ws, + float dMax); } #endif diff --git a/src/Synth/OscilGen.cpp b/src/Synth/OscilGen.cpp @@ -65,7 +65,7 @@ const rtosc::Ports OscilGen::non_realtime_ports = { rOptions(Undistorted, Arctangent, Asymmetric, Pow, Sine, Quantisize, Zigzag, Limiter, Upper Limiter, Lower Limiter, - Inverse Limiter, Clip, Asym2, Pow2, sigmoid), + Inverse Limiter, Clip, Asym2, Pow2, sigmoid, Tanh, Cubic, Square), "Shape of distortion to be applied"), rOption(Pfiltertype, rShort("filter"), rOptions(No Filter, lp, hp1, hp1b, bp1, bs1, lp2, hp2, bp2, bs2, diff --git a/src/UI/EffUI.fl b/src/UI/EffUI.fl @@ -807,6 +807,18 @@ if (filterwindow!=NULL){ code0 {o->init("parameter4");} class Fl_Osc_Dial } + Fl_Dial distp12 { + label Offs + tooltip {DC Offset} xywh {190 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + code0 {o->init("parameter12");} + class Fl_Osc_Dial + } + Fl_Dial distp11 { + label Shape + tooltip {Shape Parameter} xywh {225 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + code0 {o->init("parameter11");} + class Fl_Osc_Dial + } Fl_Dial distp7 { label LPF tooltip {Low Pass Filter} xywh {285 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 @@ -821,7 +833,7 @@ if (filterwindow!=NULL){ } Fl_Choice distp5 { label Type - xywh {190 50 60 20} box UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 align 2 textsize 10 + xywh {190 15 60 20} box UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 align 5 textsize 10 code0 {o->init("parameter5");} class Fl_Osc_Choice } { @@ -881,10 +893,22 @@ if (filterwindow!=NULL){ label Sgm xywh {95 95 100 20} labelfont 1 labelsize 10 } + MenuItem {} { + label Tanh + xywh {105 105 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Cubic + xywh {115 115 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Square + xywh {205 125 100 20} labelfont 1 labelsize 10 + } } Fl_Check_Button distp6 { label {Neg.} - xywh {260 55 15 15} down_box DOWN_BOX labelfont 1 labelsize 11 align 2 + xywh {260 15 15 15} down_box DOWN_BOX labelfont 1 labelsize 11 align 1 code0 {o->init("parameter6");} class Fl_Osc_Check } @@ -1722,6 +1746,18 @@ effdynamicfilterwindow->hide();//delete (effdynamicfilterwindow);} {} code0 {o->init("parameter4");} class Fl_Osc_Dial } + Fl_Dial distp12 { + label Offs + tooltip {DC Offset} xywh {190 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + code0 {o->init("parameter12");} + class Fl_Osc_Dial + } + Fl_Dial distp11 { + label Shape + tooltip {Shape Parameter} xywh {225 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + code0 {o->init("parameter11");} + class Fl_Osc_Dial + } Fl_Dial distp7 { label LPF tooltip {Low Pass Filter} xywh {190 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 @@ -1730,7 +1766,7 @@ effdynamicfilterwindow->hide();//delete (effdynamicfilterwindow);} {} } Fl_Choice distp5 { label Type - xywh {120 50 60 20} box UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 align 2 textsize 10 + xywh {120 15 60 20} box UP_BOX down_box BORDER_BOX labelfont 1 labelsize 11 align 5 textsize 10 code0 {o->init("parameter5");} class Fl_Osc_Choice } { @@ -1790,6 +1826,18 @@ effdynamicfilterwindow->hide();//delete (effdynamicfilterwindow);} {} label Sgm xywh {95 95 100 20} labelfont 1 labelsize 10 } + MenuItem {} { + label Tanh + xywh {105 105 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Cubic + xywh {115 115 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Square + xywh {205 125 100 20} labelfont 1 labelsize 10 + } } } } diff --git a/src/UI/OscilGenUI.fl b/src/UI/OscilGenUI.fl @@ -635,6 +635,18 @@ redrawoscil();} label Sgm xywh {95 95 100 20} labelfont 1 labelsize 10 } + MenuItem {} { + label Tanh + xywh {105 105 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Cubic + xywh {115 115 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label Square + xywh {205 125 100 20} labelfont 1 labelsize 10 + } } Fl_Dial wshpar { callback {redrawoscil();if(wsparval){wsparval->value(o->value());}}