desc:Saike 4-pole BandSplitter tags: bandsplitter version: 0.23 author: Joep Vanlier changelog: Make sure phasematcher does not have a confusing number of outputs. provides: BandJoiner.jsfx BandSplitter_phasematcher.jsfx about: # 4-pole Band Splitter 4-pole band splitter that preserves phase between the bands. It has a UI and uses much steeper crossover filters (24 dB/oct) than the default that ships with Reaper thereby providing sharper band transitions. It also has an option for linear phase FIR crossovers instead of the default IIR filters. IIRs cost less CPU and introduce no preringing or latency. The linear phase FIRs however prevent phase distortion (which can be important in some mixing settings), but introduce latency compensation. Note that when using the linear phase filters, it is not recommended to modulate the crossover frequencies as this introduces crackles. [Screenshot](https://i.imgur.com/nOhiaJB.png) ### Demos You can find a tutorial of the plugin [here](https://www.youtube.com/watch?v=JU_7gIr5RTI). license: MIT in_pin:left input in_pin:right input out_pin:left output 1 out_pin:right output 1 out_pin:left output 2 out_pin:right output 2 out_pin:left output 3 out_pin:right output 3 out_pin:left output 4 out_pin:right output 4 out_pin:left output 5 out_pin:right output 5 options:gmem=SaikeBandSplitter slider1:Cuts=1<0,4,1>-Cuts slider2:Freq1=0.2<0,1,.0001>-Frequency 1 slider3:Freq2=0.5<0,1,.0001>-Frequency 2 slider4:Freq3=0.5<0,1,.0001>-Frequency 3 slider5:Freq4=0.5<0,1,.0001>-Frequency 4 slider6:Drive1=0<-40,60,.1>-Drive 1 (dB) slider7:Drive2=0<-40,60,.1>-Drive 2 (dB) slider8:Drive3=0<-40,60,.1>-Drive 3 (dB) slider9:Drive4=0<-40,60,.1>-Drive 4 (dB) slider10:Drive5=0<-40,60,.1>-Drive 5 (dB) slider58:absolute_frequencies=1<0,1,1>-Fixed frequency range slider59:abs_placement=0<0,1,1>-Absolute placement slider60:FIR_quality=0<0,2,{Normal,High,Ultra}>-FIR Quality slider61:FIR=0<0,1,1>-FIR mode slider62:masterGainSlider=0<-26,26,.1>-Master Gain slider63:band_mode=0<0,1,1{4p,2p}>-Band mode @init /*-----------*/ /* HINTS */ /*-----------*/ function updateHintTime(hint) global(gfx_x, gfx_y) local() instance(hintTime, currentHint, delta_time) ( (hint != 0) ? ( currentHint = hint; hintTime = hintTime + delta_time; hintTime = min(1, hintTime); ) : ( 0 ); ); function set_scaling_behaviour() ( absolute_frequencies ? ( freq_max = 22050; norm_freq_min = 20.0 / freq_max; norm_freq_max = freq_max / srate; ) : ( freq_max = 0.5 * srate; norm_freq_min = 20.0 / 22050; norm_freq_max = 0.5; ); ); function drawHint_draw() global(scaling, gfx_x, gfx_y, gfx_w, gfx_h, mouse_x, mouse_y, fontface, HINT_FONT) local(w, h, globalTime) instance(hintTime, currentHint, lastGlobalTime, delta_time, lx, ly) ( globalTime = time_precise(); delta_time = globalTime - lastGlobalTime; lastGlobalTime = globalTime; ( ( abs( lx - mouse_x ) + abs( ly - mouse_y ) ) > 0 ) ? ( hintTime = 0; ); ( hintTime > .99 ) ? ( gfx_setfont(HINT_FONT); gfx_measurestr(currentHint,w,h); gfx_x = mouse_x+15; gfx_y = mouse_y+15; ( gfx_x > 0.5*gfx_w ) ? gfx_x = mouse_x - w - 8; ( gfx_y > 0.5*gfx_h ) ? gfx_y = mouse_y - h - 8; gfx_set( 0.05, 0.05, 0.1, .8 ); gfx_rect(gfx_x-2, gfx_y-2, w+4, h+4); gfx_set( .7, .7, .7, 1 ); gfx_printf(currentHint); ); lx = mouse_x; ly = mouse_y; ); /*-----------*/ /* CIRCLE */ /*-----------*/ function fancyCircle(x1, y1, rin, mute, solo, bypass) local(dx) global(globalTime, lineR, lineG, lineB, lineA, lineHighlightR, lineHighlightG, lineHighlightB) instance(hl, x, y, r, over, gfx_mode) ( x = x1; y = y1; rin == 0 ? r = 10 : r = rin; over ? ( hl = abs(sin(2*globalTime)); gfx_set(lineHighlightR, lineHighlightG, lineHighlightB, .1*hl); gfx_circle(x, y, 1.2*r, 1, 1); gfx_set(lineHighlightR, lineHighlightG, lineHighlightB, .1*hl); gfx_circle(x, y, 1.5*r, 1, 1); gfx_set(lineHighlightR, lineHighlightG, lineHighlightB, .05*hl); gfx_circle(x, y, 1.9*r, 1, 1); gfx_set(lineHighlightR, lineHighlightG, lineHighlightB, .03*hl); gfx_circle(x, y, 4*r, 1, 1); ); gfx_set(.2*lineR, .2*lineG, .2*lineB, 1); gfx_circle(x, y, r, 1, 1); gfx_set(lineR, lineG, lineB, lineA); gfx_circle(x, y, .92*r, 1, 1); gfx_set(.8*lineR, .8*lineG, .8*lineB, lineA); gfx_circle(x, y, .8*r, 1, 1); gfx_set(.6*lineR, .6*lineG, .6*lineB, lineA); gfx_circle(x, y, .75*r, 1, 1); gfx_set(.4*lineR, .4*lineG, .4*lineB, lineA); gfx_circle(x, y, .7*r, 1, 1); ( solo == 1 ) ? ( gfx_set(.2, 1, .2, .5); gfx_circle(x, y, .8*r, 1, 1); gfx_set(.2, 1, .2, .5); gfx_circle(x, y, .9*r, 1, 1); gfx_set(.2, 1, .2, .5); gfx_circle(x, y, r, 1, 1); gfx_set(.2, 1, .2, .2); gfx_circle(x, y, 1.2*r, 1, 1); gfx_set(.7, 1, .7, .3); gfx_circle(x, y, .7*r, 1, 1); gfx_set(.2, 1, .5, .08); gfx_circle(x, y, 1.1*r, 1, 0); gfx_set(.2, 1, .5, .08); gfx_circle(x, y, 1.2*r, 1, 0); gfx_set(.2, 1, .5, .08); gfx_circle(x, y, 1.4*r, 1, 0); ) : ( mute == 1 ) ? ( gfx_set(1, .2, .2, .5); gfx_circle(x, y, .8*r, 1, 1); gfx_set(1, .2, .2, .5); gfx_circle(x, y, .9*r, 1, 1); gfx_set(1, .2, .2, .5); gfx_circle(x, y, r, 1, 1); gfx_set(1, .2, .2, .2); gfx_circle(x, y, 1.2*r, 1, 1); gfx_set(1, .55, .55, 1); gfx_circle(x, y, 1*r, 0, 1); gfx_set(1, .7, .7, .1); gfx_circle(x, y, .7*r, 1, 1); gfx_set(1, .5, .5, .1); gfx_circle(x, y, 1.1*r, 1, 0); gfx_set(1, .5, .5, .1); gfx_circle(x, y, 1.2*r, 1, 0); gfx_set(1, .5, .5, .1); gfx_circle(x, y, 1.4*r, 1, 0); ); ( bypass == 1 ) ? ( gfx_set(1-mute, .2, .2, 1); dx = .5*r; gfx_line(x-dx, y-dx-1, x+dx, y+dx-1); gfx_line(x-dx, y-dx+1, x+dx, y+dx+1); gfx_line(x+dx, y-dx-1, x-dx, y+dx-1); gfx_line(x+dx, y-dx+1, x-dx, y+dx+1); gfx_line(x+dx, y-dx, x-dx, y+dx); gfx_line(x-dx, y-dx, x+dx, y+dx); ); over ? ( gfx_set(1,1,1,.4*hl); gfx_circle(x, y, .6*r, 1, 1); gfx_circle(x, y, 1.4*r, 0, 1); gfx_circle(x, y, 1.5*r, 0, 1); ); over = 0; ); /*-----------*/ /* TOGGLE */ /*-----------*/ function processMouseToggle(mx, my, mousecap) instance(x, y, w, h, on, lastleft, str, over) local(left, slack) global(hinter.updateHintTime) ( slack = 5; left = mousecap & 1; over = ( (mx >= (x-slack)) && ( mx <= (x+w+slack) ) && ( my >= (y-slack) ) && ( my <= (y+h+slack) ) ); over ? ( ( (left == 1) && (lastleft == 0) ) ? ( on = 1 - on; ); hinter.updateHintTime(str); ); lastleft = left; on ); function drawToggle(_x, _y, _w, _h, _on, wr, wg, wb, wa, r, g, b, a, _str) local(ww, hh) instance(x, y, w, h, str, on, invert, label) global(gfx_x, gfx_y, gfx_a, gfx_mode, TOGGLE_FONT, knob_font_color_r, knob_font_color_g, knob_font_color_b, knob_font_color_a) ( x = _x; y = _y; w = _w; h = _h; on = _on; str = _str; gfx_set(0, 0, 0, 0); gfx_rect(x, y, w, h); gfx_set(r, g, b, a*.2); gfx_rect(x, y, w, h); gfx_set(wr, wg, wb, wa); gfx_line(x, y, x+w, y); gfx_line(x, y, x, y+h); gfx_line(x+w, y, x+w, y+h); gfx_line(x, y+h, x+w, y+h); ( label ) ? ( gfx_set(knob_font_color_r, knob_font_color_g, knob_font_color_b, knob_font_color_a); gfx_setfont(TOGGLE_FONT); gfx_measurestr(label, ww, hh); gfx_x = floor(x+1.5*w); gfx_y = floor(y-.5*hh+.5*h); gfx_printf(label); ); ( (on && !invert) || (!on && invert) ) ? ( gfx_set(r, g, b, a); gfx_rect(x, y, w, h); gfx_a *= .6; gfx_rect(x-1, y-1, w+2, h+2); gfx_a *= .6; gfx_rect(x-2, y-2, w+4, h+4); gfx_a *= .6; gfx_rect(x-3, y-3, w+6, h+6); gfx_a *= .4; gfx_circle(x+.5*w-1, y+.5*h-1, 2*max(w,h), 2*max(w,h)); gfx_a *= .4; gfx_circle(x+.5*w-1, y+.5*h-1, 3*max(w,h), 3*max(w,h)); gfx_a *= .4; gfx_circle(x+.5*w-1, y+.5*h-1, 4*max(w,h), 4*max(w,h)); gfx_a *= .4; gfx_circle(x+.5*w-1, y+.5*h-1, 5*max(w,h), 5*max(w,h)); ); ); GRID_FONT = 15; HINT_FONT = 14; TOGGLE_FONT = 15; gfx_ext_retina = 1; function f_trafo(freq) local() global(srate, freq_max, norm_freq_min) instance() ( freq_max * exp( (1-freq) * log(norm_freq_min) ) ); function initBands() local() global() instance(lg1, lg2, lg3, lg4, lg5, b1, b2, b3, b4, b5) ( lg1 = lg2 = lg3 = lg4 = lg5 = -1000000; b1.ldrive = b2.ldrive = b3.ldrive = b4.ldrive = b5.ldrive = -1000000; b1.initialized = b2.initialized = b3.initialized = b4.initialized = b5.initialized = 0; ); maxCuts = 4; driveMax = 60; driveMin = -40; driveRange = driveMax - driveMin; filterBank.initBands(); // Precompute some things gainRange = ( gainMax - gainMin ); iGainRange = 1.0 / gainRange; iDriveRange = 1.0 / driveRange; /////////////////// /* SLIDER LAYOUT */ /////////////////// /* Keep these updated with the slider locations */ frequencyLocation = 2; driveLocation = 6; /* Shift slider data right (for different bands) */ function shiftRight(loc, fromIdx, numElements) local(midx) global() ( midx = loc + numElements; loop(numElements-fromIdx, slider(midx) = slider(midx-1); midx = midx - 1; ); ); /* Shift slider data left (for different bands) */ function shiftLeft(loc, fromIdx, numElements) local(midx) global() ( midx = loc+fromIdx; loop(numElements-fromIdx, slider(midx) = slider(midx+1); midx = midx + 1; ); ); /* Shift slider data right (for different bands) */ function shiftRightMem(loc, fromIdx, numElements) local(midx) global() ( midx = loc + numElements; loop(numElements-fromIdx, midx[] = midx[-1]; midx -= 1; ); ); /* Shift slider data left (for different bands) */ function shiftLeftMem(loc, fromIdx, numElements) local(midx) global() ( midx = loc+fromIdx; loop(numElements-fromIdx, midx[] = midx[1]; midx += 1; ); ); /* Make room for a new band in the slider data */ function addBand(idx) local(midx) instance() ( shiftRight( driveLocation, idx, maxCuts ); shiftRight( frequencyLocation, idx, maxCuts-1 ); cuts = cuts + 1; ); /* Remove a band and move other slider data accordingly */ function remBand(idx) local(midx) instance() ( shiftLeft( driveLocation, idx, maxCuts ); shiftLeft( frequencyLocation, idx, maxCuts-1 ); cuts = cuts - 1; ); /* Feed sample to FFT */ function feedSample(sample) global(fftSize) instance(recPtr, window, rStart, rEnd, fftLoc) local() ( recPtr[] = sample; recPtr = (recPtr+1) >= rEnd ? rStart : recPtr + 1; ); /* Initialize FFT windowing function */ function initWindow(windowtype) global(fftSize) instance(recPtr, window, rStart, rEnd, fftLoc) local(i, pwr, dwindowpos, windowpos) ( windowpos = 0; dwindowpos = $pi*2/fftSize; i = 0; pwr = 0; loop(fftSize*.5+1, pwr += (window[i] = ( windowtype==1 ? ( 0.53836 - cos(windowpos)*0.46164 ) : windowtype==2 ? ( 0.35875 - 0.48829 * cos(windowpos) + 0.14128 * cos(2*windowpos) - 0.01168 * cos(3*windowpos) ) : windowtype==3 ? ( 0.42 - 0.50 * cos(windowpos) + 0.08 * cos(2.0*windowpos) ) : windowtype==4 ? ( 1.0 ) : ( .21557895 - 0.41663158 * cos(windowpos) + 0.277263158 * cos(2*windowpos) - 0.083578947 * cos(3*windowpos) + 0.006947368 * cos(4*windowpos) ) ) ); i+=1; windowpos+=dwindowpos; ); pwr=.5/(pwr*2-window[i-1]); loop(fftsize*.5+1,window[i-=1]*=pwr); ); function setToFFTLoc() global(fftSize) instance(recPtr, rStart) local() ( recPtr = rStart + fftSize; ); /* Calculate current spectrum */ function calcFFT() global(fftSize) instance(recPtr, window, rStart, rEnd, fftLoc, slope, yshift) local(buf1, buf2, buf3, yOut, i, prec, ty) ( buf1=recPtr-fftSize; buf1= rEnd ? buf1 -= (rEnd-rStart); ); buf2-=1; loop(fftSize*.5 - 1, buf3[] = buf1[]*(buf2-=1)[]; buf3+=1; (buf1+=1) >= rEnd ? buf1 -= (rEnd-rStart); ); fft_real(fftLoc,fftSize); fft_permute(fftLoc,fftSize/2); buf3 = fftLoc; yOut = fftLoc; i = 0; slope = .5*log(2)*slope; yshift = slope*log(1.0+.5*fftSize); prec = (10^(-500/20*2)); loop(fftSize*0.5, ty = sqr(buf3[0])+sqr(buf3[1]); ty = log(max(ty,prec)); ty = ty + slope*log(1.0+i) - yshift; yOut[] = ty; yOut += 1; buf3 += 2; i += 1; ); ); /* Set location of a window */ function setWindowLocation(_x, _y, _w, _h) global() instance(x, y, w, h, cap) local() ( x = _x; y = _y; w = _w; h = _h; ); // Draws the basic box for the FFT function drawBackface() instance(x, y, w, h) global(backface_color_r, backface_color_g, backface_color_b, backface_color_a, grid_color_r, grid_color_g, grid_color_b, grid_color_a ) local() ( gfx_set( backface_color_r, backface_color_g, backface_color_b, backface_color_a ); gfx_rect(x, y, w, h); gfx_set( grid_color_r, grid_color_g, grid_color_b, grid_color_a ); gfx_line(x, y, x+w, y); gfx_line(x, y+h, x+w, y+h); gfx_line(x, y, x, y+h); gfx_line(x+w, y, x+w, y+h); ); // Draws the FFT Grid, frequency axis and dB axis function drawGrid() global(fftSize, srate, gfx_x, gfx_y, grid_alpha, scaling, GRID_FONT, norm_freq_max) instance(x, y, w, h, recPtr, window, rStart, rEnd, fftLoc, floorLevel, hzoffset, grid_color_r, grid_color_g, grid_color_b, grid_color_a ) local(xx, i, wsc, N, xl) ( this.drawBackface(); i = 0; wsc = w/log(1+fftSize*0.5-hzoffset); xl = x; gfx_y = y+h+2; gfx_setfont(GRID_FONT); loop(16, xx = x + log(i*fftSize/(2 * srate * norm_freq_max)-hzoffset)*wsc; gfx_set( grid_color_r, grid_color_g, grid_color_b, grid_color_a ); gfx_line(xx, y, xx, y+h); ( (xx - xl) > 30 * (1 + scaling) ) ? ( gfx_set( 1, 1, 1, 1 ); gfx_x = xx+2; gfx_line(xx, y+h, xx, y+h+7); (i>999) ? ( gfx_printf("%dk", .001*i); ) : ( gfx_printf("%d", i); ); xl = xx; ); gfx_set( .2, .2, .2, grid_alpha ); gfx_line(xx, y, xx, y+h); ( i < 100 ) ? (i += 20) : ( i < 1000 ) ? (i += 100) : ( i < 10000 ) ? (i += 1000) : ( i < 100000 ) ? (i += 10000); ); gfx_set( grid_color_r, grid_color_g, grid_color_b, grid_color_a ); N = 8; xx = y; i = 0; gfx_measurestr("8", wsc, xl); loop(N, gfx_set( 1, 1, 1, 1 ); gfx_x = x+w-20; gfx_y = xx-.5*xl; i > 0 ? gfx_printf("-%d", 4.41*i*floorLevel/N); gfx_set( .2, .2, .2, grid_alpha ); gfx_line(x, xx+1, x+w, xx+1); xx += h/N; gfx_set( grid_color_r, grid_color_g, grid_color_b, grid_color_a ); gfx_line(x, xx, x+w, xx); i += 1; ); ); function drawLine(x1, y1, x2, y2) local(dx, dy, x, smooth) global(gfx_x, gfx_y) instance() ( smooth = 1; x1 = floor(x1); x2 = floor(x2); gfx_x=x1; gfx_y=y1; x2 = floor(x2); y2 = floor(y2); smooth ? ( (x2 - x1) > 4 ? ( dx = 1.0/(x2-x1); dy = (y2 - y1)*6*dx; x = 0; loop(x2-x1, x2 = x1 + 1; y2 = y1 + dy * ( x - x*x ); gfx_line(x1-1, y1-1, x2-1, y2-1, 1); gfx_line(x1, y1-1, x2, y2-1, 1); gfx_lineto(x2,y2,1); x1 = x2; y1 = y2; x = x + dx; ); ) : ( gfx_line(x1, y1-1, x2, y2-1, 1); gfx_lineto(x2,y2,1); ); ) : ( gfx_line(x1, y1-1, x2, y2-1, 1); gfx_lineto(x2,y2,1); ); ); bandClickRange = 10; overRange = 10; function isOver(mx, my) instance(x, y, w, h) global(overRange) local() ( ( ( mx >= (x-overRange) ) && ( mx <= (x+w+overRange) ) && ( my >= (y-overRange) ) && ( my <= (y+h+overRange) ) ) ? 1 : 0; ); function clamp(value, mini, maxi) local() global() ( max(min(value,maxi),mini) ); function overRect(x, y, w, h, mx, my) local() global() ( ( (mx > x) && (mx < (x+w) ) && (my > y) && (my < (y+h) ) ) ); function pixelToWindowRatio(x_loc) instance(x, y, w, h, hzoffset) global(fftSize, norm_freq_min) local(lmin, wsc, hfft) ( hfft = 0.5*fftSize; lmin = log(norm_freq_min); wsc = w/log(1+fftSize*0.5-hzoffset); 1-log( (exp((x_loc)/wsc) + hzoffset)/hfft )/lmin ); function fftWindow_processMouse(mouse_x, mouse_y, mouse_cap) instance(x, y, w, h, lx, ly, lcap, cap, thisUI, htime, hint, x_over, dragging, lastLeftClick x1, x2, x3, x4, x5, v1, v2, v3, v4, v5, h1, h2, h3, h4, h5, c1, c2, c3, c4, c5, F1hz, F2hz, F3hz, F4hz ) global(dnoisefloor_freq, dsteps_fft, mouse_wheel, lastUI, fftSize, bandClickRange, cuts, Freq1, Freq2, Freq3, Freq4, bypassBand1, bypassBand2, bypassBand3, bypassBand4, bypassBand5, Drive1, Drive2, Drive3, Drive4, Drive5, mute1, mute2, mute3, mute4, mute5, solo1, solo2, solo3, solo4, solo5, driveMin, driveMax, maxCuts, hinter.updateHintTime, selectedBand, gfx_x, gfx_y, srate, abs_placement) local(dx, dy, over, dys, F1, F2, F3, F4, leftClick, rightClick, doubleClick, cTime, mpos, shift, ctrl, lb) ( over = mouse_x > x && mouse_y > y && mouse_x < (x+w) && mouse_y < (y+h); leftClick = ( mouse_cap & 1 == 1 && lcap & 1 == 0 ); rightClick = ( mouse_cap & 2 == 2 && lcap & 2 == 0 ); doubleClick = 0; shift = ( mouse_cap & 8 ); ctrl = ( mouse_cap & 4 ); leftClick ? ( time_precise(cTime); ( ( cTime - lastLeftClick ) < .25 ) ? ( leftClick = 0; doubleClick = 1; ); lastLeftClick = cTime; ); x_over = -1; abs_placement ? ( F1 = clamp(Freq1, 0, cuts > 1 ? Freq2 : 1); F2 = clamp(Freq2, Freq1, cuts > 2 ? Freq3 : 1); F3 = clamp(Freq3, Freq2, cuts > 3 ? Freq4 : 1); F4 = clamp(Freq4, Freq3, 1.0); ) : ( F1 = Freq1; F2 = Freq1 + Freq2 * (1 - Freq1); F3 = F2 + Freq3 * (1 - F2); F4 = F3 + Freq4 * (1 - F3); ); F1hz = f_trafo(F1); F2hz = f_trafo(F2); F3hz = f_trafo(F3); F4hz = f_trafo(F4); ( cap == 3 && mouse_cap & 1 == 1 ) ? ( dy = mouse_y - ly; dys = 100*dy/h; dragging == 1 ? ( Drive1 -= dys; Drive1 = clamp(Drive1, driveMin, driveMax); ); dragging == 2 ? ( Drive2 -= dys; Drive2 = clamp(Drive2, driveMin, driveMax); ); dragging == 3 ? ( Drive3 -= dys; Drive3 = clamp(Drive3, driveMin, driveMax); ); dragging == 4 ? ( Drive4 -= dys; Drive4 = clamp(Drive4, driveMin, driveMax); ); dragging == 5 ? ( Drive5 -= dys; Drive5 = clamp(Drive5, driveMin, driveMax); ); ) : ( cap == 2 && mouse_cap & 1 == 1 ) ? ( // Dragging a band frequency dx = mouse_x - lx; shift ? dx *= .5; ctrl ? dx *= .01; lb = .02; dragging == 1 ? ( F1 = this.pixelToWindowRatio(x1 - x + dx); abs_placement ? ( slider_automate( Freq1 = clamp( F1, lb, cuts > 1 ? F2 : 1 ) ); ) : ( slider_automate( Freq1 = clamp( F1, lb, 1 ) ); slider_automate( Freq2 = clamp( (F2 - Freq1) / (1-F1), 0, 1 ) ); ); ); dragging == 2 ? ( F2 = this.pixelToWindowRatio(x2 - x + dx); abs_placement ? ( slider_automate( Freq2 = clamp(F2, F1, cuts > 2 ? F3 : 1)); ) : ( slider_automate( Freq2 = clamp( (F2 - Freq1) / (1-Freq1), 0, 1 ) ); F2 = Freq1 + Freq2 * (1 - Freq1); slider_automate( Freq3 = clamp( (F3 - F2) / (1-F2), 0, 1) ); ); ); dragging == 3 ? ( F3 = this.pixelToWindowRatio(x3 - x + dx); abs_placement ? ( slider_automate( Freq3 = clamp( F3, F2, cuts > 3 ? F4 : 1 ) ); ) : ( slider_automate( Freq3 = clamp( (F3 - F2) / (1-F2), 0, 1 ) ); F3 = F2 + Freq3 * (1 - F2); slider_automate( Freq4 = clamp( (F4 - F3) / (1-F3), 0, 1 ) ); ); ); dragging == 4 ? ( F4 = this.pixelToWindowRatio(x4 - x + dx); abs_placement ? ( slider_automate( Freq4 = clamp( F4, F3, 1.0 ) ); ) : ( slider_automate( Freq4 = clamp( (F4 - F3) / (1-F3), 0, 1 ) ); ); ); ) : ( cap == 1 && mouse_cap & 1 == 1 ) ? ( dnoisefloor_freq -= .5*(mouse_y-ly); dnoisefloor_freq > 35 ? dnoisefloor_freq = 35; dnoisefloor_freq < -15 ? dnoisefloor_freq = -15; ) : ( // Not already capped cap = 0; /* Toggle mute/solo */ ( c1.isOver(mouse_x, mouse_y) == 1 ) ? ( c1.over = 1; hinter.updateHintTime("LMB - Toggle mute\nRMB - Toggle solo\nShift + RMB - Select multiple solo bands\nCTRL + LMB - Toggle bypass waveshaper"); leftClick ? ( ctrl ? ( bypassBand1 = 1 - bypassBand1) : ( mute1 = 1-mute1; solo1 ? (mute1 = 1; solo1 = 0; selectedBand = 0; ); ); ); rightClick ? ( solo1 = 1-solo1; selectedBand = 0; ((solo1 == 1) && (shift==0)) ? (solo2=solo3=solo4=solo5=0;); ); ) : ( c2.isOver(mouse_x, mouse_y) == 1 ) && ( cuts > 0 ) ? ( hinter.updateHintTime("LMB - Toggle mute\nRMB - Toggle solo\nShift + RMB - Select multiple solo bands\nCTRL + LMB - Toggle bypass waveshaper"); c2.over = 1; leftClick ? ( ctrl ? ( bypassBand2 = 1 - bypassBand2) : ( mute2 = 1-mute2; solo2 ? (mute2 = 1; solo2 = 0; selectedBand = 1;); ); ); rightClick ? ( solo2 = 1-solo2; selectedBand = 1; ((solo2 == 1) && (shift==0)) ? (solo1=solo3=solo4=solo5=0;); ); ) : ( c3.isOver(mouse_x, mouse_y) == 1 ) && ( cuts > 1 ) ? ( hinter.updateHintTime("LMB - Toggle mute\nRMB - Toggle solo\nShift + RMB - Select multiple solo bands\nCTRL + LMB - Toggle bypass waveshaper"); c3.over = 1; leftClick ? ( ctrl ? ( bypassBand3 = 1 - bypassBand3) : ( mute3 = 1-mute3; solo3 ? (mute3 = 1; solo3 = 0; selectedBand = 2; ); ); ); rightClick ? ( solo3 = 1-solo3; selectedBand = 2; ((solo3 == 1) && (shift==0)) ? (solo1=solo2=solo4=solo5=0;); ); ) : ( c4.isOver(mouse_x, mouse_y) == 1 ) && ( cuts > 2 ) ? ( hinter.updateHintTime("LMB - Toggle mute\nRMB - Toggle solo\nShift + RMB - Select multiple solo bands\nCTRL + LMB - Toggle bypass waveshaper"); c4.over = 1; leftClick ? ( ctrl ? ( bypassBand4 = 1 - bypassBand4) : ( mute4 = 1-mute4; solo4 ? (mute4 = 1; solo4 = 0; selectedBand = 3; ); ); ); rightClick ? ( solo4 = 1-solo4; selectedBand = 3; ((solo4 == 1) && (shift==0)) ? (solo1=solo2=solo3=solo5=0;); ); ) : ( c5.isOver(mouse_x, mouse_y) == 1 ) && ( cuts > 3 ) ? ( hinter.updateHintTime("LMB - Toggle mute\nRMB - Toggle solo\nShift + RMB - Select multiple solo bands\nCTRL + LMB - Toggle bypass waveshaper"); c5.over = 1; leftClick ? ( ctrl ? ( bypassBand5 = 1 - bypassBand5) : ( mute5 = 1-mute5; solo5 ? (mute5 = 1; solo5 = 0; selectedBand = 4; ); ); ); rightClick ? ( solo5 = 1-solo5; selectedBand = 4; ((solo5 == 1) && (shift==0)) ? (solo1=solo2=solo3=solo4=0;); ); /* Drag frequency */ ) : ( v1.isOver(mouse_x, mouse_y) == 1 ) && ( cuts > 0 ) ? ( hinter.updateHintTime("LMB + Drag - Modify crossover frequency\nRMB - Remove frequency cut\nRMB anywhere else - Add frequency cut"); v1.over = 1; leftClick ? ( cap = 2; dragging = 1; ); rightClick ? ( !abs_placement ? ( Freq2 = ( Freq2 * ( 1 - Freq1 ) + ( Freq1 - 0 ) ) / ( 1 - 0 ); ); remBand(0); ); ) : ( v2.isOver(mouse_x, mouse_y) ) && ( cuts > 1 ) ? ( hinter.updateHintTime("LMB + Drag - Modify crossover frequency\nRMB - Remove frequency cut\nRMB anywhere else - Add frequency cut"); v2.over = 1; leftClick ? ( cap = 2; dragging = 2; ); rightClick ? ( !abs_placement ? ( Freq3 = ( Freq3 * ( 1 - Freq2 ) + ( Freq2 - 0 ) ) / ( 1 - 0 ) ); remBand(1); ); ) : ( v3.isOver(mouse_x, mouse_y) ) && ( cuts > 2 ) ? ( hinter.updateHintTime("LMB + Drag - Modify crossover frequency\nRMB - Remove frequency cut\nRMB anywhere else - Add frequency cut"); v3.over = 1; leftClick ? ( cap = 2; dragging = 3; ); rightClick ? ( !abs_placement ? ( Freq4 = ( Freq4 * ( 1 - Freq3 ) + ( Freq3 - 0 ) ) / ( 1 - 0 ) ); remBand(2); ); ) : ( v4.isOver(mouse_x, mouse_y) ) && ( cuts > 3 ) ? ( hinter.updateHintTime("LMB + Drag - Modify crossover frequency\nRMB - Remove frequency cut\nRMB anywhere else - Add frequency cut"); v4.over = 1; leftClick ? ( cap = 2; dragging = 4; ); rightClick ? ( remBand(3); ); ) : ( v5.isOver(mouse_x, mouse_y) ) && ( cuts > 4 ) ? ( hinter.updateHintTime("LMB + Drag - Modify crossover frequency\nRMB - Remove frequency cut\nRMB anywhere else - Add frequency cut"); v5.over = 1; leftClick ? ( cap = 2; dragging = 5; ); rightClick ? ( remBand(4); ); /* Drag drive */ ) : ( h1.isOver(mouse_x, mouse_y) ) ? ( hinter.updateHintTime("LMB + Drag - Modify drive\nDouble LMB - Reset drive to zero\nShift + LMB + Drag - Modify drive while reducing gain"); h1.over = 1; leftClick ? ( cap = 3; dragging = 1; selectedBand = 0; ); doubleClick ? ( Drive1 = 0; selectedBand = 0; ); ) : ( h2.isOver(mouse_x, mouse_y) ) && ( cuts > 0 ) ? ( hinter.updateHintTime("LMB + Drag - Modify drive\nDouble LMB - Reset drive to zero\nShift + LMB + Drag - Modify drive while reducing gain"); h2.over = 1; leftClick ? ( cap = 3; dragging = 2; selectedBand = 1; ); doubleClick ? ( Drive2 = 0; selectedBand = 1; ); ) : ( h3.isOver(mouse_x, mouse_y) ) && ( cuts > 1 ) ? ( hinter.updateHintTime("LMB + Drag - Modify drive\nDouble LMB - Reset drive to zero\nShift + LMB + Drag - Modify drive while reducing gain"); h3.over = 1; leftClick ? ( cap = 3; dragging = 3; selectedBand = 2; ); doubleClick ? ( Drive3 = 0; selectedBand = 2; ); ) : ( h4.isOver(mouse_x, mouse_y) ) && ( cuts > 2 ) ? ( hinter.updateHintTime("LMB + Drag - Modify drive\nDouble LMB - Reset drive to zero\nShift + LMB + Drag - Modify drive while reducing gain"); h4.over = 1; leftClick ? ( cap = 3; dragging = 4; selectedBand = 3; ); doubleClick ? ( Drive4 = 0; selectedBand = 3; ); ) : ( h5.isOver(mouse_x, mouse_y) ) && ( cuts > 3 ) ? ( hinter.updateHintTime("LMB + Drag - Modify drive\nDouble LMB - Reset drive to zero\nShift + LMB + Drag - Modify drive while reducing gain"); h5.over = 1; leftClick ? ( cap = 3; dragging = 5; selectedBand = 4; ); doubleClick ? ( Drive5 = 0; selectedBand = 4; ); /* Handle clicks within the bands (creating a new band or selecting a band) */ ) : ( overRect( h1.x, y, h1.w, h, mouse_x, mouse_y ) ) ? ( leftClick ? selectedBand = 0; rightClick && (cuts < maxCuts) ? ( addBand(0); mpos = this.pixelToWindowRatio(mouse_x); !abs_placement ? ( Freq2 = ( Freq2 * (1-0) + 0 - mpos ) / ( 1 - mpos ); ); Freq1 = mpos; ); ) : ( overRect( h2.x, y, h2.w, h, mouse_x, mouse_y ) && ( cuts > 0 ) ) ? ( leftClick ? selectedBand = 1; rightClick && (cuts < maxCuts) ? ( addBand(1); mpos = this.pixelToWindowRatio(mouse_x); !abs_placement ? ( Freq3 = ( Freq3 * (1-Freq1) + Freq1 - mpos ) / ( 1 - mpos ); Freq2 = ( mpos - Freq1 ) / ( 1 - Freq1 ); ) : ( Freq2 = mpos; ); ); ) : ( overRect( h3.x, y, h3.w, h, mouse_x, mouse_y ) && ( cuts > 1 ) ) ? ( leftClick ? selectedBand = 2; rightClick && (cuts < maxCuts) ? ( addBand(2); mpos = this.pixelToWindowRatio(mouse_x); !abs_placement ? ( F2 = Freq1 + Freq2 * (1 - Freq1); Freq4 = ( Freq4 * (1-F2) + F2 - mpos ) / ( 1 - mpos ); Freq3 = ( mpos - F2 ) / ( 1 - F2 ); ) : ( Freq3 = mpos; ); ); ) : ( overRect( h4.x, y, h4.w, h, mouse_x, mouse_y ) && ( cuts > 2 ) ) ? ( leftClick ? selectedBand = 3; rightClick && (cuts < maxCuts) ? ( addBand(3); mpos = this.pixelToWindowRatio(mouse_x); !abs_placement ? ( F2 = Freq1 + Freq2 * (1 - Freq1); F3 = F2 + Freq3 * (1 - F2); Freq4 = ( mpos - F3 ) / ( 1 - F3 ); ) : ( Freq4 = mpos; ); ); ) : ( overRect( h5.x, y, h5.w, h, mouse_x, mouse_y ) && ( cuts > 3 ) ) ? ( leftClick ? selectedBand = 4; ) : ( cap = 0; ( mouse_cap & 1 == 1 && lcap & 1 == 0 && over ) ? ( cap = 1; ); ); ); mouse_wheel > 0 && over ? lastUI = thisUI; abs(floor(mouse_wheel/240)) > 0 && over ? ( dsteps_fft += floor(mouse_wheel/240); dsteps_fft > 8 ? dsteps_fft = 8; dsteps_fft < -4 ? dsteps_fft = -4; mouse_wheel = 0; ); lx = mouse_x; ly = mouse_y; lcap = mouse_cap; over ); function drawFFT(fill, r, g, b, a) global(scaling, fftSize, gfx_x, gfx_y, gfx_r, gfx_g, gfx_b, gfx_a, srate, gfx_mode, dsteps_fft, FIR1, norm_freq_max) instance(hstep, x, y, w, h, recPtr, window, rStart, rEnd, fftLoc, floorLevel, hzoffset) local(step, copyval, stepsize, buf, i, ixsc, txl, tx, ty, wsc, fill, tx0, lx0, lx, ly, ty2, ly2, fill_slast, fill_slmin, cum, cumelem) ( this.calcFFT(); cum = -10000000; cumelem = 0; i = 0; ixsc = 0; wsc = w/log(fftsize*0.5-hzoffset); fill_slmin = y; buf = fftLoc + fftsize*0.5; stepsize = floor((4+dsteps_fft)*(1+scaling)); copyval = buf[]; loop( stepsize*200, (buf+=1)[] = copyval; ); gfx_r = r; gfx_g = g; gfx_b = b; gfx_a = a; hstep = 0.5 * stepsize; buf = fftLoc; txl = x; lx = x; ly = y - max(buf[2], -floorLevel)*h / floorLevel; step = 1.0 / (2*norm_freq_max); loop(fftsize*0.5+stepsize*200, tx = floor(x + log(ixsc - hzoffset)*wsc); cum = max(cum, tx > 0 ? buf[] : -10000); cumelem += 1; ( (tx != txl) && (tx-txl > stepsize) && tx > x && i && ( tx < (x+w+stepsize) ) ) ? ( ty = y - max(cum, -floorLevel)*h / floorLevel; txl = floor(tx); tx = min(tx, x+w); (fill) ? ( tx0=tx|0; lx0=lx|0; tx0>lx0 ? ( gfx_triangle(lx0-hstep,max(y+h,ly),lx0-hstep,ly,tx0-1-hstep,ty,tx0-1-hstep,max(y+h,ty)); ) : ( tx0 > fill_slast ? ( fill_slast < h ? gfx_line(fill_slast,h,fill_slast,fill_slmin); fill_slmin=h; ) : ( fill_slmin=min(fill_slmin,ty); ); ); fill_slast=tx0; ); (fill==0) ? ( drawLine(lx-hstep, ly, tx-hstep, ty); ); lx=tx; ly=ty; ly2=ty2; cum = -1000000; cumelem = 0; ); buf+=1; i+=1; ixsc+=step; ); ); function drawLineFFT(peakLocation) global(fftSize, gfx_x, gfx_y, gfx_r, gfx_g, gfx_b, gfx_a, srate) instance(x, y, w, h, hzoffset) local(wsc, tx, base, i) ( wsc = w/log(1+fftSize*0.5-hzoffset); base = fftSize*peakLocation; tx = floor(x + log(1.0+base-hzoffset)*wsc); drawLine(tx, y, tx, y+h); ); function updateBands() global(Freq1, Freq2, Freq3, Freq4, Ceil1, Ceil2, Ceil3, Ceil4, Drive1, Drive2, Drive3, Drive4, Drive5, fftSize, driveRange, driveMax, abs_placement, cuts, norm_freq_min) instance(x, y, w, h, hzoffset, x1, x2, x3, x4, x5, /* Frequencies */ y1, y2, y3, y4, y5, /* Drives */ ) local(F1, F2, F3, F4, F5, wsc, hfft, lmin, irange, dmax) ( abs_placement ? ( F1 = clamp(Freq1, 0, cuts > 1 ? Freq2 : 1); F2 = clamp(Freq2, Freq1, cuts > 2 ? Freq3 : 1); F3 = clamp(Freq3, Freq2, cuts > 3 ? Freq4 : 1); F4 = clamp(Freq4, Freq3, 1.0); ) : ( F1 = Freq1; F2 = Freq1 + Freq2 * (1 - Freq1); F3 = F2 + Freq3 * (1 - F2); F4 = F3 + Freq4 * (1 - F3); ); hfft = 0.5*fftSize; lmin = log(norm_freq_min); wsc = w/log(1+fftSize*0.5-hzoffset); x1 = ( x + log( hfft * exp( (1-F1) * lmin ) - hzoffset ) * wsc ); x2 = ( x + log( hfft * exp( (1-F2) * lmin ) - hzoffset ) * wsc ); x3 = ( x + log( hfft * exp( (1-F3) * lmin ) - hzoffset ) * wsc ); x4 = ( x + log( hfft * exp( (1-F4) * lmin ) - hzoffset ) * wsc ); x5 = ( x + log( hfft * exp( lmin ) - hzoffset ) * wsc ); iRange = 1/driveRange; dmax = iRange * driveMax; y1 = y+h*(dmax-iRange*Drive1); y2 = y+h*(dmax-iRange*Drive2); y3 = y+h*(dmax-iRange*Drive3); y4 = y+h*(dmax-iRange*Drive4); y5 = y+h*(dmax-iRange*Drive5); ); lineR = 1; lineG = .9; lineB = 1.0; lineA = .95; lineHighlightR = 0; lineHighlightG = .7; lineHighlightB = .5; function drawGroupRect(x, y, w, h, r, g, b, a) local() instance() global() ( gfx_set(r, g, b, a); gfx_line(x, y, x+w, y); gfx_line(x, y, x, y+h); gfx_line(x+w, y, x+w, y+h); gfx_line(x, y+h, x+w, y+h); gfx_set(r, g, b, a*.5); gfx_line(x, y+h+1, x+w, y+h+1); gfx_line(x+w+1, y, x+w+1, y+h); ); function initBuffer(scopebuffer_in, scopebuffermax_in) local() global() instance(scopeptr, scopebuffermax, scopebuffer) ( scopebuffer = scopebuffer_in; scopebuffermax = scopebuffermax_in; scopeptr < scopebuffer ? ( scopeptr = scopebuffer ) : ( scopeptr > scopebuffermax ) ? scopeptr = scopebuffer ); function setOffset(offset) local() global() instance(scopeptr, readptr, scopebuffermax, scopebuffer, frac) ( readptr = scopeptr; frac = offset - floor(offset); readptr -= floor(offset); readptr < scopebuffer ? readptr += (scopebuffermax-scopebuffer+1); ); function readBuffer() local(c1, c2) global() instance(readptr, scopebuffermax, scopebuffer, frac) ( c1 = readptr[]; readptr += 1; readptr > scopebuffermax ? readptr = scopebuffer; c2 = readptr[]; c2 * (1.0-frac) + c1 * frac ); function updateBuffer(M) local() global() instance(scopeptr, scopebuffermax, scopebuffer) ( scopeptr[] = M; scopeptr += 1; scopeptr > scopebuffermax ? scopeptr = scopebuffer; M ); function clearBuffer() local() global(MAXBUFFERSIZE) instance(scopeptr, scopebuffermax, scopebuffer) ( memset( scopebuffer, 0, MAXBUFFERSIZE ); scopeptr = scopebuffer; ); function fancyLineV(x1, y1, x2, y2) local(dx) global(globalTime, lineR, lineG, lineB, lineA, lineHighlightR, lineHighlightG, lineHighlightB) instance(x, y, w, h, over) ( dx = 5; x = x1 - .5*dx; y = y1; w = dx; h = y2-y1; over ? ( gfx_set(lineHighlightR,lineHighlightG,lineHighlightB,.1); gfx_rect(x-2, y, w+5, h); gfx_set(lineHighlightR,lineHighlightG,lineHighlightB,.1); gfx_rect(x-4, y, w+9, h); gfx_set(lineHighlightR,lineHighlightG,lineHighlightB,.05); gfx_rect(x-7, y, w+15, h); gfx_set(lineHighlightR,lineHighlightG,lineHighlightB,.03); gfx_rect(x-10, y, w+20, h); ); gfx_set(.2*lineR, .2*lineG, .2*lineB, lineA); gfx_rect(x, y, w, h); gfx_set(.5*lineR, .5*lineG, .5*lineB, lineA); dx = 3; gfx_rect(x1 - .5*dx, y1, dx, y2-y1); gfx_set(.7*lineR, .7*lineG, .7*lineB, lineA); dx = 2; gfx_rect(x1 - .5*dx, y1, dx, y2-y1); gfx_set(lineR, lineG, lineB, lineA); dx = 1; gfx_rect(x1 - .5*dx, y1, dx, y2-y1); over ? ( gfx_set(1,1,1,.4*abs(sin(2*globalTime))); gfx_rect(x, y, w, h); ); over = 0; ); function fancyLineH(x1, y1, x2, y2) local(dy) global(globalTime, lineR, lineG, lineB, lineA, lineHighlightR, lineHighlightG, lineHighlightB) instance(x, y, w, h, over) ( dy = 5; x = x1; y = y1 - .5*dy; w = x2-x1; h = dy; over ? ( gfx_set(lineHighlightR,lineHighlightG,lineHighlightB,.1); gfx_rect(x, y-2, w, h+5); gfx_set(lineHighlightR,lineHighlightG,lineHighlightB,.1); gfx_rect(x, y-4, w, h+9); gfx_set(lineHighlightR,lineHighlightG,lineHighlightB,.05); gfx_rect(x, y-7, w, h+15); gfx_set(lineHighlightR,lineHighlightG,lineHighlightB,.03); gfx_rect(x, y-10, w, h+20); ); gfx_set(.2*lineR, .2*lineG, .2*lineB, lineA); gfx_rect(x, y, w, h); gfx_set(.5*lineR, .5*lineG, .5*lineB, lineA); dy = 3; gfx_rect(x1, y1 - .5*dy, x2-x1, dy ); gfx_set(.7*lineR, .7*lineG, .7*lineB, lineA); dy = 2; gfx_rect(x1, y1 - .5*dy, x2-x1, dy ); gfx_set(lineR, lineG, lineB, lineA); dy = 1; gfx_rect(x1, y1 - .5*dy, x2-x1, dy ); over ? ( gfx_set(1,1,1,.7*abs(sin(2*globalTime))); gfx_rect(x, y, w, h); ); over = 0; ); function hzLabel(dy, label) local(lww, lhh, cy, dy2) global(gfx_x, gfx_y, hzLabelW, hzLabelH, lineR, lineG, lineB, lineA) instance(x, y, w, h, over) ( cy = y + dy*h; gfx_measurestr("888888", hzLabelW, hzLabelH); gfx_set(.5*lineR, .5*lineG, .5*lineB, lineA); dy2 = 3; gfx_rect(x-.5*hzLabelW-.5*dy2, cy-.5*dy2, hzLabelW+dy2, hzLabelH+dy2); gfx_set(.7*lineR, .7*lineG, .7*lineB, lineA); dy2 = 2; gfx_rect(x-.5*hzLabelW-.5*dy2, cy-.5*dy2, hzLabelW+dy2, hzLabelH+dy2); gfx_set(lineR, lineG, lineB, lineA); dy2 = 1; gfx_rect(x-.5*hzLabelW-.5*dy2, cy-.5*dy2, hzLabelW+dy2, hzLabelH+dy2); gfx_set( 1, 1, 1, .8 ); gfx_rect(x-.5*hzLabelW-1, cy-1, hzLabelW+2, hzLabelH+2); gfx_set( 0, 0, 0, 1 ); gfx_rect(x-.5*hzLabelW, cy, hzLabelW, hzLabelH); sprintf(19, "%d", label); gfx_measurestr(19, lww, lhh); gfx_set( 1, 1, 1, 1 ); gfx_x = x - .5 *lww; gfx_y = y + dy*h; gfx_printf(19, label); ); function drawBands() global(Cuts, stest, mute1, mute2, mute3, mute4, mute5, solo1, solo2, solo3, solo4, solo5, bypassBand1, bypassBand2, bypassBand3, bypassBand4, bypassBand5, selectedBand ) instance(xc, dx, x, y, w, h, x1, x2, x3, x4, x5, // Band frequency screen positions y1, y2, y3, y4, y5, // Band drive positions v1, v2, v3, v4, v5, // Frequency handles h1, h2, h3, h4, h5, // Drive handles c1, c2, c3, c4, c5, // Circle handles F1hz, F2hz, F3hz, F4hz, F5hz, // Frequencies x_over) local() ( xc = x; dx = 5; while(xc 0) ? ( gfx_set(1,1,1,1); v1.fancyLineV(x1, y, x1, y+h); v1.hzLabel(.8, F1hz); ); (Cuts > 1) ? ( gfx_set(1,1,1,1); v2.fancyLineV(x2, y, x2, y+h); v2.hzLabel(.8, F2hz); ); (Cuts > 2) ? ( gfx_set(1,1,1,1); v3.fancyLineV(x3, y, x3, y+h); v3.hzLabel(.8, F3hz); ); (Cuts > 3) ? ( gfx_set(1,1,1,1); v4.fancyLineV(x4, y, x4, y+h); v4.hzLabel(.8, F4hz); ); (Cuts > 4) ? ( gfx_set(1,1,1,1); v5.fancyLineV(x5, y, x5, y+h); v5.hzLabel(.8, F5hz); ); gfx_rect(x_over-3, y, 5, h); ( Cuts == 0 ) ? ( h1.fancyLineH(x, y1, x+w-2, y1); c1.fancyCircle(x + .5*w, y1, 0, mute1, solo1, bypassBand1); ) : ( Cuts == 1 ) ? ( h1.fancyLineH(x, y1, x1-2, y1); c1.fancyCircle(.5*x + .5*x1, y1, 0, mute1, solo1, bypassBand1); h2.fancyLineH(x1, y2, x+w-2, y2); c2.fancyCircle(x1 + .5*(w-x1), y2, 0, mute2, solo2, bypassBand2); ) : ( Cuts == 2 ) ? ( h1.fancyLineH(x, y1, x1-2, y1); c1.fancyCircle(.5*x + .5*x1, y1, 0, mute1, solo1, bypassBand1); h2.fancyLineH(x1, y2, x2-2, y2); c2.fancyCircle(x1 + .5*(x2-x1), y2, 0, mute2, solo2, bypassBand2); h3.fancyLineH(x2, y3, x+w-2, y3); c3.fancyCircle(x2 + .5*(w-x2), y3, 0, mute3, solo3, bypassBand3); ) : ( Cuts == 3 ) ? ( h1.fancyLineH(x, y1, x1-2, y1); c1.fancyCircle(.5*x + .5*x1, y1, 0, mute1, solo1, bypassBand1); h2.fancyLineH(x1, y2, x2-2, y2); c2.fancyCircle(x1 + .5*(x2-x1), y2, 0, mute2, solo2, bypassBand2); h3.fancyLineH(x2, y3, x3-2, y3); c3.fancyCircle(x2 + .5*(x3-x2), y3, 0, mute3, solo3, bypassBand3); h4.fancyLineH(x3, y4, x+w-2, y4); c4.fancyCircle(x3 + .5*(w-x3), y4, 0, mute4, solo4, bypassBand4); ) : ( Cuts == 4 ) ? ( h1.fancyLineH(x, y1, x1-2, y1); c1.fancyCircle(.5*x + .5*x1, y1, 0, mute1, solo1, bypassBand1); h2.fancyLineH(x1, y2, x2-2, y2); c2.fancyCircle(x1 + .5*(x2-x1), y2, 0, mute2, solo2, bypassBand2); h3.fancyLineH(x2, y3, x3-2, y3); c3.fancyCircle(x2 + .5*(x3-x2), y3, 0, mute3, solo3, bypassBand3); h4.fancyLineH(x3, y4, x4-2, y4); c4.fancyCircle(x3 + .5*(x4-x3), y4, 0, mute4, solo4, bypassBand4); h5.fancyLineH(x4, y5, x+w-2, y5); c5.fancyCircle(x4 + .5*(w-x4), y5, 0, mute5, solo5, bypassBand5); ); selectedBand == 0 ? gfx_muladdrect( h1.x, y, h1.w, h, 1, 1, 1, .6, .05, .05, .105 ); selectedBand == 1 ? gfx_muladdrect( h2.x, y, h2.w, h, 1, 1, 1, .6, .05, .05, .105 ); selectedBand == 2 ? gfx_muladdrect( h3.x, y, h3.w, h, 1, 1, 1, .6, .05, .05, .105 ); selectedBand == 3 ? gfx_muladdrect( h4.x, y, h4.w, h, 1, 1, 1, .6, .05, .05, .105 ); selectedBand == 4 ? gfx_muladdrect( h5.x, y, h5.w, h, 1, 1, 1, .6, .05, .05, .105 ); ); function initSpectrum(memoryOffset, fftSize, srate) global(newUI) instance(recPtr, window, rStart, rEnd, fftLoc, hzoffset, thisUI, slope) local() ( slope = 3; hzoffset = 10*fftSize/srate; window = memoryOffset + fftSize + (fftSize*0.5 - 1); // Start and end of the buffer rStart = memoryOffset; // rpos rEnd = window; // hsize fftLoc = window + (fftSize*0.5 + 1); // fftw recPtr = rStart; thisUI = newUI+=1; this.initWindow(3) ); function resetBandMeter() local() global() instance(in, pre, post) ( in = pre = post = 0; ); function processBands() local(BW2) global( Drive1, Drive2, Drive3, Drive4, Drive5 play1, play2, play3, play4, play5 ) instance(b1, b2, b3, b4, b5, F1, F2, F3, F4, F5, g1, g2, g3, g4, g5, lg1, lg2, lg3, lg4, lg5 ) ( ( lg1 != Drive1 ) ? ( g1 = (10^(0.05*Drive1)); lg1 = Drive1; ); ( lg2 != Drive2 ) ? ( g2 = (10^(0.05*Drive2)); lg2 = Drive2; ); ( lg3 != Drive3 ) ? ( g3 = (10^(0.05*Drive3)); lg3 = Drive3; ); ( lg4 != Drive4 ) ? ( g4 = (10^(0.05*Drive4)); lg4 = Drive4; ); ( lg5 != Drive5 ) ? ( g5 = (10^(0.05*Drive5)); lg5 = Drive5; ); b1.l = b2.l = b3.l = b4.l = b5.l = 0; b1.r = b2.r = b3.r = b4.r = b5.r = 0; play1 ? ( b1.l = g1*b1.inL; b1.r = g1*b1.inR; ); play2 ? ( b2.l = g2*b2.inL; b2.r = g2*b2.inR; ); play3 ? ( b3.l = g3*b3.inL; b3.r = g3*b3.inR; ); play4 ? ( b4.l = g4*b4.inL; b4.r = g4*b4.inR; ); play5 ? ( b5.l = g5*b5.inL; b5.r = g5*b5.inR; ); ); // MEMORY LAYOUT fftSize = 2*8192; bufferDist = 65536; audioBufIn = 0; audioBufOut = bufferDist; FIR1 = 2*bufferDist; FIR2 = 3*bufferDist; FIR3 = 4*bufferDist; FIR4 = 5*bufferDist; FIR5 = 6*bufferDist; F1Block1 = 7*bufferDist; F1Block2 = 8*bufferDist; F2Block1 = 9*bufferDist; F2Block2 = 10*bufferDist; F3Block1 = 11*bufferDist; F3Block2 = 12*bufferDist; F4Block1 = 13*bufferDist; F4Block2 = 14*bufferDist; F5Block1 = 15*bufferDist; F5Block2 = 16*bufferDist; specBufferLoc = 17*bufferDist; specBuffer.initBuffer(specBufferLoc, specBufferLoc+bufferDist-4); // INITIALIZATION spectrumIn.initSpectrum(audioBufIn, fftSize, srate); spectrumOut.initSpectrum(audioBufOut, fftSize, srate); @serialize file_var(0, mute1); file_var(0, mute2); file_var(0, mute3); file_var(0, mute4); file_var(0, mute5); file_var(0, solo1); file_var(0, solo2); file_var(0, solo3); file_var(0, solo4); file_var(0, solo5); file_var(0, FIR); file_var(0, gmem_group); @slider @block (last_gmem_group != gmem_group) || (last_sent_cuts != Cuts) || (last_sent_f1 != Freq1) || (last_sent_f2 != Freq2) || (last_sent_f3 != Freq3) || (last_sent_f4 != Freq4) || (last_sent_slope != band_mode) || (last_sent_abs_placement != abs_placement) || (last_sent_absolute_frequencies != absolute_frequencies) ? ( last_gmem_group = gmem_group; last_sent_cuts = Cuts; last_sent_f1 = Freq1; last_sent_f2 = Freq2; last_sent_f3 = Freq3; last_sent_f4 = Freq4; last_sent_abs_placement = abs_placement; last_sent_slope = band_mode; last_sent_absolute_frequencies = absolute_frequencies; (gmem_group > 0) ? ( gm_ptr = 9 * (gmem_group - 1); gmem[gm_ptr] = time_precise(); /* Identity */ gmem[gm_ptr + 1] = last_sent_cuts; gmem[gm_ptr + 2] = last_sent_f1; gmem[gm_ptr + 3] = last_sent_f2; gmem[gm_ptr + 4] = last_sent_f3; gmem[gm_ptr + 5] = last_sent_f4; gmem[gm_ptr + 6] = last_sent_abs_placement; gmem[gm_ptr + 7] = last_sent_slope; gmem[gm_ptr + 8] = last_sent_absolute_frequencies; ); ); set_scaling_behaviour(); !mute1 ? play1 = 1 : play1 = 0; !mute2 ? play2 = 1 : play2 = 0; !mute3 ? play3 = 1 : play3 = 0; !mute4 ? play4 = 1 : play4 = 0; !mute5 ? play5 = 1 : play5 = 0; ( solo1 || solo2 || solo3 || solo4 || solo5 ) ? ( play1 = play2 = play3 = play4 = play5 = 0; ); solo1 ? play1 = 1; solo2 ? play2 = 1; solo3 ? play3 = 1; solo4 ? play4 = 1; solo5 ? play5 = 1; (FIR) ? ( chunkSize = (fftCalcSize - firSize - 1); newPDC = chunkSize + 0.5*firSize; ) : ( newPDC = 0; ); (pdc_delay != newPDC) ? ( pdc_top_ch = 10; pdc_bot_ch = 0; pdc_delay = newPDC; ); @sample function init_LR2(freq) global(srate, norm_freq_max, norm_freq_min, band_mode) local(f0, ct, st, div) instance(k, a1, a2, a3) ( f0 = norm_freq_max * $pi * exp( (1.0 - freq) * log(norm_freq_min) ); k = band_mode == 0 ? sqrt(2) : 2; /* Note the different peak resonance because we're combining 2 SVFs into the Linkwitz-Riley structure */ ct = cos(f0); st = sin(f0); div = 1.0 / (1.0 + k * st * ct); a1 = ct * ct * div; a2 = st * ct * div; a3 = st * st * div; ); function reset_LR() global() local() instance(ic1eq, ic2eq, ic3eq, ic4eq, ic5eq, ic6eq, k, a1, a2, a3) ( ic1eq = ic2eq = 0; ); function eval_LRLP(v0) global() local(v1, v2, v3) instance(ic3eq, ic4eq, a1, a2, a3) ( v3 = v0 - ic4eq; v1 = a1 * ic3eq + a2 * v3; v2 = ic4eq + a2 * ic3eq + a3 * v3; ic3eq = 2*v1 - ic3eq; ic4eq = 2*v2 - ic4eq; v2 ); function eval_LRAP(v0) global() local(v1, v2, v3) instance(ic5eq, ic6eq, k, a1, a2, a3) ( v3 = v0 - ic6eq; v1 = a1 * ic5eq + a2 * v3; v2 = ic6eq + a2 * ic5eq + a3 * v3; ic5eq = 2*v1 - ic5eq; ic6eq = 2*v2 - ic6eq; v0 - 2*k*v1 ); function eval_LR2(v0) global() local(v1, v2, v3, all) instance(ic1eq, ic2eq, k, a1, a2, a3, hp, lp) ( v3 = v0 - ic2eq; v1 = a1 * ic1eq + a2 * v3; v2 = ic2eq + a2 * ic1eq + a3 * v3; ic1eq = 2*v1 - ic1eq; ic2eq = 2*v2 - ic2eq; all = v0 - 2 * k * v1; lp = this.eval_LRLP(v2); hp = all - lp; ); function eval_LR1(v0) global() local(v1, v2, v3) instance(ic1eq, ic2eq, k, a1, a2, a3, hp, lp) ( v3 = v0 - ic2eq; v1 = a1 * ic1eq + a2 * v3; v2 = ic2eq + a2 * ic1eq + a3 * v3; ic1eq = 2*v1 - ic1eq; ic2eq = 2*v2 - ic2eq; lp = v2; hp = v0 - k * v1 - lp; ); function init_AP1(freq) global(srate, norm_freq_min, norm_freq_max) instance(k) local(f0, st) ( f0 = norm_freq_max * $pi * exp((1 - freq) * log(norm_freq_min)); st = sin(f0); k = st / (st + cos(f0)); ); function eval_AP1(v0) global() instance(k, state) local(v, y) ( v = (v0 - state) * k; y = v + state; state = y + v; y - (v0 - y) ); function resetBands() local() global() instance( b1, b2, b3, b4, b5 ) ( b1.r = b1.l = b2.r = b2.l = b3.r = b3.l = b4.r = b4.l = b5.l = 0; ); function resetFilters() global() local() instance( LF1, LF2, LF3, LF4 /* Lowpass left */ LA_F2, /* Allpass frequency 2 left */ LA_F3, LA_F3_2, /* Allpass frequency 3 left */ LA_F4, LA_F4_2, LA_F4_3, /* Allpass frequency 4 left */ RF1, RF2, RF3, RF4 /* Lowpass right */ RA_F2, /* Allpass frequency 2 right */ RA_F3, RA_F3_2, /* Allpass frequency 3 right */ RA_F4, RA_F4_2, RA_F4_3, /* Allpass frequency 4 right */ ) ( // LR2 => ic1eq, ic2eq, ic3eq, ic4eq // LPLP => ic3eq, ic4eq // LRAP => ic5eq, ic6eq LF1.ic1eq = LF1.ic2eq = LF1.ic3eq = LF1.ic4eq = 0; LF2.ic1eq = LF2.ic2eq = LF2.ic3eq = LF2.ic4eq = 0; LF3.ic1eq = LF3.ic2eq = LF3.ic3eq = LF3.ic4eq = 0; LF4.ic1eq = LF4.ic2eq = LF4.ic3eq = LF4.ic4eq = 0; RF1.ic1eq = RF1.ic2eq = RF1.ic3eq = RF1.ic4eq = 0; RF2.ic1eq = RF2.ic2eq = RF2.ic3eq = RF2.ic4eq = 0; RF3.ic1eq = RF3.ic2eq = RF3.ic3eq = RF3.ic4eq = 0; RF4.ic1eq = RF4.ic2eq = RF4.ic3eq = RF4.ic4eq = 0; LA_F2.state = LA_F3.state = LA_F4.state = 0; RA_F2.state = RA_F3.state = RA_F4.state = 0; LA_F3_2.state = LA_F4_2.state = LA_F4_3.state = 0; RA_F3_2.state = RA_F4_2.state = RA_F4_3.state = 0; LA_F4.ic5eq = LA_F4.ic6eq = LA_F3.ic5eq = LA_F3.ic6eq = LA_F2.ic5eq = LA_F2.ic6eq = 0; RA_F4.ic5eq = RA_F4.ic6eq = RA_F3.ic5eq = RA_F3.ic6eq = RA_F2.ic5eq = RA_F2.ic6eq = 0; LA_F4_2.ic5eq = LA_F4_2.ic6eq = LA_F3_2.ic5eq = LA_F3_2.ic6eq = LA_F4_3.ic5eq = LA_F4_3.ic6eq = 0; RA_F4_2.ic5eq = RA_F4_2.ic6eq = RA_F3_2.ic5eq = RA_F3_2.ic6eq = RA_F4_3.ic5eq = RA_F4_3.ic6eq = 0; ); // Make sure the delay is the same on all bands (identical number of filters) function splitBands(sl, sr) local() global(Cuts, FIR, band_mode, lband_mode) instance( LF1, LF2, LF3, LF4 /* Lowpass left */ LA_F2, /* Allpass frequency 2 left */ LA_F3, LA_F3_2, /* Allpass frequency 3 left */ LA_F4, LA_F4_2, LA_F4_3, /* Allpass frequency 4 left */ RF1, RF2, RF3, RF4 /* Lowpass right */ RA_F2, /* Allpass frequency 2 right */ RA_F3, RA_F3_2, /* Allpass frequency 3 right */ RA_F4, RA_F4_2, RA_F4_3, /* Allpass frequency 4 right */ b1, b2, b3, b4, b5, lcuts ) ( ( lcuts != cuts ) ? ( lcuts = cuts; this.resetBands(); ); band_mode == 0 ? ( // 4p filters (cuts==0) ? ( b1.inL = sl; b1.inR = sr; ) : (cuts==1) ? ( LF1.eval_LR2(sl); b1.inL = LF1.lp; b2.inL = LF1.hp; RF1.eval_LR2(sr); b1.inR = RF1.lp; b2.inR = RF1.hp; ) : (cuts==2) ? ( LF1.eval_LR2(sl); b1.inL = LA_F2.eval_LRAP(LF1.lp); LF2.eval_LR2(LF1.hp); b2.inL = LF2.lp; b3.inL = LF2.hp; RF1.eval_LR2(sr); b1.inR = RA_F2.eval_LRAP(RF1.lp); RF2.eval_LR2(RF1.hp); b2.inR = RF2.lp; b3.inR = RF2.hp; ) : (cuts == 3) ? ( LF1.eval_LR2(sl); b1.inL = LA_F3.eval_LRAP(LA_F2.eval_LRAP(LF1.lp)); LF2.eval_LR2(LF1.hp); b2.inL = LA_F3_2.eval_LRAP(LF2.lp); LF3.eval_LR2(LF2.hp); b3.inL = LF3.lp; b4.inL = LF3.hp; RF1.eval_LR2(sr); b1.inR = RA_F3.eval_LRAP(RA_F2.eval_LRAP(RF1.lp)); RF2.eval_LR2(RF1.hp); b2.inR = RA_F3_2.eval_LRAP(RF2.lp); RF3.eval_LR2(RF2.hp); b3.inR = RF3.lp; b4.inR = RF3.hp; ) : (cuts == 4) ? ( LF1.eval_LR2(sl); b1.inL = LA_F4.eval_LRAP(LA_F3.eval_LRAP(LA_F2.eval_LRAP(LF1.lp))); LF2.eval_LR2(LF1.hp); b2.inL = LA_F4_2.eval_LRAP(LA_F3_2.eval_LRAP(LF2.lp)); LF3.eval_LR2(LF2.hp); b3.inL = LA_F4_3.eval_LRAP(LF3.lp); LF4.eval_LR2(LF3.hp); b4.inL = LF4.lp; b5.inL = LF4.hp; RF1.eval_LR2(sr); b1.inR = RA_F4.eval_LRAP(RA_F3.eval_LRAP(RA_F2.eval_LRAP(RF1.lp))); RF2.eval_LR2(RF1.hp); b2.inR = RA_F4_2.eval_LRAP(RA_F3_2.eval_LRAP(RF2.lp)); RF3.eval_LR2(RF2.hp); b3.inR = RA_F4_3.eval_LRAP(RF3.lp); RF4.eval_LR2(RF3.hp); b4.inR = RF4.lp; b5.inR = RF4.hp; ); ) : ( // 2p filters (cuts==0) ? ( b1.inL = sl; b1.inR = sr; ) : (cuts==1) ? ( LF1.eval_LR1(sl); b1.inL = LF1.lp; b2.inL = - LF1.hp; RF1.eval_LR1(sr); b1.inR = RF1.lp; b2.inR = - RF1.hp; ) : (cuts==2) ? ( LF1.eval_LR1(sl); b1.inL = LA_F2.eval_AP1(LF1.lp); LF2.eval_LR1(LF1.hp); b2.inL = -LF2.lp; b3.inL = LF2.hp; RF1.eval_LR1(sr); b1.inR = RA_F2.eval_AP1(RF1.lp); RF2.eval_LR1(RF1.hp); b2.inR = -RF2.lp; b3.inR = RF2.hp; ) : (cuts == 3) ? ( LF1.eval_LR1(sl); b1.inL = LA_F3.eval_AP1(LA_F2.eval_AP1(LF1.lp)); LF2.eval_LR1(LF1.hp); b2.inL = - LA_F3_2.eval_AP1(LF2.lp); LF3.eval_LR1(LF2.hp); b3.inL = LF3.lp; b4.inL = - LF3.hp; RF1.eval_LR1(sr); b1.inR = RA_F3.eval_AP1(RA_F2.eval_AP1(RF1.lp)); RF2.eval_LR1(RF1.hp); b2.inR = - RA_F3_2.eval_AP1(RF2.lp); RF3.eval_LR1(RF2.hp); b3.inR = RF3.lp; b4.inR = - RF3.hp; ) : (cuts == 4) ? ( LF1.eval_LR1(sl); b1.inL = LA_F4.eval_AP1(LA_F3.eval_AP1(LA_F2.eval_AP1(LF1.lp))); LF2.eval_LR1(LF1.hp); b2.inL = - LA_F4_2.eval_AP1(LA_F3_2.eval_AP1(LF2.lp)); LF3.eval_LR1(LF2.hp); b3.inL = LA_F4_3.eval_AP1(LF3.lp); LF4.eval_LR1(- LF3.hp); b4.inL = LF4.lp; b5.inL = - LF4.hp; RF1.eval_LR1(sr); b1.inR = RA_F4.eval_AP1(RA_F3.eval_AP1(RA_F2.eval_AP1(RF1.lp))); RF2.eval_LR1(RF1.hp); b2.inR = - RA_F4_2.eval_AP1(RA_F3_2.eval_AP1(RF2.lp)); RF3.eval_LR1(RF2.hp); b3.inR = RA_F4_3.eval_AP1(RF3.lp); RF4.eval_LR1(- RF3.hp); b4.inR = RF4.lp; b5.inR = - RF4.hp; ); ); ); // Convert to linear phase function linearPhase(fft_mem, fftCalcSize) local(fwd, r1, i1, mag1, flip, i, w) global() instance() ( fwd = fft_mem; /* Making the filter linear phase involves both removing the phase; but then also shifting the impulse response to overlap with our chunk. If we just remove the phase, we'd get an impulse response like this: \____/ The goal is to shift it to the middle of the FIR section. Considering that the FIR section is at .25 of the spectrum, we have to shift it further than we normally would (a step of 1.5 * pi rather than pi. */ flip = 1; i = 0; loop(fftCalcSize, r1 = fwd[]; i1 = fwd[1]; mag1 = sqrt(sqr(r1) + sqr(i1)); fwd[] = mag1 * cos($pi*i); fwd[1] = mag1 * sin($pi*i); i += 1.5; fwd += 2; ); /*i = 0; loop(1, fft_mem[2*i] = 0; fft_mem[2*fftCalcSize-2*i] = 0; i = i + 1; );*/ ); function updateCoeffs() local(fptr1, fptr2, fptr3, fptr4, fptr5, middle, i, M) global(Cuts, Freq1, Freq2, Freq3, Freq4, FIR, FIR_quality, FIR1, FIR2, FIR3, FIR4, FIR5, bufferDist, firSize, fftCalcSize, band_mode, lband_mode, abs_placement, set_scaling_behaviour, absolute_frequencies) instance( F1, F2, F3, F4 LF1, LF2, LF3, LF4 /* Lowpass left */ LA_F2, /* Allpass frequency 2 left */ LA_F3, LA_F3_2, /* Allpass frequency 3 left */ LA_F4, LA_F4_2, LA_F4_3, /* Allpass frequency 4 left */ RF1, RF2, RF3, RF4 /* Lowpass right */ RA_F2, /* Allpass frequency 2 right */ RA_F3, RA_F3_2, /* Allpass frequency 3 right */ RA_F4, RA_F4_2, RA_F4_3, /* Allpass frequency 4 right */ lcuts, lFIR, lFIR_quality, lF1, lF2, lF3, lF4, labs_placement, labs_freq, updateRequired, ) ( updateRequired = 0; updateRequired = ( ( labs_freq != absolute_frequencies ) || ( labs_placement != abs_placement ) || ( lF1 != Freq1 ) || ( lF2 != Freq2 ) || ( lF3 != Freq3 ) || ( lF4 != Freq4 ) || ( cuts != lcuts ) || ( FIR != lFIR ) || ( FIR_quality != lFIR_quality ) || ( band_mode != lband_mode ) ); updateRequired ? ( // Make sure that if we are going to bother updating the coefficients, // we have the correct global filter scaling. set_scaling_behaviour(); M = 4; (FIR_quality > 0) ? M *= 2; (FIR_quality > 1) ? M *= 2; firSize = 512*M; fftCalcSize = 1024*M; lband_mode = band_mode; labs_placement = abs_placement; labs_freq = absolute_frequencies; lF1 = Freq1; lF2 = Freq2; lF3 = Freq3; lF4 = Freq4; lFIR = FIR; lFIR_quality = FIR_quality; lcuts = cuts; abs_placement ? ( F1 = clamp(Freq1, 0, cuts > 1 ? Freq2 : 1); F2 = clamp(Freq2, Freq1, cuts > 2 ? Freq3 : 1); F3 = clamp(Freq3, Freq2, cuts > 3 ? Freq4 : 1); F4 = clamp(Freq4, Freq3, 1.0); ) : ( F1 = Freq1; F2 = Freq1 + Freq2 * (1 - Freq1); F3 = F2 + Freq3 * (1 - F2); F4 = F3 + Freq4 * (1 - F3); ); (cuts==1) ? ( LF1.init_LR2(Freq1); RF1.k = LF1.k; RF1.a1 = LF1.a1; RF1.a2 = LF1.a2; RF1.a3 = LF1.a3; ) : (cuts==2) ? ( LF1.init_LR2(Freq1); RF1.k = LF1.k; RF1.a1 = LF1.a1; RF1.a2 = LF1.a2; RF1.a3 = LF1.a3; LF2.init_LR2(F2); RF2.k = LF2.k; RF2.a1 = LF2.a1; RF2.a2 = LF2.a2; RF2.a3 = LF2.a3; (band_mode == 0) ? ( LA_F2.init_LR2(F2); ) : ( LA_F2.init_AP1(F2); ); RA_F2.k = LA_F2.k; RA_F2.a1 = LA_F2.a1; RA_F2.a2 = LA_F2.a2; RA_F2.a3 = LA_F2.a3; ) : (cuts == 3) ? ( LF1.init_LR2(Freq1); RF1.k = LF1.k; RF1.a1 = LF1.a1; RF1.a2 = LF1.a2; RF1.a3 = LF1.a3; LF2.init_LR2(F2); RF2.k = LF2.k; RF2.a1 = LF2.a1; RF2.a2 = LF2.a2; RF2.a3 = LF2.a3; LF3.init_LR2(F3); RF3.k = LF3.k; RF3.a1 = LF3.a1; RF3.a2 = LF3.a2; RF3.a3 = LF3.a3; // All-passes for maintaining the phase relation between the bands (band_mode == 0) ? ( LA_F2.init_LR2(F2); LA_F3.init_LR2(F3); ) : ( LA_F2.init_AP1(F2); LA_F3.init_AP1(F3); ); RA_F3.k = RA_F3_2.k = LA_F3_2.k = LA_F3.k; RA_F3.a1 = RA_F3_2.a1 = LA_F3_2.a1 = LA_F3.a1; RA_F3.a2 = RA_F3_2.a2 = LA_F3_2.a2 = LA_F3.a2; RA_F3.a3 = RA_F3_2.a3 = LA_F3_2.a3 = LA_F3.a3; RA_F2.k = LA_F2.k; RA_F2.a1 = LA_F2.a1; RA_F2.a2 = LA_F2.a2; RA_F2.a3 = LA_F2.a3; ) : (cuts == 4) ? ( LF1.init_LR2(Freq1); RF1.k = LF1.k; RF1.a1 = LF1.a1; RF1.a2 = LF1.a2; RF1.a3 = LF1.a3; LF2.init_LR2(F2); RF2.k = LF2.k; RF2.a1 = LF2.a1; RF2.a2 = LF2.a2; RF2.a3 = LF2.a3; LF3.init_LR2(F3); RF3.k = LF3.k; RF3.a1 = LF3.a1; RF3.a2 = LF3.a2; RF3.a3 = LF3.a3; LF4.init_LR2(F4); RF4.k = LF4.k; RF4.a1 = LF4.a1; RF4.a2 = LF4.a2; RF4.a3 = LF4.a3; // All-passes for maintaining the phase relation between the bands (band_mode == 0) ? ( LA_F2.init_LR2(F2); LA_F3.init_LR2(F3); LA_F4.init_LR2(F4); ) : ( LA_F2.init_AP1(F2); LA_F3.init_AP1(F3); LA_F4.init_AP1(F4); ); RA_F4.k = RA_F4_2.k = RA_F4_3.k = LA_F4_2.k = LA_F4_3.k = LA_F4.k; RA_F4.a1 = RA_F4_2.a1 = RA_F4_3.a1 = LA_F4_2.a1 = LA_F4_3.a1 = LA_F4.a1; RA_F4.a2 = RA_F4_2.a2 = RA_F4_3.a2 = LA_F4_2.a2 = LA_F4_3.a2 = LA_F4.a2; RA_F4.a3 = RA_F4_2.a3 = RA_F4_3.a3 = LA_F4_2.a3 = LA_F4_3.a3 = LA_F4.a3; RA_F3.k = RA_F3_2.k = LA_F3_2.k = LA_F3.k; RA_F3.a1 = RA_F3_2.a1 = LA_F3_2.a1 = LA_F3.a1; RA_F3.a2 = RA_F3_2.a2 = LA_F3_2.a2 = LA_F3.a2; RA_F3.a3 = RA_F3_2.a3 = LA_F3_2.a3 = LA_F3.a3; RA_F2.k = LA_F2.k; RA_F2.a1 = LA_F2.a1; RA_F2.a2 = LA_F2.a2; RA_F2.a3 = LA_F2.a3; ); /* Are we dealing with a FIR filter? */ FIR ? ( // CalcFIR memset(FIR1, 0, bufferDist); memset(FIR2, 0, bufferDist); memset(FIR3, 0, bufferDist); memset(FIR4, 0, bufferDist); memset(FIR5, 0, bufferDist); this.resetFilters(); /* Fetch impulse response */ fptr1 = FIR1; fptr2 = FIR2; fptr3 = FIR3; fptr4 = FIR4; fptr5 = FIR5; this.splitBands(1.0 / fftCalcSize, 1.0 / fftCalcSize); loop(firSize, fptr1[] = this.b1.inL; fptr1 += 2; fptr2[] = this.b2.inL; fptr2 += 2; fptr3[] = this.b3.inL; fptr3 += 2; fptr4[] = this.b4.inL; fptr4 += 2; fptr5[] = this.b5.inL; fptr5 += 2; this.splitBands(0, 0); ); /* Transform impulse responses */ fft(FIR1, fftCalcSize); fft(FIR2, fftCalcSize); fft(FIR3, fftCalcSize); fft(FIR4, fftCalcSize); fft(FIR5, fftCalcSize); fft_permute(FIR1, fftCalcSize); fft_permute(FIR2, fftCalcSize); fft_permute(FIR3, fftCalcSize); fft_permute(FIR4, fftCalcSize); fft_permute(FIR5, fftCalcSize); /* Remove phase information (converts to linear phase) */ linearPhase(FIR1, fftCalcSize); linearPhase(FIR2, fftCalcSize); linearPhase(FIR3, fftCalcSize); linearPhase(FIR4, fftCalcSize); linearPhase(FIR5, fftCalcSize); fft_ipermute(FIR1, fftCalcSize); fft_ipermute(FIR2, fftCalcSize); fft_ipermute(FIR3, fftCalcSize); fft_ipermute(FIR4, fftCalcSize); fft_ipermute(FIR5, fftCalcSize); ); ); updateRequired ); function doFIR(l, r, block1, block2, fftCalcSize, filterSize, filterLoc, cPos) instance(curBlock, lastBlock, initialized, chunkSize, chunksizeT2) local(tmp, cPosT2) global(bufferdist) ( !initialized ? ( curBlock = block1; lastBlock = block2; memset(curBlock, 0, bufferdist); memset(lastBlock, 0, bufferdist); initialized = 1; chunkSize = (fftCalcSize - filterSize - 1); chunkSizeT2 = chunkSize * 2; ); cPos >= chunkSize ? ( tmp = lastBlock; lastBlock = curBlock; curBlock = tmp; /* Empty out the data beyond chunkSize of the block that has just been filled. This used to be the carried over overlap-add tail of the previous one */ memset( curBlock + chunkSizeT2, 0, (fftCalcSize-chunkSize)*2 ); /* Calculate FFT of current chunk */ fft(curBlock, fftCalcSize); //fft_permute(curBlock, fftCalcSize); /* Convolve with the FIR filter (already FFT'd) */ convolve_c(curBlock, filterLoc, fftCalcSize); //fft_ipermute(curBlock, fftCalcSize); ifft(curBlock, fftCalcSize); cPos = 0; ); /* Save sample */ cPosT2 = cPos * 2; lastBlock[cPosT2] = l; lastBlock[cPosT2+1] = r; this.inL = curBlock[cPosT2]; this.inR = curBlock[cPosT2+1]; /* Overlap add the previous tail */ ( cPos < (fftCalcSize-chunkSize) ) ? ( this.inL += lastBlock[chunkSizeT2+cPosT2]; this.inR += lastBlock[chunkSizeT2+cPosT2+1]; 1 ); cPos + 1 ); function processSample() ( FIR ? ( filterBank.resetBands(); cPosNew = filterBank.b1.doFIR(inL, inR, F1Block1, F1Block2, fftCalcSize, firSize, FIR1, cPos); ( cuts > 0 ) ? ( filterBank.b2.doFIR(inL, inR, F2Block1, F2Block2, fftCalcSize, firSize, FIR2, cPos); ); ( cuts > 1 ) ? ( filterBank.b3.doFIR(inL, inR, F3Block1, F3Block2, fftCalcSize, firSize, FIR3, cPos); ); ( cuts > 2 ) ? ( filterBank.b4.doFIR(inL, inR, F4Block1, F4Block2, fftCalcSize, firSize, FIR4, cPos); ); ( cuts > 3 ) ? ( filterBank.b5.doFIR(inL, inR, F5Block1, F5Block2, fftCalcSize, firSize, FIR5, cPos); ); cPos = cPosNew; ) : ( filterBank.splitBands(inL, inR); ); filterBank.processBands(); ); FIR ? ( specBuffer.updateBuffer(.5*(spl0+spl1)); specBuffer.setOffset(newPDC+2); cSpec = specBuffer.readBuffer(); spectrumIn.feedSample(cSpec); ) : ( spectrumIn.feedSample(.5*(spl0+spl1)); ); // Split signal into bands filterBank.updateCoeffs(); inL = spl0; inR = spl1; processSample(); spl0 = filterBank.b1.l; spl1 = filterBank.b1.r; out = spl0 + spl1; ( cuts > 0 ) ? ( spl2 = filterBank.b2.l; spl3 = filterBank.b2.r; out += spl2 + spl3; ( cuts > 1 ) ? ( spl4 = filterBank.b3.l; spl5 = filterBank.b3.r; out += spl4 + spl5; ( cuts > 2 ) ? ( spl6 = filterBank.b4.l; spl7 = filterBank.b4.r; out += spl6 + spl7; ( cuts > 3 ) ? ( spl8 = filterBank.b5.l; spl9 = filterBank.b5.r; out += spl8 + spl9; ); ); ); ); spectrumOut.feedSample(.5*out); /* Uncomment for testing flatness spl0 += spl2 + spl4 + spl6 + spl8; spl1 += spl3 + spl5 + spl7 + spl9;*/ curSample += 1; @gfx 1080 400 function createGUI(stripStart) ( scaling = gfx_ext_retina-1; spectrumX = 0; spectrumY = 0; spectrumW = gfx_w - 200*0; spectrumH = gfx_h - 25 * ( 1 + modVisible ) * (1+scaling); fontface = "Arial"; gfx_setfont(GRID_FONT, fontface, 14*(1+scaling)); gfx_setfont(HINT_FONT, fontface, 14*(1+scaling)); spectrumIn.setWindowLocation(spectrumX, spectrumY, spectrumW, spectrumH); spectrumOut.setWindowLocation(spectrumX, spectrumY, spectrumW, spectrumH); ); /* Gradients are buffered to save rendering perf */ function drawGradients(w, h, spectrumH, gradient_bg) instance(lw, lh, lSpectrumH) global(gfx_mode, gfx_dest, bg_color_r, bg_color_g, bg_color_b, bg_color_a, gfx_x, gfx_y, gfx_r, gfx_g, gfx_b, gfx_a ) local( oldDest, oldMode) ( oldMode = gfx_mode; gfx_mode = 0; gfx_set(1,1,1,1); ( ( lw != w ) || ( lh != h ) || (spectrumH != lSpectrumH) ) ? ( /* Only draw gradients when size changes */ gfx_x = gfx_y = 0; lw = w; lh = h; lSpectrumH = spectrumH; oldDest = gfx_dest; gfx_setimgdim(gradient_bg, w, h); gfx_dest = gradient_bg; gfx_set(bg_color_r, bg_color_g, bg_color_b, bg_color_a); gfx_rectto(w,spectrumH); gfx_gradrect(0, 0, w, spectrumH, gfx_r, gfx_g, gfx_b, gfx_a, .0001, .001, .0001, .0001, -.0001, -.0001, .0001, .0001); gfx_gradrect(0, 0, w, spectrumH, gfx_r, gfx_g, gfx_b, .3, .0001, .001, .0006, .0001, -.0001, -.0001, .0001, .0001); gfx_gradrect(0, 0, w, spectrumH, 0, 0, 0, 0, 0, -.001, -.0006, .0001, -.0001, -.0001, .01, -.00008); gfx_gradrect(0, spectrumH, w, h-spectrumH, 0, 0, .0, 1.0, .0001, .0001, .0002, 0, 0, 0, .0005, 0); gfx_dest = oldDest; ); gfx_x = gfx_y = 0; gfx_blit(gradient_bg, 1, 0); gfx_mode = oldMode; ); bg_color_r = 47/255; bg_color_g = 65/255; bg_color_b = 75/255; bg_color_a = 1; fft_out_r = .8; fft_out_g = .8; fft_out_b = .8; fft_out_a = 1.0; fft_in_r = .13; fft_in_g = .13; fft_in_b = .23; fft_in_a = .6; grid_color_r = .73; grid_color_g = .73; grid_color_b = .76; grid_color_a = .2; grid_alpha = .1 * (1+.5*abs(sin(2*time_precise()))); backface_color_r = .2; backface_color_g = .2; backface_color_b = .3; backface_color_a = .1; font_color_r = .8; font_color_g = .8; font_color_b = .8; font_color_a = .8; knob_font_color_r = .5; knob_font_color_g = .7; knob_font_color_b = 1; knob_font_color_a = 1; highlight_color_r = .5; highlight_color_g = .7+.2*abs(sin(2*time_precise())); highlight_color_b = .9; highlight_color_a = .6+.4*abs(sin(2*time_precise())); disabled_color_r = .4; disabled_color_g = .4; disabled_color_b = .4; disabled_color_a = .8; stripStart = 35; createGUI(stripStart); gradient.drawGradients(gfx_w, gfx_h, spectrumH, 2); globalTime = time_precise(); delta_time = globalTime - lastGlobalTime; lastGlobalTime = globalTime; gfx_r = 1; spectrumIn.drawGrid(); spectrumIn.floorLevel = 25 + dnoisefloor_freq; spectrumOut.floorLevel = 25 + dnoisefloor_freq; spectrumIn.drawFFT(1, fft_in_r, fft_in_g, fft_in_b, fft_in_a); spectrumOut.drawFFT(0, fft_out_r, fft_out_g, fft_out_b, fft_out_a); spectrumIn.updateBands(); spectrumIn.drawBands(); spectrumIn.fftWindow_processMouse(mouse_x, mouse_y, mouse_cap); tFIR.label = "FIR"; tFIR.drawToggle(20, 20, 10, 10, FIR, 1, 0.2, 0.2, 0.8, 1.0, 0.2, 0.2, 0.8, "Linear phase FIR mode.\n\nLinear phase filters preserve the phase relations between this\nand other tracks but introduce a higher CPU cost and latency.\nEnable only when needed for its linear phase properties.\n\nNote: Modulation of the frequency when in FIR mode can cause crackles."); FIR = tFIR.processMouseToggle(mouse_x, mouse_y, mouse_cap); FIR ? ( FIR_NQ.drawToggle(60, 20, 10, 10, FIR_quality == 0, 1, 0.2, 0.2, 0.8, 1.0, 0.2, 0.2, 0.8, "Normal quality mode.\n\nHigher quality modes can be useful when using splits\nat very low frequencies. Note that high quality modes\ninduce more latency and require restarting transport\n(press stop and play)."); FIR_NQ.processMouseToggle(mouse_x, mouse_y, mouse_cap) ? FIR_quality = 0; FIR_HQ.drawToggle(75, 20, 10, 10, FIR_quality == 1, 1, 0.2, 0.2, 0.8, 1.0, 0.2, 0.2, 0.8, "High quality mode.\n\nHigher quality modes can be useful when using splits\nat very low frequencies. Note that high quality modes\ninduce more latency and require restarting transport\n(press stop and play)."); FIR_HQ.processMouseToggle(mouse_x, mouse_y, mouse_cap) ? FIR_quality = 1; FIR_UQ.label = FIR_quality == 0 ? "Normal" : FIR_quality == 1 ? "High" : FIR_quality == 2 ? "Ultra"; FIR_UQ.drawToggle(90, 20, 10, 10, FIR_quality == 2, 1, 0.2, 0.2, 0.8, 1.0, 0.2, 0.2, 0.8, "Ultra quality mode.\n\nHigher quality modes can be useful when using splits\nat very low frequencies. Note that high quality modes\ninduce more latency and require restarting transport\n(press stop and play)."); FIR_UQ.processMouseToggle(mouse_x, mouse_y, mouse_cap) ? FIR_quality = 2; ); fourpole.drawToggle(20, 40, 10, 10, band_mode == 0, 1, 0.2, 0.2, 0.8, 1.0, 0.2, 0.2, 0.8, "Use 4 pole 24 dB/oct filters"); fourpole.processMouseToggle(mouse_x, mouse_y, mouse_cap) ? band_mode = 0; fourpole.label = "4p"; twopole.drawToggle(60, 40, 10, 10, band_mode == 1, 1, 0.2, 0.2, 0.8, 1.0, 0.2, 0.2, 0.8, "Use 2 pole 12 dB/oct filters"); twopole.processMouseToggle(mouse_x, mouse_y, mouse_cap) ? band_mode = 1; twopole.label = "2p"; absChoice.label = "Abs position"; absChoice.drawToggle(20, 60, 10, 10, abs_placement, 1, 0.2, 0.2, 0.8, 1.0, 0.2, 0.2, 0.8, "Absolute positioning.\n\nIn this mode the slider mapping is absolute."); abs_placement = absChoice.processMouseToggle(mouse_x, mouse_y, mouse_cap); absFrequency.label = "Absolute frequency range"; absFrequency.drawToggle(20, 80, 10, 10, absolute_frequencies, 1, 0.2, 0.2, 0.8, 1.0, 0.2, 0.2, 0.8, "Absolute frequency range.\n\nMakes settings project sample rate independent."); absolute_frequencies = absFrequency.processMouseToggle(mouse_x, mouse_y, mouse_cap); function setup_menu_string(str_var, value, start) local(s, i) global() ( s = sprintf(str_var, ""); i = start; loop(17 - start , i == 0 ? ( s = sprintf(str_var, "%s|%s%sUnlinked", s, (i == 16) ? "<" : "", (i == value) ? "!" : ""); ) : ( s = sprintf(str_var, "%s|%s%sSend to group %d", s, (i == 16) ? "<" : "", (i == value) ? "!" : "", i); ); i += 1; ); s ); link.label = gmem_group > 0 ? sprintf(7, "Link group %d", gmem_group) : "Link"; link.drawToggle(20, 100, 10, 10, gmem_group > 0, 1, 0.2, 0.2, 0.8, 1.0, 0.2, 0.2, 0.8, "Link to phase matcher.\n\nClick this to link this bandsplitter to a phase matcher."); link.processMouseToggle(mouse_x, mouse_y, mouse_cap); (link.over && (mouse_cap == 1) && (last_cap == 0)) ? ( menu_selection = gfx_showmenu(sprintf(8, "%s", setup_menu_string(8, gmem_group, 0))); gmem_group = menu_selection - 1; ); gfx_x = gfx_y = 30; gfx_setfont(1,"ARIAL",30); gfx_set(1,0,0,1); gfx_x = 30; gfx_y = 60; gfx_x = gfx_w-130*(1+scaling); gfx_y = bottomRow + 30*(1+scaling); gfx_mode = 4; hinter.drawHint_draw(); last_cap = mouse_cap;