/*****************************************************
Copyright (C) 2016 Stige T.
License: http://jsplugins.supermaailma.net/license.php
*****************************************************/

desc:Skope II [Build 160930]

slider1:1<0,10,0.01>Buffer Size [sec]
slider3:1<0,10,0.1>Y Scale CH1
slider4:1<0,10,0.1>Y Scale CH2
slider5:1<0,10,0.1>Y Scale CH3
slider6:1<0,10,0.1>Y Scale CH4

slider12:0<-1,1,0.01>Trigger Threshold [MMB]
slider13:0<0,4,1{CH1,CH2,CH3,CH4,ALL}>Trigger Channel
slider14:0<0,1000,1>X Delay [spl]

slider16:0<-1,1,0.001>Marker 1 [CTRL+LMB]
slider17:0<-1,1,0.001>Marker 2 [SHFT+LMB]

slider20:0<0,1,0.01>Display Align [Single<->Separate]

in_pin:CH1
in_pin:CH2
in_pin:CH3
in_pin:CH4

out_pin:CH1
out_pin:CH2
out_pin:CH3
out_pin:CH4

@init

//Should it update the display when playback is not active?
updateAlways = 1;

function delay_init(samples,startmem)
instance (len,sloop,splay)
(
  len = samples > srate ? srate : samples;
  sloop = splay = startmem;
);

function delay(input)
instance(sindex,splay,sloop,len)
(
  sloop[sindex] = input;
  sindex += 1;
  sindex > len ? sindex = 0;
  splay[sindex];
);

function interpolate(A, B, X) (
  (A * (1 - X) + B * X);
);

function InitDisplay() (
  xStep = gfx_w / bSize;
  interval = floor(bSize / 10000);
  halfHeight = gfx_h * 0.5;
  yMul0 = halfHeight * yScale0 * interpolate(1,0.25,arrange);
  yMul1 = halfHeight * yScale1 * interpolate(1,0.25,arrange);
  yMul2 = halfHeight * yScale2 * interpolate(1,0.25,arrange);
  yMul3 = halfHeight * yScale3 * interpolate(1,0.25,arrange);
  
  y0ffset = interpolate(0,gfx_h * -0.375,arrange);
  y1ffset = interpolate(0,gfx_h * -0.125,arrange);
  y2ffset = interpolate(0,gfx_h * 0.125,arrange);
  y3ffset = interpolate(0,gfx_h * 0.375,arrange);
);

function UpdateBufferSize(newsize) (
  bSize = max(min(newsize,maxBufferSize),minBufferSize);
  bOffset1 = bSize;
  bOffset2 = bSize * 2;
  bOffset3 = bSize * 3;
  
  bSize < lastbSize ? (
    memcpy(bSize,lastbSize,bSize);
    memcpy(bSize*2,lastbSize*2,bSize);
    memcpy(bSize*3,lastbSize*3,bSize);
    lastbSize = bSize;
  );
  bSize > lastbSize ? (
    memcpy(bSize*3,lastbSize*3,lastbSize);
    memcpy(bSize*2,lastbSize*2,lastbSize);
    memcpy(bSize,lastbSize,lastbSize);
    memset(lastbSize,0,bSize-lastbSize);
    memset(lastbSize+bSize,0,bSize-lastbSize);
    memset(lastbSize+bSize+bSize,0,bSize-lastbSize);
    memset(lastbSize+bSize+bSize+bSize,0,bSize-lastbSize);
    lastbSize = bSize;
  );
);

buffer = 0;
trigbuff = 4000000;
maxBufferSize = srate * 10;
minBufferSize = 0;

@slider

inputBufferSize = slider1;

inputBufferSize ? (
  syncLoopCount = 0;
  syncModeInit = 0;
  UpdateBufferSize(inputBufferSize * srate);
  start_play_position = 0;
  last_play_position = 0;
);

xOffset = slider14;
yScale0 = slider3;
yScale1 = slider4;
yScale2 = slider5;
yScale3 = slider6;

slider12 != thresh ? (
  thresh = slider12;
  tr.a = 1;
);
chi = slider13;

arrange = slider20;
msg.a = 1;
msg2.a = 1;

d0.delay_init(xOffset,2000000);
d1.delay_init(xOffset,2001001);
d2.delay_init(xOffset,2002002);
d3.delay_init(xOffset,2003003);

slider16 != M1 ? (
  M1 = slider16;
  M1.a = 1;
);
slider17 != M2 ? (
  M2 = slider17;
  M2.a = 1;
);

