annotate projects/tank_wars/render.cpp @ 12:a6beeba3a648

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