desc:jcjr_PeakLimit_2
//version: 1.00
// This effect Copyright (C) 2016 and later James Chandler Jr
//   http://www.errnum.com
// License: GPL - http://www.gnu.org/licenses/gpl.html
//tags: Peak Limiter
//author: James Chandler Jr

//Ceiling: default val = -1, min val = -6, max val = -0.1, change increment = 0.1 
slider1:-1<-6,-0.1,0.1>Ceiling (dB)
//Threshold: default val = -3, min val = -24, max val = -0.1, change increment = 0.1 
slider2:-3<-24,-0.1,0.1>Threshold (dB)

//GUI Level Meter images
//background
filename:0,PeakLimit_Meter_BG.png
//foreground
filename:1,PeakLimit_Meter_FG.png
//the tiny peak level indicator
filename:2,PeakLimit_Meter_PK.png
//the tiny slow RMS level indicator
filename:3,PeakLimit_Meter_SLO.png

@init
  //generic utility functions
  
  //should only call js_malloc within js @init section
  JS_MALLOC_NO_CLEARMEM = 0;
  JS_MALLOC_CLEARMEM = 1;
  g_next_js_malloc = 0;  
  function js_malloc(a_size, a_clearmem)
  local (l_result)
  (
    l_result = g_next_js_malloc;
    (a_clearmem != 0) ?
      memset(l_result, 0, a_size);
    g_next_js_malloc += a_size;
    l_result;
  );

  //Given Frequency, calculate first-order exponential Time Constant
  function FrequencyToTimeConstant(a_Frequency)
  local (l_result)
  (
    l_result = 1.0 / (2.0 * $pi * a_Frequency);
  );
  
  //Given first-order exponential Time Constant, calculate Frequency
  function TimeConstantToFrequency(a_TimeConstant)
  local (l_result)
  (
    l_result = 1.0 / (2.0 * $pi * a_TimeConstant);
  );
  
  LN2_VAL = log(2.0);
  INV_LN2_VAL = 1.0 / LN2_VAL;
  //arbitrary limits are imposed, which may or may not be sensible
  MIN_LOG2_INPUT = 10 ^ -15; //about -300 dB
  MIN_LOG2_OUTPUT = log(MIN_LOG2_INPUT) * INV_LN2_VAL;
  MAX_LOG2_INPUT = 10 ^ 15; //about +300 dB
  MAX_LOG2_OUTPUT = log(MAX_LOG2_INPUT) * INV_LN2_VAL;
  function Log_2(a_x)
  local (l_result)
  (
    ((a_x += MIN_LOG2_INPUT) >= MAX_LOG2_INPUT) ?
      l_result = MAX_LOG2_OUTPUT
    :
      l_result = log(a_x) * INV_LN2_VAL;
    l_result;
  );
  
  function Exp_2(a_x)
  local (l_result)
  (
    (a_x <= MIN_LOG2_OUTPUT) ?
      l_result = MIN_LOG2_INPUT
    :
      (a_x >= MAX_LOG2_OUTPUT) ?
        l_result = MAX_LOG2_INPUT
      :
        l_result = exp(a_x * LN2_VAL);
    l_result;
  );
  
  LOG2_TO_DB_AMPLITUDE_MUL = 20.0 * log10(2.0);
  LOG2_TO_DB_POWER_MUL = 10.0 * log10(2.0);
  DB_AMPLITUDE_TO_LOG2_MUL = 1.0 / LOG2_TO_DB_AMPLITUDE_MUL;
  DB_POWER_TO_LOG2_MUL = 1.0 / LOG2_TO_DB_POWER_MUL;
  function DB_Amplitude_To_Log2(a_DBVal) //given a dB value, calc the equivalent as raw log2 value
  (
    a_DBVal * DB_AMPLITUDE_TO_LOG2_MUL;
  );
  
  function DB_Power_To_Log2(a_DBVal) //given a dB value, calc the equivalent as raw log2 value
  (
    a_DBVal * DB_POWER_TO_LOG2_MUL;
  );
  
  function Log2_To_DB_Amplitude(a_Log2Val) //given a raw log2 value, calc the equivalent as dB
  (
    a_Log2Val * LOG2_TO_DB_AMPLITUDE_MUL;
  );
  
  function Log2_To_DB_Power(a_Log2Val) //given a raw log2 value, calc the equivalent as dB
  (
    a_Log2Val * LOG2_TO_DB_POWER_MUL;
  );
  
  LN10_VAL = log(10.0);
  INV_LN10_VAL = 1.0 / LN10_VAL;
  DB_LN10_AMPLITUDE_MUL = LN10_VAL * 0.05;
  DB_LN10_POWER_MUL = LN10_VAL * 0.1;
  AMPLITUDE_LN10_DB_MUL = 20.0 * INV_LN10_VAL;
  POWER_LN10_DB_MUL = 10.0 * INV_LN10_VAL;
  function DB_To_Amplitude(a_DBVal)
  (
    exp(a_DBVal * DB_LN10_AMPLITUDE_MUL);
  );
  
  function DB_To_Power(a_DBVal)
  (
    exp(a_DBVal * DB_LN10_POWER_MUL);
  );
  
  function Amplitude_To_DB(a_AmplitudeVal)
  (
    log(a_AmplitudeVal) * AMPLITUDE_LN10_DB_MUL;
  );
  
  function Power_To_DB(a_PowerVal)
  (
    log(a_PowerVal) * POWER_LN10_DB_MUL;
  );
  
  //First order trapezoidal filter object
  //Code adapted from Vadim Zavalishin's book "The Art of VA Filter Design"
  FIRST_ORD_FILTTYPE_LOPASS = 0;
  FIRST_ORD_FILTTYPE_HIPASS = 1;
  FIRST_ORD_FILTTYPE_ALLPASS_ADV = 2; //+180 degrees phase shift at DC, descending to 0 degrees phase shift at nyquist
  FIRST_ORD_FILTTYPE_ALLPASS_RET = 3; //0 degrees phase shift at DC, descending to -180 degrees phase shift at nyquist
  
  //FiltTyps: Use one of the above to set the return value of FirstOrdTrapezoidFilter_DoSamp()
  //        : However, all values are simultaneously accessible after calling DoSamp() by reading
  //        : TheFilter.lp, TheFilter.hp, TheFilter.ap_A, TheFilter.ap_R
  //a_FiltFC: Filter center frequency in Hz
  //a_SampRate: Samplerate of filter
  function FirstOrdTrapezoidFilter_Init(a_FiltType, a_FiltFC, a_SampRate)
  (
    this.FT = a_FiltType;
    this.SR = a_SampRate;
    this.Nyquist = floor(this.SR * 0.5);
    this.FC = min(a_FiltFC, this.Nyquist - 1);

    this.s = 0.0;
    this.lp = 0;
    this.hp = 0;
    this.ap_A = 0;
    this.ap_R = 0;
    
    //calculate coefficient 
    this.g = tan($pi * this.FC / this.SR);
    this.g /= (1 + this.g);
  );
  
  function FirstOrdTrapezoidFilter_SetFC(a_FiltFC)
  (
    this.FC = min(a_FiltFC, this.Nyquist - 1);
    this.g = tan($pi * this.FC / this.SR);
    this.g /= (1 + this.g);
  );
  
  //Returns the filtered sample
  function FirstOrdTrapezoidFilter_DoSamp(a_InSamp)
  local (l_v, l_result)
  (
    //Vadim Zavalishin code
    //v = (x-z1_state)*g/(1+g);
    //y = v + z1_state;
    //z1_state = y + v;
    l_v = (a_InSamp - this.s) * this.g;
    this.lp = l_v + this.s;
    this.s = this.lp + l_v;
    this.hp = a_InSamp - this.lp;
    this.ap_A = this.hp - this.lp;
    this.ap_R = this.lp - this.hp;
    
    (this.FT == FIRST_ORD_FILTTYPE_LOPASS) ?
      l_result = this.lp
    :
      (this.FT == FIRST_ORD_FILTTYPE_HIPASS) ?
        l_result = this.hp
      :
        (this.FT == FIRST_ORD_FILTTYPE_ALLPASS_ADV) ?
          l_result = this.ap_A
        :
          l_result = this.ap_R;
    l_result;
  );
  
  //instant attack downward, release upward
  function GainMeterEnvelope_Init()
  (
    this.RCoff = exp((-1.0 * 0.1155) / (0.03 * srate)); //meter gain recovers 1 dB per 500 ms
    this.EnvVal = 0;
  );
  
  function GainMeterEnvelope_DoSamp(a_InSamp)
  local (l_Diff, l_g)
  (
    l_Diff = this.EnvVal - a_InSamp; //smooth for the gain meter display
    (l_Diff > 0) ? //instant attack downward, but use a release time upward, make it easier for the eye to see max gain reductions
      this.EnvVal = a_InSamp
    :
      this.EnvVal = a_InSamp + this.RCoff * l_Diff;
  );  
    
  //AudioMeter object calculates various level envelopes for display, but does not actually draw the on-screen meter
  function AudioMeter_Init(a_SampRate)
  local (l_TCSamps)
  (
    this.SR = a_SampRate;
    //reaches 99 percent RMS (about 98 percent squared values) in about 3.9 TC
    //approx VU meter ballistics of 300 ms, but the 99 percent is after square root
    //without the square root, would be about 5 time constants, or 0.06 seconds
    l_TCSamps = 0.06766 * this.SR; 
    this.ARCoff_VU = 1.0 / (2.0 * l_TCSamps); //approx VU meter 300 ms ballistics
    this.ARCoff_VU /= (1 + this.ARCoff_VU);
    l_TCSamps /= 3.0; //try about 100 ms for fast RMS display
    this.ARCoff_Fast = 1.0 / (2.0 * l_TCSamps);
    this.ARCoff_Fast /= (1 + this.ARCoff_Fast);
    l_TCSamps *= 99.0; //try 33 X the VU TC, about 10000 ms to 99 percent
    this.ARCoff_Slow = 1.0 / (2.0 * l_TCSamps);
    this.ARCoff_Slow /= (1 + this.ARCoff_Slow);
    l_TCSamps = 0.100 * this.SR * 8.659; //meter peak decays 1 dB per 100 ms, smoothing screen updates at 60 times per second
    this.RCoff_Peak = 1.0 / (2.0 * l_TCSamps);
    this.RCoff_Peak /= (1 + this.RCoff_Peak);
    
    this.Env_Peak_L = 0; //Peak envelope values
    this.Env_Peak_R = 0;
    this.Max_Peak_L = 0; //Max peaks since last reset
    this.Max_Peak_R = 0;
    
    this.Env_VU_L = 0; //VU envelope values
    this.Env_VU_R = 0;
    this.Max_VU_L = 0; //Max VU since last reset
    this.Max_VU_R = 0;
  
    this.Env_Fast_L = 0; //Fast RMS envelope values
    this.Env_Fast_R = 0;
    this.Env_Slow_L = 0; //Slow, long-term envelope values
    this.Env_Slow_R = 0;
  
    this.S_Peak_L = 0; //filter state variables
    this.S_Peak_R = 0;
    this.S_Fast_L = 0;
    this.S_Fast_R = 0;
    this.S_VU_L = 0;
    this.S_VU_R = 0;
    this.S_Slow_L = 0;
    this.S_Slow_R = 0;
  );
  
  function AudioMeter_Reset()
  (
    this.Env_Peak_L = 0;
    this.Env_Peak_R = 0;
    this.Max_Peak_L = 0;
    this.Max_Peak_R = 0;
    
    this.Env_Fast_L = 0;
    this.Env_Fast_R = 0;

    this.Env_VU_L = 0;
    this.Env_VU_R = 0;
    this.Max_VU_L = 0;
    this.Max_VU_R = 0;

    this.Env_Fast_L = 0;
    this.Env_Fast_R = 0;
    this.Env_Slow_L = 0;
    this.Env_Slow_R = 0;
  
    this.S_Peak_L = 0;
    this.S_Peak_R = 0;
    this.S_Fast_L = 0;
    this.S_Fast_R = 0;
    this.S_VU_L = 0;
    this.S_VU_R = 0;
    this.S_Slow_L = 0;
    this.S_Slow_R = 0;
  );
        
  function AudioMeter_DoSamp(a_In_L, a_In_R)
  local (l_PeakL, l_PeakR, l_SqrL, l_SqrR, l_v)
  (
    l_PeakL = abs(a_In_L); //find input peak for metering
    l_PeakR = abs(a_In_R);
    this.Max_Peak_L = max(this.Max_Peak_L, l_PeakL); //remember max peak
    this.Max_Peak_R = max(this.Max_Peak_R, l_PeakR);

    (this.S_Peak_L < l_PeakL) ? //instant attack, slower release
    (
      this.S_Peak_L = l_PeakL;
      this.Env_Peak_L = l_PeakL;
    )
    :
    (
      l_v = (l_PeakL - this.S_Peak_L) * this.RCoff_Peak;
      this.Env_Peak_L = l_v + this.S_Peak_L;
      this.S_Peak_L = this.Env_Peak_L + l_v;
    );
  
    (this.S_Peak_R < l_PeakR) ?
    (
      this.S_Peak_R = l_PeakR;
      this.Env_Peak_R = l_PeakR;
    )
    :
    (
      l_v = (l_PeakR - this.S_Peak_R) * this.RCoff_Peak;
      this.Env_Peak_R = l_v + this.S_Peak_R;
      this.S_Peak_R = this.Env_Peak_R + l_v;
    );
    
    l_SqrL = a_In_L * a_In_L;
    l_SqrR = a_In_R * a_In_R;
    
    (this.S_Fast_L < l_SqrL) ? //instant attack, slower release on the fast RMS
    (
      this.S_Fast_L = l_SqrL;
      this.Env_Fast_L = l_SqrL;
    )
    :
    (
      l_v = (l_SqrL - this.S_Fast_L) * this.ARCoff_Fast;
      this.Env_Fast_L = l_v + this.S_Fast_L;
      this.S_Fast_L = this.Env_Fast_L + l_v;
    );
    
    l_v = (l_SqrL - this.S_VU_L) * this.ARCoff_VU;
    this.Env_VU_L = l_v + this.S_VU_L;
    this.S_VU_L = this.Env_VU_L + l_v;
    this.Max_VU_L = max(this.Max_VU_L, this.Env_VU_L); //remember max VU
  
    l_v = (this.Env_VU_L - this.S_Slow_L) * this.ARCoff_Slow;
    this.Env_Slow_L = l_v + this.S_Slow_L;
    this.S_Slow_L = this.Env_Slow_L + l_v;

    (this.S_Fast_R < l_SqrR) ?
    (
      this.S_Fast_R = l_SqrR;
      this.Env_Fast_R = l_SqrR;    
    )
    :
    (
      l_v = (l_SqrR - this.S_Fast_R) * this.ARCoff_Fast;
      this.Env_Fast_R = l_v + this.S_Fast_R;
      this.S_Fast_R = this.Env_Fast_R + l_v;
    );
    
    l_v = (l_SqrR - this.S_VU_R) * this.ARCoff_VU; //symmetrical attack and release
    this.Env_VU_R = l_v + this.S_VU_R;
    this.S_VU_R = this.Env_VU_R + l_v;
    this.Max_VU_R = max(this.Max_VU_R, this.Env_VU_R);
  
    l_v = (this.Env_VU_R - this.S_Slow_R) * this.ARCoff_Slow; //symmetrical attack and release
    this.Env_Slow_R = l_v + this.S_Slow_R;
    this.S_Slow_R = this.Env_Slow_R + l_v;
  );

  function AudioMeter_GetPeakMono()
  (
    max(this.Env_Peak_L, this.Env_Peak_R);
  );
  
  function AudioMeter_GetMaxPeakMono()
  (
    max(this.Max_Peak_L, this.Max_Peak_R);
  );
  
  function AudioMeter_GetVUMono()
  (
    max(this.Env_VU_L, this.Env_VU_R);
  );
  
  function AudioMeter_GetMaxVUMono()
  (
    max(this.Max_VU_L, this.Max_VU_R);
  );
  
  function AudioMeter_GetFastMono()
  (
    max(this.Env_Fast_L, this.Env_Fast_R);
  );

  function AudioMeter_GetSlowMono()
  (
    max(this.Env_Slow_L, this.Env_Slow_R);
  );

  function JOS_AP_FracDly_Init(a_FracSampDly)
  (
    this.FracSampDly = max(min(a_FracSampDly, 0.99), 0.01);
    this.k = (1 - this.FracSampDly) / (1 + this.FracSampDly);
    this.p1 = 0.0;
    this.last_p1 = 0.0;
  );

  //only call after filter was created via _Init
  function JOS_AP_FracDly_SetFracSampDly(a_FracSampDly)
  (
    this.FracSampDly = max(min(a_FracSampDly, 0.99), 0.01);
    this.k = (1 - this.FracSampDly) / (1 + this.FracSampDly);
  );

  //only call after filter was created via _Init
  //Returns the delayed sample
  function JOS_AP_FracDly_DoSamp(a_InSamp)
  local (l_result)
  (
    this.p1 = a_InSamp - (this.k * this.last_p1);
    l_result = this.last_p1 + (this.k * this.p1);
    this.last_p1 = this.p1;
    l_result;
  );

  //o_AutoRelMod.AutoRelModEnv_Init(g_PkHoldMinSmps * 0.4, srate, srate);
  //o_AutoRelMod.AutoRelModEnv_Inc(); //call if over thresh
  //o_AutoRelMod.AutoRelModEnv_Dec(); //call if under thresh
  //o_AutoRelMod.AutoRelModEnv_GetSampSpan(); //get the current samp span and store in autorel lookahead buffer
  //o_AutoRelMod.AutoRelModEnv_AutoRelTheEnvelopes(a_InputEnv_L, a_InputEnv_R, a_SampSpan);
  //input the lookahead tail sampspan, and the two lookahead tail peak envelopes
  //find the autorel left envelope at o_AutoRelMod.RelEnv_L
  //find autorel right envelope at o_AutoRelMod.RelEnv_R
  //Find meter autorel milliseconds at o_AutoRelMod.AutoRelMsSpan
  function AutoRelModEnv_Init(a_MinSampSpan, a_MaxSampSpan, a_SampRate)
  (
    this.MinSampSpan = a_MinSampSpan;
    this.MaxSampSpan = a_MaxSampSpan;
    this.SR = a_SampRate;
    this.AutoRelSampSpan = a_MinSampSpan;
    this.MsMul = 1000.0 / this.SR;
    this.AutoRelMsSpan = this.MsMul * this.AutoRelSampSpan;
    this.AutoRelModEnv = 0.0000001;
    this.RelEnv_L = 0;
    this.RelEnv_R = 0;

    this.ModEnv_AtkMul_1 = 1 / floor(80 * 0.001 * this.SR); //Mono evaluated 2 times per stereo frams, time consts twice as long
    this.ModEnv_AtkMul_2 = 1 - this.ModEnv_AtkMul_1;
    this.ModEnv_RelMul_1 = 1 / floor(400 * 0.001 * this.SR);
    this.ModEnv_RelMul_2 = 1 - this.ModEnv_RelMul_1;
  );

  function AutoRelModEnv_Inc()
  (
    this.AutoRelModEnv = this.ModEnv_AtkMul_2 * this.AutoRelModEnv + this.ModEnv_AtkMul_1;
  );
  
  function AutoRelModEnv_Dec()
  (
    this.AutoRelModEnv = max(this.ModEnv_RelMul_2 * this.AutoRelModEnv, 0.0000001);
  );
  
  //get the current autorel sample span, to store in autorel lookahead buffer
  function AutoRelModEnv_GetSampSpan()
  (
    this.AutoRelSampSpan = this.MinSampSpan + (this.MaxSampSpan * this.AutoRelModEnv);  
  );
  
  function AutoRelModEnv_AutoRelTheEnvelopes(a_InputEnv_L, a_InputEnv_R, a_SampSpan)
  local (l_RelMul_1, l_RelMul_2, l_Diff)  
  (
    l_RelMul_1 = 1.0 / a_SampSpan;
    l_RelMul_2 = 1.0 - l_RelMul_1;
   
    l_Diff = a_InputEnv_L - this.RelEnv_L;
    (l_Diff < -0.000001) ? //release
      this.RelEnv_L = this.RelEnv_L * l_RelMul_2 + a_InputEnv_L * l_RelMul_1
    :
      this.RelEnv_L = a_InputEnv_L;
    
    l_Diff = a_InputEnv_R - this.RelEnv_R;
    (l_Diff < -0.000001) ? //release
      this.RelEnv_R = this.RelEnv_R * l_RelMul_2 + a_InputEnv_R * l_RelMul_1
    :
      this.RelEnv_R = a_InputEnv_R;

    this.AutoRelMsSpan = this.MsMul * a_SampSpan;
  );
  
  //declarations
  pdc_bot_ch = 0;
  pdc_top_ch = 2;
  g_BGPicLoaded = 0; //flags to know if pics loaded
  g_FGPicLoaded = 0;
  g_PKPicLoaded = 0;
  g_SLOPicLoaded = 0;
  
  (srate >= 176400) ?
    g_OvrSmpMul = 1.0
  :
  (
    (srate >= 88200) ?
      g_OvrSmpMul = 2.0
    :
      g_OvrSmpMul = 4.0;
  );  

  g_MaxSongGain_L = 0;
  g_MinSongGain_L = 20;
  g_MaxSongGain_R = 0;
  g_MinSongGain_R = 20;
   
  LOOKAHEAD_MS = 50;
  g_LookAheadSmps = floor(LOOKAHEAD_MS * 0.001 * srate);
  g_PkHoldMinSmps = floor(0.0013 * srate); //1.3 ms
  g_PkHoldMaxSmps = floor(g_LookAheadSmps / 2) - 1; //about 25 ms
  
  g_PkLen_L = 0;
  g_PkLen_R = 0; 
  g_PkMaxVal_L = 1.0;
  g_PkMaxVal_R = 1.0;
  g_PkRelVal_L = 1.0;
  g_PkMaxVal_R = 1.0;
  g_PkRelLen_L = 0;
  g_PkRelLen_R = 0;
  g_PkRelDec_L = 0;
  g_PkRelDec_R = 0;  
  
  g_PkLim_ThreshDB = -3;
  g_PkLim_Thresh = 0.707;
  g_PkLim_ThreshInv = 1.414;
  g_MakeUpGainDB = 0;
  g_MakeUpGain = 1;
  g_CeilingDB = -1;

  //GUI meter objects
  o_InputMeter.AudioMeter_Init(srate);
  o_OutputMeter.AudioMeter_Init(srate);
  o_GainMeterEnv_L.GainMeterEnvelope_Init();
  o_GainMeterEnv_R.GainMeterEnvelope_Init();
  
  function ClearMaxes()
  (
    g_MaxSongGain_L = 0;
    g_MinSongGain_L = 20;
    g_MaxSongGain_R = 0;
    g_MinSongGain_R = 20;
    o_GainMeterEnv_L.EnvVal = g_MakeUpGain;
    o_GainMeterEnv_R.EnvVal = g_MakeUpGain;
    o_InputMeter.AudioMeter_Reset();
    o_OutputMeter.AudioMeter_Reset();    
  );
  
  function SetDBThresh(a_NewDBThresh, a_AlwaysSet)
  (
    a_NewDBThresh = max(min(a_NewDBThresh, -0.1), -24);
    ((g_PkLim_ThreshDB != a_NewDBThresh) || (a_AlwaysSet != 0)) ?
    (
      g_PkLim_ThreshDB = a_NewDBThresh;
      g_PkLim_Thresh = 10 ^ (g_PkLim_ThreshDB / 20);
      g_PkLim_ThreshInv = 1 / g_PkLim_Thresh;
      g_MakeUpGainDB = g_CeilingDB - g_PkLim_ThreshDB;
      g_MakeUpGain = 10 ^ (g_MakeUpGainDB / 20);
    );
    ClearMaxes();
  );
  
  function SetDBCeiling(a_NewDBCeiling, a_AlwaysSet)
  (
    a_NewDBCeiling = max(min(a_NewDBCeiling, -0.1), -6);
    ((g_CeilingDB != a_NewDBCeiling) || (a_AlwaysSet != 0)) ?
    (
      g_CeilingDB = a_NewDBCeiling;
      g_MakeUpGainDB = g_CeilingDB - g_PkLim_ThreshDB;
      g_MakeUpGain = 10 ^ (g_MakeUpGainDB / 20);
    );
    ClearMaxes();
  );
  
  g_SmpHeadTail = 0;

  //memory arrays
  g_SmpBuf_L = js_malloc(g_LookAheadSmps, JS_MALLOC_CLEARMEM);
  g_SmpBuf_R = js_malloc(g_LookAheadSmps, JS_MALLOC_CLEARMEM);
  g_EnvBuf_L = js_malloc(g_LookAheadSmps, JS_MALLOC_CLEARMEM);
  g_EnvBuf_R = js_malloc(g_LookAheadSmps, JS_MALLOC_CLEARMEM);
  g_AutoRelModEnv_Buf = js_malloc(g_LookAheadSmps, JS_MALLOC_CLEARMEM);

  pdc_delay = g_LookAheadSmps;
  memset(g_EnvBuf_L, 1.0, g_LookAheadSmps);
  memset(g_EnvBuf_R, 1.0, g_LookAheadSmps);
  
  o_JosApDly_075_L.JOS_AP_FracDly_Init(0.75);
  o_JosApDly_050_L.JOS_AP_FracDly_Init(0.50);
  o_JosApDly_025_L.JOS_AP_FracDly_Init(0.25);
  o_JosApDly_075_R.JOS_AP_FracDly_Init(0.75);
  o_JosApDly_050_R.JOS_AP_FracDly_Init(0.50);
  o_JosApDly_025_R.JOS_AP_FracDly_Init(0.25);

  //Smooth the lookahead envelope (mainly for attacks but also applies to releases
  g_PkEnvSmoothFiltTimeConst = 0.001 / 5; //5 time consts in 1 ms
  g_PkEnvSmoothFiltFreq = TimeConstantToFrequency(g_PkEnvSmoothFiltTimeConst);
  o_PkEnvSmooth_1_L.FirstOrdTrapezoidFilter_Init(FIRST_ORD_FILTTYPE_LOPASS, g_PkEnvSmoothFiltFreq, srate);
  o_PkEnvSmooth_2_L.FirstOrdTrapezoidFilter_Init(FIRST_ORD_FILTTYPE_LOPASS, g_PkEnvSmoothFiltFreq, srate);
  o_PkEnvSmooth_3_L.FirstOrdTrapezoidFilter_Init(FIRST_ORD_FILTTYPE_LOPASS, g_PkEnvSmoothFiltFreq, srate);
  o_PkEnvSmooth_4_L.FirstOrdTrapezoidFilter_Init(FIRST_ORD_FILTTYPE_LOPASS, g_PkEnvSmoothFiltFreq, srate);
  o_PkEnvSmooth_1_R.FirstOrdTrapezoidFilter_Init(FIRST_ORD_FILTTYPE_LOPASS, g_PkEnvSmoothFiltFreq, srate);
  o_PkEnvSmooth_2_R.FirstOrdTrapezoidFilter_Init(FIRST_ORD_FILTTYPE_LOPASS, g_PkEnvSmoothFiltFreq, srate);
  o_PkEnvSmooth_3_R.FirstOrdTrapezoidFilter_Init(FIRST_ORD_FILTTYPE_LOPASS, g_PkEnvSmoothFiltFreq, srate);
  o_PkEnvSmooth_4_R.FirstOrdTrapezoidFilter_Init(FIRST_ORD_FILTTYPE_LOPASS, g_PkEnvSmoothFiltFreq, srate);
  o_PkEnvSmooth_1_L.s = 1.0;
  o_PkEnvSmooth_2_L.s = 1.0;
  o_PkEnvSmooth_3_L.s = 1.0;
  o_PkEnvSmooth_4_L.s = 1.0;
  o_PkEnvSmooth_1_R.s = 1.0;
  o_PkEnvSmooth_2_R.s = 1.0;
  o_PkEnvSmooth_3_R.s = 1.0;
  o_PkEnvSmooth_4_R.s = 1.0;
    
  o_AutoRelMod.AutoRelModEnv_Init(g_PkHoldMinSmps * 0.4, srate, srate);
  
  SetDBThresh(slider2, 1);
  SetDBCeiling(slider1, 1);
  
