1 /**************  Test Main Program Individual Voice *********************/
2 
3 #include "SKINImsg.h"
4 #include "Instrmnt.h"
5 #include "JCRev.h"
6 #include "Drone.h"
7 #include "Sitar.h"
8 #include "Tabla.h"
9 #include "VoicDrum.h"
10 #include "Messager.h"
11 #include "RtAudio.h"
12 
13 #include <signal.h>
14 #include <cstring>
15 #include <iostream>
16 #include <algorithm>
17 #include <cstdlib>
18 
19 using std::min;
20 using namespace stk;
21 
22 StkFloat float_random(StkFloat max) // Return random float between 0.0 and max
23 {
24   StkFloat temp = (StkFloat) (max * rand() / (RAND_MAX + 1.0) );
25   return temp;
26 }
27 
28 void usage(void) {
29   // Error function in case of incorrect command-line argument specifications.
30   std::cout << "\nuseage: ragamat flags \n";
31   std::cout << "    where flag = -s RATE to specify a sample rate,\n";
32   std::cout << "    flag = -ip for realtime SKINI input by pipe\n";
33   std::cout << "           (won't work under Win95/98),\n";
34   std::cout << "    and flag = -is <port> for realtime SKINI input by socket.\n";
35   exit(0);
36 }
37 
38 bool done;
39 static void finish(int ignore){ done = true; }
40 
41 // The TickData structure holds all the class instances and data that
42 // are shared by the various processing functions.
43 struct TickData {
44   JCRev    reverbs[2];
45   Drone    drones[3];
46   Sitar    sitar;
47   VoicDrum voicDrums;
48   Tabla    tabla;
49   Messager messager;
50   Skini::Message message;
51   StkFloat lastSample;
52   StkFloat t60;
53   int counter;
54   bool settling;
55   bool haveMessage;
56   StkFloat droneChance, noteChance;
57   StkFloat drumChance, voiceChance;
58   int tempo;
59   int chanceCounter;
60   int key;
61   int ragaStep;
62   int ragaPoint;
63   int endPhase;
64   StkFloat rateScaler;
65 
66   // Default constructor.
67   TickData()
68     : t60(4.0), counter(0),
69       settling( false ), haveMessage( false ), droneChance(0.01), noteChance(0.01),
70       drumChance(0.0), voiceChance(0.0), tempo(3000), chanceCounter(3000), key(0), ragaPoint(6), endPhase(0) {}
71 };
72 
73 // Raga key numbers and drone frequencies.
74 const int ragaUp[2][13] = {{57, 60, 62, 64, 65, 68, 69, 71, 72, 76, 77, 81},
75                            {52, 54, 55, 57, 59, 60, 63, 64, 66, 67, 71, 72}};
76 
77 const int ragaDown[2][13] = {{57, 60, 62, 64, 65, 67, 69, 71, 72, 76, 79, 81},
78                              {48, 52, 53, 55, 57, 59, 60, 64, 66, 68, 70, 72}};
79 
80 StkFloat droneFreqs[3] = { 55.0, 82.5, 220.0 };
81 
82 #define DELTA_CONTROL_TICKS 64 // default sample frames between control input checks
83 
84 // The processMessage() function encapsulates the handling of control
85 // messages.  It can be easily relocated within a program structure
86 // depending on the desired scheduling scheme.
87 void processMessage( TickData* data )
88 {
89   register unsigned int value1 = data->message.intValues[0];
90   register StkFloat value2 = data->message.floatValues[1];
91   register StkFloat temp = value2 * ONE_OVER_128;
92 
93   switch( data->message.type ) {
94 
95   case __SK_Exit_:
96     if ( data->settling == false ) goto settle;
97     if ( data->endPhase < 5 ) return;
98     done = true;
99     return;
100 
101   case __SK_ControlChange_:
102 
103     switch ( value1 ) {
104 
105     case 1:
106       data->droneChance = temp;
107       break;
108 
109     case 2:
110       data->noteChance = temp;
111       break;
112 
113     case 4:
114       data->voiceChance = temp;
115       break;
116 
117     case 7:
118       data->tempo = (int) (11025 - value2 * 70.0 );
119       break;
120 
121     case 11:
122       data->drumChance = temp;
123       break;
124 
125     case 64:
126       if ( value2 == 0.0 ) {
127         data->key = 1;
128         droneFreqs[0] = 55.0;
129         droneFreqs[1] = 82.5;
130         droneFreqs[2] = 220.0;
131       }
132       else 	{
133         data->key = 0;
134         droneFreqs[0] = 82.5;
135         droneFreqs[1] = 123.5;
136         droneFreqs[2] = 330.0;
137       }
138       break;
139 
140     default:
141       break;
142     }
143 
144   } // end of type switch
145 
146   data->haveMessage = false;
147   return;
148 
149  settle:
150   // Exit and program change messages are preceeded with a short settling period.
151   data->counter = (int) (data->t60 * Stk::sampleRate());
152   data->drones[1].noteOn( droneFreqs[1], 0.1 );
153   data->settling = true;
154   std::cout << "What Need Have I for This?" << std::endl;
155 }
156 
157 // The tick() function handles sample computation and scheduling of
158 // control updates.  It will be called automatically by RtAudio when
159 // the system needs a new buffer of audio samples.
160 int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
161          double streamTime, RtAudioStreamStatus status, void *dataPointer )
162 {
163   TickData *data = (TickData *) dataPointer;
164   register StkFloat temp, outs[2], *samples = (StkFloat *) outputBuffer;
165   int i, voiceNote, counter, nTicks = (int) nBufferFrames;
166 
167   while ( nTicks > 0 && !done ) {
168 
169     if ( !data->haveMessage ) {
170       data->messager.popMessage( data->message );
171       if ( data->message.type > 0 ) {
172         data->counter = (long) (data->message.time * Stk::sampleRate());
173         data->haveMessage = true;
174       }
175       else
176         data->counter = DELTA_CONTROL_TICKS;
177     }
178 
179     counter = min( nTicks, data->counter );
180     data->counter -= counter;
181     for ( i=0; i<counter; i++ ) {
182       outs[0] = data->reverbs[0].tick( data->drones[0].tick() + data->drones[2].tick()
183                                        + data->sitar.tick() );
184       outs[1] = data->reverbs[1].tick( 1.5 * data->drones[1].tick() + 0.5 * data->voicDrums.tick()
185                                        + 0.5 * data->tabla.tick() );
186       // Mix a little left to right and back.
187       *samples++ = outs[0] + 0.3 * outs[1];
188       *samples++ = outs[1] + 0.3 * outs[0];
189       nTicks--;
190 
191       // Do a bunch of random controls unless settling down to end.
192       if ( data->settling ) {
193         if ( data->counter == 0 ) {
194           data->counter = (int) (data->t60 * Stk::sampleRate());
195           if ( data->endPhase == 0 ) {
196             data->drones[2].noteOn( droneFreqs[2], 0.1 );
197             std::cout << "What Need Have I for This?" << std::endl;
198           }
199           else if ( data->endPhase == 1 ) {
200             data->drones[0].noteOn( droneFreqs[0], 0.1 );
201             std::cout << "RagaMatic finished ... " << std::endl;
202           }
203           else if ( data->endPhase == 2 ) {
204             std::cout << "All is Bliss ... " << std::endl;
205           }
206           else if ( data->endPhase == 3 ) {
207             std::cout << "All is Bliss ..." << std::endl;
208           }
209           data->endPhase++;
210         }
211       }
212       else {
213         data->chanceCounter--;
214         if (data->chanceCounter == 0)	{
215           data->chanceCounter = (int) ( data->tempo / data->rateScaler );
216           if ( float_random(1.0) < data->droneChance )
217             data->drones[0].noteOn( droneFreqs[0], 0.1 );
218           if ( float_random(1.0) < data->droneChance )
219             data->drones[1].noteOn( droneFreqs[1], 0.1 );
220           if ( float_random(1.0) < data->droneChance )
221             data->drones[2].noteOn( droneFreqs[2], 0.1 );
222           if ( float_random(1.0) < data->noteChance ) {
223             temp = float_random(1.0);
224             if ( temp < 0.1) data->ragaStep = 0;
225             else if (temp < 0.5) data->ragaStep = 1;
226             else data->ragaStep = -1;
227             data->ragaPoint += data->ragaStep;
228             if ( data->ragaPoint < 0 )
229               data->ragaPoint -= ( 2 * data->ragaStep );
230             if ( data->ragaPoint > 11 ) data->ragaPoint = 11;
231             if ( data->ragaStep > 0 )
232               data->sitar.noteOn( Midi2Pitch[ragaUp[data->key][data->ragaPoint]],
233                                   0.05 + float_random(0.3) );
234             else
235               data->sitar.noteOn( Midi2Pitch[ragaDown[data->key][data->ragaPoint]],
236                                   0.05 + float_random(0.3) );
237           }
238           if ( float_random(1.0) < data->voiceChance ) {
239             voiceNote = (int) float_random(11);
240             data->voicDrums.noteOn( voiceNote, 0.3 + (0.4 * data->drumChance) +
241                                     float_random(0.3 * data->voiceChance));
242           }
243           if ( float_random(1.0) < data->drumChance ) {
244             voiceNote = (int) float_random(TABLA_NUMWAVES);
245             data->tabla.noteOn( voiceNote, 0.2 + (0.2 * data->drumChance) +
246                                 float_random(0.6 * data->drumChance));
247           }
248         }
249       }
250     }
251     if ( nTicks == 0 ) break;
252 
253     // Process control messages.
254     if ( data->haveMessage ) processMessage( data );
255   }
256 
257   return 0;
258 }
259 
260 int main( int argc, char *argv[] )
261 {
262   TickData data;
263   RtAudio dac;
264   int i;
265 
266   if ( argc < 2 || argc > 6 ) usage();
267 
268   // If you want to change the default sample rate (set in Stk.h), do
269   // it before instantiating any objects!  If the sample rate is
270   // specified in the command line, it will override this setting.
271   Stk::setSampleRate( 44100.0 );
272 
273   // Parse the command-line arguments.
274   unsigned int port = 2001;
275   for ( i=1; i<argc; i++ ) {
276     if ( !strcmp( argv[i], "-is" ) ) {
277       if ( i+1 < argc && argv[i+1][0] != '-' ) port = atoi(argv[++i]);
278       data.messager.startSocketInput( port );
279     }
280     else if (!strcmp( argv[i], "-ip" ) )
281       data.messager.startStdInput();
282     else if ( !strcmp( argv[i], "-s" ) && ( i+1 < argc ) && argv[i+1][0] != '-')
283       Stk::setSampleRate( atoi(argv[++i]) );
284     else
285       usage();
286   }
287 
288   // Allocate the dac here.
289   RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
290   RtAudio::StreamParameters parameters;
291   parameters.deviceId = dac.getDefaultOutputDevice();
292   parameters.nChannels = 2;
293   unsigned int bufferFrames = RT_BUFFER_SIZE;
294   try {
295     dac.openStream( &parameters, NULL, format, (unsigned int)Stk::sampleRate(), &bufferFrames, &tick, (void *)&data );
296   }
297   catch ( RtAudioError& error ) {
298     error.printMessage();
299     goto cleanup;
300   }
301 
302   data.reverbs[0].setT60( data.t60 );
303   data.reverbs[0].setEffectMix( 0.5 );
304   data.reverbs[1].setT60( 2.0 );
305   data.reverbs[1].setEffectMix( 0.2 );
306 
307   data.drones[0].noteOn( droneFreqs[0], 0.1 );
308   data.drones[1].noteOn( droneFreqs[1], 0.1 );
309   data.drones[2].noteOn( droneFreqs[2], 0.1 );
310 
311   data.rateScaler = 22050.0 / Stk::sampleRate();
312 
313   // Install an interrupt handler function.
314 	(void) signal( SIGINT, finish );
315 
316   // If realtime output, set our callback function and start the dac.
317   try {
318     dac.startStream();
319   }
320   catch ( RtAudioError &error ) {
321     error.printMessage();
322     goto cleanup;
323   }
324 
325   // Setup finished.
326   while ( !done ) {
327     // Periodically check "done" status.
328     Stk::sleep( 50 );
329   }
330 
331   // Shut down the output stream.
332   try {
333     dac.closeStream();
334   }
335   catch ( RtAudioError& error ) {
336     error.printMessage();
337   }
338 
339  cleanup:
340 
341   return 0;
342 
343 }
344