Old 07-29-2018, 05:54 AM   #1
solarfall
Human being with feelings
 
Join Date: Sep 2013
Posts: 74
Default New Jsfx: AnalogSum

Hi folks.
Here's my first attempt at emulating an analog summing device. Give it a try: put it on every track (not busses or masters) and let me know if you like it. Suggestions are welcome. Thanks

Giovanni
Attached Files
File Type: txt AnalogSum.txt (1.4 KB, 336 views)
solarfall is offline   Reply With Quote
Old 07-29-2018, 07:16 AM   #2
bezusheist
Human being with feelings
 
bezusheist's Avatar
 
Join Date: Nov 2010
Location: Mullet
Posts: 829
Default

have you plotted this ?
i wouldn't double the input gain...as is, you need to limit to -6 dBFS +/- or it starts to fold over / fall apart.
didn't check it out too much yet but that caught my eye right away...
__________________
I like turtles
bezusheist is offline   Reply With Quote
Old 07-29-2018, 07:28 AM   #3
solarfall
Human being with feelings
 
Join Date: Sep 2013
Posts: 74
Default

Yes, i plotted it. It's a classic sigmoid function. The pre and post gain is there to increase saturation. I just chose the number by ear. I guess soft clipping would be better than limiting, just to stay in the realm of analog. Does this makes sense?
solarfall is offline   Reply With Quote
Old 07-29-2018, 07:54 AM   #4
bezusheist
Human being with feelings
 
bezusheist's Avatar
 
Join Date: Nov 2010
Location: Mullet
Posts: 829
Default

the sin/cos functions have a limit (pi/2), so you need to hard clip <= that or else use another function that doesn't have a limit. (tanh(x) is very popular these days)
you could get rid of the default *2 for the gain and put an input gain slider there in place... let the user decide. (people love their input/output gain.)

i attached a screnshot of what happens with 0 dBFS sine input...that's not good...
Attached Images
File Type: png Screen Shot 2018-07-29 at 10.45.35 AM.png (31.4 KB, 607 views)
__________________
I like turtles
bezusheist is offline   Reply With Quote
Old 07-29-2018, 08:24 AM   #5
solarfall
Human being with feelings
 
Join Date: Sep 2013
Posts: 74
Default

Quote:
Originally Posted by bezusheist View Post
the sin/cos functions have a limit (pi/2), so you need to hard clip <= that or else use another function that doesn't have a limit. (tanh(x) is very popular these days)
you could get rid of the default *2 for the gain and put an input gain slider there in place... let the user decide. (people love their input/output gain.)

i attached a screnshot of what happens with 0 dBFS sine input...that's not good...
Cool! So i made some changes and removed that multiplication. Actually i've changed the entire waveshaping. For now i want something without controls for simplicity.

Code:
desc:AnalogSum
desc:AnalogSum


@init

a = rand(1);
t = 0;

mX1l=mX2l=mY1l=mY2l=mX1r=mX2r=mY1r=mY2r=0;

sx = 16+95.95*1.20103;
cx = floor(exp(sx*log(1.059))*8.17742);
res = 0;
//coeffcients
cutoff = 2 * cx / srate;
res = pow(10, 0.05 * -res);
k = 0.5 * res * sin($pi * cutoff);
c1 = 0.5 * (1 - k) / (1 + k);
c2 = (0.5 + c1) * cos($pi * cutoff);
c3 = (0.5 + c1 - c2) * 0.25;
    
mA0 = 2 * c3;
mA1 = 2 * 2 * c3;
mA2 = 2 * c3;
mB1 = 2 * -c2;
mB2 = 2 * c1;


function add_noise()
(
  spl0 += 0.00005*((rand(1)*2)-1);
  spl1 += 0.00005*((rand(1)*2)-1);
  spl0 += 0.00001*sin(2*$pi*51*t);
  spl1 += 0.00001*sin(2*$pi*51*t);
  t+=1/srate;
);


function add_harmonics()
(
    b = 2; c = 1; amp = 0.8;
    spl0 = ((sin(b*spl0)*cos(spl0/b))/b) + (spl0/(2.6*b));
    spl1 = ((sin(b*spl1)*cos(spl1/b))/b) + (spl1/(2.4*b));
    spl0 = ((c*spl0)/(1+abs(c*spl0)) + (spl0/(1.5*c)));
    spl1 = ((c*spl1)/(1+abs(c*spl1)) + (spl1/(1.7*c)));
    spl0 *= amp;
    spl1 *= amp;
);

function analog_stereo()
(
  m = 0.5*(spl0+spl1);
  s = 0.6*(spl1-spl0);
  spl1 = m + s;
  spl0 = m - s;
);

function filter()
(

  inputl = spl0;
  inputr = spl1;
  
  outputl = mA0*inputl + mA1*mX1l + mA2*mX2l - mB1*mY1l - mB2*mY2l;
  mX2l = mX1l;
  mX1l = inputl;
  mY2l = mY1l;
  mY1l = outputl;
   
  outputr = mA0*inputr + mA1*mX1r + mA2*mX2r - mB1*mY1r - mB2*mY2r;
  mX2r = mX1r;
  mX1r = inputr;
  mY2r = mY1r;
  mY1r = outputr;
  
  spl0 = outputl;
  spl1 = outputr;
);


