/* Copyright (C) 2006-2011 Nasca Octavian Paul Author: Nasca Octavian Paul This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License (version 2) for more details. You should have received a copy of the GNU General Public License (version 2) along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "globals.h" #include "Control.h" #include "XMLwrapper.h" using namespace std; Control::Control(){ player=new Player(); player->start(); wavinfo.samplerate=44100; wavinfo.nsamples=0; wavinfo.intype=FILE_WAV; wav32bit=false; process.bufsize=16384; process.stretch=4.0; process.onset_detection_sensitivity=0.0; seek_pos=0.0; window_type=W_HANN; info.render_percent=-1.0; info.cancel_render=false; volume=1.0; gui_sliders.fftsize_s=0.5; gui_sliders.stretch_s=0.5; gui_sliders.mode_s=0; ///#warning test /// process.transient.enable=true; }; Control::~Control(){ // delete player; face crash daca il las }; bool Control::set_input_filename(string filename,FILE_TYPE intype){ InputS *ai=NULL; if (intype==FILE_VORBIS) ai=new VorbisInputS; if (intype==FILE_MP3) ai=new MP3InputS; if (intype==FILE_WAV) ai=new AInputS; if (!ai) return false; wavinfo.filename=filename; wavinfo.intype=intype; bool result=ai->open(wavinfo.filename); if (result) { wavinfo.samplerate=ai->info.samplerate; wavinfo.nsamples=ai->info.nsamples; /// if (process.transient.enable) { /// pre_analyse_whole_audio(ai); /// }; delete ai; }else{ wavinfo.filename=""; wavinfo.samplerate=0; wavinfo.nsamples=0; delete ai; }; return result; }; string Control::get_input_filename(){ return wavinfo.filename; }; string Control::get_input_filename_and_info(){ int seconds=wavinfo.nsamples/wavinfo.samplerate; const int size=200; char tmp[size];tmp[size-1]=0; snprintf(tmp,size-1," ( samplerate=%d; duration=%02d:%02d:%02d )",wavinfo.samplerate,seconds/3600,(seconds/60)%60,seconds%60); string filename=wavinfo.filename; int len=filename.length(); if (len>70)filename=filename.substr(0,25)+"..."+filename.substr(len-35); return filename+tmp; }; /*string Control::get_recommanded_output_filename(){ return "none"; }; */ std::string Control::get_stretch_info(){ const int size=200; char tmp[size];tmp[size-1]=0; if (wavinfo.nsamples==0) return "Stretch: "; double realduration=wavinfo.nsamples/wavinfo.samplerate*process.stretch; if (realduration>(365.25*86400.0*1.0e12)){//more than 1 trillion years double duration=(realduration/(365.25*86400.0*1.0e12));//my snprintf(tmp,size,"Stretch: %.7gx (%g trillion years)",process.stretch,duration); return tmp; }; if (realduration>(365.25*86400.0*1.0e9)){//more than 1 billion years double duration=(realduration/(365.25*86400.0*1.0e9));//my snprintf(tmp,size,"Stretch: %.7gx (%g billion years)",process.stretch,duration); return tmp; }; if (realduration>(365.25*86400.0*1.0e6)){//more than 1 million years double duration=(realduration/(365.25*86400.0*1.0e6));//my snprintf(tmp,size,"Stretch: %.7gx (%g million years)",process.stretch,duration); return tmp; }; if (realduration>(365.25*86400.0*2000.0)){//more than two millenniums int duration=(int)(realduration/(365.25*86400.0));//years int years=duration%1000; int milleniums=duration/1000; char stryears[size];stryears[0]=0; if (years!=0){ if (years==1) snprintf(stryears,size," 1 year"); else snprintf(stryears,size," %d years",years); }; snprintf(tmp,size,"Stretch: %.7gx (%d milleniums%s)",process.stretch,milleniums,stryears); return tmp; }; if (realduration>(365.25*86400.0)){//more than 1 year int duration=(int) (realduration/3600.0);//hours int hours=duration%24; int days=(duration/24)%365; int years=duration/(365*24); char stryears[size];stryears[0]=0; if (years==1) snprintf(stryears,size,"1 year "); else snprintf(stryears,size,"%d years ",years); char strdays[size];strdays[0]=0; if (days>0){ if (days==1) snprintf(strdays,size,"1 day"); else snprintf(strdays,size,"%d days",days); }; if (years>=10) hours=0; char strhours[size];strhours[0]=0; if (hours>0){ snprintf(strhours,size," %d h",hours); }; snprintf(tmp,size,"Stretch: %.7gx (%s%s%s)",process.stretch,stryears,strdays,strhours); return tmp; }else{//less than 1 year int duration=(int)(realduration);//seconds char strdays[size];strdays[0]=0; int days=duration/86400; if (days>0){ if (days==1) snprintf(strdays,size,"1 day "); else snprintf(strdays,size,"%d days ",duration/86400); }; REALTYPE stretch=process.stretch; if (stretch>=1.0){ stretch=((int) (stretch*100.0))*0.01; }; snprintf(tmp,size,"Stretch: %.7gx (%s%.2d:%.2d:%.2d)", stretch,strdays,(duration/3600)%24,(duration/60)%60,duration%60); return tmp; }; return ""; }; string Control::get_fftsize_info(){ const int size=200; char tmp[size];tmp[size-1]=0; string fftsizelabel; fftsizelabel+="Window size (samples): "; if (wavinfo.nsamples==0) return fftsizelabel; fftsizelabel+=getfftsizestr(process.bufsize); return fftsizelabel; }; string Control::get_fftresolution_info(){ string resolution="Resolution: "; if (wavinfo.nsamples==0) return resolution; //todo: unctime and uncfreq are correct computed? Need to check later. REALTYPE unctime=process.bufsize/(REALTYPE)wavinfo.samplerate*sqrt(2.0); REALTYPE uncfreq=1.0/unctime*sqrt(2.0); char tmp[100]; snprintf(tmp,100,"%.5g seconds",unctime);resolution+=tmp; snprintf(tmp,100," (%.5g Hz)",uncfreq);resolution+=tmp; return resolution; }; void Control::startplay(bool bypass){ if ((!player->info.playing)||(player->info.samplerate!=wavinfo.samplerate)){ stopplay(); sleep(200); #ifdef HAVE_JACK JACKaudiooutputinit(player,wavinfo.samplerate); #else PAaudiooutputinit(player,wavinfo.samplerate); #endif }; if (wavinfo.filename!="") player->startplay(wavinfo.filename,seek_pos,process.stretch,process.bufsize,wavinfo.intype,bypass,&ppar,&bbpar); // sleep(100); // update_process_parameters(); }; void Control::stopplay(){ player->stop(); player->seek(0.0); seek_pos=0; #ifdef HAVE_JACK JACKclose(); #else PAfinish(); #endif }; void Control::pauseplay(){ player->pause(); }; void Control::freezeplay(){ player->freeze(); }; void Control::set_volume(REALTYPE vol){ volume=vol; player->set_volume(vol); }; void Control::set_seek_pos(REALTYPE x){ seek_pos=x; player->seek(x); }; REALTYPE Control::get_seek_pos(){ if (player->getmode()==Player::MODE_PLAY) seek_pos=player->info.position; return seek_pos; }; void Control::set_stretch_controls(double stretch_s,int mode,double fftsize_s,double onset_detection_sensitivity){ gui_sliders.stretch_s=stretch_s; gui_sliders.mode_s=mode; gui_sliders.fftsize_s=fftsize_s; double stretch=1.0; switch(mode){ case 0: stretch_s=pow(stretch_s,1.2); stretch=pow(10.0,stretch_s*4.0); break; case 1: stretch_s=pow(stretch_s,1.5); stretch=pow(10.0,stretch_s*18.0); break; case 2: stretch=1.0/pow(10.0,stretch_s*2.0); break; }; fftsize_s=pow(fftsize_s,1.5); int bufsize=(int)(pow(2.0,fftsize_s*12.0)*512.0); bufsize=optimizebufsize(bufsize); process.stretch=stretch; process.bufsize=bufsize; process.onset_detection_sensitivity=onset_detection_sensitivity; }; double Control::get_stretch_control(double stretch,int mode){ double result=1.0; switch(mode){ case 0: if (stretch<1.0) return -1; stretch=(log(stretch)/log(10))*0.25; result=pow(stretch,1.0/1.2); break; case 1: if (stretch<1.0) return -1; stretch=(log(stretch)/log(10))/18.0; result=pow(stretch,1.0/1.5); break; case 2: if (stretch>1.0) return -1; result=2.0/(log(stretch)/log(10)); break; }; return result; }; void Control::update_player_stretch(){ player->setrap(process.stretch); player->set_onset_detection_sensitivity(process.onset_detection_sensitivity); }; int abs_val(int x){ if (x<0) return -x; else return x; }; int Control::get_optimized_updown(int n,bool up){ int orig_n=n; while(true){ n=orig_n; #ifndef KISSFFT while (!(n%11)) n/=11; while (!(n%7)) n/=7; #endif while (!(n%5)) n/=5; while (!(n%3)) n/=3; while (!(n%2)) n/=2; if (n<2) break; if (up) orig_n++; else orig_n--; if (orig_n<4) return 4; }; return orig_n; }; int Control::optimizebufsize(int n){ int n1=get_optimized_updown(n,false); int n2=get_optimized_updown(n,true); if ((n-n1)<(n2-n)) return n1; else return n2; }; void Control::set_window_type(FFTWindow window){ window_type=window; if (player) player->set_window_type(window); }; string Control::Render(string inaudio,string outaudio,FILE_TYPE outtype,FILE_TYPE intype,REALTYPE pos1,REALTYPE pos2){ if (pos2open(inaudio)){ return "Error: Could not open audio file (or file format not recognized) :"+inaudio; }; BinauralBeats bb(ai->info.samplerate); bb.pars=bbpar; if (outtype==FILE_WAV) ao.newfile(outaudio,ai->info.samplerate,wav32bit); if (outtype==FILE_VORBIS) vorbisout.newfile(outaudio,ai->info.samplerate); ai->seek(pos1); int inbufsize=process.bufsize; if (inbufsize<32) inbufsize=32; short int *inbuf_i=new short int[inbufsize*8]; int outbufsize; struct{ REALTYPE *l,*r; }inbuf; ProcessedStretch *stretchl=new ProcessedStretch(process.stretch,inbufsize,window_type,false,ai->info.samplerate,1); ProcessedStretch *stretchr=new ProcessedStretch(process.stretch,inbufsize,window_type,false,ai->info.samplerate,2); stretchl->set_onset_detection_sensitivity(process.onset_detection_sensitivity); stretchr->set_onset_detection_sensitivity(process.onset_detection_sensitivity); stretchl->set_parameters(&ppar); stretchr->set_parameters(&ppar); outbufsize=stretchl->get_bufsize(); int *outbuf=new int[outbufsize*2]; int poolsize=stretchl->get_max_bufsize(); inbuf.l=new REALTYPE[poolsize]; inbuf.r=new REALTYPE[poolsize]; for (int i=0;ieof){ float in_pos=(REALTYPE) ai->info.currentsample/(REALTYPE)ai->info.nsamples; if (firstbuf){ readsize=stretchl->get_nsamples_for_fill(); firstbuf=false; }else{ readsize=stretchl->get_nsamples(in_pos*100.0); }; int readed=0; if (readsize!=0) readed=ai->read(readsize,inbuf_i); for (int i=0;iprocess(inbuf.l,readed); REALTYPE onset_r=stretchr->process(inbuf.r,readed); REALTYPE onset=(onset_l>onset_r)?onset_l:onset_r; stretchl->here_is_onset(onset); stretchr->here_is_onset(onset); bb.process(stretchl->out_buf,stretchr->out_buf,outbufsize,in_pos*100.0); for (int i=0;iout_buf[i]*=volume; stretchr->out_buf[i]*=volume; }; int nskip=stretchl->get_skip_nsamples(); if (nskip>0) ai->skip(nskip); if (outtype==FILE_WAV){ for (int i=0;iout_buf[i],r=stretchr->out_buf[i]; if (l<-1.0) l=-1.0; else if (l>1.0) l=1.0; if (r<-1.0) r=-1.0; else if (r>1.0) r=1.0; outbuf[i*2]=(int)(l*32767.0*65536.0); outbuf[i*2+1]=(int)(r*32767.0*65536.0); }; ao.write(outbufsize,outbuf); }; if (outtype==FILE_VORBIS) vorbisout.write(outbufsize,stretchl->out_buf,stretchr->out_buf); REALTYPE totalf=ai->info.currentsample/(REALTYPE)ai->info.nsamples-pos1; if (totalf>(pos2-pos1)) break; info.render_percent=(totalf*100.0/(pos2-pos1+0.001)); pause_write+=outbufsize; if (pause_write>pause_max_write){ float tmp=outbufsize/1000000.0; if (tmp>0.1) tmp=0.1; Fl::wait(0.01+tmp); pause_write=0; if (info.cancel_render) break; }; }; delete stretchl; delete stretchr; delete []outbuf; delete []inbuf_i; delete []inbuf.l; delete []inbuf.r; info.render_percent=-1.0; return ""; }; string Control::getfftsizestr(int fftsize){ int size=100; char tmp[size];tmp[size-1]=0; if (fftsize<1024.0) snprintf(tmp,size-1,"%d",fftsize); else if (fftsize<(1024.0*1024.0)) snprintf(tmp,size-1,"%.4gK",fftsize/1024.0); else if (fftsize<(1024.0*1024.0*1024.0)) snprintf(tmp,size-1,"%.4gM",fftsize/(1024.0*1024.0)); else snprintf(tmp,size-1,"%.7gG",fftsize/(1024.0*1024.0*1024.0)); return tmp; }; void Control::update_process_parameters(){ if (player) player->set_process_parameters(&ppar,&bbpar); }; bool Control::save_parameters(const char *filename){ XMLwrapper *xml=new XMLwrapper(); xml->beginbranch("PAULSTRETCH"); xml->beginbranch("STRETCH_PARAMETERS"); xml->beginbranch("BASE"); xml->addpar("bufsize",process.bufsize); xml->addparreal("stretch",process.stretch); xml->addparreal("fftsize_s",gui_sliders.fftsize_s); xml->addparreal("stretch_s",gui_sliders.stretch_s); xml->addpar("mode_s",gui_sliders.mode_s); xml->addpar("window_type",window_type); xml->addparreal("volume",volume); xml->addparreal("onset_detection_sensitivity",process.onset_detection_sensitivity); xml->endbranch(); xml->beginbranch("PROCESS"); ppar.add2XML(xml); xml->endbranch(); xml->beginbranch("BINAURAL_BEATS"); bbpar.add2XML(xml); xml->endbranch(); xml->endbranch(); xml->endbranch(); int result=xml->saveXMLfile(filename); delete xml; return true; }; bool Control::load_parameters(const char *filename){ XMLwrapper *xml=new XMLwrapper(); if (xml->loadXMLfile(filename)<0) { delete xml; return false; }; if (xml->enterbranch("PAULSTRETCH")==0) { delete xml; return false; }; if (xml->enterbranch("STRETCH_PARAMETERS")){ if (xml->enterbranch("BASE")){ process.bufsize=xml->getpar("bufsize",process.bufsize,16,2e9); process.stretch=xml->getparreal("stretch",process.stretch); gui_sliders.fftsize_s=xml->getparreal("fftsize_s",gui_sliders.fftsize_s); gui_sliders.stretch_s=xml->getparreal("stretch_s",gui_sliders.stretch_s); gui_sliders.mode_s=xml->getpar("mode_s",gui_sliders.mode_s,0,2); window_type=(FFTWindow)xml->getpar("window_type",window_type,0,4); process.onset_detection_sensitivity=xml->getparreal("onset_detection_sensitivity",0.0); volume=xml->getparreal("volume",1.0); xml->exitbranch(); }; if (xml->enterbranch("PROCESS")){ ppar.getfromXML(xml); xml->exitbranch(); }; if (xml->enterbranch("BINAURAL_BEATS")){ bbpar.getfromXML(xml); xml->exitbranch(); }; xml->exitbranch(); }; delete xml; set_stretch_controls(gui_sliders.stretch_s,gui_sliders.mode_s,gui_sliders.fftsize_s,process.onset_detection_sensitivity); set_window_type(window_type); set_volume(volume); update_process_parameters(); return true; };