[SCM] calf/master: + Multiband Compressor: new module (first version, by Markus Schmidt, based on code by me and Thor) plus associated refactoring and graph colour scheme update

js at users.alioth.debian.org js at users.alioth.debian.org
Tue May 7 15:39:36 UTC 2013


The following commit has been merged in the master branch:
commit 9912931955299424aac7f32685d1a04003765995
Author: Krzysztof Foltman <wdev at foltman.com>
Date:   Thu Oct 8 23:34:28 2009 +0100

    + Multiband Compressor: new module (first version, by Markus Schmidt, based on code by me and Thor) plus associated refactoring and graph colour scheme update

diff --git a/knob.png b/knob.png
deleted file mode 100644
index 1047e49..0000000
Binary files a/knob.png and /dev/null differ
diff --git a/src/calf/giface.h b/src/calf/giface.h
index 320b04b..63d165e 100644
--- a/src/calf/giface.h
+++ b/src/calf/giface.h
@@ -195,7 +195,7 @@ struct line_graph_iface
     /// @param subindex_dot First dot that has to be redrawn
     /// @param subindex_gridline First gridline/legend that has to be redrawn
     /// @retval Current generation (to pass when calling the function next time); if different than passed generation value, call the function again to retrieve which graph offsets should be put into cache
-    virtual int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) { subindex_graph = subindex_dot = subindex_gridline = 0; return 0; }
+    virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) { subindex_graph = subindex_dot = subindex_gridline = 0; return 0; }
     
     /// Standard destructor to make compiler happy
     virtual ~line_graph_iface() {}
diff --git a/src/calf/metadata.h b/src/calf/metadata.h
index 4d2bd0c..a10ef3b 100644
--- a/src/calf/metadata.h
+++ b/src/calf/metadata.h
@@ -136,6 +136,27 @@ struct compressor_metadata: public plugin_metadata<compressor_metadata>
     PLUGIN_NAME_ID_LABEL("compressor", "compressor", "Compressor")
 };
 
+/// Markus's multibandcompressor - metadata
+struct multibandcompressor_metadata: public plugin_metadata<multibandcompressor_metadata>
+{
+    enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true };
+    enum { param_bypass, param_level_in, param_level_out, param_meter_inL, param_meter_inR,
+           param_meter_outL, param_meter_outR, param_clip_inL, param_clip_inR, param_clip_outL, param_clip_outR,
+           param_freq0, param_freq1, param_freq2,
+           param_sep0, param_sep1, param_sep2,
+           param_q0, param_q1, param_q2,
+           param_threshold0, param_ratio0, param_attack0, param_release0, param_makeup0, param_knee0,
+           param_detection0, param_compression0, param_output0, param_bypass0, param_mute0,
+           param_threshold1, param_ratio1, param_attack1, param_release1, param_makeup1, param_knee1,
+           param_detection1, param_compression1, param_output1, param_bypass1, param_mute1,
+           param_threshold2, param_ratio2, param_attack2, param_release2, param_makeup2, param_knee2,
+           param_detection2, param_compression2, param_output2, param_bypass2, param_mute2,
+           param_threshold3, param_ratio3, param_attack3, param_release3, param_makeup3, param_knee3,
+           param_detection3, param_compression3, param_output3, param_bypass3, param_mute3,
+           param_count };
+    PLUGIN_NAME_ID_LABEL("multibandcompressor", "multibandcompressor", "Multibandcompressor")
+};
+
 /// Organ - enums for parameter IDs etc. (this mess is caused by organ split between plugin and generic class - which was
 /// a bad design decision and should be sorted out some day) XXXKF @todo
 struct organ_enums
diff --git a/src/calf/modulelist.h b/src/calf/modulelist.h
index 0c9f0b8..ca99065 100644
--- a/src/calf/modulelist.h
+++ b/src/calf/modulelist.h
@@ -10,6 +10,7 @@
     PER_MODULE_ITEM(phaser, false, "phaser")
     PER_MODULE_ITEM(multichorus, false, "multichorus")
     PER_MODULE_ITEM(compressor, false, "compressor")
+    PER_MODULE_ITEM(multibandcompressor, false, "multibandcompressor")
 #ifdef ENABLE_EXPERIMENTAL
     PER_MODULE_ITEM(fluidsynth, true, "fluidsynth")
     PER_MODULE_ITEM(wavetable, true, "wavetable")
diff --git a/src/calf/modules.h b/src/calf/modules.h
index 29aa47e..de0e46c 100644
--- a/src/calf/modules.h
+++ b/src/calf/modules.h
@@ -66,7 +66,7 @@ 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 generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
+    virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
 };
 
 class flanger_audio_module: public audio_module<flanger_metadata>, public frequency_response_line_graph
@@ -754,7 +754,7 @@ public:
     }
     
     bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
-    int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
+    int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
 };
 
 /// A multitap stereo chorus thing - processing
@@ -875,18 +875,18 @@ public:
     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 generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+    virtual int  get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
     {
-	subindex_graph = 0;
-	subindex_dot = 0;
-	subindex_gridline = generation ? INT_MAX : 0;
+        subindex_graph = 0;
+        subindex_dot = 0;
+        subindex_gridline = generation ? INT_MAX : 0;
 
         if (fabs(threshold-old_threshold) + fabs(ratio - old_ratio) + fabs(knee - old_knee) + fabs( makeup - old_makeup) + fabs( *params[param_bypass] - old_bypass) > 0.01f)
         {
-	    old_threshold = threshold;
-	    old_ratio = ratio;
-	    old_knee = knee;
-	    old_makeup = makeup;
+            old_threshold = threshold;
+            old_ratio = ratio;
+            old_knee = knee;
+            old_makeup = makeup;
             old_bypass = *params[param_bypass];
             last_generation++;
         }
@@ -897,6 +897,64 @@ public:
     }
 };
 
+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, bypass, mute, meter_out, meter_comp;
+    float old_threshold, old_ratio, old_knee, old_makeup, old_bypass;
+    int last_generation;
+    uint32_t srate;
+    bool is_active;
+    inline float output_level(float slope);
+    inline float output_gain(float linSlope, bool rms);
+public:
+    gain_reduction_audio_module();
+    void set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float byp, float mu);
+    void process(float &left, float &right);
+    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);
+};
+
+/// Multibandcompressor by Markus Schmidt
+class multibandcompressor_audio_module: public audio_module<multibandcompressor_metadata>, public line_graph_iface {
+private:
+    static const int strips = 4;
+    float meter_out[strips];
+    float meter_comp[strips];
+    bool mute[strips];
+    uint32_t clip_inL, clip_inR, clip_outL, clip_outR;
+    float meter_inL, meter_inR, meter_outL, meter_outR;
+    gain_reduction_audio_module strip[strips];
+    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();
+    void activate();
+    void deactivate();
+    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);
+};
+
+
 /// Filterclavier --- MIDI controlled filter by Hans Baier
 class filterclavier_audio_module: 
         public audio_module<filterclavier_metadata>, 
diff --git a/src/custom_ctl.cpp b/src/custom_ctl.cpp
index 586b7da..969ab7c 100644
--- a/src/custom_ctl.cpp
+++ b/src/custom_ctl.cpp
@@ -17,6 +17,8 @@
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
  * Boston, MA  02110-1301  USA
  */
+
+    
 #include "config.h"
 #include <calf/custom_ctl.h>
 #include <gdk/gdkkeysyms.h>
