Reputation: 9
So I am working with p5.js for class and I am very lost with it, as I dont understand very well. How do I animate this image to match with the sound? I tried frequency analysis but i dont know how to apply to the image. I wanted to animate it as i it was beating, like a heart, but according to the bpm sound i put in the sketch. here is the sketch + image + sound https://editor.p5js.org/FilipaRita/sketches/cUG6qNhIR
Upvotes: 0
Views: 1098
Reputation: 20180
Actually finding the BMP for an entire piece of music would be a bit complicated (see this sound.stackexchange.com question), but if you just want to detect beats in real time I think you can probably hack something together that will work. Here is a visualization that I think will help you understand the data returned by fft.analyze()
:
const avgWindow = 20;
const threshold = 0.4;
let song;
let fft;
let beat;
let lastPeak;
function preload() {
song = loadSound("https://www.paulwheeler.us/files/metronome.wav");
}
function setup() {
createCanvas(400, 400);
fft = new p5.FFT();
song.loop();
beat = millis();
}
function draw() {
// Pulse white on the beat, then fade out with an inverse cube curve
background(map(1 / pow((millis() - beat) / 1000 + 1, 3), 1, 0, 255, 100));
drawSpectrumGraph(0, 0, width, height);
}
let i = 0;
// Graphing code adapted from https://jankozeluh.g6.cz/index.html by Jan Koželuh
function drawSpectrumGraph(left, top, w, h) {
let spectrum = fft.analyze();
stroke('limegreen');
fill('darkgreen');
strokeWeight(1);
beginShape();
vertex(left, top + h);
let peak = 0;
// compute a running average of values to avoid very
// localized energy from triggering a beat.
let runningAvg = 0;
for (let i = 0; i < spectrum.length; i++) {
vertex(
//left + map(i, 0, spectrum.length, 0, w),
// Distribute the spectrum values on a logarithmic scale
// We do this because as you go higher in the spectrum
// the same perceptible difference in tone requires a
// much larger chang in frequency.
left + map(log(i), 0, log(spectrum.length), 0, w),
// Spectrum values range from 0 to 255
top + map(spectrum[i], 0, 255, h, 0)
);
runningAvg += spectrum[i] / avgWindow;
if (i >= avgWindow) {
runningAvg -= spectrum[i] / avgWindow;
}
if (runningAvg > peak) {
peak = runningAvg;
}
}
// any time there is a sudden increase in peak energy, call that a beat
if (peak > lastPeak * (1 + threshold)) {
// print(`tick ${++i}`);
beat = millis();
}
lastPeak = peak;
vertex(left + w, top + h);
endShape(CLOSE);
// this is the range of frequencies covered by the FFT
let nyquist = 22050;
// get the centroid (value in hz)
let centroid = fft.getCentroid();
// the mean_freq_index calculation is for the display.
// centroid frequency / hz per bucket
let mean_freq_index = centroid / (nyquist / spectrum.length);
stroke('red');
// convert index to x value using a logarithmic x axis
let cx = map(log(mean_freq_index), 0, log(spectrum.length), 0, width);
line(cx, 0, cx, h);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/addons/p5.sound.min.js"></script>
Hopefully this code with the comments helps you understand the data returned by fft.analyze()
and you can use this as a starting point to achieve the effect you are looking for.
Disclaimer: I have experience with p5.js but I'm not an audio expert, so there could certainly be better ways to do this. Also while this approach works for this simple audio file there's a good chance it would fail horribly for actual music or real world environments.
Upvotes: 5
Reputation: 2182
If I were you then I would cheat and add some meta data that explicitly includes the timestamps of the beats. This would be a much simpler problem if you could shift the problem of beat detection to pre-processing. Maybe even do it by hand. Rather than trying to do it at runtime. The signal processing to do beat detection in an audio signal is non-trivial.
Upvotes: 0