#include <bits/stdc++.h>
using namespace std;
/****************************************************************************
*
* NAME: PitchShift.cs
* VERSION: 1.2
* HOME URL: http://w...content-available-to-author-only...n.com
* KNOWN BUGS: none
*
* SYNOPSIS:
*
* Routine for doing pitch shifting while maintaining duration
* using the Short-Time Fourier Transform.
*
* DESCRIPTION:
*
* The routine takes:
* 1. a pitchShift factor value
* between 0.5 (one octave down) and 2. (one octave up).
* A value of exactly 1 does not change the pitch.
* 2. numSampsToProcess
* tells the routine how many samples in indata[0...numSampsToProcess-1]
* should be pitch shifted and moved to outdata[0...numSampsToProcess-1].
* The two buffers can be identical (ie. it can process the data in-place).
* 3. fftFrameSize
* defines the FFT frame size used for the processing.
* Typical values are 1024, 2048 and 4096.
* It may be any value <= MAX_FRAME_LENGTH, but it MUST be a power of 2.
* 4. osamp
* is the STFT oversampling factor
* which also determines the overlap between adjacent STFT frames.
* It should at least be 4 for moderate scaling ratios.
* A value of 32 is recommended for best quality.
* 5. sampleRate
* is the sample rate for the signal in unit Hz, ie. 44100 for 44.1 kHz audio.
*
* The data passed to the routine in indata[] should be in the range [-1.0, 1.0),
* which is also the output range for the data, make sure the data is scaled accordingly.
* For 16-bit signed integers, the input data would have to be divided by 32768, and the
* output data multiplied by the same number.
*
* COPYRIGHT 1999-2006 Stephan M. Bernsee <smb [AT] dspdimension [DOT] com>
*
* The Wide Open License (WOL)
*
* Permission to use, copy, modify, distribute and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice and this license appear in all source copies.
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF
* ANY KIND. See http://w...content-available-to-author-only...u.com/wol.htm for more information.
*
*****************************************************************************/
/****************************************************************************
*
* This code was converted to C# by Michael Knight
* madmik3 at gmail dot com.
* http://sites.google.com/site/mikescoderama/
*
*****************************************************************************/
/*****************************************************************************
*
* This code is translated to C++17 By CodingKnight at Codeforces.com
* On August 20, 2018
*
* UPDATES:
*
* 1. The MAX_FRAME_LENGTH condition on the fftFrameSize has been removed.
* 2. All data arrays have been declared using vector_t data structure
* 3. A vector_t window object has been declared and precomputed outside
* the window interleaving loop, as the array is independent of the data
* samples, and depends on the fftFrameSize only.
* 4. C++ STL complex<ldbl> library functions arg(), abs(), real(), and imag()
* have been used to deal with complex numbers.
* 5. The Pitch Shifter has been wrapped up inside a C++ class Phase_Shifter_t
* with public constructor:
*
* Pitch_Shifter_t( int fftSampleSize = 2048, int osamp = 10 )
*
* and public data processing function:
*
* void pitch_shift( ldbl pitchShift, int numSampsToProcess, ldbl sampleRate, vector_t& outdata )
*
*
*****************************************************************************/
typedef long double ldbl;
struct vector_t: vector< ldbl >
{
void initialize( int n )
{
resize( n ); for( auto &x: *this ) x = 0.0;
}
void swap( int i, int j )
{
std::swap( at( i ), at( j ) );
}
};
class Pitch_Shifter_t: public vector_t
{
static constexpr ldbl pi = acos( -1.0 ), twice_pi = 2.0 * pi;
vector_t gInFIFO, gOutFIFO, gAnaFreq, gAnaMagn, gSynFreq, gSynMagn;
vector_t gFFTworksp, gOutputAccum, gLastPhase, gSumPhase, window;
int fftFrameSize, fftFrameSize1, fftFrameSize2, gRover, inFifoLatency, k_max, osamp, stepSize;
ldbl osamp_factor;
void Short_Time_Fourier_Transform( ldbl sign )
{
/*
FFT routine, (C)1996 S.M.Bernsee. Sign = -1 is FFT, 1 is iFFT (inverse)
Fills gFFTwroksp[0...2*fftFrameSize-1] with the Fourier transform of the
time domain data in fftBuffer[0...2*fftFrameSize-1]. The FFT array takes
and returns the cosine and sine parts in an interleaved manner, ie.
gFFTworksp[0] = cosPart[0], gFFTworksp[1] = sinPart[0], asf. fftFrameSize
must be a power of 2. It expects a complex input signal (see footnote 2),
ie. when working with 'common' audio signals our input signal has to be
passed as {in[0],0.,in[1],0.,in[2],0.,...} asf. In that case, the transform
of the frequencies of interest is in gFFTworksp[0...fftFrameSize].
*/
for ( int i = 2, k = fftFrameSize1 - 2; i < k; i += 2 )
{
int j = 0;
for ( int bit_mask = 2; bit_mask < fftFrameSize1; bit_mask <<= 1, j <<= 1 )
if ( i & bit_mask )
j++;
if ( i < j )
gFFTworksp.swap( i, j ),
gFFTworksp.swap( i + 1, j + 1 );
}
for ( int k = 0, le = 4, le2 = 2, le1 = 1; k < k_max; k++, le1 = le2, le2 = le, le <<= 1 )
{
ldbl ur = 1.0,
ui = 0.0,
arg = sign * pi / ldbl( le1 );
auto z = polar( ur, arg );
ldbl wr = real( z ),
wi = imag( z );
for ( int j = 0; j < le2; j += 2 )
{
for ( int i = j, l = i + 1; i < fftFrameSize1; i += le, l += le )
{
int u = i + le2,
v = u + 1;
ldbl tr = gFFTworksp[ u ] * ur - gFFTworksp[ v ] * ui,
ti = gFFTworksp[ u ] * ui + gFFTworksp[ v ] * ur;
gFFTworksp[ u ] = gFFTworksp[ i ] - tr,
gFFTworksp[ v ] = gFFTworksp[ l ] - ti;
gFFTworksp[ i ] += tr,
gFFTworksp[ l ] += ti;
}
ldbl tr = ur * wr - ui * wi;
ui = ur * wi + ui * wr,
ur = tr;
}
}
}
public:
Pitch_Shifter_t( int _fftFrameSize = 2048, int _osamp = 10 ) : fftFrameSize( _fftFrameSize ), osamp( _osamp )
{
/*
Purpose: doing pitch shifting while maintaining duration using the Short Time Fourier Transform.
Author: (c)1999-2015 Stephan M. Bernsee <s.bernsee [AT] zynaptiq [DOT] com>
*/
fftFrameSize1 = fftFrameSize * 2,
fftFrameSize2 = fftFrameSize / 2,
stepSize = fftFrameSize / osamp,
inFifoLatency = gRover = fftFrameSize - stepSize,
k_max = int( log( fftFrameSize ) / log( 2.0 ) + 0.5 ),
osamp_factor = 2.0 / ( ldbl( fftFrameSize2 ) * ldbl( osamp ) );
initialize( fftFrameSize );
int max_phase_size = fftFrameSize2 + 1;
gInFIFO.initialize( fftFrameSize ),
gOutFIFO.initialize( fftFrameSize ),
gAnaFreq.initialize( fftFrameSize ),
gAnaMagn.initialize( fftFrameSize ),
gSynFreq.initialize( fftFrameSize ),
gSynMagn.initialize( fftFrameSize );
gFFTworksp.initialize( fftFrameSize1 ),
gOutputAccum.initialize( fftFrameSize1 );
gLastPhase.initialize( max_phase_size ),
gSumPhase.initialize( max_phase_size );
window.initialize( fftFrameSize );
for( int k = 0; k < fftFrameSize; k++ )
window[ k ] = 0.5 - .5 * cos( twice_pi * ldbl( k ) / ldbl( fftFrameSize ) );
}
void pitch_shift( ldbl pitchShift, int numSampsToProcess, ldbl sampleRate, vector_t& outdata )
{
/* set up some handy variables */
ldbl freqPerBin = sampleRate / ldbl( fftFrameSize ),
expct = twice_pi * ldbl( stepSize ) / ldbl( fftFrameSize );
/* main processing loop */
for ( int i = 0; i < numSampsToProcess; i++ )
{
/* As long as we have not yet collected enough data just read in */
gInFIFO[ gRover ] = at( i ),
outdata[ i ] = gOutFIFO[ gRover - inFifoLatency ],
gRover++;
/* now we have enough data for processing */
if ( gRover >= fftFrameSize )
{
gRover = inFifoLatency;
/* do windowing and re,im interleave */
for ( int k = 0, l = 0, m = 1; k < fftFrameSize; k++, l += 2, m += 2 )
gFFTworksp[ l ] = gInFIFO[ k ] * window[ k ],
gFFTworksp[ m ] = 0.0;
/* ***************** ANALYSIS ******************* */
/* do transform */
Short_Time_Fourier_Transform( -1.0 );
/* this is the analysis step */
for ( int k = 0, l = 0, m = 1; k <= fftFrameSize2; k++, l += 2, m += 2 )
{
/* de-interlace FFT buffer */
complex<ldbl> z( gFFTworksp[ l ], gFFTworksp[ m ] );
/* compute magnitude and phase */
ldbl magn = 2.0 * abs( z ),
phase = arg( z );
/* compute phase difference */
ldbl tmp = phase - gLastPhase[ k ];
gLastPhase[ k ] = phase;
/* subtract expected phase difference */
tmp -= ldbl( k ) * expct;
/* map delta phase into +/- Pi interval */
int qpd = int( tmp / pi );
if ( qpd >= 0 )
qpd += qpd & 1;
else
qpd -= qpd & 1;
tmp -= pi * qpd;
/* get deviation from bin frequency from the +/- Pi interval */
tmp = osamp * tmp / twice_pi;
/* compute the k-th partials' true frequency */
tmp = ldbl( k ) * freqPerBin + tmp * freqPerBin;
/* store magnitude and true frequency in analysis arrays */
gAnaMagn[ k ] = magn,
gAnaFreq[ k ] = tmp;
}
/* ***************** PROCESSING ******************* */
/* this does the actual pitch shifting */
for ( int zero = 0; zero < fftFrameSize; zero++ )
gSynMagn[ zero ] = 0,
gSynFreq[ zero ] = 0;
for ( int k = 0; k <= fftFrameSize2; k++ )
{
int index = int( ldbl( k ) * pitchShift );
if ( index <= fftFrameSize2 )
gSynMagn[ index ] += gAnaMagn[ k ],
gSynFreq[ index ] = gAnaFreq[ k ] * pitchShift;
else
break;
}
/* ***************** SYNTHESIS ******************* */
/* this is the synthesis step */
for ( int k = 0, l = 0, m = 1; k <= fftFrameSize2; k++, l += 2, m += 2 )
{
/* get true frequency from synthesis arrays */
ldbl tmp = gSynFreq[ k ];
/* subtract bin mid frequency */
tmp -= ldbl( k ) * freqPerBin;
/* get bin deviation from freq deviation */
tmp /= freqPerBin;
/* take osamp into account */
tmp = twice_pi * tmp / osamp;
/* add the overlap phase advance back in */
tmp += ldbl( k ) * expct;
/* accumulate delta phase to get bin phase */
gSumPhase[ k ] += tmp;
auto z = polar( gSynMagn[ k ], gSumPhase[ k ] );
/* get real and imag part and re-interleave */
gFFTworksp[ l ] = real( z ),
gFFTworksp[ m ] = imag( z );
}
/* zero negative frequencies */
for ( int k = fftFrameSize + 2; k < fftFrameSize1; k++ )
gFFTworksp[ k ] = 0.0;
/* do inverse transform */
Short_Time_Fourier_Transform( 1.0 );
/* do windowing and add to output accumulator */
for ( int k = 0, l = 0; k < fftFrameSize; k++, l += 2 )
gOutputAccum[ k ] += osamp_factor * window[ k ] * gFFTworksp[ l ];
for ( int k = 0; k < stepSize; k++ )
gOutFIFO[ k ] = gOutputAccum[ k ];
/* shift accumulator */
//memmove(gOutputAccum, gOutputAccum + stepSize, fftFrameSize * sizeof(ldbl));
for ( int k = 0, l = stepSize; k < fftFrameSize; k++, l++ )
gOutputAccum[ k ] = gOutputAccum[ l ];
/* move input FIFO */
for ( int k = 0, l = stepSize; k < inFifoLatency; k++, l++ )
gInFIFO[ k ] = gInFIFO[ l ];
}
}
}
};
typedef long long ll;
typedef chrono::high_resolution_clock Clock_t;
typedef Clock_t::time_point time_point_t;
time_point_t Now() { return Clock_t::now(); }
struct Timer_t
{
time_point_t start_time;
Timer_t() : start_time( Now() ) {}
typedef long double ldbl;
typedef chrono::nanoseconds nanosec_t;
ll elapsed_time()
{
nanosec_t timer = chrono::duration_cast< nanosec_t >( Now() - start_time );
return timer.count() / 1000000LL;
}
};
struct random_t: mt19937_64
{
random_t() : mt19937_64( Clock_t::now().time_since_epoch().count() ) {}
int random( int MIN, int MAX )
{
ll x_min = MIN, x_max = MAX, interval = x_max - x_min + 1, number = (*this)();
if ( number < 0 )
number += LLONG_MAX;
return x_min + ( number % interval );
}
} mt;
int main()
{
ios_base::sync_with_stdio( false ), cin.tie( nullptr ), cout.tie( nullptr );
Pitch_Shifter_t x; vector_t y;
int n = x.size(), r_max = INT_MAX / 2, r_min = -r_max;
ldbl pitch_shift, sample_rate, range = r_max;
ldbl x_avg = 0.0, y_avg = 0.0, x_min = FLT_MAX, y_min = FLT_MAX, x_max = -FLT_MAX, y_max = -FLT_MAX;
for( int i = 0; i < n; i++ )
x[ i ] = ldbl( mt.random( r_min, r_max ) ) / range,
x_min = min( x_min, x[ i ] ),
x_max = max( x_max, x[ i ] ),
x_avg += x[ i ] / ldbl( n );
y.initialize( n );
cin >> pitch_shift >> sample_rate;
Timer_t timer;
x.pitch_shift( pitch_shift, n, sample_rate, y );
ll t = timer.elapsed_time();
for( int i = 0; i < n; i++ )
y_min = min( y_min, y[ i ] ),
y_max = max( y_max, y[ i ] );
ldbl scaling_factor = 2.0 / ( y_max - y_min ), g = y_min;
y_min = FLT_MAX, y_max = -FLT_MAX;
for( int i = 0; i < n; i++ )
y[ i ] = scaling_factor * ( y[ i ] - g ) - 1.0,
y_min = min( y_min, y[ i ] ),
y_max = max( y_max, y[ i ] ),
y_avg += y[ i ] / ldbl( n );
cout << "x_avg = " << x_avg << ", x_min = " << x_min << ", x_max = " << x_max << endl,
cout << "y_avg = " << y_avg << ", y_min = " << y_min << ", y_max = " << y_max << endl,
cout << "scaling factor = " << scaling_factor << endl,
cout << "elapsed time = " << t << " msec.";
}