2012-06-10 16:08:43 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
Audacity: A Digital Audio Editor
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
Paulstretch.cpp
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
Nasca Octavian Paul (Paul Nasca)
|
|
|
|
Some GUI code was taken from the Echo effect
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
*******************************************************************//**
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
\class EffectPaulstretch
|
|
|
|
\brief An Extreme Time Stretch and Time Smear effect
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
*//*******************************************************************/
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
#include "../Audacity.h"
|
2015-06-18 14:24:36 +00:00
|
|
|
#include "Paulstretch.h"
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
#include <math.h>
|
|
|
|
#include <float.h>
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
#include <wx/intl.h>
|
|
|
|
#include <wx/valgen.h>
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-06-18 14:24:36 +00:00
|
|
|
#include "../ShuttleGui.h"
|
2015-04-17 03:53:42 +00:00
|
|
|
#include "../FFT.h"
|
|
|
|
#include "../widgets/valnum.h"
|
2016-01-02 09:00:40 +00:00
|
|
|
#include "../Prefs.h"
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-07-03 04:20:21 +00:00
|
|
|
#include "../WaveTrack.h"
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
// Define keys, defaults, minimums, and maximums for the effect parameters
|
|
|
|
//
|
2015-04-19 03:49:05 +00:00
|
|
|
// Name Type Key Def Min Max Scale
|
|
|
|
Param( Amount, float, XO("Stretch Factor"), 10.0, 1.0, FLT_MAX, 1 );
|
2015-07-20 15:29:26 +00:00
|
|
|
Param( Time, float, XO("Time Resolution"), 0.25f, 0.00099f, FLT_MAX, 1 );
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
class PaulStretch
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PaulStretch(float rap_,int in_bufsize_,float samplerate_);
|
|
|
|
//in_bufsize is also a half of a FFT buffer (in samples)
|
|
|
|
virtual ~PaulStretch();
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
void process(float *smps,int nsmps);
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
int in_bufsize;
|
|
|
|
int poolsize;//how many samples are inside the input_pool size (need to know how many samples to fill when seeking)
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
int out_bufsize;
|
|
|
|
float *out_buf;
|
|
|
|
|
|
|
|
int get_nsamples();//how many samples are required to be added in the pool next time
|
|
|
|
int get_nsamples_for_fill();//how many samples are required to be added for a complete buffer refill (at start of the song or after seek)
|
|
|
|
|
|
|
|
void set_rap(float newrap);//set the current stretch value
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual void process_spectrum(float *WXUNUSED(freq)){};
|
|
|
|
float samplerate;
|
|
|
|
|
|
|
|
private:
|
|
|
|
float *in_pool;//de marimea in_bufsize
|
|
|
|
float rap;
|
|
|
|
float *old_out_smp_buf;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
float *fft_smps,*fft_c,*fft_s,*fft_freq,*fft_tmp;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
double remained_samples;//how many fraction of samples has remained (0..1)
|
2012-06-10 16:08:43 +00:00
|
|
|
};
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
//
|
|
|
|
// EffectPaulstretch
|
|
|
|
//
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
BEGIN_EVENT_TABLE(EffectPaulstretch, wxEvtHandler)
|
|
|
|
EVT_TEXT(wxID_ANY, EffectPaulstretch::OnText)
|
|
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
|
|
EffectPaulstretch::EffectPaulstretch()
|
|
|
|
{
|
2016-01-02 09:00:40 +00:00
|
|
|
mAmount = DEF_Amount;
|
|
|
|
mTime_resolution = DEF_Time;
|
2015-05-15 11:47:51 +00:00
|
|
|
|
|
|
|
SetLinearEffectFlag(true);
|
2015-04-17 03:53:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EffectPaulstretch::~EffectPaulstretch()
|
|
|
|
{
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
// IdentInterface implementation
|
|
|
|
|
|
|
|
wxString EffectPaulstretch::GetSymbol()
|
|
|
|
{
|
|
|
|
return PAULSTRETCH_PLUGIN_SYMBOL;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString EffectPaulstretch::GetDescription()
|
|
|
|
{
|
2015-04-19 03:49:05 +00:00
|
|
|
return XO("Use Paulstretch only for an extreme time-stretch or \"stasis\" effect");
|
2015-04-17 03:53:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// EffectIdentInterface implementation
|
|
|
|
|
|
|
|
EffectType EffectPaulstretch::GetType()
|
|
|
|
{
|
|
|
|
return EffectTypeProcess;
|
|
|
|
}
|
|
|
|
|
|
|
|
// EffectClientInterface implementation
|
|
|
|
|
|
|
|
bool EffectPaulstretch::GetAutomationParameters(EffectAutomationParameters & parms)
|
|
|
|
{
|
2016-01-02 09:00:40 +00:00
|
|
|
parms.WriteFloat(KEY_Amount, mAmount);
|
|
|
|
parms.WriteFloat(KEY_Time, mTime_resolution);
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
return true;
|
2015-04-17 03:53:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool EffectPaulstretch::SetAutomationParameters(EffectAutomationParameters & parms)
|
|
|
|
{
|
|
|
|
ReadAndVerifyFloat(Amount);
|
|
|
|
ReadAndVerifyFloat(Time);
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-01-02 09:00:40 +00:00
|
|
|
mAmount = Amount;
|
|
|
|
mTime_resolution = Time;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
return true;
|
2015-04-17 03:53:42 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
// Effect implementation
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-01-02 09:00:40 +00:00
|
|
|
double EffectPaulstretch::CalcPreviewInputLength(double previewLength)
|
|
|
|
{
|
|
|
|
// FIXME: Preview is currently at the project rate, but should really be
|
|
|
|
// at the track rate (bugs 1284 and 852).
|
|
|
|
int minDuration = GetBufferSize(mProjectRate) * 2 + 1;
|
|
|
|
|
|
|
|
// Preview playback may need to be trimmed but this is the smallest selection that we can use.
|
|
|
|
double minLength = std::max<double>(minDuration / mProjectRate, previewLength / mAmount);
|
|
|
|
|
|
|
|
return minLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
bool EffectPaulstretch::Process()
|
|
|
|
{
|
2014-06-03 20:30:19 +00:00
|
|
|
CopyInputTracks();
|
2012-06-10 16:08:43 +00:00
|
|
|
SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks);
|
|
|
|
WaveTrack *track = (WaveTrack *) iter.First();
|
2012-06-14 22:46:55 +00:00
|
|
|
m_t1=mT1;
|
2012-06-10 16:08:43 +00:00
|
|
|
int count=0;
|
|
|
|
while (track) {
|
|
|
|
double trackStart = track->GetStartTime();
|
|
|
|
double trackEnd = track->GetEndTime();
|
|
|
|
double t0 = mT0 < trackStart? trackStart: mT0;
|
|
|
|
double t1 = mT1 > trackEnd? trackEnd: mT1;
|
|
|
|
|
|
|
|
if (t1 > t0) {
|
|
|
|
if (!ProcessOne(track, t0,t1,count))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
track = (WaveTrack *) iter.Next();
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
mT1=m_t1;
|
|
|
|
|
|
|
|
ReplaceProcessedTracks(true);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-01-02 09:00:40 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
void EffectPaulstretch::PopulateOrExchange(ShuttleGui & S)
|
|
|
|
{
|
|
|
|
S.StartMultiColumn(2, wxALIGN_CENTER);
|
|
|
|
{
|
2016-01-02 09:00:40 +00:00
|
|
|
FloatingPointValidator<float> vldAmount(1, &mAmount);
|
2015-04-17 03:53:42 +00:00
|
|
|
vldAmount.SetMin(MIN_Amount);
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
/* i18n-hint: This is how many times longer the sound will be, e.g. applying
|
|
|
|
* the effect to a 1-second sample, with the default Stretch Factor of 10.0
|
|
|
|
* will give an (approximately) 10 second sound
|
|
|
|
*/
|
|
|
|
S.AddTextBox(_("Stretch Factor:"), wxT(""), 10)->SetValidator(vldAmount);
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-01-02 09:00:40 +00:00
|
|
|
FloatingPointValidator<float> vldTime(3, &mTime_resolution, NUM_VAL_ONE_TRAILING_ZERO);
|
2015-04-17 03:53:42 +00:00
|
|
|
vldTime.SetMin(MIN_Time);
|
|
|
|
S.AddTextBox(_("Time Resolution (seconds):"), wxT(""), 10)->SetValidator(vldTime);
|
|
|
|
}
|
|
|
|
S.EndMultiColumn();
|
|
|
|
};
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
bool EffectPaulstretch::TransferDataToWindow()
|
|
|
|
{
|
|
|
|
if (!mUIParent->TransferDataToWindow())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
bool EffectPaulstretch::TransferDataFromWindow()
|
|
|
|
{
|
|
|
|
if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
// EffectPaulstretch implementation
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
void EffectPaulstretch::OnText(wxCommandEvent & WXUNUSED(evt))
|
|
|
|
{
|
|
|
|
EnableApply(mUIParent->TransferDataFromWindow());
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2016-01-02 09:00:40 +00:00
|
|
|
int EffectPaulstretch::GetBufferSize(double rate)
|
|
|
|
{
|
|
|
|
// Audacity's fft requires a power of 2
|
|
|
|
float tmp = rate * mTime_resolution / 2.0;
|
|
|
|
tmp = log(tmp) / log(2.0);
|
|
|
|
tmp = pow(2.0, floor(tmp + 0.5));
|
|
|
|
|
|
|
|
return std::max<int>((int)tmp, 128);
|
|
|
|
}
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int count)
|
|
|
|
{
|
2016-01-02 09:00:40 +00:00
|
|
|
int stretch_buf_size = GetBufferSize(track->GetRate());
|
|
|
|
double amount = this->mAmount;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
sampleCount start = track->TimeToLongSamples(t0);
|
|
|
|
sampleCount end = track->TimeToLongSamples(t1);
|
|
|
|
sampleCount len = (sampleCount)(end - start);
|
|
|
|
|
2016-01-02 09:00:40 +00:00
|
|
|
int minDuration = stretch_buf_size * 2 + 1;
|
|
|
|
if (len < minDuration){ //error because the selection is too short
|
|
|
|
|
|
|
|
float maxTimeRes = log(len) / log(2.0);
|
|
|
|
maxTimeRes = pow(2.0, floor(maxTimeRes) + 0.5);
|
|
|
|
maxTimeRes = maxTimeRes / track->GetRate();
|
|
|
|
|
|
|
|
if (this->IsPreviewing()) {
|
|
|
|
double defaultPreviewLen;
|
|
|
|
gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &defaultPreviewLen, 6.0);
|
|
|
|
|
|
|
|
/* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
|
|
|
|
if ((minDuration / mProjectRate) < defaultPreviewLen) {
|
|
|
|
::wxMessageBox (wxString::Format(_("Audio selection too short to preview.\n\n"
|
|
|
|
"Try increasing the audio selection to at least %.1f seconds,\n"
|
|
|
|
"or reducing the 'Time Resolution' to less than %.1f seconds."),
|
|
|
|
(minDuration / track->GetRate()) + 0.05, // round up to 1/10 s.
|
|
|
|
floor(maxTimeRes * 10.0) / 10.0),
|
|
|
|
GetName(), wxOK | wxICON_EXCLAMATION);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
|
|
|
|
::wxMessageBox (wxString::Format(_("Unable to Preview.\n\n"
|
|
|
|
"For the current audio selection, the maximum\n"
|
|
|
|
"'Time Resolution' is %.1f seconds."),
|
|
|
|
floor(maxTimeRes * 10.0) / 10.0),
|
|
|
|
GetName(), wxOK | wxICON_EXCLAMATION);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* i18n-hint: 'Time Resolution' is the name of a control in the Paulstretch effect.*/
|
|
|
|
::wxMessageBox (wxString::Format(_("The 'Time Resolution' is too long for the selection.\n\n"
|
|
|
|
"Try increasing the audio selection to at least %.1f seconds,\n"
|
|
|
|
"or reducing the 'Time Resolution' to less than %.1f seconds."),
|
|
|
|
(minDuration / track->GetRate()) + 0.05, // round up to 1/10 s.
|
|
|
|
floor(maxTimeRes * 10.0) / 10.0),
|
|
|
|
GetName(), wxOK | wxICON_EXCLAMATION);
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
return false;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
double adjust_amount=(double)len/((double)len-((double)stretch_buf_size*2.0));
|
|
|
|
amount=1.0+(amount-1.0)*adjust_amount;
|
|
|
|
|
|
|
|
WaveTrack * outputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(),track->GetRate());
|
|
|
|
|
|
|
|
PaulStretch *stretch=new PaulStretch(amount,stretch_buf_size,track->GetRate());
|
|
|
|
|
|
|
|
sampleCount nget=stretch->get_nsamples_for_fill();
|
|
|
|
|
|
|
|
int bufsize=stretch->poolsize;
|
|
|
|
float *buffer0=new float[bufsize];
|
|
|
|
float *bufferptr0=buffer0;
|
|
|
|
sampleCount outs=0;
|
|
|
|
bool first_time=true;
|
|
|
|
|
|
|
|
int fade_len=100;
|
|
|
|
if (fade_len>(bufsize/2-1)) fade_len=bufsize/2-1;
|
|
|
|
float *fade_track_smps=new float[fade_len];
|
|
|
|
sampleCount s=0;
|
|
|
|
bool cancelled=false;
|
|
|
|
|
|
|
|
while (s<len){
|
|
|
|
track->Get((samplePtr)bufferptr0,floatSample,start+s,nget);
|
|
|
|
stretch->process(buffer0,nget);
|
|
|
|
|
|
|
|
if (first_time) {
|
|
|
|
stretch->process(buffer0,0);
|
|
|
|
};
|
|
|
|
|
|
|
|
outs+=stretch->out_bufsize;
|
|
|
|
s+=nget;
|
|
|
|
|
|
|
|
if (first_time){//blend the the start of the selection
|
|
|
|
track->Get((samplePtr)fade_track_smps,floatSample,start,fade_len);
|
|
|
|
first_time=false;
|
|
|
|
for (int i=0;i<fade_len;i++){
|
|
|
|
float fi=(float)i/(float)fade_len;
|
|
|
|
stretch->out_buf[i]=stretch->out_buf[i]*fi+(1.0-fi)*fade_track_smps[i];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
if (s>=len){//blend the end of the selection
|
|
|
|
track->Get((samplePtr)fade_track_smps,floatSample,end-fade_len,fade_len);
|
|
|
|
for (int i=0;i<fade_len;i++){
|
|
|
|
float fi=(float)i/(float)fade_len;
|
|
|
|
int i2=bufsize/2-1-i;
|
|
|
|
stretch->out_buf[i2]=stretch->out_buf[i2]*fi+(1.0-fi)*fade_track_smps[fade_len-1-i];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
outputTrack->Append((samplePtr)stretch->out_buf,floatSample,stretch->out_bufsize);
|
|
|
|
|
|
|
|
nget=stretch->get_nsamples();
|
|
|
|
if (TrackProgress(count, (s / (double) len))) {
|
|
|
|
cancelled=true;
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
delete [] fade_track_smps;
|
2012-06-10 16:08:43 +00:00
|
|
|
outputTrack->Flush();
|
|
|
|
|
|
|
|
track->Clear(t0,t1);
|
2016-01-02 09:00:40 +00:00
|
|
|
bool success = track->Paste(t0,outputTrack);
|
|
|
|
if (!cancelled && success){
|
|
|
|
m_t1 = mT0 + outputTrack->GetEndTime();
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
delete stretch;
|
|
|
|
delete []buffer0;
|
|
|
|
|
|
|
|
delete outputTrack;
|
|
|
|
return !cancelled;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*************************************************************/
|
|
|
|
|
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
PaulStretch::PaulStretch(float rap_,int in_bufsize_,float samplerate_)
|
|
|
|
{
|
2012-06-10 16:08:43 +00:00
|
|
|
samplerate=samplerate_;
|
|
|
|
rap=rap_;
|
|
|
|
in_bufsize=in_bufsize_;
|
|
|
|
if (rap<1.0) rap=1.0;
|
|
|
|
out_bufsize=in_bufsize;
|
|
|
|
if (out_bufsize<8) out_bufsize=8;
|
|
|
|
|
|
|
|
out_buf=new float[out_bufsize];
|
|
|
|
old_out_smp_buf=new float[out_bufsize*2];for (int i=0;i<out_bufsize*2;i++) old_out_smp_buf[i]=0.0;
|
|
|
|
|
|
|
|
poolsize=in_bufsize_*2;
|
|
|
|
in_pool=new float[poolsize];for (int i=0;i<poolsize;i++) in_pool[i]=0.0;
|
|
|
|
|
|
|
|
remained_samples=0.0;
|
|
|
|
|
|
|
|
fft_smps=new float[poolsize];
|
|
|
|
fft_s=new float[poolsize];
|
|
|
|
fft_c=new float[poolsize];
|
|
|
|
fft_freq=new float[poolsize];
|
|
|
|
fft_tmp=new float[poolsize];
|
|
|
|
for (int i=0;i<poolsize;i++) {
|
|
|
|
fft_smps[i]=0.0;
|
|
|
|
fft_c[i]=0.0;
|
|
|
|
fft_s[i]=0.0;
|
|
|
|
fft_freq[i]=0.0;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
PaulStretch::~PaulStretch()
|
|
|
|
{
|
2012-06-10 16:08:43 +00:00
|
|
|
delete [] out_buf;
|
|
|
|
delete [] old_out_smp_buf;
|
|
|
|
delete [] in_pool;
|
|
|
|
delete [] fft_smps;
|
|
|
|
delete [] fft_c;
|
|
|
|
delete [] fft_s;
|
|
|
|
delete [] fft_freq;
|
|
|
|
delete [] fft_tmp;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
void PaulStretch::set_rap(float newrap)
|
|
|
|
{
|
2012-06-10 16:08:43 +00:00
|
|
|
if (rap>=1.0) rap=newrap;
|
|
|
|
else rap=1.0;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
void PaulStretch::process(float *smps,int nsmps)
|
|
|
|
{
|
2012-06-10 16:08:43 +00:00
|
|
|
//add new samples to the pool
|
|
|
|
if ((smps!=NULL)&&(nsmps!=0)){
|
|
|
|
if (nsmps>poolsize){
|
|
|
|
nsmps=poolsize;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
int nleft=poolsize-nsmps;
|
|
|
|
|
|
|
|
//move left the samples from the pool to make room for new samples
|
|
|
|
for (int i=0;i<nleft;i++) in_pool[i]=in_pool[i+nsmps];
|
|
|
|
|
|
|
|
//add new samples to the pool
|
2014-06-03 20:30:19 +00:00
|
|
|
for (int i=0;i<nsmps;i++) in_pool[i+nleft]=smps[i];
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
//get the samples from the pool
|
|
|
|
for (int i=0;i<poolsize;i++) fft_smps[i]=in_pool[i];
|
|
|
|
WindowFunc(3,poolsize,fft_smps);
|
|
|
|
|
|
|
|
RealFFT(poolsize,fft_smps,fft_c,fft_s);
|
|
|
|
|
|
|
|
for (int i=0;i<poolsize/2;i++) fft_freq[i]=sqrt(fft_c[i]*fft_c[i]+fft_s[i]*fft_s[i]);
|
|
|
|
process_spectrum(fft_freq);
|
|
|
|
|
|
|
|
|
|
|
|
//put randomize phases to frequencies and do a IFFT
|
2012-06-17 01:51:05 +00:00
|
|
|
float inv_2p15_2pi=1.0/16384.0*(float)M_PI;
|
2012-06-10 16:08:43 +00:00
|
|
|
for (int i=1;i<poolsize/2;i++){
|
|
|
|
unsigned int random=(rand())&0x7fff;
|
|
|
|
float phase=random*inv_2p15_2pi;
|
|
|
|
float s=fft_freq[i]*sin(phase);
|
|
|
|
float c=fft_freq[i]*cos(phase);
|
|
|
|
|
|
|
|
fft_c[i]=fft_c[poolsize-i]=c;
|
|
|
|
|
|
|
|
fft_s[i]=s;fft_s[poolsize-i]=-s;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
fft_c[0]=fft_s[0]=0.0;
|
|
|
|
fft_c[poolsize/2]=fft_s[poolsize/2]=0.0;
|
|
|
|
|
|
|
|
FFT(poolsize,true,fft_c,fft_s,fft_smps,fft_tmp);
|
|
|
|
|
|
|
|
float max=0.0,max2=0.0;
|
|
|
|
for (int i=0;i<poolsize;i++){
|
|
|
|
float a=fabs(fft_tmp[i]);
|
|
|
|
if (a>max) max=a;
|
|
|
|
float b=fabs(fft_smps[i]);
|
|
|
|
if (b>max2) max2=b;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
//make the output buffer
|
|
|
|
float tmp=1.0/(float) out_bufsize*M_PI;
|
2012-06-17 01:51:05 +00:00
|
|
|
float hinv_sqrt2=0.853553390593f;//(1.0+1.0/sqrt(2))*0.5;
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
float ampfactor=1.0;
|
|
|
|
if (rap<1.0) ampfactor=rap*0.707;
|
|
|
|
else ampfactor=(out_bufsize/(float)poolsize)*4.0;
|
|
|
|
|
|
|
|
for (int i=0;i<out_bufsize;i++) {
|
|
|
|
float a=(0.5+0.5*cos(i*tmp));
|
|
|
|
float out=fft_smps[i+out_bufsize]*(1.0-a)+old_out_smp_buf[i]*a;
|
|
|
|
out_buf[i]=out*(hinv_sqrt2-(1.0-hinv_sqrt2)*cos(i*2.0*tmp))*ampfactor;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
//copy the current output buffer to old buffer
|
|
|
|
for (int i=0;i<out_bufsize*2;i++) old_out_smp_buf[i]=fft_smps[i];
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
int PaulStretch::get_nsamples()
|
|
|
|
{
|
2012-06-10 16:08:43 +00:00
|
|
|
double r=out_bufsize/rap;
|
|
|
|
int ri=(int)floor(r);
|
|
|
|
double rf=r-floor(r);
|
|
|
|
|
|
|
|
remained_samples+=rf;
|
|
|
|
if (remained_samples>=1.0){
|
|
|
|
ri+=(int)floor(remained_samples);
|
|
|
|
remained_samples=remained_samples-floor(remained_samples);
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
if (ri>poolsize){
|
|
|
|
ri=poolsize;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
|
|
|
return ri;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|
2012-06-10 16:08:43 +00:00
|
|
|
|
2015-04-17 03:53:42 +00:00
|
|
|
int PaulStretch::get_nsamples_for_fill()
|
2013-06-21 22:10:50 +00:00
|
|
|
{
|
2015-04-17 03:53:42 +00:00
|
|
|
return poolsize;
|
2016-01-02 09:00:40 +00:00
|
|
|
}
|