/************** Effects Program *********************/ #include "Skini.h" #include "SKINImsg.h" #include "Envelope.h" #include "PRCRev.h" #include "JCRev.h" #include "NRev.h" #include "FreeVerb.h" #include "Echo.h" #include "PitShift.h" #include "LentPitShift.h" #include "Chorus.h" #include "Messager.h" #include "RtAudio.h" #include #include #include #include using std::min; using namespace stk; 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 { unsigned int effectId; PRCRev prcrev; JCRev jcrev; NRev nrev; FreeVerb frev; Echo echo; PitShift shifter; LentPitShift lshifter; Chorus chorus; Envelope envelope; Messager messager; Skini::Message message; StkFloat lastSample; StkFloat t60; int counter; bool settling; bool haveMessage; // Default constructor. TickData() : effectId(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]; data->effectId = (unsigned int) type; break; } case 22: // effect parameter change 1 data->echo.setDelay( (unsigned long) (temp * Stk::sampleRate() * 0.95) ); data->lshifter.setShift( 1.4 * temp + 0.3 ); 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 ); data->frev.setDamping( temp ); break; case 23: // effect parameter change 2 data->chorus.setModDepth( temp * 0.2 ); data->frev.setRoomSize( temp ); break; case 44: // effect mix data->echo.setEffectMix( temp ); data->shifter.setEffectMix( temp ); data->lshifter.setEffectMix( temp ); data->chorus.setEffectMix( temp ); data->prcrev.setEffectMix( temp ); data->jcrev.setEffectMix( temp ); data->nrev.setEffectMix( temp ); data->frev.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( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *dataPointer ) { TickData *data = (TickData *) dataPointer; register StkFloat *oSamples = (StkFloat *) outputBuffer, *iSamples = (StkFloat *) inputBuffer; register StkFloat sample; Effect *effect; int i, counter, nTicks = (int) nBufferFrames; 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; ieffectId < 3 ) { // Echo, PitShift and LentPitShift ... mono output if ( data->effectId == 0 ) sample = data->envelope.tick() * data->echo.tick( *iSamples++ ); else if ( data->effectId == 1 ) sample = data->envelope.tick() * data->shifter.tick( *iSamples++ ); else sample = data->envelope.tick() * data->lshifter.tick( *iSamples++ ); *oSamples++ = sample; // two channels interleaved *oSamples++ = sample; } else { // Chorus or a reverb ... stereo output if ( data->effectId == 3 ) { data->chorus.tick( *iSamples++ ); effect = (Effect *) &(data->chorus); } else if ( data->effectId == 4 ) { data->prcrev.tick( *iSamples++ ); effect = (Effect *) &(data->prcrev); } else if ( data->effectId == 5 ) { data->jcrev.tick( *iSamples++ ); effect = (Effect *) &(data->jcrev); } else if ( data->effectId == 6 ) { data->nrev.tick( *iSamples++ ); effect = (Effect *) &(data->nrev); } else { data->frev.tick( *iSamples++ ); effect = (Effect *) &(data->frev); } const StkFrames& samples = effect->lastFrame(); *oSamples++ = data->envelope.tick() * samples[0]; *oSamples++ = data->envelope.lastOut() * samples[1]; } 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; 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; i