Mercurial > hg > beaglert
comparison examples/d-box/OscillatorBank.cpp @ 300:dbeed520b014 prerelease
Renamed projects to examples
author | Giulio Moro <giuliomoro@yahoo.it> |
---|---|
date | Fri, 27 May 2016 13:58:20 +0100 |
parents | projects/d-box/OscillatorBank.cpp@8a575ba3ab52 |
children |
comparison
equal
deleted
inserted
replaced
297:a3d83ebdf49b | 300:dbeed520b014 |
---|---|
1 /* | |
2 * OscillatorBank.cpp | |
3 * | |
4 * Created on: May 23, 2014 | |
5 * Author: Victor Zappi and Andrew McPherson | |
6 */ | |
7 | |
8 | |
9 /* | |
10 * There is a problem with name consistency between this class and the Parser class in spear_parser files. | |
11 * There, a "frame" is each of the time values where partials are sampled, while a "hop" is the actual jump between frames [calculated in samples] | |
12 * Here, "hop" is used with the meaning of "frame", while "frame" became the local frame of a partial | |
13 * | |
14 * example | |
15 * | |
16 * frames: 0 1 2 | |
17 * p0 p0_0 p0_1 | |
18 * p1 p1_0 p1_1 p1_2 | |
19 * p2 p2_0 p2_1 | |
20 * | |
21 * In this case: | |
22 * in Parser there are 2 hops, 3 total frames and the 3 partials have respectively 2, 3 and 2 local frames | |
23 * | |
24 * here there are 3 total hops [the concept of jumps is discarded, cos not in use] and the 3 partials have respectively 2, 3 and 2 frames | |
25 * | |
26 * This must be fixed | |
27 */ | |
28 | |
29 // TODO: fix hop-frame name consistency | |
30 | |
31 | |
32 #include <stdlib.h> | |
33 | |
34 #include "OscillatorBank.h" | |
35 | |
36 OscillatorBank::OscillatorBank() { | |
37 loaded = false; | |
38 } | |
39 | |
40 OscillatorBank::OscillatorBank(string filename, int hopsize, int samplerate) { | |
41 loaded = false; | |
42 loadFile(filename.c_str(), hopsize, samplerate); | |
43 } | |
44 | |
45 OscillatorBank::OscillatorBank(char *filename, int hopsize, int samplerate) { | |
46 loaded = false; | |
47 loadFile(filename, hopsize, samplerate); | |
48 } | |
49 | |
50 OscillatorBank::~OscillatorBank() { | |
51 free(oscillatorPhases); | |
52 free(oscillatorNextNormFreq); | |
53 free(oscillatorNextAmp); | |
54 free(oscillatorNormFrequencies); | |
55 free(oscillatorAmplitudes); | |
56 free(oscillatorNormFreqDerivatives); | |
57 free(oscillatorAmplitudeDerivatives); | |
58 free(phaseCopies); | |
59 free(nextNormFreqCopies); | |
60 free(nextAmpCopies); | |
61 | |
62 delete[] oscStatNormFrequenciesMean; | |
63 delete[] oscStatNumHops; | |
64 delete[] lookupTable; | |
65 delete[] indicesMapping; | |
66 delete[] freqFixedDeltas; | |
67 delete[] ampFixedDeltas; | |
68 delete[] nyquistCut; | |
69 } | |
70 | |
71 bool OscillatorBank::initBank(int oversamp) { | |
72 if (!loaded) | |
73 return false; | |
74 | |
75 //---prepare look-up table | |
76 lookupTableSize = 1024; | |
77 lookupTable = new float[lookupTableSize + 1]; | |
78 for (int n = 0; n < (lookupTableSize + 1); n++) | |
79 lookupTable[n] = sin(2.0 * M_PI * (float) n / (float) lookupTableSize); | |
80 frequencyScaler = (float) lookupTableSize / rate; | |
81 nyqNorm = rate / 2 * frequencyScaler; | |
82 | |
83 if (oversamp < 1) | |
84 oversamp = 1; | |
85 | |
86 //---prepare oscillators | |
87 partials = &(parser.partials); // pointer to paser's partials | |
88 partialsHopSize = parser.getHopSize(); | |
89 lastHop = partials->getHopNum(); // last bank hop is equal to last partial frame, which is equal to partial hop num | |
90 overSampling = oversamp; | |
91 hopSize = partialsHopSize / overSampling; // if oversampling, osc bank hop num > partials hop num | |
92 hopSizeReminder = partialsHopSize % overSampling; | |
93 oscBankHopSize = hopSize; | |
94 numOfPartials = partials->getPartialNum(); | |
95 numOfOscillators = partials->getMaxActivePartialNum(); // get the maximum number of active partials at the same time | |
96 | |
97 // set to next multiple of 4 [NEON] | |
98 numOfOscillators = (numOfOscillators + 3) & ~0x3; // to be sure we can add up to 3 fake oscillators | |
99 | |
100 int err; | |
101 //---allocate buffers | |
102 // alligned buffers [NEON] | |
103 err = posix_memalign((void**) &oscillatorPhases, 16, | |
104 numOfOscillators * sizeof(float)); | |
105 err += posix_memalign((void**) &oscillatorNextNormFreq, 16, | |
106 numOfOscillators * sizeof(float)); | |
107 err += posix_memalign((void**) &oscillatorNextAmp, 16, | |
108 numOfOscillators * sizeof(float)); | |
109 err += posix_memalign((void**) &oscillatorNormFrequencies, 16, | |
110 numOfOscillators * sizeof(float)); | |
111 err += posix_memalign((void**) &oscillatorAmplitudes, 16, | |
112 numOfOscillators * sizeof(float)); | |
113 err += posix_memalign((void**) &oscillatorNormFreqDerivatives, 16, | |
114 numOfOscillators * sizeof(float)); | |
115 err += posix_memalign((void**) &oscillatorAmplitudeDerivatives, 16, | |
116 numOfOscillators * sizeof(float)); | |
117 err += posix_memalign((void**) &phaseCopies, 16, | |
118 numOfOscillators * sizeof(float)); | |
119 err += posix_memalign((void**) &nextNormFreqCopies, 16, | |
120 numOfOscillators * sizeof(float)); | |
121 err += posix_memalign((void**) &nextAmpCopies, 16, | |
122 numOfOscillators * sizeof(float)); | |
123 | |
124 // regular ones | |
125 oscStatNormFrequenciesMean = new float[numOfPartials]; | |
126 oscStatNumHops = new float[numOfPartials]; | |
127 indicesMapping = new int[numOfPartials]; | |
128 freqFixedDeltas = new float[numOfPartials]; | |
129 ampFixedDeltas = new float[numOfPartials]; | |
130 nyquistCut = new bool[numOfPartials]; | |
131 | |
132 if (err > 0) { | |
133 dbox_printf("Failed memory allocations %@#!\n"); | |
134 return false; | |
135 } | |
136 | |
137 // copy stats [they do not change] | |
138 for (int n = 0; n < numOfPartials; n++) { | |
139 oscStatNormFrequenciesMean[n] = partials->partialFreqMean[n] | |
140 * frequencyScaler; | |
141 oscStatNumHops[n] = partials->partialNumFrames[n]; // in Parser and Partials "frames" are what we call here "hops" [see comment at top of file] | |
142 } | |
143 | |
144 // deafult values | |
145 actPartNum = 0; | |
146 loopStartHop = 0; | |
147 loopEndHop = (parser.partials.getHopNum() - 2) * overSampling; | |
148 ampTh = 0.0001; | |
149 hopNumTh = 0; | |
150 pitchMultiplier = 1; | |
151 freqMovement = 1; | |
152 filterNum = 0; | |
153 note = false; | |
154 speed = 1; | |
155 nextSpeed = -1; | |
156 maxSpeed = 10; | |
157 minSpeed = 0.1; | |
158 jumpHop = -1; | |
159 | |
160 // filter | |
161 filterMaxF = 22000; | |
162 filterAmpMinF = 10 * frequencyScaler; | |
163 filterAmpMaxF = 5000 * frequencyScaler; | |
164 filterAmpMul = 10.0; | |
165 | |
166 // adsr | |
167 minAttackTime = .0001; | |
168 deltaAttackTime = 2.; | |
169 minReleaseTime = 1; | |
170 deltaReleaseTime = 2.5; | |
171 | |
172 adsr.setAttackRate(minAttackTime * rate); | |
173 adsr.setDecayRate(.0001 * rate); | |
174 adsr.setSustainLevel(1); | |
175 adsr.setReleaseRate(minReleaseTime * rate); | |
176 | |
177 state = bank_stopped; | |
178 return true; | |
179 } | |
180 | |
181 void OscillatorBank::resetOscillators() { | |
182 currentHop = -1; | |
183 loopDir = 1; | |
184 loopDirShift = 0; | |
185 fill(nyquistCut, nyquistCut + numOfPartials, false); | |
186 prevAdsrVal = 0; | |
187 prevAmpTh = ampTh; | |
188 prevHopNumTh = hopNumTh; | |
189 prevPitchMultiplier = pitchMultiplier; | |
190 prevFreqMovement = freqMovement; | |
191 prevFilterNum = filterNum; | |
192 memcpy(prevFilterFreqs, filterFreqs, filterNum * sizeof(float)); | |
193 memcpy(prevFilterQ, filterQ, filterNum * sizeof(float)); | |
194 | |
195 int activePNum = partials->activePartialNum[0]; | |
196 unsigned int *activeP = partials->activePartials[0]; | |
197 for (int i = 0; i < activePNum; i++) { | |
198 freqFixedDeltas[activeP[i]] = partials->partialFreqDelta[activeP[i]][0] | |
199 / overSampling; | |
200 ampFixedDeltas[activeP[i]] = partials->partialAmpDelta[activeP[i]][0] | |
201 / overSampling; | |
202 } | |
203 // attack! | |
204 adsr.gate(1); | |
205 note = true; | |
206 | |
207 nextHop(); | |
208 | |
209 state = bank_playing; | |
210 } | |
211 | |
212 void OscillatorBank::nextHop() { | |
213 hopSize = oscBankHopSize; | |
214 | |
215 // copy phases, next freqs and next amps from previous frame | |
216 memcpy(phaseCopies, oscillatorPhases, actPartNum * sizeof(float)); | |
217 memcpy(nextNormFreqCopies, oscillatorNextNormFreq, | |
218 actPartNum * sizeof(float)); | |
219 memcpy(nextAmpCopies, oscillatorNextAmp, actPartNum * sizeof(float)); | |
220 | |
221 // next frame is forward or backwards, cos we could be in the loop | |
222 currentHop += loopDir; | |
223 | |
224 checkDirection(); | |
225 | |
226 // if((currentHop/overSampling)%100 == 0) | |
227 // dbox_printf("currentHop %d, direction: %d\n", currentHop/overSampling, loopDir); | |
228 | |
229 // if needs jump, end here this method, cos jumpToHop() will do tee rest | |
230 if (checkJump() == 0) | |
231 return; | |
232 // otherwise, if jump is not needed or fails, continue regular stuff | |
233 | |
234 if (nextEnvState() != 0) | |
235 return; // release has ended! | |
236 | |
237 checkSpeed(); | |
238 | |
239 // now let's decide how to calculate next hop | |
240 if (!checkOversampling()) | |
241 nextOscBankHop(); | |
242 else | |
243 nextPartialHop(); | |
244 } | |
245 | |
246 void OscillatorBank::nextOscBankHop() { | |
247 int parIndex, localHop; | |
248 float parDamp = 1; | |
249 int currentPartialHop = (currentHop / overSampling) + loopDirShift; | |
250 | |
251 // if going backwards in the loop, get previous frame active partials... | |
252 actPartNum = partials->activePartialNum[currentPartialHop - loopDirShift]; | |
253 actPart = partials->activePartials[currentPartialHop - loopDirShift]; | |
254 //cout << "actPartNum: " << actPartNum << endl; | |
255 | |
256 envState = adsr.getState(); // to determine what state we will be in next hop [attack, decay, sustain, release] | |
257 | |
258 int parCnt = 0; | |
259 int currentHopReminder = currentHop % overSampling; | |
260 // steps to reach next bank hop from previous partial hop | |
261 int steps = currentHopReminder + 1; | |
262 if (loopDir < 0) | |
263 steps = overSampling - currentHopReminder + 1; | |
264 | |
265 for (int i = 0; i < actPartNum; i++) { | |
266 // find partial and frame | |
267 parIndex = actPart[i]; | |
268 //localHop = partials->localPartialFrames[currentPartialHop][parIndex]; | |
269 localHop = currentPartialHop - partials->partialStartFrame[parIndex]; // in Parser and Partials "frames" are what we call here "hops". These particular ones are local frames [see comment at top of file] | |
270 | |
271 //float delta = partials->partialFrequencies[parIndex][localHop+loopDir] - partials->partialFrequencies[parIndex][localHop]; | |
272 | |
273 // if this partial was over nyquist on previous hop... | |
274 if (nyquistCut[parIndex]) { | |
275 // ...restart from safe values | |
276 oscillatorPhases[parCnt] = 0; | |
277 //TODO add freqmove dependency | |
278 oscillatorNextNormFreq[parCnt] = | |
279 (partials->partialFrequencies[parIndex][localHop] | |
280 + freqFixedDeltas[parIndex] * (steps - 1)) | |
281 * frequencyScaler * prevPitchMultiplier; | |
282 oscillatorNextAmp[parCnt] = 0; | |
283 } else if (loopDir == 1) // otherwise recover phase, target freq and target amp from previous frame | |
284 { | |
285 if ((localHop != 0) || (currentHopReminder != 0)) { | |
286 oscillatorPhases[parCnt] = | |
287 phaseCopies[indicesMapping[parIndex]]; | |
288 oscillatorNextNormFreq[parCnt] = | |
289 nextNormFreqCopies[indicesMapping[parIndex]]; | |
290 oscillatorNextAmp[parCnt] = | |
291 nextAmpCopies[indicesMapping[parIndex]]; | |
292 } else // first oscillator hop [both for bank and partial], so no previous data are available | |
293 { | |
294 oscillatorPhases[parCnt] = 0; | |
295 //TODO add freqmove dependency | |
296 oscillatorNextNormFreq[parCnt] = | |
297 partials->partialFrequencies[parIndex][localHop] | |
298 * frequencyScaler * prevPitchMultiplier; | |
299 parDamp = calculateParDamping(parIndex, prevHopNumTh, | |
300 prevAdsrVal, oscillatorNextNormFreq[parCnt], | |
301 prevFilterNum, prevFilterFreqs, prevFilterQ); | |
302 oscillatorNextAmp[parCnt] = | |
303 partials->partialAmplitudes[parIndex][localHop] | |
304 * parDamp; | |
305 if(oscillatorNextAmp[parCnt] > 1) | |
306 oscillatorNextAmp[parCnt] = 1; | |
307 freqFixedDeltas[parIndex] = | |
308 partials->partialFreqDelta[parIndex][localHop + loopDir] | |
309 * loopDir / overSampling; | |
310 ampFixedDeltas[parIndex] = | |
311 partials->partialAmpDelta[parIndex][localHop + loopDir] | |
312 * loopDir / overSampling; | |
313 } | |
314 } else { | |
315 oscillatorPhases[parCnt] = phaseCopies[indicesMapping[parIndex]]; | |
316 oscillatorNextNormFreq[parCnt] = | |
317 nextNormFreqCopies[indicesMapping[parIndex]]; | |
318 oscillatorNextAmp[parCnt] = nextAmpCopies[indicesMapping[parIndex]]; | |
319 } | |
320 | |
321 // remove aliasing, skipping partial over nyquist freq | |
322 if (oscillatorNextNormFreq[parCnt] > nyqNorm) { | |
323 nyquistCut[parIndex] = true; | |
324 continue; | |
325 } | |
326 nyquistCut[parIndex] = false; | |
327 | |
328 // first set up freq, cos filter affects amplitude damping according to freq content | |
329 oscillatorNormFrequencies[parCnt] = oscillatorNextNormFreq[parCnt]; // to fix any possible drifts | |
330 // save next values, current for next round | |
331 oscillatorNextNormFreq[parCnt] = (freqMovement | |
332 * (partials->partialFrequencies[parIndex][localHop] | |
333 + freqFixedDeltas[parIndex] * steps) * frequencyScaler | |
334 + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex]) | |
335 * pitchMultiplier; | |
336 // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials | |
337 oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt] | |
338 - oscillatorNormFrequencies[parCnt]) / hopCounter; | |
339 // this second weird passage handles dissonance control, morphing between regular and mean frequencies | |
340 oscillatorNormFreqDerivatives[parCnt] = freqMovement | |
341 * oscillatorNormFreqDerivatives[parCnt] | |
342 + (1 - freqMovement) | |
343 * ((oscStatNormFrequenciesMean[parIndex] | |
344 * pitchMultiplier) | |
345 - oscillatorNormFrequencies[parCnt]) | |
346 / hopCounter; | |
347 | |
348 parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal, | |
349 oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ); | |
350 | |
351 // now amplitudes | |
352 oscillatorAmplitudes[parCnt] = oscillatorNextAmp[parCnt]; // to fix any possible drifts | |
353 // save next values, current for next round | |
354 //delta = partials->partialAmplitudes[parIndex][localHop+loopDir] - partials->partialAmplitudes[parIndex][localHop]; | |
355 oscillatorNextAmp[parCnt] = | |
356 (partials->partialAmplitudes[parIndex][localHop] | |
357 + ampFixedDeltas[parIndex] * steps) * parDamp; | |
358 if(oscillatorNextAmp[parCnt] > 1) | |
359 oscillatorNextAmp[parCnt] = 1; | |
360 if ((loopDir == -1) && (localHop = 1) && (currentHopReminder == 1)) | |
361 oscillatorNextAmp[parCnt] = 0; | |
362 // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials | |
363 oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt] | |
364 - oscillatorAmplitudes[parCnt]) / hopCounter; | |
365 | |
366 // finally update current mapping between oscillators and partials | |
367 indicesMapping[parIndex] = parCnt; | |
368 parCnt++; | |
369 } | |
370 actPartNum = parCnt; | |
371 // [NEON] if not multiple of 4... | |
372 if (actPartNum % 4 != 0) | |
373 addFakeOsc(); | |
374 } | |
375 | |
376 void OscillatorBank::nextPartialHop() { | |
377 unsigned int parIndex, localHop; | |
378 float parDamp = 1; | |
379 int currentPartialHop = currentHop / overSampling; | |
380 | |
381 // if going backwards in the loop, get previous frame active partials... | |
382 actPartNum = partials->activePartialNum[currentPartialHop - loopDirShift]; | |
383 actPart = partials->activePartials[currentPartialHop - loopDirShift]; | |
384 | |
385 envState = adsr.getState(); // to determine what state we will be in next hop [attack, decay, sustain, release] | |
386 | |
387 int parCnt = 0; | |
388 int steps = overSampling - 1; // steps to reach next hop [partial or bank] from previous partial hop | |
389 | |
390 for (int i = 0; i < actPartNum; i++) { | |
391 // find partial and frame | |
392 parIndex = actPart[i]; | |
393 //localHop = partials->localPartialFrames[currentPartialHop][parIndex]; | |
394 localHop = currentPartialHop - partials->partialStartFrame[parIndex]; // in Parser and Partials "frames" are what we call here "hops". These particular ones are local frames [see comment at top of file] | |
395 | |
396 // if this partial was over nyquist on previous hop... | |
397 if (nyquistCut[parIndex]) { | |
398 // ...restart from safe values | |
399 oscillatorPhases[parCnt] = 0; | |
400 //TODO add freqmove dependency | |
401 oscillatorNextNormFreq[parCnt] = | |
402 (partials->partialFrequencies[parIndex][localHop] | |
403 + freqFixedDeltas[parIndex] * steps | |
404 * (1 - loopDirShift)) * frequencyScaler | |
405 * prevPitchMultiplier; | |
406 oscillatorNextAmp[parCnt] = 0; | |
407 } else if (loopDir == 1) // otherwise recover phase, target freq and target amp from previous frame | |
408 { | |
409 if ((localHop != 0) || (overSampling > 1)) { | |
410 oscillatorPhases[parCnt] = | |
411 phaseCopies[indicesMapping[parIndex]]; | |
412 oscillatorNextNormFreq[parCnt] = | |
413 nextNormFreqCopies[indicesMapping[parIndex]]; | |
414 oscillatorNextAmp[parCnt] = | |
415 nextAmpCopies[indicesMapping[parIndex]]; | |
416 } else // first oscillator hop [both for bank and partial], so no previous data are available | |
417 { | |
418 oscillatorPhases[parCnt] = 0; | |
419 //TODO add freqmove dependency | |
420 oscillatorNextNormFreq[parCnt] = | |
421 partials->partialFrequencies[parIndex][localHop] | |
422 * frequencyScaler * prevPitchMultiplier; | |
423 parDamp = calculateParDamping(parIndex, prevHopNumTh, | |
424 prevAdsrVal, oscillatorNextNormFreq[parCnt], | |
425 prevFilterNum, prevFilterFreqs, prevFilterQ); | |
426 oscillatorNextAmp[parCnt] = | |
427 partials->partialAmplitudes[parIndex][localHop] | |
428 * parDamp; | |
429 if(oscillatorNextAmp[parCnt] > 1) | |
430 oscillatorNextAmp[parCnt] = 1; | |
431 freqFixedDeltas[parIndex] = | |
432 partials->partialFreqDelta[parIndex][localHop + loopDir] | |
433 * loopDir / overSampling; | |
434 ampFixedDeltas[parIndex] = | |
435 partials->partialAmpDelta[parIndex][localHop + loopDir] | |
436 * loopDir / overSampling; | |
437 } | |
438 } else { | |
439 if (localHop != partials->partialNumFrames[parIndex] - 1) { | |
440 oscillatorPhases[parCnt] = | |
441 phaseCopies[indicesMapping[parIndex]]; | |
442 oscillatorNextNormFreq[parCnt] = | |
443 nextNormFreqCopies[indicesMapping[parIndex]]; | |
444 oscillatorNextAmp[parCnt] = | |
445 nextAmpCopies[indicesMapping[parIndex]]; | |
446 } else // first oscillator hop [going backwards - both for bank and partial] , so no previous data are available | |
447 { | |
448 oscillatorPhases[parCnt] = 0; | |
449 //TODO add freqmove dependency | |
450 oscillatorNextNormFreq[parCnt] = | |
451 partials->partialFrequencies[parIndex][localHop] | |
452 * frequencyScaler * prevPitchMultiplier; | |
453 parDamp = calculateParDamping(parIndex, prevHopNumTh, | |
454 prevAdsrVal, oscillatorNextNormFreq[parCnt], | |
455 prevFilterNum, prevFilterFreqs, prevFilterQ); | |
456 oscillatorNextAmp[parCnt] = | |
457 partials->partialAmplitudes[parIndex][localHop] | |
458 * parDamp; | |
459 if(oscillatorNextAmp[parCnt] > 1) | |
460 oscillatorNextAmp[parCnt] = 1; | |
461 freqFixedDeltas[parIndex] = | |
462 partials->partialFreqDelta[parIndex][localHop + loopDir] | |
463 * loopDir / overSampling; | |
464 ampFixedDeltas[parIndex] = | |
465 partials->partialAmpDelta[parIndex][localHop + loopDir] | |
466 * loopDir / overSampling; | |
467 } | |
468 } | |
469 // remove aliasing, skipping partial over nyquist freq | |
470 if (oscillatorNextNormFreq[parCnt] > nyqNorm) { | |
471 //cout << nyqNorm << endl; | |
472 nyquistCut[parIndex] = true; | |
473 continue; | |
474 } | |
475 nyquistCut[parIndex] = false; | |
476 | |
477 // first set up freq, cos filter affects amplitude damping according to freq content | |
478 oscillatorNormFrequencies[parCnt] = oscillatorNextNormFreq[parCnt]; // to fix any possible drifts | |
479 // save next values, current for next round | |
480 oscillatorNextNormFreq[parCnt] = (freqMovement | |
481 * (partials->partialFrequencies[parIndex][localHop + loopDir] | |
482 - freqFixedDeltas[parIndex] * steps * loopDirShift) | |
483 * frequencyScaler | |
484 + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex]) | |
485 * pitchMultiplier; | |
486 // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials | |
487 oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt] | |
488 - oscillatorNormFrequencies[parCnt]) / hopCounter; | |
489 // this second weird passage handles dissonance control, morphing between regular and mean frequencies | |
490 oscillatorNormFreqDerivatives[parCnt] = freqMovement | |
491 * oscillatorNormFreqDerivatives[parCnt] | |
492 + (1 - freqMovement) | |
493 * ((oscStatNormFrequenciesMean[parIndex] | |
494 * pitchMultiplier) | |
495 - oscillatorNormFrequencies[parCnt]) | |
496 / hopCounter; | |
497 | |
498 parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal, | |
499 oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ); | |
500 | |
501 // now amplitudes | |
502 oscillatorAmplitudes[parCnt] = oscillatorNextAmp[parCnt]; // to fix any possible drifts | |
503 // save next values, current for next round | |
504 //delta = partials->partialAmplitudes[parIndex][localHop+loopDir] - partials->partialAmplitudes[parIndex][localHop]; | |
505 oscillatorNextAmp[parCnt] = | |
506 (partials->partialAmplitudes[parIndex][localHop + loopDir] | |
507 - (ampFixedDeltas[parIndex]) * steps * loopDirShift) | |
508 * parDamp; | |
509 if(oscillatorNextAmp[parCnt] > 1) | |
510 oscillatorNextAmp[parCnt] = 1; | |
511 | |
512 // to avoid bursts when transients are played backwards | |
513 if ((loopDir == -1) && (localHop - 1 == 0) && (overSampling == 1)) { | |
514 oscillatorNextAmp[parCnt] = 0; | |
515 } | |
516 // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials | |
517 oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt] | |
518 - oscillatorAmplitudes[parCnt]) / hopCounter; | |
519 | |
520 // if next is not going to loop boundaries, get next deltas [same direction] | |
521 if ((((currentPartialHop + loopDir) * overSampling != loopEndHop) | |
522 || (loopDir == -1)) | |
523 && (((currentPartialHop + loopDir) * overSampling + loopDir | |
524 != loopStartHop) || (loopDir == 1))) { | |
525 freqFixedDeltas[parIndex] = | |
526 partials->partialFreqDelta[parIndex][localHop + loopDir] | |
527 * loopDir / overSampling; | |
528 ampFixedDeltas[parIndex] = | |
529 partials->partialAmpDelta[parIndex][localHop + loopDir] | |
530 * loopDir / overSampling; | |
531 } else // .. otherwise, keep deltas but change sign [co swe change direction] | |
532 { | |
533 freqFixedDeltas[parIndex] = -freqFixedDeltas[parIndex]; | |
534 ampFixedDeltas[parIndex] = -ampFixedDeltas[parIndex]; | |
535 } | |
536 | |
537 // finally update current mapping between oscillators and partials | |
538 indicesMapping[parIndex] = parCnt; | |
539 parCnt++; | |
540 } | |
541 actPartNum = parCnt; | |
542 // [NEON] if not multiple of 4... | |
543 if (actPartNum % 4 != 0) | |
544 addFakeOsc(); | |
545 | |
546 updatePrevControls(); | |
547 } | |
548 | |
549 void OscillatorBank::addFakeOsc() { | |
550 // ...calculate difference | |
551 int newPartNum = (actPartNum + 3) & ~0x3; | |
552 // ...add fake oscillators until total num is multiple of 4 | |
553 for (int i = actPartNum; i < newPartNum; i++) { | |
554 oscillatorAmplitudes[i] = 0; | |
555 oscillatorNormFrequencies[i] = 0; | |
556 oscillatorAmplitudeDerivatives[i] = 0; | |
557 oscillatorNormFreqDerivatives[i] = 0; | |
558 oscillatorPhases[i] = 0; | |
559 } | |
560 // ...and update num of active partials | |
561 actPartNum = newPartNum; | |
562 } | |
563 | |
564 void OscillatorBank::play(float vel) { | |
565 // set attack and release params according to velocity | |
566 //adsr.setAttackRate((minAttackTime + ((1 - vel) * deltaAttackTime)) * rate); | |
567 adsr.setAttackRate(minAttackTime * rate); | |
568 //adsr.setReleaseRate((minReleaseTime + (1 - vel) * deltaReleaseTime) * rate); | |
569 adsr.setReleaseRate(minReleaseTime * rate); | |
570 | |
571 // set timbre | |
572 hopNumTh = log((1 - vel) + 1) / log(2) * 20000; | |
573 | |
574 state = bank_toreset; | |
575 } | |
576 | |
577 //--------------------------------------------------------------------------------------------------------------------------- | |
578 // private methods | |
579 //--------------------------------------------------------------------------------------------------------------------------- | |
580 | |
581 bool OscillatorBank::loader(char *filename, int hopsize, int samplerate) { | |
582 rate = samplerate; | |
583 loaded = parser.parseFile(filename, hopsize, samplerate); | |
584 return loaded; | |
585 } | |
586 | |
587 int OscillatorBank::jumpToHop() { | |
588 int jumpGap = abs(jumpHop - currentHop / overSampling); // gaps in partial reference | |
589 | |
590 // can't jump to self dude | |
591 if (jumpGap == 0) | |
592 return 1; | |
593 | |
594 // direction is in general maintained with jump | |
595 if (jumpHop == 0) | |
596 setDirection(1); | |
597 else if (jumpHop == lastHop) | |
598 setDirection(-1); | |
599 | |
600 dbox_printf("\tJump from %d to %d\n", currentHop / overSampling, jumpHop); | |
601 dbox_printf("\tdirection %d\n", loopDir); | |
602 | |
603 currentHop = jumpHop * overSampling; | |
604 | |
605 if (nextEnvState() != 0) | |
606 return 0; // release has ended! | |
607 | |
608 checkSpeed(); | |
609 | |
610 int parIndex, localHop, targetHop; | |
611 float parDamp = 1; | |
612 int currentPartialHop = currentHop / overSampling; | |
613 int targetPartialHop = jumpHop; | |
614 | |
615 actPartNum = partials->activePartialNum[currentPartialHop]; | |
616 actPart = partials->activePartials[currentPartialHop]; | |
617 int targetActParNum = partials->activePartialNum[targetPartialHop]; | |
618 unsigned int *targetActPar = partials->activePartials[targetPartialHop]; | |
619 | |
620 envState = adsr.getState(); // to determine what state we will be in next hop [attack, decay, sustain, release] | |
621 | |
622 int parCnt = 0; | |
623 int currentHopReminder = currentHop % overSampling; | |
624 | |
625 // steps to walk where i am [bank of partial hop] from previous partial hop | |
626 int steps = currentHopReminder * (overSampling != 1); // no oversampling 0, oversampling and going ff currentHopReminder | |
627 | |
628 for (int i = 0; i < actPartNum; i++) { | |
629 // find partial and frame | |
630 parIndex = actPart[i]; | |
631 //localHop = partials->localPartialFrames[currentPartialHop][parIndex]; | |
632 localHop = currentPartialHop - partials->partialStartFrame[parIndex]; // in Parser and Partials "frames" are what we call here "hops". These particular ones are local frames [see comment at top of file] | |
633 | |
634 // if this partial was over nyquist on previous hop... | |
635 if (nyquistCut[parIndex]) { | |
636 // ...restart from safe values | |
637 oscillatorPhases[parCnt] = 0; | |
638 //TODO add freqmove dependency | |
639 oscillatorNextNormFreq[parCnt] = | |
640 (partials->partialFrequencies[parIndex][localHop] | |
641 + freqFixedDeltas[parIndex] * steps * loopDir) | |
642 * frequencyScaler * prevPitchMultiplier; | |
643 oscillatorNextAmp[parCnt] = 0; | |
644 } else if (loopDir == 1) {// otherwise recover phase, target freq and target amp from previous frame | |
645 if ((localHop != 0) | |
646 || ((overSampling > 1) && (currentHopReminder != 0))) { | |
647 oscillatorPhases[parCnt] = | |
648 phaseCopies[indicesMapping[parIndex]]; | |
649 oscillatorNextNormFreq[parCnt] = | |
650 nextNormFreqCopies[indicesMapping[parIndex]]; | |
651 oscillatorNextAmp[parCnt] = | |
652 nextAmpCopies[indicesMapping[parIndex]]; | |
653 } else { // first oscillator hop [both for bank and partial], so no previous data are available | |
654 oscillatorPhases[parCnt] = 0; | |
655 //TODO add freqmove dependency | |
656 oscillatorNextNormFreq[parCnt] = | |
657 partials->partialFrequencies[parIndex][localHop] | |
658 * frequencyScaler * prevPitchMultiplier; | |
659 parDamp = calculateParDamping(parIndex, prevHopNumTh, | |
660 prevAdsrVal, oscillatorNextNormFreq[parCnt], | |
661 prevFilterNum, prevFilterFreqs, prevFilterQ); | |
662 oscillatorNextAmp[parCnt] = | |
663 partials->partialAmplitudes[parIndex][localHop] | |
664 * parDamp; | |
665 if(oscillatorNextAmp[parCnt] > 1) | |
666 oscillatorNextAmp[parCnt] = 1; | |
667 } | |
668 } else { | |
669 if (( (unsigned)localHop != partials->partialNumFrames[parIndex] - 1) | |
670 || ((overSampling > 1) && (currentHopReminder != 0))) { | |
671 oscillatorPhases[parCnt] = | |
672 phaseCopies[indicesMapping[parIndex]]; | |
673 oscillatorNextNormFreq[parCnt] = | |
674 nextNormFreqCopies[indicesMapping[parIndex]]; | |
675 oscillatorNextAmp[parCnt] = | |
676 nextAmpCopies[indicesMapping[parIndex]]; | |
677 } else // first oscillator hop [going backwards - both for bank and partial] , so no previous data are available, so retrieve where i am | |
678 { | |
679 oscillatorPhases[parCnt] = 0; | |
680 //TODO add freqmove dependency | |
681 oscillatorNextNormFreq[parCnt] = | |
682 partials->partialFrequencies[parIndex][localHop] | |
683 * frequencyScaler * prevPitchMultiplier; | |
684 parDamp = calculateParDamping(parIndex, prevHopNumTh, | |
685 prevAdsrVal, oscillatorNextNormFreq[parCnt], | |
686 prevFilterNum, prevFilterFreqs, prevFilterQ); | |
687 oscillatorNextAmp[parCnt] = | |
688 partials->partialAmplitudes[parIndex][localHop] | |
689 * parDamp; | |
690 if(oscillatorNextAmp[parCnt] > 1) | |
691 oscillatorNextAmp[parCnt] = 1; | |
692 } | |
693 } | |
694 // remove aliasing, skipping partial over nyquist freq | |
695 if (oscillatorNextNormFreq[parCnt] > nyqNorm) { | |
696 //cout << nyqNorm << endl; | |
697 nyquistCut[parIndex] = true; | |
698 continue; | |
699 } | |
700 nyquistCut[parIndex] = false; | |
701 | |
702 // check what happens of this partial at target hop | |
703 float targetFreqVal, targetAmpVal; | |
704 //targetHop = partials->localPartialFrames[targetPartialHop][parIndex]; | |
705 targetHop = targetPartialHop - partials->partialStartFrame[parIndex]; | |
706 | |
707 if (targetHop == -1) | |
708 targetFreqVal = targetAmpVal = 0; | |
709 else { | |
710 targetFreqVal = partials->partialFrequencies[parIndex][targetHop] | |
711 * frequencyScaler; // pitch shift will be multiplied later!!! | |
712 targetAmpVal = partials->partialFrequencies[parIndex][targetHop]; // parDamp will be multiplied later!!! | |
713 } | |
714 | |
715 // first set up freq, cos filter affects amplitude damping according to freq content | |
716 oscillatorNormFrequencies[parCnt] = oscillatorNextNormFreq[parCnt]; // to fix any possible drifts | |
717 // save next values, current for next round | |
718 oscillatorNextNormFreq[parCnt] = (freqMovement * targetFreqVal | |
719 + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex]) | |
720 * pitchMultiplier; | |
721 // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials | |
722 oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt] | |
723 - oscillatorNormFrequencies[parCnt]) / hopCounter; | |
724 // this second weird passage handles dissonance control, morphing between regular and mean frequencies | |
725 oscillatorNormFreqDerivatives[parCnt] = freqMovement | |
726 * oscillatorNormFreqDerivatives[parCnt] | |
727 + (1 - freqMovement) | |
728 * ((oscStatNormFrequenciesMean[parIndex] | |
729 * pitchMultiplier) | |
730 - oscillatorNormFrequencies[parCnt]) | |
731 / hopCounter; | |
732 | |
733 parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal, | |
734 oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ); | |
735 | |
736 // now amplitudes | |
737 oscillatorAmplitudes[parCnt] = oscillatorNextAmp[parCnt]; // to fix any possible drifts | |
738 // save next values, current for next round | |
739 oscillatorNextAmp[parCnt] = targetAmpVal * parDamp; | |
740 if(oscillatorNextAmp[parCnt] > 1) | |
741 oscillatorNextAmp[parCnt] = 1; | |
742 // to avoid bursts when transients are played backwards | |
743 if ((loopDir == -1) && (targetHop == 0) | |
744 && ((overSampling == 1) || (currentHopReminder == 0))) { | |
745 oscillatorNextAmp[parCnt] = 0; | |
746 } | |
747 // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials | |
748 oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt] | |
749 - oscillatorAmplitudes[parCnt]) / hopCounter; | |
750 | |
751 //if partial does not die at target, calculate deltas according to direction | |
752 if (targetHop != -1) { | |
753 freqFixedDeltas[parIndex] = | |
754 partials->partialFreqDelta[parIndex][targetHop] * loopDir | |
755 / overSampling; | |
756 ampFixedDeltas[parIndex] = | |
757 partials->partialAmpDelta[parIndex][targetHop] * loopDir | |
758 / overSampling; | |
759 } | |
760 | |
761 // finally update current mapping between oscillators and partials | |
762 indicesMapping[parIndex] = parCnt; | |
763 parCnt++; | |
764 } | |
765 actPartNum = parCnt; | |
766 | |
767 // now add the ones that start at target hop! | |
768 for (int i = 0; i < targetActParNum; i++) { | |
769 // find partial and frame | |
770 parIndex = targetActPar[i]; | |
771 //targetHop = partials->localPartialFrames[targetPartialHop][parIndex]; | |
772 targetHop = targetPartialHop - partials->partialStartFrame[parIndex]; // in Parser and Partials "frames" are what we call here "hops". These particular ones are local frames [see comment at top of file] | |
773 | |
774 // check if this partials was already active before the jump | |
775 //localHop = partials->localPartialFrames[currentPartialHop][parIndex]; | |
776 localHop = currentPartialHop - partials->partialStartFrame[parIndex]; | |
777 | |
778 // if yes, skip it | |
779 if (localHop != -1) | |
780 continue; | |
781 | |
782 // otherwise add it to active bunch and calcucalte values | |
783 | |
784 // first set up freq, cos filter affects amplitude damping according to freq content | |
785 oscillatorNormFrequencies[parCnt] = 0; | |
786 // save next values, current for next round | |
787 oscillatorNextNormFreq[parCnt] = (freqMovement | |
788 * partials->partialFrequencies[parIndex][targetHop] | |
789 * frequencyScaler | |
790 + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex]) | |
791 * pitchMultiplier; | |
792 // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials | |
793 oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt] | |
794 - oscillatorNormFrequencies[parCnt]) / hopCounter; | |
795 // this second weird passage handles dissonance control, morphing between regular and mean frequencies | |
796 oscillatorNormFreqDerivatives[parCnt] = freqMovement | |
797 * oscillatorNormFreqDerivatives[parCnt] | |
798 + (1 - freqMovement) | |
799 * ((oscStatNormFrequenciesMean[parIndex] | |
800 * pitchMultiplier) | |
801 - oscillatorNormFrequencies[parCnt]) | |
802 / hopCounter; | |
803 | |
804 parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal, | |
805 oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ); | |
806 | |
807 // now amplitudes | |
808 oscillatorAmplitudes[parCnt] = 0; | |
809 // save next values, current for next round | |
810 oscillatorNextAmp[parCnt] = | |
811 partials->partialFrequencies[parIndex][targetHop] * parDamp; | |
812 if(oscillatorNextAmp[parCnt] > 1) | |
813 oscillatorNextAmp[parCnt] = 1; | |
814 // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials | |
815 oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt] | |
816 - oscillatorAmplitudes[parCnt]) / hopCounter; | |
817 | |
818 //calculate deltas according to direction | |
819 freqFixedDeltas[parIndex] = | |
820 partials->partialFreqDelta[parIndex][targetHop] * loopDir | |
821 / overSampling; | |
822 ampFixedDeltas[parIndex] = | |
823 partials->partialAmpDelta[parIndex][targetHop] * loopDir | |
824 / overSampling; | |
825 | |
826 // finally update current mapping between oscillators and partials | |
827 indicesMapping[parIndex] = parCnt; | |
828 parCnt++; | |
829 | |
830 } | |
831 // [NEON] if not multiple of 4... | |
832 if (actPartNum % 4 != 0) | |
833 addFakeOsc(); | |
834 | |
835 updatePrevControls(); | |
836 | |
837 jumpHop = -1; | |
838 | |
839 return 0; | |
840 } | |
841 | |
842 int OscillatorBank::nextEnvState() { | |
843 /* | |
844 envState = Attack.getState(); // to determine what state we are in [attack, decay, sustain, release] | |
845 | |
846 // osc bank is playing the tail and the tail ends... | |
847 if( (state == bank_playing)&&(envState == env_idle) ) | |
848 { | |
849 state = bank_stopped; // ...stop bank | |
850 return 1; // and return immediately | |
851 } | |
852 else if( (envState == env_attack) || (envState == env_decay) ) | |
853 { | |
854 // run envelopes until next frame | |
855 dampWeight = Attack.process(hopSize); | |
856 } | |
857 else if(envState == env_release) | |
858 { | |
859 // run envelopes until next frame | |
860 dampWeight = Attack.process(hopSize); | |
861 releaseDamp = Release.process(hopSize); | |
862 }*/ | |
863 | |
864 envState = adsr.getState(); | |
865 // osc bank is playing the tail and the tail ends... | |
866 if ((state == bank_playing) && (envState == env_idle)) { | |
867 state = bank_stopped; // ...stop bank | |
868 adsrVal = 0; | |
869 return 1; // and return immediately | |
870 } else | |
871 adsrVal = adsr.process(hopSize); | |
872 | |
873 return 0; | |
874 } | |
875 | |
876 void OscillatorBank::checkDirection() { | |
877 // end of the loop or end of file | |
878 if (((currentHop >= loopEndHop) && (loopDir == 1)) | |
879 || ((currentHop >= lastHop) && (loopDir == 1))) { | |
880 // move backwards | |
881 setDirection(-1); | |
882 //dbox_printf("backward from %d\n", loopEndHop); | |
883 } else if (((currentHop <= loopStartHop) && (loopDir == -1)) | |
884 || ((currentHop <= 0) && (loopDir == -1))) // start of the loop or start of file | |
885 { | |
886 // move forward | |
887 setDirection(1); | |
888 //dbox_printf("forward from %d\n", loopStartHop); | |
889 } | |
890 } | |
891 | |
892 void OscillatorBank::checkSpeed() { | |
893 // speed control [alike on highways, LOL] | |
894 if (nextSpeed > 0) { | |
895 nextSpeed = (nextSpeed < maxSpeed) ? nextSpeed : maxSpeed; | |
896 nextSpeed = (nextSpeed > minSpeed) ? nextSpeed : minSpeed; | |
897 speed = nextSpeed; | |
898 nextSpeed = -1; | |
899 } | |
900 hopCounter = hopSize / speed; | |
901 } | |
902 | |
903 int OscillatorBank::checkJump() { | |
904 //check if has to jump somewhere | |
905 if (jumpHop > -1) { | |
906 // needs to jump! | |
907 if (jumpToHop() == 0) | |
908 return 0; | |
909 } | |
910 return 1; // no jump | |
911 } | |
912 | |
913 bool OscillatorBank::checkOversampling() { | |
914 //TODO fix this, but need andrew to fix oversampling multiple of period size | |
915 // if partialsHopSize is not a multiple of oversampling, change hop size to periodically match next partial hop | |
916 if (hopSizeReminder > 0) { | |
917 // if next osc bank hop overtakes next partial hop... | |
918 if ((currentHop + loopDir) * hopSize > partialsHopSize) { | |
919 hopSize = hopSizeReminder; // ...shrink osc bank hop size to match partial hop | |
920 return true; // and set next hop as matching with next partial hop | |
921 } | |
922 } else if (((currentHop + (1 - loopDirShift)) % overSampling) == 0) // if next osc bank hop matches next partial hop | |
923 return true; // ...mark next hop as partial hop | |
924 | |
925 return false; // ,otherwise mark next hop as osc bank hop | |
926 } | |
927 | |
928 void OscillatorBank::updatePrevControls() { | |
929 prevAdsrVal = adsrVal; | |
930 prevAmpTh = ampTh; | |
931 prevHopNumTh = hopNumTh; | |
932 prevPitchMultiplier = pitchMultiplier; | |
933 prevFreqMovement = freqMovement; | |
934 prevFilterNum = filterNum; | |
935 memcpy(prevFilterFreqs, filterFreqs, filterNum * sizeof(float)); | |
936 memcpy(prevFilterQ, filterQ, filterNum * sizeof(float)); | |
937 } | |
938 | |
939 float OscillatorBank::calculateParDamping(int parIndex, int hopNTh, | |
940 float adsrVl, float nextFreq, int filNum, float *filFreq, float *filQ) { | |
941 float parDamp = 1; | |
942 | |
943 // timbre | |
944 parDamp = ((float) (oscStatNumHops[parIndex] + 1)) / (hopNTh + 1); | |
945 parDamp = (parDamp > 1) ? 1 : parDamp; | |
946 parDamp = adsrVl * parDamp; | |
947 | |
948 //filters | |
949 | |
950 float filterWeights[MAX_TOUCHES]; | |
951 float filterDamp[MAX_TOUCHES]; | |
952 float filDist; | |
953 float filterWeightsAcc; | |
954 float filDmp; | |
955 float filAmp; | |
956 | |
957 // band reject notch filter | |
958 // float dist, dmp; | |
959 // for(int k=0; k<filterNum; k++) | |
960 // { | |
961 // dist = fabs(oscillatorNextNormFreq[parCnt]-filterFreqs[k]); | |
962 // if(dist<=filterQ[k]) | |
963 // { | |
964 // dmp = dist/filterQ[k]; | |
965 // parDamp *= dmp*dmp*dmp; | |
966 // } | |
967 // } | |
968 | |
969 | |
970 // each filter is a band pass notch filter | |
971 | |
972 // if at least one is active | |
973 if (filNum > 0) { | |
974 // reset values | |
975 filDist = 0; | |
976 filterWeightsAcc = 0; | |
977 filDmp = 0; | |
978 filAmp = 0; | |
979 // for each filter | |
980 for (int k = 0; k < filNum; k++) { | |
981 // here are a couple of kludges to boost sound output of hi freq filters | |
982 | |
983 // damping effect of filter increases with distance, but decreases with filter frequency [kludge] | |
984 float mul = ((filterMaxF-nextFreq)/filterMaxF) * 0.9 + 0.1 ; | |
985 //filDist = fabs(nextFreq - filFreq[k])*( ((exp(a*4)-1)/EXP_DENOM) * 0.9 + 0.1 ); | |
986 filDist = fabs(nextFreq - filFreq[k])*mul; | |
987 | |
988 // these to merge all filters contributions according to distance | |
989 filterWeights[k] = filterMaxF - filDist; | |
990 filterWeightsAcc += filterWeights[k]; | |
991 // freqs very close to filter center are slightly amplified | |
992 // the size of this amp area and the effect of amplification increase with frequency [kludge] | |
993 if (filDist | |
994 < filterAmpMinF | |
995 + (filterAmpMaxF*(1-mul) - filterAmpMinF) * (1 - filQ[k]) ) | |
996 filAmp = filQ[k] * filterAmpMul*(1-mul); | |
997 else | |
998 filAmp = 0; | |
999 // actual damping | |
1000 filDmp = 1 / (filDist * filQ[k]); | |
1001 filDmp = (filDmp > 1) ? 1 : filDmp; | |
1002 // sum damp+amplification | |
1003 filterDamp[k] = filDmp + filAmp; | |
1004 } | |
1005 // do weighted mean to merge all filters contributions | |
1006 filDmp = 0; | |
1007 for (int k = 0; k < filNum; k++) | |
1008 filDmp += filterDamp[k] * filterWeights[k]; | |
1009 filDmp /= filterWeightsAcc; | |
1010 // apply | |
1011 parDamp *= filDmp; | |
1012 } | |
1013 | |
1014 | |
1015 return parDamp; | |
1016 } |