annotate projects/d-box/render.cpp @ 151:e9c9404e3d1f ClockSync

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