@@ -70,37 +72,37 @@ calf_line_graph_draw_grid( cairo_t *c, std::string &legend, bool vertical, float
     int ox=1, oy=1;
     cairo_text_extents_t tx;
     if (!legend.empty())
-	cairo_text_extents(c, legend.c_str(), &tx);
+        cairo_text_extents(c, legend.c_str(), &tx);
     if (vertical)
     {
-	float x = floor(ox + pos * sx) + 0.5;
-	if (phase == 1)
-	{
-	    cairo_move_to(c, x, oy);
-	    cairo_line_to(c, x, oy + sy);
-	    cairo_stroke(c);
-	}
-	if (phase == 2 && !legend.empty()) {
-
-	    cairo_set_source_rgba(c, 1.0, 1.0, 1.0, 0.75);
-	    cairo_move_to(c, x - (tx.x_bearing + tx.width / 2.0), oy + sy - 2);
-	    cairo_show_text(c, legend.c_str());
-	}
+        float x = floor(ox + pos * sx) + 0.5;
+        if (phase == 1)
+        {
+            cairo_move_to(c, x, oy);
+            cairo_line_to(c, x, oy + sy);
+            cairo_stroke(c);
+        }
+        if (phase == 2 && !legend.empty()) {
+
+            cairo_set_source_rgba(c, 0.0, 0.0, 0.0, 0.7);
+            cairo_move_to(c, x - (tx.x_bearing + tx.width / 2.0) - 2, oy + sy - 2);
+            cairo_show_text(c, legend.c_str());
+        }
     }
     else
     {
-	float y = floor(oy + sy / 2 - (sy / 2 - 1) * pos) + 0.5;
-	if (phase == 1)
-	{
-	    cairo_move_to(c, ox, y);
-	    cairo_line_to(c, ox + sx, y);
-	    cairo_stroke(c);
-	}
-	if (phase == 2 && !legend.empty()) {
-	    cairo_set_source_rgba(c, 1.0, 1.0, 1.0, 0.75);
-	    cairo_move_to(c, ox + sx - 2 - tx.width, y + tx.height/2 - 1);
-	    cairo_show_text(c, legend.c_str());
-	}
+        float y = floor(oy + sy / 2 - (sy / 2 - 1) * pos) + 0.5;
+        if (phase == 1)
+        {
+            cairo_move_to(c, ox, y);
+            cairo_line_to(c, ox + sx, y);
+            cairo_stroke(c);
+        }
+        if (phase == 2 && !legend.empty()) {
+            cairo_set_source_rgba(c, 0.0, 0.0, 0.0, 0.7);
+            cairo_move_to(c, ox + sx - 4 - tx.width, y + tx.height/2);
+            cairo_show_text(c, legend.c_str());
+        }
     }
 }
 
@@ -111,13 +113,13 @@ calf_line_graph_draw_graph( cairo_t *c, float *data, int sx, int sy )
 
     for (int i = 0; i < 2 * sx; i++)
     {
-	int y = (int)(oy + sy / 2 - (sy / 2 - 1) * data[i]);
-	//if (y < oy) y = oy;
-	//if (y >= oy + sy) y = oy + sy - 1;
-	if (i)
-	    cairo_line_to(c, ox + i * 0.5, y);
-	else
-	    cairo_move_to(c, ox, y);
+        int y = (int)(oy + sy / 2 - (sy / 2 - 1) * data[i]);
+        //if (y < oy) y = oy;
+        //if (y >= oy + sy) y = oy + sy - 1;
+        if (i)
+            cairo_line_to(c, ox + i * 0.5, y);
+        else
+            cairo_move_to(c, ox, y);
     }
     cairo_stroke(c);
 }
