| robert@464 | 1 /* | 
| robert@464 | 2  * render.cpp | 
| robert@464 | 3  * | 
| robert@464 | 4  *  Created on: May 28, 2014 | 
| robert@464 | 5  *      Author: Victor Zappi | 
| robert@464 | 6  */ | 
| robert@464 | 7 | 
| robert@464 | 8 #include <Bela.h> | 
| robert@464 | 9 #include <PRU.h> | 
| robert@464 | 10 | 
| robert@464 | 11 #include "StatusLED.h" | 
| robert@464 | 12 #include "config.h" | 
| robert@464 | 13 #include "OscillatorBank.h" | 
| robert@464 | 14 #include "FeedbackOscillator.h" | 
| robert@464 | 15 #include "ADSR.h" | 
| robert@464 | 16 #include "FIRfilter.h" | 
| robert@464 | 17 #include <assert.h> | 
| robert@464 | 18 #include <cmath> | 
| robert@464 | 19 #include <vector> | 
| robert@464 | 20 | 
| robert@464 | 21 #undef DBOX_CAPE_TEST | 
| robert@464 | 22 | 
| robert@464 | 23 // Mappings from pin numbers on PCB to actual DAC channels | 
| robert@464 | 24 // This gives the DAC and ADC connectors the same effective pinout | 
| robert@464 | 25 // Update June 2016: this is no longer needed in the latest Bela | 
| robert@464 | 26 // release, but is kept here for convenience: it used to be | 
| robert@464 | 27 // 6 4 2 0 1 3 5 7 for the DAC pins | 
| robert@464 | 28 #define DAC_PIN0	0 | 
| robert@464 | 29 #define DAC_PIN1	1 | 
| robert@464 | 30 #define DAC_PIN2	2 | 
| robert@464 | 31 #define DAC_PIN3	3 | 
| robert@464 | 32 #define DAC_PIN4	4 | 
| robert@464 | 33 #define DAC_PIN5	5 | 
| robert@464 | 34 #define DAC_PIN6	6 | 
| robert@464 | 35 #define DAC_PIN7	7 | 
| robert@464 | 36 | 
| robert@464 | 37 #define ADC_PIN0	0 | 
| robert@464 | 38 #define ADC_PIN1	1 | 
| robert@464 | 39 #define ADC_PIN2	2 | 
| robert@464 | 40 #define ADC_PIN3	3 | 
| robert@464 | 41 #define ADC_PIN4	4 | 
| robert@464 | 42 #define ADC_PIN5	5 | 
| robert@464 | 43 #define ADC_PIN6	6 | 
| robert@464 | 44 #define ADC_PIN7	7 | 
| robert@464 | 45 | 
| robert@464 | 46 #define N_OCT		4.0	// maximum number of octaves on sensor 1 | 
| robert@464 | 47 | 
| robert@464 | 48 extern vector<OscillatorBank*> gOscBanks; | 
| robert@464 | 49 extern int gCurrentOscBank; | 
| robert@464 | 50 extern int gNextOscBank; | 
| robert@464 | 51 extern PRU *gPRU; | 
| robert@464 | 52 extern StatusLED gStatusLED; | 
| robert@464 | 53 extern bool gIsLoading; | 
| robert@464 | 54 extern bool gAudioIn; | 
| robert@464 | 55 | 
| robert@464 | 56 float *gOscillatorBuffer1, *gOscillatorBuffer2; | 
| robert@464 | 57 float *gOscillatorBufferRead, *gOscillatorBufferWrite; | 
| robert@464 | 58 int gOscillatorBufferReadPointer		= 0; | 
| robert@464 | 59 int gOscillatorBufferReadCurrentSize	= 0; | 
| robert@464 | 60 int gOscillatorBufferWriteCurrentSize	= 0; | 
| robert@464 | 61 bool gOscillatorNeedsRender				= false; | 
| robert@464 | 62 | 
| robert@464 | 63 int gMatrixSampleCount = 0;		// How many samples have elapsed on the matrix | 
| robert@464 | 64 | 
| robert@464 | 65 // Wavetable which changes in response to an oscillator | 
| robert@464 | 66 float *gDynamicWavetable; | 
| robert@464 | 67 int gDynamicWavetableLength; | 
| robert@464 | 68 bool gDynamicWavetableNeedsRender = false; | 
| robert@464 | 69 | 
| robert@464 | 70 // These variables handle the hysteresis oscillator used for setting the playback speed | 
| robert@464 | 71 bool gSpeedHysteresisOscillatorRising	= false; | 
| robert@464 | 72 int gSpeedHysteresisLastTrigger			= 0; | 
| robert@464 | 73 | 
| robert@464 | 74 // These variables handle the feedback oscillator used for controlling the wavetable | 
| robert@464 | 75 FeedbackOscillator gFeedbackOscillator; | 
| robert@464 | 76 float *gFeedbackOscillatorTable; | 
| robert@464 | 77 int gFeedbackOscillatorTableLength; | 
| robert@464 | 78 | 
| robert@464 | 79 // This comes from sensor.cpp where it records the most recent touch location on | 
| robert@464 | 80 // sensor 0. | 
| robert@464 | 81 extern float gSensor0LatestTouchPos; | 
| robert@464 | 82 extern int gSensor0LatestTouchNum; | 
| robert@464 | 83 float gPitchLatestInput = 0; | 
| robert@464 | 84 | 
| robert@464 | 85 extern float gSensor1LatestTouchPos[]; | 
| robert@464 | 86 //extern float gSensor1LatestTouchSizes[]; | 
| robert@464 | 87 extern int gSensor1LatestTouchCount; | 
| robert@464 | 88 extern int gSensor1LatestTouchIndex; | 
| robert@464 | 89 int gSensor1LastTouchIndex		= -1; | 
| robert@464 | 90 int gSensor1InputDelayCounter	= -1; | 
| robert@464 | 91 int gSensor1InputIndex			= 0; | 
| robert@464 | 92 float gSensor1MatrixTouchPos[5]	= {0}; | 
| robert@464 | 93 | 
| robert@464 | 94 // FSR value from matrix input | 
| robert@464 | 95 extern int gLastFSRValue; | 
| robert@464 | 96 | 
| robert@464 | 97 // Loop points from matrix input 4 | 
| robert@464 | 98 const int gLoopPointsInputBufferSize	= 256; | 
| robert@464 | 99 float gLoopPointsInputBuffer[gLoopPointsInputBufferSize]; | 
| robert@464 | 100 int gLoopPointsInputBufferPointer		= 0; | 
| robert@464 | 101 float gLoopPointMin = 0, gLoopPointMax	= 0; | 
| robert@464 | 102 | 
| robert@464 | 103 // multiplier to activate or mute audio in | 
| robert@464 | 104 int audioInStatus = 0; | 
| robert@464 | 105 | 
| robert@464 | 106 // xenomai timer | 
| robert@464 | 107 SRTIME prevChangeNs = 0; | 
| robert@464 | 108 | 
| robert@464 | 109 // pitch vars | 
| robert@464 | 110 float octaveSplitter; | 
| robert@464 | 111 float semitones[((int)N_OCT*12)+1]; | 
| robert@464 | 112 float deltaTouch	= 0; | 
| robert@464 | 113 float deltaWeightP	= 0.5 / 65536.0; | 
| robert@464 | 114 float deltaWeightI	= 0.0005 / 65536.0; | 
| robert@464 | 115 | 
| robert@464 | 116 // filter vars | 
| robert@464 | 117 ne10_fir_instance_f32_t filter[2]; | 
| robert@464 | 118 ne10_float32_t *filterIn[2]; | 
| robert@464 | 119 ne10_float32_t *filterOut[2]; | 
| robert@464 | 120 ne10_uint32_t blockSize; | 
| robert@464 | 121 ne10_float32_t *filterState[2]; | 
| robert@464 | 122 ne10_float32_t prevFiltered[2]; | 
| robert@464 | 123 int filterGain = 80; | 
| robert@464 | 124 ADSR PeakBurst[2]; | 
| robert@464 | 125 float peak[2]; | 
| robert@464 | 126 float peakThresh = 0.2; | 
| robert@464 | 127 | 
| robert@464 | 128 // Tasks for lower-priority calculation | 
| robert@464 | 129 AuxiliaryTask gMediumPriorityRender, gLowPriorityRender; | 
| robert@464 | 130 | 
| robert@464 | 131 | 
| robert@464 | 132 extern "C" { | 
| robert@464 | 133 	// Function prototype for ARM assembly implementation of oscillator bank | 
| robert@464 | 134 	void oscillator_bank_neon(int numAudioFrames, float *audioOut, | 
| robert@464 | 135 							  int activePartialNum, int lookupTableSize, | 
| robert@464 | 136 							  float *phases, float *frequencies, float *amplitudes, | 
| robert@464 | 137 							  float *freqDerivatives, float *ampDerivatives, | 
| robert@464 | 138 							  float *lookupTable); | 
| robert@464 | 139 | 
| robert@464 | 140 	void wavetable_interpolate_neon(int numSamplesIn, int numSamplesOut, | 
| robert@464 | 141 	                              float *tableIn, float *tableOut); | 
| robert@464 | 142 } | 
| robert@464 | 143 | 
| robert@464 | 144 void wavetable_interpolate(int numSamplesIn, int numSamplesOut, | 
| robert@464 | 145                               float *tableIn, float *tableOut, | 
| robert@464 | 146                               float *sineTable, float sineMix); | 
| robert@464 | 147 | 
| robert@464 | 148 inline float hysteresis_oscillator(float input, float risingThreshold, | 
| robert@464 | 149 									float fallingThreshold, bool *rising); | 
| robert@464 | 150 | 
| robert@464 | 151 void render_medium_prio(); | 
| robert@464 | 152 void render_low_prio(); | 
| robert@464 | 153 | 
| robert@464 | 154 #ifdef DBOX_CAPE_TEST | 
| robert@464 | 155 void render_capetest(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut, | 
| robert@464 | 156 			uint16_t *matrixIn, uint16_t *matrixOut); | 
| robert@464 | 157 #endif | 
| robert@464 | 158 | 
| robert@464 | 159 bool setup(BelaContext *context, void *userData) { | 
| robert@464 | 160 	int oscBankHopSize = *(int *)userData; | 
| robert@464 | 161 | 
| chris@543 | 162 	if(context->analogOutChannels <= 8 || context->analogInChannels <= 8) { | 
| chris@543 | 163 		printf("Error: D-Box needs at least 8 analog IO channels.\n"); | 
| chris@543 | 164 		return false; | 
| chris@543 | 165 	} | 
| chris@543 | 166 | 
| chris@543 | 167 	if(context->audioInChannels != context->audioOutChannels || | 
| chris@543 | 168 			context->analogInChannels != context-> analogOutChannels){ | 
| chris@543 | 169 		printf("Error: for this project, you need the same number of input and output channels.\n"); | 
| robert@464 | 170 		return false; | 
| robert@464 | 171 	} | 
| robert@464 | 172 | 
| robert@464 | 173 	// Allocate two buffers for rendering oscillator bank samples | 
| robert@464 | 174 	// One will be used for writing in the background while the other is used for reading | 
| robert@464 | 175 	// on the audio thread. 8-byte alignment needed for the NEON code. | 
| chris@543 | 176 	if(posix_memalign((void **)&gOscillatorBuffer1, 8, oscBankHopSize * context->audioOutChannels * sizeof(float))) { | 
| robert@464 | 177 		printf("Error allocating render buffers\n"); | 
| robert@464 | 178 		return false; | 
| robert@464 | 179 	} | 
| chris@543 | 180 	if(posix_memalign((void **)&gOscillatorBuffer2, 8, oscBankHopSize * context->audioOutChannels * sizeof(float))) { | 
| robert@464 | 181 		printf("Error allocating render buffers\n"); | 
| robert@464 | 182 		return false; | 
| robert@464 | 183 	} | 
| robert@464 | 184 	gOscillatorBufferWrite	= gOscillatorBuffer1; | 
| robert@464 | 185 	gOscillatorBufferRead	= gOscillatorBuffer2; | 
| robert@464 | 186 | 
| chris@543 | 187 	memset(gOscillatorBuffer1, 0, oscBankHopSize * context->audioOutChannels * sizeof(float)); | 
| chris@543 | 188 	memset(gOscillatorBuffer2, 0, oscBankHopSize * context->audioOutChannels * sizeof(float)); | 
| robert@464 | 189 | 
| robert@464 | 190 	// Initialise the dynamic wavetable used by the oscillator bank | 
| robert@464 | 191 	// It should match the size of the static one already allocated in the OscillatorBank object | 
| robert@464 | 192 	// Don't forget a guard point at the end of the table | 
| robert@464 | 193 	gDynamicWavetableLength = gOscBanks[gCurrentOscBank]->lookupTableSize; | 
| robert@464 | 194 	if(posix_memalign((void **)&gDynamicWavetable, 8, (gDynamicWavetableLength + 1) * sizeof(float))) { | 
| robert@464 | 195 		printf("Error allocating wavetable\n"); | 
| robert@464 | 196 		return false; | 
| robert@464 | 197 	} | 
| robert@464 | 198 | 
| robert@464 | 199 	gFeedbackOscillator.initialise(8192, 10.0, context->analogSampleRate); | 
| robert@464 | 200 | 
| robert@464 | 201 	for(int n = 0; n < gDynamicWavetableLength + 1; n++) | 
| robert@464 | 202 		gDynamicWavetable[n] = 0; | 
| robert@464 | 203 | 
| robert@464 | 204 	// pitch | 
| robert@464 | 205 	float midPos		= 0.5; | 
| robert@464 | 206 	octaveSplitter		= 1.0 / N_OCT; | 
| robert@464 | 207 	int numOfSemi		= 12*N_OCT; | 
| robert@464 | 208 	int middleSemitone	= 12*N_OCT/2; | 
| robert@464 | 209 	int lastSemitone	= middleSemitone+numOfSemi/2; | 
| robert@464 | 210 	float inc 			= 1.0 / (N_OCT*12.0); | 
| robert@464 | 211 	int i 				= -1; | 
| robert@464 | 212 	for(int semi=middleSemitone; semi<=lastSemitone; semi++) | 
| robert@464 | 213 		semitones[semi] = ( midPos + (++i)*inc) + 0.5; | 
| robert@464 | 214 	i 					= 0; | 
| robert@464 | 215 	for(int semi=middleSemitone-1; semi>=0; semi--) | 
| robert@464 | 216 		semitones[semi] = ( midPos - (++i)*inc) + 0.5; | 
| robert@464 | 217 | 
| robert@464 | 218 	if(gAudioIn) | 
| robert@464 | 219 		audioInStatus = 1; | 
| robert@464 | 220 | 
| robert@464 | 221 	// filter | 
| robert@464 | 222 	blockSize		= context->audioFrames; | 
| robert@464 | 223 	filterState[0]	= (ne10_float32_t *) NE10_MALLOC ((FILTER_TAP_NUM+blockSize-1) * sizeof (ne10_float32_t)); | 
| robert@464 | 224 	filterState[1]	= (ne10_float32_t *) NE10_MALLOC ((FILTER_TAP_NUM+blockSize-1) * sizeof (ne10_float32_t)); | 
| robert@464 | 225 	filterIn[0]		= (ne10_float32_t *) NE10_MALLOC (blockSize * sizeof (ne10_float32_t)); | 
| robert@464 | 226 	filterIn[1]		= (ne10_float32_t *) NE10_MALLOC (blockSize * sizeof (ne10_float32_t)); | 
| robert@464 | 227 	filterOut[0]	= (ne10_float32_t *) NE10_MALLOC (blockSize * sizeof (ne10_float32_t)); | 
| robert@464 | 228 	filterOut[1]	= (ne10_float32_t *) NE10_MALLOC (blockSize * sizeof (ne10_float32_t)); | 
| robert@464 | 229 	ne10_fir_init_float(&filter[0], FILTER_TAP_NUM, filterTaps, filterState[0], blockSize); | 
| robert@464 | 230 	ne10_fir_init_float(&filter[1], FILTER_TAP_NUM, filterTaps, filterState[1], blockSize); | 
| robert@464 | 231 | 
| robert@464 | 232 	// peak outputs | 
| robert@464 | 233 	PeakBurst[0].setAttackRate(.00001 * context->analogSampleRate); | 
| robert@464 | 234 	PeakBurst[1].setAttackRate(.00001 * context->analogSampleRate); | 
| robert@464 | 235 	PeakBurst[0].setDecayRate(.5 * context->analogSampleRate); | 
| robert@464 | 236 	PeakBurst[1].setDecayRate(.5 * context->analogSampleRate); | 
| robert@464 | 237 	PeakBurst[0].setSustainLevel(0.0); | 
| robert@464 | 238 	PeakBurst[1].setSustainLevel(0.0); | 
| robert@464 | 239 | 
| robert@464 | 240 	// Initialise auxiliary tasks | 
| robert@464 | 241 	if((gMediumPriorityRender = Bela_createAuxiliaryTask(&render_medium_prio, BELA_AUDIO_PRIORITY - 10, "dbox-calculation-medium")) == 0) | 
| robert@464 | 242 		return false; | 
| robert@464 | 243 	if((gLowPriorityRender = Bela_createAuxiliaryTask(&render_low_prio, BELA_AUDIO_PRIORITY - 15, "dbox-calculation-low")) == 0) | 
| robert@464 | 244 		return false; | 
| robert@464 | 245 | 
| robert@464 | 246 	return true; | 
| robert@464 | 247 } | 
| robert@464 | 248 | 
| robert@464 | 249 void render(BelaContext *context, void *userData) | 
| robert@464 | 250 { | 
| robert@464 | 251 #ifdef DBOX_CAPE_TEST | 
| robert@464 | 252 	render_capetest(numMatrixFrames, numAudioFrames, audioIn, audioOut, matrixIn, matrixOut); | 
| robert@464 | 253 #else | 
| robert@464 | 254 	if(gOscBanks[gCurrentOscBank]->state==bank_toreset) | 
| robert@464 | 255 		gOscBanks[gCurrentOscBank]->resetOscillators(); | 
| robert@464 | 256 | 
| robert@464 | 257 	if(gOscBanks[gCurrentOscBank]->state==bank_playing) | 
| robert@464 | 258 	{ | 
| chris@543 | 259 		assert(context->audioOutChannels == 2); | 
| robert@464 | 260 | 
| robert@464 | 261 #ifdef OLD_OSCBANK | 
| robert@464 | 262 		memset(audioOut, 0, numAudioFrames *  * sizeof(float)); | 
| robert@464 | 263 | 
| robert@464 | 264 		/* Render the oscillator bank. The oscillator bank function is written in NEON assembly | 
| robert@464 | 265 		 * and it strips out all extra checks, so find out in advance whether we can render a whole | 
| robert@464 | 266 		 * block or whether the frame will increment in the middle of this buffer. | 
| robert@464 | 267 		 */ | 
| robert@464 | 268 | 
| robert@464 | 269 		int framesRemaining = numAudioFrames; | 
| robert@464 | 270 		float *audioOutWithOffset = audioOut; | 
| robert@464 | 271 | 
| robert@464 | 272 		while(framesRemaining > 0) { | 
| robert@464 | 273 			if(gOscBanks[gCurrentOscBank]->hopCounter >= framesRemaining) { | 
| robert@464 | 274 				/* More frames left in this hop than we need this time. Render and finish */ | 
| robert@464 | 275 				oscillator_bank_neon(framesRemaining, audioOutWithOffset, | 
| robert@464 | 276 									 gOscBanks[gCurrentOscBank]->actPartNum, gOscBanks[gCurrentOscBank]->lookupTableSize, | 
| robert@464 | 277 									 gOscBanks[gCurrentOscBank]->oscillatorPhases, gOscBanks[gCurrentOscBank]->oscillatorNormFrequencies, | 
| robert@464 | 278 									 gOscBanks[gCurrentOscBank]->oscillatorAmplitudes, | 
| robert@464 | 279 									 gOscBanks[gCurrentOscBank]->oscillatorNormFreqDerivatives, | 
| robert@464 | 280 									 gOscBanks[gCurrentOscBank]->oscillatorAmplitudeDerivatives, | 
| robert@464 | 281 									 gDynamicWavetable/*gOscBanks[gCurrentOscBank]->lookupTable*/); | 
| robert@464 | 282 				gOscBanks[gCurrentOscBank]->hopCounter -= framesRemaining; | 
| robert@464 | 283 				if(gOscBanks[gCurrentOscBank]->hopCounter <= 0) | 
| robert@464 | 284 					gOscBanks[gCurrentOscBank]->nextHop(); | 
| robert@464 | 285 				framesRemaining = 0; | 
| robert@464 | 286 			} | 
| robert@464 | 287 			else { | 
| robert@464 | 288 				/* More frames to render than are left in this hop. Render and decrement the | 
| robert@464 | 289 				 * number of remaining frames; then advance to the next oscillator frame. | 
| robert@464 | 290 				 */ | 
| robert@464 | 291 				oscillator_bank_neon(gOscBanks[gCurrentOscBank]->hopCounter, audioOutWithOffset, | 
| robert@464 | 292 									 gOscBanks[gCurrentOscBank]->actPartNum, gOscBanks[gCurrentOscBank]->lookupTableSize, | 
| robert@464 | 293 									 gOscBanks[gCurrentOscBank]->oscillatorPhases, gOscBanks[gCurrentOscBank]->oscillatorNormFrequencies, | 
| robert@464 | 294 									 gOscBanks[gCurrentOscBank]->oscillatorAmplitudes, | 
| robert@464 | 295 									 gOscBanks[gCurrentOscBank]->oscillatorNormFreqDerivatives, | 
| robert@464 | 296 									 gOscBanks[gCurrentOscBank]->oscillatorAmplitudeDerivatives, | 
| robert@464 | 297 									 gDynamicWavetable/*gOscBanks[gCurrentOscBank]->lookupTable*/); | 
| robert@464 | 298 				framesRemaining -= gOscBanks[gCurrentOscBank]->hopCounter; | 
| robert@464 | 299 				audioOutWithOffset +=  * gOscBanks[gCurrentOscBank]->hopCounter; | 
| robert@464 | 300 				gOscBanks[gCurrentOscBank]->sampleCount += gOscBanks[gCurrentOscBank]->hopCounter; | 
| robert@464 | 301 				gOscBanks[gCurrentOscBank]->nextHop(); | 
| robert@464 | 302 			} | 
| robert@464 | 303 		} | 
| robert@464 | 304 #else | 
| robert@464 | 305 		for(unsigned int n = 0; n < context->audioFrames; n++) { | 
| robert@464 | 306 			context->audioOut[2*n] 	  = gOscillatorBufferRead[gOscillatorBufferReadPointer++]+context->audioIn[2*n]*audioInStatus; | 
| robert@464 | 307 			context->audioOut[2*n + 1] = gOscillatorBufferRead[gOscillatorBufferReadPointer++]+context->audioIn[2*n+1]*audioInStatus; | 
| robert@464 | 308 | 
| robert@464 | 309 			filterIn[0][n] = fabs(context->audioIn[2*n]);	// rectify for peak detection in 1 | 
| robert@464 | 310 			filterIn[1][n] = fabs(context->audioIn[2*n+1]);	// rectify for peak detection in 2 | 
| robert@464 | 311 | 
| robert@464 | 312 			/* FIXME why doesn't this work? */ | 
| robert@464 | 313 			/* | 
| robert@464 | 314 			if(gOscillatorBufferReadPointer == gOscillatorBufferCurrentSize/2) { | 
| robert@464 | 315 				gOscillatorNeedsRender = true; | 
| robert@464 | 316 				scheduleAuxiliaryTask(gLowPriorityRender); | 
| robert@464 | 317 			} */ | 
| robert@464 | 318 | 
| robert@464 | 319 			if(gOscillatorBufferReadPointer >= gOscillatorBufferReadCurrentSize) { | 
| robert@464 | 320 				// Finished reading from the buffer: swap to the next buffer | 
| robert@464 | 321 				if(gOscillatorBufferRead == gOscillatorBuffer1) { | 
| robert@464 | 322 					gOscillatorBufferRead = gOscillatorBuffer2; | 
| robert@464 | 323 					gOscillatorBufferWrite = gOscillatorBuffer1; | 
| robert@464 | 324 				} | 
| robert@464 | 325 				else { | 
| robert@464 | 326 					gOscillatorBufferRead = gOscillatorBuffer1; | 
| robert@464 | 327 					gOscillatorBufferWrite = gOscillatorBuffer2; | 
| robert@464 | 328 				} | 
| robert@464 | 329 | 
| robert@464 | 330 				// New buffer size is whatever finished writing last hop | 
| robert@464 | 331 				gOscillatorBufferReadCurrentSize = gOscillatorBufferWriteCurrentSize; | 
| robert@464 | 332 				gOscillatorBufferReadPointer = 0; | 
| robert@464 | 333 | 
| robert@464 | 334 				gOscillatorNeedsRender = true; | 
| robert@464 | 335 				Bela_scheduleAuxiliaryTask(gMediumPriorityRender); | 
| robert@464 | 336 			} | 
| robert@464 | 337 		} | 
| robert@464 | 338 #endif | 
| robert@464 | 339 	} | 
| robert@464 | 340 	else | 
| robert@464 | 341 	{ | 
| robert@464 | 342 		for(unsigned int n = 0; n < context->audioFrames; n++) { | 
| robert@464 | 343 			context->audioOut[2*n] 	  = context->audioIn[2*n]*audioInStatus; | 
| robert@464 | 344 			context->audioOut[2*n + 1] = context->audioIn[2*n+1]*audioInStatus; | 
| robert@464 | 345 | 
| robert@464 | 346 			filterIn[0][n] = fabs(context->audioIn[2*n]);	// rectify for peak detection in 1 | 
| robert@464 | 347 			filterIn[1][n] = fabs(context->audioIn[2*n+1]);	// rectify for peak detection in 2 | 
| robert@464 | 348 		} | 
| robert@464 | 349 	} | 
| robert@464 | 350 | 
| robert@464 | 351 	// low pass filter audio in 1 and 2 for peak detection | 
| robert@464 | 352 	ne10_fir_float_neon(&filter[0], filterIn[0], filterOut[0], blockSize); | 
| robert@464 | 353 	ne10_fir_float_neon(&filter[1], filterIn[1], filterOut[1], blockSize); | 
| robert@464 | 354 | 
| robert@464 | 355 	for(unsigned int n = 0; n < context->analogFrames; n++) { | 
| robert@464 | 356 | 
| robert@464 | 357 | 
| robert@464 | 358 		/* Matrix Out 0, In 0 | 
| robert@464 | 359 		 * | 
| robert@464 | 360 		 * CV loop | 
| robert@464 | 361 		 * Controls pitch of sound | 
| robert@464 | 362 		 */ | 
| robert@464 | 363 		float touchPosInt = gSensor0LatestTouchPos; | 
| robert@464 | 364 		if(touchPosInt < 0) touchPosInt = 0; | 
| robert@464 | 365 		if(touchPosInt > 1.0) touchPosInt = 1.0; | 
| robert@464 | 366 		context->analogOut[n*8 + DAC_PIN0] = touchPosInt; | 
| robert@464 | 367 | 
| robert@464 | 368 		gPitchLatestInput = context->analogIn[n*8 + ADC_PIN0]; | 
| robert@464 | 369 | 
| robert@464 | 370 | 
| robert@464 | 371 		/* Matrix Out 7 | 
| robert@464 | 372 		 * | 
| robert@464 | 373 		 * Loop feedback with Matrix In 0 | 
| robert@464 | 374 		 * Controls discreet pitch | 
| robert@464 | 375 		 */ | 
| robert@464 | 376 		float deltaTarget = 0; | 
| robert@464 | 377 		int semitoneIndex = 0; | 
| robert@464 | 378 		if(gSensor0LatestTouchNum>0) | 
| robert@464 | 379 		{ | 
| robert@464 | 380 			// current pitch is gPitchLatestInput, already retrieved | 
| robert@464 | 381 			semitoneIndex	= ( gPitchLatestInput * 12 * N_OCT  )+0.5;	// closest semitone | 
| robert@464 | 382 			deltaTarget		= (semitones[semitoneIndex]-gPitchLatestInput);				// delta between pitch and target | 
| robert@464 | 383 			deltaTouch 		+= deltaTarget*(deltaWeightI);								// update feedback [previous + current] | 
| robert@464 | 384 		} | 
| robert@464 | 385 		else | 
| robert@464 | 386 			deltaTouch = 0; | 
| robert@464 | 387 | 
| robert@464 | 388 		float nextOut = touchPosInt  + deltaTarget*deltaWeightP + deltaTouch;			// add feedback to touch -> next out | 
| robert@464 | 389 		if(nextOut < 0) nextOut = 0;												// clamp | 
| robert@464 | 390 		if(nextOut > 1.0) nextOut = 1.0;										// clamp | 
| robert@464 | 391 		context->analogOut[n*8 + DAC_PIN7] = nextOut;										// send next nextOut | 
| robert@464 | 392 | 
| robert@464 | 393 | 
| robert@464 | 394 		/* | 
| robert@464 | 395 		 * Matrix Out 1, In 1 | 
| robert@464 | 396 		 * | 
| robert@464 | 397 		 * Hysteresis (comparator) oscillator | 
| robert@464 | 398 		 * Controls speed of playback | 
| robert@464 | 399 		 */ | 
| robert@464 | 400 		bool wasRising = gSpeedHysteresisOscillatorRising; | 
| robert@464 | 401 		context->analogOut[n*8 + DAC_PIN1] = hysteresis_oscillator(context->analogIn[n*8 + ADC_PIN1], 48000.0/65536.0, | 
| robert@464 | 402 																	16000.0/65536.0, &gSpeedHysteresisOscillatorRising); | 
| robert@464 | 403 | 
| robert@464 | 404 		// Find interval of zero crossing | 
| robert@464 | 405 		if(wasRising && !gSpeedHysteresisOscillatorRising) { | 
| robert@464 | 406 			int interval = gMatrixSampleCount - gSpeedHysteresisLastTrigger; | 
| robert@464 | 407 | 
| robert@464 | 408 			// Interval since last trigger will be the new hop size; calculate to set speed | 
| robert@464 | 409 			if(interval < 1) | 
| robert@464 | 410 				interval = 1; | 
| robert@464 | 411 			//float speed = (float)gOscBanks[gCurrentOscBank]->getHopSize() / (float)interval; | 
| robert@464 | 412 			float speed = 144.0 / interval;	// Normalise to a fixed expected speed | 
| robert@464 | 413 			gOscBanks[gCurrentOscBank]->setSpeed(speed); | 
| robert@464 | 414 | 
| robert@464 | 415 			gSpeedHysteresisLastTrigger = gMatrixSampleCount; | 
| robert@464 | 416 		} | 
| robert@464 | 417 | 
| robert@464 | 418 		/* | 
| robert@464 | 419 		 * Matrix Out 2, In 2 | 
| robert@464 | 420 		 * | 
| robert@464 | 421 		 * Feedback (phase shift) oscillator | 
| robert@464 | 422 		 * Controls wavetable used for oscillator bank | 
| robert@464 | 423 		 */ | 
| robert@464 | 424 | 
| robert@464 | 425 		int tableLength = gFeedbackOscillator.process(context->analogIn[n*8 + ADC_PIN2], &context->analogOut[n*8 + DAC_PIN2]); | 
| robert@464 | 426 		if(tableLength != 0) { | 
| robert@464 | 427 			gFeedbackOscillatorTableLength = tableLength; | 
| robert@464 | 428 			gFeedbackOscillatorTable = gFeedbackOscillator.wavetable(); | 
| robert@464 | 429 			gDynamicWavetableNeedsRender = true; | 
| robert@464 | 430 			Bela_scheduleAuxiliaryTask(gLowPriorityRender); | 
| robert@464 | 431 		} | 
| robert@464 | 432 | 
| robert@464 | 433 		/* | 
| robert@464 | 434 		 * Matrix Out 3, In 3 | 
| robert@464 | 435 		 * | 
| robert@464 | 436 		 * CV loop with delay for time alignment | 
| robert@464 | 437 		 * Touch positions from sensor 1 | 
| robert@464 | 438 		 * Change every 32 samples (ca. 1.5 ms) | 
| robert@464 | 439 		 */ | 
| robert@464 | 440 		volatile int touchCount = gSensor1LatestTouchCount; | 
| robert@464 | 441 		if(touchCount == 0) | 
| robert@464 | 442 			context->analogOut[n*8 + DAC_PIN3] = 0; | 
| robert@464 | 443 		else { | 
| robert@464 | 444 			int touchIndex = (gMatrixSampleCount >> 5) % touchCount; | 
| robert@464 | 445 			context->analogOut[n*8 + DAC_PIN3] = gSensor1LatestTouchPos[touchIndex] * 56000.0f / 65536.0f; | 
| robert@464 | 446 			if(touchIndex != gSensor1LastTouchIndex) { | 
| robert@464 | 447 				// Just changed to a new touch output. Reset the counter. | 
| robert@464 | 448 				// It will take 2*matrixFrames samples for this output to come back to the | 
| robert@464 | 449 				// ADC input. But we also want to read near the end of the 32 sample block; | 
| robert@464 | 450 				// let's say 24 samples into it. | 
| robert@464 | 451 | 
| robert@464 | 452 				// FIXME this won't work for p > 2 | 
| robert@464 | 453 				gSensor1InputDelayCounter = 24 + 2*context->analogFrames; | 
| robert@464 | 454 				gSensor1InputIndex = touchIndex; | 
| robert@464 | 455 			} | 
| robert@464 | 456 			gSensor1LastTouchIndex = touchIndex; | 
| robert@464 | 457 		} | 
| robert@464 | 458 | 
| robert@464 | 459 		if(gSensor1InputDelayCounter-- >= 0 && touchCount > 0) { | 
| robert@464 | 460 			gSensor1MatrixTouchPos[gSensor1InputIndex] = context->analogIn[n*8 + ADC_PIN3]; | 
| robert@464 | 461 		} | 
| robert@464 | 462 | 
| robert@464 | 463 		/* Matrix Out 4 | 
| robert@464 | 464 		 * | 
| robert@464 | 465 		 * Sensor 1 last pos | 
| robert@464 | 466 		 */ | 
| robert@464 | 467 		touchPosInt = gSensor1LatestTouchPos[gSensor1LatestTouchIndex]; | 
| robert@464 | 468 		if(touchPosInt < 0) touchPosInt = 0; | 
| robert@464 | 469 		if(touchPosInt > 1.0) touchPosInt = 1.0; | 
| robert@464 | 470 		context->analogOut[n*8 + DAC_PIN4] = touchPosInt; | 
| robert@464 | 471 | 
| robert@464 | 472 		/* Matrix In 4 | 
| robert@464 | 473 		 * | 
| robert@464 | 474 		 * Loop points selector | 
| robert@464 | 475 		 */ | 
| robert@464 | 476 		gLoopPointsInputBuffer[gLoopPointsInputBufferPointer++] = context->analogIn[n*8 + ADC_PIN4]; | 
| robert@464 | 477 		if(gLoopPointsInputBufferPointer >= gLoopPointsInputBufferSize) { | 
| robert@464 | 478 			// Find min and max values | 
| robert@464 | 479 			float loopMax = 0, loopMin = 1.0; | 
| robert@464 | 480 			for(int i = 0; i < gLoopPointsInputBufferSize; i++) { | 
| robert@464 | 481 				if(gLoopPointsInputBuffer[i] < loopMin) | 
| robert@464 | 482 					loopMin = gLoopPointsInputBuffer[i]; | 
| robert@464 | 483 				if(gLoopPointsInputBuffer[i] > loopMax/* && gLoopPointsInputBuffer[i] != 65535*/) | 
| robert@464 | 484 					loopMax = gLoopPointsInputBuffer[i]; | 
| robert@464 | 485 			} | 
| robert@464 | 486 | 
| robert@464 | 487 			if(loopMin >= loopMax) | 
| robert@464 | 488 				loopMax = loopMin; | 
| robert@464 | 489 | 
| robert@464 | 490 			gLoopPointMax = loopMax; | 
| robert@464 | 491 			gLoopPointMin = loopMin; | 
| robert@464 | 492 			gLoopPointsInputBufferPointer = 0; | 
| robert@464 | 493 		} | 
| robert@464 | 494 | 
| robert@464 | 495 		/* Matrix Out 5 | 
| robert@464 | 496 		 * | 
| robert@464 | 497 		 * Audio In 1 peak detection and peak burst output | 
| robert@464 | 498 		 */ | 
| robert@464 | 499 | 
| robert@464 | 500 		filterOut[0][n*2+1]	*= filterGain; | 
| robert@464 | 501 		float burstOut		= PeakBurst[0].getOutput(); | 
| robert@464 | 502 		if( burstOut < 0.1) | 
| robert@464 | 503 		{ | 
| robert@464 | 504 			if( (prevFiltered[0]>=peakThresh) && (prevFiltered[0]>=filterOut[0][n*2+1]) ) | 
| robert@464 | 505 			{ | 
| robert@464 | 506 				peak[0] = prevFiltered[0]; | 
| robert@464 | 507 				PeakBurst[0].gate(1); | 
| robert@464 | 508 			} | 
| robert@464 | 509 		} | 
| robert@464 | 510 | 
| robert@464 | 511 		PeakBurst[0].process(1); | 
| robert@464 | 512 | 
| robert@464 | 513 		float convAudio = burstOut*peak[0]; | 
| robert@464 | 514 		context->analogOut[n*8 + DAC_PIN5] = convAudio; | 
| robert@464 | 515 		prevFiltered[0] = filterOut[0][n*2+1]; | 
| robert@464 | 516 		if(prevFiltered[0]>1) | 
| robert@464 | 517 			prevFiltered[0] = 1; | 
| robert@464 | 518 | 
| robert@464 | 519 		/* Matrix In 5 | 
| robert@464 | 520 		 * | 
| robert@464 | 521 		 * Dissonance, via changing frequency motion of partials | 
| robert@464 | 522 		 */ | 
| robert@464 | 523 		float amount = (float)context->analogIn[n*8 + ADC_PIN5]; | 
| robert@464 | 524 		gOscBanks[gCurrentOscBank]->freqMovement = 1.0 - amount; | 
| robert@464 | 525 | 
| robert@464 | 526 | 
| robert@464 | 527 | 
| robert@464 | 528 | 
| robert@464 | 529 		/* Matrix Out 6 | 
| robert@464 | 530 		 * | 
| robert@464 | 531 		 * Audio In 2 peak detection and peak burst output | 
| robert@464 | 532 		 */ | 
| robert@464 | 533 | 
| robert@464 | 534 		filterOut[1][n*2+1]	*= filterGain; | 
| robert@464 | 535 		burstOut			= PeakBurst[1].getOutput(); | 
| robert@464 | 536 		if( burstOut < 0.1) | 
| robert@464 | 537 		{ | 
| robert@464 | 538 			if( (prevFiltered[1]>=peakThresh) && (prevFiltered[1]>=filterOut[1][n*2+1]) ) | 
| robert@464 | 539 			{ | 
| robert@464 | 540 				peak[1] = prevFiltered[1]; | 
| robert@464 | 541 				PeakBurst[1].gate(1); | 
| robert@464 | 542 			} | 
| robert@464 | 543 		} | 
| robert@464 | 544 | 
| robert@464 | 545 		PeakBurst[1].process(1); | 
| robert@464 | 546 | 
| robert@464 | 547 		convAudio = burstOut*peak[1]; | 
| robert@464 | 548 		context->analogOut[n*8 + DAC_PIN6] = convAudio; | 
| robert@464 | 549 		prevFiltered[1] = filterOut[1][n*2+1]; | 
| robert@464 | 550 		if(prevFiltered[1]>1) | 
| robert@464 | 551 			prevFiltered[1] = 1; | 
| robert@464 | 552 | 
| robert@464 | 553 		/* Matrix In 6 | 
| robert@464 | 554 		 * | 
| robert@464 | 555 		 * Sound selector | 
| robert@464 | 556 		 */ | 
| robert@464 | 557 		if(!gIsLoading) { | 
| robert@464 | 558 			// Use hysteresis to avoid jumping back and forth between sounds | 
| robert@464 | 559 			if(gOscBanks.size() > 1) { | 
| robert@464 | 560 				float input = context->analogIn[n*8 + ADC_PIN6]; | 
| robert@464 | 561 				const float hystValue = 16000.0 / 65536.0; | 
| robert@464 | 562 | 
| robert@464 | 563 				float upHysteresisValue = ((gCurrentOscBank + 1) + hystValue) / gOscBanks.size(); | 
| robert@464 | 564 				float downHysteresisValue = (gCurrentOscBank - hystValue) / gOscBanks.size(); | 
| robert@464 | 565 | 
| robert@464 | 566 				if(input > upHysteresisValue || input < downHysteresisValue) { | 
| robert@464 | 567 					gNextOscBank = input * gOscBanks.size(); | 
| robert@464 | 568 					if(gNextOscBank < 0) | 
| robert@464 | 569 						gNextOscBank = 0; | 
| robert@464 | 570 					if((unsigned)gNextOscBank >= gOscBanks.size()) | 
| robert@464 | 571 						gNextOscBank = gOscBanks.size() - 1; | 
| robert@464 | 572 				} | 
| robert@464 | 573 			} | 
| robert@464 | 574 		} | 
| robert@464 | 575 | 
| robert@464 | 576 		/* | 
| robert@464 | 577 		 * Matrix In 7 | 
| robert@464 | 578 		 * | 
| robert@464 | 579 		 * FSR from primary touch sensor | 
| robert@464 | 580 		 * Value ranges from 0-1799 | 
| robert@464 | 581 		 */ | 
| robert@464 | 582 		gLastFSRValue = context->analogIn[n*8 + ADC_PIN7] * 1799.0; | 
| robert@464 | 583 		//gLastFSRValue = 1799 - context->analogIn[n*8 + ADC_PIN7] * (1799.0 / 65535.0); | 
| robert@464 | 584 		//dbox_printf("%i\n",gLastFSRValue); | 
| robert@464 | 585 | 
| robert@464 | 586 		gMatrixSampleCount++; | 
| robert@464 | 587 	} | 
| robert@464 | 588 | 
| robert@464 | 589 #endif /* DBOX_CAPE_TEST */ | 
| robert@464 | 590 } | 
| robert@464 | 591 | 
| robert@464 | 592 // Medium-priority render function used for audio hop calculations | 
| robert@464 | 593 void render_medium_prio() | 
| robert@464 | 594 { | 
| robert@464 | 595 | 
| robert@464 | 596 	if(gOscillatorNeedsRender) { | 
| robert@464 | 597 		gOscillatorNeedsRender = false; | 
| robert@464 | 598 | 
| robert@464 | 599 		/* Render one frame into the write buffer */ | 
| robert@464 | 600 		memset(gOscillatorBufferWrite, 0, gOscBanks[gCurrentOscBank]->hopCounter * 2 * sizeof(float)); /* assumes 2 audio channels */ | 
| robert@464 | 601 | 
| robert@464 | 602 		oscillator_bank_neon(gOscBanks[gCurrentOscBank]->hopCounter, gOscillatorBufferWrite, | 
| robert@464 | 603 							 gOscBanks[gCurrentOscBank]->actPartNum, gOscBanks[gCurrentOscBank]->lookupTableSize, | 
| robert@464 | 604 							 gOscBanks[gCurrentOscBank]->oscillatorPhases, gOscBanks[gCurrentOscBank]->oscillatorNormFrequencies, | 
| robert@464 | 605 							 gOscBanks[gCurrentOscBank]->oscillatorAmplitudes, | 
| robert@464 | 606 							 gOscBanks[gCurrentOscBank]->oscillatorNormFreqDerivatives, | 
| robert@464 | 607 							 gOscBanks[gCurrentOscBank]->oscillatorAmplitudeDerivatives, | 
| robert@464 | 608 							 /*gOscBanks[gCurrentOscBank]->lookupTable*/gDynamicWavetable); | 
| robert@464 | 609 | 
| robert@464 | 610 		gOscillatorBufferWriteCurrentSize = gOscBanks[gCurrentOscBank]->hopCounter * 2; | 
| robert@464 | 611 | 
| robert@464 | 612 		/* Update the pitch right before the hop | 
| robert@464 | 613 		 * Total CV range +/- N_OCT octaves | 
| robert@464 | 614 		 */ | 
| robert@464 | 615 		float pitch = (float)gPitchLatestInput / octaveSplitter - N_OCT/2; | 
| robert@464 | 616 		//gOscBanks[gCurrentOscBank]->pitchMultiplier = powf(2.0f, pitch); | 
| robert@464 | 617 		gOscBanks[gCurrentOscBank]->pitchMultiplier = pow(2.0f, pitch); | 
| robert@464 | 618 | 
| robert@464 | 619 #ifdef FIXME_LATER // This doesn't work very well yet | 
| robert@464 | 620 		gOscBanks[gCurrentOscBank]->filterNum = gSensor1LatestTouchCount; | 
| robert@464 | 621 		float freqScaler = gOscBanks[gCurrentOscBank]->getFrequencyScaler(); | 
| robert@464 | 622 		for(int i=0; i < gOscBanks[gCurrentOscBank]->filterNum; i++) | 
| robert@464 | 623 		{ | 
| robert@464 | 624 			// touch pos is linear but freqs are log | 
| robert@464 | 625 			gOscBanks[gCurrentOscBank]->filterFreqs[i] = ((expf(gSensor1MatrixTouchPos[i]*4)-1)/(expf(4)-1))*gOscBanks[gCurrentOscBank]->filterMaxF*freqScaler; | 
| robert@464 | 626 			gOscBanks[gCurrentOscBank]->filterQ[i] = gSensor1LatestTouchSizes[i]; | 
| robert@464 | 627 			if(gOscBanks[gCurrentOscBank]->filterFreqs[i]>500*freqScaler) | 
| robert@464 | 628 				gOscBanks[gCurrentOscBank]->filterPadding[i] = 1+100000*( (gOscBanks[gCurrentOscBank]->filterFreqs[i]-500*freqScaler)/(gOscBanks[gCurrentOscBank]->filterMaxF-500)*freqScaler ); | 
| robert@464 | 629 			else | 
| robert@464 | 630 				gOscBanks[gCurrentOscBank]->filterPadding[i] = 1; | 
| robert@464 | 631 		} | 
| robert@464 | 632 #endif | 
| robert@464 | 633 | 
| robert@464 | 634 		RTIME ticks		= rt_timer_read(); | 
| robert@464 | 635 		SRTIME ns		= rt_timer_tsc2ns(ticks); | 
| robert@464 | 636 		SRTIME delta 	= ns-prevChangeNs; | 
| robert@464 | 637 | 
| robert@464 | 638 		// switch to next bank cannot be too frequent, to avoid seg fault! [for example sef fault happens when removing both VDD and GND from breadboard] | 
| robert@464 | 639 		if(gNextOscBank != gCurrentOscBank && delta>100000000) { | 
| robert@464 | 640 | 
| robert@464 | 641 			/*printf("ticks %llu\n", (unsigned long long)ticks); | 
| robert@464 | 642 			printf("ns %llu\n", (unsigned long long)ns); | 
| robert@464 | 643 			printf("prevChangeNs %llu\n", (unsigned long long)prevChangeNs); | 
| robert@464 | 644 			printf("-------------------------->%llud\n", (unsigned long long)(ns-prevChangeNs));*/ | 
| robert@464 | 645 | 
| robert@464 | 646 			prevChangeNs = ns; | 
| robert@464 | 647 			dbox_printf("Changing to bank %d...\n", gNextOscBank); | 
| robert@464 | 648 			if(gOscBanks[gCurrentOscBank]->state==bank_playing){ | 
| robert@464 | 649 				gOscBanks[gCurrentOscBank]->stop(); | 
| robert@464 | 650 			} | 
| robert@464 | 651 | 
| robert@464 | 652 			gCurrentOscBank = gNextOscBank; | 
| robert@464 | 653 			gOscBanks[gCurrentOscBank]->hopNumTh = 0; | 
| robert@464 | 654 		} | 
| robert@464 | 655 		else { | 
| robert@464 | 656 			/* Advance to the next oscillator frame */ | 
| robert@464 | 657 			gOscBanks[gCurrentOscBank]->nextHop(); | 
| robert@464 | 658 		} | 
| robert@464 | 659 	} | 
| robert@464 | 660 } | 
| robert@464 | 661 | 
| robert@464 | 662 // Lower-priority render function which performs matrix calculations | 
| robert@464 | 663 // State should be transferred in via global variables | 
| robert@464 | 664 void render_low_prio() | 
| robert@464 | 665 { | 
| robert@464 | 666 	gPRU->setGPIOTestPin(); | 
| robert@464 | 667 	if(gDynamicWavetableNeedsRender) { | 
| robert@464 | 668 		// Find amplitude of wavetable | 
| robert@464 | 669 		float meanAmplitude = 0; | 
| robert@464 | 670 		float sineMix; | 
| robert@464 | 671 | 
| robert@464 | 672 		for(int i = 0; i < gFeedbackOscillatorTableLength; i++) { | 
| robert@464 | 673 			//meanAmplitude += fabsf(gFeedbackOscillatorTable[i]); | 
| robert@464 | 674 			meanAmplitude += fabs(gFeedbackOscillatorTable[i]); | 
| robert@464 | 675 		} | 
| robert@464 | 676 		meanAmplitude /= (float)gFeedbackOscillatorTableLength; | 
| robert@464 | 677 | 
| robert@464 | 678 		if(meanAmplitude > 0.35) | 
| robert@464 | 679 			sineMix = 0; | 
| robert@464 | 680 		else | 
| robert@464 | 681 			sineMix = (.35 - meanAmplitude) / .35; | 
| robert@464 | 682 | 
| robert@464 | 683 		//dbox_printf("amp %f mix %f\n", meanAmplitude, sineMix); | 
| robert@464 | 684 | 
| robert@464 | 685 		// Copy to main wavetable | 
| robert@464 | 686 		wavetable_interpolate(gFeedbackOscillatorTableLength, gDynamicWavetableLength, | 
| robert@464 | 687 				gFeedbackOscillatorTable, gDynamicWavetable, | 
| robert@464 | 688 				gOscBanks[gCurrentOscBank]->lookupTable, sineMix); | 
| robert@464 | 689 	} | 
| robert@464 | 690 | 
| robert@464 | 691 	if(gLoopPointMin >= 60000.0/65536.0 && gLoopPointMax >= 60000.0/65536.0) { | 
| robert@464 | 692 		// KLUDGE! | 
| robert@464 | 693 		if(gCurrentOscBank == 0) | 
| robert@464 | 694 			gOscBanks[gCurrentOscBank]->setLoopHops(50, ((float)gOscBanks[gCurrentOscBank]->getLastHop() * 0.6) - 1); | 
| robert@464 | 695 		else | 
| robert@464 | 696 			gOscBanks[gCurrentOscBank]->setLoopHops(5, ((float)gOscBanks[gCurrentOscBank]->getLastHop() * 0.7) - 1); | 
| robert@464 | 697 	} | 
| robert@464 | 698 	else { | 
| robert@464 | 699 		float normLoopPointMin = (float)gLoopPointMin * gOscBanks[gCurrentOscBank]->getLastHop(); | 
| robert@464 | 700 		float normLoopPointMax = (float)gLoopPointMax * gOscBanks[gCurrentOscBank]->getLastHop(); | 
| robert@464 | 701 | 
| robert@464 | 702 		int intLoopPointMin = normLoopPointMin; | 
| robert@464 | 703 		if(intLoopPointMin < 1) | 
| robert@464 | 704 			intLoopPointMin = 1; | 
| robert@464 | 705 		int intLoopPointMax = normLoopPointMax; | 
| robert@464 | 706 		if(intLoopPointMax <= intLoopPointMin) | 
| robert@464 | 707 			intLoopPointMax = intLoopPointMin + 1; | 
| robert@464 | 708 		if(intLoopPointMax > gOscBanks[gCurrentOscBank]->getLastHop() - 1) | 
| robert@464 | 709 			intLoopPointMax =  gOscBanks[gCurrentOscBank]->getLastHop() - 1; | 
| robert@464 | 710 | 
| robert@464 | 711 		//dbox_printf("Loop points %d-%d / %d-%d\n", gLoopPointMin, gLoopPointMax, intLoopPointMin, intLoopPointMax); | 
| robert@464 | 712 | 
| robert@464 | 713 		/* WORKS, jsut need to fix the glitch when jumps! | 
| robert@464 | 714 		 * *int currentHop = gOscBanks[gCurrentOscBank]->getCurrentHop(); | 
| robert@464 | 715 		if(currentHop < intLoopPointMin -1 ) | 
| robert@464 | 716 			gOscBanks[gCurrentOscBank]->setJumpHop(intLoopPointMin + 1); | 
| robert@464 | 717 		else if(currentHop > intLoopPointMax + 1) | 
| robert@464 | 718 			gOscBanks[gCurrentOscBank]->setJumpHop(intLoopPointMax - 1);*/ | 
| robert@464 | 719 		gOscBanks[gCurrentOscBank]->setLoopHops(intLoopPointMin, intLoopPointMax); | 
| robert@464 | 720 	} | 
| robert@464 | 721 | 
| robert@464 | 722 	if(gIsLoading) | 
| robert@464 | 723 		gStatusLED.blink(25, 75);	// Blink quickly until load finished | 
| robert@464 | 724 	else | 
| robert@464 | 725 		gStatusLED.blink(250 / gOscBanks[gCurrentOscBank]->getSpeed(), 250 / gOscBanks[gCurrentOscBank]->getSpeed()); | 
| robert@464 | 726 	gPRU->clearGPIOTestPin(); | 
| robert@464 | 727 | 
| robert@464 | 728 //	static int counter = 32; | 
| robert@464 | 729 //	if(--counter == 0) { | 
| robert@464 | 730 //		for(int i = 0; i < gLoopPointsInputBufferSize; i++) { | 
| robert@464 | 731 //			dbox_printf("%d ", gLoopPointsInputBuffer[i]); | 
| robert@464 | 732 //			if(i % 32 == 31) | 
| robert@464 | 733 //				dbox_printf("\n"); | 
| robert@464 | 734 //		} | 
| robert@464 | 735 //		dbox_printf("\n\n"); | 
| robert@464 | 736 //		counter = 32; | 
| robert@464 | 737 //	} | 
| robert@464 | 738 | 
| robert@464 | 739 	//dbox_printf("min %d max %d\n", gLoopPointMin, gLoopPointMax); | 
| robert@464 | 740 } | 
| robert@464 | 741 | 
| robert@464 | 742 // Clean up at the end of render | 
| robert@464 | 743 void cleanup(BelaContext *context, void *userData) | 
| robert@464 | 744 { | 
| robert@464 | 745 	free(gOscillatorBuffer1); | 
| robert@464 | 746 	free(gOscillatorBuffer2); | 
| robert@464 | 747 	free(gDynamicWavetable); | 
| robert@464 | 748 } | 
| robert@464 | 749 | 
| robert@464 | 750 // Interpolate one wavetable into another. The output size | 
| robert@464 | 751 // does not include the guard point at the end which will be identical | 
| robert@464 | 752 // to the first point | 
| robert@464 | 753 void wavetable_interpolate(int numSamplesIn, int numSamplesOut, | 
| robert@464 | 754                            float *tableIn, float *tableOut, | 
| robert@464 | 755                            float *sineTable, float sineMix) | 
| robert@464 | 756 { | 
| robert@464 | 757 	float fractionalScaler = (float)numSamplesIn / (float)numSamplesOut; | 
| robert@464 | 758 | 
| robert@464 | 759 	for(int k = 0; k < numSamplesOut; k++) { | 
| robert@464 | 760 		float fractionalIndex = (float) k * fractionalScaler; | 
| robert@464 | 761 		//int sB = (int)floorf(fractionalIndex); | 
| robert@464 | 762 		int sB = (int)floor(fractionalIndex); | 
| robert@464 | 763 		int sA = sB + 1; | 
| robert@464 | 764 		if(sA >= numSamplesIn) | 
| robert@464 | 765 			sA = 0; | 
| robert@464 | 766 		float fraction = fractionalIndex - sB; | 
| robert@464 | 767 		tableOut[k] = fraction * tableIn[sA] + (1.0f - fraction) * tableIn[sB]; | 
| robert@464 | 768 		tableOut[k] = sineMix * sineTable[k] + (1.0 - sineMix) * tableOut[k]; | 
| robert@464 | 769 	} | 
| robert@464 | 770 | 
| robert@464 | 771 	tableOut[numSamplesOut] = tableOut[0]; | 
| robert@464 | 772 } | 
| robert@464 | 773 | 
| robert@464 | 774 // Create a hysteresis oscillator with a matrix input and output | 
| robert@464 | 775 inline float hysteresis_oscillator(float input, float risingThreshold, float fallingThreshold, bool *rising) | 
| robert@464 | 776 { | 
| robert@464 | 777 	float value; | 
| robert@464 | 778 | 
| robert@464 | 779 	if(*rising) { | 
| robert@464 | 780 		if(input > risingThreshold) { | 
| robert@464 | 781 			*rising = false; | 
| robert@464 | 782 			value = 0; | 
| robert@464 | 783 		} | 
| robert@464 | 784 		else | 
| robert@464 | 785 			value = 1.0; | 
| robert@464 | 786 	} | 
| robert@464 | 787 	else { | 
| robert@464 | 788 		if(input < fallingThreshold) { | 
| robert@464 | 789 			*rising = true; | 
| robert@464 | 790 			value = 1.0; | 
| robert@464 | 791 		} | 
| robert@464 | 792 		else | 
| robert@464 | 793 			value = 0; | 
| robert@464 | 794 	} | 
| robert@464 | 795 | 
| robert@464 | 796 	return value; | 
| robert@464 | 797 } | 
| robert@464 | 798 | 
| robert@464 | 799 #ifdef DBOX_CAPE_TEST | 
| robert@464 | 800 // Test the functionality of the D-Box cape by checking each input and output | 
| robert@464 | 801 // Loopback cable from ADC to DAC needed | 
| robert@464 | 802 void render_capetest(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut, | 
| robert@464 | 803 			uint16_t *matrixIn, uint16_t *matrixOut) | 
| robert@464 | 804 { | 
| robert@464 | 805 	static float phase = 0.0; | 
| robert@464 | 806 	static int sampleCounter = 0; | 
| robert@464 | 807 	static int invertChannel = 0; | 
| robert@464 | 808 | 
| robert@464 | 809 	// Play a sine wave on the audio output | 
| robert@464 | 810 	for(int n = 0; n < numAudioFrames; n++) { | 
| robert@464 | 811 		audioOut[2*n] = audioOut[2*n + 1] = 0.5*sinf(phase); | 
| robert@464 | 812 		phase += 2.0 * M_PI * 440.0 / 44100.0; | 
| robert@464 | 813 		if(phase >= 2.0 * M_PI) | 
| robert@464 | 814 			phase -= 2.0 * M_PI; | 
| robert@464 | 815 	} | 
| robert@464 | 816 | 
| robert@464 | 817 	for(int n = 0; n < numMatrixFrames; n++) { | 
| robert@464 | 818 		// Change outputs every 512 samples | 
| robert@464 | 819 		if(sampleCounter < 512) { | 
| robert@464 | 820 			for(int k = 0; k < 8; k++) { | 
| robert@464 | 821 				if(k == invertChannel) | 
| robert@464 | 822 					matrixOut[n*8 + k] = 50000; | 
| robert@464 | 823 				else | 
| robert@464 | 824 					matrixOut[n*8 + k] = 0; | 
| robert@464 | 825 			} | 
| robert@464 | 826 		} | 
| robert@464 | 827 		else { | 
| robert@464 | 828 			for(int k = 0; k < 8; k++) { | 
| robert@464 | 829 				if(k == invertChannel) | 
| robert@464 | 830 					matrixOut[n*8 + k] = 0; | 
| robert@464 | 831 				else | 
| robert@464 | 832 					matrixOut[n*8 + k] = 50000; | 
| robert@464 | 833 			} | 
| robert@464 | 834 		} | 
| robert@464 | 835 | 
| robert@464 | 836 		// Read after 256 samples: input should be low | 
| robert@464 | 837 		if(sampleCounter == 256) { | 
| robert@464 | 838 			for(int k = 0; k < 8; k++) { | 
| robert@464 | 839 				if(k == invertChannel) { | 
| robert@464 | 840 					if(matrixIn[n*8 + k] < 50000) { | 
| robert@464 | 841 						dbox_printf("FAIL channel %d -- output HIGH input %d (inverted)\n", k, matrixIn[n*8 + k]); | 
| robert@464 | 842 					} | 
| robert@464 | 843 				} | 
| robert@464 | 844 				else { | 
| robert@464 | 845 					if(matrixIn[n*8 + k] > 2048) { | 
| robert@464 | 846 						dbox_printf("FAIL channel %d -- output LOW input %d\n", k, matrixIn[n*8 + k]); | 
| robert@464 | 847 					} | 
| robert@464 | 848 				} | 
| robert@464 | 849 			} | 
| robert@464 | 850 		} | 
| robert@464 | 851 		else if(sampleCounter == 768) { | 
| robert@464 | 852 			for(int k = 0; k < 8; k++) { | 
| robert@464 | 853 				if(k == invertChannel) { | 
| robert@464 | 854 					if(matrixIn[n*8 + k] > 2048) { | 
| robert@464 | 855 						dbox_printf("FAIL channel %d -- output LOW input %d (inverted)\n", k, matrixIn[n*8 + k]); | 
| robert@464 | 856 					} | 
| robert@464 | 857 				} | 
| robert@464 | 858 				else { | 
| robert@464 | 859 					if(matrixIn[n*8 + k] < 50000) { | 
| robert@464 | 860 						dbox_printf("FAIL channel %d -- output HIGH input %d\n", k, matrixIn[n*8 + k]); | 
| robert@464 | 861 					} | 
| robert@464 | 862 				} | 
| robert@464 | 863 			} | 
| robert@464 | 864 		} | 
| robert@464 | 865 | 
| robert@464 | 866 		if(++sampleCounter >= 1024) { | 
| robert@464 | 867 			sampleCounter = 0; | 
| robert@464 | 868 			invertChannel++; | 
| robert@464 | 869 			if(invertChannel >= 8) | 
| robert@464 | 870 				invertChannel = 0; | 
| robert@464 | 871 		} | 
| robert@464 | 872 	} | 
| robert@464 | 873 } | 
| robert@464 | 874 #endif | 
| robert@464 | 875 | 
| robert@464 | 876 |