/************** Effects Program *********************/ #include "Skini.h" #include "SKINI.msg" #include "Envelope.h" #include "PRCRev.h" #include "JCRev.h" #include "NRev.h" #include "Echo.h" #include "PitShift.h" #include "Chorus.h" #include "Messager.h" #include "RtAudio.h" #include #include #include #if !defined(__OS_WINDOWS__) // Windoze bogosity for VC++ 6.0 using std::min; #endif void usage(void) { // Error function in case of incorrect command-line argument specifications std::cout << "\nuseage: effects flags \n"; std::cout << " where flag = -s RATE to specify a sample rate,\n"; std::cout << " flag = -ip for realtime SKINI input by pipe\n"; std::cout << " (won't work under Win95/98),\n"; std::cout << " and flag = -is for realtime SKINI input by socket.\n"; exit(0); } bool done; static void finish(int ignore){ done = true; } // The TickData structure holds all the class instances and data that // are shared by the various processing functions. struct TickData { Effect *effect; PRCRev prcrev; JCRev jcrev; NRev nrev; Echo echo; PitShift shifter; Chorus chorus; Envelope envelope; Messager messager; Skini::Message message; StkFloat lastSample; StkFloat t60; int counter; bool settling; bool haveMessage; // Default constructor. TickData() : effect(0), t60(1.0), counter(0), settling( false ), haveMessage( 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 unsigned int value1 = data->message.intValues[0]; register StkFloat value2 = data->message.floatValues[1]; register StkFloat temp = value2 * ONE_OVER_128; switch( data->message.type ) { case __SK_Exit_: if ( data->settling == false ) goto settle; done = true; return; case __SK_NoteOn_: if ( value2 == 0.0 ) // velocity is zero ... really a NoteOff data->envelope.setTarget( 0.0 ); else // a NoteOn data->envelope.setTarget( 1.0 ); break; case __SK_NoteOff_: data->envelope.setTarget( 0.0 ); break; case __SK_ControlChange_: // Change all effect values so they are "synched" to the interface. switch ( value1 ) { case 20: { // effect type change int type = data->message.intValues[1]; if ( type == 0 ) data->effect = &(data->echo); else if ( type == 1 ) data->effect = &(data->shifter); else if ( type == 2 ) data->effect = &(data->chorus); else if ( type == 3 ) data->effect = &(data->prcrev); else if ( type == 4 ) data->effect = &(data->jcrev); else if ( type == 5 ) data->effect = &(data->nrev); break; } case 22: // effect parameter change 1 data->echo.setDelay( (unsigned long) (temp * Stk::sampleRate() * 0.95) ); // data->shifter.setShift( temp * 3 + 0.25); data->shifter.setShift( 1.4 * temp + 0.3); data->chorus.setModFrequency( temp ); data->prcrev.setT60( temp * 10.0 ); data->jcrev.setT60( temp * 10.0 ); data->nrev.setT60( temp * 10.0 ); break; case 23: // effect parameter change 2 data->chorus.setModDepth( temp * 0.2 ); break; case 44: // effect mix data->echo.setEffectMix( temp ); data->shifter.setEffectMix( temp ); data->chorus.setEffectMix( temp ); data->prcrev.setEffectMix( temp ); data->jcrev.setEffectMix( temp ); data->nrev.setEffectMix( temp ); break; default: break; } } // end of type switch data->haveMessage = false; return; settle: // Exit and program change messages are preceeded with a short settling period. data->envelope.setTarget( 0.0 ); data->counter = (int) (0.3 * data->t60 * Stk::sampleRate()); data->settling = true; } // The tick() function handles sample computation and scheduling of // control updates. It will be called automatically by RtAudio when // the system needs a new buffer of audio samples. int tick(char *buffer, int bufferSize, void *dataPointer) { TickData *data = (TickData *) dataPointer; register StkFloat sample, *samples = (StkFloat *) buffer; int i, counter, nTicks = bufferSize; while ( nTicks > 0 && !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 ( i=0; ienvelope.tick() * data->effect->tick( *samples ); *samples++ = sample; // two channels interleaved *samples++ = sample; nTicks--; } if ( nTicks == 0 ) break; // Process control messages. if ( data->haveMessage ) processMessage( data ); } return 0; } int main( int argc, char *argv[] ) { TickData data; RtAudio *adac = 0; int i; if (argc < 2 || argc > 6) usage(); // If you want to change the default sample rate (set in Stk.h), do // it before instantiating any objects! If the sample rate is // specified in the command line, it will override this setting. Stk::setSampleRate( 44100.0 ); // Parse the command-line arguments. unsigned int port = 2001; for ( i=1; isetStreamCallback( &tick, (void *)&data ); adac->startStream(); } catch (RtError &error) { error.printMessage(); goto cleanup; } // Setup finished. while ( !done ) { // Periodically check "done" status. Stk::sleep( 50 ); } // Shut down the callback and output stream. try { adac->cancelStreamCallback(); adac->closeStream(); } catch (RtError& error) { error.printMessage(); } cleanup: delete adac; std::cout << "\neffects finished ... goodbye.\n\n"; return 0; }