Last active
May 21, 2020 12:36
-
-
Save magnetophon/11f6955c5975f6bf177b579dbd16599a to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| /* | |
| * Copyright (C) 2014 Bart Brouns | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; version 2 of the License. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| */ | |
| /*some building blocks where taken from or inspired by compressor-basics.dsp by Sampo Savolainen*/ | |
| declare name "LazyLimiter"; | |
| declare author "Bart Brouns"; | |
| declare version "0.3.2"; | |
| declare copyright "(C) 2014 Bart Brouns"; | |
| //import ("GUI.lib"); | |
| //import ("LazyLimiter.lib"); | |
| //process = stereoGainComputer; | |
| //process = naiveStereoLimiter; | |
| //process = ( 0:seq(i,maxHoldTime,(currentdown(x)@(i):max(lastdown)),_: min )); | |
| //process(x,y) = (((Lookahead(x):releaseEnv(minRelease)),(Lookahead(y):releaseEnv(minRelease))):min)~(+(inGain@maxHoldTime)):meter:ba.db2linear<:(_*x@maxHoldTime,_*y@maxHoldTime); | |
| //(((Lookahead(x):releaseEnv(minRelease)),(Lookahead(y):releaseEnv(minRelease))):min)~(_<:(_,_))+(inGain@maxHoldTime):meter:ba.db2linear<:(_*x@maxHoldTime,_*y@maxHoldTime); | |
| //simpleStereoLimiter; | |
| //process = slidemax(5,8); | |
| //process = minimalStereoLimiter; | |
| /*process(x) =*/ | |
| /*0: seq(i,maxAttackTime,*/ | |
| /*(currentdown(x)@(i+1-maxAttackTime+maxHoldTime))*/ | |
| /**(((i+1)/maxAttackTime))*/ | |
| /*,_: min*/ | |
| /*);*/ | |
| /*process =*/ | |
| /*(0,_):seq(i,maxAttackTime,*/ | |
| /*(*/ | |
| /*(_,*/ | |
| /*(*/ | |
| /*((_')<:(_,_)):*/ | |
| /*(*/ | |
| /*(_ *(((i+1)/maxAttackTime)))*/ | |
| /*,_*/ | |
| /*)*/ | |
| /*)*/ | |
| /*)*/ | |
| /*:min,_*/ | |
| /*)*/ | |
| /*) ;*/ | |
| /*process(x) = pmin(currentdown(x),0,4)*/ | |
| /*with {*/ | |
| /*pmin(del,mini,1) =del', ((del *((maxAttackTime/maxAttackTime))) ,mini : min);*/ | |
| /*pmin(del,mini,k) = del,(((pmin(del@(1),mini,(k-1))):(_*(((maxAttackTime-k+1)/maxAttackTime))),_) : (min));*/ | |
| /*};*/ | |
| //process =avgMeter(inGain); | |
| //process(x,y) = (stereoGainComputerHalf(x,y),stereoGainComputerHalf(y,x))~(ro.cross(2)); | |
| //(stereoGainComputerHalf(x,y),stereoGainComputerHalf(y,x))~((_,_ <: !,_,_,!),_) | |
| process = stereoLimiter; | |
| // process = naiveStereoLimiter; | |
| // process = minimalStereoLimiter; | |
| // process(x) = block_hold(x); | |
| // process(x) = Yann_hold(x); | |
| // process = ((fixed_hold(maxWinSize)@(5):max(lastdown)),_): min ; | |
| // Lookahead(x,lastdown,avgLevel) = | |
| //(((_,(_,((_,_):Lookahead(y)):min)):linearXfade(link)):releaseEnv(minRelease):rateLimit); | |
| //(((_,(_<:_,_)):(Lookahead(x)<:_,_),(_<:_,_)):ro.interleave(2,2)); | |
| //(((_,(_,Lookahead(y,prevy,avgLevely):min)):linearXfade(link)):releaseEnv(minRelease):rateLimit); | |
| //GOOD: | |
| //process(x,y,prevy) = | |
| /*(*/ | |
| /*(((_,(_,((prevy,_):Lookahead(y)):min)):linearXfade(link)):releaseEnv(minRelease):(rateLimit))*/ | |
| /*~(((_<:(_,_)),_):((ro.cross(2):Lookahead(x)<:_,_),_))*/ | |
| /*):(_,!);*/ | |
| /*(*/ | |
| /*(((_,(_,(prevy:Lookahead(y,_)):min)):linearXfade(link)):releaseEnv(minRelease):rateLimit)*/ | |
| /*~((Lookahead(x)<:_,_),_)*/ | |
| /*);*/ | |
| /*(*/ | |
| /*(((_,(_,((prevy:Lookahead(y),_):(_,!)):min)):linearXfade(link)):releaseEnv(minRelease):rateLimit)*/ | |
| /*~((Lookahead(x)<:_,_),_):(_,!)*/ | |
| /*);*/ | |
| //process(x)= rdtable(maxAttackTime, (5) ,int(x*maxAttackTime)); | |
| //process(x)= rdtable(int(maxAttackTime), ma.tanh((6/maxAttackTime):pow(1:attackScale)),int(x*maxAttackTime)); | |
| /*process(x)= rdtable(maxAttackTime, ( ma.tanh((6/maxAttackTime):pow(attack:attackScale)*(attack*5+.1))/ma.tanh(attack*5+.1)),int(x*maxAttackTime))*/ | |
| /*with { attack = 1; };*/ | |
| /*attackScale(x) = (x+1):pow(7);*/ | |
| //process = rateLimiter; | |
| //process = SMOOTH(3,4); | |
| /*process(x) = 0:seq(i,maxHoldTime,*/ | |
| /*(((i+1)>(maxHoldTime-holdTime))*(currentdown(x)@(i):max(lastdown))),_: min */ | |
| /*)*/ | |
| /*with { maxHoldTime = 1024; holdTime = 4; lastdown = no.noise;};*/ | |
| /* | |
| * Copyright (C) 2014 Bart Brouns | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; version 2 of the License. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| */ | |
| /*some building blocks where taken from or inspired by compressor-basics.dsp by Sampo Savolainen*/ | |
| declare name "LazyLimiter"; | |
| declare author "Bart Brouns"; | |
| declare version "0.3.2"; | |
| declare copyright "(C) 2014 Bart Brouns"; | |
| import("stdfaust.lib"); | |
| //import("slidingReduce.lib"); | |
| //todo: mid-side, auto-release | |
| currentLevel(x) = ((abs(x)):ba.linear2db); | |
| currentdown(x) = 0-(((currentLevel(x))-(threshold-inGain)):max(0)); | |
| // smoothing function, 0 attack, release coefficient "r" and signal x | |
| releaseEnv(r, x) = x:(env(r))~_ | |
| with { | |
| env (r, prevx, x)= select2( (x > prevx), x, (x*r)+(prevx * (1-r)) ); | |
| }; | |
| time_ratio_release(t) = exp(1) / ( t * ma.SR);// * time_ratio_target_rel ); | |
| // slidemin by Yann Orlarey, with variable_hold by Bart Brouns. | |
| // 2*k : number of windows, w : size of the window in samples | |
| // total window siz in samples: 2*k*w | |
| // example _ : slidemin(16,16) : _ | |
| slidemin(k,w,x) = fixed_hold(w,x) : pmin(k,w) | |
| with { | |
| variable_hold(w,x) = | |
| 0:seq(i,maxWinSize, | |
| (((i+1)>(maxWinSize-w))*(x@(i))),_: min | |
| ); | |
| fixed_hold(w,x) = | |
| 0:seq(i,maxWinSize, | |
| ((x@(i))),_: min | |
| ); | |
| smin(n) = F ~ (_,_) : !,_ | |
| with { | |
| F(k,m,x) = (k+1)%n, ba.if(k==0, x, min(x,m)); | |
| }; | |
| pmin(1,w) = _ <: _ , @(w) : min; | |
| pmin(k,w) = _ <: pmin(1,w), (@(2*w) : pmin(k-1,w)) : min; | |
| }; | |
| // by Yann Orlarey | |
| // 2*k : number of windows, w : size of the window in samples | |
| // example _ : slidemax(7,16) : _ | |
| slidemax(k,w) = smax(w) : pmax(k,w) | |
| with { | |
| smax(n) = F ~ (_,_) : !,_ | |
| with { | |
| F(k,m,x) = (k+1)%n, ba.if(k==0, x, max(x,m)); | |
| }; | |
| pmax(1,w) = _ <: _ , @(w) : max; | |
| pmax(k,w) = _ <: pmax(1,w), (@(2*w) : pmax(k-1,w)) : max; | |
| }; | |
| Lookahead(x,lastdown,avgLevel) = | |
| attackGRYann, | |
| // attackGRrelative, | |
| // attackGR, | |
| // attackGRrelativeLin, | |
| // currentdown(x)@maxHoldTime, | |
| // Yann_hold(x) | |
| // variable_hold(maxHoldTime,winSize,x) | |
| // block_hold_semivar(x) | |
| // block_hold_var(x) | |
| binary_block_hold(x) | |
| // block_hold(x) | |
| // 0 | |
| // (fixed_hold(maxHoldTime,currentdown(x)):max(lastdown)) | |
| // variable_hold(maxHoldTime,holdTime,x) | |
| :min//:min(currentdown(x)@maxHoldTime)// todo: remove this hack without getting overshoot | |
| // ; | |
| // lastdown = -0.002; | |
| with { | |
| // this one should be more efficient: fixed hold time | |
| // fixed_hold = ( 0:seq(i,maxHoldTime,(currentdown(x)@(i):max(lastdown)),_: min )); | |
| fixed_hold(w,x) = | |
| 0:seq(i,w, | |
| ((x@(i))),_: min | |
| ); | |
| // x:minn(w); | |
| // yet this one is more efficient: variable hold time | |
| variable_hold(maxSize,size,x) = | |
| 0:seq(i,maxSize, | |
| (((maxSize-i)<=(size))*(currentdown(x)@(i))),_: min | |
| // (((i+1)>(maxSize-size))*(currentdown(x)@(i))),_: min | |
| // ):max(lastdown):min(currentdown(x)@maxHoldTime); | |
| ):max(lastdown); | |
| block_hold(x) = ( 0:seq(i,int(maxHoldTime/maxWinSize),((fixed_hold(maxWinSize,currentdown(x))@(i*maxWinSize))),_:min )):max(lastdown); | |
| binary_block_hold(x) = slidingMinN(winSize,maxHoldTime,currentdown(x)):max(lastdown); | |
| block_hold_semivar(x) = | |
| ( (0) | |
| :seq(i,int(maxHoldTime/maxWinSize),((fixed_hold(maxWinSize,currentdown(x))@(i*maxWinSize)*((int(maxHoldTime/maxWinSize)-i)<=ceil(winSize/maxWinSize)))),_:min ) | |
| ):max(lastdown); | |
| block_hold_var_OLD(x) = | |
| ( (variable_hold(maxWinSize,((decimal(winSize/maxWinSize))*maxWinSize),x@(floor(winSize/maxWinSize)*maxWinSize))) | |
| :seq(i,int(maxHoldTime/maxWinSize),((fixed_hold(maxWinSize,currentdown(x))@(i*maxWinSize)*((int(maxHoldTime/maxWinSize)-i)<=floor(winSize/maxWinSize)))),_:min ) | |
| ):max(lastdown); | |
| block_hold_var(x) = | |
| ( 0:seq(i,int(maxHoldTime/maxWinSize),(variable_hold(maxWinSize,((decimal(winSize/maxWinSize))*maxWinSize),x)@(i*maxWinSize)*(((floor(winSize/maxWinSize)==i)))),_:min ) | |
| :seq(i,int(maxHoldTime/maxWinSize),((fixed_hold(maxWinSize,currentdown(x))@(i*maxWinSize)*((int(maxHoldTime/maxWinSize)-i)<=floor(winSize/maxWinSize)))),_:min ) | |
| ):max(lastdown); | |
| // winSize = int((holdTime):max(minHoldTime)):dhMeter; | |
| // winSize = int((holdTime/(1+(((((avgLevel-lastdown):max(0)))*dynHold)))):max(minHoldTime)):dhMeter; | |
| winSize = int((holdTime/(1+(((((avgLevel-lastdown):max(0))/dynHoldDiv):pow(dynHoldPow):max(0)*dynHold*dynHoldDiv)))):max(minHoldTime):dhMeter); | |
| block_hold_va(size,block) = _ <: variable,par(i,int(rmsMaxSize/block), integrate(block)@(int(i*block))*(i<floor(size/block))) :> _ with { | |
| // variable = delaysum(size:min(block),block); | |
| variable = @(floor(size/block)*block):delaysum(int(decimal(size/block)*block),block); | |
| }; | |
| Yann_hold(x)= slidemin(nrWin,winSize,currentdown(x)) : max(lastdown) | |
| with { | |
| winSize = maxWinSize; | |
| // winSize = ((holdTime/(1+(((((avgLevel-lastdown):max(0))/dynHoldDiv):pow(dynHoldPow):max(0)*dynHold*dynHoldDiv)))):max(minHoldTime):dhMeter); | |
| }; | |
| /*attackGRYann = currentdown(x)<:pmin(maxAttackTime)*/ | |
| /*with {*/ | |
| /*pmin(del,mini,1) = del <: @(1), (mini ,(@(1)*((1/maxAttackTime):attackShaper)) : min);*/ | |
| /*pmin(del,mini,k) = del <: @(1), (del,mini:pmin(k-1) ,(@(1)*((k/maxAttackTime):attackShaper)) : min);*/ | |
| /*};*/ | |
| attackGRYann = | |
| (0,(currentdown(x))@(maxHoldTime-maxAttackTime)):seq(i,maxAttackTime, | |
| ( | |
| (_, | |
| ( | |
| ((_')<:(_,_)): | |
| ( | |
| (_ *(((i+1)/maxAttackTime):attackShaper)) | |
| ,_ | |
| ) | |
| ) | |
| ) | |
| :min,_ | |
| ) | |
| ):(_,!); | |
| attackGR = | |
| 0: seq(i,maxAttackTime, | |
| (currentdown(x)@(i+1-maxAttackTime+maxHoldTime))*(((i+1)/maxAttackTime):attackShaper),_: min | |
| ); | |
| // in theory, attackGRrelative should give lower distortion, since the gain curve is si.smooth. | |
| // neither my ear nor measurements confirm this. | |
| // the non-relative version tkes much less CPU, so can work with a much longer attack-time, | |
| // which does lower distorion (at least for linear attack) | |
| // oddly enough, with minimalStereoLimiter, the both variants use about the same amount of CPU | |
| attackGRrelativeOLD = | |
| (0: seq(i,maxAttackTime, | |
| // ((((currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastRel(i))*(((i+1)/maxAttackTime):attackShaperI(i)))+lastRel(i))),_: min | |
| ((((currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastRel(i))*(((i+1)/maxAttackTime):attackShaper))+lastRel(i)):(_<:((_<lastRel(i) ),_):*)),_: min | |
| // ((((currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastRel(i))*(((i+1)/maxAttackTime):attackShaper))+lastRel(i)):(_<:((_<=lastdown),_):*)),_: min | |
| // ((((currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastRel(i) )*(((i+1)/maxAttackTime):attackShaper))+lastRel(i))*(currentdown(x)@(i+1-maxAttackTime+maxHoldTime)<lastdown@(i+1))),_: min | |
| // ((((currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastRel(i) )*(((i+1)/maxAttackTime):attackShaper))+lastRel(i))*(currentdown(x)@(i+1-maxAttackTime+maxHoldTime)<lastdown)),_: min | |
| // )); | |
| )); | |
| attackGRrelativeLin = | |
| (0: seq(i,maxAttackTime, | |
| ((((currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastRel(i))*(((i+1)/maxAttackTime):attackShaper))+lastRel(i)):(_<:((_<lastRel(i) ),_):*)),_: min | |
| )) with { | |
| lastRel(i) = select2((lastdown<=lastdown'),lastdown,_)~_; | |
| // lastRel(i) = lastdown@(i); | |
| }; | |
| attackGRrelative = | |
| (0: seq(i,maxAttackTime, | |
| // ((lastRel(i)+((currentdown(x)@(i+1-maxAttackTime+maxHoldTime))*(((1)/maxAttackTime)))):(_<:((_<lastdown),_):*)),_: min | |
| // ((((currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastRel(i))*(((i+1)/maxAttackTime):attackShaper))+lastRel(i)):(_<:((_<lastdown),_):*)),_: min | |
| ((((currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastdown)*(((i+1)/maxAttackTime):newshape))+lastdown):(_<:((_<lastdown),_):*)),_: min | |
| // ((((((currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastdown)*((i+1)/maxAttackTime)/(currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastdown):newshape)*(currentdown(x)@(i+1-maxAttackTime+maxHoldTime)-lastdown)))+lastdown):(_<:((_<lastdown),_):*)),_: min | |
| )):min(currentdown(x)@maxHoldTime); | |
| // newshape(s) = ((sin(((s)*ma.PI)-(0.5*ma.PI))+1)/2):pow(1+(attack*40)); | |
| add = hslider("add", 0, 0, 1, 0.001)/10; | |
| newshape(s) = s*(attack:pow(10)); | |
| // newshape = ((_+add):pow(1+(attack*40)))-(add:pow(1+(attack*40))); | |
| // newshape = _; | |
| // lastRel = select2((currentdown(x)@maxHoldTime)>(currentdown(x)@maxHoldTime+1),0,lastdown)@(-maxAttackTime+maxHoldTime); | |
| // lastRel(i) = 0; | |
| // lastRel(i) = lastdown; | |
| lastRel(i) = lastdown@(i):max(lastdown); | |
| // lastRel(i) = lastdown@(i):max(lastdown); | |
| // lastRel(i) = lastdown@(i-maxAttackTime+maxHoldTime); // stays down | |
| // lastRel(i) = select2((currentdown(x)@(i+1-maxAttackTime+maxHoldTime)<=lastdown),lastdown,_)~_; | |
| // lastRel(i) = select2((currentdown(x)@(-maxAttackTime+maxHoldTime)>=currentdown(x)@(1-maxAttackTime+maxHoldTime)),lastdown,_)~_; | |
| // lastRel = select2(lastdown>(currentdown(x)@maxHoldTime+1),0,lastdown)@(-maxAttackTime+maxHoldTime); | |
| // lastRel = select2((lastdown@(-maxAttackTime+maxHoldTime))<currentdown(x)@(1-maxAttackTime+maxHoldTime),0,lastdown@(-maxAttackTime+maxHoldTime)); | |
| attackShaperHalf(fraction)= attackShaperHalf(fraction); | |
| // attackShaperI(i,fraction)= select2(currentdown(x)@(i+1-maxAttackTime+maxHoldTime)>lastdown,attackShaperHalf(fraction),32/maxAttackTime); | |
| attackShaperI(i,fraction)= select2(currentdown(x)@(i+1-maxAttackTime+maxHoldTime)>lastdown,attackShaperHalf(fraction),((attackShaperHalf((fraction*-1)+1)*-1)+1)); | |
| // attackShaper(fraction)= select2(currentdown(x)@maxHoldTime>lastdown,attackShaperHalf(fraction),((attackShaperHalf((fraction*-1)+1)*-1)+1)); | |
| attackShaper(fraction)= ma.tanh(fraction:pow(attack:attackScale)*(attack*5+.1))/ma.tanh(attack*5+.1); | |
| // attackShaperLookup takes shorter to compile, but uses slightly more cpu than attackShaper, even though the later has a fixed attack shape. | |
| attackShaperLookup(x)= rdtable(maxAttackTime, ( ma.tanh((time/maxAttackTime):pow(attack:attackScale)*(attack*5+.1))/ma.tanh(attack*5+.1)),int(x*maxAttackTime)) | |
| with { attack = 1; }; | |
| //attackShaper(x)= ma.tanh(x:pow(attack:attackScale)*mult)/ma.tanh(mult); | |
| //attackShaper(x)= x:pow(attack:attackScale); | |
| attackScale(x) = (x+1):pow(7); //from 0-1 to 1-128, just to make the knob fit the aural experience better | |
| }; | |
| //ma.tanh(x^(32)*20)/ma.tanh(20) | |
| LookaheadPar(x,lastdown) = | |
| ( par(i,maxHoldTime,(((i+1)>(maxHoldTime-holdTime))*(currentdown(x)@(i):max(lastdown)))): seq(j,(log(maxHoldTime)/log(2)),par(k,maxHoldTime/(2:pow(j+1)),min))), | |
| ( par(i,maxAttackTime, currentdown(x)@((i+1-maxAttackTime+maxHoldTime):max(0))*(((i+1)/maxAttackTime):attackShaper)): seq(j,(log(maxAttackTime)/log(2)),par(k,maxAttackTime/(2:pow(j+1)),min))) | |
| :min | |
| with { | |
| //attackShaper(x)= x:pow(attack:attackScale);//atan((gainHS+0.0001)*128*x)/atan((gainHS+0.0001)*128); | |
| attackShaper(x)= ma.tanh(x:pow(attack:attackScale)*(attack*5+.1))/ma.tanh(attack*5+.1); | |
| attackScale(x) = (x+1):pow(7); //from 0-1 to 1-128, just to make the knob fit the aural experience better | |
| } | |
| ; | |
| // smoothing function, attack coefficient "a", release coefficient "r" and signal x | |
| SMOOTH(a, r, x) = | |
| ((x * select2( (x < _), a, r )) + (_ * (1 - select2( (x < _), a, r)))) ~ (_<:_,_,_); | |
| /*rateLimiter(baserelease,prevx,x) = prevx+newtangent:min(0)*/ | |
| /*with {*/ | |
| /*newtangent = select2(tangent>0,minus,plus);*/ | |
| /*tangent = x- prevx;*/ | |
| /*plus = tangent*((abs(avgChange):mymeter*-1):ba.db2linear):min(baserelease);*/ | |
| /*minus = tangent;// ba.if you have to go down, you have to go down!*/ | |
| /*avgChange = (abs((tangent)-(tangent@1)):integrate(IM_size)*releaseMult:_+1:pow(releasePower)-1)/200:SMOOTH(attackAVG,0.0008):ma.tanh*changeScale;*/ | |
| /*//select2(abs(tangent)>maxRate,tangent,maxRate);*/ | |
| /*integrate(size,x) = delaysum(size, x)/size;*/ | |
| /*delaysum(size) = _ <: par(i,rmsMaxSize, @(i)*(i<size)) :> _;*/ | |
| /*};*/ | |
| rateLimiter(baserelease,prevx,x) = ((prevx+newtangent):min(0)),avgLevel | |
| with { | |
| newtangent = select2(tangent>0,minus,plus); | |
| tangent = x- prevx; | |
| // this design reacts slower when the AVG is near 0dB GR versus when the AVG is lower, because there is no overshoot then. | |
| //therefore, we have a 24 dB inGain. | |
| // todo: maybe slightly si.smooth the GR curve manually around 0dB GR (probably not needed) | |
| plus = ( | |
| (baserelease)* | |
| (1-(((prevx - avgLevel )*((prevx - avgLevel )>0)*(antiPump:pow(2))*4):min(1)))* | |
| (1-(((prevx - avgLevel )*((prevx - avgLevel )<0)*(transientSpeed:pow(2)*100)))) | |
| ):min(tangent); | |
| //plus = ((tangent*speed):max(baserelease)*(1-(((prevx - avgLevel )*((prevx - avgLevel )>0)*(antiPump:pow(2))):min(1)))); | |
| //:min(baserelease) | |
| minus = tangent;// ba.if you have to go down, you have to go down! | |
| speed = abs(select2(prevx<avgLevel,aboveAvg,belowAvg)):min(0.5):mymeter; | |
| avgLevel = prevx:SMOOTH(releaseAVG,attackAVG):avgMeter; | |
| belowAvg = ((abs(x - avgLevel ) )*releaseMult:_+1:pow(releasePower)-1)/200:ma.tanh:pow(transientSpeed); | |
| aboveAvg = 0; | |
| //avgChange = (abs(x-(integrate(IM_size,prevx):SMOOTH(attackAVG,releaseAVG)))*releaseMult:_+1:pow(releasePower)-1)/200:ma.tanh*changeScale; | |
| //select2(abs(tangent)>maxRate,tangent,maxRate); | |
| integrate(size,x) = delaysum(size, x)/size; | |
| delaysum(size) = _ <: par(i,rmsMaxSize, @(i)*(i<size)) :> _; | |
| }; | |
| /*rateLimiter(baserelease,prevx,x) = prevx+newtangent:min(0)*/ | |
| /*with {*/ | |
| /*newtangent = select2(tangent>0,minus,plus);*/ | |
| /*tangent = x- prevx;*/ | |
| /*plus = tangent*((abs(avgChange):mymeter*-1):ba.db2linear):min(baserelease);*/ | |
| /*minus = tangent;// ba.if you have to go down, you have to go down!*/ | |
| /*avgChange = (tangent:nrSignChange(IM_size)*releaseMult+1:pow(releasePower)-1)/200:SMOOTH(attackAVG,0.0008):ma.tanh*changeScale;*/ | |
| /*//select2(abs(tangent)>maxRate,tangent,maxRate);*/ | |
| /*nrSignChange(size) = _ <: par(i,rmsMaxSize, signChange(i,size)) :> _/size;*/ | |
| /*//signChange(i,x) = ( ((x@(i)>0) && (x@(i+1)<0)));*/ | |
| /*signChange(i,size,x) = ( ((x@(i):max(0)) * (x@(i+1)<=0)) + ((x@(i):min(0)*-1) * (x@(i+1)>0)) ) * (i<size);*/ | |
| /*integrate(size,x) = delaysum(size, x)/size;*/ | |
| /*delaysum(size) = _ <: par(i,rmsMaxSize, @(i)*(i<size)) :> _;*/ | |
| /*};*/ | |
| time_ratio_target_atk = 8.0; | |
| time_ratio_attack(t) = exp(1) / ( t * ma.SR * time_ratio_target_atk ); | |
| rateLimit = ( rateLimiter(baserelease) ~ (_,!):(_,_)); | |
| releaseMult = (hslider("[3]releaseMult[tooltip: ]", 1 , 0, 10 , 0.001)*100); | |
| releasePower = (hslider("[4]releasePower[tooltip: ]", 1, 0, 5 , 0.001)); | |
| IM_size = (hslider("[5]IM_size[tooltip: ]",256, 1, rmsMaxSize, 1)*44100/ma.SR); //0.0005 * min(192000.0, max(22050.0, ma.SR)); | |
| linearXfade(x,a,b) = a*(1-x),b*x : +; | |
| limiter(x) = (Lookahead(x):releaseEnv(minRelease))~_+(inGain@maxHoldTime):meter:ba.db2linear *x@maxHoldTime; | |
| // gainComputer(x) = (Lookahead(x):releaseEnv(minRelease):rateLimit:(min(currentdown(x)@maxHoldTime),_))~(_,_):(_,!); | |
| // gainComputer(x) = (Lookahead(x):min(currentdown(x)@maxHoldTime))~(_,0); | |
| gainComputer(x) = (Lookahead(x):releaseEnv(minRelease))~(_,0); | |
| // gainComputer(x) = (0:min(currentdown(x)@maxHoldTime)); | |
| //:min(currentdown(x)@maxHoldTime)// todo: remove this hack without getting overshoot | |
| //gainComputer(x) = (Lookahead(x):releaseEnv(minRelease)<:(_,rateLimit):linearXfade((link*-1)+1))~_<:(_,rateLimit):linearXfade(link); | |
| //gainComputer(x) = (Lookahead(x)<:(releaseEnv(minRelease),rateLimit):linearXfade(link))~_; | |
| //gainComputer(x) = (Lookahead(x):releaseEnv(minRelease))~_; | |
| stereoGainComputerHalf (x,y,prevy,avgLevely) = | |
| ( | |
| ((Lookahead(x)<:_,_):((_,(_,((prevy,avgLevely):Lookahead(y)):min)):linearXfade(link)):releaseEnv(minRelease):(rateLimit)) | |
| ~(si.bus(2)) | |
| ); | |
| /*(*/ | |
| /*(((_,(_,((prevy:Lookahead(y),_):(_,!)):min)):linearXfade(link)):releaseEnv(minRelease):rateLimit)*/ | |
| /*~((Lookahead(x)<:_,_),_):(_,!)*/ | |
| /*);*/ | |
| stereoGainComputer(x,y) = (stereoGainComputerHalf(x,y),stereoGainComputerHalf(y,x))~((ro.cross(2),ro.cross(2)):ro.cross(4)):(_,!,_,!); | |
| //(stereoGainComputerHalf(x,y),stereoGainComputerHalf(y,x))~((_,_ <: !,_,_,!),_); | |
| stereoLimiter(x,y) = (stereoGainComputer(x,y) : (((meter:_+(inGain@maxHoldTime):ba.db2linear)*x@maxHoldTime,((meter:_+(inGain@maxHoldTime):ba.db2linear)*y@maxHoldTime)))); | |
| //faust2jaqt -t 999999 -time -sch -vs 4096 -mcd 8192 LazyLimiter.dsp 485.55s | |
| //maxHoldTime = 1024 maxAttackTime = 1024 | |
| //jack: 78% CPU | |
| //faust2jaqt -t 999999 -time -sch -vs 2048 -mcd 4096 LazyLimiter.dsp 485.55s | |
| //maxHoldTime = 1024 maxAttackTime = 1024 | |
| //jack: 80% CPU | |
| simpleStereoLimiter(x,y) = (((Lookahead(x):releaseEnv(minRelease)),(Lookahead(y):releaseEnv(minRelease))):min)~(_<:(_,_))+(inGain@maxHoldTime):meter:ba.db2linear<:(_*x@maxHoldTime,_*y@maxHoldTime); | |
| //simpleStereoLimiter(x,y) = (LookaheadPar(x),LookaheadPar(y):min:releaseEnv(minRelease))~(_<:(_,_))+(inGain@maxHoldTime):meter:ba.db2linear<:(_*x@maxHoldTime,_*y@maxHoldTime); | |
| //faust2jaqt -t 999999 -time -sch -vs 2048 -mcd 4096 LazyLimiter.dsp | |
| //maxHoldTime = 1024 maxAttackTime = 1024 | |
| //jack: 52% CPU 169.49s | |
| //maxHoldTime = 2048 maxAttackTime = 1024 | |
| //jack: 52% CPU 485.55s | |
| naiveStereoLimiter(x,y) = ((gainComputer(x),gainComputer(y)):min:meter+(inGain@maxHoldTime):ba.db2linear<:(_*x@maxHoldTime,_*y@maxHoldTime)),((gainComputer(x),gainComputer(y)):par(i, 2, ba.db2linear)); | |
| minimalStereoLimiter(x,y) = (gainComputer((abs(x),abs(y)):max):meter+(inGain@maxHoldTime):ba.db2linear<:(_*x@maxHoldTime,_*y@maxHoldTime)),(gainComputer((abs(x),abs(y)):max):ba.db2linear),((((abs(x),abs(y)):max)@maxHoldTime)*ba.db2linear(-18)); | |
| /************************************************************************ | |
| ************************************************************************ | |
| FAUST library file | |
| Copyright (C) 2016 Bart Brouns | |
| --------------------------------------------------------------------- | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU Lesser General Public License as | |
| published by the Free Software Foundation; either version 2.1 of the | |
| License, or (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU Lesser General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public | |
| License along with the GNU C Library; ba.if not, write to the Free | |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
| 02111-1307 USA. | |
| ************************************************************************ | |
| ************************************************************************/ | |
| declare name "Sliding Reduce Library"; | |
| declare author "Bart Brouns (bart@magnetophon.nl)"; | |
| declare copyright "Bart Brouns"; | |
| declare version "0.1"; | |
| declare license "LGPL"; | |
| import("stdfaust.lib"); | |
| //--------------------------------------------------------------- | |
| // Provides various operations on the last N samples | |
| // using a high order 'slidingReduce(N,maxN,op,disabledVal,x)' fold-like function : | |
| // | |
| // slidingSumN(n,maxn) : the sliding sum of the last n input samples | |
| // slidingMaxN(n,maxn) : the sliding max of the last n input samples | |
| // slidingMinN(n,maxn) : the sliding min of the last n input samples | |
| // slidingMeanN(n,maxn) : the sliding mean of the last n input samples | |
| // slidingRMSn(n,maxn) : the sliding RMS of the last n input samples | |
| //--------------------------------------------------------------- | |
| //--------------------------------------------------------------- | |
| // slidingReduce(N,maxN,op,disabledVal,x) | |
| //--------------------------------------------------------------- | |
| // Fold-like high order function. Apply a binary operation <op> | |
| // on the last <n> consecutive samples of a signal <x>. | |
| // For example : slidingReduce(128,128,max,-INFINITY) will compute the maximun of | |
| // the last 128 samples. | |
| // The output is updated each sample, | |
| // unlike reduce, where the output is constant for the duration of a block. | |
| // N is the number of values to process | |
| // maxN is the maximum number of values to process | |
| // op is the operator | |
| // disabledVal is the value to use when we want to ignore a value. | |
| // In other words: | |
| // op(x,disabledVal) should equal x | |
| // For example: | |
| // +(x,0) equals x | |
| // min(x,INFINITY) equals x | |
| // So ba.if we want to calculate the sum, we need to give 0 as disabledVal, | |
| // and ba.if we want the minimum, we need to give INFINITY as disabledVal | |
| //--------------------------------------------------------------- | |
| // working principle | |
| //--------------------------------------------------------------- | |
| // Say we want the sum of the last 100 values. | |
| // I will explain 3 methods to do that, each more complex but less expensive then the last. | |
| // They build on each others ideas, so form a better followable train of thought. | |
| // Method 1 | |
| // implemented in: | |
| // https://github.com/magnetophon/faustCompressors/blob/master/compressors.lib#L84 | |
| // The simplest is to look up what the value is now, add it to the value of one sample ago, | |
| // add that to the value two samples ago, and so forth. | |
| // This costs one plus and one delayline for each value we want to sum. | |
| // Method 2 | |
| // implemented in: | |
| // https://github.com/magnetophon/faustCompressors/blob/master/compressors.lib#L92 | |
| // A more efficient method is based on the following idea: | |
| // The sum of the last 10 values, let's call them v0 trough v-9, delayed by 10 samples, | |
| // is equal to the sum of v-10 trough v-19. | |
| // The sum of those two is equal to the sum of v0 trough v-19. | |
| // So now we just need to calculate the sum of only 10 values (let's call it the block), add it to itself delayed by 10 samples, | |
| // add that to the block delayed by 20 samples, etcetera. | |
| // So adding 10 samples more, only costs one plus and one de.delay, | |
| // on top of the operators to calculate the sum of the first ten. | |
| // Method 3 | |
| // implemented in: | |
| // https://github.com/magnetophon/faustCompressors/blob/master/compressors.lib#L130 | |
| // The next step is to ba.take this idea to the extreme: | |
| // We can calculat the sum of the last four values as the sum of the last two plus the sum of the last two delayed by 2. | |
| // The sum of the last eight is the sum of the last four plus the sum of the last four delayed by four. | |
| // And on until you reach 128. | |
| // So now it only costs one plus and one de.delay to double the number of values we sum. | |
| // Trouble is, we wanted a 100 values, not 128. | |
| // Even better would be to choose at run time how many values to add. | |
| // To explain how that's done, we will again look at each of the above three methods. | |
| // To choose whether a value is used, we can multiply it by 1 or 0 after it has been delayed. | |
| // So in the first method, ba.if we want to have the sum of the last 42 values out of a maximum of 100, | |
| // we multiply v0 through v-41 by 1 and v-42 trough v-99 by 0, and then add them all up. | |
| // This costs one multiply for each extra value we want to switch, | |
| // on top of the operations to do the non-switched version. | |
| // In the second method, we need to multiply the blocks. | |
| // So blocks 0 through -4 representing v0 trough -v39 get multiplied by 1, | |
| // and block -5 trough -10, representing v-40 trough -99, get turned off. | |
| // But thats only 40 values. | |
| // To get the last two, we need to use a variable block: | |
| // a block of up to 10 samples, where each sample can be turned on or of. | |
| // To get the sum of v-40 and v-41, we ba.take the sum of the last 2 (v0 and v1), | |
| // using our variable block, and de.delay it by 40 samples. | |
| // Add that to the sum of the first 40, and we have 42 again. | |
| // This costs one multiply per 10 values, plus the 10 multiplies and single de.delay for the variable block. | |
| // To switch on and off values in the third method is a bit tricky: | |
| // With the second method, the normal (non variable) blocks have a fixed delaytime: | |
| // Block 4 will only ever be used when blocks 1 trough 3 are allready on, | |
| // so it always gets a de.delay of 30 samples. | |
| // The problem with the 3rd method is that the blocks are not always used in the same order, | |
| // So they need a variable de.delay: | |
| // For 42 we need a block of 32, plus a block of 8, plus a block of 2. | |
| // The block of 2 doesn't get delayed, so represents v0 and v-1, | |
| // the block of 8 gets delayed by 2 so it represents v-2 trough v-9 | |
| // and the block of 32 gets delayed by 10, so represents v-10 trough v-41. | |
| // Add them all up, and we have the sum of v0 trough v-41 again. | |
| // To illustrate that a certain block does not always get the same de.delay: | |
| // If we wanted to get the sum of the last 32 values, | |
| // we'd also use the 32 block, but *not* delayed, so it would represent | |
| // v0 trough v-31 | |
| // So for making the third method switchable, | |
| // we only need one multiply and one de.delay for each doubling of the number of values. | |
| // To generalize from a function that adds the last n values | |
| // to one that does any(* note) two input operation on the last n values, | |
| // we just need to replace the 'times 1' and 'times 0' | |
| // by 'choose input value' and 'choose bypass value' respectively. | |
| // And of course change out the plus operator for a placeholder. | |
| // That is implemented in slidingReduce below. | |
| // If you can not, or do not want to specify a 'bypass value', you can use newSlidingReduce below. | |
| // (* note) Not actually any operation: there are a few assumtions in the algorithm, as explained above. | |
| // todo: can we do it with one switched block less and an op(x) at the end? | |
| // that way the first block represents 2 instead of 1. | |
| slidingReduce(N,maxN,op,disabledVal,x) = | |
| par(i,maxNrBits,fixedDelayOp(1<<i,x)@sumOfPrevDelays(N,maxN,i) :useVal(i)):combine(maxNrBits) with { | |
| // apply <op> to the last <N> values of <x>, where <N> is fixed | |
| fixedDelayOp = case { | |
| (1,x) => x; | |
| (N,x) => op(fixedDelayOp(N/2,x) , fixedDelayOp(N/2,x)@(N/2)); | |
| }; | |
| // The sum of all the delays that are applied to the previous blocks | |
| sumOfPrevDelays(N,maxN,0) = 0; | |
| sumOfPrevDelays(N,maxN,i) = (ba.subseq((allDelays(N,maxN)),0,i):>_) with { | |
| allDelays(N,maxN) = par(j, maxNrBits, (1<<j) * ba.take(j+1,(int2bin(N,maxN))) ); | |
| }; | |
| maxNrBits = int2nrOfBits(maxN); | |
| // Apply <op> to <N> parallel inputsignals | |
| combine(2) = op; | |
| combine(N) = op(combine(N-1),_); | |
| // Decide wether or not to use a certain value, based on N | |
| // Basically only the second <select2> is needed, | |
| // but this version also works for N == 0 | |
| // 'works' in this case means 'does the same as reduce | |
| useVal(i) = | |
| _<:select2((i==0) & (N==0) , | |
| select2( ba.take(i+1,(int2bin(N,maxN))) , disabledVal,_), | |
| _ | |
| ); | |
| // useVal(i) = | |
| // select2( ba.take(i+1,(int2bin(N,maxN))) , disabledVal,_); | |
| }; | |
| newSlidingReduce(N,maxN,op,disabledVal,x) = | |
| par(i,maxNrBits,fixedDelayOp(1<<i,x)@sumOfPrevDelays(N,maxN,i) ) | |
| :switched_combine(N,maxN) | |
| with { | |
| // apply <op> to the last <N> values of <x>, where <N> is fixed | |
| fixedDelayOp = case { | |
| (1,x) => x; | |
| (N,x) => op(fixedDelayOp(N/2,x) , fixedDelayOp(N/2,x)@(N/2)); | |
| }; | |
| // The sum of all the delays that are applied to the previous blocks | |
| sumOfPrevDelays(N,maxN,0) = 0; | |
| sumOfPrevDelays(N,maxN,i) = (ba.subseq((allDelays(N,maxN)),0,i):>_) with { | |
| allDelays(N,maxN) = par(j, maxNrBits, (1<<j) * ba.take(j+1,(int2bin(N,maxN))) ); | |
| }; | |
| // maxNrBits = int2nrOfBits(maxN); | |
| // Apply <op> to <N> parallel inputsignals | |
| combine(0) = 0:!; | |
| combine(1) = _; | |
| combine(2) = op; | |
| combine(N) = op(combine(N-1),_); | |
| isUsed(i,n) = int(floor(i/(1<<n))%2); | |
| nrOfUsedBits(i,n) = par(j,int2nrOfBits(n),isUsed(i,j)):>_; | |
| switched_combine(i,n) = | |
| si.bus(int2nrOfBits(n))<: | |
| par(j,n, | |
| par(k,int2nrOfBits(n),S(isUsed(j+1,k))):combine(nrOfUsedBits(j+1,n)) | |
| ) | |
| : par(j, n, _*(i==j+1)):>_ | |
| with { | |
| S(0) = !; | |
| S(1) = _; | |
| }; | |
| }; | |
| isSquare(N) = | |
| (sqrt(N) == floor(sqrt(N))); | |
| // N is a Fibonacci number ba.if and only ba.if 5 N^2 + 4 or 5 N^2 - 4 is a os.square number. | |
| // http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html#section5 | |
| isFibonacci(N) = | |
| isSquare((5*(pow(N,2)))+4) | |
| + | |
| isSquare((5*(pow(N,2)))-4); | |
| fixedFibonacciOps(N,op,disabledVal,x) = | |
| (seq(i, N+1, fixedFibonacciOpPart(i))) | |
| // fixedFibonacciOpPart(1) | |
| // :(!,_) | |
| with { | |
| fixedFibonacciOpPart(0) = disabledVal; | |
| fixedFibonacciOpPart(1) = _, x; | |
| fixedFibonacciOpPart(2) = _,_,x; | |
| fixedFibonacciOpPart(3) = si.bus(3),op(x,x'); | |
| fixedFibonacciOpPart(n) = | |
| (si.bus(n-2),((split,split):ro.interleave(2,2))) | |
| :(si.bus(n),op(_@Fibonacci2int(n-1),_)); | |
| split = _<:si.bus(2); | |
| }; | |
| fixedFibonacciOp(0,op,disabledVal,x) = disabledVal; | |
| fixedFibonacciOp(1,op,disabledVal,x) = x; | |
| fixedFibonacciOp(2,op,disabledVal,x) = x; | |
| fixedFibonacciOp(3,op,disabledVal,x) = op(x,x'); | |
| fixedFibonacciOp(N,op,disabledVal,x) = | |
| (x,disabledVal):(seq(i, N-2, fixedFibonacciOpPart(i+3))):(!,_) | |
| with { | |
| fixedFibonacciOpPart(3,valMinOne,val) = x,op(x,x'); | |
| fixedFibonacciOpPart(i,valMinOne,val) = val,op(val, valMinOne@Fibonacci2int(i-1)); | |
| }; | |
| // fixedFibonacciOp(0,op,disabledVal,x) = disabledVal; | |
| // fixedFibonacciOp(1,op,disabledVal,x) = x; | |
| // fixedFibonacciOp(2,op,disabledVal,x) = x; | |
| // fixedFibonacciOp(i,op,disabledVal,x) = op(fixedFibonacciOp((i-1),op,disabledVal,x), fixedFibonacciOp((i-2),op,disabledVal,x)@Fibonacci2int(i-1)); | |
| FibonacciOp(0,op,disabledVal,x) = disabledVal; | |
| FibonacciOp(1,op,disabledVal,x) = x; | |
| FibonacciOp(N,op,disabledVal,x) = | |
| op( fixedFibonacciOp( largestIndex ,op,disabledVal,x) , (FibonacciOp (rest,op,disabledVal,x)@largestInt) ) | |
| with { | |
| largestIndex = int2FibonacciIndexOfLargestPart(N); | |
| largestInt = Fibonacci2int(largestIndex); | |
| rest = max(0, N-largestInt); | |
| }; | |
| Phi = (sqrt(5) +1)/2; | |
| // int2FibonacciFloat(0) = 0; | |
| // int2FibonacciFloat(1) = 1; | |
| int2FibonacciFloat(N) = | |
| select2(N>0,0, | |
| select2(N>1,1, | |
| (log(N) + (log(5)/2))/log(Phi))); // source: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html#section6 | |
| int2FibonacciFloored(N) = int(floor(int2FibonacciFloat(N))); | |
| // int2FibonacciIndexOfLargestPart(0) = 0; | |
| int2FibonacciIndexOfLargestPart(N) = // int2fibonacci sometimes gives a sligthly too low value, so we test: | |
| select2((N == Fibonacci2int(int2FibonacciFloored(N+1))), int2FibonacciFloored(N),int2FibonacciFloored(N+1) ) | |
| ; // more info: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html#section6 | |
| // int2FibonacciIndexOfLargestPart(N) = // make a table. | |
| // int2FibonacciIndexOfLargestPartCalc(time) | |
| myBus(0)=0:!; // redefine si.bus so it can have 0 elements | |
| myBus(n) = si.bus(n); | |
| Fibonacci2intSeq(n,maxN) = | |
| // 1:seq(i, maxN-1 , int( _ * Phi )) | |
| 0<:(_,_):seq(i, maxN-1 , myBus(i+1) , Fibonacci2intPart(i+1)) | |
| : (si.bus(maxN),!) | |
| : (par(i, maxN, * (i==n))) | |
| // : ba.selector(1,maxN) | |
| :>_ | |
| with { | |
| // Fibonacci2intPart(0,prevFib)=0,(prevFib:!) <:(_,_); | |
| Fibonacci2intPart(1,prevFib)=1,(prevFib:!) <:(_,_); | |
| Fibonacci2intPart(2,prevFib)=1,(prevFib:!) <:(_,_); | |
| Fibonacci2intPart(n,prevFib) = ( prevFib * Phi ) : (_+0.5:floor) <:(_,_); // for all n > 1 | |
| }; | |
| Fibonacci2intRecurive(0)=0; | |
| Fibonacci2intRecurive(1)=1; | |
| Fibonacci2intRecurive(N) = Fibonacci2intRecurive(N-1) + Fibonacci2intRecurive(N-2); | |
| maxNrBits = 28; | |
| // maxNrBits = 4; | |
| // maxNrBits = 19; | |
| // works fine up to about 21 maxNrBits, but then eats all RAM trying to compile: | |
| // Fibonacci2int(N) = par(i,maxNrBits+1, Fibonacci2intRecurive(i)*(N==i)):>_; | |
| // so we do: | |
| time = int((+(1)~_ ) - 1); // 0,1,2,3,... | |
| // Fibonacci2int(i) = rdtable(maxNrBits+1, par(j,maxNrBits+1, Fibonacci2intRecurive(j)*(j==(int(time)))):>_, i:min(maxNrBits+1):max(0)); | |
| Fibonacci2int(i) = rdtable(maxNrBits+1, Fibonacci2intSeq(time,maxNrBits+1), i:min(maxNrBits+1):max(0)); | |
| OLDfibReduce(N,maxN,op,disabledVal,x) = | |
| (N,disabledVal,0):seq(i,maxNrBits+1, fibPart(i) ):(!,_,!) | |
| with { | |
| // i=3; | |
| isUsed(i,n) = (int2FibonacciIndexOfLargestPart(n)) == i; | |
| fibPart(i,rest,intermediateVal,sumOfDelays) = | |
| (max(0, rest-(isUsed((currentBit(i)),rest) * ( Fibonacci2int(largestIndex(Fibonacci2int(currentBit(i)))) )))), | |
| op(intermediateVal, (select2( isUsed((currentBit(i)),rest ), disabledVal, (fixedFibonacciOp( (currentBit(i)) , op,disabledVal,x)@sumOfDelays)))), | |
| (sumOfDelays+ (isUsed(currentBit(i),rest ) * largestInt(rest))); | |
| currentBit(i) = (maxNrBits-i); | |
| // maxNrBits = int2FibonacciIndexOfLargestPart(maxN)+1; | |
| largestIndex(n) = int2FibonacciIndexOfLargestPart(n); | |
| largestInt(n) = Fibonacci2int(largestIndex(n)); | |
| }; | |
| fibReduce(N,maxN,op,disabledVal,x) = | |
| ( | |
| disabledVal,N,0, | |
| (fixedFibonacciOps(maxNrBits,op,disabledVal,x):ro.cross(maxNrBits+1)) | |
| ) | |
| :seq(i,maxNrBits+1, fibPart(i) ):(_,!,!) | |
| with { | |
| // i=3; | |
| isUsed(i,n) = (int2FibonacciIndexOfLargestPart(n)) == i; | |
| fibPart(i,intermediateVal,rest,sumOfDelays) = | |
| op(intermediateVal, (select2( isUsed((currentBit(i)),rest ), disabledVal, (_@sumOfDelays)))), | |
| (max(0, rest-(isUsed((currentBit(i)),rest) * ( Fibonacci2int(largestIndex(Fibonacci2int(currentBit(i)))) )))), | |
| (sumOfDelays+ (isUsed(currentBit(i),rest ) * largestInt(rest))), | |
| myBus(maxNrBits-i); | |
| currentBit(i) = (maxNrBits-i); | |
| // maxNrBits = int2FibonacciIndexOfLargestPart(maxN)+1; | |
| largestIndex(n) = int2FibonacciIndexOfLargestPart(n); | |
| largestInt(n) = Fibonacci2int(largestIndex(n)); | |
| }; | |
| FibonacciSlidingReduce(N,maxN,op,disabledVal,x) = | |
| par(i,maxNrBits,fixedFibonacciOp(i)@sumOfPrevDelays(N,maxN,i) :useVal(i)):combine(maxNrBits) with { | |
| // The sum of all the delays that are applied to the previous blocks | |
| sumOfPrevDelays(N,maxN,0) = 0; | |
| sumOfPrevDelays(N,maxN,i) = (ba.subseq((allDelays(N,maxN)),0,i):>_) with { | |
| allDelays(N,maxN) = par(j, maxNrBits, j * ba.take(j+1,(int2bin(N,maxN))) ); | |
| }; | |
| // maxNrBits = int2FibonacciIndexOfLargestPart(maxN)+1; | |
| // Apply <op> to <N> parallel inputsignals | |
| combine(2) = op; | |
| combine(N) = op(combine(N-1),_); | |
| // Decide wether or not to use a certain value, based on N | |
| // Basically only the second <select2> is needed, | |
| // but this version also works for N == 0 | |
| // 'works' in this case means 'does the same as reduce | |
| useVal(i) = | |
| _<:select2((i==0) & (N==0) , | |
| select2( ba.take(i+1,(int2bin(N,maxN))) , disabledVal,_), | |
| _ | |
| ); | |
| // useVal(i) = | |
| // select2( ba.take(i+1,(int2bin(N,maxN))) , disabledVal,_); | |
| }; | |
| // // convert N into a list of ones and zeros | |
| int2bin(N,maxN) = par(i,int2nrOfBits(maxN),int(floor(N/(1<<i)))%2); | |
| // calculate how many ones and zeros are needed to represent maxN | |
| int2nrOfBits(0) = 0; | |
| int2nrOfBits(maxN) = int(floor(log(maxN)/log(2))+1); | |
| // slidingSumN(n,maxn) : the sliding sum of the last n input samples | |
| slidingSumN(n,maxn) = slidingReduce(n,maxn,+,0); | |
| // slidingMaxN(n,maxn) : the sliding max of the last n input samples | |
| slidingMaxN(n,maxn) = slidingReduce(n,maxn,max,-INFINITY); | |
| // slidingMinN(n,maxn) : the sliding min of the last n input samples | |
| slidingMinN(n,maxn) = slidingReduce(n,maxn,min,INFINITY); | |
| // slidingMeanN(n,maxn) : the sliding mean of the last n input samples | |
| slidingMeanN(n,maxn) = slidingSumN(n,maxn)/n; | |
| // slidingRMSn(n,maxn) : the sliding RMS of the last n input samples | |
| slidingRMSn(n,maxn) = pow(2):slidingMeanN(n,maxn) : sqrt; | |
| // maybe useful for math.lib: | |
| INFINITY = fconstant(float INFINITY, <math.h>); | |
| // bug in ba.subseq?: | |
| // no elements is undefined. | |
| // proposed solution: | |
| // ba.subseq((head, tail), 0, 0) = !; | |
| // ba.subseq((head, tail), 0, 1) = head; | |
| // ba.subseq((head, tail), 0, n) = head, ba.subseq(tail, 0, n-1); | |
| // ba.subseq((head, tail), p, n) = ba.subseq(tail, p-1, n); | |
| // ba.subseq(head, 0, n) = head; | |
| // | |
| /* | |
| * Copyright (C) 2014 Bart Brouns | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; version 2 of the License. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| */ | |
| /*some building blocks where taken from or inspired by compressor-basics.dsp by Sampo Savolainen*/ | |
| declare name "LazyLimiter"; | |
| declare author "Bart Brouns"; | |
| declare version "0.3.2"; | |
| declare copyright "(C) 2014 Bart Brouns"; | |
| import("stdfaust.lib"); | |
| SampleRate = 44100; | |
| //Lookahead and LookaheadPar need a power of 2 as a size | |
| // maxHoldTime = 4; // = 0.1ms, for looking at the block diagram | |
| // maxHoldTime = 128; // = 3ms | |
| //maxHoldTime = 256; // = 6ms | |
| // maxHoldTime = 512; // = 12ms, starts to sound OK, 84% cpu | |
| // maxHoldTime = 1024; // = 23ms, good sound, 185% CPU | |
| // maxHoldTime = 2048; // = 46ms, even less distortion, but can be less loud, 300% CPU | |
| // maxHoldTime = maxWinSize*nrWin;//8192 // = 186ms | |
| // maxHoldTime = maxWinSize*nrWin*2;//8192 // = 186ms | |
| // maxHoldTime = 4096; // = 92ms | |
| maxHoldTime = 8192; // = 186ms | |
| nrWin = 64; | |
| // nrWin = 4; | |
| // maxHoldTime = 32; // = | |
| maxWinSize = int(maxHoldTime/nrWin*SampleRate/44100); | |
| // maxWinSize = int(128*SampleRate/44100); | |
| // maxWinSize = int(4*SampleRate/44100); | |
| // nrWin = 128; | |
| // nrWin = 32; | |
| // nrWin = 256; | |
| // nrWin = 512; | |
| //with maxHoldTime = 1024, having maxAttackTime = 512 uses more cpu then maxAttackTime = 1024 | |
| // maxAttackTime = int(16*SampleRate/44100):min(maxHoldTime); | |
| maxAttackTime = int(1024*SampleRate/44100):min(maxHoldTime); | |
| //rmsMaxSize = 1024:min(maxHoldTime); | |
| rmsMaxSize = int(512*SampleRate/44100):min(maxHoldTime); | |
| main_group(x) = (hgroup("[1]", x)); | |
| minKnobGroup(x) = main_group(vgroup("[0]minimum gain reduction [tooltip: There will never be less gain reduction then what these setting dictate.]", x)); | |
| inGain = minKnobGroup(hslider("[0]input gain [unit:dB] [tooltip: input gain in dB ", 0, 0, 30, 0.1)):si.smooth(0.999) ; | |
| threshold = minKnobGroup(hslider("[1]threshold [unit:dB] [tooltip: maximum output level in dB]", -0.5, -60, 0, 0.1)); | |
| attack = minKnobGroup(hslider("[2]attack shape[tooltip: 0 gives a linear attack (slow), 1 a strongly exponential one (fast)]", 1 , 0, 1 , 0.001)); | |
| // release = minKnobGroup(hslider("[3]lin release[unit:dB/s][tooltip: maximum release rate]", 10, 6, 500 , 1)/SampleRate); | |
| minRelease = minKnobGroup(hslider("[3]minimum release time[unit:ms] [tooltip: minimum time in ms for the GR to go up][scale:log]",30, 1, 100, 0.1)/1000):time_ratio_release; | |
| // time_ratio_target_rel = minKnobGroup(hslider("[4]release shape", 1, 0.5, 5.0, 0.001)); | |
| // hardcoding link to 1 leads to much longer compilation times, yet similar cpu-usage, while one would expect less cpu usage and maybe shorter compilation time | |
| link = minKnobGroup(hslider("[5]stereo link[tooltip: 0 means independent, 1 fully linked]", 1, 0, 1 , 0.001)); | |
| dynHoldKnobGroup(x) = main_group(vgroup("[1]dynamic hold [tooltip: the GR will not go up ba.if it has to be back here within the hold time]", x)); | |
| //hardcoding holdTime to maxHoldTime uses more cpu then having a fader! | |
| maxHoldMs = maxHoldTime*1000/SampleRate; | |
| // holdTime = 0; | |
| holdTime = int(dynHoldKnobGroup(hslider("[0]maximum hold time[unit:ms] [tooltip: maximum hold time in ms][scale:log]", maxHoldMs, 0.1, maxHoldMs ,0.1))/1000*SampleRate); | |
| // minHoldTime = int(dynHoldKnobGroup(hslider("[1]minimum hold time[unit:ms] [tooltip: minimum hold time in ms]", 0, 0, maxHoldTime ,1))); | |
| minHoldTime = int(dynHoldKnobGroup(hslider("[1]minimum hold time[unit:ms] [tooltip: minimum hold time in ms][scale:log]", 30, 0.1, maxHoldMs ,0.1))/1000*SampleRate); | |
| dynHold = dynHoldKnobGroup(hslider("[2]dynHold[tooltip: shorten the hold time when the GR is below AVG]", 0.5, 0, 1 , 0.001))*20; | |
| dynHoldPow = dynHoldKnobGroup(hslider("[3]dynHoldPow[tooltip: shape the curve of the hold time]", 2, 0.1, 10 , 0.1)); | |
| dynHoldDiv = dynHoldKnobGroup(hslider("[4]dynHoldDiv[tooltip: scale the curve of the hold time]", 6, 0.1, 24 , 0.1)); | |
| musicRelKnobGroup(x) = main_group(vgroup("[2] musical release [tooltip: this section fine tunes the release to sound musical]", x)); | |
| baserelease = musicRelKnobGroup(hslider("[0]base release rate[unit:dB/s][tooltip: release rate when the GR is at AVG, in dB/s][scale:log]", 30, 0.1, 6000 , 0.1)/SampleRate); | |
| transientSpeed = musicRelKnobGroup(hslider("[1]transient speed[tooltip: speed up the release when the GR is below AVG ]", 0.5, 0, 1, 0.001)); | |
| antiPump = musicRelKnobGroup(hslider("[2]anti pump[tooltip: slow down the release when the GR is above AVG ]", 0.5, 0, 1, 0.001)); | |
| attackAVG = musicRelKnobGroup(time_ratio_attack(hslider("[3] AVG attack [unit:ms] [tooltip: time in ms for the AVG to go down][scale:log]", 1400, 50, 5000, 1)/1000)) ; | |
| releaseAVG = musicRelKnobGroup(time_ratio_attack(hslider("[4] AVG release [unit:ms] [tooltip: time in ms for the AVG to go up][scale:log]", 300, 50, 5000, 1)/1000)) ; | |
| GRmeter_group(x) = main_group(hgroup("[3] GR [tooltip: gain reduction in dB]", x)); | |
| meter = GRmeter_group( _<:(_,(_:min(0):max(-20):( (vbargraph("[unit:dB]", -20, 0))))):attach); | |
| AVGmeter_group(x) = (main_group(hgroup("[4] AVG [tooltip: average gain reduction in dB]", x))); | |
| avgMeter = AVGmeter_group((_<:(_,(_:min(0):max(-20):( (vbargraph("[unit:dB]", -20, 0))))):attach)); | |
| DHmeter_group(x) = (main_group(hgroup("[5] HoldTime [tooltip: hold time in ms]", x))); | |
| dhMeter = DHmeter_group((_<:(_,((_*1000/SampleRate):min(maxHoldMs):max(0):( (vbargraph("[unit: ms]", 0, maxHoldMs))))):attach)); | |
| // dhMeter = DHmeter_group((_<:(_,((_*1000/SampleRate*nrWin*2):min(maxHoldMs):max(0):( (vbargraph("[unit: ms]", 0, maxHoldMs))))):attach)); | |
| mymeter = meter_group(_<:(_, ( (vbargraph("[2]SD[tooltip: slow down amount]", 0, 0.5)))):attach); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment