-
-
Save X-Raym/4558be0b4225f66f537b12fde0ce2f80 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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