annotate projects/d-box/render.cpp @ 12:a6beeba3a648

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