You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

583 lines
15KB

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