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