diff 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
line wrap: on
line diff
--- a/projects/tank_wars/render.cpp	Thu Apr 30 17:43:08 2015 +0100
+++ b/projects/tank_wars/render.cpp	Sun May 03 01:10:17 2015 +0100
@@ -14,6 +14,8 @@
 #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,6 +63,19 @@
 // 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.
@@ -72,22 +87,26 @@
 //
 // Return true on success; returning false halts the program.
 
-bool initialise_render(int numMatrixChannels, int numAudioChannels,
-					   int numMatrixFramesPerPeriod,
+bool initialise_render(int numAnalogChannels, int numDigitalChannels, int numAudioChannels,
+					   int numAnalogFramesPerPeriod,
 					   int numAudioFramesPerPeriod,
-					   float matrixSampleRate, float audioSampleRate,
+					   float analogSampleRate, float audioSampleRate,
 					   void *userData)
 {
 	srandom(time(NULL));
 
 	// Verify we are running with matrix enabled
-	if(numMatrixFramesPerPeriod == 0 || numMatrixChannels < 4) {
+	if(numAnalogFramesPerPeriod == 0 || numAnalogChannels < 4) {
 		rt_printf("Error: this example needs the matrix enabled with at least 4 channels\n");
 		return false;
 	}
 
+	// Initialise audio variables
+	gAudioFramesPerMatrixFrame = numAudioFramesPerPeriod / numAnalogFramesPerPeriod;
+	gOscillatorPhaseScaler = 2.0 * M_PI / audioSampleRate;
+
 	// Initialise the screen buffers
-	gScreenBufferMaxLength = 2 * matrixSampleRate / gScreenFramesPerSecond;
+	gScreenBufferMaxLength = 2 * analogSampleRate / gScreenFramesPerSecond;
 	gScreenBuffer1 = new float[gScreenBufferMaxLength];
 	gScreenBuffer2 = new float[gScreenBufferMaxLength];
 	if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) {
@@ -104,7 +123,7 @@
 
 	// Initialise the game
 	setupGame(gScreenWidth, gScreenHeight);
-	gGameFrameInterval = matrixSampleRate / gGameFramesPerSecond;
+	gGameFrameInterval = analogSampleRate / gGameFramesPerSecond;
 	gSamplesUntilNextFrame = gGameFrameInterval;
 
 	// Initialise auxiliary tasks
@@ -140,12 +159,46 @@
 // 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(int numAnalogFrames, int numDigitalFrames, int numAudioFrames, float *audioIn, float *audioOut,
+			float *analogIn, float *analogOut, uint32_t *digital)
 {
-	for(int n = 0; n < numMatrixFrames; n++) {
+	int audioIndex = 0;
+
+	for(int n = 0; n < numAnalogFrames; 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;
+			}
+
+			audioOut[2*audioIndex] = audioOut[2*audioIndex + 1] = audioSample;
+			audioIndex++;
+		}
+
 		// First-order lowpass filter to remove noise on launch FSR
-		float rawSample = analogRead(gInputLauncher, n);
+		float rawSample = AnalogRead(gInputLauncher, n);
 		float launchSample = gLauncherFilterPole * gLauncherLastSample +
 							(1.0f - gLauncherFilterPole) * rawSample;
 		gLauncherLastSample = launchSample;
@@ -164,7 +217,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 +232,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(AnalogRead(gInputTank1Angle, n),
+									0, 1.0, M_PI, 0));
+			setTank2CannonAngle(map(AnalogRead(gInputTank2Angle, n),
+									0, 1.0, M_PI, 0));
 			nextGameFrame();
+
+			// Check for collision and start sound accordingly
+			if(gameStatusCollisionOccurred()) {
+				gSoundBoomBufferPointer = 0;
+			}
 		}
 
 		if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1
@@ -199,21 +257,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));
+			AnalogWriteFrame(gOutputX, n, constrain(map(x, 0, gScreenWidth, 0, 1.0), 0, 1.0));
+			AnalogWriteFrame(gOutputY, n, 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);
+			AnalogWriteFrame(gOutputX, n, 0);
+			AnalogWriteFrame(gOutputY, n, 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;
+			AnalogWriteFrame(gOutputPlayer1LED, n, gameStatusWinner() == 1 ? val : 0);
+			AnalogWriteFrame(gOutputPlayer2LED, n, gameStatusWinner() == 2 ? val : 0);
 
 			// After 5 seconds, restart the game
 			gSamplesSinceFinish++;
@@ -222,17 +280,17 @@
 		}
 		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;
+			AnalogWriteFrame(gOutputPlayer1LED, n, val);
+			AnalogWriteFrame(gOutputPlayer2LED, n, val);
 		}
 		else if(gameStatusPlayer1Turn()) {
-			analogWrite(gOutputPlayer1LED, n, MATRIX_MAX);
-			analogWrite(gOutputPlayer2LED, n, 0);
+			AnalogWriteFrame(gOutputPlayer1LED, n, 1.0);
+			AnalogWriteFrame(gOutputPlayer2LED, n, 0);
 		}
 		else {
-			analogWrite(gOutputPlayer2LED, n, MATRIX_MAX);
-			analogWrite(gOutputPlayer1LED, n, 0);
+			AnalogWriteFrame(gOutputPlayer2LED, n, 1.0);
+			AnalogWriteFrame(gOutputPlayer1LED, n, 0);
 		}
 
 		// Check if we have reached the point where we should next update