Skip to content

Instantly share code, notes, and snippets.

@X-Raym
Created March 15, 2023 15:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save X-Raym/4558be0b4225f66f537b12fde0ce2f80 to your computer and use it in GitHub Desktop.
Save X-Raym/4558be0b4225f66f537b12fde0ce2f80 to your computer and use it in GitHub Desktop.
desc:jcjr_ChaosPhaser
// James Chandler Jr
// http://www.errnum.com
// errnum@gmail.com
// License: GPL - http://www.gnu.org/licenses/gpl.html
//Dual Phaser Input Mode (Mono, Stereo, Mid/Side)
//default val = 1, min val = 0, max val = 1, change increment = 1
slider1:1<0,2,1{Mono,Stereo,Mid/Side}>Dual Phaser Input Mode
//LFO Shape [SIN <-> TRI]
//default val = 0.5, min val = 0, max val = 1.0, change increment = 0.01
slider2:0.5<0,1,0.01>LFO Shape [SIN <-> TRI]
//LFO Rate Multiplier == 10 ^ (MenuIdx - 1)
//default val = 1, min val = 0, max val = 2, change increment = 1
slider3:1<0,2,1{0.1 to 1 Hz [LFO Rate Ctrl div 10],1 to 10 Hz [LFO Rate Ctrl mul 1],10 to 100 Hz [LFO Rate Ctrl mul 10]}>LFO Rate Range
//LFO Rate Center [Hz]
//default val = 1, min val = 1, max val = 20, change increment = 0.01
slider4:1<1,10,0.01>LFO Rate Center [Hz]
//LFO Rate Left/Right Offset [-1.0 <-> 1.0]
//If 1.0, Right LFO is 2X Left LFO, if -1.0, Right LFO is 0.5X Left LFO
//default val = 0.05, min val = -1, max val = 1, change increment = 0.01
slider5:0.1<-1.0,1.0,0.01>LFO Rate Right-vs-Left Offset [Octaves]
//LFO Random Rate
//default val = 0, min val = 0, max val =2, change increment = 0.01
slider6:0.5<0,2,0.01>LFO Random Rate [Octaves]
//LFO Chaos (-2 <-> +2)
//default val = 0.5, min val = -2, max val =2, change increment = 0.01
slider7:0.5<-2,2,0.01>LFO Chaos [Octaves]
//LFO Rate Env Follower Mod Gain
//default val = 0, min val = -10, max val =10, change increment = 0.1
slider8:0.0<-10,10,0.1>LFO Rate Env Follower Mod Gain
//Phaser Num Stages 1 thru 16
//default val = 8, min val = 1, max val = 16, change increment = 1
slider9:8<1,16,1>Phaser Num Stages
//slider9:1<0,3,1{4 Stages = 720 Deg = 2 Notches,8 Stages = 1440 Degrees = 4 Notches,12 Stages = 2160 Degrees = 6 Notches,16 Stages = 2880 Degrees = 8 Notches}>Phaser Num Stages
//Phaser Center Freq
//default val = 500, min val = 40, max val = 5000, change increment = 1
slider10:500<40,5000,1>Phaser Center Frequency [Hz]
//Phaser LFO Mod Amount [+/- Semitones]
//default val = 12, min val = 0, max val = 36, change increment = 0.1
slider11:18<0,36,0.1>Phaser LFO Mod Amt [+/- Semitones]
//Phaser Env FOllower Mod
//default val = 0, min val = -10, max val = 10, change increment = 0.1
slider12:0<-10,10,0.1>Phaser Env Follower Mod Gain
//Phaser Resonance (-99% <> +99%)
//default val = 0, min val = -99, max val = 99, change increment = 1
slider13:0<-99,99,1>Phaser Resonance [Percent]
//Wet Stereo Width (-100% <> +100%)
//default val = 100, min val = -100, max val = 100, change increment = 0.1
slider14:100<-100,100,0.1>Wet Stereo Width [Percent]
//Wet Mix Level (-100% <> +100%)
//default val = 100, min val = -100, max val = 100, change increment = 0.1
slider15:100<-100,100,0.1>Wet Mix Level [Percent]
//Dry Stereo Width (0 <> +100%)
//default val = 100, min val = 0, max val = 100, change increment = 0.1
slider16:100<0,100,0.1>Dry Stereo Width [Percent]
//Dry Mix Level (0 <> +100%)
//default val = 100, min val = 0, max val = 100, change increment = 0.1
slider17:100<0,100,0.1>Dry Mix Level [Percent]
//Output Gain -24 -> +6 dB
//default val = -6, min val = -24, max val = 6, change increment = 0.1
slider18:-6<-24,6,0.1>Output Gain [dB]
@init
//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;
);
//rename Log_2 and Exp_2 functions for "Digital Control Voltage"
//analogous to 1 volt per octave analog
//DCV value of 0 = 1 Hz. DCV value of -1 = 0.5 Hz. DCV value of 1 = 2 Hz, etc.
function FreqHzToDCV(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 DCVToFreqHz(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;
);
//initialize sin lookup table
SIN_TBL_SIZE = 4096;
INV_SIN_TBL_SIZE = 1.0 / SIN_TBL_SIZE;
SIN_TBL = js_malloc(SIN_TBL_SIZE + 1, JS_MALLOC_CLEARMEM);
TWO_PI = 2 * $pi;
_phaseinc = TWO_PI / 4096;
_phase = _phaseinc;
SIN_TBL[0] = 0;
SIN_TBL[4096] = 0;
//SIN_TBL[0] = 0 and also SIN_TBL[4096] = 0 to ease phase wrapping interpolation
_i = 1;
while (_i < 4096) //fill out elements [1] thru [4095]
(
SIN_TBL[_i] = sin(_phase);
_i += 1;
_phase += _phaseinc;
);
TRI_TBL = js_malloc(SIN_TBL_SIZE + 1, JS_MALLOC_CLEARMEM);
_triinc = 1.0 / 1024;
_triamp = _triinc;
TRI_TBL[0] = 0;
TRI_TBL[4096] = 0;
//TRI_TBL[0] = 0 and also TRI_TBL[4096] = 0 to ease phase wrapping interpolation
_i = 1;
while (_i < 4096) //fill out elements [1] thru [4095]
(
TRI_TBL[_i] = _triamp;
_i += 1;
(_i <= 1024) ?
_triamp += _triinc
:
(
(_i <= 3072) ?
_triamp -= _triinc
:
_triamp += _triinc;
);
);
//a_DCV = FreqHzToDCV(SinTriOscFrequency)
//a_TriMix -- 0 to 1.0. Will mix sin() * (1 - TriMix) + tri() * TriMix
//a_SampRate = srate
function SinTriOsc_Init(a_DCV, a_TriMix, a_RandMul, a_SampRate)
(
this.SR = a_SampRate;
this.Nyquist = floor(this.SR * 0.49);
this.SinTblIncRatio = SIN_TBL_SIZE / this.SR;
this.FreqHz = min(DCVToFreqHz(a_DCV), this.Nyquist);
this.SinTblIncPerSample = this.FreqHz * this.SinTblIncRatio;
this.SinTblPhaseIdx = 0;
this.TriAmpMul = max(min(a_TriMix, 1.0), 0.0);
this.SinAmpMul = 1.0 - this.TriAmpMul;
this.RandMul = max(min(a_RandMul, 1.0), 0.0);
this.RandDCV = 0;
this.LastRandDCV = 0;
this.Initted = 1.0;
);
function SinTriOsc_SetSinTriMix(a_TriMix)
(
(this.Initted == 1.0) ?
(
this.TriAmpMul = max(min(a_TriMix, 1.0), 0.0);
this.SinAmpMul = 1.0 - this.TriAmpMul;
);
);
function SinTriOsc_SetRandMul(a_RandMul)
(
this.RandMul = max(min(a_RandMul, 2.0), 0.0);
);
//if want to change Osc Frequency, pass a_DCV greater than -10
function SinTriOsc_DoSamp(a_DCV)
local (l_result, l_int, l_frac, l_RandModInterp, l_RandModDCV)
(
l_RandModInterp = this.SinTblPhaseIdx * INV_SIN_TBL_SIZE;
l_RandModDCV = this.RandMul * (this.LastRandDCV * (1.0 - l_RandModInterp) + this.RandDCV * l_RandModInterp);
(a_DCV > -10) ?
(
this.FreqHz = min(DCVToFreqHz(a_DCV + l_RandModDCV), this.Nyquist);
this.SinTblIncPerSample = this.FreqHz * this.SinTblIncRatio;
);
this.SinTblPhaseIdx += this.SinTblIncPerSample;
(this.SinTblPhaseIdx >= SIN_TBL_SIZE) ? //possibly phase wrap
(
this.SinTblPhaseIdx -= SIN_TBL_SIZE;
this.LastRandDCV = this.RandDCV;
this.RandDCV = rand(2) - 1.0; //should be range -1 <> +1
);
l_int = floor(this.SinTblPhaseIdx);
l_frac = this.SinTblPhaseIdx - l_int;
l_result = this.SinAmpMul * (SIN_TBL[l_int] * (1 - l_frac) + SIN_TBL[l_int + 1] * l_frac); //linear interpolate
l_result += (this.TriAmpMul * (TRI_TBL[l_int] * (1 - l_frac) + TRI_TBL[l_int + 1] * l_frac)); //linear interpolate
);
//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
//FiltTypes: 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.49);
this.FC = min(a_FiltFC, this.Nyquist);
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);
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;
);
//specialized based on FirstOrdTrapezoidFilter above
//tunes from 1 to 16 cascaded ZDF allpass filters to the same frequency
//presumably the entire cascade would therefore be "ZDF" but maybe somehow it is not, dunno
function TrapezoidAllpassCascade_Init(a_DCV_FiltFC, a_NStages, a_SampRate)
local (l_ns)
(
this.SR = a_SampRate;
this.PiDivSR = $pi / this.SR;
this.Nyquist = floor(this.SR * 0.49);
this.FC = min(DCVToFreqHz(a_DCV_FiltFC), this.Nyquist);
//calculate coefficient
this.g = tan(this.FC * this.PiDivSR);
this.g /= (1 + this.g);
this.NStages = max(min(a_NStages, 16), 1);
l_ns = this.NStages - 1; //always do at least 1 allpass
(l_ns >= 8) ?
(
this.do_8 = 1.0;
l_ns -= 8;
)
:
this.do_8 = 0.0;
(l_ns >= 4) ?
(
this.do_4 = 1.0;
l_ns -= 4;
)
:
this.do_4 = 0.0;
(l_ns >= 2) ?
(
this.do_2 = 1.0;
l_ns -= 2;
)
:
this.do_2 = 0.0;
(l_ns >= 1) ?
this.do_1 = 1.0
:
this.do_1 = 0.0;
this.s0 = 0.0;
this.s1 = 0.0;
this.s2 = 0.0;
this.s3 = 0.0;
this.s4 = 0.0;
this.s5 = 0.0;
this.s6 = 0.0;
this.s7 = 0.0;
this.s8 = 0.0;
this.s9 = 0.0;
this.s10 = 0.0;
this.s11 = 0.0;
this.s12 = 0.0;
this.s13 = 0.0;
this.s14 = 0.0;
this.s15 = 0.0;
);
function TrapezoidAllpassCascade_SetFC(a_DCV_FiltFC)
(
this.FC = min(DCVToFreqHz(a_DCV_FiltFC), this.Nyquist);
//calculate coefficient
this.g = tan(this.FC * this.PiDivSR);
this.g /= (1 + this.g);
);
function TrapezoidAllpassCascade_SetNStages(a_NStages)
local (l_ns)
(
this.NStages = max(min(a_NStages, 16), 1);
l_ns = this.NStages - 1; //always do at least 1 allpass
(l_ns >= 8) ?
(
this.do_8 = 1.0;
l_ns -= 8;
)
:
this.do_8 = 0.0;
(l_ns >= 4) ?
(
this.do_4 = 1.0;
l_ns -= 4;
)
:
this.do_4 = 0.0;
(l_ns >= 2) ?
(
this.do_2 = 1.0;
l_ns -= 2;
)
:
this.do_2 = 0.0;
(l_ns >= 1) ?
this.do_1 = 1.0
:
this.do_1 = 0.0;
);
//Returns the filtered sample
function TrapezoidAllpassCascade_DoSamp(a_DCV_FiltFC, a_InSamp)
local (l_v, l_lp, l_ap)
(
(a_DCV_FiltFC > -10.0) ? //change filter fc
(
this.FC = min(DCVToFreqHz(a_DCV_FiltFC), this.Nyquist);
//calculate coefficient
this.g = tan(this.FC * this.PiDivSR);
this.g /= (1 + this.g);
);
l_ap = a_InSamp;
//always do at least 1 stage
//allpass #0
l_v = (l_ap - this.s0) * this.g;
l_lp = l_v + this.s0;
this.s0 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
(this.do_1 != 0.0) ?
(
//allpass #1
l_v = (l_ap - this.s1) * this.g;
l_lp = l_v + this.s1;
this.s1 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
);
(this.do_2 != 0.0) ?
(
//allpass #2
l_v = (l_ap - this.s2) * this.g;
l_lp = l_v + this.s2;
this.s2 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #3
l_v = (l_ap - this.s3) * this.g;
l_lp = l_v + this.s3;
this.s3 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
);
(this.do_4 != 0.0) ?
(
//allpass #4
l_v = (l_ap - this.s4) * this.g;
l_lp = l_v + this.s4;
this.s4 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #5
l_v = (l_ap - this.s5) * this.g;
l_lp = l_v + this.s5;
this.s5 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #6
l_v = (l_ap - this.s6) * this.g;
l_lp = l_v + this.s6;
this.s6 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #7
l_v = (l_ap - this.s7) * this.g;
l_lp = l_v + this.s7;
this.s7 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
);
(this.do_8 != 0.0) ?
(
//allpass #8
l_v = (l_ap - this.s8) * this.g;
l_lp = l_v + this.s8;
this.s8 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #9
l_v = (l_ap - this.s9) * this.g;
l_lp = l_v + this.s9;
this.s9 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #10
l_v = (l_ap - this.s10) * this.g;
l_lp = l_v + this.s10;
this.s10 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #11
l_v = (l_ap - this.s11) * this.g;
l_lp = l_v + this.s11;
this.s11 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #12
l_v = (l_ap - this.s12) * this.g;
l_lp = l_v + this.s12;
this.s12 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #13
l_v = (l_ap - this.s13) * this.g;
l_lp = l_v + this.s13;
this.s13 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #14
l_v = (l_ap - this.s14) * this.g;
l_lp = l_v + this.s14;
this.s14 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
//allpass #15
l_v = (l_ap - this.s15) * this.g;
l_lp = l_v + this.s15;
this.s15 = l_lp + l_v;
l_ap = 2 * l_lp - l_ap;
);
l_ap; //return allpass cascade result
);
//A peak envelope follower designed to have as little possible audio frequency ripple
// and also minimal "short term ripple" as caused by random transient short amplitude peaks
// caused by accidental note-reinforcements in multi-note held chords.
//Filter follower effect does not sound at all good if either of those two kinds of ripple
// are very substantial in an envelope follower.
//Attempts to economically wipe out audio frequency ripple by peak-hold of most recent 25 ms
// audio (40 Hz full-wave and 20 Hz half-wave). A peak history array of 1 ms sized peaks
// works and has no sample jitter on entry to each new local max peak hold, but can have jitter
// reverting to the next-loudest-value after a loudest recent max peak gets older than 25 ms
// and is forgotten.
//I try to de-emphasize this "next-loudest-value" 1 ms granularity peak hold recovery jitter with a 2nd
// order attack-release envelope:
// Attack = 2 series stages of 2 ms
// Release = 2 series stages of 100 ms-- Seems long enough to smooth out short-term envelope wobble on held chords.
//Multiple series first-order exponential envelopes tend to assume "sigmoid shaped" attack and
// release shapes, which "start gentle" and "end gentle" with most of the action in-between.
g_PK_HIST_ARY_SIZE = 26; //history of 25 ms of previous peaks
function EnvFollow_Init(a_SampRate)
local (l_TCSamps)
(
this.SR = a_SampRate;
this.SampsPerMSBin = floor(this.SR * 0.001);
this.PkHistAry = js_malloc(g_PK_HIST_ARY_SIZE, JS_MALLOC_CLEARMEM);
this.PkHistAryCurBinIdx = 0;
this.PkHistAryMaxBinPeak = 0;
this.CurBinSampCount = 0;
this.CurBinPeak = 0;
l_TCSamps = 0.002 * this.SR; //2 ms attack time constant
this.ACoff = 1.0 / (2.0 * l_TCSamps);
this.ACoff /= (1 + this.ACoff);
l_TCSamps = 0.1 * this.SR; //100 ms release time constant
this.RCoff = 1.0 / (2.0 * l_TCSamps);
this.RCoff /= (1 + this.RCoff);
this.Env = 0; //Peak envelope value
this.s0 = 0; //filter 0 attack-release state variable
this.s1 = 0; //filter 1 attack-release state variable
);
function EnvFollow_FindPkHistAryMaxBinPeak()
local (l_i, l_MaxPeak)
(
this.PkHistAry[this.PkHistAryCurBinIdx] = this.CurBinPeak; //save off current peak
((this.PkHistAryCurBinIdx += 1) >= g_PK_HIST_ARY_SIZE) ?
this.PkHistAryCurBinIdx = 0;
this.PkHistAry[this.PkHistAryCurBinIdx] = 0; //clear the oldest peak history element
this.CurBinSampCount = 0;
this.CurBinPeak = 0;
l_MaxPeak = 0;
l_i = 0;
while (l_i < g_PK_HIST_ARY_SIZE)
(
(l_MaxPeak < this.PkHistAry[l_i]) ?
l_MaxPeak = this.PkHistAry[l_i];
l_i += 1;
);
this.PkHistAryMaxBinPeak = l_MaxPeak;
);
function EnvFollow_DoSamp(a_InSamp)
local (l_RawEnv, l_v)
(
a_InSamp = abs(a_InSamp);
(this.CurBinPeak < a_InSamp) ?
this.CurBinPeak = a_InSamp;
(this.CurBinPeak > this.PkHistAryMaxBinPeak) ?
l_RawEnv = this.CurBinPeak
:
l_RawEnv = this.PkHistAryMaxBinPeak;
((l_v = (l_RawEnv - this.s0)) > 0.0) ? //attack-release envelope 0
l_v *= this.ACoff //attack
:
l_v *= this.RCoff; //release
this.Env = l_v + this.s0;
this.s0 = this.Env + l_v;
((l_v = (this.Env - this.s1)) > 0.0) ? //attack-release envelope 1
l_v *= this.ACoff //attack
:
l_v *= this.RCoff; //release
this.Env = l_v + this.s1;
this.s1 = this.Env + l_v;
((this.CurBinSampCount += 1) >= this.SampsPerMSBin) ?
this.EnvFollow_FindPkHistAryMaxBinPeak();
this.Env;
);
//global constants
g_PHASER_INPUT_MONO = 0;
g_PHASER_INPUT_STEREO = 1;
g_PHASER_INPUT_MIDSIDE = 2;
g_GFXPLOT_VALBUF_SIZE = 8192;
//global initted flags
g_GFX_Initted = 0;
g_CurGFX_w = 0;
g_CurGFX_h = 0;
//global vars
g_PhaserInputMode = g_PHASER_INPUT_STEREO; //slider1
g_LFOWavShape = 0.5; //slider2
g_LFORateRangeIdx = 0; //slider3 10^-1 mul, 10^0 mul, 10^1 mul
g_LFO_Hz_CenterFreqSliderVal = 1.0; //slider4
g_LFO_DCV_FreqSpread = .05; //slider5
g_LFO_RandomMul = 0.0; //slider6
g_LFO_ChaosMul = 0.5; //slider7
g_LFO_EnvFollow_Mul = 0.0; //slider8
g_APCascade_NStages = 8; //slider9
g_APCascade_Fc_Hz = 500; //slider10
g_AP_LFO_Semitones = 12; //slider11
g_AP_EnvFollow_Mul = 0.0; //slider12
g_AP_Res_Percent = 0; //slider13
g_WetWidth_Percent = 100; //slider14
g_WetMix_Percent = 100; //slider15
g_DryWidth_Percent = 100; //slider16
g_DryMix_Percent = 100; //slider17
g_OutGainDB = -6; //slider18
g_LFORateRangeMul = 10.0 ^ g_LFORateRangeIdx;
g_LFO_Hz_CenterFreq = g_LFO_Hz_CenterFreqSliderVal * g_LFORateRangeMul;
g_LFO_DCV_CenterFreq = FreqHzToDCV(g_LFO_Hz_CenterFreq);
g_LFO_DCV_Freq_L = g_LFO_DCV_CenterFreq - g_LFO_DCV_FreqSpread;
g_LFO_DCV_Freq_R = g_LFO_DCV_CenterFreq + g_LFO_DCV_FreqSpread;
g_GFX_LastDrawnLFO_Val_L = 0;
g_GFX_LastDrawnLFO_Val_R = 0;
g_GFX_LastDrawnENV_Val_L = 0;
g_GFX_LastDrawnENV_Val_R = 0;
g_APCascade_Fc_DCV = FreqHzToDCV(g_APCascade_Fc_Hz);
g_AP_LFO_Mul = g_AP_LFO_Semitones / 12;
g_AP_Res_FBMul = g_AP_Res_Percent / 100;
g_WetWidth_SameMul = (g_WetWidth_Percent + 100) / 200;
g_WetWidth_ReverseMul = 1.0 - g_WetWidth_SameMul;
g_WetMix_Mul = g_WetMix_Percent / 100;
g_OutGain_Mul = DB_To_Amplitude(g_OutGainDB);
g_DryWidth_SameMul = (g_DryWidth_Percent + 100) / 200;
g_DryWidth_ReverseMul = 1.0 - g_DryWidth_SameMul;
g_DryMix_Mul = g_DryMix_Percent / 100;
g_SampsPerLFOPixel = 100; //set nominal value, to be updated in gfx init
g_LFOPixelSampCounter = 0;
g_GFX_LFOValBuf_L = js_malloc(g_GFXPLOT_VALBUF_SIZE, JS_MALLOC_CLEARMEM);
g_GFX_LFOValBuf_R = js_malloc(g_GFXPLOT_VALBUF_SIZE, JS_MALLOC_CLEARMEM);
g_GFX_EnvValBuf_L = js_malloc(g_GFXPLOT_VALBUF_SIZE, JS_MALLOC_CLEARMEM);
g_GFX_EnvValBuf_R = js_malloc(g_GFXPLOT_VALBUF_SIZE, JS_MALLOC_CLEARMEM);
g_GFX_ValBufHead = 0;
g_GFX_ValBufTail = 0;
o_LFO_L.SinTriOsc_Init(g_LFO_DCV_Freq_L, g_LFOWavShape, g_LFO_RandomMul, srate);
o_LFO_R.SinTriOsc_Init(g_LFO_DCV_Freq_R, g_LFOWavShape, g_LFO_RandomMul, srate);
o_AP_L.TrapezoidAllpassCascade_Init(g_APCascade_Fc_DCV, g_APCascade_NStages, srate);
o_AP_R.TrapezoidAllpassCascade_Init(g_APCascade_Fc_DCV, g_APCascade_NStages, srate);
o_EnvFollower_L.EnvFollow_Init(srate);
o_EnvFollower_R.EnvFollow_Init(srate);
@slider
//Phaser input mode, mono, stereo, mid-side
(slider1 != g_PhaserInputMode) ?
(
slider1 = max(min(slider1, 2.0), 0.0);
g_PhaserInputMode = slider1;
);
//LFO Shape [SIN <-> TRI]
//default val = 0.5, min val = 0, max val = 1.0, change increment = 0.01
(slider2 != g_LFOWavShape) ?
(
//If out-of-range vals are typed into into a slider textbox,
// the slider val clamps but not the textbox value.
//Therefore "redundant clamping" makes sure that weird out-of-range values
// do not remain in the textboxes after the slider performs automatic value clamping.
slider2 = max(min(slider2, 1.0), 0.0);
g_LFOWavShape = slider2;
o_LFO_L.SinTriOsc_SetSinTriMix(g_LFOWavShape);
o_LFO_R.SinTriOsc_SetSinTriMix(g_LFOWavShape);
);
//LFO Rate Multiplier 0.1, 1.0, 10.0
//LFO Rate Multiplier == 10 ^ (MenuIdx - 1)
//default val = 1, min val = 0, max val = 2, change increment = 1
(slider3 != (g_LFORateRangeIdx + 1)) ?
(
slider3 = max(min(slider3, 2), 0);
g_LFORateRangeIdx = slider3 - 1;
g_LFORateRangeMul = 10.0 ^ g_LFORateRangeIdx;
g_LFO_Hz_CenterFreq = g_LFO_Hz_CenterFreqSliderVal * g_LFORateRangeMul;
g_LFO_DCV_CenterFreq = FreqHzToDCV(g_LFO_Hz_CenterFreq);
g_LFO_DCV_Freq_L = g_LFO_DCV_CenterFreq - g_LFO_DCV_FreqSpread;
g_LFO_DCV_Freq_R = g_LFO_DCV_CenterFreq + g_LFO_DCV_FreqSpread;
g_GFX_Initted = 0; //trigger graphic oscilloscope LFO rescaling
);
//LFO Rate Center [Hz]
//default val = 1, min val = 1, max val = 10, change increment = 0.01
(slider4 != g_LFO_Hz_CenterFreqSliderVal) ?
(
slider4 = max(min(slider4, 10), 1);
g_LFO_Hz_CenterFreqSliderVal = slider4;
g_LFO_Hz_CenterFreq = g_LFO_Hz_CenterFreqSliderVal * g_LFORateRangeMul;
g_LFO_DCV_CenterFreq = FreqHzToDCV(g_LFO_Hz_CenterFreq);
g_LFO_DCV_Freq_L = g_LFO_DCV_CenterFreq - g_LFO_DCV_FreqSpread;
g_LFO_DCV_Freq_R = g_LFO_DCV_CenterFreq + g_LFO_DCV_FreqSpread;
g_GFX_Initted = 0; //trigger graphic oscilloscope LFO rescaling
);
//LFO Rate Left/Right Offset [-1.0 <-> 1.0]
//If 1.0, Right LFO is 2X Center Freq and Left LFO is 0.5X Center Freq
//If -1.0, Right LFO is 0.5X Center Freq and Left LFO is 2X Center Freq
//default val = 0.05, min val = -1, max val = 1, change increment = 0.01
(slider5 != g_LFO_DCV_FreqSpread) ?
(
slider5 = max(min(slider5, 1), -1);
g_LFO_DCV_FreqSpread = slider5;
g_LFO_DCV_Freq_L = g_LFO_DCV_CenterFreq - g_LFO_DCV_FreqSpread;
g_LFO_DCV_Freq_R = g_LFO_DCV_CenterFreq + g_LFO_DCV_FreqSpread;
);
//LFO Random Rate
//default val = 0, min val = 0, max val =2, change increment = 0.01
(slider6 != g_LFO_RandomMul) ?
(
slider6 = max(min(slider6, 2), 0);
g_LFO_RandomMul = slider6;
o_LFO_L.SinTriOsc_SetRandMul(g_LFO_RandomMul);
o_LFO_R.SinTriOsc_SetRandMul(g_LFO_RandomMul);
);
//LFO Chaos (-2 <-> +2)
//default val = 0.5, min val = -2, max val =2, change increment = 0.01
(slider7 != g_LFO_ChaosMul) ?
(
slider7 = max(min(slider7, 2), -2);
g_LFO_ChaosMul = slider7;
);
//LFO Rate Env Follower Mod Gain
//default val = 0, min val = -10, max val =10, change increment = 0.1
(slider8 != g_LFO_EnvFollow_Mul) ?
(
slider8 = max(min(slider8, 10), -10);
g_LFO_EnvFollow_Mul = slider8;
);
//g_APCascade_NStages = 1 thru 16
(slider9 != g_APCascade_NStages) ?
(
slider9 = max(min(slider9, 16), 1);
g_APCascade_NStages = slider9;
o_AP_L.TrapezoidAllpassCascade_SetNStages(g_APCascade_NStages);
o_AP_R.TrapezoidAllpassCascade_SetNStages(g_APCascade_NStages);
);
//Phaser Center Freq
//default val = 500, min val = 40, max val = 5000, change increment = 1
(slider10 != g_APCascade_Fc_Hz) ?
(
slider10 = max(min(slider10, 5000), 40);
g_APCascade_Fc_Hz = slider10;
g_APCascade_Fc_DCV = FreqHzToDCV(g_APCascade_Fc_Hz);
);
//Phaser LFO Mod Amt [+/- Semitones]
//default val = 12, min val = 0, max val = 36, change increment = 0.1
(slider11 != g_AP_LFO_Semitones) ?
(
slider11 = max(min(slider11, 36), 0);
g_AP_LFO_Semitones = slider11;
g_AP_LFO_Mul = g_AP_LFO_Semitones / 12;;
);
//Phaser Env FOllower Mod
//default val = 0, min val = -10, max val = 10, change increment = 0.1
(slider12 != g_AP_EnvFollow_Mul) ?
(
slider12 = max(min(slider12, 10), -10);
g_AP_EnvFollow_Mul = slider12;
);
//Phaser Resonance (-99% <> +99%)
//default val = 0, min val = -99, max val = 99, change increment = 1
(slider13 != g_AP_Res_Percent) ?
(
slider13 = max(min(slider13, 99), -99);
g_AP_Res_Percent = slider13;
g_AP_Res_FBMul = g_AP_Res_Percent / 100;
);
//Wet Stereo Width (-100% <> +100%)
//default val = 100, min val = -100, max val = 100, change increment = 0.1
(slider14 != g_WetWidth_Percent) ?
(
slider14 = max(min(slider14, 100), -100);
g_WetWidth_Percent = slider14;
g_WetWidth_SameMul = (g_WetWidth_Percent + 100) / 200;
g_WetWidth_ReverseMul = 1.0 - g_WetWidth_SameMul;
);
//Wet Mix Level (-1=0% <> +100%)
//default val = 100, min val = -100, max val = 100, change increment = 0.1
(slider15 != g_WetMix_Percent) ?
(
slider15 = max(min(slider15, 100), -100);
g_WetMix_Percent = slider15;
g_WetMix_Mul = g_WetMix_Percent / 100;
);
//Dry Stereo Width (0 <> +100%)
//default val = 100, min val = 0, max val = 100, change increment = 0.1
(slider16 != g_DryWidth_Percent) ?
(
slider16 = max(min(slider16, 100), 0);
g_DryWidth_Percent = slider16;
g_DryWidth_SameMul = (g_DryWidth_Percent + 100) / 200;
g_DryWidth_ReverseMul = 1.0 - g_DryWidth_SameMul;
);
//Dry Mix Level (0 <> +100%)
//default val = 100, min val = 0, max val = 100, change increment = 0.1
(slider17 != g_DryMix_Percent) ?
(
slider17 = max(min(slider17, 100), 0);
g_DryMix_Percent = slider17;
g_DryMix_Mul = g_DryMix_Percent / 100;
);
//Output Gain -24 -> +6 dB
//default val = -6, min val = -24, max val = 6, change increment = 0.1
(slider18 != g_OutGainDB) ?
(
slider18 = max(min(slider18, 6), -24);
g_OutGainDB = slider18;
g_OutGain_Mul = DB_To_Amplitude(g_OutGainDB);
);
@sample
//g_PHASER_INPUT_MONO = 0;
//g_PHASER_INPUT_STEREO = 1;
//g_PHASER_INPUT_MIDSIDE = 2;
(g_PhaserInputMode == g_PHASER_INPUT_STEREO) ?
(
s_PhaserInput_L = spl0;
s_PhaserInput_R = spl1;
)
:
(
s_PhaserInput_L = (spl0 + spl1) * 0.5;
(g_PhaserInputMode == g_PHASER_INPUT_MONO) ?
s_PhaserInput_R = s_PhaserInput_L
: //else g_PHASER_INPUT_MIDSIDE
s_PhaserInput_R = (spl0 - spl1) * 0.5;
);
s_AmpEnv_L = o_EnvFollower_L.EnvFollow_DoSamp(s_PhaserInput_L);
s_AmpEnv_R = o_EnvFollower_R.EnvFollow_DoSamp(s_PhaserInput_R);
s_LFO_Out_L = o_LFO_L.SinTriOsc_DoSamp(s_AmpEnv_L * g_LFO_EnvFollow_Mul + s_Last_LFO_R * abs(g_LFO_ChaosMul) + g_LFO_DCV_Freq_L);
s_LFO_Out_R = o_LFO_R.SinTriOsc_DoSamp(s_AmpEnv_R * g_LFO_EnvFollow_Mul + s_Last_LFO_L * g_LFO_ChaosMul + g_LFO_DCV_Freq_R);
s_Last_LFO_L = s_LFO_Out_L;
s_Last_LFO_R = s_LFO_Out_R;
((g_LFOPixelSampCounter += 1) >= g_SampsPerLFOPixel) ?
(
g_GFX_LFOValBuf_L[g_GFX_ValBufHead] = s_Last_LFO_L;
g_GFX_LFOValBuf_R[g_GFX_ValBufHead] = s_Last_LFO_R;
g_GFX_EnvValBuf_L[g_GFX_ValBufHead] = s_AmpEnv_L;
g_GFX_EnvValBuf_R[g_GFX_ValBufHead] = s_AmpEnv_R;
g_LFOPixelSampCounter = 0;
((g_GFX_ValBufHead += 1) >= g_GFXPLOT_VALBUF_SIZE) ?
g_GFX_ValBufHead = 0;
);
s_APIn_L = s_PhaserInput_L + s_LastAPOut_L * g_AP_Res_FBMul;
s_APFcMod_L = s_AmpEnv_L * g_AP_EnvFollow_Mul + s_LFO_Out_L * g_AP_LFO_Mul + g_APCascade_Fc_DCV;
s_APOut_L = o_AP_L.TrapezoidAllpassCascade_DoSamp(s_APFcMod_L, s_APIn_L);
s_LastAPOut_L = s_APOut_L;
s_APIn_R = s_PhaserInput_R + s_LastAPOut_R * g_AP_Res_FBMul;
s_APFcMod_R = s_AmpEnv_R * g_AP_EnvFollow_Mul + s_LFO_Out_R * g_AP_LFO_Mul + g_APCascade_Fc_DCV;
s_APOut_R = o_AP_R.TrapezoidAllpassCascade_DoSamp(s_APFcMod_R, s_APIn_R);
s_LastAPOut_R = s_APOut_R;
(g_PhaserInputMode != g_PHASER_INPUT_MIDSIDE) ?
(
s_Wet_L = s_APOut_L * g_WetWidth_SameMul + s_APOut_R * g_WetWidth_ReverseMul;
s_Wet_R = s_APOut_L * g_WetWidth_ReverseMul + s_APOut_R * g_WetWidth_SameMul;
s_Dry_L = spl0 * g_DryWidth_SameMul + spl1 * g_DryWidth_ReverseMul;
s_Dry_R = spl0 * g_DryWidth_ReverseMul + spl1 * g_DryWidth_SameMul;
spl0 = g_OutGain_Mul * (s_Wet_L * g_WetMix_Mul + s_Dry_L * g_DryMix_Mul);
spl1 = g_OutGain_Mul * (s_Wet_R * g_WetMix_Mul + s_Dry_R * g_DryMix_Mul);
)
: //else == g_PHASER_INPUT_MIDSIDE so mix midside then convert back to stereo
(
s_MidMix = s_APOut_L * g_WetMix_Mul + s_PhaserInput_L * g_DryMix_Mul;
s_SideMix = s_APOut_R * g_WetMix_Mul + s_PhaserInput_R * g_DryMix_Mul;
spl0 = g_OutGain_Mul * (s_MidMix + s_SideMix);
spl1 = g_OutGain_Mul * (s_MidMix - s_SideMix);
);
@gfx
((g_CurGFX_w != gfx_w) || (g_CurGFX_h != gfx_h)) ?
g_GFX_Initted = 0;
(g_GFX_Initted == 0) ?
(
g_CurGFX_w = gfx_w;
g_CurGFX_h = gfx_h;
gx_HorzDrawIdx = 0;
gx_HorzDrawMax = gfx_w - 2;
gx_BlurWidth = gfx_w * 0.1;
//gx_WavHeight = min(gfx_h, floor(gfx_w / 6));
gx_WavHeight = gfx_h;
gx_HalfHt = (gx_WavHeight - 2) * 0.5;
gfx_setimgdim(0, gfx_w, gx_WavHeight);
(gfx_w < 240) ?
gx_LFOPeriodNumPixels = floor(gfx_w * 0.5)
:
gx_LFOPeriodNumPixels = 120;
gx_LFOPixelsPerSec = min(floor(max(g_LFO_Hz_CenterFreq * gx_LFOPeriodNumPixels, 30)), gfx_w * 2);
g_SampsPerLFOPixel = max(ceil(srate / gx_LFOPixelsPerSec), ceil(srate * 0.001));
g_LFOPixelSampCounter = 0;
g_GFX_Initted = 1;
);
gfx_dest = 0; //set drawing target to the plugin window
gx_GFX_ValBufHead = g_GFX_ValBufHead; //local gfx copy so that @sample can't change it on us
(gx_HorzDrawIdx == 0) ? //positive slope zero cross trigger start of OScope display sweep
(
gx_ExitTheWhile = 0;
while ((g_GFX_ValBufTail != gx_GFX_ValBufHead) && (gx_ExitTheWhile == 0))
(
gx_TmpLFOVal = g_GFX_LFOValBuf_L[g_GFX_ValBufTail];
((gx_TmpLFOVal >= 0.0) && (g_GFX_LastDrawnLFO_Val_L <= 0.0)) ?
(
gx_ExitTheWhile = 1.0;
)
:
(
g_GFX_LastDrawnLFO_Val_L = gx_TmpLFOVal;
g_GFX_LastDrawnLFO_Val_R = g_GFX_LFOValBuf_R[g_GFX_ValBufTail];
((g_GFX_ValBufTail += 1) >= g_GFXPLOT_VALBUF_SIZE) ?
g_GFX_ValBufTail = 0;
);
);
);
gx_ExitTheWhile = 0;
while ((g_GFX_ValBufTail != gx_GFX_ValBufHead) && (gx_ExitTheWhile == 0))
(
gfx_r = 0.5;
gfx_g = 0.25;
gfx_b = 0.0;
gfx_line(gx_HorzDrawIdx + 4, 0, gx_HorzDrawIdx + 4, gx_WavHeight - 1);
gfx_r = 0.75;
gfx_g = 0.375;
gfx_b = 0.0;
gfx_line(gx_HorzDrawIdx + 5, 0, gx_HorzDrawIdx + 5 , gx_WavHeight - 1);
gfx_r = 1.0;
gfx_g = 0.5;
gfx_b = 0.0;
gfx_line(gx_HorzDrawIdx + 6, 0, gx_HorzDrawIdx + 6, gx_WavHeight - 1);
gfx_x =gx_HorzDrawIdx + 1;
gfx_y = 0;
gfx_blurto(min(gx_HorzDrawIdx + gx_BlurWidth, gfx_w), gx_WavHeight - 1);
gfx_r = 0.0;
gfx_g = 0.0;
gfx_b = 0.0;
gfx_line(gx_HorzDrawIdx + 1, 0, gx_HorzDrawIdx + 1, gx_WavHeight - 1);
gfx_line(gx_HorzDrawIdx + 2, 0, gx_HorzDrawIdx + 2, gx_WavHeight - 1);
gfx_line(gx_HorzDrawIdx + 3, 0, gx_HorzDrawIdx + 3, gx_WavHeight - 1);
gx_Env_L = g_GFX_EnvValBuf_L[g_GFX_ValBufTail];
gx_Env_R = g_GFX_EnvValBuf_R[g_GFX_ValBufTail];
gx_EnvVert_L = gx_WavHeight - (gx_WavHeight * gx_Env_L);
gx_EnvVert_R = gx_WavHeight - (gx_WavHeight * gx_Env_R);
(floor(gx_EnvVert_L) == floor(gx_EnvVert_R)) ? //just draw the left if they are equal
(
gfx_r = 0.0;
gfx_g = 0.15;
gfx_b = 0.0;
gfx_line(gx_HorzDrawIdx + 1, gx_EnvVert_L, gx_HorzDrawIdx + 1, gx_WavHeight - 1);
gfx_r = 0.0;
gfx_g = 0.8;
gfx_b = 0.0;
gx_v0 = gx_WavHeight - (gx_WavHeight * g_GFX_LastDrawnENV_Val_L);
gfx_line(gx_HorzDrawIdx, gx_v0, gx_HorzDrawIdx + 1, gx_EnvVert_L);
gfx_line(gx_HorzDrawIdx, gx_v0 + 1, gx_HorzDrawIdx + 1, gx_EnvVert_L + 1);
g_GFX_LastDrawnENV_Val_L = gx_Env_L;
g_GFX_LastDrawnENV_Val_R = gx_Env_R;
)
:
(
(gx_EnvVert_L <= gx_EnvVert_R) ? //Draw R envelope after L envelope
(
gfx_r = 0.0;
gfx_g = 0.15;
gfx_b = 0.0;
gfx_line(gx_HorzDrawIdx + 1, gx_EnvVert_L, gx_HorzDrawIdx + 1, gx_WavHeight - 1);
gfx_r = 0.0;
gfx_g = 0.8;
gfx_b = 0.0;
gx_v0 = gx_WavHeight - (gx_WavHeight * g_GFX_LastDrawnENV_Val_L);
gfx_line(gx_HorzDrawIdx, gx_v0, gx_HorzDrawIdx + 1, gx_EnvVert_L);
gfx_line(gx_HorzDrawIdx, gx_v0 + 1, gx_HorzDrawIdx + 1, gx_EnvVert_L + 1);
g_GFX_LastDrawnENV_Val_L = gx_Env_L;
gfx_r = 0.15;
gfx_g = 0.0;
gfx_b = 0.0;
gfx_line(gx_HorzDrawIdx + 1, gx_EnvVert_R, gx_HorzDrawIdx + 1, gx_WavHeight - 1);
gfx_r = 0.8;
gfx_g = 0.0;
gfx_b = 0.0;
gx_v0 = gx_WavHeight - (gx_WavHeight * g_GFX_LastDrawnENV_Val_R);
gfx_line(gx_HorzDrawIdx, gx_v0, gx_HorzDrawIdx + 1, gx_EnvVert_R);
gfx_line(gx_HorzDrawIdx, gx_v0 + 1, gx_HorzDrawIdx + 1, gx_EnvVert_R + 1);
g_GFX_LastDrawnENV_Val_R = gx_Env_R;
)
: //else draw L envelope after R envelope
(
gfx_r = 0.15;
gfx_g = 0.0;
gfx_b = 0.0;
gfx_line(gx_HorzDrawIdx + 1, gx_EnvVert_R, gx_HorzDrawIdx + 1, gx_WavHeight - 1);
gfx_r = 0.8;
gfx_g = 0.0;
gfx_b = 0.0;
gx_v0 = gx_WavHeight - (gx_WavHeight * g_GFX_LastDrawnENV_Val_R);
gfx_line(gx_HorzDrawIdx, gx_v0, gx_HorzDrawIdx + 1, gx_EnvVert_R);
gfx_line(gx_HorzDrawIdx, gx_v0 + 1, gx_HorzDrawIdx + 1, gx_EnvVert_R + 1);
g_GFX_LastDrawnENV_Val_R = gx_Env_R;
gfx_r = 0.0;
gfx_g = 0.15;
gfx_b = 0.0;
gfx_line(gx_HorzDrawIdx + 1, gx_EnvVert_L, gx_HorzDrawIdx + 1, gx_WavHeight - 1);
gfx_r = 0.0;
gfx_g = 0.8;
gfx_b = 0.0;
gx_v0 = gx_WavHeight - (gx_WavHeight * g_GFX_LastDrawnENV_Val_L);
gfx_line(gx_HorzDrawIdx, gx_v0, gx_HorzDrawIdx + 1, gx_EnvVert_L);
gfx_line(gx_HorzDrawIdx, gx_v0 + 1, gx_HorzDrawIdx + 1, gx_EnvVert_L + 1);
g_GFX_LastDrawnENV_Val_L = gx_Env_L;
);
);
gfx_r = 0.0;
gfx_g = 1.0;
gfx_b = 0.5;
gx_TmpLFOVal = g_GFX_LFOValBuf_L[g_GFX_ValBufTail];
gx_v0 = gx_HalfHt - (gx_HalfHt * g_GFX_LastDrawnLFO_Val_L);
gx_v1 = gx_HalfHt - (gx_HalfHt * gx_TmpLFOVal);
gfx_line(gx_HorzDrawIdx, gx_v0, gx_HorzDrawIdx + 1, gx_v1);
gfx_line(gx_HorzDrawIdx, gx_v0 + 1, gx_HorzDrawIdx + 1, gx_v1 + 1);
g_GFX_LastDrawnLFO_Val_L = gx_TmpLFOVal;
gfx_r = 1.0;
gfx_g = 0.5;
gfx_b = 0.0;
gx_TmpLFOVal = g_GFX_LFOValBuf_R[g_GFX_ValBufTail];
gx_v0 = gx_HalfHt - (gx_HalfHt * g_GFX_LastDrawnLFO_Val_R);
gx_v1 = gx_HalfHt - (gx_HalfHt * gx_TmpLFOVal);
gfx_line(gx_HorzDrawIdx, gx_v0, gx_HorzDrawIdx + 1, gx_v1);
gfx_line(gx_HorzDrawIdx, gx_v0 + 1, gx_HorzDrawIdx + 1, gx_v1 + 1);
g_GFX_LastDrawnLFO_Val_R = gx_TmpLFOVal;
((gx_HorzDrawIdx += 1) >= gx_HorzDrawMax) ?
(
gx_HorzDrawIdx = 0;
gx_ExitTheWhile = 1;
)
:
(
((g_GFX_ValBufTail += 1) >= g_GFXPLOT_VALBUF_SIZE) ?
g_GFX_ValBufTail = 0;
);
);
gfx_dest = -1;
gfx_blit(0, 1.0, 0.0, 0, 0, gfx_w - 1, gx_WavHeight - 1, 0, 0, gfx_w - 1, gx_WavHeight - 1, 0, 0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment