changeset 88:3a5823f7a11f

Added level meter project
author andrewm
date Sun, 19 Jul 2015 14:15:57 +0100
parents 567bd8f76714
children d41631e0fe0e
files projects/level_meter/render.cpp
diffstat 1 files changed, 131 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/level_meter/render.cpp	Sun Jul 19 14:15:57 2015 +0100
@@ -0,0 +1,131 @@
+/*
+ * render.cpp
+ *
+ *  Created on: Oct 24, 2014
+ *      Author: parallels
+ */
+
+
+#include <BeagleRT.h>
+#include <Utilities.h>
+#include <cmath>
+
+#define NUMBER_OF_SEGMENTS	10
+
+// Two levels of audio: one follows current value, the other holds
+// peaks for longer
+float gAudioLocalLevel = 0, gAudioPeakLevel = 0;
+
+// Decay rates for detecting levels
+float gLocalDecayRate = 0.99, gPeakDecayRate = 0.999;
+
+// Thresholds for LEDs: set in setup()
+float gThresholds[NUMBER_OF_SEGMENTS + 1];
+
+// High-pass filter on the input
+float gLastX[2] = {0};
+float gLastY[2] = {0};
+
+// These coefficients make a high-pass filter at 5Hz for 44.1kHz sample rate
+double gB0 = 0.99949640;
+double gB1 = -1.99899280;
+double gB2 = gB0;
+double gA1 = -1.99899254;
+double gA2 = 0.99899305;
+
+// 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(BeagleRTContext *context, void *userData)
+{	
+	// This project makes the assumption that the audio and digital
+	// sample rates are the same. But check it to be sure!
+	if(context->audioFrames != context->digitalFrames) {
+		rt_printf("Error: this project needs the audio and digital sample rates to be the same.\n");
+		return false;
+	}
+	
+	// Initialise threshold levels in -3dB steps. One extra for efficiency in render()
+	// Level = 10^(dB/20)
+	for(int i = 0; i < NUMBER_OF_SEGMENTS + 1; i++) {
+		gThresholds[i] = powf(10.0f, (-1.0 * (NUMBER_OF_SEGMENTS - i)) * .05);
+	}
+	
+	for(int i = 0; i < NUMBER_OF_SEGMENTS; i++)
+		pinModeFrame(context, 0, i, OUTPUT);
+
+	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(BeagleRTContext *context, void *userData)
+{
+	for(unsigned int n = 0; n < context->audioFrames; n++) {
+		// Get average of audio input channels
+		float sample = 0;
+		for(unsigned int ch = 0; ch < context->audioChannels; ch++) {
+			context->audioOut[n * context->audioChannels + ch] = 
+				context->audioIn[n * context->audioChannels + ch];
+			sample += context->audioIn[n * context->audioChannels + ch];
+		}
+		
+		// Do DC-blocking on the sum
+		float out = gB0 * sample + gB1 * gLastX[0] + gB2 * gLastX[1]
+						- gA1 * gLastY[0] - gA2 * gLastY[1];
+
+		gLastX[1] = gLastX[0];
+		gLastX[0] = sample;
+		gLastY[1] = gLastY[0];
+		gLastY[0] = out;
+		
+		out = fabsf(out / (float)context->audioChannels);
+		
+		// Do peak detection: fast-responding local level
+		if(out > gAudioLocalLevel)
+			gAudioLocalLevel = out;
+		else
+			gAudioLocalLevel *= gLocalDecayRate;
+		
+		// Do peak detection: slow-responding peak level
+		if(out > gAudioPeakLevel)
+			gAudioPeakLevel = out;
+		else {
+			// Make peak decay slowly by only multiplying
+			// every few samples
+			if(((context->audioSampleCount + n) & 31) == 0)
+				gAudioPeakLevel *= gPeakDecayRate;
+		}	
+		// LED bargraph on digital outputs 0-9
+		for(int led = 0; led < NUMBER_OF_SEGMENTS; led++) {
+			// All LEDs up to the local level light up. The LED
+			// for the peak level also remains lit.
+			int state = LOW;
+				
+			if(gAudioLocalLevel > gThresholds[led])
+				state = HIGH;
+			else if(gAudioPeakLevel > gThresholds[led] && gAudioPeakLevel <= gThresholds[led + 1])
+				state = HIGH;
+			
+			// Write LED
+			digitalWriteFrameOnce(context, n, led, state);
+		}
+	}
+}
+
+// cleanup() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in setup().
+
+void cleanup(BeagleRTContext *context, void *userData)
+{
+
+}