electro-music.com   Dedicated to experimental electro-acoustic
and electronic music
 
    Front Page  |  Radio
 |  Media  |  Forum  |  Wiki  |  Links
Forum with support of Syndicator RSS
 FAQFAQ   CalendarCalendar   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   LinksLinks
 RegisterRegister   ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in  Chat RoomChat Room 
go to the radio page Live at electro-music.com radio 1 Please visit the chat
poster
 Forum index » DIY Hardware and Software » ChucK programming language
distorsion
Post new topic   Reply to topic Moderators: Kassen
Page 2 of 2 [48 Posts]
View unread posts
View new posts in the last week
Mark the topic unread :: View previous topic :: View next topic
Goto page: Previous 1, 2
Author Message
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Sat Sep 01, 2007 12:19 pm    Post subject: Reply with quote  Mark this post and the followings unread

Is it because "^" in that language needs a lot of calculations?
If it's the case, can you try using
in0 * in0 instead of in0 ^ 2
and in0 * in0 * in0 instead of in0 ^ 3?
(and also on another power signs)

If i'm not wrong, using multiplication straight ahead like this could really save a lot of CPU power.
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Consul



Joined: May 05, 2007
Posts: 59
Location: Port Huron, Michigan, USA

PostPosted: Sat Sep 01, 2007 12:44 pm    Post subject: Reply with quote  Mark this post and the followings unread

Yeah, I was thinking the same thing myself. I guess it's worth a try.

EDIT: Oh, yeah! HUGE difference. Both algos are down to 1.7-1.8% now. Time to integrate these into the new Compstortion code (which has log-based input gain and output attenuation in it now).

_________________
Darren Landrum

"Never be afraid to try something new. Remember that a lone amateur built the Ark. A large group of professionals built the Titanic." - Dave Barry
Back to top
View user's profile Send private message AIM Address
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Sat Sep 01, 2007 3:02 pm    Post subject: Reply with quote  Mark this post and the followings unread

Consul: I've got a new formula for you to try:

I've been playing around with these transfer functions:
x/(1+|x|) - - - (1)
x / (1 + x^2) - - - (2)
x^3 / (1 + |x^3|) - - - (3)
and I like their characters..

But there is a problem with the x^3 / (1 + |x^3|)
is that.. if the amplitude of the input signal is very low,
the output would be much lower

The plot of this function already shows what I mean, you can take a look at:
http://electro-music.com/forum/gallery2.php?g2_itemId=6344

Actually, I'm aware of this problem since I tested it,
but I just couldn't wait to spread it because it has such an interesting overdrive characteristic and I like it.

So.. This new one will be .. hmm
sort of.. a combination of (1) and (3) in character
but without the particular problem.

f(x) = x(1+x^2) / (1 + | x(1+x^2) | )
the plot of the function:
http://electro-music.com/forum/gallery2.php?g2_itemId=6346

If we replace x^2 with x^4, we'll get a more "squarish" sound,
or replace with x^2+x^4 to get that character but with smoother sound.

I'll display the implementation in ChucK here soon.
- - -

Here's the test of the above formula's implementation in ChucK

Code:
class overdrive03_01_02
{
   // this overdrive UGen applies f(x) = x(1+x^2) / (1 + abs(x(1+x^2))) waveshaping function
   // to the "in"put signal and output to "out".
   
   Gain in; Gain out; // chuck or unchuck this to the outside world to connect;
   
   // prepare f(input) = input(1+input^2)

   Step one => Gain f1;
   1.0 => one.next;         <-- 1

   in => Gain f2;
   in => Gain inDummy1 => f2;
   3 => f2.op;            <-- input ^ 2
   f2 => f1;            <-- 1 + input ^ 2

   in => Gain f;
   f1 => f;
   3 => f.op;            <-- input(1 + input^2)

   one => Gain divisor;
   f => FullRect Abs;         <-- abs(f)
   Abs => divisor;         <-- 1 + abs(f)
   
   // calculate f / (1 + abs(f)) and send to "out"
   f => out;
   divisor => out;
   4 => out.op;    <-- make out do a division of the inputs
}


// Testing by feeding Sine Wave at 110hz into the drive unit.
overdrive03_01_02 drive;
SinOsc s => drive.in;
drive.out => dac;

110.0 => s.freq;

for(int i; i < 10; i++)
{
   i * .5 => s.gain;
   second => now;
}


and this is for the replacement of x^2 to x^4

