[SCM] calf/master: + Slightly better preset handling (will load/add/save on each preset add, to avoid problems with multiple windows); still doesn't update other windows automatically + Presets use short names of parameters + Preset parser reports errors in XML (in a slightly crappy way, but...) + Added installation of default presets + Initial preset file (contains some monosynth presets) + Monosynth: added Env->Amp parameter (more useful than I initially thought) + Monosynth: replaced 180 degree phase shift in 2x12dB LP mode with allpass filter-based phase shifter, so that one osc isn't killed when going to mono + Vintage Delay: added denormal killer for delay line

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


The following commit has been merged in the master branch:
commit 93312ffdda7ba3b0712db58af957e0400a30a821
Author: kfoltman <kfoltman at 78b06b96-2940-0410-b7fc-879d825d01d8>
Date:   Sat Dec 22 22:53:32 2007 +0000

    + Slightly better preset handling (will load/add/save on each preset add, to avoid problems with multiple windows); still doesn't update other windows automatically
    + Presets use short names of parameters
    + Preset parser reports errors in XML (in a slightly crappy way, but...)
    + Added installation of default presets
    + Initial preset file (contains some monosynth presets)
    + Monosynth: added Env->Amp parameter (more useful than I initially thought)
    + Monosynth: replaced 180 degree phase shift in 2x12dB LP mode with allpass filter-based phase shifter, so that one osc isn't killed when going to mono
    + Vintage Delay: added denormal killer for delay line
    
    
    
    git-svn-id: https://calf.svn.sourceforge.net/svnroot/calf/trunk@37 78b06b96-2940-0410-b7fc-879d825d01d8

diff --git a/Makefile.am b/Makefile.am
index 7114d1d..8f93369 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@ SUBDIRS = $(SRCDIRS)
 
 distdir = $(PACKAGE)-$(VERSION)
 
-EXTRA_DIST = TODO autogen.sh calf.glade
+EXTRA_DIST = TODO autogen.sh calf.glade presets.xml
 
 clean-local:
 	rm -f *~ *.old
diff --git a/configure.in b/configure.in
index 66d6ae6..87c8967 100644
--- a/configure.in
+++ b/configure.in
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.61)
-AC_INIT([calf],[0.0.7],[wdev at foltman.com])
+AC_INIT([calf],[0.0.8],[wdev at foltman.com])
 AC_CONFIG_SRCDIR([config.h.in])
 AC_CONFIG_HEADER([config.h])
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 1df5e0d..4d535af 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -57,8 +57,9 @@ clean-local:
 	$(RM) -f calfjackhost *~
 
 install-data-hook:
-if USE_JACK
 	install -d -m 755 $(DESTDIR)$(pkglibdir)    
+	install -c -m 644 $(top_srcdir)/presets.xml $(DESTDIR)$(pkglibdir)
+if USE_JACK
 	install -c -m 644 $(top_srcdir)/calf.glade $(DESTDIR)$(pkglibdir)
 endif
 if USE_LADSPA
@@ -76,8 +77,9 @@ endif
 uninstall-hook:
 if USE_JACK
 	rm -f $(DESTDIR)$(pkglibdir)/calf.glade 
-	rmdir $(DESTDIR)$(pkglibdir)    
 endif
+	rm -f $(DESTDIR)$(pkglibdir)/presets.xml
+	rmdir $(DESTDIR)$(pkglibdir)    
 if USE_LADSPA
 	$(RM) -f $(DESTDIR)$(with_ladspa_dir)/calf.so
 	$(RM) -f $(DESTDIR)$(with_ladspa_rdf_dir)/calf.rdf
diff --git a/src/calf/jackhost.h b/src/calf/jackhost.h
index e87e0eb..878075b 100644
--- a/src/calf/jackhost.h
+++ b/src/calf/jackhost.h
@@ -110,6 +110,7 @@ public:
     virtual int get_output_count()=0;
     virtual port *get_inputs()=0;
     virtual port *get_outputs()=0;
