Mercurial > hg > beaglert
comparison 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 |
comparison
equal
deleted
inserted
replaced
463:c47709e8b5c9 | 464:8fcfbfb32aa0 |
---|---|
1 /* | |
2 * render.cpp | |
3 * | |
4 * Created on: Oct 24, 2014 | |
5 * Author: parallels | |
6 */ | |
7 | |
8 | |
9 #include <Bela.h> | |
10 #include <cmath> | |
11 | |
12 #define ANALOG_LOW (2048.0 / 65536.0) | |
13 #define ANALOG_HIGH (50000.0 / 65536.0) | |
14 | |
15 const int gDACPinOrder[] = {6, 4, 2, 0, 1, 3, 5, 7}; | |
16 | |
17 enum { | |
18 kStateTestingAudioLeft = 0, | |
19 kStateTestingAudioRight, | |
20 kStateTestingAudioDone | |
21 }; | |
22 | |
23 uint64_t gLastErrorFrame = 0; | |
24 uint32_t gEnvelopeSampleCount = 0; | |
25 float gEnvelopeValueL = 0.5, gEnvelopeValueR = 0.5; | |
26 float gEnvelopeDecayRate = 0.9995; | |
27 int gEnvelopeLastChannel = 0; | |
28 | |
29 float gPositivePeakLevels[2] = {0, 0}; | |
30 float gNegativePeakLevels[2] = {0, 0}; | |
31 float gPeakLevelDecayRate = 0.999; | |
32 const float gPeakLevelLowThreshold = 0.02; | |
33 const float gPeakLevelHighThreshold = 0.2; | |
34 const float gDCOffsetThreshold = 0.1; | |
35 int gAudioTestState = kStateTestingAudioLeft; | |
36 int gAudioTestStateSampleCount = 0; | |
37 int gAudioTestSuccessCounter = 0; | |
38 const int gAudioTestSuccessCounterThreshold = 64; | |
39 const int gAudioTestStateSampleThreshold = 16384; | |
40 | |
41 // setup() is called once before the audio rendering starts. | |
42 // Use it to perform any initialisation and allocation which is dependent | |
43 // on the period size or sample rate. | |
44 // | |
45 // userData holds an opaque pointer to a data structure that was passed | |
46 // in from the call to initAudio(). | |
47 // | |
48 // Return true on success; returning false halts the program. | |
49 | |
50 bool setup(BelaContext *context, void *userData) | |
51 { | |
52 return true; | |
53 } | |
54 | |
55 // render() is called regularly at the highest priority by the audio engine. | |
56 // Input and output are given from the audio hardware and the other | |
57 // ADCs and DACs (if available). If only audio is available, numMatrixFrames | |
58 // will be 0. | |
59 | |
60 void render(BelaContext *context, void *userData) | |
61 { | |
62 static float phase = 0.0; | |
63 static int sampleCounter = 0; | |
64 static int invertChannel = 0; | |
65 float frequency = 0; | |
66 | |
67 // Play a sine wave on the audio output | |
68 for(unsigned int n = 0; n < context->audioFrames; n++) { | |
69 | |
70 // Peak detection on the audio inputs, with offset to catch | |
71 // DC errors | |
72 for(int ch = 0; ch < 2; ch++) { | |
73 if(context->audioIn[2*n + ch] > gPositivePeakLevels[ch]) | |
74 gPositivePeakLevels[ch] = context->audioIn[2*n + ch]; | |
75 gPositivePeakLevels[ch] += 0.1; | |
76 gPositivePeakLevels[ch] *= gPeakLevelDecayRate; | |
77 gPositivePeakLevels[ch] -= 0.1; | |
78 if(context->audioIn[2*n + ch] < gNegativePeakLevels[ch]) | |
79 gNegativePeakLevels[ch] = context->audioIn[2*n + ch]; | |
80 gNegativePeakLevels[ch] -= 0.1; | |
81 gNegativePeakLevels[ch] *= gPeakLevelDecayRate; | |
82 gNegativePeakLevels[ch] += 0.1; | |
83 } | |
84 | |
85 if(gAudioTestState == kStateTestingAudioLeft) { | |
86 context->audioOut[2*n] = 0.2 * sinf(phase); | |
87 context->audioOut[2*n + 1] = 0; | |
88 | |
89 frequency = 3000.0; | |
90 phase += 2.0 * M_PI * frequency / 44100.0; | |
91 if(phase >= 2.0 * M_PI) | |
92 phase -= 2.0 * M_PI; | |
93 | |
94 gAudioTestStateSampleCount++; | |
95 if(gAudioTestStateSampleCount >= gAudioTestStateSampleThreshold) { | |
96 // Check if we have the expected input: signal on the left but not | |
97 // on the right. Also check that there is not too much DC offset on the | |
98 // inactive signal | |
99 if((gPositivePeakLevels[0] - gNegativePeakLevels[0]) >= gPeakLevelHighThreshold | |
100 && (gPositivePeakLevels[1] - gNegativePeakLevels[1]) <= gPeakLevelLowThreshold && | |
101 fabsf(gPositivePeakLevels[1]) < gDCOffsetThreshold && | |
102 fabsf(gNegativePeakLevels[1]) < gDCOffsetThreshold) { | |
103 // Successful test: increment counter | |
104 gAudioTestSuccessCounter++; | |
105 if(gAudioTestSuccessCounter >= gAudioTestSuccessCounterThreshold) { | |
106 gAudioTestState = kStateTestingAudioRight; | |
107 gAudioTestStateSampleCount = 0; | |
108 gAudioTestSuccessCounter = 0; | |
109 } | |
110 | |
111 } | |
112 else { | |
113 if(!((context->audioFramesElapsed + n) % 22050)) { | |
114 // Debugging print messages | |
115 if((gPositivePeakLevels[0] - gNegativePeakLevels[0]) < gPeakLevelHighThreshold) | |
116 rt_printf("Left Audio In FAIL: insufficient signal: %f\n", | |
117 gPositivePeakLevels[0] - gNegativePeakLevels[0]); | |
118 else if(gPositivePeakLevels[1] - gNegativePeakLevels[1] > gPeakLevelLowThreshold) | |
119 rt_printf("Right Audio In FAIL: signal present when it should not be: %f\n", | |
120 gPositivePeakLevels[1] - gNegativePeakLevels[1]); | |
121 else if(fabsf(gPositivePeakLevels[1]) >= gDCOffsetThreshold || | |
122 fabsf(gNegativePeakLevels[1]) >= gDCOffsetThreshold) | |
123 rt_printf("Right Audio In FAIL: DC offset: (%f, %f)\n", | |
124 gPositivePeakLevels[1], gNegativePeakLevels[1]); | |
125 } | |
126 gAudioTestSuccessCounter--; | |
127 if(gAudioTestSuccessCounter <= 0) | |
128 gAudioTestSuccessCounter = 0; | |
129 } | |
130 } | |
131 } | |
132 else if(gAudioTestState == kStateTestingAudioRight) { | |
133 context->audioOut[2*n] = 0; | |
134 context->audioOut[2*n + 1] = 0.2 * sinf(phase); | |
135 | |
136 frequency = 3000.0; | |
137 phase += 2.0 * M_PI * frequency / 44100.0; | |
138 if(phase >= 2.0 * M_PI) | |
139 phase -= 2.0 * M_PI; | |
140 | |
141 gAudioTestStateSampleCount++; | |
142 if(gAudioTestStateSampleCount >= gAudioTestStateSampleThreshold) { | |
143 // Check if we have the expected input: signal on the left but not | |
144 // on the right | |
145 if((gPositivePeakLevels[1] - gNegativePeakLevels[1]) >= gPeakLevelHighThreshold | |
146 && (gPositivePeakLevels[0] - gNegativePeakLevels[0]) <= gPeakLevelLowThreshold && | |
147 fabsf(gPositivePeakLevels[0]) < gDCOffsetThreshold && | |
148 fabsf(gNegativePeakLevels[0]) < gDCOffsetThreshold) { | |
149 // Successful test: increment counter | |
150 gAudioTestSuccessCounter++; | |
151 if(gAudioTestSuccessCounter >= gAudioTestSuccessCounterThreshold) { | |
152 gAudioTestSuccessCounter = 0; | |
153 gAudioTestStateSampleCount = 0; | |
154 gAudioTestState = kStateTestingAudioDone; | |
155 } | |
156 } | |
157 else { | |
158 if(!((context->audioFramesElapsed + n) % 22050)) { | |
159 // Debugging print messages | |
160 if((gPositivePeakLevels[1] - gNegativePeakLevels[1]) < gPeakLevelHighThreshold) | |
161 rt_printf("Right Audio In FAIL: insufficient signal: %f\n", | |
162 gPositivePeakLevels[1] - gNegativePeakLevels[1]); | |
163 else if(gPositivePeakLevels[0] - gNegativePeakLevels[0] > gPeakLevelLowThreshold) | |
164 rt_printf("Left Audio In FAIL: signal present when it should not be: %f\n", | |
165 gPositivePeakLevels[0] - gNegativePeakLevels[0]); | |
166 else if(fabsf(gPositivePeakLevels[0]) >= gDCOffsetThreshold || | |
167 fabsf(gNegativePeakLevels[0]) >= gDCOffsetThreshold) | |
168 rt_printf("Left Audio In FAIL: DC offset: (%f, %f)\n", | |
169 gPositivePeakLevels[0], gNegativePeakLevels[0]); | |
170 } | |
171 gAudioTestSuccessCounter--; | |
172 if(gAudioTestSuccessCounter <= 0) | |
173 gAudioTestSuccessCounter = 0; | |
174 } | |
175 } | |
176 } | |
177 else { | |
178 // Audio input testing finished. Play tones depending on status of | |
179 // analog testing | |
180 context->audioOut[2*n] = gEnvelopeValueL * sinf(phase); | |
181 context->audioOut[2*n + 1] = gEnvelopeValueR * sinf(phase); | |
182 | |
183 // If one second has gone by with no error, play one sound, else | |
184 // play another | |
185 if(context->audioFramesElapsed + n - gLastErrorFrame > 44100) { | |
186 gEnvelopeValueL *= gEnvelopeDecayRate; | |
187 gEnvelopeValueR *= gEnvelopeDecayRate; | |
188 gEnvelopeSampleCount++; | |
189 if(gEnvelopeSampleCount > 22050) { | |
190 if(gEnvelopeLastChannel == 0) | |
191 gEnvelopeValueR = 0.5; | |
192 else | |
193 gEnvelopeValueL = 0.5; | |
194 gEnvelopeLastChannel = !gEnvelopeLastChannel; | |
195 gEnvelopeSampleCount = 0; | |
196 } | |
197 frequency = 880.0; | |
198 } | |
199 else { | |
200 gEnvelopeValueL = gEnvelopeValueR = 0.5; | |
201 gEnvelopeLastChannel = 0; | |
202 frequency = 220.0; | |
203 } | |
204 | |
205 phase += 2.0 * M_PI * frequency / 44100.0; | |
206 if(phase >= 2.0 * M_PI) | |
207 phase -= 2.0 * M_PI; | |
208 } | |
209 } | |
210 | |
211 for(unsigned int n = 0; n < context->analogFrames; n++) { | |
212 // Change outputs every 512 samples | |
213 if(sampleCounter < 512) { | |
214 for(int k = 0; k < 8; k++) { | |
215 if(k == invertChannel) | |
216 context->analogOut[n*8 + gDACPinOrder[k]] = ANALOG_HIGH; | |
217 else | |
218 context->analogOut[n*8 + gDACPinOrder[k]] = 0; | |
219 } | |
220 } | |
221 else { | |
222 for(int k = 0; k < 8; k++) { | |
223 if(k == invertChannel) | |
224 context->analogOut[n*8 + gDACPinOrder[k]] = 0; | |
225 else | |
226 context->analogOut[n*8 + gDACPinOrder[k]] = ANALOG_HIGH; | |
227 } | |
228 } | |
229 | |
230 // Read after 256 samples: input should be low | |
231 if(sampleCounter == 256) { | |
232 for(int k = 0; k < 8; k++) { | |
233 if(k == invertChannel) { | |
234 if(context->analogIn[n*8 + k] < ANALOG_HIGH) { | |
235 rt_printf("FAIL [output %d, input %d] -- output HIGH input %f (inverted)\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]); | |
236 gLastErrorFrame = context->audioFramesElapsed + n; | |
237 } | |
238 } | |
239 else { | |
240 if(context->analogIn[n*8 + k] > ANALOG_LOW) { | |
241 rt_printf("FAIL [output %d, input %d] -- output LOW --> input %f\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]); | |
242 gLastErrorFrame = context->audioFramesElapsed + n; | |
243 } | |
244 } | |
245 } | |
246 } | |
247 else if(sampleCounter == 768) { | |
248 for(int k = 0; k < 8; k++) { | |
249 if(k == invertChannel) { | |
250 if(context->analogIn[n*8 + k] > ANALOG_LOW) { | |
251 rt_printf("FAIL [output %d, input %d] -- output LOW input %f (inverted)\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]); | |
252 gLastErrorFrame = context->audioFramesElapsed + n; | |
253 } | |
254 } | |
255 else { | |
256 if(context->analogIn[n*8 + k] < ANALOG_HIGH) { | |
257 rt_printf("FAIL [output %d, input %d] -- output HIGH input %f\n", gDACPinOrder[k], k, context->analogIn[n*8 + k]); | |
258 gLastErrorFrame = context->audioFramesElapsed + n; | |
259 } | |
260 } | |
261 } | |
262 } | |
263 | |
264 if(++sampleCounter >= 1024) { | |
265 sampleCounter = 0; | |
266 invertChannel++; | |
267 if(invertChannel >= 8) | |
268 invertChannel = 0; | |
269 } | |
270 } | |
271 } | |
272 | |
273 // cleanup() is called once at the end, after the audio has stopped. | |
274 // Release any resources that were allocated in setup(). | |
275 | |
276 void cleanup(BelaContext *context, void *userData) | |
277 { | |
278 | |
279 } |