Code:
class overdrive03_01_03
{
   // this overdrive UGen applies f(x) = x(1+x^4) / (1 + abs(x(1+x^4))) waveshaping function
   // to the "in"put signal and output to "out".
   
   Gain in; Gain out; // chuck or unchuck this to the outside world to connect;
   
   // prepare f(input) = input(1+input^2)

   Step one => Gain f1;
   1.0 => one.next;         <-- 1

   in => Gain f2;
   in => Gain inDummy1 => f2;
   f2 => Gain f3;
   f2 => Gain f2Dummy1 => f3;
   3 => f2.op;            
   3 => f3.op;            <-- input ^ 4
   f2 => f1;            <-- 1 + input ^ 4

   in => Gain f;
   f1 => f;
   3 => f.op;            <-- input(1 + input^4)

   one => Gain divisor;
   f => FullRect Abs;         <-- abs(f)
   Abs => divisor;         <-- 1 + abs(f)
   
   // calculate f / (1 + abs(f)) and send to "out"
   f => out;
   divisor => out;
   4 => out.op;    <-- make out do a division of the inputs
}


// Testing by feeding Sine Wave at 110hz into the drive unit.
overdrive03_01_03 drive;
SinOsc s => drive.in;
drive.out => dac;

110.0 => s.freq;

for(int i; i < 10; i++)
{
   i * .5 => s.gain;
   second => now;
}

- - -

Hmm the sound's not that different while testing with SinOsc.
I'd better test it with more instruments soon..
Guitars, Rhodes, Organ, Synth, Drums, ETC.

NOTE: These example uses the new ChucK 1.2.1.0 comment feature:
using <-- is like commenting with // now
so if you're on an older ChucK version,
you can put // in front of every <-- I used for commenting.
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Consul



Joined: May 05, 2007
Posts: 59
Location: Port Huron, Michigan, USA

PostPosted: Sat Sep 01, 2007 3:46 pm    Post subject: Reply with quote  Mark this post and the followings unread

I really wish I had your math skills. Cool

I'll play around with this and report back.

EDIT: Well, I'm reporting back, and I can barely hear the difference between this latest algo with x^2 and the bypassed state, to tell you the truth. With the x^4 variation at very high drives, it gives something of a subtle exciter sound more than anything, which is not bad in and of itself, really. I'm going to play around with that some more, and maybe make it it's own plugin.

In the end, I think I'll stick with the first two algos, and play around with this latest one later.

Now I get to figure out how to do a filter. I don't know if I'm up to it.

EDIT 2: The Jesusonic version of these algorithms are horribly program-dependent, to the point that it's frustrating me. I don't know what to do about that, other than let people decide what they want to do with it, if anything.

_________________
Darren Landrum

"Never be afraid to try something new. Remember that a lone amateur built the Ark. A large group of professionals built the Titanic." - Dave Barry
Back to top
View user's profile Send private message AIM Address
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Tue Dec 18, 2007 3:46 am    Post subject: Reply with quote  Mark this post and the followings unread

Related to this topic I have a feature request for a WaveShaper class.
It would take a chucked input and output a shaped wave:
ouput = WaveShaper.function(input);

Different functions would at least include:
-Polynomial with user defined coefficients (up to 8th degree would propably be enough).
-Trigonometric (sin,cos,tan,asin,acos,atan)
-Exponential
-Logarithmic (natural)
-Hyperbolic (sinh,cosh,tanh,asinh,acosh,atanh) //These could be done with exp, ln and .op(4) but it's nice to have them predifined.
-Limit: if(input > upper_limit) output = upper_limit;
if(input < lower_limit) output = lower_limit;
else output = input;
Back to top
View user's profile Send private message
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Tue Dec 18, 2007 6:17 am    Post subject: Reply with quote  Mark this post and the followings unread

kijjaz wrote:

But there is a problem with the x^3 / (1 + |x^3|)
is that.. if the amplitude of the input signal is very low,
the output would be much lower.


Just replace x^3 with ( x+x^3 ) in that formula to get a slope of 1 at x=0.

(x+a*x^3) / (1 + |x + a*x^3|) //now the parameter a controls the tightness of the sound distortion.

You can play with f(y) = y / (1 + |y|) by replacing y with any function g(x) so that g(0) = 0 and dg/dx|(x = 0) = 1 (the slope is 1 at x = 0). Now also f(g(x)) will have a slope of 1 at zero. (it means that low aplitudes will output without any noticeable distortion).

