annotate projects/tank_wars/render.cpp @ 22:fbfeb5895efd matrix_gpio

Updated tank wars demo for new API
author andrewm
date Sun, 03 May 2015 01:10:17 +0100
parents 06f93bef7dd2
children 3c3a1357657d
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@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@10 81 // initialise_render() 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@22 90 bool initialise_render(int numAnalogChannels, int numDigitalChannels, int numAudioChannels,
andrewm@22 91 int numAnalogFramesPerPeriod,
andrewm@14 92 int numAudioFramesPerPeriod,
andrewm@22 93 float analogSampleRate, float audioSampleRate,
andrewm@14 94 void *userData)
andrewm@10 95 {
andrewm@10 96 srandom(time(NULL));
andrewm@10 97
andrewm@10 98 // Verify we are running with matrix enabled
andrewm@22 99 if(numAnalogFramesPerPeriod == 0 || numAnalogChannels < 4) {
andrewm@14 100 rt_printf("Error: this example needs the matrix enabled with at least 4 channels\n");
andrewm@10 101 return false;
andrewm@10 102 }
andrewm@10 103
andrewm@22 104 // Initialise audio variables
andrewm@22 105 gAudioFramesPerMatrixFrame = numAudioFramesPerPeriod / numAnalogFramesPerPeriod;
andrewm@22 106 gOscillatorPhaseScaler = 2.0 * M_PI / audioSampleRate;
andrewm@22 107
andrewm@10 108 // Initialise the screen buffers
andrewm@22 109 gScreenBufferMaxLength = 2 * analogSampleRate / gScreenFramesPerSecond;
andrewm@10 110 gScreenBuffer1 = new float[gScreenBufferMaxLength];
andrewm@10 111 gScreenBuffer2 = new float[gScreenBufferMaxLength];
andrewm@10 112 if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) {
andrewm@10 113 rt_printf("Error initialising screen buffers\n");
andrewm@10 114 return false;
andrewm@10 115 }
andrewm@10 116
andrewm@10 117 gScreenBufferRead = gScreenBuffer1;
andrewm@10 118 gScreenBufferWrite = gScreenBuffer2;
andrewm@10 119 gScreenBufferReadLength = gScreenBufferWriteLength = 0;
andrewm@10 120 gScreenBufferReadPointer = 0;
andrewm@10 121 gScreenBufferNextUpdateLocation = 0;
andrewm@10 122 gScreenNextBufferReady = false;
andrewm@10 123
andrewm@10 124 // Initialise the game
andrewm@10 125 setupGame(gScreenWidth, gScreenHeight);
andrewm@22 126 gGameFrameInterval = analogSampleRate / gGameFramesPerSecond;
andrewm@10 127 gSamplesUntilNextFrame = gGameFrameInterval;
andrewm@10 128
andrewm@10 129 // Initialise auxiliary tasks
andrewm@10 130 if((gScreenUpdateTask = createAuxiliaryTaskLoop(&screen_update, 90,
andrewm@10 131 "beaglert-screen-update")) == 0)
andrewm@10 132 return false;
andrewm@10 133
andrewm@10 134 return true;
andrewm@10 135 }
andrewm@10 136
andrewm@10 137 // Swap buffers on the screen
andrewm@10 138 void swap_buffers()
andrewm@10 139 {
andrewm@10 140 if(gScreenBufferRead == gScreenBuffer1) {
andrewm@10 141 gScreenBufferRead = gScreenBuffer2;
andrewm@10 142 gScreenBufferWrite = gScreenBuffer1;
andrewm@10 143 }
andrewm@10 144 else {
andrewm@10 145 gScreenBufferRead = gScreenBuffer1;
andrewm@10 146 gScreenBufferWrite = gScreenBuffer2;
andrewm@10 147 }
andrewm@10 148
andrewm@10 149 gScreenBufferReadLength = gScreenBufferWriteLength;
andrewm@10 150 gScreenBufferReadPointer = 0;
andrewm@10 151
andrewm@10 152 // Schedule next update for 3/4 of the way through the buffer
andrewm@10 153 gScreenBufferNextUpdateLocation = gScreenBufferReadLength * 0.75;
andrewm@10 154 gScreenNextBufferReady = false;
andrewm@10 155 }
andrewm@10 156
andrewm@10 157 // render() is called regularly at the highest priority by the audio engine.
andrewm@10 158 // Input and output are given from the audio hardware and the other
andrewm@10 159 // ADCs and DACs (if available). If only audio is available, numMatrixFrames
andrewm@10 160 // will be 0.
andrewm@10 161
andrewm@22 162 void render(int numAnalogFrames, int numDigitalFrames, int numAudioFrames, float *audioIn, float *audioOut,
andrewm@22 163 float *analogIn, float *analogOut, uint32_t *digital)
andrewm@10 164 {
andrewm@22 165 int audioIndex = 0;
andrewm@22 166
andrewm@22 167 for(int n = 0; n < numAnalogFrames; n++) {
andrewm@22 168 for(int k = 0; k < gAudioFramesPerMatrixFrame; k++) {
andrewm@22 169 // Render music and sound
andrewm@22 170 float audioSample = 0;
andrewm@22 171
andrewm@22 172 // Music plays in a loop
andrewm@22 173 if(gMusicBuffer != 0 && gMusicBufferPointer >= 0) {
andrewm@22 174 audioSample += gMusicBuffer[gMusicBufferPointer++];
andrewm@22 175 if(gMusicBufferPointer >= gMusicBufferLength)
andrewm@22 176 gMusicBufferPointer = 0;
andrewm@22 177 }
andrewm@22 178
andrewm@22 179 // Sound effect plays until finished, then stops
andrewm@22 180 if(gSoundBoomBuffer != 0 && gSoundBoomBufferPointer >= 0) {
andrewm@22 181 audioSample += gSoundBoomBuffer[gSoundBoomBufferPointer++];
andrewm@22 182 if(gSoundBoomBufferPointer >= gSoundBoomBufferLength)
andrewm@22 183 gSoundBoomBufferPointer = -1;
andrewm@22 184 }
andrewm@22 185
andrewm@22 186 // Oscillator plays to indicate projectile height
andrewm@22 187 if(gameStatusProjectileInMotion()) {
andrewm@22 188 audioSample += gSoundProjectileOscillatorGain * sinf(gSoundProjectileOscillatorPhase);
andrewm@22 189
andrewm@22 190 gSoundProjectileOscillatorPhase += gOscillatorPhaseScaler * constrain(map(gameStatusProjectileHeight(),
andrewm@22 191 1.0, 0, 300, 2000), 200, 6000);
andrewm@22 192 if(gSoundProjectileOscillatorPhase > 2.0 * M_PI)
andrewm@22 193 gSoundProjectileOscillatorPhase -= 2.0 * M_PI;
andrewm@22 194 }
andrewm@22 195
andrewm@22 196 audioOut[2*audioIndex] = audioOut[2*audioIndex + 1] = audioSample;
andrewm@22 197 audioIndex++;
andrewm@22 198 }
andrewm@22 199
andrewm@10 200 // First-order lowpass filter to remove noise on launch FSR
andrewm@22 201 float rawSample = AnalogRead(gInputLauncher, n);
andrewm@10 202 float launchSample = gLauncherFilterPole * gLauncherLastSample +
andrewm@10 203 (1.0f - gLauncherFilterPole) * rawSample;
andrewm@10 204 gLauncherLastSample = launchSample;
andrewm@10 205
andrewm@10 206 // Peak-detect on launch signal
andrewm@10 207 if(launchSample >= gLauncherPeakValue) {
andrewm@10 208 gLauncherPeakValue = launchSample;
andrewm@10 209 gLauncherTriggered = false;
andrewm@10 210 }
andrewm@10 211 else {
andrewm@10 212 if(gLauncherPeakValue - launchSample > gLauncherNoiseThreshold && !gLauncherTriggered) {
andrewm@10 213 // Detected a peak; is it big enough overall?
andrewm@10 214 if(gLauncherPeakValue >= gLauncherMinimumPeak) {
andrewm@10 215 gLauncherTriggered = true;
andrewm@10 216 // Peak detected-- fire!!
andrewm@10 217 // Set both cannon strengths but only one will
andrewm@10 218 // fire depending on whose turn it is
andrewm@10 219 float strength = map(gLauncherPeakValue,
andrewm@22 220 gLauncherMinimumPeak, 1.0,
andrewm@10 221 0.5f, 10.0f);
andrewm@10 222 setTank1CannonStrength(strength);
andrewm@10 223 setTank2CannonStrength(strength);
andrewm@10 224 fireProjectile();
andrewm@10 225 }
andrewm@10 226 }
andrewm@10 227
andrewm@10 228 gLauncherPeakValue *= gLauncherPeakFilterPole;
andrewm@10 229 }
andrewm@10 230
andrewm@10 231 if(--gSamplesUntilNextFrame <= 0) {
andrewm@10 232 // Update game physics and cannon angles
andrewm@10 233 gSamplesUntilNextFrame = gGameFrameInterval;
andrewm@10 234
andrewm@22 235 setTank1CannonAngle(map(AnalogRead(gInputTank1Angle, n),
andrewm@22 236 0, 1.0, M_PI, 0));
andrewm@22 237 setTank2CannonAngle(map(AnalogRead(gInputTank2Angle, n),
andrewm@22 238 0, 1.0, M_PI, 0));
andrewm@10 239 nextGameFrame();
andrewm@22 240
andrewm@22 241 // Check for collision and start sound accordingly
andrewm@22 242 if(gameStatusCollisionOccurred()) {
andrewm@22 243 gSoundBoomBufferPointer = 0;
andrewm@22 244 }
andrewm@10 245 }
andrewm@10 246
andrewm@10 247 if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1
andrewm@10 248 && gScreenNextBufferReady) {
andrewm@10 249 // Got to the end; swap buffers
andrewm@10 250 swap_buffers();
andrewm@10 251 }
andrewm@10 252
andrewm@10 253 // Push current screen buffer to the matrix output
andrewm@10 254 if(gScreenBufferReadPointer < gScreenBufferReadLength - 1) {
andrewm@10 255 float x = gScreenBufferRead[gScreenBufferReadPointer++];
andrewm@10 256 float y = gScreenBufferRead[gScreenBufferReadPointer++];
andrewm@10 257
andrewm@10 258 // Rescale screen coordinates to matrix ranges; invert the Y
andrewm@10 259 // coordinate to go from normal screen coordinates to scope coordinates
andrewm@22 260 AnalogWriteFrame(gOutputX, n, constrain(map(x, 0, gScreenWidth, 0, 1.0), 0, 1.0));
andrewm@22 261 AnalogWriteFrame(gOutputY, n, constrain(map(y, 0, gScreenHeight, 1.0, 0), 0, 1.0));
andrewm@10 262 }
andrewm@10 263 else {
andrewm@10 264 // Still not ready! Write 0 until something happens
andrewm@22 265 AnalogWriteFrame(gOutputX, n, 0);
andrewm@22 266 AnalogWriteFrame(gOutputY, n, 0);
andrewm@10 267 }
andrewm@10 268
andrewm@10 269 if(gameStatusWinner() != 0) {
andrewm@10 270 // Blink one LED to show who won
andrewm@10 271 // Blink both LEDs when projectile is in motion
andrewm@22 272 float val = (gSampleCounter % 4000 > 2000) ? 1.0 : 0;
andrewm@22 273 AnalogWriteFrame(gOutputPlayer1LED, n, gameStatusWinner() == 1 ? val : 0);
andrewm@22 274 AnalogWriteFrame(gOutputPlayer2LED, n, gameStatusWinner() == 2 ? val : 0);
andrewm@10 275
andrewm@10 276 // After 5 seconds, restart the game
andrewm@10 277 gSamplesSinceFinish++;
andrewm@10 278 if(gSamplesSinceFinish > 22050*5)
andrewm@10 279 gGameShouldRestart = true;
andrewm@10 280 }
andrewm@10 281 else if(gameStatusProjectileInMotion()) {
andrewm@10 282 // Blink both LEDs when projectile is in motion
andrewm@22 283 float val = (gSampleCounter % 2000 > 1000) ? 1.0 : 0;
andrewm@22 284 AnalogWriteFrame(gOutputPlayer1LED, n, val);
andrewm@22 285 AnalogWriteFrame(gOutputPlayer2LED, n, val);
andrewm@10 286 }
andrewm@10 287 else if(gameStatusPlayer1Turn()) {
andrewm@22 288 AnalogWriteFrame(gOutputPlayer1LED, n, 1.0);
andrewm@22 289 AnalogWriteFrame(gOutputPlayer2LED, n, 0);
andrewm@10 290 }
andrewm@10 291 else {
andrewm@22 292 AnalogWriteFrame(gOutputPlayer2LED, n, 1.0);
andrewm@22 293 AnalogWriteFrame(gOutputPlayer1LED, n, 0);
andrewm@10 294 }
andrewm@10 295
andrewm@10 296 // Check if we have reached the point where we should next update
andrewm@10 297 if(gScreenBufferReadPointer >= gScreenBufferNextUpdateLocation &&
andrewm@10 298 !gScreenNextBufferReady) {
andrewm@10 299 // Update the screen at lower priority than the audio thread
andrewm@10 300 scheduleAuxiliaryTask(gScreenUpdateTask);
andrewm@10 301 }
andrewm@10 302
andrewm@10 303 gSampleCounter++;
andrewm@10 304 }
andrewm@10 305 }
andrewm@10 306
andrewm@10 307 void screen_update()
andrewm@10 308 {
andrewm@10 309 // If we should restart, reinitialise the game
andrewm@10 310 if(gGameShouldRestart) {
andrewm@10 311 restartGame();
andrewm@10 312 gGameShouldRestart = false;
andrewm@10 313 gSamplesSinceFinish = 0;
andrewm@10 314 }
andrewm@10 315
andrewm@10 316 // Render the game based on the current state
andrewm@10 317 gScreenBufferWriteLength = drawGame(gScreenBufferWrite, gScreenBufferMaxLength);
andrewm@10 318
andrewm@10 319 // Flag it as ready to go
andrewm@10 320 gScreenNextBufferReady = true;
andrewm@10 321 }
andrewm@10 322
andrewm@10 323 // cleanup_render() is called once at the end, after the audio has stopped.
andrewm@10 324 // Release any resources that were allocated in initialise_render().
andrewm@10 325
andrewm@10 326 void cleanup_render()
andrewm@10 327 {
andrewm@10 328 // Clean up the game state
andrewm@10 329 cleanupGame();
andrewm@10 330 }