CSL  5.2
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; }
49 unsigned CGestalt::blockSize() { return mBlockSize; }
52 unsigned CGestalt::verbosity() { return mVerbosity; }
54 unsigned CGestalt::outPort() { return mOutPort; }
55 bool CGestalt::stopNow() { return mStopNow; }
56 
57 // handle dataFolder paths relative to "~"
58 
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 
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 
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 
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 
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 }