Old 07-22-2009, 09:06 AM   #1
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default JS function alternatives and useful snippets of code.

this is a thread in which you can contribute:


- alternatives to the default functions in jesusonic - approximations or simply "cheaper" algorithms, but less cpu extensive. share your methods in js code plus any links to original references. the default functions should be considered as the optimal base of comparison, regarding the quality / cpu trade-off.
- other code optimization techniques. - what is faster?
- any snippets of code, that may be useful to future coders.
- "dsp explained" notes - how does a filter work? how to do convolution?
- any interesting dsp related articles, books and lectures.




lubomir

Last edited by liteon; 07-22-2009 at 09:40 AM.
liteon is offline   Reply With Quote
Old 07-22-2009, 09:07 AM   #2
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default fast park–miller random number generator

a fast linear congruential generator (or specifically the park-miller variation) is described below.

basic algorithm:
Code:
@init
rnd=1;
@sample
rnd=rnd*a%m;
but the main problem is the range in our case, since with some of the common lcg parameters we go out of range:
Code:
@init
rnd=1;
@sample
rnd=rnd*16807%2147483647;
// call 0
// 1*16807%2147483647=16807
// call 1
// 16807*16807%2147483647=2824275249
// call 2
// 2824275249*16807%2147483647=1 -> out of range prevention
therefore a larger modulus cannot be used, such as the 2^31-1 -> 'marsenne prime'

so here are some parameters i've calculated based on the "zx-spectrum" multiplier,(77) but with much larger modulus.

Code:
//all with initial seed 1
rnd0*=77;                     //$x4D
rnd0%=16777219;               //$x1000003=2^24+3
rnd1*=76;                     //$x4C
rnd1%=16777221;               //$x1000005=2^24+5
rnd2*=77;                     //$x4D
rnd2%=8388609;                //$x800001=2^23+1
so basically all that is needed is a scaling factor in the end:
Code:
//full code
@init
rnd=1;
@sample
rnd*=77;
rnd%=16777219;
spl0=rnd*0.0000000593; //scale down to 0-1 range
comparison with the marsenne twister implementation in rand().
100 executions of rand() = 61% cpu
100 executions of one recursion lane lcg = 26% cpu

@gfx plots (ermm...30sec freerun)

two rand() x,y:

[img]http://img195.**************/img195/7922/mtwister.png[/img]

two recursions lanes lcg for x,y:

[img]http://img195.**************/img195/5992/gauss.png[/img]

the more expensive gaussian noise version as a bonus:
Code:
//gaussian version
@init
rnd0=rnd1=rnd2=1;
@sample
rnd0*=77;
rnd0%=16777219;
rnd1*=76;
rnd1%=16777221;
rnd2*=77;
rnd2%=8388609;
spl0=(rnd0+rnd1+rnd2)*0.00000002403333333333333; //.... *k
//k=(1/3*0.0000000721)

----
lubomir

Last edited by liteon; 07-25-2009 at 07:44 AM.
liteon is offline   Reply With Quote
Old 07-22-2009, 12:45 PM   #3
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default fast integer conversations

fast fractional to integer conversation for negative or positive input (x):

floor:
Code:
x|=0;
ceil:
Code:
x+=0.9999999999999999*sign(x);
x|=0;
round:
Code:
x+=0.5*sign(x);
x|=0;

---
lubomir

Last edited by liteon; 07-25-2009 at 04:15 AM.
liteon is offline   Reply With Quote
Old 07-22-2009, 12:50 PM   #4
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default video lectures on dsp

Prof. S.C Dutta Roy, Department of Electrical Engineering, IIT Delhi

43 - 1 hour lectures on DSP at:
https://www.youtube.com/view_play_lis...67DFCA3A66F299



----
lubomir
liteon is offline   Reply With Quote
Old 07-22-2009, 01:23 PM   #5
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default exponential frequency mapping

y range: 20-20000hz
---

x range: 0-127
Code:
y=(exp((16+x*0.945699)*0.0573250666192694)*8.17742)|0;
x range: 0-100
Code:
y=(exp((16+x*1.20103)*0.0573250666192694)*8.17742)|0;
x range: 0-1
Code:
//y=exp(x*log(20000/20)+log(20));
y=exp(x*6.90775527898214+2.99573227355399);
y|=0;
graphic:
http://img188.**************/img188/6396/expplot.png

----
lubomir

Last edited by liteon; 08-13-2009 at 07:43 PM.
liteon is offline   Reply With Quote
Old 07-22-2009, 02:28 PM   #6
cern.th.skei
Human being with feelings
 
cern.th.skei's Avatar
 
Join Date: Sep 2007
Location: trondheim, norway
Posts: 316
Default

a really cool initiative!
here's some snippets i've used in a few plugins:

a sin() approxamation for an oscillators, lfo, etc..

Code:
desc:sinusoid (approximation)
slider1: 55 < 1, 44100 > freq left  - sinusoid
slider2: 55 < 1, 44100 > freq right - sin()
@init
  p0 = 0;
  p1 = 0;
@slider
  r0 = (slider1/srate)*4;
  r1 = (slider2/srate);
@sample
// sinusoid
  p0+=r0;
  p0>2 ? p0-=4;
  s0 = p0*(2-abs(p0));
// sin()
  p1+=r1;
  p1>=1 ? p1-=1;
  s1 = sin(p1*($pi*2));
//
  spl0 = s0;
  spl1 = s1;
another random/noise thing

Code:
desc:noise/random
@init
  value   = 19; // initialize
  b_noise = 19.1919191919191919191919191919191919191919;
@sample
// -1..1
  b_noise = b_noise * b_noise;
  i_noise = floor(b_noise); // |0;
  b_noise = b_noise - i_noise;
  r0      = b_noise - 0.5;
  b_noise = b_noise + value;
