Forums > MaxMSP

waveshaping – parabolic curve function

July 27, 2006 | 11:24 am

trying to write a patch which distorts audio with a parabolic waveshaping function. for the moment, i am not interested in using wavetables in a buffer. i would rather do it as demonstrated in the patch below. my problem is adjusting the curve factor of the waveform…

any tips or helpful URL appreciated?

BTW, my maths aint too hot but am willing to learn!

thanks

justin

max v2;
#N vpatcher 309 44 919 688;
#P origin -53 -18;
#P window setfont "Sans Serif" 9.;
#P window linecount 2;
#P comment 315 284 124 196617 fix negative values so pow~ doesnt go mad…;
#P comment 235 431 202 196617 ez option might be to xfade original with processed signal to mix in curve effect…;
#P window linecount 1;
#P comment 295 115 107 196617 input test waveforms;
#P toggle 111 50 27 0;
#P newex 111 82 27 196617 + 1;
#P newex 223 115 71 196617 selector~ 2 1;
#P window linecount 2;
#P comment 139 50 36 196617 0: tri 1: sin;
#P window linecount 1;
#P newex 253 86 69 196617 triangle~ 0.5;
#P newex 253 63 64 196617 phasor~ 1.4;
#P comment 251 392 112 196617 merge pos + neg signal;
#P window linecount 2;
#P comment 342 330 183 196617 process – need to enable user to adjust the curve factor;
#P user scope~ 255 483 385 613 256 3 128 -1. 1. 0 0. 0 0. 102 255 51 135 135 135 0;
#P user ezdac~ 163 548 207 581 0;
#P toggle 18 50 27 0;
#P window linecount 1;
#P newex 18 82 27 196617 + 1;
#P newex 163 431 71 196617 selector~ 2 1;
#P newex 283 284 31 196617 +~ 1;
#P newex 283 304 33 196617 !-~ 1;
#P newex 283 350 30 196617 -~ 1;
#P newex 283 330 58 196617 pow~ 0.01;
#P newex 223 350 33 196617 !-~ 1;
#P newex 223 330 58 196617 pow~ 0.01;
#P newex 223 392 27 196617 +~;
#P newex 283 242 55 196617 clip~ -1 0;
#P newex 223 242 50 196617 clip~ 0 1;
#P newex 223 198 41 196617 *~ 0.5;
#P newex 223 178 44 196617 +~;
#P newex 318 63 58 196617 cycle~ 1.4;
#P newex 163 508 85 196617 plugout~;
#P newex 223 151 44 196617 plugin~;
#P window linecount 2;
#P comment 46 50 64 196617 0: clean 1: parabolic;
#P window linecount 1;
#P comment 339 242 100 196617 split pos + neg signal;
#P fasten 18 0 17 0 23 79 23 79;
#P fasten 28 0 27 0 116 79 116 79;
#P fasten 17 0 16 0 23 427 168 427;
#P connect 16 0 3 0;
#P fasten 6 0 16 1 228 228 198 228;
#P fasten 27 0 26 0 116 107 228 107;
#P connect 26 0 2 0;
#P connect 2 0 5 0;
#P connect 5 0 6 0;
#P connect 6 0 7 0;
#P connect 7 0 10 0;
#P connect 10 0 11 0;
#P connect 11 0 9 0;
#P connect 9 0 16 2;
#P fasten 16 0 3 1 168 478 243 478;
#P connect 13 0 9 1;
#P connect 23 0 24 0;
#P connect 24 0 26 1;
#P fasten 16 0 20 0 168 474 260 474;
#P fasten 26 0 2 1 228 134 262 134;
#P connect 2 1 5 1;
#P fasten 4 0 26 2 323 114 288 114;
#P fasten 6 0 8 0 228 228 288 228;
#P connect 8 0 15 0;
#P connect 15 0 14 0;
#P connect 14 0 12 0;
#P connect 12 0 13 0;
#P pop;


July 27, 2006 | 1:53 pm

use overdrive~?

max v2;
#N vpatcher 219 140 819 540;
#P user scope~ 291 220 421 350 256 3 128 -1. 1. 0 0. 0 0. 102 255 51 135 135 135 0;
#P user hslider 364 86 18 128 128 1 0 0;
#P window setfont "Sans Serif" 9.;
#P window linecount 1;
#P newex 365 108 111 196617 scale 0 127 1 20 1.06;
#P user ezdac~ 177 277 221 310 0;
#P flonum 367 128 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P newex 180 208 70 196617 overdrive~ 1;
#P newex 180 176 64 196617 cycle~ 1.34;
#P connect 0 0 1 0;
#P connect 2 0 1 0;
#P connect 1 0 6 0;
#P connect 5 0 4 0;
#P connect 4 0 2 0;
#P pop;


