// threebees.cpp STK tutorial program #include "BeeThree.h" #include "RtAudio.h" #include "Messager.h" #include "Voicer.h" #include "SKINI.msg" #include #if !defined(__OS_WINDOWS__) // Windoze bogosity for VC++ 6.0 using std::min; #endif // The TickData structure holds all the class instances and data that // are shared by the various processing functions. struct TickData { Voicer voicer; Messager messager; Skini::Message message; int counter; bool haveMessage; bool done; // Default constructor. TickData() : counter(0), haveMessage(false), done( false ) {} }; #define DELTA_CONTROL_TICKS 64 // default sample frames between control input checks // The processMessage() function encapsulates the handling of control // messages. It can be easily relocated within a program structure // depending on the desired scheduling scheme. 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 ) // velocity is zero ... really a NoteOff data->voicer.noteOff( value1, 64.0 ); else { // a NoteOn 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 ); } // end of switch data->haveMessage = false; return; } // This tick() function handles sample computation and scheduling of // control updates. It will be called automatically when the system // needs a new buffer of audio samples. int tick(char *buffer, int bufferSize, void *dataPointer) { TickData *data = (TickData *) dataPointer; register StkFloat *samples = (StkFloat *) buffer; int counter, nTicks = bufferSize; while ( nTicks > 0 && !data->done ) { if ( !data->haveMessage ) { data->messager.popMessage( data->message ); if ( data->message.type > 0 ) { data->counter = (long) (data->message.time * Stk::sampleRate()); data->haveMessage = true; } else data->counter = DELTA_CONTROL_TICKS; } counter = min( nTicks, data->counter ); data->counter -= counter; for ( int i=0; ivoicer.tick(); nTicks--; } if ( nTicks == 0 ) break; // Process control messages. if ( data->haveMessage ) processMessage( data ); } return 0; } int main() { // Set the global sample rate and rawwave path before creating class instances. Stk::setSampleRate( 44100.0 ); Stk::setRawwavePath( "../../rawwaves/" ); int i; TickData data; RtAudio *dac = 0; Instrmnt *instrument[3]; for ( i=0; i<3; i++ ) instrument[i] = 0; // Figure out how many bytes in an StkFloat and setup the RtAudio object. RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; int bufferSize = RT_BUFFER_SIZE; try { dac = new RtAudio(0, 1, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 4); } catch (RtError& error) { error.printMessage(); goto cleanup; } try { // Define and load the BeeThree instruments for ( i=0; i<3; i++ ) instrument[i] = new BeeThree(); } catch (StkError &) { goto cleanup; } // "Add" the instruments to the voicer. for ( i=0; i<3; i++ ) data.voicer.addInstrument( instrument[i] ); if ( data.messager.startStdInput() == false ) goto cleanup; try { dac->setStreamCallback(&tick, (void *)&data); dac->startStream(); } catch (RtError &error) { error.printMessage(); goto cleanup; } // Block waiting until callback signals done. while ( !data.done ) Stk::sleep( 100 ); // Shut down the callback and output stream. try { dac->cancelStreamCallback(); dac->closeStream(); } catch (RtError &error) { error.printMessage(); } cleanup: for ( i=0; i<3; i++ ) delete instrument[i]; delete dac; return 0; }