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.

268 lines
8.7KB

  1. #ifndef STK_LENLentPitShift_H
  2. #define STK_LENLentPitShift_H
  3. #include "Effect.h"
  4. #include "Delay.h"
  5. namespace stk {
  6. /***************************************************/
  7. /*! \class LentPitShift
  8. \brief Pitch shifter effect class based on the Lent algorithm.
  9. This class implements a pitch shifter using pitch
  10. tracking and sample windowing and shifting.
  11. by Francois Germain, 2009.
  12. */
  13. /***************************************************/
  14. class LentPitShift : public Effect
  15. {
  16. public:
  17. //! Class constructor.
  18. LentPitShift( StkFloat periodRatio = 1.0, int tMax = RT_BUFFER_SIZE );
  19. ~LentPitShift( void ) {
  20. delete window;
  21. window = NULL;
  22. delete dt;
  23. dt = NULL;
  24. delete dpt;
  25. dpt = NULL;
  26. delete cumDt;
  27. cumDt = NULL;
  28. }
  29. //! Reset and clear all internal state.
  30. void clear( void );
  31. //! Set the pitch shift factor (1.0 produces no shift).
  32. void setShift( StkFloat shift );
  33. //! Input one sample to the filter and return one output.
  34. StkFloat tick( StkFloat input );
  35. //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs.
  36. /*!
  37. The StkFrames argument reference is returned. The \c channel
  38. argument must be less than the number of channels in the
  39. StkFrames argument (the first channel is specified by 0).
  40. However, range checking is only performed if _STK_DEBUG_ is
  41. defined during compilation, in which case an out-of-range value
  42. will trigger an StkError exception.
  43. */
  44. StkFrames& tick( StkFrames& frames, unsigned int channel = 0 );
  45. //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object.
  46. /*!
  47. The \c iFrames object reference is returned. Each channel
  48. argument must be less than the number of channels in the
  49. corresponding StkFrames argument (the first channel is specified
  50. by 0). However, range checking is only performed if _STK_DEBUG_
  51. is defined during compilation, in which case an out-of-range value
  52. will trigger an StkError exception.
  53. */
  54. StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 );
  55. protected:
  56. //! Apply the effect on the input samples and store it.
  57. /*!
  58. The samples stored in the input frame vector are processed
  59. and the delayed result are stored in the output frame vector
  60. */
  61. void process( );
  62. // Frame storage vectors for process function
  63. StkFrames inputFrames;
  64. StkFrames outputFrames;
  65. int ptrFrames; // writing pointer
  66. // Input delay line
  67. Delay inputLine_;
  68. int inputPtr;
  69. // Output delay line
  70. Delay outputLine_;
  71. double outputPtr;
  72. // Pitch tracker variables
  73. unsigned long tMax_; // Maximal period measurable by the pitch tracker.
  74. // It is also the size of the window used by the pitch tracker and
  75. // the size of the frames that can be computed by the tick function
  76. StkFloat threshold_; // Threshold of detection for the pitch tracker
  77. unsigned long lastPeriod_; // Result of the last pitch tracking loop
  78. StkFloat* dt; // Array containing the euclidian distance coefficients
  79. StkFloat* cumDt; // Array containing the cumulative sum of the coefficients in dt
  80. StkFloat* dpt; // Array containing the pitch tracking function coefficients
  81. // Pitch shifter variables
  82. StkFloat env[2]; // Coefficients for the linear interpolation when modifying the output samples
  83. StkFloat* window; // Hamming window used for the input portion extraction
  84. double periodRatio_; // Ratio of modification of the signal period
  85. StkFrames zeroFrame; // Frame of tMax_ zero samples
  86. // Coefficient delay line that could be used for a dynamic calculation of the pitch
  87. //Delay* coeffLine_;
  88. };
  89. inline void LentPitShift::process()
  90. {
  91. StkFloat x_t; // input coefficient
  92. StkFloat x_t_T; // previous input coefficient at T samples
  93. StkFloat coeff; // new coefficient for the difference function
  94. int alternativePitch = tMax_; // Global minimum storage
  95. lastPeriod_ = tMax_+1; // Storage of the lowest local minimum under the threshold
  96. // Loop variables
  97. unsigned long delay_;
  98. unsigned int n;
  99. // Initialization of the dt coefficients. Since the
  100. // frames are of tMax_ length, there is no overlapping
  101. // between the successive windows where pitch tracking
  102. // is performed.
  103. for ( delay_=1; delay_<=tMax_; delay_++ )
  104. dt[delay_] = 0.;
  105. // Calculation of the dt coefficients and update of the input delay line.
  106. for ( n=0; n<inputFrames.size(); n++ ) {
  107. x_t = inputLine_.tick( inputFrames[ n ] );
  108. for ( delay_=1; delay_<= tMax_; delay_++ ) {
  109. x_t_T = inputLine_.tapOut( delay_ );
  110. coeff = x_t - x_t_T;
  111. dt[delay_] += coeff * coeff;
  112. }
  113. }
  114. // Calculation of the pitch tracking function and test for the minima.
  115. for ( delay_=1; delay_<=tMax_; delay_++ ) {
  116. cumDt[delay_] = dt[delay_] + cumDt[delay_-1];
  117. dpt[delay_] = dt[delay_] * delay_ / cumDt[delay_];
  118. // Look for a minimum
  119. if ( dpt[delay_-1]-dpt[delay_-2] < 0 && dpt[delay_]-dpt[delay_-1] > 0 ) {
  120. // Check if the minimum is under the threshold
  121. if ( dpt[delay_-1] < threshold_ ){
  122. lastPeriod_ = delay_-1;
  123. // If a minimum is found, we can stop the loop
  124. break;
  125. }
  126. else if ( dpt[alternativePitch] > dpt[delay_-1] )
  127. // Otherwise we store it if it is the current global minimum
  128. alternativePitch = delay_-1;
  129. }
  130. }
  131. // Test for the last period length.
  132. if ( dpt[delay_]-dpt[delay_-1] < 0 ) {
  133. if ( dpt[delay_] < threshold_ )
  134. lastPeriod_ = delay_;
  135. else if ( dpt[alternativePitch] > dpt[delay_] )
  136. alternativePitch = delay_;
  137. }
  138. if ( lastPeriod_ == tMax_+1 )
  139. // No period has been under the threshold so we used the global minimum
  140. lastPeriod_ = alternativePitch;
  141. // We put the new zero output coefficients in the output delay line and
  142. // we get the previous calculated coefficients
  143. outputLine_.tick( zeroFrame, outputFrames );
  144. // Initialization of the Hamming window used in the algorithm
  145. for ( int n=-(int)lastPeriod_; n<(int)lastPeriod_; n++ )
  146. window[n+lastPeriod_] = (1 + cos(PI*n/lastPeriod_)) / 2 ;
  147. int M; // Index of reading in the input delay line
  148. int N; // Index of writing in the output delay line
  149. double sample; // Temporary storage for the new coefficient
  150. // We loop for all the frames of length lastPeriod_ presents between inputPtr and tMax_
  151. for ( ; inputPtr<(int)(tMax_-lastPeriod_); inputPtr+=lastPeriod_ ) {
  152. // Test for the decision of compression/expansion
  153. while ( outputPtr < inputPtr ) {
  154. // Coefficients for the linear interpolation
  155. env[1] = fmod( outputPtr + tMax_, 1.0 );
  156. env[0] = 1.0 - env[1];
  157. M = tMax_ - inputPtr + lastPeriod_ - 1; // New reading pointer
  158. N = 2*tMax_ - (unsigned long)floor(outputPtr + tMax_) + lastPeriod_ - 1; // New writing pointer
  159. for ( unsigned int j=0; j<2*lastPeriod_; j++,M--,N-- ) {
  160. sample = inputLine_.tapOut(M) * window[j] / 2.;
  161. // Linear interpolation
  162. outputLine_.addTo(N, env[0] * sample);
  163. outputLine_.addTo(N-1, env[1] * sample);
  164. }
  165. outputPtr = outputPtr + lastPeriod_ * periodRatio_; // new output pointer
  166. }
  167. }
  168. // Shifting of the pointers waiting for the new frame of length tMax_.
  169. outputPtr -= tMax_;
  170. inputPtr -= tMax_;
  171. }
  172. inline StkFloat LentPitShift :: tick( StkFloat input )
  173. {
  174. StkFloat sample;
  175. inputFrames[ptrFrames] = input;
  176. sample = outputFrames[ptrFrames++];
  177. // Check for end condition
  178. if ( ptrFrames == (int) inputFrames.size() ){
  179. ptrFrames = 0;
  180. process( );
  181. }
  182. return sample;
  183. }
  184. inline StkFrames& LentPitShift :: tick( StkFrames& frames, unsigned int channel )
  185. {
  186. #if defined(_STK_DEBUG_)
  187. if ( channel >= frames.channels() ) {
  188. oStream_ << "LentPitShift::tick(): channel and StkFrames arguments are incompatible!";
  189. handleError( StkError::FUNCTION_ARGUMENT );
  190. }
  191. #endif
  192. StkFloat *samples = &frames[channel];
  193. unsigned int hop = frames.channels();
  194. for ( unsigned int i=0; i<frames.frames(); i++, samples += hop ) {
  195. *samples = tick( *samples );
  196. }
  197. return frames;
  198. }
  199. inline StkFrames& LentPitShift :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel )
  200. {
  201. #if defined(_STK_DEBUG_)
  202. if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) {
  203. oStream_ << "LentPitShift::tick(): channel and StkFrames arguments are incompatible!";
  204. handleError( StkError::FUNCTION_ARGUMENT );
  205. }
  206. #endif
  207. StkFloat *iSamples = &iFrames[iChannel];
  208. StkFloat *oSamples = &oFrames[oChannel];
  209. unsigned int iHop = iFrames.channels(), oHop = oFrames.channels();
  210. for ( unsigned int i=0; i<iFrames.frames(); i++, iSamples += iHop, oSamples += oHop ) {
  211. *oSamples = tick( *iSamples );
  212. }
  213. return iFrames;
  214. }
  215. } // stk namespace
  216. #endif