Mercurial > hg > beaglert
comparison projects/tank_wars/render.cpp @ 67:472e892c6e41
Merge newapi into default
author | Andrew McPherson <a.mcpherson@qmul.ac.uk> |
---|---|
date | Fri, 17 Jul 2015 15:28:18 +0100 |
parents | 3c3a1357657d |
children | 8d80eda512cd |
comparison
equal
deleted
inserted
replaced
21:0d80ff9e2227 | 67:472e892c6e41 |
---|---|
4 * Created on: Oct 24, 2014 | 4 * Created on: Oct 24, 2014 |
5 * Author: parallels | 5 * Author: parallels |
6 */ | 6 */ |
7 | 7 |
8 | 8 |
9 #include "../../include/RTAudio.h" | 9 #include <BeagleRT.h> |
10 #include "../../include/Utilities.h" | 10 #include <Utilities.h> |
11 #include "game.h" | 11 #include "game.h" |
12 #include <rtdk.h> | 12 #include <rtdk.h> |
13 #include <cmath> | 13 #include <cmath> |
14 #include <cstdlib> | 14 #include <cstdlib> |
15 #include <time.h> | 15 #include <time.h> |
16 | 16 |
17 int gAudioFramesPerMatrixFrame = 2; // Ratio in audio to matrix sample rate | |
18 | |
17 int gInputTank1Angle = 0; // Inputs for the cannon angles | 19 int gInputTank1Angle = 0; // Inputs for the cannon angles |
18 int gInputTank2Angle = 1; | 20 int gInputTank2Angle = 1; |
19 int gInputLauncher = 2; // Input for launcher FSR | 21 int gInputLauncher = 2; // Input for launcher FSR |
20 | 22 |
21 int gOutputX = 0; // Outputs for the scope | 23 int gOutputX = 0; // Outputs for the scope |
35 // 1st-order filter and peak detector for launcher input | 37 // 1st-order filter and peak detector for launcher input |
36 float gLauncherLastSample = 0; | 38 float gLauncherLastSample = 0; |
37 float gLauncherFilterPole = 0.8; | 39 float gLauncherFilterPole = 0.8; |
38 float gLauncherPeakValue = 0; | 40 float gLauncherPeakValue = 0; |
39 float gLauncherPeakFilterPole = 0.999; | 41 float gLauncherPeakFilterPole = 0.999; |
40 float gLauncherNoiseThreshold = 0.01 * MATRIX_MAX; | 42 float gLauncherNoiseThreshold = 0.01; |
41 float gLauncherMinimumPeak = 0.1 * MATRIX_MAX; | 43 float gLauncherMinimumPeak = 0.1; |
42 bool gLauncherTriggered = false; | 44 bool gLauncherTriggered = false; |
43 | 45 |
44 // Screen update rate; affects buffer size. Actual contents of buffer | 46 // Screen update rate; affects buffer size. Actual contents of buffer |
45 // may be smaller than this | 47 // may be smaller than this |
46 int gScreenWidth = 512; | 48 int gScreenWidth = 512; |
59 bool gScreenNextBufferReady; // Is the next buffer ready to go? | 61 bool gScreenNextBufferReady; // Is the next buffer ready to go? |
60 | 62 |
61 // Auxiliary (low-priority) task for updating the screen | 63 // Auxiliary (low-priority) task for updating the screen |
62 AuxiliaryTask gScreenUpdateTask; | 64 AuxiliaryTask gScreenUpdateTask; |
63 | 65 |
66 // Buffers for music and sound effects | |
67 extern float *gMusicBuffer; | |
68 extern int gMusicBufferLength; | |
69 extern float *gSoundBoomBuffer; | |
70 extern int gSoundBoomBufferLength; | |
71 | |
72 // Current state for sound and music | |
73 int gMusicBufferPointer = 0; // 0 means start of buffer... | |
74 int gSoundBoomBufferPointer = -1; // -1 means don't play... | |
75 float gSoundProjectileOscillatorPhase = 0; | |
76 float gSoundProjectileOscillatorGain = 0.2; | |
77 float gOscillatorPhaseScaler = 0; | |
78 | |
64 void screen_update(); | 79 void screen_update(); |
65 | 80 |
66 // initialise_render() is called once before the audio rendering starts. | 81 // setup() is called once before the audio rendering starts. |
67 // Use it to perform any initialisation and allocation which is dependent | 82 // Use it to perform any initialisation and allocation which is dependent |
68 // on the period size or sample rate. | 83 // on the period size or sample rate. |
69 // | 84 // |
70 // userData holds an opaque pointer to a data structure that was passed | 85 // userData holds an opaque pointer to a data structure that was passed |
71 // in from the call to initAudio(). | 86 // in from the call to initAudio(). |
72 // | 87 // |
73 // Return true on success; returning false halts the program. | 88 // Return true on success; returning false halts the program. |
74 | 89 |
75 bool initialise_render(int numMatrixChannels, int numAudioChannels, | 90 bool setup(BeagleRTContext *context, void *userData) |
76 int numMatrixFramesPerPeriod, | |
77 int numAudioFramesPerPeriod, | |
78 float matrixSampleRate, float audioSampleRate, | |
79 void *userData) | |
80 { | 91 { |
81 srandom(time(NULL)); | 92 srandom(time(NULL)); |
82 | 93 |
83 // Verify we are running with matrix enabled | 94 // Verify we are running with matrix enabled |
84 if(numMatrixFramesPerPeriod == 0 || numMatrixChannels < 4) { | 95 if(context->analogFrames == 0 || context->analogChannels < 4) { |
85 rt_printf("Error: this example needs the matrix enabled with at least 4 channels\n"); | 96 rt_printf("Error: this example needs the matrix enabled with at least 4 channels\n"); |
86 return false; | 97 return false; |
87 } | 98 } |
88 | 99 |
100 // Initialise audio variables | |
101 gAudioFramesPerMatrixFrame = context->audioFrames / context->analogFrames; | |
102 gOscillatorPhaseScaler = 2.0 * M_PI / context->audioSampleRate; | |
103 | |
89 // Initialise the screen buffers | 104 // Initialise the screen buffers |
90 gScreenBufferMaxLength = 2 * matrixSampleRate / gScreenFramesPerSecond; | 105 gScreenBufferMaxLength = 2 * context->analogSampleRate / gScreenFramesPerSecond; |
91 gScreenBuffer1 = new float[gScreenBufferMaxLength]; | 106 gScreenBuffer1 = new float[gScreenBufferMaxLength]; |
92 gScreenBuffer2 = new float[gScreenBufferMaxLength]; | 107 gScreenBuffer2 = new float[gScreenBufferMaxLength]; |
93 if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) { | 108 if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) { |
94 rt_printf("Error initialising screen buffers\n"); | 109 rt_printf("Error initialising screen buffers\n"); |
95 return false; | 110 return false; |
102 gScreenBufferNextUpdateLocation = 0; | 117 gScreenBufferNextUpdateLocation = 0; |
103 gScreenNextBufferReady = false; | 118 gScreenNextBufferReady = false; |
104 | 119 |
105 // Initialise the game | 120 // Initialise the game |
106 setupGame(gScreenWidth, gScreenHeight); | 121 setupGame(gScreenWidth, gScreenHeight); |
107 gGameFrameInterval = matrixSampleRate / gGameFramesPerSecond; | 122 gGameFrameInterval = context->analogSampleRate / gGameFramesPerSecond; |
108 gSamplesUntilNextFrame = gGameFrameInterval; | 123 gSamplesUntilNextFrame = gGameFrameInterval; |
109 | 124 |
110 // Initialise auxiliary tasks | 125 // Initialise auxiliary tasks |
111 if((gScreenUpdateTask = createAuxiliaryTaskLoop(&screen_update, 90, | 126 if((gScreenUpdateTask = BeagleRT_createAuxiliaryTask(&screen_update, 90, |
112 "beaglert-screen-update")) == 0) | 127 "beaglert-screen-update")) == 0) |
113 return false; | 128 return false; |
114 | 129 |
115 return true; | 130 return true; |
116 } | 131 } |
117 | 132 |
138 // render() is called regularly at the highest priority by the audio engine. | 153 // render() is called regularly at the highest priority by the audio engine. |
139 // Input and output are given from the audio hardware and the other | 154 // Input and output are given from the audio hardware and the other |
140 // ADCs and DACs (if available). If only audio is available, numMatrixFrames | 155 // ADCs and DACs (if available). If only audio is available, numMatrixFrames |
141 // will be 0. | 156 // will be 0. |
142 | 157 |
143 void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut, | 158 void render(BeagleRTContext *context, void *userData) |
144 uint16_t *matrixIn, uint16_t *matrixOut) | 159 { |
145 { | 160 int audioIndex = 0; |
146 for(int n = 0; n < numMatrixFrames; n++) { | 161 |
162 for(unsigned int n = 0; n < context->analogFrames; n++) { | |
163 for(int k = 0; k < gAudioFramesPerMatrixFrame; k++) { | |
164 // Render music and sound | |
165 float audioSample = 0; | |
166 | |
167 // Music plays in a loop | |
168 if(gMusicBuffer != 0 && gMusicBufferPointer >= 0) { | |
169 audioSample += gMusicBuffer[gMusicBufferPointer++]; | |
170 if(gMusicBufferPointer >= gMusicBufferLength) | |
171 gMusicBufferPointer = 0; | |
172 } | |
173 | |
174 // Sound effect plays until finished, then stops | |
175 if(gSoundBoomBuffer != 0 && gSoundBoomBufferPointer >= 0) { | |
176 audioSample += gSoundBoomBuffer[gSoundBoomBufferPointer++]; | |
177 if(gSoundBoomBufferPointer >= gSoundBoomBufferLength) | |
178 gSoundBoomBufferPointer = -1; | |
179 } | |
180 | |
181 // Oscillator plays to indicate projectile height | |
182 if(gameStatusProjectileInMotion()) { | |
183 audioSample += gSoundProjectileOscillatorGain * sinf(gSoundProjectileOscillatorPhase); | |
184 | |
185 gSoundProjectileOscillatorPhase += gOscillatorPhaseScaler * constrain(map(gameStatusProjectileHeight(), | |
186 1.0, 0, 300, 2000), 200, 6000); | |
187 if(gSoundProjectileOscillatorPhase > 2.0 * M_PI) | |
188 gSoundProjectileOscillatorPhase -= 2.0 * M_PI; | |
189 } | |
190 | |
191 context->audioOut[2*audioIndex] = context->audioOut[2*audioIndex + 1] = audioSample; | |
192 audioIndex++; | |
193 } | |
194 | |
147 // First-order lowpass filter to remove noise on launch FSR | 195 // First-order lowpass filter to remove noise on launch FSR |
148 float rawSample = analogRead(gInputLauncher, n); | 196 float rawSample = analogReadFrame(context, n, gInputLauncher); |
149 float launchSample = gLauncherFilterPole * gLauncherLastSample + | 197 float launchSample = gLauncherFilterPole * gLauncherLastSample + |
150 (1.0f - gLauncherFilterPole) * rawSample; | 198 (1.0f - gLauncherFilterPole) * rawSample; |
151 gLauncherLastSample = launchSample; | 199 gLauncherLastSample = launchSample; |
152 | 200 |
153 // Peak-detect on launch signal | 201 // Peak-detect on launch signal |
162 gLauncherTriggered = true; | 210 gLauncherTriggered = true; |
163 // Peak detected-- fire!! | 211 // Peak detected-- fire!! |
164 // Set both cannon strengths but only one will | 212 // Set both cannon strengths but only one will |
165 // fire depending on whose turn it is | 213 // fire depending on whose turn it is |
166 float strength = map(gLauncherPeakValue, | 214 float strength = map(gLauncherPeakValue, |
167 gLauncherMinimumPeak, MATRIX_MAX, | 215 gLauncherMinimumPeak, 1.0, |
168 0.5f, 10.0f); | 216 0.5f, 10.0f); |
169 setTank1CannonStrength(strength); | 217 setTank1CannonStrength(strength); |
170 setTank2CannonStrength(strength); | 218 setTank2CannonStrength(strength); |
171 fireProjectile(); | 219 fireProjectile(); |
172 } | 220 } |
177 | 225 |
178 if(--gSamplesUntilNextFrame <= 0) { | 226 if(--gSamplesUntilNextFrame <= 0) { |
179 // Update game physics and cannon angles | 227 // Update game physics and cannon angles |
180 gSamplesUntilNextFrame = gGameFrameInterval; | 228 gSamplesUntilNextFrame = gGameFrameInterval; |
181 | 229 |
182 setTank1CannonAngle(map(analogRead(gInputTank1Angle, n), | 230 setTank1CannonAngle(map(analogReadFrame(context, n, gInputTank1Angle), |
183 0, MATRIX_MAX, M_PI, 0)); | 231 0, 1.0, M_PI, 0)); |
184 setTank2CannonAngle(map(analogRead(gInputTank2Angle, n), | 232 setTank2CannonAngle(map(analogReadFrame(context, n, gInputTank2Angle), |
185 0, MATRIX_MAX, M_PI, 0)); | 233 0, 1.0, M_PI, 0)); |
186 nextGameFrame(); | 234 nextGameFrame(); |
235 | |
236 // Check for collision and start sound accordingly | |
237 if(gameStatusCollisionOccurred()) { | |
238 gSoundBoomBufferPointer = 0; | |
239 } | |
187 } | 240 } |
188 | 241 |
189 if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1 | 242 if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1 |
190 && gScreenNextBufferReady) { | 243 && gScreenNextBufferReady) { |
191 // Got to the end; swap buffers | 244 // Got to the end; swap buffers |
197 float x = gScreenBufferRead[gScreenBufferReadPointer++]; | 250 float x = gScreenBufferRead[gScreenBufferReadPointer++]; |
198 float y = gScreenBufferRead[gScreenBufferReadPointer++]; | 251 float y = gScreenBufferRead[gScreenBufferReadPointer++]; |
199 | 252 |
200 // Rescale screen coordinates to matrix ranges; invert the Y | 253 // Rescale screen coordinates to matrix ranges; invert the Y |
201 // coordinate to go from normal screen coordinates to scope coordinates | 254 // coordinate to go from normal screen coordinates to scope coordinates |
202 analogWrite(gOutputX, n, constrain(map(x, 0, gScreenWidth, 0, MATRIX_MAX), 0, MATRIX_MAX)); | 255 analogWriteFrameOnce(context, n, gOutputX, constrain(map(x, 0, gScreenWidth, 0, 1.0), 0, 1.0)); |
203 analogWrite(gOutputY, n, constrain(map(y, 0, gScreenHeight, MATRIX_MAX, 0), 0, MATRIX_MAX)); | 256 analogWriteFrameOnce(context, n, gOutputY, constrain(map(y, 0, gScreenHeight, 1.0, 0), 0, 1.0)); |
204 } | 257 } |
205 else { | 258 else { |
206 // Still not ready! Write 0 until something happens | 259 // Still not ready! Write 0 until something happens |
207 analogWrite(gOutputX, n, 0); | 260 analogWriteFrameOnce(context, n, gOutputX, 0); |
208 analogWrite(gOutputY, n, 0); | 261 analogWriteFrameOnce(context, n, gOutputY, 0); |
209 } | 262 } |
210 | 263 |
211 if(gameStatusWinner() != 0) { | 264 if(gameStatusWinner() != 0) { |
212 // Blink one LED to show who won | 265 // Blink one LED to show who won |
213 // Blink both LEDs when projectile is in motion | 266 // Blink both LEDs when projectile is in motion |
214 uint16_t val = (gSampleCounter % 4000 > 2000) ? MATRIX_MAX : 0; | 267 float val = (gSampleCounter % 4000 > 2000) ? 1.0 : 0; |
215 analogWrite(gOutputPlayer1LED, n, gameStatusWinner() == 1 ? val : 0); | 268 analogWriteFrameOnce(context, n, gOutputPlayer1LED, gameStatusWinner() == 1 ? val : 0); |
216 analogWrite(gOutputPlayer2LED, n, gameStatusWinner() == 2 ? val : 0); | 269 analogWriteFrameOnce(context, n, gOutputPlayer2LED, gameStatusWinner() == 2 ? val : 0); |
217 | 270 |
218 // After 5 seconds, restart the game | 271 // After 5 seconds, restart the game |
219 gSamplesSinceFinish++; | 272 gSamplesSinceFinish++; |
220 if(gSamplesSinceFinish > 22050*5) | 273 if(gSamplesSinceFinish > 22050*5) |
221 gGameShouldRestart = true; | 274 gGameShouldRestart = true; |
222 } | 275 } |
223 else if(gameStatusProjectileInMotion()) { | 276 else if(gameStatusProjectileInMotion()) { |
224 // Blink both LEDs when projectile is in motion | 277 // Blink both LEDs when projectile is in motion |
225 uint16_t val = (gSampleCounter % 2000 > 1000) ? MATRIX_MAX : 0; | 278 float val = (gSampleCounter % 2000 > 1000) ? 1.0 : 0; |
226 analogWrite(gOutputPlayer1LED, n, val); | 279 analogWriteFrameOnce(context, n, gOutputPlayer1LED, val); |
227 analogWrite(gOutputPlayer2LED, n, val); | 280 analogWriteFrameOnce(context, n, gOutputPlayer2LED, val); |
228 } | 281 } |
229 else if(gameStatusPlayer1Turn()) { | 282 else if(gameStatusPlayer1Turn()) { |
230 analogWrite(gOutputPlayer1LED, n, MATRIX_MAX); | 283 analogWriteFrameOnce(context, n, gOutputPlayer1LED, 1.0); |
231 analogWrite(gOutputPlayer2LED, n, 0); | 284 analogWriteFrameOnce(context, n, gOutputPlayer2LED, 0); |
232 } | 285 } |
233 else { | 286 else { |
234 analogWrite(gOutputPlayer2LED, n, MATRIX_MAX); | 287 analogWriteFrameOnce(context, n, gOutputPlayer2LED, 1.0); |
235 analogWrite(gOutputPlayer1LED, n, 0); | 288 analogWriteFrameOnce(context, n, gOutputPlayer1LED, 0); |
236 } | 289 } |
237 | 290 |
238 // Check if we have reached the point where we should next update | 291 // Check if we have reached the point where we should next update |
239 if(gScreenBufferReadPointer >= gScreenBufferNextUpdateLocation && | 292 if(gScreenBufferReadPointer >= gScreenBufferNextUpdateLocation && |
240 !gScreenNextBufferReady) { | 293 !gScreenNextBufferReady) { |
241 // Update the screen at lower priority than the audio thread | 294 // Update the screen at lower priority than the audio thread |
242 scheduleAuxiliaryTask(gScreenUpdateTask); | 295 BeagleRT_scheduleAuxiliaryTask(gScreenUpdateTask); |
243 } | 296 } |
244 | 297 |
245 gSampleCounter++; | 298 gSampleCounter++; |
246 } | 299 } |
247 } | 300 } |
260 | 313 |
261 // Flag it as ready to go | 314 // Flag it as ready to go |
262 gScreenNextBufferReady = true; | 315 gScreenNextBufferReady = true; |
263 } | 316 } |
264 | 317 |
265 // cleanup_render() is called once at the end, after the audio has stopped. | 318 // cleanup() is called once at the end, after the audio has stopped. |
266 // Release any resources that were allocated in initialise_render(). | 319 // Release any resources that were allocated in setup(). |
267 | 320 |
268 void cleanup_render() | 321 void cleanup(BeagleRTContext *context, void *userData) |
269 { | 322 { |
270 // Clean up the game state | 323 // Clean up the game state |
271 cleanupGame(); | 324 cleanupGame(); |
272 } | 325 } |