desc:VVFilter4-1
options:no_meter

import inc\mouse3Full.jsfx-inc 
//----------------------------
import Grid1-2.jsfx-inc
import Curve3-2.jsfx-inc
import Karlsen Filter2.jsfx-inc

//----------------------------
slider1:0<0,1,1{BeatSync,Retrig}>-Mode
slider2:5<0,8,1{1/16,1/8,1/4,1/2,3/4,4/4,8/4,16/4,32/4}>Loop Length
slider3:0<0,100,1>-parameter3
slider4:0<0,3,1>Current Pattern
slider5:0<0,1,1{Freq,Reso}> Filter Parameter

// Filter Sliders ------------
slider11:0<0,3,{LP,HP,BP,BRej}>Filter Type
slider12:1000<20,22050,0.1>-Frequency(Hz)
//slider13:0<0,0.6,0.01>Resonanse
slider13:0<0,0.7,0.01>-Resonanse
slider14:0<-60,18,0.1>-Gain dB 

//----------------------------

@init
AAI+=1;
//------------
ext_noinit = 1;
//------------
R = G = B = 20;  // color for test 
gfx_clear = R + G*256 + B*65536;
//==============================================================================
// -- Some default values ---
crv_max_pnts = 128; // max_n_pnts for all curves
buf = 0;     // first curve mem offset; each crv requires max_n_pnts*2 mem slots(xx + yy)

//==========================================================
  //-- Value from norm_val(Log) ----------------------------
function norm_val_to_val(norm_val)
  local(minv,maxv,scale) // лок. сохраняются между вызовами
( 
  !minv ? ( 
    minv = log(25); 
    maxv = log(18000);
    scale = maxv-minv;
   );
  exp(minv + scale*norm_val); // ret value
);

/*------------------------------------------------------------------------------
---- Curve Patterns ------------------------------------------------------------
------------------------------------------------------------------------------*/
/*--------------------------------------------------------------
---- Pattern_Mirror --------------------------------------------
--------------------------------------------------------------*/
// Set Current Pattern(mirror to original)
/* Это крайне полезная функция! Эта функция позволяет в разы упростить, 
а главное - ускорить весь дальнейший код.
Функция копирует данные текущего объекта во временный объект и обратно(в зав. от аргументов).
И дальше можно работать со временным объектом, не парясь, и не перебирая каждый раз все namespace. 
Можно сказать, это одна из основных фунуций. Некоторые вещи, правда, можно поубирать, они не важны.*/

function Pattern_Mirror(SrcPtn*, DestPtn*) 
( 
  DestPtn.LoopLenQN = SrcPtn.LoopLenQN;
  DestPtn.ActCurve = SrcPtn.ActCurve;
  // curve A -----------
  DestPtn.crv_A.Active     = SrcPtn.crv_A.Active;
  DestPtn.crv_A.max_n_pnts = SrcPtn.crv_A.max_n_pnts;
  DestPtn.crv_A.n_pnts   = SrcPtn.crv_A.n_pnts;
  DestPtn.crv_A.snap     = SrcPtn.crv_A.snap;
  DestPtn.crv_A.cap_pnt  = SrcPtn.crv_A.cap_pnt;
  // curve B -----------
  DestPtn.crv_B.Active     = SrcPtn.crv_B.Active;
  DestPtn.crv_B.max_n_pnts = SrcPtn.crv_B.max_n_pnts;
  DestPtn.crv_B.n_pnts   = SrcPtn.crv_B.n_pnts;
  DestPtn.crv_B.snap     = SrcPtn.crv_B.snap;
  DestPtn.crv_B.cap_pnt  = SrcPtn.crv_B.cap_pnt;
  // curve A wnd x,y,w,h
  DestPtn.crv_A.x = SrcPtn.crv_A.x; 
  DestPtn.crv_A.y = SrcPtn.crv_A.y; 
  DestPtn.crv_A.w = SrcPtn.crv_A.w; 
  DestPtn.crv_A.h = SrcPtn.crv_A.h;
  // curve B wnd x,y,w,h
  DestPtn.crv_B.x = SrcPtn.crv_B.x;
  DestPtn.crv_B.y = SrcPtn.crv_B.y; 
  DestPtn.crv_B.w = SrcPtn.crv_B.w; 
  DestPtn.crv_B.h = SrcPtn.crv_B.h;
  // curvebuf ----------------
  DestPtn.crv_A.curvebuf = SrcPtn.crv_A.curvebuf;  
  DestPtn.crv_A.xx = SrcPtn.crv_A.xx; // x-coord points mem slots offs
  DestPtn.crv_A.yy = SrcPtn.crv_A.yy; // y-coord points mem slots offs
  DestPtn.crv_B.curvebuf = SrcPtn.crv_B.curvebuf;  
  DestPtn.crv_B.xx = SrcPtn.crv_B.xx; // x-coord points mem slots offs
  DestPtn.crv_B.yy = SrcPtn.crv_B.yy; // y-coord points mem slots offs
);