@@ -126,37 +128,35 @@ static gboolean
 calf_line_graph_expose (GtkWidget *widget, GdkEventExpose *event)
 {
     g_assert(CALF_IS_LINE_GRAPH(widget));
-    
+
     CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
     //int ox = widget->allocation.x + 1, oy = widget->allocation.y + 1;
     int ox = 1, oy = 1;
     int sx = widget->allocation.width - 2, sy = widget->allocation.height - 2;
-    
-    cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
 
+    cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
+    GtkStyle	*style;
+    style = gtk_widget_get_style(widget);
     GdkColor sc = { 0, 0, 0, 0 };
 
     bool cache_dirty = 0;
 
-
-
     if( lg->cache_surface == NULL ) {
-	// looks like its either first call or the widget has been resized.
-	// create the cache_surface.
-	cairo_surface_t *window_surface = cairo_get_target( c );
-	lg->cache_surface = cairo_surface_create_similar( window_surface, 
-							  CAIRO_CONTENT_COLOR,
-							  widget->allocation.width,
-							  widget->allocation.height );
-	//cairo_set_source_surface( cache_cr, window_surface, 0,0 );
-	//cairo_paint( cache_cr );
-	
-	cache_dirty = 1;
+        // looks like its either first call or the widget has been resized.
+        // create the cache_surface.
+        cairo_surface_t *window_surface = cairo_get_target( c );
+        lg->cache_surface = cairo_surface_create_similar( window_surface, 
+                                  CAIRO_CONTENT_COLOR,
+                                  widget->allocation.width,
+                                  widget->allocation.height );
+        //cairo_set_source_surface( cache_cr, window_surface, 0,0 );
+        //cairo_paint( cache_cr );
+        
+        cache_dirty = 1;
     }
 
     cairo_select_font_face(c, "Bitstream Vera Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
     cairo_set_font_size(c, 9);
-
     gdk_cairo_set_source_color(c, &sc);
     cairo_rectangle(c, ox, oy, sx, sy);
     cairo_clip(c);
@@ -170,75 +170,83 @@ calf_line_graph_expose (GtkWidget *widget, GdkEventExpose *event)
         bool vertical = false;
         std::string legend;
         float *data = new float[2 * sx];
-        GdkColor sc2 = { 0, 0, 65535, 0 };
+        GdkColor sc2 = { 0, 0.35 * 65535, 0.4 * 65535, 0.2 * 65535 };
         float x, y;
         int size = 0;
-        GdkColor sc3 = { 0, 32767, 65535, 0 };
-
-	int graph_n, grid_n, dot_n, grid_n_save;
-
-	int cache_graph_index, cache_dot_index, cache_grid_index;
-	int gen_index = lg->source->get_changed_offsets( lg->last_generation, cache_graph_index, cache_dot_index, cache_grid_index );
-
-	if( cache_dirty || (gen_index != lg->last_generation) ) {
-
-	    cairo_t *cache_cr = cairo_create( lg->cache_surface );
-	    cairo_select_font_face(cache_cr, "Bitstream Vera Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
-	    cairo_set_font_size(cache_cr, 9);
-
-	    gdk_cairo_set_source_color(cache_cr, &sc);
-	    cairo_rectangle(cache_cr, ox, oy, sx, sy);
-	    cairo_clip_preserve(cache_cr);
-	    cairo_fill(cache_cr);
-
-	    cairo_impl cache_cimpl;
-	    cache_cimpl.context = cache_cr;
-
-	    lg->source->get_changed_offsets( gen_index, cache_graph_index, cache_dot_index, cache_grid_index );
-	    lg->last_generation = gen_index;
-
-	    cairo_set_line_width(cache_cr, 1);
-	    for(int phase = 1; phase <= 2; phase++)
-	    {
-		for(grid_n = 0; legend = std::string(), cairo_set_source_rgba(cache_cr, 1, 1, 1, 0.5), (grid_n<cache_grid_index) &&  lg->source->get_gridline(lg->source_id, grid_n, pos, vertical, legend, &cache_cimpl); grid_n++)
-		{
-		    calf_line_graph_draw_grid( cache_cr, legend, vertical, pos, phase, sx, sy );
-		}
-	    }
-	    grid_n_save = grid_n;
-
-	    gdk_cairo_set_source_color(cache_cr, &sc2);
-	    cairo_set_line_join(cache_cr, CAIRO_LINE_JOIN_MITER);
-	    cairo_set_line_width(cache_cr, 1);
-	    for(graph_n = 0; (graph_n<cache_graph_index) && lg->source->get_graph(lg->source_id, graph_n, data, 2 * sx, &cache_cimpl); graph_n++)
-	    {
-		calf_line_graph_draw_graph( cache_cr, data, sx, sy );
-	    }
-	    gdk_cairo_set_source_color(cache_cr, &sc3);
-	    for(dot_n = 0; (dot_n<cache_dot_index) && lg->source->get_dot(lg->source_id, dot_n, x, y, size = 3, &cache_cimpl); dot_n++)
-	    {
-		int yv = (int)(oy + sy / 2 - (sy / 2 - 1) * y);
-		cairo_arc(cache_cr, ox + x * sx, yv, size, 0, 2 * M_PI);
-		cairo_fill(cache_cr);
-	    }
-
-	    // copy window to cache.
-	    cairo_destroy( cache_cr );
-	    calf_line_graph_copy_cache_to_window( lg, c );
-	} else {
-	    grid_n_save = cache_grid_index;
-	    graph_n = cache_graph_index;
-	    dot_n = cache_dot_index;
-	    calf_line_graph_copy_cache_to_window( lg, c );
-	}
+        GdkColor sc3 = { 0, 0.35 * 65535, 0.4 * 65535, 0.2 * 65535 };
+
+        int graph_n, grid_n, dot_n, grid_n_save;
+
+        int cache_graph_index, cache_dot_index, cache_grid_index;
+        int gen_index = lg->source->get_changed_offsets( lg->source_id, lg->last_generation, cache_graph_index, cache_dot_index, cache_grid_index );
+
+        if( cache_dirty || (gen_index != lg->last_generation) ) {
+            
+            cairo_t *cache_cr = cairo_create( lg->cache_surface );
+            cairo_select_font_face(cache_cr, "Bitstream Vera Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+            cairo_set_font_size(cache_cr, 9);
+            
+            cairo_pattern_t *pt = cairo_pattern_create_linear(ox, oy, ox, sy);
+            cairo_pattern_add_color_stop_rgb(pt, 0.0,     0.85,    0.95,    0.45);
+            cairo_pattern_add_color_stop_rgb(pt, 0.5,     0.8,     0.9,     0.4);
+            cairo_pattern_add_color_stop_rgb(pt, 1.0,     0.85,    0.95,    0.45);
+            //gdk_cairo_set_source_color(cache_cr, &sc);
+            cairo_set_source (cache_cr, pt);
+            cairo_rectangle(cache_cr, ox, oy, sx, sy);
+            cairo_clip_preserve(cache_cr);
+            cairo_fill_preserve(cache_cr);
+//            cairo_set_source_rgba(cache_cr, 0, 0, 0, 0.5);
+//            cairo_set_line_width(cache_cr, 1);
+//            cairo_stroke(cache_cr);
+
+            cairo_impl cache_cimpl;
+            cache_cimpl.context = cache_cr;
+
+            lg->source->get_changed_offsets( lg->source_id, gen_index, cache_graph_index, cache_dot_index, cache_grid_index );
+            lg->last_generation = gen_index;
+
+            cairo_set_line_width(cache_cr, 1);
+            for(int phase = 1; phase <= 2; phase++)
+            {
+                for(grid_n = 0; legend = std::string(), cairo_set_source_rgba(cache_cr, 0, 0, 0, 0.6), (grid_n<cache_grid_index) &&  lg->source->get_gridline(lg->source_id, grid_n, pos, vertical, legend, &cache_cimpl); grid_n++)
+                {
+                    calf_line_graph_draw_grid( cache_cr, legend, vertical, pos, phase, sx, sy );
+                }
+            }
+            grid_n_save = grid_n;
+
+            gdk_cairo_set_source_color(cache_cr, &sc2);
+            cairo_set_line_join(cache_cr, CAIRO_LINE_JOIN_MITER);
+            cairo_set_line_width(cache_cr, 1);
+            for(graph_n = 0; (graph_n<cache_graph_index) && lg->source->get_graph(lg->source_id, graph_n, data, 2 * sx, &cache_cimpl); graph_n++)
+            {
+                calf_line_graph_draw_graph( cache_cr, data, sx, sy );
+            }
+            gdk_cairo_set_source_color(cache_cr, &sc3);
+            for(dot_n = 0; (dot_n<cache_dot_index) && lg->source->get_dot(lg->source_id, dot_n, x, y, size = 3, &cache_cimpl); dot_n++)
+            {
+                int yv = (int)(oy + sy / 2 - (sy / 2 - 1) * y);
+                cairo_arc(cache_cr, ox + x * sx, yv, size, 0, 2 * M_PI);
+                cairo_fill(cache_cr);
+            }
+
+            // copy window to cache.
+            cairo_destroy( cache_cr );
+            calf_line_graph_copy_cache_to_window( lg, c );
+        } else {
+            grid_n_save = cache_grid_index;
+            graph_n = cache_graph_index;
+            dot_n = cache_dot_index;
+            calf_line_graph_copy_cache_to_window( lg, c );
+        }
 
 
         cairo_set_line_width(c, 1);
         for(int phase = 1; phase <= 2; phase++)
         {
-            for(int gn=grid_n_save; legend = std::string(), cairo_set_source_rgba(c, 1, 1, 1, 0.5), lg->source->get_gridline(lg->source_id, gn, pos, vertical, legend, &cimpl); gn++)
+            for(int gn=grid_n_save; legend = std::string(), cairo_set_source_rgba(c, 0, 0, 0, 0.6), lg->source->get_gridline(lg->source_id, gn, pos, vertical, legend, &cimpl); gn++)
             {
-		calf_line_graph_draw_grid( c, legend, vertical, pos, phase, sx, sy );
+                calf_line_graph_draw_grid( c, legend, vertical, pos, phase, sx, sy );
             }
         }
 
@@ -247,7 +255,7 @@ calf_line_graph_expose (GtkWidget *widget, GdkEventExpose *event)
         cairo_set_line_width(c, 1);
         for(int gn = graph_n; lg->source->get_graph(lg->source_id, gn, data, 2 * sx, &cimpl); gn++)
         {
-	    calf_line_graph_draw_graph( c, data, sx, sy );
+            calf_line_graph_draw_graph( c, data, sx, sy );
         }
         gdk_cairo_set_source_color(c, &sc3);
         for(int gn = dot_n; lg->source->get_dot(lg->source_id, gn, x, y, size = 3, &cimpl); gn++)
@@ -258,12 +266,12 @@ calf_line_graph_expose (GtkWidget *widget, GdkEventExpose *event)
         }
         delete []data;
     }
-    
+
     cairo_destroy(c);
-    
+
     gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, ox - 1, oy - 1, sx + 2, sy + 2);
     // printf("exposed %p %dx%d %d+%d\n", widget->window, event->area.x, event->area.y, event->area.width, event->area.height);
-    
+
     return TRUE;
 }
 
