/**
* This sketch demonstrates how to use an FFT to analyze
* the audio being generated by an AudioPlayer.
* <p>
* FFT stands for Fast Fourier Transform, which is a
* method of analyzing audio that allows you to visualize
* the frequency content of a signal. You've seen
* visualizations like this before in music players
* and car stereos.
* <p>
* For more information about Minim and additional features,
* visit http://code.compartmental.net/minim/
*/

import peasy.test.*;
import peasy.org.apache.commons.math.*;
import peasy.*;
import peasy.org.apache.commons.math.geometry.*;

import ddf.minim.analysis.*;
import ddf.minim.*;

Minim minim;
AudioPlayer song;
FFT fftLeft, fftRight;
float[] left, right,fullFFT;

float[] fftTest = new float[256];
Mountain mount;
PeasyCam cam;

PVector pos = new PVector(0,100,0);

boolean forward;

void setup()
{
mount = new Mountain(50,256);
size(1280,720,P3D);
rectMode(CENTER);
pos.x=-mount.points[0].length/2*mount.spacing.x;
pos.z=mount.points.length/4*mount.spacing.z;
cam = new PeasyCam(this, 0);

cam.setMinimumDistance(1);
cam.setMaximumDistance(500);
noStroke();
// cam.setYawRotationMode(); // like spinning a globe

minim = new Minim(this);

// specify that we want the audio buffers of the AudioPlayer
// to be 1024 samples long because our FFT needs to have
// a power-of-two buffer size and this is a good size.
song = minim.loadFile("Daft Punk - Doin' It Right (feat. Panda Bear).mp3", 512);

// loop the file indefinitely
song.loop();

// create an FFT object that has a time-domain buffer
// the same size as jingle's sample buffer
// note that this needs to be a power of two
// and that it means the size of the spectrum will be half as large.
fftLeft = new FFT( song.bufferSize(), song.sampleRate() );
fftRight = new FFT( song.bufferSize(), song.sampleRate() );

left = new float[song.bufferSize() / 2 + 1];
right = new float[song.bufferSize() / 2 + 1];
fullFFT = new float[256];

fftLeft.window(FFT.GAUSS);
fftRight.window(FFT.GAUSS);
}

void draw()
{
update();
pushMatrix();
directionalLight(0,255,255,1,1,0);
directionalLight(255,0,0,-1,1,0);
lightFalloff(0.0, 0.0, 1.0);
translate(pos.x,pos.y,pos.z);
// cam.lookAt(0,0,mount.points.length/4*mount.spacing.z);
background(0);
fill(255);
pos.z+=2*mount.spacing.z;
mount.display();
popMatrix();
pushMatrix();
translate(0,100,0);
fill(255,0,0);
box(20);
popMatrix();
println(frameRate);
}

void keyPressed()
{
if(key=='w' || key=='W')
{
forward=true;
}
else if(key=='s' || key=='S')
{
pos.z-=mount.spacing.z;
}
else if(key=='a' || key=='A')
{
pos.x+=mount.spacing.x;
}
else if(key=='d' || key=='D')
{
pos.x-=mount.spacing.x;
}
else if(key=='q' || key=='Q')
{
pos.y+=100;
}
else if(key=='z' || key=='Z')
{
pos.y-=100;
}
}

void keyReleased()
{
if(key=='w')
forward=false;
}

void update()
{
fftLeft.forward(song.left);
fftRight.forward(song.right);
for (int i = 0; i < fftLeft.specSize (); i++)
{
left[i] = fftLeft.getBand(i)*(log(i/10+1.5)/log(2));
right[i] = fftRight.getBand(i)*(log(i/10+1.5)/log(2));
}
for(int i=0; i {
if(i else fullFFT[i]=right[fullFFT.length-i-1];
}
}

class Mountain
{
Point[][] points;
PVector spacing;
int cols, rows, pointer;
float gradient;
int speed;

Mountain(int sizeX, int sizeY)
{
points= new Point[sizeX][sizeY];
speed = 1;
spacing = new PVector(20,0,300);
for(int i=0; i {
for(int j=0; j {
points[i][j] = new Point(j*spacing.x,0,i*spacing.z);
}
}
}

void display()
{
for (int i=0; i {
beginShape(TRIANGLE_STRIP);
for (int j=0; j {
vertex (points[i][j].pos.x, points[i][j].pos.y, -points[i][j].pos.z);
if(i!=points.length-1)
{
vertex (points[i+1][j].pos.x, points[i+1][j].pos.y, -points[i+1][j].pos.z);
}
checkSpacing();
}
endShape();
}
}

void checkSpacing()
{
if(pos.z>points.length/2*spacing.z+spacing.z)
{
pos.z=points.length/2*spacing.z;
for(int i=0; i {
if(i {
// points[i] = points[i+1];
for(int j=0; j {
points[i][j].pos.y=points[i+speed][j].pos.y;
}
}
else
{
for(int j=0; j {
// for(int k=0; k// {
// fftTest[k]=random(-50,500);
// }
points[i][j].pos.y=-fullFFT[j]*200;
}
}
}
}
}

void findGradient(int i,int j)
{
if(points[i][j].pos.x>-pos.x-spacing.x/2
&& points[i][j].pos.x<-pos.x+spacing.x/2
&& points[i][j].pos.z>pos.z-spacing.z/2
&& points[i][j].pos.z {
gradient = (points[i][j].pos.y-points[i][j+1].pos.y)/(points[i][j].pos.z-points[i][j+1].pos.z);
println(points[i][j].pos.y-points[i][j+1].pos.y,points[i][j].pos.z-points[i][j+1].pos.z);
// println(gradient,i,j, pos.x, pos.z, points[i][j].pos.x,points[i][j].pos.z);
}
}
}

class Point
{
PVector pos;

Point(float x, float y, float z)
{
pos = new PVector(x,y,z);
}
}