mirror of
https://github.com/thestk/stk
synced 2026-04-23 15:48:37 +00:00
Version 4.4.2
This commit is contained in:
committed by
Stephen Sinclair
parent
b6a2202011
commit
baca57040b
267
include/LentPitShift.h
Normal file
267
include/LentPitShift.h
Normal file
@@ -0,0 +1,267 @@
|
||||
#ifndef STK_LENLentPitShift_H
|
||||
#define STK_LENLentPitShift_H
|
||||
|
||||
#include "Effect.h"
|
||||
#include "Delay.h"
|
||||
|
||||
namespace stk {
|
||||
|
||||
/***************************************************/
|
||||
/*! \class LentPitShift
|
||||
\brief Pitch shifter effect class based on the Lent algorithm.
|
||||
|
||||
This class implements a pitch shifter using pitch
|
||||
tracking and sample windowing and shifting.
|
||||
|
||||
by Francois Germain, 2009.
|
||||
*/
|
||||
/***************************************************/
|
||||
|
||||
class LentPitShift : public Effect
|
||||
{
|
||||
public:
|
||||
//! Class constructor.
|
||||
LentPitShift( StkFloat periodRatio = 1.0, int tMax = RT_BUFFER_SIZE );
|
||||
|
||||
~LentPitShift( void ) {
|
||||
delete window;
|
||||
window = NULL;
|
||||
delete dt;
|
||||
dt = NULL;
|
||||
delete dpt;
|
||||
dpt = NULL;
|
||||
delete cumDt;
|
||||
cumDt = NULL;
|
||||
}
|
||||
|
||||
//! Reset and clear all internal state.
|
||||
void clear( void );
|
||||
|
||||
//! Set the pitch shift factor (1.0 produces no shift).
|
||||
void setShift( StkFloat shift );
|
||||
|
||||
//! Input one sample to the filter and return one output.
|
||||
StkFloat tick( StkFloat input );
|
||||
|
||||
//! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs.
|
||||
/*!
|
||||
The StkFrames argument reference is returned. The \c channel
|
||||
argument must be less than the number of channels in the
|
||||
StkFrames argument (the first channel is specified by 0).
|
||||
However, range checking is only performed if _STK_DEBUG_ is
|
||||
defined during compilation, in which case an out-of-range value
|
||||
will trigger an StkError exception.
|
||||
*/
|
||||
StkFrames& tick( StkFrames& frames, unsigned int channel = 0 );
|
||||
|
||||
//! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object.
|
||||
/*!
|
||||
The \c iFrames object reference is returned. Each channel
|
||||
argument must be less than the number of channels in the
|
||||
corresponding StkFrames argument (the first channel is specified
|
||||
by 0). However, range checking is only performed if _STK_DEBUG_
|
||||
is defined during compilation, in which case an out-of-range value
|
||||
will trigger an StkError exception.
|
||||
*/
|
||||
StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 );
|
||||
|
||||
protected:
|
||||
|
||||
//! Apply the effect on the input samples and store it.
|
||||
/*!
|
||||
The samples stored in the input frame vector are processed
|
||||
and the delayed result are stored in the output frame vector
|
||||
*/
|
||||
void process( );
|
||||
|
||||
// Frame storage vectors for process function
|
||||
StkFrames inputFrames;
|
||||
StkFrames outputFrames;
|
||||
int ptrFrames; // writing pointer
|
||||
|
||||
// Input delay line
|
||||
Delay inputLine_;
|
||||
int inputPtr;
|
||||
|
||||
// Output delay line
|
||||
Delay outputLine_;
|
||||
double outputPtr;
|
||||
|
||||
// Pitch tracker variables
|
||||
unsigned long tMax_; // Maximal period measurable by the pitch tracker.
|
||||
// It is also the size of the window used by the pitch tracker and
|
||||
// the size of the frames that can be computed by the tick function
|
||||
|
||||
StkFloat threshold_; // Threshold of detection for the pitch tracker
|
||||
unsigned long lastPeriod_; // Result of the last pitch tracking loop
|
||||
StkFloat* dt; // Array containing the euclidian distance coefficients
|
||||
StkFloat* cumDt; // Array containing the cumulative sum of the coefficients in dt
|
||||
StkFloat* dpt; // Array containing the pitch tracking function coefficients
|
||||
|
||||
// Pitch shifter variables
|
||||
StkFloat env[2]; // Coefficients for the linear interpolation when modifying the output samples
|
||||
StkFloat* window; // Hamming window used for the input portion extraction
|
||||
double periodRatio_; // Ratio of modification of the signal period
|
||||
StkFrames zeroFrame; // Frame of tMax_ zero samples
|
||||
|
||||
|
||||
// Coefficient delay line that could be used for a dynamic calculation of the pitch
|
||||
//Delay* coeffLine_;
|
||||
|
||||
};
|
||||
|
||||
inline void LentPitShift::process()
|
||||
{
|
||||
StkFloat x_t; // input coefficient
|
||||
StkFloat x_t_T; // previous input coefficient at T samples
|
||||
StkFloat coeff; // new coefficient for the difference function
|
||||
|
||||
int alternativePitch = tMax_; // Global minimum storage
|
||||
lastPeriod_ = tMax_+1; // Storage of the lowest local minimum under the threshold
|
||||
|
||||
// Loop variables
|
||||
unsigned long delay_;
|
||||
unsigned int n;
|
||||
|
||||
// Initialization of the dt coefficients. Since the
|
||||
// frames are of tMax_ length, there is no overlapping
|
||||
// between the successive windows where pitch tracking
|
||||
// is performed.
|
||||
for ( delay_=1; delay_<=tMax_; delay_++ )
|
||||
dt[delay_] = 0.;
|
||||
|
||||
// Calculation of the dt coefficients and update of the input delay line.
|
||||
for ( n=0; n<inputFrames.size(); n++ ) {
|
||||
x_t = inputLine_.tick( inputFrames[ n ] );
|
||||
for ( delay_=1; delay_<= tMax_; delay_++ ) {
|
||||
x_t_T = inputLine_.contentsAt( delay_ );
|
||||
coeff = x_t - x_t_T;
|
||||
dt[delay_] += coeff * coeff;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculation of the pitch tracking function and test for the minima.
|
||||
for ( delay_=1; delay_<=tMax_; delay_++ ) {
|
||||
cumDt[delay_] = dt[delay_] + cumDt[delay_-1];
|
||||
dpt[delay_] = dt[delay_] * delay_ / cumDt[delay_];
|
||||
|
||||
// Look for a minimum
|
||||
if ( dpt[delay_-1]-dpt[delay_-2] < 0 && dpt[delay_]-dpt[delay_-1] > 0 ) {
|
||||
// Check if the minimum is under the threshold
|
||||
if ( dpt[delay_-1] < threshold_ ){
|
||||
lastPeriod_ = delay_-1;
|
||||
// If a minimum is found, we can stop the loop
|
||||
break;
|
||||
}
|
||||
else if ( dpt[alternativePitch] > dpt[delay_-1] )
|
||||
// Otherwise we store it if it is the current global minimum
|
||||
alternativePitch = delay_-1;
|
||||
}
|
||||
}
|
||||
|
||||
// Test for the last period length.
|
||||
if ( dpt[delay_]-dpt[delay_-1] < 0 ) {
|
||||
if ( dpt[delay_] < threshold_ )
|
||||
lastPeriod_ = delay_;
|
||||
else if ( dpt[alternativePitch] > dpt[delay_] )
|
||||
alternativePitch = delay_;
|
||||
}
|
||||
|
||||
if ( lastPeriod_ == tMax_+1 )
|
||||
// No period has been under the threshold so we used the global minimum
|
||||
lastPeriod_ = alternativePitch;
|
||||
|
||||
// We put the new zero output coefficients in the output delay line and
|
||||
// we get the previous calculated coefficients
|
||||
outputLine_.tick( zeroFrame, outputFrames );
|
||||
|
||||
// Initialization of the Hamming window used in the algorithm
|
||||
for ( int n=-(int)lastPeriod_; n<(int)lastPeriod_; n++ )
|
||||
window[n+lastPeriod_] = (1 + cos(PI*n/lastPeriod_)) / 2 ;
|
||||
|
||||
int M; // Index of reading in the input delay line
|
||||
int N; // Index of writing in the output delay line
|
||||
double sample; // Temporary storage for the new coefficient
|
||||
|
||||
// We loop for all the frames of length lastPeriod_ presents between inputPtr and tMax_
|
||||
for ( ; inputPtr<(int)(tMax_-lastPeriod_); inputPtr+=lastPeriod_ ) {
|
||||
// Test for the decision of compression/expansion
|
||||
while ( outputPtr < inputPtr ) {
|
||||
// Coefficients for the linear interpolation
|
||||
env[1] = fmod( outputPtr + tMax_, 1.0 );
|
||||
env[0] = 1.0 - env[1];
|
||||
M = tMax_ - inputPtr + lastPeriod_ - 1; // New reading pointer
|
||||
N = 2*tMax_ - (unsigned long)floor(outputPtr + tMax_) + lastPeriod_ - 1; // New writing pointer
|
||||
for ( unsigned int j=0; j<2*lastPeriod_; j++,M--,N-- ) {
|
||||
sample = inputLine_.contentsAt(M) * window[j] / 2.;
|
||||
// Linear interpolation
|
||||
outputLine_.addTo(N, env[0] * sample);
|
||||
outputLine_.addTo(N-1, env[1] * sample);
|
||||
}
|
||||
outputPtr = outputPtr + lastPeriod_ * periodRatio_; // new output pointer
|
||||
}
|
||||
}
|
||||
// Shifting of the pointers waiting for the new frame of length tMax_.
|
||||
outputPtr -= tMax_;
|
||||
inputPtr -= tMax_;
|
||||
}
|
||||
|
||||
|
||||
inline StkFloat LentPitShift :: tick( StkFloat input )
|
||||
{
|
||||
StkFloat sample;
|
||||
|
||||
inputFrames[ptrFrames] = input;
|
||||
|
||||
sample = outputFrames[ptrFrames++];
|
||||
|
||||
// Check for end condition
|
||||
if ( ptrFrames == (int) inputFrames.size() ){
|
||||
ptrFrames = 0;
|
||||
process( );
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
inline StkFrames& LentPitShift :: tick( StkFrames& frames, unsigned int channel )
|
||||
{
|
||||
#if defined(_STK_DEBUG_)
|
||||
if ( channel >= frames.channels() ) {
|
||||
errorString_ << "LentPitShift::tick(): channel and StkFrames arguments are incompatible!";
|
||||
handleError( StkError::FUNCTION_ARGUMENT );
|
||||
}
|
||||
#endif
|
||||
|
||||
StkFloat *samples = &frames[channel];
|
||||
unsigned int hop = frames.channels();
|
||||
for ( unsigned int i=0; i<frames.frames(); i++, samples += hop ) {
|
||||
*samples = tick( *samples );
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
inline StkFrames& LentPitShift :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel )
|
||||
{
|
||||
#if defined(_STK_DEBUG_)
|
||||
if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) {
|
||||
errorString_ << "LentPitShift::tick(): channel and StkFrames arguments are incompatible!";
|
||||
handleError( StkError::FUNCTION_ARGUMENT );
|
||||
}
|
||||
#endif
|
||||
|
||||
StkFloat *iSamples = &iFrames[iChannel];
|
||||
StkFloat *oSamples = &oFrames[oChannel];
|
||||
unsigned int iHop = iFrames.channels(), oHop = oFrames.channels();
|
||||
for ( unsigned int i=0; i<iFrames.frames(); i++, iSamples += iHop, oSamples += oHop ) {
|
||||
*oSamples = tick( *iSamples );
|
||||
}
|
||||
|
||||
return iFrames;
|
||||
}
|
||||
|
||||
} // stk namespace
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user