diff examples/d-box/OscillatorBank.cpp @ 300:dbeed520b014 prerelease

Renamed projects to examples
author Giulio Moro <giuliomoro@yahoo.it>
date Fri, 27 May 2016 13:58:20 +0100
parents projects/d-box/OscillatorBank.cpp@8a575ba3ab52
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/d-box/OscillatorBank.cpp	Fri May 27 13:58:20 2016 +0100
@@ -0,0 +1,1016 @@
+/*
+ * 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;
+}