@sample
  add_noise();
  filter();
  add_harmonics();
  analog_stereo();
solarfall is offline   Reply With Quote
Old 07-30-2018, 09:59 AM   #6
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

Why do you not just use a signoid function? All those sin and cos and tan and htan in order to approximate a function with a single exponential? In my experience, one sigmoid is slightly less efficient than one one (clamped) sin, and slightly more efficient than a real tanh, but it's definitely faster than even two sins in a row. It requires no external clamping and will never explode at any input value.

Maybe I'm missing something?

What's going on in that analog stereo function? I kind of only glanced at it.

Edit to add -

I've been doing everything with sigmoids for a while. Even rebuilding things I did with tanh just to save that tick or two. See my Logistic Sidechain Distortion in the stash.

I've also been thinking about making some "black box" type character plugs. No sliders or maybe just input and output level. Drop it on there, it does...something...and if you like it you keep it and if not, you try a different one.


Edit again - But also I think it was about 2003 when analog summing devices were really the talk of the town and I predicted that before long we'd have virtual analog summing devices.

Last edited by ashcat_lt; 07-30-2018 at 10:13 AM.
ashcat_lt is offline   Reply With Quote
Old 07-30-2018, 01:01 PM   #7
JamesPeters
Human being with feelings
 
Join Date: Aug 2011
Location: Near a big lake
Posts: 3,943
Default

Here's my 2c. Feel free to adjust for deflation.

I tried the plugin from post #5.

-It increases loudness as well as peak level (the latter by a couple dBfs) making it difficult to know what it really does in a simple on/off test. I'd recommend at least reducing the peak output to the same peak level as the bypassed signal, or provide an output level control.

-This does a lot; it's not subtle. This isn't something that should be referred to as "summing". It's distortion. Well that's the most noticeable part of it anyway, by a large margin. If you want this to be thought of in the same general realm as BussColors by AirWindows (etc.), either you need to dial it way back or provide controls for the different aspects.

Using this on a single channel, I can see its potential benefits as-is. Running this on an entire mix (every channel), a mix that I thought was already good, pretty much made it a mess especially in the high end (to be fair, I did like how it sounded for a fair portion of the frequency range). I would never mix with this plugin on every channel, and probably only ever use it on a single channel as a fairly noticeable distortion/saturation effect. If it had controls for each aspect and output level, I could then consider using it on a whole mix.

Again, this is only my opinion. Thanks for bothering to make a plugin and release it for free, in any case! Whether you modify this or not, I do appreciate your effort.
JamesPeters is offline   Reply With Quote
Old 07-30-2018, 03:09 PM   #8
solarfall
Human being with feelings
 
Join Date: Sep 2013
Posts: 74
Default

Ok following the suggestions i made a few edits to the code:
- used a single sigmoid function (witch is a combination of two);
- tried to level-match the output;
- randomized the stereo-spread effect per track.

Code:
desc:AnalogSum
desc:AnalogSum


@init

LR_diff = (rand(1)/10)-0.05;
L_pan = 1 + LR_diff;
R_pan = 1 - LR_diff;
spread = rand(1)/10;
dist = 2; amp = 0.6;
t = 0;

mX1l=mX2l=mY1l=mY2l=mX1r=mX2r=mY1r=mY2r=0;

sx = 16+95.95*1.20103;
cx = floor(exp(sx*log(1.059))*8.17742);
res = 0;
//coeffcients
cutoff = 2 * cx / srate;
res = pow(10, 0.05 * -res);
k = 0.5 * res * sin($pi * cutoff);
c1 = 0.5 * (1 - k) / (1 + k);
c2 = (0.5 + c1) * cos($pi * cutoff);
c3 = (0.5 + c1 - c2) * 0.25;
    
mA0 = 2 * c3;
mA1 = 2 * 2 * c3;
mA2 = 2 * c3;
mB1 = 2 * -c2;
mB2 = 2 * c1;


function add_noise()
(
  spl0 += 0.00005*((rand(1)*2)-1);
  spl1 += 0.00005*((rand(1)*2)-1);
  spl0 += 0.00001*sin(2*$pi*51*t);
  spl1 += 0.00001*sin(2*$pi*51*t);
  spl0 += 0.00003*sin(2*$pi*150*t);
  spl1 += 0.00003*sin(2*$pi*150*t);
  t+=1/srate;
);


function analog_pan()
(
  spl0 *= 1 + LR_diff;
  spl1 *= 1 - LR_diff;
);

function add_harmonics()
(
    spl0 = ((($e^(dist*spl0))-($e^(-dist*spl0)))/(($e^(dist*spl0))+($e^(-dist*spl0))))-(spl0/sqrt(1+(dist*(spl0^2))))+(spl0/dist);
    spl1 = ((($e^(dist*spl1))-($e^(-dist*spl1)))/(($e^(dist*spl1))+($e^(-dist*spl1))))-(spl1/sqrt(1+(dist*(spl1^2))))+(spl1/dist);
    spl0 *= amp;
    spl1 *= amp;
);

function analog_stereo()
(
  m = 0.5*(spl0+spl1);
  s = (0.6+spread)*(spl1-spl0);
  spl1 = m + s;
  spl0 = m - s;
);