+    virtual port *get_midi_port() { return get_midi() ? &midi_port : NULL; }
     virtual float *get_params()=0;
     virtual void init_module()=0;
     virtual void cache_ports()=0;
diff --git a/src/calf/modules.h b/src/calf/modules.h
index 6a20e3e..409f50c 100644
--- a/src/calf/modules.h
+++ b/src/calf/modules.h
@@ -361,6 +361,7 @@ public:
         for(uint32_t i = offset; i < end; i++)
         {
             float in_left = buffers[v][(bufptr - deltime_l) & ADDR_MASK], in_right = buffers[1 - v][(bufptr - deltime_r) & ADDR_MASK], out_left, out_right, del_left, del_right;
+            dsp::sanitize(in_left), dsp::sanitize(in_right);
 
             out_left = ins[0][i] + in_left * amt_left;
             out_right = ins[1][i] + in_right * amt_right;
diff --git a/src/calf/modules_synths.h b/src/calf/modules_synths.h
index bf094d4..258b3cb 100644
--- a/src/calf/modules_synths.h
+++ b/src/calf/modules_synths.h
@@ -37,7 +37,7 @@ class monosynth_audio_module: public null_audio_module
 public:
     enum { wave_saw, wave_sqr, wave_pulse, wave_sine, wave_triangle, wave_count };
     enum { flt_lp12, flt_lp24, flt_2lp12, flt_hp12, flt_lpbr, flt_hpbr, flt_bp6, flt_2bp6 };
-    enum { par_wave1, par_wave2, par_detune, par_osc2xpose, par_oscmode, par_oscmix, par_filtertype, par_cutoff, par_resonance, par_cutoffsep, par_envmod, par_envtores, par_attack, par_decay, par_sustain, par_release, par_keyfollow, par_legato, par_portamento, par_vel2amp, par_vel2filter, param_count };
+    enum { par_wave1, par_wave2, par_detune, par_osc2xpose, par_oscmode, par_oscmix, par_filtertype, par_cutoff, par_resonance, par_cutoffsep, par_envmod, par_envtores, par_envtoamp, par_attack, par_decay, par_sustain, par_release, par_keyfollow, par_legato, par_portamento, par_vel2filter, par_vel2amp, param_count };
     enum { in_count = 0, out_count = 2, support_midi = true, rt_capable = true };
     enum { step_size = 64 };
     static const char *port_names[];
@@ -52,10 +52,11 @@ public:
     
     float buffer[step_size], buffer2[step_size];
     uint32_t output_pos;
+    onepole<float> phaseshifter;
     biquad<float> filter;
     biquad<float> filter2;
     int wave1, wave2, filter_type;
-    float freq, start_freq, target_freq, cutoff, decay_factor, fgain, separation;
+    float freq, start_freq, target_freq, cutoff, decay_factor, fgain, fgain_delta, separation;
     float detune, xpose, xfade, pitchbend, ampctl, fltctl, queue_vel;
     float odcr, porta_time;
     int queue_note_on;
@@ -67,6 +68,9 @@ public:
         srate = sr;
         crate = sr / step_size;
         odcr = (float)(1.0 / crate);
+        phaseshifter.set_ap(1000.f, sr);
+        fgain = 0.f;
+        fgain_delta = 0.f;
     }
     void delayed_note_on()
     {
@@ -120,6 +124,7 @@ public:
         if (!(legato & 1) || envelope.released()) {
             envelope.note_on();
         }
+        envelope.advance();
         queue_note_on = -1;
     }
     void note_on(int note, int vel)
@@ -220,6 +225,7 @@ public:
             wave = filter.process_d1(wave);
             wave = filter2.process_d1(wave);
             buffer[i] = softclip(wave);
+            fgain += fgain_delta;
         }
     }
     void calculate_buffer_single()
