Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

254 lines
7.1KB

  1. /***************************************************/
  2. /*! \class FileWvIn
  3. \brief STK audio file input class.
  4. This class inherits from WvIn. It provides a "tick-level"
  5. interface to the FileRead class. It also provides variable-rate
  6. playback functionality. Audio file support is provided by the
  7. FileRead class. Linear interpolation is used for fractional read
  8. rates.
  9. FileWvIn supports multi-channel data. It is important to
  10. distinguish the tick() method that computes a single frame (and
  11. returns only the specified sample of a multi-channel frame) from
  12. the overloaded one that takes an StkFrames object for
  13. multi-channel and/or multi-frame data.
  14. FileWvIn will either load the entire content of an audio file into
  15. local memory or incrementally read file data from disk in chunks.
  16. This behavior is controlled by the optional constructor arguments
  17. \e chunkThreshold and \e chunkSize. File sizes greater than \e
  18. chunkThreshold (in sample frames) will be read incrementally in
  19. chunks of \e chunkSize each (also in sample frames).
  20. When the file end is reached, subsequent calls to the tick()
  21. functions return zeros and isFinished() returns \e true.
  22. See the FileRead class for a description of the supported audio
  23. file formats.
  24. by Perry R. Cook and Gary P. Scavone, 1995-2011.
  25. */
  26. /***************************************************/
  27. #include "FileWvIn.h"
  28. #include <cmath>
  29. namespace stk {
  30. FileWvIn :: FileWvIn( unsigned long chunkThreshold, unsigned long chunkSize )
  31. : finished_(true), interpolate_(false), time_(0.0), rate_(0.0),
  32. chunkThreshold_(chunkThreshold), chunkSize_(chunkSize)
  33. {
  34. Stk::addSampleRateAlert( this );
  35. }
  36. FileWvIn :: FileWvIn( std::string fileName, bool raw, bool doNormalize,
  37. unsigned long chunkThreshold, unsigned long chunkSize )
  38. : finished_(true), interpolate_(false), time_(0.0), rate_(0.0),
  39. chunkThreshold_(chunkThreshold), chunkSize_(chunkSize)
  40. {
  41. openFile( fileName, raw, doNormalize );
  42. Stk::addSampleRateAlert( this );
  43. }
  44. FileWvIn :: ~FileWvIn()
  45. {
  46. this->closeFile();
  47. Stk::removeSampleRateAlert( this );
  48. }
  49. void FileWvIn :: sampleRateChanged( StkFloat newRate, StkFloat oldRate )
  50. {
  51. if ( !ignoreSampleRateChange_ )
  52. this->setRate( oldRate * rate_ / newRate );
  53. }
  54. void FileWvIn :: closeFile( void )
  55. {
  56. if ( file_.isOpen() ) file_.close();
  57. finished_ = true;
  58. lastFrame_.resize( 0, 0 );
  59. }
  60. void FileWvIn :: openFile( std::string fileName, bool raw, bool doNormalize )
  61. {
  62. // Call close() in case another file is already open.
  63. this->closeFile();
  64. // Attempt to open the file ... an error might be thrown here.
  65. file_.open( fileName, raw );
  66. // Determine whether chunking or not.
  67. if ( file_.fileSize() > chunkThreshold_ ) {
  68. chunking_ = true;
  69. chunkPointer_ = 0;
  70. data_.resize( chunkSize_, file_.channels() );
  71. if ( doNormalize ) normalizing_ = true;
  72. else normalizing_ = false;
  73. }
  74. else {
  75. chunking_ = false;
  76. data_.resize( (size_t) file_.fileSize(), file_.channels() );
  77. }
  78. // Load all or part of the data.
  79. file_.read( data_, 0, doNormalize );
  80. // Resize our lastFrame container.
  81. lastFrame_.resize( 1, file_.channels() );
  82. // Set default rate based on file sampling rate.
  83. this->setRate( data_.dataRate() / Stk::sampleRate() );
  84. if ( doNormalize & !chunking_ ) this->normalize();
  85. this->reset();
  86. }
  87. void FileWvIn :: reset(void)
  88. {
  89. time_ = (StkFloat) 0.0;
  90. for ( unsigned int i=0; i<lastFrame_.size(); i++ ) lastFrame_[i] = 0.0;
  91. finished_ = false;
  92. }
  93. void FileWvIn :: normalize(void)
  94. {
  95. this->normalize( 1.0 );
  96. }
  97. // Normalize all channels equally by the greatest magnitude in all of the data.
  98. void FileWvIn :: normalize( StkFloat peak )
  99. {
  100. // When chunking, the "normalization" scaling is performed by FileRead.
  101. if ( chunking_ ) return;
  102. size_t i;
  103. StkFloat max = 0.0;
  104. for ( i=0; i<data_.size(); i++ ) {
  105. if ( fabs( data_[i] ) > max )
  106. max = (StkFloat) fabs((double) data_[i]);
  107. }
  108. if ( max > 0.0 ) {
  109. max = 1.0 / max;
  110. max *= peak;
  111. for ( i=0; i<data_.size(); i++ )
  112. data_[i] *= max;
  113. }
  114. }
  115. void FileWvIn :: setRate( StkFloat rate )
  116. {
  117. rate_ = rate;
  118. // If negative rate and at beginning of sound, move pointer to end
  119. // of sound.
  120. if ( (rate_ < 0) && (time_ == 0.0) ) time_ = file_.fileSize() - 1.0;
  121. if ( fmod( rate_, 1.0 ) != 0.0 ) interpolate_ = true;
  122. else interpolate_ = false;
  123. }
  124. void FileWvIn :: addTime( StkFloat time )
  125. {
  126. // Add an absolute time in samples
  127. time_ += time;
  128. if ( time_ < 0.0 ) time_ = 0.0;
  129. if ( time_ > file_.fileSize() - 1.0 ) {
  130. time_ = file_.fileSize() - 1.0;
  131. for ( unsigned int i=0; i<lastFrame_.size(); i++ ) lastFrame_[i] = 0.0;
  132. finished_ = true;
  133. }
  134. }
  135. StkFloat FileWvIn :: tick( unsigned int channel )
  136. {
  137. #if defined(_STK_DEBUG_)
  138. if ( channel >= data_.channels() ) {
  139. oStream_ << "FileWvIn::tick(): channel argument and soundfile data are incompatible!";
  140. handleError( StkError::FUNCTION_ARGUMENT );
  141. }
  142. #endif
  143. if ( finished_ ) return 0.0;
  144. if ( time_ < 0.0 || time_ > (StkFloat) ( file_.fileSize() - 1.0 ) ) {
  145. for ( unsigned int i=0; i<lastFrame_.size(); i++ ) lastFrame_[i] = 0.0;
  146. finished_ = true;
  147. return 0.0;
  148. }
  149. StkFloat tyme = time_;
  150. if ( chunking_ ) {
  151. // Check the time address vs. our current buffer limits.
  152. if ( ( time_ < (StkFloat) chunkPointer_ ) ||
  153. ( time_ > (StkFloat) ( chunkPointer_ + chunkSize_ - 1 ) ) ) {
  154. while ( time_ < (StkFloat) chunkPointer_ ) { // negative rate
  155. chunkPointer_ -= chunkSize_ - 1; // overlap chunks by one frame
  156. if ( chunkPointer_ < 0 ) chunkPointer_ = 0;
  157. }
  158. while ( time_ > (StkFloat) ( chunkPointer_ + chunkSize_ - 1 ) ) { // positive rate
  159. chunkPointer_ += chunkSize_ - 1; // overlap chunks by one frame
  160. if ( chunkPointer_ + chunkSize_ > file_.fileSize() ) // at end of file
  161. chunkPointer_ = file_.fileSize() - chunkSize_;
  162. }
  163. // Load more data.
  164. file_.read( data_, chunkPointer_, normalizing_ );
  165. }
  166. // Adjust index for the current buffer.
  167. tyme -= chunkPointer_;
  168. }
  169. if ( interpolate_ ) {
  170. for ( unsigned int i=0; i<lastFrame_.size(); i++ )
  171. lastFrame_[i] = data_.interpolate( tyme, i );
  172. }
  173. else {
  174. for ( unsigned int i=0; i<lastFrame_.size(); i++ )
  175. lastFrame_[i] = data_( (size_t) tyme, i );
  176. }
  177. // Increment time, which can be negative.
  178. time_ += rate_;
  179. return lastFrame_[channel];
  180. }
  181. StkFrames& FileWvIn :: tick( StkFrames& frames )
  182. {
  183. if ( !file_.isOpen() ) {
  184. #if defined(_STK_DEBUG_)
  185. oStream_ << "FileWvIn::tick(): no file data is loaded!";
  186. handleError( StkError::DEBUG_PRINT );
  187. #endif
  188. return frames;
  189. }
  190. unsigned int nChannels = lastFrame_.channels();
  191. #if defined(_STK_DEBUG_)
  192. if ( nChannels != frames.channels() ) {
  193. oStream_ << "FileWvIn::tick(): StkFrames argument is incompatible with file data!";
  194. handleError( StkError::FUNCTION_ARGUMENT );
  195. }
  196. #endif
  197. unsigned int j, counter = 0;
  198. for ( unsigned int i=0; i<frames.frames(); i++ ) {
  199. this->tick();
  200. for ( j=0; j<nChannels; j++ )
  201. frames[counter++] = lastFrame_[j];
  202. }
  203. return frames;
  204. }
  205. } // stk namespace