desc:WigWare Multi-channel Peak VU Meter
// Copyright Dr Bruce Wiggins 2016
// License: LGPL - http://www.gnu.org/licenses/lgpl-3.0.en.html
// http://www.brucewiggins.co.uk
// Twitter @BruceWiggins
// I've commented it extensively to aid in the writing of 
// JS effects.  Please let me know if you find this useful :-)
// NB. I know it isn't really a VU meter (it's a peak meter), 
// but that's what everyone searches for!
// You MUST retain the text above when sharing/distributing

slider1:16<0,64,1>Number of Channels
slider2:100<0,500,10>Sample Time (ms)
slider3:60<0,96,1>min dB Value (-ve)
slider4:10<2,20,1>peak hold (x Sample Time)

@init
ln = 64;          // max length of arrays (no of channels);
sc=6/log(2);      // dB scaling factor
scalex = 20;      // pixels width for labels on left
pcnt = 0;         // peak hold counter;
maxsamples = 1024;// mem location for maxsamples array;         
peaks = maxsamples + ln; // samples peaks
pholds = peaks + ln;     // peak holds
pctr = pholds + ln;      // peak hold counters
memset(maxsamples,0,ln); // zero/min memory at start
memset(peaks,-192,ln);
memset(pholds,-192,ln);
memset(pctr,0,ln);

@slider
//make sure we can't have more channels than the track has
//but num_ch ALWAYS inits to zero....how annoying.
num_ch>0?(nch = max(2,min(num_ch,slider1));) :
(nch = slider1;);
slider1 = nch;
sampletime=slider2*srate/1000;  // sample time ms to s
mindb = slider3;                // minimum dB value shown
minlin = pow(10,-mindb/20);     // minimum dB value as lin gain
phold = slider4;                // peak hold time multiplier

@block
//if we've gone over the sample time
bscnt>sampletime ? 
(
  ibl=0;
  loop(nch,
    //get the current peak - make sure we don't log zero
    peaks[ibl]=log(max(minlin,maxsamples[ibl]))*sc; 
    //if peak hold has held long enough, minimum it
    pctr[ibl]>phold ? (pholds[ibl] = -mindb;);
    //if peak hold is less than current peak, store it and
    //reset that channels peak hold counter, else decrement
    //the channels peak hold counter
    pholds[ibl]<peaks[ibl] ? 
    (pholds[ibl]=peaks[ibl];pctr[ibl]=0;) :
    (pctr[ibl]+=1;);
    //zero current channels max sample
    maxsamples[ibl] = 0;
    ibl+=1;
  );
  //reset timer
  bscnt=0;
  
);
bscnt += samplesblock;

@sample
//get maximum sample per channel per block of samples (abs)
ispl=0;
loop(nch,
  maxsamples[ispl] = max(maxsamples[ispl],abs(spl(ispl)));
  ispl+=1;
);

@gfx 400,200
i=0;
//decide on bar graph width
myx = floor((gfx_w-scalex)/(nch));
//offset the bar graphs to allow for scale on left
x = scalex;
//used for scaling the size of the graph
mult = gfx_h/mindb;
//draw rectangles for metres/peak hold
loop(nch,
  //draw var graphs
  val = ceil(mult*(mindb+peaks[i]));
  gfx_r = 0.0;gfx_g = 0.8;gfx_b = 0.25; //Bar Graph Colour (RGB)
  gfx_rect(x,(gfx_h-val),myx-1,val);
  
  //draw peak holds
  val2 = ceil(mult*(mindb+pholds[i]));
  gfx_r = 1.0;gfx_g =0.5;gfx_b = 0.0;   //Peak Hold Colour (RGB)
  gfx_rect(x,(gfx_h-val2),myx-1,3);
  
  //draw channel number
  gfx_x = x+1;
  gfx_y = gfx_h-gfx_texth-2;
  gfx_r = 1.0;gfx_g =1.0;gfx_b = 1.0;  //Channel Test Colour (RGB)
  gfx_drawnumber(i+1,0);
  x+=myx;
  i+=1;
  );
  
//draw dB scale on the left hand side in 6dB blocks
ppdb = gfx_h/mindb; //pixels per dB
db6 = 6 * ppdb;     //6dB in pixels
i=0;
gfx_r = 1.0;gfx_g = 1.0;gfx_b = 1.0;  //dB scale colour
loop(ceil(gfx_h/db6),
  gfx_x = 0;
  gfx_y = db6*i;
  gfx_lineto(scalex-3,gfx_y);
  gfx_x = 1;
  gfx_y+=1;
  gfx_drawnumber(i*6,0);
  i+=1;
  );

