CSL 5.2

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

- File List
- File Members

CGestalt.cpp
Go to the documentation of this file.
1 //
2 // CGestalt.cpp -- the gestalt class implementation
3 // The MVC Observer/Subject or dependency pattern classes are also here.
4 //
5 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
6 //
7
8 #include "CGestalt.h"
9 #include <stdio.h>
10 #include <stdlib.h> // for malloc
11 #include <string.h>
12 #include <math.h>
13
14 using namespace csl; // this is the namespace, dummy!
15 using namespace std;
16
17 #ifdef WIN32
18 #include <crtdbg.h>
19 #endif
20
21 ////// These are the system default values //////
22 ///
23 /// The actual start-up values are defined in CSL_Types.h
24 ///
25
26 static unsigned mNumInChannels = 2; ///< stereo inputs by default
27 static unsigned mNumOutChannels = 2; ///< stereo outputs
28
29 static unsigned mFrameRate = CSL_mFrameRate; ///< default sample rate (tested up to 96000)
30 static csl::sample mFramePeriod = 1.0f / (float) CSL_mFrameRate; ///< 1 / default sample rate
31 static unsigned mBlockSize = CSL_mBlockSize; ///< typical block size (can be as small as 128 in real usage)
32 static unsigned mMaxBufferFrames = CSL_mMaxBufferFrames; ///< max block size (set large for zooming scopes)
33 static unsigned mSndFileFrames = CSL_mSndFileFrames; ///< max block size (set large for zooming scopes)
34 static unsigned mMaxSndFileFrames = CSL_mMaxSndFileFrames; ///< max block size (set large for zooming scopes)
35
36 static unsigned mVerbosity = CSL_mVerbosity; ///< very verbose
37 static unsigned mLoggingPeriod = CSL_mLoggingPeriod; ///< log CPU every 15 sec
38 static unsigned mOutPort = CSL_mOutPort; ///< RFS output port
39 static std::string mDataFolder = CSL_DATA_DIR; ///< User's CSL data folder ()
40 static bool mStopNow = false; ///< flag to stop threads and timers
41
42 /// CGestalt Accessors for system constants
43
44 unsigned CGestalt::frameRate() { return mFrameRate; }
45 csl::sample CGestalt::framePeriod() { return mFramePeriod; }
46 unsigned CGestalt::maxBufferFrames() { return mMaxBufferFrames; }
47 unsigned CGestalt::sndFileFrames() { return mSndFileFrames; }
48 unsigned CGestalt::maxSndFileFrames() { return mMaxSndFileFrames; }
49 unsigned CGestalt::blockSize() { return mBlockSize; }
50 unsigned CGestalt::numInChannels() { return mNumInChannels; }
51 unsigned CGestalt::numOutChannels() { return mNumOutChannels; }
52 unsigned CGestalt::verbosity() { return mVerbosity; }
53 unsigned CGestalt::loggingPeriod() { return mLoggingPeriod; }
54 unsigned CGestalt::outPort() { return mOutPort; }
55 bool CGestalt::stopNow() { return mStopNow; }
56
57 // handle dataFolder paths relative to "~"
58
59 string CGestalt::dataFolder() {
60 if ((mDataFolder.size() > 0) && (mDataFolder[0] == '~')) { // find '~' in folder name
61 string ans(getenv("HOME")); // UNIX code here
62 if (mDataFolder.size() > 1)
63 ans = ans + mDataFolder.substr(1);
64 return ans;
65 } else
66 return mDataFolder; // else answer mDataFolder
67 }
68
69 void CGestalt::setFrameRate(unsigned sampleRate) {
70 mFrameRate = sampleRate;
71 mFramePeriod = 1.0f / (float) sampleRate;
72 }
73
74 // General setters
75
76 void CGestalt::setMaxBufferFrames(unsigned numFrames) { mMaxBufferFrames = numFrames; }
77 void CGestalt::setMaxSndFileFrames(unsigned numFrames) { mMaxSndFileFrames = numFrames; }
78 void CGestalt::setSndFileFrames(unsigned numFrames) { mSndFileFrames = numFrames; }
79 void CGestalt::setBlockSize(unsigned blockSize) { mBlockSize = blockSize; }
80 void CGestalt::setNumInChannels(unsigned numChannels) { mNumInChannels = numChannels; }
81 void CGestalt::setNumOutChannels(unsigned numChannels) { mNumOutChannels = numChannels; }
82 void CGestalt::setVerbosity(unsigned verb) { mVerbosity = verb; }
83 void CGestalt::setLoggingPeriod(unsigned valInSecs) { mLoggingPeriod = valInSecs; }
84 void CGestalt::setOutPort(unsigned numChannels) { mOutPort = numChannels; }
85 void CGestalt::setDataFolder(std::string dataFolder) { mDataFolder = dataFolder; }
86
87 void CGestalt::setStopNow() { mStopNow = true; }
88 void CGestalt::clearStopNow() { mStopNow = false; }
89
90 // get init file name
91
92 string initFileName() {
93 string nam(CSL_INIT_FILE);
94 if (nam[0] == '~') {
95 string ans(getenv("HOME")); // UNIX code here
96 if (nam.size() > 1)
97 ans = ans + nam.substr(1);
98 return ans;
99 } else
100 return (string(CSL_INIT_FILE));
101 }
102
103 ///< read/write the init file (typ. ~/.cslrc)
104 /// The reader takes a char key, as in
105 /// string initMsg(CGestalt::initFileText('T'));
106 /// which looks for a line starting with "T " in the file and returns the part after the "T "
107 /// The write function takes a key and a string to store.
108 ///
109
110 string CGestalt::initFileText(char key) {
111 FILE * inp = fopen(initFileName().c_str(), "r");
112 if ( ! inp)
113 return string("");
114 char init[CSL_LINE_LEN];
115 while ( ! feof(inp)) { // loop through input file
116 if(fgets(init, CSL_LINE_LEN, inp) != NULL) { // Read a line
117 if (init[0] == key) { // if the key matches, return it
118 char * str = &init[2];
119 return string((const char *)str);
120 }
121 }
122 }
123 fclose(inp);
124 return string("");
125 }
126
127 // Store a string at a key in the init file
128
129 void CGestalt::storeToInitFile(char key, string data) {
130 // logMsg("Store init %c -> %s", key, data.c_str());
131 char rcStr[CSL_STR_LEN];
132 char * rcLin = rcStr;
133 char * cr;
134 size_t len;
135 string iNam = initFileName();
136 FILE * inp = fopen(iNam.c_str(), "r+"); // open file
137 if (inp) {
138 len = fread(rcStr, 1, CSL_STR_LEN, inp);// read whole file
139 rewind(inp); // seek back to start
140 } else { // if file not there
141 inp = fopen(iNam.c_str(), "w");
142 len = 0;
143 }
144 fprintf(inp, "%c %s\n", key, data.c_str()); // write key line
145 while (rcLin < (rcStr + len)) {
146 cr = strchr(rcLin, '\n'); // find CR
147 if (cr)
148 *cr = 0; // set it to 0
149 if (rcLin[0] != key) // copy non-matching lines
150 fprintf(inp, "%s", rcLin);
151 if (cr)
152 rcLin = cr + 1;
153 else
154 rcLin += strlen(rcLin);
155 }
156 fclose(inp);
157 }
158
159 // Handle file recording and output
160
161 #define cheapPrintf(val) \
162 if (val < 10) \
163 *xpos = '0'; \
164 else \
165 *xpos = '0' + (val / 10); \
166 *(xpos+1) = '0' + (val % 10)
167
168 // create a new temp file name
169
170 string CGestalt::sndFileName() {
171 char fsnam[CSL_NAME_LEN]; // temp file name
172 FILE * dFile = NULL; // temp file ptr
173 // create out file name
174 sprintf(fsnam, "%s%s", CGestalt::dataFolder().c_str(), OUT_SFILE_NAME);
175 char * xpos = strstr(fsnam, "XX"); // find XX in the template
176 int cnt = 0;
177 if (xpos) { // if found, replace it with a number
178 do {
179 cheapPrintf(cnt); // pick next file name
180 dFile = fopen(fsnam, "r"); // try to read file
181 if (dFile != NULL) { // if it's there
182 fclose(dFile); // close it and increment counter
183 cnt++;
184 }
185 } while (dFile); // as long as you find files
186 } else {
187 logMsg("Write to file \"%s\" filename not recognized", fsnam);
188 }
189 return string(fsnam);
190 }
191
192 // Logging functions
193
194 #define UNDEFINED_IN_CRAM // undefine this to compile with CRAM service logging
195
196 #ifdef UNDEFINED_IN_CRAM
197
198 ///
199 /// Logging code: if verbosity
200 ///
201
202 // macro to process messasges that start with \n
203
204 #if 1
205 #define SWALLOW_CR()
206 #else
207 #define SWALLOW_CR() \
208 if (format[0] == '\n') { \
209 fprintf(stderr, "\n"); \
210 format[0] = ' '; \
211 }
212 #endif
213
214 //#ifdef WIN32
215 //#define BE_SILENT // logging turned off entirely (e.g., on iPhone)
216 //#endif
217
218 #ifdef BE_SILENT
219
220 void csl::logMsg(const char * format, ...) { }
221
222 void csl::logMsg(LogLevel level, const char * format, ...) { }
223
224 #else // logging
225
226 void csl::vlogMsg(const char * format, va_list args) {
227 vlogMsg(kLogInfo, format, args);
228 }
229
230 void csl::logMsg(const char * format, ...) {
231 va_list args;
232 va_start(args, format);
233 vlogMsg(format, args);
234 va_end(args);
235 }
236
237 #ifdef FMAK_AR // special printing for FMAK apps
238
239 #include "FMAKComponent.h"
240 using namespace fmak;
241 class FMAKComponent; // forward declaration of FMAK component class
242 extern FMAKComponent * gComp;
243
244 void csl::vlogMsg(LogLevel level, const char * format, va_list args) {
245 char message[CSL_LINE_LEN];
246 vsprintf(message, format, args);
247 sprintf(message + strlen(message), "\n");
248 gComp->printMsg(message);
249 // vfprintf(stderr, format, args);
250 // fprintf(stderr, "\n");
251 // fflush(stderr);
252 // usleep(1000); // sleep so GUI can update
253 }
254
255 #else // normal way
256
257 void csl::vlogMsg(LogLevel level, const char * format, va_list args) {
258 switch(level) {
259 case kLogInfo:
260 if (mVerbosity < 3) return;
261 SWALLOW_CR()
262 fprintf(stderr, CSL_LOG_PREFIX);
263 break;
264 case kLogWarning:
265 if (mVerbosity < 2) return;
266 SWALLOW_CR()
267 fprintf(stderr, "%s%s", CSL_LOG_PREFIX, "Warning: ");
268 break;
269 case kLogError:
270 if (mVerbosity < 1) return;
271 SWALLOW_CR()
272 fprintf(stderr, "%s%s", CSL_LOG_PREFIX, "Error: ");
273 break;
274 case kLogFatal:
275 SWALLOW_CR()
276 fprintf(stderr, "%s%s", CSL_LOG_PREFIX, "FATAL ERROR: ");
277 break;
278 }
279 vfprintf(stderr, format, args);
280 fprintf(stderr, "\n");
281 fflush(stderr);
282 if (level == kLogFatal)
283 exit(1);
284 }
285
286 #endif
287
288 void csl::logMsg(LogLevel level, const char * format, ...) {
289 va_list args;
290 va_start(args, format);
291 vlogMsg(level, format, args);
292 va_end(args);
293 }
294
295 ///< Log the file & line #
296
297 void csl::logLine() {
298 // ToDo
299 }
300
301 ///< log file/line as a URL
302
303 void csl::logURL() {
304 // ToDo
305 }
306
307 #endif // BE_SILENT
308
309 #endif // UNDEFINED_IN_CRAM
310
311 // A couple of useful functions that didn't fit in anywhere else...
312
313 #ifndef WIN32
314 #include <unistd.h> // for usleep
315 #else
316
317 void usleep(int usec) {
318 unsigned msec = usec / 1000;
319 unsigned now = Time::getMillisecondCounter();
320 Time::waitForMillisecondCounter(now + msec);
321 }
322 float log2f(float n) {
323 return logf(n) / logf(2.0f);
324 }
325 #endif
326
327 //#ifndef WIN32
328 ///
329 /// Global Sleep functions that work for windows and mac/unix.
330 /// Note the use of the global flag gStopNow, which interrupts timers.
331
332 #define TIMER_INTERVAL 0.25f // loop time in sec to check stopNow flag in sleep timers
333
334 // sleep for microseconds
335
336 bool csl::sleepUsec(float dur_in_usec) {
337 if (dur_in_usec <= 0.0) return false;
338 int interval;
339 int periods = (int) ((dur_in_usec / 1000000.0f) / TIMER_INTERVAL);
340 if (periods < 1) {
341 interval = (int) dur_in_usec;
342 periods = 1;
343 } else {
344 interval = (int) (TIMER_INTERVAL * 1000000.0f); // or dur_in_usec / periods;
345 }
346 int count = periods;
347 while (count && ( ! CGestalt::stopNow())) { // timers count short loops of 0.25 sec or so
348 usleep(interval); // micro-sleep
349 count--;
350 } // check global flag in timer loop
351 if (CGestalt::stopNow()) return true;
352 if ((interval * periods) < dur_in_usec) // sleep for remainder interval
353 usleep((int)(dur_in_usec - (interval * periods)));
354 if (CGestalt::stopNow()) {
355 // CGestalt::clearStopNow(); // should a timer clear the flag?
356 return true;
357 }
358 return false; // false means AOK termination
359 }
360
361 // sleep for milliseconds
362
363 bool csl::sleepMsec(float dur_in_msec) {
364 if (dur_in_msec <= 0.0) return false;
365 return sleepUsec(dur_in_msec * 1000.0f);
366 }
367
368 // sleep for seconds
369
370 bool csl::sleepSec(float dur_in_sec) {
371 if (dur_in_sec <= 0.0) return false;
372 return sleepUsec(dur_in_sec * 1000000.0f);
373 }
374
375 // current system or IO time in ticks
376
377 Timestamp csl::timeNow() {
378 #ifdef USE_JUCE
379 return Time::getHighResolutionTicks();
380 #else
381 struct timeval tv;
382 gettimeofday(&tv, NULL);
383 return ((Timestamp) tv.tv_sec * 1000) + ((Timestamp) tv.tv_usec / 1000);
384 #endif
385 }
386
387 // current system or IO time in seconds
388
389 float csl::fTimeNow() {
390 #ifdef USE_JUCE
391 return (float) Time::highResolutionTicksToSeconds(Time::getHighResolutionTicks());
392 #else
393 struct timeval tv;
394 gettimeofday(&tv, NULL);
395 return (float) tv.tv_sec + ((float) tv.tv_usec / 1000000.0f);
396 #endif
397 }
398
399 //#endif
400
401 //// Randoms
402
403 // Answer a random as a float in the range 0 - 1
404
405 float csl::fRandZ(void) {
406 return((float) rand() / (float) RAND_MAX);
407 }
408
409 // Answer a random as a float in the range -1 - +1
410
411 float csl::fRand1(void) {
412 return ((csl::fRandZ() * 2.0f) - 1.0f);
413 }
414
415 // Answer a random as a float in the range min - max
416
417 float csl::fRandM(float minV, float maxV) {
418 return (minV + (fRandZ() * (maxV - minV)));
419 }
420
421 // Answer a random as a float in the range base +- (range * base)
422
423 float csl::fRandR(float base, float range) {
424 return (base + (fRandZ() * range * base));
425 }
426
427 // Answer a random as a float in the range base +- range
428
429 float csl::fRandB(float base, float range) {
430 return (base + (fRand1() * range));
431 }
432
433 // Answer a random as a float in the range 0 - val
434
435 float csl::fRandV(float val) {
436 return (csl::fRandZ() * val);
437 }
438
439 // Integer float fcns
440
441 // Answer a random as an int in the range 0 - val
442
443 int csl::iRandV(int val) {
444 return (int)fRandV((float) val);
445 }
446
447 // Answer a random as an int in the range min - max
448
449 int csl::iRandM(int minV, int maxV) {
450 return (int)fRandM((float) minV, (float) maxV);
451 }
452
453 // Answer a random as an int in the range base +- range
454
455 int csl::iRandB(int base, int range) {
456 return (int)fRandB((float) base, (float) range);
457 }
458
459 // Answer true or false
460
461 bool csl::coin() {
462 return (csl::fRandZ() > 0.5f);
463 }
464
465 // Answer true or false with a bias (1 = always true)
466
467 bool csl::coin(float bias) {
468 return (csl::fRandZ() < bias);
469 }
470
471 /// keyToFreq -- converts from MIDI key numbers (1 - 127) to frequency in Hz.
472 // 8.17579891564371 Hz is a very low C = MIDI note 0
473
474 float csl::keyToFreq(unsigned midiKey) {
475 return (8.17579891564371f * powf(2.0f, (midiKey / 12.0f)));
476 }
477
478 /// freqToKey -- converts from frequency in Hz to MIDI key #
479 /// 8.17579891564371 Hz is MIDI key 0
480
481 unsigned csl::freqToKey(float frequency) {
482 return (unsigned) (12.0f * log2f(frequency / 8.17579891564371f));
483 }
484
485 //
486 // MVC Observable pattern implementations
487 //
488
489 // attach/remove observers from models
490
491 void Model::attachObserver (Observer * o) {
492 mObservers.push_back(o);
493 mHasObservers = true;
494 // mUpdateTime = fTimeNow();
495 if (o->mPeriod != 0) { // if the observer has an update period
496 mPeriod = o->mPeriod;
497 }
498 if (o->mKey != 0) { // if the observer has an update filter key
499 mHasObserverMap = true;
500 mObsMap[o->mKey].push_back(o);
501 }
502 #ifdef CSL_DEBUG
503 logMsg("Model::attachObserver %x", o);
504 #endif
505 }
506
507 void Model::detachObserver (Observer * o) {
508 int count = mObservers.size();
509 int i;
510 for (i = 0; i < count; i++)
511 if (mObservers[i] == o)
512 break;
513 if (i < count)
514 mObservers.erase(mObservers.begin() + i);
515 if (mObservers.size() == 0)
516 mHasObservers = false;
517 // if (o->mKey != 0) { // if the observer has an update filter key
518 // mObsMap[o->mKey].delete(o);
519 // }
520 #ifdef CSL_DEBUG
521 logMsg("Model::detachObserver");
522 #endif
523 }
524
525 // This is the (simple) body of the main notification method;
526 // send this->changed(* any_object) from within model messages
527 // that want to trigger the observers
528
529 void Model::changed(void * argument) {
530 if ( ! mHasObservers) // speed hack
531 return;
532 ObserverVector::iterator pos; // iterate over my observers
533 #ifdef CSL_DEBUG
534 logMsg("Model::update %d", mObservers.size());
535 #endif
536 //float nowt = fTimeNow();
537 //if (nowt > (mUpdateTime + mPeriod)) {
538 //mUpdateTime = nowt;
539 if (mHasObserverMap) {
540 int key = this->evaluate(argument);
541 ObserverVector obs = mObsMap[key];
542 for (pos = obs.begin(); pos != obs.end(); ++pos)
543 (* pos)->update(argument);
544 } else {
545 for (pos = mObservers.begin(); pos != mObservers.end(); ++pos)
546 (* pos)->update(argument);
547 }
548 //}
549 }