@@ -231,6 +237,7 @@ public:
             float wave = fgain * (osc1val + (osc2val - osc1val) * xfade);
             wave = filter.process_d1(wave);
             buffer[i] = softclip(wave);
+            fgain += fgain_delta;
         }
     }
     void calculate_buffer_stereo()
@@ -240,9 +247,10 @@ public:
             float osc1val = osc1.get();
             float osc2val = osc2.get();
             float wave1 = osc1val + (osc2val - osc1val) * xfade;
-            float wave2 = osc1val + ((-osc2val) - osc1val) * xfade;
+            float wave2 = phaseshifter.process_ap(wave1);
             buffer[i] = softclip(fgain * filter.process_d1(wave1));
             buffer2[i] = softclip(fgain * filter2.process_d1(wave2));
+            fgain += fgain_delta;
         }
     }
     bool is_stereo_filter() const
@@ -282,48 +290,52 @@ public:
         cutoff = dsp::clip(cutoff , 10.f, 18000.f);
         float resonance = *params[par_resonance];
         float e2r = *params[par_envtores];
+        float e2a = *params[par_envtoamp];
         resonance = resonance * (1 - e2r) + (0.7 + (resonance - 0.7) * env * env) * e2r;
         float cutoff2 = dsp::clip(cutoff * separation, 10.f, 18000.f);
+        float newfgain = 0.f;
         switch(filter_type)
         {
         case flt_lp12:
             filter.set_lp_rbj(cutoff, resonance, srate);
-            fgain = min(0.7f, 0.7f / resonance) * ampctl;
+            newfgain = min(0.7f, 0.7f / resonance) * ampctl;
             break;
         case flt_hp12:
             filter.set_hp_rbj(cutoff, resonance, srate);
-            fgain = min(0.7f, 0.7f / resonance) * ampctl;
+            newfgain = min(0.7f, 0.7f / resonance) * ampctl;
             break;
         case flt_lp24:
             filter.set_lp_rbj(cutoff, resonance, srate);
             filter2.set_lp_rbj(cutoff2, resonance, srate);
-            fgain = min(0.5f, 0.5f / resonance) * ampctl;
+            newfgain = min(0.5f, 0.5f / resonance) * ampctl;
             break;
         case flt_lpbr:
             filter.set_lp_rbj(cutoff, resonance, srate);
             filter2.set_br_rbj(cutoff2, resonance, srate);
-            fgain = min(0.5f, 0.5f / resonance) * ampctl;        
+            newfgain = min(0.5f, 0.5f / resonance) * ampctl;        
             break;
         case flt_hpbr:
             filter.set_hp_rbj(cutoff, resonance, srate);
             filter2.set_br_rbj(cutoff2, resonance, srate);
-            fgain = min(0.5f, 0.5f / resonance) * ampctl;        
+            newfgain = min(0.5f, 0.5f / resonance) * ampctl;        
             break;
         case flt_2lp12:
             filter.set_lp_rbj(cutoff, resonance, srate);
             filter2.set_lp_rbj(cutoff2, resonance, srate);
-            fgain = min(0.7f, 0.7f / resonance) * ampctl;
+            newfgain = min(0.7f, 0.7f / resonance) * ampctl;
             break;
         case flt_bp6:
             filter.set_bp_rbj(cutoff, resonance, srate);
-            fgain = ampctl;
+            newfgain = ampctl;
             break;
         case flt_2bp6:
             filter.set_bp_rbj(cutoff, resonance, srate);
             filter2.set_bp_rbj(cutoff2, resonance, srate);
-            fgain = ampctl;        
+            newfgain = ampctl;        
             break;
         }
