In this recipe, we will show an example of audio visualization based on audio-reactive particles.
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.
We will create a sample audio-reactive visualization using the following steps:
#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"
audio::TrackRef mTrack; audio::PcmBuffer32fRef mPcmBuffer; float beatForce; float beatSensitivity; float avgLvlOld; float randAngle;
ParticleSystem mParticleSystem; Vec3f attrPosition; float attrFactor; CameraPersp mCam;
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 ); }
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 );
resize
method for updating camera properties in regards to resizing windows:void MainApp::resize(ResizeEvent event) { mCam.setPerspective(45.0f, getWindowAspectRatio(), 0.1, 10000); }
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; } }
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();
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();
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.
You might want to customize the visualization for a specific music piece.
We will add GUI that affects particles' behavior using the following steps:
#include "cinder/params/Params.h"
main
class:params::InterfaceGl mParams;
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" );
draw
method, add the following code:params::InterfaceGl::draw();
13.59.197.213