Mercurial > hg > beaglert
diff examples/11-Extras/cape-test/render.cpp @ 464:8fcfbfb32aa0 prerelease
Examples reorder with subdirectories. Added header to each project. Moved Doxygen to bottom of render.cpp.
author | Robert Jack <robert.h.jack@gmail.com> |
---|---|
date | Mon, 20 Jun 2016 16:20:38 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/11-Extras/cape-test/render.cpp Mon Jun 20 16:20:38 2016 +0100 @@ -0,0 +1,279 @@ +/* + * render.cpp + * + * Created on: Oct 24, 2014 + * Author: parallels + */ + + +#include <Bela.h> +#include <cmath> + +#define ANALOG_LOW (2048.0 / 65536.0) +#define ANALOG_HIGH (50000.0 / 65536.0) + +const int gDACPinOrder[] = {6, 4, 2, 0, 1, 3, 5, 7}; + +enum { + kStateTestingAudioLeft = 0, + kStateTestingAudioRight, + kStateTestingAudioDone +}; + +uint64_t gLastErrorFrame = 0; +uint32_t gEnvelopeSampleCount = 0; +float gEnvelopeValueL = 0.5, gEnvelopeValueR = 0.5; +float gEnvelopeDecayRate = 0.9995; +int gEnvelopeLastChannel = 0; + +float gPositivePeakLevels[2] = {0, 0}; +float gNegativePeakLevels[2] = {0, 0}; +float gPeakLevelDecayRate = 0.999; +const float gPeakLevelLowThreshold = 0.02; +const float gPeakLevelHighThreshold = 0.2; +const float gDCOffsetThreshold = 0.1; +int gAudioTestState = kStateTestingAudioLeft; +int gAudioTestStateSampleCount = 0; +int gAudioTestSuccessCounter = 0; +const int gAudioTestSuccessCounterThreshold = 64; +const int gAudioTestStateSampleThreshold = 16384; + +// setup() is called once before the audio rendering starts. +// Use it to perform any initialisation and allocation which is dependent +// on the period size or sample rate. +// +// userData holds an opaque pointer to a data structure that was passed +// in from the call to initAudio(). +// +// Return true on success; returning false halts the program. + +bool setup(BelaContext *context, void *userData) +{ + return true; +} + +// render() is called regularly at the highest priority by the audio engine. +// Input and output are given from the audio hardware and the other +// ADCs and DACs (if available). If only audio is available, numMatrixFrames +// will be 0. + +void render(BelaContext *context, void *userData) +{ + static float phase = 0.0; + static int sampleCounter = 0; + static int invertChannel = 0; + float frequency = 0; + + // Play a sine wave on the audio output + for(unsigned int n = 0; n < context->audioFrames; n++) { + + // Peak detection on the audio inputs, with offset to catch + // DC errors + for(int ch = 0; ch < 2; ch++) { + if(context->audioIn[2*n + ch] > gPositivePeakLevels[ch]) + gPositivePeakLevels[ch] = context->audioIn[2*n + ch]; + gPositivePeakLevels[ch] += 0.1; + gPositivePeakLevels[ch] *= gPeakLevelDecayRate; + gPositivePeakLevels[ch] -= 0.1; + if(context->audioIn[2*n + ch] < gNegativePeakLevels[ch]) + gNegativePeakLevels[ch] = context->audioIn[2*n + ch]; + gNegativePeakLevels[ch] -= 0.1; + gNegativePeakLevels[ch] *= gPeakLevelDecayRate; + gNegativePeakLevels[ch] += 0.1; + } + + if(gAudioTestState == kStateTestingAudioLeft) { + context->audioOut[2*n] = 0.2 * sinf(phase); + context->audioOut[2*n + 1] = 0; + + frequency = 3000.0; + phase += 2.0 * M_PI * frequency / 44100.0; + if(phase >= 2.0 * M_PI) + phase -= 2.0 * M_PI; + + gAudioTestStateSampleCount++; + if(gAudioTestStateSampleCount >= gAudioTestStateSampleThreshold) { + // Check if we have the expected input: signal on the left but not + // on the right. Also check that there is not too much DC offset on the + // inactive signal + if((gPositivePeakLevels[0] - gNegativePeakLevels[0]) >= gPeakLevelHighThreshold + && (gPositivePeakLevels[1] - gNegativePeakLevels[1]) <= gPeakLevelLowThreshold && + fabsf(gPositivePeakLevels[1]) < gDCOffsetThreshold && + fabsf(gNegativePeakLevels[1]) < gDCOffsetThreshold) { + // Successful test: increment counter + gAudioTestSuccessCounter++; + if(gAudioTestSuccessCounter >= gAudioTestSuccessCounterThreshold) { + gAudioTestState = kStateTestingAudioRight; + gAudioTestStateSampleCount = 0; + gAudioTestSuccessCounter = 0; + } + + } + else { + if(!((context->audioFramesElapsed + n) % 22050)) { + // Debugging print messages + if((gPositivePeakLevels[0] - gNegativePeakLevels[0]) < gPeakLevelHighThreshold) + rt_printf("Left Audio In FAIL: insufficient signal: %f\n", + gPositivePeakLevels[0] - gNegativePeakLevels[0]); + else if(gPositivePeakLevels[1] - gNegativePeakLevels[1] > gPeakLevelLowThreshold) + rt_printf("Right Audio In FAIL: signal present when it should not be: %f\n", + gPositivePeakLevels[1] - gNegativePeakLevels[1]); + else if(fabsf(gPositivePeakLevels[1]) >= gDCOffsetThreshold || + fabsf(gNegativePeakLevels[1]) >= gDCOffsetThreshold) + rt_printf("Right Audio In FAIL: DC offset: (%f, %f)\n", + gPositivePeakLevels[1], gNegativePeakLevels[1]); + } + gAudioTestSuccessCounter--; + if(gAudioTestSuccessCounter <= 0) + gAudioTestSuccessCounter = 0; + } + } + } + else if(gAudioTestState == kStateTestingAudioRight) { + context->audioOut[2*n] = 0; + context->audioOut[2*n + 1] = 0.2 * sinf(phase); + + frequency = 3000.0; + phase += 2.0 * M_PI * frequency / 44100.0; + if(phase >= 2.0 * M_PI) + phase -= 2.0 * M_PI; + + gAudioTestStateSampleCount++; + if(gAudioTestStateSampleCount >= gAudioTestStateSampleThreshold) { + // Check if we have the expected input: signal on the left but not + // on the right + if((gPositivePeakLevels[1] - gNegativePeakLevels[1]) >= gPeakLevelHighThreshold + && (gPositivePeakLevels[0] - gNegativePeakLevels[0]) <= gPeakLevelLowThreshold && + fabsf(gPositivePeakLevels[0]) < gDCOffsetThreshold && + fabsf(gNegativePeakLevels[0]) < gDCOffsetThreshold) { + // Successful test: increment counter + gAudioTestSuccessCounter++; + if(gAudioTestSuccessCounter >= gAudioTestSuccessCounterThreshold) { + gAudioTestSuccessCounter = 0; + gAudioTestStateSampleCount = 0; + gAudioTestState = kStateTestingAudioDone; + } + } + else { + if(!((context->audioFramesElapsed + n) % 22050)) { + // Debugging print messages + if((gPositivePeakLevels[1] - gNegativePeakLevels[1]) < gPeakLevelHighThreshold) + rt_printf("Right Audio In FAIL: insufficient signal: %f\n", + gPositivePeakLevels[1] - gNegativePeakLevels[1]); + else if(gPositivePeakLevels[0] - gNegativePeakLevels[0] > gPeakLevelLowThreshold) + rt_printf("Left Audio In FAIL: signal present when it should not be: %f\n", + gPositivePeakLevels[0] - gNegativePeakLevels[0]); + else if(fabsf(gPositivePeakLevels[0]) >= gDCOffsetThreshold || + fabsf(gNegativePeakLevels[0]) >= gDCOffsetThreshold) + rt_printf("Left Audio In FAIL: DC offset: (%f, %f)\n", + gPositivePeakLevels[0], gNegativePeakLevels[0]); + } + gAudioTestSuccessCounter--; + if(gAudioTestSuccessCounter <= 0) + gAudioTestSuccessCounter = 0; + } + } + } + else { + // Audio input testing finished. Play tones depending on status of + // analog testing + context->audioOut[2*n] = gEnvelopeValueL * sinf(phase); + context->audioOut[2*n + 1] = gEnvelopeValueR * sinf(phase); + + // If one second has gone by with no error, play one sound, else + // play another + if(context->audioFramesElapsed + n - gLastErrorFrame > 44100) { + gEnvelopeValueL *= gEnvelopeDecayRate; + gEnvelopeValueR *= gEnvelopeDecayRate; + gEnvelopeSampleCount++; + if(gEnvelopeSampleCount > 22050) { + if(gEnvelopeLastChannel == 0) + gEnvelopeValueR = 0.5; + else + gEnvelopeValueL = 0.5; + gEnvelopeLastChannel = !gEnvelopeLastChannel; + gEnvelopeSampleCount = 0; + } + frequency = 880.0; + } + else { + gEnvelopeValueL = gEnvelopeValueR = 0.5; + gEnvelopeLastChannel = 0; + frequency = 220.0; + } + + phase += 2.0 * M_PI * frequency / 44100.0; + if(phase >= 2.0 * M_PI) + phase -= 2.0 * M_PI; + } + } + + for(unsigned int n = 0; n < context->analogFrames; n++) { + // Change outputs every 512 samples + if(sampleCounter < 512) { + for(int k = 0; k < 8; k++) { + if(k == invertChannel) + context->analogOut[n*8 + gDACPinOrder[k]] = ANALOG_HIGH; + else + context->analogOut[n*8 + gDACPinOrder[k]] = 0; + } + } + else { + for(int k = 0; k < 8; k++) { + if(k == invertChannel) + context->analogOut[n*8 + gDACPinOrder[k]] = 0; + else + context->analogOut[n*8 + gDACPinOrder[k]] = ANALOG_HIGH; + } + } + + // Read after 256 samples: input should be low + if(sampleCounter == 256) { + for(int k = 0; k < 8; k++) { + if(k == invertChannel) { + if(context->analogIn[n*8 + k] < ANALOG_HIGH) { + rt_printf("FAIL [output %d, input %d] -- output HIGH input %f (inverted)\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]); + gLastErrorFrame = context->audioFramesElapsed + n; + } + } + else { + if(context->analogIn[n*8 + k] > ANALOG_LOW) { + rt_printf("FAIL [output %d, input %d] -- output LOW --> input %f\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]); + gLastErrorFrame = context->audioFramesElapsed + n; + } + } + } + } + else if(sampleCounter == 768) { + for(int k = 0; k < 8; k++) { + if(k == invertChannel) { + if(context->analogIn[n*8 + k] > ANALOG_LOW) { + rt_printf("FAIL [output %d, input %d] -- output LOW input %f (inverted)\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]); + gLastErrorFrame = context->audioFramesElapsed + n; + } + } + else { + if(context->analogIn[n*8 + k] < ANALOG_HIGH) { + rt_printf("FAIL [output %d, input %d] -- output HIGH input %f\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]); + gLastErrorFrame = context->audioFramesElapsed + n; + } + } + } + } + + if(++sampleCounter >= 1024) { + sampleCounter = 0; + invertChannel++; + if(invertChannel >= 8) + invertChannel = 0; + } + } +} + +// cleanup() is called once at the end, after the audio has stopped. +// Release any resources that were allocated in setup(). + +void cleanup(BelaContext *context, void *userData) +{ + +}