we always only use the "C" locale when saving.
[ardour.git] / libs / ardour / luaproc.cc
index fc9e5de537db860cbaa2b910ae55374e0cc780d1..30b34f584b84fed6f3ee696037fe83b05ade8ffd 100644 (file)
 */
 
 #include <glib.h>
+#include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
+
 #include "pbd/gstdio_compat.h"
 
 #include "pbd/pthread_utils.h"
 
 #include "ardour/audio_buffer.h"
 #include "ardour/buffer_set.h"
+#include "ardour/filesystem_paths.h"
 #include "ardour/luabindings.h"
 #include "ardour/luaproc.h"
 #include "ardour/luascripting.h"
@@ -114,9 +118,7 @@ LuaProc::init ()
        _stats_avg[0] = _stats_avg[1] = _stats_max[0] = _stats_max[1] = _stats_cnt = 0;
 #endif
 
-#ifndef NDEBUG
        lua.Print.connect (sigc::mem_fun (*this, &LuaProc::lua_print));
-#endif
        // register session object
        lua_State* L = lua.getState ();
        LuaBindings::stddef (L);
@@ -150,6 +152,7 @@ LuaProc::init ()
 void
 LuaProc::lua_print (std::string s) {
        std::cout <<"LuaProc: " << s << "\n";
+       PBD::error << "LuaProc: " << s << "\n";
 }
 
 bool