// 0..1
  b_noiselast = b_noise;
  b_noise = b_noise + value;
  b_noise = b_noise * b_noise;
  b_noise = (b_noise+b_noiselast) * 0.5;
  i_noise = floor(b_noise);
  b_noise = b_noise - i_noise;
  r1      = b_noise;
//
  spl0 = r0;
  spl1 = r1;
and some filtering

Code:
desc:lowpass / weighted average
slider1:0.5<0,1,0.001>weight
@init
  n0 = 0;
  n1 = 0;
@slider
  weight = slider1*slider1;         // ???
@sample
  spl0 =  (n0+=((spl0-n0)*weight)); // lowpass
  spl1 -= (n1+=((spl1-n1)*weight)); // highpass
perhaps not scientifically correct, but useful when cpu and simplicity is important. the noise and sine things are around twice as fast as the builtin rand() and sin() functions.
__________________
later..
cern.th.skei is offline   Reply With Quote
Old 10-17-2013, 01:15 PM   #7
reapercurious
Human being with feelings
 
reapercurious's Avatar
 
Join Date: Jul 2007
Posts: 1,667
Default

Quote:
Originally Posted by liteon View Post
a fast linear congruential generator (or specifically the park-miller variation) is described below.

basic algorithm:
Code:
@init
rnd=1;
@sample
rnd=rnd*a%m;
but the main problem is the range in our case, since with some of the common lcg parameters we go out of range:
Code:
@init
rnd=1;
@sample
rnd=rnd*16807%2147483647;
// call 0
// 1*16807%2147483647=16807
// call 1
// 16807*16807%2147483647=2824275249
// call 2
// 2824275249*16807%2147483647=1 -> out of range prevention
therefore a larger modulus cannot be used, such as the 2^31-1 -> 'marsenne prime'

so here are some parameters i've calculated based on the "zx-spectrum" multiplier,(77) but with much larger modulus.

Code:
//all with initial seed 1
rnd0*=77;                     //$x4D
rnd0%=16777219;               //$x1000003=2^24+3
rnd1*=76;                     //$x4C
rnd1%=16777221;               //$x1000005=2^24+5
rnd2*=77;                     //$x4D
rnd2%=8388609;                //$x800001=2^23+1
so basically all that is needed is a scaling factor in the end:
Code:
//full code
@init
rnd=1;
@sample
rnd*=77;
rnd%=16777219;
spl0=rnd*0.0000000593; //scale down to 0-1 range
comparison with the marsenne twister implementation in rand().
100 executions of rand() = 61% cpu
100 executions of one recursion lane lcg = 26% cpu

@gfx plots (ermm...30sec freerun)

two rand() x,y:

[img]http://img195.**************/img195/7922/mtwister.png[/img]

two recursions lanes lcg for x,y:

[img]http://img195.**************/img195/5992/gauss.png[/img]

the more expensive gaussian noise version as a bonus:
Code:
//gaussian version
@init
rnd0=rnd1=rnd2=1;
@sample
rnd0*=77;
rnd0%=16777219;
rnd1*=76;
rnd1%=16777221;
rnd2*=77;
rnd2%=8388609;
spl0=(rnd0+rnd1+rnd2)*0.00000002403333333333333; //.... *k
//k=(1/3*0.0000000721)

----
lubomir
real world user-understandable application would be appreciated on these snippets, otherwise they are only useful to people who understand terms and concepts that are four orders elevated in mean sophistication.
reapercurious is offline   Reply With Quote
Old 10-20-2013, 04:46 AM   #8
chilledpanda
Human being with feelings
 
chilledpanda's Avatar
 
Join Date: Jan 2013
Location: United Kingdom
Posts: 28
Default

Here's a technique I found useful for debugging, by watching local memory akin to memory watch widows in professional debuggers.

