xxxxxxxxxx
172
/**
* @name Frequency Spectrum
* @description <p>Visualize the frequency spectrum of live audio input.</p>
* <p><em><span class="small"> To run this example locally, you will need the
* <a href="http://p5js.org/reference/#/libraries/p5.sound">p5.sound library</a>
* and a running <a href="https://github.com/processing/p5.js/wiki/Local-server">local server</a>.</span></em></p>
*/
let mic, fft;
// TODO:
// - have a ball that you try to get in a basket using sound. like the fft bounces it up
// and you try to get it in a hole
// - Improve collision detection so point actually hits ball (right now, we are treating bottom of ball as a rect)
// - ball size could maybe change based on amplitude of sound? getLevel()?
let ball;
let lastSpectrum = null;
let MAX_Y_VELOCITY = 80;
let DEFAULT_BALL_DIAMETER = 50;
function setup() {
createCanvas(1024, 600);
noFill();
mic = new p5.AudioIn();
mic.start();
fft = new p5.FFT();
fft.setInput(mic);
ball = new Ball(width/10, height/2, DEFAULT_BALL_DIAMETER);
let spectrum = fft.analyze();
print("The spectrum length is: ", spectrum.length);
print(mic);
print(sampleRate()); // https://p5js.org/reference/#/p5/sampleRate
}
function draw() {
background(200);
// dynamically scale based on ball height
if (ball.y < 0){
let totalHeight = height + abs(ball.y);
let scaleFactor = height / totalHeight;
translate(0, abs(ball.y));
scale(scaleFactor);
}
// draw slight grid (to enable zoom feeling)
let gridCellSize = 20;
stroke(100, 100, 100, 50);
let startY = -height * 2;
let endX = width * 2;
for (let col = 0; col < endX; col += gridCellSize){
line(col, startY, col, height);
}
for (let row = startY; row < height; row += gridCellSize){
line(0, row, endX, row);
}
let spectrum = fft.analyze();
// draw the frequency spectrum
noFill();
stroke(10);
beginShape();
for (i = 0; i < spectrum.length; i++) {
vertex(i, map(spectrum[i], 0, 255, height, 0));
}
endShape();
// check if the frequency spectrum hit our ball!
if (lastSpectrum != null){
// calculate the difference between this spectrum and the last one
// we'll use this to calculate and estimate of force against the ball
let diffSpectrum = []
for (let i = 0; i < spectrum.length; i++) {
let diff = spectrum[i] - lastSpectrum[i];
diffSpectrum.push(diff);
}
// find the maximum spectrum value undernear the ball (we currently treat the entire bottom of
// the ball as a flat line)
let maxSpectrumValUnderBall = -1;
let maxSpectrumIndex = -1;
for (let i = floor(ball.x - ball.diameter/2); i < floor(ball.x + ball.diameter/2); i++){
if(maxSpectrumValUnderBall < spectrum[i]){
maxSpectrumValUnderBall = spectrum[i];
maxSpectrumIndex = i;
}
}
// calculate the difference at the maximum spectrum index under the ball
let ySpectrumValAtBall = map(spectrum[maxSpectrumIndex], 0, 255, height, 0);
let yDiffAtBall = diffSpectrum[maxSpectrumIndex];
let xDiffAtBall = ball.x - maxSpectrumIndex;
ball.update(ySpectrumValAtBall, yDiffAtBall, xDiffAtBall);
}
ball.draw();
lastSpectrum = spectrum;
}
class Ball {
constructor(x, y, diameter) {
this.x = x;
this.y = y;
this.diameter = diameter;
this.gravity = 1;
this.yVelocity = 0;
this.xVelocity = 0;
}
update(ySpectrumVal, ySpectrumVel, xSpectrumVel){
// check to see if the new yVal is less than the current ball position
// if so, we should accept this new value (as the spectrum spike hit our ball!)
if(ySpectrumVal < this.y){
this.y = ySpectrumVal;
this.yVelocity = -ySpectrumVel;
this.xVelocity = xSpectrumVel;
//print("newYVal", ySpectrumVal, "yVel", ySpectrumVel, "xVel", this.xVelocity);
}
this.yVelocity += this.gravity; // apply some downward gravity
this.yVelocity *= 0.9; // some air resistance
// ensure we don't go too fast
if (this.yVelocity < -MAX_Y_VELOCITY){
this.yVelocity = -MAX_Y_VELOCITY;
}
// add in our velocity
this.y += this.yVelocity;
this.x += this.xVelocity;
// check for running off the left and right edges of the screen
if(this.x < 0 || this.x > width){
this.xVelocity *= -0.95; //reverse direction and slow down a bit
// make sure ball doesn't get stuck on the edge accidentally
if(this.x < 0){
this.x += 1;
}else if(this.x > width){
this.x -= 1;
}
}
// check for the bottom of the screen and make ball bounce a bit
if(this.y + this.diameter/2 > height){
this.yVelocity *= -0.95; // bounces on ground
this.y = height - this.diameter/2;
this.xVelocity *= 0.95; // give the floor some friction
print("bottom", this.yVelocity);
}
}
draw(){
fill(255, 0, 200);
ellipse(this.x, this.y, this.diameter);
}
}