reverb.cpp 21 KB

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