@block

!inputBufferSize ? (
  start_play_position = play_position;
  play_state == 1 && abs(start_play_position-last_play_position) >= 0.001 ? (
    time_sel_len = max(floor((last_play_position-start_play_position) * srate + 0.1),minBufferSize1);
    syncLoopCount += 1;
  );
  
  time_sel_len <= maxBufferSize && time_sel_len != bSize ? (
    UpdateBufferSize(time_sel_len);
    A += 1;
  );
  
  last_play_position = start_play_position + samplesblock/srate;
);

updateAlways ? (
  playState = 1;
) : (
  playState = play_state;
);

@sample

in0 = spl0;
in1 = spl1;
in2 = spl2;
in3 = spl3;

trigbuff[0] = in0 * yScale0;
trigbuff[1] = in1 * yScale1;
trigbuff[2] = in2 * yScale2;
trigbuff[3] = in3 * yScale3;

chi < 4 ? (
  trigCh.p = trigbuff[chi];
  trigCh.n = trigCh.p;
) : (
  trigCh.p = max(max(max(trigbuff[0],trigbuff[1]),trigbuff[2]),trigbuff[3]);
  trigCh.n = min(min(min(trigbuff[0],trigbuff[1]),trigbuff[2]),trigbuff[3]);  
);

xOffset ? (
  in0 = d0.delay(in0);
  in1 = d1.delay(in1);
  in2 = d2.delay(in2);
  in3 = d3.delay(in3);
);

thresh < 0 && trigCh.n <= thresh ? (
  trigPhase = 1;
);

thresh > 0 && trigCh.p >= thresh ? (
  trigPhase = 1;
);

!thresh ? trigPhase = 1;

trigPhase && playState ? (
  buffer[bufI] = in0;
  buffer[bufI+bOffset1] = in1;
  buffer[bufI+bOffset2] = in2;
  buffer[bufI+bOffset3] = in3;
  bufI >= bSize-1 ? (bufI = 0; trigPhase = 0) : (bufI += 1;);
);

@gfx 500 500

function GetdB(Y) (
  this.lin = ((gfx_h * 0.5) - Y) / (gfx_h * 0.5);
  this.dB = 20 * log10(abs(this.lin));
);

gfx_setfont(1,"Arial",12);

gfx_mode = 1.0;

InitDisplay();

gfx_a = 1;
loopI = 0;
loop(bSize,
  !(loopI % interval) ? (
  y0 = halfHeight - (buffer[loopI] * yMul0) + y0ffset;
  y1 = halfHeight - (buffer[loopI+bOffset1] * yMul1) + y1ffset;
  y2 = halfHeight - (buffer[loopI+bOffset2] * yMul2) + y2ffset;
  y3 = halfHeight - (buffer[loopI+bOffset3] * yMul3) + y3ffset;
  x = loopI * xStep;
  gfx_r = 0; gfx_g = 1; gfx_b = 0;
  gfx_line(lastx, lasty0, x, y0);
  gfx_r = 1; gfx_g = 0; gfx_b = 0;
  gfx_line(lastx, lasty1, x, y1);
  gfx_r = 0; gfx_g = 0; gfx_b = 1;
  gfx_line(lastx, lasty2, x, y2);
  gfx_r = 0; gfx_g = 1; gfx_b = 1;
  gfx_line(lastx, lasty3, x, y3);  
  lasty0 = y0;
  lasty1 = y1;
  lasty2 = y2;
  lasty3 = y3;
  lastx = x;
  );   
  loopI += 1;
);
lasty0 = halfHeight - (buffer[0] * yMul0) + y0ffset;
lasty1 = halfHeight - (buffer[bOffset1] * yMul1) + y1ffset;
lasty2 = halfHeight - (buffer[bOffset2] * yMul2) + y2ffset;
lasty3 = halfHeight - (buffer[bOffset3] * yMul3) + y3ffset;
lastx = 0;

gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 0.25;
gfx_line(bufI * xStep, 0, bufI * xStep, gfx_h);