function filter()
(

  inputl = spl0;
  inputr = spl1;
  
  outputl = mA0*inputl + mA1*mX1l + mA2*mX2l - mB1*mY1l - mB2*mY2l;
  mX2l = mX1l;
  mX1l = inputl;
  mY2l = mY1l;
  mY1l = outputl;
   
  outputr = mA0*inputr + mA1*mX1r + mA2*mX2r - mB1*mY1r - mB2*mY2r;
  mX2r = mX1r;
  mX1r = inputr;
  mY2r = mY1r;
  mY1r = outputr;
  
  spl0 = outputl;
  spl1 = outputr;
);


@sample
  add_noise();
  analog_pan();
  filter();
  analog_stereo();
  add_harmonics();
Thank you! keep comments going..

Last edited by solarfall; 07-30-2018 at 03:18 PM.
solarfall is offline   Reply With Quote
Old 07-30-2018, 05:00 PM   #9
JamesPeters
Human being with feelings
 
Join Date: Aug 2011
Location: Near a big lake
Posts: 3,943
Default

If you add controls for the functions, I'll be glad to try it again. Otherwise it's a bit too over-the-top for me in general.

Oh something else I forgot to mention: I don't like noise being intentionally added by the plugin. I'm not nostalgic for cassette tapes or something. The noise is quite noticeable and I think it takes away from what you're trying to do.
JamesPeters is offline   Reply With Quote
Old 07-31-2018, 07:26 AM   #10
solarfall
Human being with feelings
 
Join Date: Sep 2013
Posts: 74
Default

Ok, here's another turn of improvements. Now you can choose between 3 types of saturation and they get updated on all instances at once with global registers.
Code:
desc:AnalogSum
desc:AnalogSum

slider1:0<0,2,1{1,2,3}>Saturation Type

@init

LR_diff = (rand(1)/10)-0.05;
L_pan = 1 + LR_diff;
R_pan = 1 - LR_diff;
spread = rand(1)/8;
dist = 1.6;
t = 0;

mX1l=mX2l=mY1l=mY2l=mX1r=mX2r=mY1r=mY2r=0;

sx = 16+95.95*1.20103;
cx = floor(exp(sx*log(1.059))*8.17742);
res = 0;
//coeffcients
cutoff = 2 * cx / srate;
res = pow(10, 0.05 * -res);
k = 0.5 * res * sin($pi * cutoff);
c1 = 0.5 * (1 - k) / (1 + k);
c2 = (0.5 + c1) * cos($pi * cutoff);
c3 = (0.5 + c1 - c2) * 0.25;
    
mA0 = 2 * c3;
mA1 = 2 * 2 * c3;
mA2 = 2 * c3;
mB1 = 2 * -c2;
mB2 = 2 * c1;


function add_noise()
(
  spl0 += 0.00005*((rand(1)*2)-1);
  spl1 += 0.00005*((rand(1)*2)-1);
  spl0 += 0.000002*sin(2*$pi*51*t);
  spl1 += 0.000002*sin(2*$pi*51*t);
  spl0 += 0.000004*sin(2*$pi*150*t);
  spl1 += 0.000004*sin(2*$pi*150*t);
  t+=1/srate;
);

function curve1()
(
  spl0 = ((($e^(dist*spl0))-($e^(-dist*spl0)))/(($e^(dist*spl0))+($e^(-dist*spl0))))-(spl0/sqrt(1+(dist*(spl0^2))))+(spl0/dist);
  spl1 = ((($e^(dist*spl1))-($e^(-dist*spl1)))/(($e^(dist*spl1))+($e^(-dist*spl1))))-(spl1/sqrt(1+(dist*(spl1^2))))+(spl1/dist);
  spl0 *= 0.78;
  spl1 *= 0.78;
);

function curve2()
(
  spl0 = sin(tan(spl0))*cos(atan(dist*spl0))*dist*0.9;
  spl1 = sin(tan(spl1))*cos(atan(dist*spl1))*dist*0.9;
  spl0 *= 0.72;
  spl1 *= 0.72;
);

function curve3()
(
  spl0 = (dist*spl0)/(1+abs(2*dist*spl0))*((2+dist)/dist);
  spl1 = (dist*spl1)/(1+abs(2*dist*spl1))*((2+dist)/dist);
  spl0 *= 0.3;
  spl1 *= 0.3;
);

function analog_pan()
(
  spl0 *= 1 + LR_diff;
  spl1 *= 1 - LR_diff;
);

function add_harmonics()
(
    reg00 == 0 ? curve1();
    reg00 == 1 ? curve2();
    reg00 == 2 ? curve3();
    slider1=reg00;
);

function analog_stereo()
(
  m = 0.5*(spl0+spl1);
  s = (0.6+spread)*(spl1-spl0);
  spl1 = m + s;
  spl0 = m - s;
);

function filter()
(

  inputl = spl0;
  inputr = spl1;
  
  outputl = mA0*inputl + mA1*mX1l + mA2*mX2l - mB1*mY1l - mB2*mY2l;
  mX2l = mX1l;
  mX1l = inputl;
  mY2l = mY1l;
  mY1l = outputl;
   
  outputr = mA0*inputr + mA1*mX1r + mA2*mX2r - mB1*mY1r - mB2*mY2r;
  mX2r = mX1r;
  mX1r = inputr;
  mY2r = mY1r;
  mY1r = outputr;
  
  spl0 = outputl;
  spl1 = outputr;
);