July 27, 2006 | 2:24 pm

i have not been too impressed with overdrive cos it cliks and pops if u change the drive factor quickly with the mouse. that’s why i like to keep things in signal domain… (maybe c74 can fix this!)

also i’m curious about the maths behind tweaking the transients of a waveform…


July 27, 2006 | 2:38 pm

i find scaling the audio signal going into overdrive~
gets me all my waveshaping fun with out clicks and pops.
so like:

insig~ line~
/
[*~ 32.]

overdrive~

outsig~

then again one might be able to plug a line~ object into the drive
factor input of overdrive~. that might also eliminate the clicks and
pops of changing audio parameters at control rates…

> i have not been too impressed with overdrive cos it cliks and pops
> if u change the drive factor quickly with the mouse. that’s why i
> like to keep things in signal domain… (maybe c74 can fix this!)


July 27, 2006 | 2:49 pm

—–BEGIN PGP SIGNED MESSAGE—–
Hash: SHA1

On Jul 27, 2006, at 10:24 AM, justin wrote:

> i have not been too impressed with overdrive cos it cliks and pops
> if u change the drive factor quickly with the mouse. that’s why i
> like to keep things in signal domain… (maybe c74 can fix this!)

take a look at [sig~] to convert your floats to signal control and
[slide~] to smooth out changes.

feed this into [overdrive~]‘s second inlet.

—–BEGIN PGP SIGNATURE—–
Version: GnuPG v1.4.1 (Darwin)

iD8DBQFEyNJpRvcxtaNGgooRAj9qAJ9KO02lEcvHlsNRbEcI9GXCnZM+lQCfd1Am
OStX/J3BZNk/42C765aIOLE=
=vv8W
—–END PGP SIGNATURE—–


July 27, 2006 | 3:01 pm

didnt think overdrive could take sig to control drive factor, thought this was only float… i will try it out a bit later, busy at mo!

any clues as to maths behind it would still be appreciated?

cheers, j


July 27, 2006 | 4:55 pm

> any clues as to maths behind it would still be appreciated?

hm, just took a quick look at your patch and i guess you confused the
inlets of the pow~ object. left is exponent right is base.
try swapping and see if it gets you closer to where you want to go.
there are a lot of different approaches towards creating waveshaping
functions.
i don’t know which one overdrive~ uses, but it looks kind of similar
to an arctan-distortion.

#P outlet 60 191 15 0;
#P inlet 60 50 15 0;
#P window setfont "Sans Serif" 9.;
#P window linecount 1;
#P newex 60 143 51 196617 *~;
#P flonum 101 59 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P newex 60 91 27 196617 *~;
#P newex 60 115 35 196617 atan~;
#P newex 101 115 92 196617 expr 1./atan($f1);
#P comment 142 60 100 196617 dist amount;
#P connect 5 0 7 0;
#P connect 6 0 3 0;
#P connect 4 0 3 1;
#P connect 4 0 1 0;
#P connect 2 0 5 0;
#P connect 3 0 2 0;
#P connect 1 0 5 1;
#P window clipboard copycount 8;

also check the archives, i think jhno once proposed some techniques.
gruess,
vb


July 28, 2006 | 11:52 am

cheers vb!

i managed to get it to work with pow thanks to swapping inputs.
i knew i was along the right lines…

i also looked at ur patch which seems much cleaner. for a start you dont have to split positive and negative signals, which means less objects… however i managed to find jhno’s thread on reaktor saturation module, and discovered you can go even simpler!

*~ (drive factor) > tanh~ = saturation / distortion

(IMO) this also seems to produce a warmer tone with less fuzz in the hi end than my pow~ attempts, which is harsher on the transients. not sure how it compares to ur patch, altho my guess is they are probably quite similar…

thanks again!

j


July 28, 2006 | 12:57 pm

Here’s another approach using sinus. Looking at the patch it might look
ridiculous, but it sounds really good and warm. I believe this is the
distortion jhno ended up using for the overdrive in radiaL.

