// Copyright 2014, tb-software.com
// All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are permitted 
//provided that the following conditions are met:
//
//Redistributions of source code must retain the above copyright notice, this list of conditions 
//and the following disclaimer. 
//
//Redistributions in binary form must reproduce the above copyright notice, this list of conditions 
//and the following disclaimer in the documentation and/or other materials provided with the distribution. 
//
//The name of tb-software.com may not be used to endorse or 
//promote products derived from this software without specific prior written permission. 
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
//IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
//FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
//BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
//STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
//THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Provide Automatic Gainstaging for e.g. Nebula Audio VST
// 
// by TB Pro Audio 2014 (www.tb-software.com)

// Usage:
// 0. Place AutoGainStage_src before plugins you want to gainstage and AutoGainStage_cntrl after 
// 1. Setup Drive Reference Level, e.g. -18 for most Nebula custom programs (0dBVU = -18dBfs)
// 2. Set Drive Control to "Analyse" in order to determine necessary gain to reach reference 
//    level and then let the DAW play ...
// 3. Set Drive Control to "Drive" in order to "drive" the following plugins with measured add. gain, 
//    the add. gain will be compensated in the control plugin

// Changelog
// 1.0: first public release
// 1.1: Rearrangement of includes
// 1.2: Automatic PDC Calculation
// 1.3: LinkID Bug Fixed, PDC extended t0 128k Smp

desc:AutoGainStage Control JSFX V1.3 (TB Pro Audio)
options:gmem=TBProAudio_AutoGain

import memorymanager.jsfx-inc
import tbproaudio_common.jsfx-inc
import ebur128.jsfx-inc
import audiomeasurement.jsfx-inc

slider1:0<0,31,1>LinkID
slider2:0<0,1,1{Off,On}>Bypass
slider3:300<10,1000,1>RMS window (ms)

slider9:0<0,1,1{Drive,Analyse}>Drive Control
slider10:0<-30,30,0.1>Drive (dB)
slider11:-18<-30,0,0.1>Drive Reference Level (dB)

slider20:-144<-144,6,0.1>RMS Avg (dB)
slider21:-144<-144,6,0.1>RMS Max (dB)

slider30:-144<-144,6,0.1>RMS Avg GS (dB)
slider31:-144<-144,6,0.1>RMS Max GS (dB)

slider50:0<0,128000,1>PDC (Smp)

@init
MM.MemMgr_Init(0);

// @funtions

// EO@funtions
DEF_MAX_PDC = 128000;

DelayLine.DL_Init(0, DEF_MAX_PDC, 0);

AudioMeasure_Source.AM_Init(10, 1000, 300, 0.75);
AudioMeasure_SourceGainStage.AM_Init(10, 1000, 300, 0.75);

// Auto PDC Calculation
PDCM.PDC_Measure_Init(DEF_MAX_PDC+1000, 1);
PDCM.PDC_Measure_Stop();

// Gain
post_gain_val = 1.0;
pre_gain_val = 1.0;

// EO@init

@slider
  // LinkID
  linkid   = slider1;
  
  AudioMeasure_Source.AM_Set_RMSWindow(slider3);
  AudioMeasure_SourceGainStage.AM_Set_RMSWindow(slider3);
  
  // PreGain
  (slider9 == 0) ?
  (
    pre_gain_val = 10^(slider10/20);
    post_gain_val = 1/pre_gain_val;  
  );
  
  // Delay
  DelayLine.DL_SetDelay(slider50);
  
// EO@slider 

@block
  // Link with src plugin
  PDCM.PDC_Measure_Block(linkid);
  pre_gain_pos  = linkid*(2*samplesblock + 2) + 0;
  globalmem_pos = linkid*(2*samplesblock + 2) + 2;

  (play_state == 1) ? 
  ( 
    
    slider20 = AudioMeasure_Source.AM_GetRMSIVal_db();
    slider21 = AudioMeasure_Source.AM_GetRMSSTMaxVal_db();

    slider30 = AudioMeasure_SourceGainStage.AM_GetRMSIVal_db();
    slider31 = AudioMeasure_SourceGainStage.AM_GetRMSSTMaxVal_db();
    
    (slider9 == 1) ? 
    (
      slider10 = slider11- slider21;
    );
  );

// EO@block
 
@sample
  // PDC Calc
  PDCM.PDC_Measure_Process1(spl0, spl1);
  (PDCM.PDC_Measure_DataReady()) ?
  (
    slider50 = PDCM.PDC_Measure_GetPDC()|0;
    DelayLine.DL_SetDelay(slider50);
  );

  // Get source samples and put it to delay line
  gmem[pre_gain_pos] = pre_gain_val;
  DelayLine.DL_Put(gmem[globalmem_pos], gmem[globalmem_pos+1]);
  DelayLine.DL_Get();
  
  (play_state == 1) ? 
  (
    in0 = DelayLine.out0;
    in1 = DelayLine.out1;

    AudioMeasure_Source.AM_Process(in0, in1);  
    
    in0 = DelayLine.out0*pre_gain_val;
    in1 = DelayLine.out1*pre_gain_val;
    
    AudioMeasure_SourceGainStage.AM_Process(in0, in1);  
  );  
  
  (slider2 == 1) ?
  (
    spl0 = DelayLine.out0;
    spl1 = DelayLine.out1;
  ):
  ( 
    spl0 *= post_gain_val;  
    spl1 *= post_gain_val;  
  );

  // next source sample 
  globalmem_pos+=2;
  
