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( ¶meters, 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