Me and Tim discussed these issues a lot a while ago, and ended with Tim
implementing both this and the tanh approach in the overdrive external
used in Jamoma: jmod.saturation~. Note that using tanh or sinus in the
signal loop like this is computationally expensive. Lookup tables are
much cheaper. jmod.saturation use lookup and linear interpolation AFAIR.

I should add that for some funny reason the mach-o version of
jmod.saturation~ don’t like to load on Max 4.5.7 or lower. In Max 4.6 it
works fine.

Best,
Trond

#P window setfont "Sans Serif" 9.;
#P window linecount 1;
#N vpatcher 746 350 1133 665;
#P window setfont "Sans Serif" 9.;
#P newex 50 219 27 196617 *~;
#P newex 50 146 27 196617 *~;
#P newex 197 154 21 196617 sin;
#P newex 197 130 98 196617 clip 0.01 1.570796;
#P newex 197 176 31 196617 !/ 1.;
#P newex 197 61 65 196617 maximum 1.;
#P newex 197 89 129 196617 scale 0. 100. 0. 3.141593;
#P newex 50 179 35 196617 sinx~;
#P inlet 50 33 15 0;
#P inlet 197 33 15 0;
#P outlet 50 241 15 0;
#P connect 2 0 9 0;
#P connect 9 0 3 0;
#P connect 3 0 10 0;
#P connect 10 0 0 0;
#P connect 4 0 9 1;
#P connect 6 0 10 1;
#P connect 1 0 5 0;
#P connect 5 0 4 0;
#P connect 4 0 7 0;
#P connect 7 0 8 0;
#P connect 8 0 6 0;
#P pop;
#P newobj 202 202 92 196617 p sinus_overdrive;
#P user scope~ 202 238 332 368 256 3 128 -1. 1. 0 0. 0 0. 102 255 51 135
135 135 0;
#P user scope~ 60 238 190 368 256 3 128 -1. 1. 0 0. 0 0. 102 255 51 135
135 135 0;
#P newex 60 92 69 196617 triangle~ 0.5;
#P newex 60 62 64 196617 phasor~ 1.4;
#P flonum 284 74 35 9 0. 100. 3 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P user ezdac~ 418 57 462 90 0;
#P comment 283 51 100 196617 0 – 100;
#P connect 4 0 5 0;
#P connect 4 0 7 0;
#P connect 2 0 7 1;
#P connect 7 0 6 0;
#P connect 3 0 4 0;
#P window clipboard copycount 8;

justin wrote:
> cheers vb!
>
> i managed to get it to work with pow thanks to swapping inputs.
> i knew i was along the right lines…
>
> i also looked at ur patch which seems much cleaner. for a start you dont have to split positive and negative signals, which means less objects… however i managed to find jhno’s thread on reaktor saturation module, and discovered you can go even simpler!
>
> *~ (drive factor) > tanh~ = saturation / distortion
>
> (IMO) this also seems to produce a warmer tone with less fuzz in the hi end than my pow~ attempts, which is harsher on the transients. not sure how it compares to ur patch, altho my guess is they are probably quite similar…
>


July 28, 2006 | 1:54 pm

only problem i have with table lookup is that changing the curve in real-time becomes problematic, especially as far as pluggo is concerned.

i tried to use defer to de-prioritize the uzi>function>peek~ part of the patch. but it still goes mental – gui in ableton live goes real slo and automation is a no go… maybe i’m doing something wrong? not quite sure where the defer bit should go in the patch!

your sine patch is quite different to the tanh, seems to be more subtle. can u still overdrive it as much as tanh? looks like ur kinda folding a sine wave twice over itself… but doesnt seem to have the grunt of tanh if u see wot i mean!

j


July 28, 2006 | 2:15 pm

I can understand that, but it depends on what kind of changes you want
to be able to do. If you look at the example I posted, I’m effectively
changing the curve. This because input is scaled before being wave
shaped, and then rescaled afterwards according to the maximum output
value possible for a signal in the range [-1,1]. So if the overdrive
value is close to 0 almost no distortion is added, as only a small
almost linear section of the sinus curve around origo is used. As it is
increased to 100% the distortion gets more and more pronounced. But all
the way maximum amplitude of the output stays consistent.

