[SCM] calf/master: + Curve: add maximum number of points, pass CalfCurve object to callbacks, allow customized clipping + Framework: some API docs, more of incomplete handling of DSSI-like configure() (JACK host only, no support in presets etc., be patient) + Organ: more work on FM-based percussion generator, including user-settable keytrack curve (editing currently limited to white keys) + SVN: mark more files as ignored (install-sh, depcomp, missing)

js at users.alioth.debian.org js at users.alioth.debian.org
Tue May 7 15:37:27 UTC 2013


The following commit has been merged in the master branch:
commit e044c37a4700b3e31bf56fd953b547ed3f4a8d13
Author: kfoltman <kfoltman at 78b06b96-2940-0410-b7fc-879d825d01d8>
Date:   Fri Aug 1 23:05:42 2008 +0000

    + Curve: add maximum number of points, pass CalfCurve object to callbacks, allow customized clipping
    + Framework: some API docs, more of incomplete handling of DSSI-like configure() (JACK host only, no support in presets etc., be patient)
    + Organ: more work on FM-based percussion generator, including user-settable keytrack curve (editing currently limited to white keys)
    + SVN: mark more files as ignored (install-sh, depcomp, missing)
    
    
    
    git-svn-id: https://calf.svn.sourceforge.net/svnroot/calf/trunk@248 78b06b96-2940-0410-b7fc-879d825d01d8

