/**
* This sketch demonstrates how to use an FFT to analyze
* the audio being generated by an AudioPlayer.
*


* 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.
*


* 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,800,0);

boolean forward,reset,psyc;
PImage back;
float rotateSpeed;
float i;

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,250);

cam.setMinimumDistance(1800);
cam.setMaximumDistance(1800);
// 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("John Coltrane (A Love Supreme, 1964) - Part 1 - Acknowledgement.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);
back = loadImage("image.jpg");
back.resize(width,height);
frameRate(30);
}

void draw()
{
update();
pushMatrix();
noStroke();
directionalLight(0,255,255,1,1,0);
directionalLight(255,0,0,-1,1,0);
if(psyc){
pointLight(0,abs(sin(radians(i+=0.2)))*255,0,3000,0,3000);
}
cam.rotateY(rotateSpeed);
if(reset)
{
cam.reset(1000);
}
translate(pos.x,pos.y,pos.z);
background(0);
fill(255);
pos.z+=2*mount.spacing.z;
mount.display();
popMatrix();
// println(frameRate);
}

void keyPressed()
{
if(key=='w' || key=='W')
{
rotateSpeed+=0.01;
}
else if(key=='s' || key=='S')
{
rotateSpeed-=0.01;
}
else if(key=='r' || key=='R')
{
reset=true;
rotateSpeed=0;
}
else if(key=='a' || key=='A')
{
pos.x+=50;
}
else if(key=='d' || key=='D')
{
rotateSpeed=0;
}
else if(key=='q' || key=='Q')
{
pos.y+=300;
}
else if(key=='z' || key=='Z')
{
pos.y-=300;
}
else if(key=='1' || key=='2'){
song.close();
} else if(key=='x'){
i=0;
psyc=!psyc;
}
}

void keyReleased()
{
if(key=='r')
{
reset=false;
} else if(key=='1'){
song = minim.loadFile("John Coltrane (A Love Supreme, 1964) - Part 1 - Acknowledgement.mp3", 512);
song.loop();
} else if(key=='2'){
song = minim.loadFile("Daft Punk - Doin' It Right (feat. Panda Bear).mp3", 512);
song.loop();
}
}

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(17,0,150);
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 {
for(int j=0; j {
points[i][j].pos.y=points[i+speed][j].pos.y;
}
}
else
{
for(int j=0; j {
points[i][j].pos.y=-fullFFT[j]*100;
}
}
}
}
}
}

class Point
{
PVector pos;

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