EDIT:
I just made a hyberbolic tangent out of a Gain network using Gauss's continued fraction approximation.
If you need a tanh waveshaper now. Try it out.
Code:
//tanh(x) = x/(1+x²/(3+x²/(5+x²/(...

//Define the needed Gain network.
5 => int depth; //How big the resulting network will be.
Gain input;
Gain output;
Gain squared; squared.op(3); //Set operation to multiply
Gain square_dummy => squared; input => square_dummy; input => squared;
Step odd_numbers[depth];
Gain sum[depth];
Gain divider[depth];

make_tanh(); //Connect everything to get Gauss's continued fraction for the hyperbolic tangent.

//Test it:
SinOsc s => input; output => dac;

0.0 => float x;
while(x < 5.0){ //at depth = 5 the ouput follows tanh quite closely up to amplitudes 5.0, then it starts to drop. Increase deth if you need more than that.
x => s.gain;
x + 0.01 => x;
10::ms => now;
}


//---Functions---
fun void make_tanh(){
for(0 => int i; i < depth; i++){ divider[i].op(4); }//Set operation to divide;
input => divider[0];

for(0 => int i; i < depth - 1; i++){
2*i+1 => odd_numbers[i].next;
squared => divider[i+1] => sum[i];
odd_numbers[i] => sum[i] => divider[i];
}

depth*2-1 => odd_numbers[depth-1].next;
squared => sum[depth-1];
odd_numbers[depth-1] => sum[depth-1] => divider[depth -1];

divider[0] => output;
}

I also attached the above as a .ck filer for convenience.


tanhGain.ck
 Description:

Download
 Filename:  tanhGain.ck
 Filesize:  1.12 KB
 Downloaded:  509 Time(s)

Back to top
View user's profile Send private message
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Sat Feb 02, 2008 8:44 am    Post subject: Reply with quote  Mark this post and the followings unread

Here's my take on a distortion class:
Code:
class Distort{
    Gain in => Gain overdrive;
    Gain out;
    Step unity; 1.0 => unity.next;
    Event process;
   
    fun UGen chuck(UGen @ u){
        u => in;
        return out;
    }
   
    fun void stop(){
        process.broadcast();
    }
   
    fun void order1(){ // y = x / (abs(x)+1)
        overdrive => Gain div => out;
        4 => div.op;
        unity => Gain sum => div;
        overdrive => FullRect abs => sum;
        process => now;
        div =< out;
    }
    fun void order2(){ // y = (x*abs(x)+ x) / (x*x+abs(x)+1)
        overdrive => Gain nominator_sum => Gain div => out;
        4 => div.op;
        unity => Gain denom_sum => div;
        overdrive => FullRect abs => denom_sum;
        overdrive => Gain abspow2 => nominator_sum;
        3 => abspow2.op;
        abs => abspow2;
        abspow2 => FullRect pow2 => denom_sum;
        process => now;
        div =< out;
    }
    fun void ribbon(){ // y = x / (0.25*x*x + 1)
        overdrive => Gain div => out;
        4 => div.op;
        unity => Gain sum => div;
        overdrive => Gain pow2 => sum;
        3 => pow2.op;
        overdrive => Gain pow2dummy => pow2;
        0.25 => pow2.gain;
        process => now;
        div =< out;
    }
   
    fun void remove_linear(){
        in =< overdrive;
        process => now;
        in => overdrive;
    }
       
    fun void overdrive3(){ //Thanks to kijjaz for the idea
        in => Gain pow3 => overdrive;
        3 => pow3.op;
        in => Gain pow3dummy1 => pow3;
        in => Gain pow3dummy2 => pow3;
        process => now;
        pow3 =< overdrive;
    }
   
    fun void overdrive5(){
        in => Gain pow5 => overdrive;
        3 => pow5.op;
        in => Gain pow2 => pow5;
        3 => pow2.op;
        in => Gain pow2dummy => pow2 => Gain pow4dummy => pow5;
        process => now;
        pow5 =< overdrive;
    }
   
       
}
   
Distort d;
SinOsc s => d.chuck => dac; //Can be chucked almost like a real UGen :)
220.0 => s.freq;

//Play 3 second crescendos with the SinOsc

//Try order1
spork~d.order1();
0.0 => s.gain;
for(now + 3::second => time later;now < later; 10::ms => now){
    s.gain() + 0.01 => s.gain;
}

d.stop();
second => now;

