| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488 |
- /*
- * 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
- *
- */
-
- // reverberator.cpp: implementation of the reverberator class.
- // see also tapdelay.cpp
- //TODO: simple delay for use with tapped delayline
- // NB blame VC6 for horrible code, new returns null...
- //////////////////////////////////////////////////////////////////////
- #include <stdio.h>
- #include <stdlib.h>
- #include <memory.h>
- #include <math.h>
- #ifdef _DEBUG
- #include <assert.h>
- #endif
- #include "reverberator.h"
- #ifndef PI
- # define PI 3.141592653589793238462643
- #endif
- #ifndef TWOPI
- #define TWOPI (2.0 * PI)
- #endif
- #define ROOT2O2 (0.7071067811965475244)
- #define SPN 1.65436e-24 //RWD: use a proper MSVC version...?
- //////////////////////////////////////////////////////////////////////
- // Construction/Destruction
- //////////////////////////////////////////////////////////////////////
- //for example:
- //const deltap taps[4] = {{0.1,0.9},{0.25,.75},{0.5,.5},{0.618,.25}};
- //NB this accepts a zero taptime
- tapdelay::tapdelay(const deltap *taps,unsigned int ctaps,double sr) : dtaps(taps)
- {
- tapbuf = 0;
- ntaps = ctaps;
- buflen = 0;
- reader = 0;
- tapptr = 0;
- tapcoeff = 0;
- srate = sr;
- output_gain = 1.0;
- max_gain = 1.0;
- }
- #define SAFETY (2)
- bool tapdelay::create(void)
- {
- #ifdef _DEBUG
- assert(tapbuf==0);
- assert(ntaps > 0);
- #endif
- if(tapbuf)
- return false; //or some informative errorval...
- if(ntaps <=0)
- return false;
- buflen = (unsigned int)((dtaps[ntaps-1].pos * srate) + SAFETY);
- tapbuf = new double[buflen];
- if(!tapbuf)
- return false;
- memset(tapbuf,0,buflen*sizeof(double));
-
- tapptr = new unsigned int[ntaps];
- if(!tapptr){
- delete [] tapbuf; tapbuf = 0;
- return false;
- }
- tapcoeff = new double[ntaps];
- if(!tapcoeff) {
- delete [] tapbuf; tapbuf = 0;
- delete [] tapptr; tapptr = 0;
- return false;
- }
- //copy taps data, and get scalefac from total amps
- double sum = 0.0;
- for(unsigned int i = 0;i < ntaps;i++){
- //RWD.10.98 allow zero taptime, for input mix control...
- if(dtaps[i].pos == 0.0)
- tapptr[i] = 0;
- else
- tapptr[i] = buflen - (int)(dtaps[i].pos * srate);
-
- tapcoeff[i] = dtaps[i].val;
- sum += fabs(tapcoeff[i]);
- #ifdef _DEBUG
- assert(tapptr[i] < buflen);
- #endif
- }
- //some idiot might give us empty taps! */
- if(sum == 0.0){
- delete [] tapbuf; tapbuf = 0;
- delete [] tapptr; tapptr = 0;
- delete [] tapcoeff; tapcoeff = 0;
- return false;
- }
- max_gain = (double)ntaps / sum;
- one_over_ntaps = 1.0 / (double)ntaps;
- return true;
- }
- double tapdelay::get_maxgain(void)
- {
- return max_gain;
- }
- void tapdelay::set_gain(double gain)
- {
- output_gain = gain;
- }
- double tapdelay::tick(double insam)
- {
- double val = 0.0;
- tapbuf[reader++] = insam;
- if(reader == buflen)
- reader = 0;
- for(unsigned int i = 0;i < ntaps;i++) {
- val += ((tapbuf[tapptr[i]++]) * (tapcoeff[i]));
- if(tapptr[i] == buflen)
- tapptr[i] = 0;
- }
- val *= one_over_ntaps;
- return val * output_gain;
- }
- tapdelay::~tapdelay()
- {
- delete [] tapbuf;
- delete [] tapptr;
- delete [] tapcoeff;
- }
- delay::delay(unsigned int len, double gain)
- {
- delbuf= 0;
- dgain = gain;
- ptr = 0;
- buflen = len;
- }
- delay::~delay()
- {
- delete delbuf;
- }
- bool delay::create(void)
- {
- if(buflen <= 0)
- return false;
- if(delbuf)
- return false;
- delbuf = new double[buflen];
- if(!delbuf) {
- return false;
- }
- memset(delbuf,0,buflen*sizeof(double));
- return true;
- }
- double delay::tick(double insam)
- {
- double val;
- val = delbuf[ptr];
- delbuf[ptr++] = insam;
- if(ptr==buflen)
- ptr = 0;
- return val*dgain;
- }
- //////////////////////////////////////////////////////////////////////
- // allpassfilter
- //////////////////////////////////////////////////////////////////////
- //global helper functions: really belong in a new cdplib...except it's nice to have proper bool retval
- //straight from Csound: nreverb (vdelay.c)
- static bool prime( int val )
- {
- int i, last;
-
- last = (int)sqrt( (double)val );
- for ( i = 3; i <= last; i+=2 ) {
- if ( (val % i) == 0 ) return false;
- }
- return true;
- }
- unsigned int to_prime(double dur,double sr)
- {
- unsigned int time = (unsigned int) (dur * sr);
- if(time % 2== 0) time += 1;
- while(!prime(time))
- time +=2;
- return time;
- }
- #define VMODMAX (0.5)
- //standard allpass, with optional pre_delay
- allpassfilter::allpassfilter(long sr,unsigned int buflen, double ingain,unsigned int predelay = 0)
- {
- vmod = 0.1;
- vfreq = 0.5;
- rvbuf1 = 0;
- pre_dline = 0;
- writer1 = reader1 = 0;
- sinelfo = 0;
- noiselfo = 0;
- srate = sr;
- rvblen = buflen + (unsigned int)(buflen * VMODMAX);
- gain = ingain;
- pre_delay = predelay;
-
- }
- bool allpassfilter::create(void)
- {
- if((gain < -1.0) || (gain > 1.0)) //do I want negative gain vals?
- return false;
- rvbuf1 = new double[rvblen];
- if(!rvbuf1 ){
- #ifdef _DEBUG
- printf("\nallpass: failed to allocate buffer size %d",rvblen);
- #endif
- return false;
- }
-
- if(pre_delay > 0){
- pre_dline = new delay(pre_delay,1.0);
- if(!pre_dline->create()){
- #ifdef _DEBUG
- printf("\nallpass: can't create pre_dline" );
- #endif
- delete []rvbuf1; rvbuf1 = 0;
- return false;
- }
- }
- memset(rvbuf1,0,rvblen*sizeof(double));
- sinelfo = new fastlfo();
- sinelfo->init((double) srate,0.0,0,1);
- sinelfo->set_WaveType(LFO_SINE);
- sinelfo->set_freq(vfreq);
- sinelfo->set_mod(1.0);
- noiselfo = new fastlfo();
- noiselfo->init((double) srate,0.0,0,1);
- noiselfo->set_WaveType(LFO_RAND_GAUSS);
- noiselfo->set_freq(vfreq * 2.5);
- noiselfo->set_mod(1.0);
- return true;
- }
- void allpassfilter::set_vmod(double amount)
- {
- double numod = VMODMAX;
- if(amount < numod)
- numod = amount;
- vmod = numod;
- }
- void allpassfilter::set_vfreq(double freq)
- {
- vfreq = freq;
- sinelfo->set_freq(vfreq);
- noiselfo->set_freq(vfreq * 2.5);
- }
- /* TODO: optimize this: separate funcs for pre-dline and plain */
- double allpassfilter::tick(double insamp)
- {
- double output, input;
- input = insamp;
- output = (-gain) * input;
- output += rvbuf1[reader1++];
- input += gain * output;
- if(pre_dline)
- rvbuf1[writer1++] = pre_dline->tick(input);
- else
- rvbuf1[writer1++] = input;
- if(reader1 == rvblen)
- reader1 = 0;
- if(writer1 == rvblen)
- writer1 = 0;
- return output;
- }
- /* TODO: implement this! */
- double allpassfilter::vtick(double insamp)
- {
- double output, input;
- input = insamp;
- output = (-gain) * input;
- output += rvbuf1[reader1++];
- input += gain * output;
- if(pre_dline)
- rvbuf1[writer1++] = pre_dline->tick(input);
- else
- rvbuf1[writer1++] = input;
- if(reader1 == rvblen)
- reader1 = 0;
- if(writer1 == rvblen)
- writer1 = 0;
- return output;
- }
- allpassfilter::~allpassfilter()
- {
- delete [] rvbuf1;
- delete pre_dline;
- }
- //////////////////////////////////////////////////////////////////////
- //// nested_allpass
- //overall delay = ap1.pre_delay + ap2.pre_deay + ap1.length + ap2.length + post_delay
- nested_allpass::nested_allpass(double srate,double lpfreq, unsigned int outertime, unsigned int time1, unsigned int time2,
- double gain, double gain1, double gain2,
- unsigned int delay1 = 0, unsigned int delay2 = 0)
-
- {
- outer_gain = gain;
- outer_time = outertime;
- ap1_gain = gain1;
- ap2_gain = gain2;
- ap1_length = time1;
- ap2_length = time2;
- ap1_delay = delay1;
- ap2_delay = delay2;
- buf = 0;
- if(ap2_gain == 0.0)
- ap2_length = 0;
- ap1 = ap2 = 0;
- writer = reader = 0;
- lp = 0;
- lpfreq_ = lpfreq;
- sr_ = srate;
- }
- nested_allpass::~nested_allpass()
- {
- delete [] buf;
- delete ap1;
- delete ap2;
- //RWD
- delete lp;
- }
- bool nested_allpass::create(void)
- {
- if(outer_gain < -1.0 || outer_gain > 1.0)
- return false;
- if(outer_time <= 0)
- return false;
- buf = new double[outer_time];
- if(!buf)
- return false;
- memset(buf,0,outer_time * sizeof(double));
- if(ap1_length > 0 && ap1_gain > 0.0){
- ap1 = new allpassfilter((long)sr_,ap1_length,ap1_gain,ap1_delay);
- if(!ap1->create()){
- delete [] buf; buf = 0;
- return false;
- }
- }
-
- if(ap2_length >0 && ap2_gain > 0.0){
- ap2 = new allpassfilter((long)sr_,ap2_length,ap2_gain,ap2_delay);
- if(!ap2->create()){
- delete ap1; ap1 = 0;
- delete [] buf; buf = 0;
- return false;
- }
- }
- // RWD experiment: internal absorption damping
- lp = new onepole(lpfreq_,sr_,LOW_PASS);
- if(!lp){
- delete ap1; ap1 = 0;
- delete ap2; ap2 = 0;
- delete [] buf; buf = 0;
- return false;
- }
- damping_ = false;
- return true;
- }
- //messy casts - sort this out later...
- double nested_allpass::tick(double insam)
- {
- double output, input,nest_out;
- input = insam;
- output = (-outer_gain) * input;
- output += buf[reader++];
- input += outer_gain * output;
- //input is to two allpasses, and the post_delay, which acts as the overall allpass
- if(!ap2) { //no second allpass, may be one or no first allpass
- if(ap1)
- nest_out = ap1->tick(input);
- else
- //behave as normal allpass
- nest_out = input;
- }
- else
- nest_out = ap2->tick(ap1->tick(input));
- //RWD experiment!
- if(damping_)
- buf[writer++] = lp->tick(nest_out);
- else
- buf[writer++] = nest_out;
- //wrap indices
- if(writer == outer_time)
- writer = 0;
- if(reader == outer_time)
- reader = 0;
-
- return output;
- }
- //////////////////////////////////////////////////////////////////////
- // lpcomb
- //////////////////////////////////////////////////////////////////////
- lpcomb::lpcomb(unsigned int buflen, double fbgain, double lpcoeff, double pre = 1.0)
- {
- combbuf = 0;
- lp = 0;
- gain = fbgain;
- lpgain = lpcoeff;
- rvblen = buflen;
- prescale = pre;
- reader1 = writer1 = 0;
- }
- bool lpcomb::create(void)
- {
- if(prescale <= 0.0)
- return false;
- if((gain < -1.0 ) || (gain > 1.0)) //do I want negative gain vals?
- return false;
- combbuf = new double[rvblen];
- if(!combbuf)
- return false;
- lp = new lowpass1(lpgain);
- if(!lp){
- delete [] combbuf; combbuf = 0;
- return false;
- }
- memset(combbuf,0,rvblen*sizeof(double));
- return true;
- }
- //wet output only, unscaled
- double lpcomb::tick(double insam)
- {
- double input,output,lpsig;
- input = insam * prescale;
-
- output = combbuf[reader1++]; //do gain scaling outside
- lpsig = lp->tick(output);
- combbuf[writer1++] = input + (gain * lpsig) ; //feedback scaled output
-
- if(writer1 == rvblen)
- writer1 = 0;
- if(reader1 == rvblen)
- reader1 = 0;
- return output;
- }
- lpcomb::~lpcomb()
- {
- delete [] combbuf;
- delete lp;
- }
-
- //////////////////////////////////////////////////////////////////////
- // lowpass : perhaps a bit ott as a class, but it allows fairly independent fiddling
- //////////////////////////////////////////////////////////////////////
- lowpass1::lowpass1(double filtgain)
- {
- temp = output = 0.0;
- gain = filtgain;
- }
- lowpass1::~lowpass1()
- {
- }
- //IIR lowpass
- inline double lowpass1::tick(double insam)
- {
- output = temp;
- temp = insam + (output * gain);
- return output;
- }
- // NB allows zero frequency: straight pass-through
- onepole::onepole(double freq,double srate, int low_high)
- {
- if(freq == 0.0){
- a1 = 1.0;
- a2 = 0.0;
- }
- else {
- b = 2.0 - cos(TWOPI * (freq/srate));
- a2 = sqrt((b*b) - 1.0) - b;
- a1 = 1.0 + a2;
- if(low_high==HIGH_PASS)
- a2 = -a2;
- }
- lastout = 0.0;
- }
- onepole::~onepole()
- {
- //nothing to do
- }
- double onepole::tick(double input)
- {
- double output;
- output = a1*input - a2*lastout;
- lastout = output;
- return output;
- }
- //uses code from CDP eq.c
- tonecontrol::tonecontrol(double frq,double dbfac,int type,double srate)
- {
- freq = frq;
- boost = dbfac;
- sr = srate;
- tc_type = type;
- x1 = x2 = y1 = y2 = 0.0;
- a0 = a1 = a2 = b1 = b2 = 0.0;
- }
- tonecontrol::~tonecontrol()
- {
- //nothing to do;
- }
- bool tonecontrol::create(void)
- {
- if(sr <= 0.0)
- return false;
- if(freq <= 0.0)
- return false;
- //normalize freq
- freq /= sr;
- switch(tc_type){
- case(LOW_SHELVE):
- lshelve();
- break;
- case(HIGH_SHELVE):
- hshelve();
- break;
- default:
- return false;
- break;
- }
- return true;
- }
- double tonecontrol::tick(double input)
- {
- double out;
- if(boost== 0.0)
- return input;
- out = ((a0 * input) + (a1 * x1) + (a2 * x2) - (b1 * y1) - (b2 * y2));
- x2 = x1;
- x1 = input;
- y2 = y1;
- y1 = out;
- return out;
- }
- void tonecontrol::lshelve(void)
- {
- double a, A, F, tmp, b0, recipb0, asq, F2, gamma2, siggam2, gam2p1;
- double gamman, gammad, ta0, ta1, ta2, tb0, tb1, tb2, aa1, ab1;
- a = tan(PI * (freq - 0.25)); /* Warp factor */
- asq = a * a;
- A = pow(10.0, boost/20.0); /* Cvt dB to factor */
- if((boost < 6.0) && (boost > -6.0)) F = sqrt(A);
- else if (A > 1.0) F = A/sqrt(2.0);
- else F = A * sqrt(2.0);
- /* If |boost/cut| < 6dB, then doesn't make sense to use 3dB point.
- Use of root makes bandedge at half the boost/cut amount
- */
- F2 = F * F;
- tmp = A * A - F2;
- if(fabs(tmp) <= SPN) gammad = 1;
- else gammad = pow( (F2 - 1)/ tmp, 0.25); /* Fourth root */
- gamman = sqrt(A) * gammad;
- /* Once for the numerator */
- gamma2 = gamman * gamman;
- gam2p1 = 1 + gamma2;
- siggam2 = 2 * ROOT2O2 * gamman;
-
- ta0 = gam2p1 + siggam2;
- ta1 = -2 * (1 - gamma2);
- ta2 = gam2p1 - siggam2;
- /* And again for the denominator */
- gamma2 = gammad * gammad;
- gam2p1 = 1 + gamma2;
- siggam2 = 2 * ROOT2O2 * gammad;
-
- tb0 = gam2p1 + siggam2;
- tb1 = -2 * (1 - gamma2);
- tb2 = gam2p1 - siggam2;
- /* Now bilinear transform to proper centre frequency */
- aa1 = a * ta1;
- a0 = ta0 + aa1 + asq * ta2;
- a1 = 2 * a * (ta0 + ta2) + (1 + asq) * ta1;
- a2 = asq * ta0 + aa1 + ta2;
- ab1 = a * tb1;
- b0 = tb0 + ab1 + asq * tb2;
- b1 = 2 * a * (tb0 + tb2) + (1 + asq) * tb1;
- b2 = asq * tb0 + ab1 + tb2;
-
- /* Normalise b0 to 1.0 for realisability */
- recipb0 = 1 / b0;
- a0 *= recipb0;
- a1 *= recipb0;
- a2 *= recipb0;
- b1 *= recipb0;
- b2 *= recipb0;
- }
- void tonecontrol::hshelve(void)
- {
- double a, A, F, tmp, b0, recipb0, asq, F2, gamma2, siggam2, gam2p1;
- double gamman, gammad, ta0, ta1, ta2, tb0, tb1, tb2, aa1, ab1;
- a = tan(PI * (freq - 0.25)); /* Warp factor */
- asq = a * a;
- A = pow(10.0, boost/20.0); /* Cvt dB to factor */
- if(boost < 6.0 && boost > -6.0) F = sqrt(A);
- else if (A > 1.0) F = A/sqrt(2.0);
- else F = A * sqrt(2.0);
- /* If |boost/cut| < 6dB, then doesn't make sense to use 3dB point.
- Use of root makes bandedge at half the boost/cut amount
- */
- F2 = F * F;
- tmp = A * A - F2;
- if(fabs(tmp) <= SPN) gammad = 1;
- else gammad = pow( (F2 - 1)/ tmp, 0.25); /* Fourth root */
- gamman = sqrt(A) * gammad;
- /* Once for the numerator */
- gamma2 = gamman * gamman;
- gam2p1 = 1 + gamma2;
- siggam2 = 2 * ROOT2O2 * gamman;
-
- ta0 = gam2p1 + siggam2;
- ta1 = -2 * (1 - gamma2);
- ta2 = gam2p1 - siggam2;
- ta1 = -ta1;
-
- /* And again for the denominator */
- gamma2 = gammad * gammad;
- gam2p1 = 1 + gamma2;
- siggam2 = 2 * ROOT2O2 * gammad;
-
- tb0 = gam2p1 + siggam2;
- tb1 = -2 * (1 - gamma2);
- tb2 = gam2p1 - siggam2;
- tb1 = -tb1;
- /* Now bilinear transform to proper centre frequency */
- aa1 = a * ta1;
- a0 = ta0 + aa1 + asq * ta2;
- a1 = 2 * a * (ta0 + ta2) + (1 + asq) * ta1;
- a2 = asq * ta0 + aa1 + ta2;
- ab1 = a * tb1;
- b0 = tb0 + ab1 + asq * tb2;
- b1 = 2 * a * (tb0 + tb2) + (1 + asq) * tb1;
- b2 = asq * tb0 + ab1 + tb2;
-
- /* Normalise b0 to 1.0 for realisability */
- recipb0 = 1 / b0;
- a0 *= recipb0;
- a1 *= recipb0;
- a2 *= recipb0;
- b1 *= recipb0;
- b2 *= recipb0;
- }
- /***************** GARDNER DIFFUSERS ***************/
- // NOTE: the published Gardner designs are here modified by adding a onepole lp filter
- // in the internal outer feedback loop of each nested allpass filter.
- // When combined with the overall feedback filter, this will of course reduce the reverb decay time.
- // The option exists to bypass this overall feedback filter, and rely solely on the internal ones.
- // NOTE: to get an infinite reverb, all feedback filters have to be bypassed by setting lp_freq to 0.0
- // otherwise, the output will still decay eventually !
- small_diffuser::small_diffuser(unsigned int pre_delay,
- const NESTDATA *apdata1,
- const NESTDATA *apdata2,double fb_gain,double lp_freq,double srate){
- ap1_data = *apdata1;
- ap2_data = *apdata2;
- predelay_time = pre_delay;
- lpgain = fb_gain;
- lpfreq = lp_freq;
- ap1 = ap2 = 0;
- lp1 = 0;
- predelay = 0;
- out1 = out2 = 0.0;
- sr = srate;
- damping_ = false;
- }
- small_diffuser::~small_diffuser()
- {
- delete ap1;
- delete ap2;
- delete predelay;
- delete lp1;
- }
- bool small_diffuser::create(void)
- {
- if(ap1_data.time1 == 0 || ap1_data.gain1 <= 0.0){
- #ifdef _DEBUG
- printf("\ndiffuser: bad parameters(1)");
- #endif
- return false;
- }
- if(ap1_data.time2 ==0 || ap1_data.gain2 <= 0.0){
- #ifdef _DEBUG
- printf("\ndiffuser: bad parameters(2)");
- #endif
- return false;
- }
- if(ap2_data.time1 == 0 || ap2_data.gain1 < 0.0){
- #ifdef _DEBUG
- printf("\ndiffuser: bad parameters(3)");
- #endif
- return false;
- }
- if(ap2_data.time2 ==0 || ap2_data.gain2 < 0.0){
- #ifdef _DEBUG
- printf("\ndiffuser: bad parameters(4)");
- #endif
- return false;
- }
- if(sr <=0.0){
- #ifdef _DEBUG
- printf("\ndiffuser: bad srate parameter)");
- #endif
- return false;
- }
- if(lpfreq <0.0){
- #ifdef _DEBUG
- printf("\ndiffuser: bad freq parameter)");
- #endif
- return false;
- }
- ap1 = new nested_allpass(sr,lpfreq,ap1_data.time1,ap1_data.time2,ap1_data.time3,
- ap1_data.gain1,ap1_data.gain2,ap1_data.gain3);
- if(!ap1->create()){
- #ifdef _DEBUG
- printf("\ndiffuser: can't create first allpass");
- #endif
- return false;
- }
- if(ap2_data.gain1 != 0.0){ //allow ap to eliminate second block
- ap2 = new nested_allpass(sr,lpfreq,ap2_data.time1,ap2_data.time2,ap2_data.time3,
- ap2_data.gain1,ap2_data.gain2,ap2_data.gain3);
- if(!ap2->create()){
- #ifdef _DEBUG
- printf("\ndiffuser: can't create second allpass");
- #endif
- delete ap1; ap1 = 0;
- return false;
- }
- }
- if(predelay_time > 0){
- predelay = new delay(predelay_time,1.0);
- if(!predelay->create()){
- #ifdef _DEBUG
- printf("\ndiffuser: can't create predelay");
- #endif
- delete ap1; ap1 = 0;
- delete ap2; ap2 = 0;
- return false;
- }
- }
- // NB if lpfreq == 0, onepole is no-op - returns input
- lp1 = new onepole(lpfreq,sr,LOW_PASS);
- if(lp1 == 0){
- delete [] predelay; predelay = 0;
- delete ap1; ap1 = 0;
- delete ap2; ap2 = 0;
- return false;
- }
- ap1->set_damping(damping_);
- ap2->set_damping(damping_);
- return true;
- }
- double small_diffuser::tick(double insam)
- {
- double filter_out;
- double lp_in;
- double output;
- double ip;
- // may only contain one nested allpass filter:
- // input to filter is either out1 or out2
- if(ap2)
- lp_in = out2; //= both in series
- else
- lp_in = out1;
- // RWD NOTE: if the damping_ mechanism is preferred, this filter can be omitted and ...
- filter_out = lp1->tick(lp_in);
- // ... just use this
- // filter_out = lp_in ;
-
- ip = insam + lpgain * filter_out;
- if(predelay)
- ip = predelay->tick(ip);
- out1 = ap1->tick( ip);
- if(ap2){
- out2 = ap2->tick(out1);
- output = (out1 + out2) * 0.5;
- }
- else
- output = out1;
- return output;
- }
- //post-delay almost certainly doe not need to be prime...
- medium_diffuser::medium_diffuser(double post_delay,const NESTDATA *apdata1,
- const NESTDATA *apdata2,
- double gain,
- double lp_freq,
- double srate)
- {
- ap1_data = *apdata1;
- ap2_data = *apdata2;
- postdelay_time = to_prime(post_delay,srate);
- md_gain = gain;
- lpfreq = lp_freq;
- ap1 = ap2 = 0;
- ap3 = 0;
- lp1 = 0;
- delay1 = delay2 = postdelay = 0;
- sr = srate;
- out1 = out2 =out3 = 0.0;
- damping_ = false;
- }
- medium_diffuser::~medium_diffuser()
- {
- delete ap1;
- delete ap2;
- delete ap3;
- delete lp1;
- delete delay1;
- delete delay2;
- }
- bool medium_diffuser::create(void)
- {
- if(ap1_data.time1 == 0 || ap1_data.gain1 <= 0.0){
- #ifdef _DEBUG
- printf("\nmedium_diffuser: bad parameters(1)");
- #endif
- return false;
- }
- if(ap1_data.time2 ==0 || ap1_data.gain2 <= 0.0){
- #ifdef _DEBUG
- printf("\nmedium diffuser: bad parameters(2)");
- #endif
- return false;
- }
- if(ap2_data.time1 == 0 || ap2_data.gain1 < 0.0){
- #ifdef _DEBUG
- printf("\nmedium diffuser: bad parameters(3)");
- #endif
- return false;
- }
- if(ap2_data.time2 ==0 || ap2_data.gain2 < 0.0){
- #ifdef _DEBUG
- printf("\nmedium diffuser: bad parameters(4)");
- #endif
- return false;
- }
- if(sr <=0.0){
- #ifdef _DEBUG
- printf("\nmedium diffuser: bad srate parameter)");
- #endif
- return false;
- }
- if(lpfreq <0.0){
- #ifdef _DEBUG
- printf("\nmedium diffuser: bad freq parameter)");
- #endif
- return false;
- }
- ap1 = new nested_allpass(sr,lpfreq,ap1_data.time1,ap1_data.time2,ap1_data.time3,
- ap1_data.gain1,ap1_data.gain2,ap1_data.gain3);
- if(!ap1->create()){
- #ifdef _DEBUG
- printf("\nmedium diffuser: can't create first diffuser");
- #endif
- return false;
- }
- ap2 = new nested_allpass(sr,lpfreq,ap2_data.time1,ap2_data.time2,ap2_data.time3,
- ap2_data.gain1,ap2_data.gain2,ap2_data.gain3);
- if(!ap2->create()){
- #ifdef _DEBUG
- printf("\nmedium diffuser: can't create second diffuser");
- #endif
- delete ap1; ap1 = 0;
- return false;
- }
- ap3 = new allpassfilter((long)sr,to_prime(0.03,sr),0.5,to_prime(0.005,sr));
- if(!ap3->create()){
- #ifdef _DEBUG
- printf("\nmedium diffuser: can't create internal allpass filter");
- #endif
- delete ap1; ap1 = 0;
- delete ap2; ap2 = 0;
- return false;
- }
- if(postdelay_time > 0){
- postdelay = new delay(postdelay_time,1.0);
- if(!postdelay->create()){
- #ifdef _DEBUG
- printf("\nmedium diffuser: can't create postdelay");
- #endif
- delete ap1; ap1 = 0;
- delete ap2; ap2 = 0;
- delete ap3; ap3 = 0;
- return false;
- }
- }
- //internal fixed delays
- delay1 = new delay(to_prime(/*0.007*/0.067,sr),1.0);
- if(!delay1->create()){
- #ifdef _DEBUG
- printf("\nmedium diffuser: can't create internal delay1");
- #endif
- delete ap1; ap1 = 0;
- delete ap2; ap2 = 0;
- delete ap3; ap3 = 0;
- delete [] postdelay; postdelay = 0;
- return false;
- }
- delay2 = new delay(to_prime(0.015,sr),1.0);
- if(!delay2->create()){
- #ifdef _DEBUG
- printf("\nmedium diffuser: can't create internal delay2");
- #endif
- delete ap1; ap1 = 0;
- delete ap2; ap2 = 0;
- delete ap3; ap3 = 0;
- delete [] postdelay; postdelay = 0;
- delete delay1; delay1 = 0;
- return false;
- }
-
- lp1 = new onepole(lpfreq,sr,LOW_PASS);
- if(lp1 == 0){
- delete ap1; ap1 = 0;
- delete ap2; ap2 = 0;
- delete ap3; ap3 = 0;
- delete [] postdelay; postdelay = 0;
- delete delay1; delay1 = 0;
- delete delay2; delay2 = 0;
- return false;
- }
- if(lpfreq == 0.0)
- damping_ = false;
- ap1->set_damping(damping_);
- ap2->set_damping(damping_);
- return true;
- }
- double medium_diffuser::tick(double insam)
- {
- double filter_out;
- double output;
- //feedback from postdelay, thru lp filter
- // RWD NOTE: if the damping_ mechanism is preferred, this filter can be omitted and...
- filter_out = md_gain * lp1->tick(postdelay->tick(out3));
- // ... just use this
- //filter_out = (float)(md_gain * postdelay->tick(out3));
-
- //first nested-allpass, takes input plus feedback
- out1 = ap1->tick(insam + filter_out);
- //inner allpass with predelay, followed by plain delay
- out2 = delay1->tick(ap3->tick(out1));
- //second nested-allpass, takes direct input
- out3 = ap2->tick(insam + md_gain * delay2->tick(out2));
- output = 0.5 * (out1 + out2 + out3);
- return output;
- }
- large_diffuser::large_diffuser(const NESTDATA *apdata1,
- const NESTDATA *apdata2,
- double gain,
- double lp_freq,
- double srate)
- {
- ap1_data = *apdata1;
- ap2_data = *apdata2;
- ld_gain = gain;
- lpfreq = lp_freq;
- ap1 = ap2 = 0;
- ap3 = ap4 = 0;
- lp1 = 0;
- delay1 = delay2 = delay3 = delay4 = 0;
- sr = srate;
- out1 = out2 =out3 = 0.0;
- damping_ = false;
- }
- large_diffuser::~large_diffuser()
- {
- delete ap1;
- delete ap2;
- delete ap3;
- delete ap4;
- delete lp1;
- delete delay1;
- delete delay2;
- delete delay3;
- delete delay4;
- }
- bool large_diffuser::create(void)
- {
- if(ap1_data.time1 == 0 || ap1_data.gain1 <= 0.0){
- #ifdef _DEBUG
- printf("\nmedium_diffuser: bad parameters(1)");
- #endif
- return false;
- }
- if(ap1_data.time2 ==0 || ap1_data.gain2 <= 0.0){
- #ifdef _DEBUG
- printf("\nmedium diffuser: bad parameters(2)");
- #endif
- return false;
- }
- if(ap2_data.time1 == 0 || ap2_data.gain1 < 0.0){
- #ifdef _DEBUG
- printf("\nmedium diffuser: bad parameters(3)");
- #endif
- return false;
- }
- if(ap2_data.time2 ==0 || ap2_data.gain2 < 0.0){
- #ifdef _DEBUG
- printf("\nmedium diffuser: bad parameters(4)");
- #endif
- return false;
- }
- if(sr <=0.0){
- #ifdef _DEBUG
- printf("\nmedium diffuser: bad srate parameter)");
- #endif
- return false;
- }
- if(lpfreq <0.0){
- #ifdef _DEBUG
- printf("\nmedium diffuser: bad freq parameter)");
- #endif
- return false;
- }
- ap1 = new nested_allpass(sr,lpfreq,ap1_data.time1,ap1_data.time2,ap1_data.time3,
- ap1_data.gain1,ap1_data.gain2,ap1_data.gain3);
- if(!ap1->create()){
- #ifdef _DEBUG
- printf("\nlarge diffuser: can't create first nested_allpass");
- #endif
- return false;
- }
- ap2 = new nested_allpass(sr,lpfreq,ap2_data.time1,ap2_data.time2,ap2_data.time3,
- ap2_data.gain1,ap2_data.gain2,ap2_data.gain3);
- if(!ap2->create()){
- #ifdef _DEBUG
- printf("\nlarge diffuser: can't create second netsed_allpass");
- #endif
- cleanup();
- return false;
- }
- ap3 = new allpassfilter((long) sr,to_prime(0.008,sr),0.3);
- if(!ap3->create()){
- #ifdef _DEBUG
- printf("\nlarge diffuser: can't create first internal allpass filter");
- #endif
- cleanup();
- return false;
- }
- ap4 = new allpassfilter((long)sr,to_prime(0.012,sr),0.3);
- if(!ap4->create()){
- #ifdef _DEBUG
- printf("\nlarge diffuser: can't create second internal allpass filter");
- #endif
- cleanup();
- return false;
- }
- //internal fixed delays
- delay1 = new delay((unsigned int)(0.004 * sr),1.0);
- if(!delay1->create()){
- #ifdef _DEBUG
- printf("\nlarge diffuser: can't create internal delay1");
- #endif
- cleanup();
- return false;
- }
- delay2 = new delay((unsigned int)(0.017 * sr),1.0);
- if(!delay2->create()){
- #ifdef _DEBUG
- printf("\nlarge diffuser: can't create internal delay2");
- #endif
- cleanup();
- return false;
- }
- delay3 = new delay((unsigned int)(0.031 * sr),1.0);
- if(!delay3->create()){
- #ifdef _DEBUG
- printf("\nlarge diffuser: can't create internal delay3");
- #endif
- cleanup();
- return false;
- }
- delay4 = new delay((unsigned int)(0.003 * sr),1.0);
- if(!delay4->create()){
- #ifdef _DEBUG
- printf("\nlarge diffuser: can't create internal delay4");
- #endif
- cleanup();
- return false;
- }
-
- lp1 = new onepole(lpfreq,sr,LOW_PASS);
- if(lp1 == 0){
- cleanup();
- return false;
- }
- if(lpfreq == 0.0)
- damping_ = false;
-
- ap1->set_damping(damping_);
- ap2->set_damping(damping_);
- return true;
- }
- void large_diffuser::cleanup()
- {
- delete lp1; lp1 = 0;
- delete delay4; delay4 = 0;
- delete delay3; delay3 = 0;
- delete delay2; delay2 = 0;
- delete delay1; delay1 = 0;
- delete ap4; ap4 = 0;
- delete ap3; ap3 = 0;
- delete ap2; ap2 = 0;
- delete ap1; ap1 = 0;
- }
- // NOTE: the three output taps from the diffuser can lead to discrete slap-back echoes in the output.
- // possibly authentic, but may not suit all purposes. See rmverb.cpp: some delay lengths reduced from Gardner.
- // This is possible because delays are discrete: they do not share single delay line buffer.
- // true also of medium diffuser, but not so apparent as delays are smaller
- double large_diffuser::tick(double insam)
- {
- double filter_out;
- double output;
- //feedback from lpfilter
- // RWD NOTE: if the damping_ mechanism is preferred, this filter can be omitted and ...
- filter_out = ld_gain * lp1->tick(out3);
- // ... just use this
- //filter_out = (float)(ld_gain * out3);
- //first tap, from two plain allpasses, and trailing delay
- out1 = delay1->tick(ap4->tick(ap3->tick(insam + filter_out)));
- //second tap, from first nested allpass and delays at entrance and exit
- out2 = delay3->tick(ap1->tick(delay2->tick(out1)));
- //third tap, from second nested-allpass with leading delay
- out3 = ap2->tick(delay4->tick(out2));
- //out3 also goes into lpfilter... see above
- //prescribed scale factors from Gardner
- output = (out1 * 0.34) + (out2 * 0.14) + (out3 * 0.14);
- // this seems to give relatively soft reverb tail: could try this:
- //output = (float)((out1 * 0.7) + (out2 * 0.25) + (out3 * 0.25));
- return output;
- }
- moorer::moorer(const MOORERDATA *pdata,double reverbtime,double damping,double srate)
- {
- mdata = *pdata;
- reverb_time = reverbtime;
- dampfac = damping;
- sr = srate;
- comb1 = comb2 = comb3 = comb4 = comb5 = comb6 = 0;
- ap1 = ap2 = ap3 = ap4 = 0;
- out = 0.0;
- }
- moorer::~moorer()
- {
- delete comb1;
- delete comb2;
- delete comb3;
- delete comb4;
- delete comb5;
- delete comb6;
- delete ap1;
- delete ap2;
- delete ap3;
- delete ap4;
- }
- bool moorer::create(void)
- {
- fgain1 = 0.42 * dampfac;
- fgain2 = 0.46 * dampfac;
- fgain3 = 0.48 * dampfac;
- fgain4 = 0.49 * dampfac;
- fgain5 = 0.50 * dampfac;
- fgain6 = 0.52 * dampfac;
- scalefac = (1.0 / 6.0);
- cgain1 = exp(log(0.001)*(mdata.ctime1/reverb_time)) * (1. - fgain1);
- cgain2 = exp(log(0.001)*(mdata.ctime2/reverb_time)) * (1. - fgain2);
- cgain3 = exp(log(0.001)*(mdata.ctime3/reverb_time)) * (1. - fgain3);
- cgain4 = exp(log(0.001)*(mdata.ctime4/reverb_time)) * (1. - fgain4);
- cgain5 = exp(log(0.001)*(mdata.ctime5/reverb_time)) * (1. - fgain5);
- cgain6 = exp(log(0.001)*(mdata.ctime6/reverb_time)) * (1. - fgain6);
- comb1 = new lpcomb(to_prime(mdata.ctime1,sr),cgain1,fgain1,1.0);
- comb2 = new lpcomb(to_prime(mdata.ctime2,sr),cgain2,fgain2,1.0);
- comb3 = new lpcomb(to_prime(mdata.ctime3,sr),cgain3,fgain3,1.0);
- comb4 = new lpcomb(to_prime(mdata.ctime4,sr),cgain4,fgain4,1.0);
- comb5 = new lpcomb(to_prime(mdata.ctime5,sr),cgain5,fgain5,1.0);
- comb6 = new lpcomb(to_prime(mdata.ctime6,sr),cgain6,fgain6,1.0);
- ap1 = new allpassfilter((long)sr,to_prime(mdata.atime1,sr),-0.6,0);
- ap2 = new allpassfilter((long)sr,to_prime(mdata.atime2,sr),0.4,0);
- ap3 = new allpassfilter((long)sr,to_prime(mdata.atime3,sr),-0.61,0);
- ap4 = new allpassfilter((long)sr,to_prime(mdata.atime4,sr),0.39,0);
- if(!
- (comb1->create()
- && comb2->create()
- && comb3->create()
- && comb4->create()
- && comb5->create()
- && comb6->create()
- && ap1->create()
- && ap2->create()
- && ap3->create()
- && ap4->create()
- )) {
- cleanup();
- return false;
- }
- return true;
- }
- double moorer::tick(double insam)
- {
- out = comb1->tick(insam) +
- comb2->tick(insam) +
- comb3->tick(insam) +
- comb4->tick(insam) +
- comb5->tick(insam) +
- comb6->tick(insam)
- ;
- out *= scalefac;
- out = ap4->tick(ap3->tick(ap2->tick(ap1->tick(out))));
- return out;
- }
- void moorer::cleanup()
- {
- delete comb6; comb6 = 0;
- delete comb5; comb5 = 0;
- delete comb4; comb4 = 0;
- delete comb3; comb3 = 0;
- delete comb2; comb2 = 0;
- delete comb1; comb1 = 0;
- delete ap4; ap4 = 0;
- delete ap3; ap3 = 0;
- delete ap2; ap2 = 0;
- delete ap1; ap1 = 0;
- }
- /* vmtdelay *******************************/
- vcomb4::vcomb4()
- {
- dl_buf = 0;
- dl_length = 0;
- dl_input_index = 0;
- dl_srate = 0.0;
-
- }
- vcomb4::~vcomb4()
- {
- if(dl_buf && dl_length > 0)
- delete [] dl_buf;
-
- }
- bool vcomb4::init(long srate,double length_secs)
- {
- unsigned long len_frames = (unsigned long)(srate * length_secs );
- if(len_frames == 0)
- return false;
- /* round upwards to allow for interpolation */
- len_frames++;
- /* reject init if already created*/
- /* TODO: more sexy error checking/reporting... */
- if(dl_buf)
- return false;
- try {
- dl_buf = new double[len_frames];
- }
- catch(...){
- return false;
- }
- /* for VC 6 */
- if(dl_buf == 0)
- return false;
- for(unsigned long i = 0; i < len_frames;i++)
- dl_buf[i] = 0.0;
- dl_length = len_frames;
- dl_input_index = 0;
- dl_srate = srate;
- gain = 0.5;
- return true;
- }
- bool vcomb4::init(const MOORERDATA *p_mdata,double reverbtime,double damping,double sr)
- {
- if(!init((long) sr,reverbtime))
- return false;
- return true;
- }
- double vcomb4::tick(double vdelay_1,double vdelay_2,double vdelay_3,double vdelay_4,double feedback,double input)
- {
- unsigned long base_readpos1,base_readpos2,base_readpos3,base_readpos4;
- unsigned long next_index1,next_index2,next_index3,next_index4;
- double vlength,dlength,frac;
- double readpos1,readpos2,readpos3,readpos4;
- double* buf = dl_buf;
-
- dlength = (double) dl_length; /* get maxlen, save a cast later on */
- /* read pointer is vlength ~behind~ write pointer */
- /* tap1*/
- vlength = dlength - (vdelay_1 * dl_srate);
- vlength = vlength < dlength ? vlength : dlength; /* clip vdelay to max del length */
- readpos1 = dl_input_index + vlength;
- base_readpos1 = (unsigned long) ( readpos1);
- if(base_readpos1 >= dl_length) /* wrap dl indices */
- base_readpos1 -= dl_length;
- next_index1 = base_readpos1 + 1;
- if(next_index1 >= dl_length)
- next_index1 -= dl_length;
- /*tap2*/
- vlength = dlength - (vdelay_2 * dl_srate);
- vlength = vlength < dlength ? vlength : dlength;
- readpos2 = dl_input_index + vlength;
- base_readpos2 = (unsigned long) ( readpos2);
- if(base_readpos2 >= dl_length) /* wrap dl indices */
- base_readpos2 -= dl_length;
- next_index2 = base_readpos2 + 1;
- if(next_index2 >= dl_length)
- next_index2 -= dl_length;
- /*tap3*/
- vlength = dlength - (vdelay_3 * dl_srate);
- vlength = vlength < dlength ? vlength : dlength;
- readpos3 = dl_input_index + vlength;
- base_readpos3 = (unsigned long) ( readpos3);
- if(base_readpos3 >= dl_length) /* wrap dl indices */
- base_readpos3 -= dl_length;
- next_index3 = base_readpos3 + 1;
- if(next_index3 >= dl_length)
- next_index3 -= dl_length;
- /* tap4 */
- vlength = dlength - (vdelay_4 * dl_srate);
- vlength = vlength < dlength ? vlength : dlength;
- readpos4 = dl_input_index + vlength;
- base_readpos4 = (unsigned long) ( readpos4);
- if(base_readpos4 >= dl_length) /* wrap dl indices */
- base_readpos4 -= dl_length;
- next_index4 = base_readpos4 + 1;
- if(next_index4 >= dl_length)
- next_index4 -= dl_length;
- double outsum = 0.0;
- fb1 *= feedback;
- fb2 *= feedback;
- fb3 *= feedback;
- fb4 *= feedback;
- /* basic interp of variable delay pos */
- frac = readpos1 - (int) readpos1;
- outsum += fb1 * (buf[base_readpos1]+((buf[next_index1] - buf[base_readpos1]) * frac));
- frac = readpos2 - (int) readpos2;
- outsum += fb2 * (buf[base_readpos2]+((buf[next_index2] - buf[base_readpos2]) * frac));
- frac = readpos3 - (int) readpos3;
- outsum += fb3 * (buf[base_readpos3]+((buf[next_index3] - buf[base_readpos3]) * frac));
- frac = readpos4 - (int) readpos4;
- outsum += fb4 * (buf[base_readpos4]+((buf[next_index4] - buf[base_readpos4]) * frac));
- /* how do we scale all this?*/
- input += gain * (outsum);
- /* add in new sample + fraction of ouput, unscaled, for minimum decay at max feedback */
- buf[dl_input_index++] = input + outsum * 0.25;
- if(dl_input_index == dl_length)
- dl_input_index = 0;
- return input + outsum * 0.25;
- }
|