diff --git a/gtk2_ardour/plugin_ui.h b/gtk2_ardour/plugin_ui.h index edc7c609aa..0caed4ac95 100644 --- a/gtk2_ardour/plugin_ui.h +++ b/gtk2_ardour/plugin_ui.h @@ -324,8 +324,8 @@ class VSTPluginUI : public PlugUIBase, public Gtk::VBox bool configure_handler (GdkEventConfigure*, Gtk::Socket*); void save_plugin_setting (); - void create_preset_store (); void preset_chosen (); + void update_presets (); }; #endif // VST_SUPPORT diff --git a/gtk2_ardour/vst_pluginui.cc b/gtk2_ardour/vst_pluginui.cc index 6c1b359fd4..72c73f7467 100644 --- a/gtk2_ardour/vst_pluginui.cc +++ b/gtk2_ardour/vst_pluginui.cc @@ -35,7 +35,14 @@ VSTPluginUI::VSTPluginUI (boost::shared_ptr pi, boost::shared_ptr< : PlugUIBase (pi), vst (vp) { - create_preset_store (); + preset_model = ListStore::create (preset_columns); + + CellRenderer* renderer = manage (new CellRendererText()); + vst_preset_combo.pack_start (*renderer, true); + vst_preset_combo.add_attribute (*renderer, "text", 0); + vst_preset_combo.set_model (preset_model); + + update_presets (); fst_run_editor (vst->fst()); @@ -63,8 +70,19 @@ VSTPluginUI::~VSTPluginUI () void VSTPluginUI::preset_chosen () { - // we can't dispatch directly here, too many plugins only expects one GUI thread. - vst->fst()->want_program = vst_preset_combo.get_active_row_number (); + int const r = vst_preset_combo.get_active_row_number (); + + if (r < vst->first_user_preset_index()) { + /* This is a plugin-provided preset. + We can't dispatch directly here; too many plugins expects only one GUI thread. + */ + vst->fst()->want_program = r; + } else { + /* This is a user preset. This method knows about the direct dispatch restriction, too */ + TreeModel::iterator i = vst_preset_combo.get_active (); + plugin->load_preset ((*i)[preset_columns.name]); + } + socket.grab_focus (); } @@ -138,37 +156,23 @@ VSTPluginUI::configure_handler (GdkEventConfigure* ev, Gtk::Socket *socket) } void -VSTPluginUI::create_preset_store () +VSTPluginUI::update_presets () { - FST *fst = vst->fst(); - int vst_version = fst->plugin->dispatcher (fst->plugin, effGetVstVersion, 0, 0, NULL, 0.0f); + std::vector presets = plugin->get_presets (); - preset_model = ListStore::create (preset_columns); + preset_model->clear (); - for (int i = 0; i < fst->plugin->numPrograms; ++i) { - char buf[100]; - TreeModel::Row row = *(preset_model->append()); - - snprintf (buf, 90, "preset %d", i); - - if (vst_version >= 2) { - fst->plugin->dispatcher (fst->plugin, 29, i, 0, buf, 0.0); - } - - row[preset_columns.name] = buf; - row[preset_columns.number] = i; + int j = 0; + for (std::vector::const_iterator i = presets.begin(); i != presets.end(); ++i) { + TreeModel::Row row = *(preset_model->append ()); + row[preset_columns.name] = i->label; + row[preset_columns.number] = j++; } - if (fst->plugin->numPrograms > 0) { - fst->plugin->dispatcher( fst->plugin, effSetProgram, 0, 0, NULL, 0.0 ); + if (presets.size() > 0) { + vst->fst()->plugin->dispatcher (vst->fst()->plugin, effSetProgram, 0, 0, NULL, 0); } - vst_preset_combo.set_model (preset_model); - - CellRenderer* renderer = manage (new CellRendererText()); - vst_preset_combo.pack_start (*renderer, true); - vst_preset_combo.add_attribute (*renderer, "text", 0); - if (vst->fst()->current_program != -1) { vst_preset_combo.set_active (vst->fst()->current_program); } else { diff --git a/libs/ardour/ardour/ladspa_plugin.h b/libs/ardour/ardour/ladspa_plugin.h index 4358de792e..7d561dd0a8 100644 --- a/libs/ardour/ardour/ladspa_plugin.h +++ b/libs/ardour/ardour/ladspa_plugin.h @@ -99,8 +99,9 @@ class LadspaPlugin : public ARDOUR::Plugin XMLNode& get_state(); int set_state (const XMLNode&, int version); - bool save_preset (std::string name); - void remove_preset (std::string name); + + std::vector get_presets (); + bool load_preset (const std::string& uri); bool has_editor() const { return false; } @@ -134,6 +135,11 @@ class LadspaPlugin : public ARDOUR::Plugin void run_in_place (pframes_t nsamples); void latency_compute_run (); int set_state_2X (const XMLNode&, int version); + std::string do_save_preset (std::string name); + void do_remove_preset (std::string name); + std::string preset_envvar () const; + std::string preset_source (std::string) const; + bool write_preset_file (std::string); }; class LadspaPluginInfo : public PluginInfo { diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index bdaf1a153b..daf3cff4f7 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -153,6 +153,8 @@ class LV2Plugin : public ARDOUR::Plugin void init (LV2World& world, SLV2Plugin plugin, framecnt_t rate); void run (pframes_t nsamples); void latency_compute_run (); + std::string do_save_preset (std::string); + void do_remove_preset (std::string); }; diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h index 170fb03e6c..828e532c6a 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -128,28 +128,32 @@ class Plugin : public PBD::StatefulDestructible, public Latent virtual bool parameter_is_input(uint32_t) const = 0; virtual bool parameter_is_output(uint32_t) const = 0; - virtual bool save_preset (std::string) = 0; - virtual void remove_preset (std::string) = 0; - virtual bool load_preset (const std::string& uri); + bool save_preset (std::string); + void remove_preset (std::string); + virtual bool load_preset (const std::string& uri) = 0; struct PresetRecord { - PresetRecord(const std::string& u, const std::string& l) : uri(u), label(l) {} + PresetRecord (const std::string& u, const std::string& l) : uri(u), label(l) {} std::string uri; std::string label; }; - virtual std::vector get_presets(); - virtual std::string current_preset() const { return std::string(); } + const PresetRecord * preset_by_label (const std::string &); + const PresetRecord * preset_by_uri (const std::string &); + /** Return this plugin's presets; should add them to _presets */ + virtual std::vector get_presets () = 0; + virtual std::string current_preset () const { return std::string(); } + + /** Emitted when a preset is added or removed, respectively */ + PBD::Signal0 PresetAdded; + PBD::Signal0 PresetRemoved; + + /* XXX: no-one listens to this */ static PBD::Signal0 PresetFileExists; - const PresetRecord* preset_by_label(const std::string& label); - const PresetRecord* preset_by_uri(const std::string& uri); - virtual bool has_editor() const = 0; - PBD::Signal0 PresetAdded; - PBD::Signal0 PresetRemoved; PBD::Signal2 ParameterChanged; /* NOTE: this block of virtual methods looks like the interface @@ -183,23 +187,25 @@ class Plugin : public PBD::StatefulDestructible, public Latent void set_cycles (uint32_t c) { _cycles = c; } cycles_t cycles() const { return _cycles; } - protected: +protected: + friend class PluginInsert; friend struct PluginInsert::PluginControl; virtual void set_parameter (uint32_t which, float val) = 0; - bool save_preset (std::string, std::string /* vst, ladspa etc. */); - void remove_preset (std::string, std::string); - bool write_preset_file (std::string, std::string); - std::string preset_source (std::string, std::string) const; - std::string preset_envvar () const; + /** Do the actual saving of the current plugin settings to a preset of the provided name. + * Should return a URI on success, or an empty string on failure. + */ + virtual std::string do_save_preset (std::string) = 0; + /** Do the actual removal of a preset of the provided name */ + virtual void do_remove_preset (std::string) = 0; ARDOUR::AudioEngine& _engine; ARDOUR::Session& _session; PluginInfoPtr _info; uint32_t _cycles; - std::map presets; + std::map _presets; }; PluginPtr find_plugin(ARDOUR::Session&, std::string unique_id, ARDOUR::PluginType); diff --git a/libs/ardour/ardour/vst_plugin.h b/libs/ardour/ardour/vst_plugin.h index a72b625288..332872e9bd 100644 --- a/libs/ardour/ardour/vst_plugin.h +++ b/libs/ardour/ardour/vst_plugin.h @@ -80,19 +80,25 @@ class VSTPlugin : public ARDOUR::Plugin bool parameter_is_output(uint32_t i) const { return false; } bool load_preset (const std::string& preset_label); - bool save_preset (std::string name); - void remove_preset (std::string name); + virtual std::vector get_presets (); + int first_user_preset_index () const; bool has_editor () const; XMLNode& get_state(); int set_state (XMLNode const &, int); - AEffect* plugin() const { return _plugin; } - FST* fst() const { return _fst; } + AEffect * plugin () const { return _plugin; } + FST * fst () const { return _fst; } +private: - private: + void do_remove_preset (std::string name); + std::string do_save_preset (std::string name); + gchar* get_chunk (bool); + int set_chunk (gchar const *, bool); + XMLTree * presets_tree () const; + FSTHandle* handle; FST* _fst; AEffect* _plugin; diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc index 00454cea5e..54722488f6 100644 --- a/libs/ardour/ladspa_plugin.cc +++ b/libs/ardour/ladspa_plugin.cc @@ -366,18 +366,6 @@ LadspaPlugin::get_state() return *root; } -bool -LadspaPlugin::save_preset (string name) -{ - return Plugin::save_preset (name, "ladspa"); -} - -void -LadspaPlugin::remove_preset (string name) -{ - return Plugin::remove_preset (name, "ladspa"); -} - int LadspaPlugin::set_state (const XMLNode& node, int version) { @@ -717,3 +705,197 @@ LadspaPluginInfo::LadspaPluginInfo() { type = ARDOUR::LADSPA; } + + +vector +LadspaPlugin::get_presets () +{ + vector result; + uint32_t id; + std::string unique (unique_id()); + + if (!isdigit (unique[0])) { + return result; + } + + id = atol (unique.c_str()); + + lrdf_uris* set_uris = lrdf_get_setting_uris(id); + + if (set_uris) { + for (uint32_t i = 0; i < (uint32_t) set_uris->count; ++i) { + if (char* label = lrdf_get_label(set_uris->items[i])) { + PresetRecord rec(set_uris->items[i], label); + result.push_back(rec); + _presets.insert (make_pair (set_uris->items[i], rec)); + } + } + lrdf_free_uris(set_uris); + } + + return result; +} + + +bool +LadspaPlugin::load_preset (const string& preset_uri) +{ + lrdf_defaults* defs = lrdf_get_setting_values(preset_uri.c_str()); + + if (defs) { + for (uint32_t i = 0; i < (uint32_t) defs->count; ++i) { + // The defs->items[i].pid < defs->count check is to work around + // a bug in liblrdf that saves invalid values into the presets file. + if (((uint32_t) defs->items[i].pid < (uint32_t) defs->count) && parameter_is_input (defs->items[i].pid)) { + set_parameter(defs->items[i].pid, defs->items[i].value); + } + } + lrdf_free_setting_values(defs); + } + + return true; +} + +/* XXX: should be in liblrdf */ +static void +lrdf_remove_preset (const char *source, const char *setting_uri) +{ + lrdf_statement p; + lrdf_statement *q; + lrdf_statement *i; + char setting_uri_copy[64]; + char buf[64]; + + strncpy(setting_uri_copy, setting_uri, sizeof(setting_uri_copy)); + + p.subject = setting_uri_copy; + strncpy(buf, LADSPA_BASE "hasPortValue", sizeof(buf)); + p.predicate = buf; + p.object = NULL; + q = lrdf_matches(&p); + + p.predicate = NULL; + p.object = NULL; + for (i = q; i; i = i->next) { + p.subject = i->object; + lrdf_remove_matches(&p); + } + + lrdf_free_statements(q); + + p.subject = NULL; + strncpy(buf, LADSPA_BASE "hasSetting", sizeof(buf)); + p.predicate = buf; + p.object = setting_uri_copy; + lrdf_remove_matches(&p); + + p.subject = setting_uri_copy; + p.predicate = NULL; + p.object = NULL; + lrdf_remove_matches (&p); +} + +void +LadspaPlugin::do_remove_preset (string name) +{ + string const envvar = preset_envvar (); + if (envvar.empty()) { + warning << _("Could not locate HOME. Preset not removed.") << endmsg; + return; + } + + Plugin::PresetRecord const * p = preset_by_label (name); + if (!p) { + return; + } + + string const source = preset_source (envvar); + lrdf_remove_preset (source.c_str(), p->uri.c_str ()); + + write_preset_file (envvar); +} + +string +LadspaPlugin::preset_envvar () const +{ + char* envvar; + if ((envvar = getenv ("HOME")) == 0) { + return ""; + } + + return envvar; +} + +string +LadspaPlugin::preset_source (string envvar) const +{ + return string_compose ("file:%1/.ladspa/rdf/ardour-presets.n3", envvar); +} + +bool +LadspaPlugin::write_preset_file (string envvar) +{ + string path = string_compose("%1/.ladspa", envvar); + if (g_mkdir_with_parents (path.c_str(), 0775)) { + warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg; + return false; + } + + path += "/rdf"; + if (g_mkdir_with_parents (path.c_str(), 0775)) { + warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg; + return false; + } + + string const source = preset_source (envvar); + + if (lrdf_export_by_source (source.c_str(), source.substr(5).c_str())) { + warning << string_compose(_("Error saving presets file %1."), source) << endmsg; + return false; + } + + return true; +} + +string +LadspaPlugin::do_save_preset (string name) +{ + lrdf_portvalue portvalues[parameter_count()]; + lrdf_defaults defaults; + std::string unique (unique_id()); + + if (!isdigit (unique[0])) { + return false; + } + + uint32_t const id = atol (unique.c_str()); + + defaults.count = parameter_count(); + defaults.items = portvalues; + + for (uint32_t i = 0; i < parameter_count(); ++i) { + if (parameter_is_input (i)) { + portvalues[i].pid = i; + portvalues[i].value = get_parameter(i); + } + } + + string const envvar = preset_envvar (); + if (envvar.empty()) { + warning << _("Could not locate HOME. Preset not saved.") << endmsg; + return false; + } + + string const source = preset_source (envvar); + + char* uri_char = lrdf_add_preset (source.c_str(), name.c_str(), id, &defaults); + string uri (uri_char); + free (uri_char); + + if (!write_preset_file (envvar)) { + return ""; + } + + return uri; +} + diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 082c2f1ba3..35cd9f4d1a 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -353,7 +353,7 @@ LV2Plugin::get_presets() SLV2Value name = slv2_results_get_binding_value(presets, 1); PresetRecord rec(slv2_value_as_string(uri), slv2_value_as_string(name)); result.push_back(rec); - this->presets.insert(std::make_pair(slv2_value_as_string(uri), rec)); + _presets.insert(std::make_pair(slv2_value_as_string(uri), rec)); } slv2_results_free(presets); return result; @@ -380,14 +380,14 @@ LV2Plugin::load_preset(const string& uri) return true; } -bool -LV2Plugin::save_preset (string /*name*/) +std::string +LV2Plugin::do_save_preset (string /*name*/) { - return false; + return ""; } void -LV2Plugin::remove_preset (string /*name*/) +LV2Plugin::do_remove_preset (string /*name*/) { return; } diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc index 3bfb9e883f..98bc5f35ad 100644 --- a/libs/ardour/plugin.cc +++ b/libs/ardour/plugin.cc @@ -78,244 +78,34 @@ Plugin::Plugin (const Plugin& other) , _session (other._session) , _info (other._info) , _cycles (0) - , presets (other.presets) { + } Plugin::~Plugin () { -} - -const Plugin::PresetRecord* -Plugin::preset_by_label(const string& label) -{ - // FIXME: O(n) - for (map::const_iterator i = presets.begin(); i != presets.end(); ++i) { - if (i->second.label == label) { - return &i->second; - } - } - return NULL; -} - -const Plugin::PresetRecord* -Plugin::preset_by_uri(const string& uri) -{ - map::const_iterator pr = presets.find(uri); - if (pr != presets.end()) { - return &pr->second; - } else { - return NULL; - } -} - -vector -Plugin::get_presets() -{ - vector result; - uint32_t id; - std::string unique (unique_id()); - - /* XXX problem: AU plugins don't have numeric ID's. - Solution: they have a different method of providing presets. - XXX sub-problem: implement it. - */ - - if (!isdigit (unique[0])) { - return result; - } - - id = atol (unique.c_str()); - - lrdf_uris* set_uris = lrdf_get_setting_uris(id); - - if (set_uris) { - for (uint32_t i = 0; i < (uint32_t) set_uris->count; ++i) { - if (char* label = lrdf_get_label(set_uris->items[i])) { - PresetRecord rec(set_uris->items[i], label); - result.push_back(rec); - presets.insert(std::make_pair(set_uris->items[i], rec)); - } - } - lrdf_free_uris(set_uris); - } - - return result; -} - -bool -Plugin::load_preset(const string& preset_uri) -{ - lrdf_defaults* defs = lrdf_get_setting_values(preset_uri.c_str()); - - if (defs) { - for (uint32_t i = 0; i < (uint32_t) defs->count; ++i) { - // The defs->items[i].pid < defs->count check is to work around - // a bug in liblrdf that saves invalid values into the presets file. - if (((uint32_t) defs->items[i].pid < (uint32_t) defs->count) && parameter_is_input (defs->items[i].pid)) { - set_parameter(defs->items[i].pid, defs->items[i].value); - } - } - lrdf_free_setting_values(defs); - } - - return true; -} - -/* XXX: should be in liblrdf */ -static void -lrdf_remove_preset (const char *source, const char *setting_uri) -{ - lrdf_statement p; - lrdf_statement *q; - lrdf_statement *i; - char setting_uri_copy[64]; - char buf[64]; - strncpy(setting_uri_copy, setting_uri, sizeof(setting_uri_copy)); - - p.subject = setting_uri_copy; - strncpy(buf, LADSPA_BASE "hasPortValue", sizeof(buf)); - p.predicate = buf; - p.object = NULL; - q = lrdf_matches(&p); - - p.predicate = NULL; - p.object = NULL; - for (i = q; i; i = i->next) { - p.subject = i->object; - lrdf_remove_matches(&p); - } - - lrdf_free_statements(q); - - p.subject = NULL; - strncpy(buf, LADSPA_BASE "hasSetting", sizeof(buf)); - p.predicate = buf; - p.object = setting_uri_copy; - lrdf_remove_matches(&p); - - p.subject = setting_uri_copy; - p.predicate = NULL; - p.object = NULL; - lrdf_remove_matches (&p); } void -Plugin::remove_preset (string name, string domain) +Plugin::remove_preset (string name) { - string const envvar = preset_envvar (); - if (envvar.empty()) { - warning << _("Could not locate HOME. Preset not removed.") << endmsg; - return; - } - - Plugin::PresetRecord const * p = preset_by_label (name); - if (!p) { - return; - } - - string const source = preset_source (envvar, domain); - lrdf_remove_preset (source.c_str(), p->uri.c_str ()); - - presets.erase (p->uri); - - write_preset_file (envvar, domain); - + do_remove_preset (name); + _presets.erase (preset_by_label (name)->uri); PresetRemoved (); /* EMIT SIGNAL */ } -string -Plugin::preset_envvar () const +bool +Plugin::save_preset (string name) { - char* envvar; - if ((envvar = getenv ("HOME")) == 0) { - return ""; + string const uri = do_save_preset (name); + + if (!uri.empty()) { + _presets.insert (make_pair (uri, PresetRecord (uri, name))); + PresetAdded (); /* EMIT SIGNAL */ } - return envvar; -} - -string -Plugin::preset_source (string envvar, string domain) const -{ - return string_compose ("file:%1/.%2/rdf/ardour-presets.n3", envvar, domain); -} - -bool -Plugin::write_preset_file (string envvar, string domain) -{ - string path = string_compose("%1/.%2", envvar, domain); - if (g_mkdir_with_parents (path.c_str(), 0775)) { - warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg; - return false; - } - - path += "/rdf"; - if (g_mkdir_with_parents (path.c_str(), 0775)) { - warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg; - return false; - } - - string const source = preset_source (envvar, domain); - - if (lrdf_export_by_source (source.c_str(), source.substr(5).c_str())) { - warning << string_compose(_("Error saving presets file %1."), source) << endmsg; - return false; - } - - return true; -} - -bool -Plugin::save_preset (string name, string domain) -{ - lrdf_portvalue portvalues[parameter_count()]; - lrdf_defaults defaults; - uint32_t id; - std::string unique (unique_id()); - - /* XXX problem: AU plugins don't have numeric ID's. - Solution: they have a different method of providing/saving presets. - XXX sub-problem: implement it. - */ - - if (!isdigit (unique[0])) { - return false; - } - - id = atol (unique.c_str()); - - defaults.count = parameter_count(); - defaults.items = portvalues; - - for (uint32_t i = 0; i < parameter_count(); ++i) { - if (parameter_is_input (i)) { - portvalues[i].pid = i; - portvalues[i].value = get_parameter(i); - } - } - - string const envvar = preset_envvar (); - if (envvar.empty()) { - warning << _("Could not locate HOME. Preset not saved.") << endmsg; - return false; - } - - string const source = preset_source (envvar, domain); - - char* uri = lrdf_add_preset (source.c_str(), name.c_str(), id, &defaults); - - /* XXX: why is the uri apparently kept as the key in the `presets' map and also in the PresetRecord? */ - - presets.insert (make_pair (uri, PresetRecord (uri, name))); - free (uri); - - bool const r = write_preset_file (envvar, domain); - - PresetAdded (); /* EMIT SIGNAL */ - - return r; + return !uri.empty (); } PluginPtr @@ -393,4 +183,26 @@ Plugin::input_streams () const return ChanCount::ZERO; } +const Plugin::PresetRecord * +Plugin::preset_by_label (const string& label) +{ + // FIXME: O(n) + for (map::const_iterator i = _presets.begin(); i != _presets.end(); ++i) { + if (i->second.label == label) { + return &i->second; + } + } + + return 0; +} +const Plugin::PresetRecord * +Plugin::preset_by_uri (const string& uri) +{ + map::const_iterator pr = _presets.find (uri); + if (pr != _presets.end()) { + return &pr->second; + } else { + return 0; + } +} diff --git a/libs/ardour/vst_plugin.cc b/libs/ardour/vst_plugin.cc index d01a70239a..333b7d893a 100644 --- a/libs/ardour/vst_plugin.cc +++ b/libs/ardour/vst_plugin.cc @@ -142,6 +142,37 @@ VSTPlugin::nth_parameter (uint32_t n, bool& ok) const return n; } +/** Get VST chunk as base64-encoded data. + * @param single true for single program, false for all programs. + * @return 0-terminated base64-encoded data; must be passed to g_free () by caller. + */ +gchar * +VSTPlugin::get_chunk (bool single) +{ + guchar* data; + int32_t data_size = _plugin->dispatcher (_plugin, 23 /* effGetChunk */, single ? 1 : 0, 0, &data, 0); + if (data_size == 0) { + return 0; + } + + return g_base64_encode (data, data_size); +} + +/** Set VST chunk from base64-encoded data. + * @param 0-terminated base64-encoded data. + * @param single true for single program, false for all programs. + * @return 0 on success, non-0 on failure + */ +int +VSTPlugin::set_chunk (gchar const * data, bool single) +{ + gsize size = 0; + guchar* raw_data = g_base64_decode (data, &size); + int const r = _plugin->dispatcher (_plugin, 24 /* effSetChunk */, single ? 1 : 0, size, raw_data, 0); + g_free (raw_data); + return r; +} + XMLNode& VSTPlugin::get_state() { @@ -156,12 +187,8 @@ VSTPlugin::get_state() if (_plugin->flags & 32 /* effFlagsProgramsChunks */) { - /* fetch the current chunk */ - - guchar* data; - int32_t data_size; - - if ((data_size = _plugin->dispatcher (_plugin, 23 /* effGetChunk */, 0, 0, &data, false)) == 0) { + gchar* data = get_chunk (false); + if (data == 0) { return *root; } @@ -169,9 +196,8 @@ VSTPlugin::get_state() XMLNode* chunk_node = new XMLNode (X_("chunk")); - gchar * encoded_data = g_base64_encode (data, data_size); - chunk_node->add_content (encoded_data); - g_free (encoded_data); + chunk_node->add_content (data); + g_free (data); root->add_child_nocopy (*chunk_node); @@ -220,11 +246,10 @@ VSTPlugin::set_state(const XMLNode& node, int) for (n = child->children ().begin (); n != child->children ().end (); ++n) { if ((*n)->is_content ()) { - gsize chunk_size = 0; - guchar * data = g_base64_decode ((*n)->content ().c_str (), &chunk_size); - //cerr << "Dispatch setChunk for " << name() << endl; - ret = _plugin->dispatcher (_plugin, 24 /* effSetChunk */, 0, chunk_size, data, 0); - g_free (data); + /* XXX: this may be dubious for the same reasons that we delay + execution of load_preset. + */ + ret = set_chunk ((*n)->content().c_str(), false); } } @@ -331,33 +356,96 @@ bool VSTPlugin::load_preset (const string& name) { if (_plugin->flags & 32 /* effFlagsProgramsChunks */) { - error << _("no support for presets using chunks at this time") - << endmsg; + + XMLTree* t = presets_tree (); + if (t == 0) { + return false; + } + + XMLNode* root = t->root (); + + /* Load a user preset chunk from our XML file and send it via a circuitous route to the plugin */ + + for (XMLNodeList::const_iterator i = root->children().begin(); i != root->children().end(); ++i) { + assert ((*i)->name() == X_("ChunkPreset")); + + XMLProperty* uri = (*i)->property (X_("uri")); + XMLProperty* label = (*i)->property (X_("label")); + + assert (uri); + assert (label); + + if (label->value() == name) { + + if (_fst->wanted_chunk) { + g_free (_fst->wanted_chunk); + } + + for (XMLNodeList::const_iterator j = (*i)->children().begin(); j != (*i)->children().end(); ++j) { + if ((*j)->is_content ()) { + /* we can't dispatch directly here; too many plugins expect only one GUI thread */ + gsize size = 0; + guchar* raw_data = g_base64_decode ((*j)->content().c_str(), &size); + _fst->wanted_chunk = raw_data; + _fst->wanted_chunk_size = size; + _fst->want_chunk = 1; + return true; + } + } + } + } + return false; } - return Plugin::load_preset (name); + + return true; } -bool -VSTPlugin::save_preset (string name) +string +VSTPlugin::do_save_preset (string name) { if (_plugin->flags & 32 /* effFlagsProgramsChunks */) { - error << _("no support for presets using chunks at this time") - << endmsg; - return false; + + XMLTree* t = presets_tree (); + if (t == 0) { + return ""; + } + + /* Add a chunk to our XML file of user presets */ + + XMLNode* p = new XMLNode (X_("ChunkPreset")); + /* XXX: use of _presets.size() + 1 for the unique ID here is dubious at best */ + string const uri = string_compose (X_("VST:%1:%2"), unique_id (), _presets.size() + 1); + p->add_property (X_("uri"), uri); + p->add_property (X_("label"), name); + gchar* data = get_chunk (true); + p->add_content (string (data)); + g_free (data); + t->root()->add_child_nocopy (*p); + + sys::path f = ARDOUR::user_config_directory (); + f /= "presets"; + f /= "vst"; + + t->write (f.to_string ()); + delete t; + return uri; } - return Plugin::save_preset (name, "vst"); + + return ""; } void -VSTPlugin::remove_preset (string name) +VSTPlugin::do_remove_preset (string name) { if (_plugin->flags & 32 /* effFlagsProgramsChunks */) { + + /* XXX: TODO */ + error << _("no support for presets using chunks at this time") << endmsg; return; } - Plugin::remove_preset (name, "vst"); } string @@ -472,7 +560,7 @@ VSTPlugin::name () const const char * VSTPlugin::maker () const { - return "imadeit"; + return _info->creator.c_str(); } const char * @@ -545,6 +633,94 @@ VSTPluginInfo::load (Session& session) } } +vector +VSTPlugin::get_presets () +{ + vector p; + + /* Built-in presets */ + + int const vst_version = _plugin->dispatcher (_plugin, effGetVstVersion, 0, 0, NULL, 0); + for (int i = 0; i < _plugin->numPrograms; ++i) { + PresetRecord r (string_compose (X_("VST:%1:%2"), unique_id (), i), ""); + + if (vst_version >= 2) { + char buf[256]; + _plugin->dispatcher (_plugin, 29, i, 0, buf, 0); + r.label = buf; + } else { + r.label = string_compose (_("Preset %1"), i); + } + + p.push_back (r); + _presets.insert (make_pair (r.uri, r)); + } + + /* User presets from our XML file */ + + XMLTree* t = presets_tree (); + + if (t) { + XMLNode* root = t->root (); + for (XMLNodeList::const_iterator i = root->children().begin(); i != root->children().end(); ++i) { + + assert ((*i)->name() == X_("ChunkPreset")); + + XMLProperty* uri = (*i)->property (X_("uri")); + XMLProperty* label = (*i)->property (X_("label")); + + assert (uri); + assert (label); + + PresetRecord r (uri->value(), label->value()); + p.push_back (r); + _presets.insert (make_pair (r.uri, r)); + } + } + + delete t; + + return p; +} + +/** @return XMLTree with our user presets; could be a new one if no existing + * one was found, or 0 if one was present but badly-formatted. + */ +XMLTree * +VSTPlugin::presets_tree () const +{ + XMLTree* t = new XMLTree; + + sys::path p = ARDOUR::user_config_directory (); + p /= "presets"; + + if (!is_directory (p)) { + create_directory (p); + } + + p /= "vst"; + + if (!exists (p)) { + t->set_root (new XMLNode (X_("VSTPresets"))); + return t; + } + + t->set_filename (p.to_string ()); + if (!t->read ()) { + delete t; + return 0; + } + + return t; +} + +/** @return Index of the first user preset in our lists */ +int +VSTPlugin::first_user_preset_index () const +{ + return _plugin->numPrograms; +} + VSTPluginInfo::VSTPluginInfo() { type = ARDOUR::VST; diff --git a/libs/fst/fst.h b/libs/fst/fst.h index 6ef5acf794..28ee49bd01 100644 --- a/libs/fst/fst.h +++ b/libs/fst/fst.h @@ -83,6 +83,9 @@ struct _FST int vst_version; int want_program; + int want_chunk; + unsigned char *wanted_chunk; + int wanted_chunk_size; int current_program; float *want_params; float *set_params; diff --git a/libs/fst/fstinfofile.c b/libs/fst/fstinfofile.c index 2d7fb2fa6a..c7061cd6b8 100644 --- a/libs/fst/fstinfofile.c +++ b/libs/fst/fstinfofile.c @@ -14,6 +14,8 @@ #define FALSE 0 #define TRUE !FALSE +extern char * strdup (const char *); + static char *read_string( FILE *fp ) { char buf[MAX_STRING_LEN]; diff --git a/libs/fst/vstwin.c b/libs/fst/vstwin.c index 09ca8f18d3..847ca19f1a 100644 --- a/libs/fst/vstwin.c +++ b/libs/fst/vstwin.c @@ -71,6 +71,7 @@ fst_new () pthread_cond_init (&fst->window_status_change, NULL); pthread_cond_init (&fst->plugin_dispatcher_called, NULL); fst->want_program = -1; + fst->want_chunk = 0; fst->current_program = -1; return fst; } @@ -178,6 +179,12 @@ again: /* did it work? */ fst->current_program = fst->plugin->dispatcher (fst->plugin, 3, /* effGetProgram */ 0, 0, NULL, 0); fst->want_program = -1; + printf("old-style leaves CP=%d\n", fst->current_program); + } + + if (fst->want_chunk == 1) { + fst->plugin->dispatcher (fst->plugin, 24 /* effSetChunk */, 1, fst->wanted_chunk_size, fst->wanted_chunk, 0); + fst->want_chunk = 0; } if(fst->dispatcher_wantcall) {