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:
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());}}