From 519bfb22686c7a2da6d6aa7d6d16719427eb94fa Mon Sep 17 00:00:00 2001 From: richardash1981 Date: Sun, 10 Jun 2012 16:08:43 +0000 Subject: [PATCH] Commit patch adding Paulstretch (an extreme time stretching effect for synthesis) by Nasca Octavian PAUL http://hypermammut.sourceforge.net/paulstretch/ as submitted --- src/Makefile.in | 1 + src/effects/LoadEffects.cpp | 2 + src/effects/Paulstretch.cpp | 485 ++++++++++++++++++++++++++++++++++++ src/effects/Paulstretch.h | 86 +++++++ 4 files changed, 574 insertions(+) create mode 100644 src/effects/Paulstretch.cpp create mode 100644 src/effects/Paulstretch.h diff --git a/src/Makefile.in b/src/Makefile.in index affe40e53..e07f796b6 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -168,6 +168,7 @@ OBJS = \ effects/Noise.o \ effects/NoiseRemoval.o \ effects/Normalize.o \ + effects/Paulstretch.o \ effects/Phaser.o \ effects/Repair.o \ effects/Repeat.o \ diff --git a/src/effects/LoadEffects.cpp b/src/effects/LoadEffects.cpp index 62c79bf16..cdbb58241 100644 --- a/src/effects/LoadEffects.cpp +++ b/src/effects/LoadEffects.cpp @@ -24,6 +24,7 @@ #include "Compressor.h" #include "DtmfGen.h" #include "Echo.h" +#include "Paulstretch.h" #include "Equalization.h" #include "Fade.h" #include "Invert.h" @@ -248,6 +249,7 @@ void LoadEffects() em.RegisterEffect(new EffectClickRemoval()); em.RegisterEffect(new EffectCompressor()); em.RegisterEffect(new EffectEcho()); + em.RegisterEffect(new EffectPaulstretch()); em.RegisterEffect(new EffectEqualization()); em.RegisterEffect(new EffectFadeIn(), SIMPLE_EFFECT); em.RegisterEffect(new EffectFadeOut(), SIMPLE_EFFECT); diff --git a/src/effects/Paulstretch.cpp b/src/effects/Paulstretch.cpp new file mode 100644 index 000000000..9eee4564e --- /dev/null +++ b/src/effects/Paulstretch.cpp @@ -0,0 +1,485 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +Paulstretch.cpp + +Nasca Octavian Paul +Some GUI code was took from Echo effect + + *******************************************************************/ + +/** + + \class EffectPaulstretch + \brief An Extreme Time Stretch and Time Smear effect + +*//****************************************************************/ + +/** + +\class PaulstretchDialog +\brief PaulstretchDialog used with EffectPaulstretch + +*/ + +/*******************************************************************/ + +#include "../Audacity.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Paulstretch.h" +#include "../WaveTrack.h" +#include "../FFT.h" + + +EffectPaulstretch::EffectPaulstretch(){ + amount=10.0; + time_resolution=0.25; +}; + +wxString EffectPaulstretch::GetEffectDescription(){ + // Note: This is useful only after values have been set. + return wxString::Format(_("Applied effect: %s amount= %f times, time resolution = %f seconds"), + this->GetEffectName().c_str(), amount,time_resolution); + +}; + +bool EffectPaulstretch::PromptUser(){ + PaulstretchDialog dlog(this, mParent); + dlog.amount = amount; + dlog.time_resolution = time_resolution; + dlog.CentreOnParent(); + dlog.ShowModal(); + + if (dlog.GetReturnCode() == wxID_CANCEL) + return false; + + amount = dlog.amount; + time_resolution = dlog.time_resolution; + + return true; +}; + +bool EffectPaulstretch::TransferParameters(Shuttle &shuttle){ + shuttle.TransferFloat(wxT("Amount"),amount,10.0); + shuttle.TransferFloat(wxT("Time Resolution"),time_resolution,0.25); + + return true; +}; + + +bool EffectPaulstretch::Process(){ + CopyInputTracks(); + SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks); + WaveTrack *track = (WaveTrack *) iter.First(); + m_t1=0.0; + 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; +} + +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(); + + void process(float *smps,int nsmps); + + int in_bufsize; + int poolsize;//how many samples are inside the input_pool size (need to know how many samples to fill when seeking) + + 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 *freq){}; + float samplerate; + private: + float *in_pool;//de marimea in_bufsize + float rap; + float *old_out_smp_buf; + + float *fft_smps,*fft_c,*fft_s,*fft_freq,*fft_tmp; + + double remained_samples;//how many fraction of samples has remained (0..1) +}; + + +bool EffectPaulstretch::ProcessOne(WaveTrack *track,double t0,double t1,int count){ + + int stretch_buf_size;//must be power of 2 (because Audacity's fft requires it) + if (time_resolution<0.001) time_resolution=0.001; + { + float tmp=track->GetRate()*time_resolution*0.5; + tmp=log(tmp)/log(2.0); + tmp=pow(2.0,floor(tmp+0.5)); + stretch_buf_size=(int)tmp; + }; + if (stretch_buf_size<128) stretch_buf_size=128; + double amount=this->amount; + if (amount<1.0) amount=1.0; + + sampleCount start = track->TimeToLongSamples(t0); + sampleCount end = track->TimeToLongSamples(t1); + sampleCount len = (sampleCount)(end - start); + + m_t1=mT1; + + if (len<=(stretch_buf_size*2+1)){//error because the selection is too short + ::wxMessageBox(_("Error on Paulstretch:\nThe selection is too small.\n It must be much larger than Time Resolution value.")); + return false; + }; + + + 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 (sGet((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;iout_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;iout_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; + }; + }; + + delete [] fade_track_smps; + outputTrack->Flush(); + + + + track->Clear(t0,t1); + track->Paste(t0,outputTrack); + if (!cancelled){ + double flen=t1-t0; + if (s>0) m_t1=t0+flen*(double)outs/(double)(s); + }; + + + delete stretch; + delete []buffer0; + + delete outputTrack; + return !cancelled; +}; + + + +/*************************************************************/ + + + + +PaulStretch::PaulStretch(float rap_,int in_bufsize_,float samplerate_){ + 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=1.0) rap=newrap; + else rap=1.0; +}; + +void PaulStretch::process(float *smps,int nsmps){ + //add new samples to the pool + if ((smps!=NULL)&&(nsmps!=0)){ + if (nsmps>poolsize){ + nsmps=poolsize; + }; + int nleft=poolsize-nsmps; + + //move left the samples from the pool to make room for new samples + for (int i=0;imax) max=a; + float b=fabs(fft_smps[i]); + if (b>max2) max2=b; + }; + + + //make the output buffer + float tmp=1.0/(float) out_bufsize*M_PI; + float hinv_sqrt2=0.853553390593;//(1.0+1.0/sqrt(2))*0.5; + + 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=1.0){ + ri+=(int)floor(remained_samples); + remained_samples=remained_samples-floor(remained_samples); + }; + + if (ri>poolsize){ + ri=poolsize; + }; + + return ri; +}; + +int PaulStretch::get_nsamples_for_fill(){ + return poolsize; +}; + + BEGIN_EVENT_TABLE(PaulstretchDialog, EffectDialog) + EVT_BUTTON(ID_EFFECT_PREVIEW, PaulstretchDialog::OnPreview) +END_EVENT_TABLE() + + PaulstretchDialog::PaulstretchDialog(EffectPaulstretch *effect, wxWindow *parent):EffectDialog(parent,wxT("Paulstretch")){ + m_bLoopDetect=false; + m_pEffect=effect; + + m_pTextCtrl_Amount=NULL; + m_pTextCtrl_TimeResolution=NULL; + + amount=10.0; + time_resolution=0.25; + + Init(); + }; + + +void PaulstretchDialog::PopulateOrExchange(ShuttleGui & S){ + S.StartHorizontalLay(wxCENTER, false); + { + S.AddTitle(_("by Nasca Octavian Paul")); + } + + S.EndHorizontalLay(); + + S.StartHorizontalLay(wxCENTER, false); + S.EndHorizontalLay(); + S.StartMultiColumn(2, wxALIGN_CENTER); + { + m_pTextCtrl_Amount = S.AddTextBox(_("Stretch amount:"),wxT("10.0"),10); + m_pTextCtrl_Amount->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + + m_pTextCtrl_TimeResolution= S.AddTextBox(_("Time resolution(seconds):"), wxT("0.25"),10); + m_pTextCtrl_TimeResolution->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + } + S.EndMultiColumn(); + +}; + +bool PaulstretchDialog::TransferDataToWindow(){ + m_bLoopDetect = true; + + wxString str; + if (m_pTextCtrl_Amount) { + str.Printf(wxT("%g"), amount); + m_pTextCtrl_Amount->SetValue(str); + } + if (m_pTextCtrl_TimeResolution) { + str.Printf(wxT("%g"), time_resolution); + m_pTextCtrl_TimeResolution->SetValue(str); + } + + m_bLoopDetect = false; + return true; +} +bool PaulstretchDialog::TransferDataFromWindow(){ + double newValue; + wxString str; + if (m_pTextCtrl_Amount) { + str = m_pTextCtrl_Amount->GetValue(); + str.ToDouble(&newValue); + amount = (float)(newValue); + } + if (m_pTextCtrl_TimeResolution) { + str = m_pTextCtrl_TimeResolution->GetValue(); + str.ToDouble(&newValue); + time_resolution = (float)(newValue); + } + return true; +} + +void PaulstretchDialog::OnPreview(wxCommandEvent &event){ + TransferDataFromWindow(); + + // Save & restore parameters around Preview, because we didn't do OK. + float oldAmount = m_pEffect->amount; + float oldTimeResolution = m_pEffect->time_resolution; + + m_pEffect->amount = amount; + m_pEffect->time_resolution = time_resolution; + + m_pEffect->Preview(); + + m_pEffect->amount = oldAmount; + m_pEffect->time_resolution = oldTimeResolution; +} + + + diff --git a/src/effects/Paulstretch.h b/src/effects/Paulstretch.h new file mode 100644 index 000000000..9fce6dc90 --- /dev/null +++ b/src/effects/Paulstretch.h @@ -0,0 +1,86 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + Paulstretch.h + + Nasca Octavian Paul + + **********************************************************************/ + +#ifndef __AUDACITY_EFFECT_PAULSTRETCH__ +#define __AUDACITY_EFFECT_PAULSTRETCH__ + +#include "SimpleMono.h" +#include +#include + + +class WaveTrack; + +class EffectPaulstretch:public Effect{ + + public: + EffectPaulstretch(); + + virtual wxString GetEffectName() { + return wxString(_("Paulstretch...")); + } + + virtual wxString GetEffectAction() { + return wxString(_("Paulstretch")); + } + + // Useful only after PromptUser values have been set. + virtual wxString GetEffectDescription(); + + virtual bool PromptUser(); + virtual bool TransferParameters( Shuttle & shuttle ); + virtual bool Process(); + + protected: + + bool ProcessOne(WaveTrack *track,double t0,double t1,int count); + float amount; + float time_resolution;//seconds + + friend class PaulstretchDialog; + private: + double m_t1; +}; + + +//-------------------------------------------------------------------------- +// PaulstretchDialog +//-------------------------------------------------------------------------- + +class PaulstretchDialog:public EffectDialog { + public: + PaulstretchDialog(EffectPaulstretch * effect, wxWindow * parent); + + void PopulateOrExchange(ShuttleGui & S); + bool TransferDataToWindow(); + bool TransferDataFromWindow(); + + private: + // handlers + void OnPreview( wxCommandEvent &event ); + + private: + bool m_bLoopDetect; + EffectPaulstretch * m_pEffect; + + // controls + wxTextCtrl * m_pTextCtrl_Amount; + wxTextCtrl * m_pTextCtrl_TimeResolution; + + public: + // effect parameters + float amount; + float time_resolution;//seconds + private: + DECLARE_EVENT_TABLE() +}; + + +#endif +