+        newfgain *= 1.0 - (1.0 - env) * e2a;
+        fgain_delta = (newfgain - fgain) * (1.0 / step_size);
         switch(filter_type)
         {
         case flt_lp24:
diff --git a/src/calf/preset.h b/src/calf/preset.h
index c2aa766..95b9aa7 100644
--- a/src/calf/preset.h
+++ b/src/calf/preset.h
@@ -37,27 +37,14 @@ struct plugin_preset
     std::vector<std::string> param_names;
     std::vector<float> values;
     std::string blob;
-    std::string to_xml()
-    {
-        std::stringstream ss;
-        ss << "<preset bank=\"" << bank << "\" program=\"" << program << "\" plugin=\"" << plugin << "\" name=\"" << xml_escape(name) << "\">\n";
-        for (unsigned int i = 0; i < values.size(); i++) {
-            if (i < param_names.size())
-                ss << "  <param name=\"" << param_names[i] << "\" value=\"" << values[i] << "\" />\n";
-            else
-                ss << "  <param value=\"" << values[i] << "\" />\n";
-        }
-        // XXXKF I'm not writing blob here, because I don't use blobs yet anyway
-        ss << "</preset>\n";
-        return ss.str();
-    }
+    std::string to_xml();
 };
 
 struct preset_exception
 {
     std::string message, param, fulltext;
     int error;
-    preset_exception(const char *_message, const char *_param, int _error)
+    preset_exception(const std::string &_message, const std::string &_param, int _error)
     : message(_message), param(_param), error(_error)
     {
     }
@@ -73,13 +60,32 @@ struct preset_exception
     }
 };
 
-extern std::string get_preset_filename();
-extern void load_presets(const char *filename);
-extern void save_presets(const char *filename);
-extern void add_preset(const plugin_preset &sp);
+typedef std::vector<plugin_preset> preset_vector;
+
+struct preset_list
+{
+    enum parser_state
+    {
+        START,
+        LIST,
+        PRESET,
+        VALUE,
+    } state;
+
+    preset_vector presets;
+    plugin_preset parser_preset;
+
+    static std::string get_preset_filename();
+    void load(const char *filename);
+    void save(const char *filename);
+    void add(const plugin_preset &sp);
+    
+protected:
+    static void xml_start_element_handler(void *user_data, const char *name, const char *attrs[]);
+    static void xml_end_element_handler(void *user_data, const char *name);
+};
 
-// this global variable is a temporary measure
-extern std::vector<plugin_preset> presets;
+extern preset_list global_presets;
 
 };
 
