annotate examples/d-box/render.cpp @ 419:4fb06ca94e02 prerelease

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