paulstretch_cpp

PaulStretch
Log | Files | Refs | LICENSE

commit e29b5f803e76f7f8ea8b765f3d5351c8daeed86b
parent 29d28c1dc630ba5c52cafbbc0159a86ab19aa7b6
Author: Nasca Octavian PAUL <[email protected]>
Date:   Tue,  1 Mar 2011 22:13:19 +0200

Changed the stretching algorithm

Diffstat:
MControl.cpp | 4++--
MInput/AInputS.cpp | 3++-
MPlayer.cpp | 6+++---
MProcessedStretch.cpp | 2+-
MStretch.cpp | 192+++++++++++++++++++++++++++++++++----------------------------------------------
MStretch.h | 26+++++++++++++++++++-------
6 files changed, 108 insertions(+), 125 deletions(-)

diff --git a/Control.cpp b/Control.cpp @@ -476,10 +476,10 @@ string Control::Render(string inaudio,string outaudio,FILE_TYPE outtype,FILE_TYP stretchl->set_parameters(&ppar); stretchr->set_parameters(&ppar); - outbufsize=stretchl->out_bufsize; + outbufsize=stretchl->get_bufsize(); int *outbuf=new int[outbufsize*2]; - int poolsize=stretchl->poolsize; + int poolsize=stretchl->get_max_bufsize(); inbuf.l=new REALTYPE[poolsize]; inbuf.r=new REALTYPE[poolsize]; diff --git a/Input/AInputS.cpp b/Input/AInputS.cpp @@ -72,10 +72,11 @@ int AInputS::read(int nsmps,short int *smps){ int readed=afReadFrames(handle,AF_DEFAULT_TRACK,smps,nsmps); info.currentsample+=readed; if (readed!=nsmps) { + if (readed>=0) for (int i=readed;i<nsmps;i++) smps[i]=0; info.currentsample=info.nsamples; eof=true; }; - return readed; + return nsmps; }; void AInputS::seek(double pos){ diff --git a/Player.cpp b/Player.cpp @@ -270,7 +270,7 @@ void Player::newtaskcheck(){ if (stretchr) stretchr->set_parameters(task.ppar); if (binaural_beats) binaural_beats->pars=*(task.bbpar); - inbufsize=stretchl->poolsize; + inbufsize=stretchl->get_max_bufsize(); if (inbuf.l) delete []inbuf.l;inbuf.l=NULL; if (inbuf.r) delete []inbuf.r;inbuf.r=NULL; inbuf.l=new REALTYPE[inbufsize]; @@ -304,7 +304,7 @@ void Player::newtaskcheck(){ }; first_in_buf=true; - outbuf.size=stretchl->out_bufsize; + outbuf.size=stretchl->get_bufsize(); int min_samples=ai->info.samplerate*2; int n=2*PA_SOUND_BUFFER_SIZE/outbuf.size; @@ -407,7 +407,7 @@ void Player::computesamples(){ stretchr->window_type=window_type; stretchl->process(inbuf.l,readsize); stretchr->process(inbuf.r,readsize); - binaural_beats->process(stretchl->out_buf,stretchr->out_buf,stretchl->out_bufsize,in_pos_100); + binaural_beats->process(stretchl->out_buf,stretchr->out_buf,stretchl->get_bufsize(),in_pos_100); // stretchl->process_output(stretchl->out_buf,stretchl->out_bufsize); // stretchr->process_output(stretchr->out_buf,stretchr->out_bufsize); diff --git a/ProcessedStretch.cpp b/ProcessedStretch.cpp @@ -120,7 +120,7 @@ void ProcessParameters::getfromXML(XMLwrapper *xml){ }; ProcessedStretch::ProcessedStretch(REALTYPE rap_,int in_bufsize_,FFTWindow w,bool bypass_,REALTYPE samplerate_,int stereo_mode_):Stretch(rap_,in_bufsize_,w,bypass_,samplerate_,stereo_mode_){ - nfreq=out_bufsize; + nfreq=bufsize; infreq=new REALTYPE[nfreq]; sumfreq=new REALTYPE[nfreq]; tmpfreq1=new REALTYPE[nfreq]; diff --git a/Stretch.cpp b/Stretch.cpp @@ -141,38 +141,39 @@ void FFT::applywindow(FFTWindow type){ /*******************************************/ -Stretch::Stretch(REALTYPE rap_,int in_bufsize_,FFTWindow w,bool bypass_,REALTYPE samplerate_,int stereo_mode_){ +Stretch::Stretch(REALTYPE rap_,int bufsize_,FFTWindow w,bool bypass_,REALTYPE samplerate_,int stereo_mode_){ + samplerate=samplerate_; rap=rap_; - in_bufsize=in_bufsize_; + bufsize=bufsize_; bypass=bypass_; stereo_mode=stereo_mode_; - if (rap>=1.0){//stretch - out_bufsize=in_bufsize; - }else{ - //shorten - out_bufsize=(int)(in_bufsize*rap); + if (bufsize<8) bufsize=8; + + out_buf=new REALTYPE[bufsize]; + old_freq=new REALTYPE[bufsize]; + old_smps=new REALTYPE[bufsize]; + old_out_smps=new REALTYPE[bufsize*2]; + for (int i=0;i<bufsize*2;i++) old_out_smps[i]=0.0; + for (int i=0;i<bufsize;i++) { + old_freq[i]=0.0; + old_smps[i]=0.0; }; - if (out_bufsize<8) out_bufsize=8; - - if (bypass) out_bufsize=in_bufsize; - out_buf=new REALTYPE[out_bufsize]; - old_out_smp_buf=new REALTYPE[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 REALTYPE[poolsize];for (int i=0;i<poolsize;i++) in_pool[i]=0.0; - - infft=new FFT(poolsize); - outfft=new FFT(out_bufsize*2); + infft=new FFT(bufsize*2); + outfft=new FFT(bufsize*2); remained_samples=0.0; window_type=w; + require_new_buffer=false; + c_pos_percents=0.0; }; Stretch::~Stretch(){ + delete [] old_freq; delete [] out_buf; - delete [] old_out_smp_buf; - delete [] in_pool; + delete [] old_smps; + delete [] old_out_smps; delete infft; delete outfft; }; @@ -180,123 +181,92 @@ Stretch::~Stretch(){ void Stretch::set_rap(REALTYPE newrap){ if ((rap>=1.0)&&(newrap>=1.0)) rap=newrap; }; + +void Stretch::do_analyse_inbuf(REALTYPE *smps){ + //get the frequencies + for (int i=0;i<bufsize;i++) { + infft->smp[i]=old_smps[i]; + infft->smp[i+bufsize]=smps[i]; + + old_freq[i]=infft->freq[i]; + old_smps[i]=smps[i]; + }; + infft->applywindow(window_type); + infft->smp2freq(); +}; void Stretch::process(REALTYPE *smps,int nsmps){ if (bypass){ - for (int i=0;i<out_bufsize;i++) out_buf[i]=smps[i]; + for (int i=0;i<bufsize;i++) out_buf[i]=smps[i]; //post-process the output - // process_output(out_buf,out_bufsize); + // process_output(out_buf,bufsize); return; }; - //add new samples to the pool - if ((smps!=NULL)&&(nsmps!=0)){ - if (nsmps>poolsize){ - printf("Warning nsmps> inbufsize on Stretch::process() %d>%d\n",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;i<nleft;i++) in_pool[i]=in_pool[i+nsmps]; - - //add new samples to the pool - for (int i=0;i<nsmps;i++) in_pool[i+nleft]=smps[i]; - }; - - //get the samples from the pool - for (int i=0;i<poolsize;i++) infft->smp[i]=in_pool[i]; - - - infft->applywindow(window_type); - infft->smp2freq(); - - if (out_bufsize==in_bufsize){//output is the same as the input (as usual) - for (int i=0;i<in_bufsize;i++) outfft->freq[i]=infft->freq[i]; - } else { - if (out_bufsize>in_bufsize){//output is longer - REALTYPE rap=(REALTYPE)in_bufsize/(REALTYPE)out_bufsize; - for (int i=0;i<out_bufsize;i++) { - REALTYPE pos=i*rap; - int poshi=(int)floor(pos); - REALTYPE poslo=pos-floor(pos); - outfft->freq[i]=infft->freq[poshi]*(1.0-poslo)+infft->freq[poshi+1]*poslo; - }; - }else{//output is shorter - for (int i=0;i<out_bufsize;i++) outfft->freq[i]=0.0; - REALTYPE rap=(REALTYPE)out_bufsize/(REALTYPE)in_bufsize; - for (int i=0;i<in_bufsize;i++) { - REALTYPE pos=i*rap; - int poshi=(int)(floor(pos)); - // #warning sa folosesc si poslo - outfft->freq[poshi]+=infft->freq[i]; - }; + if (smps!=NULL){ + if ((nsmps!=0)&&(nsmps!=bufsize)&&(nsmps!=bufsize*2)){ + printf("Warning wrong nsmps on Stretch::process() %d,%d\n",nsmps,bufsize); + return; + }; + if (nsmps!=0){//new data arrived: update the frequency components + do_analyse_inbuf(smps); + if (nsmps==bufsize*2) do_analyse_inbuf(smps+bufsize); + }; + //compute the output spectrum + for (int i=0;i<bufsize;i++) { + outfft->freq[i]=infft->freq[i]*remained_samples+old_freq[i]*(1.0-remained_samples); + }; + + process_spectrum(outfft->freq); + + outfft->freq2smp(); + + //make the output buffer + REALTYPE tmp=1.0/(float) bufsize*M_PI; + REALTYPE hinv_sqrt2=0.853553390593;//(1.0+1.0/sqrt(2))*0.5; + + REALTYPE ampfactor=2.0; + + //remove the resulted unwanted amplitude modulation (caused by the interference of N and N+1 windowed buffer and compute the output buffer + for (int i=0;i<bufsize;i++) { + REALTYPE a=(0.5+0.5*cos(i*tmp)); + REALTYPE out=outfft->smp[i+bufsize]*(1.0-a)+old_out_smps[i]*a; + out_buf[i]=out*(hinv_sqrt2-(1.0-hinv_sqrt2)*cos(i*2.0*tmp))*ampfactor; }; - }; - - process_spectrum(outfft->freq); - - outfft->freq2smp(); - - //make the output buffer - REALTYPE tmp=1.0/(float) out_bufsize*M_PI; - REALTYPE hinv_sqrt2=0.853553390593;//(1.0+1.0/sqrt(2))*0.5; - REALTYPE ampfactor=1.0; - if (rap<1.0) ampfactor=rap*0.707; - else ampfactor=(out_bufsize/(float)poolsize)*4.0; + //copy the current output buffer to old buffer + for (int i=0;i<bufsize*2;i++) old_out_smps[i]=outfft->smp[i]; - for (int i=0;i<out_bufsize;i++) { - REALTYPE a=(0.5+0.5*cos(i*tmp)); - REALTYPE out=outfft->smp[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; }; - //copy the current output buffer to old buffer - for (int i=0;i<out_bufsize*2;i++) old_out_smp_buf[i]=outfft->smp[i]; - - //post-process the output - //process_output(out_buf,out_bufsize); -}; - - -int Stretch::get_nsamples(REALTYPE current_pos_percents){ - if (bypass) return out_bufsize; - if (rap<1.0) return poolsize/2;//pentru shorten + long double used_rap=rap*get_stretch_multiplier(c_pos_percents); - - long double used_rap=rap*get_stretch_multiplier(current_pos_percents); - - long double r=out_bufsize/used_rap; - int ri=(int)floor(r); - long double rf=r-floor(r); + long double r=1.0/used_rap; long double old_remained_samples_test=remained_samples; - remained_samples+=rf; + remained_samples+=r; + int result=0; if (remained_samples>=1.0){ - ri+=(int)floor(remained_samples); remained_samples=remained_samples-floor(remained_samples); + require_new_buffer=true; + }else{ + require_new_buffer=false; }; - long double rf_test=remained_samples-old_remained_samples_test;//this value should be almost like "rf" (for most of the time with the exception of changing the "ri" value) for extremely long stretches (otherwise the shown stretch value is not accurate) +// long double rf_test=remained_samples-old_remained_samples_test;//this value should be almost like "rf" (for most of the time with the exception of changing the "ri" value) for extremely long stretches (otherwise the shown stretch value is not accurate) //for stretch up to 10^18x "long double" must have at least 64 bits in the fraction part (true for gcc compiler on x86 and macosx) + +}; -// long double zzz=1.0;//quick test by adding a "largish" number and substracting it again -// rf_test+=zzz; -// rf_test-=zzz; - -// printf("remained_samples=%.20Lg rf=%.20Lg rf_test=%.20Lg\n",remained_samples,rf,rf_test); -// printf("rf=%g rf_test=%g\n",(double)rf,(double)(rf_test)); - - if (ri>poolsize){ - ri=poolsize; - }; - return ri; +int Stretch::get_nsamples(REALTYPE current_pos_percents){ + if (bypass) return bufsize; + c_pos_percents=current_pos_percents; + return require_new_buffer?bufsize:0; }; int Stretch::get_nsamples_for_fill(){ - return poolsize; + return bufsize*2; }; REALTYPE Stretch::get_stretch_multiplier(REALTYPE pos_percents){ diff --git a/Stretch.h b/Stretch.h @@ -62,33 +62,45 @@ class Stretch{ //in_bufsize is also a half of a FFT buffer (in samples) virtual ~Stretch(); + int get_max_bufsize(){ + return bufsize*2; + }; + int get_bufsize(){ + return bufsize; + }; + void process(REALTYPE *smps,int nsmps); // virtual void process_output(REALTYPE *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; REALTYPE *out_buf;//pot sa pun o variabila "max_out_bufsize" si asta sa fie marimea lui out_buf si pe out_bufsize sa il folosesc ca marime adaptiva - int get_nsamples(REALTYPE current_pos_percents);//how many samples are required to be added in the pool next time + int get_nsamples(REALTYPE current_pos_percents);//how many samples are required 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(REALTYPE newrap);//set the current stretch value FFTWindow window_type; protected: + int bufsize; + virtual void process_spectrum(REALTYPE *freq){}; virtual REALTYPE get_stretch_multiplier(REALTYPE pos_percents); REALTYPE samplerate; int stereo_mode;//0=mono,1=left,2=right private: - REALTYPE *in_pool;//de marimea in_bufsize + + void do_analyse_inbuf(REALTYPE *smps); + +// REALTYPE *in_pool;//de marimea in_bufsize REALTYPE rap; - REALTYPE *old_out_smp_buf; + REALTYPE *old_out_smps; + REALTYPE *old_freq,*old_smps; FFT *infft,*outfft; - long double remained_samples;//how many fraction of samples has remained (0..1) + long double remained_samples;//0..1 + REALTYPE c_pos_percents; + bool require_new_buffer; bool bypass; };