diff --git a/src/calf/ctl_curve.h b/src/calf/ctl_curve.h
index 978d013..61c0fb6 100644
--- a/src/calf/ctl_curve.h
+++ b/src/calf/ctl_curve.h
@@ -47,19 +47,22 @@ struct CalfCurve
     struct EventSink
     {
         /// Called when a point has been edited, added or removed
-        virtual void curve_changed(const point_vector &data) = 0;
+        virtual void curve_changed(CalfCurve *src, const point_vector &data) = 0;
+        /// Called to clip/snap/otherwise adjust candidate point coordinates
+        virtual void clip(CalfCurve *src, int pt, float &x, float &y, bool &hide) = 0;
     };
 
     /// Null implementation of EventSink
     struct EventAdapter: public EventSink
     {
-        virtual void curve_changed(const point_vector &data) {}
+        virtual void curve_changed(CalfCurve *src, const point_vector &data) {}
+        virtual void clip(CalfCurve *src, int pt, float &x, float &y, bool &hide) {}
     };
 
     /// Debug implementation of EventSink
     struct EventTester: public EventAdapter
     {
-        virtual void curve_changed(const point_vector &data) {
+        virtual void curve_changed(CalfCurve *src, const point_vector &data) {
             for(size_t i = 0; i < data.size(); i++)
                 g_message("Point %d: (%f, %f)", (int)i, data[i].first, data[i].second);
         }
@@ -81,6 +84,10 @@ struct CalfCurve
     GdkCursor *hand_cursor;
     /// Cached pencil (add point) cursor
     GdkCursor *pencil_cursor;
+    /// Cached arrow (do not add point) cursor
+    GdkCursor *arrow_cursor;
+    /// Maximum number of points
+    unsigned int point_limit;
 
     /// Convert logical (mapping) to physical (screen) coordinates
     void log2phys(float &x, float &y);
@@ -100,7 +107,7 @@ struct CalfCurveClass
 };
 
 /// Create a CalfCurve
-extern GtkWidget *calf_curve_new();
+extern GtkWidget *calf_curve_new(unsigned int point_limit = -1);
 
 /// Return a GObject type for class CalfCurve
 extern GType calf_curve_get_type();
diff --git a/src/calf/giface.h b/src/calf/giface.h
index f06a369..a6c22ae 100644
--- a/src/calf/giface.h
+++ b/src/calf/giface.h
@@ -108,45 +108,96 @@ struct plugin_command_info
 struct parameter_properties
 {
     float def_value, min, max, step;
+    /// OR of parameter_flags
     uint32_t flags;
+    /// for PF_ENUM: array of text values (from min to max step 1)
     const char **choices;
-    const char *short_name, *name;
+    /// parameter label (for use in LV2 label field etc.)
+    const char *short_name;
+    /// parameter human-readable name
+    const char *name;
+    /// convert from [0, 1] range to [min, max] (applying scaling)
     float from_01(double value01) const;
+    /// convert from [min, max] to [0, 1] range (applying reverse scaling)
     double to_01(float value) const;
+    /// stringify (in sensible way)
     std::string to_string(float value) const;
+    /// get required width (for reserving GUI space)
     int get_char_count() const;
+    /// get increment step based on step value (if specified) and other factors
     float get_increment() const;
 };
 
+/// 'provides live line graph values' interface
 struct line_graph_iface
 {
+    /// Obtain subindex'th graph of parameter index
+    /// @param index parameter/graph number (usually tied to particular plugin control port)
+    /// @param subindex graph number (there may be multiple overlaid graphs for one parameter, eg. for monosynth 2x12dB filters)
+    /// @param data buffer for normalized output values
+    /// @param points number of points to fill
+    /// @param context cairo context to adjust (for multicolour graphs etc.)
+    /// @retval true graph data was returned; subindex+1 graph may or may not be available
+    /// @retval false graph data was not returned; subindex+1 graph does not exist either
     virtual bool get_graph(int index, int subindex, float *data, int points, cairo_t *context) = 0;
+    /// Standard destructor to make compiler happy
     virtual ~line_graph_iface() {}
 };
 
+/// 'may receive configure variables' interface
+struct send_configure_iface
+{
+    /// Called to set configure variable
+    /// @param key variable name
+    /// @param value variable content
+    virtual void send_configure(const char *key, const char *value) = 0;
+};
+
 struct plugin_command_info;
 
+/// Interface for host-GUI-plugin interaction (should be really split in two, but ... meh)
 struct plugin_ctl_iface
 {
+    /// @return description structure for given parameter
     virtual parameter_properties *get_param_props(int param_no) = 0;
+    /// @return total number of parameters
+    virtual int get_param_count() = 0;
+    /// @return value of given parameter
     virtual float get_param_value(int param_no) = 0;
+    /// Set value of given parameter
     virtual void set_param_value(int param_no, float value) = 0;
-    virtual int get_param_count() = 0;
+    /// Return custom XML
     virtual const char *get_gui_xml() = 0;
+    /// @return line_graph_iface if any
     virtual line_graph_iface *get_line_graph_iface() = 0;
+    /// @return port offset of first control (parameter) port (= number of audio inputs + number of audio outputs in all existing plugins as for 1 Aug 2008)
     virtual int get_param_port_offset() = 0;
+    /// Load preset with given number
     virtual bool activate_preset(int bank, int program) = 0;
+    /// @return plugin long name
     virtual const char *get_name() = 0;
+    /// @return plugin LV2 label
     virtual const char *get_id() = 0;
+    /// @return plugin human-readable label
     virtual const char *get_label() = 0;
+    /// @return number of audio inputs
     virtual int get_input_count()=0;
+    /// @return number of audio outputs
     virtual int get_output_count()=0;
+    /// @return true if plugin has MIDI input
     virtual bool get_midi()=0;
+    /// @return volume level for port'th port (if supported by the implementation, currently only jack_host<Module> implements that by measuring signal level on plugin ports)
     virtual float get_level(unsigned int port)=0;
-    virtual ~plugin_ctl_iface() {}
+    /// @return NULL-terminated list of menu commands
     virtual plugin_command_info *get_commands() { return NULL; }
-    virtual char *configure(const char *key, const char *value) { return NULL; }
+    /// Execute menu command with given number
     virtual void execute(int cmd_no)=0;
+    /// Set a configure variable on a plugin
+    virtual char *configure(const char *key, const char *value) { return NULL; }
+    /// Send all configure variables set within a plugin to given destination (which may be limited to only those that plugin understands)
+    virtual void send_configures(send_configure_iface *) {}
+    /// Do-nothing destructor to silence compiler warning
+    virtual ~plugin_ctl_iface() {}
 };
 
 struct midi_event {
diff --git a/src/calf/gui.h b/src/calf/gui.h
index a08eb94..faedde2 100644
--- a/src/calf/gui.h
+++ b/src/calf/gui.h
@@ -71,6 +71,8 @@ struct param_control: public control_base
     virtual void get()=0;
     /// called to transfer the value from parameter(s) to control
     virtual void set()=0;
+    /// called on DSSI configure()
+    virtual void configure(const char *key, const char *value) {}
     virtual void hook_params();
     virtual void on_idle() {}
     virtual ~param_control();
@@ -236,7 +238,7 @@ struct curve_param_control: public param_control
 
 class plugin_gui_window;
 
-class plugin_gui
+class plugin_gui: public send_configure_iface
 {
 protected:
     int param_count;
@@ -265,6 +267,7 @@ public:
     void refresh(int param_no, param_control *originator = NULL);
     void xml_element_start(const char *element, const char *attributes[]);
     void set_param_value(int param_no, float value, param_control *originator = NULL);
+    void send_configure(const char *key, const char *value);
     void on_idle();
     ~plugin_gui();
     static void xml_element_start(void *data, const char *element, const char *attributes[]);
diff --git a/src/calf/jackhost.h b/src/calf/jackhost.h
index 4859212..922171e 100644
--- a/src/calf/jackhost.h
+++ b/src/calf/jackhost.h
@@ -414,6 +414,9 @@ public:
     virtual void execute(int cmd_no) {
         module.execute(cmd_no);
     }
+    virtual char *configure(const char *key, const char *value) { 
+        return module.configure(key, value);
+    }
 };
 
 extern jack_host_base *create_jack_host(const char *name);
diff --git a/src/calf/modules_synths.h b/src/calf/modules_synths.h
index f6e996c..7c04af8 100644
--- a/src/calf/modules_synths.h
+++ b/src/calf/modules_synths.h
@@ -273,6 +273,7 @@ public:
     static const char *get_id() { return "organ"; }    
     static const char *get_label() { return "Organ"; }    
     static plugin_command_info *get_commands();
+    virtual char *configure(const char *key, const char *value);
 };
 
 };