If you want to be able to move between radically different kinds of
curves (and not just use a larger or smaller part of it), you could also
contemplate using a jitter matrix. If you have for instance a jit.matrix
1 float32 512 2, and use jit.peek~ @interp 1 you could load one curve
into row 0 and another into row 1. By moving back and forth between row
0 and 1 you will move between the curves. Of course this is more
expensive than using a single lookup table as you are interpolating in
2D instead of 1D.

You would be able to expand this further either by adding more rows so
that you have more curves to choose between, or by dynamically changing
the cell values of one or the other row so that you substitute an old
curve for a new one.

You could also check out 2Dwave~.

Best,
Trond

justin wrote:
> only problem i have with table lookup is that changing the curve in real-time becomes problematic, especially as far as pluggo is concerned.
>
> i tried to use defer to de-prioritize the uzi>function>peek~ part of the patch. but it still goes mental – gui in ableton live goes real slo and automation is a no go… maybe i’m doing something wrong? not quite sure where the defer bit should go in the patch!
>
> your sine patch is quite different to the tanh, seems to be more subtle. can u still overdrive it as much as tanh? looks like ur kinda folding a sine wave twice over itself… but doesnt seem to have the grunt of tanh if u see wot i mean!
>


July 28, 2006 | 2:37 pm

actually i quite like ur sin distort patch to get a bit more warmth… works a treat if u put it before tanh!

thanks for all suggestions!

j


July 28, 2006 | 5:55 pm

> your sine patch is quite different to the tanh, seems to be more subtle. can u still overdrive it as much as tanh? looks like ur kinda folding a sine wave twice over itself… but doesnt seem to have the grunt of tanh if u see wot i mean!
>
> j

hmm hmm i would call the effect you can reach
with mult with [tanh~] a "saturation effect" – and
i find it still quite a subtile effect. :)

i get good results in that direction by using two
[tanh~] based DSPs in series.

if your signal input is not music, but only sinewaves
of a known frequency, then it can be worth looking
into [kink~] instead


July 28, 2006 | 6:13 pm

that arctan distortion uses half the CPU of overdrive~.
very nice


July 29, 2006 | 12:38 am

simpler!
>
> *~ (drive factor) > tanh~ = saturation / distortion
>
>

adding a +~ between *~ and tanh~ is also nice, because it creates asymetrical distortion.

cheers,

mzed


July 29, 2006 | 2:36 am

save this as

[110.saturn~]

max v2;
#N vpatcher 741 688 1348 930;
#N comlet SIGNAL;
#P outlet 47 191 15 0;
#N comlet SIGNAL;
#P inlet 47 26 15 0;
#N vpatcher 734 712 884 879;
#N comlet Negtive Part of the Signal;
#P outlet 76 116 15 0;
#N comlet Positive Part of the Signal;
#P outlet 18 116 15 0;
#N comlet Signal In;
#P inlet 76 34 15 0;
#P newex 18 71 47 196617 +~ 1.;
#P newex 18 92 68 196617 gate~ 2;
#P newex 18 50 48 196617 < ~ 0.;
#B color 12;
#P connect 3 0 0 0;
#P connect 0 0 2 0;
#P connect 2 0 1 0;
#P connect 1 0 4 0;
#P connect 3 0 1 1;
#P connect 1 1 5 0;
#P pop;
#P newobj 443 42 101 196617 p Polarsplit=;
#P newex 298 173 82 196617 -~ 0.5;
#B color 15;
#P newex 298 146 82 196617 *~ -0.5;
#B color 15;
#P newex 211 91 82 196617 -~ 3.141597;
#B color 5;
#P newex 211 60 82 196617 *~ -6.28319;
#B color 15;
#P newex 298 109 82 196617 tanh~;
#B color 5;
#P newex 102 173 82 196617 +~ 0.5;
#B color 5;
#P newex 102 146 82 196617 *~ 0.5;
#B color 5;
#P newex 15 91 82 196617 -~ 3.141596;
#B color 5;
#P newex 15 60 82 196617 *~ 6.28319;
#B color 5;
#P newex 102 109 82 196617 tanh~;
#B color 5;
#P connect 10 0 1 0;
#P connect 1 0 2 0;
#P connect 4 0 12 0;
#P connect 9 0 12 0;
#P connect 2 0 0 0;
#P connect 0 0 3 0;
#P connect 3 0 4 0;
#P connect 10 1 6 0;
#P connect 6 0 7 0;
#P connect 7 0 5 0;
#P connect 5 0 8 0;
#P connect 8 0 9 0;
#P connect 11 0 10 0;
#P pop;

