Mercurial > hg > beaglert
view projects/d-box/OscillatorBank.cpp @ 39:638bc1ae2500 staging
Improved readibility of the DIGITAL code in the PRU, using register names instead of aliases and expanding some of the macros, removing unused macros. Binaries were not modified
author | Giulio Moro <giuliomoro@yahoo.it> |
---|---|
date | Wed, 13 May 2015 12:18:10 +0100 |
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; }