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