Making sound-reactive particles

In this recipe, we will show an example of audio visualization based on audio-reactive particles.

Getting ready

Save your favorite music piece in assets folder with the name music.mp3.

Please refer to Chapter 6, Adding a Tail to Our Particles, for instructions on how to draw particles with a tile.

How to do it…

We will create a sample audio-reactive visualization using the following steps:

  1. Add the following necessary header files:
    #include "cinder/Rand.h"
    #include "cinder/MayaCamUI.h"
    #include "cinder/audio/Io.h"
    #include "cinder/audio/Output.h"
    #include "cinder/audio/FftProcessor.h"
    #include "cinder/audio/PcmBuffer.h"
    #include "ParticleSystem.h"
  2. Add the following members for audio playback and analysis:
    audio::TrackRef mTrack;
    audio::PcmBuffer32fRef mPcmBuffer;
    float beatForce;
    float beatSensitivity;
    float avgLvlOld;
    float randAngle;
  3. Add the following members for particle simulation:
    ParticleSystem mParticleSystem;
    Vec3f   attrPosition;
    float attrFactor;
    CameraPersp mCam;
  4. Inside the setup method, initialize the simulation of the members and particles:
    beatForce = 150.f;
    beatSensitivity = 0.03f;
    avgLvlOld = 0.f;
    randAngle = 15.f;
    attrPosition = Vec3f::zero();
    attrFactor = 0.05f;
    int numParticle = 450;
    for( int i=0; i<numParticle; i++ ){
     float x = Rand::randFloat( 0.0f, getWindowWidth() );
     float y = Rand::randFloat( 0.0f, getWindowHeight() );
     float z = Rand::randFloat( 0.0f, getWindowHeight() );
     float radius = Rand::randFloat(2.f, 5.f);
     float mass = radius;
     if(i>300) {
      radius = 1.f;
      mass = 1.0f; 
     }
     float drag = 0.95f;
     Particle *particle = new Particle( Vec3f( x, y, z ), radius,
      mass, drag );
     mParticleSystem.addParticle( particle );
    }
  5. Inside the setup method, initialize camera and audio playback:
    mCam.setPerspective(45.0f, 640.f/480.f, 0.1, 10000);
    mCam.setEyePoint(Vec3f(0.f,0.f,500.f));
    mCam.setCenterOfInterestPoint(Vec3f::zero());
    mTrack = audio::Output::addTrack( audio::load( getAssetPath("music.mp3").c_str() ) );
    mTrack->enablePcmBuffering( true );
  6. Implement the resize method for updating camera properties in regards to resizing windows:
    void MainApp::resize(ResizeEvent event)
    {
    mCam.setPerspective(45.0f, getWindowAspectRatio(), 0.1, 10000);
    }
  7. Inside the update method, implement a simple beat detection. We are decomposing the signal into 32 frequencies using FFT:
    float beatValue = 0.f;
    mPcmBuffer = mTrack->getPcmBuffer();
    if( mPcmBuffer ) {
     int bandCount= 32;
     std::shared_ptr<float> fftRef = audio::calculateFft( 
      mPcmBuffer->getChannelData( audio::CHANNEL_FRONT_LEFT ), 
      bandCount );
     if( fftRef ) {
      float * fftBuffer = fftRef.get();
      float avgLvl= 0.f;
      for( int i= 0; i<bandCount; i++ ) {
       avgLvl += fftBuffer[i] / (float)bandCount;
      }
      avgLvl /= (float)bandCount;
      if(avgLvl>avgLvlOld+beatSensitivity) {
       beatValue = avgLvl - beatSensitivity;
      }
      avgLvlOld = avgLvl;
     }
    }
  8. Also, inside the update method, calculate the particle simulation:
    std::vector<Particle*>::iterator it;
    for( it = mParticleSystem.particles.begin(); it != mParticleSystem.particles.end(); ++it ) {
        Vec3f attrForce = attrPosition - (*it)->position;
    attrForce *= attrFactor;
    if( attrPosition.distance( (*it)->position ) <100.f ) {
      attrForce = (*it)->position - attrPosition;
      attrForce *= 0.02f;
        }
        (*it)->forces += attrForce;
        Vec3f bearForceVec = (*it)->position - attrPosition;
        bearForceVec.normalize();
        bearForceVec.rotate(randVec3f(), randAngle);
        bearForceVec *= beatValue*randFloat(beatForce*0.5f, beatForce);
        (*it)->forces += bearForceVec;
        std::vector<Particle*>::iterator it2;
        for( it2 = mParticleSystem.particles.begin(); it2 != mParticleSystem.particles.end(); ++it2 ) {
            (*it)->forces +=  ( (*it)->position - (*it2)->position ) *0.5f * 0.0001f;
        }
    }
    mParticleSystem.update();
  9. Implement the draw method as follows:
    gl::enableAlphaBlending();
    gl::clear( ColorA::white() );
    gl::setViewport(getWindowBounds());
    gl::setModelView(mCam);
    float r = getElapsedSeconds()*10.f;
    gl::rotate(Vec3f::one()*r);
    mParticleSystem.draw();

How it works…

A particle is drawn as a black dot, or more precisely a sphere and a line as a tail. Due to specific frequency difference, forces repelling particles from the center of the attractor are applied, with a random vector added to these forces.

How it works…

There's more…

You might want to customize the visualization for a specific music piece.

Adding GUI to tweak parameters

We will add GUI that affects particles' behavior using the following steps:

  1. Add the following necessary header file:
    #include "cinder/params/Params.h"
  2. Add the following member to your application's main class:
    params::InterfaceGl    mParams;
  3. At the end of the setup method, initialize GUI using the following code:
    mParams = params::InterfaceGl( "Parameters", Vec2i( 200, 100 ) );
    mParams.addParam( "beatForce", &beatForce, "step=0.01" );
    mParams.addParam( "beatSensitivity", &beatSensitivity, "step=0.01" );
    mParams.addParam( "randAngle", &randAngle, "step=0.01" );
  4. At the and of the draw method, add the following code:
    params::InterfaceGl::draw();
..................Content has been hidden....................

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