// EO@sample

@gfx 400 30

  button_posY = 10;
  
  // Reset Button
  gfx_r = gfx_g = gfx_b = gfx_a = 1.0;

  resetx = 4*gfx_w/4-gfx_texth*7;
  
  gfx_x = resetx;
  gfx_y = button_posY;
  gfx_drawstr("RESET");
  resetx2 = gfx_x;
    
  doreset = 0;
  mouse_cap ?
  (
    mouse_x >= resetx  && 
    mouse_x <= resetx2 && 
    mouse_y >= button_posY && 
    mouse_y <= button_posY+gfx_texth 
    ? doreset = 1;
  );
   
  doreset ?
  (
    DelayLine.DL_Reset();
    AudioMeasure_Source.AM_Reset();
    AudioMeasure_SourceGainStage.AM_Reset();

    slider20 = AudioMeasure_Source.AM_GetRMSIVal_db();
    slider21 = AudioMeasure_Source.AM_GetRMSSTMaxVal_db();
    
    slider30 = AudioMeasure_SourceGainStage.AM_GetRMSIVal_db();
    slider31 = AudioMeasure_SourceGainStage.AM_GetRMSSTMaxVal_db();
    
  );
  
  // AB Button
  gfx_r = gfx_g = gfx_b = gfx_a = 1.0;
  (slider2 == 1) ? 
  (
    gfx_r = 0.0;
    gfx_g = 1.0;
    gfx_b = 0.0;
  );
  SwitchAB_x1 = 0*gfx_w/3 + 20;
  
  gfx_x = SwitchAB_x1;
  gfx_y = button_posY;
  gfx_drawstr("BYPASS");
  SwitchAB_x2 = gfx_x;
  
  doSwitchAB = 0;
  (SwitchAB_cntr > 0) ? SwitchAB_cntr -= 1;
  (mouse_cap && (SwitchAB_cntr == 0)) ?
  (
    mouse_x >= SwitchAB_x1 && 
    mouse_x <= SwitchAB_x2 && 
    mouse_y >= button_posY  && 
    mouse_y <= button_posY+gfx_texth 
    ? doSwitchAB = 1;
  );
  
  doSwitchAB ?
  (
    doSwitchAB = 0;
    (slider2 == 0) ? slider2 = 1: slider2 = 0;
    SwitchAB_cntr = 15;
  );
  
  // Drive Control
  SwitchDC_x1 = 1*gfx_w/3;
  gfx_x = SwitchDC_x1;
  gfx_y = button_posY;

  (slider9 == 0) ? 
  (
    gfx_r = 1.0;
    gfx_g = 1.0;
    gfx_b = 1.0;
    gfx_drawstr("DRIVE");
  );
  (slider9 == 1) ? 
  (
    gfx_r = 0.0;
    gfx_g = 1.0;
    gfx_b = 0.0;
    gfx_drawstr("ANALYSE");
  );
  
  SwitchDC_x2 = gfx_x;
  
  doSwitchDC = 0;
  (SwitchDC_cntr > 0) ? SwitchDC_cntr -= 1;
  (mouse_cap && (SwitchDC_cntr == 0)) ?
  (
    mouse_x >= SwitchDC_x1 && 
    mouse_x <= SwitchDC_x2 && 
    mouse_y >= button_posY  && 
    mouse_y <= button_posY+gfx_texth 
    ? doSwitchDC = 1;
  );
  
  doSwitchDC ?
  (
    doSwitchDC = 0;
    (slider9 == 0) ? slider9 = 1: slider9 = 0;
    SwitchDC_cntr = 15;
  );
  
// Sync Button
  gfx_r = 0.0;
  gfx_g = 1.0;
  gfx_b = 0.0;
  (PDCM.PDC_Measure_IsRunning()) ?
  (
    gfx_r = 1.0;
    gfx_g = 0.0;
    gfx_b = 0.0;
  );
  Sync_x1 = 2*gfx_w/3-20;;
  
  gfx_x = Sync_x1;
  gfx_y = button_posY;
  gfx_drawstr("SYNC PDC");
  Sync_x2 = gfx_x;
  
  doSync = 0;
  (Sync_cntr > 0) ? Sync_cntr -= 1;
  (mouse_cap && (Sync_cntr == 0)) ?
  (
    mouse_x >= Sync_x1 && 
    mouse_x <= Sync_x2 && 
    mouse_y >= button_posY  && 
    mouse_y <= button_posY+gfx_texth 
    ? doSync = 1;
  );
  
  doSync ?
  (
    doSync = 0;
    Sync_cntr = 15;
    (PDCM.PDC_Measure_IsRunning()) ? 
    (
      PDCM.PDC_Measure_StopStart();
    ):
    (
      PDCM.PDC_Measure_Start();
    );
    slider2 = PDCM.PDC_Measure_GetPDC();
  );    
    
// EOL@GFX
