The previous tutorial chapters were concerned only with monophonic ToolKit instrument playback and control. At this point, it should be relatively clear that one can instantiate multiple instruments and perhaps sum together their outputs or even direct their outputs to separate channels. It is less clear how one might go about controlling a group of instruments. The stk::Voicer class is designed to serve just this purpose.
The stk::Voicer class is a relatively simple voice manager. The user can dynamically add and delete instruments to/from its "control", with the option of controlling specific instruments via unique note tags and/or grouping sets of instruments via a "group" number. All sounding instrument outputs are summed and returned via the tick()
function. The stk::Voicer class responds to noteOn, noteOff, setFrequency, pitchBend, and controlChange messages, automatically assigning incoming messages to the voices in its control. When all voices are sounding and a new noteOn is encountered, the stk::Voicer interrupts the oldest sounding voice. The user is responsible for creating and deleting all instrument instances.
In the following example, we modify the controlbee.cpp
program to make use of three stk::BeeThree instruments, all controlled using a stk::Voicer.
#include "BeeThree.h"
#include "Messager.h"
#include "Voicer.h"
#include "SKINI.msg"
#include <algorithm>
using std::min;
using namespace stk;
struct TickData {
int counter;
bool haveMessage;
bool done;
TickData()
: counter(0), haveMessage(false), done( false ) {}
};
#define DELTA_CONTROL_TICKS 64 // default sample frames between control input checks
void processMessage( TickData* data )
{
register StkFloat value1 = data->message.floatValues[0];
register StkFloat value2 = data->message.floatValues[1];
switch( data->message.type ) {
case __SK_Exit_:
data->done = true;
return;
case __SK_NoteOn_:
if ( value2 == 0.0 )
data->voicer.noteOff( value1, 64.0 );
else {
data->voicer.noteOn( value1, value2 );
}
break;
case __SK_NoteOff_:
data->voicer.noteOff( value1, value2 );
break;
case __SK_ControlChange_:
data->voicer.controlChange( (int) value1, value2 );
break;
case __SK_AfterTouch_:
data->voicer.controlChange( 128, value1 );
case __SK_PitchChange_:
data->voicer.setFrequency( value1 );
break;
case __SK_PitchBend_:
data->voicer.pitchBend( value1 );
}
data->haveMessage = false;
return;
}
int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
{
TickData *data = (TickData *) dataPointer;
register StkFloat *samples = (StkFloat *) outputBuffer;
int counter, nTicks = (int) nBufferFrames;
while ( nTicks > 0 && !data->done ) {
if ( !data->haveMessage ) {
data->messager.popMessage( data->message );
if ( data->message.type > 0 ) {
data->haveMessage = true;
}
else
data->counter = DELTA_CONTROL_TICKS;
}
counter = min( nTicks, data->counter );
data->counter -= counter;
for ( int i=0; i<counter; i++ ) {
*samples++ = data->voicer.tick();
nTicks--;
}
if ( nTicks == 0 ) break;
if ( data->haveMessage ) processMessage( data );
}
return 0;
}
int main()
{
int i;
TickData data;
for ( i=0; i<3; i++ ) instrument[i] = 0;
RtAudioFormat format = (
sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
unsigned int bufferFrames = RT_BUFFER_SIZE;
try {
}
goto cleanup;
}
try {
for ( i=0; i<3; i++ )
}
goto cleanup;
}
for ( i=0; i<3; i++ )
data.voicer.addInstrument( instrument[i] );
if ( data.messager.startStdInput() == false )
goto cleanup;
try {
}
goto cleanup;
}
while ( !data.done )
try {
}
}
cleanup:
for ( i=0; i<3; i++ ) delete instrument[i];
return 0;
}
We have written this program to accept control messages from STDIN
. Assuming the program is compiled as threebees
, the three-voice SKINI scorefile bachfugue.ski
(located in the scores
directory with the examples) can be redirected to the program as:
threebees < scores/bachfugue.ski
For more fun, surf to Kern Scores for a huge assortment of other scorefiles that can be downloaded in the SKINI format.
Another easy extension would be to add the stk::Messager::startMidiInput()
function to the program and then play the instruments via a MIDI keyboard.
[Main tutorial page]