/**
* This sketch demonstrates how to use the BeatDetect object in FREQ_ENERGY mode.<br />
* You can use <code>isKick</code>, <code>isSnare</code>, </code>isHat</code>, <code>isRange</code>,
* and <code>isOnset(int)</code> to track whatever kind of beats you are looking to track, they will report
* true or false based on the state of the analysis. To "tick" the analysis you must call <code>detect</code>
* with successive buffers of audio. You can do this inside of <code>draw</code>, but you are likely to miss some
* audio buffers if you do this. The sketch implements an <code>AudioListener</code> called <code>BeatListener</code>
* so that it can call <code>detect</code> on every buffer of audio processed by the system without repeating a buffer
* or missing one.
* <p>
* This sketch plays an entire song so it may be a little slow to load.
* <p>
* For more information about Minim and additional features,
* visit http://code.compartmental.net/minim/
*/

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

Minim minim;
AudioPlayer song;
BeatDetect beat;
BeatListener bl;

int i = 10;
float z = -1500;
Cube cube;
Diamond p;
ArrayList shapeList = new ArrayList();
PeasyCam cam;

class BeatListener implements AudioListener
{
private BeatDetect beat;
private AudioPlayer source;

BeatListener(BeatDetect beat, AudioPlayer source)
{
this.source = source;
this.source.addListener(this);
this.beat = beat;
}

void samples(float[] samps)
{
beat.detect(source.mix);
}

void samples(float[] sampsL, float[] sampsR)
{
beat.detect(source.mix);
}
}

void setup()
{
cam = new PeasyCam(this, 0);
cam.setMinimumDistance(50);
cam.setMaximumDistance(500);
size(1000, 700, P3D);
noStroke();

minim = new Minim(this);

song = minim.loadFile("loop.wav", 1024);
song.loop();
// a beat detection object that is FREQ_ENERGY mode that
// expects buffers the length of song's buffer size
// and samples captured at songs's sample rate
beat = new BeatDetect(song.bufferSize(), song.sampleRate());
// set the sensitivity to 300 milliseconds
// After a beat has been detected, the algorithm will wait for 300 milliseconds
// before allowing another beat to be reported. You can use this to dampen the
// algorithm if it is giving too many false-positives. The default value is 10,
// which is essentially no damping. If you try to set the sensitivity to a negative value,
// an error will be reported and it will be set to 10 instead.
beat.setSensitivity(100);
// make a new beat listener, so that we won't miss any buffers for the analysis
bl = new BeatListener(beat, song);
}

