/**
* 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;
int fftSize = (int) pow(2,9); // 2^9=512
PeasyCam cam;
Song[] songList = new Song[9];
Song currentSong;
int y = 800;
PVector pos = new PVector(0,y,0);
boolean forward,reset,psyc;
float bkupPosX, bkupPosY;
float rotateSpeed, rotationBkup;
float cycle;
boolean up,down,leftKey,rightKey,played;
void setup()
{
songList[0] = new Song("John Coltrane (A Love Supreme, 1964) - Part 1 - Acknowledgement.mp3");
songList[1] = new Song("Daft Punk - Doin' It Right (feat. Panda Bear).mp3");
songList[2] = new Song("Dragonforce-Through-the-Fire-and-FlamesLyrics.mp3.mp3");
songList[3] = new Song("riff.mp3.mp3");
currentSong = songList[3];
size(displayWidth,displayHeight,P3D);
rectMode(CENTER);
pos.x=-currentSong.mount.points[0].length/2*currentSong.mount.spacing.x;
pos.z=currentSong.mount.points.length/4*currentSong.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(currentSong.fileLoc, 2*fftSize);
// loop the file indefinitely
// song.loop();
song.play();
// 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[fftSize];
fftLeft.window(FFT.GAUSS);
fftRight.window(FFT.GAUSS);
frameRate(30);
perspective(PI/3.0, (float)width/(float)height, 10,40000);
}
void draw()
{
update();
directionalLight(0,255,255,1,1,0);
directionalLight(255,0,0,-1,1,0);
noStroke();
if(psyc){
pointLight(0,127*(1-cos(cycle++/100.0)),0,0,3000,0);
}
cam.rotateY(rotateSpeed);
if(reset)
{
cam.reset(1000);
}
pushMatrix();
translate(pos.x,pos.y,pos.z);
background(0);
fill(255);
if(!currentSong.mount.calculated)
{
pos.z+=2*currentSong.mount.spacing.z;
currentSong.mount.display();
}
else
{
// lights();
if(song.isPlaying()) pos.z+=2*currentSong.mount.spacing.z;
currentSong.mount.display2();
}
currentSong.currentPoint = song.position();
popMatrix();
// pos.y = -currentSong.mount.points[mount.points.length-1][128].pos.y+400;


//DEBUG ONLY

pushMatrix();
noLights();
translate(0,0,100);
fill(255);
textSize(70);
textAlign(CENTER);
text(frameRate,0,-400);
text(currentSong.fileLoc,0,-200);
text(currentSong.mount.maxPointer+" "+(floor(float(currentSong.mount.maxPointer)/30*1000))+" "+song.position(),0,-100);
fill(255,0,0);
rect(0,0,500,50);
fill(0,255,0);
// rect(0,0,map(currentSong.mount.landscape.size(),0,1000,0,500),50);
rect(0,0,map(song.position(),0,song.length()-10000,0,500),50);
popMatrix();

//END DEBUG


if(up)
{
if(currentSong.mount.maxPointer+1 {
currentSong.mount.minPointer++;
currentSong.mount.maxPointer++;
}
println(currentSong.mount.minPointer,currentSong.mount.maxPointer);
}
else if(down)
{
if(currentSong.mount.minPointer-1>0)
{
currentSong.mount.minPointer--;
currentSong.mount.maxPointer--;
}
println(currentSong.mount.minPointer,currentSong.mount.maxPointer);
}
if(leftKey)
{
pos.x+=300;
}
else if(rightKey)
{
pos.x-=300;
}
}
void keyPressed()
{
if (key == 'e' || key == 'E') // CCW rotation
{
rotateSpeed += 0.01;
} else if (key == 'q' || key == 'Q') // CW rotation
{
rotateSpeed -= 0.01;
} else if (key == 'r' || key == 'R') // reset camera rotation
{
reset = true;
rotationBkup = (rotateSpeed != 0) ? rotateSpeed : rotationBkup;
rotateSpeed = 0;
} else if (key == 't' || key == 'T') // stop rotation
{
rotationBkup = (rotateSpeed != 0) ? rotateSpeed : rotationBkup;
rotateSpeed = (rotateSpeed != 0) ? 0 : rotationBkup;
} else if (key == 'a' || key == 'A') // move left
{
pos.x += 50;
} else if (key == 'd' || key == 'D') // move right
{
pos.x -= 50;
} else if (key == 'c' || key == 'C') // reset left/right
{
bkupPosX = (pos.x != -currentSong.mount.points[0].length/2*currentSong.mount.spacing.x) ? pos.x : bkupPosX;
pos.x = (pos.x != -currentSong.mount.points[0].length/2*currentSong.mount.spacing.x) ? -currentSong.mount.points[0].length/2*currentSong.mount.spacing.x : bkupPosX;
} else if (key == 'w' || key == 'W') // move up
{
pos.y += 300;
} else if (key == 's' || key == 'S') // move down
{
pos.y -= 300;
} else if (key == 'z' || key == 'Z') // reset up/down
{
bkupPosY = (pos.y != y) ? pos.y : bkupPosY;
pos.y = (pos.y != y) ? y : bkupPosY;
} else if (key == 'x' || key == 'X') // cycle colours
{
cycle = 0;
psyc = !psyc;
} else if (key == '1' || key == '2' || key=='3' || key=='4') // change song
{
song.close();
}
if(keyCode==UP)
{
up = true;
}
else if(keyCode==DOWN)
{
down = true;
}
else if(keyCode==LEFT)
{
leftKey = true;
}
else if(keyCode==RIGHT)
{
rightKey = true;
}
if(currentSong.mount.calculated)
{
if(key==' ')
{
song.cue(int(float(currentSong.mount.maxPointer)/30*1000));
println((currentSong.mount.maxPointer)/30*1000,song.position());
if(song.isPlaying()) song.pause();
else song.play();
}
}
}
void keyReleased()
{
if(key=='r')
{
reset=false;
}
if(int(key)>=48 && int(key)<58)
{
if(key=='1')
{
currentSong = songList[0];
}
else if(key=='2')
{
currentSong = songList[1];
}
else if(key=='3')
{
currentSong = songList[2];
}
else if(key=='4')
{
currentSong = songList[3];
}
song.pause();
song = minim.loadFile(currentSong.fileLoc, 2*fftSize);
// song.loop(); //Sets song.position() to 0
song.cue(currentSong.currentPoint);
if(!currentSong.mount.calculated)
song.play(); //Continues to play the song from the new position
}
if(keyCode==UP)
{
up=false;
}
else if(keyCode==DOWN)
{
down=false;
}
else if(keyCode==LEFT)
{
leftKey = false;
}
else if(keyCode==RIGHT)
{
rightKey = 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;
ArrayList landscape;
PVector spacing;
int minPointer, maxPointer;
float gradient;
int speed;
boolean calculated;

Mountain(int sizeX, int sizeY)
{
points= new Point[sizeX][sizeY];
landscape = new ArrayList();
speed = 1;
calculated = false;
spacing = new PVector(17,1,150);
minPointer = 0;
maxPointer = points.length;
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*spacing.y, -points[i][j].pos.z);
if(i!=points.length-1)
{
vertex (points[i+1][j].pos.x, points[i+1][j].pos.y*spacing.y, -points[i+1][j].pos.z);
}
checkSpacing();
}
endShape();
}
createLandscape();
}

void display2()
{
Point[][] _tempLandscape = new Point[points.length][points[0].length];
for(int i=minPointer; i {
Point[] _tempMount = landscape.get(i);
for(int j=0; j<_tempMount.length; j++)
{
_tempLandscape[i-minPointer][j]=_tempMount[j];
}
}

for (int i=0; i<_tempLandscape.length; i++) //150
{
beginShape(TRIANGLE_STRIP);
for (int j=0; j<_tempLandscape[i].length; j++) //128
{
vertex (_tempLandscape[i][j].pos.x, _tempLandscape[i][j].pos.y*spacing.y, -(i*spacing.z));
if(i!=_tempLandscape.length-1)
{
vertex (_tempLandscape[i+1][j].pos.x, _tempLandscape[i+1][j].pos.y*spacing.y, -((i+1)*spacing.z));
}
checkSpacing2();
}
endShape();
pos.z=points.length/2*spacing.z;
}
}

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
{
Point[] _ele = new Point[points[i].length];
for(int j=0; j {
_ele[j] = new Point(j*spacing.x,0,i*spacing.z);
points[i][j].pos.y=-fullFFT[j]*100;
_ele[j].pos.y = -fullFFT[j]*100;
}
landscape.add(_ele);
}
}
}
}

void checkSpacing2()
{
if(pos.z>points.length/2*spacing.z+spacing.z)
{
pos.z=points.length/2*spacing.z;
minPointer++;
maxPointer++;
println(minPointer);
}
}

void createLandscape()
{
if(!calculated)
{
if(song.position()>=song.length()-10000 || landscape.size()>1000) //
{
calculated = true;
println("\n\n\n\nTRUE!!");
song.pause();
}
}
println("Current Time:",song.position(),"Size:",landscape.size(),"Max time:",song.length()-10000);
}
}

class Point
{
PVector pos;

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

class Song
{
String fileLoc;
Mountain mount;
int currentPoint;

Song(String _fileLoc)
{
fileLoc = _fileLoc;
mount = new Mountain(150,fftSize);
currentPoint=0;
}
}