[SCM] calf/master: Merge branch 'master' into saturators

js at users.alioth.debian.org js at users.alioth.debian.org
Tue May 7 15:40:09 UTC 2013


Speed up compressor-based plugins.
More cleanups.
More cleanups.
Clean up the JACK host.
More simplification of the internal APIs.
Remove excessive pseudo-cleverness messing code up.
Un-inline most of biquad_filter_module implementation.
More cleanup work.
Remove old (outdated) amp plugin example.
More cleanups.
More de-inlinization and refactoring.
More de-inlining.
Move large methods of Calf Organ-related classes into .cpp file.
Remove unnecessary checks for TempSendSetter.
Implement marshalling of waveform and filter response graphs.
Comment out the clearlooks engine setting that conflicts with Ardour.
Add two-way string port support.
More work on the External UI. Add one-way string port support.
More work on external UI. Should be somewhat functional now.
Re-enable GTK+ GUI.
Consume all messages in osc_server::read_from_socket()
More external GUI related changes.
More work on external UI.
Add GNOME category and version number to the calf.desktop file.
Add pixmap_path to the gtkrc. Move install actions from src/ to gui/
Remove gtk_rc_parse.
More small plugin zombie carnage.
Remove more zombie small plugin code.
Simplify Multiband Compressor code and fix several bugs.
Fix multiple assertions when closing main window.
Remove zombie small plugin code.
Remove small plugins together with all the supporting infrastructure.
Add Doxygen documentation for plugin_registry class.
More refactoring.
General refactoring. Start implementing external GUI.
+ LV2: add external UI header (not used for anything yet)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
X-Git-Refname: refs/heads/master
X-Git-Reftype: branch
X-Git-Oldrev: d6a3c1a63a147948535a50da009275278ad5e6a3
X-Git-Newrev: 34569260c452f0fa4c543155ebba174a42a343dc

The following commit has been merged in the master branch:
commit 973428663d484e92e6e0d30af56940f8e5b783f4
Merge: b884d4da9d3bebdeeee91fc0afbafb8f5ca055e0 8ad44088e31dd32d8e9ca9a8aa974017a9f67eff
Author: Krzysztof <wdev at foltman.com>
Date:   Tue May 4 23:41:36 2010 +0100

    Merge branch 'master' into saturators

diff --combined src/calf/modulelist.h
index 0fc6a9c,dabd2c3..76cb551
--- a/src/calf/modulelist.h
+++ b/src/calf/modulelist.h
@@@ -17,89 -17,9 +17,12 @@@
      PER_MODULE_ITEM(equalizer5band, false, "eq5")
      PER_MODULE_ITEM(equalizer8band, false, "eq8")
      PER_MODULE_ITEM(equalizer12band, false, "eq12")
 +    PER_MODULE_ITEM(saturator, false, "saturator")
 +    PER_MODULE_ITEM(exciter, false, "exciter")
 +    PER_MODULE_ITEM(bassenhancer, false, "bassenhancer")
  #ifdef ENABLE_EXPERIMENTAL
      PER_MODULE_ITEM(fluidsynth, true, "fluidsynth")
      PER_MODULE_ITEM(wavetable, true, "wavetable")
  #endif
  #undef PER_MODULE_ITEM
  #endif
- #ifdef PER_SMALL_MODULE_ITEM
- #ifdef ENABLE_EXPERIMENTAL
-     PER_SMALL_MODULE_ITEM(lp_filter, "lowpass12")
-     PER_SMALL_MODULE_ITEM(hp_filter, "highpass12")
-     PER_SMALL_MODULE_ITEM(bp_filter, "bandpass6")
-     PER_SMALL_MODULE_ITEM(br_filter, "notch6")
-     PER_SMALL_MODULE_ITEM(onepole_lp_filter, "lowpass6")
-     PER_SMALL_MODULE_ITEM(onepole_hp_filter, "highpass6")
-     PER_SMALL_MODULE_ITEM(onepole_ap_filter, "allpass")
-     PER_SMALL_MODULE_ITEM(min, "min")
-     PER_SMALL_MODULE_ITEM(max, "max")
-     PER_SMALL_MODULE_ITEM(minus, "minus")
-     PER_SMALL_MODULE_ITEM(mul, "mul")
-     PER_SMALL_MODULE_ITEM(neg, "neg")
-     PER_SMALL_MODULE_ITEM(min_c, "min_c")
-     PER_SMALL_MODULE_ITEM(max_c, "max_c")
-     PER_SMALL_MODULE_ITEM(minus_c, "minus_c")
-     PER_SMALL_MODULE_ITEM(mul_c, "mul_c")
-     PER_SMALL_MODULE_ITEM(neg_c, "neg_c")
-     PER_SMALL_MODULE_ITEM(level2edge_c, "level2edge_c")
-     PER_SMALL_MODULE_ITEM(map_lin2exp, "lin2exp")
-     PER_SMALL_MODULE_ITEM(square_osc, "square_osc")
-     PER_SMALL_MODULE_ITEM(saw_osc, "saw_osc")
-     PER_SMALL_MODULE_ITEM(square_lfo, "square_lfo")
-     PER_SMALL_MODULE_ITEM(saw_lfo, "saw_lfo")
-     PER_SMALL_MODULE_ITEM(pulse_lfo, "pulse_lfo")
-     PER_SMALL_MODULE_ITEM(print_a, "print_a")
-     PER_SMALL_MODULE_ITEM(print_c, "print_c")
-     PER_SMALL_MODULE_ITEM(print_e, "print_e")
-     PER_SMALL_MODULE_ITEM(print_em, "print_em")
-     PER_SMALL_MODULE_ITEM(copy_em, "copy_em")
-     PER_SMALL_MODULE_ITEM(notefilter_m, "notefilter_m")
-     PER_SMALL_MODULE_ITEM(ccfilter_m, "ccfilter_m")
-     PER_SMALL_MODULE_ITEM(pcfilter_m, "pcfilter_m")
-     PER_SMALL_MODULE_ITEM(pressurefilter_m, "pressurefilter_m")
-     PER_SMALL_MODULE_ITEM(pitchbendfilter_m, "pitchbendfilter_m")
-     PER_SMALL_MODULE_ITEM(systemfilter_m, "systemfilter_m")
-     PER_SMALL_MODULE_ITEM(channelfilter_m, "channelfilter_m")
-     PER_SMALL_MODULE_ITEM(keyfilter_m, "keyfilter_m")
-     PER_SMALL_MODULE_ITEM(setchannel_m, "setchannel_m")
-     PER_SMALL_MODULE_ITEM(key_less_than_m, "key_less_than_m")
-     PER_SMALL_MODULE_ITEM(channel_less_than_m, "channel_less_than_m")
-     PER_SMALL_MODULE_ITEM(transpose_m, "transpose_m")
-     PER_SMALL_MODULE_ITEM(eventmerge_e, "eventmerge_e")
-     PER_SMALL_MODULE_ITEM(quadpower_a, "quadpower_a")
-     PER_SMALL_MODULE_ITEM(quadpower_c, "quadpower_c")
-     PER_SMALL_MODULE_ITEM(crossfader2_a, "crossfader2_a")
-     PER_SMALL_MODULE_ITEM(crossfader2_c, "crossfader2_c")
-     PER_SMALL_MODULE_ITEM(linear_inertia_c, "linear_inertia_c")
-     PER_SMALL_MODULE_ITEM(exp_inertia_c, "exp_inertia_c")
-     PER_SMALL_MODULE_ITEM(sample_hold_edge_c, "sample_hold_edge_c")
-     PER_SMALL_MODULE_ITEM(sample_hold_level_c, "sample_hold_level_c")
-     PER_SMALL_MODULE_ITEM(bit_and_c, "bit_and_c")
-     PER_SMALL_MODULE_ITEM(bit_or_c, "bit_or_c")
-     PER_SMALL_MODULE_ITEM(bit_xor_c, "bit_xor_c")
-     PER_SMALL_MODULE_ITEM(logical_and_c, "logical_and_c")
-     PER_SMALL_MODULE_ITEM(logical_or_c, "logical_or_c")
-     PER_SMALL_MODULE_ITEM(logical_xor_c, "logical_xor_c")
-     PER_SMALL_MODULE_ITEM(logical_not_c, "logical_not_c")
-     PER_SMALL_MODULE_ITEM(flipflop_c, "flipflop_c")
-     PER_SMALL_MODULE_ITEM(schmitt_c, "schmitt_c")
-     PER_SMALL_MODULE_ITEM(between_c, "between_c")
-     PER_SMALL_MODULE_ITEM(less_c, "less_c")
-     PER_SMALL_MODULE_ITEM(clip_c, "clip_c")
-     PER_SMALL_MODULE_ITEM(trigger_a2c, "trigger_a2c")
-     PER_SMALL_MODULE_ITEM(timer_c, "timer_c")
-     PER_SMALL_MODULE_ITEM(prio_mux_c, "prio_mux_c")
-     PER_SMALL_MODULE_ITEM(prio_enc8_c, "prio_enc8_c")
-     PER_SMALL_MODULE_ITEM(ifthenelse_c, "ifthenelse_c")
-     PER_SMALL_MODULE_ITEM(counter_c, "counter_c")
-     PER_SMALL_MODULE_ITEM(mux4_c, "mux4_c")
-     PER_SMALL_MODULE_ITEM(mux8_c, "mux8_c")
-     PER_SMALL_MODULE_ITEM(mux16_c, "mux16_c")
-     PER_SMALL_MODULE_ITEM(msgread_e, "msgread_e")
- #endif
- #undef PER_SMALL_MODULE_ITEM
- #endif
diff --combined src/calf/modules.h
index eec4125,f35ae23..3e4d256
--- a/src/calf/modules.h
+++ b/src/calf/modules.h
@@@ -38,44 -38,17 +38,17 @@@ using namespace dsp
  
  struct ladspa_plugin_info;
  
  class frequency_response_line_graph: public line_graph_iface 
  {
  public:
-     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
-     virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
+     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
+     virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
  };
  
  class flanger_audio_module: public audio_module<flanger_metadata>, public frequency_response_line_graph
  {
  public:
      dsp::simple_flanger<float, 2048> left, right;
-     float *ins[in_count]; 
-     float *outs[out_count];
-     float *params[param_count];
      uint32_t srate;
      bool clear_reset;
      float last_r_phase;
@@@ -85,40 -58,8 +58,8 @@@ public
          is_active = false;
      }
      void set_sample_rate(uint32_t sr);
-     void params_changed() {
-         float dry = *params[par_dryamount];
-         float wet = *params[par_amount];
-         float rate = *params[par_rate]; // 0.01*pow(1000.0f,*params[par_rate]);
-         float min_delay = *params[par_delay] / 1000.0;
-         float mod_depth = *params[par_depth] / 1000.0;
-         float fb = *params[par_fb];
-         left.set_dry(dry); right.set_dry(dry);
-         left.set_wet(wet); right.set_wet(wet);
-         left.set_rate(rate); right.set_rate(rate);
-         left.set_min_delay(min_delay); right.set_min_delay(min_delay);
-         left.set_mod_depth(mod_depth); right.set_mod_depth(mod_depth);
-         left.set_fb(fb); right.set_fb(fb);
-         float r_phase = *params[par_stereo] * (1.f / 360.f);
-         clear_reset = false;
-         if (*params[par_reset] >= 0.5) {
-             clear_reset = true;
-             left.reset_phase(0.f);
-             right.reset_phase(r_phase);
-         } else {
-             if (fabs(r_phase - last_r_phase) > 0.0001f) {
-                 right.phase = left.phase;
-                 right.inc_phase(r_phase);
-                 last_r_phase = r_phase;
-             }
-         }
-     }
-     void params_reset()
-     {
-         if (clear_reset) {
-             *params[par_reset] = 0.f;
-             clear_reset = false;
-         }
-     }
+     void params_changed();
+     void params_reset();
      void activate();
      void deactivate();
      uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
@@@ -126,61 -67,24 +67,24 @@@
          right.process(outs[1] + offset, ins[1] + offset, nsamples);
          return outputs_mask; // XXXKF allow some delay after input going blank
      }
-     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-     float freq_gain(int subindex, float freq, float srate);
+     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
+     float freq_gain(int subindex, float freq, float srate) const;
  };
  
  class phaser_audio_module: public audio_module<phaser_metadata>, public frequency_response_line_graph
  {
  public:
-     float *ins[in_count]; 
-     float *outs[out_count];
-     float *params[param_count];
+     enum { MaxStages = 12 };
      uint32_t srate;
      bool clear_reset;
      float last_r_phase;
-     dsp::simple_phaser<12> left, right;
+     dsp::simple_phaser left, right;
+     float x1vals[2][MaxStages], y1vals[2][MaxStages];
      bool is_active;
  public:
-     phaser_audio_module() {
-         is_active = false;
-     }
-     void params_changed() {
-         float dry = *params[par_dryamount];
-         float wet = *params[par_amount];
-         float rate = *params[par_rate]; // 0.01*pow(1000.0f,*params[par_rate]);
-         float base_frq = *params[par_freq];
-         float mod_depth = *params[par_depth];
-         float fb = *params[par_fb];
-         int stages = (int)*params[par_stages];
-         left.set_dry(dry); right.set_dry(dry);
-         left.set_wet(wet); right.set_wet(wet);
-         left.set_rate(rate); right.set_rate(rate);
-         left.set_base_frq(base_frq); right.set_base_frq(base_frq);
-         left.set_mod_depth(mod_depth); right.set_mod_depth(mod_depth);
-         left.set_fb(fb); right.set_fb(fb);
-         left.set_stages(stages); right.set_stages(stages);
-         float r_phase = *params[par_stereo] * (1.f / 360.f);
-         clear_reset = false;
-         if (*params[par_reset] >= 0.5) {
-             clear_reset = true;
-             left.reset_phase(0.f);
-             right.reset_phase(r_phase);
-         } else {
-             if (fabs(r_phase - last_r_phase) > 0.0001f) {
-                 right.phase = left.phase;
-                 right.inc_phase(r_phase);
-                 last_r_phase = r_phase;
-             }
-         }
-     }
-     void params_reset()
-     {
-         if (clear_reset) {
-             *params[par_reset] = 0.f;
-             clear_reset = false;
-         }
-     }
+     phaser_audio_module();
+     void params_changed();
+     void params_reset();
      void activate();
      void set_sample_rate(uint32_t sr);
      void deactivate();
@@@ -189,9 -93,9 +93,9 @@@
          right.process(outs[1] + offset, ins[1] + offset, nsamples);
          return outputs_mask; // XXXKF allow some delay after input going blank
      }
-     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
-     float freq_gain(int subindex, float freq, float srate);
+     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
+     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
+     float freq_gain(int subindex, float freq, float srate) const;
  };
  
  class reverb_audio_module: public audio_module<reverb_metadata>
@@@ -205,61 -109,9 +109,9 @@@ public
      int predelay_amt;
      float meter_wet, meter_out;
      uint32_t clip;
-     float *ins[in_count]; 
-     float *outs[out_count];
-     float *params[param_count];
      
-     void params_changed() {
-         //reverb.set_time(0.5*pow(8.0f, *params[par_decay]));
-         //reverb.set_cutoff(2000*pow(10.0f, *params[par_hfdamp]));
-         reverb.set_type_and_diffusion(fastf2i_drm(*params[par_roomsize]), *params[par_diffusion]);
-         reverb.set_time(*params[par_decay]);
-         reverb.set_cutoff(*params[par_hfdamp]);
-         amount.set_inertia(*params[par_amount]);
-         dryamount.set_inertia(*params[par_dry]);
-         left_lo.set_lp(dsp::clip(*params[par_treblecut], 20.f, (float)(srate * 0.49f)), srate);
-         left_hi.set_hp(dsp::clip(*params[par_basscut], 20.f, (float)(srate * 0.49f)), srate);
-         right_lo.copy_coeffs(left_lo);
-         right_hi.copy_coeffs(left_hi);
-         predelay_amt = (int) (srate * (*params[par_predelay]) * (1.0f / 1000.0f) + 1);
-     }
-     uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
-         numsamples += offset;
-         clip   -= std::min(clip, numsamples);
-         for (uint32_t i = offset; i < numsamples; i++) {
-             float dry = dryamount.get();
-             float wet = amount.get();
-             stereo_sample<float> s(ins[0][i], ins[1][i]);
-             stereo_sample<float> s2 = pre_delay.process(s, predelay_amt);
-             
-             float rl = s2.left, rr = s2.right;
-             rl = left_lo.process(left_hi.process(rl));
-             rr = right_lo.process(right_hi.process(rr));
-             reverb.process(rl, rr);
-             outs[0][i] = dry*s.left + wet*rl;
-             outs[1][i] = dry*s.right + wet*rr;
-             meter_wet = std::max(fabs(wet*rl), fabs(wet*rr));
-             meter_out = std::max(fabs(outs[0][i]), fabs(outs[1][i]));
-             if(outs[0][i] > 1.f or outs[1][i] > 1.f) {
-                 clip = srate >> 3;
-             }
-         }
-         reverb.extra_sanitize();
-         left_lo.sanitize();
-         left_hi.sanitize();
-         right_lo.sanitize();
-         right_hi.sanitize();
-         if(params[par_meter_wet] != NULL) {
-             *params[par_meter_wet] = meter_wet;
-         }
-         if(params[par_meter_out] != NULL) {
-             *params[par_meter_out] = meter_out;
-         }
-         if(params[par_clip] != NULL) {
-             *params[par_clip] = clip;
-         }
-         return outputs_mask;
-     }
+     void params_changed();
+     uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
      void activate();
      void set_sample_rate(uint32_t sr);
      void deactivate();
@@@ -270,9 -122,6 +122,6 @@@ class vintage_delay_audio_module: publi
  public:    
      // 1MB of delay memory per channel... uh, RAM is cheap
      enum { MAX_DELAY = 262144, ADDR_MASK = MAX_DELAY - 1 };
-     float *ins[in_count]; 
-     float *outs[out_count];
-     float *params[param_count];
      float buffers[2][MAX_DELAY];
      int bufptr, deltime_l, deltime_r, mixmode, medium, old_medium;
      /// number of table entries written (value is only important when it is less than MAX_DELAY, which means that the buffer hasn't been totally filled yet)
@@@ -285,132 -134,19 +134,19 @@@
      
      uint32_t srate;
      
-     vintage_delay_audio_module()
-     {
-         old_medium = -1;
-         for (int i = 0; i < MAX_DELAY; i++) {
-             buffers[0][i] = 0.f;
-             buffers[1][i] = 0.f;
-         }
-     }
+     vintage_delay_audio_module();
      