@@ -280,7 +288,7 @@ int calf_line_graph_update_if(CalfLineGraph *graph, int last_drawn_generation)
     if (graph->source)
     {
         int subgraph, dot, gridline;
-        generation = graph->source->get_changed_offsets(generation, subgraph, dot, gridline);
+        generation = graph->source->get_changed_offsets(graph->source_id, generation, subgraph, dot, gridline);
         if (subgraph == INT_MAX && dot == INT_MAX && gridline == INT_MAX && generation == last_drawn_generation)
             return generation;
         gtk_widget_queue_draw(GTK_WIDGET(graph));
@@ -303,11 +311,11 @@ calf_line_graph_size_allocate (GtkWidget *widget,
 {
     g_assert(CALF_IS_LINE_GRAPH(widget));
     CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
-    
+
     GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_LINE_GRAPH_GET_CLASS( lg ) );
 
     if( lg->cache_surface )
-	cairo_surface_destroy( lg->cache_surface );
+        cairo_surface_destroy( lg->cache_surface );
     lg->cache_surface = NULL;
     
     widget->allocation = *allocation;
@@ -397,87 +405,93 @@ calf_vumeter_expose (GtkWidget *widget, GdkEventExpose *event)
 {
     g_assert(CALF_IS_VUMETER(widget));
 
-    
+
     CalfVUMeter *vu = CALF_VUMETER(widget);
+    GtkStyle	*style;
     //int ox = widget->allocation.x + 1, oy = widget->allocation.y + 1;
     int ox = 1, oy = 1;
-    int sx = widget->allocation.width - 2, sy = widget->allocation.height - 2;
-    
+    int sx = widget->allocation.width - 1 - ((widget->allocation.width - 2) % 3), sy = widget->allocation.height - 2;
+    style = gtk_widget_get_style(widget);
     cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
 
     if( vu->cache_surface == NULL ) {
-	// looks like its either first call or the widget has been resized.
-	// create the cache_surface.
-	cairo_surface_t *window_surface = cairo_get_target( c );
-	vu->cache_surface = cairo_surface_create_similar( window_surface, 
-							  CAIRO_CONTENT_COLOR,
-							  widget->allocation.width,
-							  widget->allocation.height );
-
-	// And render the meterstuff again.
-    
-	cairo_t *cache_cr = cairo_create( vu->cache_surface );
-	GdkColor sc = { 0, 0, 0, 0 };
-	gdk_cairo_set_source_color(cache_cr, &sc);
-	cairo_rectangle(cache_cr, ox, oy, sx, sy);
-	cairo_fill(cache_cr);
-	cairo_set_line_width(cache_cr, 1);
-
-	for (int x = ox; x <= ox + sx; x += 3)
-	{
-	    float ts = (x - ox) * 1.0 / sx;
-	    float r = 0.f, g = 0.f, b = 0.f;
-	    switch(vu->mode)
-	    {
-		case VU_STANDARD:
-		default:
-		    if (ts < 0.75)
-			r = ts / 0.75, g = 1, b = 0;
-		    else
-			r = 1, g = 1 - (ts - 0.75) / 0.25, b = 0;
-		    //                if (vu->value < ts || vu->value <= 0)
-		    //                    r *= 0.5, g *= 0.5, b *= 0.5;
-		    break;
-		case VU_MONOCHROME_REVERSE:
-		    r = 1, g = 1, b = 0;
-		    //                if (!(vu->value < ts) || vu->value >= 1.0)
-		    //                    r *= 0.5, g *= 0.5, b *= 0.5;
-		    break;
-		case VU_MONOCHROME:
-		    r = 1, g = 1, b = 0;
-		    //                if (vu->value < ts || vu->value <= 0)
-		    //                    r *= 0.5, g *= 0.5, b *= 0.5;
-		    break;
-	    }
-	    GdkColor sc2 = { 0, (guint16)(65535 * r), (guint16)(65535 * g), (guint16)(65535 * b) };
-	    gdk_cairo_set_source_color(cache_cr, &sc2);
-	    cairo_move_to(cache_cr, x, oy);
-	    cairo_line_to(cache_cr, x, oy + sy + 1);
-	    cairo_move_to(cache_cr, x, oy + sy);
-	    cairo_line_to(cache_cr, x, oy );
-	    cairo_stroke(cache_cr);
-	}
+    // looks like its either first call or the widget has been resized.
+    // create the cache_surface.
+    cairo_surface_t *window_surface = cairo_get_target( c );
+    vu->cache_surface = cairo_surface_create_similar( window_surface, 
+                              CAIRO_CONTENT_COLOR,
+                              widget->allocation.width,
+                              widget->allocation.height );
+
+    // And render the meterstuff again.
+
+    cairo_t *cache_cr = cairo_create( vu->cache_surface );
+    GdkColor sc = { 0, 0, 0, 0 };
+    gdk_cairo_set_source_color(cache_cr,&style->bg[GTK_STATE_NORMAL]);
+    cairo_paint(cache_cr);
+    gdk_cairo_set_source_color(cache_cr, &sc);
+    cairo_rectangle(cache_cr, ox, oy, sx, sy);
+    cairo_fill(cache_cr);
+    cairo_set_line_width(cache_cr, 1);
+
+    for (int x = ox + 1; x <= ox + sx - 3; x += 3)
+    {
+        float ts = (x - ox) * 1.0 / sx;
+        float r = 0.f, g = 0.f, b = 0.f;
+        switch(vu->mode)
+        {
+        case VU_STANDARD:
+        default:
+            if (ts < 0.75)
+            r = ts / 0.75, g = 0.5 + ts * 0.66, b = 1 - ts / 0.75;
+            else
+            r = 1, g = 1 - (ts - 0.75) / 0.25, b = 0;
+            //                if (vu->value < ts || vu->value <= 0)
+            //                    r *= 0.5, g *= 0.5, b *= 0.5;
+            break;
+        case VU_MONOCHROME_REVERSE:
+            r = 0, g = 170.0 / 255.0, b = 1;
+            //                if (!(vu->value < ts) || vu->value >= 1.0)
+            //                    r *= 0.5, g *= 0.5, b *= 0.5;
+            break;
+        case VU_MONOCHROME:
+            r = 0, g = 170.0 / 255.0, b = 1;
+            //                if (vu->value < ts || vu->value <= 0)
+            //                    r *= 0.5, g *= 0.5, b *= 0.5;
+            break;
+        }
+        GdkColor sc2 = { 0, (guint16)(65535 * r + 0.2), (guint16)(65535 * g), (guint16)(65535 * b) };
+        GdkColor sc3 = { 0, (guint16)(65535 * r * 0.7), (guint16)(65535 * g * 0.7), (guint16)(65535 * b * 0.7) };
+        gdk_cairo_set_source_color(cache_cr, &sc2);
+        cairo_move_to(cache_cr, x + 0.5, oy + 1);
+        cairo_line_to(cache_cr, x + 0.5, oy + sy - 1);
+        cairo_stroke(cache_cr);
+        gdk_cairo_set_source_color(cache_cr, &sc3);
+        cairo_move_to(cache_cr, x + 1.5, oy + sy - 1);
+        cairo_line_to(cache_cr, x + 1.5, oy + 1);
+        cairo_stroke(cache_cr);
+    }
         cairo_destroy( cache_cr );
     }
 
     cairo_set_source_surface( c, vu->cache_surface, 0,0 );
     cairo_paint( c );
-    cairo_set_source_rgba( c, 0,0,0, 0.5 );
+    cairo_set_source_rgba( c, 0,0,0, 0.6 );
 
     if( vu->mode == VU_MONOCHROME_REVERSE )
-        cairo_rectangle( c, ox,oy, vu->value * (sx-ox) + 1, sy-oy+1 );
+        cairo_rectangle( c, ox,oy, vu->value * sx, sy );
     else
-        cairo_rectangle( c, ox + vu->value * (sx-ox), oy, sx-ox+1, sy-oy+1 );
+        cairo_rectangle( c, ox + vu->value * sx, oy, sx * (1 - vu->value), sy );
 
     cairo_fill( c );
 
 
 
     cairo_destroy(c);
-    
+
     gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, ox - 1, oy - 1, sx + 2, sy + 2);
     //printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
-    
+
     return TRUE;
 }
 
@@ -486,7 +500,7 @@ calf_vumeter_size_request (GtkWidget *widget,
                            GtkRequisition *requisition)
 {
     g_assert(CALF_IS_VUMETER(widget));
-    
+
     requisition->width = 50;
     requisition->height = 14;
 }
@@ -503,7 +517,7 @@ calf_vumeter_size_allocate (GtkWidget *widget,
     parent_class->size_allocate( widget, allocation );
 
     if( vu->cache_surface )
-	cairo_surface_destroy( vu->cache_surface );
+    cairo_surface_destroy( vu->cache_surface );
     vu->cache_surface = NULL;
 }
 
@@ -610,10 +624,10 @@ calf_knob_expose (GtkWidget *widget, GdkEventExpose *event)
 
     // printf("adjustment = %p value = %f\n", adj, adj->value);
     int ox = widget->allocation.x, oy = widget->allocation.y;
-    
-    ox += (widget->allocation.width - 40) / 2;
-    oy += (widget->allocation.height - 40) / 2;
-    
+
+    ox += (widget->allocation.width - self->knob_size * 20) / 2;
+    oy += (widget->allocation.height - self->knob_size * 20) / 2;
+
     int phase = (int)((adj->value - adj->lower) * 64 / (adj->upper - adj->lower));
     // skip middle phase except for true middle value
     if (self->knob_type == 1 && phase == 32) {
@@ -634,13 +648,13 @@ calf_knob_expose (GtkWidget *widget, GdkEventExpose *event)
         if (diff < -0.0001)
             phase = (phase + 63) % 64;
     }
-    gdk_draw_pixbuf(GDK_DRAWABLE(widget->window), widget->style->fg_gc[0], CALF_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget))->knob_image, phase * 40, self->knob_type * 40, ox, oy, 40, 40, GDK_RGB_DITHER_NORMAL, 0, 0);
+    gdk_draw_pixbuf(GDK_DRAWABLE(widget->window), widget->style->fg_gc[0], CALF_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget))->knob_image[self->knob_size - 1], phase * self->knob_size * 20, self->knob_type * self->knob_size * 20, ox, oy, self->knob_size * 20, self->knob_size * 20, GDK_RGB_DITHER_NORMAL, 0, 0);
     // printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
     if (gtk_widget_is_focus(widget))
     {
-        gtk_paint_focus(widget->style, window, GTK_STATE_NORMAL, NULL, widget, NULL, ox, oy, 40, 40);
+        gtk_paint_focus(widget->style, window, GTK_STATE_NORMAL, NULL, widget, NULL, ox, oy, self->knob_size * 20, self->knob_size * 20);
     }
-    
+
     return TRUE;
 }
 
@@ -649,10 +663,13 @@ calf_knob_size_request (GtkWidget *widget,
                            GtkRequisition *requisition)
 {
     g_assert(CALF_IS_KNOB(widget));
-    
+
+    CalfKnob *self = CALF_KNOB(widget);
+
     // width/height is hardwired at 40px now
-    requisition->width = 40;
-    requisition->height = 40;
+    // is now chooseable by "size" value in XML (1-4)
+    requisition->width  = 20 * self->knob_size;
+    requisition->height = 20 * self->knob_size;
 }
 
 static void
@@ -661,7 +678,7 @@ calf_knob_incr (GtkWidget *widget, int dir_down)
     g_assert(CALF_IS_KNOB(widget));
     CalfKnob *self = CALF_KNOB(widget);
     GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
-    
+
     int oldstep = (int)(0.5f + (adj->value - adj->lower) / adj->step_increment);
     int step;
     int nsteps = (int)(0.5f + (adj->upper - adj->lower) / adj->step_increment); // less 1 actually
@@ -673,7 +690,7 @@ calf_knob_incr (GtkWidget *widget, int dir_down)
         step %= nsteps;
     if (self->knob_type == 3 && step < 0)
         step = nsteps - (nsteps - step) % nsteps;
-    
+
     // trying to reduce error cumulation here, by counting from lowest or from highest
     float value = adj->lower + step * double(adj->upper - adj->lower) / nsteps;
     gtk_range_set_value(GTK_RANGE(widget), value);
@@ -711,7 +728,7 @@ calf_knob_key_press (GtkWidget *widget, GdkEventKey *event)
             self->start_y = self->last_y;
             return TRUE;
     }
-    
+
     return FALSE;
 }
 
@@ -772,9 +789,9 @@ static inline float deadzone(float value, float incr, float scale)
         value += dzw;
     if (value < 0.499)
         value -= dzw;
-    
+
     value += incr;
-    
+
     if (value >= (0.5 - dzw) && value <= (0.5 + dzw))
         return 0.5;
     if (value < 0.5)
@@ -788,27 +805,29 @@ calf_knob_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
     g_assert(CALF_IS_KNOB(widget));
     CalfKnob *self = CALF_KNOB(widget);
 
-    float scale = (event->state & GDK_SHIFT_MASK) ? 1000 : 100;
+    float scale = (event->state & GDK_SHIFT_MASK) ? 10 : 1;
     gboolean moved = FALSE;
     
     if (GTK_WIDGET_HAS_GRAB(widget)) 
     {
+        float sens=1/(75*(1+fabs((self->start_x - event->x)/10)));
         if (self->knob_type == 3)
         {
-            gtk_range_set_value(GTK_RANGE(widget), endless(self->start_value - (event->y - self->start_y) / scale));
+            gtk_range_set_value(GTK_RANGE(widget), endless(gtk_range_get_value(GTK_RANGE(widget)) - (event->y - self->last_y) / scale * sens));
         }
+//        else
+//        if (self->knob_type == 1)
+//        {
+//            gtk_range_set_value(GTK_RANGE(widget), deadzone(gtk_range_get_value(GTK_RANGE(widget)), -(event->y - self->last_y) / scale * sens, scale * sens));
+//        }
         else
-        if (self->knob_type == 1)
         {
-            gtk_range_set_value(GTK_RANGE(widget), deadzone(self->start_value, -(event->y - self->start_y) / scale, scale));
-        }
-        else
-        {
-            gtk_range_set_value(GTK_RANGE(widget), self->start_value - (event->y - self->start_y) / scale);
+            gtk_range_set_value(GTK_RANGE(widget), gtk_range_get_value(GTK_RANGE(widget)) - (event->y - self->last_y) / scale * sens);
         }
         moved = TRUE;
     }
     self->last_y = event->y;
+    self->last_x = event->x;
     return moved;
 }
 
@@ -833,7 +852,10 @@ calf_knob_class_init (CalfKnobClass *klass)
     widget_class->key_release_event = calf_knob_key_release;
     widget_class->scroll_event = calf_knob_scroll;
     GError *error = NULL;
-    klass->knob_image = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob.png", &error);
+    klass->knob_image[0] = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob1.png", &error);
+    klass->knob_image[1] = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob2.png", &error);
+    klass->knob_image[2] = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob3.png", &error);
+    klass->knob_image[3] = gdk_pixbuf_new_from_file(PKGLIBDIR "/knob4.png", &error);
     g_assert(klass->knob_image != NULL);
 }
 
@@ -905,4 +927,3 @@ calf_knob_get_type (void)
     }
     return type;
 }