First set up a slider (I use 64 as it's he last one),
This slider allows you to view local 0-to 2048,

slider64:0<0,2048,16>MEMWATCH LOC

Then change/add @slider to read the slider and set the memwatchvariable

@slider
//debugging code
memwatch = floor(slider64);


Add the follow to the @gfx uppdate block this constantly updates so allows us to constantly update the mem watch variables

@gfx

//debug code
memwatch_00 = memwatch[0];
memwatch_01 = memwatch[1];
memwatch_02 = memwatch[2];
memwatch_03 = memwatch[3];
memwatch_04 = memwatch[4];
memwatch_05 = memwatch[5];
memwatch_06 = memwatch[6];
memwatch_07 = memwatch[7];
memwatch_08 = memwatch[8];
memwatch_09 = memwatch[9];
memwatch_10 = memwatch[10];
memwatch_11 = memwatch[11];
memwatch_12 = memwatch[12];
memwatch_13 = memwatch[13];
memwatch_14 = memwatch[14];
memwatch_15 = memwatch[15];



Then in the JS development environment editor make sure the auto refresh is ticked under the left window and then locate the memwatch variables.

Move the slider and see the memory locations update in steps of 16 values.
__________________
-------------------------------------------------------------------------------
http://soundcloud.com/chilled-panda
chilledpanda is offline   Reply With Quote
Old 02-04-2014, 11:09 AM   #9
Argitoth
Human being with feelings
 
Argitoth's Avatar
 
Join Date: Feb 2008
Location: Phoenix, AZ
Posts: 2,055
Default

this page: https://ccrma.stanford.edu/~craig/ar...essenmidi.html seems to have some explanation on midi messages, but does not exactly correlate to JS explanation: http://www.reaper.fm/sdk/js/midi.php#js_midi

Is there a comprehensive list of midi messages/parameters? Like note on/off, channel pressure, midi cc, etc.
__________________
Soundemote - Home of the chaosfly and pretty oscilloscope.
MyReaperPlugin - Easy-to-use cross-platform C++ REAPER extension template
Argitoth is offline   Reply With Quote
Old 02-04-2014, 11:25 AM   #10
James HE
Human being with feelings
 
James HE's Avatar
 
Join Date: Mar 2007
Location: I'm in a barn
Posts: 4,415
Default

Quote:
Originally Posted by Argitoth View Post

Is there a comprehensive list of midi messages/parameters? Like note on/off, channel pressure, midi cc, etc.
here.

http://www.midi.org/techspecs/midimessages.php
James HE is offline   Reply With Quote
Old 07-23-2009, 02:50 AM   #11
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

Yes, great tips here!
So here are some (more basic :-) things I have noted. HTH... Here I focus on:
Quote:
Originally Posted by liteon View Post
- other code optimization techniques. - what is faster?
How to know what is faster (i.e. how to monitor the CPU) ?
We cannot trust Reaper's performance meter (it seems it does not take some things into account, such as the @gfx thread). So, what I do: I set up a test project, I clean it as much as possible (usually one media item + the js). Then, I monitor reaper with Win's performance meter (cool for threads but I also simply use the task manager when I'm in an rush) with DISABLED JS, then I just enable it. The CPU use is the diff (note: diff done in the same conditions - just enabling the JS - as play/routing/etc.. have an impact), dif made with hidden and shown GUI.
I'd like to know how other js coders monitor CPU/RAM !?

Where to code ?
This one is really basic - I know - but it must be said! I often see that and it leads to the best optimizations => put the maximum of code in the @block part (as far as possible: typically periodic processing NOT needed for each sample), and, of course, the less in the @sample.
Then, you can even lower the processing frequency, e.g.:
Code:
@block
cpt+=samplesblock;
(cpt - lastCpt) > updatefreq ?
(
   lastCpt=cpt;
   // do some heavy stuff
);
where "updatefreq" is in samples *
[edit] *if computed: only when needed! i.e. in the @init or @slider parts.

Branching
Huge impact on CPU.
=> avoid them as far as possible!

=> use logical operator precedence
Code:
cond2 = cond1 || (floor(rand(spl0) * $pi * 1000) % 2);
cond2 = cond1 && (floor(rand(spl0) * $pi * 1000) % 2);
will be faster than:
Code:
cond2 = (floor(rand(spl0) * $pi * 1000) % 2) || cond1;
cond2 = (floor(rand(spl0) * $pi * 1000) % 2) && cond1;
here, floor() & rand() are used for test purpose only

=> some code branching tips
The 2 following code lines do the same thing (where "cond1" is a "boolean"):
Code:
//1)
spi1 = condi1 * slider1 * spl0;
//2)
condi1 ? spi1 = slider1 * spl0 : spi1 = 0;
According to some (old) Justin's post, I was thinking that 1) was the fastest one. In fact, CPU monitoring shows that that fastest one is 2) (Reaper v3.06pre7).
I really don't understand why! If a dev read this post, can he just confirm/say why (very briefly) ?

=> setting a default STATIC value according to a condition
Code:
a=0; cond1 ? a = floor(rand(spl0) * $pi * 1000);
is faster than:
Code:
cond1 ? a = floor(rand(spl0) * $pi * 1000) : a=0;
Midi processing
=> counting samples (basic thing, but I've also seen this possible optimization in a bunch of JS!). We often have to evaluate time elapsed between midi events, for this we have to count samples.
Code:
@block
cpt+=samplesblock; // +use midircv()'s 1st param for sample accurate processing
is faster than:
Code:
@sample
cpt+=1;
[Edit] even more true since v3.06!

coming back later...

Last edited by Jeffos; 07-23-2009 at 01:33 PM. Reason: english wording + better with "compilable" code !
Jeffos is offline   Reply With Quote
Old 07-24-2009, 05:44 AM   #12
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default fast polynomial approximations of exp()

fast approximations of exp() with lagrange polynomials:

Code:
//stable range for x is [-1.5, 1.5]

//3 points polynomial
//y=x*(x*0.601+1.42)+1;
y=x;
y*=0.601;
y+=1.42;
y*=x;
y+=1;

//4 points polynomial
//y=x*(x*(x*0.189+0.612)+0.995)+0.975;
y=x;
y*=0.189;
y+=0.612;
y*=x;
y+=0.995;
y*=x;
y+=0.975;

//5 points polynomial
//y=x*(x*(x*(x*0.046+0.191)+0.498)+0.989)+1;
y=x;
y*=0.046;
y+=0.191;
y*=x;
y+=0.498;
y*=x;
y+=0.989;
y*=x;
y+=1;
plot comparisons with exp():

[img]http://img233.**************/img233/4125/explagrangen.gif[/img]

performance test - 100 executions per sample:
4 point -> 26% cpu
exp(x) -> 56% cpu

example:
Code:
//omega=2*$pi*fc=2*$pi*2000=12566.370614
//x=-omega/srate=-omega/44100=
x=-0.284951;
y0=exp(x);                              //0.7205110
y1=x*(x*(x*0.189+0.612)+0.995)+0.975;   //0.7367934
---
lubomir
liteon is offline   Reply With Quote
Old 07-24-2009, 06:08 AM   #13
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default fast pow() from identity

using the above approximation of exp() and the identity:
Code:
2^x=exp(x*log(2));
it is possible to get a fixed base approximation of b^x:
Code:
@init
//base 2
b=log(2);
@sample
//example with 4 point polynomial:
x*=b;
y=x;
y*=0.189;
y+=0.612;
y*=x;
y+=0.995;
y*=x;
y+=0.975;

//x=1 -> y=2.02166035;
//x=2 -> y=4.03404514;
stable within x range [-2, 2,5]

[img]http://img233.**************/img233/9909/powlagrange.gif[/img]

performance test (100 executions per sample):
4point approximation (2^x) - 33%
inline 2^x - 75%

---

lubomir
liteon is offline   Reply With Quote
Old 07-24-2009, 07:40 AM   #14
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default fractional delay approximation with an allpass filter

fractional delay approximation with an allpass filter:

Code:
//code here
@init
k=0.5;
@sample
y0=spl0-k*(y=y0+k*spl0);
spl0=y;
quick explanation:
the above is a basic one pole allpass filter. in one sentence - allpass filters affect only the phase of the signal while preserving the energy (magnitude) of the signal. this way it is possible to get relative group delay, the amount of which is dependent from a pole position (k). note that group delay value does not correspond directly to the pole i.e. k=0.5 != gd=0.5 samples. this requires extra mapping for user controlled amounts.

[img]http://img233.**************/img233/6681/gdmapping.gif[/img]

example for parameter mapping with an exponent:
Code:
//error is minimized but k should be limited to [0,1] for slider values [0,1]:
slider1:0<0,1,0.01>d
@slider
x=slider1;
k=exp(x*1.29)/$e-0.356;


various fractional delay methods described here:
http://sal.shs.arizona.edu/~smathur/.../MathurCh3.pdf
http://www.acoustics.hut.fi/~vpv/pub...cassp00-fd.pdf



---
lubomir

Last edited by liteon; 07-25-2009 at 02:54 AM.
liteon is offline   Reply With Quote
Old 07-24-2009, 09:48 AM   #15
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default code optimizations

this syntax is troublesome for the given function due to the 'pow' operator:
Code:
y=7*x^4+5*x^3-3*x^2+2*x-1;
this is better:
Code:
y=7*x*x*x*x+5*x*x*x-3*x*x+2*x-1;
even better:
Code:
y=x;
y*=7;
y+=5;
y*=x;
y-=3;
y*=x;
y+=2;
y*=x;
y-=1;
best:
Code:
y=x*(x*(x*(7*x+5)-3)+2)-1;
-----------------------------------------------
however it is slightly faster to execute:
Code:
x=6.28;
x*=2;
x|=0;
x%=3;
over:
Code:
x=6.28;
x=((2*x)|0)%3;
-----------------------------------------------
if there are more variables and recursion:
Code:
@sample
y=y0+k*spl0;
y0=spl0-k*y;
spl0=y;
could be optimized to:
Code:
y0=spl0-k*(y=y0+k*spl0);
spl0=y;
-----------------------------------------------
loops:

loop() - 73% cpu
Code:
i=0;
loop(100,
mem[i]=exp(i);
i+1;
);
while() - 76% cpu
Code:
i=0;
while(
mem[i]=exp(i);
i+=1;
i<100; //<- inlined cmp - eventual cause for 76%
);
-----------------------------------------------
min() / max() or inline cmp:

Code:
x=0.9;
//this call
x<1?x=1;
//is slower than
x=max(x,1);
-----------------------------------------------
abs() and sign() are both quite fast:
correction here:
Code:
//alternative to abs()
x=-0.5;
x*=sign(x);
//700 iterations, x=-0.5, x=abs(x) -> 82.6% cpu, x*=sign(x) -> 84.5% cpu
//700 iterations, x=0.5, x=abs(x) -> 80.1% cpu, x*=sign(x) -> 76.5% cpu
//--
//if input values are mostly with negative sign use abs(x)

---
lubomir

Last edited by liteon; 03-28-2011 at 10:40 PM.
liteon is offline   Reply With Quote
Old 08-20-2009, 08:16 AM   #16
Justin
Administrator
 
Justin's Avatar
 
Join Date: Jan 2005
Location: NYC
Posts: 12,567
Default

Quote:
Originally Posted by Jeffos View Post
=> some code branching tips
The 2 following code lines do the same thing (where "cond1" is a "boolean"):
Code:
//1)
spi1 = condi1 * slider1 * spl0;
//2)
condi1 ? spi1 = slider1 * spl0 : spi1 = 0;
According to some (old) Justin's post, I was thinking that 1) was the fastest one. In fact, CPU monitoring shows that that fastest one is 2) (Reaper v3.06pre7).
I really don't understand why! If a dev read this post, can he just confirm/say why (very briefly) ?
I think it depends on what condi1 refers to.. If it's mostly 0, then 2) should be faster. Other cases, the difference becomes less I'd think.
Justin is offline   Reply With Quote
Old 08-27-2009, 08:34 AM   #17
IXix
Human being with feelings
 
Join Date: Jan 2007
Location: mcr:uk
Posts: 3,386
Default Debugging Aid

Great thread! I can't contribute much but I'll re-post this (slightly improved) snippet here where more people might find it.

Just paste this at the bottom of your @gfx section to display a chunk of memory buffer and six general purpose debugging variables. You can set various parameters in the options bit at the top.

Code:
/*******************************************************************************
    General purpose debugging aid. Paste into the gfx section of your code.

    Variables:
    debug1...debug6      - Displayed at the top of the debug area.
    dOffset              - Index of first item of buffer data to display.

*******************************************************************************/

SHOWDEBUG = 1; //Set non-zero to enable the debug view

SHOWDEBUG ?
(    
    //Options
    dPrecision = 2;             //Decimal places for numbers
    dLeft = 5;                  //Debug window position in pixels
    dTop = 5;                   //Debug window position in pixels
    dRows = 5;                  //Row count for buffer data
    dCols = 6;                  //Column count for buffer data

    dRowHeight = 20;            //Row height offset for buffer data
    dColWidth = 80;             //Column width offset for buffer data
    dMarginX = 5;               //Left margin in pixels
    dMarginY = 5;               //Top margin in pixels
    dBufferY = 30;              //Vertical offset for buffer data display
    
    /* ------------------------------------------------------------- */

    dOld_x = gfx_x;
    dOld_y = gfx_y;
    dOld_a = gfx_a;
    dOld_r = gfx_r;
    dOld_g = gfx_g;
    dOld_b = gfx_b;
    dOld_mode = gfx_mode;

    gfx_a = 1;
    gfx_mode = 0;

    //Calculate debug window size
    dWidth = dCols * dColWidth + dMarginX;
    dHeight = dRows * dRowHeight + dBufferY;

    //Set background colour
    gfx_r=1;gfx_g=1;gfx_b=1;gfx_a=1;

    //Erase
    gfx_x = dLeft;
    gfx_y = dTop;
    gfx_rectto(dLeft + dWidth, dTop + dHeight);

    //Set text colour
    gfx_r=.7;gfx_g=0;gfx_b=.3;

    //Draw debug vars
    gfx_x = dLeft + dMarginX; gfx_y = dTop + dMarginY;
    gfx_drawNumber(debug1, dPrecision);

    gfx_x = dLeft + dMarginX + dColWidth; gfx_y = dTop + dMarginY;
    gfx_drawNumber(debug2, dPrecision);

    gfx_x = dLeft + dMarginX + dColWidth * 2; gfx_y = dTop + dMarginY;
    gfx_drawNumber(debug3, dPrecision);

    gfx_x = dLeft + dMarginX + dColWidth * 3; gfx_y = dTop + dMarginY;
    gfx_drawNumber(debug4, dPrecision);

    gfx_x = dLeft + dMarginX + dColWidth * 4; gfx_y = dTop + dMarginY;
    gfx_drawNumber(debug5, dPrecision);

    gfx_x = dLeft + dMarginX + dColWidth * 5; gfx_y = dTop + dMarginY;
    gfx_drawNumber(debug6, dPrecision);

    //Draw separator
    gfx_x = dLeft + dMarginX;
    gfx_y = dTop + dBufferY - dMarginY;
    gfx_lineto(dLeft + dWidth - dMarginX, gfx_y, 1);

    //Draw buffer data
    di = 0;
    dRow = 0;
    loop
    (
        dRows,
        dCol = 0;
        loop
        (
            dCols,
            gfx_x = dLeft + dMarginX + dCol * dColWidth;
            gfx_y = dTop + dMarginY + dBufferY + dRow * dRowHeight;
            gfx_drawNumber(dOffset[di], dPrecision);
            di += 1;
            dCol += 1;
        );
        dRow += 1;
    );

    //Restore previous state
    gfx_x = dOld_x;
    gfx_y = dOld_y;
    gfx_a = dOld_a;
    gfx_r = dOld_r;
    gfx_g = dOld_g;
    gfx_b = dOld_b;
    gfx_mode = dOld_mode;
);
You can use debug1...debug6 to report the value of a variable at specific points in your code, or they can be handy for tracing which parts of a conditional block are being executed. To use the buffer viewer, just set dOffset to the memory index you're interested in.

I've found it very helpful for dealing with my typically over-complicated scripts.

Last edited by IXix; 08-27-2009 at 08:45 AM. Reason: Added code to restore gfx variables to their original state after debug drawing is completed
IXix is offline   Reply With Quote
Old 08-28-2009, 09:35 AM   #18
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

Hey! IXix! I use it, I forget to thank you for that in the other thread! Very handy especially in comparison with how I was debugging arrays before

Quote:
Originally Posted by Justin View Post
I think it depends on what condi1 refers to.. If it's mostly 0, then 2) should be faster. Other cases, the difference becomes less I'd think.
Thanks for the feedback, Mr Frankel! The "boolean" "cond" meant a variable that's either 0 or 1 and nothing else. So, in both cases, I was hoping some stuff not to be evaluated in "cond * something_else" as the result is either 0 or something_else.
Here's the not-good-for-speakers code I used to test that (where "cond" is often updated as you can see):
Code:
desc:Jeff test 27
slider1:0<0,1,1{1,2}>Mode
@init
cond=0;

@sample
cond = !cond;

!slider1 ? 
// 1)
(spl0 = cond * floor(rand(spl0) * $pi * 100);)
// 2)
: (cond ? spl0 = floor(rand(spl0) * $pi * 100) : spl0 = 0;);
As logical operations use operator precedence, I also tried "spl0 = cond && (floor(rand(spl0) * $pi * 100))" that is to say "&&" instead of "*" but it's ko: logical operations clamp results to 0 or 1 (edit: and that's fine)

Last edited by Jeffos; 08-28-2009 at 12:17 PM.
Jeffos is offline   Reply With Quote
Old 08-28-2009, 05:22 PM   #19
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,005
Default General JS synth implementation

I don't have any general optimizations, nor any ingenius DSP code snippets, but I do have a polyphone JS synth implementation. It receives MIDI notes in full polyphony (within the chosen note range), stores the notes in a buffer, and then caches this buffer for optimal speed. In this case sound is generated using a bunch of sinuses (with the frequencies for each note pre-calculated to speed up things a litle more), but you could easily replace that with any other sound generating code snippet.

Code:
// Sinas v0.0.1
// (c) 2009 Theo Niessink <http://www.taletn.com/>
// License: GPL <http://www.gnu.org/licenses/gpl.html>

desc:Simple stacked sine synth

in_pin:none

out_pin:Mono
out_pin:Mono


@init

bot_key = 36; // C2
top_key = 96; // C7
num_key = top_key - bot_key + 1;

// Pre-calculate the frequency for each key.
//freq_buf = 0;
key = 69 - bot_key; // A @ 440 Hz

// Calculate the top octave.
i = num_key;
loop(12, freq_buf[i -= 1] = 440.0 * 2 ^ ((i - key) / 12));

// Caclulate the lower octaves by dividing down the top octave.
loop(i, freq_buf[i -= 1] = 0.5 * freq_buf[i + 12]);

key_buf = freq_buf + num_key;
//memset(key_buf, 0, num_key);

cache_buf = key_buf + num_key;
//num_cache = 0;

pi2 = $pi * 2;
speriod = 1/srate * pi2;
//position = 0.0;


@block

while(
  midirecv(ofs, msg1, msg23) ? (
    event = msg1&$xF0;
    velocity = msg23&$x7F00;

    // Note On
    event == $x90 && velocity ? (
      key = msg23&$x007F;

      key >= bot_key && key <= top_key ? (
        key -= bot_key;

        // Add the key to the cache.
        key_buf[key] == 0 ? (
          cache_buf[num_cache] = key;
          num_cache += 1;
        );

        // Set the velocity value / (127 * 256) in the keyboard buffer.
        key_buf[key] = 0.000030757874015748031496062992125984 * velocity;
      );
    ) :

    // Note Off
    (event == $x80 || (event == $x90 && !velocity)) ? (
      key = msg23&$x007F;

      key >= bot_key && key <= top_key ? (
        key -= bot_key;

        // Remove the key from the cache.
        loop(i = num_cache,
          cache_buf[i -= 1] == key ? (
            num_cache -= 1;
            i < num_cache ? memcpy(cache_buf + i, cache_buf + i + 1, num_cache - i);
          );
        );

        // Clear the velocity value in the keyboard buffer.
        key_buf[key] = 0;
      );
    ) :

    // Control Change
    event == $xB0 ? (
      cc = msg23&$x007F;

      // All Notes Off
      cc >= 123 && (num_cache > 0 || position > 0.0) ? (
        memset(key_buf, 0, num_key);
        num_cache = 0;
        position = 0.0;
      );
    );

    midisend(ofs, msg1, msg23);
    1; // Loop
  );
);


@sample

spl0 = 0.0;

// Calculate a sample for each of the keys in the cache.
loop(i = num_cache,
  key = cache_buf[i -= 1];
  f = position * freq_buf[key];
  spl0 += key_buf[key] * (sin(f) + sin(2 * f) + sin(3 * f) + sin(4 * f) + sin(5 * f));
);

spl0 *= 0.1;
spl1 = spl0;

position += speriod;
num_cache == 0 && position >= pi2 ? position -= pi2;
Without the cache the sound generating loop in the @sample section would look something like this:

Code:
loop(key = num_key,
  velocity = key_buf[key -= 1];
  velocity > 0 ? (
    f = position * freq_buf[key];
    spl0 += velocity * (sin(f) + sin(2 * f) + sin(3 * f) + sin(4 * f) + sin(5 * f));
  );
);
This would make the @sample section considerably slower, especially in idle or with only a few keys playing. On my system the cached version takes 0.3% CPU time in idle, and 3.3% with 3 keys playing. The non-cached version takes 6.4% in idle, and 9.0% with 3 keys playing.
__________________
Martinic Kee Bass - Scanner Vibrato - Elka Panther - Tale's JSFX Pack
Tale is offline   Reply With Quote
Old 09-01-2009, 06:30 PM   #20
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default methods of pseudo-random number generation with non-uniform distribution

explained below is a method for generating white noise with non-uniform distribution.

unlike the gaussian function for white gaussian noise this method uses trigonometric functions and in general can be tweaked to produce quick variations in regard of probability density with relatively fast execution times.

you may have stumbled on this before doing a simple oscillator:
Code:
//full code
@init
x=y=1;
@sample
y=sin((x+=1)*y);
this recursion algorithm is a prng with non-uniform (trigonometric) distribution which outputs in the [-1,+1] range for y using sin(), previous value y[n-1] and an 'index' variable x.

probability density function:
sin() will result in a 'negative' pdf function (less density in center ), while tan() in positive.

execution times:
-slower than the above park–miller and faster than rand() (but both implementations are uniform).
-using tan() is slower than sin(), and tan() requires limiting and some tweaks.

use of approximations:
-example link with some approximations for trigonometric functions at musicdsp - http://musicdsp.org/files/approx.h

plot for tan() - scaled:


vid:
https://stash.reaper.fm/oldsb/618700/...tribution0.gif
extra points for guessing why the @gfx sections outputs the exponential grid.

plotter algorithm:
https://stash.reaper.fm/oldsb/618769/prngplot

short read on trigonometry in non-uniform distributions:
http://www.stat.wisc.edu/~larget/math496/random2.html



------
lubomir

Last edited by liteon; 09-01-2009 at 07:30 PM.
liteon is offline   Reply With Quote
Old 09-03-2009, 05:13 PM   #21
whatsup
Human being with feelings
 
whatsup's Avatar
 
Join Date: Aug 2008
Posts: 1,144
Default

Quote:
Originally Posted by Justin View Post
I think it depends on what condi1 refers to.. If it's mostly 0, then 2) should be faster. Other cases, the difference becomes less I'd think.
or maybe it because, it take less time to compare, than to mul ?

EDIT: reffers to that:

cond1 = 0 or 1

Quote:

Code:

//1)
spi1 = condi1 * slider1 * spl0;
//2)
condi1 ? spi1 = slider1 * spl0 : spi1 = 0;
__________________
you can contack me on
whatsup@inn.co.il

Last edited by whatsup; 09-03-2009 at 05:15 PM.
whatsup is offline   Reply With Quote
Old 09-09-2009, 12:25 PM   #22
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default precision

for evaluation there are 5 decimal places of precision.
Code:
@sample
a = 5*10^-5; //0.00005
b = 4*10^-5; //0.00004
//(a == b) = 1;
a = 5*10^-4; //0.0005
b = 4*10^-4; //0.0004
//(a == b) = 0;
justin suggests:
Quote:
Originally Posted by Justin
Note (for the docs), if you need more accuracy, you could do a*1000000 == b*1000000
-----
normally the accuracy of mathematical identities is dependent from the implemented functions (if are based on standards such as IEEE) and also very importantly the precision of the format.

evaluating the identity x=exp(log(x)) in c:
Code:
x is 5 (single or double) 
y=exp(log(x))
* for single precision:
x=5.0, y=5.000000000000000000000
(x==y)=true
* for double precision:
x=5.0, y=4.999999999999999100000
(x==y)=false
in js (double precision):
Code:
x=5;
y=exp(log(x));
//x 5.000000...
//y 5.000000... <- js debugger rounds value
x*=10^7;
y*=10^7;
//x 50000000.000000...
//y 49999999.999999...
assuming that the used mathematical functions are standardized this a trick that can be used to determine the precision in programming languages with less strict variable type definitions.

---
lubomir
liteon is offline   Reply With Quote
Old 12-01-2009, 09:21 PM   #23
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default jsfxgen

spamming a little..

JSfxGEN is a modular ide for jesusonic:



http://forum.cockos.com/showthread.php?t=46286

--
liteon is offline   Reply With Quote
Old 04-25-2013, 11:40 PM   #24
bang
Human being with feelings
 
bang's Avatar
 
Join Date: Jul 2006
Posts: 554
Default newJs pseudo-object parameters

hello all!

seems that the main limitation of newJs pseudo-objects is that inside a function "this.foo" is all there is. there's no inbuilt way to refer to "that.foo". here's my current workaround technique:
Code:
function rgbapush()
  instance( r, g, b, a )
(
  stack_push( a );
  stack_push( b );
  stack_push( g );
  stack_push( r );
);

function rgbapop()
  instance( r, g, b, a )
(
  stack_pop( r );
  stack_pop( g );
  stack_pop( b );
  stack_pop( a );
);

// a.rgbapush(); b.rgbapush();
function rgbamix( mix ) // thread safe
  instance( r, g, b, a ) local( mix1 )
(
  mix1= 1-mix;
  this.rgbapop(); // this= b.rgbapush();
  // keep in sync w/ rgbapop()
  r= stack_pop() * mix1 + r * mix; //stack_pop( a.r );
  g= stack_pop() * mix1 + g * mix; //stack_pop( a.g );
  b= stack_pop() * mix1 + b * mix; //stack_pop( a.b );
  a= stack_pop() * mix1 + a * mix; //stack_pop( a.a );
);

/*** alas, doesn't work:
// a.rgbapush(); b.rgbapush();
function rgbamix2( mix )
  instance( r, g, b, a )
  local(a1, b1)
(
  mix1= 1-mix;
  b1.rgbapop(); a1.rgbapop();
  r= a1.r * mix1 + b1.r * mix;
  g= a1.g * mix1 + b1.g * mix;
  b= a1.b * mix1 + b1.b * mix;
  a= a1.a * mix1 + b1.a * mix;
); ***/
anyone have anything better than this?

enjoy! /dan
bang is offline   Reply With Quote
Old 05-26-2013, 12:32 PM   #25
Justin
Administrator
 
Justin's Avatar
 
Join Date: Jan 2005
Location: NYC
Posts: 12,567
Default

Quote:
Originally Posted by bang View Post
Code:
/*** alas, doesn't work:
// a.rgbapush(); b.rgbapush();
function rgbamix2( mix )
  instance( r, g, b, a )
  local(a1, b1)
(
  mix1= 1-mix;
  b1.rgbapop(); a1.rgbapop();
  r= a1.r * mix1 + b1.r * mix;
  g= a1.g * mix1 + b1.g * mix;
  b= a1.b * mix1 + b1.b * mix;
  a= a1.a * mix1 + b1.a * mix;
); ***/
anyone have anything better than this?
If you changed local(a1,b1) to instance(a1,b1), I believe this might work! local namespace support would be nice, too, but it isn't currently supported.
Justin is offline   Reply With Quote
Old 05-26-2013, 01:30 PM   #26
bang
Human being with feelings
 
bang's Avatar
 
Join Date: Jul 2006
Posts: 554
Default

Quote:
Originally Posted by Justin View Post
If you changed local(a1,b1) to instance(a1,b1), I believe this might work! local namespace support would be nice, too, but it isn't currently supported.
yes. but won't we end up with <psuedo-object>.a1.r/g/b/a and <psuedo-object>.b1.r/g/b/a for every psuedo-object which uses rgbamix2()? i guess i haven't wanted to load up the Js namespace any more than i need to. it already gets to be a pretty massive variables list in the debug window. :^)

and fwiw... what i'd really love to see someday is a way to alias psuedo-object namespaces inside functions. something like:
Code:
// rgbamix2( mix, a, b )
function rgbamix2( mix, @a1, @b1 )
  instance( r, g, b, a )
(
  mix1= 1-mix;
  r= a1.r * mix1 + b1.r * mix; // r= a.r * mix1 + b.r * mix
  g= a1.g * mix1 + b1.g * mix;
  b= a1.b * mix1 + b1.b * mix;
  a= a1.a * mix1 + b1.a * mix;
);
just sayin'... :^)

enjoy! /dan

ps- also fwiw, stack_push()/pop() are awesomely useful generally. thanks so much for those.

Last edited by bang; 05-26-2013 at 01:35 PM. Reason: ps
bang is offline   Reply With Quote
Old 10-10-2013, 11:49 PM   #27
Hubi
Human being with feelings
 
Join Date: Oct 2010
Posts: 75
Default

A simple button maker function:

Code:
function button(state x1 y1 x2 y2 r g b)
  (
  gfx_x = x1;
  gfx_y = y1;
  gfx_r =r;gfx_b=b;gfx_g=g;
  (mouse_x > x1 && mouse_x < x1+x2 && mouse_y > y1 && mouse_y < y1+y2) ? (
    gfx_r =r+0.05;gfx_b=b+0.05;gfx_g=g+0.05; 
    mouse_cap == 1 ? (
      state = 1;
      gfx_r =r+0.2;gfx_b=b+0.2;gfx_g=g+0.2;
      );  
    ) : (
    state = 0; 
  ); 
  gfx_rectto(gfx_x+x2,gfx_y+y2); 
  state;
  );
Code:
button_clicked = button(state,20,20,40,20,0.6,0.6,0.6);
state returns "1" if the button is clicked, otherwise it is "0"
x1 and y1 are the position of the left top corner
x2 and y2 are the size of the button
r,g,b color


it shows if the mouse hovers over the button and if it is clicked

regards
Hubi
Hubi is offline   Reply With Quote
Old 11-05-2015, 09:54 AM   #28
mrlimbic
Human being with feelings
 
mrlimbic's Avatar
 
Join Date: Nov 2009
Location: UK
Posts: 598
Default

Anyone know the maths to calculate a rolling value for the width of the stereo field?
__________________
Convert NLE XMLs to RPP.
http://vordio.net
mrlimbic is offline   Reply With Quote
Old 11-05-2015, 10:51 AM   #29
derek.john.evans
Human being with feelings
 
derek.john.evans's Avatar
 
Join Date: Feb 2011
Location: Adelaide, South Australia
Posts: 217
Default

Quote:
Originally Posted by mrlimbic View Post
Anyone know the maths to calculate a rolling value for the width of the stereo field?
Mmm. This could be one of those deceptively simple questions.

Do you mean RMS or DB or peak levels over time? It should be the same as what is used in DB level displays or compressors, just with the side being used.

vumeter has code to calculate DB over time. dynamics_meter contains code for 3 types of levels over time. Depends on how complex or "accurate" you need it.

Here is some basic code using a decay style level. Or, you can implement a window style level, which is just calculating the level of the average over a time period (window).

Code:
desc: Mid-Side Levels

@init

sc = 6 / log(2);
response = 0.5;

@sample

// Calculate the DB of mid/side
mid_db = max(log(abs(spl0 + spl1)) * sc, -80);
sid_db = max(log(abs(spl0 - spl1)) * sc, -80);

// Or, calculate the abs level of mid/side
mid_le1 = abs(spl0 + spl1);
sid_le1 = abs(spl0 - spl1);

// Or, calculate the sqrt of abs level of mid/side. (I think this might be RMS)
mid_le2 = sqrt(abs(spl0 + spl1));
sid_le2 = sqrt(abs(spl0 - spl1));

// You can then use a response formular to soften the level changes.
// This is the same type of code used in a compressor.
mid_level += (mid_db - mid_level) * response;
sid_level += (sid_db - sid_level) * response;

@gfx

// Some code to display two DB bars. Needs to be adjusted for other level types.
gfx_r = gfx_g = gfx_b = gfx_a = 1;
gfx_x = 0; gfx_y = 10; gfx_rectto(80 + mid_level, gfx_y + 16);
gfx_x = 0; gfx_y = 40; gfx_rectto(80 + sid_level, gfx_y + 16);
__________________
http://wascal.net/music/
derek.john.evans is offline   Reply With Quote
Old 11-05-2015, 11:01 AM   #30
mrlimbic
Human being with feelings
 
mrlimbic's Avatar
 
Join Date: Nov 2009
Location: UK
Posts: 598
Default

Quote:
Originally Posted by derek.john.evans View Post
Mmm. This could be one of those deceptively simple questions.

Do you mean RMS or DB or peak levels over time? It should be the same as what is used in DB level displays or compressors, just with the side being used.

vumeter has code to calculate DB over time. dynamics_meter contains code for 3 types of levels over time. Depends on how complex or "accurate" you need it.

Here is some basic code using a decay style level. Or, you can implement a window style level, which is just calculating the level of the average over a time period (window).
Thanks. I suspect RMS will be more useful. The idea is to create an automation envelope from the stereo width of one audio item and apply that to pan on another item.
__________________
Convert NLE XMLs to RPP.
http://vordio.net
mrlimbic is offline   Reply With Quote
Old 11-05-2015, 11:17 AM   #31
derek.john.evans
Human being with feelings
 
derek.john.evans's Avatar
 
Join Date: Feb 2011
Location: Adelaide, South Australia
Posts: 217
Default

Quote:
Originally Posted by mrlimbic View Post
Thanks. I suspect RMS will be more useful. The idea is to create an automation envelope from the stereo width of one audio item and apply that to pan on another item.
Sounds interesting. Have you set up the effect using current JS/VST effects?

Sounds like some kinda stereo field ducking/dodging effect. Maybe start a new thread and document your idea.
__________________
http://wascal.net/music/
derek.john.evans is offline   Reply With Quote
Old 11-05-2015, 12:02 PM   #32
mrlimbic
Human being with feelings
 
mrlimbic's Avatar
 
Join Date: Nov 2009
Location: UK
Posts: 598
Default

Quote:
Originally Posted by derek.john.evans View Post
Sounds interesting. Have you set up the effect using current JS/VST effects?

Sounds like some kinda stereo field ducking/dodging effect. Maybe start a new thread and document your idea.
Yes that's it. It is like ducking but for panning instead of volume (pucking?!!)

It seems I could do it as a multichannel effect (like ducking) or via envelope generation and stick the envelope to the pan parameter of another item.
__________________
Convert NLE XMLs to RPP.
http://vordio.net
mrlimbic 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 01:40 AM.


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