-     void params_changed()
-     {
-         float unit = 60.0 * srate / (*params[par_bpm] * *params[par_divide]);
-         deltime_l = dsp::fastf2i_drm(unit * *params[par_time_l]);
-         deltime_r = dsp::fastf2i_drm(unit * *params[par_time_r]);
-         amt_left.set_inertia(*params[par_amount]); amt_right.set_inertia(*params[par_amount]);
-         float fb = *params[par_feedback];
-         dry = *params[par_dryamount];
-         mixmode = dsp::fastf2i_drm(*params[par_mixmode]);
-         medium = dsp::fastf2i_drm(*params[par_medium]);
-         if (mixmode == 0)
-         {
-             fb_left.set_inertia(fb);
-             fb_right.set_inertia(pow(fb, *params[par_time_r] / *params[par_time_l]));
-         } else {
-             fb_left.set_inertia(fb);
-             fb_right.set_inertia(fb);
-         }
-         if (medium != old_medium)
-             calc_filters();
-     }
-     void activate() {
-         bufptr = 0;
-         age = 0;
-     }
-     void deactivate() {
-     }
-     void set_sample_rate(uint32_t sr) {
-         srate = sr;
-         old_medium = -1;
-         amt_left.set_sample_rate(sr); amt_right.set_sample_rate(sr);
-         fb_left.set_sample_rate(sr); fb_right.set_sample_rate(sr);
-     }
-     void calc_filters()
-     {
-         // parameters are heavily influenced by gordonjcp and his tape delay unit
-         // although, don't blame him if it sounds bad - I've messed with them too :)
-         biquad_left[0].set_lp_rbj(6000, 0.707, srate);
-         biquad_left[1].set_bp_rbj(4500, 0.250, srate);
-         biquad_right[0].copy_coeffs(biquad_left[0]);
-         biquad_right[1].copy_coeffs(biquad_left[1]);
-     }
-     uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
-         uint32_t ostate = 3; // XXXKF optimize!
-         uint32_t end = offset + numsamples;
-         int v = mixmode ? 1 : 0;
-         int orig_bufptr = bufptr;
-         for(uint32_t i = offset; i < end; i++)
-         {
-             float out_left, out_right, del_left, del_right;
-             // if the buffer hasn't been cleared yet (after activation), pretend we've read zeros
- 
-             if (deltime_l >= age) {
-                 del_left = ins[0][i];
-                 out_left = dry * del_left;
-                 amt_left.step();
-                 fb_left.step();
-             }
-             else
-             {
-                 float in_left = buffers[v][(bufptr - deltime_l) & ADDR_MASK];
-                 dsp::sanitize(in_left);
-                 out_left = dry * ins[0][i] + in_left * amt_left.get();
-                 del_left = ins[0][i] + in_left * fb_left.get();
-             }
-             if (deltime_r >= age) {
-                 del_right = ins[1][i];
-                 out_right = dry * del_right;
-                 amt_right.step();
-                 fb_right.step();
-             }
-             else
-             {
-                 float in_right = buffers[1 - v][(bufptr - deltime_r) & ADDR_MASK];
-                 dsp::sanitize(in_right);
-                 out_right = dry * ins[1][i] + in_right * amt_right.get();
-                 del_right = ins[1][i] + in_right * fb_right.get();
-             }
-             
-             age++;
-             outs[0][i] = out_left; outs[1][i] = out_right; buffers[0][bufptr] = del_left; buffers[1][bufptr] = del_right;
-             bufptr = (bufptr + 1) & (MAX_DELAY - 1);
-         }
-         if (age >= MAX_DELAY)
-             age = MAX_DELAY;
-         if (medium > 0) {
-             bufptr = orig_bufptr;
-             if (medium == 2)
-             {
-                 for(uint32_t i = offset; i < end; i++)
-                 {
-                     buffers[0][bufptr] = biquad_left[0].process_lp(biquad_left[1].process(buffers[0][bufptr]));
-                     buffers[1][bufptr] = biquad_right[0].process_lp(biquad_right[1].process(buffers[1][bufptr]));
-                     bufptr = (bufptr + 1) & (MAX_DELAY - 1);
-                 }
-                 biquad_left[0].sanitize();biquad_right[0].sanitize();
-             } else {
-                 for(uint32_t i = offset; i < end; i++)
-                 {
-                     buffers[0][bufptr] = biquad_left[1].process(buffers[0][bufptr]);
-                     buffers[1][bufptr] = biquad_right[1].process(buffers[1][bufptr]);
-                     bufptr = (bufptr + 1) & (MAX_DELAY - 1);
-                 }
-             }
-             biquad_left[1].sanitize();biquad_right[1].sanitize();
-             
-         }
-         return ostate;
-     }
+     void params_changed();
+     void activate();
+     void deactivate();
+     void set_sample_rate(uint32_t sr);
+     void calc_filters();
+     uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
  };
  
  class rotary_speaker_audio_module: public audio_module<rotary_speaker_metadata>
  {
  public:
-     float *ins[in_count]; 
-     float *outs[out_count];
-     float *params[param_count];
      /// Current phases and phase deltas for bass and treble rotors
      uint32_t phase_l, dphase_l, phase_h, dphase_h;
      dsp::simple_delay<1024, float> delay;
@@@ -441,220 -177,35 +177,35 @@@
      void activate();
      void deactivate();
      
-     void params_changed() {
-         set_vibrato();
-     }
-     void set_vibrato()
-     {
-         vibrato_mode = fastf2i_drm(*params[par_speed]);
-         // manual vibrato - do not recalculate speeds as they're not used anyway
-         if (vibrato_mode == 5) 
-             return;
-         if (!vibrato_mode)
-             dspeed = -1;
-         else {
-             float speed = vibrato_mode - 1;
-             if (vibrato_mode == 3)
-                 speed = hold_value;
-             if (vibrato_mode == 4)
-                 speed = mwhl_value;
-             dspeed = (speed < 0.5f) ? 0 : 1;
-         }
-         update_speed();
-     }
+     void params_changed();
+     void set_vibrato();
      /// Convert RPM speed to delta-phase
-     inline uint32_t rpm2dphase(float rpm)
-     {
-         return (uint32_t)((rpm / (60.0 * srate)) * (1 << 30)) << 2;
-     }
+     uint32_t rpm2dphase(float rpm);
      /// Set delta-phase variables based on current calculated (and interpolated) RPM speed
-     void update_speed()
-     {
-         float speed_h = aspeed_h >= 0 ? (48 + (400-48) * aspeed_h) : (48 * (1 + aspeed_h));
-         float speed_l = aspeed_l >= 0 ? 40 + (342-40) * aspeed_l : (40 * (1 + aspeed_l));
-         dphase_h = rpm2dphase(speed_h);
-         dphase_l = rpm2dphase(speed_l);
-     }
-     void update_speed_manual(float delta)
-     {
-         float ts = *params[par_treblespeed];
-         float bs = *params[par_bassspeed];
-         incr_towards(maspeed_h, ts, delta * 200, delta * 200);
-         incr_towards(maspeed_l, bs, delta * 200, delta * 200);
-         dphase_h = rpm2dphase(maspeed_h);
-         dphase_l = rpm2dphase(maspeed_l);
-     }
-     /// map a ramp [int] to a sinusoid-like function [0, 65536]
-     static inline int pseudo_sine_scl(int counter)
-     {
-         // premature optimization is a root of all evil; it can be done with integers only - but later :)
-         double v = counter * (1.0 / (65536.0 * 32768.0));
-         return (int) (32768 + 32768 * (v - v*v*v) * (1.0 / 0.3849));
-     }
+     void update_speed();
+     void update_speed_manual(float delta);
      /// Increase or decrease aspeed towards raspeed, with required negative and positive rate
-     inline bool incr_towards(float &aspeed, float raspeed, float delta_decc, float delta_acc)
-     {
-         if (aspeed < raspeed) {
-             aspeed = std::min(raspeed, aspeed + delta_acc);
-             return true;
-         }
-         else if (aspeed > raspeed) 
-         {
-             aspeed = std::max(raspeed, aspeed - delta_decc);
-             return true;
-         }        
-         return false;
-     }
-     uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask)
-     {
-         int shift = (int)(300000 * (*params[par_shift])), pdelta = (int)(300000 * (*params[par_spacing]));
-         int md = (int)(100 * (*params[par_moddepth]));
-         float mix = 0.5 * (1.0 - *params[par_micdistance]);
-         float mix2 = *params[par_reflection];
-         float mix3 = mix2 * mix2;
-         for (unsigned int i = 0; i < nsamples; i++) {
-             float in_l = ins[0][i + offset], in_r = ins[1][i + offset];
-             float in_mono = 0.5f * (in_l + in_r);
-             
-             int xl = pseudo_sine_scl(phase_l), yl = pseudo_sine_scl(phase_l + 0x40000000);
-             int xh = pseudo_sine_scl(phase_h), yh = pseudo_sine_scl(phase_h + 0x40000000);
-             // printf("%d %d %d\n", shift, pdelta, shift + pdelta + 20 * xl);
-             meter_l = xl;
-             meter_h = xh;
-             // float out_hi_l = in_mono - delay.get_interp_1616(shift + md * xh) + delay.get_interp_1616(shift + md * 65536 + pdelta - md * yh) - delay.get_interp_1616(shift + md * 65536 + pdelta + pdelta - md * xh);
-             // float out_hi_r = in_mono + delay.get_interp_1616(shift + md * 65536 - md * yh) - delay.get_interp_1616(shift + pdelta + md * xh) + delay.get_interp_1616(shift + pdelta + pdelta + md * yh);
-             float out_hi_l = in_mono + delay.get_interp_1616(shift + md * xh) - mix2 * delay.get_interp_1616(shift + md * 65536 + pdelta - md * yh) + mix3 * delay.get_interp_1616(shift + md * 65536 + pdelta + pdelta - md * xh);
-             float out_hi_r = in_mono + delay.get_interp_1616(shift + md * 65536 - md * yh) - mix2 * delay.get_interp_1616(shift + pdelta + md * xh) + mix3 * delay.get_interp_1616(shift + pdelta + pdelta + md * yh);
- 
-             float out_lo_l = in_mono + delay.get_interp_1616(shift + md * xl); // + delay.get_interp_1616(shift + md * 65536 + pdelta - md * yl);
-             float out_lo_r = in_mono + delay.get_interp_1616(shift + md * yl); // - delay.get_interp_1616(shift + pdelta + md * yl);
-             
-             out_hi_l = crossover2l.process(out_hi_l); // sanitize(out_hi_l);
-             out_hi_r = crossover2r.process(out_hi_r); // sanitize(out_hi_r);
-             out_lo_l = crossover1l.process(out_lo_l); // sanitize(out_lo_l);
-             out_lo_r = crossover1r.process(out_lo_r); // sanitize(out_lo_r);
-             
-             float out_l = out_hi_l + out_lo_l;
-             float out_r = out_hi_r + out_lo_r;
-             
-             float mic_l = out_l + mix * (out_r - out_l);
-             float mic_r = out_r + mix * (out_l - out_r);
-             
-             outs[0][i + offset] = mic_l * 0.5f;
-             outs[1][i + offset] = mic_r * 0.5f;
-             delay.put(in_mono);
-             phase_l += dphase_l;
-             phase_h += dphase_h;
-         }
-         crossover1l.sanitize();
-         crossover1r.sanitize();
-         crossover2l.sanitize();
-         crossover2r.sanitize();
-         float delta = nsamples * 1.0 / srate;
-         if (vibrato_mode == 5)
-             update_speed_manual(delta);
-         else
-         {
-             bool u1 = incr_towards(aspeed_l, dspeed, delta * 0.2, delta * 0.14);
-             bool u2 = incr_towards(aspeed_h, dspeed, delta, delta * 0.5);
-             if (u1 || u2)
-                 set_vibrato();
-         }
-         if(params[par_meter_l] != NULL) {
-             *params[par_meter_l] = (float)meter_l / 65536.0;
-         }
-         if(params[par_meter_h] != NULL) {
-             *params[par_meter_h] = (float)meter_h / 65536.0;
-         }
-         return outputs_mask;
-     }
+     bool incr_towards(float &aspeed, float raspeed, float delta_decc, float delta_acc);
+     uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
      virtual void control_change(int ctl, int val);
  };
  
- /// Compose two filters in series
- template<class F1, class F2>
- class filter_compose {
- public:
-     typedef std::complex<float> cfloat;
-     F1 f1;
-     F2 f2;
- public:
-     float process(float value) {
-         return f2.process(f1.process(value));
-     }
-     
-     cfloat h_z(const cfloat &z) {
-         return f1.h_z(z) * f2.h_z(z);
-     }
-     
-     /// Return the filter's gain at frequency freq
-     /// @param freq   Frequency to look up
-     /// @param sr     Filter sample rate (used to convert frequency to angular frequency)
-     float freq_gain(float freq, float sr)
-     {
-         typedef std::complex<double> cfloat;
-         freq *= 2.0 * M_PI / sr;
-         cfloat z = 1.0 / exp(cfloat(0.0, freq));
-         
-         return std::abs(h_z(z));
-     }
-     
-     void sanitize() {
-         f1.sanitize();
-         f2.sanitize();
-     }
- };
- 
- /// Compose two filters in parallel
- template<class F1, class F2>
- class filter_sum {
- public:
-     typedef std::complex<double> cfloat;
-     F1 f1;
-     F2 f2;
- public:
-     float process(float value) {
-         return f2.process(value) + f1.process(value);
-     }
-     
-     inline cfloat h_z(const cfloat &z) {
-         return f1.h_z(z) + f2.h_z(z);
-     }
-     
-     /// Return the filter's gain at frequency freq
-     /// @param freq   Frequency to look up
-     /// @param sr     Filter sample rate (used to convert frequency to angular frequency)
-     float freq_gain(float freq, float sr)
-     {
-         typedef std::complex<double> cfloat;
-         freq *= 2.0 * M_PI / sr;
-         cfloat z = 1.0 / exp(cfloat(0.0, freq));
-         
-         return std::abs(h_z(z));
-     }
-     
-     void sanitize() {
-         f1.sanitize();
-         f2.sanitize();
-     }
- };
- 
  template<typename FilterClass, typename Metadata>
- class filter_module_with_inertia: public FilterClass
+ class filter_module_with_inertia: public audio_module<Metadata>, public FilterClass
  {
  public:
+     /// These are pointers to the ins, outs, params arrays in the main class
      typedef filter_module_with_inertia inertia_filter_module;
+     using audio_module<Metadata>::ins;
+     using audio_module<Metadata>::outs;
+     using audio_module<Metadata>::params;
      
      inertia<exponential_ramp> inertia_cutoff, inertia_resonance, inertia_gain;
      once_per_n timer;
      bool is_active;    
-     volatile int last_generation, last_calculated_generation;
+     mutable volatile int last_generation, last_calculated_generation;
      
-     filter_module_with_inertia()
+     filter_module_with_inertia(float **ins, float **outs, float **params)
      : inertia_cutoff(exponential_ramp(128), 20)
      , inertia_resonance(exponential_ramp(128), 20)
      , inertia_gain(exponential_ramp(128), 1.0)
@@@ -745,13 -296,13 +296,13 @@@
  
  /// biquad filter module
  class filter_audio_module: 
      public filter_module_with_inertia<biquad_filter_module, filter_metadata>, 
      public frequency_response_line_graph
  {
-     float old_cutoff, old_resonance, old_mode;
+     mutable float old_cutoff, old_resonance, old_mode;
  public:    
      filter_audio_module()
+     : filter_module_with_inertia<biquad_filter_module, filter_metadata>(ins, outs, params)
      {
          last_generation = 0;
      }
@@@ -762,33 -313,14 +313,14 @@@
          inertia_filter_module::params_changed(); 
      }
          
-     void activate()
-     {
-         inertia_filter_module::activate();
-     }
-     
-     void set_sample_rate(uint32_t sr)
-     {
-         inertia_filter_module::set_sample_rate(sr);
-     }
- 
-     
-     void deactivate()
-     {
-         inertia_filter_module::deactivate();
-     }
-     
-     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-     int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
+     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
+     int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
  };
  
  /// A multitap stereo chorus thing - processing
  class multichorus_audio_module: public audio_module<multichorus_metadata>, public frequency_response_line_graph
  {
  public:
-     float *ins[in_count]; 
-     float *outs[out_count];
-     float *params[param_count];
      uint32_t srate;
      dsp::multichorus<float, sine_multi_lfo<float, 8>, filter_sum<dsp::biquad_d2<>, dsp::biquad_d2<> >, 4096> left, right;
      float last_r_phase;
@@@ -796,112 -328,76 +328,76 @@@
      bool is_active;
      
  public:    
-     multichorus_audio_module()
-     {
-         is_active = false;
-         last_r_phase = -1;
-     }
-     
-     void params_changed()
-     {
-         // delicious copy-pasta from flanger module - it'd be better to keep it common or something
-         float dry = *params[par_dryamount];
-         float wet = *params[par_amount];
-         float rate = *params[par_rate];
-         float min_delay = *params[par_delay] / 1000.0;
-         float mod_depth = *params[par_depth] / 1000.0;
-         float overlap = *params[par_overlap];
-         left.set_dry(dry); right.set_dry(dry);
-         left.set_wet(wet); right.set_wet(wet);
-         left.set_rate(rate); right.set_rate(rate);
-         left.set_min_delay(min_delay); right.set_min_delay(min_delay);
-         left.set_mod_depth(mod_depth); right.set_mod_depth(mod_depth);
-         int voices = (int)*params[par_voices];
-         left.lfo.set_voices(voices); right.lfo.set_voices(voices);
-         left.lfo.set_overlap(overlap);right.lfo.set_overlap(overlap);
-         float vphase = *params[par_vphase] * (1.f / 360.f);
-         left.lfo.vphase = right.lfo.vphase = vphase * (4096 / std::max(voices - 1, 1));
-         float r_phase = *params[par_stereo] * (1.f / 360.f);
-         if (fabs(r_phase - last_r_phase) > 0.0001f) {
-             right.lfo.phase = left.lfo.phase;
-             right.lfo.phase += chorus_phase(r_phase * 4096);
-             last_r_phase = r_phase;
-         }
-         left.post.f1.set_bp_rbj(*params[par_freq], *params[par_q], srate);
-         left.post.f2.set_bp_rbj(*params[par_freq2], *params[par_q], srate);
-         right.post.f1.copy_coeffs(left.post.f1);
-         right.post.f2.copy_coeffs(left.post.f2);
-     }
-     uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) {
-         left.process(outs[0] + offset, ins[0] + offset, numsamples);
-         right.process(outs[1] + offset, ins[1] + offset, numsamples);
-         return outputs_mask; // XXXKF allow some delay after input going blank
-     }
+     multichorus_audio_module();
+     void params_changed();
+     uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
      void activate();
      void deactivate();
      void set_sample_rate(uint32_t sr);
-     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-     float freq_gain(int subindex, float freq, float srate);
-     bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context);
-     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
+     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
+     float freq_gain(int subindex, float freq, float srate) const;
+     bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
+     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
  };
  
- class gain_reduction_audio_module {
+ /// Not a true _audio_module style class, just pretends to be one!
+ class gain_reduction_audio_module
+ {
  private:
      float linSlope, detected, kneeSqrt, kneeStart, linKneeStart, kneeStop;
      float compressedKneeStop, adjKneeStart, thres;
      float attack, release, threshold, ratio, knee, makeup, detection, stereo_link, bypass, mute, meter_out, meter_comp;
-     float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_mute, old_detection, old_stereo_link;
-     int last_generation;
+     mutable float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_mute, old_detection, old_stereo_link;
+     mutable volatile int last_generation;
      uint32_t srate;
      bool is_active;
-     inline float output_level(float slope);
-     inline float output_gain(float linSlope, bool rms);
+     inline float output_level(float slope) const;
+     inline float output_gain(float linSlope, bool rms) const;
  public:
      gain_reduction_audio_module();
      void set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float stl, float byp, float mu);
-     void process(float &left, float &right, float det_left = NULL, float det_right = NULL);
+     void update_curve();
+     void process(float &left, float &right, const float *det_left = NULL, const float *det_right = NULL);
      void activate();
      void deactivate();
      int id;
      void set_sample_rate(uint32_t sr);
      float get_output_level();
      float get_comp_level();
-     virtual bool get_graph(int subindex, float *data, int points, cairo_iface *context);
-     virtual bool get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context);
-     virtual bool get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
-     virtual int  get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
+     bool get_graph(int subindex, float *data, int points, cairo_iface *context) const;
+     bool get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context) const;
+     bool get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
+     int  get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
  };
  
  /// Compressor by Thor
  class compressor_audio_module: public audio_module<compressor_metadata>, public line_graph_iface  {
  private:
+     typedef compressor_audio_module AM;
      uint32_t clip_in, clip_out;
      float meter_in, meter_out;
      gain_reduction_audio_module compressor;
  public:
      typedef std::complex<double> cfloat;
-     float *ins[in_count];
-     float *outs[out_count];
-     float *params[param_count];
      uint32_t srate;
      bool is_active;
-     volatile int last_generation, last_calculated_generation;
+     mutable volatile int last_generation, last_calculated_generation;
      compressor_audio_module();
      void activate();
      void deactivate();
      void params_changed();
      void set_sample_rate(uint32_t sr);
      uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
-     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-     bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context);
-     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
-     int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
+     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
+     bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
+     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
+     int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
  };
  
  /// Sidecain Compressor by Markus Schmidt (based on Thor's compressor and Krzysztof's filters)
  class sidechaincompressor_audio_module: public audio_module<sidechaincompressor_metadata>, public frequency_response_line_graph  {
  private:
+     typedef sidechaincompressor_audio_module AM;
      enum CalfScModes {
          WIDEBAND,
          DEESSER_WIDE,
@@@ -914,9 -410,10 +410,10 @@@
          BANDPASS_1,
          BANDPASS_2
      };
-     float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old;
-     float f1_freq_old1, f2_freq_old1, f1_level_old1, f2_level_old1;
-     CalfScModes sc_mode, sc_mode_old, sc_mode_old1;
+     mutable float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old;
+     mutable float f1_freq_old1, f2_freq_old1, f1_level_old1, f2_level_old1;
+     CalfScModes sc_mode;
+     mutable CalfScModes sc_mode_old, sc_mode_old1;
      float f1_active, f2_active;
      uint32_t clip_in, clip_out;
      float meter_in, meter_out;
@@@ -924,59 -421,27 +421,27 @@@
      biquad_d2<float> f1L, f1R, f2L, f2R;
  public:
      typedef std::complex<double> cfloat;
-     float *ins[in_count];
-     float *outs[out_count];
-     float *params[param_count];
      uint32_t srate;
      bool is_active;
-     volatile int last_generation, last_calculated_generation;
+     mutable volatile int last_generation, last_calculated_generation;
      sidechaincompressor_audio_module();
      void activate();
      void deactivate();
      void params_changed();
-     inline cfloat h_z(const cfloat &z) {
-         switch (sc_mode) {
-             default:
-             case WIDEBAND:
-                 return false;
-                 break;
-             case DEESSER_WIDE:
-             case DERUMBLER_WIDE:
-             case WEIGHTED_1:
-             case WEIGHTED_2:
-             case WEIGHTED_3:
-             case BANDPASS_2:
-                 return f1L.h_z(z) * f2L.h_z(z);
-                 break;
-             case DEESSER_SPLIT:
-                 return f2L.h_z(z);
-                 break;
-             case DERUMBLER_SPLIT:
-             case BANDPASS_1:
-                 return f1L.h_z(z);
-                 break;
-         }
-                 
-     }
-     float freq_gain(int index, double freq, uint32_t sr)
-     {
-         typedef std::complex<double> cfloat;
-         freq *= 2.0 * M_PI / sr;
-         cfloat z = 1.0 / exp(cfloat(0.0, freq));
-         
-         return std::abs(h_z(z));
-     }
+     cfloat h_z(const cfloat &z) const;
+     float freq_gain(int index, double freq, uint32_t sr) const;
      void set_sample_rate(uint32_t sr);
      uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
-     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-     bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context);
-     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
-     int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
+     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
+     bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
+     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
+     int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
  };
  
  /// Multibandcompressor by Markus Schmidt
  class multibandcompressor_audio_module: public audio_module<multibandcompressor_metadata>, public line_graph_iface {
  private:
+     typedef multibandcompressor_audio_module AM;
      static const int strips = 4;
      bool mute[strips];
      uint32_t clip_inL, clip_inR, clip_outL, clip_outR;
@@@ -985,9 -450,6 +450,6 @@@
      dsp::biquad_d2<float> lpL0, lpR0, lpL1, lpR1, lpL2, lpR2, hpL0, hpR0, hpL1, hpR1, hpL2, hpR2;
      float freq_old[strips - 1], sep_old[strips - 1], q_old[strips - 1];
  public:
-     float *ins[in_count];
-     float *outs[out_count];
-     float *params[param_count];
      uint32_t srate;
      bool is_active;
      multibandcompressor_audio_module();
@@@ -996,10 -458,11 +458,11 @@@
      void params_changed();
      uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
      void set_sample_rate(uint32_t sr);
-     virtual bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-     virtual bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context);
-     virtual bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
-     virtual int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
+     const gain_reduction_audio_module *get_strip_by_param_index(int index) const;
+     virtual bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
+     virtual bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
+     virtual bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
+     virtual int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
  };
  
  /// Deesser by Markus Schmidt (based on Thor's compressor and Krzysztof's filters)
@@@ -1009,33 -472,30 +472,30 @@@ private
          WIDE,
          SPLIT
      };
-     float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old, f2_q_old;
-     float f1_freq_old1, f2_freq_old1, f1_level_old1, f2_level_old1, f2_q_old1;
+     mutable float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old, f2_q_old;
+     mutable float f1_freq_old1, f2_freq_old1, f1_level_old1, f2_level_old1, f2_q_old1;
      uint32_t detected_led;
      float detected, clip_out;
      uint32_t clip_led;
      gain_reduction_audio_module compressor;
      biquad_d2<float> hpL, hpR, lpL, lpR, pL, pR;
  public:
-     float *ins[in_count];
-     float *outs[out_count];
-     float *params[param_count];
      uint32_t srate;
      bool is_active;
-     volatile int last_generation, last_calculated_generation;
+     mutable volatile int last_generation, last_calculated_generation;
      deesser_audio_module();
      void activate();
      void deactivate();
      void params_changed();
-     float freq_gain(int index, double freq, uint32_t sr)
+     float freq_gain(int index, double freq, uint32_t sr) const
      {
          return hpL.freq_gain(freq, sr) * pL.freq_gain(freq, sr);
      }
      void set_sample_rate(uint32_t sr);
      uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
-     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
-     int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
+     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
+     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
+     int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
  };
  
  /// Equalizer N Band by Markus Schmidt (based on Krzysztof's filters)
@@@ -1043,6 -503,9 +503,9 @@@ template<class BaseClass, bool has_lphp
  class equalizerNband_audio_module: public audio_module<BaseClass>, public frequency_response_line_graph {
  public:
      typedef audio_module<BaseClass> AM;
+     using AM::ins;
+     using AM::outs;
+     using AM::params;
      using AM::in_count;
      using AM::out_count;
      using AM::param_count;
@@@ -1054,7 -517,7 +517,7 @@@ private
      float ls_level_old, ls_freq_old;
      float hs_level_old, hs_freq_old;
      float p_level_old[PeakBands], p_freq_old[PeakBands], p_q_old[PeakBands];
-     float old_params_for_graph[graph_param_count];
+     mutable float old_params_for_graph[graph_param_count];
      uint32_t clip_inL, clip_outL, clip_inR, clip_outR;
      float meter_inL, meter_outL, meter_inR, meter_outR;
      CalfEqMode hp_mode, lp_mode;
@@@ -1065,26 -528,23 +528,23 @@@
      inline void process_hplp(float &left, float &right);
  public:
      typedef std::complex<double> cfloat;
-     float *ins[in_count];
-     float *outs[out_count];
-     float *params[param_count];
      uint32_t srate;
      bool is_active;
-     volatile int last_generation, last_calculated_generation;
+     mutable volatile int last_generation, last_calculated_generation;
      equalizerNband_audio_module();
      void activate();
      void deactivate();
  
      void params_changed();
-     float freq_gain(int index, double freq, uint32_t sr);
+     float freq_gain(int index, double freq, uint32_t sr) const;
      void set_sample_rate(uint32_t sr)
      {
          srate = sr;
      }
      uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
-     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
-     int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
+     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
+     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
+     int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const;
  };
  
  typedef equalizerNband_audio_module<equalizer5band_metadata, false> equalizer5band_audio_module;
@@@ -1106,14 -566,15 +566,15 @@@ public
      void set_phase(float ph);
      void activate();
      void deactivate();
-     float get_value_from_phase(float ph, float off);
-     virtual bool get_graph(float *data, int points, cairo_iface *context);
-     virtual bool get_dot(float &x, float &y, int &size, cairo_iface *context);
+     float get_value_from_phase(float ph, float off) const;
+     virtual bool get_graph(float *data, int points, cairo_iface *context) const;
+     virtual bool get_dot(float &x, float &y, int &size, cairo_iface *context) const;
  };
  
  /// Pulsator by Markus Schmidt
  class pulsator_audio_module: public audio_module<pulsator_metadata>, public frequency_response_line_graph  {
  private:
+     typedef pulsator_audio_module AM;
      uint32_t clip_inL, clip_inR, clip_outL, clip_outR;
      float meter_inL, meter_inR, meter_outL, meter_outR;
      float offset_old;
@@@ -1121,9 -582,6 +582,6 @@@
      bool clear_reset;
      lfo_audio_module lfoL, lfoR;
  public:
-     float *ins[in_count];
-     float *outs[out_count];
-     float *params[param_count];
      uint32_t srate;
      bool is_active;
      pulsator_audio_module();
@@@ -1139,126 -597,20 +597,129 @@@
          }
      }
      uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
-     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-     bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context);
-     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
+     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
+     bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const;
+     bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const;
  };
  
 +class distortion_audio_module {
 +private:
 +    float blend_old, drive_old;
 +    float meter;
 +    float rdrive, rbdr, kpa, kpb, kna, knb, ap, an, imr, kc, srct, sq, pwrq;
 +    float prev_med, prev_out;
 +public:
 +    uint32_t srate;
 +    bool is_active;
 +    distortion_audio_module();
 +    void activate();
 +    void deactivate();
 +    void set_params(float blend, float drive);
 +    void set_sample_rate(uint32_t sr);
 +    float process(float in);
 +    float get_distortion_level();
 +    static inline float
 +    // NOTICE!! These routines are implemented for testing purposes only!
 +    // They're taken from TAP Plugins and will act as a placeholder until
 +    // Krzysztof's distrotion routine is ready!
 +    M(float x) {
 +
 +        if ((x > 0.000000001f) || (x < -0.000000001f))
 +            return x;
 +        else
 +            return 0.0f;
 +    }
 +
 +    static inline float
 +    D(float x) {
 +
 +        if (x > 0.000000001f)
 +            return sqrt(x);
 +        else if (x < -0.000000001f)
 +            return sqrt(-x);
 +        else
 +            return 0.0f;
 +    }
 +};
 +
 +/// Saturator by Markus Schmidt (based on Krzysztof's filters and Tom's distortion algorythm)
 +class saturator_audio_module: public audio_module<saturator_metadata> {
 +private:
 +    float hp_pre_freq_old, lp_pre_freq_old;
 +    float hp_post_freq_old, lp_post_freq_old;
 +    float p_level_old, p_freq_old, p_q_old;
 +    uint32_t clip_in, clip_out;
 +    float meter_in, meter_out, meter_drive;
 +    biquad_d2<float> lp[2][4], hp[2][4];
 +    biquad_d2<float> p[2];
 +    distortion_audio_module dist[2];
 +public:
 +    float *ins[in_count];
 +    float *outs[out_count];
 +    float *params[param_count];
 +    uint32_t srate;
 +    bool is_active;
 +    saturator_audio_module();
 +    void activate();
 +    void deactivate();
 +    void params_changed();
 +    void set_sample_rate(uint32_t sr);
 +    uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
 +};
 +
 +/// Exciter by Markus Schmidt (based on Krzysztof's filters and Tom's distortion algorythm)
 +class exciter_audio_module: public audio_module<exciter_metadata> {
 +private:
 +    float freq_old;
 +    uint32_t clip_in, clip_out;
 +    float meter_in, meter_out, meter_drive;
 +    biquad_d2<float> hp[2][4];
 +    distortion_audio_module dist[2];
 +public:
 +    float *ins[in_count];
 +    float *outs[out_count];
 +    float *params[param_count];
 +    uint32_t srate;
 +    bool is_active;
 +    exciter_audio_module();
 +    void activate();
 +    void deactivate();
 +    void params_changed();
 +    void set_sample_rate(uint32_t sr);
 +    uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
 +};
 +
 +/// Bass Enhancer by Markus Schmidt (based on Krzysztof's filters and Tom's distortion algorythm)
 +class bassenhancer_audio_module: public audio_module<bassenhancer_metadata> {
 +private:
 +    float freq_old;
 +    uint32_t clip_in, clip_out;
 +    float meter_in, meter_out, meter_drive;
 +    biquad_d2<float> lp[2][4];
 +    distortion_audio_module dist[2];
 +public:
 +    float *ins[in_count];
 +    float *outs[out_count];
 +    float *params[param_count];
 +    uint32_t srate;
 +    bool is_active;
 +    bassenhancer_audio_module();
 +    void activate();
 +    void deactivate();
 +    void params_changed();
 +    void set_sample_rate(uint32_t sr);
 +    uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
 +};
 +
  /// Filterclavier --- MIDI controlled filter by Hans Baier
  class filterclavier_audio_module: 
          public filter_module_with_inertia<biquad_filter_module, filterclavier_metadata>, 
          public frequency_response_line_graph
  {        
+     using audio_module<filterclavier_metadata>::ins;
+     using audio_module<filterclavier_metadata>::outs;
+     using audio_module<filterclavier_metadata>::params;
+ 
      const float min_gain;
      const float max_gain;
      
@@@ -1266,101 -618,20 +727,20 @@@
      int last_velocity;
          
  public:    
-     filterclavier_audio_module() 
-         : 
-             min_gain(1.0),
-             max_gain(32.0),
-             last_note(-1),
-             last_velocity(-1) {}
-     
-     void params_changed()
-     { 
-         inertia_filter_module::inertia_cutoff.set_inertia(
-             note_to_hz(last_note + *params[par_transpose], *params[par_detune]));
-         
-         float min_resonance = param_props[par_max_resonance].min;
-          inertia_filter_module::inertia_resonance.set_inertia( 
-                  (float(last_velocity) / 127.0)
-                  // 0.001: see below
-                  * (*params[par_max_resonance] - min_resonance + 0.001)
-                  + min_resonance);
-              
-         adjust_gain_according_to_filter_mode(last_velocity);
-         
-         inertia_filter_module::calculate_filter(); 
-     }
-         
-     void activate()
-     {
-         inertia_filter_module::activate();
-     }
-     
-     void set_sample_rate(uint32_t sr)
-     {
-         inertia_filter_module::set_sample_rate(sr);
-     }
- 
-     
-     void deactivate()
-     {
-         inertia_filter_module::deactivate();
-     }
+     filterclavier_audio_module();
+     void params_changed();
+     void activate();
+     void set_sample_rate(uint32_t sr);
+     void deactivate();
    
      /// MIDI control
-     virtual void note_on(int note, int vel)
-     {
-         last_note     = note;
-         last_velocity = vel;
-         inertia_filter_module::inertia_cutoff.set_inertia(
-                 note_to_hz(note + *params[par_transpose], *params[par_detune]));
- 
-         float min_resonance = param_props[par_max_resonance].min;
-         inertia_filter_module::inertia_resonance.set_inertia( 
-                 (float(vel) / 127.0) 
-                 // 0.001: if the difference is equal to zero (which happens
-                 // when the max_resonance knom is at minimum position
-                 // then the filter gain doesnt seem to snap to zero on most note offs
-                 * (*params[par_max_resonance] - min_resonance + 0.001) 
-                 + min_resonance);
-         
-         adjust_gain_according_to_filter_mode(vel);
-         
-         inertia_filter_module::calculate_filter();
-     }
+     virtual void note_on(int note, int vel);
+     virtual void note_off(int note, int vel);
      
-     virtual void note_off(int note, int vel)
-     {
-         if (note == last_note) {
-             inertia_filter_module::inertia_resonance.set_inertia(param_props[par_max_resonance].min);
-             inertia_filter_module::inertia_gain.set_inertia(min_gain);
-             inertia_filter_module::calculate_filter();
-             last_velocity = 0;
-         }
-     }
- 
-     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
+     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const;
      
  private:
-     void adjust_gain_according_to_filter_mode(int velocity) {
-         int   mode = dsp::fastf2i_drm(*params[par_mode]);
-         
-         // for bandpasses: boost gain for velocities > 0
-         if ( (mode_6db_bp <= mode) && (mode <= mode_18db_bp) ) {
-             // gain for velocity 0:   1.0
-             // gain for velocity 127: 32.0
-             float mode_max_gain = max_gain;
-             // max_gain is right for mode_6db_bp
-             if (mode == mode_12db_bp)
-                 mode_max_gain /= 6.0;
-             if (mode == mode_18db_bp)
-                 mode_max_gain /= 10.5;
-             
-             inertia_filter_module::inertia_gain.set_now(
-                     (float(velocity) / 127.0) * (mode_max_gain - min_gain) + min_gain);
-         } else {
-             inertia_filter_module::inertia_gain.set_now(min_gain);
-         }
-     }
+     void adjust_gain_according_to_filter_mode(int velocity);
  };
  
  extern std::string get_builtin_modules_rdf();
