Merge branch 'master' of https://scm.cri.ensmp.fr/git/Faustine
[Faustine.git] / interpretor / preprocessor / faust-0.9.47mr3 / examples / faust-stk / piano.dsp
1 declare name "Commuted Piano";
2 declare description "WaveGuide Commuted Piano";
3 declare author "Romain Michon (rmichon@ccrma.stanford.edu)";
4 declare copyright "Romain Michon";
5 declare version "1.0";
6 declare licence "STK-4.3"; // Synthesis Tool Kit 4.3 (MIT style license);
7 declare description "A commuted WaveGuide piano.";
8
9 import("music.lib");
10 import("instrument.lib");
11
12 //==================== GUI SPECIFICATION ================
13
14 freq = nentry("h:Basic_Parameters/freq [1][unit:Hz] [tooltip:Tone frequency]",440,20,20000,1);
15 gain = nentry("h:Basic_Parameters/gain [1][tooltip:Gain (value between 0 and 1)]",1,0,1,0.01);
16 gate = button("h:Basic_Parameters/gate [1][tooltip:noteOn = 1, noteOff = 0]");
17
18 brightnessFactor = hslider("v:Physical_Parameters/Brightness_Factor
19 [2][tooltip:A value between 0 and 1]",0,0,1,0.01);
20 detuningFactor = hslider("v:Physical_Parameters/Detuning_Factor
21 [2][tooltip:A value between 0 and 1]",0.1,0,1,0.01)*10;
22 stiffnessFactor = hslider("v:Physical_Parameters/Stiffness_Factor
23 [2][tooltip:A value between 0 and 1]",0.28,0,1,0.01)*3.7;
24 hammerHardness = hslider("v:Physical_Parameters/Hammer_Hardness
25 [2][tooltip:A value between 0 and 1]",0.1,0,1,0.01)*0.1;
26
27 //==================== COMMUTED PIANO PARAMETERS ================
28
29 //variables to set keybord splitting zone
30 DCB2_TURNOFF_KEYNUM = 92;
31 FIRST_HIGH_NOTE = 88;
32 PEDAL_ENVELOPE_T60 = 7;
33
34 //convert an amplitude in db
35 dbinv(x) = pow(10,0.05*x);
36
37 //convert a frequency in a midi note number
38 freqToNoteNumber = (log-log(440))/log(2)*12+69+0.5 : int;
39 freqn = freq : freqToNoteNumber;
40
41 //a counter that restart a every note-on
42 cntSample = *(gate)+1~_ : -(1);
43
44 //==================== PIANO SOUND BOARD ================
45
46 //exponential envelope with 3 phases for the pedal excitation
47 asympT60pedal(value,T60) = (*(factor) + constant)~_
48 with{
49 attDur = hammerHardness*float(SR);
50 target = value*((cntSample < attDur) & (gate > 0));
51 factorAtt = exp (-1/(attDur));
52 factorG = exp(-1/(2*float(SR)));
53 factorT60 = exp(-7/(T60*float(SR)));
54 factor = factorAtt*gate*(cntSample < attDur) + (cntSample >= attDur)*gate*factorG + ((gate-1)*-1)*factorT60;
55 constant = (1 - factor)*target;
56 };
57
58 //the sound of the piano sound board is generated by noise generator whose output gain is shaped by
59 //an exponential envelope
60 soundBoard = dryTapAmp*noise + pedalEnv*noise : *(0.5)
61 with{
62 //the values of the envelope cut-off time are stored in an external C++ function
63 dryTapAmpT60 = ffunction(float getValueDryTapAmpT60(float), <piano.h>,"");
64 sustainPedalLevel = ffunction(float getValueSustainPedalLevel(float), <piano.h>,"");
65
66 pedalEnvCutOffTime = 1.4;
67 noteCutOffTime = freqn : dryTapAmpT60*gain;
68 pedalEnvValue = freqn : sustainPedalLevel*0.2;
69 noteEnvValue = 0.15;
70 dryTapAmp = asympT60(noteEnvValue,0,noteCutOffTime,gate);
71 pedalEnv = asympT60pedal(pedalEnvValue,pedalEnvCutOffTime);
72 };
73
74 //==================== HAMMER MODELING ================
75
76 //To model the exitation hammer, we filter the sound from the soundboard with a serie of 4 one pole filters
77 //connected in serie
78
79 //onePole is declared in instrument.lib
80 calcHammer = onePole((1-hammerPole)*hammerGain,-hammerPole)
81 with{
82 //filter gains and coefficients are stored in external C++ files
83 loudPole = ffunction(float getValueLoudPole(float), <piano.h>,"");
84 softPole = ffunction(float getValuePoleValue(float), <piano.h>,"");
85 loudGain = ffunction(float getValueLoudGain(float), <piano.h>,"");
86 softGain = ffunction(float getValueSoftGain(float), <piano.h>,"");
87
88 loudPoleValue = loudPole(freqn) + (brightnessFactor*-0.25) + 0.02;
89 softPoleValue = softPole(freqn);
90 normalizedVelocityValue = 1;
91 loudGainValue = loudGain(freqn);
92 softGainValue = softGain(freqn);
93 overallGain = 1;
94 hammerPole = softPoleValue + (loudPoleValue - softPoleValue)*normalizedVelocityValue;
95 hammerGain = overallGain*(softGainValue + (loudGainValue - softGainValue)*normalizedVelocityValue);
96 };
97
98 hammer = seq(i,4,calcHammer);
99
100 //==================== DC BLOCKERS ================
101
102 //the values for the dcblockers a1 are stored in an external C++ file
103 DCBa1 = ffunction(float getValueDCBa1(float), <piano.h>,"");
104 dCBa1Value = freqn : DCBa1;
105 dCBb0Value = 1 - dCBa1Value;
106
107 dcBlock1 = poleZero((dCBb0Value*0.5),(dCBb0Value*-0.5),dCBa1Value);
108
109 dcBlock2a = oneZero1(0.5,-0.5);
110
111 dcBlock2b = onePole(dCBb0Value,dCBa1Value);
112
113 //==================== HIGH TUNING CALCULATION ================
114
115 //high tones are not generated with the waveguide technique but with a serie of biquad filters
116
117 r1_1 = ffunction(float getValuer1_1db(float), <piano.h>,"");
118 r1_2 = ffunction(float getValuer1_2db(float), <piano.h>,"");
119 r2 = ffunction(float getValuer2db(float), <piano.h>,"");
120 r3 = ffunction(float getValuer3db(float), <piano.h>,"");
121 e = ffunction(float getValueSecondStageAmpRatio(float), <piano.h>,"");
122 second_partial_factor = ffunction(float getValueSecondPartialFactor(float), <piano.h>,"");
123 third_partial_factor = ffunction(float getValueThirdPartialFactor(float), <piano.h>,"");
124 bq4_gEarBalled = ffunction(float getValueBq4_gEarBalled(float), <piano.h>,"");
125
126 r1_1Value = r1_1(freqn)/SR : dbinv;
127 r1_2Value = r1_2(freqn)/SR : dbinv;
128 r2Value = r2(freqn)/SR : dbinv;
129 r3Value = r3(freqn)/SR : dbinv;
130 eValue = e(freqn) : dbinv;
131 second_partial_factorValue = second_partial_factor(freqn);
132 third_partial_factorValue = third_partial_factor(freqn);
133
134 //set biquad gains and coeffs
135 gainHighBq(0) = bq4_gEarBalled(freqn)/0.5;
136 gainHighBq(1) = bq4_gEarBalled(freqn)/0.5;
137 gainHighBq(2) = 1;
138 gainHighBq(3) = 1;
139
140 b0HighBq(0) = 1;
141 b0HighBq(1) = 1;
142 b0HighBq(2) = 1;
143 b0HighBq(3) = 1;
144
145 b1HighBq(0) = 0;
146 b1HighBq(1) = 0;
147 b1HighBq(2) = -2*(eValue*r1_1Value+(1-eValue)*r1_2Value)*cos(2*PI*freq/SR);
148 b1HighBq(3) = 0;
149
150 b2HighBq(0) = 0;
151 b2HighBq(1) = 0;
152 b2HighBq(2) = eValue*r1_1Value*r1_1Value+(1-eValue)*r1_2Value*r1_2Value;
153 b2HighBq(3) = 0;
154
155 a1HighBq(0) = -2*r3Value*cos(2*PI*freq*third_partial_factorValue/SR);
156 a1HighBq(1) = -2*r2Value*cos(2*PI*freq*second_partial_factorValue/SR);
157 a1HighBq(2) = -2*r1_1Value*cos(2*PI*freq/SR);
158 a1HighBq(3) = -2*r1_2Value*cos(2*PI*freq/SR);
159
160 a2HighBq(0) = r3Value*r3Value;
161 a2HighBq(1) = r2Value*r2Value;
162 a2HighBq(2) = r1_1Value*r1_1Value;
163 a2HighBq(3) = r1_2Value*r1_2Value;
164
165 highBqs = seq(i,4,*(gainHighBq(i)) : TF2(b0HighBq(i),b1HighBq(i),b2HighBq(i),a1HighBq(i),a2HighBq(i)));
166
167 hiPass = oneZero1(b0,b1)
168 with{
169 b0 = -0.5;
170 b1 = -0.5;
171 };
172
173 //==================== STRIKE POSITION COMB FILTER EQ ================
174
175 eq = _*filterGain : TF2(b0,b1,b2,a1,a2)
176 with{
177 strikePosition = ffunction(float getValueStrikePosition(float), <piano.h>,"");
178 bandwidthFactors = ffunction(float getValueEQBandWidthFactor(float), <piano.h>,"");
179 eq_gain = ffunction(float getValueEQGain(float), <piano.h>,"");
180 eq_tuning = freq/strikePosition(freqn);
181 eq_bandwidth = bandwidthFactors(freqn)*freq;
182 filterGain = eq_gain(freqn);
183 a2 = (eq_bandwidth / SR) * (eq_bandwidth / SR);
184 a1 = -2*(eq_bandwidth / SR)*cos(2*PI*eq_tuning/SR);
185 b0 = 0.5 - 0.5 * a2;
186 b1 = 0;
187 b2 = -b0;
188 };
189
190 //==================== PIANO COUPLED STRINGS ================
191
192 //values for the couple strings are stored in externals C++ functions
193 singleStringDecRate = ffunction(float getValueSingleStringDecayRate(float), <piano.h>,"");
194 singleStringZero = ffunction(float getValueSingleStringZero(float), <piano.h>,"");
195 singleStringPole = ffunction(float getValueSingleStringPole(float), <piano.h>,"");
196 stiffnessCoefficient = ffunction(float getValueStiffnessCoefficient(float), <piano.h>,"");
197
198 //coupling filter parameters
199 g = pow(10,((singleStringDecRate(freqn)/freq)/20)); //attenuation per period
200 b = singleStringZero(freqn);
201 a = singleStringPole(freqn);
202 tempd = 3*(1-b)-g*(1-a);
203 b0Coupling = 2*(g*(1-a)-(1-b))/tempd;
204 b1Coupling = 2*(a*(1-b)-g*(1-a)*b)/tempd;
205 a1Coupling = (g*(1-a)*b - 3*a*(1-b))/tempd;
206
207 //string stiffness
208 stiffness = stiffnessFactor*stiffnessCoefficient(freqn);
209
210 stiffnessAP = poleZero(b0s,b1s,a1s)
211 with{
212 b0s = stiffness;
213 b1s = 1;
214 a1s = stiffness;
215 };
216
217 delayG(frequency,stiffnessCoefficient) = fdelay(4096,delayLength)
218 with{
219 allPassPhase(a1,WT) = atan2((a1*a1-1.0)*sin(WT),(2.0*a1+(a1*a1+1.0)*cos(WT)));
220 poleZeroPhase(b0,b1,a1,WT) = atan2(-b1*sin(WT)*(1 + a1*cos(WT)) + a1*sin(WT)*(b0 + b1*cos(WT)),
221 (b0 + b1*cos(WT))*(1 + a1*cos(WT)) + b1*sin(WT)*a1*sin(WT));
222 wT = frequency*2*PI/SR;
223 delayLength = (2*PI + 3*allPassPhase(stiffnessCoefficient, wT) +
224 poleZeroPhase((1+2*b0Coupling),
225 a1Coupling + 2*b1Coupling, a1Coupling, wT)) / wT;
226 };
227
228 coupledStrings = (parallelStrings <: (_,(_+_ <: _,_),_ : _,_,(_ : couplingFilter),_ : adder))~(_,_) : !,!,_
229 with{
230 releaseLoopGain = ffunction(float getValueReleaseLoopGain(float), <piano.h>,"");
231 hz = ffunction(float getValueDetuningHz(float), <piano.h>,"");
232 coupledStringLoopGain = gate*0.9996 + ((gate-1)*-1)*releaseLoopGain(freqn)*0.9 : smooth(0.999);
233 couplingFilter = poleZero(b0Coupling,b1Coupling,a1Coupling);
234 hzValue = hz(freqn);
235 freq1 = freq + 0.5*hzValue*detuningFactor;
236 freq2 = freq - 0.5*hzValue*detuningFactor;
237 delay1 = delayG(freq1,stiffness);
238 delay2 = delayG(freq2,stiffness);
239 parallelStrings(x,y) = _ <: (+(x)*coupledStringLoopGain : seq(i,3,stiffnessAP) : delay1),
240 (_+y*coupledStringLoopGain : seq(i,3,stiffnessAP) : delay2);
241 adder(w,x,y,z) = (y <: +(w),+(z)),x ;
242 };
243
244 //stereoizer is declared in instrument.lib and implement a stereo spacialisation in function of
245 //the frequency period in number of samples
246 stereo = stereoizer(SR/freq);
247
248 //==================== PROCESSING ================
249
250 conditionLowNote = freqn < FIRST_HIGH_NOTE;
251 conditionHighNote = freqn >= FIRST_HIGH_NOTE;
252
253 process = soundBoard <: (*(conditionLowNote)*6 : hammer : dcBlock1 : coupledStrings <: +(eq)),
254 (*(conditionHighNote) : hiPass : dcBlock1 : hammer : dcBlock2a : highBqs : dcBlock2b) :> + : *(12) :
255 stereo : instrReverb;