diff --git a/src/calf/organ.h b/src/calf/organ.h
index f339113..e50d1c1 100644
--- a/src/calf/organ.h
+++ b/src/calf/organ.h
@@ -24,6 +24,8 @@
 
 #include "synth.h"
 
+#define ORGAN_KEYTRACK_POINTS 4
+
 namespace synth 
 {
 
@@ -54,14 +56,15 @@ struct organ_parameters {
     float percussion_level;
     float percussion_wave;
     float percussion_harmonic;
+    float percussion_fm_time;
+    float percussion_fm_depth;
     float percussion_fm_wave;
     float percussion_fm_harmonic;
-    float percussion_fm_depth;
     float percussion_trigger;
     float percussion_vel2amp;
     float filter_chain;
     float master;
-    
+
     organ_filter_parameters filters[organ_parameters::FilterCount];
     organ_env_parameters envs[organ_parameters::EnvCount];
     float lfo_rate;
@@ -70,12 +73,15 @@ struct organ_parameters {
     float lfo_phase;
     float lfo_mode;
     
-    double perc_decay_const;
+    double perc_decay_const, perc_fm_decay_const;
     float multiplier[9];
     int phaseshift[9];
     float cutoff;
     unsigned int foldvalue;
     float pitch_bend;
+
+    float percussion_keytrack[ORGAN_KEYTRACK_POINTS][2];
+    
     
     organ_parameters() : pitch_bend(1.0f) {}
 
@@ -244,7 +250,8 @@ class percussion_voice: public organ_voice_base {
 public:
     int sample_rate;
     dsp::fixed_point<int64_t, 20> phase, modphase, dphase, moddphase;
-    dsp::biquad<float> filter;
+    dsp::decay fm_amp;
+    float fm_keytrack;
 
     percussion_voice(organ_parameters *_parameters)
     : organ_voice_base(_parameters)
@@ -254,27 +261,18 @@ public:
     void reset() {
         phase = 0;
         modphase = 0;
+        dphase = 0;
+        moddphase = 0;
         note = -1;
     }
 
-    void note_on(int note, int vel) {
-        // do not reset phase if voice is still running (to prevent clicks, even at cost of slight loss of "percussiveness")
-        if (!amp.get_active())
-        {
-            phase = 0;
-            modphase = 0;
-            filter.reset_d1();
-        }
-        this->note = note;
-        amp.set(1.0f + (vel - 127) * parameters->percussion_vel2amp / 127.0);
-        update_pitch();
-    }
+    void note_on(int note, int vel);
     
     void update_pitch()
     {
         float phase = synth::midi_note_to_phase(note, 0, sample_rate);
-        dphase.set(phase * parameters->percussion_harmonic * parameters->pitch_bend);
-        moddphase.set(phase * parameters->percussion_fm_harmonic * parameters->pitch_bend);
+        dphase.set(phase * parameters->percussion_harmonic * 0.5 * parameters->pitch_bend);
+        moddphase.set(phase * parameters->percussion_fm_harmonic * 0.5 * parameters->pitch_bend);
     }
 
     // this doesn't really have a voice interface
@@ -304,7 +302,9 @@ struct drawbar_organ: public synth::basic_synth {
         par_pan1, par_pan2, par_pan3, par_pan4, par_pan5, par_pan6, par_pan7, par_pan8, par_pan9, 
         par_routing1, par_routing2, par_routing3, par_routing4, par_routing5, par_routing6, par_routing7, par_routing8, par_routing9, 
         par_foldover,
-        par_percdecay, par_perclevel, par_percwave, par_percharm, par_percfmwave, par_percfmharm, par_percfmdepth, par_perctrigger, par_percvel2amp,
+        par_percdecay, par_perclevel, par_percwave, par_percharm,
+        par_percfmdecay, par_percfmdepth, par_percfmwave, par_percfmharm, 
+        par_perctrigger, par_percvel2amp,
         par_filterchain,
         par_master, 
         par_f1cutoff, par_f1res, par_f1env1, par_f1env2, par_f1env3, par_f1keyf,
diff --git a/src/ctl_curve.cpp b/src/ctl_curve.cpp
index 5c554b3..6a4ebdc 100644
--- a/src/ctl_curve.cpp
+++ b/src/ctl_curve.cpp
@@ -27,9 +27,13 @@
 static gpointer parent_class = NULL;
 
 GtkWidget *
-calf_curve_new()
+calf_curve_new(unsigned int point_limit)
 {
     GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_CURVE, NULL ));
+    g_assert(CALF_IS_CURVE(widget));
+    
+    CalfCurve *self = CALF_CURVE(widget);
+    self->point_limit = point_limit;
     return widget;
 }
 
@@ -152,6 +156,9 @@ calf_curve_button_press (GtkWidget *widget, GdkEventButton *event)
     found_pt = find_nearest(self, event->x, event->y, insert_pt);
     if (found_pt == -1 && insert_pt != -1)
     {
+        // if at point limit, do not start anything
+        if (self->points->size() >= self->point_limit)
+            return TRUE;
         float x = event->x, y = event->y;
         bool hide = false;
         self->phys2log(x, y);
@@ -170,7 +177,7 @@ calf_curve_button_press (GtkWidget *widget, GdkEventButton *event)
     self->cur_pt = found_pt;
     gtk_widget_queue_draw(widget);
     if (self->sink)
-        self->sink->curve_changed(*self->points);
+        self->sink->curve_changed(self, *self->points);
     gdk_window_set_cursor(widget->window, self->hand_cursor);
     return TRUE;
 }
@@ -185,7 +192,7 @@ calf_curve_button_release (GtkWidget *widget, GdkEventButton *event)
     self->cur_pt = -1;
     self->hide_current = false;
     if (self->sink)
-        self->sink->curve_changed(*self->points);
+        self->sink->curve_changed(self, *self->points);
     gtk_widget_queue_draw(widget);
     gdk_window_set_cursor(widget->window, self->pencil_cursor);
     return FALSE;
@@ -205,14 +212,14 @@ calf_curve_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
         self->clip(self->cur_pt, x, y, self->hide_current);
         (*self->points)[self->cur_pt] = CalfCurve::point(x, y);
         if (self->sink)
-            self->sink->curve_changed(*self->points);
+            self->sink->curve_changed(self, *self->points);
         gtk_widget_queue_draw(widget);        
     }
     else
     {
         int insert_pt = -1;
         if (find_nearest(self, event->x, event->y, insert_pt) == -1)
-            gdk_window_set_cursor(widget->window, self->pencil_cursor);
+            gdk_window_set_cursor(widget->window, self->points->size() >= self->point_limit ? self->arrow_cursor : self->pencil_cursor);
         else
             gdk_window_set_cursor(widget->window, self->hand_cursor);
     }
@@ -267,6 +274,7 @@ calf_curve_init (CalfCurve *self)
     self->hide_current = false;
     self->pencil_cursor = gdk_cursor_new(GDK_PENCIL);
     self->hand_cursor = gdk_cursor_new(GDK_FLEUR);
+    self->arrow_cursor = gdk_cursor_new(GDK_ARROW);
 }
 
 void CalfCurve::log2phys(float &x, float &y) {
@@ -281,9 +289,11 @@ void CalfCurve::phys2log(float &x, float &y) {
 
 void CalfCurve::clip(int pt, float &x, float &y, bool &hide)
 {
+    hide = false;
+    sink->clip(this, pt, x, y, hide);
+    
     float ymin = std::min(y0, y1), ymax = std::max(y0, y1);
     float yamp = ymax - ymin;
-    hide = false;
     if (pt != 0 && pt != (int)(points->size() - 1))
     {
         if (y < ymin - yamp || y > ymax + yamp)
diff --git a/src/gui.cpp b/src/gui.cpp
index ada4173..5f5dfe1 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -28,6 +28,8 @@
 #include <calf/preset_gui.h>
 #include <calf/main_win.h>
 
+#include <iostream>
+
 using namespace synth;
 using namespace std;
 
@@ -352,14 +354,37 @@ GtkWidget *keyboard_param_control::create(plugin_gui *_gui, int _param_no)
 
 // curve
 
+struct curve_param_control_callback: public CalfCurve::EventAdapter
+{
+    curve_param_control *ctl;
+    
+    curve_param_control_callback(curve_param_control *_ctl)
+    : ctl(_ctl) {}
+    
+    virtual void curve_changed(CalfCurve *src, const CalfCurve::point_vector &data) {
+        stringstream ss;
+        ss << data.size() << endl;
+        for (size_t i = 0; i < data.size(); i++)
+            ss << data[i].first << " " << data[i].second << endl;
+        ctl->gui->send_configure(ctl->attribs["key"].c_str(), ss.str().c_str());
+    }
+    virtual void clip(CalfCurve *src, int pt, float &x, float &y, bool &hide)
+    {
+        // int gridpt = floor(x * 71 * 2);
+        // clip to the middle of the nearest white key
+        x = (floor(x * 71) + 0.5)/ 71.0;
+    }
+};
+
 GtkWidget *curve_param_control::create(plugin_gui *_gui, int _param_no)
 {
     gui = _gui;
     param_no = _param_no;
+    require_attribute("key");
     
-    widget = calf_curve_new();
+    widget = calf_curve_new(get_int("maxpoints", -1));
     curve = CALF_CURVE(widget);
-    curve->sink = new CalfCurve::EventTester;
+    curve->sink = new curve_param_control_callback(this);
     // gtk_curve_set_curve_type(curve, GTK_CURVE_TYPE_LINEAR);
     return widget;
 }
@@ -811,6 +836,11 @@ GtkWidget *plugin_gui::create_from_xml(plugin_ctl_iface *_plugin, const char *xm
     return GTK_WIDGET(top_container->container);
 }
 
+void plugin_gui::send_configure(const char *key, const char *value)
+{
+    plugin->configure(key, value);
+}
+
 void plugin_gui::on_idle()
 {
     for (unsigned int i = 0; i < params.size(); i++)
diff --git a/src/organ.cpp b/src/organ.cpp
index 6aadacc..9435288 100644
--- a/src/organ.cpp
+++ b/src/organ.cpp
@@ -29,6 +29,7 @@
 #include <calf/giface.h>
 #include <calf/modules.h>
 #include <calf/modules_dev.h>
+#include <iostream>
 
 using namespace synth;
 using namespace std;
@@ -274,52 +275,68 @@ const char *organ_audio_module::get_gui_xml()
                 "</frame>"
             "</hbox>"
             "<vbox page=\"Advanced\">"
-                "<align scale-x=\"0.8\" scale-y=\"0.3\">"
+                "<align scale-x=\"1.0\" scale-y=\"0.3\">"
                     "<frame label=\"Percussive section\">"
-                        "<hbox>"
-                            "<vbox>"
-                                "<label param=\"perc_decay\"/>"
-                                "<knob param=\"perc_decay\" expand=\"0\" fill=\"0\"/>"
-                                "<value param=\"perc_decay\"/>"
-                            "</vbox>"
-                            "<vbox>"
-                                "<label param=\"perc_level\"/>"
-                                "<knob param=\"perc_level\" expand=\"0\" fill=\"0\"/>"
-                                "<value param=\"perc_level\"/>"
-                            "</vbox>"        
-                            "<vbox>"
-                                "<label param=\"perc_vel2amp\"/>"
-                                "<knob param=\"perc_vel2amp\" expand=\"0\" fill=\"0\"/>"
-                                "<value param=\"perc_vel2amp\"/>"
-                            "</vbox>"        
-                            "<vbox>"
-                                "<label param=\"perc_waveform\"/>"
-                                "<combo param=\"perc_waveform\"/>"
-                            "</vbox>"        
-                            "<vbox>"
-                                "<label param=\"perc_harmonic\"/>"
-                                "<knob param=\"perc_harmonic\" expand=\"0\" fill=\"0\"/>"
-                                "<value param=\"perc_harmonic\"/>"
-                            "</vbox>"        
-                            "<vbox>"
-                                "<label param=\"perc_fm_waveform\"/>"
-                                "<combo param=\"perc_fm_waveform\"/>"
-                            "</vbox>"        
-                            "<vbox>"
-                                "<label param=\"perc_fm_harmonic\"/>"
-                                "<knob param=\"perc_fm_harmonic\" expand=\"0\" fill=\"0\"/>"
-                                "<value param=\"perc_fm_harmonic\"/>"
-                            "</vbox>"        
-                            "<vbox>"
-                                "<label param=\"perc_fm_depth\"/>"
-                                "<knob param=\"perc_fm_depth\" expand=\"0\" fill=\"0\"/>"
-                                "<value param=\"perc_fm_depth\"/>"
-                            "</vbox>"        
-                            "<vbox>"
-                                "<label param=\"perc_trigger\"/>"
-                                "<combo param=\"perc_trigger\"/>"
-                            "</vbox>"        
-                        "</hbox>"
+                        "<vbox>"
+                            "<align scale-x=\"0.0\" scale-y=\"1.0\"><vbox><keyboard octaves=\"10\"/><curve key=\"map_curve\" maxpoints=\"4\"/></vbox></align>"
+                            "<hbox>"
+                                "<table rows=\"2\" cols=\"4\">"
+                                    "<vbox attach-x=\"0\" attach-y=\"0\">"
+                                        "<label param=\"perc_waveform\"/>"
+                                        "<combo param=\"perc_waveform\"/>"
+                                    "</vbox>"        
+                                    "<vbox attach-x=\"1\" attach-y=\"0\">"
+                                        "<label param=\"perc_harmonic\"/>"
+                                        "<knob param=\"perc_harmonic\" expand=\"0\" fill=\"0\"/>"
+                                        "<value param=\"perc_harmonic\"/>"
+                                    "</vbox>"        
+                                    "<vbox attach-x=\"2\" attach-y=\"0\">"
+                                        "<label param=\"perc_level\"/>"
+                                        "<knob param=\"perc_level\" expand=\"0\" fill=\"0\"/>"
+                                        "<value param=\"perc_level\"/>"
+                                    "</vbox>"        
+                                    "<vbox attach-x=\"3\" attach-y=\"0\">"
+                                        "<label param=\"perc_decay\"/>"
+                                        "<knob param=\"perc_decay\" expand=\"0\" fill=\"0\"/>"
+                                        "<value param=\"perc_decay\"/>"
+                                    "</vbox>"
+                                    "<vbox attach-x=\"0\" attach-y=\"1\">"
+                                        "<label param=\"perc_fm_waveform\"/>"
+                                        "<combo param=\"perc_fm_waveform\"/>"
+                                    "</vbox>"        
+                                    "<vbox attach-x=\"1\" attach-y=\"1\">"
+                                        "<label param=\"perc_fm_harmonic\"/>"
+                                        "<knob param=\"perc_fm_harmonic\" expand=\"0\" fill=\"0\"/>"
+                                        "<value param=\"perc_fm_harmonic\"/>"
+                                    "</vbox>"        
+                                    "<vbox attach-x=\"2\" attach-y=\"1\">"
+                                        "<label param=\"perc_fm_depth\"/>"
+                                        "<knob param=\"perc_fm_depth\" expand=\"0\" fill=\"0\"/>"
+                                        "<value param=\"perc_fm_depth\"/>"
+                                    "</vbox>"        
+                                    "<vbox attach-x=\"3\" attach-y=\"1\">"
+                                        "<label param=\"perc_fm_decay\"/>"
+                                        "<knob param=\"perc_fm_decay\" expand=\"0\" fill=\"0\"/>"
+                                        "<value param=\"perc_fm_decay\"/>"
+                                    "</vbox>"
+                                "</table>"
+                                "<vbox>"
+                                    "<hbox>"
+                                        "<vbox>"
+                                            "<label param=\"perc_trigger\"/>"
+                                            "<combo param=\"perc_trigger\"/>"
+                                        "</vbox>"        
+                                    "</hbox>"
+                                    "<hbox>"
+                                        "<vbox>"
+                                            "<label param=\"perc_vel2amp\"/>"
+                                            "<knob param=\"perc_vel2amp\" expand=\"0\" fill=\"0\"/>"
+                                            "<value param=\"perc_vel2amp\"/>"
+                                        "</vbox>"        
+                                    "</hbox>"
+                                "</vbox>"
+                            "</hbox>"
+                        "</vbox>"
                     "</frame>"
                 "</align>"
                 "<align scale-x=\"0.8\" scale-y=\"0.3\">"
@@ -472,13 +489,17 @@ parameter_properties organ_audio_module::param_props[] = {
     { 0,       0,  2, 0, PF_ENUM | PF_SCALE_LINEAR | PF_CTL_COMBO, organ_routing_names, "routing9", "Routing 9" },
 
     { 96,      0,  127, 128, PF_INT | PF_CTL_KNOB | PF_UNIT_NOTE, NULL, "foldnote", "Foldover" },
-    { 200,         10,  3000, 100, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "perc_decay", "P: Decay time" },
+    
+    { 200,         10,  3000, 100, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "perc_decay", "P: Carrier Decay" },
     { 0.25,      0,  1, 100, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB, NULL, "perc_level", "P: Level" },
-    { 2,         0,  organ_voice_base::wave_count_small - 1, 1, PF_ENUM | PF_CTL_COMBO, organ_wave_names, "perc_waveform", "P: Waveform" },
-    { 16,      1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "perc_harmonic", "P: Harmonic" },
-    { 2,         0,  organ_voice_base::wave_count_small - 1, 1, PF_ENUM | PF_CTL_COMBO, organ_wave_names, "perc_fm_waveform", "P: FM Waveform" },
-    { 16,      1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "perc_fm_harmonic", "P: FM Freq" },
-    { 0,          0,    1,    0, PF_FLOAT | PF_SCALE_PERC, NULL, "perc_fm_depth", "P: FM Depth" },
+    { 2,         0,  organ_voice_base::wave_count_small - 1, 1, PF_ENUM | PF_CTL_COMBO, organ_wave_names, "perc_waveform", "P: Carrier Wave" },
+    { 2,      1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "perc_harmonic", "P: Carrier Frq" },
+    
+    { 200,         10,  3000, 100, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "perc_fm_decay", "P: Modulator Decay" },
+    { 0,          0,    4,    0, PF_FLOAT | PF_SCALE_PERC, NULL, "perc_fm_depth", "P: FM Depth" },
+    { 2,         0,  organ_voice_base::wave_count_small - 1, 1, PF_ENUM | PF_CTL_COMBO, organ_wave_names, "perc_fm_waveform", "P: Modulator Wave" },
+    { 2,      1, 32, 32, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB, NULL, "perc_fm_harmonic", "P: Modulator Frq" },
+    
     { 0,         0,  organ_voice_base::perctrig_count - 1, 0, PF_ENUM | PF_CTL_COMBO, organ_percussion_trigger_names, "perc_trigger", "P: Trigger" },
     { 0,          0,    1,    0, PF_FLOAT | PF_SCALE_PERC, NULL, "perc_vel2amp", "P: Vel->Amp" },
 
@@ -1088,6 +1109,7 @@ void organ_voice::render_block() {
 void drawbar_organ::update_params()
 {
     parameters->perc_decay_const = dsp::decay::calc_exp_constant(1.0 / 1024.0, 0.001 * parameters->percussion_time * sample_rate);
+    parameters->perc_fm_decay_const = dsp::decay::calc_exp_constant(1.0 / 1024.0, 0.001 * parameters->percussion_fm_time * sample_rate);
     for (int i = 0; i < 9; i++)
     {
         parameters->multiplier[i] = parameters->harmonics[i] * pow(2.0, parameters->detune[i] * (1.0 / 1200.0));
@@ -1118,7 +1140,35 @@ void organ_audio_module::execute(int cmd_no)
     }
 }
 
-void percussion_voice::render_to(float (*buf)[2], int nsamples) {
+void percussion_voice::note_on(int note, int vel)
+{
+    // do not reset phase if voice is still running (to prevent clicks, even at cost of slight loss of "percussiveness")
+    if (!amp.get_active())
+    {
+        phase = 0;
+        modphase = 0;
+    }
+    this->note = note;
+    amp.set(1.0f + (vel - 127) * parameters->percussion_vel2amp / 127.0);
+    update_pitch();
+    float (*kt)[2] = parameters->percussion_keytrack;
+    // assume last point (will be put there by padding)
+    fm_keytrack = kt[ORGAN_KEYTRACK_POINTS - 1][1];
+    // yes binary search would be nice if we had more than those crappy 4 points
+    for (int i = 0; i < ORGAN_KEYTRACK_POINTS - 1; i++)
+    {
+        float &lower = kt[i][0], upper = kt[i + 1][0];
+        if (note >= lower && note < upper)
+        {
+            fm_keytrack = kt[i][1] + (note - lower) * (kt[i + 1][1] - kt[i][1]) / (upper - lower);
+            break;
+        }
+    }
+    fm_amp.set(fm_keytrack * (1.0f + (vel - 127) * parameters->percussion_vel2amp / 127.0));
+}
+
+void percussion_voice::render_to(float (*buf)[2], int nsamples)
+{
     if (note == -1)
         return;
 
@@ -1130,6 +1180,7 @@ void percussion_voice::render_to(float (*buf)[2], int nsamples) {
     static float zeros[ORGAN_WAVE_SIZE];
     // XXXKF the decay needs work!
     double age_const = parameters->perc_decay_const;
+    double fm_age_const = parameters->perc_fm_decay_const;
     int timbre = parameters->get_percussion_wave();
     if (timbre < 0 || timbre >= wave_count_small)
         return;
@@ -1146,18 +1197,49 @@ void percussion_voice::render_to(float (*buf)[2], int nsamples) {
     }
     for (int i = 0; i < nsamples; i++) {
         float fm = wave(fmdata, modphase);
-        fm *= ORGAN_WAVE_SIZE * parameters->percussion_fm_depth * amp.get();
+        fm *= ORGAN_WAVE_SIZE * parameters->percussion_fm_depth * fm_amp.get();
         modphase += moddphase;
+        fm_amp.age_exp(fm_age_const, 1.0 / 32768.0);
         
-        float osc = level * wave(data, phase);
+        float osc = level * wave(data, phase + dsp::fixed_point<int64_t, 20>(fm));
         osc *= level * amp.get();
         buf[i][0] += osc;
         buf[i][1] += osc;
         amp.age_exp(age_const, 1.0 / 32768.0);
         phase += dphase;
-        phase += dsp::fixed_point<int64_t, 20>(fm);
     }
 }
+
+char *organ_audio_module::configure(const char *key, const char *value)
+{
+    if (!strcmp(key, "map_curve"))
+    {
+        stringstream ss(value);
+        int points;
+        ss >> points;
+        int i;
+        float x = 0, y = 0;
+        for (i = 0; i < points; i++)
+        {
+            static const int whites[] = { 0, 2, 4, 5, 7, 9, 11 };
+            ss >> x >> y;
+            int wkey = (int)(x * 71);
+            x = whites[wkey % 7] + 12 * (wkey / 7);
+            parameters->percussion_keytrack[i][0] = x;
+            parameters->percussion_keytrack[i][1] = y;
+            // cout << "(" << x << ", " << y << ")" << endl;
+        }
+        // pad with constant Y
+        for (; i < ORGAN_KEYTRACK_POINTS; i++) {
+            parameters->percussion_keytrack[i][0] = x;
+            parameters->percussion_keytrack[i][1] = y;
+        }
+        return NULL;
+    }
+    cout << "Set configure value " << key << " to " << value;
+    return NULL;
+}
+    
 plugin_command_info *organ_audio_module::get_commands()
 {
     static plugin_command_info cmds[] = {

-- 
calf audio plugins packaging



More information about the pkg-multimedia-commits mailing list