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