diff projects/tank_wars/render.cpp @ 67:472e892c6e41

Merge newapi into default
author Andrew McPherson <a.mcpherson@qmul.ac.uk>
date Fri, 17 Jul 2015 15:28:18 +0100
parents 3c3a1357657d
children 8d80eda512cd
line wrap: on
line diff
--- a/projects/tank_wars/render.cpp	Sun Feb 08 00:20:01 2015 +0000
+++ b/projects/tank_wars/render.cpp	Fri Jul 17 15:28:18 2015 +0100
@@ -6,14 +6,16 @@
  */
 
 
-#include "../../include/RTAudio.h"
-#include "../../include/Utilities.h"
+#include <BeagleRT.h>
+#include <Utilities.h>
 #include "game.h"
 #include <rtdk.h>
 #include <cmath>
 #include <cstdlib>
 #include <time.h>
 
+int gAudioFramesPerMatrixFrame = 2; // Ratio in audio to matrix sample rate
+
 int gInputTank1Angle = 0;		// Inputs for the cannon angles
 int gInputTank2Angle = 1;
 int gInputLauncher = 2;			// Input for launcher FSR
@@ -37,8 +39,8 @@
 float gLauncherFilterPole = 0.8;
 float gLauncherPeakValue = 0;
 float gLauncherPeakFilterPole = 0.999;
-float gLauncherNoiseThreshold = 0.01 * MATRIX_MAX;
-float gLauncherMinimumPeak = 0.1 * MATRIX_MAX;
+float gLauncherNoiseThreshold = 0.01;
+float gLauncherMinimumPeak = 0.1;
 bool gLauncherTriggered = false;
 
 // Screen update rate; affects buffer size. Actual contents of buffer
@@ -61,9 +63,22 @@
 // Auxiliary (low-priority) task for updating the screen
 AuxiliaryTask gScreenUpdateTask;
 
+// Buffers for music and sound effects
+extern float *gMusicBuffer;
+extern int gMusicBufferLength;
+extern float *gSoundBoomBuffer;
+extern int gSoundBoomBufferLength;
+
+// Current state for sound and music
+int gMusicBufferPointer = 0;	  // 0 means start of buffer...
+int gSoundBoomBufferPointer = -1; // -1 means don't play...
+float gSoundProjectileOscillatorPhase = 0;
+float gSoundProjectileOscillatorGain = 0.2;
+float gOscillatorPhaseScaler = 0;
+
 void screen_update();
 
-// initialise_render() is called once before the audio rendering starts.
+// setup() is called once before the audio rendering starts.
 // Use it to perform any initialisation and allocation which is dependent
 // on the period size or sample rate.
 //
@@ -72,22 +87,22 @@
 //
 // Return true on success; returning false halts the program.
 