@slider
  (slider1 != g_CeilingDB) ?  
    SetDBCeiling(slider1, 0);
  (slider2 != g_PkLim_ThreshDB) ?  
    SetDBThresh(slider2, 0);

@block
  //
  
@sample
  g_SmpBuf_L[g_SmpHeadTail] = spl0; //save lookahead samples
  g_SmpBuf_R[g_SmpHeadTail] = spl1;
  
  s_MaxISP_L = abs(spl0);
  s_MaxISP_R = abs(spl1);

  (g_OvrSmpMul >= 4) ? //4X oversample to find ISPs
  (
    s_Tmp_L = o_JosApDly_075_L.JOS_AP_FracDly_DoSamp(spl0);
    s_MaxISP_L = max(s_MaxISP_L, abs(s_Tmp_L));    
    s_Tmp_L = o_JosApDly_050_L.JOS_AP_FracDly_DoSamp(spl0);
    s_MaxISP_L = max(s_MaxISP_L, abs(s_Tmp_L));
    s_Tmp_L = o_JosApDly_025_L.JOS_AP_FracDly_DoSamp(spl0);
    s_MaxISP_L = max(s_MaxISP_L, abs(s_Tmp_L));

    s_Tmp_R = o_JosApDly_075_R.JOS_AP_FracDly_DoSamp(spl1);
    s_MaxISP_R = max(s_MaxISP_R, abs(s_Tmp_R));    
    s_Tmp_R = o_JosApDly_050_R.JOS_AP_FracDly_DoSamp(spl1);
    s_MaxISP_R = max(s_MaxISP_R, abs(s_Tmp_R));
    s_Tmp_R = o_JosApDly_025_R.JOS_AP_FracDly_DoSamp(spl1);
    s_MaxISP_R = max(s_MaxISP_R, abs(s_Tmp_R));
  )
  :
  (
    (g_OvrSmpMul >= 2) ? //2X oversample
    (
      s_TmpL = o_JosApDly_050_L.JOS_AP_FracDly_DoSamp(spl0);
      s_MaxISP_L = max(s_MaxISP_L, abs(s_TmpL));
      s_TmpR = o_JosApDly_050_R.JOS_AP_FracDly_DoSamp(spl1);  
      s_MaxISP_R = max(s_MaxISP_R, abs(s_TmpR));
    ); 
  );
  
  s_PkVal = g_PkLim_ThreshInv * s_MaxISP_L; 
  (s_PkVal > 1.0) ?
  (
    g_PkLen_L += 1;
    g_PkMaxVal_L = max(s_PkVal, g_PkMaxVal_L);
    g_PkRelVal_L = max(g_PkRelVal_L - g_PkRelDec_L, 1.0);
    g_PkRelLen_L = max(g_PkRelLen_L - 1, 0);
    g_EnvBuf_L[g_SmpHeadTail] = max(g_PkMaxVal_L, g_PkRelVal_L);
    o_AutoRelMod.AutoRelModEnv_Inc(); //call if over thresh
  )
  : //s_PkVal <= 1.0
  (
    (g_PkLen_L > 0) ? //fill in attack and peak flat top of just-finished peak
    (
      g_PkLen_L = min(g_PkLen_L + g_PkHoldMinSmps, g_PkHoldMaxSmps);
      s_PkEnd = g_SmpHeadTail;
      s_PkStart = s_PkEnd - g_PkLen_L;
      (s_PkStart < 0) ?
        s_PkStart += g_LookAheadSmps;
  
      s_Idx = s_PkEnd;
      while (s_Idx != s_PkStart) //peak-hold with max peak value
      (
        g_EnvBuf_L[s_Idx] = max(g_EnvBuf_L[s_Idx], g_PkMaxVal_L);
        ((s_Idx -= 1) < 0) ?
          s_Idx += g_LookAheadSmps;
      );
  
      s_AtkVal = g_PkMaxVal_L;
      s_AtkDec = (g_PkMaxVal_L - 1) / g_PkLen_L;
      s_AtkStart = s_Idx - g_PkLen_L;
      (s_AtkStart < 0) ?
        s_AtkStart += g_LookAheadSmps;
      while (s_Idx != s_AtkStart) //do lookahead fade-in
      (
        s_AtkVal = max(s_AtkVal - s_AtkDec, 1.0);
        g_EnvBuf_L[s_Idx] = max(g_EnvBuf_L[s_Idx], s_AtkVal);
        ((s_Idx -= 1) < 0) ?
          s_Idx += g_LookAheadSmps;       
      );  
      g_PkRelVal_L = max(g_PkMaxVal_L, g_PkRelVal_L);
      g_PkRelLen_L = max(g_PkRelLen_L, 2 * g_PkLen_L * g_PkRelVal_L);
      g_PkRelDec_L = (g_PkRelVal_L - 1) / g_PkRelLen_L;
      g_PkLen_L = 0;
      g_PkMaxVal_L = 1.0;
    )
    :
    (
      g_PkRelVal_L = max(g_PkRelVal_L - g_PkRelDec_L, 1.0);
      g_PkRelLen_L = max(g_PkRelLen_L - 1, 0);
      g_EnvBuf_L[g_SmpHeadTail] = g_PkRelVal_L;  
    );
    o_AutoRelMod.AutoRelModEnv_Dec(); //call if under thresh
  );
  
  s_PkVal = g_PkLim_ThreshInv * s_MaxISP_R; 
  (s_PkVal > 1.0) ?
  (
    g_PkLen_R += 1;
    g_PkMaxVal_R = max(s_PkVal, g_PkMaxVal_R);
    g_PkRelVal_R = max(g_PkRelVal_R - g_PkRelDec_R, 1.0);
    g_PkRelLen_R = max(g_PkRelLen_R - 1, 0);
    g_EnvBuf_R[g_SmpHeadTail] = max(g_PkMaxVal_R, g_PkRelVal_R);
    o_AutoRelMod.AutoRelModEnv_Inc(); //call if over thresh
  )
  : //s_PkVal <= 1.0
  (
    (g_PkLen_R > 0) ? //fill in attack and peak flat top of just-finished peak
    (
      g_PkLen_R = min(g_PkLen_R + g_PkHoldMinSmps, g_PkHoldMaxSmps);
      s_PkEnd = g_SmpHeadTail;
      s_PkStart = s_PkEnd - g_PkLen_R;
      (s_PkStart < 0) ?
        s_PkStart += g_LookAheadSmps;

      s_Idx = s_PkEnd;
      while (s_Idx != s_PkStart) //peak-hold with max peak value
      (
        g_EnvBuf_R[s_Idx] = max(g_EnvBuf_R[s_Idx], g_PkMaxVal_R);
        ((s_Idx -= 1) < 0) ?
          s_Idx += g_LookAheadSmps;
      );

      s_AtkVal = g_PkMaxVal_R;
      s_AtkDec = (g_PkMaxVal_R - 1) / g_PkLen_R;
      s_AtkStart = s_Idx - g_PkLen_R;
      (s_AtkStart < 0) ?
        s_AtkStart += g_LookAheadSmps;
      while (s_Idx != s_AtkStart) //do lookahead fade-in
      (
        s_AtkVal = max(s_AtkVal - s_AtkDec, 1.0);
        g_EnvBuf_R[s_Idx] = max(g_EnvBuf_R[s_Idx], s_AtkVal);
        ((s_Idx -= 1) < 0) ?
          s_Idx += g_LookAheadSmps;       
      );  
      g_PkRelVal_R = max(g_PkMaxVal_R, g_PkRelVal_R);
      g_PkRelLen_R = max(g_PkRelLen_R, 2 * g_PkLen_R * g_PkRelVal_R);
      g_PkRelDec_R = (g_PkRelVal_R - 1) / g_PkRelLen_R;
      g_PkLen_R = 0;
      g_PkMaxVal_R = 1.0;
    )
    :
    (
      g_PkRelVal_R = max(g_PkRelVal_R - g_PkRelDec_R, 1.0);
      g_PkRelLen_R = max(g_PkRelLen_R - 1, 0);
      g_EnvBuf_R[g_SmpHeadTail] = g_PkRelVal_R;  
    );
    o_AutoRelMod.AutoRelModEnv_Dec(); //call if under thresh
  );
  
  g_AutoRelModEnv_Buf[g_SmpHeadTail] = o_AutoRelMod.AutoRelModEnv_GetSampSpan(); //get the current samp span and store in autorel lookahead buffer
  
  ((g_SmpHeadTail += 1) >= g_LookAheadSmps) ?
    g_SmpHeadTail = 0;
  
  s_Env_L = max(g_EnvBuf_L[g_SmpHeadTail], 1.0);
  g_EnvBuf_L[g_SmpHeadTail] = 1.0; 
  s_Env_L = o_PkEnvSmooth_1_L.FirstOrdTrapezoidFilter_DoSamp(s_Env_L);
  s_Env_L = o_PkEnvSmooth_2_L.FirstOrdTrapezoidFilter_DoSamp(s_Env_L);
  s_Env_L = o_PkEnvSmooth_3_L.FirstOrdTrapezoidFilter_DoSamp(s_Env_L);
  s_Env_L = o_PkEnvSmooth_4_L.FirstOrdTrapezoidFilter_DoSamp(s_Env_L);
  
  s_Env_R = max(g_EnvBuf_R[g_SmpHeadTail], 1.0);
  g_EnvBuf_R[g_SmpHeadTail] = 1.0;
  s_Env_R = o_PkEnvSmooth_1_R.FirstOrdTrapezoidFilter_DoSamp(s_Env_R);
  s_Env_R = o_PkEnvSmooth_2_R.FirstOrdTrapezoidFilter_DoSamp(s_Env_R);
  s_Env_R = o_PkEnvSmooth_3_R.FirstOrdTrapezoidFilter_DoSamp(s_Env_R);
  s_Env_R = o_PkEnvSmooth_4_R.FirstOrdTrapezoidFilter_DoSamp(s_Env_R);

  o_AutoRelMod.AutoRelModEnv_AutoRelTheEnvelopes(s_Env_L, s_Env_R, g_AutoRelModEnv_Buf[g_SmpHeadTail]);
  //input the lookahead tail sampspan, and the two lookahead tail peak envelopes
  //find the autorel left envelope at o_AutoRelMod.RelEnv_L
  //find autorel right envelope at o_AutoRelMod.RelEnv_R
  //Find meter autorel milliseconds at o_AutoRelMod.AutoRelMsSpan
  
  s_Gain_L = g_MakeUpGain / o_AutoRelMod.RelEnv_L;
  s_Gain_R = g_MakeUpGain / o_AutoRelMod.RelEnv_R;
  
  g_MaxSongGain_L = max(g_MaxSongGain_L, s_Gain_L);
  g_MinSongGain_L = min(g_MinSongGain_L, s_Gain_L);
  g_MaxSongGain_R = max(g_MaxSongGain_R, s_Gain_R);
  g_MinSongGain_R = min(g_MinSongGain_R, s_Gain_R);
  o_GainMeterEnv_L.GainMeterEnvelope_DoSamp(s_Gain_L);
  o_GainMeterEnv_R.GainMeterEnvelope_DoSamp(s_Gain_R);

  s_Samp_L = g_SmpBuf_L[g_SmpHeadTail]; //recover the delayed samples from lookahead buffers
  s_Samp_R = g_SmpBuf_R[g_SmpHeadTail];
  //send the delayed input samples to the InputMeter object
  o_InputMeter.AudioMeter_DoSamp(s_Samp_L, s_Samp_R);

  s_Samp_L *= s_Gain_L; //apply gain to the samples
  s_Samp_R *= s_Gain_R;

  spl0 = s_Samp_L;
  spl1 = s_Samp_R;
  
  //send to the OutputMeter object
  o_OutputMeter.AudioMeter_DoSamp(s_Samp_L, s_Samp_R);

