| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949 |
- /*
- * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
- * http://people.bath.ac.uk/masrwd
- * http://www.composersdesktop.com
- * This file is part of the CDP System.
- * The CDP System is free software; you can redistribute it
- * and/or modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * The CDP System 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 Lesser General Public License for more details.
- * You should have received a copy of the GNU Lesser General Public
- * License along with the CDP System; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
- #include <string.h>
- #ifdef _DEBUG
- #include <assert.h>
- #endif
- #include <algorithm>
- #include <vector>
- using namespace std;
- // TODO: clean up "new" handling, move to VC7 and get to use exceptions!
- enum cmdargs {
- PROGNAME,
- INFILE,
- OUTFILE,
- REVERBTYPE,
- RGAIN,
- MIX,
- FEEDBACK,
- LPABSORB,
- LPFREQ,
- TRTIME,
- CONFIG
- };
- enum reverb_type {
- DIFF_SMALL,
- DIFF_MEDIUM,
- DIFF_LARGE
- };
- extern "C" {
- #include "portsf.h"
- //NB requires stdio.h etc - time to change this?
- }
- #include "reverberator.h"
- //RWD.10.98 TODO: for final versions, MUST limit input delay times! can't have 108sec delays.....
- //get all reflection data
- #include "reflect.cpp"
- long readtaps(FILE *fp, deltap **taps,double sr);
- const char* cdp_version = "6.0.0";
- void usage(void);
- int
- main(int argc,char *argv[])
- {
- int i,ifd, ofd,reverbtype = DIFF_SMALL;
- FILE *fp = 0;
- long chans,outchans = 2;
- double sr,trailertime;
- cdp_processor *cdpdiff = 0;
- NESTDATA data1,data2;
- PSF_CHPEAK *peaks;
- PSF_PROPS props;
- unsigned int time1a,time1b,time1c,time2a,time2b,time2c;
- double feedback,fb_lpfreq, lp_freq,predelay = -1.0;
- double nyquist,pseudo_nyquist;
- double dry_gain,wet_gain;
- //double early_gain = 1.0;
- double diff_gain = 1.0;
- // nested-allpass diffusers
- small_diffuser *s_diffuser = 0;
- medium_diffuser *m_diffuser = 0;
- large_diffuser *l_diffuser = 0;
- // traioling plain allpasses for front pair de-correlation
- allpassfilter *ap1_l = 0,*ap2_l = 0;
- allpassfilter *ap1_r = 0,*ap2_r = 0;
- // decorrelation for other channels (surround)
- allpassfilter **chan_ap = 0;
- onepole *lp = 0;
- // tonecontrols max -12dB slope, variable;
- tonecontrol *tc_l_lowcut = 0, *tc_r_lowcut = 0;
- tonecontrol *l_highcut = 0, *r_highcut = 0;
- double lowcut_freq = 0.0,highcut_freq = 0.0;
- tapdelay *tdelay = 0;
- long ntaps = 0;
- deltap *ptaps = 0;
- bool want_mono = false;
- bool want_floats = false;
- bool usertaps = false;
- bool double_damping = false;
- if((argc==2) && strcmp(argv[1],"--version")==0) {
- fprintf(stdout,"%s\n",cdp_version);
- fflush(stdout);
- return 0;
- }
- if(psf_init()){
- fprintf(stderr,"rmverb: initialisation failed\n");
- return 1;
- }
-
- if(argc < CONFIG){
- if(argc > 1)
- fprintf(stderr,"rmverb: insufficient arguments\n");
- usage();
- }
-
- while((argc > 1) && argv[1][0]=='-'){
- char *arg = argv[1];
- switch(*(++arg)){
- case('p'):
- if(*(++arg) =='\0'){
- fprintf(stderr,"rmverb: predelay requires a parameter\n");
- usage();
- }
-
- predelay = atof(arg);
- if(predelay <0.0){
- fprintf(stderr,"rmverb: predelay must be >= 0.0\n");
- usage();
- }
- predelay *= 0.001; //get msecs from user
- break;
- case('c'):
- if(*(++arg) == '\0') {
- fprintf(stderr,"rmverb: -c flag requires a value\n");
- usage();
- }
- outchans = atoi(arg);
- if(outchans < 1 || outchans > 16){
- fprintf(stderr,"rmverb: impossible channel value requested\n");
- usage();
- }
- if(outchans==1)
- want_mono = true;
- break;
- case('d'):
- double_damping = true;
- break;
- case('e'):
- if(*(++arg)=='\0'){
- fprintf(stderr,"rmverb: -e flag needs a filename\n");
- usage();
- }
- if((fp = fopen(arg,"r")) ==0){
- fprintf(stderr,"rmverb: unable to open breakpoint file %s\n",arg);
- exit(1);
- }
- usertaps = true;
- break;
- // -f is CDP-specific: read old 32bit int format as floats
- case('f'):
- want_floats = true;
- if(*(++arg) != '\0')
- fprintf(stderr,"rmverb WARNING: -f flag does not take a parameter\n");
- break;
- case('L'):
- if(*(++arg) == '\0'){
- fprintf(stderr,"rmverb: -L flag needs frequency argument\n");
- usage();
- }
- lowcut_freq = atof(arg);
- if(lowcut_freq <= 0.0){
- fprintf(stderr,"rmverb: Lowcut freq must be greater than zero\n");
- usage();
- }
- break;
- case('H'):
- if(*(++arg) == '\0'){
- fprintf(stderr,"rmverb: -H flag needs frequency argument\n");
- usage();
- }
- highcut_freq = atof(arg);
- if(highcut_freq <= 0.0){
- fprintf(stderr,"rmverb: Highcut freq must be greater than zero\n");
- usage();
- }
- break;
- default:
- fprintf(stderr,"rmverb: illegal flag option %s\n",argv[1]);
- usage();
- break;
- }
- argc--;
- argv++;
- }
- if(argc < CONFIG){
- printf("rmverb: insufficient arguments\n");
- usage();
- }
- #ifdef _DEBUG
- if(predelay > 0.0)
- printf("got predelay = %.4lf\n",predelay);
- #endif
- chan_ap = new allpassfilter*[outchans];
- if(chan_ap==0){
- fprintf(stderr,"rmverb: no memory for multi-channel diffusion\n");
- exit(1);
- }
- if((ifd = psf_sndOpen(argv[INFILE],&props,0)) < 0) {
- fprintf(stderr,"\nrmverb: cannot open input file %s", argv[INFILE]);
- exit(1);
- }
-
- if(props.format <= PSF_FMT_UNKNOWN || props.format > PSF_AIFC){
- fprintf(stderr,"infile is not a recognised soundfile\n");
- psf_sndClose(ifd);
- return 1;
- }
-
- sr = (double) props.srate;
- nyquist = sr / 2.0;
- pseudo_nyquist = nyquist * 0.7; // filters not reliable close to nyquist
- chans = props.chans;
- if(chans > 2){
- fprintf(stderr,"rmverb can only accept mono or stereo files\n");
- exit(1);
- }
-
-
- reverbtype = atoi(argv[REVERBTYPE]) - 1;
- if((reverbtype < DIFF_SMALL) || (reverbtype > DIFF_LARGE)){
- fprintf(stderr,"rmverb: rmsize must be 1,2 or 3\n");
- exit(1);
- }
-
- diff_gain = atof(argv[RGAIN]);
- if(diff_gain <= 0.0 || diff_gain > 1.0){
- fprintf(stderr,"rgain must be > 0 and <= 1.0\n");
- exit(1);
- }
- dry_gain = atof(argv[MIX]); //global output gain from diffuser
- if(dry_gain < 0.0 || dry_gain > 1.0 ){
- printf("reverb: mix must be between 0.0 and 1.0\n");
- usage();
- }
- wet_gain = 1.0f - dry_gain;
- // some odd people like to have 100% wet signal...
- wet_gain *= 2.0; //not v scientific, but works...
-
- feedback = atof(argv[FEEDBACK]); //feedback in diffuser
- if(feedback < 0.0 || feedback >1.0){
- printf("rmverb: feedback must be within 0.0 - 1.0\n");
- usage();
- }
-
- fb_lpfreq = atof(argv[LPABSORB]); //feedback lp-filter in diffuser
- if(fb_lpfreq < 0.0 || fb_lpfreq > pseudo_nyquist){
- printf("rmverb: absorb must be within 0 to %f\n",pseudo_nyquist);
- usage();
- }
- lp_freq = atof(argv[LPFREQ]);
- if(lp_freq < 0.0 || lp_freq > pseudo_nyquist){
- printf("rmverb: lpfreq must be within 0.0 to %.4lfHz\n",pseudo_nyquist);
- usage();
- }
- trailertime = atof(argv[TRTIME]);
- if(trailertime < 0.0){
- fprintf(stderr,"rmverb: trtime must be >= 0.0\n");
- usage();
- }
- //from -m flag; will override a stereo input too
- if(want_mono)
- outchans = 1;
- // NB: now specifying two trailing allpasses for diffuser
- // handles front pair; currently using chan_ap[] only for remaining channels
- // values are experimental!
- double apdelay = 0.02;
- double apfac = 0.02 / outchans;
- for(i = 0; i < outchans; i++){ // or use a random modifier?
- chan_ap[i] = new allpassfilter(sr,to_prime(apdelay - apfac * (double)i,sr),0.4,0);
- if(chan_ap[i] ==0){
- fprintf(stderr,"rmverb: no memory for output diffusers\n");
- exit(1);
- }
- if(!chan_ap[i]->create()){
- fprintf(stderr,"rmverb: no memory for output diffusers\n");
- exit(1);
- }
- }
- // custom early reflections from time/value text-file
- if(usertaps){
- #ifdef _DEBUG
- assert(fp);
- #endif
- ntaps = readtaps(fp,&ptaps,sr);
- if(ntaps==0){
- fprintf(stderr,"rmverb: error reading tapfile\n");
- exit(1);
- }
- printf("rmverb: loaded %ld early reflections from tapfile\n",ntaps);
- fclose(fp);
- fp = 0;
- }
- //create input low/high filters, if wanted
- if(lowcut_freq > 0.0){
- if(highcut_freq > 0.0 && highcut_freq <= lowcut_freq){
- fprintf(stderr,"rmverb: Highcut frequency must be higher than Lowcut frequency\n");
- usage();
- }
- //lowcut is based on low shelving eq filter; here fixed at 12dB
- // but can easily add this as user param
- tc_l_lowcut = new tonecontrol(lowcut_freq,-12.0,LOW_SHELVE,sr);
- if(!tc_l_lowcut->create()){
- fprintf(stderr,"rmverb: unable to create Lowcut filter\n");
- exit(1);
- }
- if(chans==2){
- tc_r_lowcut = new tonecontrol(lowcut_freq,-12.0,LOW_SHELVE,sr);
- if(!tc_r_lowcut->create()){
- fprintf(stderr,"rmverb: unable to create Lowcut filter\n");
- exit(1);
- }
- }
- }
- if(highcut_freq > 0.0){
- l_highcut = new tonecontrol(highcut_freq,-12.0,HIGH_SHELVE,sr);
- if(!l_highcut->create()){
- fprintf(stderr,"rmverb: unable to create Highcut filter\n");
- exit(1);
- }
- if(chans==2){
- r_highcut = new tonecontrol(highcut_freq,-12.0,HIGH_SHELVE,sr);
- if(!r_highcut->create()){
- fprintf(stderr,"\nrmverb: unable to create Highcut filter");
- exit(1);
- }
- }
- }
- //now create diffuser
- switch(reverbtype){
- case(DIFF_SMALL):
- time1a = to_prime(0.0047, sr);
- time1b = to_prime(0.022, sr);
- time1c = to_prime(0.0083,sr);
- time2a = to_prime(0.036,sr);
- time2b = to_prime(0.03,sr);
- time2c = 0;//dummy
- data1.time1 = time1a;
- data1.time2 = time1b;
- data1.time3 = time1c;
- data1.gain1 = 0.3;
- data1.gain2 = 0.4;
- data1.gain3 = 0.6;
- data1.delay1 = 0;
- data1.delay2 = 0;
- data2.time1 = time2a;
- data2.time2 = time2b;
- data2.time3 = time2c;
- data2.gain1 = 0.1;
- data2.gain2 = 0.4;
- data2.gain3 = 0.0; //not used by gardner
- data2.delay1 = 0;
- data2.delay2 = 0;
- break;
- case(DIFF_MEDIUM):
- time1a = to_prime(/*0.0047*/0.035, sr);
- time1b = to_prime(0.0083, sr);
- time1c = to_prime(0.022,sr);
- time2a = to_prime(/*0.0292*/0.039,sr);
- time2b = to_prime(0.0098,sr);
- time2c = 0;//dummy
- data1.time1 = time1a;
- data1.time2 = time1b;
- data1.time3 = time1c;
- data1.gain1 = 0.3;
- data1.gain2 = 0.7;
- data1.gain3 = 0.5;
- data1.delay1 = 0;
- data1.delay2 = 0;
- data2.time1 = time2a;
- data2.time2 = time2b;
- data2.time3 = time2c;
- data2.gain1 = 0.3;
- data2.gain2 = 0.6;
- data2.gain3 = 0.0; //not used by gardner
- data2.delay1 = 0;
- data2.delay2 = 0;
- break;
- case(DIFF_LARGE):
- // VERY LARGE!
- // NB: orig outer delays from Gardner generate very strong discrete echoes,
- // and hence also some pulsing in the dense reverb
- // for really large spaces (caverns) this is OK, otherwise we ant to control this
- // via predelay, and earlies
- // NOTE: this will NOT WORK when sharing a single delay line!
- // in that case, outer delay MUST be > internal ones
- // TODO: devise user param to control a range of delay values
- // (maybe derived from feedback level?), to further scale room size
- time1a = to_prime(0.03 /*0.087*/, sr);
- time1b = to_prime(0.062, sr);
- time1c = to_prime(0.022,sr); //dummy: not used
- time2a = to_prime(0.018/*0.12*/,sr);
- time2b = to_prime(0.076,sr);
- time2c = to_prime(0.03,sr);
- data1.time1 = time1a;
- data1.time2 = time1b;
- data1.time3 = time1c;
- data1.gain1 = 0.5;
- data1.gain2 = 0.25;
- data1.gain3 = 0.0; //not used
- data1.delay1 = 0;
- data1.delay2 = 0;
- data2.time1 = time2a;
- data2.time2 = time2b;
- data2.time3 = time2c;
- data2.gain1 = 0.5;
- data2.gain2 = 0.25;
- data2.gain3 = 0.25;
- data2.delay1 = 0;
- data2.delay2 = 0;
- break;
- default:
- #ifdef _DEBUG
- assert(false);
- #endif
- break;
- }
- #ifdef _DEBUG
- //RWD: development only!
- if(argc==CONFIG+1){
- int got = 0;
- double time1a,time1b,time1c,time2a,time2b,time2c,gain1a,gain1b,gain1c,gain2a,gain2b,gain2c;
- FILE *fp = fopen(argv[CONFIG],"r");
- if(!fp)
- printf("rmverb: can't open datafile %s, using presets\n",argv[CONFIG]);
- else {
- // time1a,gain1a,time2a,gain2a,time3a,gain3a...
- printf("loading diffusion data...\n");
- got = fscanf(fp,"%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
- &time1a,&gain1a,
- &time1b,&gain1b,
- &time1c,&gain1c,
- &time2a,&gain2a,
- &time2b,&gain2b,
- &time2c,&gain2c);
- fclose(fp);
- if(got != 12)
- printf("rmverb: error reading data values\n");
- else {
- data1.time1 = to_prime(time1a,sr);
- data1.time2 = to_prime(time1b,sr);
- data1.time3 = to_prime(time1c,sr);
- data1.gain1 = gain1a;
- data1.gain2 = gain1b;
- data1.gain3 = gain1c;
- data2.time1 = to_prime(time2a,sr);
- data2.time2 = to_prime(time2b,sr);
- data2.time3 = to_prime(time2c,sr);
- data2.gain1 = gain2a;
- data2.gain2 = gain2b;
- data2.gain3 = gain2c;
- }
- }
- }
-
- else{
- printf("using preset parameters:\n");
- printf("DATA 1: time1 = %u\n\ttime2 = %u\n\ttime3 = %u\n",data1.time1,data1.time2,data1.time3);
- printf("DATA 2: time1 = %u\n\ttime2 = %u\n\ttime3 = %u\n",data2.time1,data2.time2,data2.time3);
- printf("feedback = %.4lf,fb_lpfreq = %.4lf\n",feedback,fb_lpfreq);
- printf("dry gain = %.4lf, wet gain = %.4lf\n",dry_gain,wet_gain);
- }
- #endif
-
- switch(reverbtype){
- case(DIFF_SMALL):
- printf("rmverb: using small room model\n");
- s_diffuser = new small_diffuser((unsigned int)(0.024 * sr),&data1,&data2,feedback,fb_lpfreq,sr);
- s_diffuser->set_damping(double_damping);
- cdpdiff = dynamic_cast<cdp_processor *>(s_diffuser);
- if(!usertaps){
- ptaps = smalltaps;
- ntaps = sizeof(smalltaps) / sizeof(deltap);
- }
- break;
- case(DIFF_MEDIUM):
- printf("rmverb: using medium room model\n");
- m_diffuser = new medium_diffuser(0.108,&data1,&data2,feedback,fb_lpfreq,sr);
- m_diffuser->set_damping(double_damping);
- cdpdiff = dynamic_cast<cdp_processor *>(m_diffuser);
- if(!usertaps){
- ptaps = mediumtaps;
- ntaps = sizeof(mediumtaps) / sizeof(deltap);
- }
- //ptaps = longtaps;
- //ntaps = sizeof(longtaps) / sizeof(deltap);
- break;
- case(DIFF_LARGE):
- printf("using large room model\n");
- //fb_lpfreq = 2600.0; // Gardner fixed val
- l_diffuser = new large_diffuser(&data1,&data2,feedback,fb_lpfreq,sr);
- l_diffuser->set_damping(double_damping);
- cdpdiff = dynamic_cast<cdp_processor *>(l_diffuser);
- if(!usertaps){
- ptaps = largetaps;
- ntaps = sizeof(largetaps) / sizeof(deltap);
- }
- break;
- default:
- #ifdef _DEBUG
- assert(false);
- #endif
- break;
- }
- if(!cdpdiff){
- puts("rmverb: no memory for reverberator\n");
- exit(1);
- }
- if(!cdpdiff->create()){
- puts("rmverb: no memory for reverberator buffers\n");
- exit(1);
- }
- /*********** GLOBAL COMPONENTS *************/
- if(lp_freq > 0.0)
- lp = new onepole(lp_freq,sr,LOW_PASS);
-
- //if user prescribes a predelay, adjust all taptimes
- if(predelay >= 0.0){
- double current_delay = ptaps[0].pos;
- double adjust = predelay - current_delay;
- for(int i = 0; i < ntaps; i++)
- ptaps[i].pos += adjust;
- }
- else
- predelay = ptaps[0].pos; //to report to user
- tdelay = new tapdelay(ptaps,ntaps,sr);
- if(!tdelay->create()) {
- fputs("rmverb: no memory for early reflections\n",stderr);
- return 1;
- }
- //max_gain = tdelay->get_maxgain();
- //if(max_gain > 1.0)
- // tdelay->set_gain(max_gain * early_gain);
- // final allpass chain: 0.02,0.014,0.009,0.006
- ap1_l = new allpassfilter(sr,to_prime(0.02,sr),-0.6,0);
- ap2_l = new allpassfilter(sr,to_prime(0.014,sr),0.4,0);
- // ap3_l = new allpassfilter(to_prime(0.009,sr),-0.6,0);
- // ap4_l = new allpassfilter(to_prime(0.006,sr),0.4,0);
- if(!(ap1_l->create()
- && ap2_l->create()
- // && ap3_l->create()
- // && ap4_l->create()
- )){
- fprintf(stderr,"rmverb: no memory for allpass chain\n");
- exit(1);
- }
- if(outchans >=2){
- // slightly different params to make stereo!
- // tune front pair precisely; further chans use formula
- ap1_r = new allpassfilter(sr,to_prime(0.025,sr),-0.6,0);
- ap2_r = new allpassfilter(sr,to_prime(0.0145,sr),0.4,0);
- // ap3_r = new allpassfilter(to_prime(0.0095,sr),-0.6,0);
- // ap4_r = new allpassfilter(to_prime(0.0065,sr),0.4,0);
- if(!(ap1_r->create()
- && ap2_r->create()
- // && ap3_r->create()
- // && ap4_r->create()
- )){
- fprintf(stderr,"rmverb: no memory for allpass chain\n");
- exit(1);
- }
- }
-
- printf("using %ld early reflections: predelay = %.2lf msecs\n",ntaps,predelay * 1000.0);
- int clipfloats = 1;
- int minheader = 0;
- if(want_floats) {
- props.samptype =PSF_SAMP_IEEE_FLOAT;
- clipfloats = 0;
- }
- props.chans = outchans;
-
- if((ofd = psf_sndCreate(argv[OUTFILE],&props,clipfloats,minheader,PSF_CREATE_RDWR)) < 0) {
- fprintf(stderr,"rmverb: cannot open output file %s\n",argv[OUTFILE]);
- return 1;
- }
- /****
- |wet
- ***** in-> ---o-->pre_lp -->earlies-o--->diffuser-->*diffgain-->--+--*---+----out
- | | | |
- | |>--------*-(earlies)->-------| *--dry
- | ^earlygain |
- |__________________________________________________________|
- ****/
- printf("\ninfile chans = %ld, outfile chans = %ld",chans,outchans);
- printf("\n");
- long outframes = 0;
- long step = (long)(0.25 * sr);
- float insamps[2], outsamps[16];
- double l_out,r_out;
- // float finsamp, foutsamp;
- for(;;){
- int rv;
- double l_ip,r_ip, out,l_direct,r_direct,mono_direct,earlies = 0.0f;
-
- //read mono/left
- if((rv = psf_sndReadFloatFrames(ifd,insamps,1)) < 0){
- fprintf(stderr,"rmverb: error reading file\n");
- exit(1);
- }
- if(rv==0) break; // end of inputfile - handle any trailertime
- l_ip = (double) insamps[0];
- //apply any conditioning to direct signal
- if(tc_l_lowcut)
- l_ip = tc_l_lowcut->tick(l_ip);
- if(l_highcut)
- l_ip = l_highcut->tick(l_ip);
- l_direct = l_ip;
- mono_direct = l_direct;
- r_direct = l_direct;
- //handle R channnel if active
- if(chans==2){
- r_ip = (double) insamps[1];
- //apply any conditioning to direct signal
- if(tc_r_lowcut)
- r_ip = tc_r_lowcut->tick(r_ip);
- if(r_highcut)
- r_ip = r_highcut->tick(r_ip);
- r_direct = r_ip;
- if(want_mono) //input merged sig to reverbreator
- mono_direct = (l_direct + r_direct) * 0.5;
- }
- // mono_direct = (mixed) mono input to reverb
- //possibly also filter it...
- if(lp)
- mono_direct = lp->tick(mono_direct); //input lowpass
- earlies = tdelay->tick(mono_direct); //early reflections
- //send (filtered) mono output from reverberator
- // TODO: find formula for diffgain...
- out = earlies + (cdpdiff->tick(earlies + mono_direct) * diff_gain * 0.707); //the dense reverberation
- // output:: use 2 allpasses per chan to generate stereo reverb, wider diffusion
- l_out = wet_gain * ap2_l->tick(ap1_l->tick(out));
- // old method - 1 allpass per channel
- //l_out = chan_ap[0]->tick(out) * wet_gain;
- if(outchans == 1)
- l_out += (mono_direct * dry_gain);
- else
- l_out += l_direct * dry_gain;
- outsamps[0] = (float) l_out;
-
- if(outchans>=2){
- r_out = wet_gain * ap2_r->tick(ap1_r->tick(out));
- // old version
- //r_out = chan_ap[1]->tick(out) * wet_gain;
- r_out += r_direct * dry_gain;
- outsamps[1] = (float) r_out;
- }
- //now any further channels; reduced level of direct sig
- for(i=2;i < outchans; i++){
- out = earlies + (cdpdiff->tick(earlies + (mono_direct * 0.3)) * diff_gain * 0.707);
- l_out = wet_gain * chan_ap[i]->tick(out);
- outsamps[i] = (float) l_out;
-
- }
- if(psf_sndWriteFloatFrames(ofd,outsamps,1) != 1){
- fprintf(stderr,"rmverb: error writing output file\n");
- return 1;
- }
- outframes++;
- if((outframes % step) == 0)
- fprintf(stdout,"%.2f\r",(double)outframes/(double)sr);
- }
-
- int trtime = (int)( trailertime * sr);
- for(i = 0; i < trtime; i++){
- double tr_l_ip,tr_r_ip = 0.0f,tr_out,tr_l_direct,tr_r_direct,
- tr_mono_direct,tr_earlies = 0.0f,tr_l_out,tr_r_out;
- //need last active samps from input filter(s)
- tr_l_ip = 0.0;
- if(tc_l_lowcut)
- tr_l_ip = tc_l_lowcut->tick(tr_l_ip);
- if(l_highcut)
- tr_l_ip = l_highcut->tick(tr_l_ip);
- tr_l_direct = tr_l_ip;
- tr_mono_direct = tr_l_direct;
- tr_r_direct = tr_l_direct;
- //handle R channnel if active
- if(chans==2){
- tr_r_ip = 0.0; // inout is zero, except if using input filters
- // get any samples from input conditioning filters
- if(tc_r_lowcut)
- tr_r_ip = tc_r_lowcut->tick(0.0);
- if(r_highcut)
- tr_r_ip = r_highcut->tick(tr_r_ip);
- tr_r_direct = tr_r_ip;
- if(want_mono)
- tr_mono_direct = (tr_l_direct + tr_r_direct) *0.5f;
- }
- // mono_direct = (mixed) mono input to reverb
- //possibly also filter it...
- if(lp)
- tr_mono_direct = lp->tick(tr_mono_direct);
- tr_earlies = tdelay->tick(tr_mono_direct);
- //send (filtered) mono output from reverberator
- tr_out = tr_earlies + (cdpdiff->tick(tr_earlies + tr_mono_direct) * diff_gain * 0.707);
- // output:: use 2 allpasses to generate stereo reverb, further diffusion
- // new version
- tr_l_out = wet_gain * ap2_l->tick(ap1_l->tick(tr_out));
- // old version
- //tr_l_out = chan_ap[0]->tick(tr_out) * wet_gain;
- if(want_mono)
- tr_l_out += (tr_mono_direct * dry_gain);
- else
- tr_l_out += tr_l_direct * dry_gain;
- outsamps[0] = (float) tr_l_out;
-
- if(outchans>=2){
- tr_r_out = wet_gain * ap2_r->tick(ap1_r->tick(tr_out));
- // old version:
- //tr_r_out = chan_ap[1]->tick(tr_out) * wet_gain;
- tr_r_out += tr_r_direct * dry_gain; // still need this cos of input filters
- outsamps[1] = (float) tr_r_out;
- }
- for(int j = 2; j < outchans; j++){
- tr_r_out = chan_ap[j]->tick(tr_out) * wet_gain;
- outsamps[j] = (float) tr_r_out;
-
- }
- if(psf_sndWriteFloatFrames(ofd,outsamps,1) != 1){
- fprintf(stderr,"rmverb: error writing output file\n");
- return 1;
- }
- outframes++;
- if((outframes % step)==0)
- //inform(step,sr);
- fprintf(stdout,"%.2f\r",(double)outframes/(double)sr);
- }
- fprintf(stdout,"%.2f\n",(double)outframes/(double)sr);
- //stopwatch(0);
-
- peaks = (PSF_CHPEAK *) malloc(sizeof(PSF_CHPEAK) * outchans);
- if(peaks && psf_sndReadPeaks(ofd,peaks,NULL)) {
- printf("\nPEAK data:\n");
- for(i=0;i < outchans;i++)
- printf("Channel %d: %.4f at frame %u: %.4lf secs\n",i+1,
- peaks[i].val,peaks[i].pos, (double) peaks[i].pos / (double)props.srate);
- }
- else {
- fputs("rmverb: warning: unable to read PEAK data\n",stderr);
- }
-
- psf_sndClose(ifd);
-
- if(psf_sndClose(ofd) < 0)
- fprintf(stderr,"rmverb: error closing outfile\n");
- printf("\n");
-
- psf_finish();
- free(peaks);
-
- delete tdelay;
-
- if(usertaps)
- delete [] ptaps;
- //can I call delete on base-class ptr?
- if(s_diffuser)
- delete s_diffuser;
- if(m_diffuser)
- delete m_diffuser;
- if(l_diffuser)
- delete l_diffuser;
- if(lp)
- delete lp;
- if(outchans > 1){
- for(i=0;i < outchans; i++)
- delete chan_ap[i];
- delete [] chan_ap;
- }
- delete tc_l_lowcut;
- delete tc_r_lowcut;
- delete l_highcut;
- delete r_highcut;
- return 0;
- }
- void
- usage(void)
- {
- fprintf(stderr,"\nrmverb: Multi-channel reverb with room simulation\n\tVersion 1.3 Release 5 CDP 1998,1999,2011");
- fprintf(stderr,
- "\nusage:\nrmverb [flags] infile outfile rmsize rgain mix fback absorb lpfreq trtime"
- "\nflags : any or all of the following"
- "\n -cN : create outfile with N channels (1 <= N <= 16 : default =2)"
- "\n -d : apply double lowpass damping (internal to nested allpasses)"
- "\n (see <absorb>. NB: reduces reverb time: "
- "\n increase feedback to compensate)"
- "\n -eFILE: read early reflections data from breakpoint textfile FILE"
- "\n -f : force floating-point output (default: infile format)"
- "\n -HN : apply 12dB Highcut filter with cutoff freq N Hz) to infile"
- "\n -LN : apply 12dB Lowcut filter with cutoff freq N Hz to infile"
- "\n (NB: currently fixed at 12dB/oct - could be made variable)\n"
- "\n -pN : force reverb predelay to N msecs (shifts early reflections)"
- "\nrmsize : 1,2 or 3: small, medium or large"
- "\nrgain : set level of dense reverb (0.0 < egain <= 1.0)"
- "\nmix : dry/wet balance (source and reverb). 1.0<-- mix -->=0.0"
- "\nfback : reverb feedback level: controls decay time. 0.0 <= fback <= 1.0"
- "\nabsorb : cutoff freq (Hz) of internal lowpass filters (models air absorption)\n"
- " (typ: 2500 for large room --- 4200 for small room "
- "\nlpfreq : lowpass filter cutoff freq(Hz) applied at input to reverb"
- "\n NB: to disable either filter (absorb,lpfreq), use the value 0"
- "\ntrtime : trailer time added to outfile for reverb tail (secs)\n");
- exit(0);
- }
- long readtaps(FILE *fp, deltap **taps,double sr)
- {
- vector<deltap> vtaps;
- deltap thistap;
- char line[256];
- int ntaps = 0;
- int linecount = 1;
- bool error = false;
- double time= 0.0, current_time = 0.0, val = 0.0;
- double sample_duration = 1.0 / sr;
- if(fp==0 || sr <= 0.0){
- *taps = 0;
- return 0;
- }
- while(fgets(line,256,fp)) {
- int got;
- if(line[0] == '\n' || line[0]== ';'){ //allow comment lines; this does not check for leading white-space...
- linecount++;
- continue;
- }
- if((got = sscanf(line,"%lf%lf",&time,&val))<2){
- fprintf(stderr,"\nerror in reflections file: line %d",linecount);
- error =true;
- break;
- }
- if(time < 0.0){
- fprintf(stderr,"\nerror in tapfile: line %d: bad time value",linecount);
- error = true;
- break;
- }
- //if (first) val = 0.0, or VERY close
- if(time < sample_duration) { //non-zero time must be at least one sample on!
- fprintf(stderr,"\nWARNING: very small taptime %.4lf treated as zero",time);
- time = 0.0;
- }
- if(current_time != 0.0 && time==current_time){
- fprintf(stderr,"\nWARNING: duplicate time in line %d: ignored",linecount);
- linecount++;
- continue;
- }
- if(time < current_time){
- fprintf(stderr,"\nerror in tapfile: time out of sequence: line %d",linecount);
- error = true;
- break;
- }
- current_time = time;
- thistap.pos = time;
- if(val <=0.0 || val > 1.0){
- fprintf(stderr,"\nerror: bad amplitude in tapfile: line %d",linecount);
- error = true;
- break;
- }
- thistap.val = val;
- vtaps.push_back(thistap);
- linecount++;
- }
-
- ntaps = vtaps.size();
- if(ntaps==0){
- fprintf(stderr,"\ntapfile contains no data!");
- error = true;
- }
- if(error){
- *taps = 0;
- return 0;
- }
- *taps = new deltap[ntaps];
- if(taps==0)
- return 0;
- vector<deltap>::iterator I = vtaps.begin();
- for(int i = 0; I != vtaps.end();i++, I++){
- (*taps)[i].pos = I->pos;
- (*taps)[i].val = I->val;
- }
- return ntaps;
- }
|