@slider

  reg00 = slider1;
  
@sample
  add_noise();
  analog_pan();
  analog_stereo();
  add_harmonics();
  filter();
solarfall is offline   Reply With Quote
Old 09-12-2018, 08:28 AM   #11
Ozman
Human being with feelings
 
Join Date: Feb 2015
Posts: 753
Default

How have the tests with this one been?
Ozman is offline   Reply With Quote
Old 09-12-2018, 08:46 AM   #12
JamesPeters
Human being with feelings
 
Join Date: Aug 2011
Location: Near a big lake
Posts: 3,943
Default

Quote:
Originally Posted by Ozman View Post
How have the tests with this one been?
Well if you were asking me, I decided to not use the plugin again unless controls are added to dial back the various aspects to taste. Also that the noise, which apparently has been intentionally added, would be removed.
JamesPeters is offline   Reply With Quote
Old 09-12-2018, 09:16 AM   #13
Ozman
Human being with feelings
 
Join Date: Feb 2015
Posts: 753
Default

Quote:
Originally Posted by JamesPeters View Post
Well if you were asking me, I decided to not use the plugin again unless controls are added to dial back the various aspects to taste. Also that the noise, which apparently has been intentionally added, would be removed.
Did you test after the modes were added?
And can one use wet/dry knob for dailing in amounts?

Oh, my bad... you were meaning being able to control the "aspects" of the noise and saturation.

I guess I'm with you on that.
Ozman is offline   Reply With Quote
Old 09-12-2018, 09:54 AM   #14
Eliseat
Human being with feelings
 
Eliseat's Avatar
 
Join Date: Mar 2018
Location: Cologne
Posts: 1,362
Default

Quote:
Originally Posted by JamesPeters View Post
Here's my 2c. Feel free to adjust for deflation.

I tried the plugin from post #5.

-It increases loudness as well as peak level (the latter by a couple dBfs) making it difficult to know what it really does in a simple on/off test. I'd recommend at least reducing the peak output to the same peak level as the bypassed signal, or provide an output level control.

-This does a lot; it's not subtle. This isn't something that should be referred to as "summing". It's distortion. Well that's the most noticeable part of it anyway, by a large margin. If you want this to be thought of in the same general realm as BussColors by AirWindows (etc.), either you need to dial it way back or provide controls for the different aspects.

Using this on a single channel, I can see its potential benefits as-is. Running this on an entire mix (every channel), a mix that I thought was already good, pretty much made it a mess especially in the high end (to be fair, I did like how it sounded for a fair portion of the frequency range). I would never mix with this plugin on every channel, and probably only ever use it on a single channel as a fairly noticeable distortion/saturation effect. If it had controls for each aspect and output level, I could then consider using it on a whole mix.

Again, this is only my opinion. Thanks for bothering to make a plugin and release it for free, in any case! Whether you modify this or not, I do appreciate your effort.

This does a lot? Why not just turn the wetness down and use it like taste? Its not even adding delay or anything critical. If you use such saturation plugins on every track or not also depends on what kind of music you make. There is no rule - only taste and ears.

There are bands making rockabilly records today with equipment right from the 60ies. And they really do a lot!
Eliseat is offline   Reply With Quote
Old 09-12-2018, 10:38 AM   #15
JamesPeters
Human being with feelings
 
Join Date: Aug 2011
Location: Near a big lake
Posts: 3,943
Default

Quote:
Originally Posted by Eliseat View Post
This does a lot? Why not just turn the wetness down and use it like taste?
If I have to use wet/dry in Reaper itself, I will. However, I want to adjust some of the plugin's parameters individually. Some, I would never use at all (as I've already mentioned). Others, I would.

There are already other plugins I use for similar effects, so I feel more comfortable just using those.

Quote:
Originally Posted by Eliseat View Post
Its not even adding delay or anything critical.
Actually, consider the "panning" aspect built into this plugin. Blending that wet/dry in Reaper's fx window can affect the sound in a way that's unwanted (phase, stereo imaging). Since it can't be adjusted separately, it's "all or nothing".

Quote:
Originally Posted by Eliseat View Post
If you use such saturation plugins on every track or not also depends on what kind of music you make. There is no rule - only taste and ears.
I know.

I'm also not saying that others shouldn't use this plugin. That's up to them. Others might appreciate this plugin. But if I'm asked to keep testing this plugin, I don't want to bother unless it ends up with controls to adjust the parameters. I provided my reasons, that's all.

Last edited by JamesPeters; 09-12-2018 at 10:57 AM.
JamesPeters is offline   Reply With Quote
Old 09-13-2018, 12:02 AM   #16
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

The picture shows some kind of severe distortion ?!?!?!?

The name "Summing" suggest, that the plugin should see all channels to be summed and somehow "mediate" between them, bnut I don't see any trace of this in the source code.

Can somebody elaborate on the theory, practical sound advantages and usage cases for the algorithm implemented here ?

Thanks,
-Michael
mschnell is offline   Reply With Quote
Old 09-13-2018, 02:49 AM   #17
Eliseat
Human being with feelings
 
