In this chapter, we will learn how to generate sounds using examples of ways to generate sounds driven by physics simulation. We will also present examples of visualizing sound with audio reactive animations.
The following recipes will cover:
In this recipe, we will learn how to generatively create a sine wave oscillator by manipulating the sound card's PCM (Pulse-code Modulation ) audio buffer. The frequency of the sine wave will be defined by the mouse's y coordinate.
We will also draw the sine wave for a visual representation.
Include the following files:
#include "cinder/audio/Output.h" #include "cinder/audio/Callback.h" #include "cinder/Rand.h" #include "cinder/CinderMath.h"
And add the following useful using
statements:
using namespace ci; using namespace ci::app; using namespace std;
We will create a sine wave oscillator using the following steps:
void audioCallback( uint64_t inSampleOffset, uint32_t ioSampleCount, audio::Buffer32f *buffer ); float mFrequency; float mPhase, mPhaseAdd; vector<float> mOutput;
setup
module we will initialize the variables and create the audio callback using the following code:mFrequency = 0.0f; mPhase = 0.0f; mPhaseAdd = 0.0f; audio::Output::play( audio::createCallback( this, &SineApp::audioCallback ) );
update
module we will update mFrequency
based on the mouse's y
position. The mouse's position will be mapped and clamped to a frequency value between 0
and 5000
:float maxFrequency = 5000.0f; float targetFrequency = ( getMousePos().y / (float)getWindowHeight() ) * maxFrequency; mFrequency = math<float>::clamp( targetFrequency, 0.0f, maxFrequency );
Let's implement the audio callback. We'll begin by resizing mOutput
if necessary. Then we will calculate and interpolate mPhaseAdd
, and then loop through all the values in the audio buffer and calculate their values based on the sine of mPhase
and add mPhaseAdd
to mPhase
:
if( mOutput.size() != ioSampleCount ){ mOutput.resize( ioSampleCount ); } int numChannels = buffer->mNumberChannels; mPhaseAdd += ( ( mFrequency / 44100.0f ) - mPhaseAdd ) * 0.1f; for( int i=0; i<ioSampleCount; i++ ){ mPhase += mPhaseAdd; float output = math<float>::sin( mPhase * 2.0f * M_PI ); for( int j=0; j<numChannels; j++ ){ buffer->mData[ i*numChannels + j ] = output; } mOutput[i] = output; }
draw
method, we will clear the background with black and draw a scaled up sine wave with a line strip using the values stored in mOutput
:gl::clear( Color( 0, 0, 0 ) ); if( mOutput.size() > 0 ){ Vec2f scale; scale.x = (float)getWindowWidth() / (float)mOutput.size(); scale.y = 100.0f; float centerY= getWindowHeight() / 2.0f; gl::begin( GL_LINE_STRIP ); for( int i=0; i<mOutput.size(); i++ ){ float x = (float)i * scale.x; float y = mOutput[i] * scale.y + centerY; gl::vertex( x, y ); } gl::end(); }
We are manipulating the PCM buffer. PCM is a method to represent audio through values' samples at regular intervals. By accessing the PCM buffer, we can directly manipulate the audio signal that will be output by the sound card.
Every time the
audioCallback
method is called, we receive a sample of the PCM buffer, where we calculate the values to generate a continuous sine wave.
In the update
module, we calculate the frequency by mapping the mouse's y
position.
In the following line in the audioCallback
implementation, we calculate how much mPhase
has to increase based on a sample rate of 44100
to generate a wave with a frequency of mFrequency
:
mPhaseAdd += ( ( mFrequency / 44100.0f ) - mPhaseAdd ) * 0.1f;
18.116.21.152