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