paulstretch_cpp

PaulStretch
Log | Files | Refs | LICENSE

Control.cpp (15465B)


      1 /*
      2   Copyright (C) 2006-2011 Nasca Octavian Paul
      3   Author: Nasca Octavian Paul
      4 
      5   This program is free software; you can redistribute it and/or modify
      6   it under the terms of version 2 of the GNU General Public License 
      7   as published by the Free Software Foundation.
      8 
      9   This program is distributed in the hope that it will be useful,
     10   but WITHOUT ANY WARRANTY; without even the implied warranty of
     11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12   GNU General Public License (version 2) for more details.
     13 
     14   You should have received a copy of the GNU General Public License (version 2)
     15   along with this program; if not, write to the Free Software Foundation,
     16   Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     17 */
     18 #include <math.h>
     19 #include <stdlib.h>
     20 #include <FL/Fl.H>
     21 #include "globals.h"
     22 #include "Control.h"
     23 #include "XMLwrapper.h"
     24 using namespace std;
     25 
     26 Control::Control(){
     27 	player=new Player();
     28 	player->start();
     29 
     30 	wavinfo.samplerate=44100;
     31 	wavinfo.nsamples=0;
     32 	wavinfo.intype=FILE_WAV;
     33 	wav32bit=false;
     34 
     35 	process.bufsize=16384;
     36 	process.stretch=4.0;
     37 	process.onset_detection_sensitivity=0.0;
     38 
     39 	seek_pos=0.0;
     40 	window_type=W_HANN;
     41 	info.render_percent=-1.0;
     42 	info.cancel_render=false;
     43 	volume=1.0;
     44 
     45 
     46 	gui_sliders.fftsize_s=0.5;
     47 	gui_sliders.stretch_s=0.5;
     48 	gui_sliders.mode_s=0;
     49 
     50 
     51 ///#warning test
     52 ///	process.transient.enable=true;
     53 };
     54 
     55 Control::~Control(){
     56 	//    delete player; face crash daca il las
     57 };
     58 	
     59 
     60 bool Control::set_input_filename(string filename,FILE_TYPE intype){
     61 	InputS *ai=NULL;
     62 	if (intype==FILE_VORBIS) ai=new VorbisInputS;
     63 	if (intype==FILE_MP3) ai=new MP3InputS;
     64 	if (intype==FILE_WAV) ai=new AInputS;
     65 	if (!ai) return false;
     66 	wavinfo.filename=filename;
     67 	wavinfo.intype=intype;
     68 	bool result=ai->open(wavinfo.filename);
     69 	if (result) {
     70 		wavinfo.samplerate=ai->info.samplerate;
     71 		wavinfo.nsamples=ai->info.nsamples;
     72 ///		if (process.transient.enable) {
     73 ///			pre_analyse_whole_audio(ai);
     74 ///		};
     75 
     76 		delete ai;
     77 	}else{
     78 		wavinfo.filename="";
     79 		wavinfo.samplerate=0;
     80 		wavinfo.nsamples=0;
     81 		delete ai;
     82 	};
     83 	return result;
     84 };
     85 
     86 string Control::get_input_filename(){
     87 	return wavinfo.filename;
     88 };
     89 string Control::get_input_filename_and_info(){
     90 	int seconds=wavinfo.nsamples/wavinfo.samplerate;
     91 	const int size=200;
     92 	char tmp[size];tmp[size-1]=0;
     93 	snprintf(tmp,size-1," ( samplerate=%d; duration=%02d:%02d:%02d )",wavinfo.samplerate,seconds/3600,(seconds/60)%60,seconds%60);
     94 
     95 	string filename=wavinfo.filename;
     96 	int len=filename.length();
     97 	if (len>70)filename=filename.substr(0,25)+"..."+filename.substr(len-35);
     98 	return filename+tmp;
     99 };
    100 /*string Control::get_recommanded_output_filename(){
    101   return "none";
    102   };
    103   */
    104 
    105 
    106 std::string Control::get_stretch_info(){
    107 	const int size=200;
    108 	char tmp[size];tmp[size-1]=0;
    109 
    110 	if (wavinfo.nsamples==0) return "Stretch: ";
    111 
    112 
    113 	double realduration=wavinfo.nsamples/wavinfo.samplerate*process.stretch;
    114 
    115 	if (realduration>(365.25*86400.0*1.0e12)){//more than 1 trillion years
    116 		double duration=(realduration/(365.25*86400.0*1.0e12));//my
    117 		snprintf(tmp,size,"Stretch: %.7gx (%g trillion years)",process.stretch,duration);
    118 		return tmp;
    119 	};
    120 	if (realduration>(365.25*86400.0*1.0e9)){//more than 1 billion years
    121 		double duration=(realduration/(365.25*86400.0*1.0e9));//my
    122 		snprintf(tmp,size,"Stretch: %.7gx (%g billion years)",process.stretch,duration);
    123 		return tmp;
    124 	};
    125 	if (realduration>(365.25*86400.0*1.0e6)){//more than 1 million years
    126 		double duration=(realduration/(365.25*86400.0*1.0e6));//my
    127 		snprintf(tmp,size,"Stretch: %.7gx (%g million years)",process.stretch,duration);
    128 		return tmp;
    129 	};
    130 	if (realduration>(365.25*86400.0*2000.0)){//more than two millenniums
    131 		int duration=(int)(realduration/(365.25*86400.0));//years
    132 		int years=duration%1000;
    133 		int milleniums=duration/1000;
    134 
    135 		char stryears[size];stryears[0]=0;
    136 		if (years!=0){
    137 			if (years==1) snprintf(stryears,size," 1 year");
    138 			else snprintf(stryears,size," %d years",years);
    139 		};
    140 		snprintf(tmp,size,"Stretch: %.7gx (%d milleniums%s)",process.stretch,milleniums,stryears);
    141 		return tmp;
    142 	};
    143 	if (realduration>(365.25*86400.0)){//more than 1 year
    144 		int duration=(int) (realduration/3600.0);//hours
    145 		int hours=duration%24;
    146 		int days=(duration/24)%365;
    147 		int years=duration/(365*24);
    148 
    149 		char stryears[size];stryears[0]=0;
    150 		if (years==1) snprintf(stryears,size,"1 year ");
    151 		else snprintf(stryears,size,"%d years ",years);
    152 
    153 		char strdays[size];strdays[0]=0;
    154 		if (days>0){
    155 			if (days==1) snprintf(strdays,size,"1 day");
    156 			else snprintf(strdays,size,"%d days",days);
    157 		};	
    158 		if (years>=10) hours=0;
    159 		char strhours[size];strhours[0]=0;
    160 		if (hours>0){
    161 			snprintf(strhours,size," %d h",hours);
    162 		};	
    163 
    164 		snprintf(tmp,size,"Stretch: %.7gx (%s%s%s)",process.stretch,stryears,strdays,strhours);
    165 		return tmp;
    166 	}else{//less than 1 year
    167 		int duration=(int)(realduration);//seconds
    168 
    169 		char strdays[size];strdays[0]=0;
    170 		int days=duration/86400;
    171 		if (days>0){
    172 			if (days==1) snprintf(strdays,size,"1 day ");
    173 			else snprintf(strdays,size,"%d days ",duration/86400);
    174 		};	
    175 		REALTYPE stretch=process.stretch;
    176 		if (stretch>=1.0){
    177 			stretch=((int) (stretch*100.0))*0.01;
    178 		};
    179 		snprintf(tmp,size,"Stretch: %.7gx (%s%.2d:%.2d:%.2d)",
    180 				stretch,strdays,(duration/3600)%24,(duration/60)%60,duration%60);
    181 		return tmp;
    182 	};
    183 	return "";
    184 };
    185 
    186 string Control::get_fftsize_info(){
    187 	const int size=200;
    188 	char tmp[size];tmp[size-1]=0;
    189 
    190 	string fftsizelabel;
    191 	fftsizelabel+="Window size (samples): ";
    192 
    193 	if (wavinfo.nsamples==0) return fftsizelabel;
    194 	fftsizelabel+=getfftsizestr(process.bufsize);
    195 
    196 	return fftsizelabel;
    197 };
    198 
    199 string Control::get_fftresolution_info(){
    200 	string resolution="Resolution: ";
    201 	if (wavinfo.nsamples==0) return resolution;
    202 
    203 	//todo: unctime and uncfreq are correct computed? Need to check later.
    204 	REALTYPE unctime=process.bufsize/(REALTYPE)wavinfo.samplerate*sqrt(2.0);
    205 	REALTYPE uncfreq=1.0/unctime*sqrt(2.0);
    206 	char tmp[100];
    207 	snprintf(tmp,100,"%.5g seconds",unctime);resolution+=tmp;
    208 	snprintf(tmp,100," (%.5g Hz)",uncfreq);resolution+=tmp;
    209 	return resolution;
    210 };
    211 
    212 
    213 void Control::startplay(bool bypass){
    214 	if ((!player->info.playing)||(player->info.samplerate!=wavinfo.samplerate)){
    215 		stopplay();
    216 		sleep(200);
    217 #ifdef HAVE_JACK
    218 		JACKaudiooutputinit(player,wavinfo.samplerate);
    219 #else
    220 		PAaudiooutputinit(player,wavinfo.samplerate);
    221 #endif
    222 	};
    223 	if (wavinfo.filename!="") player->startplay(wavinfo.filename,seek_pos,process.stretch,process.bufsize,wavinfo.intype,bypass,&ppar,&bbpar);
    224 //	sleep(100);
    225 //	update_process_parameters();
    226 };
    227 void Control::stopplay(){
    228 	player->stop();
    229 	player->seek(0.0);
    230 	seek_pos=0;
    231 #ifdef HAVE_JACK
    232 	JACKclose();
    233 #else
    234 	PAfinish();
    235 #endif
    236 };
    237 void Control::pauseplay(){
    238 	player->pause();
    239 };
    240 
    241 void Control::freezeplay(){
    242 	player->freeze();
    243 };
    244 
    245 void Control::set_volume(REALTYPE vol){
    246 	volume=vol;
    247 	player->set_volume(vol);
    248 };
    249 
    250 
    251 void Control::set_seek_pos(REALTYPE x){
    252 	seek_pos=x;
    253 	player->seek(x);
    254 };
    255 REALTYPE Control::get_seek_pos(){
    256 	if (player->getmode()==Player::MODE_PLAY) seek_pos=player->info.position;
    257 	return seek_pos;
    258 };
    259 
    260 
    261 void Control::set_stretch_controls(double stretch_s,int mode,double fftsize_s,double onset_detection_sensitivity){
    262 	gui_sliders.stretch_s=stretch_s;
    263 	gui_sliders.mode_s=mode;
    264 	gui_sliders.fftsize_s=fftsize_s;
    265 
    266 	double stretch=1.0;
    267 	switch(mode){
    268 		case 0:
    269 			stretch_s=pow(stretch_s,1.2);
    270 			stretch=pow(10.0,stretch_s*4.0);
    271 			break;
    272 		case 1:
    273 			stretch_s=pow(stretch_s,1.5);
    274 			stretch=pow(10.0,stretch_s*18.0);
    275 			break;
    276 		case 2:
    277 			stretch=1.0/pow(10.0,stretch_s*2.0);
    278 			break;
    279 	};
    280 
    281 
    282 	fftsize_s=pow(fftsize_s,1.5);
    283 	int bufsize=(int)(pow(2.0,fftsize_s*12.0)*512.0);
    284 
    285 	bufsize=optimizebufsize(bufsize);
    286 
    287 	process.stretch=stretch;
    288 	process.bufsize=bufsize;
    289 	process.onset_detection_sensitivity=onset_detection_sensitivity;
    290 
    291 };
    292 
    293 double Control::get_stretch_control(double stretch,int mode){
    294 	double result=1.0;
    295 	switch(mode){
    296 		case 0:
    297 			if (stretch<1.0) return -1;
    298 			stretch=(log(stretch)/log(10))*0.25;
    299 			result=pow(stretch,1.0/1.2);
    300 			break;
    301 		case 1:
    302 			if (stretch<1.0) return -1;
    303 			stretch=(log(stretch)/log(10))/18.0;
    304 			result=pow(stretch,1.0/1.5);
    305 			break;
    306 		case 2:
    307 			if (stretch>1.0) return -1;
    308 			result=2.0/(log(stretch)/log(10));
    309 			break;
    310 	};
    311 	return result;
    312 };
    313 
    314 
    315 void Control::update_player_stretch(){
    316 	player->setrap(process.stretch);
    317 	player->set_onset_detection_sensitivity(process.onset_detection_sensitivity);
    318 };
    319 
    320 
    321 int abs_val(int x){
    322 	if (x<0) return -x;
    323 	else return x;
    324 };
    325 
    326 
    327 int Control::get_optimized_updown(int n,bool up){
    328 	int orig_n=n;
    329 	while(true){
    330 		n=orig_n;
    331 #ifndef KISSFFT
    332 		while (!(n%11)) n/=11;
    333 		while (!(n%7)) n/=7;
    334 #endif
    335 		while (!(n%5)) n/=5;
    336 		while (!(n%3)) n/=3;
    337 		while (!(n%2)) n/=2;
    338 		if (n<2) break;
    339 		if (up) orig_n++;
    340 		else orig_n--;
    341 		if (orig_n<4) return 4;
    342 	};
    343 	return orig_n;
    344 };
    345 int Control::optimizebufsize(int n){
    346 	int n1=get_optimized_updown(n,false);
    347 	int n2=get_optimized_updown(n,true);
    348 	if ((n-n1)<(n2-n)) return n1;
    349 	else return n2;
    350 };
    351 
    352 
    353 
    354 void Control::set_window_type(FFTWindow window){
    355 	window_type=window;
    356 	if (player) player->set_window_type(window);
    357 };
    358 
    359 
    360 string Control::Render(string inaudio,string outaudio,FILE_TYPE outtype,FILE_TYPE intype,REALTYPE pos1,REALTYPE pos2){
    361 	if (pos2<pos1){
    362 		REALTYPE tmp=pos2;
    363 		pos2=pos1;
    364 		pos1=tmp;
    365 	};
    366 	InputS *ai=NULL;
    367 	switch(intype){
    368 		case FILE_VORBIS:ai=new VorbisInputS;
    369 						 break;
    370 		case FILE_MP3:ai=new MP3InputS;
    371 					  break;
    372 		default:ai=new AInputS;
    373 	};
    374 	AOutputS ao;
    375 	VorbisOutputS vorbisout;
    376 	info.cancel_render=false;
    377 	if (!ai->open(inaudio)){
    378 		return "Error: Could not open audio file (or file format not recognized) :"+inaudio;
    379 	};
    380 	BinauralBeats bb(ai->info.samplerate);
    381 	bb.pars=bbpar;
    382 	if (outtype==FILE_WAV) ao.newfile(outaudio,ai->info.samplerate,wav32bit);
    383 	if (outtype==FILE_VORBIS) vorbisout.newfile(outaudio,ai->info.samplerate);
    384 
    385 	ai->seek(pos1);
    386 
    387 	int inbufsize=process.bufsize;
    388 
    389 	if (inbufsize<32) inbufsize=32;
    390 	short int *inbuf_i=new short int[inbufsize*8];
    391 	int outbufsize;
    392 	struct{
    393 		REALTYPE *l,*r;
    394 	}inbuf;
    395 	ProcessedStretch *stretchl=new ProcessedStretch(process.stretch,inbufsize,window_type,false,ai->info.samplerate,1);
    396 	ProcessedStretch *stretchr=new ProcessedStretch(process.stretch,inbufsize,window_type,false,ai->info.samplerate,2);
    397 	stretchl->set_onset_detection_sensitivity(process.onset_detection_sensitivity);
    398 	stretchr->set_onset_detection_sensitivity(process.onset_detection_sensitivity);
    399 	stretchl->set_parameters(&ppar);
    400 	stretchr->set_parameters(&ppar);
    401 
    402 	outbufsize=stretchl->get_bufsize();
    403 	int *outbuf=new int[outbufsize*2];
    404 
    405 	int poolsize=stretchl->get_max_bufsize();
    406 
    407 	inbuf.l=new REALTYPE[poolsize];
    408 	inbuf.r=new REALTYPE[poolsize];
    409 	for (int i=0;i<poolsize;i++) inbuf.l[i]=inbuf.r[i]=0.0;
    410 
    411 	int readsize=0;
    412 	const int pause_max_write=65536;
    413 	int pause_write=0;
    414 	bool firstbuf=true;
    415 	while(!ai->eof){
    416 		float in_pos=(REALTYPE) ai->info.currentsample/(REALTYPE)ai->info.nsamples;
    417 		if (firstbuf){
    418 			readsize=stretchl->get_nsamples_for_fill();
    419 			firstbuf=false;
    420 		}else{
    421 			readsize=stretchl->get_nsamples(in_pos*100.0);
    422 		};
    423 		int readed=0;
    424 		if (readsize!=0) readed=ai->read(readsize,inbuf_i);
    425 		
    426 		for (int i=0;i<readed;i++) {
    427 			inbuf.l[i]=inbuf_i[i*2]/32768.0;
    428 			inbuf.r[i]=inbuf_i[i*2+1]/32768.0;
    429 		};
    430 		REALTYPE onset_l=stretchl->process(inbuf.l,readed);
    431 		REALTYPE onset_r=stretchr->process(inbuf.r,readed);
    432 		REALTYPE onset=(onset_l>onset_r)?onset_l:onset_r;
    433 		stretchl->here_is_onset(onset);
    434 		stretchr->here_is_onset(onset);
    435 		bb.process(stretchl->out_buf,stretchr->out_buf,outbufsize,in_pos*100.0);
    436 		for (int i=0;i<outbufsize;i++) {
    437 			stretchl->out_buf[i]*=volume;
    438 			stretchr->out_buf[i]*=volume;
    439 		};
    440 		int nskip=stretchl->get_skip_nsamples();
    441 		if (nskip>0) ai->skip(nskip);
    442 
    443 		if (outtype==FILE_WAV){	
    444 			for (int i=0;i<outbufsize;i++) {
    445 				REALTYPE l=stretchl->out_buf[i],r=stretchr->out_buf[i];
    446 				if (l<-1.0) l=-1.0;
    447 				else if (l>1.0) l=1.0;
    448 				if (r<-1.0) r=-1.0;
    449 				else if (r>1.0) r=1.0;
    450 				outbuf[i*2]=(int)(l*32767.0*65536.0);
    451 				outbuf[i*2+1]=(int)(r*32767.0*65536.0);
    452 			};
    453 			ao.write(outbufsize,outbuf);
    454 		};
    455 		if (outtype==FILE_VORBIS) vorbisout.write(outbufsize,stretchl->out_buf,stretchr->out_buf);
    456 
    457 		REALTYPE totalf=ai->info.currentsample/(REALTYPE)ai->info.nsamples-pos1;
    458 		if (totalf>(pos2-pos1)) break;
    459 
    460 		info.render_percent=(totalf*100.0/(pos2-pos1+0.001));
    461 		pause_write+=outbufsize;
    462 		if (pause_write>pause_max_write){
    463 			float tmp=outbufsize/1000000.0;
    464 			if (tmp>0.1) tmp=0.1;
    465 			Fl::wait(0.01+tmp);
    466 			pause_write=0;
    467 			if (info.cancel_render) break;
    468 		};
    469 	};
    470 
    471 	delete stretchl;
    472 	delete stretchr;    
    473 	delete []outbuf;
    474 	delete []inbuf_i;
    475 	delete []inbuf.l;
    476 	delete []inbuf.r;
    477 
    478 	info.render_percent=-1.0;
    479 	return "";
    480 };
    481 
    482 
    483 
    484 string Control::getfftsizestr(int fftsize){
    485 	int size=100;
    486 	char tmp[size];tmp[size-1]=0;
    487 	if (fftsize<1024.0) snprintf(tmp,size-1,"%d",fftsize);
    488 	else if (fftsize<(1024.0*1024.0)) snprintf(tmp,size-1,"%.4gK",fftsize/1024.0);
    489 	else if (fftsize<(1024.0*1024.0*1024.0)) snprintf(tmp,size-1,"%.4gM",fftsize/(1024.0*1024.0));
    490 	else snprintf(tmp,size-1,"%.7gG",fftsize/(1024.0*1024.0*1024.0));
    491 	return tmp;
    492 };
    493 
    494 void Control::update_process_parameters(){
    495 	if (player) player->set_process_parameters(&ppar,&bbpar);
    496 };
    497 
    498 bool Control::save_parameters(const char *filename){
    499 	XMLwrapper *xml=new XMLwrapper();
    500 	
    501 	xml->beginbranch("PAULSTRETCH");
    502 		xml->beginbranch("STRETCH_PARAMETERS");
    503 			xml->beginbranch("BASE");
    504 				xml->addpar("bufsize",process.bufsize);
    505 				xml->addparreal("stretch",process.stretch);
    506 	
    507 				xml->addparreal("fftsize_s",gui_sliders.fftsize_s);
    508 				xml->addparreal("stretch_s",gui_sliders.stretch_s);
    509 				xml->addpar("mode_s",gui_sliders.mode_s);
    510 				
    511 				xml->addpar("window_type",window_type);
    512 				xml->addparreal("volume",volume);
    513 				xml->addparreal("onset_detection_sensitivity",process.onset_detection_sensitivity);
    514 
    515 			xml->endbranch();
    516 			
    517 			xml->beginbranch("PROCESS");
    518 				ppar.add2XML(xml);
    519 			xml->endbranch();
    520 
    521 			xml->beginbranch("BINAURAL_BEATS");
    522 				bbpar.add2XML(xml);
    523 			xml->endbranch();
    524 
    525 		xml->endbranch();
    526 	xml->endbranch();
    527 
    528 	int result=xml->saveXMLfile(filename);
    529 	delete xml;
    530 	return true;
    531 };
    532 
    533 bool Control::load_parameters(const char *filename){
    534 	XMLwrapper *xml=new XMLwrapper();
    535 
    536 	if (xml->loadXMLfile(filename)<0) {
    537 		delete xml;
    538 		return false;
    539 	};
    540 
    541 
    542 	if (xml->enterbranch("PAULSTRETCH")==0) {
    543 		delete xml;
    544 		return false;
    545 	};
    546 
    547 	if (xml->enterbranch("STRETCH_PARAMETERS")){
    548 		if (xml->enterbranch("BASE")){
    549 			process.bufsize=xml->getpar("bufsize",process.bufsize,16,2e9);
    550 			process.stretch=xml->getparreal("stretch",process.stretch);
    551 			gui_sliders.fftsize_s=xml->getparreal("fftsize_s",gui_sliders.fftsize_s);
    552 			gui_sliders.stretch_s=xml->getparreal("stretch_s",gui_sliders.stretch_s);
    553 			gui_sliders.mode_s=xml->getpar("mode_s",gui_sliders.mode_s,0,2);
    554 			window_type=(FFTWindow)xml->getpar("window_type",window_type,0,4);
    555 			process.onset_detection_sensitivity=xml->getparreal("onset_detection_sensitivity",0.0);
    556 			volume=xml->getparreal("volume",1.0);
    557 			xml->exitbranch();
    558 		};
    559 
    560 		if (xml->enterbranch("PROCESS")){
    561 				ppar.getfromXML(xml);
    562 			xml->exitbranch();
    563 		};
    564 
    565 		if (xml->enterbranch("BINAURAL_BEATS")){
    566 				bbpar.getfromXML(xml);
    567 			xml->exitbranch();
    568 		};
    569 
    570 		xml->exitbranch();
    571 	};
    572 	delete xml;
    573 
    574 	set_stretch_controls(gui_sliders.stretch_s,gui_sliders.mode_s,gui_sliders.fftsize_s,process.onset_detection_sensitivity);
    575 	
    576 	set_window_type(window_type);
    577 	set_volume(volume);
    578 	update_process_parameters();
    579 	return true;
    580 };
    581 	
    582