1 declare name "BlowHole";
2 declare description "Nonlinear WaveGuide Clarinet with one register hole and one tonehole";
3 declare author "Romain Michon";
4 declare copyright "Romain Michon (rmichon@ccrma.stanford.edu)";
6 declare licence "STK-4.3"; // Synthesis Tool Kit 4.3 (MIT style license);
7 declare description "A clarinet model, with the addition of a two-port register hole and a three-port dynamic tonehole implementation, as discussed by Scavone and Cook (1998). In this implementation, the distances between the reed/register hole and tonehole/bell are fixed. As a result, both the tonehole and register hole will have variable influence on the playing frequency, which is dependent on the length of the air column. In addition, the highest playing freqeuency is limited by these fixed lengths.";
8 declare reference "https://ccrma.stanford.edu/~jos/pasp/Woodwinds.html";
11 import("instrument.lib");
13 //==================== GUI SPECIFICATION ================
15 freq = nentry("h:Basic_Parameters/freq [1][unit:Hz] [tooltip:Tone frequency]",440,20,20000,1);
16 gain = nentry("h:Basic_Parameters/gain [1][tooltip:Gain (value between 0 and 1)]",1,0,1,0.01);
17 gate = button("h:Basic_Parameters/gate [1][tooltip:noteOn = 1, noteOff = 0]");
19 reedStiffness = hslider("h:Physical_and_Nonlinearity/v:Physical_Parameters/Reed_Stiffness
20 [2][tooltip:Reed stiffness (value between 0 and 1)]",0.35,0,1,0.01);
21 toneHoleOpenness = hslider("h:Physical_and_Nonlinearity/v:Physical_Parameters/Tone_Hole_Openness
22 [2][tooltip:A value between 0 and 1]",0.12,0,1,0.01);
23 ventOpenness = hslider("h:Physical_and_Nonlinearity/v:Physical_Parameters/Vent_Openness
24 [2][tooltip:A value between 0 and 1]",0,0,1,0.01);
25 noiseGain = hslider("h:Physical_and_Nonlinearity/v:Physical_Parameters/Noise_Gain
26 [2][tooltip:Breath noise gain (value between 0 and 1)]",0,0,1,0.01);
27 pressure = hslider("h:Physical_and_Nonlinearity/v:Physical_Parameters/Pressure
28 [2][tooltip:Breath pressure (value bewteen 0 and 1)]",0.35,0,1,0.01);
30 typeModulation = nentry("h:Physical_and_Nonlinearity/v:Nonlinear_Filter_Parameters/Modulation_Type
31 [3][tooltip: 0=theta is modulated by the incoming signal; 1=theta is modulated by the averaged incoming signal;
32 2=theta is modulated by the squared incoming signal; 3=theta is modulated by a sine wave of frequency freqMod;
33 4=theta is modulated by a sine wave of frequency freq;]",0,0,4,1);
34 nonLinearity = hslider("h:Physical_and_Nonlinearity/v:Nonlinear_Filter_Parameters/Nonlinearity
35 [3][tooltip:Nonlinearity factor (value between 0 and 1)]",0,0,1,0.01);
36 frequencyMod = hslider("h:Physical_and_Nonlinearity/v:Nonlinear_Filter_Parameters/Modulation_Frequency
37 [3][unit:Hz][tooltip:Frequency of the sine wave for the modulation of theta (works if Modulation Type=3)]",220,20,1000,0.1);
38 nonLinAttack = hslider("h:Physical_and_Nonlinearity/v:Nonlinear_Filter_Parameters/Nonlinearity_Attack
39 [3][unit:s][Attack duration of the nonlinearity]",0.1,0,2,0.01);
41 vibratoFreq = hslider("h:Envelopes_and_Vibrato/v:Vibrato_Parameters/Vibrato_Freq
42 [4][unit:Hz]",5,1,15,0.1);
43 vibratoGain = hslider("h:Envelopes_and_Vibrato/v:Vibrato_Parameters/Vibrato_Gain
44 [4][tooltip:A value between 0 and 1]",0.1,0,1,0.01);
45 vibratoAttack = hslider("h:Envelopes_and_Vibrato/v:Vibrato_Parameters/Vibrato_Attack
46 [4][unit:s][tooltip:Vibrato attack duration]",0.5,0,2,0.01);
47 vibratoRelease = hslider("h:Envelopes_and_Vibrato/v:Vibrato_Parameters/Vibrato_Release
48 [4][unit:s][tooltip:Vibrato release duration]",0.01,0,2,0.01);
50 envelopeAttack = hslider("h:Envelopes_and_Vibrato/v:Envelope_Parameters/Envelope_Attack
51 [5][unit:s][tooltip:Envelope attack duration]",0.01,0,2,0.01);
52 envelopeDecay = hslider("h:Envelopes_and_Vibrato/v:Envelope_Parameters/Envelope_Decay
53 [5][unit:s][tooltip:Envelope decay duration]",0.05,0,2,0.01);
54 envelopeRelease = hslider("h:Envelopes_and_Vibrato/v:Envelope_Parameters/Envelope_Release
55 [5][unit:s][tooltip:Envelope release duration]",0.1,0,2,0.01);
57 //==================== SIGNAL PROCESSING ================
59 //----------------------- Nonlinear filter ----------------------------
60 //nonlinearities are created by the nonlinear passive allpass ladder filter declared in filter.lib
62 //nonlinear filter order (problem with compilation time if order is bigger than 2)
65 //attack - sustain - release envelope for nonlinearity (declared in instrument.lib)
66 envelopeMod = asr(nonLinAttack,100,envelopeRelease,gate);
68 //nonLinearModultor is declared in instrument.lib, it adapts allpassnn from filter.lib
69 //for using it with waveguide instruments
70 NLFM = nonLinearModulator((nonLinearity : smooth(0.999)),envelopeMod,freq,
71 typeModulation,(frequencyMod : smooth(0.999)),nlfOrder);
73 //----------------------- Synthesis parameters computing and functions declaration ----------------------------
75 //reed table parameters
76 reedTableOffset = 0.7;
77 reedTableSlope = -0.44 + (0.26*reedStiffness);
79 //the reed function is declared in instrument.lib
80 reedTable = reed(reedTableOffset,reedTableSlope);
82 // Calculate the initial tonehole three-port scattering coefficient
83 rb = 0.0075; // main bore radius
84 rth = 0.003; // tonehole radius
85 scattering = pow(rth,2)*-1 / (pow(rth,2) + 2*pow(rb,2));
87 // Calculate register hole filter coefficients
88 r_rh = 0.0015; // register vent radius
89 teVent = 1.4*r_rh; // effective length of the open hole
90 xi = 0 ; // series resistance term
91 zeta = 347.23 + 2*PI*pow(rb,2)*xi/1.1769;
92 psi = 2*PI*pow(rb,2)*teVent/(PI*pow(r_rh,2));
93 rhCoeff = (zeta - 2*SR*psi)/(zeta + 2*SR*psi);
94 rhGain = -347.23/(zeta + 2*SR*psi);
95 ventFilterGain = rhGain*ventOpenness;
98 ventFilter = *(ventFilterGain) : poleZero(1,1,rhCoeff);
100 teHole = 1.4*rth; // effective length of the open hole
101 coeff = (teHole*2*SR - 347.23)/(teHole*2*SR + 347.23);
102 scaledCoeff = (toneHoleOpenness*(coeff - 0.9995)) + 0.9995;
104 //register hole filter using a poleZero filter (declared in instrument.lib)
105 toneHoleFilter = *(1) : poleZero(b0,-1,a1)
111 //reflexion filter is a one zero filter (delcred in instrument.lib)
112 reflexionFilter = oneZero0(0.5,0.5)*-0.95;
114 //delay lengths in number of samples
115 delay0Length = 5*SR/22050;
116 delay2Length = 4*SR/22050;
117 delay1Length = (SR/freq*0.5 - 3.5) - (delay0Length + delay2Length) - (nlfOrder*nonLinearity)*(typeModulation < 2);
119 //fractional delay lines
120 delay0 = fdelay(4096,delay0Length);
121 delay1 = fdelay(4096,delay1Length);
122 delay2 = fdelay(4096,delay2Length);
124 //stereoizer is declared in instrument.lib and implement a stereo spacialisation in function of
125 //the frequency period in number of samples
126 stereo = stereoizer(SR/freq);
128 //----------------------- Algorithm implementation ----------------------------
130 //envelope(ADSR) + vibrato + noise
131 envelope = (0.55 + pressure*0.3)*asr(pressure*envelopeAttack,100,pressure*envelopeRelease,gate);
132 vibratoEnvelope = envVibrato(0.1*2*vibratoAttack,0.9*2*vibratoAttack,100,vibratoRelease,gate);
133 vibrato = vibratoGain*osc(vibratoFreq)*vibratoEnvelope;
134 breath = envelope + envelope*noiseGain*noise;
135 breathPressure = breath + (breath*vibrato);
137 //two-port junction scattering for register vent
138 twoPortJunction(portB) = (pressureDiff : ((_ <: breathPressure + *(reedTable)) <: (+(portB) : ventFilter <: +(portB),_),_))~
139 delay0 : inverter : + ,_
141 pressureDiff = -(breathPressure);
142 inverter(a,b,c) = b,c,a;
145 //three-port junction scattering (under tonehole)
146 threePortJunction(twoPortOutput) = (_ <: junctionScattering(twoPortOutput),_ : +(twoPortOutput), + :
147 reflexionFilter,_)~delay2 : !,_
149 toneHole(temp,portA2,portB2) = (portA2 + portB2-_ + temp : toneHoleFilter)~_;
150 junctionScattering(portA2,portB2) = (((portA2+portB2-2*_)*scattering) <: toneHole(_,portA2,portB2),_,_)~_ : !,_,_;
153 process = (twoPortJunction : threePortJunction,_) ~ (delay1 : NLFM) : !,*(gain)*1.5 : stereo : instrReverb;