Eliseat's Avatar
 
Join Date: Mar 2018
Location: Cologne
Posts: 1,362
Default

Quote:
Originally Posted by solarfall View Post
Ok, here's another turn of improvements. Now you can choose between 3 types of saturation and they get updated on all instances at once with global registers.
Code:
...
Hi solarfall,

i tested this version of your JSFX and i find it very useful. Its nice sounding and carves different harmonics on top of the signal. Its a great opportunity to grind drum loops or to clean synths in a mix. It helps to place them in 3d. And I really like mixes where every instrument is perfectly separated and has this tiny silk like vibration on top.

Of course it would be a great addition - like JamesPeters mentioned - to have a saturation input to maybe slowly dive into the audio material. I have no idea how much effort it costs. But anyway. I'm already satisfied with the actual state and guaranty you i will use it in further projects. Saturation is always appreciated especially in a uncomplicated way like an JS.

Lovely
Eliseat is offline   Reply With Quote
Old 09-13-2018, 03:02 AM   #18
Eliseat
Human being with feelings
 
Eliseat's Avatar
 
Join Date: Mar 2018
Location: Cologne
Posts: 1,362
Default

Quote:
Originally Posted by mschnell View Post
The picture shows some kind of severe distortion ?!?!?!?

The name "Summing" suggest, that the plugin should see all channels to be summed and somehow "mediate" between them, bnut I don't see any trace of this in the source code.

Can somebody elaborate on the theory, practical sound advantages and usage cases for the algorithm implemented here ?

Thanks,
-Michael
I would say it simulates the adding of analog gear effects just straight forward by adding fuzzyness to the signal. Its no emulation. But i always wondered why there is no real emulation of a signal flow thru various analog gear. Everybody who worked in analog times knows, it was a great bummer to don't have digital possibilities like today. But also knows, how every cable, every effect rack, tape machine and mixing desk added something (and not only good). With high end equipment it were only tiny amounts of random imperfections, noise, hum etc. But they gave the mojo whats missed in some modern productions out of the box.
Eliseat is offline   Reply With Quote
Old 09-13-2018, 03:07 AM   #19
bezusheist
Human being with feelings
 
bezusheist's Avatar
 
Join Date: Nov 2010
Location: Mullet
Posts: 829
Default

Quote:
Originally Posted by mschnell View Post
The picture shows some kind of severe distortion ?!?!?!?
that is the first version, i didn't check the other versions.
it's usually a good idea to limit the input if the function only works correctly within a certain range.
__________________
I like turtles
bezusheist is offline   Reply With Quote
Old 01-16-2019, 11:04 AM   #20
Pinknoise
Human being with feelings
 
Pinknoise's Avatar
 
Join Date: Aug 2012
Location: Around Montréal
Posts: 1,117
Default

I get this error.

Pinknoise is offline   Reply With Quote
Old 01-16-2019, 11:17 AM   #21
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

It's a JSFX audio effect, not a Lua script. Put the file in the Effects folder and insert it in an FX chain to use it.
cfillion is offline   Reply With Quote
Old 01-16-2019, 01:07 PM   #22
ErBird
Human being with feelings
 
Join Date: Jan 2017
Location: Los Angeles
Posts: 1,161
Default

Quote:
Originally Posted by solarfall View Post
used a single sigmoid function (witch is a combination of two)
Every waveshaper you've posted so far is wack AF. What's the reasoning behind these equations? Nothing beats a simple tanh, imo.

P.S. You can do tanh with one exponential instead of four.
ErBird is offline   Reply With Quote
Old 01-16-2019, 05:11 PM   #23
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

Quote:
Originally Posted by ErBird View Post
P.S. You can do tanh with one exponential instead of four.
Care to share? I've been using the Logistic function, which after shifting and scaling IS the same thing, but I wonder if you've got something else.
ashcat_lt is offline   Reply With Quote
Old 01-16-2019, 05:20 PM   #24
ErBird
Human being with feelings
 
Join Date: Jan 2017
Location: Los Angeles
Posts: 1,161
Default

Quote:
Originally Posted by ashcat_lt View Post
Care to share? I've been using the Logistic function, which after shifting and scaling IS the same thing, but I wonder if you've got something else.
That's the one. tanh(x) = 2/(1+exp(-2x))-1
It's a very nice
ErBird is offline   Reply With Quote
Old 01-16-2019, 09:25 PM   #25
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
Default

There has been a lot of discussion about sigmoids, especially tanh. You can search for it, but a few years ago (when I was a lot more active and had time) I compiled a kind of list.


https://forum.cockos.com/showthread.php?t=190087


My favorite subtle saturation is Loser/Saturation. It's subtle, it's sweet, you can cascade it, I really really really like it. If you're going to go the route of analog summing you want subtle, not obvious. This is one way to do it and it's adjustable. I'm almost positive that I figured out a more generalized way to do it that didn't rely on calling sin()... but it's apparently been years and I just don't remember.

Argitoth's post about sigmoids is definitely one to look at. If you like long-winded discussions about math you can read some of my posts, I tried to explain some of my reasoning a few different times in a few different posts.

...

