annotate projects/d-box/render.cpp @ 39:638bc1ae2500 staging

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