annotate examples/11-Extras/cape-test/render.cpp @ 556:ce391098f321 prerelease tip

THIS PROJECT HAS MOVED TO https://github.com/BelaPlatform/bela
author Giulio Moro <giuliomoro@yahoo.it>
date Sat, 25 Jun 2016 20:21:00 +0100
parents 8fcfbfb32aa0
children
rev   line source
robert@464 1 /*
robert@464 2 * render.cpp
robert@464 3 *
robert@464 4 * Created on: Oct 24, 2014
robert@464 5 * Author: parallels
robert@464 6 */
robert@464 7
robert@464 8
robert@464 9 #include <Bela.h>
robert@464 10 #include <cmath>
robert@464 11
robert@464 12 #define ANALOG_LOW (2048.0 / 65536.0)
robert@464 13 #define ANALOG_HIGH (50000.0 / 65536.0)
robert@464 14
robert@464 15 const int gDACPinOrder[] = {6, 4, 2, 0, 1, 3, 5, 7};
robert@464 16
robert@464 17 enum {
robert@464 18 kStateTestingAudioLeft = 0,
robert@464 19 kStateTestingAudioRight,
robert@464 20 kStateTestingAudioDone
robert@464 21 };
robert@464 22
robert@464 23 uint64_t gLastErrorFrame = 0;
robert@464 24 uint32_t gEnvelopeSampleCount = 0;
robert@464 25 float gEnvelopeValueL = 0.5, gEnvelopeValueR = 0.5;
robert@464 26 float gEnvelopeDecayRate = 0.9995;
robert@464 27 int gEnvelopeLastChannel = 0;
robert@464 28
robert@464 29 float gPositivePeakLevels[2] = {0, 0};
robert@464 30 float gNegativePeakLevels[2] = {0, 0};
robert@464 31 float gPeakLevelDecayRate = 0.999;
robert@464 32 const float gPeakLevelLowThreshold = 0.02;
robert@464 33 const float gPeakLevelHighThreshold = 0.2;
robert@464 34 const float gDCOffsetThreshold = 0.1;
robert@464 35 int gAudioTestState = kStateTestingAudioLeft;
robert@464 36 int gAudioTestStateSampleCount = 0;
robert@464 37 int gAudioTestSuccessCounter = 0;
robert@464 38 const int gAudioTestSuccessCounterThreshold = 64;
robert@464 39 const int gAudioTestStateSampleThreshold = 16384;
robert@464 40
robert@464 41 // setup() is called once before the audio rendering starts.
robert@464 42 // Use it to perform any initialisation and allocation which is dependent
robert@464 43 // on the period size or sample rate.
robert@464 44 //
robert@464 45 // userData holds an opaque pointer to a data structure that was passed
robert@464 46 // in from the call to initAudio().
robert@464 47 //
robert@464 48 // Return true on success; returning false halts the program.
robert@464 49
robert@464 50 bool setup(BelaContext *context, void *userData)
robert@464 51 {
robert@464 52 return true;
robert@464 53 }
robert@464 54
robert@464 55 // render() is called regularly at the highest priority by the audio engine.
robert@464 56 // Input and output are given from the audio hardware and the other
robert@464 57 // ADCs and DACs (if available). If only audio is available, numMatrixFrames
robert@464 58 // will be 0.
robert@464 59
robert@464 60 void render(BelaContext *context, void *userData)
robert@464 61 {
robert@464 62 static float phase = 0.0;
robert@464 63 static int sampleCounter = 0;
robert@464 64 static int invertChannel = 0;
robert@464 65 float frequency = 0;
robert@464 66
robert@464 67 // Play a sine wave on the audio output
robert@464 68 for(unsigned int n = 0; n < context->audioFrames; n++) {
robert@464 69
robert@464 70 // Peak detection on the audio inputs, with offset to catch
robert@464 71 // DC errors
robert@464 72 for(int ch = 0; ch < 2; ch++) {
robert@464 73 if(context->audioIn[2*n + ch] > gPositivePeakLevels[ch])
robert@464 74 gPositivePeakLevels[ch] = context->audioIn[2*n + ch];
robert@464 75 gPositivePeakLevels[ch] += 0.1;
robert@464 76 gPositivePeakLevels[ch] *= gPeakLevelDecayRate;
robert@464 77 gPositivePeakLevels[ch] -= 0.1;
robert@464 78 if(context->audioIn[2*n + ch] < gNegativePeakLevels[ch])
robert@464 79 gNegativePeakLevels[ch] = context->audioIn[2*n + ch];
robert@464 80 gNegativePeakLevels[ch] -= 0.1;
robert@464 81 gNegativePeakLevels[ch] *= gPeakLevelDecayRate;
robert@464 82 gNegativePeakLevels[ch] += 0.1;
robert@464 83 }
robert@464 84
robert@464 85 if(gAudioTestState == kStateTestingAudioLeft) {
robert@464 86 context->audioOut[2*n] = 0.2 * sinf(phase);
robert@464 87 context->audioOut[2*n + 1] = 0;
robert@464 88
robert@464 89 frequency = 3000.0;
robert@464 90 phase += 2.0 * M_PI * frequency / 44100.0;
robert@464 91 if(phase >= 2.0 * M_PI)
robert@464 92 phase -= 2.0 * M_PI;
robert@464 93
robert@464 94 gAudioTestStateSampleCount++;
robert@464 95 if(gAudioTestStateSampleCount >= gAudioTestStateSampleThreshold) {
robert@464 96 // Check if we have the expected input: signal on the left but not
robert@464 97 // on the right. Also check that there is not too much DC offset on the
robert@464 98 // inactive signal
robert@464 99 if((gPositivePeakLevels[0] - gNegativePeakLevels[0]) >= gPeakLevelHighThreshold
robert@464 100 && (gPositivePeakLevels[1] - gNegativePeakLevels[1]) <= gPeakLevelLowThreshold &&
robert@464 101 fabsf(gPositivePeakLevels[1]) < gDCOffsetThreshold &&
robert@464 102 fabsf(gNegativePeakLevels[1]) < gDCOffsetThreshold) {
robert@464 103 // Successful test: increment counter
robert@464 104 gAudioTestSuccessCounter++;
robert@464 105 if(gAudioTestSuccessCounter >= gAudioTestSuccessCounterThreshold) {
robert@464 106 gAudioTestState = kStateTestingAudioRight;
robert@464 107 gAudioTestStateSampleCount = 0;
robert@464 108 gAudioTestSuccessCounter = 0;
robert@464 109 }
robert@464 110
robert@464 111 }
robert@464 112 else {
robert@464 113 if(!((context->audioFramesElapsed + n) % 22050)) {
robert@464 114 // Debugging print messages
robert@464 115 if((gPositivePeakLevels[0] - gNegativePeakLevels[0]) < gPeakLevelHighThreshold)
robert@464 116 rt_printf("Left Audio In FAIL: insufficient signal: %f\n",
robert@464 117 gPositivePeakLevels[0] - gNegativePeakLevels[0]);
robert@464 118 else if(gPositivePeakLevels[1] - gNegativePeakLevels[1] > gPeakLevelLowThreshold)
robert@464 119 rt_printf("Right Audio In FAIL: signal present when it should not be: %f\n",
robert@464 120 gPositivePeakLevels[1] - gNegativePeakLevels[1]);
robert@464 121 else if(fabsf(gPositivePeakLevels[1]) >= gDCOffsetThreshold ||
robert@464 122 fabsf(gNegativePeakLevels[1]) >= gDCOffsetThreshold)
robert@464 123 rt_printf("Right Audio In FAIL: DC offset: (%f, %f)\n",
robert@464 124 gPositivePeakLevels[1], gNegativePeakLevels[1]);
robert@464 125 }
robert@464 126 gAudioTestSuccessCounter--;
robert@464 127 if(gAudioTestSuccessCounter <= 0)
robert@464 128 gAudioTestSuccessCounter = 0;
robert@464 129 }
robert@464 130 }
robert@464 131 }
robert@464 132 else if(gAudioTestState == kStateTestingAudioRight) {
robert@464 133 context->audioOut[2*n] = 0;
robert@464 134 context->audioOut[2*n + 1] = 0.2 * sinf(phase);
robert@464 135
robert@464 136 frequency = 3000.0;
robert@464 137 phase += 2.0 * M_PI * frequency / 44100.0;
robert@464 138 if(phase >= 2.0 * M_PI)
robert@464 139 phase -= 2.0 * M_PI;
robert@464 140
robert@464 141 gAudioTestStateSampleCount++;
robert@464 142 if(gAudioTestStateSampleCount >= gAudioTestStateSampleThreshold) {
robert@464 143 // Check if we have the expected input: signal on the left but not
robert@464 144 // on the right
robert@464 145 if((gPositivePeakLevels[1] - gNegativePeakLevels[1]) >= gPeakLevelHighThreshold
robert@464 146 && (gPositivePeakLevels[0] - gNegativePeakLevels[0]) <= gPeakLevelLowThreshold &&
robert@464 147 fabsf(gPositivePeakLevels[0]) < gDCOffsetThreshold &&
robert@464 148 fabsf(gNegativePeakLevels[0]) < gDCOffsetThreshold) {
robert@464 149 // Successful test: increment counter
robert@464 150 gAudioTestSuccessCounter++;
robert@464 151 if(gAudioTestSuccessCounter >= gAudioTestSuccessCounterThreshold) {
robert@464 152 gAudioTestSuccessCounter = 0;
robert@464 153 gAudioTestStateSampleCount = 0;
robert@464 154 gAudioTestState = kStateTestingAudioDone;
robert@464 155 }
robert@464 156 }
robert@464 157 else {
robert@464 158 if(!((context->audioFramesElapsed + n) % 22050)) {
robert@464 159 // Debugging print messages
robert@464 160 if((gPositivePeakLevels[1] - gNegativePeakLevels[1]) < gPeakLevelHighThreshold)
robert@464 161 rt_printf("Right Audio In FAIL: insufficient signal: %f\n",
robert@464 162 gPositivePeakLevels[1] - gNegativePeakLevels[1]);
robert@464 163 else if(gPositivePeakLevels[0] - gNegativePeakLevels[0] > gPeakLevelLowThreshold)
robert@464 164 rt_printf("Left Audio In FAIL: signal present when it should not be: %f\n",
robert@464 165 gPositivePeakLevels[0] - gNegativePeakLevels[0]);
robert@464 166 else if(fabsf(gPositivePeakLevels[0]) >= gDCOffsetThreshold ||
robert@464 167 fabsf(gNegativePeakLevels[0]) >= gDCOffsetThreshold)
robert@464 168 rt_printf("Left Audio In FAIL: DC offset: (%f, %f)\n",
robert@464 169 gPositivePeakLevels[0], gNegativePeakLevels[0]);
robert@464 170 }
robert@464 171 gAudioTestSuccessCounter--;
robert@464 172 if(gAudioTestSuccessCounter <= 0)
robert@464 173 gAudioTestSuccessCounter = 0;
robert@464 174 }
robert@464 175 }
robert@464 176 }
robert@464 177 else {
robert@464 178 // Audio input testing finished. Play tones depending on status of
robert@464 179 // analog testing
robert@464 180 context->audioOut[2*n] = gEnvelopeValueL * sinf(phase);
robert@464 181 context->audioOut[2*n + 1] = gEnvelopeValueR * sinf(phase);
robert@464 182
robert@464 183 // If one second has gone by with no error, play one sound, else
robert@464 184 // play another
robert@464 185 if(context->audioFramesElapsed + n - gLastErrorFrame > 44100) {
robert@464 186 gEnvelopeValueL *= gEnvelopeDecayRate;
robert@464 187 gEnvelopeValueR *= gEnvelopeDecayRate;
robert@464 188 gEnvelopeSampleCount++;
robert@464 189 if(gEnvelopeSampleCount > 22050) {
robert@464 190 if(gEnvelopeLastChannel == 0)
robert@464 191 gEnvelopeValueR = 0.5;
robert@464 192 else
robert@464 193 gEnvelopeValueL = 0.5;
robert@464 194 gEnvelopeLastChannel = !gEnvelopeLastChannel;
robert@464 195 gEnvelopeSampleCount = 0;
robert@464 196 }
robert@464 197 frequency = 880.0;
robert@464 198 }
robert@464 199 else {
robert@464 200 gEnvelopeValueL = gEnvelopeValueR = 0.5;
robert@464 201 gEnvelopeLastChannel = 0;
robert@464 202 frequency = 220.0;
robert@464 203 }
robert@464 204
robert@464 205 phase += 2.0 * M_PI * frequency / 44100.0;
robert@464 206 if(phase >= 2.0 * M_PI)
robert@464 207 phase -= 2.0 * M_PI;
robert@464 208 }
robert@464 209 }
robert@464 210
robert@464 211 for(unsigned int n = 0; n < context->analogFrames; n++) {
robert@464 212 // Change outputs every 512 samples
robert@464 213 if(sampleCounter < 512) {
robert@464 214 for(int k = 0; k < 8; k++) {
robert@464 215 if(k == invertChannel)
robert@464 216 context->analogOut[n*8 + gDACPinOrder[k]] = ANALOG_HIGH;
robert@464 217 else
robert@464 218 context->analogOut[n*8 + gDACPinOrder[k]] = 0;
robert@464 219 }
robert@464 220 }
robert@464 221 else {
robert@464 222 for(int k = 0; k < 8; k++) {
robert@464 223 if(k == invertChannel)
robert@464 224 context->analogOut[n*8 + gDACPinOrder[k]] = 0;
robert@464 225 else
robert@464 226 context->analogOut[n*8 + gDACPinOrder[k]] = ANALOG_HIGH;
robert@464 227 }
robert@464 228 }
robert@464 229
robert@464 230 // Read after 256 samples: input should be low
robert@464 231 if(sampleCounter == 256) {
robert@464 232 for(int k = 0; k < 8; k++) {
robert@464 233 if(k == invertChannel) {
robert@464 234 if(context->analogIn[n*8 + k] < ANALOG_HIGH) {
robert@464 235 rt_printf("FAIL [output %d, input %d] -- output HIGH input %f (inverted)\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]);
robert@464 236 gLastErrorFrame = context->audioFramesElapsed + n;
robert@464 237 }
robert@464 238 }
robert@464 239 else {
robert@464 240 if(context->analogIn[n*8 + k] > ANALOG_LOW) {
robert@464 241 rt_printf("FAIL [output %d, input %d] -- output LOW --> input %f\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]);
robert@464 242 gLastErrorFrame = context->audioFramesElapsed + n;
robert@464 243 }
robert@464 244 }
robert@464 245 }
robert@464 246 }
robert@464 247 else if(sampleCounter == 768) {
robert@464 248 for(int k = 0; k < 8; k++) {
robert@464 249 if(k == invertChannel) {
robert@464 250 if(context->analogIn[n*8 + k] > ANALOG_LOW) {
robert@464 251 rt_printf("FAIL [output %d, input %d] -- output LOW input %f (inverted)\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]);
robert@464 252 gLastErrorFrame = context->audioFramesElapsed + n;
robert@464 253 }
robert@464 254 }
robert@464 255 else {
robert@464 256 if(context->analogIn[n*8 + k] < ANALOG_HIGH) {
robert@464 257 rt_printf("FAIL [output %d, input %d] -- output HIGH input %f\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]);
robert@464 258 gLastErrorFrame = context->audioFramesElapsed + n;
robert@464 259 }
robert@464 260 }
robert@464 261 }
robert@464 262 }
robert@464 263
robert@464 264 if(++sampleCounter >= 1024) {
robert@464 265 sampleCounter = 0;
robert@464 266 invertChannel++;
robert@464 267 if(invertChannel >= 8)
robert@464 268 invertChannel = 0;
robert@464 269 }
robert@464 270 }
robert@464 271 }
robert@464 272
robert@464 273 // cleanup() is called once at the end, after the audio has stopped.
robert@464 274 // Release any resources that were allocated in setup().
robert@464 275
robert@464 276 void cleanup(BelaContext *context, void *userData)
robert@464 277 {
robert@464 278
robert@464 279 }