CSL  5.2
CSL_Core.h
Go to the documentation of this file.
1 ///
2 /// CSL_Core.h -- the specification file for the core classes of CSL version 5
3 ///
4 /// See the copyright notice and acknowledgment of authors in the file COPYRIGHT
5 ///
6 /// What's here:
7 ///
8 /// Core Classes
9 /// Buffer -- the multi-channel sample buffer class (passed around between generators and IO guys)
10 /// BufferCMap -- a sample buffer with channel map and count (used for many-channel processing)
11 /// Port -- used to represent signal and control inputs and outputs in named maps;
12 /// holds a UnitGenerator and its buffer
13 /// UnitGenerator -- an object that can fill a buffer with samples, the central abstraction of CSL DSP
14 ///
15 /// Mix-in classes (added to UnitGenerator)
16 /// Controllable -- superclass of the mix-ins that add control or signal inputs (held in maps)
17 /// Effect -- A (controllable) UnitGenerator subclass that process an input port (e.g., filters, panners).
18 /// All effects inherit from me.
19 /// Scalable -- A (controllable) mix-in that adds scale (multiplicative) and offset (additive)
20 /// inputs (used by most common UGens)
21 /// Phased -- a (controllable) mix-in for generators with frequency inputs and persistent phase accumulators
22 /// All of these mix-in classes add macros for handling their special named control ports, as in
23 /// DECLARE_PHASED_CONTROLS, LOAD_PHASED_CONTROLS, and UPDATE_PHASED_CONTROLS
24 /// Writeable -- a mix-in for generators that one can write into a buffer on
25 /// Seekable -- a mix-in for generators that one can position (seek) as a stream
26 /// Cacheable -- a mix-in for generators that can cache their past output values (of any size)
27 ///
28 /// Channel/Buffer processing
29 /// FanOut -- 1-in n-out fan-out object (now built in to UnitGenerator)
30 /// Splitter -- splits a stream into multiple 1-channel outputs
31 /// Joiner -- joins multiple 1-channel inputs into a single stream
32 /// Interleaver -- general inderleaver/de-interleaver
33 ///
34 /// I/O
35 /// IO -- the input/output stream/driver, its utility functions and virtual constructors
36 /// IODevice -- a holder for a sound interface with name, id, # IO channels, etc.
37 ///
38 
39 #ifndef CSL_CORE_H // This is in case you try to include this twice
40 #define CSL_CORE_H
41 
42 #include "CSL_Types.h" // CSL type definitions and central macros, Observer classes
43 #include "CSL_Exceptions.h" // CSL exception hierarchy
44 #include "CGestalt.h" // System constants class
45 
46 namespace csl { // All this happens in the CSL namespace
47 
48 ///
49 /// Sample buffer contents type (optional)
50 /// One could argue that we should use subclasses for this, but they're not behaviorally different at present.
51 ///
52 
53 #ifdef CSL_ENUMS
54 typedef enum {
55  kSamples, ///< Regular audio samples
56  kSpectra, ///< FFT complex spectral frames
57  kLPCCoeff, ///< LPC reflection coefficients
58  kIRData, ///< FIR Impulse Response frames
59  kWavelet, ///< Wavelet coefficients
60  kGeometry, ///< Spatial geometry buffers
61  kUnknown ///< Unknown or other data type
63 #else
64  #define kSamples 0
65  #define kSpectra 1
66  #define kLPCCoeff 2
67  #define kIRData 3
68  #define kWavelet 4
69  #define kGeometry 5
70  #define kUnknown 6
71  typedef int BufferContentType;
72 #endif
73 
74 //-------------------------------------------------------------------------------------------------//
75 ///
76 /// Buffer -- the multi-channel sample buffer class (passed around between generators and IO guys).
77 ///
78 /// Buffers have an opaque pointer () to their data () and know their # channels and frames.
79 /// They have Boolean aspects about their buffer allocation, and can allocate, free, zero, and check their data.
80 ///
81 /// Note that this is a "record" class in that its members are all public and it has no accessor
82 /// functions or complicated methods. It does handle sample buffer allocation and has
83 /// Boolean members to determine what its pointer state is.
84 /// Note also that Buffers are *not* thread-safe; they hand out pointers (sample*)
85 /// that are assumed to be volatile.
86 ///
87 
88 class Buffer {
89 public: /// Constructors: default is mono and default-size
90  Buffer(unsigned numChannels = 1, unsigned numFrames = CSL_mBlockSize);
91  virtual ~Buffer(); ///< Destructor
92 
93  // public data members
94  unsigned mNumChannels; ///< num channels in buffer (num mono buffers)
95  unsigned mNumFrames; ///< num frames used in each buffer
96  unsigned mNumAlloc; ///< num frames in each buffer
97  unsigned mMonoBufferByteSize; ///< size of each buffer in bytes
98  unsigned mSequence; ///< sequential serial number
99  Timestamp mTimestamp; ///< the buffer's most recent timestamp
100 
101  bool mAreBuffersAllocated; ///< are the buffers allocated?
102  bool mDidIAllocateBuffers; ///< who allocated my data buffers?
103  bool mIsPopulated; ///< does the buffer have data?
104  bool mAreBuffersZero; ///< have the buffers been zeroed out?
105  BufferContentType mType; ///< Data type flag
106  /// set the internal size variables (no buffer allocation takes place)
107  void setSize(unsigned numChannels, unsigned numFrames);
108  /// this version doesn't even allocate the pointers
109  void setSizeOnly(unsigned numChannels, unsigned numFrames);
110 
111  void checkBuffers() throw (MemoryError); ///< allocate if not already there
112  void allocateBuffers() throw (MemoryError); ///< fcn to malloc storage buffers
113  void freeBuffers(); ///< fcn to free them
114  bool canStore(unsigned numFrames); ///< answer whether the recevei can store numFrames more frames
115 
116  void zeroBuffers(); ///< fill all data with 0
117  void fillWith(sample value); ///< fill data with the given value
118  // import data from the given buffer
119  void copyFrom(Buffer & src) throw (RunTimeError);
120  void copyHeaderFrom(Buffer & source) throw (RunTimeError); ///< copy the "header" fields of a buffer
121  void copySamplesFrom(Buffer & src) throw (RunTimeError); ///< import data from the given buffer
122  void copySamplesFromTo(Buffer & src, unsigned offset) throw (RunTimeError); ///< same with write offset
123  void copyOnlySamplesFrom(Buffer & src) throw (RunTimeError);///< import data from the given buffer
124 
125  csl::Status convertRate(int fromRate, int toRate); ///< convert the sample rate using libSampleRate
126 
127  /// answer a samp ptr with offset
128  virtual SampleBuffer samplePtrFor(unsigned channel, unsigned offset);
129  /// answer a samp ptr tested for extent (offset + maxFrame)
130  virtual SampleBuffer samplePtrFor(unsigned channel, unsigned offset, unsigned maxFrame);
131 
132  /// convenience accessors for sample buffers
133  virtual SampleBuffer monoBuffer(unsigned bufNum) { return mBuffers[bufNum]; }
134  virtual SampleBuffer buffer(unsigned bufNum) { return mBuffers[bufNum]; }
135  virtual SampleBuffer * buffers() { return mBuffers; }
136  /// Set the buffer pointer (rare; used in joiners)
137  virtual void setBuffers(SampleBuffer * sPtr) { mBuffers = sPtr; };
138  virtual void setBuffer(unsigned bufNum, SampleBuffer sPtr) { mBuffers[bufNum] = sPtr; };
139  virtual void setBuffer(unsigned bufNum, unsigned offset, sample samp) { *((mBuffers[bufNum]) + offset) = samp; };
140 
141 /// Buffer Sample Processing (optional)
142 /// One could also easily add Buffer operators, such as (Buffer + Buffer) or (Buffer * Buffer)
143 
144 #ifdef CSL_DSP_BUFFER
145  float rms(unsigned chan); ///< get the root-mean-square of the samples
146  float avg(unsigned chan); ///< get the average of the samples
147  float max(unsigned chan); ///< get the max of the absolute val of the samples
148  float min(unsigned chan); ///< get the min of the samples
149  unsigned int zeroX(unsigned chan); ///< count the zero-crossings in the samples
150  unsigned int indexOfPeak(unsigned chan); ///< answer the index of the peak value
151  unsigned int indexOfPeak(unsigned chan, unsigned low, unsigned hi); ///< answer the index of the peak value
152  unsigned int indexOfMin(unsigned chan); ///< answer the index of the peak value
153  unsigned int indexOfMin(unsigned chan, unsigned low, unsigned hi); ///< answer the index of the peak value
154  void autocorrelation(unsigned chan, SampleBuffer result); ///< autocorrelation into the given array
155 #endif
156 
157 protected:
158  SampleBufferVector mBuffers; ///< the storage vector -- pointers to (SampleBuffer) buffers
159 
160 };
161 
162 ///
163 /// BufferCMap is a Sample buffer with channel map and count.
164 /// The map is so that one can have (e.g.,) a buffer that stands for 3 channels within an 8-channel space
165 ///
166 
167 class BufferCMap : public Buffer {
168 public:
169  BufferCMap(); ///< Constructors: default is useless
170  BufferCMap(unsigned numChannels, unsigned numFrames); ///< ask for a given number of "virtual" channels
171  BufferCMap(unsigned numChannels, unsigned realNumChannels, unsigned numFrames);
172  ~BufferCMap(); ///< Destructor
173 
174  unsigned mRealNumChannels; ///< the actual number of channels used
175  std::vector<int> mChannelMap; ///< the map between virtual and real channels
176 
177  /// Pointer accessor uses channel map
178  SampleBuffer monoBuffer(unsigned bufNum) { return mBuffers[mChannelMap[bufNum]]; }
179 };
180 
181 ///
182 /// UnitGenerator buffer copy policy flags (for multi-channel expansion)
183 ///
184 
185 #ifdef CSL_ENUMS
186 typedef enum {
187  kCopy, ///< compute 1 channel and copy
188  kExpand, ///< call monoNextBuffer multiple times
189  kIgnore ///< ignore extra buffer channels
191 #else
192  #define kCopy 0
193  #define kExpand 1
194  #define kIgnore 2
195  typedef int BufferCopyPolicy;
196 #endif
197 
198 class RingBuffer; ///< forward declaration
199 
200 //-------------------------------------------------------------------------------------------------//
201 ///
202 /// UnitGenerator -- the core of CSL; all unit generators inherit from this class.
203 ///
204 /// These have members for their sample rate and number of channels, and know their outputs.
205 /// The main operation is the nextBuffer() method, which is overridden in many of the subclasses.
206 ///
207 /// If more than 1 output is used, these can handle fan-out automatically, either synchronous
208 /// (as in loops in a graph) or async (as in separate call-back threads).
209 /// The mOutputCache RingBuffer may hold some large number of past samples, and can use nextBuffer()
210 /// to do n-way fan-out either synchronously or with differing buffer sizes or callback rates.
211 ///
212 /// UnitGenerator inherits from Model, meaning that it has to send this->changed((void *) dataBuffer)
213 /// from within its nextBuffer method so that dependent objects (like signal views) can get notification
214 /// when it computes samples. This mechanism could also be used for signal flow.
215 ///
216 
217 class UnitGenerator : public Model {
218 public:
219  /// Constructors (UGens are mono by default)
220  /// defaults to mono and maxBlockSize if not specified.
221  UnitGenerator(unsigned rate = CGestalt::frameRate(), unsigned chans = 1);
222  virtual ~UnitGenerator(); ///< Destructor
223 
224  // accessing methods
225  unsigned frameRate() { return mFrameRate; }; ///< get/set the receiver's frame rate
226  void setFrameRate(unsigned rate) { mFrameRate = rate; }
227 
228  virtual unsigned numChannels() { return mNumChannels; }; ///< get/set the receiver's number of outputs
229  void setNumChannels(unsigned ch) { mNumChannels = ch; }
230 
231  BufferCopyPolicy copyPolicy() { return mCopyPolicy; }; ///< get/set the receiver's buffer copy policy
233 
234 // string name() { return mName; }; ///< get/set the receiver's name string
235 // void setName(char * ch) { mName = string(ch); }
236 // void setName(string ch) { mName = ch; }
237 
238  /// get a buffer of Frames -- this is the core CSL "pull" function;
239  /// the given buffer can be written into, and a changed() message is sent.
240  virtual void nextBuffer(Buffer & outputBuffer) throw (CException);
241 
242  /// really compute the next buffer given an offset base channel;
243  /// this is called by nextBuffer, possibly multiple times
244  virtual void nextBuffer(Buffer & outputBuffer, unsigned outBufNum) throw (CException);
245 
246  /// query whether I'm fixed (StaticVariable overrides this)
247  virtual bool isFixed() { return false; };
248  /// query whether I'm currently active (Envelopes can go inactive)
249  virtual bool isActive() { return true; };
250  /// add to or return the UGen vector of outputs
251  void addOutput(UnitGenerator * ugen);
252  void removeOutput(UnitGenerator * ugen);
253  UGenVector outputs() { return mOutputs; };
254  virtual unsigned numOutputs() { return mNumOutputs; };
255  /// check for fan-out and copy previous buffer; return true if fanning out
256  bool checkFanOut(Buffer & outputBuffer) throw (CException);
257  void handleFanOut(Buffer & outputBuffer) throw (CException);
258 
259  /// set/get the value (not allowed in the abstract, useful for static values)
260  virtual void setValue(sample theValue) { throw LogicError("can't set value of a generator"); };
261  virtual sample value() { throw LogicError("can't get value of a generator"); };
262 
263  virtual void dump(); ///< pretty-print the receiver
264  virtual void trigger() { }; ///< trigger ignored here
265 
266 protected: // My data members
267  unsigned mFrameRate; ///< the frame rate -- initialized to be the default by the constructor
268  unsigned mNumChannels; ///< my "expected" number of output channels
269  BufferCopyPolicy mCopyPolicy; ///< the policy I use if asked for more or fewer channels
270  UGenVector mOutputs; ///< the vector of my output UGens
271  unsigned mNumOutputs; ///< the number of outputs
272  Buffer * mOutputCache; ///< my past output ring buffer (only used in case of fan-out)
273  unsigned mSequence; ///< the highest-seen buffer seq number
274 // string mName; ///< my name (used for editors)
275  /// utility method to zero out an outputBuffer
276  void zeroBuffer(Buffer & outputBuffer, unsigned outBufNum);
277 };
278 
279 //-------------------------------------------------------------------------------------------------//
280 ///
281 /// Port -- used to represent constant, control-rate or signal inputs and outputs in named maps;
282 /// holds a UnitGenerator and its buffer, OR a single floating-point value (in which case the
283 /// UGen pointer is set to NULL and mPtrIncrement = 0).
284 /// The nextValue() message is used to get the dynamic or static value.
285 ///
286 
287 class Port {
288 public:
289  Port(); ///< Constructors: default is a float = 0
290  Port(UnitGenerator * ug); ///< Given a UGen, use it as the input
291  Port(float value); ///< given a float, hold it as the static value
292  virtual ~Port(); ///< Destructor
293 
294  // public data members
295  UnitGenerator * mUGen; ///< my unit generator (pointer or NULL)
296  Buffer * mBuffer; ///< the buffer used to hold my output
297  float mValue; ///< my value (in case I'm fixed [mUGen == NULL])
298  float *mValuePtr; ///< my value's address (const or buffer pointer)
299  unsigned mPtrIncrement; ///< the inter-sample ptr increment (0 for const, 1 for dynamic)
300  unsigned mValueIndex; ///< my index (into the UGen's buffer)
301 
302  void checkBuffer() throw (LogicError); ///< check the port's buffer and allocate it if needed
303  inline float nextValue(); ///< answer the next value (dynamic or constant)
304  inline void nextFrame(SampleBuffer where); ///< write the val to a buffer
305  inline bool isReady(); ///< answer whether I'm ready to be read
306  void resetPtr(); ///< reset the buffer pointer without re-pulling the input
307  virtual bool isActive(); ///< answer whether I'm active
308  void dump(); ///< pretty-print the receiver
309  bool isFixed() { return (mPtrIncrement == 0); }; ///< am I fixed or dynamic
310  virtual void trigger() { if (mUGen) mUGen->trigger(); }; ///< trigger passed on here
311 
312 };
313 
314 // Answer the next value (dynamic or constant) as a fast inline function
315 
317  mValuePtr += mPtrIncrement; // increment the pointer (mPtrIncrement may be 0)
318  return *mValuePtr; // return the value
319 }
320 
321 // Write the next n-dimensional sample frame
322 
323 inline void Port::nextFrame(SampleBuffer where) {
324  for (unsigned i = 0; i < mBuffer->mNumChannels; i++)
325  *where++ = mBuffer->buffer(i)[mValueIndex];
326  mValueIndex++; // increment the counter (an unsigned, initially 0)
327 }
328 
329 // Answer whether the give port is ready (i.e., has its UGen or scalar value set up and primed)
330 
331 inline bool Port::isReady() {
332  return (mValuePtr != 0);
333 }
334 
335 //-------------------------------------------------------------------------------------------------//
336 ///
337 /// Controllable -- superclass of the mix-ins that add control or signal inputs.
338 /// This holds onto a map of port objects that represent the inputs,
339 /// and manages the naming and processing flow for dynamic inputs.
340 ///
341 /// A typical complex UGen will have several ports, e.g., for frequency, scale, and
342 /// offset in the case of an oscillator that supports AM and FM.
343 /// The pullInput() message is used to call the nextBuffer() method of a given port.
344 ///
345 
347 public:
348  Controllable() : mInputs() { }; ///< Constructor takes no arguments
349  virtual ~Controllable(); ///< Destructor (remove the output links of the ports)
350 
351  Port * getPort(CSL_MAP_KEY name);
352 protected:
353  PortMap mInputs; ///< the map of my inputs or controls (used by the mix-in classes)
354 
355  /// Plug in a unit generator to the named input slot
356  void addInput(CSL_MAP_KEY name, UnitGenerator & ugen);
357  /// Plug in a float to the named input slot
358  void addInput(CSL_MAP_KEY name, float value);
359  /// method to read the control values (in case they're dynamic).
360  /// this sends nextBuffer() to the input.
361  void pullInput(Port * thePort, unsigned numFrames) throw (CException);
362  void pullInput(Port * thePort, Buffer & theBuffer) throw (CException);
363 
364  virtual void dump(); ///< pretty-print the receiver's input/controls map
365 };
366 
367 //-------------------------------------------------------------------------------------------------//
368 ///
369 /// Scalable -- mix-in class with scale and offset control inputs (may be constants or generators).
370 /// This uses the mInput map keys CSL_SCALE and CSL_OFFSET.
371 /// Most actual unit generators inherit this as well as UnitGenerator.
372 /// We use Controllable as a virtual superclass so that we can mix it in twice (in classes that are also Phased)
373 ///
374 
375 class Scalable : public virtual Controllable {
376 public:
377  Scalable(); ///< Constructors
378  Scalable(float scale); ///< use the given static scale
379  Scalable(float scale, float offset); ///< use the given static scale & offset
380  Scalable(UnitGenerator & scale, float offset); ///< use the given dynamic scale & static offset
381  Scalable(UnitGenerator & scale, UnitGenerator & offset); ///< use the given dynamic scale & offset
382  ~Scalable(); ///< Destructor
383 
384  // accessors
385  void setScale(UnitGenerator & scale); ///< set the receiver's scale member to a UGen or a float
386  void setScale(float scale);
387 
388  void setOffset(UnitGenerator & offset); ///< set the receiver's offset member to a UGen or a float
389  void setOffset(float offset);
390  virtual void trigger(); ///< trigger passed on here
391  void isScaled(); ///< answer whether scale = 1 & offset = 0
392 
393 };
394 
395 /// Macros for all the Scalable UnitGenerators (note that these don't end with ";")
396 /// Note that these make some assumptions about variable names declared in the method;
397 
398 /// Declare the pointer to scale/offset buffers (if used) and current scale/offset values
399 
400 #define DECLARE_SCALABLE_CONTROLS \
401  Port * scalePort = mInputs[CSL_SCALE]; \
402  Port * offsetPort = mInputs[CSL_OFFSET]; \
403  float scaleValue, offsetValue
404 
405 /// Load the scale/offset-related values at the start
406 
407 #define LOAD_SCALABLE_CONTROLS \
408  Controllable::pullInput(scalePort, numFrames); \
409  scaleValue = scalePort->nextValue(); \
410  Controllable::pullInput(offsetPort, numFrames); \
411  offsetValue = offsetPort->nextValue()
412 
413 // Update the scale/offset-related values in the loop
414 
415 #define UPDATE_SCALABLE_CONTROLS \
416  scaleValue = scalePort->nextValue(); \
417  offsetValue = offsetPort->nextValue()
418 
419 #define CHECK_UPDATE_SCALABLE_CONTROLS \
420  if (scalePort) \
421  scaleValue = scalePort->nextValue(); \
422  if (offsetPort) \
423  offsetValue = offsetPort->nextValue()
424 
425 #define IS_UNSCALED \
426  (scalePort->isFixed()) && (offsetPort->isFixed()) && \
427  (scaleValue == 1.0) && (offsetValue == 0.0)
428 
429 
430 //-------------------------------------------------------------------------------------------------//
431 ///
432 /// Effect -- mix-in for classes that have unit generators as inputs (like filters).
433 /// Note that this always uses a separate buffer for the input.
434 ///
435 
436 class Effect : public UnitGenerator, public virtual Controllable {
437 public:
438  Effect(); ///< Constructors
439  Effect(UnitGenerator & input); ///< use the given input
440 
441  virtual bool isActive(); ///< am I active?
442 
443  void setInput(UnitGenerator & inp); ///< set the receiver's input generator
444 // UnitGenerator *input() { return mInputs[CSL_INPUT]->mUGen; } // no getter for now
445  bool isInline; ///< whether to use input or buffer as source
446  void setInline() { isInline = true; } ///< set the Effect to be inline
447 
448 protected:
449  SampleBuffer mInputPtr; ///< A pointer to my input's data.
450  /// method to read the input value
451  void pullInput(Buffer & outputBuffer) throw (CException);
452  void pullInput(unsigned numFrames) throw (CException);
453  virtual void trigger(); ///< trigger passed on here
454  /// get the input port
455  inline Port * inPort() { return mInputs[CSL_INPUT]; };
456 };
457 
458 //-------------------------------------------------------------------------------------------------//
459 ///
460 /// Phased -- a mix-in for objects with phase accumulators (local float) and frequency controls (an input port).
461 /// This puts an item named CSL_FREQUENCY in the Controllable parent mInputs map.
462 /// We use Controllable as a virtual superclass so that we can mix it in twice (in classes that are also Scalable)
463 ///
464 
465 class Phased : public virtual Controllable {
466 public:
467  Phased(); ///< Constructors; this one is rearely used
468  Phased(float frequency, float phase = 0); ///< use the given dynamic frequency & phase
469  Phased(UnitGenerator & freq, float phase = 0); ///< use the given dynamic or static frequency
470  ~Phased(); ///< Destructor
471 
472  /// Setter accessors
473  void setFrequency(UnitGenerator & frequency); ///< set frequency
474  void setFrequency(float frequency); ///< set frequency
475  void setPhase(float phase) { mPhase = phase; }; ///< set phase
476 
477 protected:
478  sample mPhase; ///< the actual phase accumulator
479 };
480 
481 /// Macros for all the Phased UnitGenerators (note that these don't end with ";")
482 /// These make some assumptions about variable names declared in the method;
483 /// i.e., the number of frames to compute must be named "numFrames."
484 /// Use this: unsigned numFrames = outputBuffer.mNumFrames;
485 
486 /// Declare the frequency port (accessing the mInputs map) and current value.
487 
488 #define DECLARE_PHASED_CONTROLS \
489  Port * freqPort = mInputs[CSL_FREQUENCY]; \
490  float freqValue
491 
492 /// Load the freq-related values at the start of the callback; if the frequency is a dynamic UGen input,
493 /// then pull its value, get the pointer to its buffer, and set the first value,
494 /// otherwise store the constant value
495 
496 #define LOAD_PHASED_CONTROLS \
497  Controllable::pullInput(freqPort, numFrames); \
498  freqValue = freqPort->nextValue()
499 
500 /// Update the freq-related value in the loop
501 
502 #define UPDATE_PHASED_CONTROLS \
503  freqValue = freqPort->nextValue()
504 
505 #define CHECK_UPDATE_PHASED_CONTROLS \
506  if (freqPort) \
507  freqValue = freqPort->nextValue()
508 
509 //-------------------------------------------------------------------------------------------------//
510 ///
511 /// Writeable -- a mix-in for buffers and streams that one can write to
512 ///
513 
514 class Writeable {
515 public: /// write to the receiver
516  virtual void writeBuffer(Buffer& inputBuffer) throw(CException);
517  virtual ~Writeable() { /* ?? */ };
518 
519 protected: /// write to the receiver
520  virtual void writeBuffer(Buffer & inputBuffer, unsigned bufNum) throw(CException);
521 };
522 
523 ///
524 /// Enumeration for seek flags
525 ///
526 
527 #ifdef CSL_ENUMS
528 typedef enum {
532 } SeekPosition;
533 #else
534  #define kPositionStart 0
535  #define kPositionCurrent 1
536  #define kPositionEnd 2
537  typedef int SeekPosition;
538 #endif
539 
540 //-------------------------------------------------------------------------------------------------//
541 ///
542 /// Seekable -- a mix-in for positionable streams
543 ///
544 
545 class Seekable {
546 public:
547  Seekable() : mCurrentFrame(0), mActualFrame(0.0) { } ///< Constructor
548  virtual ~Seekable() { /* ?? */ };
549 
550  unsigned mCurrentFrame; ///< where I currently am in the buffer
551  double mActualFrame; ///< where I actually am in the buffer
552 
553  /// general-purpose seek on a stream
554  virtual unsigned seekTo(int position, SeekPosition whence) throw(CException) = 0;
555  virtual void reset() throw(CException); ///< reset-to-zero
556  virtual unsigned duration() = 0; ///< number of frames in the Seekable
557 };
558 
559 ///
560 /// Cacheable -- a mix-in for caching streams
561 ///
562 
563 class Cacheable {
564 public:
565  Cacheable() : mUseCache(false) { } ; ///< Constructors
566  Cacheable(bool uC) : mUseCache(uC) { };
567 
568  bool mUseCache; ///< whether I'm to cache (vs. compute)
569 };
570 
571 /////////////// Utility UnitGenerator Classes ///////////////////////
572 
573 ///
574 /// A fan-out generator for DSP graphs with loops
575 /// This takes a single input, and provides mNumFanOuts outputs;
576 /// it only calls its input every mNumFanOuts frames.
577 /// This behavior is now standard on UnitGenerators, using their mOutputs UGenVector
578 /// (see UnitGenerators::nextBuffer).
579 ///
580 
581 class FanOut : public Effect {
582 public:
583  FanOut(UnitGenerator & in, unsigned taps); ///< Constructors
584  ~FanOut() { };
585 
586  virtual void nextBuffer(Buffer & outputBuffer) throw(CException);
587  virtual void nextBuffer(Buffer & outputBuffer, unsigned outBufNum) throw(CException);
588 
589 protected:
590  Buffer mBuffer; ///< my temp buffer
591  unsigned mNumFanOuts; ///< the number of outputs
592  unsigned mCurrent; ///< the current output
593 };
594 
595 ///
596 /// Splitter class -- a de-multiplexer for multi-channel signals
597 ///
598 
599 class Splitter : public FanOut {
600 public:
601  Splitter(UnitGenerator & in); ///< Constructor
602  ~Splitter() { };
603 
604  unsigned numChannels() { return 1; }; ///< I'm mono
605  /// nextBuffer processes splitter channels
606  virtual void nextBuffer(Buffer & outputBuffer) throw(CException);
607  virtual void nextBuffer(Buffer & outputBuffer, unsigned outBufNum) throw(CException);
608 };
609 
610 ///
611 /// Joiner class -- a multiplexer for multi-channel signals
612 ///
613 
614 class Joiner : public Effect {
615 public: ///< loop through my vector of inputs
616  Joiner() : Effect() { mNumChannels = 0; }; ///< Constructors
617  Joiner(UnitGenerator & in1, UnitGenerator & in2);
618  ~Joiner() { };
619  /// nextBuffer processes joiner channels
620  virtual void nextBuffer(Buffer & outputBuffer) throw(CException);
621  virtual void nextBuffer(Buffer & outputBuffer, unsigned outBufNum) throw(CException);
622  void addInput(UnitGenerator & in); ///< add the argument to vector of inputs
623  bool isActive();
624  virtual void trigger(); ///< trigger passed on here
625 // unsigned numChannels() { return mNumChannels; };
626 // inline Port * inPort() { return mInputs[CSL_INPUT]; };
627 // virtual unsigned numOutputs() { return mNumOutputs; };
628 
629 protected:
630 // UGenVector mInputs; ///< my vector of inputs
631 };
632 
633 ///
634 /// Interleaver handles copying interleaved sample buffers (like sound files and inter-process sockets)
635 /// to/from non-interleaved CSL-style Buffer objects.
636 ///
637 
638 class Interleaver {
639 
640 public:
641  /// Interleave = copy from CSL-style Buffer object to an interleaved sample vector
642  void interleave(Buffer & output, SampleBuffer samples, unsigned numFrames,
643  unsigned numChannels) throw (CException);
644  void interleave(Buffer & output, short * samples, unsigned numFrames,
645  unsigned numChannels) throw (CException);
646 
647  /// Interleave = copy from CSL-style Buffer object to an interleaved sample vector
648  /// Remap = re-assign channels from the source buffer to the target while interleaving
649  void interleaveAndRemap(Buffer & output, SampleBuffer samples, unsigned numFrames, unsigned numChannels,
650  unsigned *channelMap) throw (CException);
651 
652  /// De-interleave = copy from interleaved SampleBuffer to CSL Buffer object
653  void deinterleave(Buffer & output, SampleBuffer samples, unsigned numFrames,
654  unsigned numChannels) throw (CException);
655  void deinterleave(Buffer & output, short * samples, unsigned numFrames,
656  unsigned numChannels) throw (CException);
657 };
658 
659 //-------------------------------------------------------------------------------------------------//
660 ///// Support for the IO classes
661 
662 #ifndef CSL_WINDOWS // on "normal" platforms
663 
664 #ifdef DO_TIMING // Here are the macros and globals for the timing code
665 #include <sys/time.h>
666 #define GET_TIME(val) if (gettimeofday(val, 0) != 0) logMsg(kLogError, "Output: Error reading current time");
667 #define SUB_TIMES(t1, t2) (((t1->tv_sec - t2->tv_sec) * 1000000) + (t1->tv_usec - t2->tv_usec))
668 #endif
669 
670 #else // If on Windows
671 
672 #ifdef DO_TIMING // Here are the macros and globals for the timing code
673 #include <Winsock2.h>
674 #include <winsock.h>
675 #include <time.h>
676 int getSysTime(timeval *val, void * e);
677 #define GET_TIME(val) if (getSysTime(val, 0) != 0) logMsg(kLogError, "Output: Error reading current time");
678 #define SUB_TIMES(t1, t2) (((t1->tv_sec - t2->tv_sec) * 1000000) + (t1->tv_usec - t2->tv_usec))
679 #endif
680 
681 #endif // Windows
682 
683 /// IO Status flag
684 
685 #ifdef CSL_ENUMS
686 typedef enum {
693 } IO_Status;
694 #else
695  #define kIONew 0
696  #define kIOInit 1
697  #define kIOOpen 2
698  #define kIORunning 3
699  #define kIOClosed 4
700  #define kIOExit 5
701  typedef int IO_Status;
702 #endif
703 
704 //-------------------------------------------------------------------------------------------------//
705 ///
706 /// IO -- the abstract I/O scheduling class; subclasses interface to specific I/O APIs.
707 /// An IO object has a graph (a ptr to a UGen), and it registers itself with some call-back API
708 /// (like PortAudio, CoreAudio, Jack, VST, JUCE), setting up a callback function that in turn
709 /// calls the nextBuffer() method of its graph root.
710 /// One creates an IO with the desired rate, block size (optional) I/O device keys, and the number of
711 /// in and out channels; you then set its root to be your DSP graph and send it start/stop messages.
712 ///
713 /// All this is public because it's used by static call-back functions.
714 ///
715 
716 class IO : public Model { /// superclass = Model
717 public:
718  IO(unsigned s_rate = 44100, unsigned b_size = CSL_mBlockSize,
719  int in_device = -1, int out_device = -1,
720  unsigned in_chans = 2, unsigned out_chans = 2); /// default is stereo input & output
721  virtual ~IO() { };
722  // Control methods
723  virtual void open() throw(CException) { mStatus = kIOOpen; }; ///< open/close start/stop methods
724  virtual void close() throw(CException) { mStatus = kIOClosed; };
725  virtual void start() throw(CException) { mStatus = kIORunning; };
726  virtual void stop() throw(CException) { mStatus = kIOOpen; };
727  virtual void test() throw(CException) { }; ///< test the IO's graph
728 
729  void setRoot(UnitGenerator & root); /// set/clear my graph root generator
730  void clearRoot();
731  /// get a buffer from the CSL graph
732  void pullInput(Buffer & outBuffer, SampleBuffer out = 0) throw(CException);
733 
734  virtual Buffer & getInput() throw(CException); ///< Get the current input from the sound card
735  virtual Buffer & getInput(unsigned numFrames, unsigned numChannels) throw(CException);
736  unsigned getAndIncrementSequence(); ///< increment and answer my seq #
737 
738  // Data members
739  UnitGenerator * mGraph; ///< the root of my client DSP graph, often a mixer or panner
740  Buffer mInputBuffer; ///< the most recent input buffer (if it's turned on)
741  Buffer mOutputBuffer; ///< the output buffer I use (passed to nextBuffer calls)
742  SampleBuffer mInputPointer; ///< the buffer for holding the sound card input (if open)
743  unsigned * mChannelMap; ///< the output channel remapping array
744 
745  unsigned mNumFramesPlayed; ///< counter of frames I've played
746  unsigned mSequence; ///< sequence counter
747  unsigned mLoggingPeriod; ///< logging period in seconds
748  unsigned mNumInChannels; ///< # inputs
749  unsigned mNumOutChannels; ///< # outputs
750  unsigned mNumRealInChannels; ///< # physical inputs
751  unsigned mNumRealOutChannels; ///< # physical outputs
752  IO_Status mStatus; ///< status flag
753  bool mInterleaved; ///< flag if IO is interleaved
754 
755 #ifdef DO_TIMING // This is for the performance timing code
756  struct timeval mThen, mNow; ///< used for getting the real time
757  long mTimeVals, mThisSec, mTimeSum; ///< for printing run-time statistics
758  float mUsage; ///< cpu usage %
759  /// print the CPU usage message
760  void printTimeStatistics(struct timeval * tthen, struct timeval * tnow, long * tsecond,
761  long * ttimeSum, long * ttimeVals);
762 #endif
763 
764 protected: /// initialize overridden in subclasses
765  virtual void initialize(unsigned sr, unsigned bs, int is, int os, unsigned ic, unsigned oc) { };
766 };
767 
768 //-------------------------------------------------------------------------------------------------//
769 ///
770 /// IO Device class -- a holder for a sound interface with name, id, # IO channels, etc.
771 ///
772 
773 class IODevice {
774 public:
775  IODevice() { } ; /// Constructor takes all variables, calls initialize()
776  IODevice(char * name, unsigned index, unsigned maxIn, unsigned maxOut, bool isIn, bool isOut);
777  IODevice(string name, unsigned index, unsigned maxIn, unsigned maxOut, bool isIn, bool isOut);
778  /// public members
779  char mName[CSL_NAME_LEN]; ///< my device name
780  unsigned mIndex; ///< index (API-specific)
781  unsigned mMaxInputChannels; ///< # HW ins
782  unsigned mMaxOutputChannels; ///<# HW outs
783  float mFrameRate; ///< current SR
784  vector<float> mFrameRates; ///< the vector of frame rates I support
785  bool mIsDefaultIn; ///< am i the default in?
786  bool mIsDefaultOut; ///< am i the default out?
787  void dump(); ///< pretty-print the receiver' device
788 };
789 
790 } // end of namespace
791 
792 #endif // CSL_CORE_H