If I was to attempt this style of saturator I would probably use a compression-style waveshaper with a soft knee, e.g. a ratio of maybe 2:1 and a super wide knee, like from -15 or -20 to 15 or some such. Would probably want to have the saturation in parallel, probably filter it as well. You can find a post that I wrote on soft knees using a quadratic spline in that list above. Again, probably overly long-winded, but sometimes you get excited about things.
SaulT is offline   Reply With Quote
Old 01-16-2019, 09:40 PM   #26
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

Though actually, since they fixed the knee in ReaComp, I've pretty much just been using that.
ashcat_lt is offline   Reply With Quote
Old 01-16-2019, 10:01 PM   #27
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
Default

Well, I looked into it and it ended up being more of a hassle than I remembered, with an abs() and sign() call and blah. I did find this one, though, I hadn't remembered it.

out = (a*x)/sqrt(a + x*x);

It's pretty close to tanh(x) when a = 1 but of course feel free to experiment...
SaulT is offline   Reply With Quote
Old 01-16-2019, 11:27 PM   #28
ErBird
Human being with feelings
 
Join Date: Jan 2017
Location: Los Angeles
Posts: 1,161
Default

Regarding Loser's saturation, I find that one just OK. I'm not really a fan of sine saturation. It clamps to 1 a little too early (around +4 dB). Loser/Saturation basically morphs between hard clipping and sine saturation with a little extra gain, meaning the input gain is higher as the curve morphs toward sine.

Quote:
Originally Posted by SaulT View Post
out = (a*x)/sqrt(a + x*x)
I'm familiar with x/sqrt(1+x^2). Yours is that with input and output scaling, though it's not exactly well-behaved because input is scaled by 1/sqrt(a) while the output is scaled by a, so you get a massive output volume boost as a increases.

Slightly different
(a*x)/sqrt(a^2 + x^2)
would give you a variable ceiling.

I love sigmoids and have spent way too much time messing around with them.
ErBird is offline   Reply With Quote
Old 01-17-2019, 02:57 AM   #29
Eliseat
Human being with feelings
 
Eliseat's Avatar
 
Join Date: Mar 2018
Location: Cologne
Posts: 1,362
Default

Cool!

He he ... Cunning, as I am, I stole your formula and copied it into the video processor's Morph preset. And yes! It worked! Now I will get rich! I WILL RULE THE WORLD!!






Just joking! It did nothing spectacular. The gif shows a preset with a fantasy formula I made myself. What a bummer.


---
__________________
☆.。.:*・°☆.。.:*・°☆.。.:*・°☆REAPER//✿◔‿◔)°☆.。.:*・°☆.。.:*・°☆

Last edited by Eliseat; 01-17-2019 at 03:02 AM.
Eliseat is offline   Reply With Quote
Old 01-17-2019, 09:50 AM   #30
citizenkeith
Human being with feelings
 
Join Date: Jun 2014
Location: Ohio
Posts: 978
Default

Quote:
Originally Posted by ErBird View Post
Regarding Loser's saturation, I find that one just OK.
Which saturation do you prefer to use instead?
citizenkeith is online now   Reply With Quote
Old 01-17-2019, 10:34 AM   #31
Pinknoise
Human being with feelings
 
Pinknoise's Avatar
 
Join Date: Aug 2012
Location: Around Montréal
Posts: 1,117
Default

Quote:
Originally Posted by cfillion View Post
It's a JSFX audio effect, not a Lua script. Put the file in the Effects folder and insert it in an FX chain to use it.
Hey I knew that ! ;-)
Pinknoise is offline   Reply With Quote
Old 01-18-2019, 01:53 PM   #32
ErBird
Human being with feelings
 
Join Date: Jan 2017
Location: Los Angeles
Posts: 1,161
Default

Quote:
Originally Posted by citizenkeith View Post
Which saturation do you prefer to use instead?
Thanks for asking. I use my own JS which is tanh-based. Nothing out of the ordinary, but after spending time with every sigmoid algorithm I could find or come up with, I find it to be the best.

Here:
Code:
desc:Tanh Saturation

slider1:0<-6,24,1>Gain (dB)
slider2:0<-18,0,1>Ceiling (dB)

in_pin:left input
in_pin:right input
out_pin:left output
out_pin:right output

@slider

  preamp = 10^(slider1/20);
  ceiling = 10^(-slider2/20);
  inv_ceiling = 10^(slider2/20);

@sample

  spl0 *= preamp;
  spl1 *= preamp;
  
  spl0 *= ceiling;
  spl1 *= ceiling;
  
  spl0 = (2/(1+exp(-2*spl0)))-1;
  spl1 = (2/(1+exp(-2*spl1)))-1;
  
  spl0 *= inv_ceiling;
  spl1 *= inv_ceiling;
ErBird is offline   Reply With Quote
Old 01-18-2019, 06:39 PM   #33
citizenkeith
Human being with feelings
 
Join Date: Jun 2014
Location: Ohio
Posts: 978
Default

Quote:
Originally Posted by ErBird View Post
Thanks for asking. I use my own JS which is tanh-based. Nothing out of the ordinary, but after spending time with every sigmoid algorithm I could find or come up with, I find it to be the best.

Here:
Code:
desc:Tanh Saturation

slider1:0<-6,24,1>Gain (dB)
slider2:0<-18,0,1>Ceiling (dB)

