In this recipe we will pick on the previous particle system and add animations based on fast Fourier transform (FFT) analysis from an audio file.
The FFT analysis will return a list of values representing the amplitudes of several frequency windows. We will match each particle to a frequency window and use its value to animate the repulsion that each particle applies to all other particles.
This example uses Cinder's FFT processor, which is only available on Mac OS X.
We will be using the same particle system developed in the previous recipe, Creating a particle system in 2D. Create the Particle
and ParticleSystem
classes described in that recipe, and include the ParticleSystem.h
file at the top of the application's source file.
Using values from the FFT analysis we will animate our particles. Perform the following steps to do so:
ParticleSystem
object on your application's class and a variable to store the number of particles we will create.ParticleSystem mParticleSystem; int mNumParticles;
setup
method we'll create 256 random particles. The number of particles will match the number of values we will receive from the audio analysis.The particles will begin at a random position on the window and have a random size and mass. drag
will be 0.9
.
mNumParticles = 256; for( int i=0; i<mNumParticles; i++ ){ float x = ci::randFloat( 0.0f, getWindowWidth() ); float y = ci::randFloat( 0.0f, getWindowHeight() ); float radius = ci::randFloat( 5.0f, 15.0f ); float mass = radius; float drag = 0.9f; Particle *particle = new Particle ( Vec2f( x, y ), radius, mass, drag ); mParticleSystem.addParticle( particle ); }
update
method, we have to call the update
method on the particle system.void MyApp::update(){ mParticleSystem.update(); }
draw
method, we have to clear the background, calculate the window's matrices, and call the draw
method on the particle system.void MyApp::draw() { gl::clear( Color( 0, 0, 0 ) ); gl::setMatricesWindow( getWindowWidth(), getWindowHeight() ); mParticleSystem.draw(); }
#include "cinder/audio/Io.h" #include "cinder/audio/FftProcessor.h" #include "cinder/audio/PcmBuffer.h" #include "cinder/audio/Output.h"
ci::audio::TrackRef
, which is a reference to an audio track.Audio::TrackRef mAudio;
setup
method we will open a file dialog to allow the user to select which audio file to play.If the retrieved path is not empty, we will use it to load and add a new audio track.
fs::path audioPath = getOpenFilePath(); if( audioPath.empty() == false ){ mAudio = audio::Output::addTrack( audio::load( audioPath.string() ) ); }
mAudio
was successfully loaded and played. We will also enable the PCM buffer and looping.if( mAudio ){ mAudio->enablePcmBuffering( true ); mAudio->setLooping( true ); mAudio->play(); }
Add the following code snippet to the update
method:
Vec2f center = getWindowCenter(); for( vector<Particle*>::iterator it = mParticleSystem.particles.begin(); it != mParticleSystem.particles.end(); ++it ){ Particle *particle = *it; Vec2f force = ( center - particle->position ) * 0.1f; particle->forces += force; }
Declare a local variable std::shared_ptr<float>
, where the result of the FFT will be stored.
We will get a reference to the PCM buffer of mAudio
and perform an FFT analysis on its left channel. It is a good practice to perform a test to check the validity of mAudio
and its buffer.
std::shared_ptr<float>fft; if( mAudio ){ audio::PcmBuffer32fRef pcmBuffer = mAudio->getPcmBuffer(); if( pcmBuffer ){ fft = audio::calculateFft( pcmBuffer->getChannelData( audio::CHANNEL_FRONT_LEFT ), mNumParticles ); } }
Add the following code snippet to the update
method:
if( fft ){ float *values = fft.get(); for( int i=0; i<mParticleSystem.particles.size()-1; i++ ){ for( int j=i+1; j<mParticleSystem.particles.size(); j++ ){ Particle *particleA = mParticleSystem.particles[i]; Particle *particleB = mParticleSystem.particles[j]; Vec2f delta = particleA->position - particleB->position; float distanceSquared = delta.lengthSquared(); particleA->forces += ( delta / distanceSquared ) * particleB->mass * values[j] * 0.5f; particleB->forces -= ( delta / distanceSquared ) * particleA->mass * values[i] * 0.5f;
We created a particle for each one of the values the FFT analysis returns and made each particle repulse every other particle according to its correspondent frequency window amplitude. As the music evolves, the animation will react accordingly.
3.144.38.253