@@ -312,6 +315,7 @@ bool
 LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, ChanCount* imprecise)
 {
        // caller must hold process lock (no concurrent calls to interpreter
+       _output_configs.clear ();
 
        if (in.n_midi() > 0 && !_has_midi_input && !imprecise) {
                return false;
@@ -323,14 +327,9 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                return false;
        }
 
-       luabridge::LuaRef table = luabridge::getGlobal (L, "table"); //lua std lib
-       luabridge::LuaRef tablesort = table["sort"];
-       assert (tablesort.isFunction ());
-
        luabridge::LuaRef *_iotable = NULL; // can't use reference :(
        try {
                luabridge::LuaRef iotable = ioconfig ();
-               tablesort (iotable);
                if (iotable.isTable ()) {
                        _iotable = new luabridge::LuaRef (iotable);
                }
@@ -350,13 +349,14 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                return false;
        }
 
+       bool found = false;
+       bool exact_match = false;
        const int32_t audio_in = in.n_audio ();
-       int32_t audio_out;
        int32_t midi_out = 0; // TODO handle  _has_midi_output
 
        // preferred setting (provided by plugin_insert)
        assert (out.n_audio () > 0);
-       audio_out = out.n_audio ();
+       const int preferred_out = out.n_audio ();
 
        for (luabridge::Iterator i (iotable); !i.isNil (); ++i) {
                assert (i.value ().type () == LUA_TTABLE);
@@ -366,28 +366,36 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                int possible_out = io["audio_out"];
 
                // exact match
-               if ((possible_in == audio_in) && (possible_out == audio_out)) {
-                       out.set (DataType::MIDI, 0);
-                       out.set (DataType::AUDIO, audio_out);
-                       return true;
+               if ((possible_in == audio_in) && (possible_out == preferred_out)) {
+                       _output_configs.insert (preferred_out);
+                       exact_match = true;
+                       found = true;
+                       break;
                }
        }
 
        /* now allow potentially "imprecise" matches */
-       audio_out = -1;
-       bool found = false;
-
+       int32_t audio_out = -1;
        float penalty = 9999;
-       const int preferred_out = out.n_audio ();
 
-#define FOUNDCFG(nch) {                                  \
-       float p = fabsf ((float)(nch) - preferred_out);  \
-       if ((nch) > preferred_out) { p *= 1.1; }         \
-       if (p < penalty) {                               \
-               audio_out = (nch);                       \
-               penalty = p;                             \
-               found = true;                            \
-       }                                                \
+#define FOUNDCFG(nch) {                            \
+  float p = fabsf ((float)(nch) - preferred_out);  \
+  _output_configs.insert (nch);                    \
+  if ((nch) > preferred_out) { p *= 1.1; }         \
+  if (p < penalty) {                               \
+    audio_out = (nch);                             \
+    penalty = p;                                   \
+    found = true;                                  \
+  }                                                \
+}
+
+#define ANYTHINGGOES                               \
+  _output_configs.insert (0);
+
+#define UPTO(nch) {                                \
+  for (int n = 1; n < nch; ++n) {                  \
+    _output_configs.insert (n);                    \
+  }                                                \
 }
 
        for (luabridge::Iterator i (iotable); !i.isNil (); ++i) {
@@ -405,12 +413,15 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                        if (possible_out == -1) {
                                /* any configuration possible, stereo output */
                                FOUNDCFG (preferred_out);
+                               ANYTHINGGOES;
                        } else if (possible_out == -2) {
                                /* invalid, should be (0, -1) */
                                FOUNDCFG (preferred_out);
+                               ANYTHINGGOES;
                        } else if (possible_out < -2) {
                                /* variable number of outputs up to -N, */
                                FOUNDCFG (min (-possible_out, preferred_out));
+                               UPTO (-possible_out);
                        } else {
                                /* exact number of outputs */
                                FOUNDCFG (possible_out);
@@ -425,11 +436,13 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                        } else if (possible_out == -2) {
                                /* any configuration possible, pick matching */
                                FOUNDCFG (preferred_out);
+                               ANYTHINGGOES;
                        } else if (possible_out < -2) {
                                /* explicitly variable number of outputs, pick maximum */
                                FOUNDCFG (max (-possible_out, preferred_out));
                                /* and try min, too, in case the penalty is lower */
                                FOUNDCFG (min (-possible_out, preferred_out));
+                               UPTO (-possible_out)
                        } else {
                                /* exact number of outputs */
                                FOUNDCFG (possible_out);
@@ -437,17 +450,19 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                }
 
                if (possible_in == -2) {
-
                        if (possible_out == -1) {
                                /* any configuration possible, pick matching */
                                FOUNDCFG (preferred_out);
+                               ANYTHINGGOES;
                        } else if (possible_out == -2) {
                                /* invalid. interpret as (-1, -1) */
                                FOUNDCFG (preferred_out);
+                               ANYTHINGGOES;
                        } else if (possible_out < -2) {
                                /* invalid,  interpret as (<-2, <-2)
                                 * variable number of outputs up to -N, */
                                FOUNDCFG (min (-possible_out, preferred_out));
+                               UPTO (-possible_out)
                        } else {
                                /* exact number of outputs */
                                FOUNDCFG (possible_out);
@@ -466,12 +481,15 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                        } else if (possible_out == -1) {
                                /* any output configuration possible */
                                FOUNDCFG (preferred_out);
+                               ANYTHINGGOES;
                        } else if (possible_out == -2) {
                                /* invalid. interpret as (<-2, -1) */
                                FOUNDCFG (preferred_out);
+                               ANYTHINGGOES;
                        } else if (possible_out < -2) {
                                /* variable number of outputs up to -N, */
                                FOUNDCFG (min (-possible_out, preferred_out));
+                               UPTO (-possible_out)
                        } else {
                                /* exact number of outputs */
                                FOUNDCFG (possible_out);
@@ -483,13 +501,16 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                        if (possible_out == -1) {
                                /* any output configuration possible */
                                FOUNDCFG (preferred_out);
+                               ANYTHINGGOES;
                        } else if (possible_out == -2) {
                                /* invalid. interpret as (>0, -1) */
                                FOUNDCFG (preferred_out);
+                               ANYTHINGGOES;
                        } else if (possible_out < -2) {
                                /* > 0, < -2 is not specified
                                 * interpret as up to -N */
                                FOUNDCFG (min (-possible_out, preferred_out));
+                               UPTO (-possible_out)
                        } else {
                                /* exact number of outputs */
                                FOUNDCFG (possible_out);
@@ -524,13 +545,17 @@ LuaProc::can_support_io_configuration (const ChanCount& in, ChanCount& out, Chan
                }
        }
 
-
        if (!found) {
                return false;
        }
 
-       out.set (DataType::MIDI, midi_out); // currently always zero
-       out.set (DataType::AUDIO, audio_out);
+       if (exact_match) {
+               out.set (DataType::MIDI, midi_out); // currently always zero
+               out.set (DataType::AUDIO, preferred_out);
+       } else {
+               out.set (DataType::MIDI, midi_out); // currently always zero
+               out.set (DataType::AUDIO, audio_out);
+       }
        return true;
 }
 
@@ -659,8 +684,9 @@ LuaProc::connect_and_run (BufferSet& bufs,
                        (*_lua_dsp)(in_map, out_map, nframes);
                }
        } catch (luabridge::LuaException const& e) {
+               PBD::error << "LuaException: " << e.what () << "\n";
 #ifndef NDEBUG
-               printf ("LuaException: %s\n", e.what ());
+               std::cerr << "LuaException: " << e.what () << "\n";
 #endif
                return -1;
        }
@@ -687,7 +713,7 @@ LuaProc::add_state (XMLNode* root) const
 {
        XMLNode*    child;
        char        buf[32];
-       LocaleGuard lg(X_("C"));
+       LocaleGuard lg ();
 
        gchar* b64 = g_base64_encode ((const guchar*)_script.c_str (), _script.size ());
        std::string b64s (b64);
@@ -750,14 +776,14 @@ LuaProc::set_state (const XMLNode& node, int version)
 {
 #ifndef NO_PLUGIN_STATE
        XMLNodeList nodes;
-       XMLProperty *prop;
+       XMLProperty const * prop;
        XMLNodeConstIterator iter;
        XMLNode *child;
        const char *value;
        const char *port;
        uint32_t port_id;
 #endif
-       LocaleGuard lg (X_("C"));
+       LocaleGuard lg ();
 
        if (_script.empty ()) {
                if (set_script_from_state (node)) {
@@ -961,10 +987,7 @@ LuaProc::setup_lua_inline_gui (LuaState *lua_gui)
        LuaBindings::common (LG);
        LuaBindings::dsp (LG);
 
-#ifndef NDEBUG
        lua_gui->Print.connect (sigc::mem_fun (*this, &LuaProc::lua_print));
-#endif
-
        lua_gui->do_command ("function ardour () end");
        lua_gui->do_command (_script);
 
@@ -983,10 +1006,152 @@ LuaProc::setup_lua_inline_gui (LuaState *lua_gui)
        luabridge::push <float *> (LG, _shadow_data);
        lua_setglobal (LG, "CtrlPorts");
 }
+////////////////////////////////////////////////////////////////////////////////
+
+#include "ardour/search_paths.h"
+#include "sha1.c"
+
+std::string
+LuaProc::preset_name_to_uri (const std::string& name) const
+{
+       std::string uri ("urn:lua:");
+       char hash[41];
+       Sha1Digest s;
+       sha1_init (&s);
+       sha1_write (&s, (const uint8_t *) name.c_str(), name.size ());
+       sha1_write (&s, (const uint8_t *) _script.c_str(), _script.size ());
+       sha1_result_hash (&s, hash);
+       return uri + hash;
+}
+
+std::string
+LuaProc::presets_file () const
+{
+       return string_compose ("lua-%1", _info->unique_id);
+}
+
+XMLTree*
+LuaProc::presets_tree () const
+{
+       XMLTree* t = new XMLTree;
+       std::string p = Glib::build_filename (ARDOUR::user_config_directory (), "presets");
+
+       if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {
+               if (g_mkdir_with_parents (p.c_str(), 0755) != 0) {
+                       error << _("Unable to create LuaProc presets directory") << endmsg;
+               };
+       }
+
+       p = Glib::build_filename (p, presets_file ());
+
+       if (!Glib::file_test (p, Glib::FILE_TEST_EXISTS)) {
+               t->set_root (new XMLNode (X_("LuaPresets")));
+               return t;
+       }
+
+       t->set_filename (p);
+       if (!t->read ()) {
+               delete t;
+               return 0;
+       }
+       return t;
+}
+
+bool
+LuaProc::load_preset (PresetRecord r)
+{
+       boost::shared_ptr<XMLTree> t (presets_tree ());
+       if (t == 0) {
+               return false;
+       }
+
+       XMLNode* root = t->root ();
+       for (XMLNodeList::const_iterator i = root->children().begin(); i != root->children().end(); ++i) {
+               XMLProperty const * label = (*i)->property (X_("label"));
+               assert (label);
+               if (label->value() != r.label) {
+                       continue;
+               }
+
+               for (XMLNodeList::const_iterator j = (*i)->children().begin(); j != (*i)->children().end(); ++j) {
+                       if ((*j)->name() == X_("Parameter")) {
+                               XMLProperty const * index = (*j)->property (X_("index"));
+                               XMLProperty const * value = (*j)->property (X_("value"));
+                               assert (index);
+                               assert (value);
+                               set_parameter (atoi (index->value().c_str()), atof (value->value().c_str ()));
+                       }
+               }
+               return true;
+       }
+       return false;
+}
+
+std::string
+LuaProc::do_save_preset (std::string name) {
+
+       boost::shared_ptr<XMLTree> t (presets_tree ());
+       if (t == 0) {
+               return "";
+       }
+
+       std::string uri (preset_name_to_uri (name));
+
+       XMLNode* p = new XMLNode (X_("Preset"));
+       p->add_property (X_("uri"), uri);
+       p->add_property (X_("label"), name);
+
+       for (uint32_t i = 0; i < parameter_count(); ++i) {
+               if (parameter_is_input (i)) {
+                       XMLNode* c = new XMLNode (X_("Parameter"));
+                       c->add_property (X_("index"), string_compose ("%1", i));
+                       c->add_property (X_("value"), string_compose ("%1", get_parameter (i)));
+                       p->add_child_nocopy (*c);
+               }
+       }
+       t->root()->add_child_nocopy (*p);
+
+       std::string f = Glib::build_filename (ARDOUR::user_config_directory (), "presets");
+       f = Glib::build_filename (f, presets_file ());
+
+       t->write (f);
+       return uri;
+}
+
+void
+LuaProc::do_remove_preset (std::string name)
+{
+       boost::shared_ptr<XMLTree> t (presets_tree ());
+       if (t == 0) {
+               return;
+       }
+       t->root()->remove_nodes_and_delete (X_("label"), name);
+       std::string f = Glib::build_filename (ARDOUR::user_config_directory (), "presets");
+       f = Glib::build_filename (f, presets_file ());
+       t->write (f);
+}
+
+void
+LuaProc::find_presets ()
+{
+       boost::shared_ptr<XMLTree> t (presets_tree ());
+       if (t) {
+               XMLNode* root = t->root ();
+               for (XMLNodeList::const_iterator i = root->children().begin(); i != root->children().end(); ++i) {
+
+                       XMLProperty const * uri = (*i)->property (X_("uri"));
+                       XMLProperty const * label = (*i)->property (X_("label"));
+
+                       assert (uri);
+                       assert (label);
+
+                       PresetRecord r (uri->value(), label->value(), true);
+                       _presets.insert (make_pair (r.uri, r));
+               }
+       }
+}
 
 ////////////////////////////////////////////////////////////////////////////////
-#include <glibmm/miscutils.h>
-#include <glibmm/fileutils.h>
 
 LuaPluginInfo::LuaPluginInfo (LuaScriptInfoPtr lsi) {
        if (lsi->type != LuaScriptInfo::DSP) {
@@ -997,7 +1162,7 @@ LuaPluginInfo::LuaPluginInfo (LuaScriptInfoPtr lsi) {
        name = lsi->name;
        creator = lsi->author;
        category = lsi->category;
-       unique_id = "luascript"; // the interpreter is not unique.
+       unique_id = lsi->unique_id;
 
        n_inputs.set (DataType::AUDIO, 1);
        n_outputs.set (DataType::AUDIO, 1);
@@ -1035,5 +1200,19 @@ std::vector<Plugin::PresetRecord>
 LuaPluginInfo::get_presets (bool /*user_only*/) const
 {
        std::vector<Plugin::PresetRecord> p;
+       XMLTree* t = new XMLTree;
+       std::string pf = Glib::build_filename (ARDOUR::user_config_directory (), "presets", string_compose ("lua-%1", unique_id));
+       if (Glib::file_test (pf, Glib::FILE_TEST_EXISTS)) {
+               t->set_filename (pf);
+               if (t->read ()) {
+                       XMLNode* root = t->root ();
+                       for (XMLNodeList::const_iterator i = root->children().begin(); i != root->children().end(); ++i) {
+                               XMLProperty const * uri = (*i)->property (X_("uri"));
+                               XMLProperty const * label = (*i)->property (X_("label"));
+                               p.push_back (Plugin::PresetRecord (uri->value(), label->value(), true));
+                       }
+               }
+       }
+       delete t;
        return p;
 }