-bool initialise_render(int numMatrixChannels, int numAudioChannels,
-					   int numMatrixFramesPerPeriod,
-					   int numAudioFramesPerPeriod,
-					   float matrixSampleRate, float audioSampleRate,
-					   void *userData)
+bool setup(BeagleRTContext *context, void *userData)
 {
 	srandom(time(NULL));
 
 	// Verify we are running with matrix enabled
-	if(numMatrixFramesPerPeriod == 0 || numMatrixChannels < 4) {
+	if(context->analogFrames == 0 || context->analogChannels < 4) {
 		rt_printf("Error: this example needs the matrix enabled with at least 4 channels\n");
 		return false;
 	}
 
+	// Initialise audio variables
+	gAudioFramesPerMatrixFrame = context->audioFrames / context->analogFrames;
+	gOscillatorPhaseScaler = 2.0 * M_PI / context->audioSampleRate;
+
 	// Initialise the screen buffers
-	gScreenBufferMaxLength = 2 * matrixSampleRate / gScreenFramesPerSecond;
+	gScreenBufferMaxLength = 2 * context->analogSampleRate / gScreenFramesPerSecond;
 	gScreenBuffer1 = new float[gScreenBufferMaxLength];
 	gScreenBuffer2 = new float[gScreenBufferMaxLength];
 	if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) {
@@ -104,12 +119,12 @@
 
 	// Initialise the game
 	setupGame(gScreenWidth, gScreenHeight);
-	gGameFrameInterval = matrixSampleRate / gGameFramesPerSecond;
+	gGameFrameInterval = context->analogSampleRate / gGameFramesPerSecond;
 	gSamplesUntilNextFrame = gGameFrameInterval;
 
 	// Initialise auxiliary tasks
-	if((gScreenUpdateTask = createAuxiliaryTaskLoop(&screen_update, 90,
-													"beaglert-screen-update")) == 0)
+	if((gScreenUpdateTask = BeagleRT_createAuxiliaryTask(&screen_update, 90,
+														 "beaglert-screen-update")) == 0)
 		return false;
 
 	return true;
@@ -140,12 +155,45 @@
 // ADCs and DACs (if available). If only audio is available, numMatrixFrames
 // will be 0.
 
-void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
-			uint16_t *matrixIn, uint16_t *matrixOut)
+void render(BeagleRTContext *context, void *userData)
 {
-	for(int n = 0; n < numMatrixFrames; n++) {
+	int audioIndex = 0;
+
+	for(unsigned int n = 0; n < context->analogFrames; n++) {
+		for(int k = 0; k < gAudioFramesPerMatrixFrame; k++) {
+			// Render music and sound
+			float audioSample = 0;
+
+			// Music plays in a loop
+			if(gMusicBuffer != 0 && gMusicBufferPointer >= 0) {
+				audioSample += gMusicBuffer[gMusicBufferPointer++];
+				if(gMusicBufferPointer >= gMusicBufferLength)
+					gMusicBufferPointer = 0;
+			}
+
+			// Sound effect plays until finished, then stops
+			if(gSoundBoomBuffer != 0 && gSoundBoomBufferPointer >= 0) {
+				audioSample += gSoundBoomBuffer[gSoundBoomBufferPointer++];
+				if(gSoundBoomBufferPointer >= gSoundBoomBufferLength)
+					gSoundBoomBufferPointer = -1;
+			}
+
+			// Oscillator plays to indicate projectile height
+			if(gameStatusProjectileInMotion()) {
+				audioSample += gSoundProjectileOscillatorGain * sinf(gSoundProjectileOscillatorPhase);
+
+				gSoundProjectileOscillatorPhase += gOscillatorPhaseScaler * constrain(map(gameStatusProjectileHeight(),
+						1.0, 0, 300, 2000), 200, 6000);
+				if(gSoundProjectileOscillatorPhase > 2.0 * M_PI)
+					gSoundProjectileOscillatorPhase -= 2.0 * M_PI;
+			}
+
+			context->audioOut[2*audioIndex] = context->audioOut[2*audioIndex + 1] = audioSample;
+			audioIndex++;
+		}
+
 		// First-order lowpass filter to remove noise on launch FSR
-		float rawSample = analogRead(gInputLauncher, n);
+		float rawSample = analogReadFrame(context, n, gInputLauncher);
 		float launchSample = gLauncherFilterPole * gLauncherLastSample +
 							(1.0f - gLauncherFilterPole) * rawSample;
 		gLauncherLastSample = launchSample;
@@ -164,7 +212,7 @@
 					// Set both cannon strengths but only one will
 					// fire depending on whose turn it is
 					float strength = map(gLauncherPeakValue,
-									     gLauncherMinimumPeak, MATRIX_MAX,
+									     gLauncherMinimumPeak, 1.0,
 										 0.5f, 10.0f);
 					setTank1CannonStrength(strength);
 					setTank2CannonStrength(strength);
@@ -179,11 +227,16 @@
 			// Update game physics and cannon angles
 			gSamplesUntilNextFrame = gGameFrameInterval;
 
-			setTank1CannonAngle(map(analogRead(gInputTank1Angle, n),
-									0, MATRIX_MAX, M_PI, 0));
-			setTank2CannonAngle(map(analogRead(gInputTank2Angle, n),
-									0, MATRIX_MAX, M_PI, 0));
+			setTank1CannonAngle(map(analogReadFrame(context, n, gInputTank1Angle),
+									0, 1.0, M_PI, 0));
+			setTank2CannonAngle(map(analogReadFrame(context, n, gInputTank2Angle),
+									0, 1.0, M_PI, 0));
 			nextGameFrame();
+
+			// Check for collision and start sound accordingly
+			if(gameStatusCollisionOccurred()) {
+				gSoundBoomBufferPointer = 0;
+			}
 		}
 
 		if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1
@@ -199,21 +252,21 @@
 
 			// Rescale screen coordinates to matrix ranges; invert the Y
 			// coordinate to go from normal screen coordinates to scope coordinates
-			analogWrite(gOutputX, n, constrain(map(x, 0, gScreenWidth, 0, MATRIX_MAX), 0, MATRIX_MAX));
-			analogWrite(gOutputY, n, constrain(map(y, 0, gScreenHeight, MATRIX_MAX, 0), 0, MATRIX_MAX));
+			analogWriteFrameOnce(context, n, gOutputX, constrain(map(x, 0, gScreenWidth, 0, 1.0), 0, 1.0));
+			analogWriteFrameOnce(context, n, gOutputY, constrain(map(y, 0, gScreenHeight, 1.0, 0), 0, 1.0));
 		}
 		else {
 			// Still not ready! Write 0 until something happens
-			analogWrite(gOutputX, n, 0);
-			analogWrite(gOutputY, n, 0);
+			analogWriteFrameOnce(context, n, gOutputX, 0);
+			analogWriteFrameOnce(context, n, gOutputY, 0);
 		}
 
 		if(gameStatusWinner() != 0) {
 			// Blink one LED to show who won
 			// Blink both LEDs when projectile is in motion
-			uint16_t val = (gSampleCounter % 4000 > 2000) ? MATRIX_MAX : 0;
-			analogWrite(gOutputPlayer1LED, n, gameStatusWinner() == 1 ? val : 0);
-			analogWrite(gOutputPlayer2LED, n, gameStatusWinner() == 2 ? val : 0);
+			float val = (gSampleCounter % 4000 > 2000) ? 1.0 : 0;
+			analogWriteFrameOnce(context, n, gOutputPlayer1LED, gameStatusWinner() == 1 ? val : 0);
+			analogWriteFrameOnce(context, n, gOutputPlayer2LED, gameStatusWinner() == 2 ? val : 0);
 
 			// After 5 seconds, restart the game
 			gSamplesSinceFinish++;
@@ -222,24 +275,24 @@
 		}
 		else if(gameStatusProjectileInMotion()) {
 			// Blink both LEDs when projectile is in motion
-			uint16_t val = (gSampleCounter % 2000 > 1000) ? MATRIX_MAX : 0;
-			analogWrite(gOutputPlayer1LED, n, val);
-			analogWrite(gOutputPlayer2LED, n, val);
+			float val = (gSampleCounter % 2000 > 1000) ? 1.0 : 0;
+			analogWriteFrameOnce(context, n, gOutputPlayer1LED, val);
+			analogWriteFrameOnce(context, n, gOutputPlayer2LED, val);
 		}
 		else if(gameStatusPlayer1Turn()) {
-			analogWrite(gOutputPlayer1LED, n, MATRIX_MAX);
-			analogWrite(gOutputPlayer2LED, n, 0);
+			analogWriteFrameOnce(context, n, gOutputPlayer1LED, 1.0);
+			analogWriteFrameOnce(context, n, gOutputPlayer2LED, 0);
 		}
 		else {
-			analogWrite(gOutputPlayer2LED, n, MATRIX_MAX);
-			analogWrite(gOutputPlayer1LED, n, 0);
+			analogWriteFrameOnce(context, n, gOutputPlayer2LED, 1.0);
+			analogWriteFrameOnce(context, n, gOutputPlayer1LED, 0);
 		}
 
 		// Check if we have reached the point where we should next update
 		if(gScreenBufferReadPointer >= gScreenBufferNextUpdateLocation &&
 		   !gScreenNextBufferReady) {
 			// Update the screen at lower priority than the audio thread
-			scheduleAuxiliaryTask(gScreenUpdateTask);
+			BeagleRT_scheduleAuxiliaryTask(gScreenUpdateTask);
 		}
 
 		gSampleCounter++;
@@ -262,10 +315,10 @@
 	gScreenNextBufferReady = true;
 }
 
-// cleanup_render() is called once at the end, after the audio has stopped.
-// Release any resources that were allocated in initialise_render().
+// cleanup() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in setup().
 
-void cleanup_render()
+void cleanup(BeagleRTContext *context, void *userData)
 {
 	// Clean up the game state
 	cleanupGame();