# HG changeset patch
# User andrewm
# Date 1430611817 -3600
# Node ID fbfeb5895efdbe2fbee15f7c512bb91a68cd9a38
# Parent 58eb99dac9214622d7e836a73aa8570bd059f72d
Updated tank wars demo for new API
diff -r 58eb99dac921 -r fbfeb5895efd .cproject
--- a/.cproject Thu Apr 30 17:43:08 2015 +0100
+++ b/.cproject Sun May 03 01:10:17 2015 +0100
@@ -5,12 +5,12 @@
+
-
@@ -92,6 +92,7 @@
+
@@ -102,12 +103,12 @@
+
-
@@ -140,13 +141,15 @@
-
+
@@ -174,6 +178,7 @@
+
diff -r 58eb99dac921 -r fbfeb5895efd projects/analogDigitalDemo/main.cpp
--- 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
#include
#include
-#include "../include/RTAudio.h"
+#include "../../include/RTAudio.h"
#include
#include
#include
diff -r 58eb99dac921 -r fbfeb5895efd projects/analogDigitalDemo/render.cpp
--- 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
#include
diff -r 58eb99dac921 -r fbfeb5895efd projects/tank_wars/game.cpp
--- 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()
{
diff -r 58eb99dac921 -r fbfeb5895efd projects/tank_wars/game.h
--- 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);
diff -r 58eb99dac921 -r fbfeb5895efd projects/tank_wars/main.cpp
--- 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
#include
#include
+#include
#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;
}
diff -r 58eb99dac921 -r fbfeb5895efd projects/tank_wars/render.cpp
--- 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
#include
+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