1 //instrument.lib - Faust function of various types usefull for building physical model instruments
3 declare name "Faust-STK Tools Library";
4 declare author "Romain Michon (rmichon@ccrma.stanford.edu)";
6 declare version "1.0";
7 declare licence "STK-4.3"; // Synthesis Tool Kit 4.3 (MIT style license);
9 import("math.lib");
10 import("filter.lib");
11 import("effect.lib");
13 //========================= ENVELOPE GENERATORS ===============================
15 //----------------------- VIBRATO ENVELOPE ----------------------------
16 // 4 phases envelope to control vibrato gain
17 //
18 // USAGE:
19 // _ : *(envVibrato(b,a,s,r,t)) : _
20 // where
21 // b = beginning duration (silence) in seconds
22 // a = attack duration in seconds
23 // s = sustain as a percentage of the amplitude to be modified
24 // r = release duration in seconds
25 // t = trigger signal
27 envVibrato(b,a,s,r,t) = env ~ (_,_,_) : (!,!,_) // the 3 'state' signals are fed back
28 with {
29 env (p2,cnt,y) =
30 (t>0) & (p2|(y>=1)),
31 (cnt + 1)*(t>0), // counter for the first step "b"
32 (y + p1*p3*u*(s/100) - p4*w*y)*((p4==0)|(y>=eps)) // y = envelop signal
33 //*(y>=eps) // cut off tails to prevent denormals
34 with {
35 p1 = (p2==0) & (t>0) & (y<1) & (cnt>(b*SR)); // p1 = attack phase
36 p3 = 1-(cnt<(nb)); // p3 = beginning phase
37 p4 = (t<=0) & (y>0); // p4 = release phase
38 // #samples in attack, release, must be >0
39 nb = SR*b+(b==0.0) ; na = SR*a+(a==0.0); nr = SR*r+(r==0.0);
40 // attack and (-60dB) release rates
41 z = s+(s==0.0)*db2linear(-60);
42 u = 1/na; w = 1-1/pow(z*db2linear(60), 1/nr);
43 // values below this threshold are considered zero in the release phase
44 eps = db2linear(-120);
45 };
46 };
48 //----------------------- ATTACK - SUSTAIN - RELEASE ----------------------------
49 // Attack - Sustain - Release envelope
50 //
51 // USAGE:
52 // _ : *(asr(a,s,r,t)) : _
53 // where
54 // a = attack duration in seconds
55 // s = sustain as a percentage of the amplitude to be modified
56 // r = release duration in seconds
57 // t = trigger signal
59 asr(a,s,r,t) = env ~ (_,_) : (!,_) // the 2 'state' signals are fed back
60 with {
61 env (p2,y) =
62 (t>0) & (p2|(y>=1)),
63 (y + p1*u*(s/100) - p3*w*y) // y = envelop signal
64 *((p3==0)|(y>=eps)) // cut off tails to prevent denormals
65 with {
66 p1 = (p2==0) & (t>0) & (y<1); // p1 = attack phase
67 p3 = (t<=0) & (y>0); // p3 = release phase
68 // #samples in attack, release, must be >0
69 na = SR*a+(a==0.0); nr = SR*r+(r==0.0);
70 // correct zero sustain level
71 z = s+(s==0.0)*db2linear(-60);
72 // attack and (-60dB) release rates
73 u = 1/na; w = 1-1/pow(z*db2linear(60), 1/nr);
74 // values below this threshold are considered zero in the release phase
75 eps = db2linear(-120);
76 };
77 };
79 //----------------------- ASYMPT60 ----------------------------
80 // Envelope generator which asymptotically approaches a target value.
81 //
82 // USAGE:
83 // asympT60(value,trgt,T60,trig) : _
84 // where
85 // value = starting value
86 // trgt = target value
87 // T60 = ramping time
88 // trig = trigger signal
90 asympT60(value,trgt,T60,trig) = (_*factor + constant)~_
91 with{
92 cntSample = *(trig) + 1~_ : -(1);
93 attDur = float(2);
94 cndFirst = ((cntSample < attDur) & (trig > 0));
95 target = value*cndFirst + trgt*(cndFirst < 1);
96 factorAtt = exp(-7/attDur);
97 factorT60 = exp(-7/(T60*float(SR)));
98 factor = factorAtt*((cntSample < attDur) & (trig > 0)) +
99 ((cntSample >= attDur) | (trig < 1))*factorT60;
100 constant = (1 - factor)*target;
101 };
103 //========================= TABLES ===============================
105 //----------------------- CLIPPING FUNCTION ----------------------------
106 // Positive and negative clipping functions.
107 //
108 // USAGE:
109 // _ : saturationPos : _
110 // _ : saturationNeg : _
111 // _ : saturationPos : saturationNeg : _
113 saturationPos(x) = x <: (_>1),(_<=1 : *(x)) :> +;
114 saturationNeg(x) = x <: (_<-1),(_>=-1 : *(x)) :> *(-1) + _;
116 //----------------------- BOW TABLE ----------------------------
117 // Simple bow table.
118 //
119 // USAGE:
120 // index : bow(offset,slope) : _
121 // where
122 // 0 <= index <= 1
124 bow(offset,slope) = pow(abs(sample) + 0.75, -4) : saturationPos
125 with{
126 sample(y) = (y + offset)*slope;
127 };
129 //----------------------- REED TABLE ----------------------------
130 // Simple reed table to be used with waveguide models of clanrinet, saxophone, etc.
131 //
132 // USAGE:
133 // _ : reed(offset,slope) : _
134 // where
135 // offset = offset between 0 and 1
136 // slope = slope between 0 and 1
137 // REFERENCE:
138 // https://ccrma.stanford.edu/~jos/pasp/View_Single_Reed_Oscillation.html
140 reed(offset,slope) = reedTable : saturationPos : saturationNeg
141 with{
142 reedTable = offset + (slope*_);
143 };
145 //========================= FILTERS ===============================
147 //----------------------- ONE POLE ----------------------------
149 onePole(b0,a1,x) = (b0*x - a1*_)~_;
151 //----------------------- ONE POLE SWEPT ----------------------------
153 onePoleSwep(a1,x) = (1 + a1)*x - a1*x';
155 //----------------------- POLE ZERO ----------------------------
157 poleZero(b0,b1,a1,x) = (b0*x + b1*x' - a1*_)~_;
159 //----------------------- ONE ZEROS ----------------------------
160 // Simple One zero and One zero recursive filters
161 //
162 // USAGE:
163 // _ : oneZero0(b0,b1) : _
164 // _ : oneZero1(b0,b1) : _
165 // REFERENCE:
166 // https://ccrma.stanford.edu/~jos/fp2/One_Zero.html
168 oneZero0(b0,b1,x) = (*(b1) + x*b0)~_;
169 oneZero1(b0,b1,x) = (x'*b1 + x*b0);
171 //----------------------- BANDPASS FILTER WITH CONSTANT UNITY PEAK GAIN BASED ON A BIQUAD ----------------------------
174 with{
177 b0 = 0.5-0.5*a2;
178 b1 = 0;
179 b2 = -b0;
180 };
182 //----------------------- BANDPASS FILTER BASED ON A BIQUAD ----------------------------
183 // Band pass filter using a biquad (TF2 is declared in filter.lib)
184 //
185 // USAGE:
186 // _ : bandPassH(resonance,radius) : _
187 // where
188 // resonance = center frequency
192 with{
195 b0 = 1;
196 b1 = 0;
197 b2 = 0;
198 };
200 //----------------------- FLUE JET NON-LINEAR FUNCTION ----------------------------
201 // Jet Table: flue jet non-linear function, computed by a polynomial calculation
203 jetTable(x) = x <: _*(_*_-1) : saturationPos : saturationNeg;
205 //----------------------- NON LINEAR MODULATOR ----------------------------
206 // nonLinearModulator adapts the function allpassnn from filter.lib for using it with waveguide instruments
207 //
208 // USAGE:
209 // _ : nonLinearModulator(nonlinearity,env,freq,typeMod,freqMod,order) : _
210 // where
211 // nonlinearity = nonlinearity coefficient between 0 and 1
212 // env = input to connect any kind of envelope
213 // freq = current tone frequency
214 // typeMod = if 0: theta is modulated by the incoming signal;
215 // if 1: theta is modulated by the averaged incoming signal;
216 // if 2: theta is modulated by the squared incoming signal;
217 // if 3: theta is modulated by a sine wave of frequency freqMod;
218 // if 4: theta is modulated by a sine wave of frequency freq;
219 // freqMod = frequency of the sine wave modulation
220 // order = order of the filter
222 nonLinearModulator(nonlinearity,env,freq,typeMod,freqMod,order) =
223 //theta is modulated by a sine wave
224 _ <: nonLinearFilterOsc*(typeMod >= 3),
225 //theta is modulated by the incoming signal
226 (_ <: nonLinearFilterSig*nonlinearity,_*(1 - nonlinearity) :> +)*(typeMod < 3)
227 :> +
228 with{
229 //which frequency to use for the sine wave oscillator?
230 freqOscMod = (typeMod == 4)*freq + (typeMod != 4)*freqMod;
232 //the incoming signal is scaled and the envelope is applied
233 tsignorm(x) = nonlinearity*PI*x*env;
234 tsigsquared(x) = nonlinearity*PI*x*x*env; //incoming signal is squared
235 tsigav(x) = nonlinearity*PI*((x + x')/2)*env; //incoming signal is averaged with its previous sample
237 //select which version of the incoming signal of theta to use
238 tsig(x) = tsignorm(x)*(typeMod == 0) + tsigav(x)*(typeMod == 1)
239 + tsigsquared(x)*(typeMod == 2);
241 //theta is modulated by a sine wave generator
242 tosc = nonlinearity*PI*osc(freqOscMod)*env;
244 //incoming signal is sent to the nonlinear passive allpass ladder filter
245 nonLinearFilterSig(x) = x <: allpassnn(order,(par(i,order,tsig(x))));
246 nonLinearFilterOsc = _ <: allpassnn(order,(par(i,order,tosc)));
247 };
249 //========================= WAVE TABLES ===============================
251 //----------------------- STICK IMPACT ----------------------------
252 // Stick impact table.
253 //
254 // USAGE:
255 // index : readMarmstk1 : _
258 marmstk1TableSize = 246;
260 //========================= TOOLS ===============================
262 //----------------------- STEREOIZER ----------------------------
263 // This function takes a mono input signal and spacialize it in stereo
264 // in function of the period duration of the tone being played.
265 //
266 // USAGE:
267 // _ : stereo(periodDuration) : _,_
268 // where
269 // periodDuration = period duration of the tone being played in number of samples
270 // ACKNOWLEDGMENT
271 // Formulation initiated by Julius O. Smith in https://ccrma.stanford.edu/realsimple/faust_strings/
273 stereoizer(periodDuration) = _ <: _,widthdelay : stereopanner
274 with{
275 W = hslider("v:Spat/spatial width", 0.5, 0, 1, 0.01);
276 A = hslider("v:Spat/pan angle", 0.6, 0, 1, 0.01);
277 widthdelay = delay(4096,W*periodDuration/2);
278 stereopanner = _,_ : *(1.0-A), *(A);
279 };
281 //----------------------- INSTRREVERB ----------------------------
282 // GUI for zita_rev1_stereo from effect.lib
283 //
284 // USAGE:
285 // _,_ : instrRerveb
287 instrReverb = _,_ <: *(reverbGain),*(reverbGain),*(1 - reverbGain),*(1 - reverbGain) :
288 zita_rev1_stereo(rdel,f1,f2,t60dc,t60m,fsmax),_,_ <: _,!,_,!,!,_,!,_ : +,+
289 with{
290 reverbGain = hslider("v:Reverb/reverbGain",0.137,0,1,0.01) : smooth(0.999);
291 roomSize = hslider("v:Reverb/roomSize",0.72,0.01,2,0.01);
292 rdel = 20;
293 f1 = 200;
294 f2 = 6000;
295 t60dc = roomSize*3;
296 t60m = roomSize*2;
297 fsmax = 48000;
298 };