robert@464: /* robert@464: * OscillatorBank.cpp robert@464: * robert@464: * Created on: May 23, 2014 robert@464: * Author: Victor Zappi and Andrew McPherson robert@464: */ robert@464: robert@464: robert@464: /* robert@464: * There is a problem with name consistency between this class and the Parser class in spear_parser files. robert@464: * 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] robert@464: * Here, "hop" is used with the meaning of "frame", while "frame" became the local frame of a partial robert@464: * robert@464: * example robert@464: * robert@464: * frames: 0 1 2 robert@464: * p0 p0_0 p0_1 robert@464: * p1 p1_0 p1_1 p1_2 robert@464: * p2 p2_0 p2_1 robert@464: * robert@464: * In this case: robert@464: * in Parser there are 2 hops, 3 total frames and the 3 partials have respectively 2, 3 and 2 local frames robert@464: * robert@464: * 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 robert@464: * robert@464: * This must be fixed robert@464: */ robert@464: robert@464: // TODO: fix hop-frame name consistency robert@464: robert@464: robert@464: #include robert@464: robert@464: #include "OscillatorBank.h" robert@464: robert@464: OscillatorBank::OscillatorBank() { robert@464: loaded = false; robert@464: } robert@464: robert@464: OscillatorBank::OscillatorBank(string filename, int hopsize, int samplerate) { robert@464: loaded = false; robert@464: loadFile(filename.c_str(), hopsize, samplerate); robert@464: } robert@464: robert@464: OscillatorBank::OscillatorBank(char *filename, int hopsize, int samplerate) { robert@464: loaded = false; robert@464: loadFile(filename, hopsize, samplerate); robert@464: } robert@464: robert@464: OscillatorBank::~OscillatorBank() { robert@464: free(oscillatorPhases); robert@464: free(oscillatorNextNormFreq); robert@464: free(oscillatorNextAmp); robert@464: free(oscillatorNormFrequencies); robert@464: free(oscillatorAmplitudes); robert@464: free(oscillatorNormFreqDerivatives); robert@464: free(oscillatorAmplitudeDerivatives); robert@464: free(phaseCopies); robert@464: free(nextNormFreqCopies); robert@464: free(nextAmpCopies); robert@464: robert@464: delete[] oscStatNormFrequenciesMean; robert@464: delete[] oscStatNumHops; robert@464: delete[] lookupTable; robert@464: delete[] indicesMapping; robert@464: delete[] freqFixedDeltas; robert@464: delete[] ampFixedDeltas; robert@464: delete[] nyquistCut; robert@464: } robert@464: robert@464: bool OscillatorBank::initBank(int oversamp) { robert@464: if (!loaded) robert@464: return false; robert@464: robert@464: //---prepare look-up table robert@464: lookupTableSize = 1024; robert@464: lookupTable = new float[lookupTableSize + 1]; robert@464: for (int n = 0; n < (lookupTableSize + 1); n++) robert@464: lookupTable[n] = sin(2.0 * M_PI * (float) n / (float) lookupTableSize); robert@464: frequencyScaler = (float) lookupTableSize / rate; robert@464: nyqNorm = rate / 2 * frequencyScaler; robert@464: robert@464: if (oversamp < 1) robert@464: oversamp = 1; robert@464: robert@464: //---prepare oscillators robert@464: partials = &(parser.partials); // pointer to paser's partials robert@464: partialsHopSize = parser.getHopSize(); robert@464: lastHop = partials->getHopNum(); // last bank hop is equal to last partial frame, which is equal to partial hop num robert@464: overSampling = oversamp; robert@464: hopSize = partialsHopSize / overSampling; // if oversampling, osc bank hop num > partials hop num robert@464: hopSizeReminder = partialsHopSize % overSampling; robert@464: oscBankHopSize = hopSize; robert@464: numOfPartials = partials->getPartialNum(); robert@464: numOfOscillators = partials->getMaxActivePartialNum(); // get the maximum number of active partials at the same time robert@464: robert@464: // set to next multiple of 4 [NEON] robert@464: numOfOscillators = (numOfOscillators + 3) & ~0x3; // to be sure we can add up to 3 fake oscillators robert@464: robert@464: int err; robert@464: //---allocate buffers robert@464: // alligned buffers [NEON] robert@464: err = posix_memalign((void**) &oscillatorPhases, 16, robert@464: numOfOscillators * sizeof(float)); robert@464: err += posix_memalign((void**) &oscillatorNextNormFreq, 16, robert@464: numOfOscillators * sizeof(float)); robert@464: err += posix_memalign((void**) &oscillatorNextAmp, 16, robert@464: numOfOscillators * sizeof(float)); robert@464: err += posix_memalign((void**) &oscillatorNormFrequencies, 16, robert@464: numOfOscillators * sizeof(float)); robert@464: err += posix_memalign((void**) &oscillatorAmplitudes, 16, robert@464: numOfOscillators * sizeof(float)); robert@464: err += posix_memalign((void**) &oscillatorNormFreqDerivatives, 16, robert@464: numOfOscillators * sizeof(float)); robert@464: err += posix_memalign((void**) &oscillatorAmplitudeDerivatives, 16, robert@464: numOfOscillators * sizeof(float)); robert@464: err += posix_memalign((void**) &phaseCopies, 16, robert@464: numOfOscillators * sizeof(float)); robert@464: err += posix_memalign((void**) &nextNormFreqCopies, 16, robert@464: numOfOscillators * sizeof(float)); robert@464: err += posix_memalign((void**) &nextAmpCopies, 16, robert@464: numOfOscillators * sizeof(float)); robert@464: robert@464: // regular ones robert@464: oscStatNormFrequenciesMean = new float[numOfPartials]; robert@464: oscStatNumHops = new float[numOfPartials]; robert@464: indicesMapping = new int[numOfPartials]; robert@464: freqFixedDeltas = new float[numOfPartials]; robert@464: ampFixedDeltas = new float[numOfPartials]; robert@464: nyquistCut = new bool[numOfPartials]; robert@464: robert@464: if (err > 0) { robert@464: dbox_printf("Failed memory allocations %@#!\n"); robert@464: return false; robert@464: } robert@464: robert@464: // copy stats [they do not change] robert@464: for (int n = 0; n < numOfPartials; n++) { robert@464: oscStatNormFrequenciesMean[n] = partials->partialFreqMean[n] robert@464: * frequencyScaler; robert@464: oscStatNumHops[n] = partials->partialNumFrames[n]; // in Parser and Partials "frames" are what we call here "hops" [see comment at top of file] robert@464: } robert@464: robert@464: // deafult values robert@464: actPartNum = 0; robert@464: loopStartHop = 0; robert@464: loopEndHop = (parser.partials.getHopNum() - 2) * overSampling; robert@464: ampTh = 0.0001; robert@464: hopNumTh = 0; robert@464: pitchMultiplier = 1; robert@464: freqMovement = 1; robert@464: filterNum = 0; robert@464: note = false; robert@464: speed = 1; robert@464: nextSpeed = -1; robert@464: maxSpeed = 10; robert@464: minSpeed = 0.1; robert@464: jumpHop = -1; robert@464: robert@464: // filter robert@464: filterMaxF = 22000; robert@464: filterAmpMinF = 10 * frequencyScaler; robert@464: filterAmpMaxF = 5000 * frequencyScaler; robert@464: filterAmpMul = 10.0; robert@464: robert@464: // adsr robert@464: minAttackTime = .0001; robert@464: deltaAttackTime = 2.; robert@464: minReleaseTime = 1; robert@464: deltaReleaseTime = 2.5; robert@464: robert@464: adsr.setAttackRate(minAttackTime * rate); robert@464: adsr.setDecayRate(.0001 * rate); robert@464: adsr.setSustainLevel(1); robert@464: adsr.setReleaseRate(minReleaseTime * rate); robert@464: robert@464: state = bank_stopped; robert@464: return true; robert@464: } robert@464: robert@464: void OscillatorBank::resetOscillators() { robert@464: currentHop = -1; robert@464: loopDir = 1; robert@464: loopDirShift = 0; robert@464: fill(nyquistCut, nyquistCut + numOfPartials, false); robert@464: prevAdsrVal = 0; robert@464: prevAmpTh = ampTh; robert@464: prevHopNumTh = hopNumTh; robert@464: prevPitchMultiplier = pitchMultiplier; robert@464: prevFreqMovement = freqMovement; robert@464: prevFilterNum = filterNum; robert@464: memcpy(prevFilterFreqs, filterFreqs, filterNum * sizeof(float)); robert@464: memcpy(prevFilterQ, filterQ, filterNum * sizeof(float)); robert@464: robert@464: int activePNum = partials->activePartialNum[0]; robert@464: unsigned int *activeP = partials->activePartials[0]; robert@464: for (int i = 0; i < activePNum; i++) { robert@464: freqFixedDeltas[activeP[i]] = partials->partialFreqDelta[activeP[i]][0] robert@464: / overSampling; robert@464: ampFixedDeltas[activeP[i]] = partials->partialAmpDelta[activeP[i]][0] robert@464: / overSampling; robert@464: } robert@464: // attack! robert@464: adsr.gate(1); robert@464: note = true; robert@464: robert@464: nextHop(); robert@464: robert@464: state = bank_playing; robert@464: } robert@464: robert@464: void OscillatorBank::nextHop() { robert@464: hopSize = oscBankHopSize; robert@464: robert@464: // copy phases, next freqs and next amps from previous frame robert@464: memcpy(phaseCopies, oscillatorPhases, actPartNum * sizeof(float)); robert@464: memcpy(nextNormFreqCopies, oscillatorNextNormFreq, robert@464: actPartNum * sizeof(float)); robert@464: memcpy(nextAmpCopies, oscillatorNextAmp, actPartNum * sizeof(float)); robert@464: robert@464: // next frame is forward or backwards, cos we could be in the loop robert@464: currentHop += loopDir; robert@464: robert@464: checkDirection(); robert@464: robert@464: // if((currentHop/overSampling)%100 == 0) robert@464: // dbox_printf("currentHop %d, direction: %d\n", currentHop/overSampling, loopDir); robert@464: robert@464: // if needs jump, end here this method, cos jumpToHop() will do tee rest robert@464: if (checkJump() == 0) robert@464: return; robert@464: // otherwise, if jump is not needed or fails, continue regular stuff robert@464: robert@464: if (nextEnvState() != 0) robert@464: return; // release has ended! robert@464: robert@464: checkSpeed(); robert@464: robert@464: // now let's decide how to calculate next hop robert@464: if (!checkOversampling()) robert@464: nextOscBankHop(); robert@464: else robert@464: nextPartialHop(); robert@464: } robert@464: robert@464: void OscillatorBank::nextOscBankHop() { robert@464: int parIndex, localHop; robert@464: float parDamp = 1; robert@464: int currentPartialHop = (currentHop / overSampling) + loopDirShift; robert@464: robert@464: // if going backwards in the loop, get previous frame active partials... robert@464: actPartNum = partials->activePartialNum[currentPartialHop - loopDirShift]; robert@464: actPart = partials->activePartials[currentPartialHop - loopDirShift]; robert@464: //cout << "actPartNum: " << actPartNum << endl; robert@464: robert@464: envState = adsr.getState(); // to determine what state we will be in next hop [attack, decay, sustain, release] robert@464: robert@464: int parCnt = 0; robert@464: int currentHopReminder = currentHop % overSampling; robert@464: // steps to reach next bank hop from previous partial hop robert@464: int steps = currentHopReminder + 1; robert@464: if (loopDir < 0) robert@464: steps = overSampling - currentHopReminder + 1; robert@464: robert@464: for (int i = 0; i < actPartNum; i++) { robert@464: // find partial and frame robert@464: parIndex = actPart[i]; robert@464: //localHop = partials->localPartialFrames[currentPartialHop][parIndex]; robert@464: 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] robert@464: robert@464: //float delta = partials->partialFrequencies[parIndex][localHop+loopDir] - partials->partialFrequencies[parIndex][localHop]; robert@464: robert@464: // if this partial was over nyquist on previous hop... robert@464: if (nyquistCut[parIndex]) { robert@464: // ...restart from safe values robert@464: oscillatorPhases[parCnt] = 0; robert@464: //TODO add freqmove dependency robert@464: oscillatorNextNormFreq[parCnt] = robert@464: (partials->partialFrequencies[parIndex][localHop] robert@464: + freqFixedDeltas[parIndex] * (steps - 1)) robert@464: * frequencyScaler * prevPitchMultiplier; robert@464: oscillatorNextAmp[parCnt] = 0; robert@464: } else if (loopDir == 1) // otherwise recover phase, target freq and target amp from previous frame robert@464: { robert@464: if ((localHop != 0) || (currentHopReminder != 0)) { robert@464: oscillatorPhases[parCnt] = robert@464: phaseCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextNormFreq[parCnt] = robert@464: nextNormFreqCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextAmp[parCnt] = robert@464: nextAmpCopies[indicesMapping[parIndex]]; robert@464: } else // first oscillator hop [both for bank and partial], so no previous data are available robert@464: { robert@464: oscillatorPhases[parCnt] = 0; robert@464: //TODO add freqmove dependency robert@464: oscillatorNextNormFreq[parCnt] = robert@464: partials->partialFrequencies[parIndex][localHop] robert@464: * frequencyScaler * prevPitchMultiplier; robert@464: parDamp = calculateParDamping(parIndex, prevHopNumTh, robert@464: prevAdsrVal, oscillatorNextNormFreq[parCnt], robert@464: prevFilterNum, prevFilterFreqs, prevFilterQ); robert@464: oscillatorNextAmp[parCnt] = robert@464: partials->partialAmplitudes[parIndex][localHop] robert@464: * parDamp; robert@464: if(oscillatorNextAmp[parCnt] > 1) robert@464: oscillatorNextAmp[parCnt] = 1; robert@464: freqFixedDeltas[parIndex] = robert@464: partials->partialFreqDelta[parIndex][localHop + loopDir] robert@464: * loopDir / overSampling; robert@464: ampFixedDeltas[parIndex] = robert@464: partials->partialAmpDelta[parIndex][localHop + loopDir] robert@464: * loopDir / overSampling; robert@464: } robert@464: } else { robert@464: oscillatorPhases[parCnt] = phaseCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextNormFreq[parCnt] = robert@464: nextNormFreqCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextAmp[parCnt] = nextAmpCopies[indicesMapping[parIndex]]; robert@464: } robert@464: robert@464: // remove aliasing, skipping partial over nyquist freq robert@464: if (oscillatorNextNormFreq[parCnt] > nyqNorm) { robert@464: nyquistCut[parIndex] = true; robert@464: continue; robert@464: } robert@464: nyquistCut[parIndex] = false; robert@464: robert@464: // first set up freq, cos filter affects amplitude damping according to freq content robert@464: oscillatorNormFrequencies[parCnt] = oscillatorNextNormFreq[parCnt]; // to fix any possible drifts robert@464: // save next values, current for next round robert@464: oscillatorNextNormFreq[parCnt] = (freqMovement robert@464: * (partials->partialFrequencies[parIndex][localHop] robert@464: + freqFixedDeltas[parIndex] * steps) * frequencyScaler robert@464: + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex]) robert@464: * pitchMultiplier; robert@464: // 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 robert@464: oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt] robert@464: - oscillatorNormFrequencies[parCnt]) / hopCounter; robert@464: // this second weird passage handles dissonance control, morphing between regular and mean frequencies robert@464: oscillatorNormFreqDerivatives[parCnt] = freqMovement robert@464: * oscillatorNormFreqDerivatives[parCnt] robert@464: + (1 - freqMovement) robert@464: * ((oscStatNormFrequenciesMean[parIndex] robert@464: * pitchMultiplier) robert@464: - oscillatorNormFrequencies[parCnt]) robert@464: / hopCounter; robert@464: robert@464: parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal, robert@464: oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ); robert@464: robert@464: // now amplitudes robert@464: oscillatorAmplitudes[parCnt] = oscillatorNextAmp[parCnt]; // to fix any possible drifts robert@464: // save next values, current for next round robert@464: //delta = partials->partialAmplitudes[parIndex][localHop+loopDir] - partials->partialAmplitudes[parIndex][localHop]; robert@464: oscillatorNextAmp[parCnt] = robert@464: (partials->partialAmplitudes[parIndex][localHop] robert@464: + ampFixedDeltas[parIndex] * steps) * parDamp; robert@464: if(oscillatorNextAmp[parCnt] > 1) robert@464: oscillatorNextAmp[parCnt] = 1; robert@464: if ((loopDir == -1) && (localHop = 1) && (currentHopReminder == 1)) robert@464: oscillatorNextAmp[parCnt] = 0; robert@464: // 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 robert@464: oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt] robert@464: - oscillatorAmplitudes[parCnt]) / hopCounter; robert@464: robert@464: // finally update current mapping between oscillators and partials robert@464: indicesMapping[parIndex] = parCnt; robert@464: parCnt++; robert@464: } robert@464: actPartNum = parCnt; robert@464: // [NEON] if not multiple of 4... robert@464: if (actPartNum % 4 != 0) robert@464: addFakeOsc(); robert@464: } robert@464: robert@464: void OscillatorBank::nextPartialHop() { robert@464: unsigned int parIndex, localHop; robert@464: float parDamp = 1; robert@464: int currentPartialHop = currentHop / overSampling; robert@464: robert@464: // if going backwards in the loop, get previous frame active partials... robert@464: actPartNum = partials->activePartialNum[currentPartialHop - loopDirShift]; robert@464: actPart = partials->activePartials[currentPartialHop - loopDirShift]; robert@464: robert@464: envState = adsr.getState(); // to determine what state we will be in next hop [attack, decay, sustain, release] robert@464: robert@464: int parCnt = 0; robert@464: int steps = overSampling - 1; // steps to reach next hop [partial or bank] from previous partial hop robert@464: robert@464: for (int i = 0; i < actPartNum; i++) { robert@464: // find partial and frame robert@464: parIndex = actPart[i]; robert@464: //localHop = partials->localPartialFrames[currentPartialHop][parIndex]; robert@464: 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] robert@464: robert@464: // if this partial was over nyquist on previous hop... robert@464: if (nyquistCut[parIndex]) { robert@464: // ...restart from safe values robert@464: oscillatorPhases[parCnt] = 0; robert@464: //TODO add freqmove dependency robert@464: oscillatorNextNormFreq[parCnt] = robert@464: (partials->partialFrequencies[parIndex][localHop] robert@464: + freqFixedDeltas[parIndex] * steps robert@464: * (1 - loopDirShift)) * frequencyScaler robert@464: * prevPitchMultiplier; robert@464: oscillatorNextAmp[parCnt] = 0; robert@464: } else if (loopDir == 1) // otherwise recover phase, target freq and target amp from previous frame robert@464: { robert@464: if ((localHop != 0) || (overSampling > 1)) { robert@464: oscillatorPhases[parCnt] = robert@464: phaseCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextNormFreq[parCnt] = robert@464: nextNormFreqCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextAmp[parCnt] = robert@464: nextAmpCopies[indicesMapping[parIndex]]; robert@464: } else // first oscillator hop [both for bank and partial], so no previous data are available robert@464: { robert@464: oscillatorPhases[parCnt] = 0; robert@464: //TODO add freqmove dependency robert@464: oscillatorNextNormFreq[parCnt] = robert@464: partials->partialFrequencies[parIndex][localHop] robert@464: * frequencyScaler * prevPitchMultiplier; robert@464: parDamp = calculateParDamping(parIndex, prevHopNumTh, robert@464: prevAdsrVal, oscillatorNextNormFreq[parCnt], robert@464: prevFilterNum, prevFilterFreqs, prevFilterQ); robert@464: oscillatorNextAmp[parCnt] = robert@464: partials->partialAmplitudes[parIndex][localHop] robert@464: * parDamp; robert@464: if(oscillatorNextAmp[parCnt] > 1) robert@464: oscillatorNextAmp[parCnt] = 1; robert@464: freqFixedDeltas[parIndex] = robert@464: partials->partialFreqDelta[parIndex][localHop + loopDir] robert@464: * loopDir / overSampling; robert@464: ampFixedDeltas[parIndex] = robert@464: partials->partialAmpDelta[parIndex][localHop + loopDir] robert@464: * loopDir / overSampling; robert@464: } robert@464: } else { robert@464: if (localHop != partials->partialNumFrames[parIndex] - 1) { robert@464: oscillatorPhases[parCnt] = robert@464: phaseCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextNormFreq[parCnt] = robert@464: nextNormFreqCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextAmp[parCnt] = robert@464: nextAmpCopies[indicesMapping[parIndex]]; robert@464: } else // first oscillator hop [going backwards - both for bank and partial] , so no previous data are available robert@464: { robert@464: oscillatorPhases[parCnt] = 0; robert@464: //TODO add freqmove dependency robert@464: oscillatorNextNormFreq[parCnt] = robert@464: partials->partialFrequencies[parIndex][localHop] robert@464: * frequencyScaler * prevPitchMultiplier; robert@464: parDamp = calculateParDamping(parIndex, prevHopNumTh, robert@464: prevAdsrVal, oscillatorNextNormFreq[parCnt], robert@464: prevFilterNum, prevFilterFreqs, prevFilterQ); robert@464: oscillatorNextAmp[parCnt] = robert@464: partials->partialAmplitudes[parIndex][localHop] robert@464: * parDamp; robert@464: if(oscillatorNextAmp[parCnt] > 1) robert@464: oscillatorNextAmp[parCnt] = 1; robert@464: freqFixedDeltas[parIndex] = robert@464: partials->partialFreqDelta[parIndex][localHop + loopDir] robert@464: * loopDir / overSampling; robert@464: ampFixedDeltas[parIndex] = robert@464: partials->partialAmpDelta[parIndex][localHop + loopDir] robert@464: * loopDir / overSampling; robert@464: } robert@464: } robert@464: // remove aliasing, skipping partial over nyquist freq robert@464: if (oscillatorNextNormFreq[parCnt] > nyqNorm) { robert@464: //cout << nyqNorm << endl; robert@464: nyquistCut[parIndex] = true; robert@464: continue; robert@464: } robert@464: nyquistCut[parIndex] = false; robert@464: robert@464: // first set up freq, cos filter affects amplitude damping according to freq content robert@464: oscillatorNormFrequencies[parCnt] = oscillatorNextNormFreq[parCnt]; // to fix any possible drifts robert@464: // save next values, current for next round robert@464: oscillatorNextNormFreq[parCnt] = (freqMovement robert@464: * (partials->partialFrequencies[parIndex][localHop + loopDir] robert@464: - freqFixedDeltas[parIndex] * steps * loopDirShift) robert@464: * frequencyScaler robert@464: + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex]) robert@464: * pitchMultiplier; robert@464: // 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 robert@464: oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt] robert@464: - oscillatorNormFrequencies[parCnt]) / hopCounter; robert@464: // this second weird passage handles dissonance control, morphing between regular and mean frequencies robert@464: oscillatorNormFreqDerivatives[parCnt] = freqMovement robert@464: * oscillatorNormFreqDerivatives[parCnt] robert@464: + (1 - freqMovement) robert@464: * ((oscStatNormFrequenciesMean[parIndex] robert@464: * pitchMultiplier) robert@464: - oscillatorNormFrequencies[parCnt]) robert@464: / hopCounter; robert@464: robert@464: parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal, robert@464: oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ); robert@464: robert@464: // now amplitudes robert@464: oscillatorAmplitudes[parCnt] = oscillatorNextAmp[parCnt]; // to fix any possible drifts robert@464: // save next values, current for next round robert@464: //delta = partials->partialAmplitudes[parIndex][localHop+loopDir] - partials->partialAmplitudes[parIndex][localHop]; robert@464: oscillatorNextAmp[parCnt] = robert@464: (partials->partialAmplitudes[parIndex][localHop + loopDir] robert@464: - (ampFixedDeltas[parIndex]) * steps * loopDirShift) robert@464: * parDamp; robert@464: if(oscillatorNextAmp[parCnt] > 1) robert@464: oscillatorNextAmp[parCnt] = 1; robert@464: robert@464: // to avoid bursts when transients are played backwards robert@464: if ((loopDir == -1) && (localHop - 1 == 0) && (overSampling == 1)) { robert@464: oscillatorNextAmp[parCnt] = 0; robert@464: } robert@464: // 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 robert@464: oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt] robert@464: - oscillatorAmplitudes[parCnt]) / hopCounter; robert@464: robert@464: // if next is not going to loop boundaries, get next deltas [same direction] robert@464: if ((((currentPartialHop + loopDir) * overSampling != loopEndHop) robert@464: || (loopDir == -1)) robert@464: && (((currentPartialHop + loopDir) * overSampling + loopDir robert@464: != loopStartHop) || (loopDir == 1))) { robert@464: freqFixedDeltas[parIndex] = robert@464: partials->partialFreqDelta[parIndex][localHop + loopDir] robert@464: * loopDir / overSampling; robert@464: ampFixedDeltas[parIndex] = robert@464: partials->partialAmpDelta[parIndex][localHop + loopDir] robert@464: * loopDir / overSampling; robert@464: } else // .. otherwise, keep deltas but change sign [co swe change direction] robert@464: { robert@464: freqFixedDeltas[parIndex] = -freqFixedDeltas[parIndex]; robert@464: ampFixedDeltas[parIndex] = -ampFixedDeltas[parIndex]; robert@464: } robert@464: robert@464: // finally update current mapping between oscillators and partials robert@464: indicesMapping[parIndex] = parCnt; robert@464: parCnt++; robert@464: } robert@464: actPartNum = parCnt; robert@464: // [NEON] if not multiple of 4... robert@464: if (actPartNum % 4 != 0) robert@464: addFakeOsc(); robert@464: robert@464: updatePrevControls(); robert@464: } robert@464: robert@464: void OscillatorBank::addFakeOsc() { robert@464: // ...calculate difference robert@464: int newPartNum = (actPartNum + 3) & ~0x3; robert@464: // ...add fake oscillators until total num is multiple of 4 robert@464: for (int i = actPartNum; i < newPartNum; i++) { robert@464: oscillatorAmplitudes[i] = 0; robert@464: oscillatorNormFrequencies[i] = 0; robert@464: oscillatorAmplitudeDerivatives[i] = 0; robert@464: oscillatorNormFreqDerivatives[i] = 0; robert@464: oscillatorPhases[i] = 0; robert@464: } robert@464: // ...and update num of active partials robert@464: actPartNum = newPartNum; robert@464: } robert@464: robert@464: void OscillatorBank::play(float vel) { robert@464: // set attack and release params according to velocity robert@464: //adsr.setAttackRate((minAttackTime + ((1 - vel) * deltaAttackTime)) * rate); robert@464: adsr.setAttackRate(minAttackTime * rate); robert@464: //adsr.setReleaseRate((minReleaseTime + (1 - vel) * deltaReleaseTime) * rate); robert@464: adsr.setReleaseRate(minReleaseTime * rate); robert@464: robert@464: // set timbre robert@464: hopNumTh = log((1 - vel) + 1) / log(2) * 20000; robert@464: robert@464: state = bank_toreset; robert@464: } robert@464: robert@464: //--------------------------------------------------------------------------------------------------------------------------- robert@464: // private methods robert@464: //--------------------------------------------------------------------------------------------------------------------------- robert@464: robert@464: bool OscillatorBank::loader(char *filename, int hopsize, int samplerate) { robert@464: rate = samplerate; robert@464: loaded = parser.parseFile(filename, hopsize, samplerate); robert@464: return loaded; robert@464: } robert@464: robert@464: int OscillatorBank::jumpToHop() { robert@464: int jumpGap = abs(jumpHop - currentHop / overSampling); // gaps in partial reference robert@464: robert@464: // can't jump to self dude robert@464: if (jumpGap == 0) robert@464: return 1; robert@464: robert@464: // direction is in general maintained with jump robert@464: if (jumpHop == 0) robert@464: setDirection(1); robert@464: else if (jumpHop == lastHop) robert@464: setDirection(-1); robert@464: robert@464: dbox_printf("\tJump from %d to %d\n", currentHop / overSampling, jumpHop); robert@464: dbox_printf("\tdirection %d\n", loopDir); robert@464: robert@464: currentHop = jumpHop * overSampling; robert@464: robert@464: if (nextEnvState() != 0) robert@464: return 0; // release has ended! robert@464: robert@464: checkSpeed(); robert@464: robert@464: int parIndex, localHop, targetHop; robert@464: float parDamp = 1; robert@464: int currentPartialHop = currentHop / overSampling; robert@464: int targetPartialHop = jumpHop; robert@464: robert@464: actPartNum = partials->activePartialNum[currentPartialHop]; robert@464: actPart = partials->activePartials[currentPartialHop]; robert@464: int targetActParNum = partials->activePartialNum[targetPartialHop]; robert@464: unsigned int *targetActPar = partials->activePartials[targetPartialHop]; robert@464: robert@464: envState = adsr.getState(); // to determine what state we will be in next hop [attack, decay, sustain, release] robert@464: robert@464: int parCnt = 0; robert@464: int currentHopReminder = currentHop % overSampling; robert@464: robert@464: // steps to walk where i am [bank of partial hop] from previous partial hop robert@464: int steps = currentHopReminder * (overSampling != 1); // no oversampling 0, oversampling and going ff currentHopReminder robert@464: robert@464: for (int i = 0; i < actPartNum; i++) { robert@464: // find partial and frame robert@464: parIndex = actPart[i]; robert@464: //localHop = partials->localPartialFrames[currentPartialHop][parIndex]; robert@464: 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] robert@464: robert@464: // if this partial was over nyquist on previous hop... robert@464: if (nyquistCut[parIndex]) { robert@464: // ...restart from safe values robert@464: oscillatorPhases[parCnt] = 0; robert@464: //TODO add freqmove dependency robert@464: oscillatorNextNormFreq[parCnt] = robert@464: (partials->partialFrequencies[parIndex][localHop] robert@464: + freqFixedDeltas[parIndex] * steps * loopDir) robert@464: * frequencyScaler * prevPitchMultiplier; robert@464: oscillatorNextAmp[parCnt] = 0; robert@464: } else if (loopDir == 1) {// otherwise recover phase, target freq and target amp from previous frame robert@464: if ((localHop != 0) robert@464: || ((overSampling > 1) && (currentHopReminder != 0))) { robert@464: oscillatorPhases[parCnt] = robert@464: phaseCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextNormFreq[parCnt] = robert@464: nextNormFreqCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextAmp[parCnt] = robert@464: nextAmpCopies[indicesMapping[parIndex]]; robert@464: } else { // first oscillator hop [both for bank and partial], so no previous data are available robert@464: oscillatorPhases[parCnt] = 0; robert@464: //TODO add freqmove dependency robert@464: oscillatorNextNormFreq[parCnt] = robert@464: partials->partialFrequencies[parIndex][localHop] robert@464: * frequencyScaler * prevPitchMultiplier; robert@464: parDamp = calculateParDamping(parIndex, prevHopNumTh, robert@464: prevAdsrVal, oscillatorNextNormFreq[parCnt], robert@464: prevFilterNum, prevFilterFreqs, prevFilterQ); robert@464: oscillatorNextAmp[parCnt] = robert@464: partials->partialAmplitudes[parIndex][localHop] robert@464: * parDamp; robert@464: if(oscillatorNextAmp[parCnt] > 1) robert@464: oscillatorNextAmp[parCnt] = 1; robert@464: } robert@464: } else { robert@464: if (( (unsigned)localHop != partials->partialNumFrames[parIndex] - 1) robert@464: || ((overSampling > 1) && (currentHopReminder != 0))) { robert@464: oscillatorPhases[parCnt] = robert@464: phaseCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextNormFreq[parCnt] = robert@464: nextNormFreqCopies[indicesMapping[parIndex]]; robert@464: oscillatorNextAmp[parCnt] = robert@464: nextAmpCopies[indicesMapping[parIndex]]; robert@464: } else // first oscillator hop [going backwards - both for bank and partial] , so no previous data are available, so retrieve where i am robert@464: { robert@464: oscillatorPhases[parCnt] = 0; robert@464: //TODO add freqmove dependency robert@464: oscillatorNextNormFreq[parCnt] = robert@464: partials->partialFrequencies[parIndex][localHop] robert@464: * frequencyScaler * prevPitchMultiplier; robert@464: parDamp = calculateParDamping(parIndex, prevHopNumTh, robert@464: prevAdsrVal, oscillatorNextNormFreq[parCnt], robert@464: prevFilterNum, prevFilterFreqs, prevFilterQ); robert@464: oscillatorNextAmp[parCnt] = robert@464: partials->partialAmplitudes[parIndex][localHop] robert@464: * parDamp; robert@464: if(oscillatorNextAmp[parCnt] > 1) robert@464: oscillatorNextAmp[parCnt] = 1; robert@464: } robert@464: } robert@464: // remove aliasing, skipping partial over nyquist freq robert@464: if (oscillatorNextNormFreq[parCnt] > nyqNorm) { robert@464: //cout << nyqNorm << endl; robert@464: nyquistCut[parIndex] = true; robert@464: continue; robert@464: } robert@464: nyquistCut[parIndex] = false; robert@464: robert@464: // check what happens of this partial at target hop robert@464: float targetFreqVal, targetAmpVal; robert@464: //targetHop = partials->localPartialFrames[targetPartialHop][parIndex]; robert@464: targetHop = targetPartialHop - partials->partialStartFrame[parIndex]; robert@464: robert@464: if (targetHop == -1) robert@464: targetFreqVal = targetAmpVal = 0; robert@464: else { robert@464: targetFreqVal = partials->partialFrequencies[parIndex][targetHop] robert@464: * frequencyScaler; // pitch shift will be multiplied later!!! robert@464: targetAmpVal = partials->partialFrequencies[parIndex][targetHop]; // parDamp will be multiplied later!!! robert@464: } robert@464: robert@464: // first set up freq, cos filter affects amplitude damping according to freq content robert@464: oscillatorNormFrequencies[parCnt] = oscillatorNextNormFreq[parCnt]; // to fix any possible drifts robert@464: // save next values, current for next round robert@464: oscillatorNextNormFreq[parCnt] = (freqMovement * targetFreqVal robert@464: + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex]) robert@464: * pitchMultiplier; robert@464: // 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 robert@464: oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt] robert@464: - oscillatorNormFrequencies[parCnt]) / hopCounter; robert@464: // this second weird passage handles dissonance control, morphing between regular and mean frequencies robert@464: oscillatorNormFreqDerivatives[parCnt] = freqMovement robert@464: * oscillatorNormFreqDerivatives[parCnt] robert@464: + (1 - freqMovement) robert@464: * ((oscStatNormFrequenciesMean[parIndex] robert@464: * pitchMultiplier) robert@464: - oscillatorNormFrequencies[parCnt]) robert@464: / hopCounter; robert@464: robert@464: parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal, robert@464: oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ); robert@464: robert@464: // now amplitudes robert@464: oscillatorAmplitudes[parCnt] = oscillatorNextAmp[parCnt]; // to fix any possible drifts robert@464: // save next values, current for next round robert@464: oscillatorNextAmp[parCnt] = targetAmpVal * parDamp; robert@464: if(oscillatorNextAmp[parCnt] > 1) robert@464: oscillatorNextAmp[parCnt] = 1; robert@464: // to avoid bursts when transients are played backwards robert@464: if ((loopDir == -1) && (targetHop == 0) robert@464: && ((overSampling == 1) || (currentHopReminder == 0))) { robert@464: oscillatorNextAmp[parCnt] = 0; robert@464: } robert@464: // 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 robert@464: oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt] robert@464: - oscillatorAmplitudes[parCnt]) / hopCounter; robert@464: robert@464: //if partial does not die at target, calculate deltas according to direction robert@464: if (targetHop != -1) { robert@464: freqFixedDeltas[parIndex] = robert@464: partials->partialFreqDelta[parIndex][targetHop] * loopDir robert@464: / overSampling; robert@464: ampFixedDeltas[parIndex] = robert@464: partials->partialAmpDelta[parIndex][targetHop] * loopDir robert@464: / overSampling; robert@464: } robert@464: robert@464: // finally update current mapping between oscillators and partials robert@464: indicesMapping[parIndex] = parCnt; robert@464: parCnt++; robert@464: } robert@464: actPartNum = parCnt; robert@464: robert@464: // now add the ones that start at target hop! robert@464: for (int i = 0; i < targetActParNum; i++) { robert@464: // find partial and frame robert@464: parIndex = targetActPar[i]; robert@464: //targetHop = partials->localPartialFrames[targetPartialHop][parIndex]; robert@464: 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] robert@464: robert@464: // check if this partials was already active before the jump robert@464: //localHop = partials->localPartialFrames[currentPartialHop][parIndex]; robert@464: localHop = currentPartialHop - partials->partialStartFrame[parIndex]; robert@464: robert@464: // if yes, skip it robert@464: if (localHop != -1) robert@464: continue; robert@464: robert@464: // otherwise add it to active bunch and calcucalte values robert@464: robert@464: // first set up freq, cos filter affects amplitude damping according to freq content robert@464: oscillatorNormFrequencies[parCnt] = 0; robert@464: // save next values, current for next round robert@464: oscillatorNextNormFreq[parCnt] = (freqMovement robert@464: * partials->partialFrequencies[parIndex][targetHop] robert@464: * frequencyScaler robert@464: + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex]) robert@464: * pitchMultiplier; robert@464: // 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 robert@464: oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt] robert@464: - oscillatorNormFrequencies[parCnt]) / hopCounter; robert@464: // this second weird passage handles dissonance control, morphing between regular and mean frequencies robert@464: oscillatorNormFreqDerivatives[parCnt] = freqMovement robert@464: * oscillatorNormFreqDerivatives[parCnt] robert@464: + (1 - freqMovement) robert@464: * ((oscStatNormFrequenciesMean[parIndex] robert@464: * pitchMultiplier) robert@464: - oscillatorNormFrequencies[parCnt]) robert@464: / hopCounter; robert@464: robert@464: parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal, robert@464: oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ); robert@464: robert@464: // now amplitudes robert@464: oscillatorAmplitudes[parCnt] = 0; robert@464: // save next values, current for next round robert@464: oscillatorNextAmp[parCnt] = robert@464: partials->partialFrequencies[parIndex][targetHop] * parDamp; robert@464: if(oscillatorNextAmp[parCnt] > 1) robert@464: oscillatorNextAmp[parCnt] = 1; robert@464: // 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 robert@464: oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt] robert@464: - oscillatorAmplitudes[parCnt]) / hopCounter; robert@464: robert@464: //calculate deltas according to direction robert@464: freqFixedDeltas[parIndex] = robert@464: partials->partialFreqDelta[parIndex][targetHop] * loopDir robert@464: / overSampling; robert@464: ampFixedDeltas[parIndex] = robert@464: partials->partialAmpDelta[parIndex][targetHop] * loopDir robert@464: / overSampling; robert@464: robert@464: // finally update current mapping between oscillators and partials robert@464: indicesMapping[parIndex] = parCnt; robert@464: parCnt++; robert@464: robert@464: } robert@464: // [NEON] if not multiple of 4... robert@464: if (actPartNum % 4 != 0) robert@464: addFakeOsc(); robert@464: robert@464: updatePrevControls(); robert@464: robert@464: jumpHop = -1; robert@464: robert@464: return 0; robert@464: } robert@464: robert@464: int OscillatorBank::nextEnvState() { robert@464: /* robert@464: envState = Attack.getState(); // to determine what state we are in [attack, decay, sustain, release] robert@464: robert@464: // osc bank is playing the tail and the tail ends... robert@464: if( (state == bank_playing)&&(envState == env_idle) ) robert@464: { robert@464: state = bank_stopped; // ...stop bank robert@464: return 1; // and return immediately robert@464: } robert@464: else if( (envState == env_attack) || (envState == env_decay) ) robert@464: { robert@464: // run envelopes until next frame robert@464: dampWeight = Attack.process(hopSize); robert@464: } robert@464: else if(envState == env_release) robert@464: { robert@464: // run envelopes until next frame robert@464: dampWeight = Attack.process(hopSize); robert@464: releaseDamp = Release.process(hopSize); robert@464: }*/ robert@464: robert@464: envState = adsr.getState(); robert@464: // osc bank is playing the tail and the tail ends... robert@464: if ((state == bank_playing) && (envState == env_idle)) { robert@464: state = bank_stopped; // ...stop bank robert@464: adsrVal = 0; robert@464: return 1; // and return immediately robert@464: } else robert@464: adsrVal = adsr.process(hopSize); robert@464: robert@464: return 0; robert@464: } robert@464: robert@464: void OscillatorBank::checkDirection() { robert@464: // end of the loop or end of file robert@464: if (((currentHop >= loopEndHop) && (loopDir == 1)) robert@464: || ((currentHop >= lastHop) && (loopDir == 1))) { robert@464: // move backwards robert@464: setDirection(-1); robert@464: //dbox_printf("backward from %d\n", loopEndHop); robert@464: } else if (((currentHop <= loopStartHop) && (loopDir == -1)) robert@464: || ((currentHop <= 0) && (loopDir == -1))) // start of the loop or start of file robert@464: { robert@464: // move forward robert@464: setDirection(1); robert@464: //dbox_printf("forward from %d\n", loopStartHop); robert@464: } robert@464: } robert@464: robert@464: void OscillatorBank::checkSpeed() { robert@464: // speed control [alike on highways, LOL] robert@464: if (nextSpeed > 0) { robert@464: nextSpeed = (nextSpeed < maxSpeed) ? nextSpeed : maxSpeed; robert@464: nextSpeed = (nextSpeed > minSpeed) ? nextSpeed : minSpeed; robert@464: speed = nextSpeed; robert@464: nextSpeed = -1; robert@464: } robert@464: hopCounter = hopSize / speed; robert@464: } robert@464: robert@464: int OscillatorBank::checkJump() { robert@464: //check if has to jump somewhere robert@464: if (jumpHop > -1) { robert@464: // needs to jump! robert@464: if (jumpToHop() == 0) robert@464: return 0; robert@464: } robert@464: return 1; // no jump robert@464: } robert@464: robert@464: bool OscillatorBank::checkOversampling() { robert@464: //TODO fix this, but need andrew to fix oversampling multiple of period size robert@464: // if partialsHopSize is not a multiple of oversampling, change hop size to periodically match next partial hop robert@464: if (hopSizeReminder > 0) { robert@464: // if next osc bank hop overtakes next partial hop... robert@464: if ((currentHop + loopDir) * hopSize > partialsHopSize) { robert@464: hopSize = hopSizeReminder; // ...shrink osc bank hop size to match partial hop robert@464: return true; // and set next hop as matching with next partial hop robert@464: } robert@464: } else if (((currentHop + (1 - loopDirShift)) % overSampling) == 0) // if next osc bank hop matches next partial hop robert@464: return true; // ...mark next hop as partial hop robert@464: robert@464: return false; // ,otherwise mark next hop as osc bank hop robert@464: } robert@464: robert@464: void OscillatorBank::updatePrevControls() { robert@464: prevAdsrVal = adsrVal; robert@464: prevAmpTh = ampTh; robert@464: prevHopNumTh = hopNumTh; robert@464: prevPitchMultiplier = pitchMultiplier; robert@464: prevFreqMovement = freqMovement; robert@464: prevFilterNum = filterNum; robert@464: memcpy(prevFilterFreqs, filterFreqs, filterNum * sizeof(float)); robert@464: memcpy(prevFilterQ, filterQ, filterNum * sizeof(float)); robert@464: } robert@464: robert@464: float OscillatorBank::calculateParDamping(int parIndex, int hopNTh, robert@464: float adsrVl, float nextFreq, int filNum, float *filFreq, float *filQ) { robert@464: float parDamp = 1; robert@464: robert@464: // timbre robert@464: parDamp = ((float) (oscStatNumHops[parIndex] + 1)) / (hopNTh + 1); robert@464: parDamp = (parDamp > 1) ? 1 : parDamp; robert@464: parDamp = adsrVl * parDamp; robert@464: robert@464: //filters robert@464: robert@464: float filterWeights[MAX_TOUCHES]; robert@464: float filterDamp[MAX_TOUCHES]; robert@464: float filDist; robert@464: float filterWeightsAcc; robert@464: float filDmp; robert@464: float filAmp; robert@464: robert@464: // band reject notch filter robert@464: // float dist, dmp; robert@464: // for(int k=0; k 0) { robert@464: // reset values robert@464: filDist = 0; robert@464: filterWeightsAcc = 0; robert@464: filDmp = 0; robert@464: filAmp = 0; robert@464: // for each filter robert@464: for (int k = 0; k < filNum; k++) { robert@464: // here are a couple of kludges to boost sound output of hi freq filters robert@464: robert@464: // damping effect of filter increases with distance, but decreases with filter frequency [kludge] robert@464: float mul = ((filterMaxF-nextFreq)/filterMaxF) * 0.9 + 0.1 ; robert@464: //filDist = fabs(nextFreq - filFreq[k])*( ((exp(a*4)-1)/EXP_DENOM) * 0.9 + 0.1 ); robert@464: filDist = fabs(nextFreq - filFreq[k])*mul; robert@464: robert@464: // these to merge all filters contributions according to distance robert@464: filterWeights[k] = filterMaxF - filDist; robert@464: filterWeightsAcc += filterWeights[k]; robert@464: // freqs very close to filter center are slightly amplified robert@464: // the size of this amp area and the effect of amplification increase with frequency [kludge] robert@464: if (filDist robert@464: < filterAmpMinF robert@464: + (filterAmpMaxF*(1-mul) - filterAmpMinF) * (1 - filQ[k]) ) robert@464: filAmp = filQ[k] * filterAmpMul*(1-mul); robert@464: else robert@464: filAmp = 0; robert@464: // actual damping robert@464: filDmp = 1 / (filDist * filQ[k]); robert@464: filDmp = (filDmp > 1) ? 1 : filDmp; robert@464: // sum damp+amplification robert@464: filterDamp[k] = filDmp + filAmp; robert@464: } robert@464: // do weighted mean to merge all filters contributions robert@464: filDmp = 0; robert@464: for (int k = 0; k < filNum; k++) robert@464: filDmp += filterDamp[k] * filterWeights[k]; robert@464: filDmp /= filterWeightsAcc; robert@464: // apply robert@464: parDamp *= filDmp; robert@464: } robert@464: robert@464: robert@464: return parDamp; robert@464: }