//Try order2
spork~d.order2();
0.0 => s.gain;
for(now + 3::second => time later;now < later; 10::ms => now){
    s.gain() + 0.01 => s.gain;
}

d.stop();
second => now;

//Try some heavy overdrive with ribbon
spork~d.remove_linear();
spork~d.overdrive3();
spork~d.overdrive5();
spork~d.ribbon();
0.0 => s.gain;
for(now + 3::second => time later;now < later; 10::ms => now){
    s.gain() + 0.01 => s.gain;
}

d.stop();
second => now;

d.out =< dac; //d.out is silent but let's unchuck for good measure
s =< d.in; //This is actually not necessary because after calling d.stop() d.in isn't connected to anything.

1.0 => s.gain;
s => dac; //Just a final unprocessed beep

second => now;


EDIT: I just tried this one in action in a song... It generates heavy garbage when called and sporked multiple times... you have been warned.
(Or my laptop is acting up again)

EDIT2: I fixed a bug with overdrive5 and added y = x/(0.25*x*x + 1)

_________________
To boldly go where no man has bothered to go before.
Back to top
View user's profile Send private message
meestaplu



Joined: Apr 22, 2008
Posts: 1
Location: Connecticut

PostPosted: Tue Apr 22, 2008 7:35 pm    Post subject: Reply with quote  Mark this post and the followings unread

I implemented a lookup table in ChucK that lets you do a tanh distortion in less than 10 lines:

Code:
int i;
float v;
2048 => int MAX;
1.03 => float nor;
for(-MAX => i; i<MAX; 1 +=> i) {
    Math.tanh(7*(i+0.0)/MAX)/nor => v;
    //(i+0.0)/MAX => float x;
    //Math.sin(x*pi/2.0)*0.99 => v;
    lut.set(i+MAX, v);
}

SinOsc sin_osc => lut => dac;


I left a sine distortion in there, commented out, so you can see how to use different values.

It's kind of crappy right now, but it works.

It's a unit generator called LUT (lookup table) that has 4096 buckets. Bucket 0 maps input value -1.0 to an output value...bucket 4095 maps input value 1.0 to an output value.

The idea is that you precompute your transfer function -- because of the precomputation it doesn't take much CPU to do real-time processing. When I get it decent, I'll submit it as a patch to the official build so others can use it -- I don't like my .set() function, though, and would rather have an array input so you don't have to keep in mind the secret number of buckets in the LUT.

What do you think on the interface?

Matt

[edit: disabled HTML]
Back to top
View user's profile Send private message
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Wed Apr 23, 2008 12:11 am    Post subject: Reply with quote  Mark this post and the followings unread

A nice idea.
I'd really like to use chucks own float arrays as the lookup table and then just @=> it to the LookUpTable UGen.
Well actually I'm thinking about implementing DigitalTape,an interpolating lookup (and writeup :) ) table with fractional loop point control using chuck's float array as the samples.

But a CPU effecient UGen like you have in mind would come in handy too.

Btw. Where are you planning on posting the source once you're done?

_________________
To boldly go where no man has bothered to go before.
Back to top
View user's profile Send private message
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Wed Apr 23, 2008 12:29 am    Post subject: Reply with quote  Mark this post and the followings unread

oh yeah.. that'd be great. if we can have lookup table ugen that can easily be used with a float array,
we'll have a lot more implementations.
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Thu Jul 03, 2008 1:03 pm    Post subject: Reply with quote  Mark this post and the followings unread

