| andrewm@88 | 1 /* | 
| andrewm@88 | 2  * render.cpp | 
| andrewm@88 | 3  * | 
| andrewm@88 | 4  *  Created on: Oct 24, 2014 | 
| andrewm@88 | 5  *      Author: parallels | 
| andrewm@88 | 6  */ | 
| andrewm@88 | 7 | 
| andrewm@88 | 8 | 
| andrewm@88 | 9 #include <BeagleRT.h> | 
| andrewm@88 | 10 #include <Utilities.h> | 
| andrewm@88 | 11 #include <cmath> | 
| andrewm@88 | 12 | 
| andrewm@88 | 13 #define NUMBER_OF_SEGMENTS	10 | 
| andrewm@88 | 14 | 
| andrewm@88 | 15 // Two levels of audio: one follows current value, the other holds | 
| andrewm@88 | 16 // peaks for longer | 
| andrewm@88 | 17 float gAudioLocalLevel = 0, gAudioPeakLevel = 0; | 
| andrewm@88 | 18 | 
| andrewm@88 | 19 // Decay rates for detecting levels | 
| andrewm@88 | 20 float gLocalDecayRate = 0.99, gPeakDecayRate = 0.999; | 
| andrewm@88 | 21 | 
| andrewm@88 | 22 // Thresholds for LEDs: set in setup() | 
| andrewm@88 | 23 float gThresholds[NUMBER_OF_SEGMENTS + 1]; | 
| andrewm@88 | 24 | 
| andrewm@88 | 25 // High-pass filter on the input | 
| andrewm@88 | 26 float gLastX[2] = {0}; | 
| andrewm@88 | 27 float gLastY[2] = {0}; | 
| andrewm@88 | 28 | 
| andrewm@88 | 29 // These coefficients make a high-pass filter at 5Hz for 44.1kHz sample rate | 
| andrewm@88 | 30 double gB0 = 0.99949640; | 
| andrewm@88 | 31 double gB1 = -1.99899280; | 
| andrewm@88 | 32 double gB2 = gB0; | 
| andrewm@88 | 33 double gA1 = -1.99899254; | 
| andrewm@88 | 34 double gA2 = 0.99899305; | 
| andrewm@88 | 35 | 
| andrewm@88 | 36 // setup() is called once before the audio rendering starts. | 
| andrewm@88 | 37 // Use it to perform any initialisation and allocation which is dependent | 
| andrewm@88 | 38 // on the period size or sample rate. | 
| andrewm@88 | 39 // | 
| andrewm@88 | 40 // userData holds an opaque pointer to a data structure that was passed | 
| andrewm@88 | 41 // in from the call to initAudio(). | 
| andrewm@88 | 42 // | 
| andrewm@88 | 43 // Return true on success; returning false halts the program. | 
| andrewm@88 | 44 | 
| andrewm@88 | 45 bool setup(BeagleRTContext *context, void *userData) | 
| andrewm@88 | 46 { | 
| andrewm@88 | 47 	// This project makes the assumption that the audio and digital | 
| andrewm@88 | 48 	// sample rates are the same. But check it to be sure! | 
| andrewm@88 | 49 	if(context->audioFrames != context->digitalFrames) { | 
| andrewm@88 | 50 		rt_printf("Error: this project needs the audio and digital sample rates to be the same.\n"); | 
| andrewm@88 | 51 		return false; | 
| andrewm@88 | 52 	} | 
| andrewm@88 | 53 | 
| andrewm@88 | 54 	// Initialise threshold levels in -3dB steps. One extra for efficiency in render() | 
| andrewm@88 | 55 	// Level = 10^(dB/20) | 
| andrewm@88 | 56 	for(int i = 0; i < NUMBER_OF_SEGMENTS + 1; i++) { | 
| andrewm@88 | 57 		gThresholds[i] = powf(10.0f, (-1.0 * (NUMBER_OF_SEGMENTS - i)) * .05); | 
| andrewm@88 | 58 	} | 
| andrewm@88 | 59 | 
| andrewm@88 | 60 	for(int i = 0; i < NUMBER_OF_SEGMENTS; i++) | 
| andrewm@88 | 61 		pinModeFrame(context, 0, i, OUTPUT); | 
| andrewm@88 | 62 | 
| andrewm@88 | 63 	return true; | 
| andrewm@88 | 64 } | 
| andrewm@88 | 65 | 
| andrewm@88 | 66 // render() is called regularly at the highest priority by the audio engine. | 
| andrewm@88 | 67 // Input and output are given from the audio hardware and the other | 
| andrewm@88 | 68 // ADCs and DACs (if available). If only audio is available, numMatrixFrames | 
| andrewm@88 | 69 // will be 0. | 
| andrewm@88 | 70 | 
| andrewm@88 | 71 void render(BeagleRTContext *context, void *userData) | 
| andrewm@88 | 72 { | 
| andrewm@88 | 73 	for(unsigned int n = 0; n < context->audioFrames; n++) { | 
| andrewm@88 | 74 		// Get average of audio input channels | 
| andrewm@88 | 75 		float sample = 0; | 
| andrewm@88 | 76 		for(unsigned int ch = 0; ch < context->audioChannels; ch++) { | 
| andrewm@88 | 77 			context->audioOut[n * context->audioChannels + ch] = | 
| andrewm@88 | 78 				context->audioIn[n * context->audioChannels + ch]; | 
| andrewm@88 | 79 			sample += context->audioIn[n * context->audioChannels + ch]; | 
| andrewm@88 | 80 		} | 
| andrewm@88 | 81 | 
| andrewm@88 | 82 		// Do DC-blocking on the sum | 
| andrewm@88 | 83 		float out = gB0 * sample + gB1 * gLastX[0] + gB2 * gLastX[1] | 
| andrewm@88 | 84 						- gA1 * gLastY[0] - gA2 * gLastY[1]; | 
| andrewm@88 | 85 | 
| andrewm@88 | 86 		gLastX[1] = gLastX[0]; | 
| andrewm@88 | 87 		gLastX[0] = sample; | 
| andrewm@88 | 88 		gLastY[1] = gLastY[0]; | 
| andrewm@88 | 89 		gLastY[0] = out; | 
| andrewm@88 | 90 | 
| andrewm@88 | 91 		out = fabsf(out / (float)context->audioChannels); | 
| andrewm@88 | 92 | 
| andrewm@88 | 93 		// Do peak detection: fast-responding local level | 
| andrewm@88 | 94 		if(out > gAudioLocalLevel) | 
| andrewm@88 | 95 			gAudioLocalLevel = out; | 
| andrewm@88 | 96 		else | 
| andrewm@88 | 97 			gAudioLocalLevel *= gLocalDecayRate; | 
| andrewm@88 | 98 | 
| andrewm@88 | 99 		// Do peak detection: slow-responding peak level | 
| andrewm@88 | 100 		if(out > gAudioPeakLevel) | 
| andrewm@88 | 101 			gAudioPeakLevel = out; | 
| andrewm@88 | 102 		else { | 
| andrewm@88 | 103 			// Make peak decay slowly by only multiplying | 
| andrewm@88 | 104 			// every few samples | 
| andrewm@88 | 105 			if(((context->audioSampleCount + n) & 31) == 0) | 
| andrewm@88 | 106 				gAudioPeakLevel *= gPeakDecayRate; | 
| andrewm@88 | 107 		} | 
| andrewm@88 | 108 		// LED bargraph on digital outputs 0-9 | 
| andrewm@88 | 109 		for(int led = 0; led < NUMBER_OF_SEGMENTS; led++) { | 
| andrewm@88 | 110 			// All LEDs up to the local level light up. The LED | 
| andrewm@88 | 111 			// for the peak level also remains lit. | 
| andrewm@88 | 112 			int state = LOW; | 
| andrewm@88 | 113 | 
| andrewm@88 | 114 			if(gAudioLocalLevel > gThresholds[led]) | 
| andrewm@88 | 115 				state = HIGH; | 
| andrewm@88 | 116 			else if(gAudioPeakLevel > gThresholds[led] && gAudioPeakLevel <= gThresholds[led + 1]) | 
| andrewm@88 | 117 				state = HIGH; | 
| andrewm@88 | 118 | 
| andrewm@88 | 119 			// Write LED | 
| andrewm@88 | 120 			digitalWriteFrameOnce(context, n, led, state); | 
| andrewm@88 | 121 		} | 
| andrewm@88 | 122 	} | 
| andrewm@88 | 123 } | 
| andrewm@88 | 124 | 
| andrewm@88 | 125 // cleanup() is called once at the end, after the audio has stopped. | 
| andrewm@88 | 126 // Release any resources that were allocated in setup(). | 
| andrewm@88 | 127 | 
| andrewm@88 | 128 void cleanup(BeagleRTContext *context, void *userData) | 
| andrewm@88 | 129 { | 
| andrewm@88 | 130 | 
| andrewm@88 | 131 } |