changeset 22:fbfeb5895efd matrix_gpio

Updated tank wars demo for new API
author andrewm
date Sun, 03 May 2015 01:10:17 +0100
parents 58eb99dac921
children 182ae9367104
files .cproject projects/analogDigitalDemo/main.cpp projects/analogDigitalDemo/render.cpp projects/tank_wars/game.cpp projects/tank_wars/game.h projects/tank_wars/main.cpp projects/tank_wars/render.cpp
diffstat 7 files changed, 180 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/.cproject	Thu Apr 30 17:43:08 2015 +0100
+++ b/.cproject	Sun May 03 01:10:17 2015 +0100
@@ -5,12 +5,12 @@
 			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.exe.debug.528876549" moduleId="org.eclipse.cdt.core.settings" name="Debug">
 				<externalSettings/>
 				<extensions>
+					<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
 					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
 					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
 				</extensions>
 			</storageModule>
 			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
@@ -92,6 +92,7 @@
 					<sourceEntries>
 						<entry excluding="audio_routines_old.S" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="core"/>
 						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="include"/>
+						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="projects/tank_wars"/>
 						<entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="source"/>
 					</sourceEntries>
 				</configuration>
@@ -102,12 +103,12 @@
 			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.exe.release.1521194538" moduleId="org.eclipse.cdt.core.settings" name="Release">
 				<externalSettings/>
 				<extensions>
+					<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
 					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
 					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
 				</extensions>
 			</storageModule>
 			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
@@ -140,13 +141,15 @@
 									<listOptionValue builtIn="false" value="/import/teaching/ECS732/arm-gcc/arm-linux-gnueabihf/include/xenomai/include"/>
 									<listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include/ne10"/>
 								</option>
-								<option id="gnu.c.compiler.option.include.files.40806287" name="Include files (-include)" superClass="gnu.c.compiler.option.include.files" valueType="includeFiles"/>
+								<option id="gnu.c.compiler.option.include.files.40806287" name="Include files (-include)" superClass="gnu.c.compiler.option.include.files"/>
 								<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.574072828" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
 							</tool>
 							<tool command="arm-linux-gnueabihf-g++&#10;" id="cdt.managedbuild.tool.gnu.c.linker.exe.release.281634187" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.exe.release"/>
 							<tool command="arm-linux-gnueabihf-g++&#10;" id="cdt.managedbuild.tool.gnu.cpp.linker.exe.release.929570421" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.exe.release">
 								<option id="gnu.cpp.link.option.paths.277042583" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths">
 									<listOptionValue builtIn="false" value="/usr/xenomai/lib"/>
+									<listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/lib"/>
+									<listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/lib/xenomai"/>
 									<listOptionValue builtIn="false" value="/usr/local/linaro/arm-linux-gnueabihf/include/xenomai/lib"/>
 									<listOptionValue builtIn="false" value="/import/teaching/ECS732/arm-gcc/arm-linux-gnueabihf/lib"/>
 									<listOptionValue builtIn="false" value="/usr/lib/arm-linux-gnueabihf"/>
@@ -154,6 +157,7 @@
 								</option>
 								<option id="gnu.cpp.link.option.libs.1930118082" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" valueType="libs">
 									<listOptionValue builtIn="false" value="rt"/>
+									<listOptionValue builtIn="false" value="sndfile"/>
 									<listOptionValue builtIn="false" value="native"/>
 									<listOptionValue builtIn="false" value="xenomai"/>
 								</option>
@@ -174,6 +178,7 @@
 					<sourceEntries>
 						<entry excluding="audio_routines_old.S" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="core"/>
 						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="include"/>
+						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="projects/tank_wars"/>
 						<entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="source"/>
 					</sourceEntries>
 				</configuration>
--- a/projects/analogDigitalDemo/main.cpp	Thu Apr 30 17:43:08 2015 +0100
+++ b/projects/analogDigitalDemo/main.cpp	Sun May 03 01:10:17 2015 +0100
@@ -14,7 +14,7 @@
 #include <libgen.h>
 #include <signal.h>
 #include <getopt.h>
-#include "../include/RTAudio.h"
+#include "../../include/RTAudio.h"
 #include <unistd.h>
 #include <stdlib.h>
 #include <fcntl.h>
--- a/projects/analogDigitalDemo/render.cpp	Thu Apr 30 17:43:08 2015 +0100
+++ b/projects/analogDigitalDemo/render.cpp	Sun May 03 01:10:17 2015 +0100
@@ -7,7 +7,7 @@
  * Queen Mary, University of London
  */
 
-#include "../include/render.h"
+#include "../../include/render.h"
 #include <cmath>
 #include <rtdk.h>
 
--- a/projects/tank_wars/game.cpp	Thu Apr 30 17:43:08 2015 +0100
+++ b/projects/tank_wars/game.cpp	Sun May 03 01:10:17 2015 +0100
@@ -33,6 +33,9 @@
 float projectilePositionX, projectilePositionY;
 float projectileVelocityX, projectileVelocityY;
 
