From 67657003effda7da17c08bea5b666d0005352fd4 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 10 Nov 2005 16:38:22 +0000 Subject: added freeverb~ to the build system and wrote help patch svn path=/trunk/externals/freeverb~/; revision=3871 --- freeverb~-help.pd | 202 +++++++++++++ freeverb~.c | 837 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ freeverb~.cpp | 837 ------------------------------------------------------ 3 files changed, 1039 insertions(+), 837 deletions(-) create mode 100644 freeverb~-help.pd create mode 100644 freeverb~.c delete mode 100644 freeverb~.cpp diff --git a/freeverb~-help.pd b/freeverb~-help.pd new file mode 100644 index 0000000..23f5d82 --- /dev/null +++ b/freeverb~-help.pd @@ -0,0 +1,202 @@ +#N canvas 92 162 612 570 10; +#X obj 104 540 pddp; +#X obj 8 8 cnv 15 90 553 empty empty empty 20 12 0 14 -233017 -66577 +0; +#N canvas 316 127 577 600 More_Info 0; +#X obj 451 407 metro 580; +#X obj 479 429 delay 120; +#X msg 479 450 0; +#X obj 405 471 noise~; +#X obj 435 494 *~; +#X msg 450 450 1; +#X msg 361 450 0; +#X obj 317 494 *~; +#X msg 332 450 1; +#X obj 333 407 metro 700; +#X obj 361 429 delay 200; +#X obj 273 471 osc~ 400; +#X obj 26 536 freeverb~; +#X obj 43 562 dac~; +#X obj 385 364 tgl 30 0 empty empty start 1 15 1 12 -90049 -1 -1 0 +1; +#X obj 187 480 readsf~ 2; +#X msg 177 413 open \$1; +#X obj 177 394 openpanel; +#X msg 269 363 \; pd dsp 1; +#X obj 177 361 bng 30 250 50 0 empty empty open 1 15 1 12 -4080 -1 +-228; +#X obj 177 432 t b a; +#X msg 164 456 1; +#X msg 25 42 roomsize \$1; +#X msg 25 87 damping \$1; +#X msg 25 131 width \$1; +#X msg 25 175 wet \$1; +#X msg 25 219 dry \$1; +#X msg 59 242 print; +#X msg 78 281 freeze \$1; +#X msg 83 318 bypass \$1; +#X obj 83 299 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1 +; +#X obj 78 262 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1 +; +#X text 154 274 - start and stop "freeze" mode (off by default). If +on \, reverb tail gets freezed (sample and hold).; +#X obj 37 196 hsl 60 18 0 1 0 1 empty empty dry 2 9 1 12 -225271 -1 +-1 2950 0; +#X obj 37 152 hsl 60 18 0 1 0 1 empty empty wet 2 9 1 12 -225271 -1 +-1 2950 0; +#X obj 37 108 hsl 60 18 0 1 0 1 empty empty width 2 9 1 12 -262131 +-1 -1 2950 0; +#X obj 37 64 hsl 60 18 0 1 0 1 empty empty damping 2 9 1 12 -261689 +-1 -1 2950 0; +#X obj 37 19 hsl 60 18 0 2 0 1 empty empty roomsize 2 9 1 12 -261689 +-1 -1 0 0; +#X text 117 18 - size of the room to be simulated. Larger values result +in longer reverb. Values above 1 will result in feedback or 'room resonance' +(i.e. reverb getting louder); +#X floatatom 7 20 3 0 0 0 - - -; +#X text 114 65 - amount of damping of the room's surfaces. 1 means +nearly no damping \, resulting in a lot of reflection (long reverb) +\, >1 means high damping of signals (short reverb).; +#X text 116 110 - stereo width of the reverb \, i.e. how much of the +reverb part from the left and right channel mix. Turning this to 1 +(nearly gives two separate mono reverbs.; +#X text 115 152 - level of the wet (reverbed) signal \, between 0 and +1; +#X text 113 197 - level of the dry (i.e. unprocessed or original) signal +\, between 0 and 1 Note that wet and dry signals mix and thus can cause +clipping if both are set at high levels.; +#X text 115 242 - print the current values of these parameters.; +#X text 157 305 - bypass reverb processing when set to 1 Can be used +to compare reverbed signal with original signal \, and to save CPU +when reverb is not needed.; +#X connect 0 0 1 0; +#X connect 0 0 5 0; +#X connect 1 0 2 0; +#X connect 2 0 4 1; +#X connect 3 0 4 0; +#X connect 4 0 12 1; +#X connect 5 0 4 1; +#X connect 6 0 7 1; +#X connect 7 0 12 0; +#X connect 8 0 7 1; +#X connect 9 0 8 0; +#X connect 9 0 10 0; +#X connect 10 0 6 0; +#X connect 11 0 7 0; +#X connect 12 0 13 0; +#X connect 12 1 13 1; +#X connect 14 0 9 0; +#X connect 14 0 0 0; +#X connect 15 0 12 0; +#X connect 15 1 12 1; +#X connect 16 0 20 0; +#X connect 17 0 16 0; +#X connect 19 0 17 0; +#X connect 20 0 21 0; +#X connect 20 1 15 0; +#X connect 21 0 15 0; +#X connect 22 0 12 0; +#X connect 23 0 12 0; +#X connect 24 0 12 0; +#X connect 25 0 12 0; +#X connect 26 0 12 0; +#X connect 27 0 12 0; +#X connect 28 0 12 0; +#X connect 29 0 12 0; +#X connect 30 0 29 0; +#X connect 31 0 28 0; +#X connect 33 0 26 0; +#X connect 34 0 25 0; +#X connect 35 0 24 0; +#X connect 36 0 23 0; +#X connect 37 0 22 0; +#X connect 37 0 39 0; +#X restore 104 514 pd More_Info; +#N canvas 85 22 403 252 Related_Objects 0; +#X restore 104 488 pd Related_Objects; +#X text 16 41 ARGUMENTS:; +#X text 32 270 OUTLETS:; +#X text 23 305 EXAMPLES:; +#X text 20 487 SEE ALSO:; +#X obj 19 16 freeverb~; +#X text 39 223 INLETS:; +#X text 108 16 stereo reverb using the Schroeder/Moorer model; +#X text 106 249 Right: audio input for the right channel.; +#X text 106 223 Left: audio input for the left channel and message +input for settings messages.; +#X text 106 270 Signal: the two outlets are the left and right channels +of a stereo output pair.; +#X obj 482 350 metro 580; +#X obj 510 372 delay 120; +#X msg 510 393 0; +#X obj 436 414 noise~; +#X obj 466 437 *~; +#X msg 481 393 1; +#X msg 392 393 0; +#X obj 348 437 *~; +#X msg 363 393 1; +#X obj 364 350 metro 700; +#X obj 392 372 delay 200; +#X obj 304 414 osc~ 400; +#X obj 348 465 freeverb~; +#X obj 365 491 dac~; +#X obj 416 307 tgl 30 0 empty empty start 1 15 1 12 -90049 -1 -1 0 +1; +#X obj 180 436 readsf~ 2; +#X msg 170 369 open \$1; +#X obj 170 350 openpanel; +#X msg 263 312 \; pd dsp 1; +#X obj 170 317 bng 30 250 50 0 empty empty open 1 15 1 12 -4080 -1 +-228; +#X obj 170 388 t b a; +#X msg 157 412 1; +#X msg 107 44 roomsize \$1; +#X msg 107 66 damping \$1; +#X msg 107 88 width \$1; +#X msg 107 110 wet \$1; +#X msg 107 132 dry \$1; +#X msg 107 154 print; +#X text 198 43 - size of the room to be simulated; +#X msg 107 176 freeze \$1; +#X msg 107 198 bypass \$1; +#X text -179 76 comment; +#X text 195 65 - amount of damping of room's surfaces; +#X text 176 89 - stereo width of reverb; +#X text 168 131 - level of unprocessed signal to include \, between +0 and 1; +#X text 172 110 - level of reverbed signal to include \, between 0 +and 1; +#X text 170 153 - print the current values of the above parameters +; +#X text 183 176 - start/stop freeze of reverb tail \, using 1 or 0 +; +#X text 187 197 - bypass the reverb processing \, using a 1 or 0; +#X text 152 541 - Hans-Christoph Steiner 2005 \, based on Olaf Matthes' +Max help; +#X connect 14 0 15 0; +#X connect 14 0 19 0; +#X connect 15 0 16 0; +#X connect 16 0 18 1; +#X connect 17 0 18 0; +#X connect 18 0 26 1; +#X connect 19 0 18 1; +#X connect 20 0 21 1; +#X connect 21 0 26 0; +#X connect 22 0 21 1; +#X connect 23 0 22 0; +#X connect 23 0 24 0; +#X connect 24 0 20 0; +#X connect 25 0 21 0; +#X connect 26 0 27 0; +#X connect 26 1 27 1; +#X connect 28 0 23 0; +#X connect 28 0 14 0; +#X connect 29 0 26 0; +#X connect 29 1 26 1; +#X connect 30 0 34 0; +#X connect 31 0 30 0; +#X connect 33 0 31 0; +#X connect 34 0 35 0; +#X connect 34 1 29 0; +#X connect 35 0 29 0; diff --git a/freeverb~.c b/freeverb~.c new file mode 100644 index 0000000..bea9938 --- /dev/null +++ b/freeverb~.c @@ -0,0 +1,837 @@ +/* -------------------------- freeverb~ --------------------------------------- */ +/* */ +/* Tilde object that implements the Schroeder/Moorer reverb model. */ +/* Written by Olaf Matthes . */ +/* Get source at http://www.akustische-kunst.org/ */ +/* */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License */ +/* as published by the Free Software Foundation; either version 2 */ +/* of the License, or (at your option) any later version. */ +/* */ +/* This program 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 General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Also compiles for Max/MSP. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#ifdef NT +#pragma warning( disable : 4091 ) +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#ifdef PD +#include "m_pd.h" +#else // Max/MSP +#include "ext.h" +#include "z_dsp.h" +#define t_floatarg double +#endif + +#include +#include + +#define LOGTEN 2.302585092994 + +#define numcombs 8 +#define numallpasses 4 +#define muted 0 +#define fixedgain 0.015 +#define scalewet 3.0 +#define scaledry 2.0 +#define scaledamp 0.4 +#define scaleroom 0.28 +#define offsetroom 0.7 +#define initialroom 0.5 +#define initialdamp 0.5 +#define initialwet 1.0/scalewet +#define initialdry 0.0 +#define initialwidth 1.0 +#define initialmode 0 +#define initialbypass 0 +#define freezemode 0.5 +#define stereospread 23 + +/* these values assume 44.1KHz sample rate + they will probably be OK for 48KHz sample rate + but would need scaling for 96KHz (or other) sample rates. + the values were obtained by listening tests. */ +static const int combtuningL[numcombs] + = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; +static const int combtuningR[numcombs] + = { 1116+stereospread, 1188+stereospread, 1277+stereospread, 1356+stereospread, + 1422+stereospread, 1491+stereospread, 1557+stereospread, 1617+stereospread }; + +static const int allpasstuningL[numallpasses] + = { 556, 441, 341, 225 }; +static const int allpasstuningR[numallpasses] + = { 556+stereospread, 441+stereospread, 341+stereospread, 225+stereospread }; + +static char *version = "freeverb~ v1.2"; + +#ifdef PD +static t_class *freeverb_class; + +typedef struct _freeverb +{ + t_object x_obj; +#else // Max/MSP +void *freeverb_class; + +typedef struct _freeverb +{ + t_pxobject x_obj; +#endif + /* freeverb stuff */ + t_float x_gain; + t_float x_roomsize,x_roomsize1; + t_float x_damp,x_damp1; + t_float x_wet,x_wet1,x_wet2; + t_float x_dry; + t_float x_width; + t_float x_mode; + t_float x_bypass; + int x_skip; + + t_float x_allpassfeedback; /* feedback of allpass filters */ + t_float x_combfeedback; /* feedback of comb filters */ + t_float x_combdamp1; + t_float x_combdamp2; + t_float x_filterstoreL[numcombs]; /* stores last sample value */ + t_float x_filterstoreR[numcombs]; + + /* buffers for the combs */ + t_float *x_bufcombL[numcombs]; + t_float *x_bufcombR[numcombs]; + int x_combidxL[numcombs]; + int x_combidxR[numcombs]; + + /* buffers for the allpasses */ + t_float *x_bufallpassL[numallpasses]; + t_float *x_bufallpassR[numallpasses]; + int x_allpassidxL[numallpasses]; + int x_allpassidxR[numallpasses]; + + /* we'll make local copies adjusted to fit our sample rate */ + int x_combtuningL[numcombs]; + int x_combtuningR[numcombs]; + + int x_allpasstuningL[numallpasses]; + int x_allpasstuningR[numallpasses]; + +#ifdef PD + t_float x_float; +#endif +} t_freeverb; + +#ifndef IRIX +#define IS_DENORM_FLOAT(v) ((((*(unsigned long*)&(v))&0x7f800000)==0)&&((v)!=0.f)) +#define IS_NAN_FLOAT(v) (((*(unsigned long*)&(v))&0x7f800000)==0x7f800000) +#define IS_DENORM_NAN_FLOAT(v) (IS_DENORM_FLOAT(v)||IS_NAN_FLOAT(v)) +#define FIX_DENORM_NAN_FLOAT(v) ((v)=IS_DENORM_NAN_FLOAT(v)?0.f:(v)) +#else +#define FIX_DENORM_NAN_FLOAT(v); +#endif + + /* we need prototypes for Mac for everything */ +static void comb_setdamp(t_freeverb *x, t_floatarg val); +static void comb_setfeedback(t_freeverb *x, t_floatarg val); +static inline t_float comb_processL(t_freeverb *x, int filteridx, t_float input); +static inline t_float comb_processR(t_freeverb *x, int filteridx, t_float input); +static void allpass_setfeedback(t_freeverb *x, t_floatarg val); +static inline t_float allpass_processL(t_freeverb *x, int filteridx, t_float input); +static inline t_float allpass_processR(t_freeverb *x, int filteridx, t_float input); +t_int *freeverb_perform(t_int *w); +t_int *freeverb_perf8(t_int *w); +static void dsp_add_freeverb(t_freeverb *x, t_sample *in1, t_sample *in2, t_sample *out1, t_sample *out2, int n); +void freeverb_dsp(t_freeverb *x, t_signal **sp); +static void freeverb_update(t_freeverb *x); +static void freeverb_setroomsize(t_freeverb *x, t_floatarg value); +static float freeverb_getroomsize(t_freeverb *x); +static void freeverb_setdamp(t_freeverb *x, t_floatarg value); +static float freeverb_getdamp(t_freeverb *x); +static void freeverb_setwet(t_freeverb *x, t_floatarg value); +static float freeverb_getwet(t_freeverb *x); +static void freeverb_setdry(t_freeverb *x, t_floatarg value); +static float freeverb_getdry(t_freeverb *x); +static void freeverb_setwidth(t_freeverb *x, t_floatarg value); +static float freeverb_getwidth(t_freeverb *x); +static void freeverb_setmode(t_freeverb *x, t_floatarg value); +static float freeverb_getmode(t_freeverb *x); +static void freeverb_setbypass(t_freeverb *x, t_floatarg value); +static void freeverb_mute(t_freeverb *x); +static float freeverb_getdb(float f); +static void freeverb_print(t_freeverb *x); +#ifndef PD +void freeverb_assist(t_freeverb *x, void *b, long m, long a, char *s); +#endif +static void freeverb_free(t_freeverb *x); +void *freeverb_new(t_floatarg val); + +/* -------------------- comb filter stuff ----------------------- */ +static void comb_setdamp(t_freeverb *x, t_floatarg val) +{ + x->x_combdamp1 = val; + x->x_combdamp2 = 1-val; +} + +static void comb_setfeedback(t_freeverb *x, t_floatarg val) +{ + x->x_combfeedback = val; +} + +// Big to inline - but crucial for speed +static inline t_float comb_processL(t_freeverb *x, int filteridx, t_float input) +{ + t_float output; + int bufidx = x->x_combidxL[filteridx]; + + output = x->x_bufcombL[filteridx][bufidx]; + FIX_DENORM_NAN_FLOAT(output); + + x->x_filterstoreL[filteridx] = (output*x->x_combdamp2) + (x->x_filterstoreL[filteridx]*x->x_combdamp1); + FIX_DENORM_NAN_FLOAT(x->x_filterstoreL[filteridx]); + + x->x_bufcombL[filteridx][bufidx] = input + (x->x_filterstoreL[filteridx]*x->x_combfeedback); + + if(++x->x_combidxL[filteridx] >= x->x_combtuningL[filteridx]) x->x_combidxL[filteridx] = 0; + + return output; +} + +static inline t_float comb_processR(t_freeverb *x, int filteridx, t_float input) +{ + t_float output; + int bufidx = x->x_combidxR[filteridx]; + + output = x->x_bufcombR[filteridx][bufidx]; + FIX_DENORM_NAN_FLOAT(output); + + x->x_filterstoreR[filteridx] = (output*x->x_combdamp2) + (x->x_filterstoreR[filteridx]*x->x_combdamp1); + FIX_DENORM_NAN_FLOAT(x->x_filterstoreR[filteridx]); + + x->x_bufcombR[filteridx][bufidx] = input + (x->x_filterstoreR[filteridx]*x->x_combfeedback); + + if(++x->x_combidxR[filteridx] >= x->x_combtuningR[filteridx]) x->x_combidxR[filteridx] = 0; + + return output; +} + +/* -------------------- allpass filter stuff ----------------------- */ +static void allpass_setfeedback(t_freeverb *x, t_floatarg val) +{ + x->x_allpassfeedback = val; +} + +// Big to inline - but crucial for speed +static inline t_float allpass_processL(t_freeverb *x, int filteridx, t_float input) +{ + t_float output; + t_float bufout; + int bufidx = x->x_allpassidxL[filteridx]; + + bufout = (t_float)x->x_bufallpassL[filteridx][bufidx]; + FIX_DENORM_NAN_FLOAT(bufout); + + output = -input + bufout; + x->x_bufallpassL[filteridx][bufidx] = input + (bufout*x->x_allpassfeedback); + + if(++x->x_allpassidxL[filteridx] >= x->x_allpasstuningL[filteridx]) + x->x_allpassidxL[filteridx] = 0; + + return output; +} + +static inline t_float allpass_processR(t_freeverb *x, int filteridx, t_float input) +{ + t_float output; + t_float bufout; + int bufidx = x->x_allpassidxR[filteridx]; + + bufout = (t_float)x->x_bufallpassR[filteridx][bufidx]; + FIX_DENORM_NAN_FLOAT(bufout); + + output = -input + bufout; + x->x_bufallpassR[filteridx][bufidx] = input + (bufout*x->x_allpassfeedback); + + if(++x->x_allpassidxR[filteridx] >= x->x_allpasstuningR[filteridx]) + x->x_allpassidxR[filteridx] = 0; + + return output; +} + +/* -------------------- general DSP stuff ----------------------- */ +t_int *freeverb_perform(t_int *w) +{ + // assign from parameters + t_freeverb *x = (t_freeverb *)(w[1]); + t_float *in1 = (t_float *)(w[2]); + t_float *in2 = (t_float *)(w[3]); + t_float *out1 = (t_float *)(w[4]); + t_float *out2 = (t_float *)(w[5]); + int n = (int)(w[6]); + int i; + t_float outL, outR, inL, inR, input; + +#ifndef PD + if (x->x_obj.z_disabled) + goto out; +#endif + + if(x->x_bypass) + { + // Bypass, so just copy input to output + while(n--) + { + inL = *in1++; // We have to copy first before we can write to output + inR = *in2++; // since this might be at the same memory position + *out1++ = inL; + *out2++ = inR; + } + } + else + { + // DSP loop + while(n--) + { + outL = outR = 0.; + inL = *in1++; + inR = *in2++; + input = (inL + inR) * x->x_gain; + + // Accumulate comb filters in parallel + for(i=0; i < numcombs; i++) + { + outL += comb_processL(x, i, input); + outR += comb_processR(x, i, input); + } + + // Feed through allpasses in series + for(i=0; i < numallpasses; i++) + { + outL = allpass_processL(x, i, outL); + outR = allpass_processR(x, i, outR); + } + + // Calculate output REPLACING anything already there + *out1++ = outL*x->x_wet1 + outR*x->x_wet2 + inL*x->x_dry; + *out2++ = outR*x->x_wet1 + outL*x->x_wet2 + inR*x->x_dry; + } + } +#ifndef PD +out: +#endif + return(w + 7); +} + +// This is a hand unrolled version of the perform routine for +// DSP vector sizes that are multiples of 8 +t_int *freeverb_perf8(t_int *w) +{ + // assign from parameters + t_freeverb *x = (t_freeverb *)(w[1]); + t_float *in1 = (t_float *)(w[2]); + t_float *in2 = (t_float *)(w[3]); + t_float *out1 = (t_float *)(w[4]); + t_float *out2 = (t_float *)(w[5]); + int n = (int)(w[6]); + int i; + t_float outL[8], outR[8], inL[8], inR[8], input[8]; + +#ifndef PD + if (x->x_obj.z_disabled) + goto out; +#endif + + if(x->x_bypass) + { + // Bypass, so just copy input to output + for(; n; n -= 8, out1 += 8, out2 += 8, in1 += 8, in2 += 8) + { + inL[0] = in1[0]; // We have to copy first before we can write to output + inR[0] = in2[0]; // since this might be at the same memory position + out1[0] = inL[0]; + out2[0] = inR[0]; + inL[1] = in1[1]; + inR[1] = in2[1]; + out1[1] = inL[1]; + out2[1] = inR[1]; + inL[2] = in1[2]; + inR[2] = in2[2]; + out1[2] = inL[2]; + out2[2] = inR[2]; + inL[3] = in1[3]; + inR[3] = in2[3]; + out1[3] = inL[3]; + out2[3] = inR[3]; + inL[4] = in1[4]; + inR[4] = in2[4]; + out1[4] = inL[4]; + out2[4] = inR[4]; + inL[5] = in1[5]; + inR[5] = in2[5]; + out1[5] = inL[5]; + out2[5] = inR[5]; + inL[6] = in1[6]; + inR[6] = in2[6]; + out1[6] = inL[6]; + out2[6] = inR[6]; + inL[7] = in1[7]; + inR[7] = in2[7]; + out1[7] = inL[7]; + out2[7] = inR[7]; + } + } + else + { + // DSP loop + for(; n; n -= 8, out1 += 8, out2 += 8, in1 += 8, in2 += 8) + { + outL[0] = outR [0]= 0.; + inL[0] = in1[0]; + inR[0] = in2[0]; + input[0] = (inL[0] + inR[0]) * x->x_gain; + + outL[1] = outR [1]= 0.; + inL[1] = in1[1]; + inR[1] = in2[1]; + input[1] = (inL[1] + inR[1]) * x->x_gain; + + outL[2] = outR [2]= 0.; + inL[2] = in1[2]; + inR[2] = in2[2]; + input[2] = (inL[2] + inR[2]) * x->x_gain; + + outL[3] = outR [3]= 0.; + inL[3] = in1[3]; + inR[3] = in2[3]; + input[3] = (inL[3] + inR[3]) * x->x_gain; + + outL[4] = outR [4]= 0.; + inL[4] = in1[4]; + inR[4] = in2[4]; + input[4] = (inL[4] + inR[4]) * x->x_gain; + + outL[5] = outR [5]= 0.; + inL[5] = in1[5]; + inR[5] = in2[5]; + input[5] = (inL[5] + inR[5]) * x->x_gain; + + outL[6] = outR [6]= 0.; + inL[6] = in1[6]; + inR[6] = in2[6]; + input[6] = (inL[6] + inR[6]) * x->x_gain; + + outL[7] = outR [7]= 0.; + inL[7] = in1[7]; + inR[7] = in2[7]; + input[7] = (inL[7] + inR[7]) * x->x_gain; + + // Accumulate comb filters in parallel + for(i=0; i < numcombs; i++) + { + outL[0] += comb_processL(x, i, input[0]); + outR[0] += comb_processR(x, i, input[0]); + outL[1] += comb_processL(x, i, input[1]); + outR[1] += comb_processR(x, i, input[1]); + outL[2] += comb_processL(x, i, input[2]); + outR[2] += comb_processR(x, i, input[2]); + outL[3] += comb_processL(x, i, input[3]); + outR[3] += comb_processR(x, i, input[3]); + outL[4] += comb_processL(x, i, input[4]); + outR[4] += comb_processR(x, i, input[4]); + outL[5] += comb_processL(x, i, input[5]); + outR[5] += comb_processR(x, i, input[5]); + outL[6] += comb_processL(x, i, input[6]); + outR[6] += comb_processR(x, i, input[6]); + outL[7] += comb_processL(x, i, input[7]); + outR[7] += comb_processR(x, i, input[7]); + } + + // Feed through allpasses in series + for(i=0; i < numallpasses; i++) + { + outL[0] = allpass_processL(x, i, outL[0]); + outR[0] = allpass_processR(x, i, outR[0]); + outL[1] = allpass_processL(x, i, outL[1]); + outR[1] = allpass_processR(x, i, outR[1]); + outL[2] = allpass_processL(x, i, outL[2]); + outR[2] = allpass_processR(x, i, outR[2]); + outL[3] = allpass_processL(x, i, outL[3]); + outR[3] = allpass_processR(x, i, outR[3]); + outL[4] = allpass_processL(x, i, outL[4]); + outR[4] = allpass_processR(x, i, outR[4]); + outL[5] = allpass_processL(x, i, outL[5]); + outR[5] = allpass_processR(x, i, outR[5]); + outL[6] = allpass_processL(x, i, outL[6]); + outR[6] = allpass_processR(x, i, outR[6]); + outL[7] = allpass_processL(x, i, outL[7]); + outR[7] = allpass_processR(x, i, outR[7]); + } + + // Calculate output REPLACING anything already there + out1[0] = outL[0]*x->x_wet1 + outR[0]*x->x_wet2 + inL[0]*x->x_dry; + out2[0] = outR[0]*x->x_wet1 + outL[0]*x->x_wet2 + inR[0]*x->x_dry; + + out1[1] = outL[1]*x->x_wet1 + outR[1]*x->x_wet2 + inL[1]*x->x_dry; + out2[1] = outR[1]*x->x_wet1 + outL[1]*x->x_wet2 + inR[1]*x->x_dry; + out1[2] = outL[2]*x->x_wet1 + outR[2]*x->x_wet2 + inL[2]*x->x_dry; + out2[2] = outR[2]*x->x_wet1 + outL[2]*x->x_wet2 + inR[2]*x->x_dry; + out1[3] = outL[3]*x->x_wet1 + outR[3]*x->x_wet2 + inL[3]*x->x_dry; + out2[3] = outR[3]*x->x_wet1 + outL[3]*x->x_wet2 + inR[3]*x->x_dry; + out1[4] = outL[4]*x->x_wet1 + outR[4]*x->x_wet2 + inL[4]*x->x_dry; + out2[4] = outR[4]*x->x_wet1 + outL[4]*x->x_wet2 + inR[4]*x->x_dry; + out1[5] = outL[5]*x->x_wet1 + outR[5]*x->x_wet2 + inL[5]*x->x_dry; + out2[5] = outR[5]*x->x_wet1 + outL[5]*x->x_wet2 + inR[5]*x->x_dry; + out1[6] = outL[6]*x->x_wet1 + outR[6]*x->x_wet2 + inL[6]*x->x_dry; + out2[6] = outR[6]*x->x_wet1 + outL[6]*x->x_wet2 + inR[6]*x->x_dry; + out1[7] = outL[7]*x->x_wet1 + outR[7]*x->x_wet2 + inL[7]*x->x_dry; + out2[7] = outR[7]*x->x_wet1 + outL[7]*x->x_wet2 + inR[7]*x->x_dry; + } + } +#ifndef PD +out: +#endif + return(w + 7); +} + +static void dsp_add_freeverb(t_freeverb *x, t_sample *in1, t_sample *in2, + t_sample *out1, t_sample *out2, int n) +{ + if(n & 7) // check whether block size is multiple of 8 + dsp_add(freeverb_perform, 6, x, in1, in2, out1, out2, n); + else + dsp_add(freeverb_perf8, 6, x, in1, in2, out1, out2, n); +} + +void freeverb_dsp(t_freeverb *x, t_signal **sp) +{ + dsp_add_freeverb(x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[0]->s_n); +} + +// ----------- general parameter & calculation stuff ----------- + + // recalculate internal values after parameter change +static void freeverb_update(t_freeverb *x) +{ + + int i; + + x->x_wet1 = x->x_wet*(x->x_width/2 + 0.5); + x->x_wet2 = x->x_wet*((1-x->x_width)/2); + + if (x->x_mode >= freezemode) + { + x->x_roomsize1 = 1.; + x->x_damp1 = 0.; + x->x_gain = muted; + } + else + { + x->x_roomsize1 = x->x_roomsize; + x->x_damp1 = x->x_damp; + x->x_gain = (float)fixedgain; + } + + comb_setfeedback(x, x->x_roomsize1); + comb_setdamp(x, x->x_damp1); +} + + // the following functions set / get the parameters +static void freeverb_setroomsize(t_freeverb *x, t_floatarg value) +{ + x->x_roomsize = (value*scaleroom) + offsetroom; + freeverb_update(x); +} + +static float freeverb_getroomsize(t_freeverb *x) +{ + return (x->x_roomsize-offsetroom)/scaleroom; +} + +static void freeverb_setdamp(t_freeverb *x, t_floatarg value) +{ + x->x_damp = value*scaledamp; + freeverb_update(x); +} + +static float freeverb_getdamp(t_freeverb *x) +{ + return x->x_damp/scaledamp; +} + +static void freeverb_setwet(t_freeverb *x, t_floatarg value) +{ + x->x_wet = value*scalewet; + freeverb_update(x); +} + +static float freeverb_getwet(t_freeverb *x) +{ + return (x->x_wet/scalewet); +} + +static void freeverb_setdry(t_freeverb *x, t_floatarg value) +{ + x->x_dry = value*scaledry; +} + +static float freeverb_getdry(t_freeverb *x) +{ + return (x->x_dry/scaledry); +} + +static void freeverb_setwidth(t_freeverb *x, t_floatarg value) +{ + x->x_width = value; + freeverb_update(x); +} + +static float freeverb_getwidth(t_freeverb *x) +{ + return x->x_width; +} + +static void freeverb_setmode(t_freeverb *x, t_floatarg value) +{ + x->x_mode = value; + freeverb_update(x); +} + +static float freeverb_getmode(t_freeverb *x) +{ + if (x->x_mode >= freezemode) + return 1; + else + return 0; +} + +static void freeverb_setbypass(t_freeverb *x, t_floatarg value) +{ + x->x_bypass = value; + if(x->x_bypass)freeverb_mute(x); +} + + // fill delay lines with silence +static void freeverb_mute(t_freeverb *x) +{ + int i; + + if (freeverb_getmode(x) >= freezemode) + return; + + for (i=0;ix_bufcombL[i], 0x0, x->x_combtuningL[i]*sizeof(t_float)); + memset(x->x_bufcombR[i], 0x0, x->x_combtuningR[i]*sizeof(t_float)); + } + for (i=0;ix_bufallpassL[i], 0x0, x->x_allpasstuningL[i]*sizeof(t_float)); + memset(x->x_bufallpassR[i], 0x0, x->x_allpasstuningR[i]*sizeof(t_float)); + } +} + + // convert gain factor into dB +static float freeverb_getdb(float f) +{ + if (f <= 0) // equation does not work for 0... + { + return (-96); // ...so we output max. damping + } + else + { + float val = (20./LOGTEN * log(f)); + return (val); + } +} + +static void freeverb_print(t_freeverb *x) +{ + post("freeverb~:"); + if(x->x_bypass) { + post(" bypass: on"); + } else post(" bypass: off"); + if(!freeverb_getmode(x)) { + post(" mode: normal"); + } else post(" mode: freeze"); + post(" roomsize: %g", freeverb_getroomsize(x)*scaleroom+offsetroom); + post(" damping: %g %%", freeverb_getdamp(x)*100); + post(" width: %g %%", x->x_width * 100); + post(" wet level: %g dB", freeverb_getdb(freeverb_getwet(x)*scalewet)); + post(" dry level: %g dB", freeverb_getdb(freeverb_getdry(x)*scaledry)); +} + + // clean up +static void freeverb_free(t_freeverb *x) +{ + int i; +#ifndef PD + dsp_free((t_pxobject *)x); // Free the object +#endif + // free memory used by delay lines + for(i = 0; i < numcombs; i++) + { + t_freebytes(x->x_bufcombL[i], x->x_combtuningL[i]*sizeof(t_float)); + t_freebytes(x->x_bufcombR[i], x->x_combtuningR[i]*sizeof(t_float)); + } + + for(i = 0; i < numallpasses; i++) + { + t_freebytes(x->x_bufallpassL[i], x->x_allpasstuningL[i]*sizeof(t_float)); + t_freebytes(x->x_bufallpassR[i], x->x_allpasstuningR[i]*sizeof(t_float)); + } +} + +void *freeverb_new(t_floatarg f) +{ + int i; + int sr = (int)sys_getsr(); + +#ifdef PD + t_freeverb *x = (t_freeverb *)pd_new(freeverb_class); + + // add additional signal inlets and signal outlets + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); +#else // Max/MSP + t_freeverb *x = (t_freeverb *)newobject(freeverb_class); + + // zero out the struct, to be careful + if(x) + { + for(i = sizeof(t_pxobject); i < sizeof(t_freeverb); i++) + ((char*)x)[i] = 0; + } + + dsp_setup((t_pxobject *)x,2); // two signal inlets + + // two signal outlets + outlet_new((t_object *)x, "signal"); + outlet_new((t_object *)x, "signal"); +#endif + // recalculate the reverb parameters in case we don't run at 44.1kHz + for(i = 0; i < numcombs; i++) + { + x->x_combtuningL[i] = (int)(combtuningL[i] * sr / 44100); + x->x_combtuningR[i] = (int)(combtuningR[i] * sr / 44100); + } + for(i = 0; i < numallpasses; i++) + { + x->x_allpasstuningL[i] = (int)(allpasstuningL[i] * sr / 44100); + x->x_allpasstuningR[i] = (int)(allpasstuningL[i] * sr / 44100); + } + + // get memory for delay lines + for(i = 0; i < numcombs; i++) + { + x->x_bufcombL[i] = (t_float*) t_getbytes(x->x_combtuningL[i]*sizeof(t_float)); + x->x_bufcombR[i] = (t_float*) t_getbytes(x->x_combtuningR[i]*sizeof(t_float)); + x->x_combidxL[i] = 0; + x->x_combidxR[i] = 0; + } + for(i = 0; i < numallpasses; i++) + { + x->x_bufallpassL[i] = (t_float*) t_getbytes(x->x_allpasstuningL[i]*sizeof(t_float)); + x->x_bufallpassR[i] = (t_float*) t_getbytes(x->x_allpasstuningR[i]*sizeof(t_float)); + x->x_allpassidxL[i] = 0; + x->x_allpassidxR[i] = 0; + } + + // set default values + x->x_allpassfeedback = 0.5; + x->x_skip = 1; // we use every sample + freeverb_setwet(x, initialwet); + freeverb_setroomsize(x, initialroom); + freeverb_setdry(x, initialdry); + freeverb_setdamp(x, initialdamp); + freeverb_setwidth(x, initialwidth); + freeverb_setmode(x, initialmode); + freeverb_setbypass(x, initialbypass); + + // buffers will be full of rubbish - so we MUST mute them + freeverb_mute(x); + + return (x); +} + +#ifdef PD +void freeverb_tilde_setup(void) +{ + freeverb_class = class_new(gensym("freeverb~"), (t_newmethod)freeverb_new, (t_method)freeverb_free, + sizeof(t_freeverb), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(freeverb_class, t_freeverb, x_float); + class_addmethod(freeverb_class, (t_method)freeverb_dsp, gensym("dsp"), A_NULL); + class_addmethod(freeverb_class, (t_method)freeverb_setroomsize, gensym("roomsize"), A_FLOAT, A_NULL); + class_addmethod(freeverb_class, (t_method)freeverb_setdamp, gensym("damping"), A_FLOAT, A_NULL); + class_addmethod(freeverb_class, (t_method)freeverb_setwidth, gensym("width"), A_FLOAT, A_NULL); + class_addmethod(freeverb_class, (t_method)freeverb_setwet, gensym("wet"), A_FLOAT, A_NULL); + class_addmethod(freeverb_class, (t_method)freeverb_setdry, gensym("dry"), A_FLOAT, A_NULL); + class_addmethod(freeverb_class, (t_method)freeverb_setmode, gensym("freeze"), A_FLOAT, A_NULL); + class_addmethod(freeverb_class, (t_method)freeverb_setbypass, gensym("bypass"), A_FLOAT, A_NULL); + class_addmethod(freeverb_class, (t_method)freeverb_mute, gensym("clear"), A_NULL); + class_addmethod(freeverb_class, (t_method)freeverb_print, gensym("print"), A_NULL); + class_sethelpsymbol(freeverb_class, gensym("help-freeverb~.pd")); + post(version); +} + +#else +// ----------- Max/MSP ----------- +void freeverb_assist(t_freeverb *x, void *b, long m, long a, char *s) +{ + switch(m) { + case 1: // inlet + switch(a) { + case 0: + sprintf(s, "(signal/message) Left Input & Control Messages"); + break; + case 1: + sprintf(s, "(signal) Right Input"); + break; + } + break; + case 2: // outlet + switch(a) { + case 0: + sprintf(s, "(signal) Left Output"); + break; + case 1: + sprintf(s, "(signal) Right Output"); + break; + } + break; + } + +} + +extern "C" void main(void) +{ + setup((t_messlist **)&freeverb_class,(method)freeverb_new, (method)freeverb_free, + (short)sizeof(t_freeverb), 0L, A_DEFFLOAT, 0); + addmess((method)freeverb_dsp, "dsp", A_CANT, 0); + addmess((method)freeverb_assist, "assist", A_CANT, 0); + addmess((method)freeverb_setroomsize, "roomsize", A_FLOAT, 0); + addmess((method)freeverb_setdamp, "damping", A_FLOAT, 0); + addmess((method)freeverb_setwidth, "width", A_FLOAT, 0); + addmess((method)freeverb_setwet, "wet", A_FLOAT, 0); + addmess((method)freeverb_setdry, "dry", A_FLOAT, 0); + addmess((method)freeverb_setmode, "freeze", A_FLOAT, 0); + addmess((method)freeverb_setbypass, "bypass", A_FLOAT, 0); + addmess((method)freeverb_mute, "clear", 0); + addmess((method)freeverb_print, "print", 0); + dsp_initclass(); + finder_addclass("MSP Delays","freeverb~"); + post(version); +} +#endif diff --git a/freeverb~.cpp b/freeverb~.cpp deleted file mode 100644 index 53e2c0a..0000000 --- a/freeverb~.cpp +++ /dev/null @@ -1,837 +0,0 @@ -/* -------------------------- freeverb~ --------------------------------------- */ -/* */ -/* Tilde object that implements the Schroeder/Moorer reverb model. */ -/* Written by Olaf Matthes . */ -/* Get source at http://www.akustische-kunst.org/ */ -/* */ -/* This program is free software; you can redistribute it and/or */ -/* modify it under the terms of the GNU General Public License */ -/* as published by the Free Software Foundation; either version 2 */ -/* of the License, or (at your option) any later version. */ -/* */ -/* This program 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 General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* */ -/* Based on PureData by Miller Puckette and others. */ -/* Also compiles for Max/MSP. */ -/* */ -/* ---------------------------------------------------------------------------- */ - -#ifdef NT -#pragma warning( disable : 4091 ) -#pragma warning( disable : 4244 ) -#pragma warning( disable : 4305 ) -#endif - -#ifdef PD -#include "m_pd.h" -#else // Max/MSP -#include "ext.h" -#include "z_dsp.h" -#define t_floatarg double -#endif - -#include -#include - -#define LOGTEN 2.302585092994 - -#define numcombs 8 -#define numallpasses 4 -#define muted 0 -#define fixedgain 0.015 -#define scalewet 3.0 -#define scaledry 2.0 -#define scaledamp 0.4 -#define scaleroom 0.28 -#define offsetroom 0.7 -#define initialroom 0.5 -#define initialdamp 0.5 -#define initialwet 1.0/scalewet -#define initialdry 0.0 -#define initialwidth 1.0 -#define initialmode 0 -#define initialbypass 0 -#define freezemode 0.5 -#define stereospread 23 - -/* these values assume 44.1KHz sample rate - they will probably be OK for 48KHz sample rate - but would need scaling for 96KHz (or other) sample rates. - the values were obtained by listening tests. */ -static const int combtuningL[numcombs] - = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; -static const int combtuningR[numcombs] - = { 1116+stereospread, 1188+stereospread, 1277+stereospread, 1356+stereospread, - 1422+stereospread, 1491+stereospread, 1557+stereospread, 1617+stereospread }; - -static const int allpasstuningL[numallpasses] - = { 556, 441, 341, 225 }; -static const int allpasstuningR[numallpasses] - = { 556+stereospread, 441+stereospread, 341+stereospread, 225+stereospread }; - -static char *version = "freeverb~ v1.2"; - -#ifdef PD -static t_class *freeverb_class; - -typedef struct _freeverb -{ - t_object x_obj; -#else // Max/MSP -void *freeverb_class; - -typedef struct _freeverb -{ - t_pxobject x_obj; -#endif - /* freeverb stuff */ - t_float x_gain; - t_float x_roomsize,x_roomsize1; - t_float x_damp,x_damp1; - t_float x_wet,x_wet1,x_wet2; - t_float x_dry; - t_float x_width; - t_float x_mode; - t_float x_bypass; - int x_skip; - - t_float x_allpassfeedback; /* feedback of allpass filters */ - t_float x_combfeedback; /* feedback of comb filters */ - t_float x_combdamp1; - t_float x_combdamp2; - t_float x_filterstoreL[numcombs]; /* stores last sample value */ - t_float x_filterstoreR[numcombs]; - - /* buffers for the combs */ - t_float *x_bufcombL[numcombs]; - t_float *x_bufcombR[numcombs]; - int x_combidxL[numcombs]; - int x_combidxR[numcombs]; - - /* buffers for the allpasses */ - t_float *x_bufallpassL[numallpasses]; - t_float *x_bufallpassR[numallpasses]; - int x_allpassidxL[numallpasses]; - int x_allpassidxR[numallpasses]; - - /* we'll make local copies adjusted to fit our sample rate */ - int x_combtuningL[numcombs]; - int x_combtuningR[numcombs]; - - int x_allpasstuningL[numallpasses]; - int x_allpasstuningR[numallpasses]; - -#ifdef PD - t_float x_float; -#endif -} t_freeverb; - -#ifndef IRIX -#define IS_DENORM_FLOAT(v) ((((*(unsigned long*)&(v))&0x7f800000)==0)&&((v)!=0.f)) -#define IS_NAN_FLOAT(v) (((*(unsigned long*)&(v))&0x7f800000)==0x7f800000) -#define IS_DENORM_NAN_FLOAT(v) (IS_DENORM_FLOAT(v)||IS_NAN_FLOAT(v)) -#define FIX_DENORM_NAN_FLOAT(v) ((v)=IS_DENORM_NAN_FLOAT(v)?0.f:(v)) -#else -#define FIX_DENORM_NAN_FLOAT(v); -#endif - - /* we need prototypes for Mac for everything */ -static void comb_setdamp(t_freeverb *x, t_floatarg val); -static void comb_setfeedback(t_freeverb *x, t_floatarg val); -static inline t_float comb_processL(t_freeverb *x, int filteridx, t_float input); -static inline t_float comb_processR(t_freeverb *x, int filteridx, t_float input); -static void allpass_setfeedback(t_freeverb *x, t_floatarg val); -static inline t_float allpass_processL(t_freeverb *x, int filteridx, t_float input); -static inline t_float allpass_processR(t_freeverb *x, int filteridx, t_float input); -t_int *freeverb_perform(t_int *w); -t_int *freeverb_perf8(t_int *w); -static void dsp_add_freeverb(t_freeverb *x, t_sample *in1, t_sample *in2, t_sample *out1, t_sample *out2, int n); -void freeverb_dsp(t_freeverb *x, t_signal **sp); -static void freeverb_update(t_freeverb *x); -static void freeverb_setroomsize(t_freeverb *x, t_floatarg value); -static float freeverb_getroomsize(t_freeverb *x); -static void freeverb_setdamp(t_freeverb *x, t_floatarg value); -static float freeverb_getdamp(t_freeverb *x); -static void freeverb_setwet(t_freeverb *x, t_floatarg value); -static float freeverb_getwet(t_freeverb *x); -static void freeverb_setdry(t_freeverb *x, t_floatarg value); -static float freeverb_getdry(t_freeverb *x); -static void freeverb_setwidth(t_freeverb *x, t_floatarg value); -static float freeverb_getwidth(t_freeverb *x); -static void freeverb_setmode(t_freeverb *x, t_floatarg value); -static float freeverb_getmode(t_freeverb *x); -static void freeverb_setbypass(t_freeverb *x, t_floatarg value); -static void freeverb_mute(t_freeverb *x); -static float freeverb_getdb(float f); -static void freeverb_print(t_freeverb *x); -#ifndef PD -void freeverb_assist(t_freeverb *x, void *b, long m, long a, char *s); -#endif -static void freeverb_free(t_freeverb *x); -void *freeverb_new(t_floatarg val); - -/* -------------------- comb filter stuff ----------------------- */ -static void comb_setdamp(t_freeverb *x, t_floatarg val) -{ - x->x_combdamp1 = val; - x->x_combdamp2 = 1-val; -} - -static void comb_setfeedback(t_freeverb *x, t_floatarg val) -{ - x->x_combfeedback = val; -} - -// Big to inline - but crucial for speed -static inline t_float comb_processL(t_freeverb *x, int filteridx, t_float input) -{ - t_float output; - int bufidx = x->x_combidxL[filteridx]; - - output = x->x_bufcombL[filteridx][bufidx]; - FIX_DENORM_NAN_FLOAT(output); - - x->x_filterstoreL[filteridx] = (output*x->x_combdamp2) + (x->x_filterstoreL[filteridx]*x->x_combdamp1); - FIX_DENORM_NAN_FLOAT(x->x_filterstoreL[filteridx]); - - x->x_bufcombL[filteridx][bufidx] = input + (x->x_filterstoreL[filteridx]*x->x_combfeedback); - - if(++x->x_combidxL[filteridx] >= x->x_combtuningL[filteridx]) x->x_combidxL[filteridx] = 0; - - return output; -} - -static inline t_float comb_processR(t_freeverb *x, int filteridx, t_float input) -{ - t_float output; - int bufidx = x->x_combidxR[filteridx]; - - output = x->x_bufcombR[filteridx][bufidx]; - FIX_DENORM_NAN_FLOAT(output); - - x->x_filterstoreR[filteridx] = (output*x->x_combdamp2) + (x->x_filterstoreR[filteridx]*x->x_combdamp1); - FIX_DENORM_NAN_FLOAT(x->x_filterstoreR[filteridx]); - - x->x_bufcombR[filteridx][bufidx] = input + (x->x_filterstoreR[filteridx]*x->x_combfeedback); - - if(++x->x_combidxR[filteridx] >= x->x_combtuningR[filteridx]) x->x_combidxR[filteridx] = 0; - - return output; -} - -/* -------------------- allpass filter stuff ----------------------- */ -static void allpass_setfeedback(t_freeverb *x, t_floatarg val) -{ - x->x_allpassfeedback = val; -} - -// Big to inline - but crucial for speed -static inline t_float allpass_processL(t_freeverb *x, int filteridx, t_float input) -{ - t_float output; - t_float bufout; - int bufidx = x->x_allpassidxL[filteridx]; - - bufout = (t_float)x->x_bufallpassL[filteridx][bufidx]; - FIX_DENORM_NAN_FLOAT(bufout); - - output = -input + bufout; - x->x_bufallpassL[filteridx][bufidx] = input + (bufout*x->x_allpassfeedback); - - if(++x->x_allpassidxL[filteridx] >= x->x_allpasstuningL[filteridx]) - x->x_allpassidxL[filteridx] = 0; - - return output; -} - -static inline t_float allpass_processR(t_freeverb *x, int filteridx, t_float input) -{ - t_float output; - t_float bufout; - int bufidx = x->x_allpassidxR[filteridx]; - - bufout = (t_float)x->x_bufallpassR[filteridx][bufidx]; - FIX_DENORM_NAN_FLOAT(bufout); - - output = -input + bufout; - x->x_bufallpassR[filteridx][bufidx] = input + (bufout*x->x_allpassfeedback); - - if(++x->x_allpassidxR[filteridx] >= x->x_allpasstuningR[filteridx]) - x->x_allpassidxR[filteridx] = 0; - - return output; -} - -/* -------------------- general DSP stuff ----------------------- */ -t_int *freeverb_perform(t_int *w) -{ - // assign from parameters - t_freeverb *x = (t_freeverb *)(w[1]); - t_float *in1 = (t_float *)(w[2]); - t_float *in2 = (t_float *)(w[3]); - t_float *out1 = (t_float *)(w[4]); - t_float *out2 = (t_float *)(w[5]); - int n = (int)(w[6]); - int i; - t_float outL, outR, inL, inR, input; - -#ifndef PD - if (x->x_obj.z_disabled) - goto out; -#endif - - if(x->x_bypass) - { - // Bypass, so just copy input to output - while(n--) - { - inL = *in1++; // We have to copy first before we can write to output - inR = *in2++; // since this might be at the same memory position - *out1++ = inL; - *out2++ = inR; - } - } - else - { - // DSP loop - while(n--) - { - outL = outR = 0.; - inL = *in1++; - inR = *in2++; - input = (inL + inR) * x->x_gain; - - // Accumulate comb filters in parallel - for(i=0; i < numcombs; i++) - { - outL += comb_processL(x, i, input); - outR += comb_processR(x, i, input); - } - - // Feed through allpasses in series - for(i=0; i < numallpasses; i++) - { - outL = allpass_processL(x, i, outL); - outR = allpass_processR(x, i, outR); - } - - // Calculate output REPLACING anything already there - *out1++ = outL*x->x_wet1 + outR*x->x_wet2 + inL*x->x_dry; - *out2++ = outR*x->x_wet1 + outL*x->x_wet2 + inR*x->x_dry; - } - } -#ifndef PD -out: -#endif - return(w + 7); -} - -// This is a hand unrolled version of the perform routine for -// DSP vector sizes that are multiples of 8 -t_int *freeverb_perf8(t_int *w) -{ - // assign from parameters - t_freeverb *x = (t_freeverb *)(w[1]); - t_float *in1 = (t_float *)(w[2]); - t_float *in2 = (t_float *)(w[3]); - t_float *out1 = (t_float *)(w[4]); - t_float *out2 = (t_float *)(w[5]); - int n = (int)(w[6]); - int i; - t_float outL[8], outR[8], inL[8], inR[8], input[8]; - -#ifndef PD - if (x->x_obj.z_disabled) - goto out; -#endif - - if(x->x_bypass) - { - // Bypass, so just copy input to output - for(; n; n -= 8, out1 += 8, out2 += 8, in1 += 8, in2 += 8) - { - inL[0] = in1[0]; // We have to copy first before we can write to output - inR[0] = in2[0]; // since this might be at the same memory position - out1[0] = inL[0]; - out2[0] = inR[0]; - inL[1] = in1[1]; - inR[1] = in2[1]; - out1[1] = inL[1]; - out2[1] = inR[1]; - inL[2] = in1[2]; - inR[2] = in2[2]; - out1[2] = inL[2]; - out2[2] = inR[2]; - inL[3] = in1[3]; - inR[3] = in2[3]; - out1[3] = inL[3]; - out2[3] = inR[3]; - inL[4] = in1[4]; - inR[4] = in2[4]; - out1[4] = inL[4]; - out2[4] = inR[4]; - inL[5] = in1[5]; - inR[5] = in2[5]; - out1[5] = inL[5]; - out2[5] = inR[5]; - inL[6] = in1[6]; - inR[6] = in2[6]; - out1[6] = inL[6]; - out2[6] = inR[6]; - inL[7] = in1[7]; - inR[7] = in2[7]; - out1[7] = inL[7]; - out2[7] = inR[7]; - } - } - else - { - // DSP loop - for(; n; n -= 8, out1 += 8, out2 += 8, in1 += 8, in2 += 8) - { - outL[0] = outR [0]= 0.; - inL[0] = in1[0]; - inR[0] = in2[0]; - input[0] = (inL[0] + inR[0]) * x->x_gain; - - outL[1] = outR [1]= 0.; - inL[1] = in1[1]; - inR[1] = in2[1]; - input[1] = (inL[1] + inR[1]) * x->x_gain; - - outL[2] = outR [2]= 0.; - inL[2] = in1[2]; - inR[2] = in2[2]; - input[2] = (inL[2] + inR[2]) * x->x_gain; - - outL[3] = outR [3]= 0.; - inL[3] = in1[3]; - inR[3] = in2[3]; - input[3] = (inL[3] + inR[3]) * x->x_gain; - - outL[4] = outR [4]= 0.; - inL[4] = in1[4]; - inR[4] = in2[4]; - input[4] = (inL[4] + inR[4]) * x->x_gain; - - outL[5] = outR [5]= 0.; - inL[5] = in1[5]; - inR[5] = in2[5]; - input[5] = (inL[5] + inR[5]) * x->x_gain; - - outL[6] = outR [6]= 0.; - inL[6] = in1[6]; - inR[6] = in2[6]; - input[6] = (inL[6] + inR[6]) * x->x_gain; - - outL[7] = outR [7]= 0.; - inL[7] = in1[7]; - inR[7] = in2[7]; - input[7] = (inL[7] + inR[7]) * x->x_gain; - - // Accumulate comb filters in parallel - for(i=0; i < numcombs; i++) - { - outL[0] += comb_processL(x, i, input[0]); - outR[0] += comb_processR(x, i, input[0]); - outL[1] += comb_processL(x, i, input[1]); - outR[1] += comb_processR(x, i, input[1]); - outL[2] += comb_processL(x, i, input[2]); - outR[2] += comb_processR(x, i, input[2]); - outL[3] += comb_processL(x, i, input[3]); - outR[3] += comb_processR(x, i, input[3]); - outL[4] += comb_processL(x, i, input[4]); - outR[4] += comb_processR(x, i, input[4]); - outL[5] += comb_processL(x, i, input[5]); - outR[5] += comb_processR(x, i, input[5]); - outL[6] += comb_processL(x, i, input[6]); - outR[6] += comb_processR(x, i, input[6]); - outL[7] += comb_processL(x, i, input[7]); - outR[7] += comb_processR(x, i, input[7]); - } - - // Feed through allpasses in series - for(i=0; i < numallpasses; i++) - { - outL[0] = allpass_processL(x, i, outL[0]); - outR[0] = allpass_processR(x, i, outR[0]); - outL[1] = allpass_processL(x, i, outL[1]); - outR[1] = allpass_processR(x, i, outR[1]); - outL[2] = allpass_processL(x, i, outL[2]); - outR[2] = allpass_processR(x, i, outR[2]); - outL[3] = allpass_processL(x, i, outL[3]); - outR[3] = allpass_processR(x, i, outR[3]); - outL[4] = allpass_processL(x, i, outL[4]); - outR[4] = allpass_processR(x, i, outR[4]); - outL[5] = allpass_processL(x, i, outL[5]); - outR[5] = allpass_processR(x, i, outR[5]); - outL[6] = allpass_processL(x, i, outL[6]); - outR[6] = allpass_processR(x, i, outR[6]); - outL[7] = allpass_processL(x, i, outL[7]); - outR[7] = allpass_processR(x, i, outR[7]); - } - - // Calculate output REPLACING anything already there - out1[0] = outL[0]*x->x_wet1 + outR[0]*x->x_wet2 + inL[0]*x->x_dry; - out2[0] = outR[0]*x->x_wet1 + outL[0]*x->x_wet2 + inR[0]*x->x_dry; - - out1[1] = outL[1]*x->x_wet1 + outR[1]*x->x_wet2 + inL[1]*x->x_dry; - out2[1] = outR[1]*x->x_wet1 + outL[1]*x->x_wet2 + inR[1]*x->x_dry; - out1[2] = outL[2]*x->x_wet1 + outR[2]*x->x_wet2 + inL[2]*x->x_dry; - out2[2] = outR[2]*x->x_wet1 + outL[2]*x->x_wet2 + inR[2]*x->x_dry; - out1[3] = outL[3]*x->x_wet1 + outR[3]*x->x_wet2 + inL[3]*x->x_dry; - out2[3] = outR[3]*x->x_wet1 + outL[3]*x->x_wet2 + inR[3]*x->x_dry; - out1[4] = outL[4]*x->x_wet1 + outR[4]*x->x_wet2 + inL[4]*x->x_dry; - out2[4] = outR[4]*x->x_wet1 + outL[4]*x->x_wet2 + inR[4]*x->x_dry; - out1[5] = outL[5]*x->x_wet1 + outR[5]*x->x_wet2 + inL[5]*x->x_dry; - out2[5] = outR[5]*x->x_wet1 + outL[5]*x->x_wet2 + inR[5]*x->x_dry; - out1[6] = outL[6]*x->x_wet1 + outR[6]*x->x_wet2 + inL[6]*x->x_dry; - out2[6] = outR[6]*x->x_wet1 + outL[6]*x->x_wet2 + inR[6]*x->x_dry; - out1[7] = outL[7]*x->x_wet1 + outR[7]*x->x_wet2 + inL[7]*x->x_dry; - out2[7] = outR[7]*x->x_wet1 + outL[7]*x->x_wet2 + inR[7]*x->x_dry; - } - } -#ifndef PD -out: -#endif - return(w + 7); -} - -static void dsp_add_freeverb(t_freeverb *x, t_sample *in1, t_sample *in2, - t_sample *out1, t_sample *out2, int n) -{ - if(n & 7) // check whether block size is multiple of 8 - dsp_add(freeverb_perform, 6, x, in1, in2, out1, out2, n); - else - dsp_add(freeverb_perf8, 6, x, in1, in2, out1, out2, n); -} - -void freeverb_dsp(t_freeverb *x, t_signal **sp) -{ - dsp_add_freeverb(x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[0]->s_n); -} - -// ----------- general parameter & calculation stuff ----------- - - // recalculate internal values after parameter change -static void freeverb_update(t_freeverb *x) -{ - - int i; - - x->x_wet1 = x->x_wet*(x->x_width/2 + 0.5); - x->x_wet2 = x->x_wet*((1-x->x_width)/2); - - if (x->x_mode >= freezemode) - { - x->x_roomsize1 = 1.; - x->x_damp1 = 0.; - x->x_gain = muted; - } - else - { - x->x_roomsize1 = x->x_roomsize; - x->x_damp1 = x->x_damp; - x->x_gain = (float)fixedgain; - } - - comb_setfeedback(x, x->x_roomsize1); - comb_setdamp(x, x->x_damp1); -} - - // the following functions set / get the parameters -static void freeverb_setroomsize(t_freeverb *x, t_floatarg value) -{ - x->x_roomsize = (value*scaleroom) + offsetroom; - freeverb_update(x); -} - -static float freeverb_getroomsize(t_freeverb *x) -{ - return (x->x_roomsize-offsetroom)/scaleroom; -} - -static void freeverb_setdamp(t_freeverb *x, t_floatarg value) -{ - x->x_damp = value*scaledamp; - freeverb_update(x); -} - -static float freeverb_getdamp(t_freeverb *x) -{ - return x->x_damp/scaledamp; -} - -static void freeverb_setwet(t_freeverb *x, t_floatarg value) -{ - x->x_wet = value*scalewet; - freeverb_update(x); -} - -static float freeverb_getwet(t_freeverb *x) -{ - return (x->x_wet/scalewet); -} - -static void freeverb_setdry(t_freeverb *x, t_floatarg value) -{ - x->x_dry = value*scaledry; -} - -static float freeverb_getdry(t_freeverb *x) -{ - return (x->x_dry/scaledry); -} - -static void freeverb_setwidth(t_freeverb *x, t_floatarg value) -{ - x->x_width = value; - freeverb_update(x); -} - -static float freeverb_getwidth(t_freeverb *x) -{ - return x->x_width; -} - -static void freeverb_setmode(t_freeverb *x, t_floatarg value) -{ - x->x_mode = value; - freeverb_update(x); -} - -static float freeverb_getmode(t_freeverb *x) -{ - if (x->x_mode >= freezemode) - return 1; - else - return 0; -} - -static void freeverb_setbypass(t_freeverb *x, t_floatarg value) -{ - x->x_bypass = value; - if(x->x_bypass)freeverb_mute(x); -} - - // fill delay lines with silence -static void freeverb_mute(t_freeverb *x) -{ - int i; - - if (freeverb_getmode(x) >= freezemode) - return; - - for (i=0;ix_bufcombL[i], 0x0, x->x_combtuningL[i]*sizeof(t_float)); - memset(x->x_bufcombR[i], 0x0, x->x_combtuningR[i]*sizeof(t_float)); - } - for (i=0;ix_bufallpassL[i], 0x0, x->x_allpasstuningL[i]*sizeof(t_float)); - memset(x->x_bufallpassR[i], 0x0, x->x_allpasstuningR[i]*sizeof(t_float)); - } -} - - // convert gain factor into dB -static float freeverb_getdb(float f) -{ - if (f <= 0) // equation does not work for 0... - { - return (-96); // ...so we output max. damping - } - else - { - float val = (20./LOGTEN * log(f)); - return (val); - } -} - -static void freeverb_print(t_freeverb *x) -{ - post("freeverb~:"); - if(x->x_bypass) { - post(" bypass: on"); - } else post(" bypass: off"); - if(!freeverb_getmode(x)) { - post(" mode: normal"); - } else post(" mode: freeze"); - post(" roomsize: %g", freeverb_getroomsize(x)*scaleroom+offsetroom); - post(" damping: %g %%", freeverb_getdamp(x)*100); - post(" width: %g %%", x->x_width * 100); - post(" wet level: %g dB", freeverb_getdb(freeverb_getwet(x)*scalewet)); - post(" dry level: %g dB", freeverb_getdb(freeverb_getdry(x)*scaledry)); -} - - // clean up -static void freeverb_free(t_freeverb *x) -{ - int i; -#ifndef PD - dsp_free((t_pxobject *)x); // Free the object -#endif - // free memory used by delay lines - for(i = 0; i < numcombs; i++) - { - t_freebytes(x->x_bufcombL[i], x->x_combtuningL[i]*sizeof(t_float)); - t_freebytes(x->x_bufcombR[i], x->x_combtuningR[i]*sizeof(t_float)); - } - - for(i = 0; i < numallpasses; i++) - { - t_freebytes(x->x_bufallpassL[i], x->x_allpasstuningL[i]*sizeof(t_float)); - t_freebytes(x->x_bufallpassR[i], x->x_allpasstuningR[i]*sizeof(t_float)); - } -} - -void *freeverb_new(t_floatarg f) -{ - int i; - int sr = (int)sys_getsr(); - -#ifdef PD - t_freeverb *x = (t_freeverb *)pd_new(freeverb_class); - - // add additional signal inlets and signal outlets - inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); - - outlet_new(&x->x_obj, gensym("signal")); - outlet_new(&x->x_obj, gensym("signal")); -#else // Max/MSP - t_freeverb *x = (t_freeverb *)newobject(freeverb_class); - - // zero out the struct, to be careful - if(x) - { - for(i = sizeof(t_pxobject); i < sizeof(t_freeverb); i++) - ((char*)x)[i] = 0; - } - - dsp_setup((t_pxobject *)x,2); // two signal inlets - - // two signal outlets - outlet_new((t_object *)x, "signal"); - outlet_new((t_object *)x, "signal"); -#endif - // recalculate the reverb parameters in case we don't run at 44.1kHz - for(i = 0; i < numcombs; i++) - { - x->x_combtuningL[i] = (int)(combtuningL[i] * sr / 44100); - x->x_combtuningR[i] = (int)(combtuningR[i] * sr / 44100); - } - for(i = 0; i < numallpasses; i++) - { - x->x_allpasstuningL[i] = (int)(allpasstuningL[i] * sr / 44100); - x->x_allpasstuningR[i] = (int)(allpasstuningL[i] * sr / 44100); - } - - // get memory for delay lines - for(i = 0; i < numcombs; i++) - { - x->x_bufcombL[i] = (t_float*) t_getbytes(x->x_combtuningL[i]*sizeof(t_float)); - x->x_bufcombR[i] = (t_float*) t_getbytes(x->x_combtuningR[i]*sizeof(t_float)); - x->x_combidxL[i] = 0; - x->x_combidxR[i] = 0; - } - for(i = 0; i < numallpasses; i++) - { - x->x_bufallpassL[i] = (t_float*) t_getbytes(x->x_allpasstuningL[i]*sizeof(t_float)); - x->x_bufallpassR[i] = (t_float*) t_getbytes(x->x_allpasstuningR[i]*sizeof(t_float)); - x->x_allpassidxL[i] = 0; - x->x_allpassidxR[i] = 0; - } - - // set default values - x->x_allpassfeedback = 0.5; - x->x_skip = 1; // we use every sample - freeverb_setwet(x, initialwet); - freeverb_setroomsize(x, initialroom); - freeverb_setdry(x, initialdry); - freeverb_setdamp(x, initialdamp); - freeverb_setwidth(x, initialwidth); - freeverb_setmode(x, initialmode); - freeverb_setbypass(x, initialbypass); - - // buffers will be full of rubbish - so we MUST mute them - freeverb_mute(x); - - return (x); -} - -#ifdef PD -extern "C" void freeverb_tilde_setup(void) -{ - freeverb_class = class_new(gensym("freeverb~"), (t_newmethod)freeverb_new, (t_method)freeverb_free, - sizeof(t_freeverb), 0, A_DEFFLOAT, 0); - CLASS_MAINSIGNALIN(freeverb_class, t_freeverb, x_float); - class_addmethod(freeverb_class, (t_method)freeverb_dsp, gensym("dsp"), A_NULL); - class_addmethod(freeverb_class, (t_method)freeverb_setroomsize, gensym("roomsize"), A_FLOAT, A_NULL); - class_addmethod(freeverb_class, (t_method)freeverb_setdamp, gensym("damping"), A_FLOAT, A_NULL); - class_addmethod(freeverb_class, (t_method)freeverb_setwidth, gensym("width"), A_FLOAT, A_NULL); - class_addmethod(freeverb_class, (t_method)freeverb_setwet, gensym("wet"), A_FLOAT, A_NULL); - class_addmethod(freeverb_class, (t_method)freeverb_setdry, gensym("dry"), A_FLOAT, A_NULL); - class_addmethod(freeverb_class, (t_method)freeverb_setmode, gensym("freeze"), A_FLOAT, A_NULL); - class_addmethod(freeverb_class, (t_method)freeverb_setbypass, gensym("bypass"), A_FLOAT, A_NULL); - class_addmethod(freeverb_class, (t_method)freeverb_mute, gensym("clear"), A_NULL); - class_addmethod(freeverb_class, (t_method)freeverb_print, gensym("print"), A_NULL); - class_sethelpsymbol(freeverb_class, gensym("help-freeverb~.pd")); - post(version); -} - -#else -// ----------- Max/MSP ----------- -void freeverb_assist(t_freeverb *x, void *b, long m, long a, char *s) -{ - switch(m) { - case 1: // inlet - switch(a) { - case 0: - sprintf(s, "(signal/message) Left Input & Control Messages"); - break; - case 1: - sprintf(s, "(signal) Right Input"); - break; - } - break; - case 2: // outlet - switch(a) { - case 0: - sprintf(s, "(signal) Left Output"); - break; - case 1: - sprintf(s, "(signal) Right Output"); - break; - } - break; - } - -} - -extern "C" void main(void) -{ - setup((t_messlist **)&freeverb_class,(method)freeverb_new, (method)freeverb_free, - (short)sizeof(t_freeverb), 0L, A_DEFFLOAT, 0); - addmess((method)freeverb_dsp, "dsp", A_CANT, 0); - addmess((method)freeverb_assist, "assist", A_CANT, 0); - addmess((method)freeverb_setroomsize, "roomsize", A_FLOAT, 0); - addmess((method)freeverb_setdamp, "damping", A_FLOAT, 0); - addmess((method)freeverb_setwidth, "width", A_FLOAT, 0); - addmess((method)freeverb_setwet, "wet", A_FLOAT, 0); - addmess((method)freeverb_setdry, "dry", A_FLOAT, 0); - addmess((method)freeverb_setmode, "freeze", A_FLOAT, 0); - addmess((method)freeverb_setbypass, "bypass", A_FLOAT, 0); - addmess((method)freeverb_mute, "clear", 0); - addmess((method)freeverb_print, "print", 0); - dsp_initclass(); - finder_addclass("MSP Delays","freeverb~"); - post(version); -} -#endif \ No newline at end of file -- cgit v1.2.1