/* 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); } } } }