[110.saturn~.help]

max v2;
#N vpatcher 560 800 1043 1126;
#P hidden newex 42 152 62 196617 loadbang;
#P comment 69 108 92 196617 saturated phasor;
#P message 42 127 25 196617 2;
#P message 42 107 25 196617 1;
#P toggle 115 233 20 0;
#P newex 42 254 50 196617 dac~;
#P newex 42 184 186 196617 selector~ 2;
#P flonum 218 108 96 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P newex 218 136 96 196617 cycle~ 110;
#P comment 15 55 433 196620 simple audio saturation based on a hyperbolic tangent function;
#P hidden newex 341 115 104 196617 bgcolor 250 150 50;
#P newex 130 154 74 196617 110.saturn~;
#B color 5;
#P comment 8 27 142 196626 110.saturn~;
#P comment 69 128 64 196617 dry phasor;
#P hidden connect 13 0 11 0;
#P hidden connect 11 0 7 0;
#P hidden connect 10 0 7 0;
#P connect 9 0 8 0;
#P connect 7 0 8 0;
#P connect 5 0 2 0;
#P connect 2 0 7 1;
#P connect 6 0 5 0;
#P connect 5 0 7 2;
#P pop;

-110


July 29, 2006 | 10:16 am

i’ve been using something like this for waveshaping in an FM synth.

max v2;
#N vpatcher 200 121 748 652;
#P window setfont "Sans Serif" 9.;
#P window linecount 1;
#P comment 376 136 100 196617 sawness;
#P comment 374 268 100 196617 squareness;
#P flonum 276 136 96 9 1. 0 1 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P newex 168 108 64 196617 phasor~ 1.4;
#P newex 169 158 40 196617 cycle~;
#P newex 199 135 34 196617 kink~;
#N vpatcher 10 59 610 459;
#P window setfont "Sans Serif" 9.;
#P newex 50 91 27 196617 /~;
#P newex 70 138 27 196617 *~;
#P newex 93 91 34 196617 pow~;
#P newex 83 50 31 196617 abs~;
#P inlet 50 71 15 0;
#P inlet 93 71 15 0;
#P outlet 70 160 15 0;
#P connect 2 0 6 0;
#P connect 3 0 6 1;
#P connect 4 0 5 0;
#P connect 5 0 0 0;
#P connect 2 0 3 0;
#P connect 6 0 5 1;
#P connect 1 0 4 0;
#P connect 3 0 4 1;
#P pop;
#P newobj 169 196 95 196617 p punk;
#P newex 277 229 60 196617 loadmess 1;
#P flonum 277 197 96 9 1. 0 1 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P user ezdac~ 195 334 239 367 0;
#P flonum 277 267 96 9 1. 0 1 3 0 0 0 221 221 221 222 222 222 0 0 0;
#N vpatcher 10 59 610 459;
#P inlet 88 57 15 0;
#P outlet 67 198 15 0;
#P inlet 67 57 15 0;
#P window setfont "Sans Serif" 9.;
#P newex 67 150 51 196617 *~;
#P flonum 108 66 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P newex 67 98 27 196617 *~;
#P newex 67 122 35 196617 atan~;
#P newex 108 122 92 196617 expr 1./atan($f1);
#P comment 149 67 100 196617 dist amount;
#P connect 6 0 3 0;
#P connect 3 0 2 0;
#P connect 2 0 5 0;
#P connect 5 0 7 0;
#P connect 4 0 3 1;
#P connect 8 0 4 0;
#P connect 4 0 1 0;
#P connect 1 0 5 1;
#P pop;
#P newobj 169 267 94 196617 p arctan distortion;
#P user scope~ 49 336 179 466 256 3 128 -1. 1. 0 0. 0 0. 102 255 51 135 135 135 0;
#P flonum 175 41 54 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;
#P comment 376 197 100 196617 punk;
#P connect 3 0 2 0;
#P connect 1 0 11 0;
#P connect 10 0 8 0;
#P connect 8 0 3 0;
#P connect 11 0 9 0;
#P connect 9 0 10 1;
#P connect 12 0 9 1;
#P connect 4 0 3 1;
#P connect 6 0 8 1;
#P connect 7 0 6 0;
#P connect 7 0 4 0;
#P pop;


Viewing 17 posts - 1 through 17 (of 17 total)