aboutsummaryrefslogtreecommitdiff
path: root/transverb/transverb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'transverb/transverb.cpp')
-rwxr-xr-xtransverb/transverb.cpp683
1 files changed, 683 insertions, 0 deletions
diff --git a/transverb/transverb.cpp b/transverb/transverb.cpp
new file mode 100755
index 0000000..bfe2b51
--- /dev/null
+++ b/transverb/transverb.cpp
@@ -0,0 +1,683 @@
+
+/* DFX Transverb transverb by Tom 7 and Marc 3 */
+
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "transverb.h"
+#include "FIRfilter.h"
+
+
+// _ ___
+
+transverb::transverb(int argc, t_atom *argv) {
+
+ fBsize = (2700.0f-BUFFER_MIN)/(BUFFER_MAX-BUFFER_MIN); // "ms"
+ drymix = 0.8f; // "dB"
+
+ fQuality = 1.0f; // "steps 0-2"
+ fTomsound = 0.0f; // "yes / no"
+ fSpeed1mode = 0.0f;
+ fSpeed2mode = 0.0f;
+
+ mix1 = 1.0f; // "dB"
+ dist1 = 0.90009f; // "units"
+ speed1 = (0.0f-SPEED_MIN)/(SPEED_MAX-SPEED_MIN); // "units"
+ feed1 = 0.0f; // "units"
+
+ mix2 = 0.0f; // "dB"
+ dist2 = 0.1f; // "units"
+ speed2 = (1.0f-SPEED_MIN)/(SPEED_MAX-SPEED_MIN); // "units"
+ feed2 = 0.0f; // "units"
+
+ ireplace = 1; // "yes/no"
+
+
+ // default this to something, at least for the sake of getGetTailSize()
+ MAXBUF = (int) (BUFFER_MAX * 44.1f);
+ buf1[0] = NULL;
+ buf2[0] = NULL;
+#ifdef TRANSVERB_STEREO
+ buf1[1] = NULL;
+ buf2[1] = NULL;
+#endif
+ filter1 = new IIRfilter[NUM_CHANNELS];
+ filter2 = new IIRfilter[NUM_CHANNELS];
+ firCoefficients1 = new float[numFIRtaps];
+ firCoefficients2 = new float[numFIRtaps];
+ suspend();
+ srand((unsigned int)time(NULL)); // sets a seed value for rand() from the system clock
+
+ post("_ ____transverb~ ");
+
+ sr = (int) Samplerate();
+ blocksize = Blocksize();
+
+ fTomsound = (float)GetFloat(argv[0]); // creation arguments
+ fQuality = (float)GetFloat(argv[1]); // [ yet they default to reasonable values ]
+ drymix = (float)GetFloat(argv[2]);
+ ireplace = (int)GetFloat(argv[3]);
+
+ post("__ blocksize :: %d",sr);
+ post("_ samplerat :: %d",sr);
+ post("____ _ - tomsound :: %f",fTomsound);
+ post("___ _____ - quality :: %f",fQuality);
+ post("__ - drymix :: %f",drymix);
+ post("______ - replacing :: %d",ireplace);
+
+ AddInSignal();
+ AddInFloat(9);
+ AddOutSignal(); // 1 audio out [ == AddOutSignal(1) ]
+
+ SetupInOut(); // set up inlets and outlets.
+ // Must get called once!
+
+ // Now we need to bind the handler function to our
+ // inlets,
+ FLEXT_ADDMETHOD( 1,setBsize);
+ FLEXT_ADDMETHOD( 2,setMix1);
+ FLEXT_ADDMETHOD( 3,setSpeed1);
+ FLEXT_ADDMETHOD( 4,setFeed1);
+ FLEXT_ADDMETHOD( 5,setDist1);
+ FLEXT_ADDMETHOD( 6,setMix2);
+ FLEXT_ADDMETHOD( 7,setSpeed2);
+ FLEXT_ADDMETHOD( 8,setFeed2);
+ FLEXT_ADDMETHOD( 9,setDist2);
+
+ post("_ ____ ____ _");
+
+} // end of constructor
+
+
+transverb::~transverb() {
+
+ if (buf1[0])
+ free(buf1[0]);
+ if (buf2[0])
+ free(buf2[0]);
+#ifdef TRANSVERB_STEREO
+ if (buf1[1])
+ free(buf1[1]);
+ if (buf2[1])
+ free(buf2[1]);
+#endif
+ if (filter1)
+ delete[] filter1;
+ if (filter2)
+ delete[] filter2;
+ if (firCoefficients1)
+ delete[] firCoefficients1;
+ if (firCoefficients2)
+ delete[] firCoefficients2;
+
+}
+
+// GIMME class:
+FLEXT_NEW_TILDE_G("transverb~", transverb)
+
+
+
+void transverb::suspend () {
+
+ clearBuffers();
+ writer = 0;
+ read1 = read2 = 0.0;
+ smoothcount1[0] = smoothcount2[0] = 0;
+ lastr1val[0] = lastr2val[0] = 0.0f;
+ filter1[0].reset();
+ filter2[0].reset();
+#ifdef TRANSVERB_STEREO
+ smoothcount1[1] = smoothcount2[1] = 0;
+ lastr1val[1] = lastr2val[1] = 0.0f;
+ filter1[1].reset();
+ filter2[1].reset();
+#endif
+ SAMPLERATE = 44100.0f;
+ bsize = bufferScaled(fBsize);
+ speed1hasChanged = speed2hasChanged = true;
+ createAudioBuffers();
+}
+
+void transverb::createAudioBuffers() {
+
+ SAMPLERATE = 44100.0f;
+
+ long oldmax = MAXBUF;
+ MAXBUF = (int) (BUFFER_MAX * 0.001f * SAMPLERATE);
+
+ // if the sampling rate (& therefore the max buffer size) has changed,
+ // then delete & reallocate the buffers according to the sampling rate
+ if (MAXBUF != oldmax)
+ {
+ if (buf1[0] != NULL)
+ free(buf1[0]);
+ buf1[0] = NULL;
+ if (buf2[0] != NULL)
+ free(buf2[0]);
+ buf2[0] = NULL;
+#ifdef TRANSVERB_STEREO
+ if (buf1[1] != NULL)
+ free(buf1[1]);
+ buf1[1] = NULL;
+ if (buf2[1] != NULL)
+ free(buf2[1]);
+ buf2[1] = NULL;
+#endif
+ }
+ if (buf1[0] == NULL)
+ buf1[0] = (float*)malloc(MAXBUF * sizeof (float));
+ if (buf2[0] == NULL)
+ buf2[0] = (float*)malloc(MAXBUF * sizeof (float));
+#ifdef TRANSVERB_STEREO
+ if (buf1[1] == NULL)
+ buf1[1] = (float*)malloc(MAXBUF * sizeof (float));
+ if (buf2[1] == NULL)
+ buf2[1] = (float*)malloc(MAXBUF * sizeof (float));
+#endif
+}
+
+void transverb::clearBuffers() {
+ if ( (buf1[0] != NULL) && (buf2[0] != NULL) ) {
+ for (int j=0; j < MAXBUF; j++) buf1[0][j] = buf2[0][j] = 0.0f;
+ }
+#ifdef TRANSVERB_STEREO
+ if ( (buf1[1] != NULL) && (buf2[1] != NULL) ) {
+ for (int k=0; k < MAXBUF; k++) buf1[1][k] = buf2[1][k] = 0.0f;
+ }
+#endif
+}
+
+
+void transverb::m_signal(int n, float *const *in, float *const *out) {
+
+ float *outs = out[0];
+ float *ins = in[0];
+
+ // directly move everything to the vst part
+ processX((float *)ins, (float *)outs,(long int)n, ireplace);
+} // end m_signal
+
+
+void transverb::m_help() {
+ post("");
+ post("_ _____transverb~ help___ _");
+ post(" af : tomsound _ 0 / 1");
+ post(" af : quality _ 0 1 2");
+ post(" af : add/rep _ 0 / 1");
+ post(" if : buffersize _ 1 ~ 3000");
+ post(" if : mix _ 0 ~ 1");
+ post(" if : speed 1 -3 ~ 6");
+ post(" if : feed 1 0 ~ 1");
+ post(" if : distortion 1");
+ post(" if : mix 2 0 ~ 1");
+ post(" if : speed 2 -3 ~ 6");
+ post(" if : feed 2 0 ~ 1");
+ post(" if : distortion 2");
+// post(" f : dry/wet ratio");
+ post("");
+}
+
+
+
+
+
+
+
+
+
+
+
+
+void transverb::processX(float *in, float *out, long samples, int replacing) {
+ // float these 3 temp variables are for preserving states when looping through channels
+ int writertemp;
+ double read1temp, read2temp;
+ // int versions of these float values, for reducing casting operations
+ int speed1int, speed2int, read1int, read2int;
+ int lowpass1pos, lowpass2pos; // position trackers for the lowpass filters
+ float r1val, r2val; // delay buffer output values
+ double bsize_float = (double)bsize; // cut down on casting
+ int filterMode1, filterMode2; // the type of filtering to use in ultra hi-fi mode
+ float mug1, mug2; // make-up gain for lowpass filtering
+ float quietNoise = 1.0e-15f; // value added into the buffers to prevent denormals
+
+
+ // there must have not been available memory or something (like WaveLab goofing up),
+ // so try to allocate buffers now
+ if ( ((buf1[0] == NULL) || (buf2[0] == NULL))
+ #ifdef TRANSVERB_STEREO
+ || ((buf1[1] == NULL) || (buf2[1] == NULL))
+ #endif
+ ) createAudioBuffers();
+
+ // if the creation failed, then abort audio processing
+ if ( (buf1[0] == NULL) || (buf2[0] == NULL) )
+ return;
+#ifdef TRANSVERB_STEREO
+ if ( (buf1[1] == NULL) || (buf2[1] == NULL) )
+ return;
+#endif
+
+ SAMPLERATE = 44100.0f;
+
+ filterMode1 = filterMode2 = useNothing; // reset these for now
+ if (quality == ultrahifi)
+ {
+ // check to see if we need to lowpass the first delay head & init coefficients if so
+ if (speed1 > 1.0f)
+ {
+ filterMode1 = useLowpassIIR;
+ speed1int = (int)speed1;
+ // it becomes too costly to try to IIR at > 5x speeds, so switch to FIR filtering
+ if (speed1int >= 5)
+ {
+ filterMode1 = useLowpassFIR;
+ mug1 = powf( (speed1*0.2f), 0.78f ); // compensate for gain lost from filtering
+ // update the coefficients only if necessary
+ if (speed1hasChanged)
+ {
+ calculateFIRidealLowpassCoefficients((SAMPLERATE/speed1)*SHELF_START_IIR, SAMPLERATE, numFIRtaps, firCoefficients1);
+ applyKaiserWindow(numFIRtaps, firCoefficients1, 60.0f);
+ speed1hasChanged = false;
+ }
+ }
+ else if (speed1hasChanged)
+ {
+ filter1[0].calculateLowpassCoefficients((SAMPLERATE/speed1)*SHELF_START_IIR, SAMPLERATE);
+ #ifdef TRANSVERB_STEREO
+ filter1[1].copyCoefficients(filter1);
+ #endif
+ speed1hasChanged = false;
+ }
+ }
+ // we need to highpass the delay head to remove mega sub bass
+ else
+ {
+ filterMode1 = useHighpass;
+ if (speed1hasChanged)
+ {
+ filter1[0].calculateHighpassCoefficients(33.3f/speed1, SAMPLERATE);
+ #ifdef TRANSVERB_STEREO
+ filter1[1].copyCoefficients(filter1);
+ #endif
+ speed1hasChanged = false;
+ }
+ }
+
+ // check to see if we need to lowpass the second delay head & init coefficients if so
+ if (speed2 > 1.0f)
+ {
+ filterMode2 = useLowpassIIR;
+ speed2int = (int)speed2;
+ if (speed2int >= 5)
+ {
+ filterMode2 = useLowpassFIR;
+ mug2 = powf( (speed2*0.2f), 0.78f ); // compensate for gain lost from filtering
+ if (speed2hasChanged)
+ {
+ calculateFIRidealLowpassCoefficients((SAMPLERATE/speed2)*SHELF_START_IIR, SAMPLERATE, numFIRtaps, firCoefficients2);
+ applyKaiserWindow(numFIRtaps, firCoefficients2, 60.0f);
+ speed2hasChanged = false;
+ }
+ }
+ else if (speed2hasChanged)
+ {
+ filter2[0].calculateLowpassCoefficients((SAMPLERATE/speed2)*SHELF_START_IIR, SAMPLERATE);
+ #ifdef TRANSVERB_STEREO
+ filter2[1].copyCoefficients(filter2);
+ #endif
+ speed2hasChanged = false;
+ }
+ }
+ // we need to highpass the delay head to remove mega sub bass
+ else
+ {
+ filterMode2 = useHighpass;
+ if (speed2hasChanged)
+ {
+ filter2[0].calculateHighpassCoefficients(33.3f/speed2, SAMPLERATE);
+ #ifdef TRANSVERB_STEREO
+ filter2[1].copyCoefficients(filter2);
+ #endif
+ speed2hasChanged = false;
+ }
+ }
+ }
+
+ ///////////// M A R C S O U N D //////////////
+ // do it proper
+ if (!tomsound) {
+ // store these so that they can be restored before the next loop iteration
+ read1temp = read1;
+ read2temp = read2;
+ writertemp = writer;
+
+
+ for(int i=0; i < NUM_CHANNELS; i++) { // channels loop
+ lowpass1pos = (int)read1;
+ lowpass2pos = (int)read2;
+
+ for(long j=0; j < samples; j++) { // samples loop
+
+ read1int = (int)read1;
+ read2int = (int)read2;
+
+ /* read from read heads */
+ switch(quality)
+ {
+ // no interpolation or filtering
+ case dirtfi:
+ r1val = buf1[i][read1int];
+ r2val = buf2[i][read2int];
+ break;
+ // spline interpolation, but no filtering
+ case hifi:
+// r1val = interpolateLinear(buf1[i], read1, bsize, writer-read1int);
+ r1val = interpolateHermite(buf1[i], read1, bsize, writer-read1int);
+ r2val = interpolateHermite(buf2[i], read2, bsize, writer-read2int);
+ break;
+ // spline interpolation plus anti-aliasing lowpass filtering for high speeds
+ // or sub-bass-removing highpass filtering for low speeds
+ case ultrahifi:
+ float lp1, lp2;
+ switch (filterMode1)
+ {
+ case useHighpass:
+ case useLowpassIIR:
+ // interpolate the values in the IIR output history
+ r1val = interpolateHermitePostFilter(&filter1[i], read1);
+ break;
+ case useLowpassFIR:
+ // get 2 consecutive FIR output values for linear interpolation
+ lp1 = processFIRfilter(buf1[i], numFIRtaps, firCoefficients1,
+ (read1int-numFIRtaps+bsize)%bsize, bsize);
+ lp2 = processFIRfilter(buf1[i], numFIRtaps, firCoefficients1,
+ (read1int-numFIRtaps+1+bsize)%bsize, bsize);
+ // interpolate output linearly (avoid shit sound) & compensate gain
+ r1val = interpolateLinear2values(lp1, lp2, read1) * mug1;
+ break;
+ default:
+ r1val = interpolateHermite(buf1[i], read1, bsize, writer-read1int);
+ break;
+ }
+ switch (filterMode2)
+ {
+ case useHighpass:
+ case useLowpassIIR:
+ // interpolate the values in the IIR output history
+ r2val = interpolateHermitePostFilter(&filter2[i], read2);
+ break;
+ case useLowpassFIR:
+ // get 2 consecutive FIR output values for linear interpolation
+ lp1 = processFIRfilter(buf2[i], numFIRtaps, firCoefficients2,
+ (read2int-numFIRtaps+bsize)%bsize, bsize);
+ lp2 = processFIRfilter(buf2[i], numFIRtaps, firCoefficients2,
+ (read2int-numFIRtaps+1+bsize)%bsize, bsize);
+ // interpolate output linearly (avoid shit sound) & compensate gain
+ r2val = interpolateLinear2values(lp1, lp2, read2) * mug2;
+ break;
+ default:
+ r2val = interpolateHermite(buf2[i], read2, bsize, writer-read2int);
+ break;
+ }
+ break;
+ // dirt-fi style again for the safety net
+ default:
+ r1val = buf1[i][read1int];
+ r2val = buf2[i][read2int];
+ break;
+ } // end of quality switch
+
+ // crossfade the last stored smoothing sample with
+ // the current sample if smoothing is in progress
+ if (smoothcount1[i]) {
+ r1val = ( r1val * (1.0f - (smoothstep1[i]*(float)smoothcount1[i])) )
+ + (lastr1val[i] * smoothstep1[i]*(float)smoothcount1[i]);
+ (smoothcount1[i])--;
+ }
+ if (smoothcount2[i]) {
+ r2val = ( r2val * (1.0f - (smoothstep2[i]*(float)smoothcount2[i])) )
+ + (lastr2val[i] * smoothstep2[i]*(float)smoothcount2[i]);
+ (smoothcount2[i])--;
+ }
+
+ /* then write into buffer (w/ feedback) */
+
+ // mix very quiet noise (-300 dB) into the input singal
+ // to hopefully avoid any denormal values from IIR filtering
+/* buf1[i][writer] = in[i][j] + (feed1 * r1val * mix1) + quietNoise;
+ buf2[i][writer] = in[i][j] + (feed2 * r2val * mix2) + quietNoise;
+ quietNoise = -quietNoise; // flip its sign
+*/
+ buf1[i][writer] = (*in) + (feed1 * r1val * mix1);
+ buf2[i][writer] = (*in) + (feed2 * r2val * mix2);
+ undenormalize(buf1[i][writer]);
+ undenormalize(buf2[i][writer]);
+
+ /* make output
+ outputs[i][j] = (in[i][j]*drymix) + (r1val*mix1) + (r2val*mix2);
+ outputs[i][j] += (in[i][j]*drymix) + (r1val*mix1) + (r2val*mix2);
+ */
+
+ if (replacing)
+ *out++ = ((*in++)*drymix) + (r1val*mix1) + (r2val*mix2);
+ else
+ *out++ += ((*in++)*drymix) + (r1val*mix1) + (r2val*mix2);
+
+ /* start smoothing stuff if the writer has
+ passed a reader or vice versa.
+ (check the positions before wrapping around the heads)
+ */
+
+ if ( ( (read1int < writer) &&
+ (((int)(read1+(double)speed1)) >= (writer+1)) ) ||
+ ( (read1int >= writer) &&
+ (((int)(read1+(double)speed1)) <= (writer+1)) ) ) {
+ /* check because, at slow speeds,
+ it's possible to go into this twice or more in a row */
+ if (smoothcount1[i] <= 0) {
+ // store the most recent output as the channel 1 smoothing sample
+ lastr1val[i] = r1val;
+ // truncate the smoothing duration if we're using too small of a buffer size
+ smoothdur1[i] =
+ (SMOOTH_DUR > (int)(bsize_float/(double)speed1)) ?
+ (int)(bsize_float/(double)speed1) : SMOOTH_DUR;
+ smoothstep1[i] = 1.0f / (float)smoothdur1[i]; // the scalar step value
+ smoothcount1[i] = smoothdur1[i]; // set the counter to the total duration
+ }
+ }
+
+ // channel 2 smoothing stuff
+ if ( ( (read2int < writer) &&
+ (((int)(read2+(double)speed2)) >= (writer+1)) ) ||
+ ( (read2int >= writer) &&
+ (((int)(read2+(double)speed2)) <= (writer+1)) ) ) {
+ if (smoothcount2[i] <= 0) {
+ // store the most recent output as the channel 2 smoothing sample
+ lastr2val[i] = r2val;
+ // truncate the smoothing duration if we're using too small of a buffer size
+ smoothdur2[i] =
+ (SMOOTH_DUR > (int)(bsize_float/(double)speed2)) ?
+ (int)(bsize_float/(double)speed2) : SMOOTH_DUR;
+ smoothstep2[i] = 1.0f / (float)smoothdur2[i]; // the scalar step value
+ smoothcount2[i] = smoothdur2[i]; // set the counter to the total duration
+ }
+ }
+
+ /* update rw heads */
+ writer++;
+ read1 += (double)speed1;
+ read2 += (double)speed2;
+
+ // wrap around the rw heads if they've gone past the end of the buffer
+ writer %= bsize;
+ if (read1 >= bsize_float)
+ read1 = fmod(fabs(read1), bsize_float);
+ if (read2 >= bsize_float)
+ read2 = fmod(fabs(read2), bsize_float);
+
+ // if we're doing IIR lowpass filtering,
+ // then we probably need to process a few consecutive samples in order
+ // to get the continuous impulse (or whatever you call that),
+ // probably whatever the speed multiplier is, that's how many samples
+ if (filterMode1 == useLowpassIIR)
+ {
+ int lowpasscount = 0;
+ while (lowpasscount < speed1int)
+ {
+ switch (speed1int - lowpasscount)
+ {
+ case 1:
+ filter1[i].processH1(buf1[i][lowpass1pos]);
+ lowpass1pos = (lowpass1pos + 1) % bsize;
+ lowpasscount++;
+ break;
+ case 2:
+ filter1[i].processH2(buf1[i], lowpass1pos, bsize);
+ lowpass1pos = (lowpass1pos + 2) % bsize;
+ lowpasscount += 2;
+ break;
+ case 3:
+ filter1[i].processH3(buf1[i], lowpass1pos, bsize);
+ lowpass1pos = (lowpass1pos + 3) % bsize;
+ lowpasscount += 3;
+ break;
+ default:
+ filter1[i].processH4(buf1[i], lowpass1pos, bsize);
+ lowpass1pos = (lowpass1pos + 4) % bsize;
+ lowpasscount += 4;
+ break;
+ }
+ }
+ read1int = (int)read1;
+ // make sure that we don't need to do one more sample
+ if ( ((lowpass1pos < read1int) && ((lowpass1pos+1) == read1int)) ||
+ ((lowpass1pos == (bsize-1)) && (read1int == 0)) )
+ {
+ filter1[i].processH1(buf1[i][lowpass1pos]);
+ lowpass1pos = (lowpass1pos+1) % bsize;
+ }
+ }
+ // it's simpler for highpassing;
+ // we may not even need to process anything for this sample
+ else if (filterMode1 == useHighpass)
+ {
+ // only if we've traversed to a new integer sample position
+ if ((int)read1 != read1int)
+ filter1[i].process(buf1[i][read1int]);
+ }
+
+ // channel 2 filtering stuff
+ if (filterMode2 == useLowpassIIR)
+ {
+ int lowpasscount = 0;
+ while (lowpasscount < speed2int)
+ {
+ switch (speed2int - lowpasscount)
+ {
+ case 1:
+ filter2[i].processH1(buf2[i][lowpass2pos]);
+ lowpass2pos = (lowpass2pos + 1) % bsize;
+ lowpasscount++;
+ break;
+ case 2:
+ filter2[i].processH2(buf2[i], lowpass2pos, bsize);
+ lowpass2pos = (lowpass2pos + 2) % bsize;
+ lowpasscount += 2;
+ break;
+ case 3:
+ filter2[i].processH3(buf2[i], lowpass2pos, bsize);
+ lowpass2pos = (lowpass2pos + 3) % bsize;
+ lowpasscount += 3;
+ break;
+ default:
+ filter2[i].processH4(buf2[i], lowpass2pos, bsize);
+ lowpass2pos = (lowpass2pos + 4) % bsize;
+ lowpasscount += 4;
+ break;
+ }
+ }
+ read2int = (int)read2;
+ if ( ((lowpass2pos < read2int) && ((lowpass2pos+1) == read2int)) ||
+ ((lowpass2pos == (bsize-1)) && (read2int == 0)) )
+ {
+ filter2[i].processH1(buf2[i][lowpass2pos]);
+ lowpass2pos = (lowpass2pos+1) % bsize;
+ }
+ }
+ else if (filterMode2 == useHighpass)
+ {
+ if ((int)read2 != read2int)
+ filter2[i].process(buf2[i][read2int]);
+ }
+ } /* end of samples loop */
+
+ #ifdef TRANSVERB_STEREO
+ if (i == 0) {
+ // restore these place-holders for the second loop iteration
+ read1 = read1temp;
+ read2 = read2temp;
+ writer = writertemp;
+ }
+ #endif
+ } /* end of channels loop */
+ } /* end of if(TOMSOUND) */
+
+
+
+ ///////////// T O M S O U N D //////////////
+ else {
+ for(long j=0; j < samples; j++) {
+ for(int i=0; i < NUM_CHANNELS; i++) {
+
+ // read from read heads
+
+ switch(quality) {
+ case dirtfi:
+ r1val = mix1 * buf1[i][(int)read1];
+ r2val = mix2 * buf1[i][(int)read2];
+ break;
+ case hifi:
+ case ultrahifi:
+ r1val = mix1 * interpolateHermite(buf1[i], read1, bsize, 333);
+ r2val = mix2 * interpolateHermite(buf1[i], read2, bsize, 333);
+ break;
+ default:
+ r1val = mix1 * buf1[i][(int)read1];
+ r2val = mix2 * buf1[i][(int)read2];
+ break;
+ }
+
+ // then write into buffer (w/ feedback)
+
+ buf1[i][writer] =
+ *in +
+ feed1 * r1val +
+ feed2 * r2val;
+
+ // update rw heads
+
+ writer %= bsize;
+
+ read1 += (double)speed1;
+ read2 += (double)speed2;
+
+ if (read1 >= bsize_float)
+ read1 = fmod(fabs(read1), bsize_float);
+ if (read2 >= bsize_float)
+ read2 = fmod(fabs(read2), bsize_float);
+
+ if (replacing)
+ *out++ = ((*in++)*drymix) + (r1val*mix1) + (r2val*mix2);
+ else
+ *out++ += ((*in++)*drymix) + (r1val*mix1) + (r2val*mix2);
+
+// *out++ += ((*in++)*drymix) + (r1val*mix1) + (r2val*mix2);
+ }
+ }
+ }
+
+}