diff --combined src/modules.cpp
index f29355c,350e449..b6d2899
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@@ -517,83 -517,6 +517,83 @@@ CALF_PLUGIN_INFO(pulsator) = { 0x8514, 
  
  ////////////////////////////////////////////////////////////////////////////
  
 +CALF_PORT_NAMES(saturator) = {"In L", "In R", "Out L", "Out R"};
 +
 +CALF_PORT_PROPS(saturator) = {
 +    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" },
 +    { 1,           1,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_in", "Activation" },
 +    { 1,           0,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_out", "Master" },
 +    { 1,         0,           1,     0,  PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB , NULL, "mix", "Mix" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_in", "Input" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_out", "Output" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_in", "0dB" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_out", "0dB" },
 +    
 +    { 5,           0.1,         10,    0,  PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "drive", "Saturation" },
 +    { 10,         -10,          10,    0,  PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER | PF_UNIT_COEF, NULL, "blend", "Blend" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_drive", "Drive" },
 +    
 +    { 20000,      10,           20000, 0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "lp_pre_freq", "Lowpass" },
 +    { 10,         10,           20000, 0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "hp_pre_freq", "Highpass" },
 +
 +    { 20000,      10,           20000, 0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "lp_post_freq", "Lowpass" },
 +    { 10,         10,           20000, 0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "hp_post_freq", "Highpass" },
 +    
 +    { 2000,       80,           8000,  0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "p_freq", "Tone" },
 +    { 1,          0.0625,       16,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "p_level", "Amount" },
 +    { 1,          0.1,          10,    1,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "p_q", "Gradient" },
 +};
 +
 +CALF_PLUGIN_INFO(saturator) = { 0x8530, "Saturator", "Calf Saturator", "Markus Schmidt / Krzysztof Foltman", calf_plugins::calf_copyright_info, "DistortionPlugin" };
 +
 +////////////////////////////////////////////////////////////////////////////
 +
 +CALF_PORT_NAMES(exciter) = {"In L", "In R", "Out L", "Out R"};
 +
 +CALF_PORT_PROPS(exciter) = {
 +    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" },
 +    { 1,           0,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_in", "Input" },
 +    { 1,           0,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_out", "Output" },
 +    { 1,           0,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_NOBOUNDS, NULL, "amount", "Amount" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_in", "Input" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_out", "Output" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_in", "0dB" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_out", "0dB" },
 +    
 +    { 8.5,         0.1,         10,    0,  PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "drive", "Harmonics" },
 +    { 0,          -10,          10,    0,  PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER | PF_UNIT_COEF, NULL, "blend", "Blend harmonics" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_drive", "Harmonics level" },
 +    
 +    { 6000,       2000,         12000, 0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "freq", "Scope" },
 +    { 0,          0,            1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "listen", "Listen" },
 +};
 +
 +CALF_PLUGIN_INFO(exciter) = { 0x8531, "Exciter", "Calf Exciter", "Markus Schmidt / Krzysztof Foltman", calf_plugins::calf_copyright_info, "DistortionPlugin" };
 +
 +////////////////////////////////////////////////////////////////////////////
 +
 +CALF_PORT_NAMES(bassenhancer) = {"In L", "In R", "Out L", "Out R"};
 +
 +CALF_PORT_PROPS(bassenhancer) = {
 +    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" },
 +    { 1,           0,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_in", "Input" },
 +    { 1,           0,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_out", "Output" },
 +    { 1,           0,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_NOBOUNDS, NULL, "amount", "Amount" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_in", "Input" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_out", "Output" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_in", "0dB" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_out", "0dB" },
 +    
 +    { 8.5,         0.1,         10,    0,  PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "drive", "Harmonics" },
 +    { 0,          -10,          10,    0,  PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_FADER | PF_UNIT_COEF, NULL, "blend", "Blend harmonics" },
 +    { 0,           0,           1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_drive", "Harmonics level" },
 +    
 +    { 120,        10,           250,   0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "freq", "Scope" },
 +    { 0,          0,            1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "listen", "Listen" },
 +};
 +
 +CALF_PLUGIN_INFO(bassenhancer) = { 0x8532, "BassEnhancer", "Calf Bass Enhancer", "Markus Schmidt / Krzysztof Foltman", calf_plugins::calf_copyright_info, "DistortionPlugin" };
 +
  ////////////////////////////////////////////////////////////////////////////
  
  CALF_PORT_NAMES(monosynth) = {
@@@ -967,10 -890,9 +967,9 @@@ CALF_PORT_PROPS(wavetable) = 
  
  ////////////////////////////////////////////////////////////////////////////
  
- void calf_plugins::get_all_plugins(std::vector<plugin_metadata_iface *> &plugins)
+ calf_plugins::plugin_registry::plugin_registry()
  {
-     #define PER_MODULE_ITEM(name, isSynth, jackname) plugins.push_back(new name##_metadata);
-     #define PER_SMALL_MODULE_ITEM(...)
+     #define PER_MODULE_ITEM(name, isSynth, jackname) plugins.push_back((new name##_metadata));
      #include <calf/modulelist.h>
  }
  
diff --combined src/modules_dsp.cpp
index ca420ba,bf2e2e9..cac8a60
--- a/src/modules_dsp.cpp
+++ b/src/modules_dsp.cpp
@@@ -32,14 -32,16 +32,16 @@@
  using namespace dsp;
  using namespace calf_plugins;
  
+ #define SET_IF_CONNECTED(name) if (params[AM::param_##name] != NULL) *params[AM::param_##name] = name;
+ 
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
- bool frequency_response_line_graph::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+ bool frequency_response_line_graph::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
  { 
      return get_freq_gridline(subindex, pos, vertical, legend, context);
  }
  
- int frequency_response_line_graph::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+ int frequency_response_line_graph::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
  {
      subindex_graph = 0;
      subindex_dot = 0;
@@@ -68,7 -70,7 +70,7 @@@ void flanger_audio_module::deactivate(
      is_active = false;
  }
  
- bool flanger_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ bool flanger_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -80,13 -82,57 +82,57 @@@
      return false;
  }
  
- float flanger_audio_module::freq_gain(int subindex, float freq, float srate)
+ float flanger_audio_module::freq_gain(int subindex, float freq, float srate) const
  {
      return (subindex ? right : left).freq_gain(freq, srate);                
  }
  
+ void flanger_audio_module::params_changed()
+ {
+     float dry = *params[par_dryamount];
+     float wet = *params[par_amount];
+     float rate = *params[par_rate]; // 0.01*pow(1000.0f,*params[par_rate]);
+     float min_delay = *params[par_delay] / 1000.0;
+     float mod_depth = *params[par_depth] / 1000.0;
+     float fb = *params[par_fb];
+     left.set_dry(dry); right.set_dry(dry);
+     left.set_wet(wet); right.set_wet(wet);
+     left.set_rate(rate); right.set_rate(rate);
+     left.set_min_delay(min_delay); right.set_min_delay(min_delay);
+     left.set_mod_depth(mod_depth); right.set_mod_depth(mod_depth);
+     left.set_fb(fb); right.set_fb(fb);
+     float r_phase = *params[par_stereo] * (1.f / 360.f);
+     clear_reset = false;
+     if (*params[par_reset] >= 0.5) {
+         clear_reset = true;
+         left.reset_phase(0.f);
+         right.reset_phase(r_phase);
+     } else {
+         if (fabs(r_phase - last_r_phase) > 0.0001f) {
+             right.phase = left.phase;
+             right.inc_phase(r_phase);
+             last_r_phase = r_phase;
+         }
+     }
+ }
+ 
+ void flanger_audio_module::params_reset()
+ {
+     if (clear_reset) {
+         *params[par_reset] = 0.f;
+         clear_reset = false;
+     }
+ }
+ 
  ///////////////////////////////////////////////////////////////////////////////////////////////
  
+ phaser_audio_module::phaser_audio_module()
+ : left(MaxStages, x1vals[0], y1vals[0])
+ , right(MaxStages, x1vals[1], y1vals[1])
+ {
+     is_active = false;
+ }
+ 
  void phaser_audio_module::set_sample_rate(uint32_t sr)
  {
      srate = sr;
@@@ -109,7 -155,7 +155,7 @@@ void phaser_audio_module::deactivate(
      is_active = false;
  }
  
- bool phaser_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ bool phaser_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -121,16 -167,55 +167,55 @@@
      return false;
  }
  
- float phaser_audio_module::freq_gain(int subindex, float freq, float srate)
+ float phaser_audio_module::freq_gain(int subindex, float freq, float srate) const
  {
      return (subindex ? right : left).freq_gain(freq, srate);                
  }
  
- bool phaser_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+ bool phaser_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
  {
      return get_freq_gridline(subindex, pos, vertical, legend, context);
  }
  
+ void phaser_audio_module::params_changed()
+ {
+     float dry = *params[par_dryamount];
+     float wet = *params[par_amount];
+     float rate = *params[par_rate]; // 0.01*pow(1000.0f,*params[par_rate]);
+     float base_frq = *params[par_freq];
+     float mod_depth = *params[par_depth];
+     float fb = *params[par_fb];
+     int stages = (int)*params[par_stages];
+     left.set_dry(dry); right.set_dry(dry);
+     left.set_wet(wet); right.set_wet(wet);
+     left.set_rate(rate); right.set_rate(rate);
+     left.set_base_frq(base_frq); right.set_base_frq(base_frq);
+     left.set_mod_depth(mod_depth); right.set_mod_depth(mod_depth);
+     left.set_fb(fb); right.set_fb(fb);
+     left.set_stages(stages); right.set_stages(stages);
+     float r_phase = *params[par_stereo] * (1.f / 360.f);
+     clear_reset = false;
+     if (*params[par_reset] >= 0.5) {
+         clear_reset = true;
+         left.reset_phase(0.f);
+         right.reset_phase(r_phase);
+     } else {
+         if (fabs(r_phase - last_r_phase) > 0.0001f) {
+             right.phase = left.phase;
+             right.inc_phase(r_phase);
+             last_r_phase = r_phase;
+         }
+     }
+ }
+ 
+ void phaser_audio_module::params_reset()
+ {
+     if (clear_reset) {
+         *params[par_reset] = 0.f;
+         clear_reset = false;
+     }
+ }
+ 
  ///////////////////////////////////////////////////////////////////////////////////////////////
  
  void reverb_audio_module::activate()
@@@ -149,9 -234,192 +234,192 @@@ void reverb_audio_module::set_sample_ra
      amount.set_sample_rate(sr);
  }
  
+ void reverb_audio_module::params_changed()
+ {
+     reverb.set_type_and_diffusion(fastf2i_drm(*params[par_roomsize]), *params[par_diffusion]);
+     reverb.set_time(*params[par_decay]);
+     reverb.set_cutoff(*params[par_hfdamp]);
+     amount.set_inertia(*params[par_amount]);
+     dryamount.set_inertia(*params[par_dry]);
+     left_lo.set_lp(dsp::clip(*params[par_treblecut], 20.f, (float)(srate * 0.49f)), srate);
+     left_hi.set_hp(dsp::clip(*params[par_basscut], 20.f, (float)(srate * 0.49f)), srate);
+     right_lo.copy_coeffs(left_lo);
+     right_hi.copy_coeffs(left_hi);
+     predelay_amt = (int) (srate * (*params[par_predelay]) * (1.0f / 1000.0f) + 1);
+ }
+ 
+ uint32_t reverb_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
+ {
+     numsamples += offset;
+     clip   -= std::min(clip, numsamples);
+     for (uint32_t i = offset; i < numsamples; i++) {
+         float dry = dryamount.get();
+         float wet = amount.get();
+         stereo_sample<float> s(ins[0][i], ins[1][i]);
+         stereo_sample<float> s2 = pre_delay.process(s, predelay_amt);
+         
+         float rl = s2.left, rr = s2.right;
+         rl = left_lo.process(left_hi.process(rl));
+         rr = right_lo.process(right_hi.process(rr));
+         reverb.process(rl, rr);
+         outs[0][i] = dry*s.left + wet*rl;
+         outs[1][i] = dry*s.right + wet*rr;
+         meter_wet = std::max(fabs(wet*rl), fabs(wet*rr));
+         meter_out = std::max(fabs(outs[0][i]), fabs(outs[1][i]));
+         if(outs[0][i] > 1.f or outs[1][i] > 1.f) {
+             clip = srate >> 3;
+         }
+     }
+     reverb.extra_sanitize();
+     left_lo.sanitize();
+     left_hi.sanitize();
+     right_lo.sanitize();
+     right_hi.sanitize();
+     if(params[par_meter_wet] != NULL) {
+         *params[par_meter_wet] = meter_wet;
+     }
+     if(params[par_meter_out] != NULL) {
+         *params[par_meter_out] = meter_out;
+     }
+     if(params[par_clip] != NULL) {
+         *params[par_clip] = clip;
+     }
+     return outputs_mask;
+ }
+ 
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ 
+ vintage_delay_audio_module::vintage_delay_audio_module()
+ {
+     old_medium = -1;
+     for (int i = 0; i < MAX_DELAY; i++) {
+         buffers[0][i] = 0.f;
+         buffers[1][i] = 0.f;
+     }
+ }
+ 
+ void vintage_delay_audio_module::params_changed()
+ {
+     float unit = 60.0 * srate / (*params[par_bpm] * *params[par_divide]);
+     deltime_l = dsp::fastf2i_drm(unit * *params[par_time_l]);
+     deltime_r = dsp::fastf2i_drm(unit * *params[par_time_r]);
+     amt_left.set_inertia(*params[par_amount]); amt_right.set_inertia(*params[par_amount]);
+     float fb = *params[par_feedback];
+     dry = *params[par_dryamount];
+     mixmode = dsp::fastf2i_drm(*params[par_mixmode]);
+     medium = dsp::fastf2i_drm(*params[par_medium]);
+     if (mixmode == 0)
+     {
+         fb_left.set_inertia(fb);
+         fb_right.set_inertia(pow(fb, *params[par_time_r] / *params[par_time_l]));
+     } else {
+         fb_left.set_inertia(fb);
+         fb_right.set_inertia(fb);
+     }
+     if (medium != old_medium)
+         calc_filters();
+ }
+ 
+ void vintage_delay_audio_module::activate()
+ {
+     bufptr = 0;
+     age = 0;
+ }
+ 
+ void vintage_delay_audio_module::deactivate()
+ {
+ }
+ 
+ void vintage_delay_audio_module::set_sample_rate(uint32_t sr)
+ {
+     srate = sr;
+     old_medium = -1;
+     amt_left.set_sample_rate(sr); amt_right.set_sample_rate(sr);
+     fb_left.set_sample_rate(sr); fb_right.set_sample_rate(sr);
+ }
+ 
+ void vintage_delay_audio_module::calc_filters()
+ {
+     // parameters are heavily influenced by gordonjcp and his tape delay unit
+     // although, don't blame him if it sounds bad - I've messed with them too :)
+     biquad_left[0].set_lp_rbj(6000, 0.707, srate);
+     biquad_left[1].set_bp_rbj(4500, 0.250, srate);
+     biquad_right[0].copy_coeffs(biquad_left[0]);
+     biquad_right[1].copy_coeffs(biquad_left[1]);
+ }
+ 
+ uint32_t vintage_delay_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
+ {
+     uint32_t ostate = 3; // XXXKF optimize!
+     uint32_t end = offset + numsamples;
+     int v = mixmode ? 1 : 0;
+     int orig_bufptr = bufptr;
+     for(uint32_t i = offset; i < end; i++)
+     {
+         float out_left, out_right, del_left, del_right;
+         // if the buffer hasn't been cleared yet (after activation), pretend we've read zeros
+ 
+         if (deltime_l >= age) {
+             del_left = ins[0][i];
+             out_left = dry * del_left;
+             amt_left.step();
+             fb_left.step();
+         }
+         else
+         {
+             float in_left = buffers[v][(bufptr - deltime_l) & ADDR_MASK];
+             dsp::sanitize(in_left);
+             out_left = dry * ins[0][i] + in_left * amt_left.get();
+             del_left = ins[0][i] + in_left * fb_left.get();
+         }
+         if (deltime_r >= age) {
+             del_right = ins[1][i];
+             out_right = dry * del_right;
+             amt_right.step();
+             fb_right.step();
+         }
+         else
+         {
+             float in_right = buffers[1 - v][(bufptr - deltime_r) & ADDR_MASK];
+             dsp::sanitize(in_right);
+             out_right = dry * ins[1][i] + in_right * amt_right.get();
+             del_right = ins[1][i] + in_right * fb_right.get();
+         }
+         
+         age++;
+         outs[0][i] = out_left; outs[1][i] = out_right; buffers[0][bufptr] = del_left; buffers[1][bufptr] = del_right;
+         bufptr = (bufptr + 1) & (MAX_DELAY - 1);
+     }
+     if (age >= MAX_DELAY)
+         age = MAX_DELAY;
+     if (medium > 0) {
+         bufptr = orig_bufptr;
+         if (medium == 2)
+         {
+             for(uint32_t i = offset; i < end; i++)
+             {
+                 buffers[0][bufptr] = biquad_left[0].process_lp(biquad_left[1].process(buffers[0][bufptr]));
+                 buffers[1][bufptr] = biquad_right[0].process_lp(biquad_right[1].process(buffers[1][bufptr]));
+                 bufptr = (bufptr + 1) & (MAX_DELAY - 1);
+             }
+             biquad_left[0].sanitize();biquad_right[0].sanitize();
+         } else {
+             for(uint32_t i = offset; i < end; i++)
+             {
+                 buffers[0][bufptr] = biquad_left[1].process(buffers[0][bufptr]);
+                 buffers[1][bufptr] = biquad_right[1].process(buffers[1][bufptr]);
+                 bufptr = (bufptr + 1) & (MAX_DELAY - 1);
+             }
+         }
+         biquad_left[1].sanitize();biquad_right[1].sanitize();
+         
+     }
+     return ostate;
+ }
+ 
  ///////////////////////////////////////////////////////////////////////////////////////////////
  
- bool filter_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ bool filter_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -162,7 -430,7 +430,7 @@@
      return false;
  }
  
- int filter_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+ int filter_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
  {
      if (fabs(inertia_cutoff.get_last() - old_cutoff) + 100 * fabs(inertia_resonance.get_last() - old_resonance) + fabs(*params[par_mode] - old_mode) > 0.1f)
      {
@@@ -186,7 -454,102 +454,102 @@@
  
  ///////////////////////////////////////////////////////////////////////////////////////////////
  
- bool filterclavier_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ filterclavier_audio_module::filterclavier_audio_module() 
+ : filter_module_with_inertia<biquad_filter_module, filterclavier_metadata>(ins, outs, params)
+ , min_gain(1.0)
+ , max_gain(32.0)
+ , last_note(-1)
+ , last_velocity(-1)
+ {
+ }
+     
+ void filterclavier_audio_module::params_changed()
+ { 
+     inertia_filter_module::inertia_cutoff.set_inertia(
+         note_to_hz(last_note + *params[par_transpose], *params[par_detune]));
+     
+     float min_resonance = param_props[par_max_resonance].min;
+      inertia_filter_module::inertia_resonance.set_inertia( 
+              (float(last_velocity) / 127.0)
+              // 0.001: see below
+              * (*params[par_max_resonance] - min_resonance + 0.001)
+              + min_resonance);
+          
+     adjust_gain_according_to_filter_mode(last_velocity);
+     
+     inertia_filter_module::calculate_filter(); 
+ }
+ 
+ void filterclavier_audio_module::activate()
+ {
+     inertia_filter_module::activate();
+ }
+ 
+ void filterclavier_audio_module::set_sample_rate(uint32_t sr)
+ {
+     inertia_filter_module::set_sample_rate(sr);
+ }
+ 
+ void filterclavier_audio_module::deactivate()
+ {
+     inertia_filter_module::deactivate();
+ }
+ 
+ 
+ void filterclavier_audio_module::note_on(int note, int vel)
+ {
+     last_note     = note;
+     last_velocity = vel;
+     inertia_filter_module::inertia_cutoff.set_inertia(
+             note_to_hz(note + *params[par_transpose], *params[par_detune]));
+ 
+     float min_resonance = param_props[par_max_resonance].min;
+     inertia_filter_module::inertia_resonance.set_inertia( 
+             (float(vel) / 127.0) 
+             // 0.001: if the difference is equal to zero (which happens
+             // when the max_resonance knom is at minimum position
+             // then the filter gain doesnt seem to snap to zero on most note offs
+             * (*params[par_max_resonance] - min_resonance + 0.001) 
+             + min_resonance);
+     
+     adjust_gain_according_to_filter_mode(vel);
+     
+     inertia_filter_module::calculate_filter();
+ }
+ 
+ void filterclavier_audio_module::note_off(int note, int vel)
+ {
+     if (note == last_note) {
+         inertia_filter_module::inertia_resonance.set_inertia(param_props[par_max_resonance].min);
+         inertia_filter_module::inertia_gain.set_inertia(min_gain);
+         inertia_filter_module::calculate_filter();
+         last_velocity = 0;
+     }
+ }
+ 
+ void filterclavier_audio_module::adjust_gain_according_to_filter_mode(int velocity)
+ {
+     int   mode = dsp::fastf2i_drm(*params[par_mode]);
+     
+     // for bandpasses: boost gain for velocities > 0
+     if ( (mode_6db_bp <= mode) && (mode <= mode_18db_bp) ) {
+         // gain for velocity 0:   1.0
+         // gain for velocity 127: 32.0
+         float mode_max_gain = max_gain;
+         // max_gain is right for mode_6db_bp
+         if (mode == mode_12db_bp)
+             mode_max_gain /= 6.0;
+         if (mode == mode_18db_bp)
+             mode_max_gain /= 10.5;
+         
+         inertia_filter_module::inertia_gain.set_now(
+                 (float(velocity) / 127.0) * (mode_max_gain - min_gain) + min_gain);
+     } else {
+         inertia_filter_module::inertia_gain.set_now(min_gain);
+     }
+ }
+ 
+ bool filterclavier_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active || index != par_mode) {
          return false;
@@@ -198,6 -561,7 +561,7 @@@
      return false;
  }
  
+ 
  ///////////////////////////////////////////////////////////////////////////////////////////////
  
  rotary_speaker_audio_module::rotary_speaker_audio_module()
@@@ -250,8 -614,151 +614,151 @@@ void rotary_speaker_audio_module::contr
      }
  }
  
+ void rotary_speaker_audio_module::params_changed()
+ {
+     set_vibrato();
+ }
+ 
+ void rotary_speaker_audio_module::set_vibrato()
+ {
+     vibrato_mode = fastf2i_drm(*params[par_speed]);
+     // manual vibrato - do not recalculate speeds as they're not used anyway
+     if (vibrato_mode == 5) 
+         return;
+     if (!vibrato_mode)
+         dspeed = -1;
+     else {
+         float speed = vibrato_mode - 1;
+         if (vibrato_mode == 3)
+             speed = hold_value;
+         if (vibrato_mode == 4)
+             speed = mwhl_value;
+         dspeed = (speed < 0.5f) ? 0 : 1;
+     }
+     update_speed();
+ }
+ 
+ /// Convert RPM speed to delta-phase
+ uint32_t rotary_speaker_audio_module::rpm2dphase(float rpm)
+ {
+     return (uint32_t)((rpm / (60.0 * srate)) * (1 << 30)) << 2;
+ }
+ 
+ /// Set delta-phase variables based on current calculated (and interpolated) RPM speed
+ void rotary_speaker_audio_module::update_speed()
+ {
+     float speed_h = aspeed_h >= 0 ? (48 + (400-48) * aspeed_h) : (48 * (1 + aspeed_h));
+     float speed_l = aspeed_l >= 0 ? 40 + (342-40) * aspeed_l : (40 * (1 + aspeed_l));
+     dphase_h = rpm2dphase(speed_h);
+     dphase_l = rpm2dphase(speed_l);
+ }
+ 
+ void rotary_speaker_audio_module::update_speed_manual(float delta)
+ {
+     float ts = *params[par_treblespeed];
+     float bs = *params[par_bassspeed];
+     incr_towards(maspeed_h, ts, delta * 200, delta * 200);
+     incr_towards(maspeed_l, bs, delta * 200, delta * 200);
+     dphase_h = rpm2dphase(maspeed_h);
+     dphase_l = rpm2dphase(maspeed_l);
+ }
+ 
+ /// map a ramp [int] to a sinusoid-like function [0, 65536]
+ static inline int pseudo_sine_scl(int counter)
+ {
+     // premature optimization is a root of all evil; it can be done with integers only - but later :)
+     double v = counter * (1.0 / (65536.0 * 32768.0));
+     return (int) (32768 + 32768 * (v - v*v*v) * (1.0 / 0.3849));
+ }
+ 
+ /// Increase or decrease aspeed towards raspeed, with required negative and positive rate
+ inline bool rotary_speaker_audio_module::incr_towards(float &aspeed, float raspeed, float delta_decc, float delta_acc)
+ {
+     if (aspeed < raspeed) {
+         aspeed = std::min(raspeed, aspeed + delta_acc);
+         return true;
+     }
+     else if (aspeed > raspeed) 
+     {
+         aspeed = std::max(raspeed, aspeed - delta_decc);
+         return true;
+     }        
+     return false;
+ }
+ 
+ uint32_t rotary_speaker_audio_module::process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask)
+ {
+     int shift = (int)(300000 * (*params[par_shift])), pdelta = (int)(300000 * (*params[par_spacing]));
+     int md = (int)(100 * (*params[par_moddepth]));
+     float mix = 0.5 * (1.0 - *params[par_micdistance]);
+     float mix2 = *params[par_reflection];
+     float mix3 = mix2 * mix2;
+     for (unsigned int i = 0; i < nsamples; i++) {
+         float in_l = ins[0][i + offset], in_r = ins[1][i + offset];
+         float in_mono = 0.5f * (in_l + in_r);
+         
+         int xl = pseudo_sine_scl(phase_l), yl = pseudo_sine_scl(phase_l + 0x40000000);
+         int xh = pseudo_sine_scl(phase_h), yh = pseudo_sine_scl(phase_h + 0x40000000);
+         // printf("%d %d %d\n", shift, pdelta, shift + pdelta + 20 * xl);
+         meter_l = xl;
+         meter_h = xh;
+         // float out_hi_l = in_mono - delay.get_interp_1616(shift + md * xh) + delay.get_interp_1616(shift + md * 65536 + pdelta - md * yh) - delay.get_interp_1616(shift + md * 65536 + pdelta + pdelta - md * xh);
+         // float out_hi_r = in_mono + delay.get_interp_1616(shift + md * 65536 - md * yh) - delay.get_interp_1616(shift + pdelta + md * xh) + delay.get_interp_1616(shift + pdelta + pdelta + md * yh);
+         float out_hi_l = in_mono + delay.get_interp_1616(shift + md * xh) - mix2 * delay.get_interp_1616(shift + md * 65536 + pdelta - md * yh) + mix3 * delay.get_interp_1616(shift + md * 65536 + pdelta + pdelta - md * xh);
+         float out_hi_r = in_mono + delay.get_interp_1616(shift + md * 65536 - md * yh) - mix2 * delay.get_interp_1616(shift + pdelta + md * xh) + mix3 * delay.get_interp_1616(shift + pdelta + pdelta + md * yh);
+ 
+         float out_lo_l = in_mono + delay.get_interp_1616(shift + md * xl); // + delay.get_interp_1616(shift + md * 65536 + pdelta - md * yl);
+         float out_lo_r = in_mono + delay.get_interp_1616(shift + md * yl); // - delay.get_interp_1616(shift + pdelta + md * yl);
+         
+         out_hi_l = crossover2l.process(out_hi_l); // sanitize(out_hi_l);
+         out_hi_r = crossover2r.process(out_hi_r); // sanitize(out_hi_r);
+         out_lo_l = crossover1l.process(out_lo_l); // sanitize(out_lo_l);
+         out_lo_r = crossover1r.process(out_lo_r); // sanitize(out_lo_r);
+         
+         float out_l = out_hi_l + out_lo_l;
+         float out_r = out_hi_r + out_lo_r;
+         
+         float mic_l = out_l + mix * (out_r - out_l);
+         float mic_r = out_r + mix * (out_l - out_r);
+         
+         outs[0][i + offset] = mic_l * 0.5f;
+         outs[1][i + offset] = mic_r * 0.5f;
+         delay.put(in_mono);
+         phase_l += dphase_l;
+         phase_h += dphase_h;
+     }
+     crossover1l.sanitize();
+     crossover1r.sanitize();
+     crossover2l.sanitize();
+     crossover2r.sanitize();
+     float delta = nsamples * 1.0 / srate;
+     if (vibrato_mode == 5)
+         update_speed_manual(delta);
+     else
+     {
+         bool u1 = incr_towards(aspeed_l, dspeed, delta * 0.2, delta * 0.14);
+         bool u2 = incr_towards(aspeed_h, dspeed, delta, delta * 0.5);
+         if (u1 || u2)
+             set_vibrato();
+     }
+     if(params[par_meter_l] != NULL) {
+         *params[par_meter_l] = (float)meter_l / 65536.0;
+     }
+     if(params[par_meter_h] != NULL) {
+         *params[par_meter_h] = (float)meter_h / 65536.0;
+     }
+     return outputs_mask;
+ }
+ 
+ 
  ///////////////////////////////////////////////////////////////////////////////////////////////
  
+ multichorus_audio_module::multichorus_audio_module()
+ {
+     is_active = false;
+     last_r_phase = -1;
+ }
+ 
  void multichorus_audio_module::activate()
  {
      is_active = true;
@@@ -269,7 -776,7 +776,7 @@@ void multichorus_audio_module::set_samp
      right.setup(sr);
  }
  
- bool multichorus_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ bool multichorus_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -285,7 -792,7 +792,7 @@@
          return ::get_graph(*this, subindex, data, points);
      }
      if (index == par_rate && subindex < nvoices) {
-         sine_multi_lfo<float, 8> &lfo = left.lfo;
+         const sine_multi_lfo<float, 8> &lfo = left.lfo;
          for (int i = 0; i < points; i++) {
              float phase = i * 2 * M_PI / points;
              // original -65536 to 65535 value
@@@ -298,7 -805,7 +805,7 @@@
      return false;
  }
  
- bool multichorus_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
+ bool multichorus_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const
  {
      int voice = subindex >> 1;
      int nvoices = (int)*params[par_voices];
@@@ -308,7 -815,7 +815,7 @@@
      float unit = (1 - *params[par_overlap]);
      float scw = 1 + unit * (nvoices - 1);
      set_channel_color(context, subindex);
-     sine_multi_lfo<float, 8> &lfo = (subindex & 1 ? right : left).lfo;
+     const sine_multi_lfo<float, 8> &lfo = (subindex & 1 ? right : left).lfo;
      if (index == par_rate)
      {
          x = (double)(lfo.phase + lfo.vphase * voice) / 4096.0;
@@@ -325,7 -832,7 +832,7 @@@
      return true;
  }
  
- bool multichorus_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+ bool multichorus_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
  {
      if (index == par_rate && !subindex)
      {
@@@ -338,13 -845,53 +845,53 @@@
      return false;
  }
  
- float multichorus_audio_module::freq_gain(int subindex, float freq, float srate)
+ float multichorus_audio_module::freq_gain(int subindex, float freq, float srate) const
  {
      if (subindex == 2)
          return *params[par_amount] * left.post.freq_gain(freq, srate);
      return (subindex ? right : left).freq_gain(freq, srate);                
  }
  
+ void multichorus_audio_module::params_changed()
+ {
+     // delicious copy-pasta from flanger module - it'd be better to keep it common or something
+     float dry = *params[par_dryamount];
+     float wet = *params[par_amount];
+     float rate = *params[par_rate];
+     float min_delay = *params[par_delay] / 1000.0;
+     float mod_depth = *params[par_depth] / 1000.0;
+     float overlap = *params[par_overlap];
+     left.set_dry(dry); right.set_dry(dry);
+     left.set_wet(wet); right.set_wet(wet);
+     left.set_rate(rate); right.set_rate(rate);
+     left.set_min_delay(min_delay); right.set_min_delay(min_delay);
+     left.set_mod_depth(mod_depth); right.set_mod_depth(mod_depth);
+     int voices = (int)*params[par_voices];
+     left.lfo.set_voices(voices); right.lfo.set_voices(voices);
+     left.lfo.set_overlap(overlap);right.lfo.set_overlap(overlap);
+     float vphase = *params[par_vphase] * (1.f / 360.f);
+     left.lfo.vphase = right.lfo.vphase = vphase * (4096 / std::max(voices - 1, 1));
+     float r_phase = *params[par_stereo] * (1.f / 360.f);
+     if (fabs(r_phase - last_r_phase) > 0.0001f) {
+         right.lfo.phase = left.lfo.phase;
+         right.lfo.phase += chorus_phase(r_phase * 4096);
+         last_r_phase = r_phase;
+     }
+     left.post.f1.set_bp_rbj(*params[par_freq], *params[par_q], srate);
+     left.post.f2.set_bp_rbj(*params[par_freq2], *params[par_q], srate);
+     right.post.f1.copy_coeffs(left.post.f1);
+     right.post.f2.copy_coeffs(left.post.f2);
+ }
+ 
+ uint32_t multichorus_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
+ {
+     left.process(outs[0] + offset, ins[0] + offset, numsamples);
+     right.process(outs[1] + offset, ins[1] + offset, numsamples);
+     return outputs_mask; // XXXKF allow some delay after input going blank
+ }
+ 
+ 
+ 
  /// Multibandcompressor by Markus Schmidt
  ///
  /// This module splits the signal in four different bands
@@@ -399,8 -946,8 +946,8 @@@ void multibandcompressor_audio_module::
          hpL0.set_hp_rbj((float)(*params[param_freq0] * (1 + *params[param_sep0])), *params[param_q0], (float)srate);
          hpR0.copy_coeffs(hpL0);
          freq_old[0] = *params[param_freq0];
-         sep_old[0]  = *params[param_sep2];
-         q_old[0]    = *params[param_q2];
+         sep_old[0]  = *params[param_sep0];
+         q_old[0]    = *params[param_q0];
      }
      if(*params[param_freq1] != freq_old[1] or *params[param_sep1] != sep_old[1] or *params[param_q1] != q_old[1]) {
          lpL1.set_lp_rbj((float)(*params[param_freq1] * (1 - *params[param_sep1])), *params[param_q1], (float)srate);
@@@ -408,8 -955,8 +955,8 @@@
          hpL1.set_hp_rbj((float)(*params[param_freq1] * (1 + *params[param_sep1])), *params[param_q1], (float)srate);
          hpR1.copy_coeffs(hpL1);
          freq_old[1] = *params[param_freq1];
-         sep_old[1]  = *params[param_sep2];
-         q_old[1]    = *params[param_q2];
+         sep_old[1]  = *params[param_sep1];
+         q_old[1]    = *params[param_q1];
      }
      if(*params[param_freq2] != freq_old[2] or *params[param_sep2] != sep_old[2] or *params[param_q2] != q_old[2]) {
          lpL2.set_lp_rbj((float)(*params[param_freq2] * (1 - *params[param_sep2])), *params[param_q2], (float)srate);
@@@ -421,22 -968,10 +968,10 @@@
          q_old[2]    = *params[param_q2];
      }
      // set the params of all strips
-     for (int j = 0; j < strips; j ++) {
-         switch (j) {
-             case 0:
-                 strip[j].set_params(*params[param_attack0], *params[param_release0], *params[param_threshold0], *params[param_ratio0], *params[param_knee0], *params[param_makeup0], *params[param_detection0], 1.f, *params[param_bypass0], *params[param_mute0]);
-                 break;
-             case 1:
-                 strip[j].set_params(*params[param_attack1], *params[param_release1], *params[param_threshold1], *params[param_ratio1], *params[param_knee1], *params[param_makeup1], *params[param_detection1], 1.f, *params[param_bypass1], *params[param_mute1]);
-                 break;
-             case 2:
-                 strip[j].set_params(*params[param_attack2], *params[param_release2], *params[param_threshold2], *params[param_ratio2], *params[param_knee2], *params[param_makeup2], *params[param_detection2], 1.f, *params[param_bypass2], *params[param_mute2]);
-                 break;
-             case 3:
-                 strip[j].set_params(*params[param_attack3], *params[param_release3], *params[param_threshold3], *params[param_ratio3], *params[param_knee3], *params[param_makeup3], *params[param_detection3], 1.f, *params[param_bypass3], *params[param_mute3]);
-                 break;
-         }
-     }
+     strip[0].set_params(*params[param_attack0], *params[param_release0], *params[param_threshold0], *params[param_ratio0], *params[param_knee0], *params[param_makeup0], *params[param_detection0], 1.f, *params[param_bypass0], *params[param_mute0]);
+     strip[1].set_params(*params[param_attack1], *params[param_release1], *params[param_threshold1], *params[param_ratio1], *params[param_knee1], *params[param_makeup1], *params[param_detection1], 1.f, *params[param_bypass1], *params[param_mute1]);
+     strip[2].set_params(*params[param_attack2], *params[param_release2], *params[param_threshold2], *params[param_ratio2], *params[param_knee2], *params[param_makeup2], *params[param_detection2], 1.f, *params[param_bypass2], *params[param_mute2]);
+     strip[3].set_params(*params[param_attack3], *params[param_release3], *params[param_threshold3], *params[param_ratio3], *params[param_knee3], *params[param_makeup3], *params[param_detection3], 1.f, *params[param_bypass3], *params[param_mute3]);
  }
  
  void multibandcompressor_audio_module::set_sample_rate(uint32_t sr)
@@@ -448,10 -983,24 +983,24 @@@
      }
  }
  
+ #define BYPASSED_COMPRESSION(index) \
+     if(params[param_compression##index] != NULL) \
+         *params[param_compression##index] = 1.0; \
+     if(params[param_output##index] != NULL) \
+         *params[param_output##index] = 0.0; 
+ 
+ #define ACTIVE_COMPRESSION(index) \
+     if(params[param_compression##index] != NULL) \
+         *params[param_compression##index] = strip[index].get_comp_level(); \
+     if(params[param_output##index] != NULL) \
+         *params[param_output##index] = strip[index].get_output_level();
+ 
  uint32_t multibandcompressor_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
  {
      bool bypass = *params[param_bypass] > 0.5f;
      numsamples += offset;
+     for (int i = 0; i < strips; i++)
+         strip[i].update_curve();
      if(bypass) {
          // everything bypassed
          while(offset < numsamples) {
@@@ -596,165 -1145,75 +1145,75 @@@
      } // process all strips (no bypass)
      
      // draw meters
-     if(params[param_clip_inL] != NULL) {
-         *params[param_clip_inL] = clip_inL;
-     }
-     if(params[param_clip_inR] != NULL) {
-         *params[param_clip_inR] = clip_inR;
-     }
-     if(params[param_clip_outL] != NULL) {
-         *params[param_clip_outL] = clip_outL;
-     }
-     if(params[param_clip_outR] != NULL) {
-         *params[param_clip_outR] = clip_outR;
-     }
-     
-     if(params[param_meter_inL] != NULL) {
-         *params[param_meter_inL] = meter_inL;
-     }
-     if(params[param_meter_inR] != NULL) {
-         *params[param_meter_inR] = meter_inR;
-     }
-     if(params[param_meter_outL] != NULL) {
-         *params[param_meter_outL] = meter_outL;
-     }
-     if(params[param_meter_outR] != NULL) {
-         *params[param_meter_outR] = meter_outR;
-     }
+     SET_IF_CONNECTED(clip_inL);
+     SET_IF_CONNECTED(clip_inR);
+     SET_IF_CONNECTED(clip_outL);
+     SET_IF_CONNECTED(clip_outR);
+     SET_IF_CONNECTED(meter_inL);
+     SET_IF_CONNECTED(meter_inR);
+     SET_IF_CONNECTED(meter_outL);
+     SET_IF_CONNECTED(meter_outR);
      // draw strip meters
      if(bypass > 0.5f) {
-         if(params[param_compression0] != NULL) {
-             *params[param_compression0] = 1.0f;
-         }
-         if(params[param_compression1] != NULL) {
-             *params[param_compression1] = 1.0f;
-         }
-         if(params[param_compression2] != NULL) {
-             *params[param_compression2] = 1.0f;
-         }
-         if(params[param_compression3] != NULL) {
-             *params[param_compression3] = 1.0f;
-         }
- 
-         if(params[param_output0] != NULL) {
-             *params[param_output0] = 0.0f;
-         }
-         if(params[param_output1] != NULL) {
-             *params[param_output1] = 0.0f;
-         }
-         if(params[param_output2] != NULL) {
-             *params[param_output2] = 0.0f;
-         }
-         if(params[param_output3] != NULL) {
-             *params[param_output3] = 0.0f;
-         }
+         BYPASSED_COMPRESSION(0)
+         BYPASSED_COMPRESSION(1)
+         BYPASSED_COMPRESSION(2)
+         BYPASSED_COMPRESSION(3)
      } else {
-         if(params[param_compression0] != NULL) {
-             *params[param_compression0] = strip[0].get_comp_level();
-         }
-         if(params[param_compression1] != NULL) {
-             *params[param_compression1] = strip[1].get_comp_level();
-         }
-         if(params[param_compression2] != NULL) {
-             *params[param_compression2] = strip[2].get_comp_level();
-         }
-         if(params[param_compression3] != NULL) {
-             *params[param_compression3] = strip[3].get_comp_level();
-         }
- 
-         if(params[param_output0] != NULL) {
-             *params[param_output0] = strip[0].get_output_level();
-         }
-         if(params[param_output1] != NULL) {
-             *params[param_output1] = strip[1].get_output_level();
-         }
-         if(params[param_output2] != NULL) {
-             *params[param_output2] = strip[2].get_output_level();
-         }
-         if(params[param_output3] != NULL) {
-             *params[param_output3] = strip[3].get_output_level();
-         }
+         ACTIVE_COMPRESSION(0)
+         ACTIVE_COMPRESSION(1)
+         ACTIVE_COMPRESSION(2)
+         ACTIVE_COMPRESSION(3)
      }
      // whatever has to be returned x)
      return outputs_mask;
  }
- bool multibandcompressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ 
+ const gain_reduction_audio_module *multibandcompressor_audio_module::get_strip_by_param_index(int index) const
  {
      // let's handle by the corresponding strip
      switch (index) {
          case param_compression0:
-             return strip[0].get_graph(subindex, data, points, context);
-             break;
+             return &strip[0];
          case param_compression1:
-             return strip[1].get_graph(subindex, data, points, context);
-             break;
+             return &strip[1];
          case param_compression2:
-             return strip[2].get_graph(subindex, data, points, context);
-             break;
+             return &strip[2];
          case param_compression3:
-             return strip[3].get_graph(subindex, data, points, context);
-             break;
+             return &strip[3];
      }
-     return false;
+     return NULL;
  }
  
- bool multibandcompressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
+ bool multibandcompressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
-     // let's handle by the corresponding strip
-     switch (index) {
-         case param_compression0:
-             return strip[0].get_dot(subindex, x, y, size, context);
-             break;
-         case param_compression1:
-             return strip[1].get_dot(subindex, x, y, size, context);
-             break;
-         case param_compression2:
-             return strip[2].get_dot(subindex, x, y, size, context);
-             break;
-         case param_compression3:
-             return strip[3].get_dot(subindex, x, y, size, context);
-             break;
-     }
+     const gain_reduction_audio_module *m = get_strip_by_param_index(index);
+     if (m)
+         return m->get_graph(subindex, data, points, context);
      return false;
  }
  
- bool multibandcompressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+ bool multibandcompressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const
  {
-     // let's handle by the corresponding strip
-     switch (index) {
-         case param_compression0:
-             return strip[0].get_gridline(subindex, pos, vertical, legend, context);
-             break;
-         case param_compression1:
-             return strip[1].get_gridline(subindex, pos, vertical, legend, context);
-             break;
-         case param_compression2:
-             return strip[2].get_gridline(subindex, pos, vertical, legend, context);
-             break;
-         case param_compression3:
-             return strip[3].get_gridline(subindex, pos, vertical, legend, context);
-             break;
-     }
+     const gain_reduction_audio_module *m = get_strip_by_param_index(index);
+     if (m)
+         return m->get_dot(subindex, x, y, size, context);
+     return false;
+ }
+ 
+ bool multibandcompressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
+ { 
+     const gain_reduction_audio_module *m = get_strip_by_param_index(index);
+     if (m)
+         return m->get_gridline(subindex, pos, vertical, legend, context);
      return false;
  }
  
- int multibandcompressor_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+ int multibandcompressor_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
  {
-     // let's handle by the corresponding strip
-     switch (index) {
-         case param_compression0:
-             return strip[0].get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline);
-             break;
-         case param_compression1:
-             return strip[1].get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline);
-             break;
-         case param_compression2:
-             return strip[2].get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline);
-             break;
-         case param_compression3:
-             return strip[3].get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline);
-             break;
-     }
+     const gain_reduction_audio_module *m = get_strip_by_param_index(index);
+     if (m)
+         return m->get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline);
      return 0;
  }
  
@@@ -820,6 -1279,8 +1279,8 @@@ uint32_t compressor_audio_module::proce
          clip_in    -= std::min(clip_in,  numsamples);
          clip_out   -= std::min(clip_out,  numsamples);
          
+         compressor.update_curve();        
+         
          while(offset < numsamples) {
              // cycle through samples
              float outL = 0.f;
@@@ -832,10 -1293,8 +1293,8 @@@
              
              float leftAC = inL;
              float rightAC = inR;
-             float leftSC = inL;
-             float rightSC = inR;
              
-             compressor.process(leftAC, rightAC, leftSC, rightSC);
+             compressor.process(leftAC, rightAC);
              
              outL = leftAC;
              outR = rightAC;
@@@ -860,18 -1319,10 +1319,10 @@@
          } // cycle trough samples
      }
      // draw meters
-     if(params[param_clip_in] != NULL) {
-         *params[param_clip_in] = clip_in;
-     }
-     if(params[param_clip_out] != NULL) {
-         *params[param_clip_out] = clip_out;
-     }
-     if(params[param_meter_in] != NULL) {
-         *params[param_meter_in] = meter_in;
-     }
-     if(params[param_meter_out] != NULL) {
-         *params[param_meter_out] = meter_out;
-     }
+     SET_IF_CONNECTED(clip_in)
+     SET_IF_CONNECTED(clip_out)
+     SET_IF_CONNECTED(meter_in)
+     SET_IF_CONNECTED(meter_out)
      // draw strip meter
      if(bypass > 0.5f) {
          if(params[param_compression] != NULL) {
@@@ -885,28 -1336,28 +1336,28 @@@
      // whatever has to be returned x)
      return outputs_mask;
  }
- bool compressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ bool compressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active)
          return false;
      return compressor.get_graph(subindex, data, points, context);
  }
  
- bool compressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
+ bool compressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const
  {
      if (!is_active)
          return false;
      return compressor.get_dot(subindex, x, y, size, context);
  }
  
- bool compressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+ bool compressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
  {
      if (!is_active)
          return false;
      return compressor.get_gridline(subindex, pos, vertical, legend, context);
  }
  
- int compressor_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+ int compressor_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
  {
      if (!is_active)
          return false;
@@@ -944,6 -1395,40 +1395,40 @@@ void sidechaincompressor_audio_module::
      compressor.deactivate();
  }
  
+ sidechaincompressor_audio_module::cfloat sidechaincompressor_audio_module::h_z(const cfloat &z) const
+ {
+     switch (sc_mode) {
+         default:
+         case WIDEBAND:
+             return false;
+             break;
+         case DEESSER_WIDE:
+         case DERUMBLER_WIDE:
+         case WEIGHTED_1:
+         case WEIGHTED_2:
+         case WEIGHTED_3:
+         case BANDPASS_2:
+             return f1L.h_z(z) * f2L.h_z(z);
+             break;
+         case DEESSER_SPLIT:
+             return f2L.h_z(z);
+             break;
+         case DERUMBLER_SPLIT:
+         case BANDPASS_1:
+             return f1L.h_z(z);
+             break;
+     }            
+ }
+ 
+ float sidechaincompressor_audio_module::freq_gain(int index, double freq, uint32_t sr) const
+ {
+     typedef std::complex<double> cfloat;
+     freq *= 2.0 * M_PI / sr;
+     cfloat z = 1.0 / exp(cfloat(0.0, freq));
+     
+     return std::abs(h_z(z));
+ }
+ 
  void sidechaincompressor_audio_module::params_changed()
  {
      // set the params of all filters
@@@ -1078,6 -1563,7 +1563,7 @@@ uint32_t sidechaincompressor_audio_modu
          
          clip_in    -= std::min(clip_in,  numsamples);
          clip_out   -= std::min(clip_out,  numsamples);
+         compressor.update_curve();        
          
          while(offset < numsamples) {
              // cycle through samples
@@@ -1100,7 -1586,7 +1586,7 @@@
              switch ((int)*params[param_sc_mode]) {
                  default:
                  case WIDEBAND:
-                     compressor.process(leftAC, rightAC, leftSC, rightSC);
+                     compressor.process(leftAC, rightAC);
                      break;
                  case DEESSER_WIDE:
                  case DERUMBLER_WIDE:
@@@ -1112,14 -1598,14 +1598,14 @@@
                      rightSC = f2R.process(f1R.process(rightSC));
                      leftMC = leftSC;
                      rightMC = rightSC;
-                     compressor.process(leftAC, rightAC, leftSC, rightSC);
+                     compressor.process(leftAC, rightAC, &leftSC, &rightSC);
                      break;
                  case DEESSER_SPLIT:
                      leftSC = f2L.process(leftSC);
                      rightSC = f2R.process(rightSC);
                      leftMC = leftSC;
                      rightMC = rightSC;
-                     compressor.process(leftSC, rightSC, leftSC, rightSC);
+                     compressor.process(leftSC, rightSC, &leftSC, &rightSC);
                      leftAC = f1L.process(leftAC);
                      rightAC = f1R.process(rightAC);
                      leftAC += leftSC;
@@@ -1130,7 -1616,7 +1616,7 @@@
                      rightSC = f1R.process(rightSC);
                      leftMC = leftSC;
                      rightMC = rightSC;
-                     compressor.process(leftSC, rightSC, leftSC, rightSC);
+                     compressor.process(leftSC, rightSC);
                      leftAC = f2L.process(leftAC);
                      rightAC = f2R.process(rightAC);
                      leftAC += leftSC;
@@@ -1141,7 -1627,7 +1627,7 @@@
                      rightSC = f1R.process(rightSC);
                      leftMC = leftSC;
                      rightMC = rightSC;
-                     compressor.process(leftAC, rightAC, leftSC, rightSC);
+                     compressor.process(leftAC, rightAC, &leftSC, &rightSC);
                      break;
              }
              
@@@ -1178,18 -1664,10 +1664,10 @@@
              
      }
      // draw meters
-     if(params[param_clip_in] != NULL) {
-         *params[param_clip_in] = clip_in;
-     }
-     if(params[param_clip_out] != NULL) {
-         *params[param_clip_out] = clip_out;
-     }
-     if(params[param_meter_in] != NULL) {
-         *params[param_meter_in] = meter_in;
-     }
-     if(params[param_meter_out] != NULL) {
-         *params[param_meter_out] = meter_out;
-     }
+     SET_IF_CONNECTED(clip_in)
+     SET_IF_CONNECTED(clip_out)
+     SET_IF_CONNECTED(meter_in)
+     SET_IF_CONNECTED(meter_out)
      // draw strip meter
      if(bypass > 0.5f) {
          if(params[param_compression] != NULL) {
@@@ -1203,7 -1681,7 +1681,7 @@@
      // whatever has to be returned x)
      return outputs_mask;
  }
- bool sidechaincompressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ bool sidechaincompressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -1216,7 -1694,7 +1694,7 @@@
      return false;
  }
  
- bool sidechaincompressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
+ bool sidechaincompressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -1226,7 -1704,7 +1704,7 @@@
      return false;
  }
  
- bool sidechaincompressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+ bool sidechaincompressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -1238,7 -1716,7 +1716,7 @@@
  //    return false;
  }
  
- int sidechaincompressor_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+ int sidechaincompressor_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
  {
      if (!is_active)
          return false;
@@@ -1353,6 -1831,7 +1831,7 @@@ uint32_t deesser_audio_module::process(
          
          detected_led -= std::min(detected_led,  numsamples);
          clip_led   -= std::min(clip_led,  numsamples);
+         compressor.update_curve();        
          
          while(offset < numsamples) {
              // cycle through samples
@@@ -1379,14 -1858,14 +1858,14 @@@
              switch ((int)*params[param_mode]) {
                  default:
                  case WIDE:
-                     compressor.process(leftAC, rightAC, leftSC, rightSC);
+                     compressor.process(leftAC, rightAC, &leftSC, &rightSC);
                      break;
                  case SPLIT:
                      hpL.sanitize();
                      hpR.sanitize();
                      leftRC = hpL.process(leftRC);
                      rightRC = hpR.process(rightRC);
-                     compressor.process(leftRC, rightRC, leftSC, rightSC);
+                     compressor.process(leftRC, rightRC, &leftSC, &rightSC);
                      leftAC = lpL.process(leftAC);
                      rightAC = lpR.process(rightAC);
                      leftAC += leftRC;
@@@ -1452,7 -1931,7 +1931,7 @@@
      // whatever has to be returned x)
      return outputs_mask;
  }
- bool deesser_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ bool deesser_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -1463,14 -1942,14 +1942,14 @@@
      return false;
  }
  
- bool deesser_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+ bool deesser_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
  {
      return get_freq_gridline(subindex, pos, vertical, legend, context);
      
  //    return false;
  }
  
- int deesser_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+ int deesser_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
  {
      if (!is_active) {
          return false;
@@@ -1534,13 -2013,26 +2013,26 @@@ void gain_reduction_audio_module::deact
      is_active = false;
  }
  
- void gain_reduction_audio_module::process(float &left, float &right, float det_left, float det_right)
+ void gain_reduction_audio_module::update_curve()
+ {
+     float linThreshold = threshold;
+     float linKneeSqrt = sqrt(knee);
+     linKneeStart = linThreshold / linKneeSqrt;
+     adjKneeStart = linKneeStart*linKneeStart;
+     float linKneeStop = linThreshold * linKneeSqrt;
+     thres = log(linThreshold);
+     kneeStart = log(linKneeStart);
+     kneeStop = log(linKneeStop);
+     compressedKneeStop = (kneeStop - thres) / ratio + thres;
+ }
+ 
+ void gain_reduction_audio_module::process(float &left, float &right, const float *det_left, const float *det_right)
  {
      if(!det_left) {
-         det_left = left;
+         det_left = &left;
      }
      if(!det_right) {
-         det_right = right;
+         det_right = &right;
      }
      float gain = 1.f;
      if(bypass < 0.5f) {
@@@ -1548,19 -2040,10 +2040,10 @@@
          // greatest sounding compressor I've heard!
          bool rms = detection == 0;
          bool average = stereo_link == 0;
-         float linThreshold = threshold;
          float attack_coeff = std::min(1.f, 1.f / (attack * srate / 4000.f));
          float release_coeff = std::min(1.f, 1.f / (release * srate / 4000.f));
-         float linKneeSqrt = sqrt(knee);
-         linKneeStart = linThreshold / linKneeSqrt;
-         adjKneeStart = linKneeStart*linKneeStart;
-         float linKneeStop = linThreshold * linKneeSqrt;
-         thres = log(linThreshold);
-         kneeStart = log(linKneeStart);
-         kneeStop = log(linKneeStop);
-         compressedKneeStop = (kneeStop - thres) / ratio + thres;
          
-         float absample = average ? (fabs(det_left) + fabs(det_right)) * 0.5f : std::max(fabs(det_left), fabs(det_right));
+         float absample = average ? (fabs(*det_left) + fabs(*det_right)) * 0.5f : std::max(fabs(*det_left), fabs(*det_right));
          if(rms) absample *= absample;
              
          linSlope += (absample - linSlope) * (absample > linSlope ? attack_coeff : release_coeff);
@@@ -1577,11 -2060,11 +2060,11 @@@
      }
  }
  
- float gain_reduction_audio_module::output_level(float slope) {
+ float gain_reduction_audio_module::output_level(float slope) const {
      return slope * output_gain(slope, false) * makeup;
  }
  
- float gain_reduction_audio_module::output_gain(float linSlope, bool rms) {
+ float gain_reduction_audio_module::output_gain(float linSlope, bool rms) const {
      //this calculation is also thor's work
      if(linSlope > (rms ? adjKneeStart : linKneeStart)) {
          float slope = log(linSlope);
@@@ -1638,7 -2121,7 +2121,7 @@@ float gain_reduction_audio_module::get_
      return meter_comp;
  }
  
- bool gain_reduction_audio_module::get_graph(int subindex, float *data, int points, cairo_iface *context)
+ bool gain_reduction_audio_module::get_graph(int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -1663,7 -2146,7 +2146,7 @@@
      return true;
  }
  
- bool gain_reduction_audio_module::get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context)
+ bool gain_reduction_audio_module::get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -1682,7 -2165,7 +2165,7 @@@
      return false;
  }
  
- bool gain_reduction_audio_module::get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+ bool gain_reduction_audio_module::get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
  {
      bool tmp;
      vertical = (subindex & 1) != 0;
@@@ -1701,7 -2184,7 +2184,7 @@@
      return result;
  }
  
- int gain_reduction_audio_module::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+ int gain_reduction_audio_module::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
  {
      subindex_graph = 0;
      subindex_dot = 0;
@@@ -1862,8 -2345,6 +2345,6 @@@ inline void equalizerNband_audio_module
      }
  }
  
- #define SET_IF_CONNECTED(param) if (params[AM::param_##param] != NULL) *params[AM::param_##param] = param;
- 
  template<class BaseClass, bool has_lphp>
  uint32_t equalizerNband_audio_module<BaseClass, has_lphp>::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
  {
@@@ -1986,10 -2467,8 +2467,8 @@@
      return outputs_mask;
  }
  
- #undef SET_IF_CONNECTED
- 
  template<class BaseClass, bool has_lphp>
- bool equalizerNband_audio_module<BaseClass, has_lphp>::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ bool equalizerNband_audio_module<BaseClass, has_lphp>::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -2001,7 -2480,7 +2480,7 @@@
  }
  
  template<class BaseClass, bool has_lphp>
- bool equalizerNband_audio_module<BaseClass, has_lphp>::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+ bool equalizerNband_audio_module<BaseClass, has_lphp>::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
  {
      if (!is_active) {
          return false;
@@@ -2011,7 -2490,7 +2490,7 @@@
  }
  
  template<class BaseClass, bool has_lphp>
- int equalizerNband_audio_module<BaseClass, has_lphp>::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+ int equalizerNband_audio_module<BaseClass, has_lphp>::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const
  {
      if (!is_active) {
          return false;
@@@ -2043,7 -2522,7 +2522,7 @@@
      return false;
  }
  
- static inline float adjusted_lphp_gain(float **params, int param_active, int param_mode, biquad_d2<float> filter, float freq, float srate)
+ static inline float adjusted_lphp_gain(const float *const *params, int param_active, int param_mode, const biquad_d2<float> &filter, float freq, float srate)
  {
      if(*params[param_active] > 0.f) {
          float gain = filter.freq_gain(freq, srate);
@@@ -2060,7 -2539,7 +2539,7 @@@
  }
  
  template<class BaseClass, bool use_hplp>
- float equalizerNband_audio_module<BaseClass, use_hplp>::freq_gain(int index, double freq, uint32_t sr)
+ float equalizerNband_audio_module<BaseClass, use_hplp>::freq_gain(int index, double freq, uint32_t sr) const
  {
      float ret = 1.f;
      if (use_hplp)
@@@ -2105,7 -2584,7 +2584,7 @@@ float lfo_audio_module::get_value(
      return get_value_from_phase(phase, offset) * amount;
  }
  
- float lfo_audio_module::get_value_from_phase(float ph, float off)
+ float lfo_audio_module::get_value_from_phase(float ph, float off) const
  {
      float val = 0.f;
      float phs = ph + off;
@@@ -2172,7 -2651,7 +2651,7 @@@ void lfo_audio_module::set_params(floa
      amount = a;
  }
  
- bool lfo_audio_module::get_graph(float *data, int points, cairo_iface *context)
+ bool lfo_audio_module::get_graph(float *data, int points, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -2183,7 -2662,7 +2662,7 @@@
      return true;
  }
  
- bool lfo_audio_module::get_dot(float &x, float &y, int &size, cairo_iface *context)
+ bool lfo_audio_module::get_dot(float &x, float &y, int &size, cairo_iface *context) const
  {
      if (!is_active)
          return false;
@@@ -2350,35 -2829,19 +2829,19 @@@ uint32_t pulsator_audio_module::process
          } // cycle trough samples
      }
      // draw meters
-     if(params[param_clip_inL] != NULL) {
-         *params[param_clip_inL] = clip_inL;
-     }
-     if(params[param_clip_inR] != NULL) {
-         *params[param_clip_inR] = clip_inR;
-     }
-     if(params[param_clip_outL] != NULL) {
-         *params[param_clip_outL] = clip_outL;
-     }
-     if(params[param_clip_outR] != NULL) {
-         *params[param_clip_outR] = clip_outR;
-     }
-     
-     if(params[param_meter_inL] != NULL) {
-         *params[param_meter_inL] = meter_inL;
-     }
-     if(params[param_meter_inR] != NULL) {
-         *params[param_meter_inR] = meter_inR;
-     }
-     if(params[param_meter_outL] != NULL) {
-         *params[param_meter_outL] = meter_outL;
-     }
-     if(params[param_meter_outR] != NULL) {
-         *params[param_meter_outR] = meter_outR;
-     }
+     SET_IF_CONNECTED(clip_inL)
+     SET_IF_CONNECTED(clip_inR)
+     SET_IF_CONNECTED(clip_outL)
+     SET_IF_CONNECTED(clip_outR)
+     SET_IF_CONNECTED(meter_inL)
+     SET_IF_CONNECTED(meter_inR)
+     SET_IF_CONNECTED(meter_outL)
+     SET_IF_CONNECTED(meter_outR)
      // whatever has to be returned x)
      return outputs_mask;
  }
- bool pulsator_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
+ 
+ bool pulsator_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const
  {
      if (!is_active) {
          return false;
@@@ -2394,7 -2857,8 +2857,8 @@@
      }
      return false;
  }
- bool pulsator_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
+ 
+ bool pulsator_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const
  {
      if (!is_active) {
          return false;
@@@ -2411,7 -2875,8 +2875,8 @@@
      }
      return false;
  }
- bool pulsator_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+ 
+ bool pulsator_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
  {
     if (index == param_freq && !subindex)
      {
@@@ -2421,794 -2886,3 +2886,794 @@@
      }
      return false;
  }
 +
 +/// Saturator Band by Markus Schmidt
 +///
 +/// This module is based on Krzysztof's filters and Tom's distortion routine.
 +/// It provides a blendable saturation stage followed by a highpass, a lowpass and a peak filter
 +///////////////////////////////////////////////////////////////////////////////////////////////
 +
 +saturator_audio_module::saturator_audio_module()
 +{
 +    is_active = false;
 +    srate = 0;
 +    clip_in    = 0.f;
 +    clip_out   = 0.f;
 +    meter_in  = 0.f;
 +    meter_out = 0.f;
 +    meter_drive = 0.f;
 +}
 +
 +void saturator_audio_module::activate()
 +{
 +    is_active = true;
 +    // set all filters
 +    params_changed();
 +}
 +void saturator_audio_module::deactivate()
 +{
 +    is_active = false;
 +}
 +
 +void saturator_audio_module::params_changed()
 +{
 +    // set the params of all filters
 +    if(*params[param_lp_pre_freq] != lp_pre_freq_old) {
 +        lp[0][0].set_lp_rbj(*params[param_lp_pre_freq], 0.707, (float)srate);
 +        if(in_count > 1 && out_count > 1)
 +            lp[1][0].copy_coeffs(lp[0][0]);
 +        lp[0][1].copy_coeffs(lp[0][0]);
 +        if(in_count > 1 && out_count > 1)
 +            lp[1][1].copy_coeffs(lp[0][0]);
 +        lp_pre_freq_old = *params[param_lp_pre_freq];
 +    }
 +    if(*params[param_hp_pre_freq] != hp_pre_freq_old) {
 +        hp[0][0].set_hp_rbj(*params[param_hp_pre_freq], 0.707, (float)srate);
 +        if(in_count > 1 && out_count > 1)
 +            hp[1][0].copy_coeffs(hp[0][0]);
 +        hp[0][1].copy_coeffs(hp[0][0]);
 +        if(in_count > 1 && out_count > 1)
 +            hp[1][1].copy_coeffs(hp[0][0]);
 +        hp_pre_freq_old = *params[param_hp_pre_freq];
 +    }
 +    if(*params[param_lp_post_freq] != lp_post_freq_old) {
 +        lp[0][2].set_lp_rbj(*params[param_lp_post_freq], 0.707, (float)srate);
 +        if(in_count > 1 && out_count > 1)
 +            lp[1][2].copy_coeffs(lp[0][2]);
 +        lp[0][3].copy_coeffs(lp[0][2]);
 +        if(in_count > 1 && out_count > 1)
 +            lp[1][3].copy_coeffs(lp[0][2]);
 +        lp_post_freq_old = *params[param_lp_post_freq];
 +    }
 +    if(*params[param_hp_post_freq] != hp_post_freq_old) {
 +        hp[0][2].set_hp_rbj(*params[param_hp_post_freq], 0.707, (float)srate);
 +        if(in_count > 1 && out_count > 1)
 +            hp[1][2].copy_coeffs(hp[0][2]);
 +        hp[0][3].copy_coeffs(hp[0][2]);
 +        if(in_count > 1 && out_count > 1)
 +            hp[1][3].copy_coeffs(hp[0][2]);
 +        hp_post_freq_old = *params[param_hp_post_freq];
 +    }
 +    if(*params[param_p_freq] != p_freq_old or *params[param_p_level] != p_level_old or *params[param_p_q] != p_q_old) {
 +        p[0].set_peakeq_rbj((float)*params[param_p_freq], (float)*params[param_p_q], (float)*params[param_p_level], (float)srate);
 +        if(in_count > 1 && out_count > 1)
 +            p[1].copy_coeffs(p[0]);
 +        p_freq_old = *params[param_p_freq];
 +        p_level_old = *params[param_p_level];
 +        p_q_old = *params[param_p_q];
 +    }
 +    // set distortion
 +    dist[0].set_params(*params[param_blend], *params[param_drive]);
 +    if(in_count > 1 && out_count > 1)
 +        dist[1].set_params(*params[param_blend], *params[param_drive]);
 +}
 +
 +void saturator_audio_module::set_sample_rate(uint32_t sr)
 +{
 +    srate = sr;
 +    dist[0].set_sample_rate(sr);
 +    if(in_count > 1 && out_count > 1)
 +        dist[1].set_sample_rate(sr);
 +}
 +
 +uint32_t saturator_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
 +{
 +    bool bypass = *params[param_bypass] > 0.5f;
 +    numsamples += offset;
 +    if(bypass) {
 +        // everything bypassed
 +        while(offset < numsamples) {
 +            if(in_count > 1 && out_count > 1) {
 +                outs[0][offset] = ins[0][offset];
 +                outs[1][offset] = ins[1][offset];
 +            } else if(in_count > 1) {
 +                outs[0][offset] = (ins[0][offset] + ins[1][offset]) / 2;
 +            } else if(out_count > 1) {
 +                outs[0][offset] = ins[0][offset];
 +                outs[1][offset] = ins[0][offset];
 +            } else {
 +                outs[0][offset] = ins[0][offset];
 +            }
 +            ++offset;
 +        }
 +        // displays, too
 +        clip_in    = 0.f;
 +        clip_out   = 0.f;
 +        meter_in  = 0.f;
 +        meter_out = 0.f;
 +        meter_drive = 0.f;
 +
 +    } else {
 +        
 +        clip_in    -= std::min(clip_in,  numsamples);
 +        clip_out   -= std::min(clip_out, numsamples);
 +        meter_in = 0.f;
 +        meter_out = 0.f;
 +        meter_drive = 0.f;
 +        float in_avg[2] = {0.f, 0.f};
 +        float out_avg[2] = {0.f, 0.f};
 +        float tube_avg = 0.f;
 +        // process
 +        while(offset < numsamples) {
 +            // cycle through samples
 +            float out[2], in[2] = {0.f, 0.f};
 +            float maxIn, maxOut = 0.f;
 +            int c = 0;
 +            
 +            if(in_count > 1 && out_count > 1) {
 +                // stereo in/stereo out
 +                // handle full stereo
 +                in[0] = ins[0][offset];
 +                in[1] = ins[1][offset];
 +                c = 2;
 +            } else {
 +                // in and/or out mono
 +                // handle mono
 +                in[0] = ins[0][offset];
 +                in[1] = in[0];
 +                c = 1;
 +            }
 +            
 +            float proc[2];
 +            proc[0] = in[0] * *params[param_level_in];
 +            proc[1] = in[1] * *params[param_level_in];
 +            
 +            for (int i = 0; i < c; ++i) {
 +                // all pre filters in chain
 +                proc[i] = lp[i][1].process(lp[i][0].process(proc[i]));
 +                proc[i] = hp[i][1].process(hp[i][0].process(proc[i]));
 +                
 +                // get average for display purposes before...
 +                in_avg[i] += fabs(pow(proc[i], 2.f));
 +
 +                // ...saturate...
 +                proc[i] = dist[i].process(proc[i]);
 +                
 +                // ...and get average after...
 +                out_avg[i] += fabs(pow(proc[i], 2.f));
 +                
 +                // tone control
 +                proc[i] = p[i].process(proc[i]);
 +
 +                // all post filters in chain
 +                proc[i] = lp[i][2].process(lp[i][3].process(proc[i]));
 +                proc[i] = hp[i][2].process(hp[i][3].process(proc[i]));
 +                
 +                //subtract gain
 +                 proc[i] /= *params[param_level_in];
 +            }
 +            
 +            if(in_count > 1 && out_count > 1) {
 +                // full stereo
 +                out[0] = ((proc[0] * *params[param_mix]) + in[0] * (1 - *params[param_mix])) * *params[param_level_out];
 +                outs[0][offset] = out[0];
 +                out[1] = ((proc[1] * *params[param_mix]) + in[1] * (1 - *params[param_mix])) * *params[param_level_out];
 +                outs[1][offset] = out[1];
 +                maxIn = std::max(fabs(in[0]), fabs(in[1]));
 +                maxOut = std::max(fabs(out[0]), fabs(out[1]));
 +            } else if(out_count > 1) {
 +                // mono -> pseudo stereo
 +                out[0] = ((proc[0] * *params[param_mix]) + in[0] * (1 - *params[param_mix])) * *params[param_level_out];
 +                outs[0][offset] = out[0];
 +                out[1] = out[0];
 +                outs[1][offset] = out[1];
 +                maxOut = fabs(out[0]);
 +                maxIn = fabs(in[0]);
 +            } else {
 +                // stereo -> mono
 +                // or full mono
 +                out[0] = ((proc[0] * *params[param_mix]) + in[0] * (1 - *params[param_mix])) * *params[param_level_out];
 +                outs[0][offset] = out[0];
 +                maxIn = fabs(in[0]);
 +                maxOut = fabs(out[0]);
 +            }
 +            
 +            if(maxIn > 1.f) {
 +                clip_in  = srate >> 3;
 +            }
 +            if(maxOut > 1.f) {
 +                clip_out = srate >> 3;
 +            }
 +            // set up in / out meters
 +            if(maxIn > meter_in) {
 +                meter_in = maxIn;
 +            }
 +            if(maxOut > meter_out) {
 +                meter_out = maxOut;
 +            }
 +            
 +            // next sample
 +            ++offset;
 +        } // cycle trough samples
 +        
 +        tube_avg = (sqrt(std::max(out_avg[0], out_avg[1])) / numsamples) - (sqrt(std::max(in_avg[0], in_avg[1])) / numsamples);
 +        meter_drive = (5.0f * fabs(tube_avg) * (float(*params[param_blend]) + 30.0f));
 +        // printf("out:%.6f in: %.6f avg: %.6f drv: %.3f\n", sqrt(std::max(out_avg[0], out_avg[1])) / numsamples, sqrt(std::max(in_avg[0], in_avg[1])) / numsamples, tube_avg, meter_drive);
 +        // clean up
 +        lp[0][0].sanitize();
 +        lp[1][0].sanitize();
 +        lp[0][1].sanitize();
 +        lp[1][1].sanitize();
 +        lp[0][2].sanitize();
 +        lp[1][2].sanitize();
 +        lp[0][3].sanitize();
 +        lp[1][3].sanitize();
 +        hp[0][0].sanitize();
 +        hp[1][0].sanitize();
 +        hp[0][1].sanitize();
 +        hp[1][1].sanitize();
 +        hp[0][2].sanitize();
 +        hp[1][2].sanitize();
 +        hp[0][3].sanitize();
 +        hp[1][3].sanitize();
 +        p[0].sanitize();
 +        p[1].sanitize();
 +    }
 +    // draw meters
 +    if(params[param_clip_in] != NULL) {
 +        *params[param_clip_in] = clip_in;
 +    }
 +    if(params[param_clip_out] != NULL) {
 +        *params[param_clip_out] = clip_out;
 +    }
 +    
 +    if(params[param_meter_in] != NULL) {
 +        *params[param_meter_in] = meter_in;
 +    }
 +    if(params[param_meter_out] != NULL) {
 +        *params[param_meter_out] = meter_out;
 +    }
 +    if(params[param_meter_drive] != NULL) {
 +        *params[param_meter_drive] = meter_drive;
 +    }
 +    // whatever has to be returned x)
 +    return outputs_mask;
 +}
 +
 +/// Exciter by Markus Schmidt
 +///
 +/// This module is based on Krzysztof's filters and Tom's distortion routine.
 +/// It sends the signal through a highpass, saturates it and sends it through a highpass again
 +///////////////////////////////////////////////////////////////////////////////////////////////
 +
 +exciter_audio_module::exciter_audio_module()
 +{
 +    is_active = false;
 +    srate = 0;
 +    clip_in    = 0.f;
 +    clip_out   = 0.f;
 +    meter_in  = 0.f;
 +    meter_out = 0.f;
 +    meter_drive = 0.f;
 +}
 +
 +void exciter_audio_module::activate()
 +{
 +    is_active = true;
 +    // set all filters
 +    params_changed();
 +}
 +void exciter_audio_module::deactivate()
 +{
 +    is_active = false;
 +}
 +
 +void exciter_audio_module::params_changed()
 +{
 +    // set the params of all filters
 +    if(*params[param_freq] != freq_old) {
 +        hp[0][0].set_hp_rbj(*params[param_freq], 0.707, (float)srate);
 +        hp[0][1].copy_coeffs(hp[0][0]);
 +        hp[0][2].copy_coeffs(hp[0][0]);
 +        hp[0][3].copy_coeffs(hp[0][0]);
 +        if(in_count > 1 && out_count > 1) {
 +            hp[1][0].copy_coeffs(hp[0][0]);
 +            hp[1][1].copy_coeffs(hp[0][0]);
 +            hp[1][2].copy_coeffs(hp[0][0]);
 +            hp[1][3].copy_coeffs(hp[0][0]);
 +        }
 +        freq_old = *params[param_freq];
 +    }
 +    // set distortion
 +    dist[0].set_params(*params[param_blend], *params[param_drive]);
 +    if(in_count > 1 && out_count > 1)
 +        dist[1].set_params(*params[param_blend], *params[param_drive]);
 +}
 +
 +void exciter_audio_module::set_sample_rate(uint32_t sr)
 +{
 +    srate = sr;
 +    dist[0].set_sample_rate(sr);
 +    if(in_count > 1 && out_count > 1)
 +        dist[1].set_sample_rate(sr);
 +}
 +
 +uint32_t exciter_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
 +{
 +    bool bypass = *params[param_bypass] > 0.5f;
 +    numsamples += offset;
 +    if(bypass) {
 +        // everything bypassed
 +        while(offset < numsamples) {
 +            if(in_count > 1 && out_count > 1) {
 +                outs[0][offset] = ins[0][offset];
 +                outs[1][offset] = ins[1][offset];
 +            } else if(in_count > 1) {
 +                outs[0][offset] = (ins[0][offset] + ins[1][offset]) / 2;
 +            } else if(out_count > 1) {
 +                outs[0][offset] = ins[0][offset];
 +                outs[1][offset] = ins[0][offset];
 +            } else {
 +                outs[0][offset] = ins[0][offset];
 +            }
 +            ++offset;
 +        }
 +        // displays, too
 +        clip_in    = 0.f;
 +        clip_out   = 0.f;
 +        meter_in  = 0.f;
 +        meter_out = 0.f;
 +        meter_drive = 0.f;
 +    } else {
 +        
 +        clip_in    -= std::min(clip_in,  numsamples);
 +        clip_out   -= std::min(clip_out, numsamples);
 +        meter_in = 0.f;
 +        meter_out = 0.f;
 +        meter_drive = 0.f;
 +        
 +        // process
 +        while(offset < numsamples) {
 +            // cycle through samples
 +            float out[2], in[2] = {0.f, 0.f};
 +            float maxIn, maxOut, maxDrive = 0.f;
 +            int c = 0;
 +            
 +            if(in_count > 1 && out_count > 1) {
 +                // stereo in/stereo out
 +                // handle full stereo
 +                in[0] = ins[0][offset];
 +                in[0] *= *params[param_level_in];
 +                in[1] = ins[1][offset];
 +                in[1] *= *params[param_level_in];
 +                c = 2;
 +            } else {
 +                // in and/or out mono
 +                // handle mono
 +                in[0] = ins[0][offset];
 +                in[0] *= *params[param_level_in];
 +                in[1] = in[0];
 +                c = 1;
 +            }
 +            
 +            float proc[2];
 +            proc[0] = in[0];
 +            proc[1] = in[1];
 +            
 +            for (int i = 0; i < c; ++i) {
 +                // all pre filters in chain
 +                proc[i] = hp[i][1].process(hp[i][0].process(proc[i]));
 +                
 +                // saturate
 +                proc[i] = dist[i].process(proc[i]);
 +
 +                // all post filters in chain
 +                proc[i] = hp[i][2].process(hp[i][3].process(proc[i]));
 +            }
 +            
 +            if(in_count > 1 && out_count > 1) {
 +                // full stereo
 +                if(*params[param_listen] > 0.f)
 +                    out[0] = proc[0] * *params[param_amount] * *params[param_level_out];
 +                else
 +                    out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out];
 +                outs[0][offset] = out[0];
 +                if(*params[param_listen] > 0.f)
 +                    out[1] = proc[1] * *params[param_amount] * *params[param_level_out];
 +                else
 +                    out[1] = (proc[1] * *params[param_amount] + in[1]) * *params[param_level_out];
 +                outs[1][offset] = out[1];
 +                maxIn = std::max(fabs(in[0]), fabs(in[1]));
 +                maxOut = std::max(fabs(out[0]), fabs(out[1]));
 +                maxDrive = std::max(dist[0].get_distortion_level() * *params[param_amount],
 +                                            dist[1].get_distortion_level() * *params[param_amount]);
 +            } else if(out_count > 1) {
 +                // mono -> pseudo stereo
 +                if(*params[param_listen] > 0.f)
 +                    out[0] = proc[0] * *params[param_amount] * *params[param_level_out];
 +                else
 +                    out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out];
 +                outs[0][offset] = out[0];
 +                out[1] = out[0];
 +                outs[1][offset] = out[1];
 +                maxOut = fabs(out[0]);
 +                maxIn = fabs(in[0]);
 +                maxDrive = dist[0].get_distortion_level() * *params[param_amount];
 +            } else {
 +                // stereo -> mono
 +                // or full mono
 +                if(*params[param_listen] > 0.f)
 +                    out[0] = proc[0] * *params[param_amount] * *params[param_level_out];
 +                else
 +                    out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out];
 +                outs[0][offset] = out[0];
 +                maxIn = fabs(in[0]);
 +                maxOut = fabs(out[0]);
 +                maxDrive = dist[0].get_distortion_level() * *params[param_amount];
 +            }
 +            
 +            if(maxIn > 1.f) {
 +                clip_in  = srate >> 3;
 +            }
 +            if(maxOut > 1.f) {
 +                clip_out = srate >> 3;
 +            }
 +            // set up in / out meters
 +            if(maxIn > meter_in) {
 +                meter_in = maxIn;
 +            }
 +            if(maxOut > meter_out) {
 +                meter_out = maxOut;
 +            }
 +            if(maxDrive > meter_drive) {
 +                meter_drive = maxDrive;
 +            }
 +            
 +            // next sample
 +            ++offset;
 +        } // cycle trough samples
 +        // clean up
 +        hp[0][0].sanitize();
 +        hp[1][0].sanitize();
 +        hp[0][1].sanitize();
 +        hp[1][1].sanitize();
 +        hp[0][2].sanitize();
 +        hp[1][2].sanitize();
 +        hp[0][3].sanitize();
 +        hp[1][3].sanitize();
 +    }
 +    // draw meters
 +    if(params[param_clip_in] != NULL) {
 +        *params[param_clip_in] = clip_in;
 +    }
 +    if(params[param_clip_out] != NULL) {
 +        *params[param_clip_out] = clip_out;
 +    }
 +    
 +    if(params[param_meter_in] != NULL) {
 +        *params[param_meter_in] = meter_in;
 +    }
 +    if(params[param_meter_out] != NULL) {
 +        *params[param_meter_out] = meter_out;
 +    }
 +    if(params[param_meter_drive] != NULL) {
 +        *params[param_meter_drive] = meter_drive;
 +    }
 +    // whatever has to be returned x)
 +    return outputs_mask;
 +}
 +
 +/// Bass Enhancer by Markus Schmidt
 +///
 +/// This module is based on Krzysztof's filters and Tom's distortion routine.
 +/// It sends the signal through a lowpass, saturates it and sends it through a lowpass again
 +///////////////////////////////////////////////////////////////////////////////////////////////
 +
 +bassenhancer_audio_module::bassenhancer_audio_module()
 +{
 +    is_active = false;
 +    srate = 0;
 +    clip_in    = 0.f;
 +    clip_out   = 0.f;
 +    meter_in  = 0.f;
 +    meter_out = 0.f;
 +    meter_drive = 0.f;
 +}
 +
 +void bassenhancer_audio_module::activate()
 +{
 +    is_active = true;
 +    // set all filters
 +    params_changed();
 +}
 +void bassenhancer_audio_module::deactivate()
 +{
 +    is_active = false;
 +}
 +
 +void bassenhancer_audio_module::params_changed()
 +{
 +    // set the params of all filters
 +    if(*params[param_freq] != freq_old) {
 +        lp[0][0].set_lp_rbj(*params[param_freq], 0.707, (float)srate);
 +        lp[0][1].copy_coeffs(lp[0][0]);
 +        lp[0][2].copy_coeffs(lp[0][0]);
 +        lp[0][3].copy_coeffs(lp[0][0]);
 +        if(in_count > 1 && out_count > 1) {
 +            lp[1][0].copy_coeffs(lp[0][0]);
 +            lp[1][1].copy_coeffs(lp[0][0]);
 +            lp[1][2].copy_coeffs(lp[0][0]);
 +            lp[1][3].copy_coeffs(lp[0][0]);
 +        }
 +        freq_old = *params[param_freq];
 +    }
 +    // set distortion
 +    dist[0].set_params(*params[param_blend], *params[param_drive]);
 +    if(in_count > 1 && out_count > 1)
 +        dist[1].set_params(*params[param_blend], *params[param_drive]);
 +}
 +
 +void bassenhancer_audio_module::set_sample_rate(uint32_t sr)
 +{
 +    srate = sr;
 +    dist[0].set_sample_rate(sr);
 +    if(in_count > 1 && out_count > 1)
 +        dist[1].set_sample_rate(sr);
 +}
 +
 +uint32_t bassenhancer_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask)
 +{
 +    bool bypass = *params[param_bypass] > 0.5f;
 +    numsamples += offset;
 +    if(bypass) {
 +        // everything bypassed
 +        while(offset < numsamples) {
 +            if(in_count > 1 && out_count > 1) {
 +                outs[0][offset] = ins[0][offset];
 +                outs[1][offset] = ins[1][offset];
 +            } else if(in_count > 1) {
 +                outs[0][offset] = (ins[0][offset] + ins[1][offset]) / 2;
 +            } else if(out_count > 1) {
 +                outs[0][offset] = ins[0][offset];
 +                outs[1][offset] = ins[0][offset];
 +            } else {
 +                outs[0][offset] = ins[0][offset];
 +            }
 +            ++offset;
 +        }
 +        // displays, too
 +        clip_in    = 0.f;
 +        clip_out   = 0.f;
 +        meter_in  = 0.f;
 +        meter_out = 0.f;
 +        meter_drive = 0.f;
 +    } else {
 +        
 +        clip_in    -= std::min(clip_in,  numsamples);
 +        clip_out   -= std::min(clip_out, numsamples);
 +        meter_in = 0.f;
 +        meter_out = 0.f;
 +        meter_drive = 0.f;
 +        
 +        // process
 +        while(offset < numsamples) {
 +            // cycle through samples
 +            float out[2], in[2] = {0.f, 0.f};
 +            float maxIn, maxOut, maxDrive = 0.f;
 +            int c = 0;
 +            
 +            if(in_count > 1 && out_count > 1) {
 +                // stereo in/stereo out
 +                // handle full stereo
 +                in[0] = ins[0][offset];
 +                in[0] *= *params[param_level_in];
 +                in[1] = ins[1][offset];
 +                in[1] *= *params[param_level_in];
 +                c = 2;
 +            } else {
 +                // in and/or out mono
 +                // handle mono
 +                in[0] = ins[0][offset];
 +                in[0] *= *params[param_level_in];
 +                in[1] = in[0];
 +                c = 1;
 +            }
 +            
 +            float proc[2];
 +            proc[0] = in[0];
 +            proc[1] = in[1];
 +            
 +            for (int i = 0; i < c; ++i) {
 +                // all pre filters in chain
 +                proc[i] = lp[i][1].process(lp[i][0].process(proc[i]));
 +                
 +                // saturate
 +                proc[i] = dist[i].process(proc[i]);
 +
 +                // all post filters in chain
 +                proc[i] = lp[i][2].process(lp[i][3].process(proc[i]));
 +            }
 +            
 +            if(in_count > 1 && out_count > 1) {
 +                // full stereo
 +                if(*params[param_listen] > 0.f)
 +                    out[0] = proc[0] * *params[param_amount] * *params[param_level_out];
 +                else
 +                    out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out];
 +                outs[0][offset] = out[0];
 +                if(*params[param_listen] > 0.f)
 +                    out[1] = proc[1] * *params[param_amount] * *params[param_level_out];
 +                else
 +                    out[1] = (proc[1] * *params[param_amount] + in[1]) * *params[param_level_out];
 +                outs[1][offset] = out[1];
 +                maxIn = std::max(fabs(in[0]), fabs(in[1]));
 +                maxOut = std::max(fabs(out[0]), fabs(out[1]));
 +                maxDrive = std::max(dist[0].get_distortion_level() * *params[param_amount],
 +                                            dist[1].get_distortion_level() * *params[param_amount]);
 +            } else if(out_count > 1) {
 +                // mono -> pseudo stereo
 +                if(*params[param_listen] > 0.f)
 +                    out[0] = proc[0] * *params[param_amount] * *params[param_level_out];
 +                else
 +                    out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out];
 +                outs[0][offset] = out[0];
 +                out[1] = out[0];
 +                outs[1][offset] = out[1];
 +                maxOut = fabs(out[0]);
 +                maxIn = fabs(in[0]);
 +                maxDrive = dist[0].get_distortion_level() * *params[param_amount];
 +            } else {
 +                // stereo -> mono
 +                // or full mono
 +                if(*params[param_listen] > 0.f)
 +                    out[0] = proc[0] * *params[param_amount] * *params[param_level_out];
 +                else
 +                    out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out];
 +                outs[0][offset] = out[0];
 +                maxIn = fabs(in[0]);
 +                maxOut = fabs(out[0]);
 +                maxDrive = dist[0].get_distortion_level() * *params[param_amount];
 +            }
 +            
 +            if(maxIn > 1.f) {
 +                clip_in  = srate >> 3;
 +            }
 +            if(maxOut > 1.f) {
 +                clip_out = srate >> 3;
 +            }
 +            // set up in / out meters
 +            if(maxIn > meter_in) {
 +                meter_in = maxIn;
 +            }
 +            if(maxOut > meter_out) {
 +                meter_out = maxOut;
 +            }
 +            if(maxDrive > meter_drive) {
 +                meter_drive = maxDrive;
 +            }
 +            
 +            // next sample
 +            ++offset;
 +        } // cycle trough samples
 +        // clean up
 +        lp[0][0].sanitize();
 +        lp[1][0].sanitize();
 +        lp[0][1].sanitize();
 +        lp[1][1].sanitize();
 +        lp[0][2].sanitize();
 +        lp[1][2].sanitize();
 +        lp[0][3].sanitize();
 +        lp[1][3].sanitize();
 +    }
 +    // draw meters
 +    if(params[param_clip_in] != NULL) {
 +        *params[param_clip_in] = clip_in;
 +    }
 +    if(params[param_clip_out] != NULL) {
 +        *params[param_clip_out] = clip_out;
 +    }
 +    
 +    if(params[param_meter_in] != NULL) {
 +        *params[param_meter_in] = meter_in;
 +    }
 +    if(params[param_meter_out] != NULL) {
 +        *params[param_meter_out] = meter_out;
 +    }
 +    if(params[param_meter_drive] != NULL) {
 +        *params[param_meter_drive] = meter_drive;
 +    }
 +    // whatever has to be returned x)
 +    return outputs_mask;
 +}
 +
 +
 +/// Distortion Module by Tom Szilagyi
 +///
 +/// This module provides a blendable saturation stage
 +///////////////////////////////////////////////////////////////////////////////////////////////
 +
 +distortion_audio_module::distortion_audio_module()
 +{
 +    is_active = false;
 +    srate = 0;
 +    meter = 0.f;
 +}
 +
 +void distortion_audio_module::activate()
 +{
 +    is_active = true;
 +    set_params(0.f, 0.f);
 +}
 +void distortion_audio_module::deactivate()
 +{
 +    is_active = false;
 +}
 +
 +void distortion_audio_module::set_params(float blend, float drive)
 +{
 +    // set distortion coeffs
 +    // NOTICE!! This routine is implemented for testing purposes only!
 +    // It is taken from TAP Plugins and will act as a placeholder until
 +    // Krzysztof's distrotion routine is ready!
 +    if ((drive_old != drive) || (blend_old != blend)) {
 +        rdrive = 12.0f / drive;
 +        rbdr = rdrive / (10.5f - blend) * 780.0f / 33.0f;
 +        kpa = D(2.0f * (rdrive*rdrive) - 1.0f) + 1.0f;
 +        kpb = (2.0f - kpa) / 2.0f;
 +        ap = ((rdrive*rdrive) - kpa + 1.0f) / 2.0f;
 +        kc = kpa / D(2.0f * D(2.0f * (rdrive*rdrive) - 1.0f) - 2.0f * rdrive*rdrive);
 +
 +        srct = (0.1f * srate) / (0.1f * srate + 1.0f);
 +        sq = kc*kc + 1.0f;
 +        knb = -1.0f * rbdr / D(sq);
 +        kna = 2.0f * kc * rbdr / D(sq);
 +        an = rbdr*rbdr / sq;
 +        imr = 2.0f * knb + D(2.0f * kna + 4.0f * an - 1.0f);
 +        pwrq = 2.0f / (imr + 1.0f);
 +        
 +        drive_old = drive;
 +        blend_old = blend;
 +    }
 +}
 +
 +void distortion_audio_module::set_sample_rate(uint32_t sr)
 +{
 +    srate = sr;
 +}
 +
 +float distortion_audio_module::process(float(in))
 +{
 +    // NOTICE!! This routine is implemented for testing purposes only!
 +    // It is taken from TAP Plugins and will act as a placeholder until
 +    // Krzysztof's distrotion routine is ready!
 +    meter = 0.f;
 +    float out = 0.f;
 +    float proc = in;
 +    float med;
 +    if (proc >= 0.0f) {
 +        med = (D(ap + proc * (kpa - proc)) + kpb) * pwrq;
 +    } else {
 +        med = (D(an - proc * (kna + proc)) + knb) * pwrq * -1.0f;
 +    }
 +    proc = srct * (med - prev_med + prev_out);
 +    prev_med = M(med);
 +    prev_out = M(proc);
 +    out = proc;
 +    meter = proc;
 +    return out;
 +}
 +
 +float distortion_audio_module::get_distortion_level()
 +{
 +    return meter;
 +}

-- 
calf audio plugins packaging



More information about the pkg-multimedia-commits mailing list