CSL 5.2

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

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
63 CSLService :: ~CSLService() {
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
134 CSLService * server;
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