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 }