/
piano_coupled_strings.cpp
204 lines (161 loc) · 5.22 KB
/
piano_coupled_strings.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include <math.h>
#include "piano_coupled_strings.h"
#include "piano_coefficients.h"
#define LOOP_GAIN_CUTOFF_T60 (.05)
#define MAX_LOOP_GAIN (.9996)
#define FIRST_DAMPERLESS_KEYNUM 89
#define NO_MORE_STIFFNESS_CUTOFF_KEYNUM 92
CoupledStrings::CoupledStrings()
: Effect(),
delay1(300, 2048), delay2(301, 2048)
{
// initialize control parameters
detuningFactor = 1;
stiffnessFactor = 1;
setFrequency(440);
loopGain.setValue( MAX_LOOP_GAIN );
loopGain.setTarget( MAX_LOOP_GAIN );
// feedback values
prev_y1 = prev_y2 = 0;
}
StkFloat CoupledStrings::AllPassPhase(StkFloat a1, StkFloat wT)
{
// Return the phase delay of a pole-zero allpass filter
return atan2((a1*a1-1.0)*sin(wT),(2.0*a1+(a1*a1+1.0)*cos(wT)));
}
StkFloat CoupledStrings::PoleZeroPhase(StkFloat b0, StkFloat b1, StkFloat a1, StkFloat wT)
{
// Return the phase delay of a pole-zero filter
StkFloat temp = atan2(-b1*sin(wT)*(1 + a1*cos(wT)) + a1*sin(wT)*(b0 + b1*cos(wT)),
(b0 + b1*cos(wT))*(1 + a1*cos(wT)) + b1*sin(wT)*a1*sin(wT));
return temp;
}
StkFloat CoupledStrings::delayLength(StkFloat freq, StkFloat stiffnessCoefficient)
{
// Calculate delay length for a particular frequency
// accounting for stiffness allpass filter delays
// and coupling filter delay
StkFloat wT = freq * 2*M_PI / Stk::sampleRate();
int num_strings = 2;
StkFloat len = (2*M_PI
+ 3*AllPassPhase(stiffnessCoefficient, wT)
+ PoleZeroPhase(1 + num_strings*(couplingB0),
couplingA1 + num_strings*couplingB1,
couplingA1, wT))
/wT;
return len;
}
int CoupledStrings::FrequencyToNoteNumber(StkFloat frequency)
{
return (int)((log(frequency)-log(440.0))/log(2.0)*12+69+0.5);
}
StkFloat CoupledStrings::NoteNumberToFrequency(int note)
{
return (StkFloat) (440.0/32.0) * pow( 2, (note - 9) / 12.0 );
}
void CoupledStrings::setDetuningFactor(StkFloat factor)
{
if (factor < 0) factor = 0;
if (factor > 1) factor = 1;
detuningFactor = factor * 9.0 + 1.0; // range 1 - 10
setFrequency(frequency);
}
void CoupledStrings::setStiffnessFactor(StkFloat factor)
{
if (factor < 0) factor = 0;
if (factor > 1) factor = 1;
stiffnessFactor = factor * 0.8 + 1; // range 1 - 1.8
setFrequency(frequency);
}
void CoupledStrings::calcCouplingFilter()
{
// Coupling Filter
StkFloat attenuationPerPeriod = pow(10.0,((singleStringDecayRate.getValue(noteNumber)/frequency)/20.0));
StkFloat g = attenuationPerPeriod;
StkFloat b = singleStringZero.getValue(noteNumber);
StkFloat a = singleStringPole.getValue(noteNumber);
StkFloat tempd = 3*(1-b)-g*(1-a);
couplingB0 = 2*(g*(1-a)-(1-b))/tempd;
couplingB1 = 2*(a*(1-b)-g*(1-a)*b)/tempd;
couplingA1 = (g*(1-a)*b - 3*a*(1-b))/tempd;
couplingPoleZero.setB0(couplingB0);
couplingPoleZero.setB1(couplingB1);
couplingPoleZero.setA1(couplingA1);
}
void CoupledStrings::setFrequency(StkFloat freq)
{
frequency = freq;
noteNumber = FrequencyToNoteNumber(frequency);
StkFloat hz = detuningHz.getValue(noteNumber);
freq1 = frequency + 0.5*hz*detuningFactor;
freq2 = frequency - 0.5*hz*detuningFactor;
// Coupling Filter
calcCouplingFilter();
// Stiffness Allpasses
StkFloat stiffness = stiffnessCoefficient.getValue(noteNumber);
if (noteNumber < NO_MORE_STIFFNESS_CUTOFF_KEYNUM){
if(stiffnessFactor>1.0) //assume between 0 and 2
stiffness -= (stiffness+1.0)*(stiffnessFactor-1.0);
else stiffness *= stiffnessFactor;
}
for (int i=0; i<6; i++)
stiffnessAP[i].setAllpass(stiffness);
delay1.setDelay(delayLength(freq1, stiffness));
delay2.setDelay(delayLength(freq2, stiffness));
}
void CoupledStrings::noteOn (StkFloat frequency, StkFloat amplitude)
{
setFrequency(frequency);
loopGain.setValue( MAX_LOOP_GAIN );
loopGain.setTarget( MAX_LOOP_GAIN );
}
void CoupledStrings::noteOff (StkFloat amplitude)
{
if (noteNumber < FIRST_DAMPERLESS_KEYNUM) {
loopGain.setTarget( releaseLoopGain.getValue(noteNumber) );
loopGain.setT60( LOOP_GAIN_CUTOFF_T60 );
}
}
void CoupledStrings::controlChange (int number, StkFloat value)
{
}
StkFloat CoupledStrings::computeSample(StkFloat input)
{
int i;
StkFloat y, y1, y2;
StkFloat cutoff = loopGain.tick();
y1 = input + (prev_y1 * cutoff);
y2 = input + (prev_y2 * cutoff);
StkFloat stringOut = y1 + y2;
y = couplingPoleZero.tick(stringOut);
y1 += y;
y2 += y;
for (i=0; i<3; i++) {
y1 = stiffnessAP[i].tick(y1);
y2 = stiffnessAP[i+3].tick(y2);
}
prev_y1 = delay1.tick(y1);
prev_y2 = delay2.tick(y2);
return stringOut;
}
StkFrames& CoupledStrings::tick( StkFrames& frames, unsigned int channel )
{
#if defined(_STK_DEBUG_)
if ( channel >= frames.channels() ) {
errorString_ << "Delay::tick(): channel and StkFrames arguments are incompatible!";
handleError( StkError::FUNCTION_ARGUMENT );
}
#endif
StkFloat *samples = &frames[channel];
unsigned int hop = frames.channels();
for ( unsigned int i=0; i<frames.frames(); i++, samples += hop ) {
*samples = computeSample(*samples);
}
lastFrame_[0] = *(samples-hop);
return frames;
}
StkFloat CoupledStrings::tick( StkFloat input, unsigned int channel )
{
lastFrame_[0] = computeSample(input);
return lastFrame_[0];
}