annotate projects/tank_wars/render.cpp @ 151:e9c9404e3d1f ClockSync

Pff partially working. No PID. When setting the audio clock on the bbb to 44098 the master and slave clock keep diverging instead of converging ...
author Giulio Moro <giuliomoro@yahoo.it>
date Tue, 22 Sep 2015 04:10:07 +0100
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 }