summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/FFTOctaveAnalyzer.cpp98
-rw-r--r--src/FFTOctaveAnalyzer.h45
-rw-r--r--src/fft.cpp489
-rw-r--r--src/fft.h33
-rw-r--r--src/main.cpp17
-rw-r--r--src/src.zipbin0 -> 5669 bytes
-rw-r--r--src/testApp.cpp827
-rw-r--r--src/testApp.h158
8 files changed, 1667 insertions, 0 deletions
diff --git a/src/FFTOctaveAnalyzer.cpp b/src/FFTOctaveAnalyzer.cpp
new file mode 100644
index 0000000..4c91eeb
--- /dev/null
+++ b/src/FFTOctaveAnalyzer.cpp
@@ -0,0 +1,98 @@
+#include "ofMain.h"
+#include "FFTOctaveAnalyzer.h"
+
+
+void FFTOctaveAnalyzer::setup(float samplingRate, int nBandsInTheFFT, int nAveragesPerOctave){
+
+ samplingRate = samplingRate;
+ nSpectrum = nBandsInTheFFT;
+ spectrumFrequencySpan = (samplingRate / 2.0f) / (float)(nSpectrum);
+ nAverages = nBandsInTheFFT;
+ // fe: 2f for octave bands, sqrt(2) for half-octave bands, cuberoot(2) for third-octave bands, etc
+ if (nAveragesPerOctave==0) // um, wtf?
+ nAveragesPerOctave = 1;
+ nAveragesPerOctave = nAveragesPerOctave;
+ averageFrequencyIncrement = pow(2.0f, 1.0f/(float)(nAveragesPerOctave));
+ // this isn't currently configurable (used once here then no effect), but here's some reasoning:
+ // 43 is a good value if you want to approximate "computer" octaves: 44100/2/2/2/2/2/2/2/2/2/2
+ // 55 is a good value if you'd rather approximate A-440 octaves: 440/2/2/2
+ // 65 is a good value if you'd rather approximate "middle-C" octaves: ~262/2/2
+ // you could easily double it if you felt the lowest band was just rumble noise (as it probably is)
+ // but don't go much smaller unless you have a huge fft window size (see below for more why)
+ // keep in mind, if you change it, that the number of actual bands may change +/-1, and
+ // for some values, the last averaging band may not be very useful (may extend above nyquist)
+ firstOctaveFrequency = 55.0f;
+ // for each spectrum[] bin, calculate the mapping into the appropriate average[] bin.
+ // this gives us roughly log-sized averaging bins, subject to how "fine" the spectrum bins are.
+ // with more spectrum bins, you can better map into the averaging bins (especially at low
+ // frequencies) or use more averaging bins per octave. with an fft window size of 2048,
+ // sampling rate of 44100, and first octave around 55, that's about enough to do half-octave
+ // analysis. if you don't have enough spectrum bins to map adequately into averaging bins
+ // at the requested number per octave then you'll end up with "empty" averaging bins, where
+ // there is no spectrum available to map into it. (so... if you have "nonreactive" averages,
+ // either increase fft buffer size, or decrease number of averages per octave, etc)
+ spe2avg = new int[nSpectrum];
+ int avgidx = 0;
+ float averageFreq = firstOctaveFrequency; // the "top" of the first averaging bin
+ // we're looking for the "top" of the first spectrum bin, and i'm just sort of
+ // guessing that this is where it is (or possibly spectrumFrequencySpan/2?)
+ // ... either way it's probably close enough for these purposes
+ float spectrumFreq = spectrumFrequencySpan;
+ for (int speidx=0; speidx < nSpectrum; speidx++) {
+ while (spectrumFreq > averageFreq) {
+ avgidx++;
+ averageFreq *= averageFrequencyIncrement;
+ }
+ spe2avg[speidx] = avgidx;
+ spectrumFreq += spectrumFrequencySpan;
+ }
+ nAverages = avgidx;
+ averages = new float[nAverages];
+ peaks = new float[nAverages];
+ peakHoldTimes = new int[nAverages];
+ peakHoldTime = 0; // arbitrary
+ peakDecayRate = 0.9f; // arbitrary
+ linearEQIntercept = 1.0f; // unity -- no eq by default
+ linearEQSlope = 0.0f; // unity -- no eq by default
+}
+
+void FFTOctaveAnalyzer::calculate(float * fftData){
+
+ int last_avgidx = 0; // tracks when we've crossed into a new averaging bin, so store current average
+ float sum = 0.0f; // running total of spectrum data
+ int count = 0; // count of spectrums accumulated (for averaging)
+ for (int speidx=0; speidx < nSpectrum; speidx++) {
+ count++;
+ sum += fftData[speidx] * (linearEQIntercept + (float)(speidx) * linearEQSlope);
+ int avgidx = spe2avg[speidx];
+ if (avgidx != last_avgidx) {
+
+ for (int j = last_avgidx; j < avgidx; j++){
+ averages[j] = sum / (float)(count);
+ }
+ count = 0;
+ sum = 0.0f;
+ }
+ last_avgidx = avgidx;
+ }
+ // the last average was probably not calculated...
+ if ((count > 0) && (last_avgidx < nAverages)){
+ averages[last_avgidx] = sum / (float)(count);
+ }
+
+ // update the peaks separately
+ for (int i=0; i < nAverages; i++) {
+ if (averages[i] >= peaks[i]) {
+ // save new peak level, also reset the hold timer
+ peaks[i] = averages[i];
+ peakHoldTimes[i] = peakHoldTime;
+ } else {
+ // current average does not exceed peak, so hold or decay the peak
+ if (peakHoldTimes[i] > 0) {
+ peakHoldTimes[i]--;
+ } else {
+ peaks[i] *= peakDecayRate;
+ }
+ }
+ }
+}
diff --git a/src/FFTOctaveAnalyzer.h b/src/FFTOctaveAnalyzer.h
new file mode 100644
index 0000000..8fef773
--- /dev/null
+++ b/src/FFTOctaveAnalyzer.h
@@ -0,0 +1,45 @@
+
+#ifndef _FFTANALYZER
+#define _FFTANALYZER
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846 /* pi */
+#endif
+
+
+class FFTOctaveAnalyzer {
+
+public:
+
+ //FFT fft; // a reference to the FFT instance
+
+ float samplingRate; // sampling rate in Hz (needed to calculate frequency spans)
+ int nSpectrum; // number of spectrum bins in the fft
+ int nAverages; // number of averaging bins here
+ int nAveragesPerOctave; // number of averages per octave as requested by user
+ float spectrumFrequencySpan; // the "width" of an fft spectrum bin in Hz
+ float firstOctaveFrequency; // the "top" of the first averaging bin here in Hz
+ float averageFrequencyIncrement; // the root-of-two multiplier between averaging bin frequencies
+ float * averages; // the actual averages
+ float * peaks; // peaks of the averages, aka "maxAverages" in other implementations
+ int * peakHoldTimes; // how long to hold THIS peak meter? decay if == 0
+ int peakHoldTime; // how long do we hold peaks? (in fft frames)
+ float peakDecayRate; // how quickly the peaks decay: 0f=instantly .. 1f=not at all
+ int * spe2avg; // the mapping between spectrum[] indices and averages[] indices
+ // the fft's log equalizer() is no longer of any use (it would be nonsense to log scale
+ // the spectrum values into log-sized average bins) so here's a quick-and-dirty linear
+ // equalizer instead:
+ float linearEQSlope; // the rate of linear eq
+ float linearEQIntercept; // the base linear scaling used at the first averaging bin
+ // the formula is: spectrum[i] * (linearEQIntercept + i * linearEQSlope)
+ // so.. note that clever use of it can also provide a "gain" control of sorts
+ // (fe: set intercept to 2f and slope to 0f to double gain)
+
+ void setup(float samplingRate, int nBandsInTheFFT, int nAveragesPerOctave);
+
+ void calculate(float * fftData);
+
+};
+
+
+#endif
diff --git a/src/fft.cpp b/src/fft.cpp
new file mode 100644
index 0000000..ca3217e
--- /dev/null
+++ b/src/fft.cpp
@@ -0,0 +1,489 @@
+/**********************************************************************
+
+ fft.cpp
+
+
+ This class is a C++ wrapper for original code
+ written by Dominic Mazzoni in September 2000
+
+ This file contains a few FFT routines, including a real-FFT
+ routine that is almost twice as fast as a normal complex FFT,
+ and a power spectrum routine which is more convenient when
+ you know you don't care about phase information. It now also
+ contains a few basic windowing functions.
+
+ Some of this code was based on a free implementation of an FFT
+ by Don Cross, available on the web at:
+
+ http://www.intersrv.com/~dcross/fft.html
+
+ The basic algorithm for his code was based on Numerical Recipes
+ in Fortran. I optimized his code further by reducing array
+ accesses, caching the bit reversal table, and eliminating
+ float-to-double conversions, and I added the routines to
+ calculate a real FFT and a real power spectrum.
+
+ Note: all of these routines use single-precision floats.
+ I have found that in practice, floats work well until you
+ get above 8192 samples. If you need to do a larger FFT,
+ you need to use doubles.
+
+**********************************************************************/
+
+#include "fft.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+int **gFFTBitTable = NULL;
+const int MaxFastBits = 16;
+
+int IsPowerOfTwo(int x)
+{
+ if (x < 2)
+ return false;
+
+ if (x & (x - 1)) /* Thanks to 'byang' for this cute trick! */
+ return false;
+
+ return true;
+}
+
+int NumberOfBitsNeeded(int PowerOfTwo)
+{
+ int i;
+
+ if (PowerOfTwo < 2) {
+ fprintf(stderr, "Error: FFT called with size %d\n", PowerOfTwo);
+ exit(1);
+ }
+
+ for (i = 0;; i++)
+ if (PowerOfTwo & (1 << i))
+ return i;
+}
+
+int ReverseBits(int index, int NumBits)
+{
+ int i, rev;
+
+ for (i = rev = 0; i < NumBits; i++) {
+ rev = (rev << 1) | (index & 1);
+ index >>= 1;
+ }
+
+ return rev;
+}
+
+void InitFFT()
+{
+ gFFTBitTable = new int *[MaxFastBits];
+
+ int len = 2;
+ for (int b = 1; b <= MaxFastBits; b++) {
+
+ gFFTBitTable[b - 1] = new int[len];
+
+ for (int i = 0; i < len; i++)
+ gFFTBitTable[b - 1][i] = ReverseBits(i, b);
+
+ len <<= 1;
+ }
+}
+
+inline int FastReverseBits(int i, int NumBits)
+{
+ if (NumBits <= MaxFastBits)
+ return gFFTBitTable[NumBits - 1][i];
+ else
+ return ReverseBits(i, NumBits);
+}
+
+/*
+ * Complex Fast Fourier Transform
+ */
+
+void FFT(int NumSamples,
+ bool InverseTransform,
+ float *RealIn, float *ImagIn, float *RealOut, float *ImagOut)
+{
+ int NumBits; /* Number of bits needed to store indices */
+ int i, j, k, n;
+ int BlockSize, BlockEnd;
+
+ double angle_numerator = 2.0 * M_PI;
+ float tr, ti; /* temp real, temp imaginary */
+
+ if (!IsPowerOfTwo(NumSamples)) {
+ fprintf(stderr, "%d is not a power of two\n", NumSamples);
+ exit(1);
+ }
+
+ if (!gFFTBitTable)
+ InitFFT();
+
+ if (InverseTransform)
+ angle_numerator = -angle_numerator;
+
+ NumBits = NumberOfBitsNeeded(NumSamples);
+
+ /*
+ ** Do simultaneous data copy and bit-reversal ordering into outputs...
+ */
+
+ for (i = 0; i < NumSamples; i++) {
+ j = FastReverseBits(i, NumBits);
+ RealOut[j] = RealIn[i];
+ ImagOut[j] = (ImagIn == NULL) ? 0.0 : ImagIn[i];
+ }
+
+ /*
+ ** Do the FFT itself...
+ */
+
+ BlockEnd = 1;
+ for (BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1) {
+
+ double delta_angle = angle_numerator / (double) BlockSize;
+
+ float sm2 = sin(-2 * delta_angle);
+ float sm1 = sin(-delta_angle);
+ float cm2 = cos(-2 * delta_angle);
+ float cm1 = cos(-delta_angle);
+ float w = 2 * cm1;
+ float ar0, ar1, ar2, ai0, ai1, ai2;
+
+ for (i = 0; i < NumSamples; i += BlockSize) {
+ ar2 = cm2;
+ ar1 = cm1;
+
+ ai2 = sm2;
+ ai1 = sm1;
+
+ for (j = i, n = 0; n < BlockEnd; j++, n++) {
+ ar0 = w * ar1 - ar2;
+ ar2 = ar1;
+ ar1 = ar0;
+
+ ai0 = w * ai1 - ai2;
+ ai2 = ai1;
+ ai1 = ai0;
+
+ k = j + BlockEnd;
+ tr = ar0 * RealOut[k] - ai0 * ImagOut[k];
+ ti = ar0 * ImagOut[k] + ai0 * RealOut[k];
+
+ RealOut[k] = RealOut[j] - tr;
+ ImagOut[k] = ImagOut[j] - ti;
+
+ RealOut[j] += tr;
+ ImagOut[j] += ti;
+ }
+ }
+
+ BlockEnd = BlockSize;
+ }
+
+ /*
+ ** Need to normalize if inverse transform...
+ */
+
+ if (InverseTransform) {
+ float denom = (float) NumSamples;
+
+ for (i = 0; i < NumSamples; i++) {
+ RealOut[i] /= denom;
+ ImagOut[i] /= denom;
+ }
+ }
+}
+
+/*
+ * Real Fast Fourier Transform
+ *
+ * This function was based on the code in Numerical Recipes in C.
+ * In Num. Rec., the inner loop is based on a single 1-based array
+ * of interleaved real and imaginary numbers. Because we have two
+ * separate zero-based arrays, our indices are quite different.
+ * Here is the correspondence between Num. Rec. indices and our indices:
+ *
+ * i1 <-> real[i]
+ * i2 <-> imag[i]
+ * i3 <-> real[n/2-i]
+ * i4 <-> imag[n/2-i]
+ */
+
+void RealFFT(int NumSamples, float *RealIn, float *RealOut, float *ImagOut)
+{
+ int Half = NumSamples / 2;
+ int i;
+
+ float theta = M_PI / Half;
+
+ float *tmpReal = new float[Half];
+ float *tmpImag = new float[Half];
+
+ for (i = 0; i < Half; i++) {
+ tmpReal[i] = RealIn[2 * i];
+ tmpImag[i] = RealIn[2 * i + 1];
+ }
+
+ FFT(Half, 0, tmpReal, tmpImag, RealOut, ImagOut);
+
+ float wtemp = float (sin(0.5 * theta));
+
+ float wpr = -2.0 * wtemp * wtemp;
+ float wpi = float (sin(theta));
+ float wr = 1.0 + wpr;
+ float wi = wpi;
+
+ int i3;
+
+ float h1r, h1i, h2r, h2i;
+
+ for (i = 1; i < Half / 2; i++) {
+
+ i3 = Half - i;
+
+ h1r = 0.5 * (RealOut[i] + RealOut[i3]);
+ h1i = 0.5 * (ImagOut[i] - ImagOut[i3]);
+ h2r = 0.5 * (ImagOut[i] + ImagOut[i3]);
+ h2i = -0.5 * (RealOut[i] - RealOut[i3]);
+
+ RealOut[i] = h1r + wr * h2r - wi * h2i;
+ ImagOut[i] = h1i + wr * h2i + wi * h2r;
+ RealOut[i3] = h1r - wr * h2r + wi * h2i;
+ ImagOut[i3] = -h1i + wr * h2i + wi * h2r;
+
+ wr = (wtemp = wr) * wpr - wi * wpi + wr;
+ wi = wi * wpr + wtemp * wpi + wi;
+ }
+
+ RealOut[0] = (h1r = RealOut[0]) + ImagOut[0];
+ ImagOut[0] = h1r - ImagOut[0];
+
+ delete[]tmpReal;
+ delete[]tmpImag;
+}
+
+/*
+ * PowerSpectrum
+ *
+ * This function computes the same as RealFFT, above, but
+ * adds the squares of the real and imaginary part of each
+ * coefficient, extracting the power and throwing away the
+ * phase.
+ *
+ * For speed, it does not call RealFFT, but duplicates some
+ * of its code.
+ */
+
+void PowerSpectrum(int NumSamples, float *In, float *Out)
+{
+ int Half = NumSamples / 2;
+ int i;
+
+ float theta = M_PI / Half;
+
+ float *tmpReal = new float[Half];
+ float *tmpImag = new float[Half];
+ float *RealOut = new float[Half];
+ float *ImagOut = new float[Half];
+
+ for (i = 0; i < Half; i++) {
+ tmpReal[i] = In[2 * i];
+ tmpImag[i] = In[2 * i + 1];
+ }
+
+ FFT(Half, 0, tmpReal, tmpImag, RealOut, ImagOut);
+
+ float wtemp = float (sin(0.5 * theta));
+
+ float wpr = -2.0 * wtemp * wtemp;
+ float wpi = float (sin(theta));
+ float wr = 1.0 + wpr;
+ float wi = wpi;
+
+ int i3;
+
+ float h1r, h1i, h2r, h2i, rt, it;
+ //float total=0;
+
+ for (i = 1; i < Half / 2; i++) {
+
+ i3 = Half - i;
+
+ h1r = 0.5 * (RealOut[i] + RealOut[i3]);
+ h1i = 0.5 * (ImagOut[i] - ImagOut[i3]);
+ h2r = 0.5 * (ImagOut[i] + ImagOut[i3]);
+ h2i = -0.5 * (RealOut[i] - RealOut[i3]);
+
+ rt = h1r + wr * h2r - wi * h2i; //printf("Realout%i = %f",i,rt);total+=fabs(rt);
+ it = h1i + wr * h2i + wi * h2r; // printf(" Imageout%i = %f\n",i,it);
+
+ Out[i] = rt * rt + it * it;
+
+ rt = h1r - wr * h2r + wi * h2i;
+ it = -h1i + wr * h2i + wi * h2r;
+
+ Out[i3] = rt * rt + it * it;
+
+ wr = (wtemp = wr) * wpr - wi * wpi + wr;
+ wi = wi * wpr + wtemp * wpi + wi;
+ }
+ //printf("total = %f\n",total);
+ rt = (h1r = RealOut[0]) + ImagOut[0];
+ it = h1r - ImagOut[0];
+ Out[0] = rt * rt + it * it;
+
+ rt = RealOut[Half / 2];
+ it = ImagOut[Half / 2];
+ Out[Half / 2] = rt * rt + it * it;
+
+ delete[]tmpReal;
+ delete[]tmpImag;
+ delete[]RealOut;
+ delete[]ImagOut;
+}
+
+/*
+ * Windowing Functions
+ */
+
+int NumWindowFuncs()
+{
+ return 4;
+}
+
+char *WindowFuncName(int whichFunction)
+{
+ switch (whichFunction) {
+ default:
+ case 0:
+ return "Rectangular";
+ case 1:
+ return "Bartlett";
+ case 2:
+ return "Hamming";
+ case 3:
+ return "Hanning";
+ }
+}
+
+void WindowFunc(int whichFunction, int NumSamples, float *in)
+{
+ int i;
+
+ if (whichFunction == 1) {
+ // Bartlett (triangular) window
+ for (i = 0; i < NumSamples / 2; i++) {
+ in[i] *= (i / (float) (NumSamples / 2));
+ in[i + (NumSamples / 2)] *=
+ (1.0 - (i / (float) (NumSamples / 2)));
+ }
+ }
+
+ if (whichFunction == 2) {
+ // Hamming
+ for (i = 0; i < NumSamples; i++)
+ in[i] *= 0.54 - 0.46 * cos(2 * M_PI * i / (NumSamples - 1));
+ }
+
+ if (whichFunction == 3) {
+ // Hanning
+ for (i = 0; i < NumSamples; i++)
+ in[i] *= 0.50 - 0.50 * cos(2 * M_PI * i / (NumSamples - 1));
+ }
+}
+
+/* constructor */
+fft::fft() {
+}
+
+/* destructor */
+fft::~fft() {
+
+
+}
+
+/* Calculate the power spectrum */
+void fft::powerSpectrum(int start, int half, float *data, int windowSize,float *magnitude,float *phase, float *power, float *avg_power) {
+ int i;
+ int windowFunc = 3;
+ float total_power = 0.0f;
+
+ /* processing variables*/
+ float *in_real = new float[windowSize];
+ float *in_img = new float[windowSize];
+ float *out_real = new float[windowSize];
+ float *out_img = new float[windowSize];
+
+ for (i = 0; i < windowSize; i++) {
+ in_real[i] = data[start + i];
+ }
+
+ WindowFunc(windowFunc, windowSize, in_real);
+ RealFFT(windowSize, in_real, out_real, out_img);
+
+ for (i = 0; i < half; i++) {
+ /* compute power */
+ power[i] = out_real[i]*out_real[i] + out_img[i]*out_img[i];
+ total_power += power[i];
+ /* compute magnitude and phase */
+ magnitude[i] = 2.0*sqrt(power[i]);
+
+ if (magnitude[i] < 0.000001){ // less than 0.1 nV
+ magnitude[i] = 0; // out of range
+ } else {
+ magnitude[i] = 20.0*log10(magnitude[i] + 1); // get to to db scale
+ }
+
+
+ phase[i] = atan2(out_img[i],out_real[i]);
+ }
+ /* calculate average power */
+ *(avg_power) = total_power / (float) half;
+
+ delete[]in_real;
+ delete[]in_img;
+ delete[]out_real;
+ delete[]out_img;
+}
+
+
+void fft::inversePowerSpectrum(int start, int half, int windowSize, float *finalOut,float *magnitude,float *phase) {
+ int i;
+ int windowFunc = 3;
+
+ /* processing variables*/
+ float *in_real = new float[windowSize];
+ float *in_img = new float[windowSize];
+ float *out_real = new float[windowSize];
+ float *out_img = new float[windowSize];
+
+ /* get real and imag part */
+ for (i = 0; i < half; i++) {
+ in_real[i] = magnitude[i]*cos(phase[i]);
+ in_img[i] = magnitude[i]*sin(phase[i]);
+ }
+
+ /* zero negative frequencies */
+ for (i = half; i < windowSize; i++) {
+ in_real[i] = 0.0;
+ in_img[i] = 0.0;
+ }
+
+ FFT(windowSize, 1, in_real, in_img, out_real, out_img); // second parameter indicates inverse transform
+ WindowFunc(windowFunc, windowSize, out_real);
+
+ for (i = 0; i < windowSize; i++) {
+ finalOut[start + i] += out_real[i];
+ }
+
+ delete[]in_real;
+ delete[]in_img;
+ delete[]out_real;
+ delete[]out_img;
+}
+
+
diff --git a/src/fft.h b/src/fft.h
new file mode 100644
index 0000000..bae5b37
--- /dev/null
+++ b/src/fft.h
@@ -0,0 +1,33 @@
+
+#ifndef _FFT
+#define _FFT
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846 /* pi */
+#endif
+
+
+class fft {
+
+ public:
+
+ fft();
+ ~fft();
+
+ /* Calculate the power spectrum */
+ void powerSpectrum(int start, int half, float *data, int windowSize,float *magnitude,float *phase, float *power, float *avg_power);
+ /* ... the inverse */
+ void inversePowerSpectrum(int start, int half, int windowSize, float *finalOut,float *magnitude,float *phase);
+
+ void processLogXScale(const float * data,unsigned int insize, double fMax,
+ float * avout, float * pkout, unsigned int outsize,
+ float f1, float f2);
+
+
+ float avout[15];
+ float pkout[15];
+
+};
+
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..29b0a27
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,17 @@
+#include "ofMain.h"
+#include "testApp.h"
+
+//========================================================================
+int main( ){
+
+ ofSetupOpenGL(1024,768, OF_FULLSCREEN); // <-------- setup the GL context
+
+
+
+ // this kicks off the running of my app
+ // can be OF_WINDOW or OF_FULLSCREEN
+ // pass in width and height too:
+
+ ofRunApp(new testApp);
+
+}
diff --git a/src/src.zip b/src/src.zip
new file mode 100644
index 0000000..9f80003
--- /dev/null
+++ b/src/src.zip
Binary files differ
diff --git a/src/testApp.cpp b/src/testApp.cpp
new file mode 100644
index 0000000..9aff6c9
--- /dev/null
+++ b/src/testApp.cpp
@@ -0,0 +1,827 @@
+#include "testApp.h"
+
+ /*
+ maybe draw pixel lines to a range of buffers and then play them in various 3D directions
+ pixel lines can have nice, missing corners and be nicely smoothed
+
+ is it insane not to do this in vvvv
+
+ can there be an editing interface
+
+ have a play with vvvv/ max and try to replicate this idea QUICKLY?
+ */
+
+
+//--------------------------------------------------------------
+void testApp::setup(){
+
+ midiIn.listPorts();
+
+ midiIn.openPort(1);
+ midiIn.addListener(this);
+
+ midiOut.listPorts();
+ midiOut.openPort(1);
+
+ // 0 output channeLs,
+ // 2 input channels
+ // 44100 samples per second
+ // BUFFER_SIZE samples per buffer
+ // 4 num buffers (latency)
+
+ soundStream.listDevices();
+
+ left = new float[BUFFER_SIZE];
+ right = new float[BUFFER_SIZE];
+
+ soundStream.setup(this, 0, 2, 44100, BUFFER_SIZE, 4);
+
+ ofSetHexColor(0x666666);
+
+ lFFTanalyzer.setup(44100, BUFFER_SIZE/2,32);
+
+ lFFTanalyzer.peakHoldTime = 15; // hold longer
+ lFFTanalyzer.peakDecayRate = 0.95f; // decay slower
+ lFFTanalyzer.linearEQIntercept = 0.9f; // reduced gain at lowest frequency
+ lFFTanalyzer.linearEQSlope = 0.01f; // increasing gain at higher frequencies
+
+ rFFTanalyzer.setup(44100, BUFFER_SIZE/2,32);
+
+ rFFTanalyzer.peakHoldTime = 15; // hold longer
+ rFFTanalyzer.peakDecayRate = 0.95f; // decay slower
+ rFFTanalyzer.linearEQIntercept = 0.9f; // reduced gain at lowest frequency
+ rFFTanalyzer.linearEQSlope = 0.01f; // increasing gain at higher frequencies
+
+ ofSetFrameRate(60);
+
+ ofBackground(0,0,0);
+
+ F_movieSpeed=0.0;
+
+ //blendImage.loadImage("blend01.png");
+ blendImage.loadMovie("blend01.mov");
+ blendImage.play();
+ blendImage.setLoopState(OF_LOOP_NORMAL);
+ blendImage.setSpeed(F_movieSpeed);
+
+ I_movieSource=0;
+ I_moviePlaying=0;
+
+ renderImage.allocate(ofGetWidth(),ofGetHeight(),GL_RGB);
+ maskShader.load("composite");
+
+ //testImage.loadImage("mask.png");
+
+
+ maskShader.begin();
+ maskShader.setUniformTexture("Tex0", blendImage.getTextureReference(), 0);
+ maskShader.setUniformTexture("Tex1", renderImage.getTextureReference(), 1);
+ maskShader.end();
+
+ showFPS=false;
+
+ //defaults
+
+ F_scale=0;
+ F_drawFrames=10;
+ F_drawStep=0;
+ F_drawDecay=0.95;
+ F_lineWidth=1.0;
+ F_drawAxis=90; //Y
+
+
+ F_xRotate=0;
+ F_yRotate=0;
+ F_zRotate=0;
+
+ F_xRotation=0;
+ F_yRotation=180;
+ F_zRotation=0;
+
+ I_fade1=0;
+ I_fade2=0;
+
+ thisFFTbuffer=0;
+
+ lastFrameTime=ofGetElapsedTimef();
+
+ draworder=true; //forwards;
+
+ visMode=WAVEFORM;
+
+ B_vSync=true;
+ B_fill=true;
+
+ inputMode=PICTURE;
+
+ camWidth=720;
+ camHeight=576;
+ grabber.initGrabber(camWidth,camHeight);
+ gammamap=new unsigned char[256];
+ line1=new unsigned char[camWidth];
+ line2=new unsigned char[camWidth];
+ outBuffer=new unsigned char[camWidth*camHeight*3];
+
+ whitePt=0.9;
+ blackPt=0.3;
+ gamma=1.0;
+
+ //calc gamma map
+ for (int i=0;i<256;i++){
+ float ewp=max(whitePt,blackPt+0.1f); //limit range to 0.1
+ gammamap[i]=(unsigned char)(pow(min(1.0f,max(0.0f,(((float(i)/255.0f)-blackPt)/(ewp-blackPt)))),gamma)*255.0);
+ }
+
+ outTexture.allocate(camWidth,camHeight, GL_RGB);
+ deInterlace=true;
+ B_glitch=false;
+
+ blanker.allocate(64,64,OF_IMAGE_COLOR);
+ // blanker.loadImage("black.png");
+
+
+
+
+ setMidiState();
+}
+
+
+
+//--------------------------------------------------------------
+void testApp::update(){
+ if (inputMode==GRABBER) grabber.grabFrame();
+
+ if (grabber.isFrameNew()){
+ int time=ofGetElapsedTimeMillis();
+ frameTime=time-grabTime;
+ grabTime=time;
+ unsigned char * pixels = grabber.getPixels();
+ if(!deInterlace) {
+ int totalPixels = camWidth*camHeight;
+ for (int i = 0; i < totalPixels; i++){
+ unsigned char gs=(unsigned char)((int(pixels[i*3])+int(pixels[i*3+1])+int(pixels[i*3+2]))/3);
+ outBuffer[i*3] = gammamap[gs];
+ outBuffer[i*3+1] = gammamap[gs];
+ outBuffer[i*3+2] = gammamap[gs];
+ }
+ }
+ else {
+ field2=true;
+ unsigned char* lp1=line1;
+ unsigned char* lp2=line2;
+ unsigned char* tp;
+ int j = 0;
+ for (int i=0;i<camWidth;i++) lp1[i]=gammamap[(unsigned char)((int(pixels[(((j*camWidth)+i)*3)])+int(pixels[(((j*camWidth)+i)*3)+1])+int(pixels[(((j*camWidth)+i)*3)+2]))/3)];//
+ //lp1[i]=gsGammaMap(&pixels[(((j*camWidth)+i)*3)]);
+ //
+ for (j = 2; j < camHeight; j+=2){
+ for (int i=0;i<camWidth;i++) {
+ //lp2[i]=gsGammaMap(&pixels[(((j*camWidth)+i)*3)]);
+ lp2[i]=gammamap[(unsigned char)((int(pixels[(((j*camWidth)+i)*3)])+int(pixels[(((j*camWidth)+i)*3)+1])+int(pixels[(((j*camWidth)+i)*3)+2]))/3)];
+ unsigned char mp=(unsigned char)((int(lp1[i])+int(lp2[i]))/2);
+ outBuffer[(j*camWidth+i)*3] = mp;
+ outBuffer[(j*camWidth+i)*3+1] = mp;
+ outBuffer[(j*camWidth+i)*3+2] = mp;
+ outBuffer[((j+1)*camWidth+i)*3] = lp2[i];
+ outBuffer[((j+1)*camWidth+i)*3+1] = lp2[i];
+ outBuffer[((j+1)*camWidth+i)*3+2] = lp2[i];
+ }
+ tp=lp1;
+ lp1=lp2;
+ lp2=tp;
+ }
+ }
+ outTexture.loadData(outBuffer, camWidth,camHeight, GL_RGB);
+ }
+ if (deInterlace&&use2fields&&field2&&((ofGetElapsedTimeMillis()-grabTime)>=(frameTime/2))){
+ field2=false;
+ unsigned char * pixels = grabber.getPixels();
+ unsigned char* lp1=line1;
+ unsigned char* lp2=line2;
+ unsigned char* tp;
+ int j = 1;
+ for (int i=0;i<camWidth;i++) lp1[i]=gammamap[(unsigned char)((int(pixels[(((j*camWidth)+i)*3)])+int(pixels[(((j*camWidth)+i)*3)+1])+int(pixels[(((j*camWidth)+i)*3)+2]))/3)];
+ for (j = 3; j < camHeight-1; j+=2){
+ for (int i=0;i<camWidth;i++) {
+ lp2[i]=gammamap[(unsigned char)((int(pixels[(((j*camWidth)+i)*3)])+int(pixels[(((j*camWidth)+i)*3)+1])+int(pixels[(((j*camWidth)+i)*3)+2]))/3)];
+ char mp=(unsigned char)((int(lp1[i])+int(lp2[i]))/2);
+ outBuffer[(j*camWidth+i)*3] = mp;
+ outBuffer[(j*camWidth+i)*3+1] = mp;
+ outBuffer[(j*camWidth+i)*3+2] = mp;
+ outBuffer[((j+1)*camWidth+i)*3] = lp2[i];
+ outBuffer[((j+1)*camWidth+i)*3+1] = lp2[i];
+ outBuffer[((j+1)*camWidth+i)*3+2] = lp2[i];
+ }
+ tp=lp1;
+ lp1=lp2;
+ lp2=tp;
+ }
+ outTexture.loadData(outBuffer, camWidth,camHeight, GL_RGB);
+ }
+
+
+ if (I_movieSource!=I_moviePlaying) {
+ blendImage.stop();
+ switch(I_movieSource){
+ case 0:
+ blendImage.loadMovie("gradblend.mov");
+ break;
+ case 1:
+ blendImage.loadMovie("gradblend01.mov");
+ break;
+ case 2:
+ blendImage.loadMovie("ll_waltz1.mov");
+ break;
+ case 3:
+ blendImage.loadMovie("ll_longtrains.avi");
+ break;
+
+ }
+ blendImage.play();
+ blendImage.setLoopState(OF_LOOP_NORMAL);
+ I_moviePlaying=I_movieSource;
+ }
+ blendImage.setSpeed(F_movieSpeed);
+ blendImage.idleMovie();
+}
+
+//--------------------------------------------------------------
+void testApp::draw(){
+ ofSetVerticalSync(B_vSync);
+
+
+
+ static int index=0;
+ float lavg_power = 0.0f;
+ float ravg_power = 0.0f;
+
+ if (visMode==FFT_RAW||visMode==FFT_AVG) {
+ myfft.powerSpectrum(0,(int)BUFFER_SIZE/2, left,BUFFER_SIZE,&lmagnitude[0],&lphase[0],&lpower[0],&lavg_power);
+ myfft.powerSpectrum(0,(int)BUFFER_SIZE/2, right,BUFFER_SIZE,&rmagnitude[0],&rphase[0],&rpower[0],&ravg_power);
+ }
+ if (visMode==FFT_AVG) {
+ lFFTanalyzer.calculate(lmagnitude);
+ rFFTanalyzer.calculate(rmagnitude);
+ }
+
+ thisFFTbuffer=(thisFFTbuffer-1);
+ thisFFTbuffer=thisFFTbuffer<0?BUFFER_FRAMES-1:thisFFTbuffer;
+
+ switch(visMode) {
+ case FFT_AVG:
+ for (int i=0;i<rFFTanalyzer.nAverages;i++) {
+ FFTbuffer[0][i][thisFFTbuffer]=lFFTanalyzer.averages[i];
+ FFTbuffer[1][i][thisFFTbuffer]=rFFTanalyzer.averages[i];
+ }
+ break;
+ case FFT_RAW:
+ for (int i=0;i<BUFFER_SIZE;i++) {
+ FFTbuffer[0][i][thisFFTbuffer]=lmagnitude[i]*3;
+ FFTbuffer[1][i][thisFFTbuffer]=rmagnitude[i]*3;
+ }
+ break;
+ case WAVEFORM:
+ for (int i=0;i<BUFFER_SIZE/2;i++) {
+ FFTbuffer[0][i][thisFFTbuffer]=left[i]*100;
+ FFTbuffer[1][i][thisFFTbuffer]=right[i]*100;
+ }
+ break;
+ }
+
+ //ofDisableAlphaBlending();
+
+ renderImage.begin(); //render to FOB
+ glDisable(GL_BLEND);
+
+ float hw=ofGetWidth()/2;
+ float hh=ofGetHeight()/2;
+ float xStep = ((float)ofGetWidth())/(lFFTanalyzer.nAverages*2);
+ float zStep = ((float)ofGetWidth())/F_drawFrames;
+
+ float newTime=ofGetElapsedTimef();
+ float interval=newTime-lastFrameTime;
+ lastFrameTime=newTime;
+
+
+ if (B_glitch) {
+ //glEnable(GL_BLEND);
+ //glBlendFunc(GL_CONSTANT_COLOR,GL_ONE);
+ //glBlendColor(1.0f,1.0f,1.0f,0.9f);
+ //blanker.draw(0,0,ofGetWidth(),ofGetHeight());
+ }
+ else {
+ glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
+ }
+
+
+ F_xRotation+=(F_xRotate*interval);
+ F_yRotation+=(F_yRotate*interval);
+ F_zRotation+=(F_zRotate*interval);
+ F_drawFrames=max(1.99f,min(F_drawFrames+(F_drawStep*interval),(float)BUFFER_FRAMES));
+
+ ofPushMatrix();
+ ofTranslate(hw,hh);
+ ofRotateX(F_xRotation);
+ ofRotateY(F_yRotation);
+ ofRotateZ(F_zRotation);
+
+
+ if (inputMode==GRABBER) {
+ ofSetColor(I_fade2,I_fade2,I_fade2);
+ ofPushMatrix();
+ ofRotateX(90);
+ ofRotateZ(-90);
+ outTexture.draw(hw,-hh,-ofGetWidth(),ofGetHeight());
+ ofPopMatrix();
+ }
+
+ GLfloat fogColor[4] = {0,0,0, 1.0};
+ glFogi(GL_FOG_MODE, GL_LINEAR);
+ glFogfv(GL_FOG_COLOR, fogColor);
+ glFogf(GL_FOG_DENSITY,1.0 );
+ glHint(GL_FOG_HINT, GL_DONT_CARE);
+ glFogf(GL_FOG_START, ofGetWidth()*(0.25+F_drawDecay));
+ glFogf(GL_FOG_END, ofGetWidth()*(0.75+F_drawDecay));
+ glEnable(GL_FOG);
+
+ //reverse draw order when looking from back
+ int jStart=0;
+ int jEnd=F_drawFrames-1;
+ int jStep=1;
+ float F_yseg=F_yRotation;
+ while (F_yseg<-90) F_yseg+=360;
+ while (F_yseg>270) F_yseg-=360;
+ if (F_yseg>90) {
+ jStart=jEnd;
+ jEnd=0;
+ jStep=-1;
+ if (draworder) {
+ draworder=false;
+ //printf("switched to reverse order\n");
+ }
+ }
+ else if (!draworder) {
+ draworder=true;
+ //printf("switched to normal order\n");
+ }
+
+ for (int j=jStart;j!=jEnd;j+=jStep) {
+ int fB=(thisFFTbuffer+j)%BUFFER_FRAMES;
+
+ ofPushMatrix(); //coordinate transform for FFT draw direction
+ ofTranslate(0,0,(j*zStep)-hw);
+ ofRotateX(F_drawAxis);
+
+ ofFill();
+ ofSetColor(0,0,0);
+ if (B_fill) {
+ glBegin(GL_QUAD_STRIP);
+ for (int i = 0; i < lFFTanalyzer.nAverages-1; i++){
+ glVertex3f((i*xStep)-hw,-(FFTbuffer[0][i][fB] * F_scale),0);
+ glVertex3f((i*xStep)-hw,0,0);
+ }
+ for (int i =lFFTanalyzer.nAverages, k=lFFTanalyzer.nAverages-1; i < lFFTanalyzer.nAverages+rFFTanalyzer.nAverages; i++, k--){
+ glVertex3f((i*xStep)-hw,-(FFTbuffer[1][(lFFTanalyzer.nAverages+rFFTanalyzer.nAverages)-(i+1)][fB] * F_scale),0);
+ glVertex3f((i*xStep)-hw,0,0);
+ }
+ glEnd();
+ }
+
+ ofNoFill();
+ ofSetLineWidth(F_lineWidth);
+ ofSetColor(I_fade1,I_fade1,I_fade1);
+ glBegin(GL_LINE_STRIP);
+ for (int i = 0; i < lFFTanalyzer.nAverages; i++){
+ glVertex3f((i*xStep)-hw,-(FFTbuffer[0][i][fB] * F_scale),0);
+ }
+ for (int i =lFFTanalyzer.nAverages, k=lFFTanalyzer.nAverages-1; i < lFFTanalyzer.nAverages+rFFTanalyzer.nAverages; i++, k--){
+ glVertex3f((i*xStep)-hw,-(FFTbuffer[1][(lFFTanalyzer.nAverages+rFFTanalyzer.nAverages)-(i+1)][fB] * F_scale),0);
+ }
+ glEnd();
+ ofPopMatrix();
+ }
+ ofPopMatrix();
+ renderImage.end();
+
+
+
+
+ //ofEndShape(false);
+
+
+ //fbImage.grabScreen(0,0,ofGetWidth(),ofGetHeight());
+/*
+
+
+
+
+ ofSetHexColor(0xaaaaaa);
+ int width=2048/FFTanalyzer.nAverages;
+ for (int i = 0; i < FFTanalyzer.nAverages; i++){
+ ofRect(i*width,768,width,-FFTanalyzer.averages[i] * 20);
+ }
+ ofSetHexColor(0xffffff);
+ for (int i = 0; i < (int)(BUFFER_SIZE/2 - 1); i++){
+ ofRect(i,768-freq[i]*10.0f,1,1);
+ }
+
+ ofSetHexColor(0xff0000);
+ for (int i = 0; i < FFTanalyzer.nAverages; i++){
+ ofRect(i*width,768-FFTanalyzer.peaks[i] * 20,width,-1);
+ }
+
+ float avgStep = 1024/FFTanalyzer.nAverages;
+
+
+ ofNoFill();
+ ofSetLineWidth(1);
+
+ ofSetColor(223, 218, 218);
+ ofDrawBitmapString("FFT average", 4, 18);
+ ofBeginShape();
+ for (int i = 0; i < FFTanalyzer.nAverages; i++){
+ ofVertex(i*avgStep, 284 -FFTanalyzer.averages[i] * 20);
+ }
+ ofEndShape(false);
+
+
+ ofSetColor(97,181,243);
+ ofDrawBitmapString("FFT magnitude", 4, 38);
+ ofBeginShape();
+ for (int i = 0; i < BUFFER_SIZE; i++){
+ ofVertex(i*avgStep, 384 -magnitude[i] * 20);
+ }
+ ofEndShape(false);
+
+
+ ofSetColor(97,243,174);
+ ofDrawBitmapString("FFT phase", 4, 58);
+ ofBeginShape();
+ for (int i = 0; i < BUFFER_SIZE; i++){
+ ofVertex(i*avgStep, 484 -phase[i] * 20);
+ }
+ ofEndShape(false);
+
+ ofSetColor(243,174,94);
+ ofDrawBitmapString("FFT power", 4, 78);
+ ofBeginShape();
+ for (int i = 0; i < BUFFER_SIZE; i++){
+ ofVertex(i*avgStep, 584 -power[i] * 20);
+ }
+ ofEndShape(false);
+
+
+ //float * averages; // the actual averages
+ //float * peaks; // peaks of the averages, aka "maxAverages" in other implementations
+
+*/
+
+ ofEnableAlphaBlending();
+
+ maskShader.begin();
+
+ glActiveTexture(GL_TEXTURE0_ARB);
+ blendImage.getTextureReference().bind();
+ glActiveTexture(GL_TEXTURE1_ARB);
+ renderImage.getTextureReference().bind();
+
+ glBegin(GL_QUADS);
+
+ glMultiTexCoord2d(GL_TEXTURE0_ARB, 0, 0);
+ glMultiTexCoord2d(GL_TEXTURE1_ARB, 0, 0);
+ glVertex2f( 0, 0);
+
+ glMultiTexCoord2d(GL_TEXTURE0_ARB, blendImage.getWidth(), 0);
+ glMultiTexCoord2d(GL_TEXTURE1_ARB, renderImage.getWidth(), 0);
+ glVertex2f( ofGetWidth(), 0);
+
+ glMultiTexCoord2d(GL_TEXTURE0_ARB, blendImage.getWidth(), blendImage.getHeight());
+ glMultiTexCoord2d(GL_TEXTURE1_ARB, renderImage.getWidth(), renderImage.getHeight() );
+ glVertex2f( ofGetWidth(), ofGetHeight());
+
+ glMultiTexCoord2d(GL_TEXTURE0_ARB, 0, blendImage.getHeight());
+ glMultiTexCoord2d(GL_TEXTURE1_ARB, 0, renderImage.getHeight() );
+ glVertex2f( 0, ofGetHeight() );
+
+ glEnd();
+
+ glActiveTexture(GL_TEXTURE1_ARB);
+ renderImage.getTextureReference().unbind();
+
+ glActiveTexture(GL_TEXTURE0_ARB);
+ blendImage.getTextureReference().unbind();
+
+ maskShader.end();
+/*
+ glPushMatrix();
+ glTranslatef(ofGetWidth()*0.5,ofGetHeight()*0.5,0);
+ glRotatef((float)ofGetFrameNum(),0,1.0,0);
+ renderImage.draw(ofGetWidth()*-0.5,ofGetHeight()*-0.5);
+ glPopMatrix();
+ */
+
+ if (showFPS) {
+ ofDrawBitmapString(ofToString(ofGetFrameRate(), 2),20,20);
+ ofDrawBitmapString(ofToString(F_xRotation, 4)+" "+ofToString(F_yRotation, 4)+" "+ofToString(F_zRotation, 4),20,30);
+ ofDrawBitmapString(ofToString(F_yseg, 4),20,40);
+ }
+}
+
+
+//--------------------------------------------------------------
+void testApp::keyPressed (int key){
+ if(key == 'f'){
+ showFPS=!showFPS;
+ }
+}
+
+//--------------------------------------------------------------
+void testApp::mouseMoved(int x, int y ){
+
+}
+
+//--------------------------------------------------------------
+void testApp::mouseDragged(int x, int y, int button){
+
+}
+
+//--------------------------------------------------------------
+void testApp::mousePressed(int x, int y, int button){
+
+}
+
+//--------------------------------------------------------------
+void testApp::mouseReleased(){
+
+}
+
+
+//--------------------------------------------------------------
+void testApp::audioIn(float * input, int bufferSize, int nChannels){
+ /*if (sampnum<10) {
+ printf("size %i, channels %i\n",bufferSize,nChannels);
+ sampnum++;
+ }
+ */
+ // samples are "interleaved"
+ //if (nChannels==2) {
+ for (int i = 0; i < bufferSize; i++){
+ left[i] = input[i*2];
+ right[i] = input[i*2+1];
+ }
+ //}
+
+}
+
+void testApp::newMidiMessage(ofxMidiEventArgs& eventArgs){
+
+ //newMessage(eventArgs.port, eventArgs.channel, eventArgs.byteTwo, eventArgs.timestamp);
+
+//byteOne : message type
+
+ /*
+ int port;
+ int channel;
+ int status;
+ int byteOne;
+ int byteTwo;
+ double timestamp;
+ */
+
+ //printf("%d %d %d %d %d\n",eventArgs.port,eventArgs.channel,eventArgs.status,eventArgs.byteOne,eventArgs.byteTwo);
+
+ bool noteOn; //this old thing!
+
+ switch(eventArgs.status) {
+ case 144: //noteon-off
+
+ break;
+ case 176: //control change 1-8 x 4 banks
+ switch(eventArgs.byteOne) {
+ case 1:
+ F_scale = ((float)eventArgs.byteTwo)*.2;
+ break;
+ case 2:
+ F_lineWidth=(float)eventArgs.byteTwo*0.1;
+ break;
+ case 3:
+ F_drawAxis=((float) eventArgs.byteTwo-64)*(360.0/127);
+ break;
+ case 4:
+ break;
+ case 5:
+ break;
+
+ //65-80 buttons
+
+ //radio button set
+ case 65:
+ if (eventArgs.byteTwo==127) {
+ visMode=FFT_AVG;
+ midiOut.sendControlChange(1, 66, 0);
+ midiOut.sendControlChange(1, 67, 0);
+ }
+ else midiOut.sendControlChange(1, 65, 127);
+ break;
+ case 66:
+ if (eventArgs.byteTwo==127) {
+ visMode=FFT_RAW;
+ midiOut.sendControlChange(1, 65, 0);
+ midiOut.sendControlChange(1, 67, 0);
+ }
+ else midiOut.sendControlChange(1, 66, 127);
+ break;
+ case 67:
+ if (eventArgs.byteTwo==127) {
+ visMode=WAVEFORM;
+ midiOut.sendControlChange(1, 65, 0);
+ midiOut.sendControlChange(1, 66, 0);
+ }
+ else midiOut.sendControlChange(1, 67, 127);
+ break;
+
+ //radio button set
+ case 68:
+ if (eventArgs.byteTwo==127) {
+ inputMode=PICTURE;
+ midiOut.sendControlChange(1, 69, 0);
+
+ }
+ else midiOut.sendControlChange(1, 68, 127);
+ break;
+ case 69:
+ if (eventArgs.byteTwo==127) {
+ inputMode=GRABBER;
+ midiOut.sendControlChange(1, 68, 0);
+
+ }
+ else midiOut.sendControlChange(1, 69, 127);
+ break;
+ case 70:
+ B_glitch=(eventArgs.byteTwo==127);
+ break;
+ case 71:
+ B_fill=(eventArgs.byteTwo==127);
+ break;
+ case 72:
+ B_vSync=(eventArgs.byteTwo==127);
+ break;
+
+ //buttons to zero rotations
+ case 73:
+ F_xRotate=0;
+ midiOut.sendControlChange(1, 73, 0);
+ midiOut.sendControlChange(1, 81, 64);
+ break;
+ case 74:
+ F_yRotate=0;
+ midiOut.sendControlChange(1, 74, 0);
+ midiOut.sendControlChange(1, 82, 64);
+ break;
+ case 75:
+ F_zRotate=0;
+ midiOut.sendControlChange(1, 75, 0);
+ midiOut.sendControlChange(1, 83, 64);
+ break;
+ case 76:
+ F_drawStep=0;
+ midiOut.sendControlChange(1, 76, 0);
+ midiOut.sendControlChange(1, 84, 64);
+ break;
+ case 77:
+ if (eventArgs.byteTwo==127) {
+ I_movieSource=0;
+ midiOut.sendControlChange(1, 78, 0);
+ midiOut.sendControlChange(1, 79, 0);
+ midiOut.sendControlChange(1, 80, 0);
+
+ }
+ else midiOut.sendControlChange(1, 77, 127);
+ break;
+ case 78:
+ if (eventArgs.byteTwo==127) {
+ I_movieSource=1;
+ midiOut.sendControlChange(1, 77, 0);
+ midiOut.sendControlChange(1, 79, 0);
+ midiOut.sendControlChange(1, 80, 0);
+ }
+ else midiOut.sendControlChange(1, 78, 127);
+ break;
+ case 79:
+ if (eventArgs.byteTwo==127) {
+ I_movieSource=2;
+ midiOut.sendControlChange(1, 77, 0);
+ midiOut.sendControlChange(1, 78, 0);
+ midiOut.sendControlChange(1, 80, 0);
+ }
+ else midiOut.sendControlChange(1, 79, 127);
+ break;
+ case 80:
+ if (eventArgs.byteTwo==127) {
+ I_movieSource=3;
+ midiOut.sendControlChange(1, 77, 0);
+ midiOut.sendControlChange(1, 78, 0);
+ midiOut.sendControlChange(1, 79, 0);
+ }
+ else midiOut.sendControlChange(1, 80, 127);
+ break;
+
+ // probably make controls logarithmic
+ case 81:
+ F_xRotate=(((float) eventArgs.byteTwo)-64)*.1;
+ break;
+ case 82:
+ F_yRotate=(((float) eventArgs.byteTwo)-64)*.1;
+ break;
+ case 83:
+ F_zRotate=(((float) eventArgs.byteTwo)-64)*.1;
+ break;
+ case 84:
+ F_drawStep=(((float) eventArgs.byteTwo)-64)*.1;
+ break;
+ case 85:
+ F_movieSpeed=((float)eventArgs.byteTwo)/64.0;
+ break;
+ case 86:
+ F_drawDecay=((float)eventArgs.byteTwo)/127.0;
+ break;
+ case 87:
+ I_fade2=((float)eventArgs.byteTwo)*2;
+ break;
+ case 88:
+ I_fade1=((float)eventArgs.byteTwo)*2;
+ //printf("fog- %f\n", F_drawDecay);
+ break;
+
+ case 91: //top
+ F_xRotation=90;
+ F_yRotation=90;
+ F_zRotation=0;
+ midiOut.sendControlChange(1, 91, 0);
+ break;
+ case 92: //front
+ F_xRotation=0;
+ F_yRotation=180;
+ F_zRotation=0;
+ midiOut.sendControlChange(1, 92, 0);
+ break;
+
+ //89-92 bottom 4
+
+ }
+ //printf("rotation- %f %f %f\n",F_xRotate,F_yRotate,F_zRotate);
+ //printf(inputMode==PICTURE?"PICTURE mode\n":"GRABBER mode\n");
+ }
+}
+void testApp::setMidiState(){
+ int knobs[32];
+ int buttons[16];
+ int faders[8];
+ for (int i=0;i<32;i++) knobs[i]=0;
+ for (int i=0;i<16;i++) buttons[i]=0;
+ for (int i=0;i<8;i++) faders[i]=0;
+
+ knobs[0] = (F_scale * 5);
+ knobs[1]=(F_lineWidth*10);
+ knobs[2]=(F_drawAxis/(360.0/127))+64;
+
+ //radio button set
+ buttons[0]=(visMode==FFT_AVG?127:0);
+ buttons[1]=(visMode==FFT_RAW?127:0);
+ buttons[2]=(visMode==WAVEFORM?127:0);
+
+ //radio button set
+ buttons[3]=(inputMode==PICTURE?127:0);
+ buttons[4]=(inputMode==GRABBER?127:0);
+ buttons[5]=B_glitch?127:0;
+ buttons[6]=B_fill?127:0;
+ buttons[7]=B_vSync?127:0;
+
+ buttons[12]=I_movieSource==0?127:0;
+ buttons[13]=I_movieSource==1?127:0;
+ buttons[14]=I_movieSource==2?127:0;
+ buttons[15]=I_movieSource==3?127:0;
+
+ faders[0]=(F_xRotate*10)+64;
+ faders[1]=(F_yRotate*10)+64;
+ faders[2]=(F_zRotate*10)+64;
+ faders[3]=(F_drawStep*10)+64;
+ faders[4]=F_movieSpeed*127;
+
+ faders[5]=(F_drawDecay*127);
+
+ faders[6]=I_fade1/2;
+ faders[7]=I_fade1/2;
+
+ for (int i=0;i<32;i++) midiOut.sendControlChange(1, i+1, knobs[i]);
+ for (int i=0;i<16;i++) midiOut.sendControlChange(1, i+65, buttons[i]);
+ for (int i=0;i<8;i++) midiOut.sendControlChange(1, i+81, faders[i]);
+}
+
+/*
+void sendNoteOn(int channel, int id, int value);
+void sendNoteOff(int channel, int id, int value);
+void sendControlChange(int channel, int id, int value);
+*/
+
+
diff --git a/src/testApp.h b/src/testApp.h
new file mode 100644
index 0000000..43540c6
--- /dev/null
+++ b/src/testApp.h
@@ -0,0 +1,158 @@
+#ifndef _TEST_APP
+#define _TEST_APP
+
+#include "ofMain.h"
+#include "fft.h"
+#include "FFTOctaveAnalyzer.h"
+
+#define BUFFER_SIZE 1024
+#define BUFFER_FRAMES 512
+
+/*
+ok seriously lets make this good
+
+recording - storage for a bunch of fft
+
+average them on the fly? seems likely
+
+*/
+
+#define OF_ADDON_USING_OFXMIDIIN
+
+#include "ofxMidi.h"
+
+#define FFT_AVG 1
+#define FFT_RAW 2
+#define WAVEFORM 3
+
+#define PICTURE 1
+#define GRABBER 2
+
+
+class testApp : public ofBaseApp, public ofxMidiListener{
+
+ public:
+
+ void setup();
+ void update();
+ void draw();
+
+ void keyPressed (int key);
+ void mouseMoved(int x, int y );
+ void mouseDragged(int x, int y, int button);
+ void mousePressed(int x, int y, int button);
+ void mouseReleased();
+
+ void audioIn(float * input, int bufferSize, int nChannels);
+
+ FFTOctaveAnalyzer lFFTanalyzer;
+ FFTOctaveAnalyzer rFFTanalyzer;
+
+ ofxMidiIn midiIn;
+ ofxMidiOut midiOut;
+ void newMidiMessage(ofxMidiEventArgs& eventArgs);
+
+ void setMidiState();
+
+
+ private:
+
+ float * left;
+ float * right;
+ int bufferCounter;
+ fft myfft;
+
+ float FFTbuffer[2][BUFFER_SIZE][BUFFER_FRAMES];
+
+ int thisFFTbuffer;
+
+ float lmagnitude[BUFFER_SIZE];
+ float lphase[BUFFER_SIZE];
+ float lpower[BUFFER_SIZE];
+ float lfreq[BUFFER_SIZE/2];
+
+ float rmagnitude[BUFFER_SIZE];
+ float rphase[BUFFER_SIZE];
+ float rpower[BUFFER_SIZE];
+ float rfreq[BUFFER_SIZE/2];
+
+ ofSoundStream soundStream;
+
+ ofImage fbImage;
+
+ //ofImage blendImage;
+
+ ofVideoPlayer blendImage;
+
+ ofFbo renderImage;
+ ofShader maskShader;
+
+ bool showFPS;
+
+ ofImage testImage;
+
+ //controllable variables
+ float F_scale;
+ float F_drawFrames;
+ float F_drawStep;
+
+ float F_drawDecay;
+ float F_lineWidth;
+ float F_drawAxis;
+
+ float F_xRotation;
+ float F_yRotation;
+ float F_zRotation;
+
+ float F_xRotate;
+ float F_yRotate;
+ float F_zRotate;
+
+ float lastFrameTime;
+
+ bool draworder;
+ bool B_vSync;
+ bool B_fill;
+
+ int visMode;
+ int inputMode;
+
+ int I_fade1;
+ int I_fade2;
+
+ int I_movieSource;
+ int I_moviePlaying;
+
+ bool B_glitch;
+
+
+
+ ofVideoGrabber grabber;
+
+ unsigned char *gammamap;
+ unsigned char *line1;
+ unsigned char *line2;
+ unsigned char *outBuffer;
+
+ int camWidth,camHeight;
+
+ float whitePt;
+ float blackPt;
+ float gamma;
+
+ bool field2; //flag that there is a 2nd field waiting to be processed
+ bool use2fields;
+ int frameTime,grabTime; //keep track of field timing
+
+
+ bool deInterlace;
+ ofTexture outTexture;
+
+ float F_movieSpeed;
+
+
+ ofImage blanker;
+};
+
+#endif
+