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