X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fvst_plugin.cc;h=1afc496b1697e7d5cc5c5bb4093cc6a27af89733;hb=7dffe671da3489206f06d57c96595d5eaabb9ec4;hp=8f30ff98e929ff560b41467c5a91081f56d5b50d;hpb=94b4c264d156d79efc687a98f17514b128960118;p=ardour.git diff --git a/libs/ardour/vst_plugin.cc b/libs/ardour/vst_plugin.cc index 8f30ff98e9..1afc496b16 100644 --- a/libs/ardour/vst_plugin.cc +++ b/libs/ardour/vst_plugin.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 Paul Davis + Copyright (C) 2010 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,95 +17,57 @@ */ -#include -#include -#include -#include - -#include -#include // so libraptor doesn't complain -#include -#include -#include // for memmove -#include -#include - -#include - -#include -#include - -#include "pbd/compose.h" -#include "pbd/error.h" +#include "pbd/locale_guard.h" #include "pbd/pathscanner.h" -#include "pbd/xml++.h" - -#include - +#include "ardour/vst_plugin.h" +#include "ardour/vestige/aeffectx.h" #include "ardour/session.h" -#include "ardour/audioengine.h" +#include "ardour/vst_types.h" #include "ardour/filesystem_paths.h" -#include "ardour/vst_plugin.h" -#include "ardour/buffer_set.h" #include "ardour/audio_buffer.h" -#include "ardour/midi_buffer.h" - -#include "pbd/stl_delete.h" #include "i18n.h" -#include using namespace std; -using namespace ARDOUR; using namespace PBD; -using std::min; -using std::max; +using namespace ARDOUR; -VSTPlugin::VSTPlugin (AudioEngine& e, Session& session, FSTHandle* h) - : Plugin (e, session) - , _have_pending_stop_events (false) +VSTPlugin::VSTPlugin (AudioEngine& engine, Session& session, VSTHandle* handle) + : Plugin (engine, session) + , _handle (handle) + , _state (0) + , _plugin (0) { - handle = h; + +} - if ((_fst = fst_instantiate (handle, Session::vst_callback, this)) == 0) { - throw failed_constructor(); - } +VSTPlugin::~VSTPlugin () +{ + +} - _plugin = _fst->plugin; +void +VSTPlugin::set_plugin (AEffect* e) +{ + _plugin = e; _plugin->user = this; /* set rate and blocksize */ - _plugin->dispatcher (_plugin, effSetSampleRate, 0, 0, NULL, - (float) session.frame_rate()); - _plugin->dispatcher (_plugin, effSetBlockSize, 0, - session.get_block_size(), NULL, 0.0f); - - /* set program to zero */ - - _plugin->dispatcher (_plugin, effSetProgram, 0, 0, NULL, 0.0f); - - // Plugin::setup_controls (); + _plugin->dispatcher (_plugin, effSetSampleRate, 0, 0, NULL, (float) _session.frame_rate()); + _plugin->dispatcher (_plugin, effSetBlockSize, 0, _session.get_block_size(), NULL, 0.0f); } -VSTPlugin::VSTPlugin (const VSTPlugin &other) - : Plugin (other) - , _have_pending_stop_events (false) +void +VSTPlugin::deactivate () { - handle = other.handle; - - if ((_fst = fst_instantiate (handle, Session::vst_callback, this)) == 0) { - throw failed_constructor(); - } - _plugin = _fst->plugin; - - // Plugin::setup_controls (); + _plugin->dispatcher (_plugin, effMainsChanged, 0, 0, NULL, 0.0f); } -VSTPlugin::~VSTPlugin () +void +VSTPlugin::activate () { - deactivate (); - fst_close (_fst); + _plugin->dispatcher (_plugin, effMainsChanged, 0, 1, NULL, 0.0f); } int @@ -114,27 +76,26 @@ VSTPlugin::set_block_size (pframes_t nframes) deactivate (); _plugin->dispatcher (_plugin, effSetBlockSize, 0, nframes, NULL, 0.0f); activate (); - return 0; + return 0; } float -VSTPlugin::default_value (uint32_t port) +VSTPlugin::default_value (uint32_t) { return 0; } -void -VSTPlugin::set_parameter (uint32_t which, float val) -{ - _plugin->setParameter (_plugin, which, val); - //ParameterChanged (which, val); /* EMIT SIGNAL */ -} - -float +float VSTPlugin::get_parameter (uint32_t which) const { return _plugin->getParameter (_plugin, which); +} +void +VSTPlugin::set_parameter (uint32_t which, float val) +{ + _plugin->setParameter (_plugin, which, val); + Plugin::set_parameter (which, val); } uint32_t @@ -149,14 +110,14 @@ VSTPlugin::nth_parameter (uint32_t n, bool& ok) const * @return 0-terminated base64-encoded data; must be passed to g_free () by caller. */ gchar * -VSTPlugin::get_chunk (bool single) +VSTPlugin::get_chunk (bool single) const { 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); } @@ -175,23 +136,16 @@ VSTPlugin::set_chunk (gchar const * data, bool single) return r; } -XMLNode& -VSTPlugin::get_state() +void +VSTPlugin::add_state (XMLNode* root) const { - XMLNode *root = new XMLNode (state_node_name()); LocaleGuard lg (X_("POSIX")); - if (_fst->current_program != -1) { - char buf[32]; - snprintf (buf, sizeof (buf), "%d", _fst->current_program); - root->add_property ("current-program", buf); - } - if (_plugin->flags & 32 /* effFlagsProgramsChunks */) { gchar* data = get_chunk (false); if (data == 0) { - return *root; + return; } /* store information */ @@ -210,19 +164,17 @@ VSTPlugin::get_state() for (int32_t n = 0; n < _plugin->numParams; ++n) { char index[64]; char val[32]; - snprintf (index, sizeof (index), "param_%d", n); + snprintf (index, sizeof (index), "param-%d", n); snprintf (val, sizeof (val), "%.12g", _plugin->getParameter (_plugin, n)); parameters->add_property (index, val); } root->add_child_nocopy (*parameters); } - - return *root; } int -VSTPlugin::set_state(const XMLNode& node, int) +VSTPlugin::set_state (const XMLNode& node, int version) { LocaleGuard lg (X_("POSIX")); @@ -231,12 +183,6 @@ VSTPlugin::set_state(const XMLNode& node, int) return 0; } - const XMLProperty* prop; - - if ((prop = node.property ("current-program")) != 0) { - _fst->current_program = atoi (prop->value().c_str()); - } - XMLNode* child; int ret = -1; @@ -244,7 +190,6 @@ VSTPlugin::set_state(const XMLNode& node, int) XMLPropertyList::const_iterator i; XMLNodeList::const_iterator n; - int ret = -1; for (n = child->children ().begin (); n != child->children ().end (); ++n) { if ((*n)->is_content ()) { @@ -263,23 +208,21 @@ VSTPlugin::set_state(const XMLNode& node, int) int32_t param; float val; - sscanf ((*i)->name().c_str(), "param_%d", ¶m); + sscanf ((*i)->name().c_str(), "param-%d", ¶m); sscanf ((*i)->value().c_str(), "%f", &val); _plugin->setParameter (_plugin, param, val); } - /* program number is not knowable */ - - _fst->current_program = -1; - ret = 0; } + Plugin::set_state (node, version); return ret; } + int VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) const { @@ -291,8 +234,6 @@ VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) if (_plugin->dispatcher (_plugin, effGetParameterProperties, which, 0, &prop, 0)) { -#ifdef VESTIGE_COMPLETE - /* i have yet to find or hear of a VST plugin that uses this */ if (prop.flags & kVstParameterUsesIntegerMinMax) { @@ -328,7 +269,6 @@ VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) desc.logarithmic = false; desc.sr_dependent = false; desc.label = prop.label; -#endif } else { @@ -355,129 +295,196 @@ VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) } bool -VSTPlugin::load_preset (const string& name) +VSTPlugin::load_preset (PresetRecord r) { - if (_plugin->flags & 32 /* effFlagsProgramsChunks */) { + bool s; - XMLTree* t = presets_tree (); - if (t == 0) { - return false; - } + if (r.user) { + s = load_user_preset (r); + } else { + s = load_plugin_preset (r); + } - XMLNode* root = t->root (); + if (s) { + Plugin::load_preset (r); + } - /* 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")); + return s; +} - assert (uri); - assert (label); +bool +VSTPlugin::load_plugin_preset (PresetRecord r) +{ + /* This is a plugin-provided preset. + We can't dispatch directly here; too many plugins expects only one GUI thread. + */ + + /* Extract the index of this preset from the URI */ + int id; + int index; +#ifndef NDEBUG + int const p = sscanf (r.uri.c_str(), "VST:%d:%d", &id, &index); + assert (p == 2); +#else + sscanf (r.uri.c_str(), "VST:%d:%d", &id, &index); +#endif + + _state->want_program = index; + return true; +} + +bool +VSTPlugin::load_user_preset (PresetRecord r) +{ + /* This is a user preset; we load it, and this code also knows about the + non-direct-dispatch thing. + */ + + boost::shared_ptr 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* label = (*i)->property (X_("label")); + + assert (label); + + if (label->value() != r.label) { + continue; + } - if (label->value() == name) { + if (_plugin->flags & 32 /* effFlagsProgramsChunks */) { - if (_fst->wanted_chunk) { - g_free (_fst->wanted_chunk); + /* Load a user preset chunk from our XML file and send it via a circuitous route to the plugin */ + + if (_state->wanted_chunk) { + g_free (_state->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); + _state->wanted_chunk = raw_data; + _state->wanted_chunk_size = size; + _state->want_chunk = 1; + return true; } - - 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; + + } else { + + for (XMLNodeList::const_iterator j = (*i)->children().begin(); j != (*i)->children().end(); ++j) { + if ((*j)->name() == X_("Parameter")) { + XMLProperty* index = (*j)->property (X_("index")); + XMLProperty* 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; } - - return true; + return false; } -string +string VSTPlugin::do_save_preset (string name) { - if (_plugin->flags & 32 /* effFlagsProgramsChunks */) { + boost::shared_ptr t (presets_tree ()); + if (t == 0) { + return ""; + } - XMLTree* t = presets_tree (); - if (t == 0) { - return ""; - } + XMLNode* p = 0; + /* 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); - /* Add a chunk to our XML file of user presets */ + if (_plugin->flags & 32 /* effFlagsProgramsChunks */) { - 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 = new XMLNode (X_("ChunkPreset")); 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"; + } else { - t->write (f.to_string ()); - delete t; - return uri; + 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); + } + } } - return ""; + t->root()->add_child_nocopy (*p); + + sys::path f = ARDOUR::user_config_directory (); + f /= "presets"; + f /= presets_file (); + + t->write (f.to_string ()); + return uri; } -void +void VSTPlugin::do_remove_preset (string name) { - if (_plugin->flags & 32 /* effFlagsProgramsChunks */) { - - /* XXX: TODO */ - - error << _("no support for presets using chunks at this time") - << endmsg; + boost::shared_ptr t (presets_tree ()); + if (t == 0) { return; } + + t->root()->remove_nodes_and_delete (X_("label"), name); + + sys::path f = ARDOUR::user_config_directory (); + f /= "presets"; + f /= presets_file (); + + t->write (f.to_string ()); } -string +string VSTPlugin::describe_parameter (Evoral::Parameter param) { - char name[64]; + char name[64] = "Unkown"; _plugin->dispatcher (_plugin, effGetParamName, param.id(), 0, name, 0); return name; } -framecnt_t +framecnt_t VSTPlugin::signal_latency () const { if (_user_latency) { return _user_latency; } -#ifdef VESTIGE_HEADER - return *((framecnt_t *) (((char *) &_plugin->flags) + 12)); /* initialDelay */ -#else - return _plugin->initial_delay; -#endif + return *((int32_t *) (((char *) &_plugin->flags) + 12)); /* initialDelay */ } -set +set VSTPlugin::automatable () const { set ret; - for (uint32_t i = 0; i < parameter_count(); ++i){ + for (uint32_t i = 0; i < parameter_count(); ++i) { ret.insert (ret.end(), Evoral::Parameter(PluginAutomation, 0, i)); } @@ -486,9 +493,11 @@ VSTPlugin::automatable () const int VSTPlugin::connect_and_run (BufferSet& bufs, - ChanMapping in_map, ChanMapping out_map, - pframes_t nframes, framecnt_t offset) + ChanMapping in_map, ChanMapping out_map, + pframes_t nframes, framecnt_t offset) { + Plugin::connect_and_run (bufs, in_map, out_map, nframes, offset); + float *ins[_plugin->numInputs]; float *outs[_plugin->numOutputs]; int32_t i; @@ -504,99 +513,63 @@ VSTPlugin::connect_and_run (BufferSet& bufs, int out_index = 0; for (i = 0; i < (int32_t) _plugin->numOutputs; ++i) { outs[i] = bufs.get_audio(min((uint32_t) out_index, nbufs - 1)).data() + offset; - - /* unbelievably, several VST plugins still rely on Cubase - behaviour and do not silence the buffer in processReplacing - when they have no output. - */ - - // memset (outs[i], 0, sizeof (Sample) * nframes); out_index++; } - if (bufs.count().n_midi() > 0) { - - /* Track notes that we are sending to the plugin */ - MidiBuffer& b = bufs.get_midi (0); - bool looped; - _tracker.track (b.begin(), b.end(), looped); - - if (_have_pending_stop_events) { - /* Transmit note-offs that are pending from the last transport stop */ - bufs.merge_from (_pending_stop_events, 0); - _have_pending_stop_events = false; - } - VstEvents* v = bufs.get_vst_midi (0); _plugin->dispatcher (_plugin, effProcessEvents, 0, 0, v, 0); } /* we already know it can support processReplacing */ - _plugin->processReplacing (_plugin, ins, outs, nframes); return 0; } -void -VSTPlugin::deactivate () -{ - _plugin->dispatcher (_plugin, effMainsChanged, 0, 0, NULL, 0.0f); -} - -void -VSTPlugin::activate () -{ - _plugin->dispatcher (_plugin, effMainsChanged, 0, 1, NULL, 0.0f); -} - -string -VSTPlugin::unique_id() const +string +VSTPlugin::unique_id () const { char buf[32]; -#ifdef VESTIGE_HEADER - snprintf (buf, sizeof (buf), "%d", *((int32_t*) &_plugin->unused_id)); -#else - snprintf (buf, sizeof (buf), "%d", _plugin->uniqueID); -#endif - return string (buf); + snprintf (buf, sizeof (buf), "%d", _plugin->uniqueID); + + return string (buf); } -const char * +const char * VSTPlugin::name () const { - return handle->name; + return _handle->name; } -const char * +const char * VSTPlugin::maker () const { return _info->creator.c_str(); } -const char * +const char * VSTPlugin::label () const { - return handle->name; + return _handle->name; } -uint32_t -VSTPlugin::parameter_count() const +uint32_t +VSTPlugin::parameter_count () const { return _plugin->numParams; } -bool +bool VSTPlugin::has_editor () const { return _plugin->flags & effFlagsHasEditor; } -void -VSTPlugin::print_parameter (uint32_t param, char *buf, uint32_t len) const +void +VSTPlugin::print_parameter (uint32_t param, char *buf, uint32_t /*len*/) const { char *first_nonws; @@ -610,6 +583,7 @@ VSTPlugin::print_parameter (uint32_t param, char *buf, uint32_t len) const while (*first_nonws && isspace (*first_nonws)) { first_nonws++; } + if (*first_nonws == '\0') { return; } @@ -617,84 +591,48 @@ VSTPlugin::print_parameter (uint32_t param, char *buf, uint32_t len) const memmove (buf, first_nonws, strlen (buf) - (first_nonws - buf) + 1); } -PluginPtr -VSTPluginInfo::load (Session& session) -{ - try { - PluginPtr plugin; - - if (Config->get_use_vst()) { - FSTHandle* handle; - - handle = fst_load(path.c_str()); - - if ( (int)handle == -1) { - error << string_compose(_("VST: cannot load module from \"%1\""), path) << endmsg; - } else { - plugin.reset (new VSTPlugin (session.engine(), session, handle)); - } - } else { - error << _("You asked ardour to not use any VST plugins") << endmsg; - return PluginPtr ((Plugin*) 0); - } - - plugin->set_info(PluginInfoPtr(new VSTPluginInfo(*this))); - return plugin; - } - - catch (failed_constructor &err) { - return PluginPtr ((Plugin*) 0); - } -} - -vector -VSTPlugin::get_presets () +void +VSTPlugin::find_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), ""); - + PresetRecord r (string_compose (X_("VST:%1:%2"), unique_id (), i), "", false); + if (vst_version >= 2) { char buf[256]; - _plugin->dispatcher (_plugin, 29, i, 0, buf, 0); - r.label = buf; + if (_plugin->dispatcher (_plugin, 29, i, 0, buf, 0) == 1) { + r.label = buf; + } else { + r.label = string_compose (_("Preset %1"), i); + } } 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 (); + boost::shared_ptr 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); + PresetRecord r (uri->value(), label->value(), true); _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 @@ -712,13 +650,13 @@ VSTPlugin::presets_tree () const create_directory (p); } - p /= "vst"; + p /= presets_file (); if (!exists (p)) { t->set_root (new XMLNode (X_("VSTPresets"))); return t; } - + t->set_filename (p.to_string ()); if (!t->read ()) { delete t; @@ -735,20 +673,9 @@ VSTPlugin::first_user_preset_index () const return _plugin->numPrograms; } -void -VSTPlugin::realtime_handle_transport_stopped () +string +VSTPlugin::presets_file () const { - /* Create note-offs for any active notes and put them in _pending_stop_events, to be picked - up on the next call to connect_and_run (). - */ - - _pending_stop_events.ensure_buffers (DataType::MIDI, 1, 4096); - _pending_stop_events.get_midi(0).clear (); - _tracker.resolve_notes (_pending_stop_events.get_midi (0), 0); - _have_pending_stop_events = true; + return string_compose ("vst-%1", unique_id ()); } -VSTPluginInfo::VSTPluginInfo() -{ - type = ARDOUR::VST; -}