annotate projects/tank_wars/render.cpp @ 223:ec9425f728bc mergingClockSync

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