reverb.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. /*
  2. * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
  3. * http://people.bath.ac.uk/masrwd
  4. * http://www.composersdesktop.com
  5. * This file is part of the CDP System.
  6. * The CDP System is free software; you can redistribute it
  7. * and/or modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * The CDP System is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. * See the GNU Lesser General Public License for more details.
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with the CDP System; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. *
  19. */
  20. /* MODULE FOR REVERB.EXE */
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <math.h>
  24. #ifdef _DEBUG
  25. #include <assert.h>
  26. #endif
  27. #include <algorithm>
  28. #include <vector>
  29. using namespace std;
  30. /***** FORMULA FOR LOOP GAIN *****
  31. g = pow(10, - ( ((60*ilooptime)/reverbtime) / 20))
  32. derived from Moore:
  33. reverbtime = 60/(-20.log10(g)) * ilooptime
  34. then we set igain = g(1 - filtgain), officially...
  35. */
  36. extern "C" {
  37. #include <sfsys.h>
  38. #include <osbind.h>
  39. #include <cdplib.h> //NB requires stdio.h etc - time to change this?
  40. }
  41. #include "reverberator.h"
  42. #include <string.h>
  43. void usage(void);
  44. long readtaps(FILE *fp, deltap **taps,double sr);
  45. //my library of early reflections...
  46. //by doing it this way I can use the sizeof() operator on the array names
  47. #include "reflect.cpp"
  48. enum cmdargs {
  49. PROGNAME,
  50. INFILE,
  51. OUTFILE,
  52. RGAIN,
  53. MIX,
  54. REVERBTIME,
  55. DAMPING,
  56. LPFREQ,
  57. TRTIME,
  58. CONFIG
  59. };
  60. const char* cdp_version = "5.0.0";
  61. ///////////// Moorer reverb
  62. int
  63. main(int argc,char *argv[])
  64. {
  65. int ifd=-1, ofd=-1,i;
  66. //double delaytime,gain;
  67. double rvbtime, damping, trailertime = 0.0;
  68. long chans,outchans = 2;
  69. double sr;
  70. //all this stuff from Brandt Csound implementation of posh Moorer
  71. MOORERDATA mrdata = {0.050,0.056,0.061,0.068,0.072,0.078,0.02,0.014,0.009,0.006};
  72. double predelay = 0.0;
  73. moorer *moorerverb = 0;
  74. tapdelay *tdelay = 0;
  75. deltap *ptaps = 0;
  76. allpassfilter **chan_ap = 0; //ptr to array of allpassfilter ptrs
  77. tonecontrol *tc_l_lowcut = 0, *tc_r_lowcut = 0;
  78. // could be replaced with a tonecontrol for deeper slope
  79. onepole *l_highcut = 0, *r_highcut = 0, *lp = 0;
  80. FILE *fp_earlies = 0;
  81. long ntaps;
  82. bool want_mono = false;
  83. bool want_floats = false;
  84. bool usertaps = false;
  85. double lowcut_freq = 0.0,highcut_freq = 0.0,lp_freq = 0.0;
  86. float dry_gain,wet_gain,diff_gain;
  87. CHPEAK *peaks;
  88. SFPROPS props;
  89. // test vcomb with variable delay taps
  90. //vmtcomb4 vcomb;
  91. //fastlfo lfo1,lfo2,lfo3,lfo4;
  92. if((argc==2) && strcmp(argv[1],"--version")==0) {
  93. fprintf(stdout,"%s\n",cdp_version);
  94. fflush(stdout);
  95. return 0;
  96. }
  97. if(sflinit("reverb")){
  98. sfperror("reverb: initialisation");
  99. exit(1);
  100. }
  101. if(argc < CONFIG) usage();
  102. while((argc > 1) && argv[1][0]=='-'){
  103. char *arg = argv[1];
  104. switch(*(++arg)){
  105. case('p'):
  106. if(*(++arg) =='\0'){
  107. fprintf(stderr,"\npredelay requires a parameter");
  108. usage();
  109. }
  110. predelay = atof(arg);
  111. if(predelay <0.0){
  112. fprintf(stderr,"\npredelay must be >= 0.0");
  113. usage();
  114. }
  115. predelay *= 0.001; //arg is in msecs
  116. break;
  117. case('c'):
  118. if(*(++arg) == '\0') {
  119. fprintf(stderr,"\n:reverb: -c flag requires a value");
  120. usage();
  121. }
  122. outchans = atoi(arg);
  123. if(outchans < 1 || outchans > 16){
  124. fprintf(stderr,"\nreverb: impossible channel value requested");
  125. usage();
  126. }
  127. if(outchans==1)
  128. want_mono = true;
  129. break;
  130. case('e'):
  131. if(*(++arg)=='\0'){
  132. fprintf(stderr,"\nreverb: -e flag needs a filename");
  133. usage();
  134. }
  135. if((fp_earlies = fopen(arg,"r")) ==0){
  136. fprintf(stderr,"\nreverb: unable to open breakpoint file %s",arg);
  137. exit(1);
  138. }
  139. usertaps = true;
  140. break;
  141. case('f'):
  142. want_floats = true;
  143. if(*(++arg) != '\0')
  144. fprintf(stderr,"\nreverb: WARNING: -f flag does not take a parameter");
  145. break;
  146. case('L'):
  147. if(*(++arg) == '\0'){
  148. fprintf(stderr,"\nreverb: Lowcut flag needs frequency argument");
  149. usage();
  150. }
  151. lowcut_freq = atof(arg);
  152. if(lowcut_freq <= 0.0){
  153. fprintf(stderr,"\nreverb: Lowcut freq must be greater than zero");
  154. usage();
  155. }
  156. break;
  157. case('H'):
  158. if(*(++arg) == '\0'){
  159. fprintf(stderr,"\nreverb: Highcut flag needs frequency argument");
  160. usage();
  161. }
  162. highcut_freq = atof(arg);
  163. if(highcut_freq <= 0.0){
  164. fprintf(stderr,"\nreverb: Highcut freq must be greater than zero");
  165. usage();
  166. }
  167. break;
  168. default:
  169. fprintf(stderr,"\nreverb: illegal flag option %s",argv[1]);
  170. usage();
  171. break;
  172. }
  173. argc--;
  174. argv++;
  175. }
  176. peaks = (CHPEAK *) malloc(sizeof(CHPEAK) * outchans);
  177. if(peaks==NULL){
  178. fputs("\nNo memory for PEAK data\n",stderr);
  179. exit(1);
  180. }
  181. chan_ap = new allpassfilter*[outchans];
  182. if(chan_ap==0){
  183. fputs("\nreverb: no memory for multi-channel diffusion",stderr);
  184. exit(1);
  185. }
  186. if(argc < CONFIG)
  187. usage();
  188. if(argv[INFILE] != 0) {
  189. if((ifd = sndopenEx(argv[INFILE],0,CDP_OPEN_RDONLY)) < 0) {
  190. fprintf(stderr,"\nreverb: cannot open input file %s\n", argv[INFILE]);
  191. exit(1);
  192. }
  193. }
  194. if(!snd_headread(ifd,&props)){
  195. fprintf(stderr,"\nerror reading infile header: %s\n",rsferrstr);
  196. sndcloseEx(ifd);
  197. exit(1);
  198. }
  199. if(props.type != wt_wave){
  200. fprintf(stderr,"\ninfile is not a waveform file");
  201. sndcloseEx(ifd);
  202. exit(1);
  203. }
  204. sr = (double) props.srate;
  205. chans = props.chans;
  206. if(chans > 2){
  207. fprintf(stderr,"\nreverb works only on mono or stereo files");
  208. exit(1);
  209. }
  210. diff_gain = atof(argv[RGAIN]);
  211. if(diff_gain < 0.0 || diff_gain > 1.0 ){
  212. printf("\nreverb: rgain must be >= 0.0 and <= 1.0");
  213. usage();
  214. }
  215. dry_gain = (float) atof(argv[MIX]); //global output gain from diffuser
  216. if(dry_gain < 0.0 || dry_gain >= 1.0 ){
  217. printf("\nreverb: mix must be between 0.0 and 1.0");
  218. usage();
  219. }
  220. wet_gain = 1.0f - dry_gain;
  221. //probably not very scientific, but it works intuitively...
  222. //wet_gain *= 2;
  223. rvbtime = atof(argv[REVERBTIME]);
  224. if(rvbtime <= 0.0){
  225. fprintf(stderr,
  226. "\nreverb: rvbtime must be > 0\n");
  227. exit(1);
  228. }
  229. damping = atof(argv[DAMPING]);
  230. if(damping < 0.0 || damping > 1.0){
  231. fprintf(stderr,"\nreverb: absorb must be in the range 0.0 - 1.0\n");
  232. exit(1);
  233. }
  234. double filt_limit = sr / 3.0;
  235. lp_freq = atof(argv[LPFREQ]);
  236. if(lp_freq < 0.0 || lp_freq > filt_limit){
  237. printf("\napass: lp_freq must be within 0.0 to %.4lfHz",filt_limit);
  238. usage();
  239. }
  240. trailertime = atof(argv[TRTIME]);
  241. if(trailertime < 0.0)
  242. trailertime = 0.0;
  243. if(argc==CONFIG+1){
  244. int got = 0;
  245. double dtime1,dtime2,dtime3,dtime4,dtime5,dtime6,
  246. adtime1,adtime2,adtime3,adtime4;
  247. FILE *fp = fopen(argv[CONFIG],"r");
  248. if(!fp)
  249. printf("\nreverb: can't open datafile %s: using presets",argv[CONFIG]);
  250. else{
  251. got = fscanf(fp,"%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
  252. &dtime1,
  253. &dtime2,
  254. &dtime3,
  255. &dtime4,
  256. &dtime5,
  257. &dtime6,
  258. &adtime1, //allpasses
  259. &adtime2,
  260. &adtime3,
  261. &adtime4
  262. );
  263. fclose(fp);
  264. if(got != 10)
  265. printf("\nreverb: error reading looptime values");
  266. else {
  267. //create prime delay times from the msec vals
  268. //get this in a loop later on...
  269. //RWD TODO: check times are not too huge!
  270. mrdata.ctime1 = dtime1 * 0.001;
  271. mrdata.ctime2 = dtime2 * 0.001;
  272. mrdata.ctime3 = dtime3 * 0.001;
  273. mrdata.ctime4 = dtime4 * 0.001;
  274. mrdata.ctime5 = dtime5 * 0.001;
  275. mrdata.ctime6 = dtime6 * 0.001;
  276. mrdata.atime1 = adtime1 * 0.001;
  277. mrdata.atime2 = adtime2 * 0.001;
  278. mrdata.atime3 = adtime3 * 0.001;
  279. mrdata.atime4 = adtime4 * 0.001;
  280. }
  281. }
  282. }
  283. //create input low/high filters, if wanted
  284. if(lowcut_freq > 0.0){
  285. if(highcut_freq > 0.0 && highcut_freq <= lowcut_freq){
  286. fprintf(stderr,"\nreverb: Highcut frequency must be higher than Lowcut frequency");
  287. usage();
  288. }
  289. //lowcut is based on low shelving eq filter; here fixed at 12dB
  290. tc_l_lowcut = new tonecontrol(lowcut_freq,-12.0,LOW_SHELVE,sr);
  291. if(!tc_l_lowcut->create()){
  292. fprintf(stderr,"\nreverb: unable to create Lowcut filter");
  293. exit(1);
  294. }
  295. if(chans==2){
  296. tc_r_lowcut = new tonecontrol(lowcut_freq,-12.0,LOW_SHELVE,sr);
  297. if(!tc_r_lowcut->create()){
  298. fprintf(stderr,"\nreverb: unable to create Lowcut filter");
  299. exit(1);
  300. }
  301. }
  302. }
  303. if(highcut_freq > 0.0){
  304. l_highcut = new onepole(highcut_freq,sr,LOW_PASS);
  305. if(chans==2)
  306. r_highcut = new onepole(highcut_freq,sr,LOW_PASS);
  307. }
  308. if(lp_freq > 0.0)
  309. lp = new onepole(lp_freq,sr,LOW_PASS);
  310. moorerverb = new moorer(&mrdata,rvbtime,damping,sr);
  311. //further external allpasses will be used to create multi-channel diffusion
  312. for(i = 0; i < outchans; i++){
  313. chan_ap[i] = new allpassfilter(sr,to_prime(0.005 + 0.00025 * (double)i,sr),0.4 ,0);
  314. if(chan_ap[i] == 0) {
  315. fprintf(stderr,"\nreverb: no memory for output diffusers");
  316. exit(1);
  317. }
  318. if(!chan_ap[i]->create()){
  319. fprintf(stderr,"\nreverb: no memory for output diffusers");
  320. exit(1);
  321. }
  322. }
  323. if(usertaps){
  324. #ifdef _DEBUG
  325. assert(fp_earlies);
  326. #endif
  327. ntaps = readtaps(fp_earlies,&ptaps,sr);
  328. if(ntaps==0){
  329. fprintf(stderr,"\nreverb: error reading tapfile");
  330. exit(1);
  331. }
  332. printf("\nloaded %ld early reflections from tapfile",ntaps);
  333. fclose(fp_earlies);
  334. fp_earlies = 0;
  335. }
  336. else {
  337. if(rvbtime <= 0.6){
  338. ntaps = sizeof(smalltaps) / sizeof(deltap);
  339. ptaps = smalltaps;
  340. printf("\nsmall-room: using %ld early reflections",ntaps);
  341. }
  342. else if(rvbtime > 0.6 && rvbtime <= 1.3){
  343. ntaps = sizeof(mediumtaps) / sizeof(deltap);
  344. ptaps = mediumtaps;
  345. printf("\nmedium-room: using %ld early reflections",ntaps);
  346. }
  347. else{
  348. ntaps = sizeof(largetaps) / sizeof(deltap);
  349. ptaps = largetaps;
  350. printf("\nlarge-room: using %ld early reflections",ntaps);
  351. }
  352. }
  353. tdelay = new tapdelay(ptaps,ntaps,sr);
  354. //if user prescribes a predelay, adjust all taptimes
  355. if(predelay > 0.0){
  356. double current_delay = ptaps[0].pos;
  357. double adjust = predelay - current_delay;
  358. for(int i = 0; i < ntaps; i++)
  359. ptaps[i].pos += adjust;
  360. }
  361. if(!moorerverb->create()){
  362. fprintf(stderr,"\nreverb: unable to create reverberator");
  363. exit(1);
  364. }
  365. if(!tdelay->create()){
  366. fprintf(stderr,"\nreverb: unable to create early reflections");
  367. exit(1);
  368. }
  369. //double maxgain = tdelay->get_maxgain();
  370. //if(maxgain > 1.0)
  371. // tdelay->set_gain(maxgain * early_gain);
  372. printf("\npredelay = %.4lf secs",ptaps[0].pos);
  373. //wet_gain *= floor(maxgain);
  374. if(want_floats)
  375. props.samptype =FLOAT32;
  376. props.chans = outchans;
  377. if((ofd = sndcreat_ex(argv[OUTFILE],-1,&props,SFILE_CDP,CDP_CREATE_NORMAL)) < 0) {
  378. fprintf(stderr,"\nreverb: cannot open output file %s\n",argv[OUTFILE]);
  379. exit(1);
  380. }
  381. printf("\n");
  382. long outsamps = 0;
  383. long step = (long) (0.25 * sr);
  384. #ifdef NOTDEF
  385. // vcomb test
  386. long seed = lfo1.init((double)inprops.srate,0.0,seed);
  387. lfo1.set_WaveType(LFO_RANDOM);
  388. lfo1.set_freq(0.5);
  389. lfo1.set_mod(1.0); /* no change - we control modf range externally */
  390. lfo2.init((double)inprops.srate,0.0,seed);
  391. lfo2.set_WaveType(LFO_RANDOM);
  392. lfo2.set_freq(0.55);
  393. lfo2.set_mod(1.0);
  394. lfo3.init((double)inprops.srate,0.0,seed);
  395. lfo3.set_WaveType(LFO_RANDOM);
  396. lfo3.set_freq(0.61);
  397. lfo3.set_mod(1.0);
  398. lfo4.init((double)inprops.srate,0.0,seed);
  399. lfo4.set_WaveType(LFO_RANDOM);
  400. lfo4.set_freq(0.66);
  401. lfo4.set_mod(1.0);
  402. vcomb.init(double)srate,0.8);
  403. double delay1,delay2,delay3,delay4;
  404. double delaymod = 0.05;
  405. delay1 = 0.05;
  406. delay2 = 0.056;
  407. delay3 = 0,061;
  408. delay4 = 0.068;
  409. vcomb.init(double)srate,delay4 + (delay4 * delaymod));
  410. vcomb.setfgains(
  411. #endif
  412. //stopwatch(1);
  413. //run main single-sample loop
  414. float l_ip,r_ip,l_out,r_out,l_direct,r_direct,mono_direct;
  415. for(;;){
  416. int rv;
  417. float out,earlies;
  418. //read mon/left channnel
  419. if((rv = fgetfloatEx(&l_ip,ifd,0)) < 0){
  420. fprintf(stderr,"\nreverb: error reading infile");
  421. exit(1);
  422. }
  423. if(!rv) break; // end of inputfile - handle any trailertime
  424. //apply any conditioning to direct signal
  425. if(tc_l_lowcut)
  426. l_ip = (float) tc_l_lowcut->tick((double)l_ip);
  427. if(l_highcut)
  428. l_ip = (float) l_highcut->tick((double)l_ip);
  429. l_direct = l_ip;
  430. mono_direct = l_direct;
  431. r_direct = l_direct;
  432. //handle R channnel if active
  433. if(chans==2){
  434. if((rv = fgetfloatEx(&r_ip,ifd,0)) < 0){
  435. fprintf(stderr,"\nError in reading file");
  436. exit(1);
  437. }
  438. if(!rv) break;
  439. //apply any conditioning to direct signal
  440. if(tc_r_lowcut)
  441. r_ip = (float) tc_r_lowcut->tick((double)r_ip);
  442. if(r_highcut)
  443. r_ip = (float) r_highcut->tick((double)r_ip);
  444. r_direct = r_ip;
  445. if(want_mono)
  446. mono_direct = (l_direct + r_direct) * 0.5f;
  447. }
  448. //mono_direct = (mixed) input to reverberator
  449. //possibly also filter it...
  450. if(lp)
  451. mono_direct = (float) lp->tick(mono_direct); //input lowpass
  452. earlies = tdelay->tick(mono_direct); // ---> early reflections
  453. //send (filtered) mono output from reverberator
  454. out = earlies + moorerverb->tick(earlies + mono_direct) * diff_gain;
  455. //and send thru multi_channel diffusers
  456. //NB must handle first 2 chans as special cases, then loop thru the rest
  457. //l_out = want_mono ? mono_direct : l_direct;
  458. //l_out *= dry_gain;
  459. l_out = chan_ap[0]->tick(out) * wet_gain;
  460. if(want_mono)
  461. l_out += (mono_direct * dry_gain);
  462. else
  463. l_out += l_direct * dry_gain;
  464. if((float)fabs((double)l_out) > peaks[0].value) {
  465. peaks[0].value = (float)fabs((double)l_out);
  466. peaks[0].position = outsamps;
  467. }
  468. if(fputfloatEx(&l_out,ofd) < 1){
  469. fprintf(stderr,"\nreverb: error writing output file");
  470. exit(1);
  471. }
  472. if(outchans>=2){
  473. r_out = r_direct * dry_gain;
  474. r_out += chan_ap[1]->tick(out) * wet_gain;
  475. if((float)fabs((double)r_out) > peaks[1].value) {
  476. peaks[1].value = (float)fabs((double)r_out);
  477. peaks[1].position = outsamps;
  478. }
  479. if(fputfloatEx(&r_out,ofd) < 1){
  480. fprintf(stderr,"\nreverb: error writing output file");
  481. exit(1);
  482. }
  483. }
  484. //now any further channels
  485. for(i=2;i < outchans; i++){
  486. l_out = chan_ap[i]->tick(out) * wet_gain;
  487. if((float)fabs((double)l_out) > peaks[i].value) {
  488. peaks[i].value = (float)fabs((double)l_out);
  489. peaks[i].position = outsamps;
  490. }
  491. if(fputfloatEx(&out,ofd) < 1){
  492. fprintf(stderr,"\nreverb: error writing output file");
  493. exit(1);
  494. }
  495. }
  496. outsamps++;
  497. if((outsamps % step) == 0)
  498. //inform(step,sr);
  499. fprintf(stdout,"%.2f\r",(double)outsamps/(double)sr);
  500. }
  501. //end of infile; play out reverb tail as much as we're allowed
  502. int trtime = (int)( trailertime * sr);
  503. for(i = 0; i < trtime; i++){
  504. float tr_l_ip,tr_r_ip = 0.0f, tr_out,tr_earlies,tr_l_direct,tr_mono_direct,tr_r_direct,
  505. tr_l_out,tr_r_out;
  506. tr_l_ip = 0.0;
  507. if(tc_l_lowcut)
  508. tr_l_ip = (float) tc_l_lowcut->tick(tr_l_ip);
  509. if(l_highcut)
  510. tr_l_ip = (float) l_highcut->tick(tr_l_ip);
  511. tr_l_direct = tr_l_ip;
  512. tr_mono_direct = tr_l_direct;
  513. tr_r_direct = tr_l_direct;
  514. //handle R channnel if active
  515. if(chans==2){
  516. tr_r_ip = 0.0; // inout is zero, except if using input filters
  517. // get any samples from input conditioning filters
  518. if(tc_r_lowcut)
  519. tr_r_ip = (float) tc_r_lowcut->tick(tr_r_ip);
  520. if(r_highcut)
  521. tr_r_ip = (float) r_highcut->tick(tr_r_ip);
  522. tr_r_direct = tr_r_ip;
  523. if(want_mono)
  524. tr_mono_direct = (tr_l_direct + tr_r_direct) *0.5f;
  525. }
  526. if(lp)
  527. tr_mono_direct = (float) lp->tick(tr_mono_direct);
  528. tr_earlies = tdelay->tick(tr_mono_direct);
  529. //send (filtered) mono output from reverberator
  530. tr_out = tr_earlies + moorerverb->tick(tr_earlies * tr_mono_direct) * diff_gain;
  531. tr_l_out = chan_ap[0]->tick(tr_out) * wet_gain;
  532. if(want_mono)
  533. tr_l_out += (tr_mono_direct * dry_gain);
  534. else
  535. tr_l_out += tr_l_direct * dry_gain;
  536. if((float)fabs((double)tr_l_out) > peaks[0].value) {
  537. peaks[0].value = (float)fabs((double)tr_l_out);
  538. peaks[0].position = outsamps;
  539. }
  540. if(fputfloatEx(&tr_l_out,ofd) < 1){
  541. fprintf(stderr,"\nreverb: error writing to output file");
  542. exit(1);
  543. }
  544. if(outchans>=2){
  545. tr_r_out = tr_r_direct * dry_gain;
  546. tr_r_out += chan_ap[1]->tick(tr_out) * wet_gain;
  547. if((float)fabs((double)tr_r_out) > peaks[1].value) {
  548. peaks[1].value = (float)fabs((double)tr_r_out);
  549. peaks[1].position = outsamps;
  550. }
  551. if(fputfloatEx(&tr_r_out,ofd) < 1){
  552. fprintf(stderr,"\nreverb: error writing to output file");
  553. exit(1);
  554. }
  555. }
  556. for(int j = 2;j < outchans;j++){
  557. float ch_out;
  558. ch_out = chan_ap[j]->tick(tr_out) * wet_gain;
  559. if((float)fabs((double)ch_out) > peaks[j].value) {
  560. peaks[j].value = (float)fabs((double)ch_out);
  561. peaks[j].position = outsamps;
  562. }
  563. if(fputfloatEx(&ch_out,ofd) < 1){
  564. fprintf(stderr,"\nreverb: error writing to output file");
  565. exit(1);
  566. }
  567. }
  568. outsamps++;
  569. if((outsamps % step) ==0)
  570. //inform(step,sr);
  571. fprintf(stdout,"%.2f\r",(double)outsamps/(double)sr);
  572. }
  573. //stopwatch(0);
  574. printf("\nPEAK data:\n");
  575. for(i=0;i < outchans;i++)
  576. printf("Channel %d: %.4f at frame %d: %.4lf secs\n",i+1,
  577. peaks[i].value,peaks[i].position, (double) peaks[i].position / (double)props.srate);
  578. sndputpeaks(ofd,outchans,peaks);
  579. sndcloseEx(ifd);
  580. if(sndcloseEx(ofd) < 0)
  581. fprintf(stderr,"\nwarning: error closing outfile");
  582. delete [] peaks;
  583. if(moorerverb)
  584. delete moorerverb;
  585. for(i=0;i < outchans;i++)
  586. delete chan_ap[i];
  587. delete [] chan_ap;
  588. delete tdelay;
  589. if(usertaps)
  590. delete [] ptaps;
  591. return 0;
  592. }
  593. void
  594. usage(void)
  595. {
  596. fprintf(stderr,"\nreverb: Multi-channel reverberator\n\tVersion 1.1 Release 4 CDP 1998,1999\n");
  597. fprintf(stderr,
  598. "usage: reverb [flags] infile outfile rgain mix rvbtime absorb\n"
  599. " lpfreq trailertime [times.txt]\n"
  600. "flags : any or all of the following:\n"
  601. " -cN : create outfile with N channels (1 <= N <= 16 : default = 2)\n"
  602. " -eFILE: read early reflections from breakpoint textfile FILE\n"
  603. " -f : force floating-point output (default: infile format)\n"
  604. " -HN : apply Highcut filter with cutoff freq N Hz to infile (6db/oct)\n"
  605. " -LN : apply Lowcut filter with cutoff freq N Hz to infile (12dB/oct)\n"
  606. " -pN : force reverb predelay to N msecs (shifts early reflections)\n"
  607. "rgain : set level of dense reverb (0.0 <= rgain <= 1.0)\n"
  608. "mix : dry/wet balance (source and reverb): 1.0<-- mix -->= 0.0\n"
  609. "rvbtime : reverb decay time (to -60dB) in seconds\n"
  610. "absorb : degree of hf damping to suggest air absorption: 0.0 <= absorb <= 1.0\n"
  611. "lpfreq : lowpass filter cutoff freq (Hz) applied at input to reverb\n"
  612. " to disable either filter (absorb, lpfreq), use the value 0\n"
  613. "trtime : trailer time added to outfile for reverb tail (secs)\n"
  614. "times.txt: list of delay times (msecs) for 6 comb and 4 allpass filters\n"
  615. );
  616. exit(0);
  617. }
  618. long readtaps(FILE *fp, deltap **taps,double sr)
  619. {
  620. vector<deltap> vtaps;
  621. deltap thistap;
  622. char line[256];
  623. int ntaps = 0;
  624. int linecount = 1;
  625. bool error = false;
  626. double time= 0.0, current_time = 0.0, val = 0.0;
  627. double sample_duration = 1.0 / sr;
  628. if(fp==0 || sr <= 0.0){
  629. *taps = 0;
  630. return 0;
  631. }
  632. while(fgets(line,256,fp)) {
  633. int got;
  634. if(line[0] == '\n' || line[0]== ';'){ //allow comment lines; this does not check for leading white-space...
  635. linecount++;
  636. continue;
  637. }
  638. if((got = sscanf(line,"%lf%lf",&time,&val))<2){
  639. fprintf(stderr,"\nerror in reflections file: line %d",linecount);
  640. error =true;
  641. break;
  642. }
  643. if(time < 0.0){
  644. fprintf(stderr,"\nerror in tapfile: line %d: bad time value",linecount);
  645. error = true;
  646. break;
  647. }
  648. //if (first) val = 0.0, or VERY close
  649. if(time < sample_duration) { //non-zero time must be at least one sample on!
  650. fprintf(stderr,"\nWARNING: very small taptime %.4lf treated as zero",time);
  651. time = 0.0;
  652. }
  653. if(current_time != 0.0 && time==current_time){
  654. fprintf(stderr,"\nWARNING: duplicate time in line %d: ignored",linecount);
  655. linecount++;
  656. continue;
  657. }
  658. if(time < current_time){
  659. fprintf(stderr,"\nerror in tapfile: time out of sequence: line %d",linecount);
  660. error = true;
  661. break;
  662. }
  663. current_time = time;
  664. thistap.pos = time;
  665. if(val <=0.0 || val > 1.0){
  666. fprintf(stderr,"\nerror: bad amplitude in tapfile: line %d",linecount);
  667. error = true;
  668. break;
  669. }
  670. thistap.val = val;
  671. vtaps.push_back(thistap);
  672. linecount++;
  673. }
  674. ntaps = vtaps.size();
  675. if(ntaps==0){
  676. fprintf(stderr,"\ntapfile contains no data!");
  677. error = true;
  678. }
  679. if(error){
  680. *taps = 0;
  681. return 0;
  682. }
  683. *taps = new deltap[ntaps];
  684. if(taps==0)
  685. return 0;
  686. vector<deltap>::iterator I = vtaps.begin();
  687. for(int i = 0; I != vtaps.end();i++, I++){
  688. (*taps)[i].pos = I->pos;
  689. (*taps)[i].val = I->val;
  690. }
  691. return ntaps;
  692. }