annotate projects/tank_wars/render.cpp @ 17:85e8b08a7471 matrix_gpio

- added example project matrix_gpio_demo - added defines for GPIO_HIGH and GPIO_LOW
author Giulio Moro <giuliomoro@yahoo.it>
date Mon, 27 Apr 2015 18:27:04 +0100
parents 06f93bef7dd2
children fbfeb5895efd
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@10 9 #include "../../include/RTAudio.h"
andrewm@10 10 #include "../../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@10 17 int gInputTank1Angle = 0; // Inputs for the cannon angles
andrewm@10 18 int gInputTank2Angle = 1;
andrewm@10 19 int gInputLauncher = 2; // Input for launcher FSR
andrewm@10 20
andrewm@10 21 int gOutputX = 0; // Outputs for the scope
andrewm@10 22 int gOutputY = 1;
andrewm@10 23 int gOutputPlayer1LED = 2;
andrewm@10 24 int gOutputPlayer2LED = 3;
andrewm@10 25
andrewm@10 26 int gGameFramesPerSecond = 60; // How often the physics are updated
andrewm@10 27 int gGameFrameInterval; // ...and in frames
andrewm@10 28 int gSamplesUntilNextFrame; // Counter until next update
andrewm@10 29 int gSamplesSinceFinish = 0; // How long since somebody won?
andrewm@10 30 bool gGameShouldRestart = false;// Whether we need to reinitiliase the game
andrewm@10 31
andrewm@10 32 // Counter for overall number of samples that have elapsed
andrewm@10 33 unsigned int gSampleCounter = 0;
andrewm@10 34
andrewm@10 35 // 1st-order filter and peak detector for launcher input
andrewm@10 36 float gLauncherLastSample = 0;
andrewm@10 37 float gLauncherFilterPole = 0.8;
andrewm@10 38 float gLauncherPeakValue = 0;
andrewm@10 39 float gLauncherPeakFilterPole = 0.999;
andrewm@10 40 float gLauncherNoiseThreshold = 0.01 * MATRIX_MAX;
andrewm@10 41 float gLauncherMinimumPeak = 0.1 * MATRIX_MAX;
andrewm@10 42 bool gLauncherTriggered = false;
andrewm@10 43
andrewm@10 44 // Screen update rate; affects buffer size. Actual contents of buffer
andrewm@10 45 // may be smaller than this
andrewm@10 46 int gScreenWidth = 512;
andrewm@10 47 int gScreenHeight = 512;
andrewm@10 48 int gScreenFramesPerSecond = 25;
andrewm@10 49
andrewm@10 50 // Double buffer for rendering screen. Each is an interleaved buffer
andrewm@10 51 // of XY data.
andrewm@10 52 float *gScreenBuffer1, *gScreenBuffer2;
andrewm@10 53 float *gScreenBufferWrite, *gScreenBufferRead;
andrewm@10 54 int gScreenBufferMaxLength; // What is the total buffer allocated?
andrewm@10 55 int gScreenBufferReadLength; // How long is the read buffer?
andrewm@10 56 int gScreenBufferWriteLength; // How long is the write (next) buffer?
andrewm@10 57 int gScreenBufferReadPointer; // Where are we in the read buffer now?
andrewm@10 58 int gScreenBufferNextUpdateLocation; // When should we render the next buffer?
andrewm@10 59 bool gScreenNextBufferReady; // Is the next buffer ready to go?
andrewm@10 60
andrewm@10 61 // Auxiliary (low-priority) task for updating the screen
andrewm@10 62 AuxiliaryTask gScreenUpdateTask;
andrewm@10 63
andrewm@10 64 void screen_update();
andrewm@10 65
andrewm@10 66 // initialise_render() is called once before the audio rendering starts.
andrewm@10 67 // Use it to perform any initialisation and allocation which is dependent
andrewm@10 68 // on the period size or sample rate.
andrewm@10 69 //
andrewm@10 70 // userData holds an opaque pointer to a data structure that was passed
andrewm@10 71 // in from the call to initAudio().
andrewm@10 72 //
andrewm@10 73 // Return true on success; returning false halts the program.
andrewm@10 74
andrewm@14 75 bool initialise_render(int numMatrixChannels, int numAudioChannels,
andrewm@14 76 int numMatrixFramesPerPeriod,
andrewm@14 77 int numAudioFramesPerPeriod,
andrewm@14 78 float matrixSampleRate, float audioSampleRate,
andrewm@14 79 void *userData)
andrewm@10 80 {
andrewm@10 81 srandom(time(NULL));
andrewm@10 82
andrewm@10 83 // Verify we are running with matrix enabled
andrewm@14 84 if(numMatrixFramesPerPeriod == 0 || numMatrixChannels < 4) {
andrewm@14 85 rt_printf("Error: this example needs the matrix enabled with at least 4 channels\n");
andrewm@10 86 return false;
andrewm@10 87 }
andrewm@10 88
andrewm@10 89 // Initialise the screen buffers
andrewm@10 90 gScreenBufferMaxLength = 2 * matrixSampleRate / gScreenFramesPerSecond;
andrewm@10 91 gScreenBuffer1 = new float[gScreenBufferMaxLength];
andrewm@10 92 gScreenBuffer2 = new float[gScreenBufferMaxLength];
andrewm@10 93 if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) {
andrewm@10 94 rt_printf("Error initialising screen buffers\n");
andrewm@10 95 return false;
andrewm@10 96 }
andrewm@10 97
andrewm@10 98 gScreenBufferRead = gScreenBuffer1;
andrewm@10 99 gScreenBufferWrite = gScreenBuffer2;
andrewm@10 100 gScreenBufferReadLength = gScreenBufferWriteLength = 0;
andrewm@10 101 gScreenBufferReadPointer = 0;
andrewm@10 102 gScreenBufferNextUpdateLocation = 0;
andrewm@10 103 gScreenNextBufferReady = false;
andrewm@10 104
andrewm@10 105 // Initialise the game
andrewm@10 106 setupGame(gScreenWidth, gScreenHeight);
andrewm@10 107 gGameFrameInterval = matrixSampleRate / gGameFramesPerSecond;
andrewm@10 108 gSamplesUntilNextFrame = gGameFrameInterval;
andrewm@10 109
andrewm@10 110 // Initialise auxiliary tasks
andrewm@10 111 if((gScreenUpdateTask = createAuxiliaryTaskLoop(&screen_update, 90,
andrewm@10 112 "beaglert-screen-update")) == 0)
andrewm@10 113 return false;
andrewm@10 114
andrewm@10 115 return true;
andrewm@10 116 }
andrewm@10 117
andrewm@10 118 // Swap buffers on the screen
andrewm@10 119 void swap_buffers()
andrewm@10 120 {
andrewm@10 121 if(gScreenBufferRead == gScreenBuffer1) {
andrewm@10 122 gScreenBufferRead = gScreenBuffer2;
andrewm@10 123 gScreenBufferWrite = gScreenBuffer1;
andrewm@10 124 }
andrewm@10 125 else {
andrewm@10 126 gScreenBufferRead = gScreenBuffer1;
andrewm@10 127 gScreenBufferWrite = gScreenBuffer2;
andrewm@10 128 }
andrewm@10 129
andrewm@10 130 gScreenBufferReadLength = gScreenBufferWriteLength;
andrewm@10 131 gScreenBufferReadPointer = 0;
andrewm@10 132
andrewm@10 133 // Schedule next update for 3/4 of the way through the buffer
andrewm@10 134 gScreenBufferNextUpdateLocation = gScreenBufferReadLength * 0.75;
andrewm@10 135 gScreenNextBufferReady = false;
andrewm@10 136 }
andrewm@10 137
andrewm@10 138 // render() is called regularly at the highest priority by the audio engine.
andrewm@10 139 // Input and output are given from the audio hardware and the other
andrewm@10 140 // ADCs and DACs (if available). If only audio is available, numMatrixFrames
andrewm@10 141 // will be 0.
andrewm@10 142
andrewm@10 143 void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
andrewm@10 144 uint16_t *matrixIn, uint16_t *matrixOut)
andrewm@10 145 {
andrewm@10 146 for(int n = 0; n < numMatrixFrames; n++) {
andrewm@10 147 // First-order lowpass filter to remove noise on launch FSR
andrewm@10 148 float rawSample = analogRead(gInputLauncher, n);
andrewm@10 149 float launchSample = gLauncherFilterPole * gLauncherLastSample +
andrewm@10 150 (1.0f - gLauncherFilterPole) * rawSample;
andrewm@10 151 gLauncherLastSample = launchSample;
andrewm@10 152
andrewm@10 153 // Peak-detect on launch signal
andrewm@10 154 if(launchSample >= gLauncherPeakValue) {
andrewm@10 155 gLauncherPeakValue = launchSample;
andrewm@10 156 gLauncherTriggered = false;
andrewm@10 157 }
andrewm@10 158 else {
andrewm@10 159 if(gLauncherPeakValue - launchSample > gLauncherNoiseThreshold && !gLauncherTriggered) {
andrewm@10 160 // Detected a peak; is it big enough overall?
andrewm@10 161 if(gLauncherPeakValue >= gLauncherMinimumPeak) {
andrewm@10 162 gLauncherTriggered = true;
andrewm@10 163 // Peak detected-- fire!!
andrewm@10 164 // Set both cannon strengths but only one will
andrewm@10 165 // fire depending on whose turn it is
andrewm@10 166 float strength = map(gLauncherPeakValue,
andrewm@10 167 gLauncherMinimumPeak, MATRIX_MAX,
andrewm@10 168 0.5f, 10.0f);
andrewm@10 169 setTank1CannonStrength(strength);
andrewm@10 170 setTank2CannonStrength(strength);
andrewm@10 171 fireProjectile();
andrewm@10 172 }
andrewm@10 173 }
andrewm@10 174
andrewm@10 175 gLauncherPeakValue *= gLauncherPeakFilterPole;
andrewm@10 176 }
andrewm@10 177
andrewm@10 178 if(--gSamplesUntilNextFrame <= 0) {
andrewm@10 179 // Update game physics and cannon angles
andrewm@10 180 gSamplesUntilNextFrame = gGameFrameInterval;
andrewm@10 181
andrewm@10 182 setTank1CannonAngle(map(analogRead(gInputTank1Angle, n),
andrewm@10 183 0, MATRIX_MAX, M_PI, 0));
andrewm@10 184 setTank2CannonAngle(map(analogRead(gInputTank2Angle, n),
andrewm@10 185 0, MATRIX_MAX, M_PI, 0));
andrewm@10 186 nextGameFrame();
andrewm@10 187 }
andrewm@10 188
andrewm@10 189 if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1
andrewm@10 190 && gScreenNextBufferReady) {
andrewm@10 191 // Got to the end; swap buffers
andrewm@10 192 swap_buffers();
andrewm@10 193 }
andrewm@10 194
andrewm@10 195 // Push current screen buffer to the matrix output
andrewm@10 196 if(gScreenBufferReadPointer < gScreenBufferReadLength - 1) {
andrewm@10 197 float x = gScreenBufferRead[gScreenBufferReadPointer++];
andrewm@10 198 float y = gScreenBufferRead[gScreenBufferReadPointer++];
andrewm@10 199
andrewm@10 200 // Rescale screen coordinates to matrix ranges; invert the Y
andrewm@10 201 // coordinate to go from normal screen coordinates to scope coordinates
andrewm@10 202 analogWrite(gOutputX, n, constrain(map(x, 0, gScreenWidth, 0, MATRIX_MAX), 0, MATRIX_MAX));
andrewm@10 203 analogWrite(gOutputY, n, constrain(map(y, 0, gScreenHeight, MATRIX_MAX, 0), 0, MATRIX_MAX));
andrewm@10 204 }
andrewm@10 205 else {
andrewm@10 206 // Still not ready! Write 0 until something happens
andrewm@10 207 analogWrite(gOutputX, n, 0);
andrewm@10 208 analogWrite(gOutputY, n, 0);
andrewm@10 209 }
andrewm@10 210
andrewm@10 211 if(gameStatusWinner() != 0) {
andrewm@10 212 // Blink one LED to show who won
andrewm@10 213 // Blink both LEDs when projectile is in motion
andrewm@10 214 uint16_t val = (gSampleCounter % 4000 > 2000) ? MATRIX_MAX : 0;
andrewm@10 215 analogWrite(gOutputPlayer1LED, n, gameStatusWinner() == 1 ? val : 0);
andrewm@10 216 analogWrite(gOutputPlayer2LED, n, gameStatusWinner() == 2 ? val : 0);
andrewm@10 217
andrewm@10 218 // After 5 seconds, restart the game
andrewm@10 219 gSamplesSinceFinish++;
andrewm@10 220 if(gSamplesSinceFinish > 22050*5)
andrewm@10 221 gGameShouldRestart = true;
andrewm@10 222 }
andrewm@10 223 else if(gameStatusProjectileInMotion()) {
andrewm@10 224 // Blink both LEDs when projectile is in motion
andrewm@10 225 uint16_t val = (gSampleCounter % 2000 > 1000) ? MATRIX_MAX : 0;
andrewm@10 226 analogWrite(gOutputPlayer1LED, n, val);
andrewm@10 227 analogWrite(gOutputPlayer2LED, n, val);
andrewm@10 228 }
andrewm@10 229 else if(gameStatusPlayer1Turn()) {
andrewm@10 230 analogWrite(gOutputPlayer1LED, n, MATRIX_MAX);
andrewm@10 231 analogWrite(gOutputPlayer2LED, n, 0);
andrewm@10 232 }
andrewm@10 233 else {
andrewm@10 234 analogWrite(gOutputPlayer2LED, n, MATRIX_MAX);
andrewm@10 235 analogWrite(gOutputPlayer1LED, n, 0);
andrewm@10 236 }
andrewm@10 237
andrewm@10 238 // Check if we have reached the point where we should next update
andrewm@10 239 if(gScreenBufferReadPointer >= gScreenBufferNextUpdateLocation &&
andrewm@10 240 !gScreenNextBufferReady) {
andrewm@10 241 // Update the screen at lower priority than the audio thread
andrewm@10 242 scheduleAuxiliaryTask(gScreenUpdateTask);
andrewm@10 243 }
andrewm@10 244
andrewm@10 245 gSampleCounter++;
andrewm@10 246 }
andrewm@10 247 }
andrewm@10 248
andrewm@10 249 void screen_update()
andrewm@10 250 {
andrewm@10 251 // If we should restart, reinitialise the game
andrewm@10 252 if(gGameShouldRestart) {
andrewm@10 253 restartGame();
andrewm@10 254 gGameShouldRestart = false;
andrewm@10 255 gSamplesSinceFinish = 0;
andrewm@10 256 }
andrewm@10 257
andrewm@10 258 // Render the game based on the current state
andrewm@10 259 gScreenBufferWriteLength = drawGame(gScreenBufferWrite, gScreenBufferMaxLength);
andrewm@10 260
andrewm@10 261 // Flag it as ready to go
andrewm@10 262 gScreenNextBufferReady = true;
andrewm@10 263 }
andrewm@10 264
andrewm@10 265 // cleanup_render() is called once at the end, after the audio has stopped.
andrewm@10 266 // Release any resources that were allocated in initialise_render().
andrewm@10 267
andrewm@10 268 void cleanup_render()
andrewm@10 269 {
andrewm@10 270 // Clean up the game state
andrewm@10 271 cleanupGame();
andrewm@10 272 }