1 module stk.stk;
2 
3 public import std.math : sin, cos;
4 
5 /**
6 
7 STK instrument abstract base class.
8 
9 This class provides a common interface for
10 all STK instruments.
11 
12 Porting portions of STK library to D.
13 https://github.com/thestk/stk by Perry R. Cook and Gary P. Scavone, 1995--2016.
14 
15 */
16 
17 alias StkFloat = double;
18 
19 enum StkFormat : uint {
20     STK_SINT8 = 0x01,   /*!< -128 to +127 */
21     STK_SINT16 = 0x02,  /*!< -32768 to +32767 */
22     STK_SINT24 = 0x04,  /*!< Lower 3 bytes of 32-bit signed integer. */
23     STK_SINT32 = 0x08,  /*!< -2147483648 to +2147483647. */
24     STK_FLOAT32 = 0x10, /*!< Normalized between plus/minus 1.0. */
25     STK_FLOAT64 = 0x20  /*!< Normalized between plus/minus 1.0. */
26 }
27 
28 const StkFloat PI           = 3.14159265358979;
29 const StkFloat TWO_PI       = 2 * PI;
30 const StkFloat ONE_OVER_128 = 0.0078125;
31 
32 // use StkErrorType instead of StkError::Type
33 enum StkErrorType {
34     STATUS,
35     WARNING,
36     DEBUG_PRINT,
37     MEMORY_ALLOCATION,
38     MEMORY_ACCESS,
39     FUNCTION_ARGUMENT,
40     FILE_NOT_FOUND,
41     FILE_UNKNOWN_FORMAT,
42     FILE_ERROR,
43     PROCESS_THREAD,
44     PROCESS_SOCKET,
45     PROCESS_SOCKET_IPADDR,
46     AUDIO_SYSTEM,
47     MIDI_SYSTEM,
48     UNSPECIFIED
49 }
50 
51 //! STK error handling class.
52 /*!
53 This is a fairly abstract exception handling class.  There could
54 be sub-classes to take care of more specific error conditions ... or
55 not.
56 */
57 class StkError
58 {
59 
60 protected:
61     string message_;
62     StkErrorType type_;
63 
64 public:
65     //! The constructor.
66     this(string message, StkErrorType type = StkErrorType.UNSPECIFIED) {
67         message_ = message;
68         type_ = type;
69     }
70 
71     //! The destructor.
72     ~this() {};
73 
74     //! Prints thrown error message to stderr.
75     void printMessage() { 
76         //std::cerr << '\n' << message_ << "\n\n"; 
77     }
78 
79     //! Returns the thrown error message type.
80     StkErrorType getType() const { return type_; }
81 
82     //! Returns the thrown error message string.
83     string getMessage() const { return message_; }
84 
85     //! Returns the thrown error message as a C string.
86     const(char *)getMessageCString() {
87         import std..string : toStringz;
88         return message_.toStringz; 
89     }
90 };
91 
92 class Stk
93 {
94     //! Static method that returns the current STK sample rate.
95     static StkFloat sampleRate() {
96         return srate_;
97     }
98 
99     //! Static method that sets the STK sample rate.
100     /*!
101     The sample rate set using this method is queried by all STK
102     classes that depend on its value.  It is initialized to the
103     default SRATE set in Stk.h.  Many STK classes use the sample rate
104     during instantiation.  Therefore, if you wish to use a rate that
105     is different from the default rate, it is imperative that it be
106     set \e BEFORE STK objects are instantiated.  A few classes that
107     make use of the global STK sample rate are automatically notified
108     when the rate changes so that internal class data can be
109     appropriately updated.  However, this has not been fully
110     implemented.  Specifically, classes that appropriately update
111     their own data when either a setFrequency() or noteOn() function
112     is called do not currently receive the automatic notification of
113     rate change.  If the user wants a specific class instance to
114     ignore such notifications, perhaps in a multi-rate context, the
115     function Stk::ignoreSampleRateChange() should be called.
116     */
117     void setSampleRate( StkFloat rate ) {
118         if ( rate > 0.0 && rate != srate_ ) {
119             StkFloat oldRate = srate_;
120             srate_ = rate;
121 
122             for ( uint i=0; i<alertList_.length; i++ )
123                 alertList_[i].sampleRateChanged(srate_, oldRate);
124         }
125     }
126 
127     //! A function to enable/disable the automatic updating of class data when the STK sample rate changes.
128     /*!
129     This function allows the user to enable or disable class data
130     updates in response to global sample rate changes on a class by
131     class basis.
132     */
133     void ignoreSampleRateChange( bool ignore = true ) { ignoreSampleRateChange_ = ignore; };
134 
135     //! Static method that frees memory from alertList_.
136     static void  clear_alertList(){
137         alertList_.length = 0;
138     }
139 
140     //! Static method that returns the current rawwave path.
141     static string rawwavePath() { 
142         return rawwavepath_;
143     }
144 
145     //! Static method that sets the STK rawwave path.
146     static void setRawwavePath( string path ) {
147         if (path.length)
148             rawwavepath_ = path;
149 
150         // Make sure the path includes a "/"
151         if ( rawwavepath_[$ - 1] != '/' )
152             rawwavepath_ ~= "/";
153     }
154 
155     //! Static method that byte-swaps a 16-bit data type.
156     static void swap16( ubyte *ptr ) {
157         ubyte val;
158         // Swap 1st and 2nd bytes
159         val = *(ptr);
160         *(ptr) = *(ptr+1);
161         *(ptr+1) = val;
162     }
163 
164     //! Static method that byte-swaps a 32-bit data type.
165     static void swap32( ubyte *ptr ) {
166         ubyte val;
167 
168         // Swap 1st and 4th bytes
169         val = *(ptr);
170         *(ptr) = *(ptr+3);
171         *(ptr+3) = val;
172 
173         //Swap 2nd and 3rd bytes
174         ptr += 1;
175         val = *(ptr);
176         *(ptr) = *(ptr+1);
177         *(ptr+1) = val;
178     }
179 
180     //! Static method that byte-swaps a 64-bit data type.
181     static void swap64( ubyte *ptr ) {
182         ubyte val;
183 
184         // Swap 1st and 8th bytes
185         val = *(ptr);
186         *(ptr) = *(ptr+7);
187         *(ptr+7) = val;
188 
189         // Swap 2nd and 7th bytes
190         ptr += 1;
191         val = *(ptr);
192         *(ptr) = *(ptr+5);
193         *(ptr+5) = val;
194 
195         // Swap 3rd and 6th bytes
196         ptr += 1;
197         val = *(ptr);
198         *(ptr) = *(ptr+3);
199         *(ptr+3) = val;
200 
201         // Swap 4th and 5th bytes
202         ptr += 1;
203         val = *(ptr);
204         *(ptr) = *(ptr+1);
205         *(ptr+1) = val;
206     }
207 
208     //! Static cross-platform method to sleep for a number of milliseconds.
209     static void sleep( uint milliseconds ) {
210         import core.thread;
211         Thread.sleep(dur!"msecs"(milliseconds));
212     }
213 
214     //! Static method to check whether a value is within a specified range.
215     static bool inRange( StkFloat value, StkFloat min, StkFloat max ) {
216         if ( value < min ) return false;
217         else if ( value > max ) return false;
218         else return true;
219     }
220 
221     //! Static function for error reporting and handling using c-strings.
222     static void handleError( const char *message, StkErrorType type ) {
223         // TODO
224     }
225 
226     //! Static function for error reporting and handling using c++ strings.
227     static void handleError( string message, StkErrorType type ) {
228         // TODO
229     }
230 
231 
232     //! Toggle display of WARNING and STATUS messages.
233     static void showWarnings( bool status ) { showWarnings_ = status; }
234 
235     //! Toggle display of error messages before throwing exceptions.
236     static void printErrors( bool status ) { printErrors_ = status; }
237 
238 private:
239     static StkFloat srate_ = 44100;
240     static string rawwavepath_ = "rawwaves/";
241     static bool showWarnings_ = true;
242     static bool printErrors_ = true;
243     static Stk[] alertList_;
244 
245 protected:
246 
247     //static std::ostringstream oStream_;
248     bool ignoreSampleRateChange_ = false;
249 
250     //! Default constructor.
251     this() {
252     }
253 
254     //! Class destructor.
255     ~this() {
256     }
257 
258     //! This function should be implemented in subclasses that depend on the sample rate.
259     void sampleRateChanged( StkFloat newRate, StkFloat oldRate ) {
260         // This function should be reimplemented in classes that need to
261         // make internal variable adjustments in response to a global sample
262         // rate change.
263     }
264 
265     //! Add class pointer to list for sample rate change notification.
266     public static void addSampleRateAlert( Stk ptr ) {
267         for ( uint i=0; i<alertList_.length; i++ )
268             if ( alertList_[i] is ptr ) return;
269 
270         alertList_ ~= ptr;
271     }
272 
273     //! Remove class pointer from list for sample rate change notification.
274     public static void removeSampleRateAlert( Stk ptr ) {
275         for ( uint i=0; i < alertList_.length; i++ ) {
276             if ( alertList_[i] is ptr ) {
277                 for (uint j = i; j + 1 < alertList_.length; j++)
278                     alertList_[j] = alertList_[j + 1];
279                 alertList_.length = alertList_.length - 1;
280                 return;
281             }
282         }
283     }
284 
285     //! Internal function for error reporting that assumes message in \c oStream_ variable.
286     void handleError( StkErrorType type ) const {
287     }
288 }
289 
290 /***************************************************/
291 /*! \class StkFrames
292 \brief An STK class to handle vectorized audio data.
293 
294 This class can hold single- or multi-channel audio data.  The data
295 type is always StkFloat and the channel format is always
296 interleaved.  In an effort to maintain efficiency, no
297 out-of-bounds checks are performed in this class unless
298 _STK_DEBUG_ is defined.
299 
300 Internally, the data is stored in a one-dimensional C array.  An
301 indexing operator is available to set and retrieve data values.
302 Alternately, one can use pointers to access the data, using the
303 index operator to get an address for a particular location in the
304 data:
305 
306 StkFloat* ptr = &myStkFrames[0];
307 
308 Note that this class can also be used as a table with interpolating
309 lookup.
310 
311 Possible future improvements in this class could include functions
312 to convert to and return other data types.
313 
314 by Perry R. Cook and Gary P. Scavone, 1995--2016.
315 */
316 /***************************************************/
317 
318 struct StkFrames
319 {
320 public:
321 
322     //! Overloaded constructor that initializes the frame data to the specified size with \c value.
323     this(StkFloat value, uint nFrames, uint nChannels) {
324         uint size_ = nFrames_ * nChannels_;
325 
326         if ( size_ > 0 ) {
327             data_.length = size_;
328             data_[0 .. $] = value;
329         }
330         dataRate_ = Stk.sampleRate();
331     }
332 
333     //! The destructor.
334     ~this() {
335     }
336 
337     // A copy constructor.
338     this( const ref StkFrames f ) {
339         nFrames_ = f.nFrames_;
340         nChannels_ = f.nChannels_;
341         data_ = f.data_.dup;
342         dataRate_ = Stk.sampleRate();
343     }
344 
345     // Assignment operator that returns a reference to self.
346     ref StkFrames opAssign( const ref StkFrames f ) {
347         nFrames_ = f.nFrames_;
348         nChannels_ = f.nChannels_;
349         data_ = f.data_.dup;
350         dataRate_ = Stk.sampleRate();
351         return this;
352     }
353 
354     //! Subscript operator that returns a reference to element \c n of self.
355     /*!
356     The result can be used as an lvalue. This reference is valid
357     until the resize function is called or the array is destroyed. The
358     index \c n must be between 0 and size less one.  No range checking
359     is performed unless _STK_DEBUG_ is defined.
360     */
361     ref StkFloat opIndex( size_t n ) {
362         assert(n < data_.length);
363         return data_[n];
364     }
365 
366     //! Subscript operator that returns the value at element \c n of self.
367     /*!
368     The index \c n must be between 0 and size less one.  No range
369     checking is performed unless _STK_DEBUG_ is defined.
370     */
371     StkFloat opIndex( size_t n ) const {
372         assert(n < data_.length);
373         return data_[n];
374     }
375 
376     //! Sum operator
377     /*!
378     The dimensions of the argument are expected to be the same as
379     self.  No range checking is performed unless _STK_DEBUG_ is
380     defined.
381     */
382     StkFrames opBinary(string op)(const ref StkFrames frames) const if (op == "+") {
383         assert(nFrames_ == frames.nFrames_);
384         assert(nChannels_ == frames.nChannels_);
385         StkFrames sum = new StkFrames(cast(uint)nFrames_,nChannels_);
386         for(uint i = 0; i < _size; i++)
387             sum.data_.ptr[i] = data_.ptr[i] + frames.data_.ptr[i];
388         return sum;
389     }
390 
391     //! Assignment by sum operator into self.
392     /*!
393     The dimensions of the argument are expected to be the same as
394     self.  No range checking is performed unless _STK_DEBUG_ is
395     defined.
396     */
397     void opOpAssign(string op)(ref StkFrames f) if (op == "+") {
398         assert(nFrames_ == f.nFrames_);
399         assert(nChannels_ == f.nChannels_);
400         for(uint i = 0; i < _size; i++)
401             data_[i] += f.data_[i];
402     }
403 
404     //! Assignment by product operator into self.
405     /*!
406     The dimensions of the argument are expected to be the same as
407     self.  No range checking is performed unless _STK_DEBUG_ is
408     defined.
409     */
410     void opOpAssign(string op)( ref StkFrames f ) if (op == "*") {
411         for(uint i = 0; i < _size; i++)
412             data_[i] *= f.data_[i];
413     }
414 
415     //! Channel / frame subscript operator that returns a reference.
416     /*!
417     The result can be used as an lvalue. This reference is valid
418     until the resize function is called or the array is destroyed. The
419     \c frame index must be between 0 and frames() - 1.  The \c channel
420     index must be between 0 and channels() - 1.  No range checking is
421     performed unless _STK_DEBUG_ is defined.
422     */
423     ref StkFloat opCall(size_t frame, uint channel) {
424         return data_[ frame * nChannels_ + channel ];
425     }
426     ref StkFloat opIndex(size_t frame, uint channel) {
427         return data_[ frame * nChannels_ + channel ];
428     }
429 
430     //! Channel / frame subscript operator that returns a value.
431     /*!
432     The \c frame index must be between 0 and frames() - 1.  The \c
433     channel index must be between 0 and channels() - 1.  No range checking
434     is performed unless _STK_DEBUG_ is defined.
435     */
436     StkFloat opCall( size_t frame, uint channel ) const {
437         return data_[ frame * nChannels_ + channel ];
438     }
439     StkFloat opIndex( size_t frame, uint channel ) const {
440         return data_[ frame * nChannels_ + channel ];
441     }
442 
443     //! Return an interpolated value at the fractional frame index and channel.
444     /*!
445     This function performs linear interpolation.  The \c frame
446     index must be between 0.0 and frames() - 1.  The \c channel index
447     must be between 0 and channels() - 1.  No range checking is
448     performed unless _STK_DEBUG_ is defined.
449     */
450     StkFloat interpolate( StkFloat frame, uint channel = 0 ) const {
451         size_t iIndex = cast(size_t)frame;                    // integer part of index
452         StkFloat output;
453         StkFloat alpha = frame - cast(StkFloat)iIndex;  // fractional part of index
454         iIndex = iIndex * nChannels_ + channel;
455         output = data_[ iIndex ];
456         if ( alpha > 0.0 ) {
457             uint nindex = cast(uint)(iIndex + nChannels_);
458             if (nindex >= data_.length)
459                 nindex -= cast(uint)data_.length;
460             output += ( alpha * ( data_[ nindex ] - output ) );
461         }
462 
463         return output;
464     }
465 
466     //! Returns the total number of audio samples represented by the object.
467     @property uint size() const { return cast(uint)data_.length; }
468 
469     //! Returns \e true if the object size is zero and \e false otherwise.
470     @property bool empty() const {
471         return data_.length == 0;
472     }
473 
474     //! Resize self to represent the specified number of channels and frames.
475     /*!
476     Changes the size of self based on the number of frames and
477     channels.  No element assignment is performed.  No memory
478     deallocation occurs if the new size is smaller than the previous
479     size.  Further, no new memory is allocated when the new size is
480     smaller or equal to a previously allocated size.
481     */
482     void resize( size_t nFrames, uint nChannels = 1 ) {
483         uint size_ = nFrames_ * nChannels_;
484 
485         if ( size_ > 0 ) {
486             data_.length = size_;
487             data_[0 .. $] = 0;
488         } else {
489             data_ = null;
490         }
491         dataRate_ = Stk.sampleRate();
492     }
493 
494     //! Resize self to represent the specified number of channels and frames and perform element initialization.
495     /*!
496     Changes the size of self based on the number of frames and
497     channels, and assigns \c value to every element.  No memory
498     deallocation occurs if the new size is smaller than the previous
499     size.  Further, no new memory is allocated when the new size is
500     smaller or equal to a previously allocated size.
501     */
502     void resize(size_t nFrames, uint nChannels, StkFloat value) {
503         uint size_ = nFrames_ * nChannels_;
504 
505         if ( size_ > 0 ) {
506             data_.length = size_;
507             data_[0 .. $] = value;
508         } else {
509             data_ = null;
510         }
511         dataRate_ = Stk.sampleRate();
512     }
513 
514     //! Retrieves a single channel
515     /*!
516     Copies the specified \c channel into \c destinationFrames's \c destinationChannel. \c destinationChannel must be between 0 and destination.channels() - 1 and
517     \c channel must be between 0 and channels() - 1. destination.frames() must be >= frames().
518     No range checking is performed unless _STK_DEBUG_ is defined.
519     */
520     ref StkFrames getChannel(uint sourceChannel, ref StkFrames destinationFrames, uint destinationChannel) const {
521         uint sourceHop = nChannels_;
522         uint destinationHop = destinationFrames.nChannels_;
523         for (int i = sourceChannel, j = destinationChannel; i < nFrames_ * nChannels_; i+=sourceHop,j+=destinationHop) {
524             destinationFrames[j] = data_[i];
525         }
526         return destinationFrames;
527     }
528 
529     //! Sets a single channel
530     /*!
531     Copies the \c sourceChannel of \c sourceFrames into the \c channel of self.
532     SourceFrames.frames() must be equal to frames().
533     No range checking is performed unless _STK_DEBUG_ is defined.
534     */
535     void setChannel(uint destinationChannel, const ref StkFrames sourceFrames, uint sourceChannel) {
536         uint sourceHop = sourceFrames.nChannels_;
537         uint destinationHop = nChannels_;
538         for (int i  = destinationChannel,j = sourceChannel ; i < nFrames_ * nChannels_; i+=destinationHop,j+=sourceHop) {
539             data_[i] = sourceFrames[j];
540         }
541     }
542 
543     //! Return the number of channels represented by the data.
544     @property uint channels() const { return nChannels_; }
545 
546     //! Return the number of sample frames represented by the data.
547     @property uint frames() const { return cast(uint)nFrames_; };
548 
549     //! Set the sample rate associated with the StkFrames data.
550     /*!
551     By default, this value is set equal to the current STK sample
552     rate at the time of instantiation.
553     */
554     void setDataRate( StkFloat rate ) { dataRate_ = rate; }
555 
556     //! Return the sample rate associated with the StkFrames data.
557     /*!
558     By default, this value is set equal to the current STK sample
559     rate at the time of instantiation.
560     */
561     @property StkFloat dataRate() const { return dataRate_; }
562 
563     void fill(StkFloat value) {
564         foreach(ref f; data_)
565             f = value;
566     }
567 
568 private:
569     StkFloat[] data_;
570     StkFloat dataRate_;
571     uint nFrames_;
572     uint nChannels_;
573 }