mouse_y >= 0 && mouse_y <= gfx_h && mouse_x >= 0 && mouse_x <= gfx_w ? (
  gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 0.33;
  gfx_line(mouse_x, 0, mouse_x, gfx_h);
  gfx_line(0, mouse_y, gfx_w, mouse_y);
  !arrange ? (
    cY.GetdB(mouse_y);
    valstr = strcat(strcat(sprintf(#,"%.3f",cY.lin)," ["),strcat(sprintf(#,"%.2f",cY.dB),"dB]"));
    gfx_measurestr(valstr,valstr.w,valstr.h);
    cO = mouse_y > 12 ? 12 : -2;
    gfx_x = gfx_w - valstr.w - 5; gfx_y = mouse_y - cO;
    gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 0.5;
    gfx_drawstr(valstr);
  );
);

mouse_cap & 1 ? (
  dragStart < 0 ? (
    dragStart = mouse_x;
  );
  gfx_x = dragStart; gfx_y = 0;
  gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 0.25;
  gfx_rectto(mouse_x, gfx_h);
  gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 1;
  ms = sprintf(#,"%.2f",(((mouse_x - dragStart)/ gfx_w) * bSize) / srate * 1000);
  spls = sprintf(#,"%.0f",((mouse_x - dragStart)/ gfx_w) * bSize);
  valstr = strcat(strcat(strcat(spls," spl, "),ms)," ms");
  gfx_measurestr(valstr,valstr.w,valstr.h);
  gfx_x = ((mouse_x + dragStart) * 0.5)- (valstr.w * 0.5); gfx_y = gfx_h - 20;
  gfx_drawstr(valstr);
) : (
  dragStart = -1;
);

mouse_cap & 2 ? (
  freembuf(0);
  bufI = 0;
);

mouse_cap == 5 ? (
  slider16 = M1 = (((gfx_h - mouse_y) / gfx_h) - 0.5) * 2;
  sliderchange(slider16);
  M1.a = 1;
);

mouse_cap == 9 ? (
  slider17 = M2 = (((gfx_h - mouse_y) / gfx_h) - 0.5) * 2;
  sliderchange(slider17);
  M2.a = 1;
);

!arrange && mouse_cap & 64 ? (
  slider12 = thresh = (((gfx_h - mouse_y) / gfx_h) - 0.5) * 2;
  sliderchange(slider12);
  thresh == 0.0001 ? thresh = 0;
  tr.a = 1;
);

arrange == 1 ? (
  gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 0.5;
  gfx_line(0, y0ffset+gfx_h*0.625, gfx_w,  y0ffset+gfx_h*0.625);
  gfx_line(0, y1ffset+gfx_h*0.625, gfx_w,  y1ffset+gfx_h*0.625);
  gfx_line(0, y2ffset+gfx_h*0.625, gfx_w,  y2ffset+gfx_h*0.625);
);

gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 0.1;
gfx_line(0, y0ffset+gfx_h*0.5, gfx_w,  y0ffset+gfx_h*0.5);
gfx_line(0, y1ffset+gfx_h*0.5, gfx_w,  y1ffset+gfx_h*0.5);
gfx_line(0, y2ffset+gfx_h*0.5, gfx_w,  y2ffset+gfx_h*0.5);
gfx_line(0, y3ffset+gfx_h*0.5, gfx_w,  y3ffset+gfx_h*0.5); 

M1 && !arrange ? (
  gfx_r = 1; gfx_g = 0.5; gfx_b = 0.75; gfx_a = 0.5;
  M1.pos = (-M1 + 1) * 0.5;
  gfx_line(0, M1.pos * gfx_h, gfx_w, M1.pos * gfx_h);
);

M2 && !arrange ? (
  gfx_r = 1; gfx_g = 0.5; gfx_b = 0.75; gfx_a = 0.5;
  M2.pos = (-M2 + 1) * 0.5;
  gfx_line(0, M2.pos * gfx_h, gfx_w, M2.pos * gfx_h);
);

thresh && !arrange ? (
  gfx_r = 0.5; gfx_g = 0.75; gfx_b = 1; gfx_a = tr.a;
  tr.Y = (1-thresh) * gfx_h * 0.5;
  gfx_line(0, tr.Y, gfx_w, tr.Y);
  gfx_x = 1; gfx_y = tr.Y;
  gfx_drawstr("TRIG");
  tr.a > 0.25 ? tr.a *= 0.98;
);

(M1 || M2) && arrange && msg.a > 0.01 ? (
  gfx_setfont(1,"Arial",12,'b');
  gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = msg.a;
  msg = "MARKERS WILL NOT BE DISPLAYED ON SEPARATE LANES!";
  gfx_measurestr(msg,msg.w,msg.h);
  gfx_x = (gfx_w * 0.5) - (msg.w * 0.5); gfx_y = 10;
  gfx_drawstr(msg);
  msg.a *= 0.98;
);

mouse_cap == 21 || mouse_cap == 25 ? (
  loopI = 0;
  loop(bSize,
    !(loopI % interval) ? (
      y0 = buffer[loopI] * yScale0;
      y1 = buffer[loopI+bOffset1] * yScale1;
      y2 = buffer[loopI+bOffset2] * yScale2;
      y3 = buffer[loopI+bOffset3] * yScale3;
      
      yMax = max(max(max(y0,y1),max(y2,y3)),yMax);
      yMin = min(min(min(y0,y1),min(y2,y3)),yMin);
    );   
    loopI += 1;
  );
  aM = yMax >= abs(yMin) ? yMax : yMin;
  mouse_cap == 21 ? (
    slider16 = M1 = aM;
    M1.a = 1;
  );
  mouse_cap == 25 ? (
    slider17 = M2 = aM;
    M2.a = 1;
  );
  yMax = yMin = 0;
);

!arrange ? (
  gfx_setfont(1,"Arial",14,'b');
  gfx_r = 1; gfx_g = 1; gfx_b = 1;
  gfx_x = 10; gfx_y = 5;
  M1 ? (
    gfx_a = M1.a;
    gfx_drawstr("M1: ");
    gfx_drawstr(strcat(sprintf(#,"%.2f",20 * log10(abs(M1)))," dB    "));
    M1.a > 0.33 ? M1.a *= 0.98;
  );
  M2 ? (
    gfx_a = M2.a;
    gfx_drawstr("M2: ");
    gfx_drawstr(strcat(sprintf(#,"%.2f",20 * log10(abs(M2)))," dB    "));
    M2.a > 0.33 ? M2.a *= 0.98;
  );
  
  M1 && M2 ? (
    gfx_a = max(M1.a,M2.a);
    gfx_drawstr("DIF: ");
    gfx_drawstr(strcat(sprintf(#,"%.2f",abs(20 * log10(abs(M1/M2))))," dB"));
  );
);

!inputBufferSize ? (
  gfx_x = 5; gfx_y = gfx_h-18;
  gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a =  msg2.a;
  gfx_setfont(1,"Arial",14,'b');
  gfx_drawstr("SYNC MODE");
  time_sel_len <= maxBufferSize ? (
    syncLoopCount == 1 ? (
      gfx_drawstr(" <Waiting for the loop to finish>");
    ) : (
      gfx_drawstr(" [");
      gfx_drawnumber(bSize,0);
      gfx_drawstr(" spl / ");
      gfx_drawnumber(1/(srate/bSize),3);
      gfx_drawstr(" s]");      
      msg2.a > 0.33 ? msg2.a *= 0.98;
    );
  ) : (
    gfx_r = 1; gfx_g = 0; gfx_b = 0;
    gfx_drawstr(" <ERROR: LOOP TOO LONG!>");
  );
);

help.x = gfx_w - 13;
help.y = gfx_h - 18;

gfx_r = 1; gfx_g = 1; gfx_b = 1; gfx_a = 0.5;
gfx_x = help.x; gfx_y = help.y;
gfx_setfont(1,"Arial",16,'b');
gfx_drawstr("?");

mouse_x >= help.x && mouse_x <= help.x + 5 && mouse_y >= help.y && mouse_y <= help.y + 14 && !mouse_cap ? (
  displayHelp = 1;
);

displayHelp > 0.01 ? (
  gfx_a = displayHelp;
  gfx_mode = 0;
  middleX = gfx_w/2; middleY = gfx_h/2;
  gfx_r = 0.1; gfx_g = 0.1; gfx_b = 0.1;
  gfx_rect(middleX-120,middleY-120,240,240);
  gfx_r = 1; gfx_g = 1; gfx_b = 1;
  gfx_x = middleX-100; gfx_y = middleY-100;
  gfx_setfont(1,"Arial",12);
  gfx_drawstr("SET MARKER 1: CTRL + LMB\nSET MARKER 2: SHIFT + LMB\n\nAUTO SET MARKER 1: CTRL + ALT + LMB\nAUTO SET MARKER 2: SHFT + ALT + LMB\n\nSET TRIGGER THRESHOLD: MMB\n\nCLEAR DISPLAY: RMB\nSELECTION: LMB + DRAG\n\nSYNC DISPLAY TO LOOP: Buffer Size = 0 \n\nSKOPE II by Stige T. (C) 2016");
  displayHelp *= 0.9;
);
