├── docs ├── attack.gif ├── plotAttack.gnu ├── blockDiagram.dsp ├── blockDiagram-svg │ ├── releaseEnvelope-0x9432fe0.svg │ ├── releaseEnvelope-0x9a09038.svg │ ├── currentLevel-0x9430ff0.svg │ ├── currentLevel-0x9a07048.svg │ ├── GainCalculator-0x94330d0.svg │ ├── GainCalculator-0x9a09128.svg │ ├── minimumGainReduc-0x9432f48.svg │ ├── minimumGainReduc-0x9a08fa0.svg │ ├── linear2db-0x9431be0.svg │ ├── linear2db-0x9a07c38.svg │ ├── SR-0x9434d28.svg │ ├── SR-0x9a0ad80.svg │ ├── db2linear-0x94373b8.svg │ ├── db2linear-0x9a0d410.svg │ ├── p-0x94356f0.svg │ ├── p-0x9a0b748.svg │ └── process.svg └── HardWorkingLimiterMono.dsp ├── .gitignore ├── Makefile ├── LazyLimiter.dsp ├── experiments ├── simpleLimiter.dsp ├── LLng2.dsp └── LLng.dsp ├── GUI.lib ├── README.md └── LICENSE /docs/attack.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnetophon/LazyLimiter/HEAD/docs/attack.gif -------------------------------------------------------------------------------- /docs/plotAttack.gnu: -------------------------------------------------------------------------------- 1 | reset 2 | set term gif animate 3 | set output "attack.gif" 4 | 5 | attackShaper(x,attack)= tanh(x**((attack+1)**7)*(attack*5+.1))/tanh(attack*5+.1) 6 | 7 | n=50 #n frames 8 | dt=0.02 9 | set xrange [0:1] 10 | do for [i=0:n]{ 11 | plot attackShaper(x,i*dt) title sprintf("attack percentage: %i",i*2) 12 | } 13 | set output 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## FAUST 3 | ################# 4 | 5 | *.cpp 6 | *.tmp.cpp 7 | *.tmp.dsp 8 | *.temp-svg 9 | *.0.temp-svg 10 | *.1.temp-svg 11 | *.2.temp-svg 12 | *.*~ 13 | LazyLimiter 14 | LazyLimiter-svg 15 | LazyLimiter.lv2 16 | /faust-graphs.html 17 | /LLng-svg/GR-0x7f0b72503be0.svg 18 | /LLng-svg/*.svg 19 | /LLng 20 | /faust.* 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /usr/bin/env bash 2 | DSP_FILES := $(wildcard *.dsp) 3 | JAQT_TARGETS := $(basename $(DSP_FILES)) 4 | LV2_TARGETS := $(addsuffix .lv2, $(basename $(DSP_FILES))) 5 | DESTDIR ?= 6 | PREFIX ?= /usr/local 7 | BINDIR ?= $(DESTDIR)$(PREFIX)/bin 8 | LIBDIR ?= $(DESTDIR)$(PREFIX)/lib 9 | LV2_URI_PREFIX := https://magnetophon.nl 10 | 11 | 12 | .PHONY: all jaqt lv2 install install-jaqt install-lv2 clean 13 | 14 | all: jaqt lv2 15 | 16 | jaqt: $(JAQT_TARGETS) 17 | 18 | lv2: $(LV2_TARGETS) 19 | 20 | %: %.dsp 21 | faust2jaqt -time -vec -t -1 $< 22 | 23 | %.lv2: %.dsp 24 | faust2lv2 -uri-prefix $(LV2_URI_PREFIX) -time -vec -gui -t -1 $< 25 | 26 | install-jaqt: $(JAQT_TARGETS) 27 | install -d $(BINDIR) 28 | $(foreach f, $(JAQT_TARGETS), install -m 755 $(f) $(BINDIR)/$(notdir $(f));) 29 | 30 | install-lv2: $(LV2_TARGETS) 31 | install -d $(LIBDIR)/lv2 32 | $(foreach p,$(LV2_TARGETS),cp -rfd $(p) $(LIBDIR)/lv2;) 33 | 34 | install: install-jaqt install-lv2 35 | 36 | clean: 37 | rm -rf $(JAQT_TARGETS) $(LV2_TARGETS) 38 | -------------------------------------------------------------------------------- /docs/blockDiagram.dsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Bart Brouns 3 | * This program is free software; you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License as published by 5 | * the Free Software Foundation; version 2 of the License. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU General Public License for more details. 11 | */ 12 | 13 | /*some building blocks where taken from or inspired by compressor-basics.dsp by Sampo Savolainen*/ 14 | 15 | declare name "LazyLimiter blockdiagram"; 16 | declare version "0.3.3"; 17 | declare author "Bart Brouns"; 18 | declare license "GPL-2.0-only"; 19 | declare copyright "© 2023 Bart Brouns "; 20 | 21 | import("stdfaust.lib"); //for ba.linear2db 22 | import("stdfaust.lib"); //for an.amp_follower 23 | import ("../GUI.lib"); 24 | import ("../LazyLimiter.lib"); 25 | 26 | process(audio) = GainCalculator(audio) : ba.db2linear * audio@LookAheadTime; 27 | 28 | // just for visual indication in the blockdiagram. you can't actually change it and expect the code to work. 29 | LookAheadTime = 4; 30 | 31 | GainCalculator(audio) = (minimumGainReduction(audio) : releaseEnvelope)~_; 32 | 33 | // this extra abstraction layer is needed to make the feedback loop work. 34 | // not so great for educational purposes. 35 | minimumGainReduction(audio,lastdown) = ((attackGainReduction(audio) , hold(audio,lastdown)): min); 36 | 37 | attackGainReduction(audio) = 38 | ( 39 | currentdown(audio)@1*(1/4), 40 | currentdown(audio)@2*(2/4), 41 | currentdown(audio)@3*(3/4), 42 | currentdown(audio)@4*(4/4) 43 | ): (min,min):min; 44 | 45 | hold(audio,lastdown) = 46 | ( 47 | (currentdown(audio)@(0):max(lastdown)), 48 | (currentdown(audio)@(1):max(lastdown)), 49 | (currentdown(audio)@(2):max(lastdown)), 50 | (currentdown(audio)@(3):max(lastdown)) 51 | ): (min,min):min; 52 | 53 | release = 0.1; 54 | releaseEnvelope = an.amp_follower(release); 55 | -------------------------------------------------------------------------------- /LazyLimiter.dsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Bart Brouns 3 | * This program is free software; you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License as published by 5 | * the Free Software Foundation; version 2 of the License. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU General Public License for more details. 11 | */ 12 | 13 | /*some building blocks where taken from or inspired by compressor-basics.dsp by Sampo Savolainen*/ 14 | 15 | declare name "LazyLimiter"; 16 | declare version "0.3.3"; 17 | declare author "Bart Brouns"; 18 | declare license "GPL-2.0-only"; 19 | declare copyright "© 2023 Bart Brouns "; 20 | 21 | import ("GUI.lib"); 22 | import ("LazyLimiter.lib"); 23 | 24 | //process = stereoGainComputer; 25 | //process = naiveStereoLimiter; 26 | //process = ( 0:seq(i,maxHoldTime,(currentdown(x)@(i):max(lastdown)),_: min )); 27 | //process(x,y) = (((Lookahead(x):releaseEnv(minRelease)),(Lookahead(y):releaseEnv(minRelease))):min)~(+(inGain@maxHoldTime)):meter:ba.db2linear<:(_*x@maxHoldTime,_*y@maxHoldTime); 28 | 29 | //(((Lookahead(x):releaseEnv(minRelease)),(Lookahead(y):releaseEnv(minRelease))):min)~(_<:(_,_))+(inGain@maxHoldTime):meter:ba.db2linear<:(_*x@maxHoldTime,_*y@maxHoldTime); 30 | //simpleStereoLimiter; 31 | //process = slidemax(5,8); 32 | //process = minimalStereoLimiter; 33 | 34 | /*process(x) =*/ 35 | /*0: seq(i,maxAttackTime,*/ 36 | /*(currentdown(x)@(i+1-maxAttackTime+maxHoldTime))*/ 37 | /**(((i+1)/maxAttackTime))*/ 38 | /*,_: min*/ 39 | /*);*/ 40 | /*process =*/ 41 | /*(0,_):seq(i,maxAttackTime,*/ 42 | /*(*/ 43 | /*(_,*/ 44 | /*(*/ 45 | /*((_')<:(_,_)):*/ 46 | /*(*/ 47 | /*(_ *(((i+1)/maxAttackTime)))*/ 48 | /*,_*/ 49 | /*)*/ 50 | /*)*/ 51 | /*)*/ 52 | /*:min,_*/ 53 | /*)*/ 54 | /*) ;*/ 55 | 56 | /*process(x) = pmin(currentdown(x),0,4)*/ 57 | /*with {*/ 58 | /*pmin(del,mini,1) =del', ((del *((maxAttackTime/maxAttackTime))) ,mini : min);*/ 59 | /*pmin(del,mini,k) = del,(((pmin(del@(1),mini,(k-1))):(_*(((maxAttackTime-k+1)/maxAttackTime))),_) : (min));*/ 60 | /*};*/ 61 | 62 | //process =avgMeter(inGain); 63 | //process(x,y) = (stereoGainComputerHalf(x,y),stereoGainComputerHalf(y,x))~(ro.cross(2)); 64 | //(stereoGainComputerHalf(x,y),stereoGainComputerHalf(y,x))~((_,_ <: !,_,_,!),_) 65 | process = stereoLimiter; 66 | // process = naiveStereoLimiter; 67 | // process = minimalStereoLimiter; 68 | // process(x) = block_hold(x); 69 | // process(x) = Yann_hold(x); 70 | // process = ((fixed_hold(maxWinSize)@(5):max(lastdown)),_): min ; 71 | // Lookahead(x,lastdown,avgLevel) = 72 | 73 | //(((_,(_,((_,_):Lookahead(y)):min)):linearXfade(link)):releaseEnv(minRelease):rateLimit); 74 | 75 | //(((_,(_<:_,_)):(Lookahead(x)<:_,_),(_<:_,_)):ro.interleave(2,2)); 76 | 77 | //(((_,(_,Lookahead(y,prevy,avgLevely):min)):linearXfade(link)):releaseEnv(minRelease):rateLimit); 78 | 79 | //GOOD: 80 | //process(x,y,prevy) = 81 | /*(*/ 82 | /*(((_,(_,((prevy,_):Lookahead(y)):min)):linearXfade(link)):releaseEnv(minRelease):(rateLimit))*/ 83 | /*~(((_<:(_,_)),_):((ro.cross(2):Lookahead(x)<:_,_),_))*/ 84 | /*):(_,!);*/ 85 | 86 | /*(*/ 87 | /*(((_,(_,(prevy:Lookahead(y,_)):min)):linearXfade(link)):releaseEnv(minRelease):rateLimit)*/ 88 | /*~((Lookahead(x)<:_,_),_)*/ 89 | /*);*/ 90 | 91 | /*(*/ 92 | /*(((_,(_,((prevy:Lookahead(y),_):(_,!)):min)):linearXfade(link)):releaseEnv(minRelease):rateLimit)*/ 93 | /*~((Lookahead(x)<:_,_),_):(_,!)*/ 94 | /*);*/ 95 | 96 | //process(x)= rdtable(maxAttackTime, (5) ,int(x*maxAttackTime)); 97 | //process(x)= rdtable(int(maxAttackTime), ma.tanh((6/maxAttackTime):pow(1:attackScale)),int(x*maxAttackTime)); 98 | /*process(x)= rdtable(maxAttackTime, ( ma.tanh((6/maxAttackTime):pow(attack:attackScale)*(attack*5+.1))/ma.tanh(attack*5+.1)),int(x*maxAttackTime))*/ 99 | /*with { attack = 1; };*/ 100 | /*attackScale(x) = (x+1):pow(7);*/ 101 | //process = rateLimiter; 102 | //process = SMOOTH(3,4); 103 | /*process(x) = 0:seq(i,maxHoldTime,*/ 104 | /*(((i+1)>(maxHoldTime-holdTime))*(currentdown(x)@(i):max(lastdown))),_: min */ 105 | /*)*/ 106 | /*with { maxHoldTime = 1024; holdTime = 4; lastdown = no.noise;};*/ 107 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/releaseEnvelope-0x9432fe0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | abs 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | env 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | releaseEnvelope 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/releaseEnvelope-0x9a09038.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | abs 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | env 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | releaseEnvelope 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/currentLevel-0x9430ff0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | audio 11 | 12 | 13 | 14 | abs 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | linear2db 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | currentLevel(x1) 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/currentLevel-0x9a07048.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | audio 11 | 12 | 13 | 14 | abs 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | linear2db 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | currentLevel(x1) 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /docs/HardWorkingLimiterMono.dsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Bart Brouns 3 | * This program is free software; you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License as published by 5 | * the Free Software Foundation; version 2 of the License. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU General Public License for more details. 11 | */ 12 | 13 | /*some building blocks where taken from or inspired on compressor-basics.dsp by Sampo Savolainen*/ 14 | 15 | declare name "HardWorkingLimiterMono"; 16 | declare author "Bart Brouns"; 17 | declare version "0.1"; 18 | declare copyright "© 2023 Bart Brouns "; 19 | 20 | import ("LookaheadLimiter.lib"); 21 | 22 | //LookaheadPar needs a power of 2 as a size 23 | //the following maxHoldTime related bug-comments only manifest with another implementation of "currentdown" 24 | //maxHoldTime = 4; // = 0.1ms 25 | //maxHoldTime = 128; // = 3ms 26 | //maxHoldTime = 256; // = 6ms //no overs till here, independent of -vec compile option 27 | //maxHoldTime = 512; // = 12ms //no overs till here, but only without -vec or with both -vec and -lv 1 28 | maxHoldTime = 1024; // = 23ms //always gives overs with par lookahead, never gives overs with seq lookahead. Unfortunately, seq @ 1024 doesn't like ratelimiter: as soon as it is faded in, we get silence. 29 | // seq @ < 1024 works fine... 30 | //maxHoldTime = 2048; // = 46ms 31 | //maxHoldTime = 8192; // = 186ms 32 | 33 | //with maxHoldTime = 1024, having maxAttackTime = 512 uses more cpu then maxAttackTime = 1024 34 | maxAttackTime = 1024:min(maxHoldTime); 35 | 36 | rmsMaxSize = 4096; 37 | 38 | time_ratio_target = 1.5; 39 | 40 | //time_ratio_target_atk = hslider("attack time ratio", 1.5, 0.2, 10.0, 0.1); 41 | time_ratio_target_atk = 8.0; 42 | //time_ratio_target_rel = 4.0; // this could be too slow 43 | //time_ratio_target_rel = 1.5; 44 | 45 | main_group(x) = (hgroup("[1]", x)); 46 | 47 | meter_group(x) = main_group(hgroup("[1]", x)); 48 | knob_group(x) = main_group(vgroup("[2]", x)); 49 | 50 | detector_group(x) = knob_group(vgroup("[0]detector", x)); 51 | post_group(x) = knob_group(vgroup("[1]", x)); 52 | ratelimit_group(x) = knob_group(vgroup("[2]ratelimit", x)); 53 | 54 | shape_group(x) = post_group(vgroup("[0]shape", x)); 55 | out_group(x) = post_group(vgroup("[2]", x)); 56 | 57 | envelop = abs : max ~ -(100.0/ma.SR) ; 58 | 59 | meter = meter_group(_<:(_, ( (vbargraph("[1]GR[unit:dB][tooltip: gain reduction in dB]", -60, 0)))):attach); 60 | mtr = meter_group(_<:(_, ( (vbargraph("punch", 0, 128)))):attach); 61 | mymeter = meter_group(_<:(_, ( (vbargraph("[1][unit:dB][tooltip: input level in dB]", 0, 144)))):attach); 62 | 63 | threshold = knob_group(hslider("[0]threshold [unit:dB] [tooltip: maximum output level]", -12, -60, 0, 0.1)); 64 | attack = knob_group(hslider("[1]attack shape[tooltip: attack speed]", 0.841 , 0, 1 , 0.001)); 65 | //hardcoding holdTime to maxHoldTime uses more cpu then having a fader! 66 | holdTime = knob_group(hslider("[2]hold time[tooltip: maximum hold time]", maxHoldTime, 0, maxHoldTime , 1)); 67 | release = knob_group(hslider("[3]lin release[unit:dB/s][tooltip: maximum release rate]", 113, 6, 500 , 1)/ma.SR); 68 | logRelease = knob_group(hslider("[4]release time[unit:ms] [tooltip: Time constant in ms (1/e smoothing time) for the compression gain to approach (exponentially) a new higher target level (the compression 'releasing')]",150, 0.1, 500, 0.1)/1000):time_ratio_release; 69 | time_ratio_target_rel = knob_group(hslider("[5]release shape", 0.5, 0.2, 5.0, 0.1)); 70 | // 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 71 | link = knob_group(hslider("[6]stereo link[tooltip: ]", 1, 0, 1 , 0.001)); 72 | 73 | ratelimit = knob_group(hslider("[0]ratelimit amount[tooltip: ]", 1, 0, 1 , 0.001)); 74 | 75 | mult = ratelimit_group(hslider("[3]mult[tooltip: ]", 1 , 0.1,20, 0.1)); 76 | 77 | IMattack = ratelimit_group(time_ratio_attack(hslider("[6] Attack [unit:ms] [tooltip: Time constant in ms (1/e smoothing time) for the compression gain to approach (exponentially) a new lower target level (the compression `kicking in')]", 23.7, 0.1, 500, 0.1)/1000)) ; 78 | IMrelease = ratelimit_group(time_ratio_release(hslider("[7] Release [unit:ms] [tooltip: Time constant in ms (1/e smoothing time) for the compression gain to approach (exponentially) a new higher target level (the compression 'releasing')]",0.1, 0.1, 2000, 0.1)/1000)); 79 | 80 | maxChange = hslider("[0]maxChange[tooltip: ]", 84 , 1, 144 , 1); 81 | decayPower = ratelimit_group(hslider("[4]decayPower[tooltip: ]", 10, 0, 10 , 0.001)); 82 | decayMult = ratelimit_group(hslider("[3]decayMult[tooltip: ]", 200 , 0,500, 1))*10; 83 | 84 | IMpower = ratelimit_group(hslider("[1]IMpower[tooltip: ]", -64 , -128, 0 , 0.001)):limPowerScale; 85 | IM_size = ratelimit_group(hslider("[5]IM_size[tooltip: ]",108, 1, rmsMaxSize, 1)*44100/ma.SR); //0.0005 * min(192000.0, max(22050.0, ma.SR)); 86 | 87 | process = limiter; 88 | //process = naiveStereoLimiter; 89 | //process = simpleStereoLimiter; 90 | //process = stereoLimiter; 91 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/GainCalculator-0x94330d0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | minimumGainReduction(x1) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | releaseEnvelope 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | GainCalculator(x1) 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/GainCalculator-0x9a09128.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | minimumGainReduction(x1) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | releaseEnvelope 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | GainCalculator(x1) 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /experiments/simpleLimiter.dsp: -------------------------------------------------------------------------------- 1 | import("stdfaust.lib"); 2 | process = GR@totalLatency,smoothGRl(GR); 3 | 4 | // maxAttack = 128; 5 | 6 | // TODO: 7 | // get to GR earlier, then for the last bit, xfade from dir to pos. 8 | // - ramp of 64 samples towards GR:slidingMin(32) 9 | // 10 | //better separate out the attack and release 11 | // release: xfade from dir 12 | // trigger of each ramp compares itself to the un-shaped version and to the shaped versions of the others 13 | 14 | // make separate transient stage and slow release, loke pro-L: 15 | // Apart from the fast 'transient' stage, the limiter has a slower 'release' stage that responds to the overall dynamics of the incoming audio. 16 | // The Attack and Release knobs control how quickly and heavily the release stage sets in. Shorter attack times will allow the release stage 17 | // to set in sooner; longer release times will cause it to have more effect. 18 | // In general, short attack times and long release times are safer and cleaner, but they can also cause pumping and reduce clarity. On the 19 | 20 | 21 | t= hslider("time", 0.1, 0, 1, 0.001); 22 | noiseLVL = hslider("noise", 0, 0, 1, 0.01); 23 | rate = hslider("rate", 20, 10, 20000, 10); 24 | 25 | // expo = 3; // for block diagram 26 | // expo = 6; // 64 samples, 1.3 ms at 48k 27 | expo = 7; // 128 samples, 2.6 ms at 48k 28 | // expo = 8; // 256 samples, 5.3 ms at 48k, the max lookahead of fabfilter pro-L is 5ms 29 | // TODO sAndH(reset) parameters 30 | smoothGRl(GR) = FB~(_,_) :(_,!) 31 | with { 32 | FB(prev,oldDownSpeed) = 33 | par(i, expo, fade(i)):ro.interleave(2,expo) 34 | :((minN(expo) 35 | :min(GR@totalLatency)//brute force fade of 64 samples not needed for binary tree attack ? 36 | :attOrRelease(GR):smoothie(GR)),maxN(expo)) 37 | // ,( rampOne(0) * speed(0)) 38 | with { 39 | new(i) = lowestGRblock(GR,size(i))@(totalLatency-size(i)); 40 | newH(i) = new(i):ba.sAndH( reset(i)| (attPhase(prev)==0) ); 41 | prevH(i) = prev:ba.sAndH( reset(i)| (attPhase(prev)==0) ); 42 | reset(i) = 43 | (newDownSpeed(i) > currentDownSpeed) | (prev<=(lowestGRblock(GR,size(0))')); 44 | // (newDownSpeed(i) > currentDownSpeed) ; 45 | fade(i) = 46 | crossfade(currentPosAndDir(i),newH(i) ,ramp(size(i),reset(i)):rampShaper(i)) // crossfade from current direction to new position 47 | // crossfade(prevH(i),newH(i) ,ramp(size(i),reset(i)):rampShaper(i)) // TODO crossfade from current direction to new position 48 | // crossfade(prevH(i),newH(i) ,ramp(size(i),reset(i))) // TODO crossfade from current direction to new position 49 | // :min(GR@totalLatency)//brute force fade of 64 samples not needed for binary tree attack ? 50 | // sample and hold oldDownSpeed: 51 | , (select2((newDownSpeed(i) > currentDownSpeed),currentDownSpeed ,newDownSpeed(i))); 52 | rampShaper(i) = _:pow(power(i))*mult(i):max(smallestRamp(reset(i))); 53 | power(i) = LinArrayParametricMid(hslider("power bottom", 1, 0.01, 10, 0.01),hslider("power mid", 1, 0.01, 10, 0.01),hslider("power band", (expo/2)-1, 0, expo, 1),hslider("power top", 0.5, 0.01, 10, 0.01),i,expo); 54 | mult(i) = 1;// LinArrayParametricMid(hslider("mult bottom", 1, 0.001, 1 ,0.001),hslider("mult mid", 1, 0.001, 1 ,0.001),hslider("mult band", (expo/2)-1, 0, expo, 1),hslider("mult top", 1, 0.001, 1 ,0.001),i,expo); 55 | // currentPosAndDir(i) = prevH(i)-( ramp(size(i),reset(i)) * hslider("ramp", 0, 0, 1, 0.01) * (prevH(i)'-prevH(i))); 56 | currentPosAndDir(i) = prevH(i)-( rampOne(i) * speed(i)); 57 | rampOne(i) = (select2(reset(i),_+1,1):min(size(i)))~_; 58 | // speed(i) = (prev-prev'):ba.sAndH( reset(i)| (attPhase(prev)==0) ); 59 | speed(i) = (prev-prev'):ba.sAndH( select2( checkbox("speed(i)"),reset(i), reset(i)| (attPhase(prev)==0) )); 60 | // speed(i) = (prev-prev'):ba.sAndH( reset(i) ); 61 | // newDownSpeed(i) = (select2(checkbox("newdown"),prev,(prev'-currentDownSpeed)) -new(i) )/size(i); 62 | newDownSpeed(i) = (prev -new(i) )/size(i); 63 | currentDownSpeed = oldDownSpeed*(speedIsZero==0); 64 | // speedIsZero = (prev==GR@(totalLatency)) ; // TODO: needs more checks, not attack 65 | // speedIsZero = (prev==prev') ; 66 | speedIsZero = select2(checkbox("speedIsZero"),(prev==GR@(totalLatency)),(prev==prev')); 67 | // speedIsZero = select2(checkbox("speed"),(prev==GR@(totalLatency+1)),(prev==prev')); 68 | size(i) = pow(2,(expo-i)); 69 | // attOrRelease(GR,newGR) = select2(attPhase(prev) ,lowestGRblock(GR,size(0)),newGR); 70 | attOrRelease(GR,newGR) = select2(newGR<=lowestGRblock(GR,size(0)),newGR,lowestGRblock(GR,size(0))); 71 | // smoothie(GR) = crossfade( lowestGRblock(GR,size(0)) , (2*prev)-prev', hslider("smoothie", 0, 0, 1, 0.01):pow(0.5): (_<:select2(prev 0:!; 100 | (op,1) => _; 101 | (op,2) => op; 102 | (op,N) => (opWithNInputs(op,N-1),_) : op; 103 | }; 104 | }; 105 | 106 | 107 | 108 | LinArrayParametricMid(bottom,mid,band,top,element,nrElements) = 109 | select2(band<=element +1,midToBottomVal(element), midToTopVal(element)) 110 | with { 111 | midToBottomVal(element) = (midToBottom(element)*bottom) + (((midToBottom(element)*-1)+1)*mid); 112 | midToBottom(element) = (band-(element +1))/(band-1); 113 | 114 | midToTopVal(element) = (midToTop(element)*top) + (((midToTop(element)*-1)+1)*mid); 115 | midToTop(element) = (element +1-band)/(nrElements-band); 116 | }; 117 | 118 | attackBruteForce = 119 | case { 120 | (GR,0) => GR/(maxAttack+1); 121 | (GR,n) => min(attack(GR,n-1), GR@n/(maxAttack-n+1)); 122 | }; 123 | 124 | // totalLatency = maxAttack; 125 | totalLatency = pow(2,expo); 126 | GR = vgroup("GR", no.lfnoise0(totalLatency * 8 *t * (no.lfnoise0(totalLatency/2):max(0.1) )):pow(3)*(1-noiseLVL) +(no.lfnoise(rate):pow(3) *noiseLVL):min(0)) ; 127 | -------------------------------------------------------------------------------- /GUI.lib: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Bart Brouns 3 | * This program is free software; you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License as published by 5 | * the Free Software Foundation; version 2 of the License. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU General Public License for more details. 11 | */ 12 | 13 | /*some building blocks where taken from or inspired by compressor-basics.dsp by Sampo Savolainen*/ 14 | 15 | declare name "LazyLimiter GUI"; 16 | declare version "0.3.3"; 17 | declare author "Bart Brouns"; 18 | declare license "GPL-2.0-only"; 19 | declare copyright "© 2023 Bart Brouns "; 20 | 21 | 22 | import("stdfaust.lib"); 23 | 24 | SampleRate = 44100; 25 | //Lookahead and LookaheadPar need a power of 2 as a size 26 | // maxHoldTime = 4; // = 0.1ms, for looking at the block diagram 27 | // maxHoldTime = 128; // = 3ms 28 | //maxHoldTime = 256; // = 6ms 29 | // maxHoldTime = 512; // = 12ms, starts to sound OK, 84% cpu 30 | // maxHoldTime = 1024; // = 23ms, good sound, 185% CPU 31 | // maxHoldTime = 2048; // = 46ms, even less distortion, but can be less loud, 300% CPU 32 | // maxHoldTime = maxWinSize*nrWin;//8192 // = 186ms 33 | // maxHoldTime = maxWinSize*nrWin*2;//8192 // = 186ms 34 | // maxHoldTime = 4096; // = 92ms 35 | maxHoldTime = 8192; // = 186ms 36 | nrWin = 64; 37 | // nrWin = 4; 38 | // maxHoldTime = 32; // = 39 | maxWinSize = int(maxHoldTime/nrWin*SampleRate/44100); 40 | // maxWinSize = int(128*SampleRate/44100); 41 | // maxWinSize = int(4*SampleRate/44100); 42 | // nrWin = 128; 43 | // nrWin = 32; 44 | // nrWin = 256; 45 | // nrWin = 512; 46 | //with maxHoldTime = 1024, having maxAttackTime = 512 uses more cpu then maxAttackTime = 1024 47 | // maxAttackTime = int(16*SampleRate/44100):min(maxHoldTime); 48 | maxAttackTime = int(1024*SampleRate/44100):min(maxHoldTime); 49 | 50 | //rmsMaxSize = 1024:min(maxHoldTime); 51 | rmsMaxSize = int(512*SampleRate/44100):min(maxHoldTime); 52 | 53 | main_group(x) = (hgroup("[1]", x)); 54 | 55 | minKnobGroup(x) = main_group(vgroup("[0]minimum gain reduction [tooltip: There will never be less gain reduction then what these setting dictate.]", x)); 56 | inGain = minKnobGroup(hslider("[0]input gain [unit:dB] [tooltip: input gain in dB ", 0, 0, 30, 0.1)):si.smooth(0.999) ; 57 | threshold = minKnobGroup(hslider("[1]threshold [unit:dB] [tooltip: maximum output level in dB]", -0.5, -60, 0, 0.1)); 58 | attack = minKnobGroup(hslider("[2]attack shape[tooltip: 0 gives a linear attack (slow), 1 a strongly exponential one (fast)]", 1 , 0, 1 , 0.001)); 59 | // release = minKnobGroup(hslider("[3]lin release[unit:dB/s][tooltip: maximum release rate]", 10, 6, 500 , 1)/SampleRate); 60 | 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; 61 | // time_ratio_target_rel = minKnobGroup(hslider("[4]release shape", 1, 0.5, 5.0, 0.001)); 62 | // 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 63 | link = minKnobGroup(hslider("[5]stereo link[tooltip: 0 means independent, 1 fully linked]", 1, 0, 1 , 0.001)); 64 | 65 | 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)); 66 | //hardcoding holdTime to maxHoldTime uses more cpu then having a fader! 67 | maxHoldMs = maxHoldTime*1000/SampleRate; 68 | // holdTime = 0; 69 | 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); 70 | // minHoldTime = int(dynHoldKnobGroup(hslider("[1]minimum hold time[unit:ms] [tooltip: minimum hold time in ms]", 0, 0, maxHoldTime ,1))); 71 | 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); 72 | dynHold = dynHoldKnobGroup(hslider("[2]dynHold[tooltip: shorten the hold time when the GR is below AVG]", 0.5, 0, 1 , 0.001))*20; 73 | dynHoldPow = dynHoldKnobGroup(hslider("[3]dynHoldPow[tooltip: shape the curve of the hold time]", 2, 0.1, 10 , 0.1)); 74 | dynHoldDiv = dynHoldKnobGroup(hslider("[4]dynHoldDiv[tooltip: scale the curve of the hold time]", 6, 0.1, 24 , 0.1)); 75 | 76 | musicRelKnobGroup(x) = main_group(vgroup("[2] musical release [tooltip: this section fine tunes the release to sound musical]", x)); 77 | 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); 78 | transientSpeed = musicRelKnobGroup(hslider("[1]transient speed[tooltip: speed up the release when the GR is below AVG ]", 0.5, 0, 1, 0.001)); 79 | antiPump = musicRelKnobGroup(hslider("[2]anti pump[tooltip: slow down the release when the GR is above AVG ]", 0.5, 0, 1, 0.001)); 80 | 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)) ; 81 | 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)) ; 82 | 83 | GRmeter_group(x) = main_group(hgroup("[3] GR [tooltip: gain reduction in dB]", x)); 84 | meter = GRmeter_group( _<:(_,(_:min(0):max(-20):( (vbargraph("[unit:dB]", -20, 0))))):attach); 85 | AVGmeter_group(x) = (main_group(hgroup("[4] AVG [tooltip: average gain reduction in dB]", x))); 86 | avgMeter = AVGmeter_group((_<:(_,(_:min(0):max(-20):( (vbargraph("[unit:dB]", -20, 0))))):attach)); 87 | DHmeter_group(x) = (main_group(hgroup("[5] HoldTime [tooltip: hold time in ms]", x))); 88 | dhMeter = DHmeter_group((_<:(_,((_*1000/SampleRate):min(maxHoldMs):max(0):( (vbargraph("[unit: ms]", 0, maxHoldMs))))):attach)); 89 | // dhMeter = DHmeter_group((_<:(_,((_*1000/SampleRate*nrWin*2):min(maxHoldMs):max(0):( (vbargraph("[unit: ms]", 0, maxHoldMs))))):attach)); 90 | 91 | mymeter = meter_group(_<:(_, ( (vbargraph("[2]SD[tooltip: slow down amount]", 0, 0.5)))):attach); 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A clean yet fast lookahead limiter written in Faust. 2 | It uses somewhat of a 'brute force' algorithm , so it's quite CPU-hungry. 3 | 4 | #features 5 | 6 | * brick-wall limiter 7 | * starts fading down before each peak 8 | * fade down can be anything between linear, and strongly exponential 9 | * will not start fading up if it needs to be down at least the same amount soon 10 | * the elaborate auto release algorithm allows you to set a musical trade-off between clean and loud 11 | 12 | In combination, these features provide the holy grail of limiters: fast reaction on peaks, yet hardly any distortion on sustained material. 13 | Sine waves even have zero distortion down to the very low bass, at any level. 14 | 15 | The cost is heavy CPU usage, and a lot of latency (186 ms by default) 16 | 17 | ## building and installing: 18 | ``` 19 | git clone https://github.com/magnetophon/LazyLimiter 20 | cd CharacterCompressor 21 | make 22 | sudo make install 23 | ``` 24 | 25 | #usage: 26 | 27 | ## minimum gain reduction 28 | There will never be less gain reduction then what these setting dictate. 29 | You can have gain reduction completely without distortion, yet still react quick to transients. 30 | To hear this effect, set the minimum hold time from the next section to full and the anti pump from the 3rd section to 0. 31 | While very clean and fast, this sound has two problems: 32 | 1. It can be a bit too conservative. The dynamic hold section can fix that. 33 | 2. Simultaneously, it releases to quick in quiet parts. The musical release section fixes that. 34 | ### input gain 35 | Input gain in dB 36 | ### threshold 37 | maximum output level in dB 38 | ### attack shape 39 | 0 gives a linear attack (slow), 1 a strongly exponential one (fast). 40 | Linear sounds cleaner, exponential punchier/louder. 41 | This is how the curve of the attack varies it's shape: 42 | ![](https://github.com/magnetophon/LazyLimiter/raw/master/docs/attack.gif) 43 | ### minimum release time 44 | Minimum time in ms for the GR to go up 45 | ### stereo link 46 | 0 means independent, 1 fully linked 47 | 48 | ## dynamic hold 49 | The GR will not go up if it has to be back here within the hold time. 50 | ### maximum hold time 51 | maximum hold time in ms 52 | ### minimum hold time 53 | minimum hold time in ms 54 | ### dynHold 55 | shorten the hold time when the GR is below AVG 56 | ### dynHoldPow 57 | shape the curve of the hold time 58 | ### dynHoldDiv 59 | scale the curve of the hold time 60 | 61 | ## musical release 62 | this section fine tunes the release to sound musical 63 | ### base release rate 64 | release rate when the GR is at AVG, in dB/s 65 | ### transient speed 66 | speed up the release when the GR is below AVG 67 | ### anti pump 68 | slow down the release when the GR is above AVG 69 | ### AVG attack 70 | time in ms for the AVG to go down 71 | ### AVG release 72 | time in ms for the AVG to go up 73 | 74 | ## metering section: 75 | - gain reduction in dB 76 | - average gain reduction in dB 77 | - hold time in ms 78 | 79 | #Inner workings 80 | 81 | ## conceptual idea 82 | Here is [a block-diagram](https://github.com/magnetophon/LazyLimiter/raw/master/docs/blockDiagram-svg/process.svg) to help explain. 83 | A clickable version can be found at https://magnetophon.github.io/LazyLimiter/. 84 | In this example, the lookahead time has been set to 4 samples, the actual limiter uses 8192 at a samplerate of 44100, and even more at higher samplerates. 85 | 86 | As with any lookahead limiter, there is a block calculating the gain reduction (GR), and that value is multiplied with the delayed signal. 87 | 88 | Notice that all values inside GainCalculator are in dB: 89 | 0dB meaning no gain reduction, and -infinite meaning full gain reduction; silence, and 90 | In other words, the smaller the value, the more gain reduction. 91 | 92 | Inside the GainCalculator, there are 3 blocks doing the work: attackGainReduction, hold and releaseEnvelope. 93 | 1. attackGainReduction calculates gradual fade down towards the eventual gain reduction. 94 | 2. hold makes sure we don't fade back up if we need to be down at least the same amount soon. 95 | Together they make up minimumGainReduction. 96 | 3. this goes into releaseEnvelope, to tweak the release to be musical. 97 | 98 | ###attackGainReduction 99 | 100 | The attack is calculated as follows: 101 | - currentdown represents the amount of decibels we need to go down for the current input sample to stay below the threshold. 102 | - we make an array of 4, as follows: 103 | ``` 104 | currentdown@1*(1/4) 105 | currentdown@2*(2/4) 106 | currentdown@3*(3/4) 107 | currentdown@4*(4/4) 108 | ``` 109 | - we take the minimum value of this array 110 | In effect, we have created a constantly moving linear fade-down with a duration of 4 samples. 111 | 112 | ###hold 113 | 114 | Hold works as follows: 115 | - lastdown represents the amount of decibels we where down at the previous sample, iow a feedback loop coming from the end of the GainCalculator. 116 | - we make an array of 4, as follows: 117 | ``` 118 | (currentdown@(0):max(lastdown)) 119 | (currentdown@(1):max(lastdown)) 120 | (currentdown@(2):max(lastdown)) 121 | (currentdown@(3):max(lastdown)) 122 | ``` 123 | - again we take the minimum of these values. 124 | - in plain English: we check if any of the coming samples needs the same or more gain reduction then we currently have, and if so, we stay down. 125 | 126 | ###releaseEnvelope 127 | 128 | We take the minimum of attack and hold, and enter it into the release function, which is just a 0 attack, logarithmic release envelope follower. 129 | This is the signal that is multiplied with the delayed audio, as mentioned in the explanation of attack. 130 | 131 | ## actual implementation: 132 | 133 | You can choose the maximum attack and hold time at compile time by changing [maxAttackTime](https://github.com/magnetophon/LazyLimiter/blob/master/GUI.lib#L38) and [maxHoldTime](https://github.com/magnetophon/LazyLimiter/blob/master/GUI.lib#L30). 134 | This way various compromises between quality and CPU usage can be made. 135 | They are scaled with samplerate, but you have to [manually set it](https://github.com/magnetophon/LazyLimiter/blob/master/GUI.lib#L21) at compile time. 136 | 137 | I've made the shape of the attack curve variable, by putting a wave-shaping function after the "1/4 trough 4/4" of the attack example. 138 | Both the hold time and the time of the releaseEnvelope automatically adapt to the input material. 139 | 140 | I am looking for ways to reduce the amount of parameters, either by choosing good defaults or by intelligently coupling them. 141 | 142 | # Thanks 143 | I got a lot of [inspiration](https://github.com/sampov2/foo-plugins/blob/master/src/faust-source/compressor-basics.dsp#L126-L139) from Sampo Savolainen's [foo-plugins](https://github.com/sampov2/foo-plugins). 144 | 145 | My first implementation was a lot like the blockdiagram in the explanation; at usable predelay values it ate CPU's for breakfast. 146 | [Yann Orlarey](http://www.grame.fr/qui-sommes-nous/compositeurs-associes/yann-orlarey) provided [the brainpower to replace the cpu-power](https://github.com/magnetophon/LazyLimiter/blob/master/LazyLimiter.lib#L54-L66) and made this thing actually usable! 147 | 148 | Many thanks, also to the rest of the Faust team! 149 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/minimumGainReduc-0x9432f48.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | lastdown 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | attackGainReduction(x1) 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | hold(x1)(x2) 28 | 29 | 30 | 31 | 32 | min 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | minimumGainReduction(x1) 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/minimumGainReduc-0x9a08fa0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | lastdown 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | attackGainReduction(x1) 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | hold(x1)(x2) 28 | 29 | 30 | 31 | 32 | min 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | minimumGainReduction(x1) 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/linear2db-0x9431be0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | x 11 | 12 | 13 | 14 | 15 | 16 | 20 17 | 18 | 19 | 20 | x 21 | 22 | 23 | 24 | log10 25 | 26 | 27 | 28 | 29 | 30 | * 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | linear2db 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/linear2db-0x9a07c38.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | x 11 | 12 | 13 | 14 | 15 | 16 | 20 17 | 18 | 19 | 20 | x 21 | 22 | 23 | 24 | log10 25 | 26 | 27 | 28 | 29 | 30 | * 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | linear2db 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /experiments/LLng2.dsp: -------------------------------------------------------------------------------- 1 | import("stdfaust.lib"); 2 | declare author "Bart Brouns"; 3 | declare license "GPLv3"; 4 | 5 | process = 6 | GR@totalLatencyLinear 7 | ,smoothGRlinear(GR) 8 | // ,(smoothGRlinear(GR):max(-1):min(1)) 9 | // , (attRel~_) 10 | ; 11 | 12 | totalLatency = pow(2,expo); 13 | totalLatencyLinear = nrBlocks * blockSize; 14 | // nrBlocks = 1; 15 | // blockSize = 1024; 16 | // nrBlocks = 2; 17 | // blockSize = 512; 18 | // nrBlocks = 4; 19 | // blockSize = 128; 20 | // nrBlocks = 16; 21 | // blockSize = 64; 22 | // nrBlocks = 1; 23 | // blockSize = 512; 24 | // nrBlocks = 1; 25 | // blockSize = 128; 26 | nrBlocks = 4; 27 | blockSize = 2; 28 | // expo = 4; 29 | // expo = 10; 30 | expo = 8; 31 | // TODO sAndH(reset) parameters 32 | smoothGRl(GR) = FB~(_,_) :(_,!) 33 | with { 34 | FB(prev,oldDownSpeed) = 35 | par(i, expo, fade(i)):ro.interleave(2,expo):(minN(expo),maxN(expo)) 36 | with { 37 | new(i) = lowestGRblock(GR,size(i))@(totalLatency-size(i)); 38 | newH(i) = new(i):ba.sAndH( reset(i)| (attPhase(prev)==0) ); 39 | prevH(i) = prev:ba.sAndH( reset(i)| (attPhase(prev)==0) ); 40 | reset(i) = 41 | (newDownSpeed(i) > currentDownSpeed); 42 | fade(i) = 43 | // crossfade(currentPosAndDir(i),newH(i) ,ramp(size(i),reset(i)):rampShaper(i)) // TODO crossfade from current direction to new position 44 | crossfade(prevH(i),newH(i) ,ramp(size(i),reset(i)):rampShaper(i)) // TODO crossfade from current direction to new position 45 | :min(GR@totalLatency)//brute force fade of 64 samples not needed for binary tree attack ? 46 | // sample and hold oldDownSpeed: 47 | , (select2((newDownSpeed(i) > currentDownSpeed),currentDownSpeed ,newDownSpeed(i))); 48 | rampShaper(i) = _:pow(power(i))*mult(i):max(smallestRamp(reset(i))); 49 | power(i) = LinArrayParametricMid(hslider("power bottom", 1, 0.01, 10, 0.01),hslider("power mid", 1, 0.01, 10, 0.01),hslider("power band", (expo/2)-1, 0, expo, 1),hslider("power top", 0.5, 0.01, 10, 0.01),i,expo); 50 | mult(i) = LinArrayParametricMid(hslider("mult bottom", 1, 0.001, 1 ,0.001),hslider("mult mid", 1, 0.001, 1 ,0.001),hslider("mult band", (expo/2)-1, 0, expo, 1),hslider("mult top", 1, 0.001, 1 ,0.001),i,expo); 51 | currentPosAndDir(i) = prevH(i)-( ramp(size(i),reset(i)) * hslider("ramp", 0, 0, 1, 0.01) * (prevH(i)'-prevH(i))); 52 | // newDownSpeed(i) = (select2(checkbox("newdown"),prev,(prev'-currentDownSpeed)) -new(i) )/size(i); 53 | newDownSpeed(i) = (prev -new(i) )/size(i); 54 | currentDownSpeed = oldDownSpeed*(speedIsZero==0); 55 | // speedIsZero = (prev==GR@(totalLatency)) ; // TODO: needs more checks, not attack 56 | // speedIsZero = (prev==prev') ; 57 | speedIsZero = select2(checkbox("speed"),(prev==GR@(totalLatency)),(prev==prev')); 58 | size(i) = pow(2,(expo-i)); 59 | }; // ^^ needs prev and oldDownSpeed 60 | attPhase(prev) = lowestGRblock(GR,totalLatency) 0:!; 81 | (op,1) => _; 82 | (op,2) => op; 83 | (op,N) => (opWithNInputs(op,N-1),_) : op; 84 | }; 85 | }; 86 | 87 | smoothGRlinear(GR) = FB~(_,_) 88 | :(_,!) 89 | with { 90 | FB(prev,oldDownSpeed) = 91 | // select2(attPhase(prev), 92 | (par(i, nrBlocks, fade(i)):ro.interleave(2,nrBlocks):(minN(nrBlocks),maxN(nrBlocks))) 93 | // , (lowestGRblock(GR,totalLatencyLinear),select2(checkbox("0"),oldDownSpeed,0)) 94 | // ) 95 | with { 96 | new(i) = lowestGRblock(GR,size(i))@(i*blockSize); 97 | newH(i) = new(i):ba.sAndH( reset(i)| (attPhase(prev)==0) ); 98 | prevH(i) = prev:ba.sAndH( reset(i)| (attPhase(prev)==0) ); 99 | reset(i) = 100 | (newDownSpeed(i) > currentDownSpeed); 101 | fade(i) = 102 | crossfade(currentPosAndDir(i),newH(i) ,ramp(size(i),reset(i)):rampShaper(i)) // TODO crossfade from current direction to new position 103 | // crossfade(prevH(i),newH(i) ,ramp(size(i),reset(i)):rampShaper(i)) // TODO crossfade from current direction to new position 104 | :min(GR@totalLatencyLinear)//TODO: make into brute force fade of 64 samples 105 | // sample and hold oldDownSpeed: 106 | , (select2((newDownSpeed(i) > currentDownSpeed),currentDownSpeed ,newDownSpeed(i))); 107 | rampShaper(i) = _:pow(power(i))*mult(i):max(smallestRamp(reset(i))); 108 | power(i) = LinArrayParametricMid(hslider("power bottom", 1, 0.001, 100, 0.001),hslider("power mid", 1, 0.001, 100, 0.001),hslider("band", (nrBlocks/2)-1:max(0), 0, nrBlocks, 1),hslider("power top", 1, 0.001, 100, 0.001),i,nrBlocks); 109 | mult(i) = LinArrayParametricMid(hslider("mult bottom", 1, 0.001, 1 ,0.001),hslider("mult mid", 1, 0.001, 1 ,0.001),hslider("band", (nrBlocks/2)-1:max(0), 0, nrBlocks, 1),hslider("mult top", 1, 0.001, 1 ,0.001),i,nrBlocks); 110 | currentPosAndDir(i) = prevH(i)-( rampOne(i) * speed(i)); 111 | rampOne(i) = (select2(reset(i),_+1,1):min(size(i)))~_; 112 | speed(i) = (prev-prev'):ba.sAndH( reset(i)| (attPhase(prev)==0) ); 113 | // speed(i) = (prev-prev'):ba.sAndH( reset(i) ); 114 | // newDownSpeed(i) = (select2(checkbox("newdown"),prev,(prev'-currentDownSpeed)) -new(i) )/size(i); 115 | newDownSpeed(i) = (prev -new(i) )/size(i); 116 | currentDownSpeed = oldDownSpeed*(speedIsZero==0); 117 | // speedIsZero = (prev==GR@(totalLatencyLinear)) ; // TODO: needs more checks, not attack 118 | // speedIsZero = (prev==prev') ; 119 | speedIsZero = select2(checkbox("speed"),(prev==GR@(totalLatencyLinear)),(prev==prev')); 120 | size(i) = (nrBlocks-i)*blockSize; 121 | }; // ^^ needs prev and oldDownSpeed 122 | attPhase(prev) = lowestGRblock(GR,totalLatencyLinear) 0:!; 142 | (op,1) => _; 143 | (op,2) => op; 144 | (op,N) => (opWithNInputs(op,N-1),_) : op; 145 | }; 146 | }; 147 | 148 | // make a log array of values, from bottom to top 149 | LogArray(bottom,top,nrElements) = par(i,nrElements, pow((pow((top/bottom),1/(nrElements-1))),i)*bottom); 150 | 151 | LinArrayParametricMid(bottom,mid,band,top,element,nrElements) = 152 | select2(band<=element +1,midToBottomVal(element), midToTopVal(element)) 153 | with { 154 | midToBottomVal(element) = (midToBottom(element)*bottom) + (((midToBottom(element)*-1)+1)*mid); 155 | midToBottom(element) = (band-(element +1))/(band-1); 156 | 157 | midToTopVal(element) = (midToTop(element)*top) + (((midToTop(element)*-1)+1)*mid); 158 | midToTop(element) = (element +1-band)/(nrElements-band); 159 | }; 160 | 161 | GR = vgroup("GR", no.lfnoise0(totalLatency *t * (no.lfnoise0(totalLatency/2):max(0.1) )):pow(3)*(1-noiseLVL) +(no.lfnoise(rate):pow(3) *noiseLVL):min(0)) ; 162 | t= hslider("time", 0.1, 0, 1, 0.001); 163 | noiseLVL = hslider("noise", 0, 0, 1, 0.01); 164 | rate = hslider("rate", 20, 10, 20000, 10); 165 | 166 | attRel(prev) = select2( 167 | GR:ba.slidingMin(totalLatency,totalLatency) 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 192000 11 | 12 | 13 | 14 | 1 15 | 16 | 17 | 18 | fSamplingFreq 19 | 20 | 21 | 22 | max 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | min 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | SR 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/SR-0x9a0ad80.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 192000 11 | 12 | 13 | 14 | 1 15 | 16 | 17 | 18 | fSamplingFreq 19 | 20 | 21 | 22 | max 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | min 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | SR 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/db2linear-0x94373b8.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | x 11 | 12 | 13 | 14 | 15 | 16 | 10 17 | 18 | 19 | 20 | x 21 | 22 | 23 | 24 | 20 25 | 26 | 27 | 28 | / 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | pow 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | db2linear 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/db2linear-0x9a0d410.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | x 11 | 12 | 13 | 14 | 15 | 16 | 10 17 | 18 | 19 | 20 | x 21 | 22 | 23 | 24 | 20 25 | 26 | 27 | 28 | / 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | pow 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | db2linear 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/p-0x94356f0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -1 11 | 12 | 13 | 14 | 0.1 15 | 16 | 17 | 18 | 19 | 20 | 21 | release 22 | 23 | 24 | 25 | 26 | 27 | SR 28 | 29 | 30 | 31 | 32 | * 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | / 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | exp 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | p 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/p-0x9a0b748.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -1 11 | 12 | 13 | 14 | 0.1 15 | 16 | 17 | 18 | 19 | 20 | 21 | release 22 | 23 | 24 | 25 | 26 | 27 | SR 28 | 29 | 30 | 31 | 32 | * 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | / 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | exp 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | p 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /docs/blockDiagram-svg/process.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | audio 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | GainCalculator(x1) 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | db2linear 26 | 27 | 28 | 29 | 30 | 31 | 32 | audio 33 | 34 | 35 | 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | 43 | LookAheadTime 44 | 45 | 46 | @ 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | * 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | process 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /experiments/LLng.dsp: -------------------------------------------------------------------------------- 1 | declare name "LazyLimiter ng"; 2 | declare author "Bart Brouns"; 3 | declare license "GPLv3"; 4 | declare version "0.1"; 5 | 6 | import("stdfaust.lib"); 7 | maxmsp = library("maxmsp.lib"); 8 | comp = library("../FaustCompressors/compressors.lib"); 9 | 10 | // process(x) = (gainCalculator(x):ba.db2linear)*(x@LookAheadTime); 11 | process = 12 | // GR; 13 | // lookahead_compression_gain_mono(strength,threshold,0,0,knee,x); 14 | // lim(1); 15 | gainCompareGraphs ; 16 | // GR:SandH(hslider("hld", 0, 0, 1, 1)); 17 | 18 | 19 | // comp.compressor_N_chan_demo(2); 20 | 21 | lim(N) = 22 | (si.bus(N) <: 23 | ( 24 | ( 25 | par(i, N, abs:ba.linear2db), 26 | si.bus(N) 27 | ) 28 | :lookahead_compression_gain_N_chan(strength,threshold,0,0,knee,hold,link,N),si.bus(N) 29 | ) 30 | ) 31 | // :(ro.interleave(N,2):par(i,N,(meter:ba.db2linear)*(_@maxHold))) 32 | :ro.interleave(N,2):par(i,N,(((meter:ba.db2linear<:si.bus(2))),(_@maxHold)):(_,*)):ro.interleave(2,N):ro.cross(N*2):par(i, 2, ro.cross(N)) 33 | with { 34 | meter = _<:(_, (max(maxGR):(hbargraph("[1][unit:dB][tooltip: gain reduction in dB]", maxGR, 0)))):attach; 35 | }; 36 | 37 | 38 | // generalise compression gains for N channels. 39 | // first we define a mono version: 40 | lookahead_compression_gain_N_chan(strength,thresh,att,rel,knee,hold,link,1) = 41 | lookahead_compression_gain_mono(strength,thresh,att,rel,knee,hold)~_; 42 | 43 | // The actual N-channel version: 44 | // Calculate the maximum gain reduction of N channels, 45 | // and then crossfade between that and each channel's own gain reduction, 46 | // to link/unlink channels 47 | lookahead_compression_gain_N_chan(strength,thresh,att,rel,knee,hold,link,N) = 48 | ( ro.interleave(N,2): 49 | ( 50 | par(i, N, lookahead_compression_gain_mono(strength,threshold,0,0,knee,hold)) 51 | <:(si.bus(N),(minN(N)<:si.bus(N))):ro.interleave(N,2):par(i,N,(comp.crossfade(link))) 52 | ) 53 | )~si.bus(N); 54 | 55 | // ( ro.interleave(N,2): 56 | // ( 57 | // par(i, N, lookahead_compression_gain_mono(strength,thresh,att,rel,knee,hold)) 58 | // <:(si.bus(N),(minN(N)<:si.bus(N))):ro.interleave(N,2):par(i,N,(comp.crossfade(link))) 59 | // ) 60 | // )~(si.bus(N)); 61 | 62 | lookahead_compression_gain_mono(strength,thresh,att,rel,knee,hold,lastdown,level) = 63 | ( 64 | comp.gain_computer(1,thresh,knee,level):deltaGR(LookAheadTime,lastdown) 65 | // :deltaGR(LookAheadTime) 66 | ) *strength; 67 | 68 | // ( 69 | // comp.gain_computer(1,thresh,knee,level)@(maxHold-LookAheadTime):deltaGR(LookAheadTime,lastdown) 70 | // , 71 | // (comp.gain_computer(1,thresh,knee,level):ba.slidingMin(hold,maxHold):max(lastdown)) 72 | // ) : min; 73 | 74 | 75 | 76 | 77 | strength = (hslider("[0] Strength [style:knob] 78 | [tooltip: A compression Strength of 0 means no gain reduction and 1 means full gain reduction]", 79 | 1, 0, 8, 0.01)); 80 | threshold = (hslider("[1] Threshold [unit:dB] [style:knob] 81 | [tooltip: When the signal level exceeds the Threshold (in dB), its level is compressed according to the Strength]", 82 | 0, maxGR, 10, 0.1)); 83 | knee = (hslider("[2] Knee [unit:dB] [style:knob] 84 | [tooltip: soft knee amount in dB]", 85 | 6, 0, 30, 0.1)); 86 | link = (hslider("[4] link [style:knob] 87 | [tooltip: 0 means all channels get individual gain reduction, 1 means they all get the same gain reduction]", 88 | 1, 0, 1, 0.01)); 89 | 90 | hold = hslider("[5] Hold time [style:knob]", 0, 0, maxHold, 1); 91 | 92 | gainCompareGraphs = 93 | GR@maxHold 94 | , 95 | // (line(lowestGRi(5,GR),pow(2,6))@(maxHold-LookAheadTime))// fast fade 96 | // lowestGRi(5,GR) 97 | (GR:(deltaGR(LookAheadTime)~(_,_))) 98 | // ,(GR:ba.slidingMin(hold,maxHold)@(maxHold-hold)) 99 | // , ((ramp(powI(1),lowestGRi(1,GR))/powI(1))@(maxHold-LookAheadTime)) 100 | // ba.countup(powI(5),(GR-GR')!=0) 101 | // ba.countup(powI(5),(GR-GR')!=0) 102 | ; 103 | tmp=4; 104 | 105 | del(x) = x-x'; 106 | // del = delFB~_; 107 | // delFB(FB,x) = x-FB; 108 | 109 | // deltaGR(maxI,GR) = FBdeltaGR(maxI,GR)~_ 110 | // with { 111 | // FBdeltaGR(maxI,GR,FB) = 112 | // par(i, expo, 113 | // ( 114 | // (lowestGRi(i,GR)-FB) 115 | // / ((powI(i)-ramp(powI(i),lowestGRi(i,GR)))+1) 116 | // ) 117 | // : attackRelease(i) 118 | // ) 119 | // : ((minN(expo) :smoothie ) +FB) 120 | // // :min(0) 121 | // :min(GR@LookAheadTime) 122 | // ; 123 | // deltaGR(maxI,GR) = FBdeltaGR(maxI,GR)~_ 124 | 125 | SandH(sample,x) = select2(sample,_,x)~_; 126 | 127 | deltaGR(maxI,FB,FBraw,GR) = 128 | ( 129 | // par(i, expo, 130 | select2(lowestGRi(i,GR)@(maxHold-LookAheadTime)>=FB , 131 | ( 132 | ((lowestGRi(i,GR)@(maxHold-LookAheadTime):SandH(trig(i,GR,FB,FBraw))-FB)) 133 | // / ((powI(i)-ramp(powI(i),lowestGRi(i,GR))@(maxHold-LookAheadTime))+1) 134 | / ((powI(i)-1- attackRamp(i,FB,FBraw)):max(0)+1) 135 | ), 136 | ( 137 | ((lowestGRi(i,GR)@(maxHold-LookAheadTime)-FB)) 138 | // / ((powI(i)-ramp(powI(i),lowestGRi(i,GR))@(maxHold-LookAheadTime))+1) 139 | / ((powI(i)-ramp(powI(i),lowestGRi(i,GR))@(maxHold-LookAheadTime))+1) 140 | ) 141 | ) 142 | // ) 143 | // <: attackReleaseBlock(expo) 144 | // : minN(expo) 145 | : holdFunction 146 | <: 147 | ( 148 | ((AR : smoothRel(smooR): smoothAttack(smooA) +FB) :min(GR@maxHold)) 149 | , (_+FBraw:min(GR@maxHold)) 150 | ) 151 | ) 152 | // ,((FB-FB') *pow(2,expo)) 153 | // ,(deltaI(expo-1,GR,FB) *pow(2,expo)) 154 | // ,(attackRamp(expo-1,FB,FBraw) / powI(expo-1)) 155 | ,(trig(i,GR,FB,FBraw)*0.5) 156 | // ,(trig4(i,GR,FB) * 0.25) 157 | // ,(trig2(i,GR,FB)*0.25) 158 | // par(i, nrBands, 159 | // ( 160 | // (lowestGRlinI(i,GR)@(maxHold-LookAheadTime)-FB) 161 | 162 | // / ((linI(i)-ramp(linI(i),lowestGRlinI(i,GR))@(maxHold-LookAheadTime))+1) 163 | // ) 164 | // ) 165 | // : attackReleaseBlock(nrBands) 166 | // : ((minN(nrBands): holdFunction :smoothRel(smooR): smoothAttack(smooA) ) +FB) 167 | // :min(GR@maxHold) 168 | with { 169 | i=expo-1; 170 | linI(i) = (i+1)*pow(2,expo)/nrBands; // the lenght of each block 171 | lowestGRlinI(i,GR) = GR:ba.slidingMin(linI(i),linI(i))@delCompLin(i); 172 | 173 | holdFunction(attackedGR) = 174 | attackedGR*( 175 | (GR:ba.slidingMin(hold,maxHold)@(maxHold-hold) > FB) 176 | + 177 | (attackedGR<0) 178 | :min(1) 179 | ); 180 | // attackedGR; 181 | // holdFunction = _;//@(maxHold-LookAheadTime);//*(GR:ba.slidingMin(hold,maxHold) >= FB); 182 | // (comp.gain_computer(1,thresh,knee,level):ba.slidingMin(hold,maxHold):max(lastdown)) 183 | delCompLin(i) = pow(2,expo)-linI(i); 184 | attackReleaseBlock(size) = 185 | attackPlusInputs(size) : par(i, size, attackRelease(i)); 186 | // smoothie(delta) = select2(delta>=0,delta,smoothieFB(delta)~_); 187 | // smoothieFB(delta,FB) = min(( (FB*smoo) + (delta*(1-smoo))):max(0) , delta); 188 | smoothAttack(smoo,delta) = select2((delta<=_)*(delta<0) ,delta,((_*smoo) + (delta*(1-smoo))) )~(_<:(_,_)); 189 | smoothRel(smoo,delta) = smoothieFB(smoo,delta)~_; 190 | smoothieFB(smoo,delta,FB) = select2(delta>=0,delta,min(( (FB*smoo) + (delta*(1-smoo))):max(0) , delta)); 191 | smooA = hslider("smooA", 0, 0,0.999, 0.001):pow(1/128); 192 | smooR = hslider("smooR", 0, 0,0.999, 0.001):pow(1/128); 193 | // countup(n,trig) : _ 194 | // * `n`: the maximum count value 195 | // * `trig`: the trigger signal (1: start at 0; 0: increase until `n`) 196 | // ramp(maxI,GR) = ((ba.countup(maxI,(GR-GR')!=0)/maxI):attackShaper)*maxI; 197 | // ramp(maxI,GR) = ba.countup(maxI,(GR-GR')!=0); 198 | 199 | AR(delta) = 200 | select2((delta)<=0, 1+release,bottom)*delta; 201 | 202 | attackRelease(i,attack,delta) = 203 | select2((delta)<=0, 204 | releaseFunc, 205 | // (1/(i+1))+attack : min(1) 206 | attackFunc(i,attack) 207 | )*delta; 208 | releaseFunc = 1+release; 209 | // releaseFunc(delta) = min((delta*smoo)+( (1+release) *(1-smoo))); 210 | // attackFunc(i) = 211 | // ( 212 | // ( 213 | // 1/ 214 | // ( pow(( (i-curve):max(1)) , 2.5) ) // linear from i=4 215 | // // ) + pow(attack,2) : min(1) 216 | // // ) + pow(attack,2) : min(1)+ ((i==3)*1) + ((i==1)*-0.75) 217 | // ) + pow(attack,2) : min(1)+ ((i==curve)*1) 218 | // //+ ((i==(curve-2))*-0.75) 219 | // ) 220 | // ; 221 | // attackFunc(i,attack) = 222 | // ( 223 | // 1/ 224 | // ( i*64:max(1)*((attack:min(1)*-1)+1:pow(4)) ) 225 | // ) : min(1) + (attack-1:max(0):pow(1)) 226 | // ; 227 | attackFunc(i,attack) = attack; 228 | // attackFunc(i) = 229 | // ( 230 | // ( 231 | // 1/ 232 | // ( (i-curve)*64:max(1)*(attack:pow(4)) ) 233 | // ) : min(1)+ ((i==curve)*1) 234 | // ) 235 | // ; 236 | curve = hslider("curve", 3, -1, nrBands, 1); 237 | // curve = hslider("curve", 3, 2, expo, 1); 238 | // (1/(i+1))-attack : min(1)); 239 | attack = hslider("attack", 0, 0, 1, 0.001); 240 | // OK for expo=10 241 | // release = (((hslider("release", 0, 0, 1, 0.001):pow(0.2))*-1)+1)*32; 242 | // OK for 10 and 13 243 | release = (((hslider("release", 0, 0, 1, 0.001):pow(0.2))*-1)+1)*pow(2,expo/2); 244 | // release = (((hslider("release", 0, 0, 1, 0.001):pow(0.2))*-1)+1)*pow(2,expo)/32; 245 | attackPlusInputs(size) = 246 | ( 247 | VocoderLinArrayParametricMid(bottom,mid,band(size),top,size), 248 | si.bus(size) 249 | ): ro.interleave(size,2) ; 250 | bottom = hslider("bottom", 2, 0, 30, 0.01); 251 | mid = hslider("mid", 1, 0, 3, 0.01); 252 | top = hslider("top", 0, 0, 3, 0.01); 253 | band(size) = hslider("band", 8, 1, size, 1); 254 | }; 255 | 256 | // trig(i,GR,FB) = deltaI(i,GR,FB)<(FB-FB'):ba.impulsify ;//* (FB==GR@maxHold); 257 | trig(i,GR,FB,FBraw) = select3(sel,trig1(i,GR,FB,FBraw) , trig2(i,GR,FB,FBraw), trig3(i,GR,FB,FBraw) ); 258 | sel = hslider("sel", 0, 0, 2, 1); 259 | trig1(i,GR,FB,FBraw) = deltaI(i,GR,FB) <(FBraw-FBraw') ; 260 | trig2(i,GR,FB,FBraw) = deltaI(i,GR,FB) <(FBraw-FBraw') : ba.impulsify; 261 | trig3(i,GR,FB,FBraw) = deltaI(i,GR,FB) <(FB-FB') ; 262 | // trig2(i,GR,FB) = deltaI(i,GR,FB) <(FB-FB') * (FB> (lowestGRi(i,GR)@(maxHold-LookAheadTime))) ; 263 | // trig3(i,GR,FB) = ((GR/powI(i))-FB) <(FB-FB') ; 264 | trig4(i,GR,FB) = deltaI(i,GR,FB) <(FB-FB') * (FB> (lowestGRi(i,GR)@(maxHold-LookAheadTime))) ; 265 | // : ba.impulsify; 266 | //(FB==GR@maxHold); 267 | invert = _==0; 268 | mult = hslider("mult", 1, 0, 3, 0.01); 269 | ramp(maxI,GR) = ba.countup(maxI,(GR>GR'))*mult; 270 | attackRamp(i,FB,FBraw) = ba.countup(powI(i),(trig(i,GR,FB,FBraw))); 271 | deltaI(i,GR,FB) = (lowestGRi(i,GR)@(maxHold-LookAheadTime)-FB)/powI(i); 272 | // attackRamp(i) = ba.countup(powI(i),(lowestGRi(i,GR)-lowestGRi(i,GR)')!=0); 273 | // todo: ramp if lowestgr(10) !=lowestgr(5) 274 | /* 275 | 276 | slow = 277 | band 16 278 | bottom 1.92 279 | mid 1.80 280 | release 0.71 281 | smoo = 0.829 282 | top 0 283 | 284 | fast = 285 | band 3 286 | bottom 1.92 287 | mid 0.08 288 | release 0 289 | smoo = 0.046 290 | top 0 291 | 292 | 293 | 294 | */ 295 | 296 | lss = 3; 297 | 298 | // lin from 32 (i=4) 299 | // *2 from 16-8 300 | // *1 from 8-4 301 | // *0.25 from 4-0 302 | 303 | // auto-attack-release: 304 | // vocoder, each band represents a fixed A&R, (lower is longer of course) 305 | // and each influences the end result 306 | // use VOF code to normalise and focus 307 | 308 | // if (somewhere in the next "length" samples, we are going down quicker then we are now): 309 | // then (fade down to that speed) 310 | // second implementation option: 311 | // if knikpunt 312 | // then (fade down to 0 speed at knikpunt, then "normal release") 313 | // TODO: remove this length mess & delay! :) 314 | // length = 2; 315 | // length = pow(2,6); // 64 316 | // length = pow(2,7); // 128 317 | length = pow(2,9); // 128 318 | // smoother(length,x) = smootherFB(x,length)~_; 319 | smoother(length,x) = smootherFB(x,length)~(!,!,_); 320 | smootherFB(x,length,FB) = 321 | reset, 322 | ramp 323 | , 324 | // select2(minDelta<(delta@length) ,x@length, deltaXfade) 325 | deltaXfade 326 | with { 327 | // delta = x-FB; 328 | delta = x-x'; 329 | minDelta = (delta):ba.slidingMin(length,length); 330 | deltaXfade = xFade(ramp*downSmooth,(x@length)-FB,minDelta)+FB; 331 | // deltaXfade = xFade(ramp*downSmooth,FB-FB',minDelta)+FB; 332 | // deltaXfade = ( 333 | // (minDelta-(delta@length)) 334 | // / ((length-ramp(minDelta)+1)) 335 | // ) 336 | // +FB; 337 | xFade(x,a,b) = a*(1-x)+b*(x); 338 | downSmooth = hslider("downSmooth", 0, 0, 1, 0.01); 339 | // ramp = ba.countup(length,(minDelta=0)@length; 344 | // reset = delta<=0; 345 | // reset = delta 0:!; 401 | (op,1) => _; 402 | (op,2) => op; 403 | // (op,N) => (opWithNInputs(op,N/2),opWithNInputs(op,N/2)) : op; 404 | (op,N) => (opWithNInputs(op,N-1),_) : op; 405 | // (op,N) => (opWithNInputs(op,nUp(N)),opWithNInputs(op,nDown(N))) : op with { 406 | // nDown(N) = int(floor( N/2)); 407 | // nUp(N) = int(floor((N/2)+0.5)); 408 | // }; 409 | }; 410 | 411 | gainCalculator(x) = x; 412 | attack = (hslider("[2]attack shape[tooltip: 0 gives a linear attack (slow), 1 a strongly exponential one (fast)]", 1 , 0, 1 , 0.001)); 413 | attackShaper(fraction)= ma.tanh(fraction:pow(attack:attackScale)*(attack*5+.1))/ma.tanh(attack*5+.1); 414 | attackScale(x) = (x+1):pow(7); //from 0-1 to 1-128, just to make the knob fit the aural experience better 415 | 416 | 417 | VocoderLinArrayParametricMid(bottom,mid,band,top,size) = 418 | par(i, size, select2(band<=i+1,midToBottomVal(i),midToTopVal(i))) 419 | with { 420 | midToBottomVal(i) = (midToBottom(i)*bottom) + (((midToBottom(i)*-1)+1)*mid); 421 | midToBottom(i) = (band-(i+1))/(band-1); 422 | 423 | midToTopVal(i) = (midToTop(i)*top) + (((midToTop(i)*-1)+1)*mid); 424 | midToTop(i) = (i+1-band)/(size-band); 425 | }; 426 | 427 | // nrBands = 32; 428 | nrBands = 4; 429 | 430 | // expo = 4; 431 | expo = 10; // 10 = 1024 samples 432 | 433 | // maxHold = pow(2,6); 434 | maxHold = pow(2,13); 435 | // expo = 13; // 13 = 8192 samples, = 0.185759637188 sec = 186ms 436 | 437 | maxGR = -30; 438 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | --------------------------------------------------------------------------------