//--------------------------------------------------------------
// Это ВСЕ надо назвать более понятно!!!
function UpdateParPtn(Pattern) // CurPtn to Original
(
    Pattern == 0 ? Pattern_Mirror(CurPtn, P0) : // update original
    Pattern == 1 ? Pattern_Mirror(CurPtn, P1) : 
    Pattern == 2 ? Pattern_Mirror(CurPtn, P2) :
    Pattern == 3 ? Pattern_Mirror(CurPtn, P3);
);

function UpdateCurPtn(Pattern) // Original to CurPtn
(
    Pattern == 0 ? Pattern_Mirror(P0, CurPtn) : // set current
    Pattern == 1 ? Pattern_Mirror(P1, CurPtn) : 
    Pattern == 2 ? Pattern_Mirror(P2, CurPtn) :
    Pattern == 3 ? Pattern_Mirror(P3, CurPtn);
);

//--------------------------------------------------------------
//----------------------------
function LoopLenQN_fromSldr()
  local(str,nm,dnm)
( 
  nm = dnm = 0;
  strcpy_fromslider(str, slider2);
  match("%d/%d", str , nm, dnm );
  nm/dnm * 4; // return
);

//----------------------------
function LoopLenQN_toSldr(LL)
( 
  slider2 = 0; // reset
  while( LoopLenQN_fromSldr() < LL && slider2 < 10) (slider2+=1); // set slider
);

/*--------------------------------------------------------------
---- Pattern Functions -----------------------------------------
--------------------------------------------------------------*/
/* Паттерны можно сконфигурировать иначе, в целом, как угодно для других задач.
Например, кривые одна над другой или в ряд, или в клетку и тп. 
Можно и несколько вариантов в одном проекте, даже нужно сделать*/

function Pattern_Init(x,y,w,h, buf, crv_max_pnts)
(
  this.LoopLenQN = 4;  // def LoopLenQN
  this.ActCurve = 0;     // def act crv
  this.crv_A.Active = 1;
  this.crv_B.Active = 0;
  this.crv_A.Curve_Init(x,y,w,h, buf,                  crv_max_pnts); // 0
  this.crv_A.Curve_Const(0.8);
  this.crv_B.Curve_Init(x,y,w,h, buf + crv_max_pnts*2, crv_max_pnts); // 1
  this.crv_B.Curve_Const(0.5);
);
//------------------
function Pattern_SetActCurve(act_crv)
( 
  this.ActCurve = act_crv;
  this.crv_A.Active = !act_crv; 
  this.crv_B.Active = act_crv;
);
//------------------
function Pattern_SetLoopLenQN(LoopLenQN)
( 
  this.LoopLenQN = LoopLenQN; 
);
//------------------
function Pattern_Draw()
  local(fa, ra)
( 
  fa = ra = 0.7;
  this.crv_A.Active ? ra = 0.15 : fa = 0.15;
  gfx_set(1,1,0, fa);
  this.crv_A.Curve_Draw();
  gfx_set(0,1,0, ra);
  this.crv_B.Curve_Draw();
);


//----------------------------
P0.Pattern_Init(50,20,720,240, buf, crv_max_pnts);
P1.Pattern_Init(50,20,720,240, buf += crv_max_pnts*4, crv_max_pnts);
P2.Pattern_Init(50,20,720,240, buf += crv_max_pnts*4, crv_max_pnts);
P3.Pattern_Init(50,20,720,240, buf += crv_max_pnts*4, crv_max_pnts);

//-------------
Pattern_Mirror(P0, CurPtn); // def pattern P0


@slider
AASer ? AASldr+=1;
/*Тут надо что-то делать, мутно. И в целом эти слайдеры не нужны.
При смене пресета слайдеры не обновляются в соотв. с сериалайз, 
а если обновлять закручено...
Все станет очень просто -е сли не прикручивать автоматизацию.
Если прикручивать - нужно искать другой подход.*/


//--------------------------------------
Pattern != slider4 ? (
  UpdateParPtn(Pattern);
  Pattern = slider4; // New Pattern
  UpdateCurPtn(Pattern);
  //-----------------
  LoopLenQN_toSldr(CurPtn.LoopLenQN);
  AXXX+=1;
);

//--------------------------------------
LL = LoopLenQN_fromSldr();
LL != CurPtn.LoopLenQN ? CurPtn.LoopLenQN = LL;
CurPtn.Pattern_SetActCurve(slider5);

//-- Filter ---------
FilterK.SetParameters(slider11, slider12, slider13);

@serialize
AASer+=1;
/*Важно, перед сохранением пресетов, сериалайз, 
нужно тоже скидывать CurPtn в оригинал, а потом поднимать!!!
Сейчас возможны косяки на этом! Но вроде нормально.*/
UpdateParPtn(Pattern); // before serialize
//--------------------------------------
function Pattern_Serialize()
(     
   file_var(0, this.LoopLenQN);
   file_var(0, this.ActCurve);
   file_var(0, this.crv_A.n_pnts);
   file_var(0, this.crv_B.n_pnts);
   // поправить, max тут нет смысла хранить?, можно только текущее n_pnts для каждой.
   // только нужно делать xx и yy  отдельно!
   file_mem(0, this.crv_A.curvebuf, this.crv_A.max_n_pnts*2); // curve points
   file_mem(0, this.crv_B.curvebuf, this.crv_B.max_n_pnts*2); // curve points
);

//--------------------------------------
P0.Pattern_Serialize();
P1.Pattern_Serialize();
P2.Pattern_Serialize();
P3.Pattern_Serialize();
//CurPtn.Pattern_Serialize();
//--------------------------------------
UpdateCurPtn(Pattern); // after serialize
//-------------------
LoopLenQN_toSldr(CurPtn.LoopLenQN); // upd LL


//==========================================================
@block
/* Тут надо переделать.
Их вообще нет смысла обновлять, этих слайдеров не будет, кроме pass.
А pass будет вручную выбираться(наверное, может и нет). */
//slider12 = cutoffFreq;
//slider13 = resonance; 


/* Позиция(beat_pos) автом. обновляется при проигрывании, позволяет учитывать изм. темпа.
В остановл. состоянии используются данные в поз. курсора */
play_state ? beat_pos = beat_position; // beat position on block start(upd when playing only)
beats_per_spl = tempo/(60 * srate);


//==========================================================
@sample
//---------------------------------
beat_pos += beats_per_spl; // accurate beat position

//---------------------------------
function ApplyCurve()
(
  Xpos = beat_pos/CurPtn.looplenQN;
  Xpos = Xpos - floor(Xpos);   // Rel pos(0...1 НЕ дробная часть, а именно отн. позиция)!
  this.Curve_App_to_Val(Xpos);    
);


//-- Apply Curves -----------------
CurPtn.crv_A.ApplyCurve(); // Freq
CurPtn.crv_B.ApplyCurve(); // Reso

cutoffFreq = norm_val_to_val(CurPtn.crv_A.out_val);
resonance = 0.7 * CurPtn.crv_B.out_val;

//-- Filter -----------------------
FilterK.SetParameters(slider11, cutoffFreq, resonance); // (pass, cutoffFreq, resonance)
FilterK.in_l = spl0;  // Filter IN L
FilterK.in_r = spl1;  // Filter IN R
FilterK.Apply();      // Filtering
spl0 = FilterK.out_l; // Filter Out L
spl1 = FilterK.out_r; // Filter Out R

//==========================================================
@gfx 800 400
function UpdateCoords()
(
  CurPtn.crv_A.w = CurPtn.crv_B.w = gfx_w-80;
  CurPtn.crv_A.h = CurPtn.crv_B.h = gfx_h-80;
);

//----------------
function Curve_Grid()
  local(x,y,w,h, n_lines, mode)
(  
  x = CurPtn.crv_A.x; y = CurPtn.crv_A.y; 
  w = CurPtn.crv_A.w; h = CurPtn.crv_A.h;
  n_lines = 17;
  CurPtn.ActCurve ? mode = "Prc" : mode = "Hz";
  Draw_BG(x,y,w,h);             // Background
  gfx_set(0.5,0.7,0.5, 0.15);     // grid lines color
  Draw_Grid(x,y,w,h, n_lines);
  gfx_set(0.6,0.6,0.6,0.8);     // values color
  gfx_setfont(1,"Calibri", 14); // values font
  Draw_GridValues(x,y,w,h, mode)
);

//-- Draw Controls ---------------------
function Draw_Controls()
( 
  UpdateCoords();
  Curve_Grid();
  CurPtn.Pattern_Draw(); 
);


//--------------------------------------
GetMouseState(); 
Draw_Controls();
SetMouseLastState();