in_pin:left input
in_pin:right input
out_pin:left output
out_pin:right output

@slider

  preamp = 10^(slider1/20);
  ceiling = 10^(-slider2/20);
  inv_ceiling = 10^(slider2/20);

@sample

  spl0 *= preamp;
  spl1 *= preamp;
  
  spl0 *= ceiling;
  spl1 *= ceiling;
  
  spl0 = (2/(1+exp(-2*spl0)))-1;
  spl1 = (2/(1+exp(-2*spl1)))-1;
  
  spl0 *= inv_ceiling;
  spl1 *= inv_ceiling;
Thank you for sharing the code! I'll be sure to check this out ASAP.
citizenkeith is online now   Reply With Quote
Old 01-18-2019, 06:43 PM   #34
sai'ke
Human being with feelings
 
sai'ke's Avatar
 
Join Date: Aug 2009
Location: NL
Posts: 1,453
Default

I do like the tanh, but saturation can lead to pretty bad aliasing. For most waveshapers, you can use a trick to reduce the aliasing quite a bit at low cost (without resorting to oversampling). One can construct a continuous time approximation of the input signal and then apply the non-linearity to it in continuous time. The paper title is in the comments if you are interested in reading more about it. It's quite a fun read. If all you're doing is waveshaping, then at least the rect version is pretty easy to implement. All you need is an integrated version of your waveshaping function (F0 in this code).

Anyways, code if you like. I'll add it to my jsfx repo too.
Code:
DO NOT USE THIS VERSION, BETTER VERSION IN LATER POST.

desc:Anti-aliased Tanh Saturation
tags: saturation distortion anti-aliased
version: 1.00
author: Joep Vanlier
license: MIT

Uses technique from: Parker et al, "REDUCING THE ALIASING OF NONLINEAR WAVESHAPING USING CONTINUOUS-TIME CONVOLUTION",
Proceedings of the 19th International Conference on Digital Audio Effects (DAFx-16), Brno, Czech Republic, September 5–9, 2016
I have only implemented the rect version, since the linear one depends on Li2 and LUTs aren't so fast in JSFX.

in_pin:left input
in_pin:right input
out_pin:left output
out_pin:right output

slider1:0<-6,24,1>Gain (dB)
slider2:0<-18,0,1>Ceiling (dB)
slider3:1<0,1,1>Antialias?
slider4:0<0,1,1>Fix DC?

@init
bpos=0;

@slider
preamp      = 10^(slider1/20);
ceiling     = 10^(-slider2/20);
inv_ceiling = 10^(slider2/20);

@block
blah+=samplesblock;

@sample
spl0=spl0;
spl1=spl1;

@sample 
  function F0(x, em2x)
  local()
  global()
  instance()
  (
    x - log(2/(1 + em2x))
  );
  
  function atanh_prec(x, em2x)
  local() 
  global()
  instance()
  (
    (2/(1+em2x))-1
  );
  
  function atanh(x)
  local()
  global()
  instance()
  (
    (2/(1+exp(-2*x)))-1
  );
  
  function antialiased_atanh_rect(x)
  local(diff, em2x, F0_xn)
  global(slider4)
  instance(antialias, F0_xnm1, xnm1)
  (
    em2x      = exp(-2*x);
    F0_xn     = F0(x, em2x);
    
    diff      = ( x - xnm1 );
    antialias = (abs(diff) > 0.000000001) ? ( F0_xn - F0_xnm1 ) / diff : .5 * atanh_prec(.5*(x+xnm1), em2x);
    F0_xnm1   = F0_xn;
    xnm1      = x;

    antialias
  );  

  function fix_dc(x)
  local()
  global()
  instance(DC_fixed, prev)
  (
    DC_fixed=0.999*DC_fixed + x - prev;
    prev=x;
    DC_fixed
  );

  spl0 *= preamp;
  spl1 *= preamp;
  
  spl0 *= ceiling;
  spl1 *= ceiling;
  
  slider3 ? (
    spl0 = ch0.antialiased_atanh_rect(spl0);
    spl1 = ch1.antialiased_atanh_rect(spl1);
  ) : (
    spl0 = atanh(spl0);
    spl1 = atanh(spl1);
  );
  
  slider4 ? (
    spl0 = dc0.fix_dc(spl0);
    spl1 = dc1.fix_dc(spl1);
  );
  
  spl0 *= inv_ceiling;
  spl1 *= inv_ceiling;
On the left you see a sine sweep without the anti-aliasing, on the right you see one with (same colorscale of course). The difference is substantial.

__________________
[Tracker Plugin: Thread|Github|Reapack] | [Routing Plugin: Thread|Reapack] | [More JSFX: Thread|Descriptions|Reapack]

Last edited by sai'ke; 01-20-2019 at 05:23 AM.
sai'ke is offline   Reply With Quote
Old 01-18-2019, 07:05 PM   #35
ErBird
Human being with feelings
 
Join Date: Jan 2017
Location: Los Angeles
Posts: 1,161
Default

Thanks! This is very interesting. I'll have to read up to understand what's going on here.

