From e7b24dd7da9de84e218f7d7be623f0cf8b9d1b9c Mon Sep 17 00:00:00 2001 From: "N.N." Date: Mon, 17 Feb 2003 14:12:16 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r415, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/dfx/; revision=416 --- transverb/transverb.cpp | 683 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 683 insertions(+) create mode 100755 transverb/transverb.cpp (limited to 'transverb/transverb.cpp') 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 +#include +#include +#include +#include + +#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); + } + } + } + +} -- cgit v1.2.1