Making our particles sound reactive

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.

Getting ready

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.

How to do it…

Using values from the FFT analysis we will animate our particles. Perform the following steps to do so:

  1. Declare a ParticleSystem object on your application's class and a variable to store the number of particles we will create.
    ParticleSystem mParticleSystem;
    int mNumParticles;
  2. In the 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 );
    }
  3. In the update method, we have to call the update method on the particle system.
    void MyApp::update(){
    mParticleSystem.update();
    }
  4. In the 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();
    }
  5. Now let's load and play an audio file. We start by including the necessary files to load, play, and perform the FFT analysis. Add the following code snippet at the top of the source file:
    #include "cinder/audio/Io.h"
    #include "cinder/audio/FftProcessor.h"
    #include "cinder/audio/PcmBuffer.h"
    #include "cinder/audio/Output.h"
  6. Now declare ci::audio::TrackRef, which is a reference to an audio track.
    Audio::TrackRef mAudio;
  7. In the 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()   ) );
    }
  8. We'll check if 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();
    }
  9. Now that we have an audio file playing, we need to start animating the particles. First we need to apply an elastic force towards the center of the window. We do so by iterating the over all particles and adding a force, which is one-tenth of the difference between the particle's position and the window's center position.

    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;
        }
  10. Now we have to calculate the FFT analysis. This will be done once after every frame in the update.

    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 );
      }
        }
  11. We will use the values from the FFT analysis to scale the repulsion each particle is applying.

    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;
  12. Build and run the application; you will be prompted to select an audio file. Select it and it will begin playing. The particles will move and push each other around according to the audio's frequencies.
    How to do it…

How it works…

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.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.46.141