1 /**************  Effects Program  *********************/
2 
3 #include "Skini.h"
4 #include "SKINImsg.h"
5 #include "Envelope.h"
6 #include "PRCRev.h"
7 #include "JCRev.h"
8 #include "NRev.h"
9 #include "FreeVerb.h"
10 #include "Echo.h"
11 #include "PitShift.h"
12 #include "LentPitShift.h"
13 #include "Chorus.h"
14 #include "Messager.h"
15 #include "RtAudio.h"
16 
17 #include <signal.h>
18 #include <cstring>
19 #include <iostream>
20 #include <algorithm>
21 using std::min;
22 
23 using namespace stk;
24 
usage(void)25 void usage(void) {
26   // Error function in case of incorrect command-line argument specifications
27   std::cout << "\nuseage: effects flags \n";
28   std::cout << "    where flag = -s RATE to specify a sample rate,\n";
29   std::cout << "    flag = -ip for realtime SKINI input by pipe\n";
30   std::cout << "           (won't work under Win95/98),\n";
31   std::cout << "    and flag = -is <port> for realtime SKINI input by socket.\n";
32   exit(0);
33 }
34 
35 bool done;
finish(int ignore)36 static void finish(int ignore){ done = true; }
37 
38 // The TickData structure holds all the class instances and data that
39 // are shared by the various processing functions.
40 struct TickData {
41   unsigned int effectId;
42   PRCRev   prcrev;
43   JCRev    jcrev;
44   NRev     nrev;
45   FreeVerb frev;
46   Echo     echo;
47   PitShift shifter;
48   LentPitShift lshifter;
49   Chorus   chorus;
50   Envelope envelope;
51   Messager messager;
52   Skini::Message message;
53   StkFloat lastSample;
54   StkFloat t60;
55   int counter;
56   bool settling;
57   bool haveMessage;
58 
59   // Default constructor.
TickDataTickData60   TickData()
61     : effectId(0), t60(1.0), counter(0),
62       settling( false ), haveMessage( false ) {}
63 };
64 
65 #define DELTA_CONTROL_TICKS 64 // default sample frames between control input checks
66 
67 // The processMessage() function encapsulates the handling of control
68 // messages.  It can be easily relocated within a program structure
69 // depending on the desired scheduling scheme.
processMessage(TickData * data)70 void processMessage( TickData* data )
71 {
72   register unsigned int value1 = data->message.intValues[0];
73   register StkFloat value2 = data->message.floatValues[1];
74   register StkFloat temp = value2 * ONE_OVER_128;
75 
76   switch( data->message.type ) {
77 
78   case __SK_Exit_:
79     if ( data->settling == false ) goto settle;
80     done = true;
81     return;
82 
83   case __SK_NoteOn_:
84     if ( value2 == 0.0 ) // velocity is zero ... really a NoteOff
85       data->envelope.setTarget( 0.0 );
86     else // a NoteOn
87       data->envelope.setTarget( 1.0 );
88     break;
89 
90   case __SK_NoteOff_:
91     data->envelope.setTarget( 0.0 );
92     break;
93 
94   case __SK_ControlChange_:
95     // Change all effect values so they are "synched" to the interface.
96     switch ( value1 ) {
97 
98     case 20: { // effect type change
99       int type = data->message.intValues[1];
100       data->effectId = (unsigned int) type;
101       break;
102     }
103 
104     case 22: // effect parameter change 1
105       data->echo.setDelay( (unsigned long) (temp * Stk::sampleRate() * 0.95) );
106       data->lshifter.setShift( 1.4 * temp + 0.3 );
107       data->shifter.setShift( 1.4 * temp + 0.3 );
108       data->chorus.setModFrequency( temp );
109       data->prcrev.setT60( temp * 10.0 );
110       data->jcrev.setT60( temp * 10.0 );
111       data->nrev.setT60( temp * 10.0 );
112       data->frev.setDamping( temp );
113       break;
114 
115     case 23: // effect parameter change 2
116       data->chorus.setModDepth( temp * 0.2 );
117       data->frev.setRoomSize( temp );
118       break;
119 
120     case 44: // effect mix
121       data->echo.setEffectMix( temp );
122       data->shifter.setEffectMix( temp );
123       data->lshifter.setEffectMix( temp );
124       data->chorus.setEffectMix( temp );
125       data->prcrev.setEffectMix( temp );
126       data->jcrev.setEffectMix( temp );
127       data->nrev.setEffectMix( temp );
128       data->frev.setEffectMix( temp );
129       break;
130 
131     default:
132       break;
133     }
134 
135   } // end of type switch
136 
137   data->haveMessage = false;
138   return;
139 
140  settle:
141   // Exit and program change messages are preceeded with a short settling period.
142   data->envelope.setTarget( 0.0 );
143   data->counter = (int) (0.3 * data->t60 * Stk::sampleRate());
144   data->settling = true;
145 }
146 
147 // The tick() function handles sample computation and scheduling of
148 // control updates.  It will be called automatically by RtAudio when
149 // the system needs a new buffer of audio samples.
tick(void * outputBuffer,void * inputBuffer,unsigned int nBufferFrames,double streamTime,RtAudioStreamStatus status,void * dataPointer)150 int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
151          double streamTime, RtAudioStreamStatus status, void *dataPointer )
152 {
153   TickData *data = (TickData *) dataPointer;
154   register StkFloat *oSamples = (StkFloat *) outputBuffer, *iSamples = (StkFloat *) inputBuffer;
155   register StkFloat sample;
156   Effect *effect;
157   int i, counter, nTicks = (int) nBufferFrames;
158 
159   while ( nTicks > 0 && !done ) {
160 
161     if ( !data->haveMessage ) {
162       data->messager.popMessage( data->message );
163       if ( data->message.type > 0 ) {
164         data->counter = (long) (data->message.time * Stk::sampleRate());
165         data->haveMessage = true;
166       }
167       else
168         data->counter = DELTA_CONTROL_TICKS;
169     }
170 
171     counter = min( nTicks, data->counter );
172     data->counter -= counter;
173     for ( i=0; i<counter; i++ ) {
174       if ( data->effectId < 3 ) { // Echo, PitShift and LentPitShift ... mono output
175         if ( data->effectId == 0 )
176           sample = data->envelope.tick() * data->echo.tick( *iSamples++ );
177         else if ( data->effectId == 1 )
178           sample = data->envelope.tick() * data->shifter.tick( *iSamples++ );
179         else
180           sample = data->envelope.tick() * data->lshifter.tick( *iSamples++ );
181         *oSamples++ = sample; // two channels interleaved
182         *oSamples++ = sample;
183       }
184       else { // Chorus or a reverb ... stereo output
185         if ( data->effectId == 3 ) {
186           data->chorus.tick( *iSamples++ );
187           effect = (Effect *) &(data->chorus);
188         }
189         else if ( data->effectId == 4 ) {
190           data->prcrev.tick( *iSamples++ );
191           effect = (Effect *) &(data->prcrev);
192         }
193         else if ( data->effectId == 5 ) {
194           data->jcrev.tick( *iSamples++ );
195           effect = (Effect *) &(data->jcrev);
196         }
197         else if ( data->effectId == 6 ) {
198           data->nrev.tick( *iSamples++ );
199           effect = (Effect *) &(data->nrev);
200         }
201         else {
202           data->frev.tick( *iSamples++ );
203           effect = (Effect *) &(data->frev);
204         }
205         const StkFrames& samples = effect->lastFrame();
206         *oSamples++ = data->envelope.tick() * samples[0];
207         *oSamples++ = data->envelope.lastOut() * samples[1];
208       }
209       nTicks--;
210     }
211     if ( nTicks == 0 ) break;
212 
213     // Process control messages.
214     if ( data->haveMessage ) processMessage( data );
215   }
216 
217   return 0;
218 }
219 
main(int argc,char * argv[])220 int main( int argc, char *argv[] )
221 {
222   TickData data;
223   RtAudio adac;
224   int i;
225 
226   if ( argc < 2 || argc > 6 ) usage();
227 
228   // If you want to change the default sample rate (set in Stk.h), do
229   // it before instantiating any objects!  If the sample rate is
230   // specified in the command line, it will override this setting.
231   Stk::setSampleRate( 44100.0 );
232 
233   // Parse the command-line arguments.
234   unsigned int port = 2001;
235   for ( i=1; i<argc; i++ ) {
236     if ( !strcmp( argv[i], "-is" ) ) {
237       if ( i+1 < argc && argv[i+1][0] != '-' ) port = atoi(argv[++i]);
238       data.messager.startSocketInput( port );
239     }
240     else if (!strcmp( argv[i], "-ip" ) )
241       data.messager.startStdInput();
242     else if ( !strcmp( argv[i], "-s" ) && ( i+1 < argc ) && argv[i+1][0] != '-')
243       Stk::setSampleRate( atoi(argv[++i]) );
244     else
245       usage();
246   }
247 
248   // Allocate the adac here.
249   RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
250   RtAudio::StreamParameters oparameters, iparameters;
251   oparameters.deviceId = adac.getDefaultOutputDevice();
252   oparameters.nChannels = 2;
253   iparameters.deviceId = adac.getDefaultInputDevice();
254   iparameters.nChannels = 1;
255   unsigned int bufferFrames = RT_BUFFER_SIZE;
256   try {
257     adac.openStream( &oparameters, &iparameters, format, (unsigned int)Stk::sampleRate(), &bufferFrames, &tick, (void *)&data );
258   }
259   catch ( RtAudioError& error ) {
260     error.printMessage();
261     goto cleanup;
262   }
263 
264   data.envelope.setRate( 0.001 );
265 
266   // Install an interrupt handler function.
267 	(void) signal( SIGINT, finish );
268 
269   // If realtime output, set our callback function and start the dac.
270   try {
271     adac.startStream();
272   }
273   catch ( RtAudioError &error ) {
274     error.printMessage();
275     goto cleanup;
276   }
277 
278   // Setup finished.
279   while ( !done ) {
280     // Periodically check "done" status.
281     Stk::sleep( 50 );
282   }
283 
284   // Shut down the output stream.
285   try {
286     adac.closeStream();
287   }
288   catch ( RtAudioError& error ) {
289     error.printMessage();
290   }
291 
292  cleanup:
293 
294 	std::cout << "\neffects finished ... goodbye.\n\n";
295   return 0;
296 }
297