Today I was chatting with avaruus (in #chuck in freenode irc) and we talked about distortion, so I came back to this topic.
After looking around seeing some general overdrive I prepared as classes in here,
I came up with the idea of joining some ideas in one new class.

here is the result:

overdrive transfer function:
f(x) = f(x) = (ax + bx^2 + cx^3) / (1 + |ax| + bx^2 + |cx^3|)
with initial value a = 1, b = 1, c = 1

here's the easy-to-use result:
Code:
class kjzGenOverdrive01
{
    // this class calculate overdrive transfer function from this function:
    // f(x) = (ax + bx^2 + cx^3) / (1 + bx^2 + |ax + cx^3|)
   
    // to set a, b, c
    // use aInit.next, bInit.next, cInit.next
   
    // or if you want to adjust a, b, c by multiplying with other signal,
    // chuck the signals to ax, bxx, cxxx (their .op's are all MULTIPLY in here)
   
    Gain input, output; // chuck or unchuck this to connect
   
    Step aInit; aInit.next(1); // initialize a
    Step bInit; bInit.next(1); // initialize b
    Step cInit; cInit.next(1); // initialize c
   
    aInit => Gain ax; input => ax; ax.op(3); // calculate ax
    bInit => Gain bxx; input => bxx; input => Gain dummyB1 => bxx; bxx.op(3); // calculate bx^2
    cInit => Gain cxxx; input => cxxx; input => Gain dummyC1 => cxxx; input => Gain dummyC2 => cxxx;
    cxxx.op(3); // calculate cx^3
   
    Gain UPPER; // prepare the upper part of the division
    ax => UPPER; bxx => UPPER; cxxx => UPPER; // calculate ax + bx^2 + cx^3
   
    Step one; // prepare 1
    ax => Gain axANDcxxx; cxxx => axANDcxxx; // calculate ax + cx^3
    axANDcxxx => FullRect ABSaxANDcxxx; // calculate |ax + cx^3|
   
    Gain LOWER; // prepare the lower part of the division
    one => LOWER; bxx => LOWER; ABSaxANDcxxx => LOWER; // calculate 1 + bx^2 + |ax + cx^3|
   
    UPPER => output;
    LOWER => output;
    output.op(4); // calculate f(x) by dividing UPPER with LOWER
}


and this.. the test code:
Code:
kjzGenOverdrive01 OD;

// change these to hear different drive characters
OD.aInit.next(2);
OD.bInit.next(3);
OD.cInit.next(5);
// - -

SinOsc s => OD.input;
OD.output => dac;

for(int i; i < 10; i++)
{
    (i + 1) => s.gain;
    second => now;
}


please feel free to test (over)drive.
- - - - -

There is also another thing, the easy clipper I posted quite some time ago on the wiki, you can check out also at http://wiki.cs.princeton.edu/forums.html/ChucK/kijjaz-utility-classes.ck
Code:
class kjzClipper01 // clip signal within -1 to 1 with simple UGens
{
   Gain input; // chuck input signal to this
   Gain output; // chuck this out to have the result
   
   Step one; 1 => one.next;
   input => HalfRect a;
   one => a; // calculate a from HalfRect(input + 1)
   one => Gain two; 2 => two.gain;
   -1 => a.gain;
   a => HalfRect b;
   two => b; // calculate b from HalfRect(2 - HalfRect(input + 1))
   -1 => b.gain;
   one => output;
   b => output; // the result we want: 1 - HalfRect(2 - HalfRect(input + 1))
}
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Sun Jul 06, 2008 8:17 pm    Post subject: Reply with quote  Mark this post and the followings unread

This is the same version,
but the coefficients (a, b, c) can be adjusted for both upper and lower part of the division.
with this, the overdrive can be more dangerous to control,
but can craft a more variety of tones,
for example, using negative coefficients.

Code:
class kjzGenOverdrive01deep
{
    // this class calculate overdrive transfer function from this function:
    // f(x) = (a1x + b1x^2 + c1x^3) / (1 + |a2x| + b2x^2 + |c2x^3|)
   
    // to set a, b, c
    // use a1Init.next, b1Init.next, c1Init.next
    // and a1Init.next, b1Init.next, c1Init.next
   
    // or if you want to adjust a, b, c by multiplying with other signal,
    // chuck the signals to ax, bxx, cxxx (their .op's are all MULTIPLY in here)
   
    Gain input, output; // chuck or unchuck this to connect
   
    Step a1Init; a1Init.next(1); // initialize a1
    Step b1Init; b1Init.next(1); // initialize b1
    Step c1Init; c1Init.next(1); // initialize c1
    Step a2Init; a2Init.next(1); // initialize a2
    Step b2Init; b2Init.next(1); // initialize b2
    Step c2Init; c2Init.next(1); // initialize c2
   
    a1Init => Gain a1x; input => a1x; a1x.op(3); // calculate a1x
    b1Init => Gain b1xx; input => b1xx; input => Gain dummyB1 => b1xx; b1xx.op(3); // calculate b1x^2
    c1Init => Gain c1xxx; input => c1xxx; input => Gain dummyC1 => c1xxx; input => Gain dummyC2 => c1xxx;
    c1xxx.op(3); // calculate c1x^3
   
    Gain UPPER; // prepare the upper part of the division
    a1x => UPPER; b1xx => UPPER; c1xxx => UPPER; // calculate a1x + b1x^2 + c1x^3
   
    Step one; // prepare 1
    a2Init => Gain a2x; input => a2x; a2x.op(3); // calculate a2x
    b2Init => Gain b2xx; input => b2xx; input => Gain dummyB1_2 => b2xx; b2xx.op(3); // calculate b2x^2
    c2Init => Gain c2xxx; input => c2xxx; input => Gain dummyC1_2 => c2xxx; input => Gain dummyC2_2 => c2xxx;
    c2xxx.op(3); // calculate c2x^3
   
    a2x => Gain a2xANDc2xxx; c2xxx => a2xANDc2xxx; // calculate a2x + c2x^3
    a2xANDc2xxx => FullRect ABSa2xANDc2xxx; // calculate |a2x + c2x^3|
   
    Gain LOWER; // prepare the lower part of the division
    one => LOWER; b2xx => LOWER; ABSa2xANDc2xxx => LOWER; // calculate 1 + b2x^2 + |a2x + c2x^3|
   
    UPPER => output;
    LOWER => output;
    output.op(4); // calculate f(x) by dividing UPPER with LOWER
}


Test code:

Code:
kjzGenOverdrive01deep OD;

// change these to hear different drive characters
OD.a1Init.next(1);
OD.b1Init.next(0);
OD.c1Init.next(0);
OD.a2Init.next(0);
OD.b2Init.next(-1);
OD.c2Init.next(1);

// - -

SinOsc s => OD.input;
OD.output => dac;

for(int i; i < 10; i++)
{
    (i + 1) => s.gain;
    second => now;
}
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Inventor
Stream Operator


Joined: Oct 13, 2007
Posts: 6221
Location: near Austin, Tx, USA
Audio files: 267

PostPosted: Mon Jul 21, 2008 1:05 am    Post subject: Reply with quote  Mark this post and the followings unread

Folks, I'm working with the following distortion class for my Guitar Motion Sensor (GMS) project, and I have some questions.

Code:
// distortion class
class distortion {
   
    // variables
    int my_stage;  // identifies the stage of this reverb
    float my_extremeness;  // the extremeness of this reverb
    float my_control;  // the control value of this reverb
   
    // the patch
    Gain input => Gain boost => Gain divide => Gain output;
    input => FullRect fwr => Gain add => divide;
    Step step => add;
   
    // the patch parameters
    1.5 => boost.gain;
    4 => divide.op;  // make it a divider
    1.0 => step.next;
   
    // potentiometer adjust function
    fun void potentiometer_adjust () {
        while (true) {
            potentiometer => now;
            extremeness[my_stage] => my_extremeness;
            my_extremeness * my_control => input.gain;
        }
    }
   
    // accelerometer adjust function
    fun void accelerometer_adjust () {
        while (true) {
            accelerometer => now;
            control[my_stage] => my_control;
            my_extremeness * my_control => input.gain;
        }
    }
}


I guess the main question is how would you control it. The software provides two input controls, both ranging from 0 to 1 as floats. They are "extremeness" and "accelerometer", where extremeness is set by user control to a fixed value during configuration, and accelerometer is the dynamic input from the system. How would you use these controls to shape distortion? Isn't there a tau parameter that shapes the curve?

_________________
"Let's make noise for peace." - Kijjaz
Back to top
View user's profile Send private message Send e-mail
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Mon Jul 21, 2008 3:51 am    Post subject: Reply with quote  Mark this post and the followings unread

Inventor wrote:
Isn't there a tau parameter that shapes the curve?

I don't see a tau in x/(1+|x|) that you used but we can add one:
x/(1+|x|+tau*(0.25*x^2 - |x|))
Now the tau parameter morphs between the first order:
x/(1+|x|)
and the ribbon distortion:
x/(1+0.25*x^2)


distortion2.png
 Description:
Plot of x/(1+|x|+tau*(0.25*x^2 - |x|))
 Filesize:  58.27 KB
 Viewed:  37686 Time(s)

distortion2.png



_________________
To boldly go where no man has bothered to go before.
Back to top
View user's profile Send private message
Inventor
Stream Operator


Joined: Oct 13, 2007
Posts: 6221
Location: near Austin, Tx, USA
Audio files: 267

PostPosted: Mon Jul 21, 2008 4:51 am    Post subject: Reply with quote  Mark this post and the followings unread

I was thinking of something like this, but i'm not sure. The idea is that the user sets the "extremeness" of the effect, so that if extremeness is low the effect barely occurs even for full control input range, but for large extremeness the effect is more pronounced. Maybe it should be tau*X/(1+tau*|x|)? Dunno. Plots are tau=1, 2.0, and 0.5.


Distortion.jpg
 Description:
distortion of X/(1+tau*|X|)
 Filesize:  45.81 KB
 Viewed:  314 Time(s)
This image has been reduced to fit the page. Click on it to enlarge.

Distortion.jpg



_________________
"Let's make noise for peace." - Kijjaz
Back to top
View user's profile Send private message Send e-mail
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Mon Jul 21, 2008 5:37 am    Post subject: Reply with quote  Mark this post and the followings unread

Inventor wrote:
tau*x/(1+tau*|x|)

For tau < 1.0 I wouldn't put it before the expression. It limits the dynamic range too much. So here's my suggestion:
x , tau = 0
x/(1+tau*|x|) , tau <= 1
tau*x/(1+tau*|x|) , tau greater than 1

So that's basically:
max(tau,1)*x/(1+tau*|x|)

Chuck doesn't have the max function but you can make one from the HalfRect UGen.

max(tau,1) := 1 - halfrect(1 - tau)

_________________
To boldly go where no man has bothered to go before.
Back to top
View user's profile Send private message
Inventor
Stream Operator


Joined: Oct 13, 2007
Posts: 6221
Location: near Austin, Tx, USA
Audio files: 267

PostPosted: Mon Jul 21, 2008 6:42 am    Post subject: Reply with quote  Mark this post and the followings unread

OK, I can do that. Now, is doing that essentially the same as scaling the input by a variable amount? I know, lotsa questions but I want to get this right. Also, on tremolo, I have the following function:

Code:
// tremolo class
class tremolo {
   
    // parameters
    4.0 => float frequency_depth;  // how deep to vary frequency
   
    // variables
    int my_stage;  // identifies the stage of this reverb
    float my_extremeness;  // the extremeness of this reverb
    float my_control;  // the control value of this reverb
   
    // the patch
    Gain input => Gain multiply => Gain output;
    SinOsc sinosc => Gain sum => multiply;
    Step step => sum;
   
    // the patch parameters
    3 => multiply.op;  // make it multiply
    1.0 => step.next;  // sine wave offset
       
    // potentiometer adjust function
    fun void potentiometer_adjust () {
        while (true) {
            potentiometer => now;
            extremeness[my_stage] => my_extremeness;
            my_extremeness * frequency_depth => sinosc.freq;
        }
    }
   
    // accelerometer adjust function
    fun void accelerometer_adjust () {
        while (true) {
            accelerometer => now;
            control[my_stage] => my_control;
            my_control => sinosc.gain;
            my_control => step.next;
        }
    }
}


I don't know, is that frequency_depth value of 4.0 too small? Is the tremolo waveform (1+sin(x)) too subtle for rock? Should it be halfrect(sin(x)) for more zero-time?

_________________
"Let's make noise for peace." - Kijjaz
Back to top
View user's profile Send private message Send e-mail
rogan



Joined: Dec 16, 2007
Posts: 83
Location: Urbana, IL
Audio files: 5

PostPosted: Wed Sep 24, 2008 11:31 pm    Post subject: Reply with quote  Mark this post and the followings unread

Foolin around with some real simple distortion, but I thought I'd share.

Wobble wobble:

Code:

// Sounds so good with Sqr Tri and Sin !!

SinOsc signal => Gain one;
signal => Gain two;

.75 => one.gain;
1.0 - one.gain() => two.gain;

one => SqrOsc overdrive => LPF lpf => dac;
two => TriOsc od2 => lpf => dac;



1 => overdrive.sync; // set sync option to Phase Mod.
1 => od2.sync;

1 => signal.gain;
48 =>signal.freq;

SinOsc roller => blackhole;

2 => roller.sync;
10 => roller.gain;
3.147 => roller.freq;

signal => roller;

72 => lpf.freq;

while (true) {
    lpf.freq() + roller.last() => lpf.freq;
    4::ms => now;
    if (Std.rand2f(0.,1.) < .005) {
        change() => signal.freq;   
    }
}

fun float change () {
    Std.rand2f(0.,1.) => float rand;
   
    if ( rand < 0.4 ) {
        return 48.0;
    } else if ( rand < 0.8 ) {
        return 37.0;
    } else {
        return 33.0;
    }
}

Back to top
View user's profile Send private message
Inventor
Stream Operator


Joined: Oct 13, 2007
Posts: 6221
Location: near Austin, Tx, USA
Audio files: 267

PostPosted: Fri Sep 26, 2008 11:11 am    Post subject: Reply with quote  Mark this post and the followings unread

Just a little observation on how to control the intensity of the distortion: vary the constant like this:

Code:
input / (k + Math.abs(input) ) => output


When you set k to 1 you get a little distortion, and when you set it to 0.1 you get a lot.

At first I tried leaving the value of k=1 and multiplying the input by some value to increase distortion, but i got tons of noise because I was obviously amplifying the noise. duh.

So eventually, and it's comical how long it took me to figure this out but i'm busy with 1,000 things, i figured out that mathematically speaking, multiplying the input by alpha is the same as dividing the constant k by alpha. no duh.

Think about it at the extreme case of alpha = infinity, then k = 0 and the output is simply the sign of the input, which is the sharpest distortion possible.

That may seem really obvious in hindsight but it wasn't obvious to me at first glance, so there you go. Another one ChucKs the dust.

_________________
"Let's make noise for peace." - Kijjaz
Back to top
View user's profile Send private message Send e-mail
cbit



Joined: Dec 01, 2005
Posts: 35

PostPosted: Sun Nov 15, 2009 11:06 am    Post subject: Reply with quote  Mark this post and the followings unread

kijjaz wrote:

Hmm.. How can I test the exact consumption rate of CPU that
chuck is consuming? hopefully for comparing between shreds?
I'd like to try that sometimes to compare between various methods. ^_^


Did you find a way? I'm interested in this too. Thanks for the nice overdrive class!

_________________
http://basementhum.blogspot.com
Back to top
View user's profile Send private message Visit poster's website
Kassen
Janitor
Janitor


Joined: Jul 06, 2004
Posts: 7678
Location: The Hague, NL
G2 patch files: 3

PostPosted: Mon Nov 16, 2009 11:49 am    Post subject: Reply with quote  Mark this post and the followings unread

On Linux or OSX; open a shell and type "top", that should give you cpu usage for ChucK and other programs on your system. On Windows the activity monitor should display this (hit ctrl+alt+delete to bring it up)
_________________
Kassen
Back to top
View user's profile Send private message Send e-mail Visit poster's website
kurtk



Joined: Jan 27, 2010
Posts: 8
Location: usa

PostPosted: Fri Apr 30, 2010 5:56 am    Post subject: Reply with quote  Mark this post and the followings unread

Hey all,
I was reading through this thread about distortion algorithms and there are some really good ones here. The problem is that in ChucK we have to do math every sample to implement them. I think ChucK needs 'Table' UGen that can hold arbitrary function tables. It would need an accessor to the raw values inside of it so it could be filled like a normal ChucK array. Maybe a size constructor. And maybe some methods to set the mode of indexing/reading from the table when a UGen is connected (i.e. [0, 1] or [-1, 1]. And of course interpolation.
Like this...

Code:

Phasor phs => Table t => dac;
1024 => t.size;
for (0 => int i; i < t.size(); i++) {
    Math.sin((i $ float/t.size())*2*pi) => t[i];
}
day => now;


Just something I have always been missing in ChucK.
(now we just need separate delay read and write objects...)
Later.
Kurt

_________________
.............................................
http://kurtkotheimer.com
.............................................
Back to top
View user's profile Send private message Visit poster's website
heuermh



Joined: Dec 15, 2006
Posts: 19
Location: minneapolis

PostPosted: Thu Nov 15, 2012 10:52 am    Post subject: Reply with quote  Mark this post and the followings unread

I have implemented a WaveShaper Chugen and several subclasses that use the various functions discussed here in LiCK.

Example here

https://github.com/heuermh/lick/blob/master/examples/distExample.ck

Thanks to everyone on this forum topic for inspiration. I've linked to here in the source; if you would prefer I use different class names or otherwise change the attribution, please let me know.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic Moderators: Kassen
Page 2 of 2 [48 Posts]
View unread posts
View new posts in the last week
Goto page: Previous 1, 2
Mark the topic unread :: View previous topic :: View next topic
 Forum index » DIY Hardware and Software » ChucK programming language
Jump to:  

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Forum with support of Syndicator RSS
Powered by phpBB © 2001, 2005 phpBB Group
Copyright © 2003 through 2009 by electro-music.com - Conditions Of Use