+// Infor needed for sound rendering
+bool collisionJustOccurred = false;
+
 // Useful utility function for generating random floating-point values
 float randomFloat(float low, float hi)
 {
@@ -101,6 +104,7 @@
 		<= tankRadius * tankRadius)
 	{
 		projectileInMotion = false;
+		collisionJustOccurred = true;
 		playerHasWon = 2;
 	}
 	else if((tank2X - projectilePositionX)*(tank2X - projectilePositionX) +
@@ -108,16 +112,19 @@
 		<= tankRadius * tankRadius)
 	{
 		projectileInMotion = false;
+		collisionJustOccurred = true;
 		playerHasWon = 1;
 	}
 	else if(projectilePositionX < 0 || projectilePositionX >= screenWidth) {
 		// Check collision whether projectile has exited the screen to the left or right
 		projectileInMotion = false;
+		collisionJustOccurred = true;
 		nextPlayersTurn();
 	}
 	else if(projectilePositionY >= groundLevel[(int)floorf(projectilePositionX)]) {
 		// Check for projectile collision with ground
 		projectileInMotion = false;
+		collisionJustOccurred = true;
 		nextPlayersTurn();
 	}
 }
@@ -185,6 +192,20 @@
 	return playerHasWon;
 }
 
+bool gameStatusCollisionOccurred()
+{
+	if(collisionJustOccurred) {
+		collisionJustOccurred = false;
+		return true;
+	}
+	return false;
+}
+
+float gameStatusProjectileHeight()
+{
+	return projectilePositionY / (float)screenHeight;
+}
+
 // Clean up any allocated memory for the game
 void cleanupGame()
 {
--- a/projects/tank_wars/game.h	Thu Apr 30 17:43:08 2015 +0100
+++ b/projects/tank_wars/game.h	Sun May 03 01:10:17 2015 +0100
@@ -26,6 +26,8 @@
 bool gameStatusPlayer1Turn();
 bool gameStatusProjectileInMotion();
 int gameStatusWinner();
+bool gameStatusCollisionOccurred();
+float gameStatusProjectileHeight();
 
 // Render screen; returns length of buffer used
 int drawGame(float *buffer, int bufferSize);
--- a/projects/tank_wars/main.cpp	Thu Apr 30 17:43:08 2015 +0100
+++ b/projects/tank_wars/main.cpp	Sun May 03 01:10:17 2015 +0100
@@ -10,12 +10,54 @@
 #include <libgen.h>
 #include <signal.h>
 #include <getopt.h>
+#include <sndfile.h>
 #include "../../include/RTAudio.h"
 
 extern int gScreenFramesPerSecond;
 
+float *gMusicBuffer = 0;
+int gMusicBufferLength = 0;
+float *gSoundBoomBuffer = 0;
+int gSoundBoomBufferLength = 0;
+
 using namespace std;
 
+// Load a sound sample from file
+int loadSoundFile(const string& path, float **buffer, int *bufferLength)
+{
+	SNDFILE *sndfile ;
+	SF_INFO sfinfo ;
+
+	if (!(sndfile = sf_open (path.c_str(), SFM_READ, &sfinfo))) {
+		cout << "Couldn't open file " << path << endl;
+		return 1;
+	}
+
+	int numChan = sfinfo.channels;
+	if(numChan != 1)
+	{
+		cout << "Error: " << path << " is not a mono file" << endl;
+		return 1;
+	}
+
+	*bufferLength = sfinfo.frames * numChan;
+	*buffer = new float[*bufferLength];
+	if(*buffer == 0){
+		cout << "Could not allocate buffer" << endl;
+		return 1;
+	}
+
+	int subformat = sfinfo.format & SF_FORMAT_SUBMASK;
+	int readcount = sf_read_float(sndfile, *buffer, *bufferLength);
+
+	// Pad with zeros in case we couldn't read whole file
+	for(int k = readcount; k < *bufferLength; k++)
+		(*buffer)[k] = 0;
+
+	sf_close(sndfile);
+	return 0;
+}
+
 // Handle Ctrl-C by requesting that the audio rendering stop
 void interrupt_handler(int var)
 {
@@ -36,6 +78,8 @@
 int main(int argc, char *argv[])
 {
 	RTAudioSettings settings;	// Standard audio settings
+	string musicFileName = "music.wav";
+	string soundBoomFileName = "boom.wav";
 
 	struct option customOptions[] =
 	{
@@ -70,6 +114,14 @@
 		}
 	}
 
+	// Load the sound files
+	if(loadSoundFile(musicFileName, &gMusicBuffer, &gMusicBufferLength) != 0) {
+		cout << "Warning: unable to load sound file " << musicFileName << endl;
+	}
+	if(loadSoundFile(soundBoomFileName, &gSoundBoomBuffer, &gSoundBoomBufferLength) != 0) {
+		cout << "Warning: unable to load sound file " << soundBoomFileName << endl;
+	}
+
 	// Initialise the PRU audio device
 	if(BeagleRT_initAudio(&settings, 0) != 0) {
 		cout << "Error: unable to initialise audio" << endl;
@@ -97,6 +149,12 @@
 	// Clean up any resources allocated for audio
 	BeagleRT_cleanupAudio();
 
+	// Release sound files
+	if(gMusicBuffer != 0)
+		free(gMusicBuffer);
+	if(gSoundBoomBuffer != 0)
+		free(gSoundBoomBuffer);
+
 	// All done!
 	return 0;
 }
--- 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