CSL  5.2
CRAM_Service.cpp
Go to the documentation of this file.
1 //
2 // CRAM_Service.cpp -- CREATE Real-time Applications Manager (CRAM) service wrapper code for using CSL as a CRAM-managed service
3 // See the copyright and acknowledgment of authors in the file COPYRIGHT
4 //
5 // The CSLService class is the wrapper used to manage CSL programs from CRAM.
6 // This file has the main() function that starts any of a number of different CSL services under the control of CRAM.
7 //
8 // Usage:
9 // CSL_Server service_name listener_port emergency_port CSL_function [-a] [-o UDP_IO-port] [-i RemoteFrameStream-in-IP/port]
10 // e.g.,
11 // CSL_Server CSL31 12345 12355 bells -- play bells sending sound out to the local server
12 // CSL_Server CSL31 12345 12355 voices -o 33123 -- play voices listening for client requests on port 33123
13 // CSL_Server CSL31 12345 12355 mixer -i 128.111.92.51 12445 -i 128.111.92.60 12445 -- mix stereo sample streams from the two given servers
14 //
15 
16 #include "CRAM_Service.h"
17 #include <time.h>
18 #include <signal.h>
19 #include <iostream>
20 #include "CSL_All.h" // include all of CSL core
21 #include "PAIO.h"
22 #include "UDP_IO.h"
23 
24 #ifndef CSL_WINDOWS
25 #include "CAIO.h"
26 #include <unistd.h> // for usleep
27 #endif
28 
29 using namespace cram;
30 using namespace csl;
31 using namespace std;
32 
33 IO * gIO = NULL; // the global IO guy
34 vector <char *> hosts; // lists of remote inputs for the mixer
35 vector <unsigned> ports;
36 
37 // top-level C functions for the CSL players
38 
39 extern "C" void * fm_bells(void * ptr);
40 extern "C" void * swept_noise(void * ptr);
41 extern "C" void * phonemes(void * ptr);
42 extern "C" void * mixer(void * ptr);
43 
44 // The constructor just sets up the CRAM service
45 
46 CSLService :: CSLService(char * name, unsigned short lport, unsigned short eport, char * which)
47  : Server(name, lport, eport) {
48  mLog.logMsg("[CSL Server created]");
49  mWhich = (char *) malloc(strlen(which) + 1);
50  strcpy(mWhich, which);
51  mAppStatus = CRAM_INIT;
52  if ( ! strcmp(mWhich, "bells")) {
53  mOptions.push_back(make_pair (string("density"), string("10")));
54  mOptions.push_back(make_pair (string("reverb"), string("0.97")));
55  } else if ( ! strcmp(mWhich, "noise")) {
56  mOptions.push_back(make_pair (string("lo_frequency"), string("0.2")));
57  mOptions.push_back(make_pair (string("hi_frequency"), string("0.35")));
58  } else if ( ! strcmp(mWhich, "voices")) {
59  mOptions.push_back(make_pair (string("period"), string("10")));
60  }
61 }
62 
64  free(mWhich);
65 }
66 
67 // Class methods
68 
69 // Start is the method that starts CSL playing
70 
71 void CSLService :: start(void) {
72  mLog.logMsg("[Starting CSL server %s]", mName);
73  mAppStatus = CRAM_ON;
74  int err;
75  // Start up one of the player threads, depending on the value of the selector
76  if ( ! strcmp(mWhich, "bells")) {
77  err = pthread_create( & mThread, NULL, fm_bells, (void *) & mLog);
78  } else if ( ! strcmp(mWhich, "noise")) {
79  err = pthread_create( & mThread, NULL, swept_noise, (void *) & mLog);
80  } else if ( ! strcmp(mWhich, "voices")) {
81  err = pthread_create( & mThread, NULL, phonemes, (void *) & mLog);
82  } else if ( ! strcmp(mWhich, "mixer")) {
83  err = pthread_create( & mThread, NULL, mixer, (void *) & mLog);
84  } else {
85  err = -999;
86  }
87  if (err != 0 ) {
88  mLog.logMsg(cram::kLogError, "Error starting CSL thread %d\n", err);
89  mAppStatus = CRAM_FAILED;
90  }
91 }
92 
93 // Stop and exit
94 
95 void CSLService :: stop(void) {
96  mLog.logMsg(kLogImportant, "[Stopping service %s]", mName);
97  gIO->clear_root(); // shut down the output
98  gIO->stop();
99  gIO->close();
100  mAppStatus = CRAM_OFF;
101  pthread_kill(mThread, SIGINT); // kill the CSL play thread
102  Server::stop(); // kill the main listener threads, causing main() to exit
103 }
104 
105 void CSLService :: suspend(void) { // suspend method
106  mLog.logMsg("[Suspend service %s]", mName);
107  mAppStatus = CRAM_SUSPENDED;
108  gIO->stop();
109 }
110 
111 void CSLService :: resume(void) { // resume method
112  mLog.logMsg("[Resums service %s]", mName);
113  mAppStatus = CRAM_ON;
114  gIO->start();
115 }
116 
117 // Set CSL-specific options
118 
119 void CSLService :: set_opt(const char * nam, const char * val) {
120  int i_val;
121 
122  mLog.logMsg("[Configure %s = %s]", nam, val);
123  if ( ! strcmp(nam, "period")) {
124  i_val = atoi(val);
125  if (i_val < 10) i_val = 10;
126  if (i_val > 600) i_val = 600;
127  return;
128  }
129  Program :: set_opt(nam, val); // else hand it up to the superclass
130 }
131 
132 //// MAIN ////
133 
135 
136 // Usage:
137 // CSL_Server service_name listener_port emergency_port CSL_function [-a] [-o out-port] [-i in-ip in-port] ... [-i in-ip in-port]
138 
139 int main(int argc, char * argv[]) {
140  bool autoPlay = false;
141 
142  if (argc < 5) {
143  printf("Usage: %s service_name listener_port emergency_port program-selector\n", argv[0]);
144  exit(0);
145  } // argv 2 and 3 have to be the CRAM listener ports
146  int lport = atoi(argv[2]);
147  int eport = atoi(argv[3]);
148  char * which = argv[4];
149  // all remaining arguments are of the form [-o out-port] [-i in-ip in-port]
150  if (argc > 5) {
151  for (int i = 5; i < argc; i++) {
152  if ( ! strcmp(argv[i], "-o")) {
153  int outPort = atoi(argv[++i]);
154  gIO = new UDP_IO; // Socket-based IO
155  gIO->open(outPort);
156  } else if ( ! strcmp(argv[i], "-i")) {
157  hosts.push_back(argv[++i]);
158  ports.push_back(atoi(argv[++i]));
159  } else if ( ! strcmp(argv[i], "-a")) {
160  autoPlay = true;
161  }
162  }
163  } // Create the server instance
164  if (gIO == NULL) { // output port not specified
165  gIO = new PAIO; // PortAudio IO
166  gIO->open();
167  }
168  server = new CSLService(argv[1], lport, eport, which);
169  if (autoPlay) { // if auto-play, start service
170  server->start();
171  while(true)
172  sleep_sec(1);
173  }
174  // start the listener and emergency threads
175  if (server->start_threads() != 0) {
176  printf("\t[%s: FATAL: Application start-up failed]\n", argv[1]);
177  } else { // suspend until emergency thread terminates
178  printf("\t[%s: Server running]\n", argv[1]);
179  pthread_join(server->mEThread, NULL);
180  printf("\t[%s: Server terminated]\n", argv[1]);
181  }
182  delete server;
183  printf("\t[%s: Server exiting]\n", argv[1]);
184 }
185 
186 //// CSL Server DSP graph code
187 
188 // Random FM bells panning L/R with loads of reverb
189 
190 extern "C" void * fm_bells(void * ptr) {
191  Logger * mLog = (Logger *) ptr;
192  float frq;
193  mLog->logMsg("\tplaying FM bells...");
194  // Create the DSP graph
195  ADSR a_env(1, 0.01, 0.1, 0.1, 0.6); // amplitude env = std ADSR
196  a_env.scale_values(0.25);
197  ADSR i_env(1, 0.001, 0.1, 0.5, 0.5); // index env
198  Sine vox, mod(110); // declare 2 oscillators
199  DynamicVariable var(110, i_env); // multiply index envelope by mod freq
200  MulOp i_mul(mod, var); // scale the modulator by its envelope
201  AddOp adder(i_mul, 220.0); // add in the modulation
202  vox.set_frequency(adder); // set the carrier's frequency
203  MulOp a_mul(vox, a_env); // scale the carrier's output by the amplitude envelope
204  Sine pos(0.25); // LFO panning
205  Panner pan(a_mul, pos);
206 // Stereoverb verb(pan); // add some stereo reverb
207 // gIO->set_root(verb); // plug the graph into the output
208  gIO->set_root(pan); // plug the graph into the output
209  gIO->start(); // start the IO
210  while(true) { // endless loop playing bells
211  frq = 500 + (frandom() * 2000); // pick a new base freq
212  mod.set_frequency(frq * 1.414); // plug the frequency into the graph
213  var.set_value(frq);
214  adder.set_operand(frq);
215  a_env.trigger(); // trigger the envelopes
216  i_env.trigger();
217  sleep_sec(1 + (frandom() * 1)); // sleep a few sec
218  } // ...never reached
219 }
220 
221 // Swept noise instrument
222 
223 extern "C" void * swept_noise(void * ptr) {
224  PinkNoise wnoise; // pink noise source
225  StaticVariable bw(150);
226  // lo-freq noise
227  RandEnv center(0.2, 400.0, 600.0); // frequency, amplitude, offset
228  Butter filt(wnoise, wnoise.sample_rate(), Butter::BW_BAND_PASS, center, bw);
229  filt.set_bandwidth(150.0);
230  RandEnv ampl(0.15, 0.2, 0.2); // frequency, amplitude, offset
231  MulOp mult(filt, ampl);
232  RandEnv pos(0.1);
233  Panner pan(mult, pos);
234  // hi-freq noise
235  RandEnv center2(0.35, 1500.0, 8000.0); // frequency, amplitude, offset
236  Butter filt2(wnoise, wnoise.sample_rate(), Butter::BW_BAND_PASS, center2, bw);
237  filt2.set_bandwidth(300.0);
238  RandEnv ampl2(0.2, 0.2, 0.2); // frequency, amplitude, offset
239  MulOp mult2(filt2, ampl2);
240  RandEnv pos2(0.1);
241  Panner pan2(mult2, pos2);
242  // mix the panners and send to stereo reverb
243  AddOp add(pan, pan2); // summer
244 // Stereoverb verb(add); // composite splitter/stereo-reverb/joiner class
245 // verb.set_room_size(0.98); // very large room
246 // gIO->set_root(verb); // plug the graph into the output
247  gIO->set_root(add); // plug the graph into the output
248  gIO->start(); // start the IO
249  while(true) { // endless loop
250  sleep_sec(10);
251  }
252 }
253 
254 // Mixer instrument that takes a list of remote stream references
255 
256 extern "C" void * mixer(void * ptr) {
257  Logger * mLog = (Logger *) ptr;
258  Mixer mix(2); // stereo mixer
259  // loop to add all of the specified remote inputs to the mixer
260  for (unsigned i = 0; i < hosts.size(); i++)
261  mix.add_input( * (new RemoteFrameStream(hosts[i], ports[i],
262  CSL_DEFAULT_RESPONSE_PORT + (i * 4),
263  2, CSL_RFS_BUFFER_SIZE)));
264  mLog->logMsg("\tplaying CSL mixer...");
265  Stereoverb verb(mix); // add some stereo reverb
266  verb.set_wet_level(0.5);
267  verb.set_room_size(0.983);
268  gIO->start(); // start the IO
269  gIO->set_root(verb); // start the mixer/reverb
270  while(true) { // endless loop
271  sleep_sec(10);
272  }
273 }
274 
275 // Support for speaker streams
276 
277 #define LL_FILENAME "/usr/local/CSL/Data/SpeakerDB.txt"
278 #define SS_FILENAME "/usr/local/CSL/Data/SpeakerDB.aiff"
279 
280 vector <SoundCue *> gSpeakers; // global list of sound cues
281 SoundFile * gSndFile; // global sound file with speakers
282 
283 void load_cues(void) {
284  FILE * inp;
285  char c_name[32], line[128];
286  unsigned start, stop;
287  // load the large sample file
288  gSndFile = new SoundFile(SS_FILENAME);
289  gSndFile->open_for_read();
290  gSndFile->read_buffer_from_file(gSndFile->duration());
291  // now load the sound cue data
292  inp = fopen(LL_FILENAME, "r");
293  if (inp == NULL) {
294  printf("\tError opening file %s\n", LL_FILENAME);
295  return;
296  }
297  while (!feof(inp)) {
298  fgets(line, 128, inp);
299  sscanf(line, "%s %u %u\n", c_name, &start, &stop);
300  gSpeakers.push_back(new SoundCue(c_name, gSndFile, start, stop));
301  // printf("\tSC: %s %d - %d\n", c_name, start, stop);
302  }
303  fclose(inp);
304 }
305 
306 // Phoneme stream instrument -- Play phrases at random positions every so often
307 
308 extern "C" void * phonemes(void * ptr) {
309  load_cues();
310  SoundCue * voice = gSpeakers[random() % gSpeakers.size()];
311  voice->set_to_end();
312  StaticVariable pos(0);
313 
314  MulOp mult( * voice, 0.1);
315  Panner pan(mult, pos);
316 // Stereoverb verb(pan); // add some stereo reverb
317 // verb.set_wet_level(0.5);
318 // verb.set_room_size(0.985);
319 // gIO->set_root(verb); // plug the graph into the output
320  gIO->set_root(pan); // plug the graph into the output
321  gIO->start(); // start the IO
322 
323  while(true) { // get a speaker to play
324  voice = gSpeakers[random() % gSpeakers.size()];
325  voice->set_to_end();
326  pan.set_input( * voice);
327  pos.set_value(frandom() * 2 - 1);
328  voice->trigger(); // now trigger the sample
329  sleep_sec(10 + (frandom() * 15));
330  }
331 }
332 
333 // Logging functions
334 
335 void csl::logMsg(LogLevel level, char * format, ...) {
336  va_list args;
337  cram::LogLevel cLev;
338  switch (level) {
339  case kLogInfo: cLev = cram::kLogInfo; break;
340  case kLogWarning: cLev = cram::kLogWarning; break;
341  case kLogError: cLev = cram::kLogError; break;
342  case kLogFatal: cLev = cram::kLogFatal; break;
343  }
344  va_start(args, format);
345  server->mLog.vlogMsg(cLev, format, args);
346  va_end(args);
347 }
348 
349 void csl::logMsg(char * format, ...) {
350  va_list args;
351  va_start(args, format);
352  server->mLog.vlogMsg(cram::kLogInfo, format, args);
353  va_end(args);
354 }
355