Mercurial > hg > beaglert
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++ " 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++ " 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