diff options
-rw-r--r-- | ingen/Status.hpp | 2 | ||||
-rw-r--r-- | ingen/URIs.hpp | 1 | ||||
-rw-r--r-- | src/URIs.cpp | 2 | ||||
-rw-r--r-- | src/gui/NodeMenu.cpp | 60 | ||||
-rw-r--r-- | src/gui/NodeMenu.hpp | 5 | ||||
-rw-r--r-- | src/server/BlockImpl.cpp | 11 | ||||
-rw-r--r-- | src/server/BlockImpl.hpp | 11 | ||||
-rw-r--r-- | src/server/LV2Block.cpp | 27 | ||||
-rw-r--r-- | src/server/LV2Block.hpp | 4 | ||||
-rw-r--r-- | src/server/events/Delta.cpp | 63 | ||||
-rw-r--r-- | src/server/events/Delta.hpp | 11 | ||||
-rw-r--r-- | src/server/events/SetPortValue.cpp | 4 | ||||
-rw-r--r-- | src/server/events/SetPortValue.hpp | 6 | ||||
-rw-r--r-- | wscript | 2 |
14 files changed, 149 insertions, 60 deletions
diff --git a/ingen/Status.hpp b/ingen/Status.hpp index ed388ccf..1fae5358 100644 --- a/ingen/Status.hpp +++ b/ingen/Status.hpp @@ -28,6 +28,7 @@ enum class Status { BAD_REQUEST, BAD_URI, BAD_VALUE_TYPE, + BAD_VALUE, CLIENT_NOT_FOUND, CREATION_FAILED, DIRECTION_MISMATCH, @@ -60,6 +61,7 @@ ingen_status_string(Status st) case Status::BAD_REQUEST: return "Invalid request"; case Status::BAD_URI: return "Invalid URI"; case Status::BAD_VALUE_TYPE: return "Invalid value type"; + case Status::BAD_VALUE: return "Invalid value"; case Status::CLIENT_NOT_FOUND: return "Client not found"; case Status::CREATION_FAILED: return "Creation failed"; case Status::DIRECTION_MISMATCH: return "Direction mismatch"; diff --git a/ingen/URIs.hpp b/ingen/URIs.hpp index 70406f57..2dc3f2f6 100644 --- a/ingen/URIs.hpp +++ b/ingen/URIs.hpp @@ -149,6 +149,7 @@ public: const Quark patch_subject; const Quark patch_value; const Quark patch_wildcard; + const Quark pset_preset; const Quark pprops_logarithmic; const Quark rdf_type; const Quark rdfs_seeAlso; diff --git a/src/URIs.cpp b/src/URIs.cpp index a70e8d77..66d7965b 100644 --- a/src/URIs.cpp +++ b/src/URIs.cpp @@ -24,6 +24,7 @@ #include "lv2/lv2plug.in/ns/ext/morph/morph.h" #include "lv2/lv2plug.in/ns/ext/parameters/parameters.h" #include "lv2/lv2plug.in/ns/ext/patch/patch.h" +#include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" #include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h" #include "lv2/lv2plug.in/ns/ext/time/time.h" @@ -139,6 +140,7 @@ URIs::URIs(Forge& f, URIMap* map) , patch_subject (forge, map, LV2_PATCH__subject) , patch_value (forge, map, LV2_PATCH__value) , patch_wildcard (forge, map, LV2_PATCH__wildcard) + , pset_preset (forge, map, LV2_PRESETS__preset) , pprops_logarithmic (forge, map, LV2_PORT_PROPS__logarithmic) , rdf_type (forge, map, NS_RDF "type") , rdfs_seeAlso (forge, map, NS_RDFS "seeAlso") diff --git a/src/gui/NodeMenu.cpp b/src/gui/NodeMenu.cpp index 08524ade..b99ce546 100644 --- a/src/gui/NodeMenu.cpp +++ b/src/gui/NodeMenu.cpp @@ -108,18 +108,11 @@ NodeMenu::init(App& app, SPtr<const Client::BlockModel> block) sigc::mem_fun(this, &NodeMenu::on_preset_activated), string(lilv_node_as_string(preset))))); - // I have no idea why this is necessary, signal_activated doesn't work - // in this menu (and only this menu) - Gtk::MenuItem* item = &(_presets_menu->items().back()); - item->signal_button_release_event().connect( - sigc::bind<0>(sigc::mem_fun(this, &NodeMenu::on_preset_clicked), - string(lilv_node_as_string(preset)))); - lilv_nodes_free(labels); ++n_presets; } else { app.log().error( - fmt("Preset <%1> has no rdfs:label\n") + fmt("Preset <%1%> has no rdfs:label\n") % lilv_node_as_string(lilv_nodes_get(presets, i))); } } @@ -177,7 +170,7 @@ NodeMenu::on_menu_randomize() { _app->interface()->bundle_begin(); - const BlockModel* const bm = (const BlockModel*)_object.get(); + const SPtr<const BlockModel> bm = block(); for (const auto& p : bm->ports()) { if (p->is_input() && _app->can_control(p.get())) { float min = 0.0f, max = 1.0f; @@ -198,60 +191,19 @@ NodeMenu::on_menu_disconnect() _app->interface()->disconnect_all(_object->parent()->path(), _object->path()); } -static void -set_port_value(const char* port_symbol, - void* user_data, - const void* value, - uint32_t size, - uint32_t type) -{ - NodeMenu* menu = (NodeMenu*)user_data; - const BlockModel* const block = (const BlockModel*)menu->object().get(); - - if (!Raul::Symbol::is_valid(port_symbol)) { - menu->app()->log().error( - fmt("Preset with invalid port symbol `%1'\n") % port_symbol); - return; - } - - menu->app()->set_property( - Node::path_to_uri(block->path().child(Raul::Symbol(port_symbol))), - menu->app()->uris().ingen_value, - menu->app()->forge().alloc(size, type, value)); -} - void NodeMenu::on_preset_activated(const std::string& uri) { - const BlockModel* const block = (const BlockModel*)_object.get(); - const PluginModel* const plugin = dynamic_cast<const PluginModel*>(block->plugin()); - - LilvNode* pset = lilv_new_uri(plugin->lilv_world(), uri.c_str()); - LilvState* state = lilv_state_new_from_world( - plugin->lilv_world(), - &_app->world()->uri_map().urid_map_feature()->urid_map, - pset); - - if (state) { - lilv_state_restore(state, NULL, set_port_value, this, 0, NULL); - lilv_state_free(state); - } - - lilv_node_free(pset); -} + _app->set_property(block()->uri(), + _app->uris().pset_preset, + _app->forge().alloc_uri(uri)); -bool -NodeMenu::on_preset_clicked(const std::string& uri, GdkEventButton* ev) -{ - on_preset_activated(uri); - return false; } bool NodeMenu::has_control_inputs() { - const BlockModel* const bm = (const BlockModel*)_object.get(); - for (const auto& p : bm->ports()) + for (const auto& p : block()->ports()) if (p->is_input() && p->is_numeric()) return true; diff --git a/src/gui/NodeMenu.hpp b/src/gui/NodeMenu.hpp index d84bc84f..f28fe334 100644 --- a/src/gui/NodeMenu.hpp +++ b/src/gui/NodeMenu.hpp @@ -48,12 +48,15 @@ public: sigc::signal<void, bool> signal_embed_gui; protected: + SPtr<const Client::BlockModel> block() const { + return dynamic_ptr_cast<const Client::BlockModel>(_object); + } + void on_menu_disconnect(); void on_menu_embed_gui(); void on_menu_enabled(); void on_menu_randomize(); void on_preset_activated(const std::string& uri); - bool on_preset_clicked(const std::string& uri, GdkEventButton* ev); Gtk::MenuItem* _popup_gui_menuitem; Gtk::CheckMenuItem* _embed_gui_menuitem; diff --git a/src/server/BlockImpl.cpp b/src/server/BlockImpl.cpp index ce6b169c..d21514ef 100644 --- a/src/server/BlockImpl.cpp +++ b/src/server/BlockImpl.cpp @@ -160,6 +160,17 @@ BlockImpl::nth_port_by_type(uint32_t n, bool input, PortType type) return NULL; } +PortImpl* +BlockImpl::port_by_symbol(const char* symbol) +{ + for (uint32_t p = 0; _ports && p < _ports->size(); ++p) { + if (_ports->at(p)->symbol() == symbol) { + return _ports->at(p); + } + } + return NULL; +} + void BlockImpl::pre_process(ProcessContext& context) { diff --git a/src/server/BlockImpl.hpp b/src/server/BlockImpl.hpp index b3064168..965adbcc 100644 --- a/src/server/BlockImpl.hpp +++ b/src/server/BlockImpl.hpp @@ -21,6 +21,8 @@ #include <boost/intrusive/slist.hpp> +#include "lilv/lilv.h" + #include "raul/Array.hpp" #include "BufferRef.hpp" @@ -98,6 +100,12 @@ public: /** Enable or disable (bypass) this block. */ void set_enabled(bool e) { _enabled = e; } + /** Load a preset from the world for this block. */ + virtual LilvState* load_preset(const Raul::URI& uri) { return NULL; } + + /** Restore `state`. */ + virtual void apply_state(LilvState* state) {} + /** Learn the next incoming MIDI event (for internals) */ virtual void learn() {} @@ -122,6 +130,9 @@ public: virtual Node* port(uint32_t index) const; virtual PortImpl* port_impl(uint32_t index) const { return (*_ports)[index]; } + /** Get a port by symbol. */ + virtual PortImpl* port_by_symbol(const char* symbol); + /** Blocks that are connected to this Block's inputs. */ std::list<BlockImpl*>& providers() { return _providers; } diff --git a/src/server/LV2Block.cpp b/src/server/LV2Block.cpp index 3f6f4be1..64c80764 100644 --- a/src/server/LV2Block.cpp +++ b/src/server/LV2Block.cpp @@ -20,6 +20,7 @@ #include <cmath> #include "lv2/lv2plug.in/ns/ext/morph/morph.h" +#include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/options/options.h" #include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h" #include "lv2/lv2plug.in/ns/ext/state/state.h" @@ -541,6 +542,32 @@ LV2Block::post_process(ProcessContext& context) } } +LilvState* +LV2Block::load_preset(const Raul::URI& uri) +{ + World* world = &_lv2_plugin->lv2_info()->world(); + LilvWorld* lworld = _lv2_plugin->lv2_info()->lv2_world(); + LilvNode* preset = lilv_new_uri(lworld, uri.c_str()); + + // Load preset into world if necessary + lilv_world_load_resource(lworld, preset); + + // Load preset from world + LV2_URID_Map* map = &world->uri_map().urid_map_feature()->urid_map; + LilvState* state = lilv_state_new_from_world(lworld, map, preset); + + lilv_node_free(preset); + return state; +} + +void +LV2Block::apply_state(LilvState* state) +{ + for (uint32_t v = 0; v < _polyphony; ++v) { + lilv_state_restore(state, instance(v), NULL, NULL, 0, NULL); + } +} + void LV2Block::set_port_buffer(uint32_t voice, uint32_t port_num, diff --git a/src/server/LV2Block.hpp b/src/server/LV2Block.hpp index ec2f99f4..6b343df7 100644 --- a/src/server/LV2Block.hpp +++ b/src/server/LV2Block.hpp @@ -63,6 +63,10 @@ public: void run(ProcessContext& context); void post_process(ProcessContext& context); + LilvState* load_preset(const Raul::URI& uri); + + void apply_state(LilvState* state); + void set_port_buffer(uint32_t voice, uint32_t port_num, BufferRef buf, diff --git a/src/server/events/Delta.cpp b/src/server/events/Delta.cpp index 03a7ccd5..8df79994 100644 --- a/src/server/events/Delta.cpp +++ b/src/server/events/Delta.cpp @@ -17,6 +17,7 @@ #include <vector> #include <thread> +#include "ingen/Log.hpp" #include "ingen/Store.hpp" #include "ingen/URIs.hpp" #include "raul/Maid.hpp" @@ -60,6 +61,7 @@ Delta::Delta(Engine& engine, , _object(NULL) , _graph(NULL) , _compiled_graph(NULL) + , _state(NULL) , _context(context) , _type(type) , _poly_lock(engine.store()->mutex(), std::defer_lock) @@ -98,6 +100,37 @@ Delta::~Delta() delete _create_event; } +void +Delta::add_set_event(const char* port_symbol, + const void* value, + uint32_t size, + uint32_t type) +{ + BlockImpl* block = dynamic_cast<BlockImpl*>(_object); + PortImpl* port = block->port_by_symbol(port_symbol); + if (!port) { + _engine.log().warn(fmt("Unknown port `%1' in state") % port_symbol); + return; + } + + SetPortValue* ev = new SetPortValue( + _engine, _request_client, _request_id, _time, + port, Atom(size, type, value), true); + + ev->pre_process(); + _set_events.push_back(ev); +} + +static void +s_add_set_event(const char* port_symbol, + void* user_data, + const void* value, + uint32_t size, + uint32_t type) +{ + ((Delta*)user_data)->add_set_event(port_symbol, value, size, type); +} + bool Delta::pre_process() { @@ -216,6 +249,22 @@ Delta::pre_process() } else { _status = Status::BAD_VALUE_TYPE; } + } else if (key == uris.pset_preset) { + if (value.type() == uris.forge.URI) { + const char* str = value.ptr<char>(); + if (Raul::URI::is_valid(str)) { + op = SpecialType::PRESET; + const Raul::URI uri(str); + if ((_state = block->load_preset(Raul::URI(str)))) { + lilv_state_emit_port_values( + _state, s_add_set_event, this); + } + } else { + _status = Status::BAD_VALUE; + } + } else { + _status = Status::BAD_VALUE_TYPE; + } } } @@ -362,6 +411,9 @@ Delta::execute(ProcessContext& context) } } break; + case SpecialType::PRESET: + block->set_enabled(false); + break; case SpecialType::NONE: if (port) { if (key == uris.lv2_minimum) { @@ -382,6 +434,15 @@ Delta::post_process() _poly_lock.unlock(); } + if (_state) { + BlockImpl* block = dynamic_cast<BlockImpl*>(_object); + if (block) { + block->apply_state(_state); + block->set_enabled(true); + } + lilv_state_free(_state); + } + Broadcaster::Transfer t(*_engine.broadcaster()); if (_create_event) { @@ -392,7 +453,7 @@ Delta::post_process() } for (auto& s : _set_events) { - if (s->status() != Status::SUCCESS) { + if (s->synthetic() || s->status() != Status::SUCCESS) { s->post_process(); // Set failed, report error } } diff --git a/src/server/events/Delta.hpp b/src/server/events/Delta.hpp index 43edacbf..750411ff 100644 --- a/src/server/events/Delta.hpp +++ b/src/server/events/Delta.hpp @@ -19,6 +19,8 @@ #include <vector> +#include "lilv/lilv.h" + #include "raul/URI.hpp" #include "ControlBindings.hpp" @@ -87,6 +89,11 @@ public: ~Delta(); + void add_set_event(const char* port_symbol, + const void* value, + uint32_t size, + uint32_t type); + bool pre_process(); void execute(ProcessContext& context); void post_process(); @@ -98,7 +105,8 @@ private: ENABLE_BROADCAST, POLYPHONY, POLYPHONIC, - CONTROL_BINDING + CONTROL_BINDING, + PRESET }; typedef std::vector<SetPortValue*> SetEvents; @@ -113,6 +121,7 @@ private: Ingen::Resource* _object; GraphImpl* _graph; CompiledGraph* _compiled_graph; + LilvState* _state; Resource::Graph _context; ControlBindings::Key _binding; Type _type; diff --git a/src/server/events/SetPortValue.cpp b/src/server/events/SetPortValue.cpp index df0cb06d..655ff281 100644 --- a/src/server/events/SetPortValue.cpp +++ b/src/server/events/SetPortValue.cpp @@ -39,10 +39,12 @@ SetPortValue::SetPortValue(Engine& engine, int32_t id, SampleCount timestamp, PortImpl* port, - const Atom& value) + const Atom& value, + bool synthetic) : Event(engine, client, id, timestamp) , _port(port) , _value(value) + , _synthetic(synthetic) { } diff --git a/src/server/events/SetPortValue.hpp b/src/server/events/SetPortValue.hpp index 852c694e..ed81db47 100644 --- a/src/server/events/SetPortValue.hpp +++ b/src/server/events/SetPortValue.hpp @@ -42,7 +42,8 @@ public: int32_t id, SampleCount timestamp, PortImpl* port, - const Atom& value); + const Atom& value, + bool synthetic = false); ~SetPortValue(); @@ -50,12 +51,15 @@ public: void execute(ProcessContext& context); void post_process(); + bool synthetic() const { return _synthetic; } + private: void apply(Context& context); PortImpl* _port; const Atom _value; ControlBindings::Key _binding; + bool _synthetic; }; } // namespace Events @@ -67,7 +67,7 @@ def configure(conf): autowaf.check_pkg(conf, 'gthread-2.0', uselib_store='GTHREAD', atleast_version='2.14.0', mandatory=True) autowaf.check_pkg(conf, 'lilv-0', uselib_store='LILV', - atleast_version='0.21.0', mandatory=True) + atleast_version='0.21.2', mandatory=True) autowaf.check_pkg(conf, 'suil-0', uselib_store='SUIL', atleast_version='0.2.0', mandatory=True) autowaf.check_pkg(conf, 'sratom-0', uselib_store='SRATOM', |