diff --git a/src/gui.cpp b/src/gui.cpp
index 76a342a..472fa88 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -332,15 +332,16 @@ plugin_gui_window::plugin_gui_window()
 string plugin_gui_window::make_gui_preset_list(GtkActionGroup *grp)
 {
     string preset_xml = preset_pre_xml;
-    for (unsigned int i = 0; i < presets.size(); i++)
+    preset_vector &pvec = global_presets.presets;
+    for (unsigned int i = 0; i < pvec.size(); i++)
     {
-        if (presets[i].plugin != gui->effect_name)
+        if (global_presets.presets[i].plugin != gui->effect_name)
             continue;
         stringstream ss;
         ss << "preset" << i;
-        preset_xml += "          <menuitem name=\""+presets[i].name+"\" action=\""+ss.str()+"\"/>\n";
+        preset_xml += "          <menuitem name=\""+pvec[i].name+"\" action=\""+ss.str()+"\"/>\n";
         
-        GtkActionEntry ae = { ss.str().c_str(), NULL, presets[i].name.c_str(), NULL, NULL, (GCallback)activate_preset };
+        GtkActionEntry ae = { ss.str().c_str(), NULL, pvec[i].name.c_str(), NULL, NULL, (GCallback)activate_preset };
         gtk_action_group_add_actions_full(preset_actions, &ae, 1, (gpointer)new activate_preset_params(gui, i), action_destroy_notify);
     }
     preset_xml += preset_post_xml;
diff --git a/src/jackhost.cpp b/src/jackhost.cpp
index 3128499..3b6ccdc 100644
--- a/src/jackhost.cpp
+++ b/src/jackhost.cpp
@@ -78,6 +78,7 @@ static struct option long_options[] = {
     {"plugin", 0, 0, 'p'},
     {"input", 1, 0, 'i'},
     {"output", 1, 0, 'o'},
+    {"connect-midi", 1, 0, 'M'},
     {0,0,0,0},
 };
 
@@ -85,7 +86,7 @@ void print_help(char *argv[])
 {
     printf("JACK host for Calf effects\n"
         "Syntax: %s [--client <name>] [--input <name>] [--output <name>] [--midi <name>]\n"
-        "       [--help] [--version] pluginname ...\n", 
+        "       [--connect-midi <name>] [--help] [--version] pluginname ...\n", 
         argv[0]);
 }
 
@@ -111,11 +112,12 @@ int main(int argc, char *argv[])
     vector<jack_host_base *> plugins;
     vector<plugin_gui_window *> guis;
     set<int> chains;
+    string autoconnect_midi;
     gtk_init(&argc, &argv);
     glade_init();
     while(1) {
         int option_index;
-        int c = getopt_long(argc, argv, "c:i:o:m:ephv", long_options, &option_index);
+        int c = getopt_long(argc, argv, "c:i:o:m:M:ephv", long_options, &option_index);
         if (c == -1)
             break;
         switch(c) {
@@ -142,6 +144,9 @@ int main(int argc, char *argv[])
             case 'm':
                 client.midi_name = string(optarg) + "_%d";
                 break;
+            case 'M':
+                autoconnect_midi = string(optarg);
+                break;
         }
     }
     while(optind < argc) {
@@ -157,11 +162,17 @@ int main(int argc, char *argv[])
     }
     try {
         struct stat st;
-        if (!stat(get_preset_filename().c_str(), &st))
-            load_presets(get_preset_filename().c_str());
-        else if (!stat(PKGLIBDIR "/presets.xml", &st))
-            load_presets(PKGLIBDIR "/presets.xml");
-        
+        if (!stat(preset_list::get_preset_filename().c_str(), &st))
+            global_presets.load(preset_list::get_preset_filename().c_str());
+        if (global_presets.presets.empty() && !stat(PKGLIBDIR "/presets.xml", &st))
+            global_presets.load(PKGLIBDIR "/presets.xml");
+    }
+    catch(synth::preset_exception &e)
+    {
+        fprintf(stderr, "Error while loading presets: %s\n", e.what());
+        exit(1);
+    }
+    try {
         client.open(client_name);
         string cnp = string(client_name) + ":";
         for (unsigned int i = 0; i < names.size(); i++) {
@@ -219,6 +230,13 @@ int main(int argc, char *argv[])
                 client.connect(cnp + plugins[last]->get_outputs()[1].name, "system:playback_2");
             }
         }
+        if (autoconnect_midi != "") {
+            for (unsigned int i = 0; i < plugins.size(); i++)
+            {
+                if (plugins[i]->get_midi())
+                    client.connect(autoconnect_midi, cnp + plugins[i]->get_midi_port()->name);
+            }
+        }
         gtk_main();
         client.deactivate();
         for (unsigned int i = 0; i < names.size(); i++) {
@@ -228,7 +246,8 @@ int main(int argc, char *argv[])
         }
         client.close();
         
-        save_presets(get_preset_filename().c_str());
+        // this is now done on preset add
+        // save_presets(get_preset_filename().c_str());
     }
     catch(std::exception &e)
     {
diff --git a/src/modules.cpp b/src/modules.cpp
index 447feed..2a91c4d 100644
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@ -193,16 +193,17 @@ const char *monosynth_filter_choices[] = {
 parameter_properties monosynth_audio_module::param_props[] = {
     { wave_saw,         0, wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO, monosynth_waveform_names, "o1_wave", "Osc1 Wave" },
     { wave_sqr,         0, wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO, monosynth_waveform_names, "o2_wave", "Osc2 Wave" },
-    { 10,         0,  100, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "osc_detune", "O1<>2 Detune" },
-    { 12,       -24,   24, 1.01, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_SEMITONES, NULL, "osc_xpose", "Osc transpose" },
+    { 10,         0,  100, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "o12_detune", "O1<>2 Detune" },
+    { 12,       -24,   24, 1.01, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_SEMITONES, NULL, "o2_xpose", "Osc 2 transpose" },
     { 0,          0,    5, 1.01, PF_ENUM | PF_CTL_COMBO, monosynth_mode_names, "phase_mode", "Phase mode" },
     { 0.5,        0,    1, 1.01, PF_FLOAT | PF_SCALE_PERC, NULL, "o12_mix", "O1<>2 Mix" },
     { 1,          0,    7, 1.01, PF_ENUM | PF_CTL_COMBO, monosynth_filter_choices, "filter", "Filter" },
     { 33,        10,16000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "cutoff", "Cutoff" },
     { 2,        0.7,    8, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB, NULL, "res", "Resonance" },
     { 0,      -2400, 2400, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "filter_sep", "Separation" },
-    { 8000,  -10800,10800, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "e2cutoff", "Env->Cutoff" },
-    { 1,          0,    1, 1.01, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "e2res", "Env->Res" },
+    { 8000,  -10800,10800, 1.01, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_CENTS, NULL, "env2cutoff", "Env->Cutoff" },
+    { 1,          0,    1, 1.01, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "env2res", "Env->Res" },
+    { 1,          0,    1, 1.01, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "env2amp", "Env->Amp" },
     
     { 1,          1,20000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_a", "Attack" },
     { 350,       10,20000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "adsr_d", "Decay" },
@@ -213,8 +214,8 @@ parameter_properties monosynth_audio_module::param_props[] = {
     { 0,          0,    3, 1.01, PF_ENUM | PF_CTL_COMBO, monosynth_legato_names, "legato", "Legato Mode" },
     { 1,          1, 2000, 1.01, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "portamento", "Portamento" },
     
+    { 0.5,        0,    1,  0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "vel2filter", "Vel->Filter" },
     { 0,          0,    1,  0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "vel2amp", "Vel->Amp" },
-    { 0.5,        0,    1,  0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "vel2flt", "Vel->Filter" },
 };
 
 static synth::ladspa_info monosynth_info = { 0x8480, "Monosynth", "Calf Monosynth", "Krzysztof Foltman", copyright, "SynthesizerPlugin" };
diff --git a/src/preset.cpp b/src/preset.cpp
index c1bbdce..41eb23d 100644
--- a/src/preset.cpp
+++ b/src/preset.cpp
@@ -29,32 +29,39 @@
 using namespace synth;
 using namespace std;
 
-vector<plugin_preset> synth::presets;
+synth::preset_list synth::global_presets;
 
-string synth::get_preset_filename()
+std::string plugin_preset::to_xml()
 {
-    const char *home = getenv("HOME");
-    return string(home)+"/.calfpresets";
+    std::stringstream ss;
+    ss << "<preset bank=\"" << bank << "\" program=\"" << program << "\" plugin=\"" << xml_escape(plugin) << "\" name=\"" << xml_escape(name) << "\">\n";
+    for (unsigned int i = 0; i < values.size(); i++) {
+        if (i < param_names.size())
+            ss << "  <param name=\"" << xml_escape(param_names[i]) << "\" value=\"" << values[i] << "\" />\n";
+        else
+            ss << "  <param value=\"" << values[i] << "\" />\n";
+    }
+    // XXXKF I'm not writing blob here, because I don't use blobs yet anyway
+    ss << "</preset>\n";
+    return ss.str();
 }
 
-enum PresetParserState
+string synth::preset_list::get_preset_filename()
 {
-    START,
-    LIST,
-    PRESET,
-    VALUE,
-};
-
-static PresetParserState xml_parser_state;
-static plugin_preset parser_preset;
+    const char *home = getenv("HOME");
+    return string(home)+"/.calfpresets";
+}
 
-static void xml_start_element_handler(void *user_data, const char *name, const char *attrs[])
+void preset_list::xml_start_element_handler(void *user_data, const char *name, const char *attrs[])
 {
-    switch(xml_parser_state)
+    preset_list &self = *(preset_list *)user_data;
+    parser_state &state = self.state;
+    plugin_preset &parser_preset = self.parser_preset;
+    switch(state)
     {
     case START:
         if (!strcmp(name, "presets")) {
-            xml_parser_state = LIST;
+            state = LIST;
             return;
         }
         break;
@@ -67,15 +74,15 @@ static void xml_start_element_handler(void *user_data, const char *name, const c
             parser_preset.param_names.clear();
             parser_preset.values.clear();
             for(; *attrs; attrs += 2) {
-                if (!strcmp(attrs[0], "bank")) parser_preset.bank = atoi(attrs[1]);
+                if (!strcmp(attrs[0], "bank")) self.parser_preset.bank = atoi(attrs[1]);
                 else
-                if (!strcmp(attrs[0], "program")) parser_preset.program = atoi(attrs[1]);
+                if (!strcmp(attrs[0], "program")) self.parser_preset.program = atoi(attrs[1]);
                 else
-                if (!strcmp(attrs[0], "name")) parser_preset.name = attrs[1];
+                if (!strcmp(attrs[0], "name")) self.parser_preset.name = attrs[1];
                 else
-                if (!strcmp(attrs[0], "plugin")) parser_preset.plugin = attrs[1];
+                if (!strcmp(attrs[0], "plugin")) self.parser_preset.plugin = attrs[1];
             }
-            xml_parser_state = PRESET;
+            state = PRESET;
             return;
         }
         break;
@@ -91,9 +98,9 @@ static void xml_start_element_handler(void *user_data, const char *name, const c
                     str >> value;
                 }
             }
-            parser_preset.param_names.push_back(name);
-            parser_preset.values.push_back(value);
-            xml_parser_state = VALUE;
+            self.parser_preset.param_names.push_back(name);
+            self.parser_preset.values.push_back(value);
+            state = VALUE;
             return;
         }
         break;
@@ -104,28 +111,31 @@ static void xml_start_element_handler(void *user_data, const char *name, const c
     throw preset_exception("Invalid XML element: %s", name, 0);
 }
 
-static void xml_end_element_handler(void *user_data, const char *name)
+void preset_list::xml_end_element_handler(void *user_data, const char *name)
 {
-    switch(xml_parser_state)
+    preset_list &self = *(preset_list *)user_data;
+    preset_vector &presets = self.presets;
+    parser_state &state = self.state;
+    switch(state)
     {
     case START:
         break;
     case LIST:
         if (!strcmp(name, "presets")) {
-            xml_parser_state = START;
+            state = START;
             return;
         }
         break;
     case PRESET:
         if (!strcmp(name, "preset")) {
-            presets.push_back(parser_preset);
-            xml_parser_state = LIST;
+            presets.push_back(self.parser_preset);
+            state = LIST;
             return;
         }
         break;
     case VALUE:
         if (!strcmp(name, "param")) {
-            xml_parser_state = PRESET;
+            state = PRESET;
             return;
         }
         break;
@@ -133,10 +143,11 @@ static void xml_end_element_handler(void *user_data, const char *name)
     throw preset_exception("Invalid XML element close: %s", name, 0);
 }
 
-void synth::load_presets(const char *filename)
+void preset_list::load(const char *filename)
 {
-    xml_parser_state = START;
+    state = START;
     XML_Parser parser = XML_ParserCreate("UTF-8");
+    XML_SetUserData(parser, this);
     int fd = open(filename, O_RDONLY);
     if (fd < 0) 
         throw preset_exception("Could not load the presets from ", filename, errno);
@@ -148,13 +159,17 @@ void synth::load_presets(const char *filename)
         // XXXKF not an optimal error/EOF handling :)
         if (len <= 0)
             break;
-        XML_Parse(parser, buf, len, 0);
+        XML_Status status = XML_Parse(parser, buf, len, 0);
+        if (status == XML_STATUS_ERROR)
+            throw preset_exception(string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ", filename, errno);
     } while(1);
-    XML_Parse(parser, buf, 0, 1);
+    XML_Status status = XML_Parse(parser, buf, 0, 1);
+    if (status == XML_STATUS_ERROR)
+        throw preset_exception(string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ", filename, errno);
     close(fd);
 }
 
-void synth::save_presets(const char *filename)
+void preset_list::save(const char *filename)
 {
     string xml = "<presets>\n";
     for (unsigned int i = 0; i < presets.size(); i++)
@@ -168,7 +183,7 @@ void synth::save_presets(const char *filename)
     close(fd);
 }
 
-void synth::add_preset(const plugin_preset &sp)
+void preset_list::add(const plugin_preset &sp)
 {
     presets.push_back(sp);
 }
diff --git a/src/preset_gui.cpp b/src/preset_gui.cpp
index 4447bbf..0005326 100644
--- a/src/preset_gui.cpp
+++ b/src/preset_gui.cpp
@@ -46,10 +46,20 @@ void store_preset_ok(GtkAction *action, plugin_gui *gui)
     sp.plugin = gui->effect_name;
     int count = gui->plugin->get_param_count();
     for (int i = 0; i < count; i++) {
-        sp.param_names.push_back(gui->plugin->get_param_props(i)->name);
+        sp.param_names.push_back(gui->plugin->get_param_props(i)->short_name);
         sp.values.push_back(gui->plugin->get_param_value(i));
     }
-    add_preset(sp);
+    preset_list tmp;
+    try {
+        tmp.load(tmp.get_preset_filename().c_str());
+    }
+    catch(...)
+    {
+        tmp = global_presets;
+    }
+    tmp.add(sp);
+    global_presets = tmp;
+    global_presets.save(tmp.get_preset_filename().c_str());
     gtk_widget_destroy(store_preset_dlg);
     gui->window->fill_gui_presets();
 }
@@ -77,19 +87,25 @@ void synth::store_preset(GtkWindow *toplevel, plugin_gui *gui)
 void synth::activate_preset(GtkAction *action, activate_preset_params *params)
 {
     plugin_gui *gui = params->gui;
-    plugin_preset &p = presets[params->preset];
+    plugin_preset &p = global_presets.presets[params->preset];
     if (p.plugin != gui->effect_name)
         return;
     map<string, int> names;
     int count = gui->plugin->get_param_count();
-    for (int i = 0; i < count; i++) 
+    // this is deliberately done in two separate loops - if you wonder why, just think for a while :)
+    for (int i = 0; i < count; i++)
         names[gui->plugin->get_param_props(i)->name] = i;
+    for (int i = 0; i < count; i++)
+        names[gui->plugin->get_param_props(i)->short_name] = i;
     // no support for unnamed parameters... tough luck :)
     for (unsigned int i = 0; i < min(p.param_names.size(), p.values.size()); i++)
     {
         map<string, int>::iterator pos = names.find(p.param_names[i]);
-        if (pos == names.end())
+        if (pos == names.end()) {
+            // XXXKF should have a mechanism for notifying a GUI
+            printf("Warning: unknown parameter %s for plugin %s\n", p.param_names[i].c_str(), gui->effect_name);
             continue;
+        }
         gui->plugin->set_param_value(pos->second, p.values[i]);
     }
     gui->refresh();

-- 
calf audio plugins packaging



More information about the pkg-multimedia-commits mailing list