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