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.

238 lines
6.6KB

  1. /***************************************************/
  2. /*! \class FileLoop
  3. \brief STK file looping / oscillator class.
  4. This class provides audio file looping functionality. Any audio
  5. file that can be loaded by FileRead can be looped using this
  6. class.
  7. FileLoop supports multi-channel data. It is important to
  8. distinguish the tick() method that computes a single frame (and
  9. returns only the specified sample of a multi-channel frame) from
  10. the overloaded one that takes an StkFrames object for
  11. multi-channel and/or multi-frame data.
  12. by Perry R. Cook and Gary P. Scavone, 1995--2017.
  13. */
  14. /***************************************************/
  15. #include "FileLoop.h"
  16. #include <cmath>
  17. namespace stk {
  18. FileLoop :: FileLoop( unsigned long chunkThreshold, unsigned long chunkSize )
  19. : FileWvIn( chunkThreshold, chunkSize ), phaseOffset_(0.0)
  20. {
  21. Stk::addSampleRateAlert( this );
  22. }
  23. FileLoop :: FileLoop( std::string fileName, bool raw, bool doNormalize,
  24. unsigned long chunkThreshold, unsigned long chunkSize,
  25. bool doInt2FloatScaling )
  26. : FileWvIn( chunkThreshold, chunkSize ), phaseOffset_(0.0)
  27. {
  28. this->openFile( fileName, raw, doNormalize, doInt2FloatScaling );
  29. Stk::addSampleRateAlert( this );
  30. }
  31. FileLoop :: ~FileLoop( void )
  32. {
  33. Stk::removeSampleRateAlert( this );
  34. }
  35. void FileLoop :: openFile( std::string fileName, bool raw, bool doNormalize, bool doInt2FloatScaling )
  36. {
  37. // Call close() in case another file is already open.
  38. this->closeFile();
  39. // Attempt to open the file ... an error might be thrown here.
  40. file_.open( fileName, raw );
  41. // Determine whether chunking or not.
  42. if ( file_.fileSize() > chunkThreshold_ ) {
  43. chunking_ = true;
  44. chunkPointer_ = 0;
  45. data_.resize( chunkSize_ + 1, file_.channels() );
  46. }
  47. else {
  48. chunking_ = false;
  49. data_.resize( file_.fileSize() + 1, file_.channels() );
  50. }
  51. if ( doInt2FloatScaling )
  52. int2floatscaling_ = true;
  53. else
  54. int2floatscaling_ = false;
  55. // Load all or part of the data.
  56. file_.read( data_, 0, int2floatscaling_ );
  57. if ( chunking_ ) { // If chunking, save the first sample frame for later.
  58. firstFrame_.resize( 1, data_.channels() );
  59. for ( unsigned int i=0; i<data_.channels(); i++ )
  60. firstFrame_[i] = data_[i];
  61. }
  62. else { // If not chunking, copy the first sample frame to the last.
  63. for ( unsigned int i=0; i<data_.channels(); i++ )
  64. data_( data_.frames() - 1, i ) = data_[i];
  65. }
  66. // Resize our lastOutputs container.
  67. lastFrame_.resize( 1, file_.channels() );
  68. // Close the file unless chunking
  69. fileSize_ = file_.fileSize();
  70. if ( !chunking_ ) file_.close();
  71. // Set default rate based on file sampling rate.
  72. this->setRate( data_.dataRate() / Stk::sampleRate() );
  73. if ( doNormalize & !chunking_ ) this->normalize();
  74. this->reset();
  75. }
  76. void FileLoop :: setRate( StkFloat rate )
  77. {
  78. rate_ = rate;
  79. if ( fmod( rate_, 1.0 ) != 0.0 ) interpolate_ = true;
  80. else interpolate_ = false;
  81. }
  82. void FileLoop :: addTime( StkFloat time )
  83. {
  84. // Add an absolute time in samples.
  85. time_ += time;
  86. while ( time_ < 0.0 )
  87. time_ += fileSize_;
  88. while ( time_ >= fileSize_ )
  89. time_ -= fileSize_;
  90. }
  91. void FileLoop :: addPhase( StkFloat angle )
  92. {
  93. // Add a time in cycles (one cycle = fileSize).
  94. time_ += fileSize_ * angle;
  95. while ( time_ < 0.0 )
  96. time_ += fileSize_;
  97. while ( time_ >= fileSize_ )
  98. time_ -= fileSize_;
  99. }
  100. void FileLoop :: addPhaseOffset( StkFloat angle )
  101. {
  102. // Add a phase offset in cycles, where 1.0 = fileSize.
  103. phaseOffset_ = fileSize_ * angle;
  104. }
  105. StkFloat FileLoop :: tick( unsigned int channel )
  106. {
  107. #if defined(_STK_DEBUG_)
  108. if ( channel >= data_.channels() ) {
  109. oStream_ << "FileLoop::tick(): channel argument and soundfile data are incompatible!";
  110. handleError( StkError::FUNCTION_ARGUMENT );
  111. }
  112. #endif
  113. if ( finished_ ) return 0.0;
  114. // Check limits of time address ... if necessary, recalculate modulo
  115. // fileSize.
  116. while ( time_ < 0.0 )
  117. time_ += fileSize_;
  118. while ( time_ >= fileSize_ )
  119. time_ -= fileSize_;
  120. StkFloat tyme = time_;
  121. if ( phaseOffset_ ) {
  122. tyme += phaseOffset_;
  123. while ( tyme < 0.0 )
  124. tyme += fileSize_;
  125. while ( tyme >= fileSize_ )
  126. tyme -= fileSize_;
  127. }
  128. if ( chunking_ ) {
  129. // Check the time address vs. our current buffer limits.
  130. if ( ( time_ < (StkFloat) chunkPointer_ ) ||
  131. ( time_ > (StkFloat) ( chunkPointer_ + chunkSize_ - 1 ) ) ) {
  132. while ( time_ < (StkFloat) chunkPointer_ ) { // negative rate
  133. chunkPointer_ -= chunkSize_ - 1; // overlap chunks by one frame
  134. if ( chunkPointer_ < 0 ) chunkPointer_ = 0;
  135. }
  136. while ( time_ > (StkFloat) ( chunkPointer_ + chunkSize_ - 1 ) ) { // positive rate
  137. chunkPointer_ += chunkSize_ - 1; // overlap chunks by one frame
  138. if ( chunkPointer_ + chunkSize_ > fileSize_ ) { // at end of file
  139. chunkPointer_ = fileSize_ - chunkSize_ + 1; // leave extra frame at end of buffer
  140. // Now fill extra frame with first frame data.
  141. for ( unsigned int j=0; j<firstFrame_.channels(); j++ )
  142. data_( data_.frames() - 1, j ) = firstFrame_[j];
  143. }
  144. }
  145. // Load more data.
  146. file_.read( data_, chunkPointer_, int2floatscaling_ );
  147. }
  148. // Adjust index for the current buffer.
  149. tyme -= chunkPointer_;
  150. }
  151. if ( interpolate_ ) {
  152. for ( unsigned int i=0; i<lastFrame_.size(); i++ )
  153. lastFrame_[i] = data_.interpolate( tyme, i );
  154. }
  155. else {
  156. for ( unsigned int i=0; i<lastFrame_.size(); i++ )
  157. lastFrame_[i] = data_( (size_t) tyme, i );
  158. }
  159. // Increment time, which can be negative.
  160. time_ += rate_;
  161. return lastFrame_[channel];
  162. }
  163. StkFrames& FileLoop :: tick( StkFrames& frames, unsigned int channel)
  164. {
  165. if ( finished_ ) {
  166. #if defined(_STK_DEBUG_)
  167. oStream_ << "FileLoop::tick(): no file data is loaded!";
  168. handleError( StkError::DEBUG_PRINT );
  169. #endif
  170. return frames;
  171. }
  172. unsigned int nChannels = lastFrame_.channels();
  173. #if defined(_STK_DEBUG_)
  174. if ( channel > frames.channels() - nChannels ) {
  175. oStream_ << "FileLoop::tick(): channel and StkFrames arguments are incompatible!";
  176. handleError( StkError::FUNCTION_ARGUMENT );
  177. }
  178. #endif
  179. StkFloat *samples = &frames[channel];
  180. unsigned int j, hop = frames.channels() - nChannels;
  181. if ( nChannels == 1 ) {
  182. for ( unsigned int i=0; i<frames.frames(); i++, samples += hop )
  183. *samples++ = tick();
  184. }
  185. else {
  186. for ( unsigned int i=0; i<frames.frames(); i++, samples += hop ) {
  187. *samples++ = tick();
  188. for ( j=1; j<nChannels; j++ )
  189. *samples++ = lastFrame_[j];
  190. }
  191. }
  192. return frames;
  193. }
  194. } // stk namespace