-
diff --git a/src/modules.cpp b/src/modules.cpp
index 048eb93..f4cc264 100644
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@ -235,6 +235,92 @@ CALF_PLUGIN_INFO(compressor) = { 0x8502, "Compressor", "Calf Compressor", "Thor
 
 ////////////////////////////////////////////////////////////////////////////
 
+CALF_PORT_NAMES(multibandcompressor) = {"In L", "In R", "Out L", "Out R"};
+
+const char *multibandcompressor_detection_names[] = { "RMS", "Peak" };
+
+CALF_PORT_PROPS(multibandcompressor) = {
+	{ 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" },
+	{ 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_inL", "Input L" },
+	{ 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_inR", "Input R" },
+	{ 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_outL", "Output L" },
+	{ 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_outR", "Output R" },
+    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_inL", "0dB" },
+    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_inR", "0dB" },
+    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outL", "0dB" },
+    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outR", "0dB" },
+    
+    { 120,         10,          20000, 0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq0", "Split 1/2" },
+    { 1200,        10,          20000, 0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq1", "Split 2/3" },
+    { 6000,        10,          20000, 0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq2", "Split 3/4" },
+    
+    { -0.17,      -0.5,         0.5,   0,  PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep0", "S" },
+    { -0.17,      -0.5,         0.5,   0,  PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep1", "S" },
+    { -0.17,      -0.5,         0.5,   0,  PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep2", "S" },
+    
+    { 0.895025,    0.25,        4,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q0", "Q" },
+    { 0.895025,    0.25,        4,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q1", "Q" },
+    { 0.895025,    0.25,        4,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q2", "Q" },
+    
+    
+    { 0.0625,      0.000976563, 1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold0", "Threshold" },
+    { 3,           1,           20,    21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio0", "Ratio" },
+    { 50,          0.01,        2000,  0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack0", "Attack" },
+    { 100,         0.01,        2000,  0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release0", "Release" },
+    { 2,           1,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup0", "Makeup" },
+    { 2.828427125, 1,           8,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee0", "Knee" },
+    { 1,           0,           1,     0,  PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection0", "Detection" },
+    { 1,           0.03125,     1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression0", "Gain Reduction" },
+    { 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, "output0", "Output" },
+    { 1,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass0", "Bypass" },
+    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "mute0", "Mute" },
+    
+    
+    { 0.03125,     0.000976563, 1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold1", "Threshold" },
+    { 3,           1,           20,    21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio1", "Ratio" },
+    { 25,          0.01,        2000,  0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack1", "Attack" },
+    { 50,          0.01,        2000,  0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release1", "Release" },
+    { 2,           1,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup1", "Makeup" },
+    { 2.828427125, 1,           8,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee1", "Knee" },
+    { 1,           0,           1,     0,  PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection1", "Detection" },
+    { 1,           0.03125,     1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression1", "Gain Reduction" },
+    { 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, "output1", "Output" },
+    { 1,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass1", "Bypass" },
+    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "mute1", "Mute" },
+    
+    
+    { 0.015625,    0.000976563, 1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold2", "Threshold" },
+    { 3,           1,           20,    21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio2", "Ratio" },
+    { 12.5,        0.01,        2000,  0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack2", "Attack" },
+    { 25,          0.01,        2000,  0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release2", "Release" },
+    { 2,           1,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup2", "Makeup" },
+    { 2.828427125, 1,           8,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee2", "Knee" },
+    { 1,           0,           1,     0,  PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection2", "Detection" },
+    { 1,           0.03125,     1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression2", "Gain Reduction" },
+    { 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, "output2", "Output" },
+    { 1,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass2", "Bypass" },
+    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "mute2", "Mute" },
+    
+    
+    { 0.0078125,   0.000976563, 1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold3", "Threshold" },
+    { 3,           1,           20,    21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio3", "Ratio" },
+    { 6.25,        0.01,        2000,  0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack3", "Attack" },
+    { 12.5,        0.01,        2000,  0,  PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release3", "Release" },
+    { 2,           1,           64,    0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup3", "Makeup" },
+    { 2.828427125, 1,           8,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee3", "Knee" },
+    { 1,           0,           1,     0,  PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection3", "Detection" },
+    { 1,           0.03125,     1,     0,  PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "compression3", "Gain Reduction" },
+    { 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, "output3", "Output" },
+    { 1,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass3", "Bypass" },
+    { 0,           0,           1,     0,  PF_BOOL | PF_CTL_TOGGLE, NULL, "mute3", "Mute" },
+};
+
+CALF_PLUGIN_INFO(multibandcompressor) = { 0x8502, "Multibandcompressor", "Calf Multibandcompressor", "Markus Schmidt", calf_plugins::calf_copyright_info, "CompressorPlugin" };
+
+////////////////////////////////////////////////////////////////////////////
+
 CALF_PORT_NAMES(monosynth) = {
     "Out L", "Out R", 
 };
diff --git a/src/modules_dsp.cpp b/src/modules_dsp.cpp
index ac8fb77..8ce742d 100644
--- a/src/modules_dsp.cpp
+++ b/src/modules_dsp.cpp
@@ -59,9 +59,9 @@ static inline float dB_grid_inv(float pos)
 static void set_channel_color(cairo_iface *context, int channel)
 {
     if (channel & 1)
-        context->set_source_rgba(0.75, 1, 0);
+        context->set_source_rgba(0.35, 0.4, 0.2, 1);
     else
-        context->set_source_rgba(0, 1, 0.75);
+        context->set_source_rgba(0.35, 0.4, 0.2, 0.5);
     context->set_line_width(1.5);
 }
 
@@ -88,9 +88,9 @@ static bool get_freq_gridline(int subindex, float &pos, bool &vertical, std::str
                 freq = 10000 * (subindex - 27 + 1);
             pos = log(freq / 20.0) / log(1000);
             if (!legend.empty())
-                context->set_source_rgba(0.25, 0.25, 0.25, 0.75);
+                context->set_source_rgba(0, 0, 0, 0.2);
             else
-                context->set_source_rgba(0.25, 0.25, 0.25, 0.5);
+                context->set_source_rgba(0, 0, 0, 0.1);
             return true;
         }
         subindex -= 28;
@@ -102,7 +102,7 @@ static bool get_freq_gridline(int subindex, float &pos, bool &vertical, std::str
     if (pos < -1)
         return false;
     if (subindex != 4)
-        context->set_source_rgba(0.25, 0.25, 0.25, subindex & 1 ? 0.5 : 0.75);
+        context->set_source_rgba(0, 0, 0, subindex & 1 ? 0.1 : 0.2);
     if (!(subindex & 1))
     {
         std::stringstream ss;
@@ -120,7 +120,7 @@ bool frequency_response_line_graph::get_gridline(int index, int subindex, float
     return get_freq_gridline(subindex, pos, vertical, legend, context);
 }
 
-int frequency_response_line_graph::get_changed_offsets(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)
 {
     subindex_graph = 0;
     subindex_dot = 0;
@@ -243,7 +243,7 @@ bool filter_audio_module::get_graph(int index, int subindex, float *data, int po
     return false;
 }
 
-int filter_audio_module::get_changed_offsets(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)
 {
     if (fabs(inertia_cutoff.get_last() - old_cutoff) + 100 * fabs(inertia_resonance.get_last() - old_resonance) + fabs(*params[par_mode] - old_mode) > 0.1f)
     {
@@ -360,7 +360,7 @@ bool multichorus_audio_module::get_graph(int index, int subindex, float *data, i
         if (subindex < 2)
             set_channel_color(context, subindex);
         else {
-            context->set_source_rgba(0, 1, 0);
+            context->set_source_rgba(0.35, 0.4, 0.2);
             context->set_line_width(1.0);
         }
         return ::get_graph(*this, subindex, data, points);
@@ -471,9 +471,9 @@ bool compressor_audio_module::get_graph(int index, int subindex, float *data, in
             data[i] = dB_grid(output); 
     }
     if (subindex == (*params[param_bypass] > 0.5f ? 1 : 0))
-        context->set_source_rgba(0.5, 0.5, 0.5, 0.5);
+        context->set_source_rgba(0.35, 0.4, 0.2, 0.3);
     else {
-        context->set_source_rgba(0, 1, 0, 1);
+        context->set_source_rgba(0.35, 0.4, 0.2, 1);
         context->set_line_width(2);
     }
     return true;
@@ -637,3 +637,628 @@ uint32_t compressor_audio_module::process(uint32_t offset, uint32_t numsamples,
 
     return inputs_mask;
 }
+
+/// Multibandcompressor by Markus Schmidt
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+multibandcompressor_audio_module::multibandcompressor_audio_module()
+{
+    is_active = false;
+    srate = 0;
+    // zero all dsplays
+    clip_inL    = 0.f;
+    clip_inR    = 0.f;
+    clip_outL   = 0.f;
+    clip_outR   = 0.f;
+    meter_inL  = 0.f;
+    meter_inR  = 0.f;
+    meter_outL = 0.f;
+    meter_outR = 0.f;
+}
+
+void multibandcompressor_audio_module::activate()
+{
+    is_active = true;
+    // set all filters and strips
+    params_changed();
+    // activate all strips
+    for (int j = 0; j < strips; j ++) {
+        strip[j].activate();
+        strip[j].id = j;
+    }
+}
+
+void multibandcompressor_audio_module::deactivate()
+{
+    is_active = false;
+    // deactivate all strips
+    for (int j = 0; j < strips; j ++) {
+        strip[j].deactivate();
+    }
+}
+
+void multibandcompressor_audio_module::params_changed()
+{
+    // set the params of all filters
+    if(*params[param_freq0] != freq_old[0] or *params[param_sep0] != sep_old[0] or *params[param_q0] != q_old[0]) {
+        lpL0.set_lp_rbj((float)(*params[param_freq0] * (1 - *params[param_sep0])), *params[param_q0], (float)srate);
+        lpR0.set_lp_rbj((float)(*params[param_freq0] * (1 - *params[param_sep0])), *params[param_q0], (float)srate);
+        hpL0.set_hp_rbj((float)(*params[param_freq0] * (1 + *params[param_sep0])), *params[param_q0], (float)srate);
+        hpR0.set_hp_rbj((float)(*params[param_freq0] * (1 + *params[param_sep0])), *params[param_q0], (float)srate);
+        freq_old[0] = *params[param_freq0];
+        sep_old[0]  = *params[param_sep2];
+        q_old[0]    = *params[param_q2];
+    }
+    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);
+        lpR1.set_lp_rbj((float)(*params[param_freq1] * (1 - *params[param_sep1])), *params[param_q1], (float)srate);
+        hpL1.set_hp_rbj((float)(*params[param_freq1] * (1 + *params[param_sep1])), *params[param_q1], (float)srate);
+        hpR1.set_hp_rbj((float)(*params[param_freq1] * (1 + *params[param_sep1])), *params[param_q1], (float)srate);
+        freq_old[1] = *params[param_freq1];
+        sep_old[1]  = *params[param_sep2];
+        q_old[1]    = *params[param_q2];
+    }
+    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);
+        lpR2.set_lp_rbj((float)(*params[param_freq2] * (1 - *params[param_sep2])), *params[param_q2], (float)srate);
+        hpL2.set_hp_rbj((float)(*params[param_freq2] * (1 + *params[param_sep2])), *params[param_q2], (float)srate);
+        hpR2.set_hp_rbj((float)(*params[param_freq2] * (1 + *params[param_sep2])), *params[param_q2], (float)srate);
+        freq_old[2] = *params[param_freq2];
+        sep_old[2]  = *params[param_sep2];
+        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], *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], *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], *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], *params[param_bypass3], *params[param_mute3]);
+                break;
+        }
+    }
+}
+
+void multibandcompressor_audio_module::set_sample_rate(uint32_t sr)
+{
+    srate = sr;
+    // set srate of all strips
+    for (int j = 0; j < strips; j ++) {
+        strip[j].set_sample_rate(srate);
+    }
+}
+
+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;
+    if(bypass) {
+        // everything bypassed
+        while(offset < numsamples) {
+            outs[0][offset] = ins[0][offset];
+            outs[1][offset] = ins[1][offset];
+            ++offset;
+        }
+        // displays, too
+        clip_inL    = 0.f;
+        clip_inR    = 0.f;
+        clip_outL   = 0.f;
+        clip_outR   = 0.f;
+        meter_inL  = 0.f;
+        meter_inR  = 0.f;
+        meter_outL = 0.f;
+        meter_outR = 0.f;
+    } else {
+        // process all strips
+        
+        // determine mute state of strips
+        mute[0] = *params[param_mute0] > 0.f ? true : false;
+        mute[1] = *params[param_mute1] > 0.f ? true : false;
+        mute[2] = *params[param_mute2] > 0.f ? true : false;
+        mute[3] = *params[param_mute3] > 0.f ? true : false;
+        
+        // let meters fall a bit
+        clip_inL    -= std::min(clip_inL,  numsamples);
+        clip_inR    -= std::min(clip_inR,  numsamples);
+        clip_outL   -= std::min(clip_outL, numsamples);
+        clip_outR   -= std::min(clip_outR, numsamples);
+        meter_inL  -= meter_inL * 5.f * numsamples / srate;
+        meter_inR  -= meter_inR * 5.f * numsamples / srate;
+        meter_outL -= meter_outL * 5.f * numsamples / srate;
+        meter_outR -= meter_outR * 5.f * numsamples / srate;
+        for(int k = 0; k < strips; k ++) {
+            meter_out[k] -= meter_out[k] * 5.f * numsamples / srate;
+        }
+        
+        while(offset < numsamples) {
+            // cycle trough samples
+            float inL = ins[0][offset];
+            float inR = ins[1][offset];
+            // in level
+            inR *= *params[param_level_in];
+            inL *= *params[param_level_in];
+            // out vars
+            float outL = 0.f;
+            float outR = 0.f;
+            for (int i = 0; i < strips; i ++) {
+                // cycle trough strips
+                if (!mute[i]) {
+                    // strip unmuted
+                    float left  = inL;
+                    float right = inR;
+                    // send trough filters
+                    switch (i) {
+                        case 0:
+                            left  = lpL0.process(left);
+                            right = lpR0.process(right);
+                            lpL0.sanitize();
+                            lpR0.sanitize();
+                            break;
+                        case 1:
+                            left  = lpL1.process(left);
+                            right = lpR1.process(right);
+                            left  = hpL0.process(left);
+                            right = hpR0.process(right);
+                            lpL1.sanitize();
+                            lpR1.sanitize();
+                            hpL0.sanitize();
+                            hpR0.sanitize();
+                            break;
+                        case 2:
+                            left  = lpL2.process(left);
+                            right = lpR2.process(right);
+                            left  = hpL1.process(left);
+                            right = hpR1.process(right);
+                            lpL2.sanitize();
+                            lpR2.sanitize();
+                            hpL1.sanitize();
+                            hpR1.sanitize();
+                            break;
+                        case 3:
+                            left  = hpL2.process(left);
+                            right = hpR2.process(right);
+                            hpL2.sanitize();
+                            hpR2.sanitize();
+                            break;
+                    }
+                    // process gain reduction
+                    strip[i].process(left, right);
+                    // sum up output
+                    outL += left;
+                    outR += right;
+                } else {
+                    // strip muted
+                    
+                }
+                
+                
+            } // process single strip
+            
+            // even out filters gain reduction
+            // 3dB - levelled manually (based on sep and q settings)
+            outL *= 1.414213562;
+            outR *= 1.414213562;
+            
+            // out level
+            outL *= *params[param_level_out];
+            outR *= *params[param_level_out];
+            
+            // send to output
+            outs[0][offset] = outL;
+            outs[1][offset] = outR;
+            
+            // clip LED's
+            if(inL > 1.f) {
+                clip_inL  = srate >> 3;
+            }
+            if(inR > 1.f) {
+                clip_inR  = srate >> 3;
+            }
+            if(outL > 1.f) {
+                clip_outL = srate >> 3;
+            }
+            if(outR > 1.f) {
+                clip_outR = srate >> 3;
+            }
+            // in / out meters
+            if(meter_inL < inL) {
+                meter_inL = inL;
+            }
+            if(meter_inR < inR) {
+                meter_inR = inR;
+            }
+            if(meter_outL < outL) {
+                meter_outL = outL;
+            }
+            if(meter_outR < outR) {
+                meter_outR = outR;
+            }
+            // next sample
+            ++offset;
+//            printf("inL: %+7.4f inR: %+7.4f outL: %+7.4f outR: %+7.4f input: %+7.4f output: %+7.4f\n", inL, inR, outL, outR, *params[param_level_in], *params[param_level_out]);
+        } // cycle trough samples
+        
+    } // 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;
+    }
+    // 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;
+        }
+    } 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();
+        }
+    }
+    // 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)
+{ 
+//    printf("get_graph   : %i\n", index);
+    switch (index) {
+        case param_compression0:
+            return strip[0].get_graph(subindex, data, points, context);
+            break;
+        case param_compression1:
+            return strip[1].get_graph(subindex, data, points, context);
+            break;
+        case param_compression2:
+            return strip[2].get_graph(subindex, data, points, context);
+            break;
+        case param_compression3:
+            return strip[3].get_graph(subindex, data, points, context);
+            break;
+    }
+    return false;
+}
+
+bool multibandcompressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
+{
+//    printf("get_dot     : %i\n", index);
+    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;
+    }
+    return false;
+}
+
+bool multibandcompressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+{
+//    printf("get_gridline: %i\n", index);
+    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;
+    }
+    return false;
+}
+
+int multibandcompressor_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+{   
+//    printf("get_changed : %i\n", index);
+    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;
+    }
+    return 0;
+}
+/// Gain reduction module by Markus Schmidt
+////////////////////////////////////////////////////////////////////////////////
+gain_reduction_audio_module::gain_reduction_audio_module()
+{
+    is_active       = false;
+    last_generation = 0;
+}
+
+void gain_reduction_audio_module::activate()
+{
+    is_active = true;
+    linSlope = 0.f;
+    meter_out = 0.f;
+    meter_comp = 0.f;
+    float l, r;
+    float byp = bypass;
+    bypass = 0.0;
+    process(l, r);
+    bypass = byp;
+}
+
+void gain_reduction_audio_module::deactivate()
+{
+    is_active = false;
+}
+
+void gain_reduction_audio_module::process(float &left, float &right)
+{
+    if(bypass > 0.5f) {
+        meter_comp = 1.f;
+        meter_out = 0.f;
+        return;
+    }
+    // this routine is mainly copied from thor's compressor module
+    // greatest sounding compressor I've heard!
+    bool rms = detection == 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 compression = 1.f;
+    float absample = (fabs(left) + fabs(right)) * 0.5f;
+    if(rms) absample *= absample;
+    meter_out -= meter_out * 5.f * 1 / srate;
+    
+    linSlope += (absample - linSlope) * (absample > linSlope ? attack_coeff : release_coeff);
+    
+    float gain = 1.f;
+
+    if(linSlope > 0.f) {
+        gain = output_gain(linSlope, rms);
+    }
+
+    compression = gain;
+    gain *= makeup;
+    
+    left *= gain;
+    right *= gain;
+
+    float maxLR = std::max(fabs(left), fabs(right));
+    
+    if(maxLR > meter_out) {
+        meter_out = maxLR;
+    }
+    meter_comp = compression;
+    
+    detected = rms ? sqrt(linSlope) : linSlope;
+}
+
+float gain_reduction_audio_module::output_level(float slope) {
+    return slope * output_gain(slope, false) * makeup;
+}
+
+float gain_reduction_audio_module::output_gain(float linSlope, bool rms) {
+    //this calculation is also thor's work
+    if(linSlope > (rms ? adjKneeStart : linKneeStart)) {
+        float slope = log(linSlope);
+        if(rms) slope *= 0.5f;
+
+        float gain = 0.f;
+        float delta = 0.f;
+        if(IS_FAKE_INFINITY(ratio)) {
+            gain = thres;
+            delta = 0.f;
+        } else {
+            gain = (slope - thres) / ratio + thres;
+            delta = 1.f / ratio;
+        }
+        
+        if(knee > 1.f && slope < kneeStop) {
+            gain = hermite_interpolation(slope, kneeStart, kneeStop, kneeStart, compressedKneeStop, 1.f, delta);
+        }
+        
+        return exp(gain - slope);
+    }
+
+    return 1.f;
+}
+
+void gain_reduction_audio_module::set_sample_rate(uint32_t sr)
+{
+    srate = sr;
+}
+void gain_reduction_audio_module::set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float byp, float mu)
+{
+    // set all params
+    attack          = att;
+    release         = rel;
+    threshold       = thr;
+    ratio           = rat;
+    knee            = kn;
+    makeup          = mak;
+    detection       = det;
+    bypass          = byp;
+    mute            = mu;
+    // if bypass, zero meters
+    if(bypass > 0.5 or mute > 0.f) {
+        meter_out  = 0.f;
+        meter_comp = 1.f;
+    }
+}
+float gain_reduction_audio_module::get_output_level() {
+    // returns output level (max(left, right))
+    return meter_out;
+}
+float gain_reduction_audio_module::get_comp_level() {
+    // returns amount of compression
+    return meter_comp;
+}
+
+bool gain_reduction_audio_module::get_graph(int subindex, float *data, int points, cairo_iface *context)
+{
+    if (!is_active)
+        return false;
+    if (subindex > 1) // 1
+        return false;
+    for (int i = 0; i < points; i++)
+    {
+        float input = dB_grid_inv(-1.0 + i * 2.0 / (points - 1));
+        float output = output_level(input);
+        if (subindex == 0)
+            data[i] = dB_grid(input);
+        else
+            data[i] = dB_grid(output); 
+    }
+    if (subindex == (bypass > 0.5f ? 1 : 0))
+        context->set_source_rgba(0.35, 0.4, 0.2, 0.3);
+    else {
+        context->set_source_rgba(0.35, 0.4, 0.2, 1);
+        context->set_line_width(2);
+    }
+    return true;
+}
+
+bool gain_reduction_audio_module::get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context)
+{
+    if (!is_active)
+        return false;
+    if (!subindex)
+    {
+        x = 0.5 + 0.5 * dB_grid(detected);
+        y = dB_grid(bypass > 0.5f ? detected : output_level(detected));
+        return bypass > 0.5f ? false : true;
+    }
+    return false;
+}
+
+bool gain_reduction_audio_module::get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
+{
+    bool tmp;
+    vertical = (subindex & 1) != 0;
+    bool result = get_freq_gridline(subindex >> 1, pos, tmp, legend, context, false);
+    if (result && vertical) {
+        if ((subindex & 4) && !legend.empty()) {
+            legend = "";
+        }
+        else {
+            size_t pos = legend.find(" dB");
+            if (pos != std::string::npos)
+                legend.erase(pos);
+        }
+        pos = 0.5 + 0.5 * pos;
+    }
+    return result;
+}
+
+int gain_reduction_audio_module::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
+{
+    subindex_graph = 0;
+    subindex_dot = 0;
+    subindex_gridline = generation ? INT_MAX : 0;
+
+    if (fabs(threshold-old_threshold) + fabs(ratio - old_ratio) + fabs(knee - old_knee) + fabs(makeup - old_makeup) + fabs(bypass - old_bypass) > 0.01f)
+    {
+        old_threshold = threshold;
+        old_ratio = ratio;
+        old_knee = knee;
+        old_makeup = makeup;
+        old_bypass = bypass;
+        last_generation++;
+    }
+
+    if (generation == last_generation)
+        subindex_graph = 2;
+    return last_generation;
+}

-- 
calf audio plugins packaging



More information about the pkg-multimedia-commits mailing list