@gfx 664, 290
  lg_DoTheGFX = 1; //if images don't load, don't do the GFX
  ((g_BGPicLoaded < 1) && (lg_DoTheGFX >= 1)) ? //load background meter picture if needed
  (
    (g_BGPicLoaded > -1) ?
    (
      lg_TmpLoadResult = gfx_loadimg(0, 0);
      (lg_TmpLoadResult >= 0) ?
        g_BGPicLoaded = 1
      :
      (
        g_BGPicLoaded = -1;
        lg_DoTheGFX = 0;
      );
    )
    :
      lg_DoTheGFX = 0;  
  );

  ((g_FGPicLoaded < 1) && (lg_DoTheGFX >= 1)) ? //load meter indicator forground picture if needed
  (
    (g_FGPicLoaded > -1) ?
    (
      lg_TmpLoadResult = gfx_loadimg(1, 1);
      (lg_TmpLoadResult >= 0) ?
        g_FGPicLoaded = 1
      :
      (
        g_FGPicLoaded = -1;
        lg_DoTheGFX = 0;
      );
    )
    :
      lg_DoTheGFX = 0;  
  );

  ((g_PKPicLoaded < 1) && (lg_DoTheGFX >= 1)) ? //load peak indicator picture if needed
  (
    (g_PKPicLoaded > -1) ?
    (
      lg_TmpLoadResult = gfx_loadimg(2, 2);
      (lg_TmpLoadResult >= 0) ?
        g_PKPicLoaded = 1
      :
      (
        g_PKPicLoaded = -1;
        lg_DoTheGFX = 0;
      );
    )
    :
      lg_DoTheGFX = 0;  
  );
  
  ((g_SLOPicLoaded < 1) && (lg_DoTheGFX >= 1)) ? //load SLOW RMS indicator picture if needed
  (
    (g_SLOPicLoaded > -1) ?
    (
      lg_TmpLoadResult = gfx_loadimg(3, 3);
      (lg_TmpLoadResult >= 0) ?
        g_SLOPicLoaded = 1
      :
      (
        g_SLOPicLoaded = -1;
        lg_DoTheGFX = 0;
      );
    )
    :
      lg_DoTheGFX = 0;  
  );
  
  (lg_DoTheGFX > 0) ? //only do graphics if pics are loaded
  (
    //gfx_blit(source image, scale, rotation, srcx, srcy, srcw, srch, destx, desty, destw, desth, rotxoffs, rotyoffs)
    gfx_dest = 0; //set drawing dest to the background meter picture
    gfx_blit(1, 1.0, 0.0, 0, 0, 535, 26, 93, 50, 535, 26, 0, 0); //offscreen blit INPUT meter background, as if all LED's are turned off
    gfx_blit(1, 1.0, 0.0, 0, 0, 535, 26, 93, 112, 535, 26, 0, 0); //offscreen blit OUTPUT meter background
    gfx_blit(1, 1.0, 0.0, 0, 26, 535, 26, 93, 174, 535, 26, 0, 0); //offscreen blit GAIN meter background    
    gfx_blit(1, 1.0, 0.0, 0, 104, 535, 26, 93, 236, 535, 26, 0, 0); //offscreen blit Auto-Release meter background    
    
    //============ Draw Input Meter LEFT ===========
    lg_DB = Power_To_DB(o_InputMeter.Env_Fast_L);
    (lg_DB >= -61) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = floor(lg_DB * 525 / 67);
      gfx_blit(1, 1.0, 0.0, 0, 156, lg_HPix, 13, 93, 50, lg_HPix, 13, 0, 0); //offscreen blit INPUT meter VU rms level
    );

    lg_DB = Power_To_DB(o_InputMeter.Env_VU_L);
    (lg_DB >= -61) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = floor(lg_DB * 525 / 67);
      gfx_blit(1, 1.0, 0.0, 0, 52, lg_HPix, 13, 93, 50, lg_HPix, 13, 0, 0); //offscreen blit INPUT meter VU rms level
      gfx_r = 0.0;
      gfx_g = 0.5;
      gfx_b = 0.0;
      gfx_line(92 + lg_HPix, 50, 93 + lg_HPix, 62);
      gfx_line(93 + lg_HPix, 50, 93 + lg_HPix, 62);
      gfx_line(94 + lg_HPix, 50, 94 + lg_HPix, 62);
    );  

    lg_DB = Power_To_DB(o_InputMeter.Env_Slow_L);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67) - 6; //subtract width of peak image
      gfx_blit(3, 1.0, 0.0, 0, 0, 6, 13, lg_HPix, 50, 6, 13, 0, 0); //offscreen blit INPUT meter Peak indicator
    );
    
    lg_DB = Amplitude_To_DB(o_InputMeter.Env_Peak_L);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67) - 6; //subtract width of peak image
      gfx_blit(2, 1.0, 0.0, 0, 0, 6, 13, lg_HPix, 50, 6, 13, 0, 0); //offscreen blit INPUT meter Peak indicator
    );
    
    lg_DB = Power_To_DB(o_InputMeter.Max_VU_L);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67);
      lg_HPix = min(lg_HPix, 625);
      gfx_r = 0.5;
      gfx_g = 1.0;
      gfx_b = 0.5;
      gfx_line(lg_HPix, 50, lg_HPix, 62);
      gfx_r = 0.0;
      gfx_g = 0.5;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 1, 50, lg_HPix - 1, 62);
      gfx_line(lg_HPix + 1, 50, lg_HPix + 1, 62);
      gfx_r = 0.0;
      gfx_g = 0.25;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 2, 50, lg_HPix - 2, 62);
      gfx_line(lg_HPix + 2, 50, lg_HPix + 2, 62);
    );

    lg_DB = Amplitude_To_DB(o_InputMeter.Max_Peak_L);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67);
      lg_HPix = min(lg_HPix, 625);
      gfx_r = 1.0;
      gfx_g = 0.75;
      gfx_b = 0.0;
      gfx_line(lg_HPix, 50, lg_HPix, 62);
      gfx_r = 0.7;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 1, 50, lg_HPix - 1, 62);
      gfx_line(lg_HPix + 1, 50, lg_HPix + 1, 62);
      gfx_r = 0.25;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 2, 50, lg_HPix - 2, 62);
      gfx_line(lg_HPix + 2, 50, lg_HPix + 2, 62);
    );

    //============ Draw Input Meter RIGHT ===========
    lg_DB = Power_To_DB(o_InputMeter.Env_Fast_R);
    (lg_DB >= -61) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = floor(lg_DB * 525 / 67);
      gfx_blit(1, 1.0, 0.0, 0, 169, lg_HPix, 13, 93, 63, lg_HPix, 13, 0, 0); //offscreen blit INPUT meter VU rms level
    );

    lg_DB = Power_To_DB(o_InputMeter.Env_VU_R);
    (lg_DB >= -61) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = floor(lg_DB * 525 / 67);
      gfx_blit(1, 1.0, 0.0, 0, 65, lg_HPix, 13, 93, 63, lg_HPix, 13, 0, 0); //offscreen blit INPUT meter VU rms level
      gfx_r = 0.0;
      gfx_g = 0.5;
      gfx_b = 0.0;
      gfx_line(92 + lg_HPix, 63, 93 + lg_HPix, 75);
      gfx_line(93 + lg_HPix, 63, 93 + lg_HPix, 75);
      gfx_line(94 + lg_HPix, 63, 94 + lg_HPix, 75);
    );  

    lg_DB = Power_To_DB(o_InputMeter.Env_Slow_R);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67) - 6; //subtract width of peak image
      gfx_blit(3, 1.0, 0.0, 0, 13, 6, 13, lg_HPix, 63, 6, 13, 0, 0); //offscreen blit INPUT meter Peak indicator
    );
    
    lg_DB = Amplitude_To_DB(o_InputMeter.Env_Peak_R);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67) - 6; //subtract width of peak image
      gfx_blit(2, 1.0, 0.0, 0, 13, 6, 13, lg_HPix, 63, 6, 13, 0, 0); //offscreen blit INPUT meter Peak indicator
    );
    
    lg_DB = Power_To_DB(o_InputMeter.Max_VU_R);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67);
      lg_HPix = min(lg_HPix, 625);
      gfx_r = 0.5;
      gfx_g = 1.0;
      gfx_b = 0.5;
      gfx_line(lg_HPix, 63, lg_HPix, 75);
      gfx_r = 0.0;
      gfx_g = 0.5;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 1, 63, lg_HPix - 1, 75);
      gfx_line(lg_HPix + 1, 63, lg_HPix + 1, 75);
      gfx_r = 0.0;
      gfx_g = 0.25;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 2, 63, lg_HPix - 2, 75);
      gfx_line(lg_HPix + 2, 63, lg_HPix + 2, 75);
    );

    lg_DB = Amplitude_To_DB(o_InputMeter.Max_Peak_R);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67);
      lg_HPix = min(lg_HPix, 625);
      gfx_r = 1.0;
      gfx_g = 0.75;
      gfx_b = 0.0;
      gfx_line(lg_HPix, 63, lg_HPix, 75);
      gfx_r = 0.7;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 1, 63, lg_HPix - 1, 75);
      gfx_line(lg_HPix + 1, 63, lg_HPix + 1, 75);
      gfx_r = 0.25;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 2, 63, lg_HPix - 2, 75);
      gfx_line(lg_HPix + 2, 63, lg_HPix + 2, 75);
    );
    
    //============ Draw Output Meter LEFT ===========
    lg_DB = Power_To_DB(o_OutputMeter.Env_Fast_L);
    (lg_DB >= -61) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = floor(lg_DB * 525 / 67);
      gfx_blit(1, 1.0, 0.0, 0, 156, lg_HPix, 13, 93, 112, lg_HPix, 13, 0, 0); //offscreen blit INPUT meter VU rms level
    );

    lg_DB = Power_To_DB(o_OutputMeter.Env_VU_L);
    (lg_DB >= -61) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = floor(lg_DB * 525 / 67);
      gfx_blit(1, 1.0, 0.0, 0, 52, lg_HPix, 13, 93, 112, lg_HPix, 13, 0, 0); //offscreen blit INPUT meter VU rms level
      gfx_r = 0.0;
      gfx_g = 0.5;
      gfx_b = 0.0;
      gfx_line(92 + lg_HPix, 112, 93 + lg_HPix, 124);
      gfx_line(93 + lg_HPix, 112, 93 + lg_HPix, 124);
      gfx_line(94 + lg_HPix, 112, 94 + lg_HPix, 124);
    );  

    lg_DB = Power_To_DB(o_OutputMeter.Env_Slow_L);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67) - 6; //subtract width of peak image
      gfx_blit(3, 1.0, 0.0, 0, 0, 6, 13, lg_HPix, 112, 6, 13, 0, 0); //offscreen blit INPUT meter Peak indicator
    );
    
    lg_DB = Amplitude_To_DB(o_OutputMeter.Env_Peak_L);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67) - 6; //subtract width of peak image
      gfx_blit(2, 1.0, 0.0, 0, 0, 6, 13, lg_HPix, 112, 6, 13, 0, 0); //offscreen blit INPUT meter Peak indicator
    );
    
    lg_DB = Power_To_DB(o_OutputMeter.Max_VU_L);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67);
      lg_HPix = min(lg_HPix, 625);
      gfx_r = 0.5;
      gfx_g = 1.0;
      gfx_b = 0.5;
      gfx_line(lg_HPix, 112, lg_HPix, 124);
      gfx_r = 0.0;
      gfx_g = 0.5;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 1, 112, lg_HPix - 1, 124);
      gfx_line(lg_HPix + 1, 112, lg_HPix + 1, 124);
      gfx_r = 0.0;
      gfx_g = 0.25;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 2, 112, lg_HPix - 2, 124);
      gfx_line(lg_HPix + 2, 112, lg_HPix + 2, 124);
    );

    lg_DB = Amplitude_To_DB(o_OutputMeter.Max_Peak_L);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67);
      lg_HPix = min(lg_HPix, 625);
      gfx_r = 1.0;
      gfx_g = 0.75;
      gfx_b = 0.0;
      gfx_line(lg_HPix, 112, lg_HPix, 124);
      gfx_r = 0.7;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 1, 112, lg_HPix - 1, 124);
      gfx_line(lg_HPix + 1, 112, lg_HPix + 1, 124);
      gfx_r = 0.25;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 2, 112, lg_HPix - 2, 124);
      gfx_line(lg_HPix + 2, 112, lg_HPix + 2, 124);
    );
    
    //============ Draw Output Meter RIGHT ===========
    lg_DB = Power_To_DB(o_OutputMeter.Env_Fast_R);
    (lg_DB >= -61) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = floor(lg_DB * 525 / 67);
      gfx_blit(1, 1.0, 0.0, 0, 169, lg_HPix, 13, 93, 125, lg_HPix, 13, 0, 0); //offscreen blit INPUT meter VU rms level
    );

    lg_DB = Power_To_DB(o_OutputMeter.Env_VU_R);
    (lg_DB >= -61) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = floor(lg_DB * 525 / 67);
      gfx_blit(1, 1.0, 0.0, 0, 65, lg_HPix, 13, 93, 125, lg_HPix, 13, 0, 0); //offscreen blit INPUT meter VU rms level
      gfx_r = 0.0;
      gfx_g = 0.5;
      gfx_b = 0.0;
      gfx_line(92 + lg_HPix, 125, 93 + lg_HPix, 137);
      gfx_line(93 + lg_HPix, 125, 93 + lg_HPix, 137);
      gfx_line(94 + lg_HPix, 125, 94 + lg_HPix, 137);
    );  

    lg_DB = Power_To_DB(o_OutputMeter.Env_Slow_R);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67) - 6; //subtract width of peak image
      gfx_blit(3, 1.0, 0.0, 0, 13, 6, 13, lg_HPix, 125, 6, 13, 0, 0); //offscreen blit INPUT meter Peak indicator
    );
    
    lg_DB = Amplitude_To_DB(o_OutputMeter.Env_Peak_R);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67) - 6; //subtract width of peak image
      gfx_blit(2, 1.0, 0.0, 0, 13, 6, 13, lg_HPix, 125, 6, 13, 0, 0); //offscreen blit INPUT meter Peak indicator
    );
    
    lg_DB = Power_To_DB(o_OutputMeter.Max_VU_R);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67);
      lg_HPix = min(lg_HPix, 625);
      gfx_r = 0.5;
      gfx_g = 1.0;
      gfx_b = 0.5;
      gfx_line(lg_HPix, 125, lg_HPix, 137);
      gfx_r = 0.0;
      gfx_g = 0.5;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 1, 125, lg_HPix - 1, 137);
      gfx_line(lg_HPix + 1, 125, lg_HPix + 1, 137);
      gfx_r = 0.0;
      gfx_g = 0.25;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 2, 125, lg_HPix - 2, 137);
      gfx_line(lg_HPix + 2, 125, lg_HPix + 2, 137);
    );

    lg_DB = Amplitude_To_DB(o_OutputMeter.Max_Peak_R);
    (lg_DB >= -60) ?
    (
      lg_DB = min(lg_DB, 6);
      lg_DB += 62;
      lg_HPix = 93 + floor(lg_DB * 525 / 67);
      lg_HPix = min(lg_HPix, 625);
      gfx_r = 1.0;
      gfx_g = 0.75;
      gfx_b = 0.0;
      gfx_line(lg_HPix, 125, lg_HPix, 137);
      gfx_r = 0.7;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 1, 125, lg_HPix - 1, 137);
      gfx_line(lg_HPix + 1, 125, lg_HPix + 1, 137);
      gfx_r = 0.25;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_HPix - 2, 125, lg_HPix - 2, 137);
      gfx_line(lg_HPix + 2, 125, lg_HPix + 2, 137);
    );
    
    (g_MakeUpGainDB > 0.01) ? //sat lg_FG_HPixel_End and lg_BK_HPixel_End for both left and right
    (
      (g_MakeUpGainDB <= 1.0) ? //linear plot
        lg_BK_HPixel_End = floor(g_MakeUpGainDB * 24.0)
      :
        lg_BK_HPixel_End = floor(24 * (g_MakeUpGainDB ^ 0.6544));
      
      lg_FG_HPixel_End = lg_BK_HPixel_End + 343.0;
      lg_BK_HPixel_End += 436.0;    
    )
    :
    (
      (g_MakeUpGainDB < 0.01) ?
      (
        lg_TmpMakeUpGainDB = -1 * g_MakeUpGainDB;
        (lg_TmpMakeUpGainDB <= 1.0) ? //linear plot
          lg_BK_HPixel_End = floor(lg_TmpMakeUpGainDB * 24.0)
        :
          lg_BK_HPixel_End = floor(24 * (lg_TmpMakeUpGainDB ^ 0.687793742));
          
        lg_FG_HPixel_End = 343 - lg_BK_HPixel_End;
        lg_BK_HPixel_End = 436 - lg_BK_HPixel_End;
      )
      :
      ( //otherwise set exactly at zero gain pixel
        lg_FG_HPixel_End = 343;
        lg_BK_HPixel_End = 436;      
      );
    );

    //=============== Draw GainMeter LEFT ==============
    lg_DB = Amplitude_To_DB(o_GainMeterEnv_L.EnvVal);
    (lg_DB > 0.01) ?
    (
      lg_DB = min(lg_DB, 24);  
      lg_BK_HPixel_Start = 436.0;
      lg_FG_HPixel_Start = 343.0;
      (lg_DB <= 1.0) ? //linear plot
        lg_BK_HPixel_Start = floor(lg_DB * 24.0)
      :
        lg_BK_HPixel_Start = floor(24 * (lg_DB ^ 0.6544));
      
      lg_FG_HPixel_Start = lg_BK_HPixel_Start + 343;
      lg_BK_HPixel_Start += 436;

      (lg_BK_HPixel_End > lg_BK_HPixel_Start) ?      
        gfx_blit(1, 1.0, 0.0, lg_FG_HPixel_Start, 78, lg_FG_HPixel_End - lg_FG_HPixel_Start, 13, lg_BK_HPixel_Start,
                174, lg_BK_HPixel_End - lg_BK_HPixel_Start, 13, 0, 0); //offscreen blit GAIN meter FG  
    )
    :
    (
      (lg_DB < -0.01) ?
      (
        lg_DB = max(lg_DB, -48);
        lg_DB = -1 * lg_DB; //negate
        (lg_DB <= 1.0) ? //linear plot
          lg_BK_HPixel_Start = floor(lg_DB * 24.0)
        :
          lg_BK_HPixel_Start = floor(24 * (lg_DB ^ 0.687793742));
          
        lg_FG_HPixel_Start = 343 - lg_BK_HPixel_Start;
        lg_BK_HPixel_Start = 436 - lg_BK_HPixel_Start;

        (lg_BK_HPixel_End > lg_BK_HPixel_Start) ?      
          gfx_blit(1, 1.0, 0.0, lg_FG_HPixel_Start, 78, lg_FG_HPixel_End - lg_FG_HPixel_Start, 13, lg_BK_HPixel_Start,
                  174, lg_BK_HPixel_End - lg_BK_HPixel_Start, 13, 0, 0); //offscreen blit GAIN meter FG  
      );
    );
    
    lg_DB = Amplitude_To_DB(g_MaxSongGain_L);
    ((lg_DB >= -46) && (lg_DB <= 24)) ?
    (
      (lg_DB >= 0) ?
      (
        (lg_DB <= 1.0) ? //linear plot
          lg_Line_HPixel = floor((lg_DB * 24) + 436)
        :
          lg_Line_HPixel = floor((24 * (lg_DB ^ 0.6544)) + 436);      
      )
      :
      (
        lg_DB = 0 - lg_DB; //negate
        (lg_DB <= 1.0) ? //linear plot
          lg_Line_HPixel = floor(436 - (lg_DB * 24))
        :
          lg_Line_HPixel = floor(436 - (24 * (lg_DB ^ 0.687793742)));
      );
      lg_Line_HPixel = min(lg_Line_HPixel, 625) + 2;
      (o_GainMeterEnv_L.EnvVal < (g_MakeUpGain * 0.999)) ?
      (
        gfx_r = 1.0;
        gfx_g = 0.9;
        gfx_b = 0;
        gfx_line(lg_Line_HPixel, 174, lg_Line_HPixel, 186);
        gfx_r = 1.0;
        gfx_g = 0.7;
        gfx_b = 0;
        gfx_line(lg_Line_HPixel - 1, 174, lg_Line_HPixel - 1, 186);
        gfx_line(lg_Line_HPixel + 1, 174, lg_Line_HPixel + 1, 186);
        gfx_r = 1.0;
        gfx_g = 0.5;
        gfx_b = 0;
        gfx_line(lg_Line_HPixel - 2, 174, lg_Line_HPixel - 2, 186);
        gfx_line(lg_Line_HPixel + 2, 174, lg_Line_HPixel + 2, 186);
      )
      :
      (
        gfx_r = 1.0;
        gfx_g = 0.75;
        gfx_b = 0.0;
        gfx_line(lg_Line_HPixel, 174, lg_Line_HPixel, 186);
        gfx_r = 0.7;
        gfx_g = 0.0;
        gfx_b = 0.0;
        gfx_line(lg_Line_HPixel - 1, 174, lg_Line_HPixel - 1, 186);
        gfx_line(lg_Line_HPixel + 1, 174, lg_Line_HPixel + 1, 186);
        gfx_r = 0.25;
        gfx_g = 0.0;
        gfx_b = 0.0;
        gfx_line(lg_Line_HPixel - 2, 174, lg_Line_HPixel - 2, 186);
        gfx_line(lg_Line_HPixel + 2, 174, lg_Line_HPixel + 2, 186);
      );
    );
    
    lg_DB = Amplitude_To_DB(g_MinSongGain_L);
    ((lg_DB >= -46) && (lg_DB <= 24)) ?
    (
      (lg_DB >= 0) ?
      (
        (lg_DB <= 1.0) ? //linear plot
          lg_Line_HPixel = floor((lg_DB * 24) + 436)
        :
          lg_Line_HPixel = floor((24 * (lg_DB ^ 0.6544)) + 436);      
      )
      :
      (
        lg_DB = 0 - lg_DB; //negate
        (lg_DB <= 1.0) ? //linear plot
          lg_Line_HPixel = floor(436 - (lg_DB * 24))
        :
          lg_Line_HPixel = floor(436 - (24 * (lg_DB ^ 0.687793742)));
      );
      lg_Line_HPixel = min(lg_Line_HPixel, 625);
      gfx_r = 1.0;
      gfx_g = 0.75;
      gfx_b = 0.0;
      gfx_line(lg_Line_HPixel, 174, lg_Line_HPixel, 186);
      gfx_r = 0.7;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_Line_HPixel - 1, 174, lg_Line_HPixel - 1, 186);
      gfx_line(lg_Line_HPixel + 1, 174, lg_Line_HPixel + 1, 186);
      gfx_r = 0.25;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_Line_HPixel - 2, 174, lg_Line_HPixel - 2, 186);
      gfx_line(lg_Line_HPixel + 2, 174, lg_Line_HPixel + 2, 186);
    );

    //=============== Draw GainMeter RIGHT ==============
    lg_DB = Amplitude_To_DB(o_GainMeterEnv_R.EnvVal);
    (lg_DB > 0.01) ?
    (
      lg_DB = min(lg_DB, 24);  
      lg_BK_HPixel_Start = 436.0;
      lg_FG_HPixel_Start = 343.0;
      (lg_DB <= 1.0) ? //linear plot
        lg_BK_HPixel_Start = floor(lg_DB * 24.0)
      :
        lg_BK_HPixel_Start = floor(24 * (lg_DB ^ 0.6544));
      
      lg_FG_HPixel_Start = lg_BK_HPixel_Start + 343;
      lg_BK_HPixel_Start += 436;

      (lg_BK_HPixel_End > lg_BK_HPixel_Start) ?      
        gfx_blit(1, 1.0, 0.0, lg_FG_HPixel_Start, 91, lg_FG_HPixel_End - lg_FG_HPixel_Start, 13, lg_BK_HPixel_Start,
                187, lg_BK_HPixel_End - lg_BK_HPixel_Start, 13, 0, 0); //offscreen blit GAIN meter FG  
    )
    :
    (
      (lg_DB < -0.01) ?
      (
        lg_DB = max(lg_DB, -48);
        lg_DB = -1 * lg_DB; //negate
        (lg_DB <= 1.0) ? //linear plot
          lg_BK_HPixel_Start = floor(lg_DB * 24.0)
        :
          lg_BK_HPixel_Start = floor(24 * (lg_DB ^ 0.687793742));
          
        lg_FG_HPixel_Start = 343 - lg_BK_HPixel_Start;
        lg_BK_HPixel_Start = 436 - lg_BK_HPixel_Start;

        (lg_BK_HPixel_End > lg_BK_HPixel_Start) ?      
          gfx_blit(1, 1.0, 0.0, lg_FG_HPixel_Start, 91, lg_FG_HPixel_End - lg_FG_HPixel_Start, 13, lg_BK_HPixel_Start,
                  187, lg_BK_HPixel_End - lg_BK_HPixel_Start, 13, 0, 0); //offscreen blit GAIN meter FG  
      );
    );
    
    lg_DB = Amplitude_To_DB(g_MaxSongGain_R);
    ((lg_DB >= -46) && (lg_DB <= 24)) ?
    (
      (lg_DB >= 0) ?
      (
        (lg_DB <= 1.0) ? //linear plot
          lg_Line_HPixel = floor((lg_DB * 24) + 436)
        :
          lg_Line_HPixel = floor((24 * (lg_DB ^ 0.6544)) + 436);      
      )
      :
      (
        lg_DB = 0 - lg_DB; //negate
        (lg_DB <= 1.0) ? //linear plot
          lg_Line_HPixel = floor(436 - (lg_DB * 24))
        :
          lg_Line_HPixel = floor(436 - (24 * (lg_DB ^ 0.687793742)));
      );
      lg_Line_HPixel = min(lg_Line_HPixel, 625) + 2;
      (o_GainMeterEnv_R.EnvVal < (g_MakeUpGain * 0.999)) ?
      (
        gfx_r = 1.0;
        gfx_g = 0.9;
        gfx_b = 0.0;
        gfx_line(lg_Line_HPixel, 187, lg_Line_HPixel, 199);
        gfx_r = 1.0;
        gfx_g = 0.7;
        gfx_b = 0.0;
        gfx_line(lg_Line_HPixel - 1, 187, lg_Line_HPixel - 1, 199);
        gfx_line(lg_Line_HPixel + 1, 187, lg_Line_HPixel + 1, 199);
        gfx_r = 1.0;
        gfx_g = 0.5;
        gfx_b = 0.0;
        gfx_line(lg_Line_HPixel - 2, 187, lg_Line_HPixel - 2, 199);
        gfx_line(lg_Line_HPixel + 2, 187, lg_Line_HPixel + 2, 199);
      )
      :
      (
        gfx_r = 1.0;
        gfx_g = 0.75;
        gfx_b = 0.0;
        gfx_line(lg_Line_HPixel, 187, lg_Line_HPixel, 199);
        gfx_r = 0.7;
        gfx_g = 0.0;
        gfx_b = 0.0;
        gfx_line(lg_Line_HPixel - 1, 187, lg_Line_HPixel - 1, 199);
        gfx_line(lg_Line_HPixel + 1, 187, lg_Line_HPixel + 1, 199);
        gfx_r = 0.25;
        gfx_g = 0.0;
        gfx_b = 0.0;
        gfx_line(lg_Line_HPixel - 2, 187, lg_Line_HPixel - 2, 199);
        gfx_line(lg_Line_HPixel + 2, 187, lg_Line_HPixel + 2, 199);
      );        
    );
    
    lg_DB = Amplitude_To_DB(g_MinSongGain_R);
    ((lg_DB >= -46) && (lg_DB <= 24)) ?
    (
      (lg_DB >= 0) ?
      (
        (lg_DB <= 1.0) ? //linear plot
          lg_Line_HPixel = floor((lg_DB * 24) + 436)
        :
          lg_Line_HPixel = floor((24 * (lg_DB ^ 0.6544)) + 436);      
      )
      :
      (
        lg_DB = 0 - lg_DB; //negate
        (lg_DB <= 1.0) ? //linear plot
          lg_Line_HPixel = floor(436 - (lg_DB * 24))
        :
          lg_Line_HPixel = floor(436 - (24 * (lg_DB ^ 0.687793742)));
      );
      lg_Line_HPixel = min(lg_Line_HPixel, 625);
      gfx_r = 1.0;
      gfx_g = 0.75;
      gfx_b = 0.0;
      gfx_line(lg_Line_HPixel, 187, lg_Line_HPixel, 199);
      gfx_r = 0.7;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_Line_HPixel - 1, 187, lg_Line_HPixel - 1, 199);
      gfx_line(lg_Line_HPixel + 1, 187, lg_Line_HPixel + 1, 199);
      gfx_r = 0.25;
      gfx_g = 0.0;
      gfx_b = 0.0;
      gfx_line(lg_Line_HPixel - 2, 187, lg_Line_HPixel - 2, 199);
      gfx_line(lg_Line_HPixel + 2, 187, lg_Line_HPixel + 2, 199);
    );
    
    (o_AutoRelMod.AutoRelMsSpan > 1.01) ?
    (
      lg_FG_HPixel_Start = 0;
      lg_BK_HPixel_Start = 93; 
      lg_FG_HPixel_End = floor(178 * log10(o_AutoRelMod.AutoRelMsSpan));
      lg_BK_HPixel_End = lg_BK_HPixel_Start + lg_FG_HPixel_End; 
      gfx_blit(1, 1.0, 0.0, lg_FG_HPixel_Start, 130, lg_FG_HPixel_End, 26, lg_BK_HPixel_Start, 236, lg_FG_HPixel_End, 26, 0, 0); //offscreen blit Auto-Release meter background 
    );
        
    gfx_dest = -1; //set drawing target to the plugin window
    gfx_blit(0, 1.0, 0.0, 0, 0, 664, 290, 0, 0, gfx_w, gfx_h, 0, 0); //blit the updated offscreen meter image   
  );

