comparison projects/d-box/render.cpp @ 0:8a575ba3ab52

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