Quote:
Originally Posted by sai'ke View Post
I do like the tanh, but saturation can lead to pretty bad aliasing.
Of course you're right in theory, but I haven't found aliasing to be a problem at reasonable gain levels. In fact, I was testing this just last night. On a pure input tone the aliasing is easily noticable after a certain amount of gain. But on more complex signals it's much harder to pick out the aliasing if you can hear it at all.
ErBird is offline   Reply With Quote
Old 01-18-2019, 07:19 PM   #36
sai'ke
Human being with feelings
 
sai'ke's Avatar
 
Join Date: Aug 2009
Location: NL
Posts: 1,453
Default

No prob.

And yeah, you're right, it depends on how hard you push the saturator and how busy the high frequencies are already. I just took this sine sweep because it's easy to visualize in this case.

On synths, I can usually spot aliasing pretty well though. Given that the difference in computational cost is low, I'd probably go with the anti-aliased one.

Really though, I've read this paper a while ago, and just wanted an excuse to try it out. DSP is fun
__________________
[Tracker Plugin: Thread|Github|Reapack] | [Routing Plugin: Thread|Reapack] | [More JSFX: Thread|Descriptions|Reapack]
sai'ke is offline   Reply With Quote
Old 01-18-2019, 07:33 PM   #37
JamesPeters
Human being with feelings
 
Join Date: Aug 2011
Location: Near a big lake
Posts: 3,943
Default

Here are a couple other JS that might be interesting to those of us who haven't used them already:

https://stash.reaper.fm/v/16208/wildwavefu.zip

https://stash.reaper.fm/v/25168/js_plugins.zip (under "waveshaper", "harmonics" is the plugin. There are some other cool plugins in this too. )
JamesPeters is offline   Reply With Quote
Old 01-18-2019, 11:09 PM   #38
TonE
Human being with feelings
 
Join Date: Feb 2009
Location: Reaper HAS send control via midi !!!
Posts: 4,031
Default

Quote:
Originally Posted by sai'ke View Post
Really though, I've read this paper a while ago, and just wanted an excuse to try it out. DSP is fun
Your games seem so, too, you are a master in any field you touch or enter. Respects.

https://www.youtube.com/watch?v=Rs4zZG25u-s

Not knowing much of these, to me it feels, the art is in finetuning all. So a boring game can be made almost something much more interesting just by finetuning all the important parameters, plus a few original and interesting changes here and there, ignoring what is available so far. Example would be the scoring or energy distribution algorithms among players, not only using 'egoistic algorithms', meaning all score/energy to me, nothing to the rest.

Which language you used above? What are your recommendations? Possible to test?

Same for Hypercoaster. Does it make a big change in feel if used with a 3D glass/display or what is the official term?

https://www.youtube.com/watch?v=I1VpPjfANKg

Last edited by TonE; 01-18-2019 at 11:14 PM.
TonE is offline   Reply With Quote
Old 01-19-2019, 02:59 AM   #39
Eliseat
Human being with feelings
 
Eliseat's Avatar
 
Join Date: Mar 2018
Location: Cologne
Posts: 1,362
Default

As this thread is boarded with a general saturation discussion I also have to ask something. (And for such a thing its always good to have Sai'ke (The Grandmaster of Filtering) around.)

I read once about the Spectre vst saturation plugin in a forum discussion where all people praised it. A demo download gave me the certainty that this is really an amazing plugin. It has such an impact that you can shape even flat and boring sound into warm and sparkling mixes.
My question is: Is this only an over-saturated EQ that could be reproduced by sending saturated signal thru ReaEq? Because that doesn't even come close to that amazing sound of Spectre.

Quote:
Spectre processes the difference between the input signal and the EQ signal, introducing harmonic content to just the part of the spectrum that you want from a variety of saturation algorithms based on classic recording hardware.
I can read this again and again but don't come to a conclusion. Does this mean they subtract (delta) the original signal from the processed EQ to add it (saturated) in again?

Anyway. This is by far the best saturation plugin I've ever heard. And its flexibility is unbeatable. Maybe this could be a cool project for a Reaper JS. (づ。◕‿‿◕。)づ

__________________
☆.。.:*・°☆.。.:*・°☆.。.:*・°☆REAPER//✿◔‿◔)°☆.。.:*・°☆.。.:*・°☆

Last edited by Eliseat; 01-19-2019 at 03:05 AM.
Eliseat is offline   Reply With Quote
Old 01-19-2019, 02:41 PM   #40
ErBird
Human being with feelings
 
Join Date: Jan 2017
Location: Los Angeles
Posts: 1,161
Default

Quote:
Originally Posted by sai'ke View Post
Anyways, code if you like. I'll add it to my jsfx repo too.
Joep,

I've encountered a couple problems with this implementation. See my video.

1) Feeding in a sine tone from tone generator, there is click injected at the top peak, depending on frequency. 441 Hz seems to be "safe". 440 Hz is not. Flipping from 440 to 441 then back to 440, the click sometimes goes away. Restarting the transport brings it back. Using MOscillator instead, the problem doesn't exist.

2) With >51 dB of gain, the lower peak folds over. What should be -1 flips up to +1, with increasing severity as the gain increases.



Anyway, I still need to read the paper to understand your code. I'm very interested in what's causing this and if this anti-aliased algorithm can handle higher gain levels. Any ideas?

Last edited by ErBird; 01-19-2019 at 02:57 PM.
ErBird is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 10:03 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.