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