void draw()
{
background(0);
directionalLight(225, 255, 255, 1, 0.5, -1);

spotLight(255, 255, 109, // Color
0, 40, 200, // Position
0, -0.5, -0.5, // Direction
PI / 2, 2); // Angle, concentration

// directionalLight(0, 0, 255, 0, -0.5, 1);
for (int i=0; i {
Shape _shape = shapeList.get(i);
_shape.display();
_shape.move();

if(_shape.checkBorder())
{
shapeList.remove(i);
}

}
if ( beat.isRange(3, 4, 2) ) {
cube = new Cube(new PVector(1000+width, random(-1000, 1000)-height/2, z), new PVector(i*random(2, 10), i*random(2, 10), i*random(2, 10)), color(255, 255, 255), 8);
shapeList.add(cube);
}
if ( beat.isRange(6, 12, 5) ) {
p = new Diamond(new PVector(1000+width, random(-1000, 1000)-height/2, z), new PVector(i*random(2, 10), i*random(2, 10), i*random(2, 10)), color(255, 255, 255), 8);
shapeList.add(p);
}
if ( beat.isRange(13, 26, 10) ) {
p = new Diamond(new PVector(1000+width, random(-1000, 1000)-height/2, z), new PVector(i*random(2, 10), i*random(2, 10), i*random(2, 10)), color(255, 255, 255), 8);
shapeList.add(p);
}
}

class Ball
{
PVector position;
PVector dir;
PVector velocity;
PVector acceleration;
PVector rand;
float max = 0.3;
color c;

Ball(color _c)
{
position = new PVector(width/2, height/2);
velocity = new PVector();
acceleration = new PVector();
c = _c;
}


void display()
{
fill(c);
ellipse(position.x, position.y, 10, 10);
}


void update()
{

dir.normalize();
dir.mult(1);
acceleration = dir;
velocity.add(acceleration);
position.add(velocity);
}

void pos()
{
rand = new PVector(random(0, width), random(0, height));
dir = PVector.sub(rand, position);
}


}

class Cube extends Shape
{

Cube(PVector _position, PVector _size, color _colour, float _speed)
{
size = _size;
colour = _colour;
position = _position;
speed = _speed;
customShape=createShape();
create();
}

void create()
{
customShape.beginShape(QUAD);
//front
customShape.vertex(-size.x, -size.y, size.z);
customShape.vertex(-size.x, size.y, size.z);
customShape.vertex(size.x, size.y, size.z);
customShape.vertex(size.x, -size.y, size.z);
//right
customShape.vertex(size.x, -size.y, size.z);
customShape.vertex(size.x, -size.y, -size.z);
customShape.vertex(size.x, size.y, -size.z);
customShape.vertex(size.x, size.y, size.z);
//left
customShape.vertex(-size.x, -size.y, -size.z);
customShape.vertex(-size.x, -size.y, size.z);
customShape.vertex(-size.x, size.y, size.z);
customShape.vertex(-size.x, size.y, -size.z);
//top
customShape.vertex(-size.x, -size.y, -size.z);
customShape.vertex(size.x, -size.y, -size.z);
customShape.vertex(size.x, -size.y, size.z);
customShape.vertex(-size.x, -size.y, size.z);
//bottom
customShape.vertex(-size.x, size.y, size.z);
customShape.vertex(size.x, size.y, size.z);
customShape.vertex(size.x, size.y, -size.z);
customShape.vertex(-size.x, size.y, -size.z);
//back
customShape.vertex(-size.x, -size.y, -size.z);
customShape.vertex(-size.x, size.y, -size.z);
customShape.vertex(size.x, size.y, -size.z);
customShape.vertex(size.x, -size.y, -size.z);
customShape.endShape(CLOSE);
}
}

class Diamond extends Shape
{

Diamond(PVector _position, PVector _size, color _colour, float _speed)
{
size = _size;
colour = _colour;
position = _position;
speed = _speed;
customShape=createShape();
create();
}

void create()
{
customShape.beginShape(TRIANGLE);
//Right Front Top
customShape.vertex(0, -size.y, 0);
customShape.vertex(0, 0, size.z);
customShape.vertex(size.x, 0, 0);
//Left Front Top
customShape.vertex(0, -size.y, 0);
customShape.vertex(0, 0, size.z);
customShape.vertex(-size.x, 0, 0);
//Right Back Top
customShape.vertex(0, -size.y, 0);
customShape.vertex(0, 0, -size.z);
customShape.vertex(size.x, 0, 0);
//Left Back Top
customShape.vertex(0, -size.y, 0);
customShape.vertex(0, 0, -size.z);
customShape.vertex(-size.x, 0, 0);

//Right Front Bottom
customShape.vertex(0, size.y, 0);
customShape.vertex(0, 0, size.z);
customShape.vertex(size.x, 0, 0);
//Left Front Bottom
customShape.vertex(0, size.y, 0);
customShape.vertex(0, 0, size.z);
customShape.vertex(-size.x, 0, 0);
//Right Back Bottom
customShape.vertex(0, size.y, 0);
customShape.vertex(0, 0, -size.z);
customShape.vertex(size.x, 0, 0);
//Left Back Bottom
customShape.vertex(0, size.y, 0);
customShape.vertex(0, 0, -size.z);
customShape.vertex(-size.x, 0, 0);
customShape.endShape();
}
}

class Shape
{
PVector position, size;
color colour;
PShape customShape;
float speed, velocity, i;

void display()
{
pushMatrix();
translate(position.x, position.y, position.z);
// rotateX(HALF_PI/2);
rotateY(i+=0.02);
fill(colour);
shape(customShape, 0, 0);
popMatrix();
}

void move()
{
position.x-=speed;
}

boolean checkBorder()
{
if (position.x<-8*width)
{
return true;
}
return false;
}
}