Mercurial > hg > beaglert
changeset 497:37f10f61433a prerelease
merge
author | Giulio Moro <giuliomoro@yahoo.it> |
---|---|
date | Tue, 21 Jun 2016 20:01:26 +0100 |
parents | eb237b131ec7 (current diff) 5779ed0562ac (diff) |
children | c932df224fd3 |
files | examples/10-Instruments/oscillator_bank/audio_routines.S examples/10-Instruments/oscillator_bank/main.cpp examples/10-Instruments/oscillator_bank/render.cpp examples/10-Instruments/tank_wars/game.cpp examples/10-Instruments/tank_wars/game.h examples/10-Instruments/tank_wars/main.cpp examples/10-Instruments/tank_wars/render.cpp examples/10-Instruments/tank_wars/vector_graphics.cpp examples/10-Instruments/tank_wars/vector_graphics.h |
diffstat | 19 files changed, 1509 insertions(+), 1500 deletions(-) [+] |
line wrap: on
line diff
--- a/examples/05-Communication/OSC/render.cpp Tue Jun 21 20:00:12 2016 +0100 +++ b/examples/05-Communication/OSC/render.cpp Tue Jun 21 20:01:26 2016 +0100 @@ -31,40 +31,40 @@ // this example is designed to be run alongside resources/osc/osc.js -// parse messages recieved by OSC Server +// parse messages received by OSC Server // msg is Message class of oscpkt: http://gruntthepeon.free.fr/oscpkt/ void parseMessage(oscpkt::Message msg){ - rt_printf("recieved message to: %s\n", msg.addressPattern().c_str()); + rt_printf("received message to: %s\n", msg.addressPattern().c_str()); int intArg; float floatArg; if (msg.match("/osc-test").popInt32(intArg).popFloat(floatArg).isOkNoMoreArgs()){ - rt_printf("recieved int %i and float %f\n", intArg, floatArg); + rt_printf("received int %i and float %f\n", intArg, floatArg); } } bool setup(BelaContext *context, void *userData) { - // setup the OSC server to recieve on port 7562 + // setup the OSC server to receive on port 7562 oscServer.setup(7562); // setup the OSC client to send on port 7563 oscClient.setup(7563); // the following code sends an OSC message to address /osc-setup // then waits 1 second for a reply on /osc-setup-reply - bool handshakeRecieved = false; + bool handshakeReceived = false; oscClient.sendMessageNow(oscClient.newMessage.to("/osc-setup").end()); - oscServer.recieveMessageNow(1000); + oscServer.receiveMessageNow(1000); while (oscServer.messageWaiting()){ if (oscServer.popMessage().match("/osc-setup-reply")){ - handshakeRecieved = true; + handshakeReceived = true; } } - if (handshakeRecieved){ - rt_printf("handshake recieved!\n"); + if (handshakeReceived){ + rt_printf("handshake received!\n"); } else { rt_printf("timeout!\n"); } @@ -74,10 +74,10 @@ void render(BelaContext *context, void *userData) { - // recieve OSC messages, parse them, and send back an acknowledgment + // receive OSC messages, parse them, and send back an acknowledgment while (oscServer.messageWaiting()){ parseMessage(oscServer.popMessage()); - oscClient.queueMessage(oscClient.newMessage.to("/osc-acknowledge").add(5).add(4.2f).add(std::string("OSC message recieved")).end()); + oscClient.queueMessage(oscClient.newMessage.to("/osc-acknowledge").add(5).add(4.2f).add(std::string("OSC message received")).end()); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/10-Instruments/oscillator-bank/audio_routines.S Tue Jun 21 20:01:26 2016 +0100 @@ -0,0 +1,161 @@ +@ +@ audio_routines.S +@ +@ NEON-based functions for time-critical audio processing +@ +@ Andrew McPherson 2014 +@ Queen Mary University of London +@ + + .syntax unified + .arch armv7-a + .fpu neon + +@ void oscillator_bank_neon(int numAudioFrames, float *audioOut, +@ int activePartialNum, int lookupTableSize, +@ float *phases, float *frequencies, float *amplitudes, +@ float *freqDerivatives, float *ampDerivatives, +@ float *lookupTable); + +@ Registers: +@ r0: numAudioFrames How many frames to render +@ r1: audioOut Buffer for audio output samples [stereo] +@ r2: activePartialNum How many active partials to render +@ r3: lookupTableSize Size of lookup table +@ ---- other arguments start on the stack and are moved: ----- +@ r4: phases Phase of each oscillator (pointer) +@ r5: frequencies Normalised frequency of each oscillator (pointer) +@ r6: amplitudes Normalised amplitude of each oscillator (pointer) +@ r7: freqDerivatives Derivative of frequency for each oscillator (pointer) +@ r8: ampDerivatives Derivative of amplitude for each oscillator (pointer) +@ r9: lookupTable Lookup table containing one oscillation +@ +@ Alignment requirements: +@ audioOut: 8-byte boundary +@ phases: 16-byte boundary +@ frequencies: 16-byte boundary +@ amplitudes: 16-byte boundary +@ freqDerivatives: 16-byte bounary +@ ampDerivatives: 16-byte boundary +@ lookupTable: 4-byte boundary (TODO: check this) + + .align 2 + .global oscillator_bank_neon + .thumb + .thumb_func + .type oscillator_bank_neon, %function +oscillator_bank_neon: + + +dSample .dn D6.F32 +qPhases .qn Q8.F32 +dPhases_0 .dn D16.F32 +dPhases_1 .dn D17.F32 +qFreqs .qn Q9.F32 +dFreqs_0 .dn D18.F32 +dFreqs_1 .dn D19.F32 +qAmps .qn Q10.F32 +dAmps_0 .dn D20.F32 +dAmps_1 .dn D21.F32 +qFreqDs .qn Q11.F32 +dFreqDs_0 .dn D22.F32 +dFreqDs_1 .dn D23.F32 +qAmpDs .qn Q12.F32 +dAmpDs_0 .dn D24.F32 +dAmpDs_1 .dn D25.F32 + +qBaseInts .qn Q13.U32 @ Base indexes: unsigned ints x4 +dBaseInts_0 .dn D26.U32 +dBaseInts_1 .dn D27.U32 +qFractions .qn Q14.F32 @ Fraction indexes: floats x4 +qTableBase .qn Q15.U32 @ Base of lookup table + + cmp r0, #0 @ Check for trivial case 1: zero frames + it eq + bxeq lr @ Return if that's the case (otherwise might have odd behaviour) + cmp r2, #4 @ Check for trivial case 2: zero oscillators + it lt + bxlt lr @ Return if that's the case + + push {r4-r11} @ Now arguments start 32 bytes above SP + add r11, sp, #32 @ Pointer to 32 bytes into the stack + ldm r11, {r4-r9} @ Load 6 arguments into registers + + vdup qTableBase, r9 @ Move lookup table base index into 4 ints + + @ Outer loop: iterate over the number of oscillators, choosing 4 at a + @ time to work with. +oscbank_oscillator_loop: + vld1 {dPhases_0, dPhases_1}, [r4] @ no increment; will store at end of sample loop + vld1 {dFreqs_0, dFreqs_1}, [r5] + vld1 {dAmps_0, dAmps_1}, [r6] + vld1 {dFreqDs_0, dFreqDs_1}, [r7]! @ increment; won't update at end of sample loop + vld1 {dAmpDs_0, dAmpDs_1}, [r8]! + + push {r0-r1,r4-r8} + @ --- inner loop: iterate over the number of samples --- +oscbank_sample_loop: + vcvt qBaseInts, qPhases @ Take floor(phases) + vmov q2.f32, #1.0 @ Load 1.0 into every slot of q2 + vshl q0.U32, qBaseInts, #2 @ Shift the indexes left 2 (*4 for float addressing) + vcvt qFractions, qBaseInts @ int back to float + vadd q0.U32, q0.U32, qTableBase @ Find memory addresses + + vmov r4, r5, d0 @ Move two indexes to ARM registers + vmov r6, r7, d1 @ Move two more indexes to ARM registers + vsub qFractions, qPhases, qFractions @ fraction = phase - floor(phase) + + vldr.64 d0, [r4] @ Load two consecutive floats at each location + vldr.64 d1, [r5] @ These hold the previous and following samples in the table + vldr.64 d2, [r6] @ TODO: check whether these work at 4-byte alignment + vldr.64 d3, [r7] + + @ Format at this point: + @ Osc0(before) Osc0(after) Osc1(before) Osc1(after) Osc2(before) Osc2(after) Osc3(before) Osc3(after) + @ We want: + @ Osc0(before) Osc1(before) Osc2(before) Osc3(before) Osc0(after) Osc1(after) Osc2(after) Osc3(after) + + vuzp.32 q0, q1 @ Now q0 contains before, q1 contains after + vsub q2.f32, q2.f32, qFractions @ q2 = 1.0 - fraction + vmul q1.f32, q1.f32, qFractions @ q1 = fraction * after + vmul q0.f32, q0.f32, q2.f32 @ q0 = (1.0 - fraction) * before + + vadd qPhases, qPhases, qFreqs @ Update phases + vadd qFreqs, qFreqs, qFreqDs @ Update frequencies + + vadd q0.f32, q0.f32, q1.f32 @ Add two interpolated components to get the final sample + vdup q2.u32, r3 @ Put lookup table size into each element of q2 + vcvt qBaseInts, qPhases @ Take floor of new phases + vmul q0.f32, q0.f32, qAmps @ Multiply samples by current amplitude + + vld1 dSample, [r1] @ Load the current stereo samples + vpadd d2.f32, d0.f32, d1.f32 @ Pairwise accumulate q0 (output sample) into d2 + + vand q2, q2, qBaseInts @ Logical AND of new phase int leaves 1 bit set only if phase >= table size + vpadd d3.f32, d2.f32, d2.f32 @ Pairwise accumulate d2 into d0 --> d0[0] and d0[1] both hold total of 4 oscillators + vadd qAmps, qAmps, qAmpDs @ Update amplitudes + vcvt q0.f32, q2.u32 @ Convert int back to float after AND operation + + vadd dSample, dSample, d3.f32 @ Add oscillator outputs to each channel + + subs r0, r0, #1 @ numFrames-- + vsub qPhases, qPhases, q0.f32 @ Keep phases in table range + vst1 dSample, [r1]! @ Store back in buffer and increment by 8 + + it gt + bgt oscbank_sample_loop @ Loop if numFrames > 0 + + @ --- end inner loop --- + pop {r0-r1,r4-r8} @ Restore registers: restores audioOut and numFrames, among others + + vst1 {dPhases_0, dPhases_1}, [r4]! @ Store phases back to array + vst1 {dFreqs_0, dFreqs_1}, [r5]! @ Store frequencies back to array + vst1 {dAmps_0, dAmps_1}, [r6]! @ Store amplitudes back to array + @ No need to update r7, r8 + + subs r2, r2, #4 @ numPartials -= 4 + it gt + bgt oscbank_oscillator_loop @ Loop if numPartials > 0 + + pop {r4-r11} + bx lr
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/10-Instruments/oscillator-bank/main.cpp Tue Jun 21 20:01:26 2016 +0100 @@ -0,0 +1,116 @@ +/* + * main.cpp + * + * Created on: Oct 24, 2014 + * Author: parallels + */ + +#include <iostream> +#include <cstdlib> +#include <libgen.h> +#include <signal.h> +#include <getopt.h> +#include <Bela.h> + +using namespace std; + +int gNumOscillators = 32; +int gWavetableLength = 1024; + +// Handle Ctrl-C by requesting that the audio rendering stop +void interrupt_handler(int var) +{ + gShouldStop = true; +} + +// Print usage information +void usage(const char * processName) +{ + cerr << "Usage: " << processName << " [options]" << endl; + + Bela_usage(); + + cerr << " --num-oscillators [-n] oscs: Set the number of oscillators to use (default: 32)\n"; + cerr << " --wavetable [-w] length: Set the wavetable length in samples (default: 1024)\n"; + cerr << " --help [-h]: Print this menu\n"; +} + +int main(int argc, char *argv[]) +{ + BelaInitSettings settings; // Standard audio settings + + struct option customOptions[] = + { + {"help", 0, NULL, 'h'}, + {"num-oscillators", 1, NULL, 'n'}, + {"wavetable", 1, NULL, 'w'}, + {NULL, 0, NULL, 0} + }; + + // Set default settings + Bela_defaultSettings(&settings); + + // Parse command-line arguments + while (1) { + int c; + if ((c = Bela_getopt_long(argc, argv, "hn:w:", customOptions, &settings)) < 0) + break; + switch (c) { + case 'h': + usage(basename(argv[0])); + exit(0); + case 'n': + gNumOscillators = atoi(optarg); + if(gNumOscillators <= 0) { + usage(basename(argv[0])); + exit(0); + } + break; + case 'w': + gWavetableLength = atoi(optarg); + if(gWavetableLength < 4) + gWavetableLength = 4; + if(gWavetableLength > 16384) + gWavetableLength = 16384; + break; + case '?': + default: + usage(basename(argv[0])); + exit(1); + } + } + + // Initialise the PRU audio device + if(Bela_initAudio(&settings, 0) != 0) { + cout << "Error: unable to initialise audio" << endl; + return -1; + } + + if(settings.verbose) { + cout << "--> Using " << gNumOscillators << " oscillators and wavetable of " << gWavetableLength << " samples\n"; + } + + // Start the audio device running + if(Bela_startAudio()) { + cout << "Error: unable to start real-time audio" << endl; + return -1; + } + + // Set up interrupt handler to catch Control-C and SIGTERM + signal(SIGINT, interrupt_handler); + signal(SIGTERM, interrupt_handler); + + // Run until told to stop + while(!gShouldStop) { + usleep(100000); + } + + // Stop the audio device + Bela_stopAudio(); + + // Clean up any resources allocated for audio + Bela_cleanupAudio(); + + // All done! + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/10-Instruments/oscillator-bank/render.cpp Tue Jun 21 20:01:26 2016 +0100 @@ -0,0 +1,247 @@ +/* + ____ _____ _ _ +| __ )| ____| | / \ +| _ \| _| | | / _ \ +| |_) | |___| |___ / ___ \ +|____/|_____|_____/_/ \_\ + +The platform for ultra-low latency audio and sensor processing + +http://bela.io + +A project of the Augmented Instruments Laboratory within the +Centre for Digital Music at Queen Mary University of London. +http://www.eecs.qmul.ac.uk/~andrewm + +(c) 2016 Augmented Instruments Laboratory: Andrew McPherson, + Astrid Bin, Liam Donovan, Christian Heinrichs, Robert Jack, + Giulio Moro, Laurel Pardue, Victor Zappi. All rights reserved. + +The Bela software is distributed under the GNU Lesser General Public License +(LGPL 3.0), available here: https://www.gnu.org/licenses/lgpl-3.0.txt +*/ + + +#include <Bela.h> +#include <rtdk.h> +#include <cstdlib> +#include <cmath> +#include <cstring> +#include <time.h> + +const float kMinimumFrequency = 20.0f; +const float kMaximumFrequency = 8000.0f; + +float *gWavetable; // Buffer holding the precalculated sine lookup table +float *gPhases; // Buffer holding the phase of each oscillator +float *gFrequencies; // Buffer holding the frequencies of each oscillator +float *gAmplitudes; // Buffer holding the amplitudes of each oscillator +float *gDFrequencies; // Buffer holding the derivatives of frequency +float *gDAmplitudes; // Buffer holding the derivatives of amplitude + +float gAudioSampleRate; +int gSampleCount; // Sample counter for indicating when to update frequencies +float gNewMinFrequency; +float gNewMaxFrequency; + +// Task for handling the update of the frequencies using the matrix +AuxiliaryTask gFrequencyUpdateTask; + +// These settings are carried over from main.cpp +// Setting global variables is an alternative approach +// to passing a structure to userData in setup() + +extern int gNumOscillators; +extern int gWavetableLength; + +void recalculate_frequencies(); + +extern "C" { + // Function prototype for ARM assembly implementation of oscillator bank + void oscillator_bank_neon(int numAudioFrames, float *audioOut, + int activePartialNum, int lookupTableSize, + float *phases, float *frequencies, float *amplitudes, + float *freqDerivatives, float *ampDerivatives, + float *lookupTable); +} + +// 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. +// +// userData holds an opaque pointer to a data structure that was passed +// in from the call to initAudio(). +// +// Return true on success; returning false halts the program. +bool setup(BelaContext *context, void *userData) +{ + srandom(time(NULL)); + + if(context->audioChannels != 2) { + rt_printf("Error: this example needs stereo audio enabled\n"); + return false; + } + + // Initialise the sine wavetable + if(posix_memalign((void **)&gWavetable, 8, (gWavetableLength + 1) * sizeof(float))) { + rt_printf("Error allocating wavetable\n"); + return false; + } + for(int n = 0; n < gWavetableLength + 1; n++) + gWavetable[n] = sinf(2.0 * M_PI * (float)n / (float)gWavetableLength); + + // Allocate the other buffers + if(posix_memalign((void **)&gPhases, 16, gNumOscillators * sizeof(float))) { + rt_printf("Error allocating phase buffer\n"); + return false; + } + if(posix_memalign((void **)&gFrequencies, 16, gNumOscillators * sizeof(float))) { + rt_printf("Error allocating frequency buffer\n"); + return false; + } + if(posix_memalign((void **)&gAmplitudes, 16, gNumOscillators * sizeof(float))) { + rt_printf("Error allocating amplitude buffer\n"); + return false; + } + if(posix_memalign((void **)&gDFrequencies, 16, gNumOscillators * sizeof(float))) { + rt_printf("Error allocating frequency derivative buffer\n"); + return false; + } + if(posix_memalign((void **)&gDAmplitudes, 16, gNumOscillators * sizeof(float))) { + rt_printf("Error allocating amplitude derivative buffer\n"); + return false; + } + + // Initialise buffer contents + + float freq = kMinimumFrequency; + float increment = (kMaximumFrequency - kMinimumFrequency) / (float)gNumOscillators; + + for(int n = 0; n < gNumOscillators; n++) { + gPhases[n] = 0.0; + + if(context->analogFrames == 0) { + // Random frequencies when used without matrix + gFrequencies[n] = kMinimumFrequency + (kMaximumFrequency - kMinimumFrequency) * ((float)random() / (float)RAND_MAX); + } + else { + // Constant spread of frequencies when used with matrix + gFrequencies[n] = freq; + freq += increment; + } + + // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians + gFrequencies[n] *= (float)gWavetableLength / context->audioSampleRate; + gAmplitudes[n] = ((float)random() / (float)RAND_MAX) / (float)gNumOscillators; + gDFrequencies[n] = gDAmplitudes[n] = 0.0; + } + + increment = 0; + freq = 440.0; + + for(int n = 0; n < gNumOscillators; n++) { + // Update the frequencies to a regular spread, plus a small amount of randomness + // to avoid weird phase effects + float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; + float newFreq = freq * randScale; + + // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians + gFrequencies[n] = newFreq * (float)gWavetableLength / context->audioSampleRate; + + freq += increment; + } + + // Initialise auxiliary tasks + if((gFrequencyUpdateTask = Bela_createAuxiliaryTask(&recalculate_frequencies, 85, "bela-update-frequencies")) == 0) + return false; + + //for(int n = 0; n < gNumOscillators; n++) + // rt_printf("%f\n", gFrequencies[n]); + + gAudioSampleRate = context->audioSampleRate; + gSampleCount = 0; + + return true; +} + +// render() is called regularly at the highest priority by the audio engine. +// Input and output are given from the audio hardware and the other +// ADCs and DACs (if available). If only audio is available, numMatrixFrames +// will be 0. + +void render(BelaContext *context, void *userData) +{ + // Initialise buffer to 0 + memset(context->audioOut, 0, 2 * context->audioFrames * sizeof(float)); + + // Render audio frames + oscillator_bank_neon(context->audioFrames, context->audioOut, + gNumOscillators, gWavetableLength, + gPhases, gFrequencies, gAmplitudes, + gDFrequencies, gDAmplitudes, + gWavetable); + + if(context->analogFrames != 0 && (gSampleCount += context->audioFrames) >= 128) { + gSampleCount = 0; + gNewMinFrequency = map(context->analogIn[0], 0, 1.0, 1000.0f, 8000.0f); + gNewMaxFrequency = map(context->analogIn[1], 0, 1.0, 1000.0f, 8000.0f); + + // Make sure max >= min + if(gNewMaxFrequency < gNewMinFrequency) { + float temp = gNewMaxFrequency; + gNewMaxFrequency = gNewMinFrequency; + gNewMinFrequency = temp; + } + + // Request that the lower-priority task run at next opportunity + //Bela_scheduleAuxiliaryTask(gFrequencyUpdateTask); + } +} + +// This is a lower-priority call to update the frequencies which will happen +// periodically when the matrix is enabled. By placing it at a lower priority, +// it has minimal effect on the audio performance but it will take longer to +// complete if the system is under heavy audio load. + +void recalculate_frequencies() +{ + float freq = gNewMinFrequency; + float increment = (gNewMaxFrequency - gNewMinFrequency) / (float)gNumOscillators; + + for(int n = 0; n < gNumOscillators; n++) { + // Update the frequencies to a regular spread, plus a small amount of randomness + // to avoid weird phase effects + float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; + float newFreq = freq * randScale; + + // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians + gFrequencies[n] = newFreq * (float)gWavetableLength / gAudioSampleRate; + + freq += increment; + } +} + + +// cleanup() is called once at the end, after the audio has stopped. +// Release any resources that were allocated in setup(). + +void cleanup(BelaContext *context, void *userData) +{ + free(gWavetable); + free(gPhases); + free(gFrequencies); + free(gAmplitudes); + free(gDFrequencies); + free(gDAmplitudes); +} + +/** +\example 10-oscillator-bank + +Oscillator Bank +---------------------- + +These files demonstrate an oscillator bank implemented in assembly code +that is used as part of the d-box project. +*/ +
--- a/examples/10-Instruments/oscillator_bank/audio_routines.S Tue Jun 21 20:00:12 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ -@ -@ audio_routines.S -@ -@ NEON-based functions for time-critical audio processing -@ -@ Andrew McPherson 2014 -@ Queen Mary University of London -@ - - .syntax unified - .arch armv7-a - .fpu neon - -@ void oscillator_bank_neon(int numAudioFrames, float *audioOut, -@ int activePartialNum, int lookupTableSize, -@ float *phases, float *frequencies, float *amplitudes, -@ float *freqDerivatives, float *ampDerivatives, -@ float *lookupTable); - -@ Registers: -@ r0: numAudioFrames How many frames to render -@ r1: audioOut Buffer for audio output samples [stereo] -@ r2: activePartialNum How many active partials to render -@ r3: lookupTableSize Size of lookup table -@ ---- other arguments start on the stack and are moved: ----- -@ r4: phases Phase of each oscillator (pointer) -@ r5: frequencies Normalised frequency of each oscillator (pointer) -@ r6: amplitudes Normalised amplitude of each oscillator (pointer) -@ r7: freqDerivatives Derivative of frequency for each oscillator (pointer) -@ r8: ampDerivatives Derivative of amplitude for each oscillator (pointer) -@ r9: lookupTable Lookup table containing one oscillation -@ -@ Alignment requirements: -@ audioOut: 8-byte boundary -@ phases: 16-byte boundary -@ frequencies: 16-byte boundary -@ amplitudes: 16-byte boundary -@ freqDerivatives: 16-byte bounary -@ ampDerivatives: 16-byte boundary -@ lookupTable: 4-byte boundary (TODO: check this) - - .align 2 - .global oscillator_bank_neon - .thumb - .thumb_func - .type oscillator_bank_neon, %function -oscillator_bank_neon: - - -dSample .dn D6.F32 -qPhases .qn Q8.F32 -dPhases_0 .dn D16.F32 -dPhases_1 .dn D17.F32 -qFreqs .qn Q9.F32 -dFreqs_0 .dn D18.F32 -dFreqs_1 .dn D19.F32 -qAmps .qn Q10.F32 -dAmps_0 .dn D20.F32 -dAmps_1 .dn D21.F32 -qFreqDs .qn Q11.F32 -dFreqDs_0 .dn D22.F32 -dFreqDs_1 .dn D23.F32 -qAmpDs .qn Q12.F32 -dAmpDs_0 .dn D24.F32 -dAmpDs_1 .dn D25.F32 - -qBaseInts .qn Q13.U32 @ Base indexes: unsigned ints x4 -dBaseInts_0 .dn D26.U32 -dBaseInts_1 .dn D27.U32 -qFractions .qn Q14.F32 @ Fraction indexes: floats x4 -qTableBase .qn Q15.U32 @ Base of lookup table - - cmp r0, #0 @ Check for trivial case 1: zero frames - it eq - bxeq lr @ Return if that's the case (otherwise might have odd behaviour) - cmp r2, #4 @ Check for trivial case 2: zero oscillators - it lt - bxlt lr @ Return if that's the case - - push {r4-r11} @ Now arguments start 32 bytes above SP - add r11, sp, #32 @ Pointer to 32 bytes into the stack - ldm r11, {r4-r9} @ Load 6 arguments into registers - - vdup qTableBase, r9 @ Move lookup table base index into 4 ints - - @ Outer loop: iterate over the number of oscillators, choosing 4 at a - @ time to work with. -oscbank_oscillator_loop: - vld1 {dPhases_0, dPhases_1}, [r4] @ no increment; will store at end of sample loop - vld1 {dFreqs_0, dFreqs_1}, [r5] - vld1 {dAmps_0, dAmps_1}, [r6] - vld1 {dFreqDs_0, dFreqDs_1}, [r7]! @ increment; won't update at end of sample loop - vld1 {dAmpDs_0, dAmpDs_1}, [r8]! - - push {r0-r1,r4-r8} - @ --- inner loop: iterate over the number of samples --- -oscbank_sample_loop: - vcvt qBaseInts, qPhases @ Take floor(phases) - vmov q2.f32, #1.0 @ Load 1.0 into every slot of q2 - vshl q0.U32, qBaseInts, #2 @ Shift the indexes left 2 (*4 for float addressing) - vcvt qFractions, qBaseInts @ int back to float - vadd q0.U32, q0.U32, qTableBase @ Find memory addresses - - vmov r4, r5, d0 @ Move two indexes to ARM registers - vmov r6, r7, d1 @ Move two more indexes to ARM registers - vsub qFractions, qPhases, qFractions @ fraction = phase - floor(phase) - - vldr.64 d0, [r4] @ Load two consecutive floats at each location - vldr.64 d1, [r5] @ These hold the previous and following samples in the table - vldr.64 d2, [r6] @ TODO: check whether these work at 4-byte alignment - vldr.64 d3, [r7] - - @ Format at this point: - @ Osc0(before) Osc0(after) Osc1(before) Osc1(after) Osc2(before) Osc2(after) Osc3(before) Osc3(after) - @ We want: - @ Osc0(before) Osc1(before) Osc2(before) Osc3(before) Osc0(after) Osc1(after) Osc2(after) Osc3(after) - - vuzp.32 q0, q1 @ Now q0 contains before, q1 contains after - vsub q2.f32, q2.f32, qFractions @ q2 = 1.0 - fraction - vmul q1.f32, q1.f32, qFractions @ q1 = fraction * after - vmul q0.f32, q0.f32, q2.f32 @ q0 = (1.0 - fraction) * before - - vadd qPhases, qPhases, qFreqs @ Update phases - vadd qFreqs, qFreqs, qFreqDs @ Update frequencies - - vadd q0.f32, q0.f32, q1.f32 @ Add two interpolated components to get the final sample - vdup q2.u32, r3 @ Put lookup table size into each element of q2 - vcvt qBaseInts, qPhases @ Take floor of new phases - vmul q0.f32, q0.f32, qAmps @ Multiply samples by current amplitude - - vld1 dSample, [r1] @ Load the current stereo samples - vpadd d2.f32, d0.f32, d1.f32 @ Pairwise accumulate q0 (output sample) into d2 - - vand q2, q2, qBaseInts @ Logical AND of new phase int leaves 1 bit set only if phase >= table size - vpadd d3.f32, d2.f32, d2.f32 @ Pairwise accumulate d2 into d0 --> d0[0] and d0[1] both hold total of 4 oscillators - vadd qAmps, qAmps, qAmpDs @ Update amplitudes - vcvt q0.f32, q2.u32 @ Convert int back to float after AND operation - - vadd dSample, dSample, d3.f32 @ Add oscillator outputs to each channel - - subs r0, r0, #1 @ numFrames-- - vsub qPhases, qPhases, q0.f32 @ Keep phases in table range - vst1 dSample, [r1]! @ Store back in buffer and increment by 8 - - it gt - bgt oscbank_sample_loop @ Loop if numFrames > 0 - - @ --- end inner loop --- - pop {r0-r1,r4-r8} @ Restore registers: restores audioOut and numFrames, among others - - vst1 {dPhases_0, dPhases_1}, [r4]! @ Store phases back to array - vst1 {dFreqs_0, dFreqs_1}, [r5]! @ Store frequencies back to array - vst1 {dAmps_0, dAmps_1}, [r6]! @ Store amplitudes back to array - @ No need to update r7, r8 - - subs r2, r2, #4 @ numPartials -= 4 - it gt - bgt oscbank_oscillator_loop @ Loop if numPartials > 0 - - pop {r4-r11} - bx lr
--- a/examples/10-Instruments/oscillator_bank/main.cpp Tue Jun 21 20:00:12 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/* - * main.cpp - * - * Created on: Oct 24, 2014 - * Author: parallels - */ - -#include <iostream> -#include <cstdlib> -#include <libgen.h> -#include <signal.h> -#include <getopt.h> -#include <Bela.h> - -using namespace std; - -int gNumOscillators = 32; -int gWavetableLength = 1024; - -// Handle Ctrl-C by requesting that the audio rendering stop -void interrupt_handler(int var) -{ - gShouldStop = true; -} - -// Print usage information -void usage(const char * processName) -{ - cerr << "Usage: " << processName << " [options]" << endl; - - Bela_usage(); - - cerr << " --num-oscillators [-n] oscs: Set the number of oscillators to use (default: 32)\n"; - cerr << " --wavetable [-w] length: Set the wavetable length in samples (default: 1024)\n"; - cerr << " --help [-h]: Print this menu\n"; -} - -int main(int argc, char *argv[]) -{ - BelaInitSettings settings; // Standard audio settings - - struct option customOptions[] = - { - {"help", 0, NULL, 'h'}, - {"num-oscillators", 1, NULL, 'n'}, - {"wavetable", 1, NULL, 'w'}, - {NULL, 0, NULL, 0} - }; - - // Set default settings - Bela_defaultSettings(&settings); - - // Parse command-line arguments - while (1) { - int c; - if ((c = Bela_getopt_long(argc, argv, "hn:w:", customOptions, &settings)) < 0) - break; - switch (c) { - case 'h': - usage(basename(argv[0])); - exit(0); - case 'n': - gNumOscillators = atoi(optarg); - if(gNumOscillators <= 0) { - usage(basename(argv[0])); - exit(0); - } - break; - case 'w': - gWavetableLength = atoi(optarg); - if(gWavetableLength < 4) - gWavetableLength = 4; - if(gWavetableLength > 16384) - gWavetableLength = 16384; - break; - case '?': - default: - usage(basename(argv[0])); - exit(1); - } - } - - // Initialise the PRU audio device - if(Bela_initAudio(&settings, 0) != 0) { - cout << "Error: unable to initialise audio" << endl; - return -1; - } - - if(settings.verbose) { - cout << "--> Using " << gNumOscillators << " oscillators and wavetable of " << gWavetableLength << " samples\n"; - } - - // Start the audio device running - if(Bela_startAudio()) { - cout << "Error: unable to start real-time audio" << endl; - return -1; - } - - // Set up interrupt handler to catch Control-C and SIGTERM - signal(SIGINT, interrupt_handler); - signal(SIGTERM, interrupt_handler); - - // Run until told to stop - while(!gShouldStop) { - usleep(100000); - } - - // Stop the audio device - Bela_stopAudio(); - - // Clean up any resources allocated for audio - Bela_cleanupAudio(); - - // All done! - return 0; -}
--- a/examples/10-Instruments/oscillator_bank/render.cpp Tue Jun 21 20:00:12 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,238 +0,0 @@ -/* - ____ _____ _ _ -| __ )| ____| | / \ -| _ \| _| | | / _ \ -| |_) | |___| |___ / ___ \ -|____/|_____|_____/_/ \_\.io - - */ - -/* - * render.cpp - * - * Created on: Oct 24, 2014 - * Author: parallels - */ - -/** -\example 4_oscillator_bank - -Oscillator Bank ----------------------- - -These files demonstrate an oscillator bank implemented in assembly code -that is used as part of the d-box project. -*/ - -#include <Bela.h> -#include <rtdk.h> -#include <cstdlib> -#include <cmath> -#include <cstring> -#include <time.h> - -const float kMinimumFrequency = 20.0f; -const float kMaximumFrequency = 8000.0f; - -float *gWavetable; // Buffer holding the precalculated sine lookup table -float *gPhases; // Buffer holding the phase of each oscillator -float *gFrequencies; // Buffer holding the frequencies of each oscillator -float *gAmplitudes; // Buffer holding the amplitudes of each oscillator -float *gDFrequencies; // Buffer holding the derivatives of frequency -float *gDAmplitudes; // Buffer holding the derivatives of amplitude - -float gAudioSampleRate; -int gSampleCount; // Sample counter for indicating when to update frequencies -float gNewMinFrequency; -float gNewMaxFrequency; - -// Task for handling the update of the frequencies using the matrix -AuxiliaryTask gFrequencyUpdateTask; - -// These settings are carried over from main.cpp -// Setting global variables is an alternative approach -// to passing a structure to userData in setup() - -extern int gNumOscillators; -extern int gWavetableLength; - -void recalculate_frequencies(); - -extern "C" { - // Function prototype for ARM assembly implementation of oscillator bank - void oscillator_bank_neon(int numAudioFrames, float *audioOut, - int activePartialNum, int lookupTableSize, - float *phases, float *frequencies, float *amplitudes, - float *freqDerivatives, float *ampDerivatives, - float *lookupTable); -} - -// 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. -// -// userData holds an opaque pointer to a data structure that was passed -// in from the call to initAudio(). -// -// Return true on success; returning false halts the program. -bool setup(BelaContext *context, void *userData) -{ - srandom(time(NULL)); - - if(context->audioChannels != 2) { - rt_printf("Error: this example needs stereo audio enabled\n"); - return false; - } - - // Initialise the sine wavetable - if(posix_memalign((void **)&gWavetable, 8, (gWavetableLength + 1) * sizeof(float))) { - rt_printf("Error allocating wavetable\n"); - return false; - } - for(int n = 0; n < gWavetableLength + 1; n++) - gWavetable[n] = sinf(2.0 * M_PI * (float)n / (float)gWavetableLength); - - // Allocate the other buffers - if(posix_memalign((void **)&gPhases, 16, gNumOscillators * sizeof(float))) { - rt_printf("Error allocating phase buffer\n"); - return false; - } - if(posix_memalign((void **)&gFrequencies, 16, gNumOscillators * sizeof(float))) { - rt_printf("Error allocating frequency buffer\n"); - return false; - } - if(posix_memalign((void **)&gAmplitudes, 16, gNumOscillators * sizeof(float))) { - rt_printf("Error allocating amplitude buffer\n"); - return false; - } - if(posix_memalign((void **)&gDFrequencies, 16, gNumOscillators * sizeof(float))) { - rt_printf("Error allocating frequency derivative buffer\n"); - return false; - } - if(posix_memalign((void **)&gDAmplitudes, 16, gNumOscillators * sizeof(float))) { - rt_printf("Error allocating amplitude derivative buffer\n"); - return false; - } - - // Initialise buffer contents - - float freq = kMinimumFrequency; - float increment = (kMaximumFrequency - kMinimumFrequency) / (float)gNumOscillators; - - for(int n = 0; n < gNumOscillators; n++) { - gPhases[n] = 0.0; - - if(context->analogFrames == 0) { - // Random frequencies when used without matrix - gFrequencies[n] = kMinimumFrequency + (kMaximumFrequency - kMinimumFrequency) * ((float)random() / (float)RAND_MAX); - } - else { - // Constant spread of frequencies when used with matrix - gFrequencies[n] = freq; - freq += increment; - } - - // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians - gFrequencies[n] *= (float)gWavetableLength / context->audioSampleRate; - gAmplitudes[n] = ((float)random() / (float)RAND_MAX) / (float)gNumOscillators; - gDFrequencies[n] = gDAmplitudes[n] = 0.0; - } - - increment = 0; - freq = 440.0; - - for(int n = 0; n < gNumOscillators; n++) { - // Update the frequencies to a regular spread, plus a small amount of randomness - // to avoid weird phase effects - float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; - float newFreq = freq * randScale; - - // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians - gFrequencies[n] = newFreq * (float)gWavetableLength / context->audioSampleRate; - - freq += increment; - } - - // Initialise auxiliary tasks - if((gFrequencyUpdateTask = Bela_createAuxiliaryTask(&recalculate_frequencies, 85, "bela-update-frequencies")) == 0) - return false; - - //for(int n = 0; n < gNumOscillators; n++) - // rt_printf("%f\n", gFrequencies[n]); - - gAudioSampleRate = context->audioSampleRate; - gSampleCount = 0; - - return true; -} - -// render() is called regularly at the highest priority by the audio engine. -// Input and output are given from the audio hardware and the other -// ADCs and DACs (if available). If only audio is available, numMatrixFrames -// will be 0. - -void render(BelaContext *context, void *userData) -{ - // Initialise buffer to 0 - memset(context->audioOut, 0, 2 * context->audioFrames * sizeof(float)); - - // Render audio frames - oscillator_bank_neon(context->audioFrames, context->audioOut, - gNumOscillators, gWavetableLength, - gPhases, gFrequencies, gAmplitudes, - gDFrequencies, gDAmplitudes, - gWavetable); - - if(context->analogFrames != 0 && (gSampleCount += context->audioFrames) >= 128) { - gSampleCount = 0; - gNewMinFrequency = map(context->analogIn[0], 0, 1.0, 1000.0f, 8000.0f); - gNewMaxFrequency = map(context->analogIn[1], 0, 1.0, 1000.0f, 8000.0f); - - // Make sure max >= min - if(gNewMaxFrequency < gNewMinFrequency) { - float temp = gNewMaxFrequency; - gNewMaxFrequency = gNewMinFrequency; - gNewMinFrequency = temp; - } - - // Request that the lower-priority task run at next opportunity - //Bela_scheduleAuxiliaryTask(gFrequencyUpdateTask); - } -} - -// This is a lower-priority call to update the frequencies which will happen -// periodically when the matrix is enabled. By placing it at a lower priority, -// it has minimal effect on the audio performance but it will take longer to -// complete if the system is under heavy audio load. - -void recalculate_frequencies() -{ - float freq = gNewMinFrequency; - float increment = (gNewMaxFrequency - gNewMinFrequency) / (float)gNumOscillators; - - for(int n = 0; n < gNumOscillators; n++) { - // Update the frequencies to a regular spread, plus a small amount of randomness - // to avoid weird phase effects - float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; - float newFreq = freq * randScale; - - // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians - gFrequencies[n] = newFreq * (float)gWavetableLength / gAudioSampleRate; - - freq += increment; - } -} - - -// cleanup() is called once at the end, after the audio has stopped. -// Release any resources that were allocated in setup(). - -void cleanup(BelaContext *context, void *userData) -{ - free(gWavetable); - free(gPhases); - free(gFrequencies); - free(gAmplitudes); - free(gDFrequencies); - free(gDAmplitudes); -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/10-Instruments/tank-wars/game.cpp Tue Jun 21 20:01:26 2016 +0100 @@ -0,0 +1,339 @@ +/* + * game.cpp + * + * Created on: Nov 10, 2014 + * Author: parallels + */ + +#include <cmath> +#include <cstdlib> +#include "vector_graphics.h" +#include <Utilities.h> + +// Virtual screen size +int screenWidth, screenHeight; + +// Basic information on the terrain and the tanks +float *groundLevel; // Y coordinate of the ground for each X coordinate +float tank1X, tank1Y, tank2X, tank2Y; // Positions of the two tanks +float tankRadius = 20; // Radius of the tanks +float cannonLength = 40; // How long the cannon on each tank extends +float gravity = 0.05; // Strength of gravity + +// Current state of the game +int playerHasWon = 0; // 1 if player 1 wins, 2 if player 2 wins, 0 if game in progress +bool player1Turn = true; // true if it's player 1's turn; false otherwise +float tank1CannonAngle = M_PI/2; +float tank2CannonAngle = M_PI/2; // Direction the tank cannons are pointing +float tank1CannonStrength = 3; +float tank2CannonStrength = 3; // Strength of intended projectile launch + +// Location of the projectile +bool projectileInMotion = false; +float projectilePositionX, projectilePositionY; +float projectileVelocityX, projectileVelocityY; + +// Infor needed for sound rendering +bool collisionJustOccurred = false; +bool tankHitJustOccurred = false; + +// Useful utility function for generating random floating-point values +float randomFloat(float low, float hi) +{ + float r = (float)random() / (float)RAND_MAX; + return map(r, 0, 1, low, hi); +} + +// Restart the game, without reallocating memory +void restartGame() +{ + float player1Height = screenHeight * 3/4; // randomFloat(screenHeight/2, screenHeight-5); + float player2Height = screenHeight - 5; // randomFloat(screenHeight/2, screenHeight-5); + for(int i = 0; i < screenWidth * 0.2; i++) { + groundLevel[i] = player1Height; + } + for(int i = screenWidth * 0.2; i < screenWidth * 0.8; i++) { + groundLevel[i] = player1Height + (player2Height - player1Height) * (i - screenWidth*0.2)/(screenWidth*0.6); + } + for(int i = screenWidth * 0.8; i < screenWidth; i++) { + groundLevel[i] = player2Height; + } + + // Set the location of the two tanks so they rest on the ground at opposite sides + tank1X = screenWidth * 0.1; + tank1Y = player1Height; + tank2X = screenWidth * 0.9; + tank2Y = player2Height; + + playerHasWon = 0; + projectileInMotion = false; +} + +// Initialise the game +void setupGame(int width, int height) +{ + // Set the screen size + screenWidth = width; + screenHeight = height; + + // Initialize the ground level + groundLevel = new float[screenWidth]; + + restartGame(); +} + +// Advance the turn to the next player +void nextPlayersTurn() { + player1Turn = !player1Turn; +} + + +// Move forward one frame on the game physics +void nextGameFrame() +{ + if(!projectileInMotion) + return; + + // Update position of projectile + projectilePositionX += projectileVelocityX; + projectilePositionY += projectileVelocityY; + projectileVelocityY += gravity; + + // Check collision with tanks first: a collision with tank 1 means player 2 wins and vice-versa + if((tank1X - projectilePositionX)*(tank1X - projectilePositionX) + + (tank1Y - projectilePositionY)*(tank1Y - projectilePositionY) + <= tankRadius * tankRadius) + { + projectileInMotion = false; + collisionJustOccurred = false; + tankHitJustOccurred = true; + playerHasWon = 2; + } + else if((tank2X - projectilePositionX)*(tank2X - projectilePositionX) + + (tank2Y - projectilePositionY)*(tank2Y - projectilePositionY) + <= tankRadius * tankRadius) + { + projectileInMotion = false; + collisionJustOccurred = false; + tankHitJustOccurred = 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(); + } +} + +// Updates for game state +void setTank1CannonAngle(float angle) +{ + tank1CannonAngle = angle; +} + +void setTank2CannonAngle(float angle) +{ + tank2CannonAngle = angle; +} + +void setTank1CannonStrength(float strength) +{ + tank1CannonStrength = strength; +} + +void setTank2CannonStrength(float strength) +{ + tank2CannonStrength = strength; +} + +// FIRE! +void fireProjectile() +{ + // Can't fire while projectile is already moving, or if someone has won + if(projectileInMotion) + return; + if(playerHasWon != 0) + return; + + if(player1Turn) { + projectilePositionX = tank1X + cannonLength * cosf(tank1CannonAngle); + projectilePositionY = tank1Y - cannonLength * sinf(tank1CannonAngle); + projectileVelocityX = tank1CannonStrength * cosf(tank1CannonAngle); + projectileVelocityY = -tank1CannonStrength * sinf(tank1CannonAngle); + } + else { + projectilePositionX = tank2X + cannonLength * cosf(tank2CannonAngle); + projectilePositionY = tank2Y - cannonLength * sinf(tank2CannonAngle); + projectileVelocityX = tank2CannonStrength * cosf(tank2CannonAngle); + projectileVelocityY = -tank2CannonStrength * sinf(tank2CannonAngle); + } + + // GO! + projectileInMotion = true; +} + +// Game state queries +bool gameStatusPlayer1Turn() +{ + return player1Turn; +} + +bool gameStatusProjectileInMotion() +{ + return projectileInMotion; +} + +int gameStatusWinner() +{ + return playerHasWon; +} + +bool gameStatusCollisionOccurred() +{ + if(collisionJustOccurred) { + collisionJustOccurred = false; + return true; + } + return false; +} + +bool gameStatusTankHitOccurred() +{ + if(tankHitJustOccurred) { + tankHitJustOccurred = false; + return true; + } + return false; +} + + +float gameStatusProjectileHeight() +{ + return projectilePositionY / (float)screenHeight; +} + +// Clean up any allocated memory for the game +void cleanupGame() +{ + delete groundLevel; +} + +// Drawing routines. Arguments are (interleaved) buffer to render +// into, the available size, and the target for how many samples +// to use (actual usage might vary slightly). Regardless of +// lengthTarget, never use more than bufferSize samples. + +int drawGround(float *buffer, int bufferSize, int framesTarget) +{ + int length; + + // Calculate total length of ground line, to arrive at a speed calculation + float totalLineLength = 0.4f*screenWidth + + sqrtf(0.36f*screenWidth*screenWidth + + (tank2Y-tank1Y)*(tank2Y-tank1Y)); + + // Speed is calculated in pixels per frame + float speed = totalLineLength / (float)framesTarget; + + // Draw three lines: platforms for tanks and the connecting line. + // Eventually, render a more complex ground from the array. + length = renderLine(0, tank1Y, screenWidth * 0.2, tank1Y, + speed, buffer, bufferSize); + length += renderLine(screenWidth * 0.2, tank1Y, screenWidth * 0.8, tank2Y, + speed, &buffer[length], bufferSize - length); + length += renderLine(screenWidth * 0.8, tank2Y, screenWidth, tank2Y, + speed, &buffer[length], bufferSize - length); + + return length; +} + +int drawTanks(float *buffer, int bufferSize, int framesTarget) +{ + int length = 0; + + // Calculate total length of tank lines, to arrive at a speed calculation + float totalLineLength = 2.0*M_PI*tankRadius + 2.0*(cannonLength - tankRadius); + + // Speed is calculated in pixels per frame + float speed = totalLineLength / (float)framesTarget; + + if(playerHasWon != 2) { + // Tank 1 body = semicircle + line + length += renderArc(tank1X, tank1Y, tankRadius, M_PI, 2.0 * M_PI, + speed, buffer, bufferSize); + length += renderLine(tank1X + tankRadius, tank1Y, + tank1X - tankRadius, tank1Y, + speed, &buffer[length], bufferSize - length); + // Tank 1 cannon (line depending on angle) + length += renderLine(tank1X + tankRadius * cosf(tank1CannonAngle), + tank1Y - tankRadius * sinf(tank1CannonAngle), + tank1X + cannonLength * cosf(tank1CannonAngle), + tank1Y - cannonLength * sinf(tank1CannonAngle), + speed, &buffer[length], bufferSize - length); + } + + if(playerHasWon != 1) { + // Same idea for tank 2 + length += renderArc(tank2X, tank2Y, tankRadius, M_PI, 2.0 * M_PI, + speed, &buffer[length], bufferSize - length); + length += renderLine(tank2X + tankRadius, tank2Y, + tank2X - tankRadius, tank2Y, + speed, &buffer[length], bufferSize - length); + length += renderLine(tank2X + tankRadius * cosf(tank2CannonAngle), + tank2Y - tankRadius * sinf(tank2CannonAngle), + tank2X + cannonLength * cosf(tank2CannonAngle), + tank2Y - cannonLength * sinf(tank2CannonAngle), + speed, &buffer[length], bufferSize - length); + } + + return length; +} + +int drawProjectile(float *buffer, int bufferSize, int framesTarget) +{ + if(!projectileInMotion) + return 0; + + // Draw a point for a specified number of frames (each containing X and Y) + // Return the number of items used in the buffer, which will be twice + // the number of frames unless the buffer is full + + if(bufferSize/2 < framesTarget) { + renderPoint(projectilePositionX, projectilePositionY, buffer, bufferSize/2); + return bufferSize; + } + else { + renderPoint(projectilePositionX, projectilePositionY, buffer, framesTarget); + return framesTarget*2; + } +} + +// Main drawing routine entry point +int drawGame(float *buffer, int bufferSize) +{ + int length; + + // Based on buffer size, come up with speeds for each of the elements + // 50% of time to ground; 30% to the tanks and 20% to the projectile + // Give a margin of 25% beyond so we don't run out of buffer space + // if things take longer to draw than we guess they will + const float amountToUse = 0.375; // 0.75/2 because two samples per frame + const float groundFraction = 0.5 * amountToUse; + const float tankFraction = 0.3 * amountToUse; + const float projectileFraction = 0.2 * amountToUse; + + length = drawGround(buffer, bufferSize, bufferSize * groundFraction); + length += drawTanks(&buffer[length], bufferSize - length, + bufferSize * tankFraction); + length += drawProjectile(&buffer[length], bufferSize - length, + bufferSize * projectileFraction); + + return length; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/10-Instruments/tank-wars/game.h Tue Jun 21 20:01:26 2016 +0100 @@ -0,0 +1,39 @@ +/* + * game.h + * + * Created on: Nov 10, 2014 + * Author: parallels + */ + +#ifndef GAME_H_ +#define GAME_H_ + +// Initialisation +void setupGame(int width, int height); +void restartGame(); + +// Update physics +void nextGameFrame(); + +// State updaters +void setTank1CannonAngle(float angle); +void setTank2CannonAngle(float angle); +void setTank1CannonStrength(float strength); +void setTank2CannonStrength(float strength); +void fireProjectile(); + +// State queries +bool gameStatusPlayer1Turn(); +bool gameStatusProjectileInMotion(); +int gameStatusWinner(); +bool gameStatusCollisionOccurred(); +bool gameStatusTankHitOccurred(); +float gameStatusProjectileHeight(); + +// Render screen; returns length of buffer used +int drawGame(float *buffer, int bufferSize); + +// Cleanup and memory release +void cleanupGame(); + +#endif /* GAME_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/10-Instruments/tank-wars/main.cpp Tue Jun 21 20:01:26 2016 +0100 @@ -0,0 +1,169 @@ +/* + * main.cpp + * + * Created on: Oct 24, 2014 + * Author: parallels + */ + +#include <iostream> +#include <cstdlib> +#include <libgen.h> +#include <signal.h> +#include <getopt.h> +#include <sndfile.h> +#include <Bela.h> + +extern int gScreenFramesPerSecond; + +float *gMusicBuffer = 0; +int gMusicBufferLength = 0; +float *gSoundBoomBuffer = 0; +int gSoundBoomBufferLength = 0; +float *gSoundHitBuffer = 0; +int gSoundHitBufferLength = 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) +{ + gShouldStop = true; +} + +// Print usage information +void usage(const char * processName) +{ + cerr << "Usage: " << processName << " [options]" << endl; + + Bela_usage(); + + cerr << " --fps [-f] value: Set target frames per second\n"; + cerr << " --help [-h]: Print this menu\n"; +} + +int main(int argc, char *argv[]) +{ + BelaInitSettings settings; // Standard audio settings + string musicFileName = "music.wav"; + string soundBoomFileName = "boom.wav"; + string soundHitFileName = "hit.wav"; + + struct option customOptions[] = + { + {"help", 0, NULL, 'h'}, + {"fps", 1, NULL, 'f'}, + {NULL, 0, NULL, 0} + }; + + // Set default settings + Bela_defaultSettings(&settings); + + // Parse command-line arguments + while (1) { + int c; + if ((c = Bela_getopt_long(argc, argv, "hf:", customOptions, &settings)) < 0) + break; + switch (c) { + case 'f': + gScreenFramesPerSecond = atoi(optarg); + if(gScreenFramesPerSecond < 1) + gScreenFramesPerSecond = 1; + if(gScreenFramesPerSecond > 100) + gScreenFramesPerSecond = 100; + break; + case 'h': + usage(basename(argv[0])); + exit(0); + case '?': + default: + usage(basename(argv[0])); + exit(1); + } + } + + // 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; + } + if(loadSoundFile(soundHitFileName, &gSoundHitBuffer, &gSoundHitBufferLength) != 0) { + cout << "Warning: unable to load sound file " << soundHitFileName << endl; + } + + // Initialise the PRU audio device + if(Bela_initAudio(&settings, 0) != 0) { + cout << "Error: unable to initialise audio" << endl; + return -1; + } + + // Start the audio device running + if(Bela_startAudio()) { + cout << "Error: unable to start real-time audio" << endl; + return -1; + } + + // Set up interrupt handler to catch Control-C and SIGTERM + signal(SIGINT, interrupt_handler); + signal(SIGTERM, interrupt_handler); + + // Run until told to stop + while(!gShouldStop) { + usleep(100000); + } + + // Stop the audio device + Bela_stopAudio(); + + // Clean up any resources allocated for audio + Bela_cleanupAudio(); + + // Release sound files + if(gMusicBuffer != 0) + free(gMusicBuffer); + if(gSoundBoomBuffer != 0) + free(gSoundBoomBuffer); + if(gSoundHitBuffer != 0) + free(gSoundHitBuffer); + + // All done! + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/10-Instruments/tank-wars/render.cpp Tue Jun 21 20:01:26 2016 +0100 @@ -0,0 +1,337 @@ +/* + * render.cpp + * + * Created on: Oct 24, 2014 + * Author: parallels + */ + + +#include <Bela.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 + +int gOutputX = 0; // Outputs for the scope +int gOutputY = 1; +int gOutputPlayer1LED = 2; +int gOutputPlayer2LED = 3; + +int gGameFramesPerSecond = 60; // How often the physics are updated +int gGameFrameInterval; // ...and in frames +int gSamplesUntilNextFrame; // Counter until next update +int gSamplesSinceFinish = 0; // How long since somebody won? +bool gGameShouldRestart = false;// Whether we need to reinitiliase the game + +// Counter for overall number of samples that have elapsed +unsigned int gSampleCounter = 0; + +// 1st-order filter and peak detector for launcher input +float gLauncherLastSample = 0; +float gLauncherFilterPole = 0.8; +float gLauncherPeakValue = 0; +float gLauncherPeakFilterPole = 0.999; +float gLauncherNoiseThreshold = 0.01; +float gLauncherMinimumPeak = 0.1; +bool gLauncherTriggered = false; + +// Screen update rate; affects buffer size. Actual contents of buffer +// may be smaller than this +int gScreenWidth = 512; +int gScreenHeight = 512; +int gScreenFramesPerSecond = 25; + +// Double buffer for rendering screen. Each is an interleaved buffer +// of XY data. +float *gScreenBuffer1, *gScreenBuffer2; +float *gScreenBufferWrite, *gScreenBufferRead; +int gScreenBufferMaxLength; // What is the total buffer allocated? +int gScreenBufferReadLength; // How long is the read buffer? +int gScreenBufferWriteLength; // How long is the write (next) buffer? +int gScreenBufferReadPointer; // Where are we in the read buffer now? +int gScreenBufferNextUpdateLocation; // When should we render the next buffer? +bool gScreenNextBufferReady; // Is the next buffer ready to go? + +// 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; +extern float *gSoundHitBuffer; +extern int gSoundHitBufferLength; + +// Current state for sound and music +int gMusicBufferPointer = 0; // 0 means start of buffer... +int gSoundBoomBufferPointer = -1; // -1 means don't play... +int gSoundHitBufferPointer = -1; +float gSoundProjectileOscillatorPhase = 0; +float gSoundProjectileOscillatorGain = 0.2; +float gOscillatorPhaseScaler = 0; + +void screen_update(); + +// 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. +// +// userData holds an opaque pointer to a data structure that was passed +// in from the call to initAudio(). +// +// Return true on success; returning false halts the program. + +bool setup(BelaContext *context, void *userData) +{ + srandom(time(NULL)); + + // Verify we are running with matrix enabled + 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 * context->analogSampleRate / gScreenFramesPerSecond; + gScreenBuffer1 = new float[gScreenBufferMaxLength]; + gScreenBuffer2 = new float[gScreenBufferMaxLength]; + if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) { + rt_printf("Error initialising screen buffers\n"); + return false; + } + + gScreenBufferRead = gScreenBuffer1; + gScreenBufferWrite = gScreenBuffer2; + gScreenBufferReadLength = gScreenBufferWriteLength = 0; + gScreenBufferReadPointer = 0; + gScreenBufferNextUpdateLocation = 0; + gScreenNextBufferReady = false; + + // Initialise the game + setupGame(gScreenWidth, gScreenHeight); + gGameFrameInterval = context->analogSampleRate / gGameFramesPerSecond; + gSamplesUntilNextFrame = gGameFrameInterval; + + // Initialise auxiliary tasks + if((gScreenUpdateTask = Bela_createAuxiliaryTask(&screen_update, 90, + "bela-screen-update")) == 0) + return false; + + return true; +} + +// Swap buffers on the screen +void swap_buffers() +{ + if(gScreenBufferRead == gScreenBuffer1) { + gScreenBufferRead = gScreenBuffer2; + gScreenBufferWrite = gScreenBuffer1; + } + else { + gScreenBufferRead = gScreenBuffer1; + gScreenBufferWrite = gScreenBuffer2; + } + + gScreenBufferReadLength = gScreenBufferWriteLength; + gScreenBufferReadPointer = 0; + + // Schedule next update for 3/4 of the way through the buffer + gScreenBufferNextUpdateLocation = gScreenBufferReadLength * 0.75; + gScreenNextBufferReady = false; +} + +// render() is called regularly at the highest priority by the audio engine. +// Input and output are given from the audio hardware and the other +// ADCs and DACs (if available). If only audio is available, numMatrixFrames +// will be 0. + +void render(BelaContext *context, void *userData) +{ + 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; + } + + if(gSoundHitBuffer != 0 && gSoundHitBufferPointer >= 0) { + audioSample += gSoundHitBuffer[gSoundHitBufferPointer++]; + if(gSoundHitBufferPointer >= gSoundHitBufferLength) + gSoundHitBufferPointer = -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(context, n, gInputLauncher); + float launchSample = gLauncherFilterPole * gLauncherLastSample + + (1.0f - gLauncherFilterPole) * rawSample; + gLauncherLastSample = launchSample; + + // Peak-detect on launch signal + if(launchSample >= gLauncherPeakValue) { + gLauncherPeakValue = launchSample; + gLauncherTriggered = false; + } + else { + if(gLauncherPeakValue - launchSample > gLauncherNoiseThreshold && !gLauncherTriggered) { + // Detected a peak; is it big enough overall? + if(gLauncherPeakValue >= gLauncherMinimumPeak) { + gLauncherTriggered = true; + // Peak detected-- fire!! + // Set both cannon strengths but only one will + // fire depending on whose turn it is + float strength = map(gLauncherPeakValue, + gLauncherMinimumPeak, 1.0, + 0.5f, 10.0f); + setTank1CannonStrength(strength); + setTank2CannonStrength(strength); + fireProjectile(); + } + } + + gLauncherPeakValue *= gLauncherPeakFilterPole; + } + + if(--gSamplesUntilNextFrame <= 0) { + // Update game physics and cannon angles + gSamplesUntilNextFrame = gGameFrameInterval; + + setTank1CannonAngle(map(analogRead(context, n, gInputTank1Angle), + 0, 1.0, M_PI, 0)); + setTank2CannonAngle(map(analogRead(context, n, gInputTank2Angle), + 0, 1.0, M_PI, 0)); + nextGameFrame(); + + // Check for collision and start sound accordingly + if(gameStatusCollisionOccurred()) { + gSoundBoomBufferPointer = 0; + } + + if(gameStatusTankHitOccurred()) { + gSoundHitBufferPointer = 0; + } + } + + if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1 + && gScreenNextBufferReady) { + // Got to the end; swap buffers + swap_buffers(); + } + + // Push current screen buffer to the matrix output + if(gScreenBufferReadPointer < gScreenBufferReadLength - 1) { + float x = gScreenBufferRead[gScreenBufferReadPointer++]; + float y = gScreenBufferRead[gScreenBufferReadPointer++]; + + // Rescale screen coordinates to matrix ranges; invert the Y + // coordinate to go from normal screen coordinates to scope coordinates + analogWriteOnce(context, n, gOutputX, constrain(map(x, 0, gScreenWidth, 0, 1.0), 0, 1.0)); + analogWriteOnce(context, n, gOutputY, constrain(map(y, 0, gScreenHeight, 1.0, 0), 0, 1.0)); + } + else { + // Still not ready! Write 0 until something happens + analogWriteOnce(context, n, gOutputX, 0); + analogWriteOnce(context, n, gOutputY, 0); + } + + if(gameStatusWinner() != 0) { + // Blink one LED to show who won + // Blink both LEDs when projectile is in motion + float val = (gSampleCounter % 4000 > 2000) ? 1.0 : 0; + analogWriteOnce(context, n, gOutputPlayer1LED, gameStatusWinner() == 1 ? val : 0); + analogWriteOnce(context, n, gOutputPlayer2LED, gameStatusWinner() == 2 ? val : 0); + + // After 5 seconds, restart the game + gSamplesSinceFinish++; + if(gSamplesSinceFinish > 22050*5) + gGameShouldRestart = true; + } + else if(gameStatusProjectileInMotion()) { + // Blink both LEDs when projectile is in motion + float val = (gSampleCounter % 2000 > 1000) ? 1.0 : 0; + analogWriteOnce(context, n, gOutputPlayer1LED, val); + analogWriteOnce(context, n, gOutputPlayer2LED, val); + } + else if(gameStatusPlayer1Turn()) { + analogWriteOnce(context, n, gOutputPlayer1LED, 1.0); + analogWriteOnce(context, n, gOutputPlayer2LED, 0); + } + else { + analogWriteOnce(context, n, gOutputPlayer2LED, 1.0); + analogWriteOnce(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 + Bela_scheduleAuxiliaryTask(gScreenUpdateTask); + } + + gSampleCounter++; + } +} + +void screen_update() +{ + // If we should restart, reinitialise the game + if(gGameShouldRestart) { + restartGame(); + gGameShouldRestart = false; + gSamplesSinceFinish = 0; + } + + // Render the game based on the current state + gScreenBufferWriteLength = drawGame(gScreenBufferWrite, gScreenBufferMaxLength); + + // Flag it as ready to go + gScreenNextBufferReady = true; +} + +// cleanup() is called once at the end, after the audio has stopped. +// Release any resources that were allocated in setup(). + +void cleanup(BelaContext *context, void *userData) +{ + // Clean up the game state + cleanupGame(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/10-Instruments/tank-wars/vector_graphics.cpp Tue Jun 21 20:01:26 2016 +0100 @@ -0,0 +1,72 @@ +/* + * vector_graphics.cpp + * + * Created on: Nov 10, 2014 + * Author: parallels + */ + +#include <cmath> + +// Draw a line between two points at a specified rate in +// pixels per buffer sample. Indicate maximum available space. +// Returns space used +int renderLine(float x1, float y1, float x2, float y2, float speed, + float *buffer, int maxLength) { + // Figure out length of line and therefore how many samples + // are needed to represent it based on the speed (rounded to nearest int) + float totalLineLength = sqrtf((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); + int samplesNeeded = floorf(totalLineLength / speed + 0.5); + + // Now render into the buffer + int length = 0; + float scaleFactor = 1.0f / samplesNeeded; + for(int n = 0; n < samplesNeeded; n++) { + if(length >= maxLength - 1) + return length; + // X coordinate + *buffer++ = x1 + (float)n * scaleFactor * (x2 - x1); + // Y coordinate + *buffer++ = y1 + (float)n * scaleFactor * (y2 - y1); + length += 2; + } + + return length; +} + +// Draw an arc around a centre point at a specified rate of pixels +// per buffer sample. Indicate maximum available space. +// Returns space used +int renderArc(float x, float y, float radius, float thetaMin, float thetaMax, + float speed, float *buffer, int maxLength) { + // Figure out circumference of arc and therefore how many samples + // are needed to represent it based on the speed (rounded to nearest int) + float circumference = (thetaMax - thetaMin) * radius; + int samplesNeeded = floorf(circumference / speed + 0.5); + + // Now render into the buffer + int length = 0; + float scaleFactor = 1.0f / samplesNeeded; + for(int n = 0; n < samplesNeeded; n++) { + if(length >= maxLength - 1) + return length; + // Get current angle + float theta = thetaMin + (float)n * scaleFactor * (thetaMax - thetaMin); + + // Convert polar to cartesian coordinates + *buffer++ = x + radius * cosf(theta); + *buffer++ = y + radius * sinf(theta); + + length += 2; + } + + return length; +} + +// Draw a single point for a specified number of frames +void renderPoint(float x, float y, float *buffer, float length) { + while(length > 0) { + *buffer++ = x; + *buffer++ = y; + length--; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/10-Instruments/tank-wars/vector_graphics.h Tue Jun 21 20:01:26 2016 +0100 @@ -0,0 +1,18 @@ +/* + * vector_graphics.h + * + * Created on: Nov 10, 2014 + * Author: parallels + */ + +#ifndef VECTOR_GRAPHICS_H_ +#define VECTOR_GRAPHICS_H_ + +int renderLine(float x1, float y1, float x2, float y2, float speed, + float *buffer, int maxLength); +int renderArc(float x, float y, float radius, float thetaMin, float thetaMax, + float speed, float *buffer, int maxLength); +void renderPoint(float x, float y, float *buffer, float length); + + +#endif /* VECTOR_GRAPHICS_H_ */
--- a/examples/10-Instruments/tank_wars/game.cpp Tue Jun 21 20:00:12 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,339 +0,0 @@ -/* - * game.cpp - * - * Created on: Nov 10, 2014 - * Author: parallels - */ - -#include <cmath> -#include <cstdlib> -#include "vector_graphics.h" -#include <Utilities.h> - -// Virtual screen size -int screenWidth, screenHeight; - -// Basic information on the terrain and the tanks -float *groundLevel; // Y coordinate of the ground for each X coordinate -float tank1X, tank1Y, tank2X, tank2Y; // Positions of the two tanks -float tankRadius = 20; // Radius of the tanks -float cannonLength = 40; // How long the cannon on each tank extends -float gravity = 0.05; // Strength of gravity - -// Current state of the game -int playerHasWon = 0; // 1 if player 1 wins, 2 if player 2 wins, 0 if game in progress -bool player1Turn = true; // true if it's player 1's turn; false otherwise -float tank1CannonAngle = M_PI/2; -float tank2CannonAngle = M_PI/2; // Direction the tank cannons are pointing -float tank1CannonStrength = 3; -float tank2CannonStrength = 3; // Strength of intended projectile launch - -// Location of the projectile -bool projectileInMotion = false; -float projectilePositionX, projectilePositionY; -float projectileVelocityX, projectileVelocityY; - -// Infor needed for sound rendering -bool collisionJustOccurred = false; -bool tankHitJustOccurred = false; - -// Useful utility function for generating random floating-point values -float randomFloat(float low, float hi) -{ - float r = (float)random() / (float)RAND_MAX; - return map(r, 0, 1, low, hi); -} - -// Restart the game, without reallocating memory -void restartGame() -{ - float player1Height = screenHeight * 3/4; // randomFloat(screenHeight/2, screenHeight-5); - float player2Height = screenHeight - 5; // randomFloat(screenHeight/2, screenHeight-5); - for(int i = 0; i < screenWidth * 0.2; i++) { - groundLevel[i] = player1Height; - } - for(int i = screenWidth * 0.2; i < screenWidth * 0.8; i++) { - groundLevel[i] = player1Height + (player2Height - player1Height) * (i - screenWidth*0.2)/(screenWidth*0.6); - } - for(int i = screenWidth * 0.8; i < screenWidth; i++) { - groundLevel[i] = player2Height; - } - - // Set the location of the two tanks so they rest on the ground at opposite sides - tank1X = screenWidth * 0.1; - tank1Y = player1Height; - tank2X = screenWidth * 0.9; - tank2Y = player2Height; - - playerHasWon = 0; - projectileInMotion = false; -} - -// Initialise the game -void setupGame(int width, int height) -{ - // Set the screen size - screenWidth = width; - screenHeight = height; - - // Initialize the ground level - groundLevel = new float[screenWidth]; - - restartGame(); -} - -// Advance the turn to the next player -void nextPlayersTurn() { - player1Turn = !player1Turn; -} - - -// Move forward one frame on the game physics -void nextGameFrame() -{ - if(!projectileInMotion) - return; - - // Update position of projectile - projectilePositionX += projectileVelocityX; - projectilePositionY += projectileVelocityY; - projectileVelocityY += gravity; - - // Check collision with tanks first: a collision with tank 1 means player 2 wins and vice-versa - if((tank1X - projectilePositionX)*(tank1X - projectilePositionX) + - (tank1Y - projectilePositionY)*(tank1Y - projectilePositionY) - <= tankRadius * tankRadius) - { - projectileInMotion = false; - collisionJustOccurred = false; - tankHitJustOccurred = true; - playerHasWon = 2; - } - else if((tank2X - projectilePositionX)*(tank2X - projectilePositionX) + - (tank2Y - projectilePositionY)*(tank2Y - projectilePositionY) - <= tankRadius * tankRadius) - { - projectileInMotion = false; - collisionJustOccurred = false; - tankHitJustOccurred = 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(); - } -} - -// Updates for game state -void setTank1CannonAngle(float angle) -{ - tank1CannonAngle = angle; -} - -void setTank2CannonAngle(float angle) -{ - tank2CannonAngle = angle; -} - -void setTank1CannonStrength(float strength) -{ - tank1CannonStrength = strength; -} - -void setTank2CannonStrength(float strength) -{ - tank2CannonStrength = strength; -} - -// FIRE! -void fireProjectile() -{ - // Can't fire while projectile is already moving, or if someone has won - if(projectileInMotion) - return; - if(playerHasWon != 0) - return; - - if(player1Turn) { - projectilePositionX = tank1X + cannonLength * cosf(tank1CannonAngle); - projectilePositionY = tank1Y - cannonLength * sinf(tank1CannonAngle); - projectileVelocityX = tank1CannonStrength * cosf(tank1CannonAngle); - projectileVelocityY = -tank1CannonStrength * sinf(tank1CannonAngle); - } - else { - projectilePositionX = tank2X + cannonLength * cosf(tank2CannonAngle); - projectilePositionY = tank2Y - cannonLength * sinf(tank2CannonAngle); - projectileVelocityX = tank2CannonStrength * cosf(tank2CannonAngle); - projectileVelocityY = -tank2CannonStrength * sinf(tank2CannonAngle); - } - - // GO! - projectileInMotion = true; -} - -// Game state queries -bool gameStatusPlayer1Turn() -{ - return player1Turn; -} - -bool gameStatusProjectileInMotion() -{ - return projectileInMotion; -} - -int gameStatusWinner() -{ - return playerHasWon; -} - -bool gameStatusCollisionOccurred() -{ - if(collisionJustOccurred) { - collisionJustOccurred = false; - return true; - } - return false; -} - -bool gameStatusTankHitOccurred() -{ - if(tankHitJustOccurred) { - tankHitJustOccurred = false; - return true; - } - return false; -} - - -float gameStatusProjectileHeight() -{ - return projectilePositionY / (float)screenHeight; -} - -// Clean up any allocated memory for the game -void cleanupGame() -{ - delete groundLevel; -} - -// Drawing routines. Arguments are (interleaved) buffer to render -// into, the available size, and the target for how many samples -// to use (actual usage might vary slightly). Regardless of -// lengthTarget, never use more than bufferSize samples. - -int drawGround(float *buffer, int bufferSize, int framesTarget) -{ - int length; - - // Calculate total length of ground line, to arrive at a speed calculation - float totalLineLength = 0.4f*screenWidth - + sqrtf(0.36f*screenWidth*screenWidth - + (tank2Y-tank1Y)*(tank2Y-tank1Y)); - - // Speed is calculated in pixels per frame - float speed = totalLineLength / (float)framesTarget; - - // Draw three lines: platforms for tanks and the connecting line. - // Eventually, render a more complex ground from the array. - length = renderLine(0, tank1Y, screenWidth * 0.2, tank1Y, - speed, buffer, bufferSize); - length += renderLine(screenWidth * 0.2, tank1Y, screenWidth * 0.8, tank2Y, - speed, &buffer[length], bufferSize - length); - length += renderLine(screenWidth * 0.8, tank2Y, screenWidth, tank2Y, - speed, &buffer[length], bufferSize - length); - - return length; -} - -int drawTanks(float *buffer, int bufferSize, int framesTarget) -{ - int length = 0; - - // Calculate total length of tank lines, to arrive at a speed calculation - float totalLineLength = 2.0*M_PI*tankRadius + 2.0*(cannonLength - tankRadius); - - // Speed is calculated in pixels per frame - float speed = totalLineLength / (float)framesTarget; - - if(playerHasWon != 2) { - // Tank 1 body = semicircle + line - length += renderArc(tank1X, tank1Y, tankRadius, M_PI, 2.0 * M_PI, - speed, buffer, bufferSize); - length += renderLine(tank1X + tankRadius, tank1Y, - tank1X - tankRadius, tank1Y, - speed, &buffer[length], bufferSize - length); - // Tank 1 cannon (line depending on angle) - length += renderLine(tank1X + tankRadius * cosf(tank1CannonAngle), - tank1Y - tankRadius * sinf(tank1CannonAngle), - tank1X + cannonLength * cosf(tank1CannonAngle), - tank1Y - cannonLength * sinf(tank1CannonAngle), - speed, &buffer[length], bufferSize - length); - } - - if(playerHasWon != 1) { - // Same idea for tank 2 - length += renderArc(tank2X, tank2Y, tankRadius, M_PI, 2.0 * M_PI, - speed, &buffer[length], bufferSize - length); - length += renderLine(tank2X + tankRadius, tank2Y, - tank2X - tankRadius, tank2Y, - speed, &buffer[length], bufferSize - length); - length += renderLine(tank2X + tankRadius * cosf(tank2CannonAngle), - tank2Y - tankRadius * sinf(tank2CannonAngle), - tank2X + cannonLength * cosf(tank2CannonAngle), - tank2Y - cannonLength * sinf(tank2CannonAngle), - speed, &buffer[length], bufferSize - length); - } - - return length; -} - -int drawProjectile(float *buffer, int bufferSize, int framesTarget) -{ - if(!projectileInMotion) - return 0; - - // Draw a point for a specified number of frames (each containing X and Y) - // Return the number of items used in the buffer, which will be twice - // the number of frames unless the buffer is full - - if(bufferSize/2 < framesTarget) { - renderPoint(projectilePositionX, projectilePositionY, buffer, bufferSize/2); - return bufferSize; - } - else { - renderPoint(projectilePositionX, projectilePositionY, buffer, framesTarget); - return framesTarget*2; - } -} - -// Main drawing routine entry point -int drawGame(float *buffer, int bufferSize) -{ - int length; - - // Based on buffer size, come up with speeds for each of the elements - // 50% of time to ground; 30% to the tanks and 20% to the projectile - // Give a margin of 25% beyond so we don't run out of buffer space - // if things take longer to draw than we guess they will - const float amountToUse = 0.375; // 0.75/2 because two samples per frame - const float groundFraction = 0.5 * amountToUse; - const float tankFraction = 0.3 * amountToUse; - const float projectileFraction = 0.2 * amountToUse; - - length = drawGround(buffer, bufferSize, bufferSize * groundFraction); - length += drawTanks(&buffer[length], bufferSize - length, - bufferSize * tankFraction); - length += drawProjectile(&buffer[length], bufferSize - length, - bufferSize * projectileFraction); - - return length; -}
--- a/examples/10-Instruments/tank_wars/game.h Tue Jun 21 20:00:12 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -/* - * game.h - * - * Created on: Nov 10, 2014 - * Author: parallels - */ - -#ifndef GAME_H_ -#define GAME_H_ - -// Initialisation -void setupGame(int width, int height); -void restartGame(); - -// Update physics -void nextGameFrame(); - -// State updaters -void setTank1CannonAngle(float angle); -void setTank2CannonAngle(float angle); -void setTank1CannonStrength(float strength); -void setTank2CannonStrength(float strength); -void fireProjectile(); - -// State queries -bool gameStatusPlayer1Turn(); -bool gameStatusProjectileInMotion(); -int gameStatusWinner(); -bool gameStatusCollisionOccurred(); -bool gameStatusTankHitOccurred(); -float gameStatusProjectileHeight(); - -// Render screen; returns length of buffer used -int drawGame(float *buffer, int bufferSize); - -// Cleanup and memory release -void cleanupGame(); - -#endif /* GAME_H_ */
--- a/examples/10-Instruments/tank_wars/main.cpp Tue Jun 21 20:00:12 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,169 +0,0 @@ -/* - * main.cpp - * - * Created on: Oct 24, 2014 - * Author: parallels - */ - -#include <iostream> -#include <cstdlib> -#include <libgen.h> -#include <signal.h> -#include <getopt.h> -#include <sndfile.h> -#include <Bela.h> - -extern int gScreenFramesPerSecond; - -float *gMusicBuffer = 0; -int gMusicBufferLength = 0; -float *gSoundBoomBuffer = 0; -int gSoundBoomBufferLength = 0; -float *gSoundHitBuffer = 0; -int gSoundHitBufferLength = 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) -{ - gShouldStop = true; -} - -// Print usage information -void usage(const char * processName) -{ - cerr << "Usage: " << processName << " [options]" << endl; - - Bela_usage(); - - cerr << " --fps [-f] value: Set target frames per second\n"; - cerr << " --help [-h]: Print this menu\n"; -} - -int main(int argc, char *argv[]) -{ - BelaInitSettings settings; // Standard audio settings - string musicFileName = "music.wav"; - string soundBoomFileName = "boom.wav"; - string soundHitFileName = "hit.wav"; - - struct option customOptions[] = - { - {"help", 0, NULL, 'h'}, - {"fps", 1, NULL, 'f'}, - {NULL, 0, NULL, 0} - }; - - // Set default settings - Bela_defaultSettings(&settings); - - // Parse command-line arguments - while (1) { - int c; - if ((c = Bela_getopt_long(argc, argv, "hf:", customOptions, &settings)) < 0) - break; - switch (c) { - case 'f': - gScreenFramesPerSecond = atoi(optarg); - if(gScreenFramesPerSecond < 1) - gScreenFramesPerSecond = 1; - if(gScreenFramesPerSecond > 100) - gScreenFramesPerSecond = 100; - break; - case 'h': - usage(basename(argv[0])); - exit(0); - case '?': - default: - usage(basename(argv[0])); - exit(1); - } - } - - // 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; - } - if(loadSoundFile(soundHitFileName, &gSoundHitBuffer, &gSoundHitBufferLength) != 0) { - cout << "Warning: unable to load sound file " << soundHitFileName << endl; - } - - // Initialise the PRU audio device - if(Bela_initAudio(&settings, 0) != 0) { - cout << "Error: unable to initialise audio" << endl; - return -1; - } - - // Start the audio device running - if(Bela_startAudio()) { - cout << "Error: unable to start real-time audio" << endl; - return -1; - } - - // Set up interrupt handler to catch Control-C and SIGTERM - signal(SIGINT, interrupt_handler); - signal(SIGTERM, interrupt_handler); - - // Run until told to stop - while(!gShouldStop) { - usleep(100000); - } - - // Stop the audio device - Bela_stopAudio(); - - // Clean up any resources allocated for audio - Bela_cleanupAudio(); - - // Release sound files - if(gMusicBuffer != 0) - free(gMusicBuffer); - if(gSoundBoomBuffer != 0) - free(gSoundBoomBuffer); - if(gSoundHitBuffer != 0) - free(gSoundHitBuffer); - - // All done! - return 0; -}
--- a/examples/10-Instruments/tank_wars/render.cpp Tue Jun 21 20:00:12 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,337 +0,0 @@ -/* - * render.cpp - * - * Created on: Oct 24, 2014 - * Author: parallels - */ - - -#include <Bela.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 - -int gOutputX = 0; // Outputs for the scope -int gOutputY = 1; -int gOutputPlayer1LED = 2; -int gOutputPlayer2LED = 3; - -int gGameFramesPerSecond = 60; // How often the physics are updated -int gGameFrameInterval; // ...and in frames -int gSamplesUntilNextFrame; // Counter until next update -int gSamplesSinceFinish = 0; // How long since somebody won? -bool gGameShouldRestart = false;// Whether we need to reinitiliase the game - -// Counter for overall number of samples that have elapsed -unsigned int gSampleCounter = 0; - -// 1st-order filter and peak detector for launcher input -float gLauncherLastSample = 0; -float gLauncherFilterPole = 0.8; -float gLauncherPeakValue = 0; -float gLauncherPeakFilterPole = 0.999; -float gLauncherNoiseThreshold = 0.01; -float gLauncherMinimumPeak = 0.1; -bool gLauncherTriggered = false; - -// Screen update rate; affects buffer size. Actual contents of buffer -// may be smaller than this -int gScreenWidth = 512; -int gScreenHeight = 512; -int gScreenFramesPerSecond = 25; - -// Double buffer for rendering screen. Each is an interleaved buffer -// of XY data. -float *gScreenBuffer1, *gScreenBuffer2; -float *gScreenBufferWrite, *gScreenBufferRead; -int gScreenBufferMaxLength; // What is the total buffer allocated? -int gScreenBufferReadLength; // How long is the read buffer? -int gScreenBufferWriteLength; // How long is the write (next) buffer? -int gScreenBufferReadPointer; // Where are we in the read buffer now? -int gScreenBufferNextUpdateLocation; // When should we render the next buffer? -bool gScreenNextBufferReady; // Is the next buffer ready to go? - -// 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; -extern float *gSoundHitBuffer; -extern int gSoundHitBufferLength; - -// Current state for sound and music -int gMusicBufferPointer = 0; // 0 means start of buffer... -int gSoundBoomBufferPointer = -1; // -1 means don't play... -int gSoundHitBufferPointer = -1; -float gSoundProjectileOscillatorPhase = 0; -float gSoundProjectileOscillatorGain = 0.2; -float gOscillatorPhaseScaler = 0; - -void screen_update(); - -// 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. -// -// userData holds an opaque pointer to a data structure that was passed -// in from the call to initAudio(). -// -// Return true on success; returning false halts the program. - -bool setup(BelaContext *context, void *userData) -{ - srandom(time(NULL)); - - // Verify we are running with matrix enabled - 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 * context->analogSampleRate / gScreenFramesPerSecond; - gScreenBuffer1 = new float[gScreenBufferMaxLength]; - gScreenBuffer2 = new float[gScreenBufferMaxLength]; - if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) { - rt_printf("Error initialising screen buffers\n"); - return false; - } - - gScreenBufferRead = gScreenBuffer1; - gScreenBufferWrite = gScreenBuffer2; - gScreenBufferReadLength = gScreenBufferWriteLength = 0; - gScreenBufferReadPointer = 0; - gScreenBufferNextUpdateLocation = 0; - gScreenNextBufferReady = false; - - // Initialise the game - setupGame(gScreenWidth, gScreenHeight); - gGameFrameInterval = context->analogSampleRate / gGameFramesPerSecond; - gSamplesUntilNextFrame = gGameFrameInterval; - - // Initialise auxiliary tasks - if((gScreenUpdateTask = Bela_createAuxiliaryTask(&screen_update, 90, - "bela-screen-update")) == 0) - return false; - - return true; -} - -// Swap buffers on the screen -void swap_buffers() -{ - if(gScreenBufferRead == gScreenBuffer1) { - gScreenBufferRead = gScreenBuffer2; - gScreenBufferWrite = gScreenBuffer1; - } - else { - gScreenBufferRead = gScreenBuffer1; - gScreenBufferWrite = gScreenBuffer2; - } - - gScreenBufferReadLength = gScreenBufferWriteLength; - gScreenBufferReadPointer = 0; - - // Schedule next update for 3/4 of the way through the buffer - gScreenBufferNextUpdateLocation = gScreenBufferReadLength * 0.75; - gScreenNextBufferReady = false; -} - -// render() is called regularly at the highest priority by the audio engine. -// Input and output are given from the audio hardware and the other -// ADCs and DACs (if available). If only audio is available, numMatrixFrames -// will be 0. - -void render(BelaContext *context, void *userData) -{ - 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; - } - - if(gSoundHitBuffer != 0 && gSoundHitBufferPointer >= 0) { - audioSample += gSoundHitBuffer[gSoundHitBufferPointer++]; - if(gSoundHitBufferPointer >= gSoundHitBufferLength) - gSoundHitBufferPointer = -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(context, n, gInputLauncher); - float launchSample = gLauncherFilterPole * gLauncherLastSample + - (1.0f - gLauncherFilterPole) * rawSample; - gLauncherLastSample = launchSample; - - // Peak-detect on launch signal - if(launchSample >= gLauncherPeakValue) { - gLauncherPeakValue = launchSample; - gLauncherTriggered = false; - } - else { - if(gLauncherPeakValue - launchSample > gLauncherNoiseThreshold && !gLauncherTriggered) { - // Detected a peak; is it big enough overall? - if(gLauncherPeakValue >= gLauncherMinimumPeak) { - gLauncherTriggered = true; - // Peak detected-- fire!! - // Set both cannon strengths but only one will - // fire depending on whose turn it is - float strength = map(gLauncherPeakValue, - gLauncherMinimumPeak, 1.0, - 0.5f, 10.0f); - setTank1CannonStrength(strength); - setTank2CannonStrength(strength); - fireProjectile(); - } - } - - gLauncherPeakValue *= gLauncherPeakFilterPole; - } - - if(--gSamplesUntilNextFrame <= 0) { - // Update game physics and cannon angles - gSamplesUntilNextFrame = gGameFrameInterval; - - setTank1CannonAngle(map(analogRead(context, n, gInputTank1Angle), - 0, 1.0, M_PI, 0)); - setTank2CannonAngle(map(analogRead(context, n, gInputTank2Angle), - 0, 1.0, M_PI, 0)); - nextGameFrame(); - - // Check for collision and start sound accordingly - if(gameStatusCollisionOccurred()) { - gSoundBoomBufferPointer = 0; - } - - if(gameStatusTankHitOccurred()) { - gSoundHitBufferPointer = 0; - } - } - - if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1 - && gScreenNextBufferReady) { - // Got to the end; swap buffers - swap_buffers(); - } - - // Push current screen buffer to the matrix output - if(gScreenBufferReadPointer < gScreenBufferReadLength - 1) { - float x = gScreenBufferRead[gScreenBufferReadPointer++]; - float y = gScreenBufferRead[gScreenBufferReadPointer++]; - - // Rescale screen coordinates to matrix ranges; invert the Y - // coordinate to go from normal screen coordinates to scope coordinates - analogWriteOnce(context, n, gOutputX, constrain(map(x, 0, gScreenWidth, 0, 1.0), 0, 1.0)); - analogWriteOnce(context, n, gOutputY, constrain(map(y, 0, gScreenHeight, 1.0, 0), 0, 1.0)); - } - else { - // Still not ready! Write 0 until something happens - analogWriteOnce(context, n, gOutputX, 0); - analogWriteOnce(context, n, gOutputY, 0); - } - - if(gameStatusWinner() != 0) { - // Blink one LED to show who won - // Blink both LEDs when projectile is in motion - float val = (gSampleCounter % 4000 > 2000) ? 1.0 : 0; - analogWriteOnce(context, n, gOutputPlayer1LED, gameStatusWinner() == 1 ? val : 0); - analogWriteOnce(context, n, gOutputPlayer2LED, gameStatusWinner() == 2 ? val : 0); - - // After 5 seconds, restart the game - gSamplesSinceFinish++; - if(gSamplesSinceFinish > 22050*5) - gGameShouldRestart = true; - } - else if(gameStatusProjectileInMotion()) { - // Blink both LEDs when projectile is in motion - float val = (gSampleCounter % 2000 > 1000) ? 1.0 : 0; - analogWriteOnce(context, n, gOutputPlayer1LED, val); - analogWriteOnce(context, n, gOutputPlayer2LED, val); - } - else if(gameStatusPlayer1Turn()) { - analogWriteOnce(context, n, gOutputPlayer1LED, 1.0); - analogWriteOnce(context, n, gOutputPlayer2LED, 0); - } - else { - analogWriteOnce(context, n, gOutputPlayer2LED, 1.0); - analogWriteOnce(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 - Bela_scheduleAuxiliaryTask(gScreenUpdateTask); - } - - gSampleCounter++; - } -} - -void screen_update() -{ - // If we should restart, reinitialise the game - if(gGameShouldRestart) { - restartGame(); - gGameShouldRestart = false; - gSamplesSinceFinish = 0; - } - - // Render the game based on the current state - gScreenBufferWriteLength = drawGame(gScreenBufferWrite, gScreenBufferMaxLength); - - // Flag it as ready to go - gScreenNextBufferReady = true; -} - -// cleanup() is called once at the end, after the audio has stopped. -// Release any resources that were allocated in setup(). - -void cleanup(BelaContext *context, void *userData) -{ - // Clean up the game state - cleanupGame(); -}
--- a/examples/10-Instruments/tank_wars/vector_graphics.cpp Tue Jun 21 20:00:12 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* - * vector_graphics.cpp - * - * Created on: Nov 10, 2014 - * Author: parallels - */ - -#include <cmath> - -// Draw a line between two points at a specified rate in -// pixels per buffer sample. Indicate maximum available space. -// Returns space used -int renderLine(float x1, float y1, float x2, float y2, float speed, - float *buffer, int maxLength) { - // Figure out length of line and therefore how many samples - // are needed to represent it based on the speed (rounded to nearest int) - float totalLineLength = sqrtf((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)); - int samplesNeeded = floorf(totalLineLength / speed + 0.5); - - // Now render into the buffer - int length = 0; - float scaleFactor = 1.0f / samplesNeeded; - for(int n = 0; n < samplesNeeded; n++) { - if(length >= maxLength - 1) - return length; - // X coordinate - *buffer++ = x1 + (float)n * scaleFactor * (x2 - x1); - // Y coordinate - *buffer++ = y1 + (float)n * scaleFactor * (y2 - y1); - length += 2; - } - - return length; -} - -// Draw an arc around a centre point at a specified rate of pixels -// per buffer sample. Indicate maximum available space. -// Returns space used -int renderArc(float x, float y, float radius, float thetaMin, float thetaMax, - float speed, float *buffer, int maxLength) { - // Figure out circumference of arc and therefore how many samples - // are needed to represent it based on the speed (rounded to nearest int) - float circumference = (thetaMax - thetaMin) * radius; - int samplesNeeded = floorf(circumference / speed + 0.5); - - // Now render into the buffer - int length = 0; - float scaleFactor = 1.0f / samplesNeeded; - for(int n = 0; n < samplesNeeded; n++) { - if(length >= maxLength - 1) - return length; - // Get current angle - float theta = thetaMin + (float)n * scaleFactor * (thetaMax - thetaMin); - - // Convert polar to cartesian coordinates - *buffer++ = x + radius * cosf(theta); - *buffer++ = y + radius * sinf(theta); - - length += 2; - } - - return length; -} - -// Draw a single point for a specified number of frames -void renderPoint(float x, float y, float *buffer, float length) { - while(length > 0) { - *buffer++ = x; - *buffer++ = y; - length--; - } -}
--- a/examples/10-Instruments/tank_wars/vector_graphics.h Tue Jun 21 20:00:12 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -/* - * vector_graphics.h - * - * Created on: Nov 10, 2014 - * Author: parallels - */ - -#ifndef VECTOR_GRAPHICS_H_ -#define VECTOR_GRAPHICS_H_ - -int renderLine(float x1, float y1, float x2, float y2, float speed, - float *buffer, int maxLength); -int renderArc(float x, float y, float radius, float thetaMin, float thetaMax, - float speed, float *buffer, int maxLength); -void renderPoint(float x, float y, float *buffer, float length); - - -#endif /* VECTOR_GRAPHICS_H_ */