diff options
Diffstat (limited to 'src')
225 files changed, 0 insertions, 41856 deletions
diff --git a/src/AtomReader.cpp b/src/AtomReader.cpp deleted file mode 100644 index 218110e4..00000000 --- a/src/AtomReader.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cstdlib> -#include <utility> - -#include "ingen/AtomReader.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/Message.hpp" -#include "ingen/Node.hpp" -#include "ingen/URIMap.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "raul/Path.hpp" - -namespace Ingen { - -AtomReader::AtomReader(URIMap& map, URIs& uris, Log& log, Interface& iface) - : _map(map) - , _uris(uris) - , _log(log) - , _iface(iface) -{} - -void -AtomReader::get_atom(const LV2_Atom* in, Atom& out) -{ - if (in) { - if (in->type == _uris.atom_URID) { - const LV2_Atom_URID* urid = (const LV2_Atom_URID*)in; - const char* uri = _map.unmap_uri(urid->body); - if (uri) { - out = Atom(sizeof(int32_t), _uris.atom_URID, &urid->body); - } else { - _log.error(fmt("Unable to unmap URID %1%\n") % urid->body); - } - } else { - out = Atom(in->size, in->type, LV2_ATOM_BODY_CONST(in)); - } - } -} - -void -AtomReader::get_props(const LV2_Atom_Object* obj, - Ingen::Properties& props) -{ - if (obj->body.otype) { - const Atom type(sizeof(int32_t), _uris.atom_URID, &obj->body.otype); - props.emplace(_uris.rdf_type, type); - } - LV2_ATOM_OBJECT_FOREACH(obj, p) { - Atom val; - get_atom(&p->value, val); - props.emplace(URI(_map.unmap_uri(p->key)), val); - } -} - -boost::optional<URI> -AtomReader::atom_to_uri(const LV2_Atom* atom) -{ - if (!atom) { - return boost::optional<URI>(); - } else if (atom->type == _uris.atom_URI) { - const char* str = (const char*)LV2_ATOM_BODY_CONST(atom); - if (URI::is_valid(str)) { - return URI(str); - } else { - _log.warn(fmt("Invalid URI <%1%>\n") % str); - } - } else if (atom->type == _uris.atom_Path) { - const char* str = (const char*)LV2_ATOM_BODY_CONST(atom); - if (!strncmp(str, "file://", 5)) { - return URI(str); - } else { - return URI(std::string("file://") + str); - } - } else if (atom->type == _uris.atom_URID) { - const char* str = _map.unmap_uri(((const LV2_Atom_URID*)atom)->body); - if (str) { - return URI(str); - } else { - _log.warn(fmt("Unknown URID %1%\n") % str); - } - } - return boost::optional<URI>(); -} - -boost::optional<Raul::Path> -AtomReader::atom_to_path(const LV2_Atom* atom) -{ - boost::optional<URI> uri = atom_to_uri(atom); - if (uri && uri_is_path(*uri)) { - return uri_to_path(*uri); - } - return boost::optional<Raul::Path>(); -} - -Resource::Graph -AtomReader::atom_to_context(const LV2_Atom* atom) -{ - Resource::Graph ctx = Resource::Graph::DEFAULT; - if (atom) { - boost::optional<URI> maybe_uri = atom_to_uri(atom); - if (maybe_uri) { - ctx = Resource::uri_to_graph(*maybe_uri); - } else { - _log.warn("Message has invalid context\n"); - } - } - return ctx; -} - -bool -AtomReader::is_message(const URIs& uris, const LV2_Atom* msg) -{ - if (msg->type != uris.atom_Object) { - return false; - } - - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)msg; - return (obj->body.otype == uris.patch_Get || - obj->body.otype == uris.patch_Delete || - obj->body.otype == uris.patch_Put || - obj->body.otype == uris.patch_Set || - obj->body.otype == uris.patch_Patch || - obj->body.otype == uris.patch_Move || - obj->body.otype == uris.patch_Response); -} - -bool -AtomReader::write(const LV2_Atom* msg, int32_t default_id) -{ - if (msg->type != _uris.atom_Object) { - _log.warn(fmt("Unknown message type <%1%>\n") - % _map.unmap_uri(msg->type)); - return false; - } - - const LV2_Atom_Object* obj = (const LV2_Atom_Object*)msg; - const LV2_Atom* subject = nullptr; - const LV2_Atom* number = nullptr; - - lv2_atom_object_get(obj, - (LV2_URID)_uris.patch_subject, &subject, - (LV2_URID)_uris.patch_sequenceNumber, &number, - NULL); - - const boost::optional<URI> subject_uri = atom_to_uri(subject); - - const int32_t seq = ((number && number->type == _uris.atom_Int) - ? ((const LV2_Atom_Int*)number)->body - : default_id); - - if (obj->body.otype == _uris.patch_Get) { - if (subject_uri) { - _iface(Get{seq, *subject_uri}); - } - } else if (obj->body.otype == _uris.ingen_BundleStart) { - _iface(BundleBegin{seq}); - } else if (obj->body.otype == _uris.ingen_BundleEnd) { - _iface(BundleEnd{seq}); - } else if (obj->body.otype == _uris.patch_Delete) { - const LV2_Atom_Object* body = nullptr; - lv2_atom_object_get(obj, (LV2_URID)_uris.patch_body, &body, 0); - - if (subject_uri && !body) { - _iface(Del{seq, *subject_uri}); - return true; - } else if (body && body->body.otype == _uris.ingen_Arc) { - const LV2_Atom* tail = nullptr; - const LV2_Atom* head = nullptr; - const LV2_Atom* incidentTo = nullptr; - lv2_atom_object_get(body, - (LV2_URID)_uris.ingen_tail, &tail, - (LV2_URID)_uris.ingen_head, &head, - (LV2_URID)_uris.ingen_incidentTo, &incidentTo, - NULL); - - boost::optional<Raul::Path> subject_path(atom_to_path(subject)); - boost::optional<Raul::Path> tail_path(atom_to_path(tail)); - boost::optional<Raul::Path> head_path(atom_to_path(head)); - boost::optional<Raul::Path> other_path(atom_to_path(incidentTo)); - if (tail_path && head_path) { - _iface(Disconnect{seq, *tail_path, *head_path}); - } else if (subject_path && other_path) { - _iface(DisconnectAll{seq, *subject_path, *other_path}); - } else { - _log.warn("Delete of unknown object\n"); - return false; - } - } - } else if (obj->body.otype == _uris.patch_Put) { - const LV2_Atom_Object* body = nullptr; - const LV2_Atom* context = nullptr; - lv2_atom_object_get(obj, - (LV2_URID)_uris.patch_body, &body, - (LV2_URID)_uris.patch_context, &context, - 0); - if (!body) { - _log.warn("Put message has no body\n"); - return false; - } else if (!subject_uri) { - _log.warn("Put message has no subject\n"); - return false; - } - - if (body->body.otype == _uris.ingen_Arc) { - LV2_Atom* tail = nullptr; - LV2_Atom* head = nullptr; - lv2_atom_object_get(body, - (LV2_URID)_uris.ingen_tail, &tail, - (LV2_URID)_uris.ingen_head, &head, - NULL); - if (!tail || !head) { - _log.warn("Arc has no tail or head\n"); - return false; - } - - boost::optional<Raul::Path> tail_path(atom_to_path(tail)); - boost::optional<Raul::Path> head_path(atom_to_path(head)); - if (tail_path && head_path) { - _iface(Connect{seq, *tail_path, *head_path}); - } else { - _log.warn("Arc has non-path tail or head\n"); - } - } else { - Ingen::Properties props; - get_props(body, props); - _iface(Put{seq, *subject_uri, props, atom_to_context(context)}); - } - } else if (obj->body.otype == _uris.patch_Set) { - if (!subject_uri) { - _log.warn("Set message has no subject\n"); - return false; - } - - const LV2_Atom_URID* prop = nullptr; - const LV2_Atom* value = nullptr; - const LV2_Atom* context = nullptr; - lv2_atom_object_get(obj, - (LV2_URID)_uris.patch_property, &prop, - (LV2_URID)_uris.patch_value, &value, - (LV2_URID)_uris.patch_context, &context, - 0); - if (!prop || ((const LV2_Atom*)prop)->type != _uris.atom_URID) { - _log.warn("Set message missing property\n"); - return false; - } else if (!value) { - _log.warn("Set message missing value\n"); - return false; - } - - Atom atom; - get_atom(value, atom); - _iface(SetProperty{seq, - *subject_uri, - URI(_map.unmap_uri(prop->body)), - atom, - atom_to_context(context)}); - } else if (obj->body.otype == _uris.patch_Patch) { - if (!subject_uri) { - _log.warn("Patch message has no subject\n"); - return false; - } - - const LV2_Atom_Object* remove = nullptr; - const LV2_Atom_Object* add = nullptr; - const LV2_Atom* context = nullptr; - lv2_atom_object_get(obj, - (LV2_URID)_uris.patch_remove, &remove, - (LV2_URID)_uris.patch_add, &add, - (LV2_URID)_uris.patch_context, &context, - 0); - if (!remove) { - _log.warn("Patch message has no remove\n"); - return false; - } else if (!add) { - _log.warn("Patch message has no add\n"); - return false; - } - - Ingen::Properties add_props; - get_props(add, add_props); - - Ingen::Properties remove_props; - get_props(remove, remove_props); - - _iface(Delta{seq, *subject_uri, remove_props, add_props, - atom_to_context(context)}); - } else if (obj->body.otype == _uris.patch_Copy) { - if (!subject) { - _log.warn("Copy message has no subject\n"); - return false; - } - - const LV2_Atom* dest = nullptr; - lv2_atom_object_get(obj, (LV2_URID)_uris.patch_destination, &dest, 0); - if (!dest) { - _log.warn("Copy message has no destination\n"); - return false; - } - - boost::optional<URI> subject_uri(atom_to_uri(subject)); - if (!subject_uri) { - _log.warn("Copy message has non-path subject\n"); - return false; - } - - boost::optional<URI> dest_uri(atom_to_uri(dest)); - if (!dest_uri) { - _log.warn("Copy message has non-URI destination\n"); - return false; - } - - _iface(Copy{seq, *subject_uri, *dest_uri}); - } else if (obj->body.otype == _uris.patch_Move) { - if (!subject) { - _log.warn("Move message has no subject\n"); - return false; - } - - const LV2_Atom* dest = nullptr; - lv2_atom_object_get(obj, (LV2_URID)_uris.patch_destination, &dest, 0); - if (!dest) { - _log.warn("Move message has no destination\n"); - return false; - } - - boost::optional<Raul::Path> subject_path(atom_to_path(subject)); - if (!subject_path) { - _log.warn("Move message has non-path subject\n"); - return false; - } - - boost::optional<Raul::Path> dest_path(atom_to_path(dest)); - if (!dest_path) { - _log.warn("Move message has non-path destination\n"); - return false; - } - - _iface(Move{seq, *subject_path, *dest_path}); - } else if (obj->body.otype == _uris.patch_Response) { - const LV2_Atom* seq = nullptr; - const LV2_Atom* body = nullptr; - lv2_atom_object_get(obj, - (LV2_URID)_uris.patch_sequenceNumber, &seq, - (LV2_URID)_uris.patch_body, &body, - 0); - if (!seq || seq->type != _uris.atom_Int) { - _log.warn("Response message has no sequence number\n"); - return false; - } else if (!body || body->type != _uris.atom_Int) { - _log.warn("Response message body is not integer\n"); - return false; - } - _iface(Response{((const LV2_Atom_Int*)seq)->body, - (Ingen::Status)((const LV2_Atom_Int*)body)->body, - subject_uri ? subject_uri->c_str() : ""}); - } else if (obj->body.otype == _uris.ingen_BundleStart) { - _iface(BundleBegin{seq}); - } else if (obj->body.otype == _uris.ingen_BundleEnd) { - _iface(BundleEnd{seq}); - } else { - _log.warn(fmt("Unknown object type <%1%>\n") - % _map.unmap_uri(obj->body.otype)); - } - - return true; -} - -} // namespace Ingen diff --git a/src/AtomWriter.cpp b/src/AtomWriter.cpp deleted file mode 100644 index f235aafc..00000000 --- a/src/AtomWriter.cpp +++ /dev/null @@ -1,652 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** @page protocol Ingen Protocol - * @tableofcontents - * - * @section introduction Controlling Ingen - * - * Ingen is controlled via a simple RDF-based protocol. This conceptual - * protocol can be used in two concrete ways: - * - * 1. When Ingen is running as a process, a socket accepts messages in - * (textual) Turtle syntax, and responds in the same syntax. Transfers are - * delimited by NULL characters in the stream, so the client knows when to - * finish parsing to interpret responses. By default, Ingen listens on - * unix:///tmp/ingen.sock and tcp://localhost:16180 - * - * 2. When Ingen is running as an LV2 plugin, the control and notify ports - * accept and respond using (binary) LV2 atoms. Messages are read and written - * as events with atom:Object bodies. The standard rules for LV2 event - * transmission apply, but note that the graph manipulations described here are - * executed asynchronously and not with sample accuracy, so the response may - * come at a later frame or cycle. - * - * For documentation purposes, this page discusses messages in Turtle syntax, - * but the same protocol is used in the LV2 case, just in a more compact binary - * encoding. - * - * Conceptually, Ingen represents a tree of objects, each of which has a path - * (like /main/in or /main/osc/out) and a set of properties. A property is a - * URI key and some value. All changes to Ingen are represented as - * manipulations of this tree of dictionaries. The core of the protocol is - * based on the LV2 patch extension, which defines several messages for - * manipulating data in this model which resemble HTTP methods. - */ - -#include <cassert> -#include <cstdlib> -#include <string> - -#include <boost/variant/apply_visitor.hpp> - -#include "ingen/AtomSink.hpp" -#include "ingen/AtomWriter.hpp" -#include "ingen/Node.hpp" -#include "ingen/URIMap.hpp" -#include "raul/Path.hpp" -#include "serd/serd.h" - -namespace Ingen { - -AtomWriter::AtomWriter(URIMap& map, URIs& uris, AtomSink& sink) - : _map(map) - , _uris(uris) - , _sink(sink) -{ - lv2_atom_forge_init(&_forge, &map.urid_map_feature()->urid_map); - _out.set_forge_sink(&_forge); -} - -void -AtomWriter::finish_msg() -{ - assert(!_forge.stack); - _sink.write(_out.atom()); - _out.clear(); -} - -void -AtomWriter::message(const Message& message) -{ - boost::apply_visitor(*this, message); -} - -/** @page protocol - * @subsection Bundles - * - * An [ingen:BundleStart](http://drobilla.net/ns/ingen#BundleStart) marks the - * start of a bundle in the operation stream. A bundle groups a series of - * messages for coarse-grained undo or atomic execution. - * - * @code{.ttl} - * [] a ingen:BundleStart . - * @endcode - - * An [ingen:BundleEnd](http://drobilla.net/ns/ingen#BundleEnd) marks the end - * of a bundle in the operation stream. - * - * @code{.ttl} - * [] a ingen:BundleEnd . - * @endcode - */ -void -AtomWriter::operator()(const BundleBegin& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.ingen_BundleStart, message.seq); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -void -AtomWriter::operator()(const BundleEnd& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.ingen_BundleEnd, message.seq); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -void -AtomWriter::forge_uri(const URI& uri) -{ - if (serd_uri_string_has_scheme((const uint8_t*)uri.c_str())) { - lv2_atom_forge_urid(&_forge, _map.map_uri(uri.c_str())); - } else { - lv2_atom_forge_uri(&_forge, uri.c_str(), uri.length()); - } -} - -void -AtomWriter::forge_properties(const Properties& properties) -{ - for (auto p : properties) { - lv2_atom_forge_key(&_forge, _map.map_uri(p.first.c_str())); - if (p.second.type() == _forge.URI) { - forge_uri(URI(p.second.ptr<char>())); - } else { - lv2_atom_forge_atom(&_forge, p.second.size(), p.second.type()); - lv2_atom_forge_write(&_forge, p.second.get_body(), p.second.size()); - } - } -} - -void -AtomWriter::forge_arc(const Raul::Path& tail, const Raul::Path& head) -{ - LV2_Atom_Forge_Frame arc; - lv2_atom_forge_object(&_forge, &arc, 0, _uris.ingen_Arc); - lv2_atom_forge_key(&_forge, _uris.ingen_tail); - forge_uri(path_to_uri(tail)); - lv2_atom_forge_key(&_forge, _uris.ingen_head); - forge_uri(path_to_uri(head)); - lv2_atom_forge_pop(&_forge, &arc); -} - -void -AtomWriter::forge_request(LV2_Atom_Forge_Frame* frame, LV2_URID type, int32_t id) -{ - lv2_atom_forge_object(&_forge, frame, 0, type); - - if (id) { - lv2_atom_forge_key(&_forge, _uris.patch_sequenceNumber); - lv2_atom_forge_int(&_forge, id); - } -} - -void -AtomWriter::forge_context(Resource::Graph ctx) -{ - switch (ctx) { - case Resource::Graph::EXTERNAL: - lv2_atom_forge_key(&_forge, _uris.patch_context); - forge_uri(_uris.ingen_externalContext); - break; - case Resource::Graph::INTERNAL: - lv2_atom_forge_key(&_forge, _uris.patch_context); - forge_uri(_uris.ingen_internalContext); - break; - default: break; - } -} - -/** @page protocol - * @section methods Methods - * @subsection Put - * - * Send a [Put](http://lv2plug.in/ns/ext/patch#Put) to set properties on an - * object, creating it if necessary. - * - * If the object already exists, all existing object properties with keys that - * match any property in the message are first removed. Other properties are - * left unchanged. - * - * If the object does not yet exist, the message must contain sufficient - * information to create it, including at least one rdf:type property. - * - * @code{.ttl} - * [] - * a patch:Put ; - * patch:subject </main/osc> ; - * patch:body [ - * a ingen:Block ; - * lv2:prototype <http: //drobilla.net/plugins/mda/Shepard> - * ] . - * @endcode - */ -void -AtomWriter::operator()(const Put& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Put, message.seq); - forge_context(message.ctx); - lv2_atom_forge_key(&_forge, _uris.patch_subject); - forge_uri(message.uri); - lv2_atom_forge_key(&_forge, _uris.patch_body); - - LV2_Atom_Forge_Frame body; - lv2_atom_forge_object(&_forge, &body, 0, 0); - forge_properties(message.properties); - lv2_atom_forge_pop(&_forge, &body); - - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @subsection Patch - * - * Send a [Patch](http://lv2plug.in/ns/ext/patch#Patch) to manipulate the - * properties of an object. A set of properties are first removed, then - * another is added. Analogous to WebDAV PROPPATCH. - * - * The special value [patch:wildcard](http://lv2plug.in/ns/ext/patch#wildcard) - * may be used to specify that any value with the given key should be removed. - * - * @code{.ttl} - * [] - * a patch:Patch ; - * patch:subject </main/osc> ; - * patch:add [ - * lv2:name "Osckillator" ; - * ingen:canvasX 32.0 ; - * ingen:canvasY 32.0 ; - * ] ; - * patch:remove [ - * eg:name "Old name" ; # Remove specific value - * ingen:canvasX patch:wildcard ; # Remove all - * ingen:canvasY patch:wildcard ; # Remove all - * ] . - * @endcode - */ -void -AtomWriter::operator()(const Delta& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Patch, message.seq); - forge_context(message.ctx); - lv2_atom_forge_key(&_forge, _uris.patch_subject); - forge_uri(message.uri); - - lv2_atom_forge_key(&_forge, _uris.patch_remove); - LV2_Atom_Forge_Frame remove_obj; - lv2_atom_forge_object(&_forge, &remove_obj, 0, 0); - forge_properties(message.remove); - lv2_atom_forge_pop(&_forge, &remove_obj); - - lv2_atom_forge_key(&_forge, _uris.patch_add); - LV2_Atom_Forge_Frame add_obj; - lv2_atom_forge_object(&_forge, &add_obj, 0, 0); - forge_properties(message.add); - lv2_atom_forge_pop(&_forge, &add_obj); - - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @subsection Copy - * - * Send a [Copy](http://lv2plug.in/ns/ext/copy#Copy) to copy an object from - * its current location (subject) to another (destination). - * - * If both the subject and destination are inside Ingen, like block paths, then the old object - * is copied by, for example, creating a new plugin instance. - * - * If the subject is a filename (file URI or atom:Path) and the destination is - * inside Ingen, then the subject must be an Ingen graph file or bundle, which - * is loaded to the specified destination path. - * - * If the subject is inside Ingen and the destination is a filename, then the - * subject is saved to an Ingen bundle at the given destination. - * - * @code{.ttl} - * [] - * a patch:Copy ; - * patch:subject </main/osc> ; - * patch:destination </main/osc2> . - * @endcode - */ -void -AtomWriter::operator()(const Copy& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Copy, message.seq); - lv2_atom_forge_key(&_forge, _uris.patch_subject); - forge_uri(message.old_uri); - lv2_atom_forge_key(&_forge, _uris.patch_destination); - forge_uri(message.new_uri); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @subsection Move - * - * Send a [Move](http://lv2plug.in/ns/ext/move#Move) to move an object from its - * current location (subject) to another (destination). - * - * Both subject and destination must be paths in Ingen with the same parent, - * moving between graphs is currently not supported. - * - * @code{.ttl} - * [] - * a patch:Move ; - * patch:subject </main/osc> ; - * patch:destination </main/osc2> . - * @endcode - */ -void -AtomWriter::operator()(const Move& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Move, message.seq); - lv2_atom_forge_key(&_forge, _uris.patch_subject); - forge_uri(path_to_uri(message.old_path)); - lv2_atom_forge_key(&_forge, _uris.patch_destination); - forge_uri(path_to_uri(message.new_path)); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @subsection Delete - * - * Send a [Delete](http://lv2plug.in/ns/ext/delete#Delete) to remove an - * object from the engine and destroy it. - * - * All properties of the object are lost, as are all references to the object - * (e.g. any connections to it). - * - * @code{.ttl} - * [] - * a patch:Delete ; - * patch:subject </main/osc> . - * @endcode - */ -void -AtomWriter::operator()(const Del& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Delete, message.seq); - lv2_atom_forge_key(&_forge, _uris.patch_subject); - forge_uri(message.uri); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @subsection Set - * - * Send a [Set](http://lv2plug.in/ns/ext/patch#Set) to set a property on an - * object. Any existing value for that property is removed. - * - * @code{.ttl} - * [] - * a patch:Set ; - * patch:subject </main/osc> ; - * patch:property lv2:name ; - * patch:value "Oscwellator" . - * @endcode - */ -void -AtomWriter::operator()(const SetProperty& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Set, message.seq); - forge_context(message.ctx); - lv2_atom_forge_key(&_forge, _uris.patch_subject); - forge_uri(message.subject); - lv2_atom_forge_key(&_forge, _uris.patch_property); - lv2_atom_forge_urid(&_forge, _map.map_uri(message.predicate.c_str())); - lv2_atom_forge_key(&_forge, _uris.patch_value); - lv2_atom_forge_atom(&_forge, message.value.size(), message.value.type()); - lv2_atom_forge_write(&_forge, message.value.get_body(), message.value.size()); - - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @subsection Undo - * - * Use [ingen:Undo](http://drobilla.net/ns/ingen#Undo) to undo the last change - * to the engine. Undo transactions can be delimited using bundle markers, if - * the last operations(s) received were in a bundle, then an Undo will undo the - * effects of that entire bundle. - * - * @code{.ttl} - * [] a ingen:Undo . - * @endcode - */ -void -AtomWriter::operator()(const Undo& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.ingen_Undo, message.seq); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @subsection Undo - * - * Use [ingen:Redo](http://drobilla.net/ns/ingen#Redo) to redo the last undone change. - * - * @code{.ttl} - * [] a ingen:Redo . - * @endcode - */ -void -AtomWriter::operator()(const Redo& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.ingen_Redo, message.seq); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @subsection Get - * - * Send a [Get](http://lv2plug.in/ns/ext/patch#Get) to get the description - * of the subject. - * - * @code{.ttl} - * [] - * a patch:Get ; - * patch:subject </main/osc> . - * @endcode - */ -void -AtomWriter::operator()(const Get& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Get, message.seq); - lv2_atom_forge_key(&_forge, _uris.patch_subject); - forge_uri(message.subject); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * - * @section arcs Arc Manipulation - */ - -/** @page protocol - * @subsection Connect Connecting Ports - * - * Ports are connected by putting an arc with the desired tail (an output port) - * and head (an input port). The tail and head must both be within the - * subject, which must be a graph. - * - * @code{.ttl} - * [] - * a patch:Put ; - * patch:subject </main/> ; - * patch:body [ - * a ingen:Arc ; - * ingen:tail </main/osc/out> ; - * ingen:head </main/filt/in> ; - * ] . - * @endcode - */ -void -AtomWriter::operator()(const Connect& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Put, message.seq); - lv2_atom_forge_key(&_forge, _uris.patch_subject); - forge_uri(path_to_uri(Raul::Path::lca(message.tail, message.head))); - lv2_atom_forge_key(&_forge, _uris.patch_body); - forge_arc(message.tail, message.head); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @subsection Disconnect Disconnecting Ports - * - * Ports are disconnected by deleting the arc between them. The description of - * the arc is the same as in the put command used to create the connection. - * - * @code{.ttl} - * [] - * a patch:Delete ; - * patch:body [ - * a ingen:Arc ; - * ingen:tail </main/osc/out> ; - * ingen:head </main/filt/in> ; - * ] . - * @endcode - */ -void -AtomWriter::operator()(const Disconnect& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Delete, message.seq); - lv2_atom_forge_key(&_forge, _uris.patch_body); - forge_arc(message.tail, message.head); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @subsection DisconnectAll Fully Disconnecting Anything - * - * All arcs to or from anything can be removed by giving the special property - * ingen:incidentTo rather than a specific head and tail. This works for both - * ports and blocks (where the effect is to disconnect everything from ports on - * that block). - * - * @code{.ttl} - * [] - * a patch:Delete ; - * patch:subject </main> ; - * patch:body [ - * a ingen:Arc ; - * ingen:incidentTo </main/osc/out> - * ] . - * @endcode - */ -void -AtomWriter::operator()(const DisconnectAll& message) -{ - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Delete, message.seq); - - lv2_atom_forge_key(&_forge, _uris.patch_subject); - forge_uri(path_to_uri(message.graph)); - - lv2_atom_forge_key(&_forge, _uris.patch_body); - LV2_Atom_Forge_Frame arc; - lv2_atom_forge_object(&_forge, &arc, 0, _uris.ingen_Arc); - lv2_atom_forge_key(&_forge, _uris.ingen_incidentTo); - forge_uri(path_to_uri(message.path)); - lv2_atom_forge_pop(&_forge, &arc); - - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -/** @page protocol - * @section Responses - * - * Ingen responds to requests if the patch:sequenceNumber property is set. For - * example: - * @code{.ttl} - * [] - * a patch:Get ; - * patch:sequenceNumber 42 ; - * patch:subject </main/osc> . - * @endcode - * - * Might receive a response like: - * @code{.ttl} - * [] - * a patch:Response ; - * patch:sequenceNumber 42 ; - * patch:subject </main/osc> ; - * patch:body 0 . - * @endcode - * - * Where 0 is a status code, 0 meaning success and any other value being an - * error. Information about status codes, including error message strings, - * are defined in ingen.lv2/errors.ttl. - * - * Note that a response is only a status response, operations that manipulate - * the graph may generate new data on the stream, e.g. the above get request - * would also receive a put that describes /main/osc in the stream immediately - * following the response. - */ -void -AtomWriter::operator()(const Response& response) -{ - const auto& subject = response.subject; - if (!response.id) { - return; - } - - LV2_Atom_Forge_Frame msg; - forge_request(&msg, _uris.patch_Response, 0); - lv2_atom_forge_key(&_forge, _uris.patch_sequenceNumber); - lv2_atom_forge_int(&_forge, response.id); - if (!subject.empty()) { - lv2_atom_forge_key(&_forge, _uris.patch_subject); - lv2_atom_forge_uri(&_forge, subject.c_str(), subject.length()); - } - lv2_atom_forge_key(&_forge, _uris.patch_body); - lv2_atom_forge_int(&_forge, static_cast<int>(response.status)); - lv2_atom_forge_pop(&_forge, &msg); - finish_msg(); -} - -void -AtomWriter::operator()(const Error&) -{ -} - -/** @page protocol - * @section loading Loading and Unloading Bundles - * - * The property ingen:loadedBundle on the engine can be used to load - * or unload bundles from Ingen's world. For example: - * - * @code{.ttl} - * # Load /old.lv2 - * [] - * a patch:Put ; - * patch:subject </> ; - * patch:body [ - * ingen:loadedBundle <file:///old.lv2/> - * ] . - * - * # Replace /old.lv2 with /new.lv2 - * [] - * a patch:Patch ; - * patch:subject </> ; - * patch:remove [ - * ingen:loadedBundle <file:///old.lv2/> - * ]; - * patch:add [ - * ingen:loadedBundle <file:///new.lv2/> - * ] . - * @endcode - */ - -} // namespace Ingen diff --git a/src/ClashAvoider.cpp b/src/ClashAvoider.cpp deleted file mode 100644 index ae4438a4..00000000 --- a/src/ClashAvoider.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <cstdio> -#include <sstream> -#include <string> -#include <utility> - -#include "ingen/ClashAvoider.hpp" -#include "ingen/Store.hpp" -#include "ingen/paths.hpp" - -namespace Ingen { - -ClashAvoider::ClashAvoider(const Store& store) - : _store(store) -{} - -const URI -ClashAvoider::map_uri(const URI& in) -{ - if (uri_is_path(in)) { - return path_to_uri(map_path(uri_to_path(in))); - } else { - return in; - } -} - -const Raul::Path -ClashAvoider::map_path(const Raul::Path& in) -{ - unsigned offset = 0; - bool has_offset = false; - const size_t pos = in.find_last_of('_'); - if (pos != std::string::npos && pos != (in.length()-1)) { - const std::string trailing = in.substr(pos + 1); - has_offset = (sscanf(trailing.c_str(), "%u", &offset) > 0); - } - - // Path without _n suffix - std::string base_path_str = in; - if (has_offset) { - base_path_str = base_path_str.substr(0, base_path_str.find_last_of('_')); - } - - Raul::Path base_path(base_path_str); - - auto m = _symbol_map.find(in); - if (m != _symbol_map.end()) { - return m->second; - } else { - typedef std::pair<SymbolMap::iterator, bool> InsertRecord; - - // See if parent is mapped - Raul::Path parent = in.parent(); - do { - auto p = _symbol_map.find(parent); - if (p != _symbol_map.end()) { - const Raul::Path mapped = Raul::Path( - p->second.base() + in.substr(parent.base().length())); - InsertRecord i = _symbol_map.emplace(in, mapped); - return i.first->second; - } - parent = parent.parent(); - } while (!parent.is_root()); - - if (!exists(in) && _symbol_map.find(in) == _symbol_map.end()) { - // No clash, use symbol unmodified - InsertRecord i = _symbol_map.emplace(in, in); - assert(i.second); - return i.first->second; - - } else { - // Append _2 _3 etc until an unused symbol is found - while (true) { - auto o = _offsets.find(base_path); - if (o != _offsets.end()) { - offset = ++o->second; - } else { - std::string parent_str = in.parent().base(); - parent_str = parent_str.substr(0, parent_str.find_last_of("/")); - if (parent_str.empty()) { - parent_str = "/"; - } - } - - if (offset == 0) { - offset = 2; - } - - std::stringstream ss; - ss << base_path << "_" << offset; - if (!exists(Raul::Path(ss.str()))) { - std::string name = base_path.symbol(); - if (name == "") { - name = "_"; - } - Raul::Symbol sym(name); - std::string str = ss.str(); - InsertRecord i = _symbol_map.emplace(in, Raul::Path(str)); - offset = _store.child_name_offset(in.parent(), sym, false); - _offsets.emplace(base_path, offset); - return i.first->second; - } else { - if (o != _offsets.end()) { - offset = ++o->second; - } else { - ++offset; - } - } - } - } - } -} - -bool -ClashAvoider::exists(const Raul::Path& path) const -{ - return _store.find(path) != _store.end(); -} - -} // namespace Ingen diff --git a/src/ColorContext.cpp b/src/ColorContext.cpp deleted file mode 100644 index 23b568f1..00000000 --- a/src/ColorContext.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/ColorContext.hpp" -#include "ingen_config.h" - -#ifdef HAVE_ISATTY -# include <unistd.h> -#else -inline int isatty(int fd) { return 0; } -#endif - -namespace Ingen { - -ColorContext::ColorContext(FILE* stream, Color color) - : _stream(stream) -{ - if (isatty(fileno(_stream))) { - fprintf(_stream, "\033[0;%dm", (int)color); - } -} - -ColorContext::~ColorContext() -{ - if (isatty(fileno(_stream))) { - fprintf(_stream, "\033[0m"); - fflush(_stream); - } -} - -} // namespace Ingen diff --git a/src/Configuration.cpp b/src/Configuration.cpp deleted file mode 100644 index c797cf93..00000000 --- a/src/Configuration.cpp +++ /dev/null @@ -1,386 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <cerrno> -#include <cstring> -#include <iostream> -#include <thread> - -#include "ingen/Configuration.hpp" -#include "ingen/Forge.hpp" -#include "ingen/Log.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/filesystem.hpp" -#include "ingen/ingen.h" -#include "ingen/runtime_paths.hpp" -#include "sord/sordmm.hpp" -#include "sratom/sratom.h" - -namespace Ingen { - -Configuration::Configuration(Forge& forge) - : _forge(forge) - , _shortdesc("A realtime modular audio processor.") - , _desc( - "Ingen is a flexible modular system that be used in various ways.\n" - "The engine can run as a server controlled via a network protocol,\n" - "as an LV2 plugin, or in a monolithic process with a GUI. The GUI\n" - "may be run separately to control a remote engine, and many clients\n" - "may connect to an engine at once.\n\n" - "Examples:\n" - " ingen -e # Run engine, listen for connections\n" - " ingen -g # Run GUI, connect to running engine\n" - " ingen -eg # Run engine and GUI in one process\n" - " ingen -eg foo.ingen # Run engine and GUI and load a graph") - , _max_name_length(0) -{ - add("atomicBundles", "atomic-bundles", 'a', "Execute bundles atomically", GLOBAL, forge.Bool, forge.make(false)); - add("bufferSize", "buffer-size", 'b', "Buffer size in samples", GLOBAL, forge.Int, forge.make(1024)); - add("clientPort", "client-port", 'C', "Client port", GLOBAL, forge.Int, Atom()); - add("connect", "connect", 'c', "Connect to engine URI", SESSION, forge.String, forge.alloc("unix:///tmp/ingen.sock")); - add("engine", "engine", 'e', "Run (JACK) engine", SESSION, forge.Bool, forge.make(false)); - add("enginePort", "engine-port", 'E', "Engine listen port", GLOBAL, forge.Int, forge.make(16180)); - add("socket", "socket", 'S', "Engine socket path", GLOBAL, forge.String, forge.alloc("/tmp/ingen.sock")); - add("gui", "gui", 'g', "Launch the GTK graphical interface", SESSION, forge.Bool, forge.make(false)); - add("", "help", 'h', "Print this help message", SESSION, forge.Bool, forge.make(false)); - add("", "version", 'V', "Print version information", SESSION, forge.Bool, forge.make(false)); - add("jackName", "jack-name", 'n', "JACK name", GLOBAL, forge.String, forge.alloc("ingen")); - add("jackServer", "jack-server", 's', "JACK server name", GLOBAL, forge.String, forge.alloc("")); - add("uuid", "uuid", 'u', "JACK session UUID", GLOBAL, forge.String, Atom()); - add("load", "load", 'l', "Load graph", SESSION, forge.String, Atom()); - add("serverLoad", "server-load", 'i', "Load graph (server side)", SESSION, forge.String, Atom()); - add("save", "save", 'o', "Save graph", SESSION, forge.String, Atom()); - add("execute", "execute", 'x', "File of commands to execute", SESSION, forge.String, Atom()); - add("path", "path", 'L', "Target path for loaded graph", SESSION, forge.String, Atom()); - add("queueSize", "queue-size", 'q', "Event queue size", GLOBAL, forge.Int, forge.make(4096)); - add("flushLog", "flush-log", 'f', "Flush logs after every entry", GLOBAL, forge.Bool, forge.make(false)); - add("dump", "dump", 'd', "Print debug output", SESSION, forge.Bool, forge.make(false)); - add("trace", "trace", 't', "Show LV2 plugin trace messages", SESSION, forge.Bool, forge.make(false)); - add("threads", "threads", 'p', "Number of processing threads", GLOBAL, forge.Int, forge.make(int32_t(std::max(std::thread::hardware_concurrency(), 1U)))); - add("humanNames", "human-names", 0, "Show human names in GUI", GUI, forge.Bool, forge.make(true)); - add("portLabels", "port-labels", 0, "Show port labels in GUI", GUI, forge.Bool, forge.make(true)); - add("graphDirectory", "graph-directory", 0, "Default directory for opening graphs", GUI, forge.String, Atom()); -} - -Configuration& -Configuration::add(const std::string& key, - const std::string& name, - char letter, - const std::string& desc, - Scope scope, - const LV2_URID type, - const Atom& value) -{ - assert(value.type() == type || value.type() == 0); - _max_name_length = std::max(_max_name_length, name.length()); - _options.emplace(name, Option{key, name, letter, desc, scope, type, value}); - if (!key.empty()) { - _keys.emplace(key, name); - } - if (letter != '\0') { - _short_names.emplace(letter, name); - } - return *this; -} - -std::string -Configuration::variable_string(LV2_URID type) const -{ - if (type == _forge.String) { - return "=STRING"; - } else if (type == _forge.Int) { - return "=INT"; - } - return ""; -} - -void -Configuration::print_usage(const std::string& program, std::ostream& os) -{ - os << "Usage: " << program << " [OPTION]... [GRAPH]" << std::endl; - os << _shortdesc << std::endl << std::endl; - os << _desc << std::endl << std::endl; - os << "Options:" << std::endl; - for (const auto& o : _options) { - const Option& option = o.second; - os << " "; - if (option.letter != '\0') { - os << "-" << option.letter << ", "; - } else { - os << " "; - } - os.width(_max_name_length + 11); - os << std::left; - os << (std::string("--") + o.first + variable_string(option.type)); - os << option.desc << std::endl; - } -} - -int -Configuration::set_value_from_string(Configuration::Option& option, - const std::string& value) -{ - if (option.type == _forge.Int) { - char* endptr = nullptr; - int intval = static_cast<int>(strtol(value.c_str(), &endptr, 10)); - if (endptr && *endptr == '\0') { - option.value = _forge.make(intval); - } else { - throw OptionError( - (fmt("Option `%1%' has non-integer value `%2%'") - % option.name % value).str()); - } - } else if (option.type == _forge.String) { - option.value = _forge.alloc(value.c_str()); - assert(option.value.type() == _forge.String); - } else if (option.type == _forge.Bool) { - option.value = _forge.make(bool(!strcmp(value.c_str(), "true"))); - assert(option.value.type() == _forge.Bool); - } else { - throw OptionError( - (fmt("Bad option type `%1%'") % option.name).str()); - } - return EXIT_SUCCESS; -} - -/** Parse command line arguments. */ -void -Configuration::parse(int argc, char** argv) -{ - for (int i = 1; i < argc; ++i) { - if (argv[i][0] != '-' || !strcmp(argv[i], "-")) { - // File argument - const Options::iterator o = _options.find("load"); - if (!o->second.value.is_valid()) { - _options.find("load")->second.value = _forge.alloc(argv[i]); - } else { - throw OptionError("Multiple graphs specified"); - } - } else if (argv[i][1] == '-') { - // Long option - std::string name = std::string(argv[i]).substr(2); - const char* equals = strchr(argv[i], '='); - if (equals) { - name = name.substr(0, name.find('=')); - } - - const Options::iterator o = _options.find(name); - if (o == _options.end()) { - throw OptionError( - (fmt("Unrecognized option `%1%'") % name).str()); - } else if (o->second.type == _forge.Bool) { // --flag - o->second.value = _forge.make(true); - } else if (equals) { // --opt=val - set_value_from_string(o->second, equals + 1); - } else if (++i < argc) { // --opt val - set_value_from_string(o->second, argv[i]); - } else { - throw OptionError( - (fmt("Missing value for `%1%'") % name).str()); - } - } else { - // Short option - const size_t len = strlen(argv[i]); - for (size_t j = 1; j < len; ++j) { - const char letter = argv[i][j]; - const ShortNames::iterator n = _short_names.find(letter); - if (n == _short_names.end()) { - throw OptionError( - (fmt("Unrecognized option `%1%'") % letter).str()); - } - - const Options::iterator o = _options.find(n->second); - if (j < len - 1) { // Non-final POSIX style flag - if (o->second.type != _forge.Bool) { - throw OptionError( - (fmt("Missing value for `%1%'") % letter).str()); - } - o->second.value = _forge.make(true); - } else if (o->second.type == _forge.Bool) { // -f - o->second.value = _forge.make(true); - } else if (++i < argc) { // -v val - set_value_from_string(o->second, argv[i]); - } else { - throw OptionError( - (fmt("Missing value for `%1%'") % letter).str()); - } - } - } - } -} - -bool -Configuration::load(const FilePath& path) -{ - if (!filesystem::exists(path)) { - return false; - } - - SerdNode node = serd_node_new_file_uri( - (const uint8_t*)path.c_str(), nullptr, nullptr, true); - const std::string uri((const char*)node.buf); - - Sord::World world; - Sord::Model model(world, uri, SORD_SPO, false); - SerdEnv* env = serd_env_new(&node); - model.load_file(env, SERD_TURTLE, uri, uri); - - Sord::Node nodemm(world, Sord::Node::URI, (const char*)node.buf); - Sord::Node nil; - for (Sord::Iter i = model.find(nodemm, nil, nil); !i.end(); ++i) { - const Sord::Node& pred = i.get_predicate(); - const Sord::Node& obj = i.get_object(); - if (pred.to_string().substr(0, sizeof(INGEN_NS) - 1) == INGEN_NS) { - const std::string key = pred.to_string().substr(sizeof(INGEN_NS) - 1); - const Keys::iterator k = _keys.find(key); - if (k != _keys.end() && obj.type() == Sord::Node::LITERAL) { - set_value_from_string(_options.find(k->second)->second, - obj.to_string()); - } - } - } - - serd_node_free(&node); - serd_env_free(env); - return true; -} - -FilePath -Configuration::save(URIMap& uri_map, - const std::string& app, - const FilePath& filename, - unsigned scopes) -{ - // Save to file if it is absolute, otherwise save to user config dir - FilePath path = filename; - if (!path.is_absolute()) { - path = FilePath(user_config_dir()) / app / filename; - } - - // Create parent directories if necessary - const FilePath dir = path.parent_path(); - if (!filesystem::create_directories(dir)) { - throw FileError((fmt("Error creating directory %1% (%2%)") - % dir % strerror(errno)).str()); - } - - // Attempt to open file for writing - FILE* file = fopen(path.c_str(), "w"); - if (!file) { - throw FileError((fmt("Failed to open file %1% (%2%)") - % path % strerror(errno)).str()); - } - - // Use the file's URI as the base URI - SerdURI base_uri; - SerdNode base = serd_node_new_file_uri( - (const uint8_t*)path.c_str(), nullptr, &base_uri, true); - - // Create environment with ingen prefix - SerdEnv* env = serd_env_new(&base); - serd_env_set_prefix_from_strings( - env, (const uint8_t*)"ingen", (const uint8_t*)INGEN_NS); - - // Create Turtle writer - SerdWriter* writer = serd_writer_new( - SERD_TURTLE, - (SerdStyle)(SERD_STYLE_RESOLVED|SERD_STYLE_ABBREVIATED), - env, - &base_uri, - serd_file_sink, - file); - - // Write a prefix directive for each prefix in the environment - serd_env_foreach(env, (SerdPrefixSink)serd_writer_set_prefix, writer); - - // Create an atom serialiser and connect it to the Turtle writer - Sratom* sratom = sratom_new(&uri_map.urid_map_feature()->urid_map); - sratom_set_pretty_numbers(sratom, true); - sratom_set_sink(sratom, (const char*)base.buf, - (SerdStatementSink)serd_writer_write_statement, nullptr, - writer); - - // Write a statement for each valid option - for (auto o : _options) { - const Atom& value = o.second.value; - if (!(o.second.scope & scopes) || - o.second.key.empty() || - !value.is_valid()) { - continue; - } - - const std::string key(std::string("ingen:") + o.second.key); - SerdNode pred = serd_node_from_string( - SERD_CURIE, (const uint8_t*)key.c_str()); - sratom_write(sratom, &uri_map.urid_unmap_feature()->urid_unmap, 0, - &base, &pred, value.type(), value.size(), value.get_body()); - } - - sratom_free(sratom); - serd_writer_free(writer); - serd_env_free(env); - serd_node_free(&base); - fclose(file); - - return path; -} - -std::list<FilePath> -Configuration::load_default(const std::string& app, const FilePath& filename) -{ - std::list<FilePath> loaded; - - const std::vector<FilePath> dirs = system_config_dirs(); - for (const auto& d : dirs) { - const FilePath path = d / app / filename; - if (load(path)) { - loaded.push_back(path); - } - } - - const FilePath path = user_config_dir() / app / filename; - if (load(path)) { - loaded.push_back(path); - } - - return loaded; -} - -const Atom& -Configuration::option(const std::string& long_name) const -{ - static const Atom nil; - auto o = _options.find(long_name); - if (o == _options.end()) { - return nil; - } else { - return o->second.value; - } -} - -bool -Configuration::set(const std::string& long_name, const Atom& value) -{ - auto o = _options.find(long_name); - if (o != _options.end()) { - o->second.value = value; - return true; - } - return false; -} - -} // namespace Ingen diff --git a/src/FilePath.cpp b/src/FilePath.cpp deleted file mode 100644 index 557fabc9..00000000 --- a/src/FilePath.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2018 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/FilePath.hpp" - -namespace Ingen { - -template <typename Char> -static bool -is_sep(const Char chr) -{ -#ifdef USE_WINDOWS_FILE_PATHS - return chr == L'/' || chr == preferred_separator; -#else - return chr == '/'; -#endif -} - -FilePath& -FilePath::operator=(FilePath&& path) noexcept -{ - _str = std::move(path._str); - path.clear(); - return *this; -} - -FilePath& -FilePath::operator=(string_type&& str) -{ - return *this = FilePath(std::move(str)); -} - -FilePath& -FilePath::operator/=(const FilePath& path) -{ - const FilePath::string_type str = path.string(); - if (!_str.empty() && !is_sep(_str.back()) && !str.empty() && - !is_sep(str.front())) { - _str += preferred_separator; - } - - _str += str; - return *this; -} - -FilePath& -FilePath::operator+=(const FilePath& path) -{ - return operator+=(path.native()); -} - -FilePath& -FilePath::operator+=(const string_type& str) -{ - _str += str; - return *this; -} - -FilePath& -FilePath::operator+=(const value_type* str) -{ - _str += str; - return *this; -} - -FilePath& -FilePath::operator+=(value_type chr) -{ - _str += chr; - return *this; -} - -FilePath& -FilePath::operator+=(boost::basic_string_view<value_type> sv) -{ - _str.append(sv.data(), sv.size()); - return *this; -} - -FilePath -FilePath::root_name() const -{ -#ifdef USE_WINDOWS_FILE_PATHS - if (_str.length() >= 2 && _str[0] >= 'A' && _str[0] <= 'Z' && - _str[1] == ':') { - return FilePath(_str.substr(0, 2)); - } -#endif - - return FilePath(); -} - -FilePath -FilePath::root_directory() const -{ -#ifdef USE_WINDOWS_FILE_PATHS - const auto name = root_name().string(); - return name.empty() ? Path() : Path(name + preferred_separator); -#endif - - return _str[0] == '/' ? FilePath("/") : FilePath(); -} - -FilePath -FilePath::root_path() const -{ -#ifdef USE_WINDOWS_FILE_PATHS - const auto name = root_name(); - return name.empty() ? FilePath() : name / root_directory(); -#endif - return root_directory(); -} - -FilePath -FilePath::relative_path() const -{ - const auto root = root_path(); - return root.empty() ? FilePath() - : FilePath(_str.substr(root.string().length())); -} - -FilePath -FilePath::parent_path() const -{ - if (empty() || *this == root_path()) { - return *this; - } - - const auto first_sep = find_first_sep(); - const auto last_sep = find_last_sep(); - return ((last_sep == std::string::npos || last_sep == first_sep) - ? root_path() - : FilePath(_str.substr(0, last_sep))); -} - -FilePath -FilePath::filename() const -{ - return ((empty() || *this == root_path()) - ? FilePath() - : FilePath(_str.substr(find_last_sep() + 1))); -} - -FilePath -FilePath::stem() const -{ - const auto name = filename(); - const auto dot = name.string().find('.'); - return ((dot == std::string::npos) ? name - : FilePath(name.string().substr(0, dot))); -} - -FilePath -FilePath::extension() const -{ - const auto name = filename().string(); - const auto dot = name.find('.'); - return ((dot == std::string::npos) ? FilePath() - : FilePath(name.substr(dot, dot))); -} - -bool -FilePath::is_absolute() const -{ -#ifdef USE_WINDOWS_FILE_PATHS - return !root_name().empty(); -#else - return !root_directory().empty(); -#endif -} - -std::size_t -FilePath::find_first_sep() const -{ - const auto i = std::find_if(_str.begin(), _str.end(), is_sep<value_type>); - return i == _str.end() ? std::string::npos : (i - _str.begin()); -} - -std::size_t -FilePath::find_last_sep() const -{ - const auto i = std::find_if(_str.rbegin(), _str.rend(), is_sep<value_type>); - return (i == _str.rend() ? std::string::npos - : (_str.length() - 1 - (i - _str.rbegin()))); -} - -bool -operator==(const FilePath& lhs, const FilePath& rhs) noexcept -{ - return lhs.string() == rhs.string(); -} - -bool -operator!=(const FilePath& lhs, const FilePath& rhs) noexcept -{ - return !(lhs == rhs); -} - -bool -operator<(const FilePath& lhs, const FilePath& rhs) noexcept -{ - return lhs.string().compare(rhs.string()) < 0; -} - -bool -operator<=(const FilePath& lhs, const FilePath& rhs) noexcept -{ - return !(rhs < lhs); -} - -bool -operator>(const FilePath& lhs, const FilePath& rhs) noexcept -{ - return rhs < lhs; -} - -bool -operator>=(const FilePath& lhs, const FilePath& rhs) noexcept -{ - return !(lhs < rhs); -} - -FilePath -operator/(const FilePath& lhs, const FilePath& rhs) -{ - return FilePath(lhs) /= rhs; -} - -} // namespace Ingen diff --git a/src/Forge.cpp b/src/Forge.cpp deleted file mode 100644 index 688d994d..00000000 --- a/src/Forge.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <sstream> - -#include "ingen/Forge.hpp" -#include "ingen/URI.hpp" -#include "ingen/URIMap.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" - -namespace Ingen { - -Forge::Forge(URIMap& map) - : _map(map) -{ - lv2_atom_forge_init(this, &map.urid_map_feature()->urid_map); -} - -Atom -Forge::make_urid(const Ingen::URI& u) -{ - const LV2_URID urid = _map.map_uri(u.string()); - return Atom(sizeof(int32_t), URID, &urid); -} - -std::string -Forge::str(const Atom& atom, bool quoted) -{ - std::ostringstream ss; - if (atom.type() == Int) { - ss << atom.get<int32_t>(); - } else if (atom.type() == Float) { - ss << atom.get<float>(); - } else if (atom.type() == Bool) { - ss << (atom.get<int32_t>() ? "true" : "false"); - } else if (atom.type() == URI) { - ss << (quoted ? "<" : "") - << atom.ptr<const char>() - << (quoted ? ">" : ""); - } else if (atom.type() == URID) { - ss << (quoted ? "<" : "") - << _map.unmap_uri(atom.get<int32_t>()) - << (quoted ? ">" : ""); - } else if (atom.type() == Path) { - ss << (quoted ? "<" : "") - << atom.ptr<const char>() - << (quoted ? ">" : ""); - } else if (atom.type() == String) { - ss << (quoted ? "\"" : "") - << atom.ptr<const char>() - << (quoted ? "\"" : ""); - } - return ss.str(); -} - -} // namespace Ingen diff --git a/src/LV2Features.cpp b/src/LV2Features.cpp deleted file mode 100644 index 356df42c..00000000 --- a/src/LV2Features.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cstdlib> - -#include "ingen/LV2Features.hpp" - -namespace Ingen { - -void -LV2Features::Feature::free_feature(LV2_Feature* feature) -{ - free(feature->data); - free(feature); -} - -LV2Features::LV2Features() -{ -} - -void -LV2Features::add_feature(SPtr<Feature> feature) -{ - _features.push_back(feature); -} - -LV2Features::FeatureArray::FeatureArray(FeatureVector& features) - : _features(features) -{ - _array = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * (features.size() + 1)); - _array[features.size()] = nullptr; - for (size_t i = 0; i < features.size(); ++i) { - _array[i] = features[i].get(); - } -} - -LV2Features::FeatureArray::~FeatureArray() -{ - free(_array); -} - -bool -LV2Features::is_supported(const std::string& uri) const -{ - if (uri == "http://lv2plug.in/ns/lv2core#isLive") { - return true; - } - - for (const auto& f : _features) { - if (f->uri() == uri) { - return true; - } - } - return false; -} - -SPtr<LV2Features::FeatureArray> -LV2Features::lv2_features(World* world, Node* node) const -{ - FeatureArray::FeatureVector vec; - for (const auto& f : _features) { - SPtr<LV2_Feature> fptr = f->feature(world, node); - if (fptr) { - vec.push_back(fptr); - } - } - return SPtr<FeatureArray>(new FeatureArray(vec)); -} - -} // namespace Ingen diff --git a/src/Library.cpp b/src/Library.cpp deleted file mode 100644 index 148b27d0..00000000 --- a/src/Library.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2018 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Library.hpp" - -#ifdef _WIN32 -# include <windows.h> -# define dlopen(path, flags) LoadLibrary(path) -# define dlclose(lib) FreeLibrary((HMODULE)lib) -# define dlerror() "unknown error" -#else -# include <dlfcn.h> -#endif - -namespace Ingen { - -Library::Library(const FilePath& path) : _lib(dlopen(path.c_str(), RTLD_NOW)) -{} - -Library::~Library() -{ - dlclose(_lib); -} - -Library::VoidFuncPtr -Library::get_function(const char* name) -{ -#ifdef _WIN32 - return (VoidFuncPtr)GetProcAddress((HMODULE)_lib, name); -#else - typedef VoidFuncPtr (*VoidFuncGetter)(void*, const char*); - VoidFuncGetter dlfunc = (VoidFuncGetter)dlsym; - return dlfunc(_lib, name); -#endif -} - -const char* -Library::get_last_error() -{ - return dlerror(); -} - -} // namespace Ingen diff --git a/src/Log.cpp b/src/Log.cpp deleted file mode 100644 index 6145bcd1..00000000 --- a/src/Log.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cstdio> - -#include "ingen/Log.hpp" -#include "ingen/Node.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" -#include "ingen/ColorContext.hpp" - -namespace Ingen { - -Log::Log(LV2_Log_Log* log, URIs& uris) - : _log(log) - , _uris(uris) - , _flush(false) - , _trace(false) -{} - -void -Log::rt_error(const char* msg) -{ -#ifndef NDEBUG - va_list args; - vtprintf(_uris.log_Error, msg, args); -#endif -} - -void -Log::error(const std::string& msg) -{ - va_list args; - vtprintf(_uris.log_Error, msg.c_str(), args); -} - -void -Log::warn(const std::string& msg) -{ - va_list args; - vtprintf(_uris.log_Warning, msg.c_str(), args); -} - -void -Log::info(const std::string& msg) -{ - va_list args; - vtprintf(_uris.log_Note, msg.c_str(), args); -} - -void -Log::trace(const std::string& msg) -{ - va_list args; - vtprintf(_uris.log_Trace, msg.c_str(), args); -} - -void -Log::print(FILE* stream, const std::string& msg) -{ - fprintf(stream, "%s", msg.c_str()); - if (_flush) { - fflush(stdout); - } -} - -int -Log::vtprintf(LV2_URID type, const char* fmt, va_list args) -{ - int ret = 0; - if (type == _uris.log_Trace && !_trace) { - return 0; - } else if (_sink) { - _sink(type, fmt, args); - } - - if (_log) { - ret = _log->vprintf(_log->handle, type, fmt, args); - } else if (type == _uris.log_Error) { - ColorContext ctx(stderr, ColorContext::Color::RED); - ret = vfprintf(stderr, fmt, args); - } else if (type == _uris.log_Warning) { - ColorContext ctx(stderr, ColorContext::Color::YELLOW); - ret = vfprintf(stderr, fmt, args); - } else if (type == _uris.log_Note) { - ColorContext ctx(stderr, ColorContext::Color::GREEN); - ret = vfprintf(stdout, fmt, args); - } else if (_trace && type == _uris.log_Trace) { - ColorContext ctx(stderr, ColorContext::Color::GREEN); - ret = vfprintf(stderr, fmt, args); - } else { - fprintf(stderr, "Unknown log type %d\n", type); - return 0; - } - if (_flush) { - fflush(stdout); - } - return ret; -} - -static int -log_vprintf(LV2_Log_Handle handle, LV2_URID type, const char* fmt, va_list args) -{ - Log::Feature::Handle* f = (Log::Feature::Handle*)handle; - va_list noargs; - - int ret = f->log->vtprintf(type, f->node->path().c_str(), noargs); - ret += f->log->vtprintf(type, ": ", noargs); - ret += f->log->vtprintf(type, fmt, args); - - return ret; -} - -static int -log_printf(LV2_Log_Handle handle, LV2_URID type, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - const int ret = log_vprintf(handle, type, fmt, args); - va_end(args); - - return ret; -} - -static void -free_log_feature(LV2_Feature* feature) { - LV2_Log_Log* lv2_log = (LV2_Log_Log*)feature->data; - free(lv2_log->handle); - free(feature); -} - -SPtr<LV2_Feature> -Log::Feature::feature(World* world, Node* block) -{ - Handle* handle = (Handle*)calloc(1, sizeof(Handle)); - handle->lv2_log.handle = handle; - handle->lv2_log.printf = log_printf; - handle->lv2_log.vprintf = log_vprintf; - handle->log = &world->log(); - handle->node = block; - - LV2_Feature* f = (LV2_Feature*)malloc(sizeof(LV2_Feature)); - f->URI = LV2_LOG__log; - f->data = &handle->lv2_log; - - return SPtr<LV2_Feature>(f, &free_log_feature); -} - -} // namespace Ingen diff --git a/src/Parser.cpp b/src/Parser.cpp deleted file mode 100644 index dace07ed..00000000 --- a/src/Parser.cpp +++ /dev/null @@ -1,713 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <map> -#include <set> -#include <string> -#include <utility> - -#include "ingen/Atom.hpp" -#include "ingen/AtomForgeSink.hpp" -#include "ingen/Forge.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/Parser.hpp" -#include "ingen/URI.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" -#include "ingen/filesystem.hpp" -#include "ingen/paths.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "serd/serd.h" -#include "sord/sordmm.hpp" -#include "sratom/sratom.h" - -#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" -#define NS_RDFS "http://www.w3.org/2000/01/rdf-schema#" - -namespace Ingen { - -std::set<Parser::ResourceRecord> -Parser::find_resources(Sord::World& world, - const URI& manifest_uri, - const URI& type_uri) -{ - const Sord::URI base (world, manifest_uri.string()); - const Sord::URI type (world, type_uri.string()); - const Sord::URI rdf_type (world, NS_RDF "type"); - const Sord::URI rdfs_seeAlso(world, NS_RDFS "seeAlso"); - const Sord::Node nil; - - SerdEnv* env = serd_env_new(sord_node_to_serd_node(base.c_obj())); - Sord::Model model(world, manifest_uri.string()); - model.load_file(env, SERD_TURTLE, manifest_uri.string()); - - std::set<ResourceRecord> resources; - for (Sord::Iter i = model.find(nil, rdf_type, type); !i.end(); ++i) { - const Sord::Node resource = i.get_subject(); - const std::string resource_uri = resource.to_c_string(); - std::string file_path = ""; - Sord::Iter f = model.find(resource, rdfs_seeAlso, nil); - if (!f.end()) { - uint8_t* p = serd_file_uri_parse(f.get_object().to_u_string(), nullptr); - file_path = (const char*)p; - serd_free(p); - } - resources.insert(ResourceRecord(resource, file_path)); - } - - serd_env_free(env); - return resources; -} - -static boost::optional<Raul::Path> -get_path(const URI base, const URI uri) -{ - const URI relative = uri.make_relative(base); - const std::string uri_str = "/" + relative.string(); - return Raul::Path::is_valid(uri_str) ? Raul::Path(uri_str) - : boost::optional<Raul::Path>(); -} - -static bool -skip_property(Ingen::URIs& uris, const Sord::Node& predicate) -{ - return (predicate == INGEN__file || - predicate == uris.ingen_arc || - predicate == uris.ingen_block || - predicate == uris.lv2_port); -} - -static Properties -get_properties(Ingen::World* world, - Sord::Model& model, - const Sord::Node& subject, - Resource::Graph ctx) -{ - LV2_URID_Map* map = &world->uri_map().urid_map_feature()->urid_map; - Sratom* sratom = sratom_new(map); - - LV2_Atom_Forge forge; - lv2_atom_forge_init(&forge, map); - - AtomForgeSink out(&forge); - - const Sord::Node nil; - Properties props; - for (Sord::Iter i = model.find(subject, nil, nil); !i.end(); ++i) { - if (!skip_property(world->uris(), i.get_predicate())) { - out.clear(); - sratom_read(sratom, &forge, world->rdf_world()->c_obj(), - model.c_obj(), i.get_object().c_obj()); - const LV2_Atom* atom = out.atom(); - Atom atomm; - atomm = world->forge().alloc( - atom->size, atom->type, LV2_ATOM_BODY_CONST(atom)); - props.emplace(i.get_predicate(), Property(atomm, ctx)); - } - } - - sratom_free(sratom); - return props; -} - -typedef std::pair<Raul::Path, Properties> PortRecord; - -static boost::optional<PortRecord> -get_port(Ingen::World* world, - Sord::Model& model, - const Sord::Node& subject, - Resource::Graph ctx, - const Raul::Path& parent, - uint32_t* index) -{ - const URIs& uris = world->uris(); - - // Get all properties - Properties props = get_properties(world, model, subject, ctx); - - // Get index if requested (for Graphs) - if (index) { - Properties::const_iterator i = props.find(uris.lv2_index); - if (i == props.end() - || i->second.type() != world->forge().Int - || i->second.get<int32_t>() < 0) { - world->log().error(fmt("Port %1% has no valid index\n") % subject); - return boost::optional<PortRecord>(); - } - *index = i->second.get<int32_t>(); - } - - // Get symbol - Properties::const_iterator s = props.find(uris.lv2_symbol); - std::string sym; - if (s != props.end() && s->second.type() == world->forge().String) { - sym = s->second.ptr<char>(); - } else { - const std::string subject_str = subject.to_string(); - const size_t last_slash = subject_str.find_last_of("/"); - - sym = ((last_slash == std::string::npos) - ? subject_str - : subject_str.substr(last_slash + 1)); - } - - if (!Raul::Symbol::is_valid(sym)) { - world->log().error(fmt("Port %1% has invalid symbol `%2%'\n") - % subject % sym); - return boost::optional<PortRecord>(); - } - - const Raul::Symbol port_sym(sym); - const Raul::Path port_path(parent.child(port_sym)); - - props.erase(uris.lv2_symbol); // Don't set symbol property in engine - return make_pair(port_path, props); -} - -static boost::optional<Raul::Path> -parse( - World* world, - Interface* target, - Sord::Model& model, - const URI& base_uri, - Sord::Node& subject, - boost::optional<Raul::Path> parent = boost::optional<Raul::Path>(), - boost::optional<Raul::Symbol> symbol = boost::optional<Raul::Symbol>(), - boost::optional<Properties> data = boost::optional<Properties>()); - -static boost::optional<Raul::Path> -parse_graph( - World* world, - Interface* target, - Sord::Model& model, - const URI& base_uri, - const Sord::Node& subject, - Resource::Graph ctx, - boost::optional<Raul::Path> parent = boost::optional<Raul::Path>(), - boost::optional<Raul::Symbol> symbol = boost::optional<Raul::Symbol>(), - boost::optional<Properties> data = boost::optional<Properties>()); - -static boost::optional<Raul::Path> -parse_block( - World* world, - Interface* target, - Sord::Model& model, - const URI& base_uri, - const Sord::Node& subject, - const Raul::Path& path, - boost::optional<Properties> data = boost::optional<Properties>()); - -static bool -parse_properties( - World* world, - Interface* target, - Sord::Model& model, - const Sord::Node& subject, - Resource::Graph ctx, - const URI& uri, - boost::optional<Properties> data = boost::optional<Properties>()); - -static bool -parse_arcs( - World* world, - Interface* target, - Sord::Model& model, - const URI& base_uri, - const Sord::Node& subject, - const Raul::Path& graph); - -static boost::optional<Raul::Path> -parse_block(Ingen::World* world, - Ingen::Interface* target, - Sord::Model& model, - const URI& base_uri, - const Sord::Node& subject, - const Raul::Path& path, - boost::optional<Properties> data) -{ - const URIs& uris = world->uris(); - - // Try lv2:prototype and old ingen:prototype for backwards compatibility - const Sord::URI prototype_predicates[] = { - Sord::URI(*world->rdf_world(), uris.lv2_prototype), - Sord::URI(*world->rdf_world(), uris.ingen_prototype) - }; - - // Get prototype - Sord::Node prototype; - for (const Sord::URI& pred : prototype_predicates) { - prototype = model.get(subject, pred, Sord::Node()); - if (prototype.is_valid()) { - break; - } - } - - if (!prototype.is_valid()) { - world->log().error( - fmt("Block %1% (%2%) missing mandatory lv2:prototype\n") % - subject % path); - return boost::optional<Raul::Path>(); - } - - const uint8_t* type_uri = (const uint8_t*)prototype.to_c_string(); - if (!serd_uri_string_has_scheme(type_uri) || - !strncmp((const char*)type_uri, "file:", 5)) { - // Prototype is a file, subgraph - SerdURI base_uri_parts; - serd_uri_parse((const uint8_t*)base_uri.c_str(), &base_uri_parts); - - SerdURI ignored; - SerdNode sub_uri = serd_node_new_uri_from_string( - type_uri, - &base_uri_parts, - &ignored); - - const std::string sub_uri_str = (const char*)sub_uri.buf; - const std::string sub_file = sub_uri_str + "/main.ttl"; - - const SerdNode sub_base = serd_node_from_string( - SERD_URI, (const uint8_t*)sub_file.c_str()); - - Sord::Model sub_model(*world->rdf_world(), sub_file); - SerdEnv* env = serd_env_new(&sub_base); - sub_model.load_file(env, SERD_TURTLE, sub_file); - serd_env_free(env); - - Sord::URI sub_node(*world->rdf_world(), sub_file); - parse_graph(world, target, sub_model, sub_base, - sub_node, Resource::Graph::INTERNAL, - path.parent(), Raul::Symbol(path.symbol())); - - parse_graph(world, target, model, base_uri, - subject, Resource::Graph::EXTERNAL, - path.parent(), Raul::Symbol(path.symbol())); - } else { - // Prototype is non-file URI, plugin - Properties props = get_properties( - world, model, subject, Resource::Graph::DEFAULT); - props.emplace(uris.rdf_type, uris.forge.make_urid(uris.ingen_Block)); - target->put(path_to_uri(path), props); - } - return path; -} - -static boost::optional<Raul::Path> -parse_graph(Ingen::World* world, - Ingen::Interface* target, - Sord::Model& model, - const URI& base_uri, - const Sord::Node& subject, - Resource::Graph ctx, - boost::optional<Raul::Path> parent, - boost::optional<Raul::Symbol> symbol, - boost::optional<Properties> data) -{ - const URIs& uris = world->uris(); - - const Sord::URI ingen_block(*world->rdf_world(), uris.ingen_block); - const Sord::URI lv2_port(*world->rdf_world(), LV2_CORE__port); - - const Sord::Node& graph = subject; - const Sord::Node nil; - - // Build graph path and symbol - Raul::Path graph_path; - if (parent && symbol) { - graph_path = parent->child(*symbol); - } else if (parent) { - graph_path = *parent; - } else { - graph_path = Raul::Path("/"); - } - - if (!symbol) { - symbol = Raul::Symbol("_"); - } - - // Create graph - Properties props = get_properties(world, model, subject, ctx); - target->put(path_to_uri(graph_path), props, ctx); - - // For each port on this graph - typedef std::map<uint32_t, PortRecord> PortRecords; - PortRecords ports; - for (Sord::Iter p = model.find(graph, lv2_port, nil); !p.end(); ++p) { - Sord::Node port = p.get_object(); - - // Get all properties - uint32_t index = 0; - boost::optional<PortRecord> port_record = get_port( - world, model, port, ctx, graph_path, &index); - if (!port_record) { - world->log().error(fmt("Invalid port %1%\n") % port); - return boost::optional<Raul::Path>(); - } - - // Store port information in ports map - if (ports.find(index) == ports.end()) { - ports[index] = *port_record; - } else { - world->log().error(fmt("Ignored port %1% with duplicate index %2%\n") - % port % index); - } - } - - // Create ports in order by index - for (const auto& p : ports) { - target->put(path_to_uri(p.second.first), - p.second.second, - ctx); - } - - if (ctx != Resource::Graph::INTERNAL) { - return graph_path; // Not parsing graph internals, finished now - } - - // For each block in this graph - for (Sord::Iter n = model.find(subject, ingen_block, nil); !n.end(); ++n) { - Sord::Node node = n.get_object(); - URI node_uri = node; - assert(!node_uri.path().empty() && node_uri.path() != "/"); - const Raul::Path block_path = graph_path.child( - Raul::Symbol(FilePath(node_uri.path()).stem().string())); - - // Parse and create block - parse_block(world, target, model, base_uri, node, block_path, - boost::optional<Properties>()); - - // For each port on this block - for (Sord::Iter p = model.find(node, lv2_port, nil); !p.end(); ++p) { - Sord::Node port = p.get_object(); - - Resource::Graph subctx = Resource::Graph::DEFAULT; - if (!model.find(node, - Sord::URI(*world->rdf_world(), uris.rdf_type), - Sord::URI(*world->rdf_world(), uris.ingen_Graph)).end()) { - subctx = Resource::Graph::EXTERNAL; - } - - // Get all properties - boost::optional<PortRecord> port_record = get_port( - world, model, port, subctx, block_path, nullptr); - if (!port_record) { - world->log().error(fmt("Invalid port %1%\n") % port); - return boost::optional<Raul::Path>(); - } - - // Create port and/or set all port properties - target->put(path_to_uri(port_record->first), - port_record->second, - subctx); - } - } - - // Now that all ports and blocks exist, create arcs inside graph - parse_arcs(world, target, model, base_uri, subject, graph_path); - - return graph_path; -} - -static bool -parse_arc(Ingen::World* world, - Ingen::Interface* target, - Sord::Model& model, - const URI& base_uri, - const Sord::Node& subject, - const Raul::Path& graph) -{ - const URIs& uris = world->uris(); - - const Sord::URI ingen_tail(*world->rdf_world(), uris.ingen_tail); - const Sord::URI ingen_head(*world->rdf_world(), uris.ingen_head); - const Sord::Node nil; - - Sord::Iter t = model.find(subject, ingen_tail, nil); - Sord::Iter h = model.find(subject, ingen_head, nil); - - if (t.end()) { - world->log().error("Arc has no tail\n"); - return false; - } else if (h.end()) { - world->log().error("Arc has no head\n"); - return false; - } - - const boost::optional<Raul::Path> tail_path = get_path( - base_uri, t.get_object()); - if (!tail_path) { - world->log().error("Arc tail has invalid URI\n"); - return false; - } - - const boost::optional<Raul::Path> head_path = get_path( - base_uri, h.get_object()); - if (!head_path) { - world->log().error("Arc head has invalid URI\n"); - return false; - } - - if (!(++t).end()) { - world->log().error("Arc has multiple tails\n"); - return false; - } else if (!(++h).end()) { - world->log().error("Arc has multiple heads\n"); - return false; - } - - target->connect(graph.child(*tail_path), graph.child(*head_path)); - - return true; -} - -static bool -parse_arcs(Ingen::World* world, - Ingen::Interface* target, - Sord::Model& model, - const URI& base_uri, - const Sord::Node& subject, - const Raul::Path& graph) -{ - const Sord::URI ingen_arc(*world->rdf_world(), world->uris().ingen_arc); - const Sord::Node nil; - - for (Sord::Iter i = model.find(subject, ingen_arc, nil); !i.end(); ++i) { - parse_arc(world, target, model, base_uri, i.get_object(), graph); - } - - return true; -} - -static bool -parse_properties(Ingen::World* world, - Ingen::Interface* target, - Sord::Model& model, - const Sord::Node& subject, - Resource::Graph ctx, - const URI& uri, - boost::optional<Properties> data) -{ - Properties properties = get_properties(world, model, subject, ctx); - - target->put(uri, properties, ctx); - - // Set passed properties last to override any loaded values - if (data) { - target->put(uri, data.get(), ctx); - } - - return true; -} - -static boost::optional<Raul::Path> -parse(Ingen::World* world, - Ingen::Interface* target, - Sord::Model& model, - const URI& base_uri, - Sord::Node& subject, - boost::optional<Raul::Path> parent, - boost::optional<Raul::Symbol> symbol, - boost::optional<Properties> data) -{ - const URIs& uris = world->uris(); - - const Sord::URI graph_class (*world->rdf_world(), uris.ingen_Graph); - const Sord::URI block_class (*world->rdf_world(), uris.ingen_Block); - const Sord::URI arc_class (*world->rdf_world(), uris.ingen_Arc); - const Sord::URI internal_class(*world->rdf_world(), uris.ingen_Internal); - const Sord::URI in_port_class (*world->rdf_world(), LV2_CORE__InputPort); - const Sord::URI out_port_class(*world->rdf_world(), LV2_CORE__OutputPort); - const Sord::URI lv2_class (*world->rdf_world(), LV2_CORE__Plugin); - const Sord::URI rdf_type (*world->rdf_world(), uris.rdf_type); - const Sord::Node nil; - - // Parse explicit subject graph - if (subject.is_valid()) { - return parse_graph(world, target, model, base_uri, - subject, Resource::Graph::INTERNAL, - parent, symbol, data); - } - - // Get all subjects and their types (?subject a ?type) - typedef std::map< Sord::Node, std::set<Sord::Node> > Subjects; - Subjects subjects; - for (Sord::Iter i = model.find(subject, rdf_type, nil); !i.end(); ++i) { - const Sord::Node& subject = i.get_subject(); - const Sord::Node& rdf_class = i.get_object(); - - assert(rdf_class.is_uri()); - auto s = subjects.find(subject); - if (s == subjects.end()) { - std::set<Sord::Node> types; - types.insert(rdf_class); - subjects.emplace(subject, types); - } else { - s->second.insert(rdf_class); - } - } - - // Parse and create each subject - for (const auto& i : subjects) { - const Sord::Node& s = i.first; - const std::set<Sord::Node>& types = i.second; - boost::optional<Raul::Path> ret; - if (types.find(graph_class) != types.end()) { - ret = parse_graph(world, target, model, base_uri, - s, Resource::Graph::INTERNAL, - parent, symbol, data); - } else if (types.find(block_class) != types.end()) { - const Raul::Path rel_path(*get_path(base_uri, s)); - const Raul::Path path = parent ? parent->child(rel_path) : rel_path; - ret = parse_block(world, target, model, base_uri, s, path, data); - } else if (types.find(in_port_class) != types.end() || - types.find(out_port_class) != types.end()) { - const Raul::Path rel_path(*get_path(base_uri, s)); - const Raul::Path path = parent ? parent->child(rel_path) : rel_path; - parse_properties(world, target, model, - s, Resource::Graph::DEFAULT, - path_to_uri(path), data); - ret = path; - } else if (types.find(arc_class) != types.end()) { - Raul::Path parent_path(parent ? parent.get() : Raul::Path("/")); - parse_arc(world, target, model, base_uri, s, parent_path); - } else { - world->log().error("Subject has no known types\n"); - } - } - - return boost::optional<Raul::Path>(); -} - -bool -Parser::parse_file(Ingen::World* world, - Ingen::Interface* target, - const FilePath& path, - boost::optional<Raul::Path> parent, - boost::optional<Raul::Symbol> symbol, - boost::optional<Properties> data) -{ - // Get absolute file path - FilePath file_path = path; - if (!file_path.is_absolute()) { - file_path = filesystem::current_path() / file_path; - } - - // Find file to use as manifest - const bool is_bundle = filesystem::is_directory(file_path); - const FilePath manifest_path = - (is_bundle ? file_path / "manifest.ttl" : file_path); - - URI manifest_uri(manifest_path); - - // Find graphs in manifest - const std::set<ResourceRecord> resources = find_resources( - *world->rdf_world(), manifest_uri, URI(INGEN__Graph)); - - if (resources.empty()) { - world->log().error(fmt("No graphs found in %1%\n") % path); - return false; - } - - /* Choose the graph to load. If this is a manifest, then there should only be - one, but if this is a graph file, subgraphs will be returned as well. - In this case, choose the one with the file URI. */ - URI uri; - for (const ResourceRecord& r : resources) { - if (r.uri == URI(manifest_path)) { - uri = r.uri; - file_path = r.filename; - break; - } - } - - if (uri.empty()) { - // Didn't find a graph with the same URI as the file, use the first - uri = (*resources.begin()).uri; - file_path = (*resources.begin()).filename; - } - - if (file_path.empty()) { - // No seeAlso file, use manifest (probably the graph file itself) - file_path = manifest_path; - } - - // Initialise parsing environment - const URI file_uri = URI(file_path); - const uint8_t* uri_c_str = (const uint8_t*)uri.c_str(); - SerdNode base_node = serd_node_from_string(SERD_URI, uri_c_str); - SerdEnv* env = serd_env_new(&base_node); - - // Load graph into model - Sord::Model model(*world->rdf_world(), uri.string(), SORD_SPO|SORD_PSO, false); - model.load_file(env, SERD_TURTLE, file_uri); - serd_env_free(env); - - world->log().info(fmt("Loading %1% from %2%\n") % uri % file_path); - if (parent) { - world->log().info(fmt("Parent: %1%\n") % parent->c_str()); - } - if (symbol) { - world->log().info(fmt("Symbol: %1%\n") % symbol->c_str()); - } - - Sord::Node subject(*world->rdf_world(), Sord::Node::URI, uri.string()); - boost::optional<Raul::Path> parsed_path - = parse(world, target, model, model.base_uri(), - subject, parent, symbol, data); - - if (parsed_path) { - target->set_property(path_to_uri(*parsed_path), - URI(INGEN__file), - world->forge().alloc_uri(uri.string())); - return true; - } else { - world->log().warn("Document URI lost\n"); - return false; - } -} - -boost::optional<URI> -Parser::parse_string(Ingen::World* world, - Ingen::Interface* target, - const std::string& str, - const URI& base_uri, - boost::optional<Raul::Path> parent, - boost::optional<Raul::Symbol> symbol, - boost::optional<Properties> data) -{ - // Load string into model - Sord::Model model(*world->rdf_world(), base_uri, SORD_SPO|SORD_PSO, false); - - SerdEnv* env = serd_env_new(nullptr); - if (!base_uri.empty()) { - const SerdNode base = serd_node_from_string( - SERD_URI, (const uint8_t*)base_uri.c_str()); - serd_env_set_base_uri(env, &base); - } - model.load_string(env, SERD_TURTLE, str.c_str(), str.length(), base_uri); - - URI actual_base((const char*)serd_env_get_base_uri(env, nullptr)->buf); - serd_env_free(env); - - world->log().info(fmt("Parsing string (base %1%)\n") % base_uri); - - Sord::Node subject; - parse(world, target, model, actual_base, subject, parent, symbol, data); - return actual_base; -} - -} // namespace Ingen diff --git a/src/Resource.cpp b/src/Resource.cpp deleted file mode 100644 index d0261eee..00000000 --- a/src/Resource.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cstdlib> -#include <utility> - -#include "ingen/Atom.hpp" -#include "ingen/Forge.hpp" -#include "ingen/Resource.hpp" -#include "ingen/URIs.hpp" - -namespace Ingen { - -bool -Resource::add_property(const URI& uri, const Atom& value, Graph ctx) -{ - // Ignore duplicate statements - typedef Properties::const_iterator iterator; - const std::pair<iterator, iterator> range = _properties.equal_range(uri); - for (iterator i = range.first; i != range.second && i != _properties.end(); ++i) { - if (i->second == value && i->second.context() == ctx) { - return false; - } - } - - if (uri != _uris.ingen_activity) { - // Insert new property - const Atom& v = _properties.emplace(uri, Property(value, ctx))->second; - on_property(uri, v); - } else { - // Announce ephemeral activity, but do not store - on_property(uri, value); - } - - return true; -} - -const Atom& -Resource::set_property(const URI& uri, const Atom& value, Resource::Graph ctx) -{ - // Erase existing property in this context - for (auto i = _properties.find(uri); - (i != _properties.end()) && (i->first == uri);) { - auto next = i; - ++next; - if (i->second.context() == ctx) { - const Atom value(i->second); - _properties.erase(i); - on_property_removed(uri, value); - } - i = next; - } - - if (uri != _uris.ingen_activity) { - // Insert new property - const Atom& v = _properties.emplace(uri, Property(value, ctx))->second; - on_property(uri, v); - return v; - } else { - // Announce ephemeral activity, but do not store - on_property(uri, value); - return value; - } -} - -const Atom& -Resource::set_property(const URI& uri, - const URIs::Quark& value, - Resource::Graph ctx) -{ - return set_property(uri, value.urid, ctx); -} - -void -Resource::remove_property(const URI& uri, const Atom& value) -{ - if (_uris.patch_wildcard == value) { - _properties.erase(uri); - } else { - for (auto i = _properties.find(uri); - i != _properties.end() && (i->first == uri); - ++i) { - if (i->second == value) { - _properties.erase(i); - break; - } - } - } - on_property_removed(uri, value); -} - -void -Resource::remove_property(const URI& uri, const URIs::Quark& value) -{ - remove_property(uri, value.urid); - remove_property(uri, value.uri); -} - -bool -Resource::has_property(const URI& uri, const Atom& value) const -{ - return _properties.contains(uri, value); -} - -bool -Resource::has_property(const URI& uri, const URIs::Quark& value) const -{ - Properties::const_iterator i = _properties.find(uri); - for (; (i != _properties.end()) && (i->first == uri); ++i) { - if (value == i->second) { - return true; - } - } - return false; -} - -const Atom& -Resource::set_property(const URI& uri, const Atom& value) const -{ - return const_cast<Resource*>(this)->set_property(uri, value); -} - -const Atom& -Resource::get_property(const URI& uri) const -{ - static const Atom nil; - Properties::const_iterator i = _properties.find(uri); - return (i != _properties.end()) ? i->second : nil; -} - -bool -Resource::type(const URIs& uris, - const Properties& properties, - bool& graph, - bool& block, - bool& port, - bool& is_output) -{ - typedef Properties::const_iterator iterator; - const std::pair<iterator, iterator> types_range = properties.equal_range(uris.rdf_type); - - graph = block = port = is_output = false; - for (iterator i = types_range.first; i != types_range.second; ++i) { - const Atom& atom = i->second; - if (atom.type() != uris.forge.URI && atom.type() != uris.forge.URID) { - continue; // Non-URI type, ignore garbage data - } - - if (uris.ingen_Graph == atom) { - graph = true; - } else if (uris.ingen_Block == atom) { - block = true; - } else if (uris.lv2_InputPort == atom) { - port = true; - is_output = false; - } else if (uris.lv2_OutputPort == atom) { - port = true; - is_output = true; - } - } - - if (graph && block && !port) { // => graph - block = false; - return true; - } else if (port && (graph || block)) { // nonsense - port = false; - return false; - } else if (graph || block || port) { // recognized type - return true; - } else { // unknown - return false; - } -} - -void -Resource::set_properties(const Properties& props) -{ - /* Note a simple loop that calls set_property is inappropriate here since - it will not correctly set multiple properties in p (notably rdf:type) - */ - - // Erase existing properties with matching keys - for (const auto& p : props) { - _properties.erase(p.first); - on_property_removed(p.first, _uris.patch_wildcard.urid); - } - - // Set new properties - add_properties(props); -} - -void -Resource::add_properties(const Properties& props) -{ - for (const auto& p : props) { - add_property(p.first, p.second, p.second.context()); - } -} - -void -Resource::remove_properties(const Properties& props) -{ - for (const auto& p : props) { - remove_property(p.first, p.second); - } -} - -Properties -Resource::properties(Resource::Graph ctx) const -{ - Properties props; - for (const auto& p : _properties) { - if (p.second.context() == ctx) { - props.emplace(p.first, p.second); - } - } - - return props; -} - -} // namespace Ingen diff --git a/src/Serialiser.cpp b/src/Serialiser.cpp deleted file mode 100644 index 034ff96b..00000000 --- a/src/Serialiser.cpp +++ /dev/null @@ -1,583 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <cerrno> -#include <cstdlib> -#include <cstring> -#include <set> -#include <string> -#include <utility> - -#include "ingen/Arc.hpp" -#include "ingen/FilePath.hpp" -#include "ingen/Forge.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/Node.hpp" -#include "ingen/Resource.hpp" -#include "ingen/Serialiser.hpp" -#include "ingen/Store.hpp" -#include "ingen/URI.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" -#include "ingen/filesystem.hpp" -#include "ingen/runtime_paths.hpp" -#include "lv2/lv2plug.in/ns/ext/state/state.h" -#include "lv2/lv2plug.in/ns/extensions/ui/ui.h" -#include "raul/Path.hpp" -#include "sord/sordmm.hpp" -#include "sratom/sratom.h" - -namespace Ingen { - -struct Serialiser::Impl { - explicit Impl(World& world) - : _root_path("/") - , _world(world) - , _model(nullptr) - , _sratom(sratom_new(&_world.uri_map().urid_map_feature()->urid_map)) - {} - - ~Impl() { - sratom_free(_sratom); - } - - enum class Mode { TO_FILE, TO_STRING }; - - void start_to_file(const Raul::Path& root, - const FilePath& filename); - - std::set<const Resource*> serialise_graph(SPtr<const Node> graph, - const Sord::Node& graph_id); - - void serialise_block(SPtr<const Node> block, - const Sord::Node& class_id, - const Sord::Node& block_id); - - void serialise_port(const Node* port, - Resource::Graph context, - const Sord::Node& port_id); - - void serialise_properties(Sord::Node id, - const Properties& props); - - void write_bundle(SPtr<const Node> graph, const URI& uri); - - Sord::Node path_rdf_node(const Raul::Path& path); - - void write_manifest(const FilePath& bundle_path, - SPtr<const Node> graph); - - void write_plugins(const FilePath& bundle_path, - const std::set<const Resource*> plugins); - - void serialise_arc(const Sord::Node& parent, - SPtr<const Arc> arc); - - std::string finish(); - - Raul::Path _root_path; - Mode _mode; - URI _base_uri; - FilePath _basename; - World& _world; - Sord::Model* _model; - Sratom* _sratom; -}; - -Serialiser::Serialiser(World& world) - : me(new Impl(world)) -{} - -Serialiser::~Serialiser() -{ - delete me; -} - -void -Serialiser::Impl::write_manifest(const FilePath& bundle_path, - SPtr<const Node> graph) -{ - const FilePath manifest_path(bundle_path / "manifest.ttl"); - const FilePath binary_path(ingen_module_path("lv2")); - - start_to_file(Raul::Path("/"), manifest_path); - - Sord::World& world = _model->world(); - const URIs& uris = _world.uris(); - - const std::string filename("main.ttl"); - const Sord::URI subject(world, filename, _base_uri); - - _model->add_statement(subject, - Sord::URI(world, uris.rdf_type), - Sord::URI(world, uris.ingen_Graph)); - _model->add_statement(subject, - Sord::URI(world, uris.rdf_type), - Sord::URI(world, uris.lv2_Plugin)); - _model->add_statement(subject, - Sord::URI(world, uris.rdfs_seeAlso), - Sord::URI(world, filename, _base_uri)); - _model->add_statement(subject, - Sord::URI(world, uris.lv2_prototype), - Sord::URI(world, uris.ingen_GraphPrototype)); - - finish(); -} - -void -Serialiser::Impl::write_plugins(const FilePath& bundle_path, - const std::set<const Resource*> plugins) -{ - const FilePath plugins_path(bundle_path / "plugins.ttl"); - - start_to_file(Raul::Path("/"), plugins_path); - - Sord::World& world = _model->world(); - const URIs& uris = _world.uris(); - - for (const auto& p : plugins) { - const Atom& minor = p->get_property(uris.lv2_minorVersion); - const Atom& micro = p->get_property(uris.lv2_microVersion); - - _model->add_statement(Sord::URI(world, p->uri()), - Sord::URI(world, uris.rdf_type), - Sord::URI(world, uris.lv2_Plugin)); - - if (minor.is_valid() && micro.is_valid()) { - _model->add_statement(Sord::URI(world, p->uri()), - Sord::URI(world, uris.lv2_minorVersion), - Sord::Literal::integer(world, minor.get<int32_t>())); - _model->add_statement(Sord::URI(world, p->uri()), - Sord::URI(world, uris.lv2_microVersion), - Sord::Literal::integer(world, micro.get<int32_t>())); - } - } - - finish(); -} - -void -Serialiser::write_bundle(SPtr<const Node> graph, const URI& uri) -{ - me->write_bundle(graph, uri); -} - -void -Serialiser::Impl::write_bundle(SPtr<const Node> graph, const URI& uri) -{ - FilePath path(uri.path()); - if (filesystem::exists(path) && !filesystem::is_directory(path)) { - path = path.parent_path(); - } - - _world.log().info(fmt("Writing bundle %1%\n") % path); - filesystem::create_directories(path); - - const FilePath main_file = path / "main.ttl"; - const Raul::Path old_root_path = _root_path; - - start_to_file(graph->path(), main_file); - - std::set<const Resource*> plugins = serialise_graph( - graph, - Sord::URI(_model->world(), main_file, _base_uri)); - - finish(); - write_manifest(path, graph); - write_plugins(path, plugins); - - _root_path = old_root_path; -} - -/** Begin a serialization to a file. - * - * This must be called before any serializing methods. - */ -void -Serialiser::Impl::start_to_file(const Raul::Path& root, - const FilePath& filename) -{ - _base_uri = URI(filename); - _basename = filename.stem(); - if (_basename == "main") { - _basename = filename.parent_path().stem(); - } - - _model = new Sord::Model(*_world.rdf_world(), _base_uri); - _mode = Mode::TO_FILE; - _root_path = root; -} - -void -Serialiser::start_to_string(const Raul::Path& root, const URI& base_uri) -{ - me->_root_path = root; - me->_base_uri = base_uri; - me->_model = new Sord::Model(*me->_world.rdf_world(), base_uri); - me->_mode = Impl::Mode::TO_STRING; -} - -void -Serialiser::start_to_file(const Raul::Path& root, const std::string& filename) -{ - me->start_to_file(root, filename); -} - -std::string -Serialiser::finish() -{ - return me->finish(); -} - -std::string -Serialiser::Impl::finish() -{ - std::string ret = ""; - if (_mode == Mode::TO_FILE) { - SerdStatus st = _model->write_to_file(_base_uri, SERD_TURTLE); - if (st) { - _world.log().error(fmt("Error writing file %1% (%2%)\n") - % _base_uri % serd_strerror(st)); - } - } else { - ret = _model->write_to_string(_base_uri, SERD_TURTLE); - } - - delete _model; - _model = nullptr; - _base_uri = URI(); - - return ret; -} - -Sord::Node -Serialiser::Impl::path_rdf_node(const Raul::Path& path) -{ - assert(_model); - assert(path == _root_path || path.is_child_of(_root_path)); - return Sord::URI(_model->world(), - path.substr(_root_path.base().length()), - _base_uri); -} - -void -Serialiser::serialise(SPtr<const Node> object) -{ - if (!me->_model) { - throw std::logic_error("serialise called without serialisation in progress"); - } - - if (object->graph_type() == Node::GraphType::GRAPH) { - me->serialise_graph(object, me->path_rdf_node(object->path())); - } else if (object->graph_type() == Node::GraphType::BLOCK) { - const Sord::URI plugin_id(me->_model->world(), object->plugin()->uri()); - me->serialise_block(object, plugin_id, me->path_rdf_node(object->path())); - } else if (object->graph_type() == Node::GraphType::PORT) { - me->serialise_port(object.get(), - Resource::Graph::DEFAULT, - me->path_rdf_node(object->path())); - } else { - me->serialise_properties(me->path_rdf_node(object->path()), - object->properties()); - } -} - -std::set<const Resource*> -Serialiser::Impl::serialise_graph(SPtr<const Node> graph, - const Sord::Node& graph_id) -{ - Sord::World& world = _model->world(); - const URIs& uris = _world.uris(); - - _model->add_statement(graph_id, - Sord::URI(world, uris.rdf_type), - Sord::URI(world, uris.ingen_Graph)); - - _model->add_statement(graph_id, - Sord::URI(world, uris.rdf_type), - Sord::URI(world, uris.lv2_Plugin)); - - _model->add_statement(graph_id, - Sord::URI(world, uris.lv2_extensionData), - Sord::URI(world, LV2_STATE__interface)); - - _model->add_statement(graph_id, - Sord::URI(world, LV2_UI__ui), - Sord::URI(world, "http://drobilla.net/ns/ingen#GraphUIGtk2")); - - // If the graph has no doap:name (required by LV2), use the basename - if (graph->properties().find(uris.doap_name) == graph->properties().end()) { - _model->add_statement(graph_id, - Sord::URI(world, uris.doap_name), - Sord::Literal(world, _basename)); - } - - const Properties props = graph->properties(Resource::Graph::INTERNAL); - serialise_properties(graph_id, props); - - std::set<const Resource*> plugins; - - const Store::const_range kids = _world.store()->children_range(graph); - for (Store::const_iterator n = kids.first; n != kids.second; ++n) { - if (n->first.parent() != graph->path()) { - continue; - } - - if (n->second->graph_type() == Node::GraphType::GRAPH) { - SPtr<Node> subgraph = n->second; - - SerdURI base_uri; - serd_uri_parse((const uint8_t*)_base_uri.c_str(), &base_uri); - - const std::string sub_bundle_path = subgraph->path().substr(1) + ".ingen"; - - SerdURI subgraph_uri; - SerdNode subgraph_node = serd_node_new_uri_from_string( - (const uint8_t*)sub_bundle_path.c_str(), - &base_uri, - &subgraph_uri); - - const Sord::URI subgraph_id(world, (const char*)subgraph_node.buf); - - // Save our state - URI my_base_uri = _base_uri; - Sord::Model* my_model = _model; - - // Write child bundle within this bundle - write_bundle(subgraph, subgraph_id); - - // Restore our state - _base_uri = my_base_uri; - _model = my_model; - - // Serialise reference to graph block - const Sord::Node block_id(path_rdf_node(subgraph->path())); - _model->add_statement(graph_id, - Sord::URI(world, uris.ingen_block), - block_id); - serialise_block(subgraph, subgraph_id, block_id); - } else if (n->second->graph_type() == Node::GraphType::BLOCK) { - SPtr<const Node> block = n->second; - - const Sord::URI class_id(world, block->plugin()->uri()); - const Sord::Node block_id(path_rdf_node(n->second->path())); - _model->add_statement(graph_id, - Sord::URI(world, uris.ingen_block), - block_id); - serialise_block(block, class_id, block_id); - - plugins.insert(block->plugin()); - } - } - - for (uint32_t i = 0; i < graph->num_ports(); ++i) { - Node* p = graph->port(i); - const Sord::Node port_id = path_rdf_node(p->path()); - - // Ensure lv2:name always exists so Graph is a valid LV2 plugin - if (p->properties().find(uris.lv2_name) == p->properties().end()) { - p->set_property(uris.lv2_name, - _world.forge().alloc(p->symbol().c_str())); - } - - _model->add_statement(graph_id, - Sord::URI(world, LV2_CORE__port), - port_id); - serialise_port(p, Resource::Graph::DEFAULT, port_id); - serialise_port(p, Resource::Graph::INTERNAL, port_id); - } - - for (const auto& a : graph->arcs()) { - serialise_arc(graph_id, a.second); - } - - return plugins; -} - -void -Serialiser::Impl::serialise_block(SPtr<const Node> block, - const Sord::Node& class_id, - const Sord::Node& block_id) -{ - const URIs& uris = _world.uris(); - - _model->add_statement(block_id, - Sord::URI(_model->world(), uris.rdf_type), - Sord::URI(_model->world(), uris.ingen_Block)); - _model->add_statement(block_id, - Sord::URI(_model->world(), uris.lv2_prototype), - class_id); - - // Serialise properties, but remove possibly stale state:state (set again below) - Properties props = block->properties(); - props.erase(uris.state_state); - serialise_properties(block_id, props); - - if (_base_uri.scheme() == "file") { - const FilePath base_path = _base_uri.file_path(); - const FilePath graph_dir = base_path.parent_path(); - const FilePath state_dir = graph_dir / block->symbol(); - const FilePath state_file = state_dir / "state.ttl"; - if (block->save_state(state_dir)) { - _model->add_statement(block_id, - Sord::URI(_model->world(), uris.state_state), - Sord::URI(_model->world(), URI(state_file))); - } - } - - for (uint32_t i = 0; i < block->num_ports(); ++i) { - Node* const p = block->port(i); - const Sord::Node port_id = path_rdf_node(p->path()); - serialise_port(p, Resource::Graph::DEFAULT, port_id); - _model->add_statement(block_id, - Sord::URI(_model->world(), uris.lv2_port), - port_id); - } -} - -void -Serialiser::Impl::serialise_port(const Node* port, - Resource::Graph context, - const Sord::Node& port_id) -{ - URIs& uris = _world.uris(); - Sord::World& world = _model->world(); - Properties props = port->properties(context); - - if (context == Resource::Graph::INTERNAL) { - // Always write lv2:symbol for Graph ports (required for lv2:Plugin) - _model->add_statement(port_id, - Sord::URI(world, uris.lv2_symbol), - Sord::Literal(world, port->path().symbol())); - } else if (context == Resource::Graph::EXTERNAL) { - // Never write lv2:index for plugin instances (not persistent/stable) - props.erase(uris.lv2_index); - } - - if (context == Resource::Graph::INTERNAL && - port->has_property(uris.rdf_type, uris.lv2_ControlPort) && - port->has_property(uris.rdf_type, uris.lv2_InputPort)) - { - const Atom& val = port->get_property(uris.ingen_value); - if (val.is_valid()) { - props.erase(uris.lv2_default); - props.emplace(uris.lv2_default, val); - } else { - _world.log().warn("Control input has no value, lv2:default omitted.\n"); - } - } else if (context != Resource::Graph::INTERNAL && - !port->has_property(uris.rdf_type, uris.lv2_InputPort)) { - props.erase(uris.ingen_value); - } - - serialise_properties(port_id, props); -} - -void -Serialiser::serialise_arc(const Sord::Node& parent, - SPtr<const Arc> arc) -{ - return me->serialise_arc(parent, arc); -} - -void -Serialiser::Impl::serialise_arc(const Sord::Node& parent, - SPtr<const Arc> arc) -{ - if (!_model) { - throw std::logic_error( - "serialise_arc called without serialisation in progress"); - } - - Sord::World& world = _model->world(); - const URIs& uris = _world.uris(); - - const Sord::Node src = path_rdf_node(arc->tail_path()); - const Sord::Node dst = path_rdf_node(arc->head_path()); - const Sord::Node arc_id = Sord::Node::blank_id(*_world.rdf_world()); - _model->add_statement(arc_id, - Sord::URI(world, uris.ingen_tail), - src); - _model->add_statement(arc_id, - Sord::URI(world, uris.ingen_head), - dst); - - if (parent.is_valid()) { - _model->add_statement(parent, - Sord::URI(world, uris.ingen_arc), - arc_id); - } else { - _model->add_statement(arc_id, - Sord::URI(world, uris.rdf_type), - Sord::URI(world, uris.ingen_Arc)); - } -} - -static bool -skip_property(Ingen::URIs& uris, const Sord::Node& predicate) -{ - return (predicate == INGEN__file || - predicate == uris.ingen_arc || - predicate == uris.ingen_block || - predicate == uris.lv2_port); -} - -void -Serialiser::Impl::serialise_properties(Sord::Node id, - const Properties& props) -{ - LV2_URID_Unmap* unmap = &_world.uri_map().urid_unmap_feature()->urid_unmap; - SerdNode base = serd_node_from_string(SERD_URI, - (const uint8_t*)_base_uri.c_str()); - SerdEnv* env = serd_env_new(&base); - SordInserter* inserter = sord_inserter_new(_model->c_obj(), env); - - sratom_set_sink(_sratom, _base_uri.c_str(), - (SerdStatementSink)sord_inserter_write_statement, nullptr, - inserter); - - sratom_set_pretty_numbers(_sratom, true); - - for (const auto& p : props) { - const Sord::URI key(_model->world(), p.first); - if (!skip_property(_world.uris(), key)) { - if (p.second.type() == _world.uris().atom_URI && - !strncmp((const char*)p.second.get_body(), "ingen:/main/", 13)) { - /* Value is a graph URI relative to the running engine. - Chop the prefix and save the path relative to the graph file. - This allows saving references to bundle resources. */ - sratom_write(_sratom, unmap, 0, - sord_node_to_serd_node(id.c_obj()), - sord_node_to_serd_node(key.c_obj()), - p.second.type(), p.second.size(), - (const char*)p.second.get_body() + 13); - } else { - sratom_write(_sratom, unmap, 0, - sord_node_to_serd_node(id.c_obj()), - sord_node_to_serd_node(key.c_obj()), - p.second.type(), p.second.size(), p.second.get_body()); - } - } - } - - sord_inserter_free(inserter); - serd_env_free(env); -} - -} // namespace Ingen diff --git a/src/SocketReader.cpp b/src/SocketReader.cpp deleted file mode 100644 index 13e95430..00000000 --- a/src/SocketReader.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cerrno> - -#include <poll.h> - -#include "ingen/AtomForgeSink.hpp" -#include "ingen/AtomReader.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/SocketReader.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/World.hpp" -#include "raul/Socket.hpp" -#include "sord/sordmm.hpp" -#include "sratom/sratom.h" - -namespace Ingen { - -SocketReader::SocketReader(Ingen::World& world, - Interface& iface, - SPtr<Raul::Socket> sock) - : _world(world) - , _iface(iface) - , _inserter(nullptr) - , _msg_node(nullptr) - , _socket(std::move(sock)) - , _exit_flag(false) - , _thread(&SocketReader::run, this) -{} - -SocketReader::~SocketReader() -{ - _exit_flag = true; - _socket->shutdown(); - _thread.join(); -} - -SerdStatus -SocketReader::set_base_uri(SocketReader* iface, - const SerdNode* uri_node) -{ - return sord_inserter_set_base_uri(iface->_inserter, uri_node); -} - -SerdStatus -SocketReader::set_prefix(SocketReader* iface, - const SerdNode* name, - const SerdNode* uri_node) -{ - return sord_inserter_set_prefix(iface->_inserter, name, uri_node); -} - -SerdStatus -SocketReader::write_statement(SocketReader* iface, - SerdStatementFlags flags, - const SerdNode* graph, - const SerdNode* subject, - const SerdNode* predicate, - const SerdNode* object, - const SerdNode* object_datatype, - const SerdNode* object_lang) -{ - if (!iface->_msg_node) { - iface->_msg_node = sord_node_from_serd_node( - iface->_world.rdf_world()->c_obj(), iface->_env, subject, nullptr, nullptr); - } - - return sord_inserter_write_statement( - iface->_inserter, flags, graph, - subject, predicate, object, - object_datatype, object_lang); -} - -void -SocketReader::run() -{ - Sord::World* world = _world.rdf_world(); - LV2_URID_Map* map = &_world.uri_map().urid_map_feature()->urid_map; - - // Open socket as a FILE for reading directly with serd - FILE* f = fdopen(_socket->fd(), "r"); - if (!f) { - _world.log().error(fmt("Failed to open connection (%1%)\n") - % strerror(errno)); - // Connection gone, exit - _socket.reset(); - return; - } - - // Set up sratom and a forge to build LV2 atoms from model - Sratom* sratom = sratom_new(map); - LV2_Atom_Forge forge; - lv2_atom_forge_init(&forge, map); - - AtomForgeSink buffer(&forge); - - SordNode* base_uri = nullptr; - SordModel* model = nullptr; - { - // Lock RDF world - std::lock_guard<std::mutex> lock(_world.rdf_mutex()); - - // Use <ingen:/> as base URI, so relative URIs are like bundle paths - base_uri = sord_new_uri(world->c_obj(), (const uint8_t*)"ingen:/"); - - // Make a model and reader to parse the next Turtle message - _env = world->prefixes().c_obj(); - model = sord_new(world->c_obj(), SORD_SPO, false); - - // Create an inserter for writing incoming triples to model - _inserter = sord_inserter_new(model, _env); - } - - SerdReader* reader = serd_reader_new( - SERD_TURTLE, this, nullptr, - (SerdBaseSink)set_base_uri, - (SerdPrefixSink)set_prefix, - (SerdStatementSink)write_statement, - nullptr); - - serd_env_set_base_uri(_env, sord_node_to_serd_node(base_uri)); - serd_reader_start_stream(reader, f, (const uint8_t*)"(socket)", false); - - // Make an AtomReader to call Ingen Interface methods based on Atom - AtomReader ar(_world.uri_map(), _world.uris(), _world.log(), _iface); - - struct pollfd pfd; - pfd.fd = _socket->fd(); - pfd.events = POLLIN; - pfd.revents = 0; - - while (!_exit_flag) { - if (feof(f)) { - break; // Lost connection - } - - // Wait for input to arrive at socket - int ret = poll(&pfd, 1, -1); - if (ret == -1 || (pfd.revents & (POLLERR|POLLHUP|POLLNVAL))) { - on_hangup(); - break; // Hangup - } else if (!ret) { - continue; // No data, shouldn't happen - } - - // Lock RDF world - std::lock_guard<std::mutex> lock(_world.rdf_mutex()); - - // Read until the next '.' - SerdStatus st = serd_reader_read_chunk(reader); - if (st == SERD_FAILURE || !_msg_node) { - continue; // Read nothing, e.g. just whitespace - } else if (st) { - _world.log().error(fmt("Read error: %1%\n") - % serd_strerror(st)); - continue; - } - - // Build an LV2_Atom at chunk.buf from the message - sratom_read(sratom, &forge, world->c_obj(), model, _msg_node); - - // Call _iface methods based on atom content - ar.write(buffer.atom()); - - // Reset everything for the next iteration - buffer.clear(); - sord_node_free(world->c_obj(), _msg_node); - _msg_node = nullptr; - } - - // Lock RDF world - std::lock_guard<std::mutex> lock(_world.rdf_mutex()); - - // Destroy everything - fclose(f); - sord_inserter_free(_inserter); - serd_reader_end_stream(reader); - sratom_free(sratom); - serd_reader_free(reader); - sord_free(model); - _socket.reset(); -} - -} // namespace Ingen diff --git a/src/SocketWriter.cpp b/src/SocketWriter.cpp deleted file mode 100644 index 68091bcc..00000000 --- a/src/SocketWriter.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2012-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <errno.h> -#include <sys/types.h> -#include <sys/socket.h> - -#include "ingen/SocketWriter.hpp" -#include "raul/Socket.hpp" - -#ifndef MSG_NOSIGNAL -# define MSG_NOSIGNAL 0 -#endif - -namespace Ingen { - -SocketWriter::SocketWriter(URIMap& map, - URIs& uris, - const URI& uri, - SPtr<Raul::Socket> sock) - : TurtleWriter(map, uris, uri) - , _socket(std::move(sock)) -{} - -size_t -SocketWriter::text_sink(const void* buf, size_t len) -{ - ssize_t ret = send(_socket->fd(), buf, len, MSG_NOSIGNAL); - if (ret < 0) { - return 0; - } - return ret; -} - -void -SocketWriter::bundle_end() -{ - TurtleWriter::bundle_end(); - - // Send a NULL byte to indicate end of bundle - const char end[] = { 0 }; - send(_socket->fd(), end, 1, MSG_NOSIGNAL); -} - -} // namespace Ingen diff --git a/src/Store.cpp b/src/Store.cpp deleted file mode 100644 index 327ce416..00000000 --- a/src/Store.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <sstream> - -#include "ingen/Node.hpp" -#include "ingen/Store.hpp" - -namespace Ingen { - -void -Store::add(Node* o) -{ - if (find(o->path()) != end()) { - return; - } - - emplace(o->path(), SPtr<Node>(o)); - - for (uint32_t i = 0; i < o->num_ports(); ++i) { - add(o->port(i)); - } -} - -/* - TODO: These methods are currently O(n_children) but should logarithmic. The - std::map methods do not allow passing a comparator, but std::upper_bound - does. This should be achievable by making a rooted comparator that is a - normal ordering except compares a special sentinel value as the greatest - element that is a child of the parent. Searching for this sentinel should - then find the end of the descendants in logarithmic time. -*/ - -Store::iterator -Store::find_descendants_end(const iterator parent) -{ - iterator descendants_end = parent; - ++descendants_end; - while (descendants_end != end() && - descendants_end->first.is_child_of(parent->first)) { - ++descendants_end; - } - - return descendants_end; -} - -Store::const_iterator -Store::find_descendants_end(const const_iterator parent) const -{ - const_iterator descendants_end = parent; - ++descendants_end; - while (descendants_end != end() && - descendants_end->first.is_child_of(parent->first)) { - ++descendants_end; - } - - return descendants_end; -} - -Store::const_range -Store::children_range(SPtr<const Node> o) const -{ - const const_iterator parent = find(o->path()); - if (parent != end()) { - const_iterator first_child = parent; - ++first_child; - return std::make_pair(first_child, find_descendants_end(parent)); - } - return make_pair(end(), end()); -} - -void -Store::remove(const iterator top, Objects& removed) -{ - if (top != end()) { - const iterator descendants_end = find_descendants_end(top); - removed.insert(top, descendants_end); - erase(top, descendants_end); - } -} - -void -Store::rename(const iterator top, const Raul::Path& new_path) -{ - const Raul::Path old_path = top->first; - - // Remove the object and all its descendants - Objects removed; - remove(top, removed); - - // Rename all the removed objects - for (Objects::const_iterator i = removed.begin(); i != removed.end(); ++i) { - const Raul::Path path = (i->first == old_path) - ? new_path - : new_path.child( - Raul::Path(i->first.substr(old_path.base().length() - 1))); - - i->second->set_path(path); - assert(find(path) == end()); // Shouldn't be dropping objects! - emplace(path, i->second); - } -} - -unsigned -Store::child_name_offset(const Raul::Path& parent, - const Raul::Symbol& symbol, - bool allow_zero) const -{ - unsigned offset = 0; - - while (true) { - std::stringstream ss; - ss << symbol; - if (offset > 0) { - ss << "_" << offset; - } - if (find(parent.child(Raul::Symbol(ss.str()))) == end() && - (allow_zero || offset > 0)) { - break; - } else if (offset == 0) { - offset = 2; - } else { - ++offset; - } - } - - return offset; -} - -} // namespace Ingen diff --git a/src/StreamWriter.cpp b/src/StreamWriter.cpp deleted file mode 100644 index 45853055..00000000 --- a/src/StreamWriter.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2012-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/ColorContext.hpp" -#include "ingen/StreamWriter.hpp" - -namespace Ingen { - -StreamWriter::StreamWriter(URIMap& map, - URIs& uris, - const URI& uri, - FILE* stream, - ColorContext::Color color) - : TurtleWriter(map, uris, uri) - , _stream(stream) - , _color(color) -{} - -size_t -StreamWriter::text_sink(const void* buf, size_t len) -{ - ColorContext ctx(_stream, _color); - return fwrite(buf, 1, len, _stream); -} - -} // namespace Ingen diff --git a/src/TurtleWriter.cpp b/src/TurtleWriter.cpp deleted file mode 100644 index 368184d4..00000000 --- a/src/TurtleWriter.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2012-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/TurtleWriter.hpp" -#include "ingen/URIMap.hpp" - -#define USTR(s) ((const uint8_t*)(s)) - -namespace Ingen { - -static size_t -c_text_sink(const void* buf, size_t len, void* stream) -{ - TurtleWriter* writer = (TurtleWriter*)stream; - return writer->text_sink(buf, len); -} - -static SerdStatus -write_prefix(void* handle, const SerdNode* name, const SerdNode* uri) -{ - serd_writer_set_prefix((SerdWriter*)handle, name, uri); - return SERD_SUCCESS; -} - -TurtleWriter::TurtleWriter(URIMap& map, URIs& uris, const URI& uri) - : AtomWriter(map, uris, *this) - , _map(map) - , _sratom(sratom_new(&map.urid_map_feature()->urid_map)) - , _uri(uri) - , _wrote_prefixes(false) -{ - // Use <ingen:/> as base URI, so relative URIs are like bundle paths - _base = serd_node_from_string(SERD_URI, (const uint8_t*)"ingen:/"); - serd_uri_parse(_base.buf, &_base_uri); - - // Set up serialisation environment - _env = serd_env_new(&_base); - serd_env_set_prefix_from_strings(_env, USTR("atom"), USTR("http://lv2plug.in/ns/ext/atom#")); - serd_env_set_prefix_from_strings(_env, USTR("doap"), USTR("http://usefulinc.com/ns/doap#")); - serd_env_set_prefix_from_strings(_env, USTR("ingen"), USTR(INGEN_NS)); - serd_env_set_prefix_from_strings(_env, USTR("lv2"), USTR("http://lv2plug.in/ns/lv2core#")); - serd_env_set_prefix_from_strings(_env, USTR("midi"), USTR("http://lv2plug.in/ns/ext/midi#")); - serd_env_set_prefix_from_strings(_env, USTR("owl"), USTR("http://www.w3.org/2002/07/owl#")); - serd_env_set_prefix_from_strings(_env, USTR("patch"), USTR("http://lv2plug.in/ns/ext/patch#")); - serd_env_set_prefix_from_strings(_env, USTR("rdf"), USTR("http://www.w3.org/1999/02/22-rdf-syntax-ns#")); - serd_env_set_prefix_from_strings(_env, USTR("rdfs"), USTR("http://www.w3.org/2000/01/rdf-schema#")); - serd_env_set_prefix_from_strings(_env, USTR("xsd"), USTR("http://www.w3.org/2001/XMLSchema#")); - - // Make a Turtle writer that writes to text_sink - _writer = serd_writer_new( - SERD_TURTLE, - (SerdStyle)(SERD_STYLE_RESOLVED|SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED), - _env, - &_base_uri, - c_text_sink, - this); - - // Configure sratom to write directly to the writer (and thus text_sink) - sratom_set_sink(_sratom, - (const char*)_base.buf, - (SerdStatementSink)serd_writer_write_statement, - (SerdEndSink)serd_writer_end_anon, - _writer); -} - -TurtleWriter::~TurtleWriter() -{ - sratom_free(_sratom); - serd_writer_free(_writer); - serd_env_free(_env); -} - -bool -TurtleWriter::write(const LV2_Atom* msg, int32_t default_id) -{ - if (!_wrote_prefixes) { - // Write namespace prefixes once to reduce traffic - serd_env_foreach(_env, write_prefix, _writer); - _wrote_prefixes = true; - } - - sratom_write(_sratom, &_map.urid_unmap_feature()->urid_unmap, 0, - nullptr, nullptr, msg->type, msg->size, LV2_ATOM_BODY_CONST(msg)); - serd_writer_finish(_writer); - return true; -} - -} // namespace Ingen diff --git a/src/URI.cpp b/src/URI.cpp deleted file mode 100644 index 3e2d2a29..00000000 --- a/src/URI.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2018 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> - -#include "ingen/FilePath.hpp" -#include "ingen/URI.hpp" - -namespace Ingen { - -URI::URI() - : _node(SERD_NODE_NULL) - , _uri(SERD_URI_NULL) -{} - -URI::URI(const std::string& str) - : _node(serd_node_new_uri_from_string((const uint8_t*)str.c_str(), - NULL, - &_uri)) -{} - -URI::URI(const char* str) - : _node(serd_node_new_uri_from_string((const uint8_t*)str, NULL, &_uri)) -{} - -URI::URI(const std::string& str, const URI& base) - : _node(serd_node_new_uri_from_string((const uint8_t*)str.c_str(), - &base._uri, - &_uri)) -{} - -URI::URI(SerdNode node) - : _node(serd_node_new_uri_from_node(&node, NULL, &_uri)) -{ - assert(node.type == SERD_URI); -} - -URI::URI(SerdNode node, SerdURI uri) - : _node(node) - , _uri(uri) -{ - assert(node.type == SERD_URI); -} - -URI::URI(const Sord::Node& node) - : URI(*node.to_serd_node()) -{ -} - -URI::URI(const FilePath& path) - : _node(serd_node_new_file_uri((const uint8_t*)path.c_str(), - NULL, - &_uri, - true)) -{} - -URI::URI(const URI& uri) - : _node(serd_node_new_uri(&uri._uri, NULL, &_uri)) -{} - -URI& -URI::operator=(const URI& uri) -{ - serd_node_free(&_node); - _node = serd_node_new_uri(&uri._uri, NULL, &_uri); - return *this; -} - -URI::URI(URI&& uri) - : _node(uri._node) - , _uri(uri._uri) -{ - uri._node = SERD_NODE_NULL; - uri._uri = SERD_URI_NULL; -} - -URI& -URI::operator=(URI&& uri) -{ - _node = uri._node; - _uri = uri._uri; - uri._node = SERD_NODE_NULL; - uri._uri = SERD_URI_NULL; - return *this; -} - -URI::~URI() -{ - serd_node_free(&_node); -} - -URI -URI::make_relative(const URI& base) const -{ - SerdURI uri; - SerdNode node = serd_node_new_relative_uri(&_uri, &base._uri, NULL, &uri); - return URI(node, uri); -} - -} // namespace Ingen diff --git a/src/URIMap.cpp b/src/URIMap.cpp deleted file mode 100644 index 9ce1f178..00000000 --- a/src/URIMap.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cstdint> - -#include "ingen/Log.hpp" -#include "ingen/URI.hpp" -#include "ingen/URIMap.hpp" - -namespace Ingen { - -URIMap::URIMap(Log& log, LV2_URID_Map* map, LV2_URID_Unmap* unmap) - : _urid_map_feature(new URIDMapFeature(this, map, log)) - , _urid_unmap_feature(new URIDUnmapFeature(this, unmap)) -{ -} - -URIMap::URIDMapFeature::URIDMapFeature(URIMap* map, - LV2_URID_Map* impl, - Log& log) - : Feature(LV2_URID__map, &urid_map) - , log(log) -{ - if (impl) { - urid_map = *impl; - } else { - urid_map.map = default_map; - urid_map.handle = map; - } -} - -LV2_URID -URIMap::URIDMapFeature::default_map(LV2_URID_Map_Handle h, - const char* c_uri) -{ - URIMap* const map((URIMap*)h); - std::string uri(c_uri); - std::lock_guard<std::mutex> lock(map->_mutex); - - auto record = map->_map.emplace(uri, map->_map.size() + 1); - const auto id = record.first->second; - if (record.second) { - assert(id == map->_map.size()); - assert(id == map->_unmap.size() + 1); - map->_unmap.emplace_back(std::move(uri)); - } - return id; -} - -LV2_URID -URIMap::URIDMapFeature::map(const char* uri) -{ - if (!URI::is_valid(uri)) { - log.error(fmt("Attempt to map invalid URI <%1%>\n") % uri); - return 0; - } - return urid_map.map(urid_map.handle, uri); -} - -URIMap::URIDUnmapFeature::URIDUnmapFeature(URIMap* map, - LV2_URID_Unmap* impl) - : Feature(LV2_URID__unmap, &urid_unmap) -{ - if (impl) { - urid_unmap = *impl; - } else { - urid_unmap.unmap = default_unmap; - urid_unmap.handle = map; - } -} - -const char* -URIMap::URIDUnmapFeature::default_unmap(LV2_URID_Unmap_Handle h, - LV2_URID urid) -{ - URIMap* const map((URIMap*)h); - std::lock_guard<std::mutex> lock(map->_mutex); - - return (urid > 0 && urid <= map->_unmap.size() - ? map->_unmap[urid - 1].c_str() - : NULL); -} - -const char* -URIMap::URIDUnmapFeature::unmap(LV2_URID urid) -{ - return urid_unmap.unmap(urid_unmap.handle, urid); -} - -uint32_t -URIMap::map_uri(const char* uri) -{ - const uint32_t urid = _urid_map_feature->map(uri); -#ifdef INGEN_DEBUG_URIDS - fprintf(stderr, "Map URI %3u <= %s\n", urid, uri); -#endif - return urid; -} - -const char* -URIMap::unmap_uri(uint32_t urid) const -{ - const char* uri = _urid_unmap_feature->unmap(urid); -#ifdef INGEN_DEBUG_URIDS - fprintf(stderr, "Unmap URI %3u => %s\n", urid, uri); -#endif - return uri; -} - -} // namespace Ingen diff --git a/src/URIs.cpp b/src/URIs.cpp deleted file mode 100644 index af03b7b5..00000000 --- a/src/URIs.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Forge.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/URIs.hpp" -#include "ingen/ingen.h" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h" -#include "lv2/lv2plug.in/ns/ext/log/log.h" -#include "lv2/lv2plug.in/ns/ext/midi/midi.h" -#include "lv2/lv2plug.in/ns/ext/morph/morph.h" -#include "lv2/lv2plug.in/ns/ext/options/options.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/port-props/port-props.h" -#include "lv2/lv2plug.in/ns/ext/presets/presets.h" -#include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h" -#include "lv2/lv2plug.in/ns/ext/state/state.h" -#include "lv2/lv2plug.in/ns/ext/time/time.h" -#include "lv2/lv2plug.in/ns/ext/worker/worker.h" -#include "lv2/lv2plug.in/ns/lv2core/lv2.h" - -namespace Ingen { - -URIs::Quark::Quark(Forge& forge, - URIMap* map, - LilvWorld* lworld, - const char* str) - : URI(str) - , urid(forge.make_urid(URI(str))) - , uri(forge.alloc_uri(str)) - , lnode(lilv_new_uri(lworld, str)) -{} - -URIs::Quark::Quark(const Quark& copy) - : URI(copy) - , urid(copy.urid) - , uri(copy.uri) - , lnode(lilv_node_duplicate(copy.lnode)) -{} - -URIs::Quark::~Quark() -{ - lilv_node_free(lnode); -} - -#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" -#define NS_RDFS "http://www.w3.org/2000/01/rdf-schema#" - -URIs::URIs(Forge& forge, URIMap* map, LilvWorld* lworld) - : forge(forge) - , atom_AtomPort (forge, map, lworld, LV2_ATOM__AtomPort) - , atom_Bool (forge, map, lworld, LV2_ATOM__Bool) - , atom_Chunk (forge, map, lworld, LV2_ATOM__Chunk) - , atom_Float (forge, map, lworld, LV2_ATOM__Float) - , atom_Int (forge, map, lworld, LV2_ATOM__Int) - , atom_Object (forge, map, lworld, LV2_ATOM__Object) - , atom_Path (forge, map, lworld, LV2_ATOM__Path) - , atom_Sequence (forge, map, lworld, LV2_ATOM__Sequence) - , atom_Sound (forge, map, lworld, LV2_ATOM__Sound) - , atom_String (forge, map, lworld, LV2_ATOM__String) - , atom_URI (forge, map, lworld, LV2_ATOM__URI) - , atom_URID (forge, map, lworld, LV2_ATOM__URID) - , atom_bufferType (forge, map, lworld, LV2_ATOM__bufferType) - , atom_eventTransfer (forge, map, lworld, LV2_ATOM__eventTransfer) - , atom_supports (forge, map, lworld, LV2_ATOM__supports) - , bufsz_maxBlockLength (forge, map, lworld, LV2_BUF_SIZE__maxBlockLength) - , bufsz_minBlockLength (forge, map, lworld, LV2_BUF_SIZE__minBlockLength) - , bufsz_sequenceSize (forge, map, lworld, LV2_BUF_SIZE__sequenceSize) - , doap_name (forge, map, lworld, "http://usefulinc.com/ns/doap#name") - , ingen_Arc (forge, map, lworld, INGEN__Arc) - , ingen_Block (forge, map, lworld, INGEN__Block) - , ingen_BundleEnd (forge, map, lworld, INGEN__BundleEnd) - , ingen_BundleStart (forge, map, lworld, INGEN__BundleStart) - , ingen_Graph (forge, map, lworld, INGEN__Graph) - , ingen_GraphPrototype (forge, map, lworld, INGEN__GraphPrototype) - , ingen_Internal (forge, map, lworld, INGEN__Internal) - , ingen_Redo (forge, map, lworld, INGEN__Redo) - , ingen_Undo (forge, map, lworld, INGEN__Undo) - , ingen_activity (forge, map, lworld, INGEN__activity) - , ingen_arc (forge, map, lworld, INGEN__arc) - , ingen_block (forge, map, lworld, INGEN__block) - , ingen_broadcast (forge, map, lworld, INGEN__broadcast) - , ingen_canvasX (forge, map, lworld, INGEN__canvasX) - , ingen_canvasY (forge, map, lworld, INGEN__canvasY) - , ingen_enabled (forge, map, lworld, INGEN__enabled) - , ingen_externalContext (forge, map, lworld, INGEN__externalContext) - , ingen_file (forge, map, lworld, INGEN__file) - , ingen_head (forge, map, lworld, INGEN__head) - , ingen_incidentTo (forge, map, lworld, INGEN__incidentTo) - , ingen_internalContext (forge, map, lworld, INGEN__internalContext) - , ingen_loadedBundle (forge, map, lworld, INGEN__loadedBundle) - , ingen_maxRunLoad (forge, map, lworld, INGEN__maxRunLoad) - , ingen_meanRunLoad (forge, map, lworld, INGEN__meanRunLoad) - , ingen_minRunLoad (forge, map, lworld, INGEN__minRunLoad) - , ingen_numThreads (forge, map, lworld, INGEN__numThreads) - , ingen_polyphonic (forge, map, lworld, INGEN__polyphonic) - , ingen_polyphony (forge, map, lworld, INGEN__polyphony) - , ingen_prototype (forge, map, lworld, INGEN__prototype) - , ingen_sprungLayout (forge, map, lworld, INGEN__sprungLayout) - , ingen_tail (forge, map, lworld, INGEN__tail) - , ingen_uiEmbedded (forge, map, lworld, INGEN__uiEmbedded) - , ingen_value (forge, map, lworld, INGEN__value) - , log_Error (forge, map, lworld, LV2_LOG__Error) - , log_Note (forge, map, lworld, LV2_LOG__Note) - , log_Trace (forge, map, lworld, LV2_LOG__Trace) - , log_Warning (forge, map, lworld, LV2_LOG__Warning) - , lv2_AudioPort (forge, map, lworld, LV2_CORE__AudioPort) - , lv2_CVPort (forge, map, lworld, LV2_CORE__CVPort) - , lv2_ControlPort (forge, map, lworld, LV2_CORE__ControlPort) - , lv2_InputPort (forge, map, lworld, LV2_CORE__InputPort) - , lv2_OutputPort (forge, map, lworld, LV2_CORE__OutputPort) - , lv2_Plugin (forge, map, lworld, LV2_CORE__Plugin) - , lv2_appliesTo (forge, map, lworld, LV2_CORE__appliesTo) - , lv2_binary (forge, map, lworld, LV2_CORE__binary) - , lv2_connectionOptional(forge, map, lworld, LV2_CORE__connectionOptional) - , lv2_control (forge, map, lworld, LV2_CORE__control) - , lv2_default (forge, map, lworld, LV2_CORE__default) - , lv2_designation (forge, map, lworld, LV2_CORE__designation) - , lv2_enumeration (forge, map, lworld, LV2_CORE__enumeration) - , lv2_extensionData (forge, map, lworld, LV2_CORE__extensionData) - , lv2_index (forge, map, lworld, LV2_CORE__index) - , lv2_integer (forge, map, lworld, LV2_CORE__integer) - , lv2_maximum (forge, map, lworld, LV2_CORE__maximum) - , lv2_microVersion (forge, map, lworld, LV2_CORE__microVersion) - , lv2_minimum (forge, map, lworld, LV2_CORE__minimum) - , lv2_minorVersion (forge, map, lworld, LV2_CORE__minorVersion) - , lv2_name (forge, map, lworld, LV2_CORE__name) - , lv2_port (forge, map, lworld, LV2_CORE__port) - , lv2_portProperty (forge, map, lworld, LV2_CORE__portProperty) - , lv2_prototype (forge, map, lworld, LV2_CORE__prototype) - , lv2_sampleRate (forge, map, lworld, LV2_CORE__sampleRate) - , lv2_scalePoint (forge, map, lworld, LV2_CORE__scalePoint) - , lv2_symbol (forge, map, lworld, LV2_CORE__symbol) - , lv2_toggled (forge, map, lworld, LV2_CORE__toggled) - , midi_Bender (forge, map, lworld, LV2_MIDI__Bender) - , midi_ChannelPressure (forge, map, lworld, LV2_MIDI__ChannelPressure) - , midi_Controller (forge, map, lworld, LV2_MIDI__Controller) - , midi_MidiEvent (forge, map, lworld, LV2_MIDI__MidiEvent) - , midi_NoteOn (forge, map, lworld, LV2_MIDI__NoteOn) - , midi_binding (forge, map, lworld, LV2_MIDI__binding) - , midi_controllerNumber (forge, map, lworld, LV2_MIDI__controllerNumber) - , midi_noteNumber (forge, map, lworld, LV2_MIDI__noteNumber) - , morph_AutoMorphPort (forge, map, lworld, LV2_MORPH__AutoMorphPort) - , morph_MorphPort (forge, map, lworld, LV2_MORPH__MorphPort) - , morph_currentType (forge, map, lworld, LV2_MORPH__currentType) - , morph_supportsType (forge, map, lworld, LV2_MORPH__supportsType) - , opt_interface (forge, map, lworld, LV2_OPTIONS__interface) - , param_sampleRate (forge, map, lworld, LV2_PARAMETERS__sampleRate) - , patch_Copy (forge, map, lworld, LV2_PATCH__Copy) - , patch_Delete (forge, map, lworld, LV2_PATCH__Delete) - , patch_Get (forge, map, lworld, LV2_PATCH__Get) - , patch_Message (forge, map, lworld, LV2_PATCH__Message) - , patch_Move (forge, map, lworld, LV2_PATCH__Move) - , patch_Patch (forge, map, lworld, LV2_PATCH__Patch) - , patch_Put (forge, map, lworld, LV2_PATCH__Put) - , patch_Response (forge, map, lworld, LV2_PATCH__Response) - , patch_Set (forge, map, lworld, LV2_PATCH__Set) - , patch_add (forge, map, lworld, LV2_PATCH__add) - , patch_body (forge, map, lworld, LV2_PATCH__body) - , patch_context (forge, map, lworld, LV2_PATCH__context) - , patch_destination (forge, map, lworld, LV2_PATCH__destination) - , patch_property (forge, map, lworld, LV2_PATCH__property) - , patch_remove (forge, map, lworld, LV2_PATCH__remove) - , patch_sequenceNumber (forge, map, lworld, LV2_PATCH__sequenceNumber) - , patch_subject (forge, map, lworld, LV2_PATCH__subject) - , patch_value (forge, map, lworld, LV2_PATCH__value) - , patch_wildcard (forge, map, lworld, LV2_PATCH__wildcard) - , pprops_logarithmic (forge, map, lworld, LV2_PORT_PROPS__logarithmic) - , pset_Preset (forge, map, lworld, LV2_PRESETS__Preset) - , pset_preset (forge, map, lworld, LV2_PRESETS__preset) - , rdf_type (forge, map, lworld, NS_RDF "type") - , rdfs_Class (forge, map, lworld, NS_RDFS "Class") - , rdfs_label (forge, map, lworld, NS_RDFS "label") - , rdfs_seeAlso (forge, map, lworld, NS_RDFS "seeAlso") - , rsz_minimumSize (forge, map, lworld, LV2_RESIZE_PORT__minimumSize) - , state_loadDefaultState(forge, map, lworld, LV2_STATE__loadDefaultState) - , state_state (forge, map, lworld, LV2_STATE__state) - , time_Position (forge, map, lworld, LV2_TIME__Position) - , time_bar (forge, map, lworld, LV2_TIME__bar) - , time_barBeat (forge, map, lworld, LV2_TIME__barBeat) - , time_beatUnit (forge, map, lworld, LV2_TIME__beatUnit) - , time_beatsPerBar (forge, map, lworld, LV2_TIME__beatsPerBar) - , time_beatsPerMinute (forge, map, lworld, LV2_TIME__beatsPerMinute) - , time_frame (forge, map, lworld, LV2_TIME__frame) - , time_speed (forge, map, lworld, LV2_TIME__speed) - , work_schedule (forge, map, lworld, LV2_WORKER__schedule) -{} - -} // namespace Ingen diff --git a/src/World.cpp b/src/World.cpp deleted file mode 100644 index 568ab405..00000000 --- a/src/World.cpp +++ /dev/null @@ -1,355 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cstdlib> -#include <map> -#include <memory> -#include <string> -#include <utility> - -#include "ingen/Configuration.hpp" -#include "ingen/DataAccess.hpp" -#include "ingen/EngineBase.hpp" -#include "ingen/Forge.hpp" -#include "ingen/InstanceAccess.hpp" -#include "ingen/LV2Features.hpp" -#include "ingen/Log.hpp" -#include "ingen/Module.hpp" -#include "ingen/Parser.hpp" -#include "ingen/Serialiser.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" -#include "ingen/filesystem.hpp" -#include "ingen/ingen.h" -#include "ingen/runtime_paths.hpp" -#include "lilv/lilv.h" -#include "sord/sordmm.hpp" - -using std::string; - -namespace Ingen { - -class EngineBase; -class Interface; -class Parser; -class Serialiser; -class Store; - -/** Load a dynamic module from the default path. - * - * This will check in the directories specified in the environment variable - * INGEN_MODULE_PATH (typical colon delimited format), then the default module - * installation directory (ie /usr/local/lib/ingen), in that order. - * - * \param name The base name of the module, e.g. "ingen_jack" - */ -static std::unique_ptr<Library> -ingen_load_library(Log& log, const string& name) -{ - std::unique_ptr<Library> library; - - // Search INGEN_MODULE_PATH first - const char* const module_path = getenv("INGEN_MODULE_PATH"); - if (module_path) { - string dir; - std::istringstream iss(module_path); - while (getline(iss, dir, search_path_separator)) { - FilePath filename = Ingen::ingen_module_path(name, FilePath(dir)); - if (filesystem::exists(filename)) { - library = std::unique_ptr<Library>(new Library(filename)); - if (*library) { - return library; - } else { - log.error(Library::get_last_error()); - } - } - } - } - - // Try default directory if not found - library = std::unique_ptr<Library>(new Library(Ingen::ingen_module_path(name))); - - if (*library) { - return library; - } else if (!module_path) { - log.error(fmt("Unable to find %1% (%2%)\n") - % name % Library::get_last_error()); - return nullptr; - } else { - log.error(fmt("Unable to load %1% from %2% (%3%)\n") - % name % module_path % Library::get_last_error()); - return nullptr; - } -} - -class World::Impl { -public: - Impl(LV2_URID_Map* map, - LV2_URID_Unmap* unmap, - LV2_Log_Log* lv2_log) - : argc(nullptr) - , argv(nullptr) - , lv2_features(nullptr) - , rdf_world(new Sord::World()) - , lilv_world(lilv_world_new()) - , uri_map(new URIMap(log, map, unmap)) - , forge(new Forge(*uri_map)) - , uris(new URIs(*forge, uri_map, lilv_world)) - , conf(*forge) - , log(lv2_log, *uris) - { - lv2_features = new LV2Features(); - lv2_features->add_feature(uri_map->urid_map_feature()); - lv2_features->add_feature(uri_map->urid_unmap_feature()); - lv2_features->add_feature(SPtr<InstanceAccess>(new InstanceAccess())); - lv2_features->add_feature(SPtr<DataAccess>(new DataAccess())); - lv2_features->add_feature(SPtr<Log::Feature>(new Log::Feature())); - lilv_world_load_all(lilv_world); - - // Set up RDF namespaces - rdf_world->add_prefix("atom", "http://lv2plug.in/ns/ext/atom#"); - rdf_world->add_prefix("doap", "http://usefulinc.com/ns/doap#"); - rdf_world->add_prefix("ingen", INGEN_NS); - rdf_world->add_prefix("lv2", "http://lv2plug.in/ns/lv2core#"); - rdf_world->add_prefix("midi", "http://lv2plug.in/ns/ext/midi#"); - rdf_world->add_prefix("owl", "http://www.w3.org/2002/07/owl#"); - rdf_world->add_prefix("patch", "http://lv2plug.in/ns/ext/patch#"); - rdf_world->add_prefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); - rdf_world->add_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#"); - rdf_world->add_prefix("xsd", "http://www.w3.org/2001/XMLSchema#"); - - // Load internal 'plugin' information into lilv world - LilvNode* rdf_type = lilv_new_uri( - lilv_world, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"); - LilvNode* ingen_Plugin = lilv_new_uri( - lilv_world, INGEN__Plugin); - LilvNodes* internals = lilv_world_find_nodes( - lilv_world, nullptr, rdf_type, ingen_Plugin); - LILV_FOREACH(nodes, i, internals) { - const LilvNode* internal = lilv_nodes_get(internals, i); - lilv_world_load_resource(lilv_world, internal); - } - lilv_nodes_free(internals); - lilv_node_free(rdf_type); - lilv_node_free(ingen_Plugin); - } - - ~Impl() - { - if (engine) { - engine->quit(); - } - - // Delete module objects but save pointers to libraries - typedef std::list<std::unique_ptr<Library>> Libs; - Libs libs; - for (auto& m : modules) { - libs.emplace_back(std::move(m.second->library)); - delete m.second; - } - - serialiser.reset(); - parser.reset(); - interface.reset(); - engine.reset(); - store.reset(); - - interface_factories.clear(); - script_runners.clear(); - - delete rdf_world; - delete lv2_features; - delete uris; - delete forge; - delete uri_map; - - lilv_world_free(lilv_world); - - // Module libraries go out of scope and close here - } - - typedef std::map<const std::string, Module*> Modules; - Modules modules; - - typedef std::map<const std::string, World::InterfaceFactory> InterfaceFactories; - InterfaceFactories interface_factories; - - typedef bool (*ScriptRunner)(World* world, const char* filename); - typedef std::map<const std::string, ScriptRunner> ScriptRunners; - ScriptRunners script_runners; - - int* argc; - char*** argv; - LV2Features* lv2_features; - Sord::World* rdf_world; - LilvWorld* lilv_world; - URIMap* uri_map; - Forge* forge; - URIs* uris; - LV2_Log_Log* lv2_log; - Configuration conf; - Log log; - SPtr<Interface> interface; - SPtr<EngineBase> engine; - SPtr<Serialiser> serialiser; - SPtr<Parser> parser; - SPtr<Store> store; - std::mutex rdf_mutex; - std::string jack_uuid; -}; - -World::World(LV2_URID_Map* map, LV2_URID_Unmap* unmap, LV2_Log_Log* log) - : _impl(new Impl(map, unmap, log)) -{ - _impl->serialiser = SPtr<Serialiser>(new Serialiser(*this)); - _impl->parser = SPtr<Parser>(new Parser()); -} - -World::~World() -{ - delete _impl; -} - -void -World::load_configuration(int& argc, char**& argv) -{ - _impl->argc = &argc; - _impl->argv = &argv; - - // Parse default configuration files - const auto files = _impl->conf.load_default("ingen", "options.ttl"); - for (const auto& f : files) { - _impl->log.info(fmt("Loaded configuration %1%\n") % f); - } - - // Parse command line options, overriding configuration file values - _impl->conf.parse(argc, argv); - _impl->log.set_flush(_impl->conf.option("flush-log").get<int32_t>()); - _impl->log.set_trace(_impl->conf.option("trace").get<int32_t>()); -} - -void World::set_engine(SPtr<EngineBase> e) { _impl->engine = e; } -void World::set_interface(SPtr<Interface> i) { _impl->interface = i; } -void World::set_store(SPtr<Store> s) { _impl->store = s; } - -SPtr<EngineBase> World::engine() { return _impl->engine; } -SPtr<Interface> World::interface() { return _impl->interface; } -SPtr<Parser> World::parser() { return _impl->parser; } -SPtr<Serialiser> World::serialiser() { return _impl->serialiser; } -SPtr<Store> World::store() { return _impl->store; } - -int& World::argc() { return *_impl->argc; } -char**& World::argv() { return *_impl->argv; } -Configuration& World::conf() { return _impl->conf; } -Log& World::log() { return _impl->log; } - -std::mutex& World::rdf_mutex() { return _impl->rdf_mutex; } - -Sord::World* World::rdf_world() { return _impl->rdf_world; } -LilvWorld* World::lilv_world() { return _impl->lilv_world; } - -LV2Features& World::lv2_features() { return *_impl->lv2_features; } -Forge& World::forge() { return *_impl->forge; } -URIs& World::uris() { return *_impl->uris; } -URIMap& World::uri_map() { return *_impl->uri_map; } - -bool -World::load_module(const char* name) -{ - auto i = _impl->modules.find(name); - if (i != _impl->modules.end()) { - return true; - } - log().info(fmt("Loading %1% module\n") % name); - std::unique_ptr<Ingen::Library> lib = ingen_load_library(log(), name); - Ingen::Module* (*module_load)() = - lib ? (Ingen::Module* (*)())lib->get_function("ingen_module_load") - : nullptr; - if (module_load) { - Module* module = module_load(); - if (module) { - module->library = std::move(lib); - module->load(this); - _impl->modules.emplace(string(name), module); - return true; - } - } - - log().error(fmt("Failed to load module `%1%' (%2%)\n") % name % lib->get_last_error()); - return false; -} - -bool -World::run_module(const char* name) -{ - auto i = _impl->modules.find(name); - if (i == _impl->modules.end()) { - log().error(fmt("Attempt to run unloaded module `%1%'\n") % name); - return false; - } - - i->second->run(this); - return true; -} - -/** Get an interface for a remote engine at `engine_uri` - */ -SPtr<Interface> -World::new_interface(const URI& engine_uri, SPtr<Interface> respondee) -{ - const Impl::InterfaceFactories::const_iterator i = - _impl->interface_factories.find(std::string(engine_uri.scheme())); - if (i == _impl->interface_factories.end()) { - log().warn(fmt("Unknown URI scheme `%1%'\n") % engine_uri.scheme()); - return SPtr<Interface>(); - } - - return i->second(this, engine_uri, respondee); -} - -/** Run a script of type `mime_type` at filename `filename` */ -bool -World::run(const std::string& mime_type, const std::string& filename) -{ - const Impl::ScriptRunners::const_iterator i = _impl->script_runners.find(mime_type); - if (i == _impl->script_runners.end()) { - log().warn(fmt("Unknown script MIME type `%1%'\n") % mime_type); - return false; - } - - return i->second(this, filename.c_str()); -} - -void -World::add_interface_factory(const std::string& scheme, InterfaceFactory factory) -{ - _impl->interface_factories.emplace(scheme, factory); -} - -void -World::set_jack_uuid(const std::string& uuid) -{ - _impl->jack_uuid = uuid; -} - -std::string -World::jack_uuid() -{ - return _impl->jack_uuid; -} - -} // namespace Ingen diff --git a/src/client/BlockModel.cpp b/src/client/BlockModel.cpp deleted file mode 100644 index 910f7037..00000000 --- a/src/client/BlockModel.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <cmath> -#include <string> - -#include "ingen/client/BlockModel.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" - -namespace Ingen { -namespace Client { - -BlockModel::BlockModel(URIs& uris, - SPtr<PluginModel> plugin, - const Raul::Path& path) - : ObjectModel(uris, path) - , _plugin_uri(plugin->uri()) - , _plugin(plugin) - , _num_values(0) - , _min_values(nullptr) - , _max_values(nullptr) -{ -} - -BlockModel::BlockModel(URIs& uris, - const URI& plugin_uri, - const Raul::Path& path) - : ObjectModel(uris, path) - , _plugin_uri(plugin_uri) - , _num_values(0) - , _min_values(nullptr) - , _max_values(nullptr) -{ -} - -BlockModel::BlockModel(const BlockModel& copy) - : ObjectModel(copy) - , _plugin_uri(copy._plugin_uri) - , _num_values(copy._num_values) - , _min_values((float*)malloc(sizeof(float) * _num_values)) - , _max_values((float*)malloc(sizeof(float) * _num_values)) -{ - memcpy(_min_values, copy._min_values, sizeof(float) * _num_values); - memcpy(_max_values, copy._max_values, sizeof(float) * _num_values); -} - -BlockModel::~BlockModel() -{ - clear(); -} - -void -BlockModel::remove_port(SPtr<PortModel> port) -{ - for (auto i = _ports.begin(); i != _ports.end(); ++i) { - if ((*i) == port) { - _ports.erase(i); - break; - } - } - _signal_removed_port.emit(port); -} - -void -BlockModel::remove_port(const Raul::Path& port_path) -{ - for (auto i = _ports.begin(); i != _ports.end(); ++i) { - if ((*i)->path() == port_path) { - _ports.erase(i); - break; - } - } -} - -void -BlockModel::clear() -{ - _ports.clear(); - assert(_ports.empty()); - delete[] _min_values; - delete[] _max_values; - _min_values = nullptr; - _max_values = nullptr; -} - -void -BlockModel::add_child(SPtr<ObjectModel> c) -{ - assert(c->parent().get() == this); - - //ObjectModel::add_child(c); - - SPtr<PortModel> pm = dynamic_ptr_cast<PortModel>(c); - assert(pm); - add_port(pm); -} - -bool -BlockModel::remove_child(SPtr<ObjectModel> c) -{ - assert(c->path().is_child_of(path())); - assert(c->parent().get() == this); - - //bool ret = ObjectModel::remove_child(c); - - SPtr<PortModel> pm = dynamic_ptr_cast<PortModel>(c); - assert(pm); - remove_port(pm); - - //return ret; - return true; -} - -void -BlockModel::add_port(SPtr<PortModel> pm) -{ - assert(pm); - assert(pm->path().is_child_of(path())); - assert(pm->parent().get() == this); - - // Store should have handled this by merging the two - assert(find(_ports.begin(), _ports.end(), pm) == _ports.end()); - - _ports.push_back(pm); - _signal_new_port.emit(pm); -} - -SPtr<const PortModel> -BlockModel::get_port(const Raul::Symbol& symbol) const -{ - for (auto p : _ports) { - if (p->symbol() == symbol) { - return p; - } - } - return SPtr<PortModel>(); -} - -SPtr<const PortModel> -BlockModel::get_port(uint32_t index) const -{ - return _ports[index]; -} - -Ingen::Node* -BlockModel::port(uint32_t index) const -{ - assert(index < num_ports()); - return const_cast<Ingen::Node*>( - dynamic_cast<const Ingen::Node*>(_ports[index].get())); -} - -void -BlockModel::default_port_value_range(SPtr<const PortModel> port, - float& min, - float& max, - uint32_t srate) const -{ - // Default control values - min = 0.0; - max = 1.0; - - // Get range from client-side LV2 data - if (_plugin && _plugin->lilv_plugin()) { - if (!_min_values) { - _num_values = lilv_plugin_get_num_ports(_plugin->lilv_plugin()); - _min_values = new float[_num_values]; - _max_values = new float[_num_values]; - lilv_plugin_get_port_ranges_float(_plugin->lilv_plugin(), - _min_values, _max_values, nullptr); - } - - if (!std::isnan(_min_values[port->index()])) { - min = _min_values[port->index()]; - } - if (!std::isnan(_max_values[port->index()])) { - max = _max_values[port->index()]; - } - } - - if (port->port_property(_uris.lv2_sampleRate)) { - min *= srate; - max *= srate; - } -} - -void -BlockModel::port_value_range(SPtr<const PortModel> port, - float& min, - float& max, - uint32_t srate) const -{ - assert(port->parent().get() == this); - - default_port_value_range(port, min, max); - - // Possibly overriden - const Atom& min_atom = port->get_property(_uris.lv2_minimum); - const Atom& max_atom = port->get_property(_uris.lv2_maximum); - if (min_atom.type() == _uris.forge.Float) { - min = min_atom.get<float>(); - } - if (max_atom.type() == _uris.forge.Float) { - max = max_atom.get<float>(); - } - - if (max <= min) { - max = min + 1.0; - } - - if (port->port_property(_uris.lv2_sampleRate)) { - min *= srate; - max *= srate; - } -} - -std::string -BlockModel::label() const -{ - const Atom& name_property = get_property(_uris.lv2_name); - if (name_property.type() == _uris.forge.String) { - return name_property.ptr<char>(); - } else if (plugin_model()) { - return plugin_model()->human_name(); - } else { - return symbol().c_str(); - } -} - -std::string -BlockModel::port_label(SPtr<const PortModel> port) const -{ - const Atom& name = port->get_property(URI(LV2_CORE__name)); - if (name.is_valid() && name.type() == _uris.forge.String) { - return name.ptr<char>(); - } - - if (_plugin && _plugin->lilv_plugin()) { - LilvWorld* w = _plugin->lilv_world(); - const LilvPlugin* plug = _plugin->lilv_plugin(); - LilvNode* sym = lilv_new_string(w, port->symbol().c_str()); - const LilvPort* lport = lilv_plugin_get_port_by_symbol(plug, sym); - if (lport) { - LilvNode* lname = lilv_port_get_name(plug, lport); - if (lname && lilv_node_is_string(lname)) { - std::string ret(lilv_node_as_string(lname)); - lilv_node_free(lname); - return ret; - } - lilv_node_free(lname); - } - } - - return port->symbol().c_str(); -} - -void -BlockModel::set(SPtr<ObjectModel> model) -{ - SPtr<BlockModel> block = dynamic_ptr_cast<BlockModel>(model); - if (block) { - _plugin_uri = block->_plugin_uri; - _plugin = block->_plugin; - } - - ObjectModel::set(model); -} - -} // namespace Client -} // namespace Ingen diff --git a/src/client/ClientStore.cpp b/src/client/ClientStore.cpp deleted file mode 100644 index 792f8949..00000000 --- a/src/client/ClientStore.cpp +++ /dev/null @@ -1,487 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <boost/variant/apply_visitor.hpp> - -#include "ingen/Log.hpp" -#include "ingen/client/ArcModel.hpp" -#include "ingen/client/BlockModel.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" -#include "ingen/client/ObjectModel.hpp" -#include "ingen/client/PluginModel.hpp" -#include "ingen/client/PortModel.hpp" -#include "ingen/client/SigClientInterface.hpp" - -namespace Ingen { -namespace Client { - -ClientStore::ClientStore(URIs& uris, - Log& log, - SPtr<SigClientInterface> emitter) - : _uris(uris) - , _log(log) - , _emitter(emitter) - , _plugins(new Plugins()) -{ - if (emitter) { - emitter->signal_message().connect( - sigc::mem_fun(this, &ClientStore::message)); - } -} - -void -ClientStore::clear() -{ - Store::clear(); - _plugins->clear(); -} - -void -ClientStore::add_object(SPtr<ObjectModel> object) -{ - // If we already have "this" object, merge the existing one into the new - // one (with precedence to the new values). - auto existing = find(object->path()); - if (existing != end()) { - dynamic_ptr_cast<ObjectModel>(existing->second)->set(object); - } else { - if (!object->path().is_root()) { - SPtr<ObjectModel> parent = _object(object->path().parent()); - if (parent) { - assert(object->path().is_child_of(parent->path())); - object->set_parent(parent); - parent->add_child(object); - assert(parent && (object->parent() == parent)); - - (*this)[object->path()] = object; - _signal_new_object.emit(object); - } else { - _log.error(fmt("Object %1% with no parent\n") % object->path()); - } - } else { - (*this)[object->path()] = object; - _signal_new_object.emit(object); - } - } - - for (auto p : object->properties()) { - object->signal_property().emit(p.first, p.second); - } -} - -SPtr<ObjectModel> -ClientStore::remove_object(const Raul::Path& path) -{ - // Find the object, the "top" of the tree to remove - const iterator top = find(path); - if (top == end()) { - return SPtr<ObjectModel>(); - } - - SPtr<ObjectModel> object = dynamic_ptr_cast<ObjectModel>(top->second); - - // Remove object and any adjacent arcs from parent if applicable - if (object && object->parent()) { - SPtr<PortModel> port = dynamic_ptr_cast<PortModel>(object); - if (port && dynamic_ptr_cast<GraphModel>(port->parent())) { - disconnect_all(port->parent()->path(), path); - if (port->parent()->parent()) { - disconnect_all(port->parent()->parent()->path(), path); - } - } else if (port && port->parent()->parent()) { - disconnect_all(port->parent()->parent()->path(), path); - } else { - disconnect_all(object->parent()->path(), path); - } - - object->parent()->remove_child(object); - } - - // Remove the object and all its descendants - Objects removed; - remove(top, removed); - - // Notify everything that needs to know this object has been removed - if (object) { - object->signal_destroyed().emit(); - } - - return object; -} - -SPtr<PluginModel> -ClientStore::_plugin(const URI& uri) -{ - const Plugins::iterator i = _plugins->find(uri); - return (i == _plugins->end()) ? SPtr<PluginModel>() : (*i).second; -} - -SPtr<PluginModel> -ClientStore::_plugin(const Atom& uri) -{ - /* FIXME: Should probably be stored with URIs rather than strings, to make - this a fast case. */ - - const Plugins::iterator i = _plugins->find(URI(_uris.forge.str(uri, false))); - return (i == _plugins->end()) ? SPtr<PluginModel>() : (*i).second; -} - -SPtr<const PluginModel> -ClientStore::plugin(const URI& uri) const -{ - return const_cast<ClientStore*>(this)->_plugin(uri); -} - -SPtr<ObjectModel> -ClientStore::_object(const Raul::Path& path) -{ - const iterator i = find(path); - if (i == end()) { - return SPtr<ObjectModel>(); - } else { - SPtr<ObjectModel> model = dynamic_ptr_cast<ObjectModel>(i->second); - assert(model); - assert(model->path().is_root() || model->parent()); - return model; - } -} - -SPtr<const ObjectModel> -ClientStore::object(const Raul::Path& path) const -{ - return const_cast<ClientStore*>(this)->_object(path); -} - -SPtr<Resource> -ClientStore::_resource(const URI& uri) -{ - if (uri_is_path(uri)) { - return _object(uri_to_path(uri)); - } else { - return _plugin(uri); - } -} - -SPtr<const Resource> -ClientStore::resource(const URI& uri) const -{ - return const_cast<ClientStore*>(this)->_resource(uri); -} - -void -ClientStore::add_plugin(SPtr<PluginModel> pm) -{ - SPtr<PluginModel> existing = _plugin(pm->uri()); - if (existing) { - existing->set(pm); - } else { - _plugins->emplace(pm->uri(), pm); - _signal_new_plugin.emit(pm); - } -} - -/* ****** Signal Handlers ******** */ - -void -ClientStore::operator()(const Del& del) -{ - if (uri_is_path(del.uri)) { - remove_object(uri_to_path(del.uri)); - } else { - auto p = _plugins->find(del.uri); - if (p != _plugins->end()) { - _plugins->erase(p); - _signal_plugin_deleted.emit(del.uri); - } - } -} - -void -ClientStore::operator()(const Copy&) -{ - _log.error("Client store copy unsupported\n"); -} - -void -ClientStore::operator()(const Move& msg) -{ - const iterator top = find(msg.old_path); - if (top != end()) { - rename(top, msg.new_path); - } -} - -void -ClientStore::message(const Message& msg) -{ - boost::apply_visitor(*this, msg); -} - -void -ClientStore::operator()(const Put& msg) -{ - typedef Properties::const_iterator Iterator; - - const auto& uri = msg.uri; - const auto& properties = msg.properties; - - bool is_graph, is_block, is_port, is_output; - Resource::type(uris(), properties, - is_graph, is_block, is_port, is_output); - - // Check for specially handled types - const Iterator t = properties.find(_uris.rdf_type); - if (t != properties.end()) { - const Atom& type(t->second); - if (_uris.pset_Preset == type) { - const Iterator p = properties.find(_uris.lv2_appliesTo); - const Iterator l = properties.find(_uris.rdfs_label); - SPtr<PluginModel> plug; - if (p == properties.end()) { - _log.error(fmt("Preset <%1%> with no plugin\n") % uri.c_str()); - } else if (l == properties.end()) { - _log.error(fmt("Preset <%1%> with no label\n") % uri.c_str()); - } else if (l->second.type() != _uris.forge.String) { - _log.error(fmt("Preset <%1%> label is not a string\n") % uri.c_str()); - } else if (!(plug = _plugin(p->second))) { - _log.error(fmt("Preset <%1%> for unknown plugin %2%\n") - % uri.c_str() % _uris.forge.str(p->second, true)); - } else { - plug->add_preset(uri, l->second.ptr<char>()); - } - return; - } else if (_uris.ingen_Graph == type) { - is_graph = true; - } else if (_uris.ingen_Internal == type || _uris.lv2_Plugin == type) { - SPtr<PluginModel> p(new PluginModel(uris(), uri, type, properties)); - add_plugin(p); - return; - } - } - - if (!uri_is_path(uri)) { - _log.error(fmt("Put for unknown subject <%1%>\n") - % uri.c_str()); - return; - } - - const Raul::Path path(uri_to_path(uri)); - - SPtr<ObjectModel> obj = dynamic_ptr_cast<ObjectModel>(_object(path)); - if (obj) { - obj->set_properties(properties); - return; - } - - if (path.is_root()) { - is_graph = true; - } - - if (is_graph) { - SPtr<GraphModel> model(new GraphModel(uris(), path)); - model->set_properties(properties); - add_object(model); - } else if (is_block) { - auto p = properties.find(_uris.lv2_prototype); - if (p == properties.end()) { - p = properties.find(_uris.ingen_prototype); - } - - SPtr<PluginModel> plug; - if (p->second.is_valid() && (p->second.type() == _uris.forge.URI || - p->second.type() == _uris.forge.URID)) { - const URI uri(_uris.forge.str(p->second, false)); - if (!(plug = _plugin(uri))) { - plug = SPtr<PluginModel>( - new PluginModel(uris(), uri, Atom(), Properties())); - add_plugin(plug); - } - - SPtr<BlockModel> bm(new BlockModel(uris(), plug, path)); - bm->set_properties(properties); - add_object(bm); - } else { - _log.warn(fmt("Block %1% has no prototype\n") % path.c_str()); - } - } else if (is_port) { - PortModel::Direction pdir = (is_output) - ? PortModel::Direction::OUTPUT - : PortModel::Direction::INPUT; - uint32_t index = 0; - const Iterator i = properties.find(_uris.lv2_index); - if (i != properties.end() && i->second.type() == _uris.forge.Int) { - index = i->second.get<int32_t>(); - } - - SPtr<PortModel> p(new PortModel(uris(), path, index, pdir)); - p->set_properties(properties); - add_object(p); - } else { - _log.warn(fmt("Ignoring %1% of unknown type\n") % path.c_str()); - } -} - -void -ClientStore::operator()(const Delta& msg) -{ - const auto& uri = msg.uri; - if (uri == URI("ingen:/clients/this")) { - // Client property, which we don't store (yet?) - return; - } - - if (!uri_is_path(uri)) { - _log.error(fmt("Delta for unknown subject <%1%>\n") - % uri.c_str()); - return; - } - - const Raul::Path path(uri_to_path(uri)); - - SPtr<ObjectModel> obj = _object(path); - if (obj) { - obj->remove_properties(msg.remove); - obj->add_properties(msg.add); - } else { - _log.warn(fmt("Failed to find object `%1%'\n") % path.c_str()); - } -} - -void -ClientStore::operator()(const SetProperty& msg) -{ - const auto& subject_uri = msg.subject; - const auto& predicate = msg.predicate; - const auto& value = msg.value; - - if (subject_uri == URI("ingen:/engine")) { - _log.info(fmt("Engine property <%1%> = %2%\n") - % predicate.c_str() % _uris.forge.str(value, false)); - return; - } - SPtr<Resource> subject = _resource(subject_uri); - if (subject) { - if (predicate == _uris.ingen_activity) { - /* Activity is transient, trigger any live actions (like GUI - blinkenlights) but do not store the property. */ - subject->on_property(predicate, value); - } else { - subject->set_property(predicate, value, msg.ctx); - } - } else { - SPtr<PluginModel> plugin = _plugin(subject_uri); - if (plugin) { - plugin->set_property(predicate, value); - } else if (predicate != _uris.ingen_activity) { - _log.warn(fmt("Property <%1%> for unknown object %2%\n") - % predicate.c_str() % subject_uri.c_str()); - } - } -} - -SPtr<GraphModel> -ClientStore::connection_graph(const Raul::Path& tail_path, - const Raul::Path& head_path) -{ - SPtr<GraphModel> graph; - - if (tail_path.parent() == head_path.parent()) { - graph = dynamic_ptr_cast<GraphModel>(_object(tail_path.parent())); - } - - if (!graph && tail_path.parent() == head_path.parent().parent()) { - graph = dynamic_ptr_cast<GraphModel>(_object(tail_path.parent())); - } - - if (!graph && tail_path.parent().parent() == head_path.parent()) { - graph = dynamic_ptr_cast<GraphModel>(_object(head_path.parent())); - } - - if (!graph) { - graph = dynamic_ptr_cast<GraphModel>(_object(tail_path.parent().parent())); - } - - if (!graph) { - _log.error(fmt("Unable to find graph for arc %1% => %2%\n") - % tail_path % head_path); - } - - return graph; -} - -bool -ClientStore::attempt_connection(const Raul::Path& tail_path, - const Raul::Path& head_path) -{ - SPtr<PortModel> tail = dynamic_ptr_cast<PortModel>(_object(tail_path)); - SPtr<PortModel> head = dynamic_ptr_cast<PortModel>(_object(head_path)); - - if (tail && head) { - SPtr<GraphModel> graph = connection_graph(tail_path, head_path); - SPtr<ArcModel> arc(new ArcModel(tail, head)); - graph->add_arc(arc); - return true; - } else { - _log.warn(fmt("Failed to connect %1% => %2%\n") - % tail_path % head_path); - return false; - } -} - -void -ClientStore::operator()(const Connect& msg) -{ - attempt_connection(msg.tail, msg.head); -} - -void -ClientStore::operator()(const Disconnect& msg) -{ - SPtr<PortModel> tail = dynamic_ptr_cast<PortModel>(_object(msg.tail)); - SPtr<PortModel> head = dynamic_ptr_cast<PortModel>(_object(msg.head)); - SPtr<GraphModel> graph = connection_graph(msg.tail, msg.head); - if (graph) { - graph->remove_arc(tail.get(), head.get()); - } -} - -void -ClientStore::operator()(const DisconnectAll& msg) -{ - SPtr<GraphModel> graph = dynamic_ptr_cast<GraphModel>(_object(msg.graph)); - SPtr<ObjectModel> object = _object(msg.path); - - if (!graph || !object) { - _log.error(fmt("Bad disconnect all notification %1% in %2%\n") - % msg.path % msg.graph); - return; - } - - const GraphModel::Arcs arcs = graph->arcs(); - for (auto a : arcs) { - SPtr<ArcModel> arc = dynamic_ptr_cast<ArcModel>(a.second); - if (arc->tail()->parent() == object - || arc->head()->parent() == object - || arc->tail()->path() == msg.path - || arc->head()->path() == msg.path) { - graph->remove_arc(arc->tail().get(), arc->head().get()); - } - } -} - -} // namespace Client -} // namespace Ingen diff --git a/src/client/GraphModel.cpp b/src/client/GraphModel.cpp deleted file mode 100644 index 0723e59b..00000000 --- a/src/client/GraphModel.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> - -#include "ingen/URIs.hpp" -#include "ingen/client/ArcModel.hpp" -#include "ingen/client/BlockModel.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" - -namespace Ingen { -namespace Client { - -void -GraphModel::add_child(SPtr<ObjectModel> c) -{ - assert(c->parent().get() == this); - - SPtr<PortModel> pm = dynamic_ptr_cast<PortModel>(c); - if (pm) { - add_port(pm); - return; - } - - SPtr<BlockModel> bm = dynamic_ptr_cast<BlockModel>(c); - if (bm) { - _signal_new_block.emit(bm); - } -} - -bool -GraphModel::remove_child(SPtr<ObjectModel> o) -{ - assert(o->path().is_child_of(path())); - assert(o->parent().get() == this); - - SPtr<PortModel> pm = dynamic_ptr_cast<PortModel>(o); - if (pm) { - remove_arcs_on(pm); - remove_port(pm); - } - - SPtr<BlockModel> bm = dynamic_ptr_cast<BlockModel>(o); - if (bm) { - _signal_removed_block.emit(bm); - } - - return true; -} - -void -GraphModel::remove_arcs_on(SPtr<PortModel> p) -{ - // Remove any connections which referred to this object, - // since they can't possibly exist anymore - for (auto j = _arcs.begin(); j != _arcs.end();) { - auto next = j; - ++next; - - SPtr<ArcModel> arc = dynamic_ptr_cast<ArcModel>(j->second); - if (arc->tail_path().parent() == p->path() - || arc->tail_path() == p->path() - || arc->head_path().parent() == p->path() - || arc->head_path() == p->path()) { - _signal_removed_arc.emit(arc); - _arcs.erase(j); // Cuts our reference - } - j = next; - } -} - -void -GraphModel::clear() -{ - _arcs.clear(); - - BlockModel::clear(); - - assert(_arcs.empty()); - assert(_ports.empty()); -} - -SPtr<ArcModel> -GraphModel::get_arc(const Node* tail, const Node* head) -{ - auto i = _arcs.find(std::make_pair(tail, head)); - if (i != _arcs.end()) { - return dynamic_ptr_cast<ArcModel>(i->second); - } else { - return SPtr<ArcModel>(); - } -} - -/** Add a connection to this graph. - * - * A reference to `arc` is taken, released on deletion or removal. - * If `arc` only contains paths (not pointers to the actual ports), the ports - * will be found and set. The ports referred to not existing as children of - * this graph is a fatal error. - */ -void -GraphModel::add_arc(SPtr<ArcModel> arc) -{ - // Store should have 'resolved' the connection already - assert(arc); - assert(arc->tail()); - assert(arc->head()); - assert(arc->tail()->parent()); - assert(arc->head()->parent()); - assert(arc->tail_path() != arc->head_path()); - assert(arc->tail()->parent().get() == this - || arc->tail()->parent()->parent().get() == this); - assert(arc->head()->parent().get() == this - || arc->head()->parent()->parent().get() == this); - - SPtr<ArcModel> existing = get_arc( - arc->tail().get(), arc->head().get()); - - if (existing) { - assert(arc->tail() == existing->tail()); - assert(arc->head() == existing->head()); - } else { - _arcs.emplace(std::make_pair(arc->tail().get(), arc->head().get()), - arc); - _signal_new_arc.emit(arc); - } -} - -void -GraphModel::remove_arc(const Node* tail, const Node* head) -{ - auto i = _arcs.find(std::make_pair(tail, head)); - if (i != _arcs.end()) { - SPtr<ArcModel> arc = dynamic_ptr_cast<ArcModel>(i->second); - _signal_removed_arc.emit(arc); - _arcs.erase(i); - } -} - -bool -GraphModel::enabled() const -{ - const Atom& enabled = get_property(_uris.ingen_enabled); - return (enabled.is_valid() && enabled.get<int32_t>()); -} - -uint32_t -GraphModel::internal_poly() const -{ - const Atom& poly = get_property(_uris.ingen_polyphony); - return poly.is_valid() ? poly.get<int32_t>() : 1; -} - -bool -GraphModel::polyphonic() const -{ - const Atom& poly = get_property(_uris.ingen_polyphonic); - return poly.is_valid() && poly.get<int32_t>(); -} - -} // namespace Client -} // namespace Ingen diff --git a/src/client/ObjectModel.cpp b/src/client/ObjectModel.cpp deleted file mode 100644 index 8d40b120..00000000 --- a/src/client/ObjectModel.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Node.hpp" -#include "ingen/URIs.hpp" -#include "ingen/client/ObjectModel.hpp" - -namespace Ingen { -namespace Client { - -ObjectModel::ObjectModel(URIs& uris, const Raul::Path& path) - : Node(uris, path) - , _path(path) - , _symbol((path == "/") ? "root" : path.symbol()) -{ -} - -ObjectModel::ObjectModel(const ObjectModel& copy) - : Node(copy) - , _parent(copy._parent) - , _path(copy._path) - , _symbol(copy._symbol) -{ -} - -bool -ObjectModel::is_a(const URIs::Quark& type) const -{ - return has_property(_uris.rdf_type, type); -} - -void -ObjectModel::on_property(const URI& uri, const Atom& value) -{ - _signal_property.emit(uri, value); -} - -void -ObjectModel::on_property_removed(const URI& uri, const Atom& value) -{ - _signal_property_removed.emit(uri, value); -} - -const Atom& -ObjectModel::get_property(const URI& key) const -{ - static const Atom null_atom; - auto i = properties().find(key); - return (i != properties().end()) ? i->second : null_atom; -} - -bool -ObjectModel::polyphonic() const -{ - const Atom& polyphonic = get_property(_uris.ingen_polyphonic); - return (polyphonic.is_valid() && polyphonic.get<int32_t>()); -} - -/** Merge the data of `o` with self, as much as possible. - * - * This will merge the two models, but with any conflict take the value in - * `o` as correct. The paths of the two models MUST be equal. - */ -void -ObjectModel::set(SPtr<ObjectModel> o) -{ - assert(_path == o->path()); - if (o->_parent) { - _parent = o->_parent; - } - - for (auto v : o->properties()) { - Resource::set_property(v.first, v.second); - _signal_property.emit(v.first, v.second); - } -} - -void -ObjectModel::set_path(const Raul::Path& p) -{ - _path = p; - _symbol = Raul::Symbol(p.is_root() ? "root" : p.symbol()); - set_uri(path_to_uri(p)); - _signal_moved.emit(); -} - -void -ObjectModel::set_parent(SPtr<ObjectModel> p) -{ - assert(_path.is_child_of(p->path())); - _parent = p; -} - -} // namespace Client -} // namespace Ingen diff --git a/src/client/PluginModel.cpp b/src/client/PluginModel.cpp deleted file mode 100644 index 5427b75e..00000000 --- a/src/client/PluginModel.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <string> -#include <algorithm> - -#include <boost/optional.hpp> - -#include "raul/Path.hpp" - -#include "ingen/Atom.hpp" -#include "ingen/client/GraphModel.hpp" -#include "ingen/client/PluginModel.hpp" -#include "ingen/client/PluginUI.hpp" - -#include "ingen_config.h" - -using std::string; - -namespace Ingen { -namespace Client { - -LilvWorld* PluginModel::_lilv_world = nullptr; -const LilvPlugins* PluginModel::_lilv_plugins = nullptr; - -Sord::World* PluginModel::_rdf_world = nullptr; - -PluginModel::PluginModel(URIs& uris, - const URI& uri, - const Atom& type, - const Properties& properties) - : Resource(uris, uri) - , _type(type) - , _fetched(false) -{ - if (!_type.is_valid()) { - if (uri.string().find("ingen-internals") != string::npos) { - _type = uris.ingen_Internal.urid; - } else { - _type = uris.lv2_Plugin.urid; // Assume LV2 and hope for the best... - } - } - - add_property(uris.rdf_type, type); - add_properties(properties); - - LilvNode* plugin_uri = lilv_new_uri(_lilv_world, uri.c_str()); - _lilv_plugin = lilv_plugins_get_by_uri(_lilv_plugins, plugin_uri); - lilv_node_free(plugin_uri); - - if (uris.ingen_Internal == _type) { - set_property(uris.doap_name, - uris.forge.alloc(std::string(uri.fragment().substr(1)))); - } -} - -static size_t -last_uri_delim(const std::string& str) -{ - for (size_t i = str.length() - 1; i > 0; --i) { - switch (str[i]) { - case ':': case '/': case '?': case '#': - return i; - } - } - return string::npos; -} - -static bool -contains_alpha_after(const std::string& str, size_t begin) -{ - for (size_t i = begin; i < str.length(); ++i) { - if (isalpha(str[i])) { - return true; - } - } - return false; -} - -const Atom& -PluginModel::get_property(const URI& key) const -{ - static const Atom nil; - const Atom& val = Resource::get_property(key); - if (val.is_valid()) { - return val; - } - - // No lv2:symbol from data or engine, invent one - if (key == _uris.lv2_symbol) { - string str = this->uri(); - size_t last_delim = last_uri_delim(str); - while (last_delim != string::npos && - !contains_alpha_after(str, last_delim)) { - str = str.substr(0, last_delim); - last_delim = last_uri_delim(str); - } - str = str.substr(last_delim + 1); - - std::string symbol = Raul::Symbol::symbolify(str); - set_property(_uris.lv2_symbol, _uris.forge.alloc(symbol)); - return get_property(key); - } - - if (_lilv_plugin) { - boost::optional<const Atom&> ret; - LilvNode* lv2_pred = lilv_new_uri(_lilv_world, key.c_str()); - LilvNodes* values = lilv_plugin_get_value(_lilv_plugin, lv2_pred); - lilv_node_free(lv2_pred); - LILV_FOREACH(nodes, i, values) { - const LilvNode* val = lilv_nodes_get(values, i); - if (lilv_node_is_uri(val)) { - ret = set_property( - key, _uris.forge.make_urid(URI(lilv_node_as_uri(val)))); - break; - } else if (lilv_node_is_string(val)) { - ret = set_property( - key, _uris.forge.alloc(lilv_node_as_string(val))); - break; - } else if (lilv_node_is_float(val)) { - ret = set_property( - key, _uris.forge.make(lilv_node_as_float(val))); - break; - } else if (lilv_node_is_int(val)) { - ret = set_property( - key, _uris.forge.make(lilv_node_as_int(val))); - break; - } - } - lilv_nodes_free(values); - - if (ret) { - return *ret; - } - } - - return nil; -} - -void -PluginModel::set(SPtr<PluginModel> p) -{ - _type = p->_type; - - if (p->_lilv_plugin) { - _lilv_plugin = p->_lilv_plugin; - } - - for (auto v : p->properties()) { - Resource::set_property(v.first, v.second); - _signal_property.emit(v.first, v.second); - } - - _signal_changed.emit(); -} - -void -PluginModel::add_preset(const URI& uri, const std::string& label) -{ - _presets.emplace(uri, label); - _signal_preset.emit(uri, label); -} - -Raul::Symbol -PluginModel::default_block_symbol() const -{ - const Atom& name_atom = get_property(_uris.lv2_symbol); - if (name_atom.is_valid() && name_atom.type() == _uris.forge.String) { - return Raul::Symbol::symbolify(name_atom.ptr<char>()); - } else { - return Raul::Symbol("_"); - } -} - -string -PluginModel::human_name() const -{ - const Atom& name_atom = get_property(_uris.doap_name); - if (name_atom.type() == _uris.forge.String) { - return name_atom.ptr<char>(); - } else { - return default_block_symbol().c_str(); - } -} - -string -PluginModel::port_human_name(uint32_t i) const -{ - if (_lilv_plugin) { - const LilvPort* port = lilv_plugin_get_port_by_index(_lilv_plugin, i); - LilvNode* name = lilv_port_get_name(_lilv_plugin, port); - const string ret(lilv_node_as_string(name)); - lilv_node_free(name); - return ret; - } - return ""; -} - -PluginModel::ScalePoints -PluginModel::port_scale_points(uint32_t i) const -{ - // TODO: Non-float scale points - ScalePoints points; - if (_lilv_plugin) { - const LilvPort* port = lilv_plugin_get_port_by_index(_lilv_plugin, i); - LilvScalePoints* sp = lilv_port_get_scale_points(_lilv_plugin, port); - LILV_FOREACH(scale_points, i, sp) { - const LilvScalePoint* p = lilv_scale_points_get(sp, i); - points.emplace( - lilv_node_as_float(lilv_scale_point_get_value(p)), - lilv_node_as_string(lilv_scale_point_get_label(p))); - } - } - return points; -} - -bool -PluginModel::has_ui() const -{ - if (_lilv_plugin) { - LilvUIs* uis = lilv_plugin_get_uis(_lilv_plugin); - const bool ret = (lilv_nodes_size(uis) > 0); - lilv_uis_free(uis); - return ret; - } - return false; -} - -SPtr<PluginUI> -PluginModel::ui(Ingen::World* world, - SPtr<const BlockModel> block) const -{ - if (!_lilv_plugin) { - return SPtr<PluginUI>(); - } - - return PluginUI::create(world, block, _lilv_plugin); -} - -static std::string -heading(const std::string& text, bool html, unsigned level) -{ - if (html) { - const std::string tag = std::string("h") + std::to_string(level); - return std::string("<") + tag + ">" + text + "</" + tag + ">\n"; - } else { - return text + ":\n\n"; - } -} - -static std::string -link(const std::string& addr, bool html) -{ - if (html) { - return std::string("<a href=\"") + addr + "\">" + addr + "</a>"; - } else { - return addr; - } -} - -std::string -PluginModel::get_documentation(const LilvNode* subject, bool html) const -{ - std::string doc; - - LilvNode* lv2_documentation = lilv_new_uri(_lilv_world, - LV2_CORE__documentation); - LilvNode* rdfs_comment = lilv_new_uri(_lilv_world, - LILV_NS_RDFS "comment"); - - LilvNodes* vals = lilv_world_find_nodes( - _lilv_world, subject, lv2_documentation, nullptr); - const bool doc_is_html = vals; - if (!vals) { - vals = lilv_world_find_nodes( - _lilv_world, subject, rdfs_comment, nullptr); - } - - if (vals) { - const LilvNode* val = lilv_nodes_get_first(vals); - if (lilv_node_is_string(val)) { - doc += lilv_node_as_string(val); - } - } - - if (html && !doc_is_html) { - for (std::size_t i = 0; i < doc.size(); ++i) { - if (doc.substr(i, 2) == "\n\n") { - doc.replace(i, 2, "<br/><br/>"); - i += strlen("<br/><br/>"); - } - } - } - - lilv_node_free(rdfs_comment); - lilv_node_free(lv2_documentation); - - return doc; -} - -std::string -PluginModel::documentation(const bool html) const -{ - LilvNode* subject = (_lilv_plugin) - ? lilv_node_duplicate(lilv_plugin_get_uri(_lilv_plugin)) - : lilv_new_uri(_lilv_world, uri().c_str()); - - const std::string doc(get_documentation(subject, html)); - - lilv_node_free(subject); - - return (heading(human_name(), html, 2) + - link(uri(), html) + (html ? "<br/><br/>" : "\n\n") + - doc); -} - -std::string -PluginModel::port_documentation(uint32_t index, bool html) const -{ - if (!_lilv_plugin) { - return ""; - } - - const LilvPort* port = lilv_plugin_get_port_by_index(_lilv_plugin, index); - if (!port) { - return ""; - } - - return (heading(port_human_name(index), html, 2) + - get_documentation(lilv_port_get_node(_lilv_plugin, port), html)); -} - -const LilvPort* -PluginModel::lilv_port(uint32_t index) const -{ - return lilv_plugin_get_port_by_index(_lilv_plugin, index); -} - -void -PluginModel::set_lilv_world(LilvWorld* world) -{ - _lilv_world = world; - _lilv_plugins = lilv_world_get_all_plugins(_lilv_world); -} - -} // namespace Client -} // namespace Ingen diff --git a/src/client/PluginUI.cpp b/src/client/PluginUI.cpp deleted file mode 100644 index df983f7f..00000000 --- a/src/client/PluginUI.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/URIs.hpp" -#include "ingen/client/BlockModel.hpp" -#include "ingen/client/PluginUI.hpp" -#include "ingen/client/PortModel.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "lv2/lv2plug.in/ns/extensions/ui/ui.h" - -namespace Ingen { -namespace Client { - -SuilHost* PluginUI::ui_host = nullptr; - -static SPtr<const PortModel> -get_port(PluginUI* ui, uint32_t port_index) -{ - if (port_index >= ui->block()->ports().size()) { - ui->world()->log().error( - fmt("%1% UI tried to access invalid port %2%\n") - % ui->block()->plugin()->uri().c_str() % port_index); - return SPtr<const PortModel>(); - } - return ui->block()->ports()[port_index]; -} - -static void -lv2_ui_write(SuilController controller, - uint32_t port_index, - uint32_t buffer_size, - uint32_t format, - const void* buffer) -{ - PluginUI* const ui = (PluginUI*)controller; - const URIs& uris = ui->world()->uris(); - SPtr<const PortModel> port = get_port(ui, port_index); - if (!port) { - return; - } - - // float (special case, always 0) - if (format == 0) { - if (buffer_size != 4) { - ui->world()->log().error( - fmt("%1% UI wrote corrupt float with bad size\n") - % ui->block()->plugin()->uri().c_str()); - return; - } - const float value = *(const float*)buffer; - if (port->value().type() == uris.atom_Float && - value == port->value().get<float>()) { - return; // Ignore feedback - } - - ui->signal_property_changed()( - port->uri(), - uris.ingen_value, - ui->world()->forge().make(value), - Resource::Graph::DEFAULT); - - } else if (format == uris.atom_eventTransfer.urid.get<LV2_URID>()) { - const LV2_Atom* atom = (const LV2_Atom*)buffer; - Atom val = ui->world()->forge().alloc( - atom->size, atom->type, LV2_ATOM_BODY_CONST(atom)); - ui->signal_property_changed()(port->uri(), - uris.ingen_activity, - val, - Resource::Graph::DEFAULT); - } else { - ui->world()->log().warn( - fmt("Unknown value format %1% from LV2 UI\n") - % format % ui->block()->plugin()->uri().c_str()); - } -} - -static uint32_t -lv2_ui_port_index(SuilController controller, const char* port_symbol) -{ - PluginUI* const ui = (PluginUI*)controller; - - const BlockModel::Ports& ports = ui->block()->ports(); - for (uint32_t i = 0; i < ports.size(); ++i) { - if (ports[i]->symbol() == port_symbol) { - return i; - } - } - return LV2UI_INVALID_PORT_INDEX; -} - -static uint32_t -lv2_ui_subscribe(SuilController controller, - uint32_t port_index, - uint32_t protocol, - const LV2_Feature* const* features) -{ - PluginUI* const ui = (PluginUI*)controller; - SPtr<const PortModel> port = get_port(ui, port_index); - if (!port) { - return 1; - } - - ui->signal_property_changed()( - ui->block()->ports()[port_index]->uri(), - ui->world()->uris().ingen_broadcast, - ui->world()->forge().make(true), - Resource::Graph::DEFAULT); - - return 0; -} - -static uint32_t -lv2_ui_unsubscribe(SuilController controller, - uint32_t port_index, - uint32_t protocol, - const LV2_Feature* const* features) -{ - PluginUI* const ui = (PluginUI*)controller; - SPtr<const PortModel> port = get_port(ui, port_index); - if (!port) { - return 1; - } - - ui->signal_property_changed()( - ui->block()->ports()[port_index]->uri(), - ui->world()->uris().ingen_broadcast, - ui->world()->forge().make(false), - Resource::Graph::DEFAULT); - - return 0; -} - -PluginUI::PluginUI(Ingen::World* world, - SPtr<const BlockModel> block, - LilvUIs* uis, - const LilvUI* ui, - const LilvNode* ui_type) - : _world(world) - , _block(std::move(block)) - , _instance(nullptr) - , _uis(uis) - , _ui(ui) - , _ui_node(lilv_node_duplicate(lilv_ui_get_uri(ui))) - , _ui_type(lilv_node_duplicate(ui_type)) -{ -} - -PluginUI::~PluginUI() -{ - for (uint32_t i : _subscribed_ports) { - lv2_ui_unsubscribe(this, i, 0, nullptr); - } - suil_instance_free(_instance); - lilv_node_free(_ui_node); - lilv_node_free(_ui_type); - lilv_uis_free(_uis); - lilv_world_unload_resource(_world->lilv_world(), lilv_ui_get_uri(_ui)); -} - -SPtr<PluginUI> -PluginUI::create(Ingen::World* world, - SPtr<const BlockModel> block, - const LilvPlugin* plugin) -{ - if (!PluginUI::ui_host) { - PluginUI::ui_host = suil_host_new(lv2_ui_write, - lv2_ui_port_index, - lv2_ui_subscribe, - lv2_ui_unsubscribe); - } - - static const char* gtk_ui_uri = LV2_UI__GtkUI; - - LilvNode* gtk_ui = lilv_new_uri(world->lilv_world(), gtk_ui_uri); - - LilvUIs* uis = lilv_plugin_get_uis(plugin); - const LilvUI* ui = nullptr; - const LilvNode* ui_type = nullptr; - LILV_FOREACH(uis, u, uis) { - const LilvUI* this_ui = lilv_uis_get(uis, u); - if (lilv_ui_is_supported(this_ui, - suil_ui_supported, - gtk_ui, - &ui_type)) { - // TODO: Multiple UI support - ui = this_ui; - break; - } - } - - if (!ui) { - lilv_node_free(gtk_ui); - return SPtr<PluginUI>(); - } - - // Create the PluginUI, but don't instantiate yet - SPtr<PluginUI> ret(new PluginUI(world, block, uis, ui, ui_type)); - ret->_features = world->lv2_features().lv2_features( - world, const_cast<BlockModel*>(block.get())); - - return ret; -} - -bool -PluginUI::instantiate() -{ - const URIs& uris = _world->uris(); - const std::string plugin_uri = _block->plugin()->uri(); - LilvWorld* lworld = _world->lilv_world(); - - // Load seeAlso files to access data like portNotification descriptions - lilv_world_load_resource(lworld, lilv_ui_get_uri(_ui)); - - /* Subscribe (enable broadcast) for any requested port notifications. This - must be done before instantiation so responses to any events sent by the - UI's init() will be sent back to this client. */ - LilvNode* ui_portNotification = lilv_new_uri(lworld, LV2_UI__portNotification); - LilvNode* ui_plugin = lilv_new_uri(lworld, LV2_UI__plugin); - LilvNodes* notes = lilv_world_find_nodes( - lworld, lilv_ui_get_uri(_ui), ui_portNotification, nullptr); - LILV_FOREACH(nodes, n, notes) { - const LilvNode* note = lilv_nodes_get(notes, n); - const LilvNode* sym = lilv_world_get(lworld, note, uris.lv2_symbol, nullptr); - const LilvNode* plug = lilv_world_get(lworld, note, ui_plugin, nullptr); - if (!plug) { - _world->log().error(fmt("%1% UI %2% notification missing plugin\n") - % plugin_uri % lilv_node_as_string(_ui_node)); - } else if (!sym) { - _world->log().error(fmt("%1% UI %2% notification missing symbol\n") - % plugin_uri % lilv_node_as_string(_ui_node)); - } else if (!lilv_node_is_uri(plug)) { - _world->log().error(fmt("%1% UI %2% notification has non-URI plugin\n") - % plugin_uri % lilv_node_as_string(_ui_node)); - } else if (!strcmp(lilv_node_as_uri(plug), plugin_uri.c_str())) { - // Notification is valid and for this plugin - uint32_t index = lv2_ui_port_index(this, lilv_node_as_string(sym)); - if (index != LV2UI_INVALID_PORT_INDEX) { - lv2_ui_subscribe(this, index, 0, nullptr); - _subscribed_ports.insert(index); - } - } - } - lilv_nodes_free(notes); - lilv_node_free(ui_plugin); - lilv_node_free(ui_portNotification); - - const char* bundle_uri = lilv_node_as_uri(lilv_ui_get_bundle_uri(_ui)); - const char* binary_uri = lilv_node_as_uri(lilv_ui_get_binary_uri(_ui)); - char* bundle_path = lilv_file_uri_parse(bundle_uri, nullptr); - char* binary_path = lilv_file_uri_parse(binary_uri, nullptr); - - // Instantiate the actual plugin UI via Suil - _instance = suil_instance_new( - PluginUI::ui_host, - this, - LV2_UI__GtkUI, - plugin_uri.c_str(), - lilv_node_as_uri(lilv_ui_get_uri(_ui)), - lilv_node_as_uri(_ui_type), - bundle_path, - binary_path, - _features->array()); - - lilv_free(binary_path); - lilv_free(bundle_path); - - if (!_instance) { - _world->log().error("Failed to instantiate LV2 UI\n"); - // Cancel any subscriptions - for (uint32_t i : _subscribed_ports) { - lv2_ui_unsubscribe(this, i, 0, nullptr); - } - return false; - } - - return true; -} - -SuilWidget -PluginUI::get_widget() -{ - return (SuilWidget*)suil_instance_get_widget(_instance); -} - -void -PluginUI::port_event(uint32_t port_index, - uint32_t buffer_size, - uint32_t format, - const void* buffer) -{ - if (_instance) { - suil_instance_port_event( - _instance, port_index, buffer_size, format, buffer); - } else { - _world->log().warn("LV2 UI port event with no instance\n"); - } -} - -bool -PluginUI::is_resizable() const -{ - LilvWorld* w = _world->lilv_world(); - const LilvNode* s = _ui_node; - LilvNode* p = lilv_new_uri(w, LV2_CORE__optionalFeature); - LilvNode* fs = lilv_new_uri(w, LV2_UI__fixedSize); - LilvNode* nrs = lilv_new_uri(w, LV2_UI__noUserResize); - - LilvNodes* fs_matches = lilv_world_find_nodes(w, s, p, fs); - LilvNodes* nrs_matches = lilv_world_find_nodes(w, s, p, nrs); - - lilv_nodes_free(nrs_matches); - lilv_nodes_free(fs_matches); - lilv_node_free(nrs); - lilv_node_free(fs); - lilv_node_free(p); - - return !fs_matches && !nrs_matches; -} - -} // namespace Client -} // namespace Ingen diff --git a/src/client/PortModel.cpp b/src/client/PortModel.cpp deleted file mode 100644 index 5c9a8c77..00000000 --- a/src/client/PortModel.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/client/BlockModel.hpp" -#include "ingen/client/PortModel.hpp" - -namespace Ingen { -namespace Client { - -void -PortModel::on_property(const URI& uri, const Atom& value) -{ - if (uri == _uris.ingen_activity) { - // Don't store activity, it is transient - signal_activity().emit(value); - return; - } - - ObjectModel::on_property(uri, value); - - if (uri == _uris.ingen_value) { - signal_value_changed().emit(value); - } -} - -bool -PortModel::supports(const URIs::Quark& value_type) const -{ - return has_property(_uris.atom_supports, value_type); -} - -bool -PortModel::port_property(const URIs::Quark& uri) const -{ - return has_property(_uris.lv2_portProperty, uri); -} - -bool -PortModel::is_uri() const -{ - // FIXME: Resource::has_property doesn't work, URI != URID - for (auto p : properties()) { - if (p.second.type() == _uris.atom_URID && - static_cast<LV2_URID>(p.second.get<int32_t>()) == _uris.atom_URID) { - return true; - } - } - return false; -} - -void -PortModel::set(SPtr<ObjectModel> model) -{ - ObjectModel::set(model); - - SPtr<PortModel> port = dynamic_ptr_cast<PortModel>(model); - if (port) { - _index = port->_index; - _direction = port->_direction; - _signal_value_changed.emit(get_property(_uris.ingen_value)); - } -} - -} // namespace Client -} // namespace Ingen diff --git a/src/client/ingen_client.cpp b/src/client/ingen_client.cpp deleted file mode 100644 index fe9d6605..00000000 --- a/src/client/ingen_client.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Module.hpp" -#include "ingen/World.hpp" - -#include "ingen_config.h" - -struct IngenClientModule : public Ingen::Module { - void load(Ingen::World* world) {} -}; - -extern "C" { - -Ingen::Module* -ingen_module_load() -{ - return new IngenClientModule(); -} - -} // extern "C" diff --git a/src/client/wscript b/src/client/wscript deleted file mode 100644 index df575c0d..00000000 --- a/src/client/wscript +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python -from waflib.extras import autowaf as autowaf - -def build(bld): - obj = bld(features = 'cxx cxxshlib', - includes = ['../..'], - export_includes = ['../..'], - name = 'libingen_client', - target = 'ingen_client', - install_path = '${LIBDIR}', - use = 'libingen') - autowaf.use_lib(bld, obj, 'GLIBMM LV2 LILV SUIL RAUL SERD SORD SIGCPP') - - obj.source = ''' - BlockModel.cpp - ClientStore.cpp - GraphModel.cpp - ObjectModel.cpp - PluginModel.cpp - PluginUI.cpp - PortModel.cpp - ingen_client.cpp - ''' diff --git a/src/gui/App.cpp b/src/gui/App.cpp deleted file mode 100644 index 9f1a29ca..00000000 --- a/src/gui/App.cpp +++ /dev/null @@ -1,499 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <fstream> -#include <string> -#include <utility> - -#include <boost/variant/get.hpp> -#include <gtk/gtkwindow.h> -#include <gtkmm/stock.h> - -#include "ganv/Edge.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/EngineBase.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/QueuedInterface.hpp" -#include "ingen/StreamWriter.hpp" -#include "ingen/World.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" -#include "ingen/client/ObjectModel.hpp" -#include "ingen/client/SigClientInterface.hpp" -#include "ingen/runtime_paths.hpp" -#include "lilv/lilv.h" -#include "raul/Path.hpp" -#include "suil/suil.h" - -#include "App.hpp" -#include "ConnectWindow.hpp" -#include "GraphTreeWindow.hpp" -#include "GraphWindow.hpp" -#include "LoadPluginWindow.hpp" -#include "MessagesWindow.hpp" -#include "NodeModule.hpp" -#include "Port.hpp" -#include "RDFS.hpp" -#include "Style.hpp" -#include "SubgraphModule.hpp" -#include "ThreadedLoader.hpp" -#include "WidgetFactory.hpp" -#include "WindowFactory.hpp" -#include "rgba.hpp" - -namespace Raul { class Deletable; } - -namespace Ingen { - -namespace Client { class PluginModel; } - -using namespace Client; - -namespace GUI { - -class Port; - -Gtk::Main* App::_main = nullptr; - -App::App(Ingen::World* world) - : _style(new Style(*this)) - , _about_dialog(nullptr) - , _window_factory(new WindowFactory(*this)) - , _world(world) - , _sample_rate(48000) - , _block_length(1024) - , _n_threads(1) - , _mean_run_load(0.0f) - , _min_run_load(0.0f) - , _max_run_load(0.0f) - , _enable_signal(true) - , _requested_plugins(false) - , _is_plugin(false) -{ - _world->conf().load_default("ingen", "gui.ttl"); - - WidgetFactory::get_widget_derived("connect_win", _connect_window); - WidgetFactory::get_widget_derived("messages_win", _messages_window); - WidgetFactory::get_widget_derived("graph_tree_win", _graph_tree_window); - WidgetFactory::get_widget("about_win", _about_dialog); - _connect_window->init_dialog(*this); - _messages_window->init_window(*this); - _graph_tree_window->init_window(*this); - _about_dialog->property_program_name() = "Ingen"; - _about_dialog->property_logo_icon_name() = "ingen"; - - PluginModel::set_rdf_world(*world->rdf_world()); - PluginModel::set_lilv_world(world->lilv_world()); - - using namespace std::placeholders; - world->log().set_sink(std::bind(&MessagesWindow::log, _messages_window, _1, _2, _3)); -} - -App::~App() -{ - delete _style; - delete _window_factory; -} - -SPtr<App> -App::create(Ingen::World* world) -{ - suil_init(&world->argc(), &world->argv(), SUIL_ARG_NONE); - - // Add RC file for embedded GUI Gtk style - const std::string rc_path = Ingen::data_file_path("ingen_style.rc"); - Gtk::RC::add_default_file(rc_path); - - _main = Gtk::Main::instance(); - if (!_main) { - Glib::set_application_name("Ingen"); - gtk_window_set_default_icon_name("ingen"); - _main = new Gtk::Main(&world->argc(), &world->argv()); - } - - App* app = new App(world); - - // Load configuration settings - app->style()->load_settings(); - app->style()->apply_settings(); - - // Set default window icon - app->_about_dialog->property_program_name() = "Ingen"; - app->_about_dialog->property_logo_icon_name() = "ingen"; - gtk_window_set_default_icon_name("ingen"); - - return SPtr<App>(app); -} - -void -App::run() -{ - _connect_window->start(*this, world()); - - // Run main iterations here until we're attached to the engine. Otherwise - // with 'ingen -egl' we'd get a bunch of notifications about load - // immediately before even knowing about the root graph or plugins) - while (!_connect_window->attached()) { - if (_main->iteration()) { - break; - } - } - - _main->run(); -} - -void -App::attach(SPtr<Ingen::Interface> client) -{ - assert(!_client); - assert(!_store); - assert(!_loader); - - if (_world->engine()) { - _world->engine()->register_client(client); - } - - _client = client; - _store = SPtr<ClientStore>(new ClientStore(_world->uris(), _world->log(), sig_client())); - _loader = SPtr<ThreadedLoader>(new ThreadedLoader(*this, _world->interface())); - if (!_world->store()) { - _world->set_store(_store); - } - - if (_world->conf().option("dump").get<int32_t>()) { - _dumper = SPtr<StreamWriter>(new StreamWriter(_world->uri_map(), - _world->uris(), - URI("ingen:/client"), - stderr, - ColorContext::Color::CYAN)); - - sig_client()->signal_message().connect( - sigc::mem_fun(*_dumper.get(), &StreamWriter::message)); - } - - _graph_tree_window->init(*this, *_store); - sig_client()->signal_message().connect(sigc::mem_fun(this, &App::message)); -} - -void -App::detach() -{ - if (_world->interface()) { - _window_factory->clear(); - _store->clear(); - - _loader.reset(); - _store.reset(); - _client.reset(); - _world->set_interface(SPtr<Interface>()); - } -} - -void -App::request_plugins_if_necessary() -{ - if (!_requested_plugins) { - _world->interface()->get(URI("ingen:/plugins")); - _requested_plugins = true; - } -} - -SPtr<SigClientInterface> -App::sig_client() -{ - SPtr<QueuedInterface> qi = dynamic_ptr_cast<QueuedInterface>(_client); - if (qi) { - return dynamic_ptr_cast<SigClientInterface>(qi->sink()); - } - return dynamic_ptr_cast<SigClientInterface>(_client); -} - -SPtr<Serialiser> -App::serialiser() -{ - return _world->serialiser(); -} - -void -App::message(const Message& msg) -{ - if (const Response* const r = boost::get<Response>(&msg)) { - response(r->id, r->status, r->subject); - } else if (const Error* const e = boost::get<Error>(&msg)) { - error_message(e->message); - } else if (const Put* const p = boost::get<Put>(&msg)) { - put(p->uri, p->properties, p->ctx); - } else if (const SetProperty* const s = boost::get<SetProperty>(&msg)) { - property_change(s->subject, s->predicate, s->value, s->ctx); - } -} - -void -App::response(int32_t id, Status status, const std::string& subject) -{ - if (status != Status::SUCCESS) { - std::string msg = ingen_status_string(status); - if (!subject.empty()) { - msg += ": " + subject; - } - error_message(msg); - } -} - -void -App::error_message(const std::string& str) -{ - _messages_window->post_error(str); -} - -void -App::set_property(const URI& subject, - const URI& key, - const Atom& value, - Resource::Graph ctx) -{ - // Send message to server - interface()->set_property(subject, key, value, ctx); - - /* The server does not feed back set messages (kludge to prevent control - feedback and bandwidth wastage, see Delta.cpp). So, assume everything - went as planned here and fire the signal ourselves as if the server - feedback came back immediately. */ - if (key != uris().ingen_activity) { - sig_client()->signal_message().emit(SetProperty{0, subject, key, value, ctx}); - } -} - -void -App::set_tooltip(Gtk::Widget* widget, const LilvNode* node) -{ - const std::string comment = RDFS::comment(_world, node); - if (!comment.empty()) { - widget->set_tooltip_text(comment); - } -} - -void -App::put(const URI& uri, - const Properties& properties, - Resource::Graph ctx) -{ - _enable_signal = false; - for (const auto& p : properties) { - property_change(uri, p.first, p.second); - } - _enable_signal = true; - _status_text = status_text(); - signal_status_text_changed.emit(_status_text); -} - -void -App::property_change(const URI& subject, - const URI& key, - const Atom& value, - Resource::Graph ctx) -{ - if (subject != URI("ingen:/engine")) { - return; - } else if (key == uris().param_sampleRate && value.type() == forge().Int) { - _sample_rate = value.get<int32_t>(); - } else if (key == uris().bufsz_maxBlockLength && value.type() == forge().Int) { - _block_length = value.get<int32_t>(); - } else if (key == uris().ingen_numThreads && value.type() == forge().Int) { - _n_threads = value.get<int>(); - } else if (key == uris().ingen_minRunLoad && value.type() == forge().Float) { - _min_run_load = value.get<float>(); - } else if (key == uris().ingen_meanRunLoad && value.type() == forge().Float) { - _mean_run_load = value.get<float>(); - } else if (key == uris().ingen_maxRunLoad && value.type() == forge().Float) { - _max_run_load = value.get<float>(); - } else { - _world->log().warn(fmt("Unknown engine property %1%\n") % key); - return; - } - - if (_enable_signal) { - _status_text = status_text(); - signal_status_text_changed.emit(_status_text); - } -} - -static std::string -fraction_label(float f) -{ - static const uint32_t GREEN = 0x4A8A0EFF; - static const uint32_t RED = 0x960909FF; - - const uint32_t col = rgba_interpolate(GREEN, RED, std::min(f, 1.0f)); - char col_str[8]; - snprintf(col_str, sizeof(col_str), "%02X%02X%02X", - RGBA_R(col), RGBA_G(col), RGBA_B(col)); - return (fmt("<span color='#%s'>%d%%</span>") % col_str % (f * 100)).str(); -} - -std::string -App::status_text() const -{ - return (fmt("%2.1f kHz / %.1f ms, %s, %s DSP") - % (_sample_rate / 1e3f) - % (_block_length * 1e3f / (float)_sample_rate) - % ((_n_threads == 1) - ? "1 thread" - : (fmt("%1% threads") % _n_threads).str()) - % fraction_label(_max_run_load)).str(); -} - -void -App::port_activity(Port* port) -{ - std::pair<ActivityPorts::iterator, bool> inserted = _activity_ports.emplace(port, false); - if (inserted.second) { - inserted.first->second = false; - } - - port->set_highlighted(true); -} - -void -App::activity_port_destroyed(Port* port) -{ - auto i = _activity_ports.find(port); - if (i != _activity_ports.end()) { - _activity_ports.erase(i); - } -} - -bool -App::animate() -{ - for (auto i = _activity_ports.begin(); i != _activity_ports.end(); ) { - auto next = i; - ++next; - - if ((*i).second) { // saw it last time, unhighlight and pop - (*i).first->set_highlighted(false); - _activity_ports.erase(i); - } else { - (*i).second = true; - } - - i = next; - } - - return true; -} - -/******** Event Handlers ************/ - -void -App::register_callbacks() -{ - Glib::signal_timeout().connect( - sigc::mem_fun(*this, &App::gtk_main_iteration), 33, G_PRIORITY_DEFAULT); -} - -bool -App::gtk_main_iteration() -{ - if (!_client) { - return false; - } - - animate(); - - if (_messages_window) { - _messages_window->flush(); - } - - _enable_signal = false; - if (_world->engine()) { - if (!_world->engine()->main_iteration()) { - Gtk::Main::quit(); - return false; - } - } else { - dynamic_ptr_cast<QueuedInterface>(_client)->emit(); - } - _enable_signal = true; - - return true; -} - -void -App::show_about() -{ - _about_dialog->run(); - _about_dialog->hide(); -} - -/** Prompt (if necessary) and quit application (if confirmed). - * @return true iff the application quit. - */ -bool -App::quit(Gtk::Window* dialog_parent) -{ - bool quit = true; - if (_world->engine() && _connect_window->attached()) { - Gtk::MessageDialog d( - "The engine is running in this process. Quitting will terminate Ingen." - "\n\n" "Are you sure you want to quit?", - true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true); - if (dialog_parent) { - d.set_transient_for(*dialog_parent); - } - d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE); - quit = (d.run() == Gtk::RESPONSE_CLOSE); - } - - if (!quit) { - return false; - } - - Gtk::Main::quit(); - - try { - const std::string path = _world->conf().save( - _world->uri_map(), "ingen", "gui.ttl", Configuration::GUI); - std::cout << (fmt("Saved GUI settings to %1%\n") % path); - } catch (const std::exception& e) { - std::cerr << (fmt("Error saving GUI settings (%1%)\n") - % e.what()); - } - - return true; -} - -bool -App::can_control(const Client::PortModel* port) const -{ - return port->is_a(uris().lv2_ControlPort) - || port->is_a(uris().lv2_CVPort) - || (port->is_a(uris().atom_AtomPort) - && (port->supports(uris().atom_Float) - || port->supports(uris().atom_String))); -} - -uint32_t -App::sample_rate() const -{ - return _sample_rate; -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/App.hpp b/src/gui/App.hpp deleted file mode 100644 index 75661449..00000000 --- a/src/gui/App.hpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_APP_HPP -#define INGEN_GUI_APP_HPP - -#include <unordered_map> -#include <string> - -#include <gtkmm/aboutdialog.h> -#include <gtkmm/main.h> -#include <gtkmm/window.h> - -#include "ingen/Atom.hpp" -#include "ingen/Message.hpp" -#include "ingen/Resource.hpp" -#include "ingen/Status.hpp" -#include "ingen/World.hpp" -#include "ingen/ingen.h" -#include "ingen/types.hpp" -#include "lilv/lilv.h" -#include "raul/Deletable.hpp" - -namespace Ingen { - -class Interface; -class Log; -class Port; -class Serialiser; -class StreamWriter; -class World; - -namespace Client { - -class ClientStore; -class GraphModel; -class PluginModel; -class PortModel; -class SigClientInterface; - -} - -namespace GUI { - -class ConnectWindow; -class GraphCanvas; -class GraphTreeView; -class GraphTreeWindow; -class MessagesWindow; -class Port; -class Style; -class ThreadedLoader; -class WindowFactory; - -/** Ingen Gtk Application. - * \ingroup GUI - */ -class INGEN_API App -{ -public: - ~App(); - - void error_message(const std::string& str); - - void attach(SPtr<Ingen::Interface> client); - - void detach(); - - void request_plugins_if_necessary(); - - void register_callbacks(); - bool gtk_main_iteration(); - - void show_about(); - bool quit(Gtk::Window* dialog_parent); - - void port_activity(Port* port); - void activity_port_destroyed(Port* port); - bool can_control(const Client::PortModel* port) const; - - bool signal() const { return _enable_signal; } - void enable_signals(bool b) { _enable_signal = b; } - bool disable_signals() { - bool old = _enable_signal; - _enable_signal = false; - return old; - } - - void set_property(const URI& subject, - const URI& key, - const Atom& value, - Resource::Graph ctx = Resource::Graph::DEFAULT); - - /** Set the tooltip for a widget from its RDF documentation. */ - void set_tooltip(Gtk::Widget* widget, const LilvNode* node); - - uint32_t sample_rate() const; - - bool is_plugin() const { return _is_plugin; } - void set_is_plugin(bool b) { _is_plugin = b; } - - ConnectWindow* connect_window() const { return _connect_window; } - MessagesWindow* messages_dialog() const { return _messages_window; } - GraphTreeWindow* graph_tree() const { return _graph_tree_window; } - Style* style() const { return _style; } - WindowFactory* window_factory() const { return _window_factory; } - - Ingen::Forge& forge() const { return _world->forge(); } - SPtr<Ingen::Interface> interface() const { return _world->interface(); } - SPtr<Ingen::Interface> client() const { return _client; } - SPtr<Client::ClientStore> store() const { return _store; } - SPtr<ThreadedLoader> loader() const { return _loader; } - - SPtr<Client::SigClientInterface> sig_client(); - - SPtr<Serialiser> serialiser(); - - static SPtr<App> create(Ingen::World* world); - - void run(); - - std::string status_text() const; - - sigc::signal<void, const std::string&> signal_status_text_changed; - - inline Ingen::World* world() const { return _world; } - inline Ingen::URIs& uris() const { return _world->uris(); } - inline Ingen::Log& log() const { return _world->log(); } - -protected: - explicit App(Ingen::World* world); - - void message(const Ingen::Message& msg); - - bool animate(); - void response(int32_t id, Ingen::Status status, const std::string& subject); - - void put(const URI& uri, - const Properties& properties, - Resource::Graph ctx); - - void property_change(const URI& subject, - const URI& key, - const Atom& value, - Resource::Graph ctx = Resource::Graph::DEFAULT); - - static Gtk::Main* _main; - - SPtr<Ingen::Interface> _client; - SPtr<Client::ClientStore> _store; - SPtr<ThreadedLoader> _loader; - SPtr<StreamWriter> _dumper; - - Style* _style; - - ConnectWindow* _connect_window; - MessagesWindow* _messages_window; - GraphTreeWindow* _graph_tree_window; - Gtk::AboutDialog* _about_dialog; - WindowFactory* _window_factory; - - Ingen::World* _world; - - int32_t _sample_rate; - int32_t _block_length; - int32_t _n_threads; - float _mean_run_load; - float _min_run_load; - float _max_run_load; - std::string _status_text; - - typedef std::unordered_map<Port*, bool> ActivityPorts; - ActivityPorts _activity_ports; - - bool _enable_signal; - bool _requested_plugins; - bool _is_plugin; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_APP_HPP diff --git a/src/gui/Arc.cpp b/src/gui/Arc.cpp deleted file mode 100644 index c14b2e88..00000000 --- a/src/gui/Arc.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "Arc.hpp" -#include "ingen/client/ArcModel.hpp" -#include "ingen/client/BlockModel.hpp" - -#define NS_INTERNALS "http://drobilla.net/ns/ingen-internals#" - -namespace Ingen { -namespace GUI { - -Arc::Arc(Ganv::Canvas& canvas, - SPtr<const Client::ArcModel> model, - Ganv::Node* src, - Ganv::Node* dst) - : Ganv::Edge(canvas, src, dst) - , _arc_model(model) -{ - SPtr<const Client::ObjectModel> tparent = model->tail()->parent(); - SPtr<const Client::BlockModel> tparent_block; - if ((tparent_block = dynamic_ptr_cast<const Client::BlockModel>(tparent))) { - if (tparent_block->plugin_uri() == NS_INTERNALS "BlockDelay") { - g_object_set(_gobj, "dash-length", 4.0, NULL); - set_constraining(false); - } - } -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/Arc.hpp b/src/gui/Arc.hpp deleted file mode 100644 index 382ca305..00000000 --- a/src/gui/Arc.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_ARC_HPP -#define INGEN_GUI_ARC_HPP - -#include <cassert> - -#include "ganv/Edge.hpp" -#include "ingen/types.hpp" - -namespace Ingen { - -namespace Client { class ArcModel; } - -namespace GUI { - -/** An Arc (directed edge) in a Graph. - * - * \ingroup GUI - */ -class Arc : public Ganv::Edge -{ -public: - Arc(Ganv::Canvas& canvas, - SPtr<const Client::ArcModel> model, - Ganv::Node* src, - Ganv::Node* dst); - - SPtr<const Client::ArcModel> model() const { return _arc_model; } - -private: - SPtr<const Client::ArcModel> _arc_model; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_ARC_HPP diff --git a/src/gui/BreadCrumbs.cpp b/src/gui/BreadCrumbs.cpp deleted file mode 100644 index ae7882e3..00000000 --- a/src/gui/BreadCrumbs.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <list> -#include <string> - -#include <boost/variant/get.hpp> - -#include "ingen/client/SigClientInterface.hpp" - -#include "App.hpp" -#include "BreadCrumbs.hpp" - -namespace Ingen { -namespace GUI { - -using std::string; - -BreadCrumbs::BreadCrumbs(App& app) - : Gtk::HBox() - , _active_path("/") - , _full_path("/") - , _enable_signal(true) -{ - app.sig_client()->signal_message().connect( - sigc::mem_fun(this, &BreadCrumbs::message)); - - set_can_focus(false); -} - -SPtr<GraphView> -BreadCrumbs::view(const Raul::Path& path) -{ - for (const auto& b : _breadcrumbs) { - if (b->path() == path) { - return b->view(); - } - } - - return SPtr<GraphView>(); -} - -/** Sets up the crumbs to display `path`. - * - * If `path` is already part of the shown path, it will be selected and the - * children preserved. - */ -void -BreadCrumbs::build(Raul::Path path, SPtr<GraphView> view) -{ - bool old_enable_signal = _enable_signal; - _enable_signal = false; - - if (!_breadcrumbs.empty() && (path.is_parent_of(_full_path) || path == _full_path)) { - // Moving to a path we already contain, just switch the active button - for (const auto& b : _breadcrumbs) { - if (b->path() == path) { - b->set_active(true); - if (!b->view()) { - b->set_view(view); - } - - // views are expensive, having two around for the same graph is a bug - assert(b->view() == view); - - } else { - b->set_active(false); - } - } - - _active_path = path; - _enable_signal = old_enable_signal; - - } else if (!_breadcrumbs.empty() && path.is_child_of(_full_path)) { - // Moving to a child of the full path, just append crumbs (preserve view cache) - - string suffix = path.substr(_full_path.length()); - while (suffix.length() > 0) { - if (suffix[0] == '/') { - suffix = suffix.substr(1); - } - const string name = suffix.substr(0, suffix.find("/")); - _full_path = _full_path.child(Raul::Symbol(name)); - BreadCrumb* but = create_crumb(_full_path, view); - pack_start(*but, false, false, 1); - _breadcrumbs.push_back(but); - but->show(); - if (suffix.find("/") == string::npos) { - break; - } else { - suffix = suffix.substr(suffix.find("/")+1); - } - } - - for (const auto& b : _breadcrumbs) { - b->set_active(false); - } - _breadcrumbs.back()->set_active(true); - - } else { - // Rebuild from scratch - // Getting here is bad unless absolutely necessary, since the GraphView cache is lost - - _full_path = path; - _active_path = path; - - // Empty existing breadcrumbs - for (const auto& b : _breadcrumbs) { - remove(*b); - } - _breadcrumbs.clear(); - - // Add root - BreadCrumb* root_but = create_crumb(Raul::Path("/"), view); - pack_start(*root_but, false, false, 1); - _breadcrumbs.push_front(root_but); - root_but->set_active(root_but->path() == _active_path); - - Raul::Path working_path("/"); - string suffix = path.substr(1); - while (suffix.length() > 0) { - if (suffix[0] == '/') { - suffix = suffix.substr(1); - } - const string name = suffix.substr(0, suffix.find("/")); - working_path = working_path.child(Raul::Symbol(name)); - BreadCrumb* but = create_crumb(working_path, view); - pack_start(*but, false, false, 1); - _breadcrumbs.push_back(but); - but->set_active(working_path == _active_path); - but->show(); - if (suffix.find("/") == string::npos) { - break; - } else { - suffix = suffix.substr(suffix.find("/")+1); - } - } - } - - _enable_signal = old_enable_signal; -} - -/** Create a new crumb, assigning it a reference to `view` if their paths - * match, otherwise ignoring `view`. - */ -BreadCrumbs::BreadCrumb* -BreadCrumbs::create_crumb(const Raul::Path& path, - SPtr<GraphView> view) -{ - BreadCrumb* but = manage( - new BreadCrumb(path, - ((view && path == view->graph()->path()) - ? view : SPtr<GraphView>()))); - - but->signal_toggled().connect( - sigc::bind(sigc::mem_fun(this, &BreadCrumbs::breadcrumb_clicked), - but)); - - return but; -} - -void -BreadCrumbs::breadcrumb_clicked(BreadCrumb* crumb) -{ - if (_enable_signal) { - _enable_signal = false; - - if (!crumb->get_active()) { - // Tried to turn off the current active button, bad user, no cookie - crumb->set_active(true); - } else { - signal_graph_selected.emit(crumb->path(), crumb->view()); - if (crumb->path() != _active_path) { - crumb->set_active(false); - } - } - _enable_signal = true; - } -} - -void -BreadCrumbs::message(const Message& msg) -{ - if (const Del* const del = boost::get<Del>(&msg)) { - object_destroyed(del->uri); - } -} - -void -BreadCrumbs::object_destroyed(const URI& uri) -{ - for (auto i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) { - if ((*i)->path() == uri.c_str()) { - // Remove all crumbs after the removed one (inclusive) - for (auto j = i; j != _breadcrumbs.end(); ) { - BreadCrumb* bc = *j; - j = _breadcrumbs.erase(j); - remove(*bc); - } - break; - } - } -} - -void -BreadCrumbs::object_moved(const Raul::Path& old_path, const Raul::Path& new_path) -{ - for (const auto& b : _breadcrumbs) { - if (b->path() == old_path) { - b->set_path(new_path); - } - } -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/BreadCrumbs.hpp b/src/gui/BreadCrumbs.hpp deleted file mode 100644 index 467d3bfc..00000000 --- a/src/gui/BreadCrumbs.hpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_BREADCRUMBS_HPP -#define INGEN_GUI_BREADCRUMBS_HPP - -#include <list> - -#include <gtkmm/box.h> -#include <gtkmm/label.h> -#include <gtkmm/togglebutton.h> - -#include "raul/Path.hpp" - -#include "ingen/client/GraphModel.hpp" -#include "ingen/types.hpp" - -#include "GraphView.hpp" - -namespace Ingen { -namespace GUI { - -/** Collection of breadcrumb buttons forming a path. - * This doubles as a cache for GraphViews. - * - * \ingroup GUI - */ -class BreadCrumbs : public Gtk::HBox -{ -public: - explicit BreadCrumbs(App& app); - - SPtr<GraphView> view(const Raul::Path& path); - - void build(Raul::Path path, SPtr<GraphView> view); - - sigc::signal<void, const Raul::Path&, SPtr<GraphView> > signal_graph_selected; - -private: - /** Breadcrumb button. - * - * Each Breadcrumb stores a reference to a GraphView for quick switching. - * So, the amount of allocated GraphViews at a given time is equal to the - * number of visible breadcrumbs (which is the perfect cache for GUI - * responsiveness balanced with mem consumption). - * - * \ingroup GUI - */ - class BreadCrumb : public Gtk::ToggleButton - { - public: - BreadCrumb(const Raul::Path& path, SPtr<GraphView> view = SPtr<GraphView>()) - : _path(path) - , _view(view) - { - assert(!view || view->graph()->path() == path); - set_border_width(0); - set_path(path); - set_can_focus(false); - show_all(); - } - - void set_view(SPtr<GraphView> view) { - assert(!view || view->graph()->path() == _path); - _view = view; - } - - const Raul::Path& path() const { return _path; } - SPtr<GraphView> view() const { return _view; } - - void set_path(const Raul::Path& path) { - remove(); - const char* text = (path.is_root()) ? "/" : path.symbol(); - Gtk::Label* lab = manage(new Gtk::Label(text)); - lab->set_padding(0, 0); - lab->show(); - add(*lab); - - if (_view && _view->graph()->path() != path) - _view.reset(); - } - - private: - Raul::Path _path; - SPtr<GraphView> _view; - }; - - BreadCrumb* create_crumb(const Raul::Path& path, - SPtr<GraphView> view = SPtr<GraphView>()); - - void breadcrumb_clicked(BreadCrumb* crumb); - - void message(const Message& msg); - void object_destroyed(const URI& uri); - void object_moved(const Raul::Path& old_path, const Raul::Path& new_path); - - Raul::Path _active_path; - Raul::Path _full_path; - bool _enable_signal; - std::list<BreadCrumb*> _breadcrumbs; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_BREADCRUMBS_HPP diff --git a/src/gui/ConnectWindow.cpp b/src/gui/ConnectWindow.cpp deleted file mode 100644 index 458a43dd..00000000 --- a/src/gui/ConnectWindow.cpp +++ /dev/null @@ -1,572 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cstdlib> -#include <limits> -#include <sstream> -#include <string> - -#include <boost/variant/get.hpp> -#include <gtkmm/stock.h> - -#include "raul/Process.hpp" - -#include "ingen/Configuration.hpp" -#include "ingen/EngineBase.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/Module.hpp" -#include "ingen/QueuedInterface.hpp" -#include "ingen/World.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" -#include "ingen/client/SigClientInterface.hpp" -#include "ingen/client/SocketClient.hpp" -#include "ingen_config.h" - -#include "App.hpp" -#include "ConnectWindow.hpp" -#include "WindowFactory.hpp" - -using namespace Ingen::Client; - -namespace Ingen { -namespace GUI { - -ConnectWindow::ConnectWindow(BaseObjectType* cobject, - Glib::RefPtr<Gtk::Builder> xml) - : Dialog(cobject) - , _xml(std::move(xml)) - , _icon(nullptr) - , _progress_bar(nullptr) - , _progress_label(nullptr) - , _url_entry(nullptr) - , _server_radio(nullptr) - , _port_spinbutton(nullptr) - , _launch_radio(nullptr) - , _internal_radio(nullptr) - , _activate_button(nullptr) - , _deactivate_button(nullptr) - , _disconnect_button(nullptr) - , _connect_button(nullptr) - , _quit_button(nullptr) - , _mode(Mode::CONNECT_REMOTE) - , _connect_uri("unix:///tmp/ingen.sock") - , _ping_id(-1) - , _attached(false) - , _finished_connecting(false) - , _widgets_loaded(false) - , _connect_stage(0) - , _quit_flag(false) -{ -} - -void -ConnectWindow::message(const Message& msg) -{ - if (const Response* const r = boost::get<Response>(&msg)) { - ingen_response(r->id, r->status, r->subject); - } else if (const Error* const e = boost::get<Error>(&msg)) { - error(e->message); - } -} - -void -ConnectWindow::error(const std::string& msg) -{ - if (!is_visible()) { - present(); - set_connecting_widget_states(); - } - - if (_progress_label) { - _progress_label->set_text(msg); - } - - if (_app && _app->world()) { - _app->world()->log().error(msg + "\n"); - } -} - -void -ConnectWindow::start(App& app, Ingen::World* world) -{ - _app = &app; - - if (world->engine()) { - _mode = Mode::INTERNAL; - } - - set_connected_to(world->interface()); - connect(bool(world->interface())); -} - -void -ConnectWindow::ingen_response(int32_t id, - Status status, - const std::string& subject) -{ - if (id == _ping_id) { - if (status != Status::SUCCESS) { - error("Failed to get root patch"); - } else { - _attached = true; - } - } -} - -void -ConnectWindow::set_connected_to(SPtr<Ingen::Interface> engine) -{ - _app->world()->set_interface(engine); - - if (!_widgets_loaded) { - return; - } - - if (engine) { - _icon->set(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); - _progress_bar->set_fraction(1.0); - _progress_label->set_text("Connected to engine"); - _url_entry->set_sensitive(false); - _url_entry->set_text(engine->uri().string()); - _connect_button->set_sensitive(false); - _disconnect_button->set_label("gtk-disconnect"); - _disconnect_button->set_sensitive(true); - _port_spinbutton->set_sensitive(false); - _launch_radio->set_sensitive(false); - _internal_radio->set_sensitive(false); - _activate_button->set_sensitive(true); - _deactivate_button->set_sensitive(true); - - } else { - _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); - _progress_bar->set_fraction(0.0); - _connect_button->set_sensitive(true); - _disconnect_button->set_sensitive(false); - _internal_radio->set_sensitive(true); - _server_radio->set_sensitive(true); - _launch_radio->set_sensitive(true); - _activate_button->set_sensitive(false); - _deactivate_button->set_sensitive(false); - - if (_mode == Mode::CONNECT_REMOTE) { - _url_entry->set_sensitive(true); - } else if (_mode == Mode::LAUNCH_REMOTE) { - _port_spinbutton->set_sensitive(true); - } - - _progress_label->set_text(std::string("Disconnected")); - } -} - -void -ConnectWindow::set_connecting_widget_states() -{ - if (!_widgets_loaded) { - return; - } - - _connect_button->set_sensitive(false); - _disconnect_button->set_label("gtk-cancel"); - _disconnect_button->set_sensitive(true); - _server_radio->set_sensitive(false); - _launch_radio->set_sensitive(false); - _internal_radio->set_sensitive(false); - _url_entry->set_sensitive(false); - _port_spinbutton->set_sensitive(false); -} - -bool -ConnectWindow::connect_remote(const URI& uri) -{ - Ingen::World* world = _app->world(); - - SPtr<SigClientInterface> sci(new SigClientInterface()); - SPtr<QueuedInterface> qi(new QueuedInterface(sci)); - - SPtr<Ingen::Interface> iface(world->new_interface(uri, qi)); - if (iface) { - world->set_interface(iface); - _app->attach(qi); - _app->register_callbacks(); - return true; - } - - return false; -} - -/** Set up initial connect stage and launch connect callback. */ -void -ConnectWindow::connect(bool existing) -{ - if (_app->client()) { - error("Already connected"); - return; - } else if (_attached) { - _attached = false; - } - - set_connecting_widget_states(); - _connect_stage = 0; - - Ingen::World* world = _app->world(); - - if (_mode == Mode::CONNECT_REMOTE) { - std::string uri_str = world->conf().option("connect").ptr<char>(); - if (existing) { - uri_str = world->interface()->uri(); - _connect_stage = 1; - SPtr<Client::SocketClient> client = dynamic_ptr_cast<Client::SocketClient>(world->interface()); - if (client) { - _app->attach(client->respondee()); - _app->register_callbacks(); - } else { - error("Connected with invalid client interface type"); - return; - } - } else if (_widgets_loaded) { - uri_str = _url_entry->get_text(); - } - - if (!URI::is_valid(uri_str)) { - error((fmt("Invalid socket URI %1%") % uri_str).str()); - return; - } - - _connect_uri = URI(uri_str); - - } else if (_mode == Mode::LAUNCH_REMOTE) { - const std::string port = std::to_string(_port_spinbutton->get_value_as_int()); - const char* cmd[] = { "ingen", "-e", "-E", port.c_str(), nullptr }; - - if (!Raul::Process::launch(cmd)) { - error("Failed to launch engine process"); - return; - } - - _connect_uri = URI(std::string("tcp://localhost:") + port); - - } else if (_mode == Mode::INTERNAL) { - if (!world->engine()) { - if (!world->load_module("server")) { - error("Failed to load server module"); - return; - } else if (!world->load_module("jack")) { - error("Failed to load jack module"); - return; - } else if (!world->engine()->activate()) { - error("Failed to activate engine"); - return; - } - } - } - - set_connecting_widget_states(); - if (_widgets_loaded) { - _progress_label->set_text("Connecting..."); - } - Glib::signal_timeout().connect( - sigc::mem_fun(this, &ConnectWindow::gtk_callback), 33); -} - -void -ConnectWindow::disconnect() -{ - _connect_stage = -1; - _attached = false; - - _app->detach(); - set_connected_to(SPtr<Ingen::Interface>()); - - if (!_widgets_loaded) { - return; - } - - _activate_button->set_sensitive(false); - _deactivate_button->set_sensitive(false); - - _progress_bar->set_fraction(0.0); - _connect_button->set_sensitive(true); - _disconnect_button->set_sensitive(false); -} - -void -ConnectWindow::activate() -{ - if (!_app->interface()) { - return; - } - - _app->interface()->set_property(URI("ingen:/driver"), - _app->uris().ingen_enabled, - _app->forge().make(true)); -} - -void -ConnectWindow::deactivate() -{ - if (!_app->interface()) { - return; - } - - _app->interface()->set_property(URI("ingen:/driver"), - _app->uris().ingen_enabled, - _app->forge().make(false)); -} - -void -ConnectWindow::on_show() -{ - if (!_widgets_loaded) { - load_widgets(); - } - - if (_attached) { - set_connected_to(_app->interface()); - } - - Gtk::Dialog::on_show(); -} - -void -ConnectWindow::load_widgets() -{ - _xml->get_widget("connect_icon", _icon); - _xml->get_widget("connect_progress_bar", _progress_bar); - _xml->get_widget("connect_progress_label", _progress_label); - _xml->get_widget("connect_server_radiobutton", _server_radio); - _xml->get_widget("connect_url_entry", _url_entry); - _xml->get_widget("connect_launch_radiobutton", _launch_radio); - _xml->get_widget("connect_port_spinbutton", _port_spinbutton); - _xml->get_widget("connect_internal_radiobutton", _internal_radio); - _xml->get_widget("connect_activate_button", _activate_button); - _xml->get_widget("connect_deactivate_button", _deactivate_button); - _xml->get_widget("connect_disconnect_button", _disconnect_button); - _xml->get_widget("connect_connect_button", _connect_button); - _xml->get_widget("connect_quit_button", _quit_button); - - _server_radio->signal_toggled().connect( - sigc::mem_fun(this, &ConnectWindow::server_toggled)); - _launch_radio->signal_toggled().connect( - sigc::mem_fun(this, &ConnectWindow::launch_toggled)); - _internal_radio->signal_clicked().connect( - sigc::mem_fun(this, &ConnectWindow::internal_toggled)); - _activate_button->signal_clicked().connect( - sigc::mem_fun(this, &ConnectWindow::activate)); - _deactivate_button->signal_clicked().connect( - sigc::mem_fun(this, &ConnectWindow::deactivate)); - _disconnect_button->signal_clicked().connect( - sigc::mem_fun(this, &ConnectWindow::disconnect)); - _connect_button->signal_clicked().connect( - sigc::bind(sigc::mem_fun(this, &ConnectWindow::connect), false)); - _quit_button->signal_clicked().connect( - sigc::mem_fun(this, &ConnectWindow::quit_clicked)); - - _url_entry->set_text(_app->world()->conf().option("connect").ptr<char>()); - if (URI::is_valid(_url_entry->get_text())) { - _connect_uri = URI(_url_entry->get_text()); - } - - _port_spinbutton->set_range(1, std::numeric_limits<uint16_t>::max()); - _port_spinbutton->set_increments(1, 100); - _port_spinbutton->set_value( - _app->world()->conf().option("engine-port").get<int32_t>()); - - _progress_bar->set_pulse_step(0.01); - _widgets_loaded = true; - - server_toggled(); -} - -void -ConnectWindow::on_hide() -{ - Gtk::Dialog::on_hide(); - if (_app->window_factory()->num_open_graph_windows() == 0) { - quit(); - } -} - -void -ConnectWindow::quit_clicked() -{ - if (_app->quit(this)) { - _quit_flag = true; - } -} - -void -ConnectWindow::server_toggled() -{ - _url_entry->set_sensitive(true); - _port_spinbutton->set_sensitive(false); - _mode = Mode::CONNECT_REMOTE; -} - -void -ConnectWindow::launch_toggled() -{ - _url_entry->set_sensitive(false); - _port_spinbutton->set_sensitive(true); - _mode = Mode::LAUNCH_REMOTE; -} - -void -ConnectWindow::internal_toggled() -{ - _url_entry->set_sensitive(false); - _port_spinbutton->set_sensitive(false); - _mode = Mode::INTERNAL; -} - -void -ConnectWindow::next_stage() -{ - static const char* labels[] = { - "Connecting...", - "Pinging engine...", - "Attaching to engine...", - "Requesting root graph...", - "Loading plugins...", - "Connected" - }; - - - ++_connect_stage; - if (_widgets_loaded) { - _progress_label->set_text(labels[_connect_stage]); - } -} - -bool -ConnectWindow::gtk_callback() -{ - /* If I call this a "state machine" it's not ugly code any more */ - - if (_quit_flag) { - return false; // deregister this callback - } - - // Timing stuff for repeated attach attempts - timeval now; - gettimeofday(&now, nullptr); - static const timeval start = now; - static timeval last = now; - static unsigned attempts = 0; - - // Show if attempted connection goes on for a noticeable amount of time - if (!is_visible()) { - const float ms_since_start = (now.tv_sec - start.tv_sec) * 1000.0f + - (now.tv_usec - start.tv_usec) * 0.001f; - if (ms_since_start > 500) { - present(); - set_connecting_widget_states(); - } - } - - if (_connect_stage == 0) { - const float ms_since_last = (now.tv_sec - last.tv_sec) * 1000.0f + - (now.tv_usec - last.tv_usec) * 0.001f; - if (ms_since_last >= 250) { - last = now; - if (_mode == Mode::INTERNAL) { - SPtr<SigClientInterface> client(new SigClientInterface()); - _app->world()->interface()->set_respondee(client); - _app->attach(client); - _app->register_callbacks(); - next_stage(); - } else if (connect_remote(_connect_uri)) { - next_stage(); - } - } - } else if (_connect_stage == 1) { - _attached = false; - _app->sig_client()->signal_message().connect( - sigc::mem_fun(this, &ConnectWindow::message)); - - _ping_id = g_random_int_range(1, std::numeric_limits<int32_t>::max()); - _app->interface()->set_response_id(_ping_id); - _app->interface()->get(URI("ingen:/engine")); - last = now; - attempts = 0; - next_stage(); - - } else if (_connect_stage == 2) { - if (_attached) { - next_stage(); - } else { - const float ms_since_last = (now.tv_sec - last.tv_sec) * 1000.0f + - (now.tv_usec - last.tv_usec) * 0.001f; - if (attempts > 10) { - error("Failed to ping engine"); - _connect_stage = -1; - } else if (ms_since_last > 1000) { - _app->interface()->set_response_id(_ping_id); - _app->interface()->get(URI("ingen:/engine")); - last = now; - ++attempts; - } - } - } else if (_connect_stage == 3) { - _app->interface()->get(URI(main_uri().string() + "/")); - next_stage(); - } else if (_connect_stage == 4) { - if (_app->store()->size() > 0) { - SPtr<const GraphModel> root = dynamic_ptr_cast<const GraphModel>( - _app->store()->object(Raul::Path("/"))); - if (root) { - set_connected_to(_app->interface()); - _app->window_factory()->present_graph(root); - next_stage(); - } - } - } else if (_connect_stage == 5) { - hide(); - _connect_stage = 0; // set ourselves up for next time (if there is one) - _finished_connecting = true; - _app->interface()->set_response_id(1); - return false; // deregister this callback - } - - if (_widgets_loaded) { - _progress_bar->pulse(); - } - - if (_connect_stage == -1) { // we were cancelled - if (_widgets_loaded) { - _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); - _progress_bar->set_fraction(0.0); - _connect_button->set_sensitive(true); - _disconnect_button->set_sensitive(false); - _disconnect_button->set_label("gtk-disconnect"); - _progress_label->set_text(std::string("Disconnected")); - } - return false; - } else { - return true; - } -} - -void -ConnectWindow::quit() -{ - _quit_flag = true; - Gtk::Main::quit(); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/ConnectWindow.hpp b/src/gui/ConnectWindow.hpp deleted file mode 100644 index 08560361..00000000 --- a/src/gui/ConnectWindow.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_CONNECTWINDOW_HPP -#define INGEN_GUI_CONNECTWINDOW_HPP - -#include <gtkmm/builder.h> -#include <gtkmm/button.h> -#include <gtkmm/entry.h> -#include <gtkmm/image.h> -#include <gtkmm/label.h> -#include <gtkmm/progressbar.h> -#include <gtkmm/radiobutton.h> -#include <gtkmm/spinbutton.h> - -#include "ingen/types.hpp" -#include "lilv/lilv.h" - -#include "Window.hpp" - -namespace Ingen { -namespace GUI { - -class App; - -/** The initially visible "Connect to engine" window. - * - * This handles actually connecting to the engine and making sure everything - * is ready before really launching the app. - * - * \ingroup GUI - */ -class ConnectWindow : public Dialog -{ -public: - ConnectWindow(BaseObjectType* cobject, - Glib::RefPtr<Gtk::Builder> xml); - - void set_connected_to(SPtr<Ingen::Interface> engine); - void start(App& app, Ingen::World* world); - - bool attached() const { return _finished_connecting; } - bool quit_flag() const { return _quit_flag; } - -private: - enum class Mode { CONNECT_REMOTE, LAUNCH_REMOTE, INTERNAL }; - - void message(const Message& msg); - - void error(const std::string& msg); - - void ingen_response(int32_t id, Status status, const std::string& subject); - - void server_toggled(); - void launch_toggled(); - void internal_toggled(); - - void disconnect(); - void next_stage(); - bool connect_remote(const URI& uri); - void connect(bool existing); - void activate(); - void deactivate(); - void quit_clicked(); - void on_show(); - void on_hide(); - - void load_widgets(); - void set_connecting_widget_states(); - - bool gtk_callback(); - void quit(); - - const Glib::RefPtr<Gtk::Builder> _xml; - - Gtk::Image* _icon; - Gtk::ProgressBar* _progress_bar; - Gtk::Label* _progress_label; - Gtk::Entry* _url_entry; - Gtk::RadioButton* _server_radio; - Gtk::SpinButton* _port_spinbutton; - Gtk::RadioButton* _launch_radio; - Gtk::RadioButton* _internal_radio; - Gtk::Button* _activate_button; - Gtk::Button* _deactivate_button; - Gtk::Button* _disconnect_button; - Gtk::Button* _connect_button; - Gtk::Button* _quit_button; - - Mode _mode; - URI _connect_uri; - int32_t _ping_id; - bool _attached; - bool _finished_connecting; - bool _widgets_loaded; - int _connect_stage; - bool _quit_flag; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_CONNECTWINDOW_HPP diff --git a/src/gui/GraphBox.cpp b/src/gui/GraphBox.cpp deleted file mode 100644 index 6f9969be..00000000 --- a/src/gui/GraphBox.cpp +++ /dev/null @@ -1,922 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <sstream> -#include <string> - -#include <boost/format.hpp> -#include <glib/gstdio.h> -#include <glibmm/fileutils.h> -#include <gtkmm/stock.h> - -#include "ingen/Configuration.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" - -#include "App.hpp" -#include "BreadCrumbs.hpp" -#include "ConnectWindow.hpp" -#include "GraphCanvas.hpp" -#include "GraphTreeWindow.hpp" -#include "GraphView.hpp" -#include "GraphWindow.hpp" -#include "LoadGraphWindow.hpp" -#include "LoadPluginWindow.hpp" -#include "MessagesWindow.hpp" -#include "NewSubgraphWindow.hpp" -#include "Style.hpp" -#include "ThreadedLoader.hpp" -#include "WidgetFactory.hpp" -#include "WindowFactory.hpp" -#include "ingen_config.h" - -#ifdef HAVE_WEBKIT -#include <webkit/webkit.h> -#endif - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -static const int STATUS_CONTEXT_ENGINE = 0; -static const int STATUS_CONTEXT_GRAPH = 1; -static const int STATUS_CONTEXT_HOVER = 2; - -GraphBox::GraphBox(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Gtk::VBox(cobject) - , _app(nullptr) - , _window(nullptr) - , _breadcrumbs(nullptr) - , _has_shown_documentation(false) - , _enable_signal(true) -{ - property_visible() = false; - - xml->get_widget("graph_win_alignment", _alignment); - xml->get_widget("graph_win_status_bar", _status_bar); - xml->get_widget("graph_import_menuitem", _menu_import); - xml->get_widget("graph_save_menuitem", _menu_save); - xml->get_widget("graph_save_as_menuitem", _menu_save_as); - xml->get_widget("graph_export_image_menuitem", _menu_export_image); - xml->get_widget("graph_redo_menuitem", _menu_redo); - xml->get_widget("graph_undo_menuitem", _menu_undo); - xml->get_widget("graph_cut_menuitem", _menu_cut); - xml->get_widget("graph_copy_menuitem", _menu_copy); - xml->get_widget("graph_paste_menuitem", _menu_paste); - xml->get_widget("graph_delete_menuitem", _menu_delete); - xml->get_widget("graph_select_all_menuitem", _menu_select_all); - xml->get_widget("graph_close_menuitem", _menu_close); - xml->get_widget("graph_quit_menuitem", _menu_quit); - xml->get_widget("graph_view_control_window_menuitem", _menu_view_control_window); - xml->get_widget("graph_view_engine_window_menuitem", _menu_view_engine_window); - xml->get_widget("graph_properties_menuitem", _menu_view_graph_properties); - xml->get_widget("graph_parent_menuitem", _menu_parent); - xml->get_widget("graph_refresh_menuitem", _menu_refresh); - xml->get_widget("graph_fullscreen_menuitem", _menu_fullscreen); - xml->get_widget("graph_animate_signals_menuitem", _menu_animate_signals); - xml->get_widget("graph_sprung_layout_menuitem", _menu_sprung_layout); - xml->get_widget("graph_human_names_menuitem", _menu_human_names); - xml->get_widget("graph_show_port_names_menuitem", _menu_show_port_names); - xml->get_widget("graph_zoom_in_menuitem", _menu_zoom_in); - xml->get_widget("graph_zoom_out_menuitem", _menu_zoom_out); - xml->get_widget("graph_zoom_normal_menuitem", _menu_zoom_normal); - xml->get_widget("graph_zoom_full_menuitem", _menu_zoom_full); - xml->get_widget("graph_increase_font_size_menuitem", _menu_increase_font_size); - xml->get_widget("graph_decrease_font_size_menuitem", _menu_decrease_font_size); - xml->get_widget("graph_normal_font_size_menuitem", _menu_normal_font_size); - xml->get_widget("graph_doc_pane_menuitem", _menu_show_doc_pane); - xml->get_widget("graph_status_bar_menuitem", _menu_show_status_bar); - xml->get_widget("graph_arrange_menuitem", _menu_arrange); - xml->get_widget("graph_view_messages_window_menuitem", _menu_view_messages_window); - xml->get_widget("graph_view_graph_tree_window_menuitem", _menu_view_graph_tree_window); - xml->get_widget("graph_help_about_menuitem", _menu_help_about); - xml->get_widget("graph_documentation_paned", _doc_paned); - xml->get_widget("graph_documentation_scrolledwindow", _doc_scrolledwindow); - - _menu_view_control_window->property_sensitive() = false; - _menu_import->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_import)); - _menu_save->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_save)); - _menu_save_as->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_save_as)); - _menu_export_image->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_export_image)); - _menu_redo->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_redo)); - _menu_undo->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_undo)); - _menu_copy->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_copy)); - _menu_paste->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_paste)); - _menu_delete->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_delete)); - _menu_select_all->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_select_all)); - _menu_close->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_close)); - _menu_quit->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_quit)); - _menu_parent->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_parent_activated)); - _menu_refresh->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_refresh_activated)); - _menu_fullscreen->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_fullscreen_toggled)); - _menu_animate_signals->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_animate_signals_toggled)); - _menu_sprung_layout->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_sprung_layout_toggled)); - _menu_human_names->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_human_names_toggled)); - _menu_show_doc_pane->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_doc_pane_toggled)); - _menu_show_status_bar->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_status_bar_toggled)); - _menu_show_port_names->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_port_names_toggled)); - _menu_arrange->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_arrange)); - _menu_zoom_in->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_zoom_in)); - _menu_zoom_out->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_zoom_out)); - _menu_zoom_normal->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_zoom_normal)); - _menu_zoom_full->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_zoom_full)); - _menu_increase_font_size->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_increase_font_size)); - _menu_decrease_font_size->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_decrease_font_size)); - _menu_normal_font_size->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_normal_font_size)); - _menu_view_engine_window->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_show_engine)); - _menu_view_graph_properties->signal_activate().connect( - sigc::mem_fun(this, &GraphBox::event_show_properties)); - - Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get(); - clipboard->signal_owner_change().connect( - sigc::mem_fun(this, &GraphBox::event_clipboard_changed)); - -#ifdef __APPLE__ - _menu_paste->set_sensitive(true); -#endif - - _status_label = Gtk::manage(new Gtk::Label("STATUS")); - _status_bar->pack_start(*_status_label, false, true, 0); - _status_label->show(); -} - -GraphBox::~GraphBox() -{ - delete _breadcrumbs; -} - -SPtr<GraphBox> -GraphBox::create(App& app, SPtr<const GraphModel> graph) -{ - GraphBox* result = nullptr; - Glib::RefPtr<Gtk::Builder> xml = WidgetFactory::create("graph_win"); - xml->get_widget_derived("graph_win_vbox", result); - result->init_box(app); - result->set_graph(graph, SPtr<GraphView>()); - - if (app.is_plugin()) { - result->_menu_close->set_sensitive(false); - result->_menu_quit->set_sensitive(false); - } - - return SPtr<GraphBox>(result); -} - -void -GraphBox::init_box(App& app) -{ - _app = &app; - - const URI engine_uri(_app->interface()->uri()); - if (engine_uri == "ingen:/clients/event_writer") { - _status_bar->push("Running internal engine", STATUS_CONTEXT_ENGINE); - } else { - _status_bar->push( - (fmt("Connected to %1%") % engine_uri).str(), - STATUS_CONTEXT_ENGINE); - } - - _menu_view_messages_window->signal_activate().connect( - sigc::mem_fun<void>(_app->messages_dialog(), &MessagesWindow::present)); - _menu_view_graph_tree_window->signal_activate().connect( - sigc::mem_fun<void>(_app->graph_tree(), &GraphTreeWindow::present)); - - _menu_help_about->signal_activate().connect( - sigc::hide_return(sigc::mem_fun(_app, &App::show_about))); - - _breadcrumbs = new BreadCrumbs(*_app); - _breadcrumbs->signal_graph_selected.connect( - sigc::mem_fun(this, &GraphBox::set_graph_from_path)); - - _status_label->set_markup(app.status_text()); - app.signal_status_text_changed.connect( - sigc::mem_fun(*this, &GraphBox::set_status_text)); -} - -void -GraphBox::set_status_text(const std::string& text) -{ - _status_label->set_markup(text); -} - -void -GraphBox::set_graph_from_path(const Raul::Path& path, SPtr<GraphView> view) -{ - if (view) { - assert(view->graph()->path() == path); - _app->window_factory()->present_graph(view->graph(), _window, view); - } else { - SPtr<const GraphModel> model = dynamic_ptr_cast<const GraphModel>( - _app->store()->object(path)); - if (model) { - _app->window_factory()->present_graph(model, _window); - } - } -} - -/** Sets the graph for this box and initializes everything. - * - * If `view` is NULL, a new view will be created. - */ -void -GraphBox::set_graph(SPtr<const GraphModel> graph, - SPtr<GraphView> view) -{ - if (!graph || graph == _graph) { - return; - } - - _enable_signal = false; - - new_port_connection.disconnect(); - removed_port_connection.disconnect(); - edit_mode_connection.disconnect(); - _entered_connection.disconnect(); - _left_connection.disconnect(); - - _status_bar->pop(STATUS_CONTEXT_GRAPH); - - _graph = graph; - _view = view; - - if (!_view) { - _view = _breadcrumbs->view(graph->path()); - } - - if (!_view) { - _view = GraphView::create(*_app, graph); - } - - assert(_view); - - graph->signal_property().connect( - sigc::mem_fun(this, &GraphBox::property_changed)); - - if (!_view->canvas()->supports_sprung_layout()) { - _menu_sprung_layout->set_active(false); - _menu_sprung_layout->set_sensitive(false); - } - - // Add view to our alignment - if (_view->get_parent()) { - _view->get_parent()->remove(*_view.get()); - } - - _alignment->remove(); - _alignment->add(*_view.get()); - - if (_breadcrumbs->get_parent()) { - _breadcrumbs->get_parent()->remove(*_breadcrumbs); - } - - _view->breadcrumb_container()->remove(); - _view->breadcrumb_container()->add(*_breadcrumbs); - _view->breadcrumb_container()->show(); - - _breadcrumbs->build(graph->path(), _view); - _breadcrumbs->show(); - - _menu_view_control_window->property_sensitive() = false; - - for (const auto& p : graph->ports()) { - if (_app->can_control(p.get())) { - _menu_view_control_window->property_sensitive() = true; - break; - } - } - - _menu_parent->property_sensitive() = bool(graph->parent()); - - new_port_connection = graph->signal_new_port().connect( - sigc::mem_fun(this, &GraphBox::graph_port_added)); - removed_port_connection = graph->signal_removed_port().connect( - sigc::mem_fun(this, &GraphBox::graph_port_removed)); - - show(); - _alignment->show_all(); - - _menu_human_names->set_active( - _app->world()->conf().option("human-names").get<int32_t>()); - _menu_show_port_names->set_active( - _app->world()->conf().option("port-labels").get<int32_t>()); - - _doc_paned->set_position(std::numeric_limits<int>::max()); - _doc_scrolledwindow->hide(); - - _enable_signal = true; -} - -void -GraphBox::graph_port_added(SPtr<const PortModel> port) -{ - if (port->is_input() && _app->can_control(port.get())) { - _menu_view_control_window->property_sensitive() = true; - } -} - -void -GraphBox::graph_port_removed(SPtr<const PortModel> port) -{ - if (!(port->is_input() && _app->can_control(port.get()))) { - return; - } - - for (const auto& p : _graph->ports()) { - if (p->is_input() && _app->can_control(p.get())) { - _menu_view_control_window->property_sensitive() = true; - return; - } - } - - _menu_view_control_window->property_sensitive() = false; -} - -void -GraphBox::property_changed(const URI& predicate, const Atom& value) -{ - if (predicate == _app->uris().ingen_sprungLayout) { - if (value.type() == _app->uris().forge.Bool) { - _menu_sprung_layout->set_active(value.get<int32_t>()); - } - } -} - -void -GraphBox::set_documentation(const std::string& doc, bool html) -{ - _doc_scrolledwindow->remove(); - if (doc.empty()) { - _doc_scrolledwindow->hide(); - return; - } -#ifdef HAVE_WEBKIT - WebKitWebView* view = WEBKIT_WEB_VIEW(webkit_web_view_new()); - webkit_web_view_load_html_string(view, doc.c_str(), ""); - Gtk::Widget* widget = Gtk::manage(Glib::wrap(GTK_WIDGET(view))); - _doc_scrolledwindow->add(*widget); - widget->show(); -#else - Gtk::TextView* view = Gtk::manage(new Gtk::TextView()); - view->get_buffer()->set_text(doc); - view->set_wrap_mode(Gtk::WRAP_WORD); - _doc_scrolledwindow->add(*view); - view->show(); -#endif -} - -void -GraphBox::show_status(const ObjectModel* model) -{ - std::stringstream msg; - msg << model->path(); - - const PortModel* port = nullptr; - const BlockModel* block = nullptr; - - if ((port = dynamic_cast<const PortModel*>(model))) { - show_port_status(port, port->value()); - - } else if ((block = dynamic_cast<const BlockModel*>(model))) { - const PluginModel* plugin = dynamic_cast<const PluginModel*>(block->plugin()); - if (plugin) { - msg << ((boost::format(" (%1%)") % plugin->human_name()).str()); - } - _status_bar->push(msg.str(), STATUS_CONTEXT_HOVER); - } -} - -void -GraphBox::show_port_status(const PortModel* port, const Atom& value) -{ - std::stringstream msg; - msg << port->path(); - - const BlockModel* parent = dynamic_cast<const BlockModel*>(port->parent().get()); - if (parent) { - const PluginModel* plugin = dynamic_cast<const PluginModel*>(parent->plugin()); - if (plugin) { - const std::string& human_name = plugin->port_human_name(port->index()); - if (!human_name.empty()) { - msg << " (" << human_name << ")"; - } - } - } - - if (value.is_valid()) { - msg << " = " << _app->forge().str(value, true); - } - - _status_bar->pop(STATUS_CONTEXT_HOVER); - _status_bar->push(msg.str(), STATUS_CONTEXT_HOVER); -} - -void -GraphBox::object_entered(const ObjectModel* model) -{ - show_status(model); -} - -void -GraphBox::object_left(const ObjectModel* model) -{ - _status_bar->pop(STATUS_CONTEXT_GRAPH); - _status_bar->pop(STATUS_CONTEXT_HOVER); -} - -void -GraphBox::event_show_engine() -{ - if (_graph) { - _app->connect_window()->show(); - } -} - -void -GraphBox::event_clipboard_changed(GdkEventOwnerChange* ev) -{ - Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get(); - _menu_paste->set_sensitive(clipboard->wait_is_text_available()); -} - -void -GraphBox::event_show_properties() -{ - _app->window_factory()->present_properties(_graph); -} - -void -GraphBox::event_import() -{ - _app->window_factory()->present_load_graph(_graph); -} - -void -GraphBox::event_save() -{ - const Atom& document = _graph->get_property(_app->uris().ingen_file); - if (!document.is_valid() || document.type() != _app->uris().forge.URI) { - event_save_as(); - } else { - save_graph(URI(document.ptr<char>())); - } -} - -void -GraphBox::error(const Glib::ustring& message, - const Glib::ustring& secondary_text) -{ - Gtk::MessageDialog dialog( - message, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - dialog.set_secondary_text(secondary_text); - if (_window) { - dialog.set_transient_for(*_window); - } - dialog.run(); -} - -bool -GraphBox::confirm(const Glib::ustring& message, - const Glib::ustring& secondary_text) -{ - Gtk::MessageDialog dialog( - message, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true); - dialog.set_secondary_text(secondary_text); - if (_window) { - dialog.set_transient_for(*_window); - } - return dialog.run() == Gtk::RESPONSE_YES; -} - -void -GraphBox::save_graph(const URI& uri) -{ - if (_app->interface()->uri().string().substr(0, 3) == "tcp") { - _status_bar->push( - (boost::format("Saved %1% to %2% on client") - % _graph->path() % uri).str(), - STATUS_CONTEXT_GRAPH); - _app->loader()->save_graph(_graph, uri); - } else { - _status_bar->push( - (boost::format("Saved %1% to %2% on server") - % _graph->path() % uri).str(), - STATUS_CONTEXT_GRAPH); - _app->interface()->copy(_graph->uri(), uri); - } -} - -void -GraphBox::event_save_as() -{ - const URIs& uris = _app->uris(); - while (true) { - Gtk::FileChooserDialog dialog( - "Save Graph", Gtk::FILE_CHOOSER_ACTION_SAVE); - if (_window) { - dialog.set_transient_for(*_window); - } - - dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - Gtk::Button* save_button = dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); - save_button->property_has_default() = true; - - Gtk::FileFilter filt; - filt.add_pattern("*.ingen"); - filt.set_name("Ingen bundles"); - dialog.set_filter(filt); - - // Set current folder to most sensible default - const Atom& document = _graph->get_property(uris.ingen_file); - const Atom& dir = _app->world()->conf().option("graph-directory"); - if (document.type() == uris.forge.URI) { - dialog.set_uri(document.ptr<char>()); - } else if (dir.is_valid()) { - dialog.set_current_folder(dir.ptr<char>()); - } - - if (dialog.run() != Gtk::RESPONSE_OK) { - break; - } - - std::string filename = dialog.get_filename(); - std::string basename = Glib::path_get_basename(filename); - - if (basename.find('.') == std::string::npos) { - filename += ".ingen"; - basename += ".ingen"; - } else if (filename.substr(filename.length() - 4) == ".ttl") { - const Glib::ustring dir = Glib::path_get_dirname(filename); - if (dir.substr(dir.length() - 6) != ".ingen") { - error("<b>File does not appear to be in an Ingen bundle."); - } - } else if (filename.substr(filename.length() - 6) != ".ingen") { - error("<b>Ingen bundles must end in \".ingen\"</b>"); - continue; - } - - const std::string symbol(basename.substr(0, basename.find('.'))); - - if (!Raul::Symbol::is_valid(symbol)) { - error( - "<b>Ingen bundle names must be valid symbols.</b>", - "All characters must be _, a-z, A-Z, or 0-9, but the first may not be 0-9."); - continue; - } - - //_graph->set_property(uris.lv2_symbol, Atom(symbol.c_str())); - - bool confirmed = true; - if (Glib::file_test(filename, Glib::FILE_TEST_IS_DIR)) { - if (Glib::file_test(Glib::build_filename(filename, "manifest.ttl"), - Glib::FILE_TEST_EXISTS)) { - confirmed = confirm( - (boost::format("<b>The bundle \"%1%\" already exists." - " Replace it?</b>") % basename).str()); - } else { - confirmed = confirm( - (boost::format("<b>A directory named \"%1%\" already exists," - "but is not an Ingen bundle. " - "Save into it anyway?</b>") % basename).str(), - "This will create at least 2 .ttl files in this directory," - "and possibly several more files and/or directories, recursively. " - "Existing files will be overwritten."); - } - } else if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { - confirmed = confirm( - (boost::format("<b>A file named \"%1%\" already exists. " - "Replace it with an Ingen bundle?</b>") - % basename).str()); - if (confirmed) { - ::g_remove(filename.c_str()); - } - } - - if (confirmed) { - const Glib::ustring uri = Glib::filename_to_uri(filename); - save_graph(URI(uri)); - - const_cast<GraphModel*>(_graph.get())->set_property( - uris.ingen_file, - _app->forge().alloc_uri(uri.c_str())); - } - - _app->world()->conf().set( - "graph-directory", - _app->world()->forge().alloc(dialog.get_current_folder())); - - break; - } -} - -void -GraphBox::event_export_image() -{ - Gtk::FileChooserDialog dialog("Export Image", Gtk::FILE_CHOOSER_ACTION_SAVE); - dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); - dialog.set_default_response(Gtk::RESPONSE_OK); - if (_window) { - dialog.set_transient_for(*_window); - } - - typedef std::map<std::string, std::string> Types; - Types types; - types["*.dot"] = "Graphviz DOT"; - types["*.pdf"] = "Portable Document Format"; - types["*.ps"] = "PostScript"; - types["*.svg"] = "Scalable Vector Graphics"; - for (Types::const_iterator t = types.begin(); t != types.end(); ++t) { - Gtk::FileFilter filt; - filt.add_pattern(t->first); - filt.set_name(t->second); - dialog.add_filter(filt); - if (t->first == "*.pdf") { - dialog.set_filter(filt); - } - } - - Gtk::CheckButton* bg_but = new Gtk::CheckButton("Draw _Background", true); - Gtk::Alignment* extra = new Gtk::Alignment(1.0, 0.5, 0.0, 0.0); - bg_but->set_active(true); - extra->add(*Gtk::manage(bg_but)); - extra->show_all(); - dialog.set_extra_widget(*Gtk::manage(extra)); - - if (dialog.run() == Gtk::RESPONSE_OK) { - const std::string filename = dialog.get_filename(); - if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { - Gtk::MessageDialog confirm( - std::string("File exists! Overwrite ") + filename + "?", - true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true); - confirm.set_transient_for(dialog); - if (confirm.run() != Gtk::RESPONSE_YES) { - return; - } - } - _view->canvas()->export_image(filename.c_str(), bg_but->get_active()); - _status_bar->push((boost::format("Rendered %1% to %2%") - % _graph->path() % filename).str(), - STATUS_CONTEXT_GRAPH); - } -} - -void -GraphBox::event_copy() -{ - if (_view) { - _view->canvas()->copy_selection(); - } -} - -void -GraphBox::event_redo() -{ - _app->interface()->redo(); -} - -void -GraphBox::event_undo() -{ - _app->interface()->undo(); -} - -void -GraphBox::event_paste() -{ - if (_view) { - _view->canvas()->paste(); - } -} - -void -GraphBox::event_delete() -{ - if (_view) { - _view->canvas()->destroy_selection(); - } -} - -void -GraphBox::event_select_all() -{ - if (_view) { - _view->canvas()->select_all(); - } -} - -void -GraphBox::event_close() -{ - if (_window) { - _app->window_factory()->remove_graph_window(_window); - } -} - -void -GraphBox::event_quit() -{ - _app->quit(_window); -} - -void -GraphBox::event_zoom_in() -{ - _view->canvas()->set_font_size(_view->canvas()->get_font_size() + 1.0); -} - -void -GraphBox::event_zoom_out() -{ - _view->canvas()->set_font_size(_view->canvas()->get_font_size() - 1.0); -} - -void -GraphBox::event_zoom_normal() -{ - _view->canvas()->set_zoom(1.0); -} - -void -GraphBox::event_zoom_full() -{ - _view->canvas()->zoom_full(); -} - -void -GraphBox::event_increase_font_size() -{ - _view->canvas()->set_font_size(_view->canvas()->get_font_size() + 1.0); -} -void -GraphBox::event_decrease_font_size() -{ - _view->canvas()->set_font_size(_view->canvas()->get_font_size() - 1.0); -} -void -GraphBox::event_normal_font_size() -{ - _view->canvas()->set_font_size(_view->canvas()->get_default_font_size()); -} - -void -GraphBox::event_arrange() -{ - _app->interface()->bundle_begin(); - _view->canvas()->arrange(); - _app->interface()->bundle_end(); -} - -void -GraphBox::event_parent_activated() -{ - SPtr<Client::GraphModel> parent = dynamic_ptr_cast<Client::GraphModel>(_graph->parent()); - if (parent) { - _app->window_factory()->present_graph(parent, _window); - } -} - -void -GraphBox::event_refresh_activated() -{ - _app->interface()->get(_graph->uri()); -} - -void -GraphBox::event_fullscreen_toggled() -{ - // FIXME: ugh, use GTK signals to track state and know for sure - static bool is_fullscreen = false; - - if (_window) { - if (!is_fullscreen) { - _window->fullscreen(); - is_fullscreen = true; - } else { - _window->unfullscreen(); - is_fullscreen = false; - } - } -} - -void -GraphBox::event_doc_pane_toggled() -{ - if (_menu_show_doc_pane->get_active()) { - _doc_scrolledwindow->show_all(); - if (!_has_shown_documentation) { - const Gtk::Allocation allocation = get_allocation(); - _doc_paned->set_position(allocation.get_width() / 1.61803399); - _has_shown_documentation = true; - } - } else { - _doc_scrolledwindow->hide(); - } -} - -void -GraphBox::event_status_bar_toggled() -{ - if (_menu_show_status_bar->get_active()) { - _status_bar->show(); - } else { - _status_bar->hide(); - } -} - -void -GraphBox::event_animate_signals_toggled() -{ - _app->interface()->set_property( - URI("ingen:/clients/this"), - _app->uris().ingen_broadcast, - _app->forge().make((bool)_menu_animate_signals->get_active())); -} - -void -GraphBox::event_sprung_layout_toggled() -{ - const bool sprung = _menu_sprung_layout->get_active(); - - _view->canvas()->set_sprung_layout(sprung); - - Properties properties; - properties.emplace(_app->uris().ingen_sprungLayout, - _app->forge().make(sprung)); - _app->interface()->put(_graph->uri(), properties); -} - -void -GraphBox::event_human_names_toggled() -{ - _view->canvas()->show_human_names(_menu_human_names->get_active()); - _app->world()->conf().set( - "human-names", - _app->world()->forge().make(_menu_human_names->get_active())); -} - -void -GraphBox::event_port_names_toggled() -{ - _app->world()->conf().set( - "port-labels", - _app->world()->forge().make(_menu_show_port_names->get_active())); - if (_menu_show_port_names->get_active()) { - _view->canvas()->set_direction(GANV_DIRECTION_RIGHT); - _view->canvas()->show_port_names(true); - } else { - _view->canvas()->set_direction(GANV_DIRECTION_DOWN); - _view->canvas()->show_port_names(false); - } -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/GraphBox.hpp b/src/gui/GraphBox.hpp deleted file mode 100644 index fd9bf9c0..00000000 --- a/src/gui/GraphBox.hpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_GRAPH_BOX_HPP -#define INGEN_GUI_GRAPH_BOX_HPP - -#include <string> - -#include <gtkmm/alignment.h> -#include <gtkmm/box.h> -#include <gtkmm/builder.h> -#include <gtkmm/menushell.h> -#include <gtkmm/messagedialog.h> -#include <gtkmm/paned.h> -#include <gtkmm/scrolledwindow.h> -#include <gtkmm/statusbar.h> - -#include "ingen/ingen.h" -#include "ingen/types.hpp" - -#include "Window.hpp" - -namespace Raul { -class Atom; -class Path; -} - -namespace Ingen { - -class URI; - -namespace Client { -class GraphModel; -class PortModel; -class ObjectModel; -} - -namespace GUI { - -class BreadCrumbs; -class LoadGraphBox; -class LoadPluginWindow; -class NewSubgraphWindow; -class GraphDescriptionWindow; -class GraphView; -class GraphWindow; -class SubgraphModule; - -/** A window for a graph. - * - * \ingroup GUI - */ -class INGEN_API GraphBox : public Gtk::VBox -{ -public: - GraphBox(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - ~GraphBox(); - - static SPtr<GraphBox> create( - App& app, SPtr<const Client::GraphModel> graph); - - void init_box(App& app); - - void set_status_text(const std::string& text); - - void set_graph(SPtr<const Client::GraphModel> graph, - SPtr<GraphView> view); - - void set_window(GraphWindow* win) { _window = win; } - - bool documentation_is_visible() { return _doc_scrolledwindow->is_visible(); } - void set_documentation(const std::string& doc, bool html); - - SPtr<const Client::GraphModel> graph() const { return _graph; } - SPtr<GraphView> view() const { return _view; } - - void show_port_status(const Client::PortModel* port, - const Atom& value); - - void set_graph_from_path(const Raul::Path& path, SPtr<GraphView> view); - - void object_entered(const Client::ObjectModel* model); - void object_left(const Client::ObjectModel* model); - -private: - void graph_port_added(SPtr<const Client::PortModel> port); - void graph_port_removed(SPtr<const Client::PortModel> port); - void property_changed(const URI& predicate, const Atom& value); - void show_status(const Client::ObjectModel* model); - - void error(const Glib::ustring& message, - const Glib::ustring& secondary_text=""); - - bool confirm(const Glib::ustring& message, - const Glib::ustring& secondary_text=""); - - void save_graph(const URI& uri); - - void event_import(); - void event_save(); - void event_save_as(); - void event_export_image(); - void event_redo(); - void event_undo(); - void event_copy(); - void event_paste(); - void event_delete(); - void event_select_all(); - void event_close(); - void event_quit(); - void event_parent_activated(); - void event_refresh_activated(); - void event_fullscreen_toggled(); - void event_doc_pane_toggled(); - void event_status_bar_toggled(); - void event_animate_signals_toggled(); - void event_sprung_layout_toggled(); - void event_human_names_toggled(); - void event_port_names_toggled(); - void event_zoom_in(); - void event_zoom_out(); - void event_zoom_normal(); - void event_zoom_full(); - void event_increase_font_size(); - void event_decrease_font_size(); - void event_normal_font_size(); - void event_arrange(); - void event_show_properties(); - void event_show_engine(); - void event_clipboard_changed(GdkEventOwnerChange* ev); - - App* _app; - SPtr<const Client::GraphModel> _graph; - SPtr<GraphView> _view; - GraphWindow* _window; - - sigc::connection new_port_connection; - sigc::connection removed_port_connection; - sigc::connection edit_mode_connection; - - Gtk::MenuItem* _menu_import; - Gtk::MenuItem* _menu_save; - Gtk::MenuItem* _menu_save_as; - Gtk::MenuItem* _menu_export_image; - Gtk::MenuItem* _menu_redo; - Gtk::MenuItem* _menu_undo; - Gtk::MenuItem* _menu_cut; - Gtk::MenuItem* _menu_copy; - Gtk::MenuItem* _menu_paste; - Gtk::MenuItem* _menu_delete; - Gtk::MenuItem* _menu_select_all; - Gtk::MenuItem* _menu_close; - Gtk::MenuItem* _menu_quit; - Gtk::CheckMenuItem* _menu_animate_signals; - Gtk::CheckMenuItem* _menu_sprung_layout; - Gtk::CheckMenuItem* _menu_human_names; - Gtk::CheckMenuItem* _menu_show_port_names; - Gtk::CheckMenuItem* _menu_show_doc_pane; - Gtk::CheckMenuItem* _menu_show_status_bar; - Gtk::MenuItem* _menu_zoom_in; - Gtk::MenuItem* _menu_zoom_out; - Gtk::MenuItem* _menu_zoom_normal; - Gtk::MenuItem* _menu_zoom_full; - Gtk::MenuItem* _menu_increase_font_size; - Gtk::MenuItem* _menu_decrease_font_size; - Gtk::MenuItem* _menu_normal_font_size; - Gtk::MenuItem* _menu_parent; - Gtk::MenuItem* _menu_refresh; - Gtk::MenuItem* _menu_fullscreen; - Gtk::MenuItem* _menu_arrange; - Gtk::MenuItem* _menu_view_engine_window; - Gtk::MenuItem* _menu_view_control_window; - Gtk::MenuItem* _menu_view_graph_properties; - Gtk::MenuItem* _menu_view_messages_window; - Gtk::MenuItem* _menu_view_graph_tree_window; - Gtk::MenuItem* _menu_help_about; - - Gtk::Alignment* _alignment; - BreadCrumbs* _breadcrumbs; - Gtk::Statusbar* _status_bar; - Gtk::Label* _status_label; - - Gtk::HPaned* _doc_paned; - Gtk::ScrolledWindow* _doc_scrolledwindow; - - sigc::connection _entered_connection; - sigc::connection _left_connection; - - /** Invisible bin used to store breadcrumbs when not shown by a view */ - Gtk::Alignment _breadcrumb_bin; - - bool _has_shown_documentation; - bool _enable_signal; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_GRAPH_BOX_HPP diff --git a/src/gui/GraphCanvas.cpp b/src/gui/GraphCanvas.cpp deleted file mode 100644 index 0ae4d709..00000000 --- a/src/gui/GraphCanvas.cpp +++ /dev/null @@ -1,899 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <algorithm> -#include <cassert> -#include <map> -#include <set> -#include <string> - -#include <boost/optional.hpp> -#include <gtkmm/stock.h> - -#include "ganv/Canvas.hpp" -#include "ganv/Circle.hpp" -#include "ingen/ClashAvoider.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/Serialiser.hpp" -#include "ingen/World.hpp" -#include "ingen/client/BlockModel.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" -#include "ingen/client/PluginModel.hpp" -#include "ingen/ingen.h" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" - -#include "App.hpp" -#include "Arc.hpp" -#include "GraphCanvas.hpp" -#include "GraphPortModule.hpp" -#include "GraphWindow.hpp" -#include "LoadPluginWindow.hpp" -#include "NewSubgraphWindow.hpp" -#include "NodeModule.hpp" -#include "PluginMenu.hpp" -#include "Port.hpp" -#include "SubgraphModule.hpp" -#include "ThreadedLoader.hpp" -#include "WidgetFactory.hpp" -#include "WindowFactory.hpp" - -using std::string; - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -static int -port_order(const GanvPort* a, const GanvPort* b, void* data) -{ - const Port* pa = dynamic_cast<const Port*>(Glib::wrap(a)); - const Port* pb = dynamic_cast<const Port*>(Glib::wrap(b)); - if (pa && pb) { - return ((int)pa->model()->index() - (int)pb->model()->index()); - } - return 0; -} - -GraphCanvas::GraphCanvas(App& app, - SPtr<const GraphModel> graph, - int width, - int height) - : Canvas(width, height) - , _app(app) - , _graph(std::move(graph)) - , _auto_position_count(0) - , _menu_x(0) - , _menu_y(0) - , _paste_count(0) - , _menu(nullptr) - , _internal_menu(nullptr) - , _plugin_menu(nullptr) - , _human_names(true) - , _show_port_names(true) - , _menu_dirty(false) -{ - Glib::RefPtr<Gtk::Builder> xml = WidgetFactory::create("canvas_menu"); - xml->get_widget("canvas_menu", _menu); - - xml->get_widget("canvas_menu_add_audio_input", _menu_add_audio_input); - xml->get_widget("canvas_menu_add_audio_output", _menu_add_audio_output); - xml->get_widget("canvas_menu_add_cv_input", _menu_add_cv_input); - xml->get_widget("canvas_menu_add_cv_output", _menu_add_cv_output); - xml->get_widget("canvas_menu_add_control_input", _menu_add_control_input); - xml->get_widget("canvas_menu_add_control_output", _menu_add_control_output); - xml->get_widget("canvas_menu_add_event_input", _menu_add_event_input); - xml->get_widget("canvas_menu_add_event_output", _menu_add_event_output); - xml->get_widget("canvas_menu_load_plugin", _menu_load_plugin); - xml->get_widget("canvas_menu_load_graph", _menu_load_graph); - xml->get_widget("canvas_menu_new_graph", _menu_new_graph); - xml->get_widget("canvas_menu_edit", _menu_edit); - xml->get_widget("canvas_menu_properties", _menu_properties); - - const URIs& uris = _app.uris(); - - // Add port menu items - _menu_add_audio_input->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &GraphCanvas::menu_add_port), - "audio_in", "Audio In", uris.lv2_AudioPort, false)); - _menu_add_audio_output->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &GraphCanvas::menu_add_port), - "audio_out", "Audio Out", uris.lv2_AudioPort, true)); - _menu_add_cv_input->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &GraphCanvas::menu_add_port), - "cv_in", "CV In", uris.lv2_CVPort, false)); - _menu_add_cv_output->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &GraphCanvas::menu_add_port), - "cv_out", "CV Out", uris.lv2_CVPort, true)); - _menu_add_control_input->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &GraphCanvas::menu_add_port), - "control_in", "Control In", uris.lv2_ControlPort, false)); - _menu_add_control_output->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &GraphCanvas::menu_add_port), - "control_out", "Control Out", uris.lv2_ControlPort, true)); - _menu_add_event_input->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &GraphCanvas::menu_add_port), - "event_in", "Event In", uris.atom_AtomPort, false)); - _menu_add_event_output->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &GraphCanvas::menu_add_port), - "event_out", "Event Out", uris.atom_AtomPort, true)); - - signal_event.connect( - sigc::mem_fun(this, &GraphCanvas::on_event)); - signal_connect.connect( - sigc::mem_fun(this, &GraphCanvas::connect)); - signal_disconnect.connect( - sigc::mem_fun(this, &GraphCanvas::disconnect)); - - // Connect to model signals to track state - _graph->signal_new_block().connect( - sigc::mem_fun(this, &GraphCanvas::add_block)); - _graph->signal_removed_block().connect( - sigc::mem_fun(this, &GraphCanvas::remove_block)); - _graph->signal_new_port().connect( - sigc::mem_fun(this, &GraphCanvas::add_port)); - _graph->signal_removed_port().connect( - sigc::mem_fun(this, &GraphCanvas::remove_port)); - _graph->signal_new_arc().connect( - sigc::mem_fun(this, &GraphCanvas::connection)); - _graph->signal_removed_arc().connect( - sigc::mem_fun(this, &GraphCanvas::disconnection)); - - _app.store()->signal_new_plugin().connect( - sigc::mem_fun(this, &GraphCanvas::add_plugin)); - _app.store()->signal_plugin_deleted().connect( - sigc::mem_fun(this, &GraphCanvas::remove_plugin)); - - // Connect widget signals to do things - _menu_load_plugin->signal_activate().connect( - sigc::mem_fun(this, &GraphCanvas::menu_load_plugin)); - _menu_load_graph->signal_activate().connect( - sigc::mem_fun(this, &GraphCanvas::menu_load_graph)); - _menu_new_graph->signal_activate().connect( - sigc::mem_fun(this, &GraphCanvas::menu_new_graph)); - _menu_properties->signal_activate().connect( - sigc::mem_fun(this, &GraphCanvas::menu_properties)); - - show_human_names(app.world()->conf().option("human-names").get<int32_t>()); - show_port_names(app.world()->conf().option("port-labels").get<int32_t>()); - set_port_order(port_order, nullptr); -} - -void -GraphCanvas::show_menu(bool position, unsigned button, uint32_t time) -{ - _app.request_plugins_if_necessary(); - - if (!_internal_menu || _menu_dirty) { - build_menus(); - } - - if (position) { - _menu->popup(sigc::mem_fun(this, &GraphCanvas::auto_menu_position), button, time); - } else { - _menu->popup(button, time); - } -} - -void -GraphCanvas::build_menus() -{ - // Build (or clear existing) internal plugin menu - if (_internal_menu) { - _internal_menu->items().clear(); - } else { - _menu->items().push_back( - Gtk::Menu_Helpers::ImageMenuElem( - "In_ternal", - *(manage(new Gtk::Image(Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU))))); - Gtk::MenuItem* internal_menu_item = &(_menu->items().back()); - _internal_menu = Gtk::manage(new Gtk::Menu()); - internal_menu_item->set_submenu(*_internal_menu); - _menu->reorder_child(*internal_menu_item, 4); - } - - // Build skeleton LV2 plugin class heirarchy for 'Plugin' menu - if (_plugin_menu) { - _plugin_menu->clear(); - } else { - _plugin_menu = Gtk::manage(new PluginMenu(*_app.world())); - _menu->items().push_back( - Gtk::Menu_Helpers::ImageMenuElem( - "_Plugin", - *(manage(new Gtk::Image(Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU))))); - Gtk::MenuItem* plugin_menu_item = &(_menu->items().back()); - plugin_menu_item->set_submenu(*_plugin_menu); - _menu->reorder_child(*plugin_menu_item, 5); - _plugin_menu->signal_load_plugin.connect( - sigc::mem_fun(this, &GraphCanvas::load_plugin)); - } - - // Add known plugins to menu heirarchy - SPtr<const ClientStore::Plugins> plugins = _app.store()->plugins(); - for (const auto& p : *plugins.get()) { - add_plugin(p.second); - } - - _menu_dirty = false; -} - -void -GraphCanvas::build() -{ - const Store::const_range kids = _app.store()->children_range(_graph); - - // Create modules for blocks - for (Store::const_iterator i = kids.first; i != kids.second; ++i) { - SPtr<BlockModel> block = dynamic_ptr_cast<BlockModel>(i->second); - if (block && block->parent() == _graph) { - add_block(block); - } - } - - // Create pseudo modules for ports (ports on this canvas, not on our module) - for (const auto& p : _graph->ports()) { - add_port(p); - } - - // Create arcs - for (const auto& a : _graph->arcs()) { - connection(dynamic_ptr_cast<ArcModel>(a.second)); - } -} - -static void -show_module_human_names(GanvNode* node, void* data) -{ - bool b = *(bool*)data; - if (GANV_IS_MODULE(node)) { - Ganv::Module* module = Glib::wrap(GANV_MODULE(node)); - NodeModule* nmod = dynamic_cast<NodeModule*>(module); - if (nmod) { - nmod->show_human_names(b); - } - - GraphPortModule* pmod = dynamic_cast<GraphPortModule*>(module); - if (pmod) { - pmod->show_human_names(b); - } - } -} - -void -GraphCanvas::show_human_names(bool b) -{ - _human_names = b; - _app.world()->conf().set("human-names", _app.forge().make(b)); - - for_each_node(show_module_human_names, &b); -} - -static void -ensure_port_labels(GanvNode* node, void* data) -{ - if (GANV_IS_MODULE(node)) { - Ganv::Module* module = Glib::wrap(GANV_MODULE(node)); - for (Ganv::Port* p : *module) { - Ingen::GUI::Port* port = dynamic_cast<Ingen::GUI::Port*>(p); - if (port) { - port->ensure_label(); - } - } - } -} - -void -GraphCanvas::show_port_names(bool b) -{ - ganv_canvas_set_direction(gobj(), b ? GANV_DIRECTION_RIGHT : GANV_DIRECTION_DOWN); - for_each_node(ensure_port_labels, &b); -} - -void -GraphCanvas::add_plugin(SPtr<PluginModel> p) -{ - if (_internal_menu && _app.uris().ingen_Internal == p->type()) { - _internal_menu->items().push_back( - Gtk::Menu_Helpers::MenuElem( - std::string("_") + p->human_name(), - sigc::bind(sigc::mem_fun(this, &GraphCanvas::load_plugin), p))); - } else if (_plugin_menu) { - _plugin_menu->add_plugin(p); - } -} - -void -GraphCanvas::remove_plugin(const URI& uri) -{ - // Flag menus as dirty so they will be rebuilt when needed next - _menu_dirty = true; -} - -void -GraphCanvas::add_block(SPtr<const BlockModel> bm) -{ - SPtr<const GraphModel> pm = dynamic_ptr_cast<const GraphModel>(bm); - NodeModule* module; - if (pm) { - module = SubgraphModule::create(*this, pm, _human_names); - } else { - module = NodeModule::create(*this, bm, _human_names); - } - - module->show(); - _views.emplace(bm, module); - if (_pastees.find(bm->path()) != _pastees.end()) { - module->set_selected(true); - } -} - -void -GraphCanvas::remove_block(SPtr<const BlockModel> bm) -{ - auto i = _views.find(bm); - - if (i != _views.end()) { - const guint n_ports = i->second->num_ports(); - for (gint p = n_ports - 1; p >= 0; --p) { - delete i->second->get_port(p); - } - delete i->second; - _views.erase(i); - } -} - -void -GraphCanvas::add_port(SPtr<const PortModel> pm) -{ - GraphPortModule* view = GraphPortModule::create(*this, pm); - _views.emplace(pm, view); - view->show(); -} - -void -GraphCanvas::remove_port(SPtr<const PortModel> pm) -{ - auto i = _views.find(pm); - - // Port on this graph - if (i != _views.end()) { - delete i->second; - _views.erase(i); - - } else { - NodeModule* module = dynamic_cast<NodeModule*>(_views[pm->parent()]); - module->delete_port_view(pm); - } - - assert(_views.find(pm) == _views.end()); -} - -Ganv::Port* -GraphCanvas::get_port_view(SPtr<PortModel> port) -{ - Ganv::Module* module = _views[port]; - - // Port on this graph - if (module) { - GraphPortModule* ppm = dynamic_cast<GraphPortModule*>(module); - return ppm - ? *ppm->begin() - : dynamic_cast<Ganv::Port*>(module); - } else { - module = dynamic_cast<NodeModule*>(_views[port->parent()]); - if (module) { - for (const auto& p : *module) { - GUI::Port* pv = dynamic_cast<GUI::Port*>(p); - if (pv && pv->model() == port) { - return pv; - } - } - } - } - - return nullptr; -} - -/** Called when a connection is added to the model. */ -void -GraphCanvas::connection(SPtr<const ArcModel> arc) -{ - Ganv::Port* const tail = get_port_view(arc->tail()); - Ganv::Port* const head = get_port_view(arc->head()); - - if (tail && head) { - new GUI::Arc(*this, arc, tail, head); - } else { - _app.log().error(fmt("Unable to find ports to connect %1% => %2%\n") - % arc->tail_path() % arc->head_path()); - } -} - -/** Called when a connection is removed from the model. */ -void -GraphCanvas::disconnection(SPtr<const ArcModel> arc) -{ - Ganv::Port* const tail = get_port_view(arc->tail()); - Ganv::Port* const head = get_port_view(arc->head()); - - if (tail && head) { - remove_edge_between(tail, head); - if (arc->head()->is_a(_app.uris().lv2_AudioPort)) { - GUI::Port* const h = dynamic_cast<GUI::Port*>(head); - if (h) { - h->activity(_app.forge().make(0.0f)); // Reset peaks - } - } - } else { - _app.log().error(fmt("Unable to find ports to disconnect %1% => %2%\n") - % arc->tail_path() % arc->head_path()); - } -} - -/** Called when the user connects ports on the canvas. */ -void -GraphCanvas::connect(Ganv::Node* tail, Ganv::Node* head) -{ - const GUI::Port* const t = dynamic_cast<GUI::Port*>(tail); - const GUI::Port* const h = dynamic_cast<GUI::Port*>(head); - - if (t && h) { - _app.interface()->connect(t->model()->path(), h->model()->path()); - } -} - -/** Called when the user disconnects ports on the canvas. */ -void -GraphCanvas::disconnect(Ganv::Node* tail, Ganv::Node* head) -{ - const GUI::Port* const t = dynamic_cast<GUI::Port*>(tail); - const GUI::Port* const h = dynamic_cast<GUI::Port*>(head); - - if (t && h) { - _app.interface()->disconnect(t->model()->path(), h->model()->path()); - } -} - -void -GraphCanvas::auto_menu_position(int& x, int& y, bool& push_in) -{ - std::pair<int, int> scroll_offsets; - get_scroll_offsets(scroll_offsets.first, scroll_offsets.second); - - if (_auto_position_count > 1 && scroll_offsets != _auto_position_scroll_offsets) { - // Scroll changed since last auto position, reset - _menu_x = 0; - _menu_y = 0; - _auto_position_count = 0; - } - - if (_menu_x == 0 && _menu_y == 0) { - // No menu position set, start near top left of canvas - widget().translate_coordinates( - *_app.window_factory()->graph_window(_graph), - 64, 64, _menu_x, _menu_y); - - int origin_x; - int origin_y; - widget().get_window()->get_origin(origin_x, origin_y); - _menu_x += origin_x; - _menu_y += origin_y; - } - - const int cascade = _auto_position_count * 32; - - x = _menu_x + cascade; - y = _menu_y + cascade; - push_in = true; - - ++_auto_position_count; - _auto_position_scroll_offsets = scroll_offsets; -} - -bool -GraphCanvas::on_event(GdkEvent* event) -{ - assert(event); - - bool ret = false; - - switch (event->type) { - case GDK_BUTTON_PRESS: - if (event->button.button == 3) { - _auto_position_count = 1; - _menu_x = (int)event->button.x_root; - _menu_y = (int)event->button.y_root; - show_menu(false, event->button.button, event->button.time); - ret = true; - } - break; - - case GDK_KEY_PRESS: - switch (event->key.keyval) { - case GDK_Delete: - destroy_selection(); - ret = true; - break; - case GDK_Home: - scroll_to(0, 0); - break; - case GDK_space: - case GDK_Menu: - show_menu(true, 3, event->key.time); - default: break; - } - break; - - case GDK_MOTION_NOTIFY: - _paste_count = 0; - break; - - default: break; - } - - return ret; -} - -void -GraphCanvas::clear_selection() -{ - GraphWindow* win = _app.window_factory()->graph_window(_graph); - if (win) { - win->set_documentation("", false); - } - - Ganv::Canvas::clear_selection(); -} - -static void -destroy_node(GanvNode* node, void* data) -{ - if (!GANV_IS_MODULE(node)) { - return; - } - - App* app = (App*)data; - Ganv::Module* module = Glib::wrap(GANV_MODULE(node)); - NodeModule* node_module = dynamic_cast<NodeModule*>(module); - - if (node_module) { - app->interface()->del(node_module->block()->uri()); - } else { - GraphPortModule* port_module = dynamic_cast<GraphPortModule*>(module); - if (port_module && - strcmp(port_module->port()->path().symbol(), "control") && - strcmp(port_module->port()->path().symbol(), "notify")) { - app->interface()->del(port_module->port()->uri()); - } - } -} - -static void -destroy_arc(GanvEdge* arc, void* data) -{ - App* app = (App*)data; - Ganv::Edge* arcmm = Glib::wrap(arc); - - Port* tail = dynamic_cast<Port*>(arcmm->get_tail()); - Port* head = dynamic_cast<Port*>(arcmm->get_head()); - app->interface()->disconnect(tail->model()->path(), head->model()->path()); -} - -void -GraphCanvas::destroy_selection() -{ - _app.interface()->bundle_begin(); - for_each_selected_edge(destroy_arc, &_app); - for_each_selected_node(destroy_node, &_app); - _app.interface()->bundle_end(); -} - -static void -serialise_node(GanvNode* node, void* data) -{ - Serialiser* serialiser = (Serialiser*)data; - if (!GANV_IS_MODULE(node)) { - return; - } - - Ganv::Module* module = Glib::wrap(GANV_MODULE(node)); - NodeModule* node_module = dynamic_cast<NodeModule*>(module); - - if (node_module) { - serialiser->serialise(node_module->block()); - } else { - GraphPortModule* port_module = dynamic_cast<GraphPortModule*>(module); - if (port_module) { - serialiser->serialise(port_module->port()); - } - } -} - -static void -serialise_arc(GanvEdge* arc, void* data) -{ - Serialiser* serialiser = (Serialiser*)data; - if (!GANV_IS_EDGE(arc)) { - return; - } - - GUI::Arc* garc = dynamic_cast<GUI::Arc*>(Glib::wrap(GANV_EDGE(arc))); - if (garc) { - serialiser->serialise_arc(Sord::Node(), garc->model()); - } -} - -void -GraphCanvas::copy_selection() -{ - std::lock_guard<std::mutex> lock(_app.world()->rdf_mutex()); - - Serialiser serialiser(*_app.world()); - serialiser.start_to_string(_graph->path(), _graph->base_uri()); - - for_each_selected_node(serialise_node, &serialiser); - for_each_selected_edge(serialise_arc, &serialiser); - - Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get(); - clipboard->set_text(serialiser.finish()); - _paste_count = 0; -} - -void -GraphCanvas::paste() -{ - typedef Properties::const_iterator PropIter; - - std::lock_guard<std::mutex> lock(_app.world()->rdf_mutex()); - - const Glib::ustring str = Gtk::Clipboard::get()->wait_for_text(); - SPtr<Parser> parser = _app.loader()->parser(); - const URIs& uris = _app.uris(); - const Raul::Path& parent = _graph->path(); - if (!parser) { - _app.log().error("Unable to load parser, paste unavailable\n"); - return; - } - - // Prepare for paste - clear_selection(); - _pastees.clear(); - ++_paste_count; - - // Make a client store to serve as clipboard - ClientStore clipboard(_app.world()->uris(), _app.log()); - clipboard.set_plugins(_app.store()->plugins()); - clipboard.put(main_uri(), - {{uris.rdf_type, Property(uris.ingen_Graph)}}); - - // Parse clipboard text into clipboard store - boost::optional<URI> base_uri = parser->parse_string( - _app.world(), &clipboard, str, main_uri()); - - // Figure out the copy graph base path - Raul::Path copy_root("/"); - if (base_uri) { - std::string base = *base_uri; - if (base[base.size() - 1] == '/') { - base = base.substr(0, base.size() - 1); - } - copy_root = uri_to_path(URI(base)); - } - - // Find the minimum x and y coordinate of objects to be pasted - float min_x = std::numeric_limits<float>::max(); - float min_y = std::numeric_limits<float>::max(); - for (const auto& c : clipboard) { - if (c.first.parent() == Raul::Path("/")) { - const Atom& x = c.second->get_property(uris.ingen_canvasX); - const Atom& y = c.second->get_property(uris.ingen_canvasY); - if (x.type() == uris.atom_Float) { - min_x = std::min(min_x, x.get<float>()); - } - if (y.type() == uris.atom_Float) { - min_y = std::min(min_y, y.get<float>()); - } - } - } - - // Find canvas paste origin based on pointer position - int widget_point_x, widget_point_y, scroll_x, scroll_y; - widget().get_pointer(widget_point_x, widget_point_y); - get_scroll_offsets(scroll_x, scroll_y); - const int paste_x = widget_point_x + scroll_x + (20.0f * _paste_count); - const int paste_y = widget_point_y + scroll_y + (20.0f * _paste_count); - - _app.interface()->bundle_begin(); - - // Put each top level object in the clipboard store - ClashAvoider avoider(*_app.store().get()); - for (const auto& c : clipboard) { - if (c.first.is_root() || c.first.parent() != Raul::Path("/")) { - continue; - } - - const SPtr<Node> node = c.second; - const Raul::Path& old_path = copy_root.child(node->path()); - const URI& old_uri = path_to_uri(old_path); - const Raul::Path& new_path = avoider.map_path(parent.child(node->path())); - - Properties props{{uris.lv2_prototype, - _app.forge().make_urid(old_uri)}}; - - // Set the same types - const auto t = node->properties().equal_range(uris.rdf_type); - props.insert(t.first, t.second); - - // Set coordinates so paste origin is at the mouse pointer - PropIter xi = node->properties().find(uris.ingen_canvasX); - PropIter yi = node->properties().find(uris.ingen_canvasY); - if (xi != node->properties().end()) { - const float x = xi->second.get<float>() - min_x + paste_x; - props.insert({xi->first, Property(_app.forge().make(x), - xi->second.context())}); - } - if (yi != node->properties().end()) { - const float y = yi->second.get<float>() - min_y + paste_y; - props.insert({yi->first, Property(_app.forge().make(y), - yi->second.context())}); - } - - _app.interface()->put(path_to_uri(new_path), props); - _pastees.insert(new_path); - } - - // Connect objects - for (auto a : clipboard.object(Raul::Path("/"))->arcs()) { - _app.interface()->connect( - avoider.map_path(parent.child(a.second->tail_path())), - avoider.map_path(parent.child(a.second->head_path()))); - } - - _app.interface()->bundle_end(); -} - -void -GraphCanvas::generate_port_name( - const string& sym_base, string& symbol, - const string& name_base, string& name) -{ - symbol = sym_base; - name = name_base; - - char num_buf[5]; - uint32_t i = 1; - for ( ; i < 9999; ++i) { - snprintf(num_buf, sizeof(num_buf), "%u", i); - symbol = sym_base + "_"; - symbol += num_buf; - if (!_graph->get_port(Raul::Symbol::symbolify(symbol))) { - break; - } - } - - assert(Raul::Path::is_valid(string("/") + symbol)); - - name.append(" ").append(num_buf); -} - -void -GraphCanvas::menu_add_port(const string& sym_base, - const string& name_base, - const URI& type, - bool is_output) -{ - string sym, name; - generate_port_name(sym_base, sym, name_base, name); - const Raul::Path& path = _graph->path().child(Raul::Symbol(sym)); - - const URIs& uris = _app.uris(); - - Properties props = get_initial_data(Resource::Graph::INTERNAL); - props.emplace(uris.rdf_type, _app.forge().make_urid(type)); - if (type == uris.atom_AtomPort) { - props.emplace(uris.atom_bufferType, Property(uris.atom_Sequence)); - } - props.emplace( - uris.rdf_type, - Property(is_output ? uris.lv2_OutputPort : uris.lv2_InputPort)); - props.emplace(uris.lv2_index, - _app.forge().make(int32_t(_graph->num_ports()))); - props.emplace(uris.lv2_name, _app.forge().alloc(name.c_str())); - _app.interface()->put(path_to_uri(path), props); -} - -void -GraphCanvas::load_plugin(WPtr<PluginModel> weak_plugin) -{ - SPtr<PluginModel> plugin = weak_plugin.lock(); - if (!plugin) { - return; - } - - Raul::Symbol symbol = plugin->default_block_symbol(); - unsigned offset = _app.store()->child_name_offset(_graph->path(), symbol); - if (offset != 0) { - std::stringstream ss; - ss << symbol << "_" << offset; - symbol = Raul::Symbol(ss.str()); - } - - const URIs& uris = _app.uris(); - const Raul::Path path = _graph->path().child(symbol); - - // FIXME: polyphony? - Properties props = get_initial_data(); - props.emplace(uris.rdf_type, Property(uris.ingen_Block)); - props.emplace(uris.lv2_prototype, uris.forge.make_urid(plugin->uri())); - _app.interface()->put(path_to_uri(path), props); -} - -/** Try to guess a suitable location for a new module. - */ -void -GraphCanvas::get_new_module_location(double& x, double& y) -{ - int scroll_x; - int scroll_y; - get_scroll_offsets(scroll_x, scroll_y); - x = scroll_x + 20; - y = scroll_y + 20; -} - -Properties -GraphCanvas::get_initial_data(Resource::Graph ctx) -{ - Properties result; - const URIs& uris = _app.uris(); - result.emplace(uris.ingen_canvasX, - Property(_app.forge().make((float)_menu_x), ctx)); - result.emplace(uris.ingen_canvasY, - Property(_app.forge().make((float)_menu_y), ctx)); - return result; -} - -void -GraphCanvas::menu_load_plugin() -{ - _app.window_factory()->present_load_plugin(_graph, get_initial_data()); -} - -void -GraphCanvas::menu_load_graph() -{ - _app.window_factory()->present_load_subgraph( - _graph, get_initial_data(Resource::Graph::EXTERNAL)); -} - -void -GraphCanvas::menu_new_graph() -{ - _app.window_factory()->present_new_subgraph( - _graph, get_initial_data(Resource::Graph::EXTERNAL)); -} - -void -GraphCanvas::menu_properties() -{ - _app.window_factory()->present_properties(_graph); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/GraphCanvas.hpp b/src/gui/GraphCanvas.hpp deleted file mode 100644 index a7340744..00000000 --- a/src/gui/GraphCanvas.hpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_GRAPHCANVAS_HPP -#define INGEN_GUI_GRAPHCANVAS_HPP - -#include <string> -#include <map> -#include <set> - -#include "lilv/lilv.h" - -#include "ganv/Canvas.hpp" -#include "ganv/Module.hpp" -#include "ingen/Node.hpp" -#include "ingen/client/ArcModel.hpp" -#include "ingen/types.hpp" -#include "raul/Path.hpp" - -#include "NodeModule.hpp" - -namespace Ingen { - -namespace Client { class GraphModel; } - -namespace GUI { - -class NodeModule; -class PluginMenu; - -/** Graph canvas widget. - * - * \ingroup GUI - */ -class GraphCanvas : public Ganv::Canvas -{ -public: - GraphCanvas(App& app, - SPtr<const Client::GraphModel> graph, - int width, - int height); - - virtual ~GraphCanvas() {} - - App& app() { return _app; } - - void build(); - void show_human_names(bool b); - void show_port_names(bool b); - bool show_port_names() const { return _show_port_names; } - - void add_plugin(SPtr<Client::PluginModel> p); - void remove_plugin(const URI& uri); - void add_block(SPtr<const Client::BlockModel> bm); - void remove_block(SPtr<const Client::BlockModel> bm); - void add_port(SPtr<const Client::PortModel> pm); - void remove_port(SPtr<const Client::PortModel> pm); - void connection(SPtr<const Client::ArcModel> arc); - void disconnection(SPtr<const Client::ArcModel> arc); - - void get_new_module_location(double& x, double& y); - - void clear_selection(); - void destroy_selection(); - void copy_selection(); - void paste(); - - void show_menu(bool position, unsigned button, uint32_t time); - - bool on_event(GdkEvent* event); - -private: - enum class ControlType { NUMBER, BUTTON }; - void generate_port_name( - const std::string& sym_base, std::string& symbol, - const std::string& name_base, std::string& name); - - void menu_add_port(const std::string& sym_base, - const std::string& name_base, - const URI& type, - bool is_output); - - void menu_load_plugin(); - void menu_new_graph(); - void menu_load_graph(); - void menu_properties(); - void load_plugin(WPtr<Client::PluginModel> weak_plugin); - - void build_menus(); - - void auto_menu_position(int& x, int& y, bool& push_in); - - typedef std::multimap<const std::string, const LilvPluginClass*> LV2Children; - - Properties get_initial_data(Resource::Graph ctx=Resource::Graph::DEFAULT); - - Ganv::Port* get_port_view(SPtr<Client::PortModel> port); - - void connect(Ganv::Node* tail, - Ganv::Node* head); - - void disconnect(Ganv::Node* tail, - Ganv::Node* head); - - App& _app; - SPtr<const Client::GraphModel> _graph; - - typedef std::map<SPtr<const Client::ObjectModel>, Ganv::Module*> Views; - Views _views; - - int _auto_position_count; - std::pair<int, int> _auto_position_scroll_offsets; - - int _menu_x; - int _menu_y; - int _paste_count; - - // Track pasted objects so they can be selected when they arrive - std::set<Raul::Path> _pastees; - - Gtk::Menu* _menu; - Gtk::Menu* _internal_menu; - PluginMenu* _plugin_menu; - Gtk::MenuItem* _menu_add_audio_input; - Gtk::MenuItem* _menu_add_audio_output; - Gtk::MenuItem* _menu_add_control_input; - Gtk::MenuItem* _menu_add_control_output; - Gtk::MenuItem* _menu_add_cv_input; - Gtk::MenuItem* _menu_add_cv_output; - Gtk::MenuItem* _menu_add_event_input; - Gtk::MenuItem* _menu_add_event_output; - Gtk::MenuItem* _menu_load_plugin; - Gtk::MenuItem* _menu_load_graph; - Gtk::MenuItem* _menu_new_graph; - Gtk::MenuItem* _menu_properties; - Gtk::CheckMenuItem* _menu_edit; - - bool _human_names; - bool _show_port_names; - bool _menu_dirty; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_GRAPHCANVAS_HPP diff --git a/src/gui/GraphPortModule.cpp b/src/gui/GraphPortModule.cpp deleted file mode 100644 index 5987b0e3..00000000 --- a/src/gui/GraphPortModule.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <string> -#include <utility> - -#include "ingen/Configuration.hpp" -#include "ingen/Interface.hpp" -#include "ingen/client/BlockModel.hpp" -#include "ingen/client/GraphModel.hpp" - -#include "App.hpp" -#include "Style.hpp" -#include "GraphCanvas.hpp" -#include "GraphPortModule.hpp" -#include "GraphWindow.hpp" -#include "Port.hpp" -#include "PortMenu.hpp" -#include "RenameWindow.hpp" -#include "WidgetFactory.hpp" -#include "WindowFactory.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -GraphPortModule::GraphPortModule(GraphCanvas& canvas, - SPtr<const Client::PortModel> model) - : Ganv::Module(canvas, "", 0, 0, false) // FIXME: coords? - , _model(model) - , _port(nullptr) -{ - assert(model); - - assert(dynamic_ptr_cast<const GraphModel>(model->parent())); - - set_stacked(model->polyphonic()); - if (model->is_input() && !model->is_numeric()) { - set_is_source(true); - } - - model->signal_property().connect( - sigc::mem_fun(this, &GraphPortModule::property_changed)); - - signal_moved().connect( - sigc::mem_fun(this, &GraphPortModule::store_location)); -} - -GraphPortModule* -GraphPortModule::create(GraphCanvas& canvas, - SPtr<const PortModel> model) -{ - GraphPortModule* ret = new GraphPortModule(canvas, model); - Port* port = Port::create(canvas.app(), *ret, model, true); - - ret->set_port(port); - if (model->is_numeric()) { - port->show_control(); - } - - for (const auto& p : model->properties()) { - ret->property_changed(p.first, p.second); - } - - return ret; -} - -App& -GraphPortModule::app() const -{ - return ((GraphCanvas*)canvas())->app(); -} - -bool -GraphPortModule::show_menu(GdkEventButton* ev) -{ - return _port->show_menu(ev); -} - -void -GraphPortModule::store_location(double ax, double ay) -{ - const URIs& uris = app().uris(); - - const Atom x(app().forge().make(static_cast<float>(ax))); - const Atom y(app().forge().make(static_cast<float>(ay))); - - if (x != _model->get_property(uris.ingen_canvasX) || - y != _model->get_property(uris.ingen_canvasY)) - { - app().interface()->put( - _model->uri(), - {{uris.ingen_canvasX, Property(x, Property::Graph::INTERNAL)}, - {uris.ingen_canvasY, Property(y, Property::Graph::INTERNAL)}}); - } -} - -void -GraphPortModule::show_human_names(bool b) -{ - const URIs& uris = app().uris(); - const Atom& name = _model->get_property(uris.lv2_name); - if (b && name.type() == uris.forge.String) { - set_name(name.ptr<char>()); - } else { - set_name(_model->symbol().c_str()); - } -} - -void -GraphPortModule::set_name(const std::string& n) -{ - _port->set_label(n.c_str()); -} - -void -GraphPortModule::property_changed(const URI& key, const Atom& value) -{ - const URIs& uris = app().uris(); - if (value.type() == uris.forge.Float) { - if (key == uris.ingen_canvasX) { - move_to(value.get<float>(), get_y()); - } else if (key == uris.ingen_canvasY) { - move_to(get_x(), value.get<float>()); - } - } else if (value.type() == uris.forge.String) { - if (key == uris.lv2_name && - app().world()->conf().option("human-names").get<int32_t>()) { - set_name(value.ptr<char>()); - } else if (key == uris.lv2_symbol && - !app().world()->conf().option("human-names").get<int32_t>()) { - set_name(value.ptr<char>()); - } - } else if (value.type() == uris.forge.Bool) { - if (key == uris.ingen_polyphonic) { - set_stacked(value.get<int32_t>()); - } - } -} - -void -GraphPortModule::set_selected(gboolean b) -{ - if (b != get_selected()) { - Module::set_selected(b); - } -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/GraphPortModule.hpp b/src/gui/GraphPortModule.hpp deleted file mode 100644 index 97bc2e5b..00000000 --- a/src/gui/GraphPortModule.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_GRAPHPORTMODULE_HPP -#define INGEN_GUI_GRAPHPORTMODULE_HPP - -#include <string> - -#include "ganv/Module.hpp" - -#include "Port.hpp" - -namespace Raul { class Atom; } - -namespace Ingen { namespace Client { -class PortModel; -} } - -namespace Ingen { -namespace GUI { - -class GraphCanvas; -class Port; -class PortMenu; - -/** A "module" to represent a graph's port on its own canvas. - * - * Translation: This is the nameless single port pseudo module thingy. - * - * \ingroup GUI - */ -class GraphPortModule : public Ganv::Module -{ -public: - static GraphPortModule* create( - GraphCanvas& canvas, - SPtr<const Client::PortModel> model); - - App& app() const; - - virtual void store_location(double ax, double ay); - void show_human_names(bool b); - - void set_name(const std::string& n); - - SPtr<const Client::PortModel> port() const { return _model; } - -protected: - GraphPortModule(GraphCanvas& canvas, - SPtr<const Client::PortModel> model); - - bool show_menu(GdkEventButton* ev); - void set_selected(gboolean b); - - void set_port(Port* port) { _port = port; } - - void property_changed(const URI& key, const Atom& value); - - SPtr<const Client::PortModel> _model; - Port* _port; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_GRAPHPORTMODULE_HPP diff --git a/src/gui/GraphTreeWindow.cpp b/src/gui/GraphTreeWindow.cpp deleted file mode 100644 index 1eb6557b..00000000 --- a/src/gui/GraphTreeWindow.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "App.hpp" -#include "GraphTreeWindow.hpp" -#include "SubgraphModule.hpp" -#include "WindowFactory.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" -#include "raul/Path.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -GraphTreeWindow::GraphTreeWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Window(cobject) - , _app(nullptr) - , _enable_signal(true) -{ - xml->get_widget_derived("graphs_treeview", _graphs_treeview); - - _graph_treestore = Gtk::TreeStore::create(_graph_tree_columns); - _graphs_treeview->set_window(this); - _graphs_treeview->set_model(_graph_treestore); - Gtk::TreeViewColumn* name_col = Gtk::manage( - new Gtk::TreeViewColumn("Graph", _graph_tree_columns.name_col)); - Gtk::TreeViewColumn* enabled_col = Gtk::manage( - new Gtk::TreeViewColumn("Run", _graph_tree_columns.enabled_col)); - name_col->set_resizable(true); - name_col->set_expand(true); - - _graphs_treeview->append_column(*name_col); - _graphs_treeview->append_column(*enabled_col); - Gtk::CellRendererToggle* enabled_renderer = dynamic_cast<Gtk::CellRendererToggle*>( - _graphs_treeview->get_column_cell_renderer(1)); - enabled_renderer->property_activatable() = true; - - _graph_tree_selection = _graphs_treeview->get_selection(); - - _graphs_treeview->signal_row_activated().connect( - sigc::mem_fun(this, &GraphTreeWindow::event_graph_activated)); - enabled_renderer->signal_toggled().connect( - sigc::mem_fun(this, &GraphTreeWindow::event_graph_enabled_toggled)); - - _graphs_treeview->columns_autosize(); -} - -void -GraphTreeWindow::init(App& app, ClientStore& store) -{ - _app = &app; - store.signal_new_object().connect( - sigc::mem_fun(this, &GraphTreeWindow::new_object)); -} - -void -GraphTreeWindow::new_object(SPtr<ObjectModel> object) -{ - SPtr<GraphModel> graph = dynamic_ptr_cast<GraphModel>(object); - if (graph) { - add_graph(graph); - } -} - -void -GraphTreeWindow::add_graph(SPtr<GraphModel> pm) -{ - if (!pm->parent()) { - Gtk::TreeModel::iterator iter = _graph_treestore->append(); - Gtk::TreeModel::Row row = *iter; - if (pm->path().is_root()) { - row[_graph_tree_columns.name_col] = _app->interface()->uri().string(); - } else { - row[_graph_tree_columns.name_col] = pm->symbol().c_str(); - } - row[_graph_tree_columns.enabled_col] = pm->enabled(); - row[_graph_tree_columns.graph_model_col] = pm; - _graphs_treeview->expand_row(_graph_treestore->get_path(iter), true); - } else { - Gtk::TreeModel::Children children = _graph_treestore->children(); - Gtk::TreeModel::iterator c = find_graph(children, pm->parent()); - - if (c != children.end()) { - Gtk::TreeModel::iterator iter = _graph_treestore->append(c->children()); - Gtk::TreeModel::Row row = *iter; - row[_graph_tree_columns.name_col] = pm->symbol().c_str(); - row[_graph_tree_columns.enabled_col] = pm->enabled(); - row[_graph_tree_columns.graph_model_col] = pm; - _graphs_treeview->expand_row(_graph_treestore->get_path(iter), true); - } - } - - pm->signal_property().connect( - sigc::bind(sigc::mem_fun(this, &GraphTreeWindow::graph_property_changed), - pm)); - - pm->signal_moved().connect( - sigc::bind(sigc::mem_fun(this, &GraphTreeWindow::graph_moved), - pm)); - - pm->signal_destroyed().connect( - sigc::bind(sigc::mem_fun(this, &GraphTreeWindow::remove_graph), - pm)); -} - -void -GraphTreeWindow::remove_graph(SPtr<GraphModel> pm) -{ - Gtk::TreeModel::iterator i = find_graph(_graph_treestore->children(), pm); - if (i != _graph_treestore->children().end()) { - _graph_treestore->erase(i); - } -} - -Gtk::TreeModel::iterator -GraphTreeWindow::find_graph(Gtk::TreeModel::Children root, - SPtr<Client::ObjectModel> graph) -{ - for (Gtk::TreeModel::iterator c = root.begin(); c != root.end(); ++c) { - SPtr<GraphModel> pm = (*c)[_graph_tree_columns.graph_model_col]; - if (graph == pm) { - return c; - } else if ((*c)->children().size() > 0) { - Gtk::TreeModel::iterator ret = find_graph(c->children(), graph); - if (ret != c->children().end()) { - return ret; - } - } - } - return root.end(); -} - -/** Show the context menu for the selected graph in the graphs treeview. - */ -void -GraphTreeWindow::show_graph_menu(GdkEventButton* ev) -{ - Gtk::TreeModel::iterator active = _graph_tree_selection->get_selected(); - if (active) { - Gtk::TreeModel::Row row = *active; - SPtr<GraphModel> pm = row[_graph_tree_columns.graph_model_col]; - if (pm) { - _app->log().warn("TODO: graph menu from tree window"); - } - } -} - -void -GraphTreeWindow::event_graph_activated(const Gtk::TreeModel::Path& path, - Gtk::TreeView::Column* col) -{ - Gtk::TreeModel::iterator active = _graph_treestore->get_iter(path); - Gtk::TreeModel::Row row = *active; - SPtr<GraphModel> pm = row[_graph_tree_columns.graph_model_col]; - - _app->window_factory()->present_graph(pm); -} - -void -GraphTreeWindow::event_graph_enabled_toggled(const Glib::ustring& path_str) -{ - Gtk::TreeModel::Path path(path_str); - Gtk::TreeModel::iterator active = _graph_treestore->get_iter(path); - Gtk::TreeModel::Row row = *active; - - SPtr<GraphModel> pm = row[_graph_tree_columns.graph_model_col]; - assert(pm); - - if (_enable_signal) { - _app->set_property(pm->uri(), - _app->uris().ingen_enabled, - _app->forge().make((bool)!pm->enabled())); - } -} - -void -GraphTreeWindow::graph_property_changed(const URI& key, - const Atom& value, - SPtr<GraphModel> graph) -{ - const URIs& uris = _app->uris(); - _enable_signal = false; - if (key == uris.ingen_enabled && value.type() == uris.forge.Bool) { - Gtk::TreeModel::iterator i = find_graph(_graph_treestore->children(), graph); - if (i != _graph_treestore->children().end()) { - Gtk::TreeModel::Row row = *i; - row[_graph_tree_columns.enabled_col] = value.get<int32_t>(); - } else { - _app->log().error(fmt("Unable to find graph %1%\n") - % graph->path()); - } - } - _enable_signal = true; -} - -void -GraphTreeWindow::graph_moved(SPtr<GraphModel> graph) -{ - _enable_signal = false; - - Gtk::TreeModel::iterator i - = find_graph(_graph_treestore->children(), graph); - - if (i != _graph_treestore->children().end()) { - Gtk::TreeModel::Row row = *i; - row[_graph_tree_columns.name_col] = graph->symbol().c_str(); - } else { - _app->log().error(fmt("Unable to find graph %1%\n") - % graph->path()); - } - - _enable_signal = true; -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/GraphTreeWindow.hpp b/src/gui/GraphTreeWindow.hpp deleted file mode 100644 index 005f39a8..00000000 --- a/src/gui/GraphTreeWindow.hpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_GRAPHTREEWINDOW_HPP -#define INGEN_GUI_GRAPHTREEWINDOW_HPP - -#include <gtkmm/builder.h> -#include <gtkmm/treemodel.h> -#include <gtkmm/treestore.h> -#include <gtkmm/treeview.h> - -#include "Window.hpp" - -namespace Raul { class Path; } - -namespace Ingen { - -namespace Client { class ClientStore; class ObjectModel; } - -namespace GUI { - -class GraphWindow; -class GraphTreeView; - -/** Window with a TreeView of all loaded graphs. - * - * \ingroup GUI - */ -class GraphTreeWindow : public Window -{ -public: - GraphTreeWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - void init(App& app, Client::ClientStore& store); - - void new_object(SPtr<Client::ObjectModel> object); - - void graph_property_changed(const URI& key, - const Atom& value, - SPtr<Client::GraphModel> graph); - - void graph_moved(SPtr<Client::GraphModel> graph); - - void add_graph(SPtr<Client::GraphModel> pm); - void remove_graph(SPtr<Client::GraphModel> pm); - void show_graph_menu(GdkEventButton* ev); - -protected: - void event_graph_activated(const Gtk::TreeModel::Path& path, - Gtk::TreeView::Column* col); - - void event_graph_enabled_toggled(const Glib::ustring& path_str); - - Gtk::TreeModel::iterator find_graph( - Gtk::TreeModel::Children root, - SPtr<Client::ObjectModel> graph); - - GraphTreeView* _graphs_treeview; - - struct GraphTreeModelColumns : public Gtk::TreeModel::ColumnRecord - { - GraphTreeModelColumns() { - add(name_col); - add(enabled_col); - add(graph_model_col); - } - - Gtk::TreeModelColumn<Glib::ustring> name_col; - Gtk::TreeModelColumn<bool> enabled_col; - Gtk::TreeModelColumn<SPtr<Client::GraphModel> > graph_model_col; - }; - - App* _app; - GraphTreeModelColumns _graph_tree_columns; - Glib::RefPtr<Gtk::TreeStore> _graph_treestore; - Glib::RefPtr<Gtk::TreeSelection> _graph_tree_selection; - bool _enable_signal; -}; - -/** Derived TreeView class to support context menus for graphs */ -class GraphTreeView : public Gtk::TreeView -{ -public: - GraphTreeView(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Gtk::TreeView(cobject) - , _window(NULL) - {} - - void set_window(GraphTreeWindow* win) { _window = win; } - - bool on_button_press_event(GdkEventButton* ev) { - bool ret = Gtk::TreeView::on_button_press_event(ev); - - if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3)) - _window->show_graph_menu(ev); - - return ret; - } - -private: - GraphTreeWindow* _window; - -}; // struct GraphTreeView - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_GRAPHTREEWINDOW_HPP diff --git a/src/gui/GraphView.cpp b/src/gui/GraphView.cpp deleted file mode 100644 index e6361249..00000000 --- a/src/gui/GraphView.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <fstream> - -#include "ingen/Interface.hpp" -#include "ingen/client/GraphModel.hpp" - -#include "App.hpp" -#include "LoadPluginWindow.hpp" -#include "NewSubgraphWindow.hpp" -#include "GraphCanvas.hpp" -#include "GraphTreeWindow.hpp" -#include "GraphView.hpp" -#include "WidgetFactory.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -GraphView::GraphView(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Gtk::Box(cobject) - , _app(nullptr) - , _breadcrumb_container(nullptr) - , _enable_signal(true) -{ - property_visible() = false; - - xml->get_widget("graph_view_breadcrumb_container", _breadcrumb_container); - xml->get_widget("graph_view_toolbar", _toolbar); - xml->get_widget("graph_view_process_but", _process_but); - xml->get_widget("graph_view_poly_spin", _poly_spin); - xml->get_widget("graph_view_scrolledwindow", _canvas_scrolledwindow); - - _toolbar->set_toolbar_style(Gtk::TOOLBAR_ICONS); - _canvas_scrolledwindow->property_hadjustment().get_value()->set_step_increment(10); - _canvas_scrolledwindow->property_vadjustment().get_value()->set_step_increment(10); -} - -GraphView::~GraphView() -{ - _canvas_scrolledwindow->remove(); -} - -void -GraphView::init(App& app) -{ - _app = &app; -} - -void -GraphView::set_graph(SPtr<const GraphModel> graph) -{ - assert(!_canvas); // FIXME: remove - - assert(_breadcrumb_container); // ensure created - - _graph = graph; - _canvas = SPtr<GraphCanvas>(new GraphCanvas(*_app, graph, 1600*2, 1200*2)); - _canvas->build(); - - _canvas_scrolledwindow->add(_canvas->widget()); - - _poly_spin->set_range(1, 128); - _poly_spin->set_increments(1, 4); - _poly_spin->set_value(graph->internal_poly()); - - for (const auto& p : graph->properties()) { - property_changed(p.first, p.second); - } - - // Connect model signals to track state - graph->signal_property().connect( - sigc::mem_fun(this, &GraphView::property_changed)); - - // Connect widget signals to do things - _process_but->signal_toggled().connect( - sigc::mem_fun(this, &GraphView::process_toggled)); - - _poly_spin->signal_value_changed().connect( - sigc::mem_fun(*this, &GraphView::poly_changed)); - - _canvas->widget().grab_focus(); -} - -SPtr<GraphView> -GraphView::create(App& app, SPtr<const GraphModel> graph) -{ - GraphView* result = nullptr; - Glib::RefPtr<Gtk::Builder> xml = WidgetFactory::create("warehouse_win"); - xml->get_widget_derived("graph_view_box", result); - result->init(app); - result->set_graph(graph); - return SPtr<GraphView>(result); -} - -void -GraphView::process_toggled() -{ - if (!_enable_signal) { - return; - } - - _app->set_property(_graph->uri(), - _app->uris().ingen_enabled, - _app->forge().make((bool)_process_but->get_active())); -} - -void -GraphView::poly_changed() -{ - const int poly = _poly_spin->get_value_as_int(); - if (_enable_signal && poly != (int)_graph->internal_poly()) { - _app->set_property(_graph->uri(), - _app->uris().ingen_polyphony, - _app->forge().make(poly)); - } -} - -void -GraphView::property_changed(const URI& predicate, const Atom& value) -{ - _enable_signal = false; - if (predicate == _app->uris().ingen_enabled) { - if (value.type() == _app->uris().forge.Bool) { - _process_but->set_active(value.get<int32_t>()); - } - } else if (predicate == _app->uris().ingen_polyphony) { - if (value.type() == _app->uris().forge.Int) { - _poly_spin->set_value(value.get<int32_t>()); - } - } - _enable_signal = true; -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/GraphView.hpp b/src/gui/GraphView.hpp deleted file mode 100644 index 03569831..00000000 --- a/src/gui/GraphView.hpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_GRAPHVIEW_HPP -#define INGEN_GUI_GRAPHVIEW_HPP - -#include <gtkmm/box.h> -#include <gtkmm/builder.h> -#include <gtkmm/scrolledwindow.h> -#include <gtkmm/spinbutton.h> -#include <gtkmm/toggletoolbutton.h> -#include <gtkmm/toolbar.h> -#include <gtkmm/toolitem.h> -#include <gtkmm/toolitem.h> - -#include "ingen/types.hpp" - -namespace Raul { class Atom; } - -namespace Ingen { - -namespace Client { -class PortModel; -class MetadataModel; -class GraphModel; -class ObjectModel; -} - -namespace GUI { - -class App; -class LoadPluginWindow; -class NewSubgraphWindow; -class GraphCanvas; -class GraphDescriptionWindow; -class SubgraphModule; - -/** The graph specific contents of a GraphWindow (ie the canvas and whatever else). - * - * \ingroup GUI - */ -class GraphView : public Gtk::Box -{ -public: - GraphView(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - ~GraphView(); - - void init(App& app); - - SPtr<GraphCanvas> canvas() const { return _canvas; } - SPtr<const Client::GraphModel> graph() const { return _graph; } - Gtk::ToolItem* breadcrumb_container() const { return _breadcrumb_container; } - - static SPtr<GraphView> create(App& app, - SPtr<const Client::GraphModel> graph); - -private: - void set_graph(SPtr<const Client::GraphModel> graph); - - void process_toggled(); - void poly_changed(); - void clear_clicked(); - - void property_changed(const URI& predicate, const Atom& value); - - App* _app; - - SPtr<const Client::GraphModel> _graph; - SPtr<GraphCanvas> _canvas; - - Gtk::ScrolledWindow* _canvas_scrolledwindow; - Gtk::Toolbar* _toolbar; - Gtk::ToggleToolButton* _process_but; - Gtk::SpinButton* _poly_spin; - Gtk::ToolItem* _breadcrumb_container; - - bool _enable_signal; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_GRAPHVIEW_HPP diff --git a/src/gui/GraphWindow.cpp b/src/gui/GraphWindow.cpp deleted file mode 100644 index b5a89c79..00000000 --- a/src/gui/GraphWindow.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" - -#include "App.hpp" -#include "GraphCanvas.hpp" -#include "GraphView.hpp" -#include "GraphWindow.hpp" -#include "WindowFactory.hpp" - -namespace Ingen { -namespace GUI { - -GraphWindow::GraphWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Window(cobject) - , _box(nullptr) - , _position_stored(false) - , _x(0) - , _y(0) -{ - property_visible() = false; - - xml->get_widget_derived("graph_win_vbox", _box); - - set_title("Ingen"); -} - -GraphWindow::~GraphWindow() -{ - delete _box; -} - -void -GraphWindow::init_window(App& app) -{ - Window::init_window(app); - _box->init_box(app); - _box->set_window(this); -} - -void -GraphWindow::on_show() -{ - if (_position_stored) { - move(_x, _y); - } - - Gtk::Window::on_show(); - - _box->view()->canvas()->widget().grab_focus(); -} - -void -GraphWindow::on_hide() -{ - _position_stored = true; - get_position(_x, _y); - Gtk::Window::on_hide(); -} - -bool -GraphWindow::on_key_press_event(GdkEventKey* event) -{ - // Disable Window C-w handling so quit works correctly - return Gtk::Window::on_key_press_event(event); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/GraphWindow.hpp b/src/gui/GraphWindow.hpp deleted file mode 100644 index b4e51d7b..00000000 --- a/src/gui/GraphWindow.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_GRAPH_WINDOW_HPP -#define INGEN_GUI_GRAPH_WINDOW_HPP - -#include <string> - -#include <gtkmm/builder.h> - -#include "ingen/types.hpp" - -#include "GraphBox.hpp" -#include "Window.hpp" - -namespace Ingen { - -namespace Client { -class GraphModel; -} - -namespace GUI { - -/** A window for a graph. - * - * \ingroup GUI - */ -class GraphWindow : public Window -{ -public: - GraphWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - ~GraphWindow(); - - void init_window(App& app); - - SPtr<const Client::GraphModel> graph() const { return _box->graph(); } - GraphBox* box() const { return _box; } - - bool documentation_is_visible() { return _box->documentation_is_visible(); } - - void set_documentation(const std::string& doc, bool html) { - _box->set_documentation(doc, html); - } - - void show_port_status(const Client::PortModel* model, - const Atom& value) { - _box->show_port_status(model, value); - } - -protected: - void on_hide(); - void on_show(); - bool on_key_press_event(GdkEventKey* event); - -private: - GraphBox* _box; - bool _position_stored; - int _x; - int _y; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_GRAPH_WINDOW_HPP diff --git a/src/gui/LoadGraphWindow.cpp b/src/gui/LoadGraphWindow.cpp deleted file mode 100644 index b02ca510..00000000 --- a/src/gui/LoadGraphWindow.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <list> -#include <string> - -#include <boost/optional.hpp> -#include <glibmm/miscutils.h> - -#include "ingen/Configuration.hpp" -#include "ingen/Interface.hpp" -#include "ingen/client/BlockModel.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" -#include "ingen/runtime_paths.hpp" - -#include "App.hpp" -#include "GraphView.hpp" -#include "LoadGraphWindow.hpp" -#include "Style.hpp" -#include "ThreadedLoader.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -LoadGraphWindow::LoadGraphWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Gtk::FileChooserDialog(cobject) - , _app(nullptr) - , _merge_ports(false) -{ - xml->get_widget("load_graph_symbol_label", _symbol_label); - xml->get_widget("load_graph_symbol_entry", _symbol_entry); - xml->get_widget("load_graph_ports_label", _ports_label); - xml->get_widget("load_graph_merge_ports_radio", _merge_ports_radio); - xml->get_widget("load_graph_insert_ports_radio", _insert_ports_radio); - xml->get_widget("load_graph_poly_voices_radio", _poly_voices_radio); - xml->get_widget("load_graph_poly_from_file_radio", _poly_from_file_radio); - xml->get_widget("load_graph_poly_spinbutton", _poly_spinbutton); - xml->get_widget("load_graph_ok_button", _ok_button); - xml->get_widget("load_graph_cancel_button", _cancel_button); - - _cancel_button->signal_clicked().connect( - sigc::mem_fun(this, &LoadGraphWindow::cancel_clicked)); - _ok_button->signal_clicked().connect( - sigc::mem_fun(this, &LoadGraphWindow::ok_clicked)); - _merge_ports_radio->signal_toggled().connect( - sigc::mem_fun(this, &LoadGraphWindow::merge_ports_selected)); - _insert_ports_radio->signal_toggled().connect( - sigc::mem_fun(this, &LoadGraphWindow::insert_ports_selected)); - _poly_from_file_radio->signal_toggled().connect( - sigc::bind(sigc::mem_fun(_poly_spinbutton, &Gtk::SpinButton::set_sensitive), - false)); - _poly_voices_radio->signal_toggled().connect( - sigc::bind(sigc::mem_fun(_poly_spinbutton, &Gtk::SpinButton::set_sensitive), - true)); - - signal_selection_changed().connect( - sigc::mem_fun(this, &LoadGraphWindow::selection_changed)); - - Gtk::FileFilter file_filter; - file_filter.add_pattern("*.ttl"); - file_filter.set_name("Ingen graph files (*.ttl)"); - add_filter(file_filter); - - Gtk::FileFilter bundle_filter; - bundle_filter.add_pattern("*.ingen"); - bundle_filter.set_name("Ingen bundles (*.ingen)"); - add_filter(bundle_filter); - - property_select_multiple() = true; - - // Add global examples directory to "shortcut folders" (bookmarks) - const FilePath examples_dir = Ingen::data_file_path("graphs"); - if (Glib::file_test(examples_dir, Glib::FILE_TEST_IS_DIR)) { - add_shortcut_folder(examples_dir.string()); - } -} - -void -LoadGraphWindow::present(SPtr<const GraphModel> graph, - bool import, - Properties data) -{ - _import = import; - set_graph(graph); - _symbol_label->property_visible() = !import; - _symbol_entry->property_visible() = !import; - _ports_label->property_visible() = _import; - _merge_ports_radio->property_visible() = _import; - _insert_ports_radio->property_visible() = _import; - _initial_data = data; - Gtk::Window::present(); -} - -/** Sets the graph model for this window and initializes everything. - * - * This function MUST be called before using the window in any way! - */ -void -LoadGraphWindow::set_graph(SPtr<const GraphModel> graph) -{ - _graph = graph; - _symbol_entry->set_text(""); - _symbol_entry->set_sensitive(!_import); - _poly_spinbutton->set_value(graph->internal_poly()); -} - -void -LoadGraphWindow::on_show() -{ - const Atom& dir = _app->world()->conf().option("graph-directory"); - if (dir.is_valid()) { - set_current_folder(dir.ptr<char>()); - } - Gtk::FileChooserDialog::on_show(); -} - -void -LoadGraphWindow::merge_ports_selected() -{ - _merge_ports = true; -} - -void -LoadGraphWindow::insert_ports_selected() -{ - _merge_ports = false; -} - -void -LoadGraphWindow::ok_clicked() -{ - if (!_graph) { - hide(); - return; - } - - const URIs& uris = _app->uris(); - - if (_poly_voices_radio->get_active()) { - _initial_data.emplace( - uris.ingen_polyphony, - _app->forge().make(_poly_spinbutton->get_value_as_int())); - } - - if (get_uri() == "") { - return; - } - - if (_import) { - // If unset load_graph will load value - boost::optional<Raul::Path> parent; - boost::optional<Raul::Symbol> symbol; - if (!_graph->path().is_root()) { - parent = _graph->path().parent(); - symbol = _graph->symbol(); - } - - _app->loader()->load_graph( - true, FilePath(get_filename()), parent, symbol, _initial_data); - - } else { - std::list<Glib::ustring> uri_list = get_filenames(); - for (auto u : uri_list) { - // Cascade - Atom& x = _initial_data.find(uris.ingen_canvasX)->second; - x = _app->forge().make(x.get<float>() + 20.0f); - Atom& y = _initial_data.find(uris.ingen_canvasY)->second; - y = _app->forge().make(y.get<float>() + 20.0f); - - Raul::Symbol symbol(symbol_from_filename(u)); - if (uri_list.size() == 1 && _symbol_entry->get_text() != "") { - symbol = Raul::Symbol::symbolify(_symbol_entry->get_text()); - } - - symbol = avoid_symbol_clash(symbol); - - _app->loader()->load_graph( - false, FilePath(URI(u).path()), _graph->path(), symbol, _initial_data); - } - } - - _graph.reset(); - hide(); - - _app->world()->conf().set( - "graph-directory", - _app->world()->forge().alloc(get_current_folder())); -} - -void -LoadGraphWindow::cancel_clicked() -{ - _graph.reset(); - hide(); -} - -Raul::Symbol -LoadGraphWindow::symbol_from_filename(const Glib::ustring& filename) -{ - std::string symbol_str = Glib::path_get_basename(get_filename()); - symbol_str = symbol_str.substr(0, symbol_str.find('.')); - return Raul::Symbol::symbolify(symbol_str); -} - -Raul::Symbol -LoadGraphWindow::avoid_symbol_clash(const Raul::Symbol& symbol) -{ - unsigned offset = _app->store()->child_name_offset( - _graph->path(), symbol); - - if (offset != 0) { - std::stringstream ss; - ss << symbol << "_" << offset; - return Raul::Symbol(ss.str()); - } else { - return symbol; - } -} - -void -LoadGraphWindow::selection_changed() -{ - if (_import) { - return; - } - - if (get_filenames().size() != 1) { - _symbol_entry->set_text(""); - _symbol_entry->set_sensitive(false); - } else { - _symbol_entry->set_text( - avoid_symbol_clash(symbol_from_filename(get_filename())).c_str()); - _symbol_entry->set_sensitive(true); - } -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/LoadGraphWindow.hpp b/src/gui/LoadGraphWindow.hpp deleted file mode 100644 index 8ec5ed4b..00000000 --- a/src/gui/LoadGraphWindow.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_LOADGRAPHWINDOW_HPP -#define INGEN_GUI_LOADGRAPHWINDOW_HPP - -#include <gtkmm/builder.h> -#include <gtkmm/button.h> -#include <gtkmm/entry.h> -#include <gtkmm/filechooserdialog.h> -#include <gtkmm/label.h> -#include <gtkmm/radiobutton.h> -#include <gtkmm/spinbutton.h> - -#include "ingen/Node.hpp" -#include "ingen/types.hpp" - -namespace Ingen { - -namespace Client { class GraphModel; } - -namespace GUI { - -/** 'Load Graph' Window. - * - * Loaded from XML as a derived object. - * - * \ingroup GUI - */ -class LoadGraphWindow : public Gtk::FileChooserDialog -{ -public: - LoadGraphWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - void init(App& app) { _app = &app; } - - void set_graph(SPtr<const Client::GraphModel> graph); - - void present(SPtr<const Client::GraphModel> graph, - bool import, - Properties data); - -protected: - void on_show(); - -private: - void merge_ports_selected(); - void insert_ports_selected(); - - void selection_changed(); - void cancel_clicked(); - void ok_clicked(); - - Raul::Symbol symbol_from_filename(const Glib::ustring& filename); - Raul::Symbol avoid_symbol_clash(const Raul::Symbol& symbol); - - App* _app; - - Properties _initial_data; - - SPtr<const Client::GraphModel> _graph; - - Gtk::Label* _symbol_label; - Gtk::Entry* _symbol_entry; - Gtk::Label* _ports_label; - Gtk::RadioButton* _merge_ports_radio; - Gtk::RadioButton* _insert_ports_radio; - Gtk::RadioButton* _poly_voices_radio; - Gtk::RadioButton* _poly_from_file_radio; - Gtk::SpinButton* _poly_spinbutton; - Gtk::Button* _ok_button; - Gtk::Button* _cancel_button; - - bool _import; - bool _merge_ports; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_LOADGRAPHWINDOW_HPP diff --git a/src/gui/LoadPluginWindow.cpp b/src/gui/LoadPluginWindow.cpp deleted file mode 100644 index bd08f457..00000000 --- a/src/gui/LoadPluginWindow.cpp +++ /dev/null @@ -1,511 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <string> - -#include <stddef.h> - -#include <cassert> -#include <algorithm> - -#include "ingen/Interface.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" - -#include "App.hpp" -#include "LoadPluginWindow.hpp" -#include "GraphCanvas.hpp" -#include "GraphView.hpp" -#include "GraphWindow.hpp" - -#include "ingen_config.h" - -using std::string; - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -LoadPluginWindow::LoadPluginWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Window(cobject) - , _name_offset(0) - , _has_shown(false) - , _refresh_list(true) -{ - xml->get_widget("load_plugin_plugins_treeview", _plugins_treeview); - xml->get_widget("load_plugin_polyphonic_checkbutton", _polyphonic_checkbutton); - xml->get_widget("load_plugin_name_entry", _name_entry); - xml->get_widget("load_plugin_add_button", _add_button); - xml->get_widget("load_plugin_close_button", _close_button); - - xml->get_widget("load_plugin_filter_combo", _filter_combo); - xml->get_widget("load_plugin_search_entry", _search_entry); - - // Set up the plugins list - _plugins_liststore = Gtk::ListStore::create(_plugins_columns); - _plugins_treeview->set_model(_plugins_liststore); - _plugins_treeview->append_column("_Name", _plugins_columns._col_name); - _plugins_treeview->append_column("_Type", _plugins_columns._col_type); - _plugins_treeview->append_column("_Project", _plugins_columns._col_project); - _plugins_treeview->append_column("_Author", _plugins_columns._col_author); - _plugins_treeview->append_column("_URI", _plugins_columns._col_uri); - - // This could be nicer.. store the TreeViewColumns locally maybe? - _plugins_treeview->get_column(0)->set_sort_column(_plugins_columns._col_name); - _plugins_treeview->get_column(1)->set_sort_column(_plugins_columns._col_type); - _plugins_treeview->get_column(2)->set_sort_column(_plugins_columns._col_project); - _plugins_treeview->get_column(2)->set_sort_column(_plugins_columns._col_author); - _plugins_treeview->get_column(3)->set_sort_column(_plugins_columns._col_uri); - for (int i = 0; i < 5; ++i) { - _plugins_treeview->get_column(i)->set_resizable(true); - } - - // Set up the search criteria combobox - _criteria_liststore = Gtk::ListStore::create(_criteria_columns); - _filter_combo->set_model(_criteria_liststore); - - Gtk::TreeModel::iterator iter = _criteria_liststore->append(); - Gtk::TreeModel::Row row = *iter; - row[_criteria_columns._col_label] = "Name contains"; - row[_criteria_columns._col_criteria] = CriteriaColumns::Criteria::NAME; - _filter_combo->set_active(iter); - - row = *(iter = _criteria_liststore->append()); - row[_criteria_columns._col_label] = "Type contains"; - row[_criteria_columns._col_criteria] = CriteriaColumns::Criteria::TYPE; - - row = *(iter = _criteria_liststore->append()); - row[_criteria_columns._col_label] = "Project contains"; - row[_criteria_columns._col_criteria] = CriteriaColumns::Criteria::PROJECT; - - row = *(iter = _criteria_liststore->append()); - row[_criteria_columns._col_label] = "Author contains"; - row[_criteria_columns._col_criteria] = CriteriaColumns::Criteria::AUTHOR; - - row = *(iter = _criteria_liststore->append()); - row[_criteria_columns._col_label] = "URI contains"; - row[_criteria_columns._col_criteria] = CriteriaColumns::Criteria::URI; - _filter_combo->pack_start(_criteria_columns._col_label); - - _add_button->signal_clicked().connect( - sigc::mem_fun(this, &LoadPluginWindow::add_clicked)); - _close_button->signal_clicked().connect( - sigc::mem_fun(this, &Window::hide)); - _plugins_treeview->signal_row_activated().connect( - sigc::mem_fun(this, &LoadPluginWindow::plugin_activated)); - _search_entry->signal_activate().connect( - sigc::mem_fun(this, &LoadPluginWindow::add_clicked)); - _search_entry->signal_changed().connect( - sigc::mem_fun(this, &LoadPluginWindow::filter_changed)); - _name_entry->signal_changed().connect( - sigc::mem_fun(this, &LoadPluginWindow::name_changed)); - - _search_entry->signal_icon_release().connect( - sigc::mem_fun(this, &LoadPluginWindow::name_cleared)); - - _selection = _plugins_treeview->get_selection(); - _selection->set_mode(Gtk::SELECTION_MULTIPLE); - _selection->signal_changed().connect( - sigc::mem_fun(this, &LoadPluginWindow::plugin_selection_changed)); - - //m_add_button->grab_default(); -} - -void -LoadPluginWindow::present(SPtr<const GraphModel> graph, - Properties data) -{ - set_graph(graph); - _initial_data = data; - Gtk::Window::present(); -} - -/** Called every time the user types into the name input box. - * Used to display warning messages, and enable/disable the OK button. - */ -void -LoadPluginWindow::name_changed() -{ - // Toggle add button sensitivity according name legality - if (_selection->get_selected_rows().size() == 1) { - const string sym = _name_entry->get_text(); - if (!Raul::Symbol::is_valid(sym)) { - _add_button->property_sensitive() = false; - } else if (_app->store()->find(_graph->path().child(Raul::Symbol(sym))) - != _app->store()->end()) { - _add_button->property_sensitive() = false; - } else { - _add_button->property_sensitive() = true; - } - } -} - -void -LoadPluginWindow::name_cleared(Gtk::EntryIconPosition pos, const GdkEventButton* event) -{ - _search_entry->set_text(""); -} - -/** Sets the graph controller for this window and initializes everything. - * - * This function MUST be called before using the window in any way! - */ -void -LoadPluginWindow::set_graph(SPtr<const GraphModel> graph) -{ - if (_graph) { - _graph = graph; - plugin_selection_changed(); - } else { - _graph = graph; - } -} - -/** Populates the plugin list on the first show. - * - * This is done here instead of construction time as the list population is - * really expensive and bogs down creation of a graph. This is especially - * important when many graph notifications are sent at one time from the - * engine. - */ -void -LoadPluginWindow::on_show() -{ - if (!_has_shown) { - _app->store()->signal_new_plugin().connect( - sigc::mem_fun(this, &LoadPluginWindow::add_plugin)); - _has_shown = true; - } - - if (_refresh_list) { - set_plugins(_app->store()->plugins()); - _refresh_list = false; - } - - Gtk::Window::on_show(); -} - -void -LoadPluginWindow::set_plugins(SPtr<const ClientStore::Plugins> plugins) -{ - _rows.clear(); - _plugins_liststore->clear(); - - for (const auto& p : *plugins.get()) { - add_plugin(p.second); - } - - _plugins_liststore->set_sort_column(1, Gtk::SORT_ASCENDING); - _plugins_treeview->columns_autosize(); -} - -void -LoadPluginWindow::new_plugin(SPtr<const PluginModel> pm) -{ - if (is_visible()) { - add_plugin(pm); - } else { - _refresh_list = true; - } -} - -static std::string -get_project_name(SPtr<const PluginModel> plugin) -{ - std::string name; - if (plugin->lilv_plugin()) { - LilvNode* project = lilv_plugin_get_project(plugin->lilv_plugin()); - if (!project) { - return ""; - } - - LilvNode* doap_name = lilv_new_uri( - plugin->lilv_world(), "http://usefulinc.com/ns/doap#name"); - LilvNodes* names = lilv_world_find_nodes( - plugin->lilv_world(), project, doap_name, nullptr); - - if (names) { - name = lilv_node_as_string(lilv_nodes_get_first(names)); - } - - lilv_nodes_free(names); - lilv_node_free(doap_name); - lilv_node_free(project); - } - return name; -} - -static std::string -get_author_name(SPtr<const PluginModel> plugin) -{ - std::string name; - if (plugin->lilv_plugin()) { - LilvNode* author = lilv_plugin_get_author_name(plugin->lilv_plugin()); - if (author) { - name = lilv_node_as_string(author); - } - lilv_node_free(author); - } - return name; -} - -void -LoadPluginWindow::set_row(Gtk::TreeModel::Row& row, - SPtr<const PluginModel> plugin) -{ - const URIs& uris = _app->uris(); - const Atom& name = plugin->get_property(uris.doap_name); - if (name.is_valid() && name.type() == uris.forge.String) { - row[_plugins_columns._col_name] = name.ptr<char>(); - } - - if (uris.lv2_Plugin == plugin->type()) { - row[_plugins_columns._col_type] = lilv_node_as_string( - lilv_plugin_class_get_label( - lilv_plugin_get_class(plugin->lilv_plugin()))); - - row[_plugins_columns._col_project] = get_project_name(plugin); - row[_plugins_columns._col_author] = get_author_name(plugin); - } else if (uris.ingen_Internal == plugin->type()) { - row[_plugins_columns._col_type] = "Internal"; - row[_plugins_columns._col_project] = "Ingen"; - row[_plugins_columns._col_author] = "David Robillard"; - } else if (uris.ingen_Graph == plugin->type()) { - row[_plugins_columns._col_type] = "Graph"; - } else { - row[_plugins_columns._col_type] = ""; - } - - row[_plugins_columns._col_uri] = plugin->uri().string(); - row[_plugins_columns._col_plugin] = plugin; -} - -void -LoadPluginWindow::add_plugin(SPtr<const PluginModel> plugin) -{ - if (plugin->lilv_plugin() && lilv_plugin_is_replaced(plugin->lilv_plugin())) { - return; - } - - Gtk::TreeModel::iterator iter = _plugins_liststore->append(); - Gtk::TreeModel::Row row = *iter; - _rows.emplace(plugin->uri(), iter); - - set_row(row, plugin); - - plugin->signal_property().connect( - sigc::bind<0>(sigc::mem_fun(this, &LoadPluginWindow::plugin_property_changed), - plugin->uri())); -} - -///// Event Handlers ////// - -void -LoadPluginWindow::plugin_activated(const Gtk::TreeModel::Path& path, - Gtk::TreeViewColumn* col) -{ - add_clicked(); -} - -void -LoadPluginWindow::plugin_selection_changed() -{ - size_t n_selected = _selection->get_selected_rows().size(); - if (n_selected == 0) { - _name_offset = 0; - _name_entry->set_text(""); - _name_entry->set_sensitive(false); - } else if (n_selected == 1) { - Gtk::TreeModel::iterator iter = _plugins_liststore->get_iter( - *_selection->get_selected_rows().begin()); - if (iter) { - Gtk::TreeModel::Row row = *iter; - SPtr<const PluginModel> p = row.get_value( - _plugins_columns._col_plugin); - _name_offset = _app->store()->child_name_offset( - _graph->path(), p->default_block_symbol()); - _name_entry->set_text(generate_module_name(p, _name_offset)); - _name_entry->set_sensitive(true); - } else { - _name_offset = 0; - _name_entry->set_text(""); - _name_entry->set_sensitive(false); - } - } else { - _name_entry->set_text(""); - _name_entry->set_sensitive(false); - } -} - -/** Generate an automatic name for this Node. - * - * Offset is an offset of the number that will be appended to the plugin's - * label, needed if the user adds multiple plugins faster than the engine - * sends the notification back. - */ -string -LoadPluginWindow::generate_module_name(SPtr<const PluginModel> plugin, - int offset) -{ - std::stringstream ss; - ss << plugin->default_block_symbol(); - if (offset != 0) { - ss << "_" << offset; - } - return ss.str(); -} - -void -LoadPluginWindow::load_plugin(const Gtk::TreeModel::iterator& iter) -{ - const URIs& uris = _app->uris(); - Gtk::TreeModel::Row row = *iter; - SPtr<const PluginModel> plugin = row.get_value(_plugins_columns._col_plugin); - bool polyphonic = _polyphonic_checkbutton->get_active(); - string name = _name_entry->get_text(); - - if (name.empty()) { - name = generate_module_name(plugin, _name_offset); - } - - if (name.empty() || !Raul::Symbol::is_valid(name)) { - Gtk::MessageDialog dialog( - *this, - "Unable to choose a default name, please provide one", - false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - - dialog.run(); - } else { - Raul::Path path = _graph->path().child(Raul::Symbol::symbolify(name)); - Properties props = _initial_data; - props.emplace(uris.rdf_type, Property(uris.ingen_Block)); - props.emplace(uris.lv2_prototype, _app->forge().make_urid(plugin->uri())); - props.emplace(uris.ingen_polyphonic, _app->forge().make(polyphonic)); - _app->interface()->put(path_to_uri(path), props); - - if (_selection->get_selected_rows().size() == 1) { - _name_offset = (_name_offset == 0) ? 2 : _name_offset + 1; - _name_entry->set_text(generate_module_name(plugin, _name_offset)); - } - - // Cascade next block - Atom& x = _initial_data.find(uris.ingen_canvasX)->second; - x = _app->forge().make(x.get<float>() + 20.0f); - Atom& y = _initial_data.find(uris.ingen_canvasY)->second; - y = _app->forge().make(y.get<float>() + 20.0f); - } -} - -void -LoadPluginWindow::add_clicked() -{ - _selection->selected_foreach_iter( - sigc::mem_fun(*this, &LoadPluginWindow::load_plugin)); -} - -void -LoadPluginWindow::filter_changed() -{ - _rows.clear(); - _plugins_liststore->clear(); - string search = _search_entry->get_text(); - transform(search.begin(), search.end(), search.begin(), ::toupper); - - // Get selected criteria - const Gtk::TreeModel::Row row = *(_filter_combo->get_active()); - CriteriaColumns::Criteria criteria = row[_criteria_columns._col_criteria]; - - string field; - - Gtk::TreeModel::Row model_row; - Gtk::TreeModel::iterator model_iter; - size_t num_visible = 0; - const URIs& uris = _app->uris(); - - for (const auto& p : *_app->store()->plugins().get()) { - const SPtr<PluginModel> plugin = p.second; - const Atom& name = plugin->get_property(uris.doap_name); - - switch (criteria) { - case CriteriaColumns::Criteria::NAME: - if (name.is_valid() && name.type() == uris.forge.String) { - field = name.ptr<char>(); - } - break; - case CriteriaColumns::Criteria::TYPE: - if (plugin->lilv_plugin()) { - field = lilv_node_as_string( - lilv_plugin_class_get_label( - lilv_plugin_get_class(plugin->lilv_plugin()))); - } - break; - case CriteriaColumns::Criteria::PROJECT: - field = get_project_name(plugin); - break; - case CriteriaColumns::Criteria::AUTHOR: - field = get_author_name(plugin); - break; - case CriteriaColumns::Criteria::URI: - field = plugin->uri(); - break; - } - - transform(field.begin(), field.end(), field.begin(), ::toupper); - - if (field.find(search) != string::npos) { - model_iter = _plugins_liststore->append(); - model_row = *model_iter; - set_row(model_row, plugin); - ++num_visible; - } - } - - if (num_visible == 1) { - _selection->unselect_all(); - _selection->select(model_iter); - } -} - -bool -LoadPluginWindow::on_key_press_event(GdkEventKey* event) -{ - if (event->keyval == GDK_w && event->state & GDK_CONTROL_MASK) { - hide(); - return true; - } else { - return Gtk::Window::on_key_press_event(event); - } -} - -void -LoadPluginWindow::plugin_property_changed(const URI& plugin, - const URI& predicate, - const Atom& value) -{ - const URIs& uris = _app->uris(); - if (predicate == uris.doap_name) { - Rows::const_iterator i = _rows.find(plugin); - if (i != _rows.end() && value.type() == uris.forge.String) { - (*i->second)[_plugins_columns._col_name] = value.ptr<char>(); - } - } -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/LoadPluginWindow.hpp b/src/gui/LoadPluginWindow.hpp deleted file mode 100644 index 29314c7a..00000000 --- a/src/gui/LoadPluginWindow.hpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_LOADPLUGINWINDOW_HPP -#define INGEN_GUI_LOADPLUGINWINDOW_HPP - -#include <map> -#include <string> - -#include <gtkmm/builder.h> -#include <gtkmm/combobox.h> -#include <gtkmm/liststore.h> -#include <gtkmm/treemodel.h> -#include <gtkmm/treeview.h> - -#include "ingen/Node.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/types.hpp" -#include "ingen_config.h" - -#include "Window.hpp" - -namespace Ingen { - -namespace Client { -class GraphModel; -class PluginModel; -} - -namespace GUI { - -/** 'Load Plugin' window. - * - * Loaded from XML as a derived object. - * - * \ingroup GUI - */ -class LoadPluginWindow : public Window -{ -public: - LoadPluginWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - void set_graph(SPtr<const Client::GraphModel> graph); - void set_plugins(SPtr<const Client::ClientStore::Plugins> plugins); - - void add_plugin(SPtr<const Client::PluginModel> plugin); - - void present(SPtr<const Client::GraphModel> graph, - Properties data); - -protected: - void on_show(); - bool on_key_press_event(GdkEventKey* event); - -private: - /** Columns for the plugin list */ - class ModelColumns : public Gtk::TreeModel::ColumnRecord { - public: - ModelColumns() { - add(_col_name); - add(_col_type); - add(_col_project); - add(_col_author); - add(_col_uri); - add(_col_plugin); - } - - Gtk::TreeModelColumn<Glib::ustring> _col_name; - Gtk::TreeModelColumn<Glib::ustring> _col_type; - Gtk::TreeModelColumn<Glib::ustring> _col_project; - Gtk::TreeModelColumn<Glib::ustring> _col_author; - Gtk::TreeModelColumn<Glib::ustring> _col_uri; - - // Not displayed: - Gtk::TreeModelColumn< SPtr<const Client::PluginModel> > _col_plugin; - }; - - /** Column for the filter criteria combo box. */ - class CriteriaColumns : public Gtk::TreeModel::ColumnRecord { - public: - enum class Criteria { NAME, TYPE, PROJECT, AUTHOR, URI, }; - - CriteriaColumns() { - add(_col_label); - add(_col_criteria); - } - - Gtk::TreeModelColumn<Glib::ustring> _col_label; - Gtk::TreeModelColumn<Criteria> _col_criteria; - }; - - void add_clicked(); - void filter_changed(); - void clear_clicked(); - void name_changed(); - void name_cleared(Gtk::EntryIconPosition pos, const GdkEventButton* event); - - void set_row(Gtk::TreeModel::Row& row, - SPtr<const Client::PluginModel> plugin); - - void new_plugin(SPtr<const Client::PluginModel> pm); - - void plugin_property_changed(const URI& plugin, - const URI& predicate, - const Atom& value); - - void plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col); - void plugin_selection_changed(); - - std::string generate_module_name(SPtr<const Client::PluginModel> plugin, - int offset=0); - - void load_plugin(const Gtk::TreeModel::iterator& iter); - - Properties _initial_data; - - SPtr<const Client::GraphModel> _graph; - - typedef std::map<URI, Gtk::TreeModel::iterator> Rows; - Rows _rows; - - Glib::RefPtr<Gtk::ListStore> _plugins_liststore; - ModelColumns _plugins_columns; - - Glib::RefPtr<Gtk::ListStore> _criteria_liststore; - CriteriaColumns _criteria_columns; - - Glib::RefPtr<Gtk::TreeSelection> _selection; - - int _name_offset; // see comments for generate_plugin_name - - bool _has_shown; - bool _refresh_list; - Gtk::TreeView* _plugins_treeview; - Gtk::CheckButton* _polyphonic_checkbutton; - Gtk::Entry* _name_entry; - Gtk::Button* _close_button; - Gtk::Button* _add_button; - Gtk::ComboBox* _filter_combo; - Gtk::Entry* _search_entry; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_LOADPLUGINWINDOW_HPP diff --git a/src/gui/MessagesWindow.cpp b/src/gui/MessagesWindow.cpp deleted file mode 100644 index 581e732c..00000000 --- a/src/gui/MessagesWindow.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <string> - -#include "ingen/URIs.hpp" - -#include "App.hpp" -#include "MessagesWindow.hpp" - -namespace Ingen { -namespace GUI { -using std::string; - -MessagesWindow::MessagesWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Window(cobject) -{ - xml->get_widget("messages_textview", _textview); - xml->get_widget("messages_clear_button", _clear_button); - xml->get_widget("messages_close_button", _close_button); - - _clear_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::clear_clicked)); - _close_button->signal_clicked().connect(sigc::mem_fun(this, &Window::hide)); - - for (int s = Gtk::STATE_NORMAL; s <= Gtk::STATE_INSENSITIVE; ++s) { - _textview->modify_base((Gtk::StateType)s, Gdk::Color("#000000")); - _textview->modify_text((Gtk::StateType)s, Gdk::Color("#EEEEEC")); - } -} - -void -MessagesWindow::init_window(App& app) -{ - Glib::RefPtr<Gtk::TextTag> tag = Gtk::TextTag::create(); - tag->property_foreground() = "#EF2929"; - _tags.emplace(app.uris().log_Error, tag); - _error_tag = tag; - - tag = Gtk::TextTag::create(); - tag->property_foreground() = "#FCAF3E"; - _tags.emplace(app.uris().log_Warning, tag); - - tag = Gtk::TextTag::create(); - tag->property_foreground() = "#8AE234"; - _tags.emplace(app.uris().log_Trace, tag); - - for (const auto& t : _tags) { - _textview->get_buffer()->get_tag_table()->add(t.second); - } -} - -void -MessagesWindow::post_error(const string& msg) -{ - Glib::RefPtr<Gtk::TextBuffer> text_buf = _textview->get_buffer(); - text_buf->insert_with_tag(text_buf->end(), msg, _error_tag); - text_buf->insert(text_buf->end(), "\n"); - - if (!_clear_button->is_sensitive()) { - _clear_button->set_sensitive(true); - } - - set_urgency_hint(true); - if (!is_visible()) { - present(); - } -} - -int -MessagesWindow::log(LV2_URID type, const char* fmt, va_list args) -{ - std::lock_guard<std::mutex> lock(_mutex); - -#ifdef HAVE_VASPRINTF - char* buf = nullptr; - const int len = vasprintf(&buf, fmt, args); -#else - char* buf = g_strdup_vprintf(fmt, args); - const int len = strlen(buf); -#endif - - _stream << type << ' ' << buf << '\0'; - free(buf); - - return len; -} - -void -MessagesWindow::flush() -{ - while (true) { - LV2_URID type; - std::string line; - { - std::lock_guard<std::mutex> lock(_mutex); - if (!_stream.rdbuf()->in_avail()) { - return; - } - _stream >> type; - std::getline(_stream, line, '\0'); - } - - Glib::RefPtr<Gtk::TextBuffer> text_buf = _textview->get_buffer(); - - auto t = _tags.find(type); - if (t != _tags.end()) { - text_buf->insert_with_tag(text_buf->end(), line, t->second); - } else { - text_buf->insert(text_buf->end(), line); - } - } - - if (!_clear_button->is_sensitive()) { - _clear_button->set_sensitive(true); - } -} - -void -MessagesWindow::clear_clicked() -{ - Glib::RefPtr<Gtk::TextBuffer> text_buf = _textview->get_buffer(); - text_buf->erase(text_buf->begin(), text_buf->end()); - _clear_button->set_sensitive(false); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/MessagesWindow.hpp b/src/gui/MessagesWindow.hpp deleted file mode 100644 index fa9eae1d..00000000 --- a/src/gui/MessagesWindow.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_MESSAGESWINDOW_HPP -#define INGEN_GUI_MESSAGESWINDOW_HPP - -#include <mutex> -#include <sstream> -#include <string> - -#include <gtkmm/builder.h> -#include <gtkmm/button.h> -#include <gtkmm/textview.h> -#include "lv2/lv2plug.in/ns/ext/log/log.h" - -#include "Window.hpp" - -namespace Ingen { -namespace GUI { - -/** Messages Window. - * - * Loaded from XML as a derived object. - * This is shown when errors occur (e.g. during graph loading). - * - * \ingroup GUI - */ -class MessagesWindow : public Window -{ -public: - MessagesWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - void init_window(App& app); - - int log(LV2_URID type, const char* fmt, va_list args); - void flush(); - - void post_error(const std::string& msg); - -private: - void clear_clicked(); - - std::mutex _mutex; - std::stringstream _stream; - Gtk::TextView* _textview; - Gtk::Button* _clear_button; - Gtk::Button* _close_button; - - Glib::RefPtr<Gtk::TextTag> _error_tag; - std::map< LV2_URID, Glib::RefPtr<Gtk::TextTag> > _tags; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_MESSAGESWINDOW_HPP diff --git a/src/gui/NewSubgraphWindow.cpp b/src/gui/NewSubgraphWindow.cpp deleted file mode 100644 index f9dc8fc4..00000000 --- a/src/gui/NewSubgraphWindow.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <string> - -#include "ingen/Interface.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" - -#include "App.hpp" -#include "NewSubgraphWindow.hpp" -#include "GraphView.hpp" - -namespace Ingen { -namespace GUI { - -NewSubgraphWindow::NewSubgraphWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Window(cobject) -{ - xml->get_widget("new_subgraph_name_entry", _name_entry); - xml->get_widget("new_subgraph_message_label", _message_label); - xml->get_widget("new_subgraph_polyphony_spinbutton", _poly_spinbutton); - xml->get_widget("new_subgraph_ok_button", _ok_button); - xml->get_widget("new_subgraph_cancel_button", _cancel_button); - - _name_entry->signal_changed().connect(sigc::mem_fun(this, &NewSubgraphWindow::name_changed)); - _ok_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubgraphWindow::ok_clicked)); - _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubgraphWindow::cancel_clicked)); - - _ok_button->property_sensitive() = false; - - _poly_spinbutton->get_adjustment()->configure(1.0, 1.0, 128, 1.0, 10.0, 0); -} - -void -NewSubgraphWindow::present(SPtr<const Client::GraphModel> graph, - Properties data) -{ - set_graph(graph); - _initial_data = data; - Gtk::Window::present(); -} - -/** Sets the graph controller for this window and initializes everything. - * - * This function MUST be called before using the window in any way! - */ -void -NewSubgraphWindow::set_graph(SPtr<const Client::GraphModel> graph) -{ - _graph = graph; -} - -/** Called every time the user types into the name input box. - * Used to display warning messages, and enable/disable the OK button. - */ -void -NewSubgraphWindow::name_changed() -{ - std::string name = _name_entry->get_text(); - if (!Raul::Symbol::is_valid(name)) { - _message_label->set_text("Name contains invalid characters."); - _ok_button->property_sensitive() = false; - } else if (_app->store()->find(_graph->path().child(Raul::Symbol(name))) - != _app->store()->end()) { - _message_label->set_text("An object already exists with that name."); - _ok_button->property_sensitive() = false; - } else { - _message_label->set_text(""); - _ok_button->property_sensitive() = true; - } -} - -void -NewSubgraphWindow::ok_clicked() -{ - const uint32_t poly = _poly_spinbutton->get_value_as_int(); - const Raul::Path path = _graph->path().child( - Raul::Symbol::symbolify(_name_entry->get_text())); - - // Create graph - Properties props; - props.emplace(_app->uris().rdf_type, Property(_app->uris().ingen_Graph)); - props.emplace(_app->uris().ingen_polyphony, _app->forge().make(int32_t(poly))); - props.emplace(_app->uris().ingen_enabled, _app->forge().make(bool(true))); - _app->interface()->put( - path_to_uri(path), props, Resource::Graph::INTERNAL); - - // Set external (block perspective) properties - props = _initial_data; - props.emplace(_app->uris().rdf_type, Property(_app->uris().ingen_Graph)); - _app->interface()->put( - path_to_uri(path), _initial_data, Resource::Graph::EXTERNAL); - - hide(); -} - -void -NewSubgraphWindow::cancel_clicked() -{ - hide(); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/NewSubgraphWindow.hpp b/src/gui/NewSubgraphWindow.hpp deleted file mode 100644 index 395856ba..00000000 --- a/src/gui/NewSubgraphWindow.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_NEWSUBGRAPHWINDOW_HPP -#define INGEN_GUI_NEWSUBGRAPHWINDOW_HPP - -#include <gtkmm/builder.h> -#include <gtkmm/button.h> -#include <gtkmm/entry.h> -#include <gtkmm/label.h> -#include <gtkmm/spinbutton.h> - -#include "ingen/Node.hpp" -#include "ingen/types.hpp" - -#include "Window.hpp" - -namespace Ingen { - -namespace Client { class GraphModel; } - -namespace GUI { - -/** 'New Subgraph' window. - * - * Loaded from XML as a derived object. - * - * \ingroup GUI - */ -class NewSubgraphWindow : public Window -{ -public: - NewSubgraphWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - void set_graph(SPtr<const Client::GraphModel> graph); - - void present(SPtr<const Client::GraphModel> graph, - Properties data); - -private: - void name_changed(); - void ok_clicked(); - void cancel_clicked(); - - Properties _initial_data; - SPtr<const Client::GraphModel> _graph; - - Gtk::Entry* _name_entry; - Gtk::Label* _message_label; - Gtk::SpinButton* _poly_spinbutton; - Gtk::Button* _ok_button; - Gtk::Button* _cancel_button; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_NEWSUBGRAPHWINDOW_HPP diff --git a/src/gui/NodeMenu.cpp b/src/gui/NodeMenu.cpp deleted file mode 100644 index 1b1b1677..00000000 --- a/src/gui/NodeMenu.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <string> - -#include <gtkmm/entry.h> -#include <gtkmm/filechooserdialog.h> -#include <gtkmm/image.h> -#include <gtkmm/stock.h> - -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/client/BlockModel.hpp" -#include "ingen/client/PluginModel.hpp" -#include "lv2/lv2plug.in/ns/ext/presets/presets.h" - -#include "App.hpp" -#include "NodeMenu.hpp" -#include "WidgetFactory.hpp" -#include "WindowFactory.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -NodeMenu::NodeMenu(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : ObjectMenu(cobject, xml) - , _presets_menu(nullptr) -{ - xml->get_widget("node_popup_gui_menuitem", _popup_gui_menuitem); - xml->get_widget("node_embed_gui_menuitem", _embed_gui_menuitem); - xml->get_widget("node_enabled_menuitem", _enabled_menuitem); - xml->get_widget("node_randomize_menuitem", _randomize_menuitem); -} - -void -NodeMenu::init(App& app, SPtr<const Client::BlockModel> block) -{ - ObjectMenu::init(app, block); - - _learn_menuitem->signal_activate().connect( - sigc::mem_fun(this, &NodeMenu::on_menu_learn)); - _popup_gui_menuitem->signal_activate().connect( - sigc::mem_fun(signal_popup_gui, &sigc::signal<void>::emit)); - _embed_gui_menuitem->signal_toggled().connect( - sigc::mem_fun(this, &NodeMenu::on_menu_embed_gui)); - _enabled_menuitem->signal_toggled().connect( - sigc::mem_fun(this, &NodeMenu::on_menu_enabled)); - _randomize_menuitem->signal_activate().connect( - sigc::mem_fun(this, &NodeMenu::on_menu_randomize)); - - SPtr<PluginModel> plugin = block->plugin_model(); - if (plugin) { - // Get the plugin to receive related presets - _preset_connection = plugin->signal_preset().connect( - sigc::mem_fun(this, &NodeMenu::add_preset)); - - if (!plugin->fetched()) { - _app->interface()->get(plugin->uri()); - plugin->set_fetched(true); - } - } - - if (plugin && plugin->has_ui()) { - _popup_gui_menuitem->show(); - _embed_gui_menuitem->show(); - const Atom& ui_embedded = block->get_property( - _app->uris().ingen_uiEmbedded); - _embed_gui_menuitem->set_active( - ui_embedded.is_valid() && ui_embedded.get<int32_t>()); - } else { - _popup_gui_menuitem->hide(); - _embed_gui_menuitem->hide(); - } - - const Atom& enabled = block->get_property(_app->uris().ingen_enabled); - _enabled_menuitem->set_active(!enabled.is_valid() || enabled.get<int32_t>()); - - if (plugin && _app->uris().lv2_Plugin == plugin->type()) { - _presets_menu = Gtk::manage(new Gtk::Menu()); - _presets_menu->items().push_back( - Gtk::Menu_Helpers::MenuElem( - "_Save Preset...", - sigc::mem_fun(this, &NodeMenu::on_save_preset_activated))); - _presets_menu->items().push_back(Gtk::Menu_Helpers::SeparatorElem()); - - for (const auto& p : plugin->presets()) { - add_preset(p.first, p.second); - } - - items().push_front( - Gtk::Menu_Helpers::ImageMenuElem( - "_Presets", - *(manage(new Gtk::Image(Gtk::Stock::INDEX, Gtk::ICON_SIZE_MENU))))); - - Gtk::MenuItem* presets_menu_item = &(items().front()); - presets_menu_item->set_submenu(*_presets_menu); - } - - if (has_control_inputs()) { - _randomize_menuitem->show(); - } else { - _randomize_menuitem->hide(); - } - - if (plugin && (plugin->uri() == "http://drobilla.net/ns/ingen-internals#Controller" - || plugin->uri() == "http://drobilla.net/ns/ingen-internals#Trigger")) { - _learn_menuitem->show(); - } else { - _learn_menuitem->hide(); - } - - if (!_popup_gui_menuitem->is_visible() && - !_embed_gui_menuitem->is_visible() && - !_randomize_menuitem->is_visible()) { - _separator_menuitem->hide(); - } - - _enable_signal = true; -} - -void -NodeMenu::add_preset(const URI& uri, const std::string& label) -{ - if (_presets_menu) { - _presets_menu->items().push_back( - Gtk::Menu_Helpers::MenuElem( - label, - sigc::bind(sigc::mem_fun(this, &NodeMenu::on_preset_activated), - uri))); - } -} - -void -NodeMenu::on_menu_embed_gui() -{ - signal_embed_gui.emit(_embed_gui_menuitem->get_active()); -} - -void -NodeMenu::on_menu_enabled() -{ - _app->set_property(_object->uri(), - _app->uris().ingen_enabled, - _app->forge().make(bool(_enabled_menuitem->get_active()))); -} - -void -NodeMenu::on_menu_randomize() -{ - _app->interface()->bundle_begin(); - - 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; - bm->port_value_range(p, min, max, _app->sample_rate()); - const float val = g_random_double_range(0.0, 1.0) * (max - min) + min; - _app->set_property(p->uri(), - _app->uris().ingen_value, - _app->forge().make(val)); - } - } - - _app->interface()->bundle_end(); -} - -void -NodeMenu::on_menu_disconnect() -{ - _app->interface()->disconnect_all(_object->parent()->path(), _object->path()); -} - -void -NodeMenu::on_save_preset_activated() -{ - Gtk::FileChooserDialog dialog("Save Preset", Gtk::FILE_CHOOSER_ACTION_SAVE); - dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); - dialog.set_default_response(Gtk::RESPONSE_OK); - dialog.set_current_folder(Glib::build_filename(Glib::get_home_dir(), ".lv2")); - - Gtk::HBox* extra = Gtk::manage(new Gtk::HBox()); - Gtk::Label* label = Gtk::manage(new Gtk::Label("URI (Optional): ")); - Gtk::Entry* entry = Gtk::manage(new Gtk::Entry()); - extra->pack_start(*label, false, true, 4); - extra->pack_start(*entry, true, true, 4); - extra->show_all(); - dialog.set_extra_widget(*Gtk::manage(extra)); - - if (dialog.run() == Gtk::RESPONSE_OK) { - const std::string user_uri = dialog.get_uri(); - const std::string user_path = Glib::filename_from_uri(user_uri); - const std::string dirname = Glib::path_get_dirname(user_path); - const std::string basename = Glib::path_get_basename(user_path); - const std::string sym = Raul::Symbol::symbolify(basename); - const std::string plugname = block()->plugin_model()->human_name(); - const std::string prefix = Raul::Symbol::symbolify(plugname); - const std::string bundle = prefix + "_" + sym + ".preset.lv2/"; - const std::string file = sym + ".ttl"; - const std::string real_path = Glib::build_filename(dirname, bundle, file); - const std::string real_uri = Glib::filename_to_uri(real_path); - - Properties props{ - { _app->uris().rdf_type, - _app->uris().pset_Preset }, - { _app->uris().rdfs_label, - _app->forge().alloc(basename) }, - { _app->uris().lv2_prototype, - _app->forge().make_urid(block()->uri()) }}; - _app->interface()->put(URI(real_uri), props); - } -} - -void -NodeMenu::on_preset_activated(const std::string& uri) -{ - _app->set_property(block()->uri(), - _app->uris().pset_preset, - _app->forge().make_urid(URI(uri))); -} - -bool -NodeMenu::has_control_inputs() -{ - for (const auto& p : block()->ports()) { - if (p->is_input() && p->is_numeric()) { - return true; - } - } - - return false; -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/NodeMenu.hpp b/src/gui/NodeMenu.hpp deleted file mode 100644 index 5d9f1e6d..00000000 --- a/src/gui/NodeMenu.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_NODEMENU_HPP -#define INGEN_GUI_NODEMENU_HPP - -#include <string> - -#include <gtkmm/builder.h> -#include <gtkmm/menu.h> -#include <gtkmm/menushell.h> - -#include "ObjectMenu.hpp" -#include "ingen/client/BlockModel.hpp" -#include "ingen/types.hpp" - -namespace Ingen { -namespace GUI { - -/** Menu for a Node. - * - * \ingroup GUI - */ -class NodeMenu : public ObjectMenu -{ -public: - NodeMenu(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - void init(App& app, SPtr<const Client::BlockModel> block); - - bool has_control_inputs(); - - sigc::signal<void> signal_popup_gui; - sigc::signal<void, bool> signal_embed_gui; - -protected: - SPtr<const Client::BlockModel> block() const { - return dynamic_ptr_cast<const Client::BlockModel>(_object); - } - - void add_preset(const URI& uri, const std::string& label); - - void on_menu_disconnect(); - void on_menu_embed_gui(); - void on_menu_enabled(); - void on_menu_randomize(); - void on_save_preset_activated(); - void on_preset_activated(const std::string& uri); - - Gtk::MenuItem* _popup_gui_menuitem; - Gtk::CheckMenuItem* _embed_gui_menuitem; - Gtk::CheckMenuItem* _enabled_menuitem; - Gtk::MenuItem* _randomize_menuitem; - Gtk::Menu* _presets_menu; - sigc::connection _preset_connection; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_NODEMENU_HPP diff --git a/src/gui/NodeModule.cpp b/src/gui/NodeModule.cpp deleted file mode 100644 index dadffff0..00000000 --- a/src/gui/NodeModule.cpp +++ /dev/null @@ -1,518 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <string> - -#include <gtkmm/eventbox.h> - -#include "lv2/lv2plug.in/ns/ext/atom/util.h" - -#include "ingen/Atom.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/client/BlockModel.hpp" -#include "ingen/client/GraphModel.hpp" -#include "ingen/client/PluginModel.hpp" -#include "ingen/client/PluginUI.hpp" - -#include "App.hpp" -#include "GraphCanvas.hpp" -#include "GraphWindow.hpp" -#include "NodeMenu.hpp" -#include "NodeModule.hpp" -#include "Port.hpp" -#include "RenameWindow.hpp" -#include "Style.hpp" -#include "SubgraphModule.hpp" -#include "WidgetFactory.hpp" -#include "WindowFactory.hpp" -#include "ingen_config.h" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -NodeModule::NodeModule(GraphCanvas& canvas, - SPtr<const BlockModel> block) - : Ganv::Module(canvas, block->path().symbol(), 0, 0, true) - , _block(block) - , _gui_widget(nullptr) - , _gui_window(nullptr) - , _initialised(false) -{ - block->signal_new_port().connect( - sigc::mem_fun(this, &NodeModule::new_port_view)); - block->signal_removed_port().connect( - sigc::hide_return(sigc::mem_fun(this, &NodeModule::delete_port_view))); - block->signal_property().connect( - sigc::mem_fun(this, &NodeModule::property_changed)); - block->signal_moved().connect( - sigc::mem_fun(this, &NodeModule::rename)); - - signal_event().connect( - sigc::mem_fun(this, &NodeModule::on_event)); - - signal_moved().connect( - sigc::mem_fun(this, &NodeModule::store_location)); - - signal_selected().connect( - sigc::mem_fun(this, &NodeModule::on_selected)); - - const PluginModel* plugin = dynamic_cast<const PluginModel*>(block->plugin()); - if (plugin) { - plugin->signal_changed().connect( - sigc::mem_fun(this, &NodeModule::plugin_changed)); - } - - for (const auto& p : block->properties()) { - property_changed(p.first, p.second); - } - - if (_block->has_property(app().uris().ingen_uiEmbedded, - app().uris().forge.make(true))) { - // Schedule idle callback to embed GUI once ports arrive - Glib::signal_timeout().connect( - sigc::mem_fun(*this, &NodeModule::idle_init), 25, G_PRIORITY_DEFAULT_IDLE); - } else { - _initialised = true; - } -} - -NodeModule::~NodeModule() -{ - delete _gui_widget; - delete _gui_window; -} - -bool -NodeModule::idle_init() -{ - if (_block->ports().size() == 0) { - return true; // Need to embed GUI, but ports haven't shown up yet - } - - // Ports have arrived, embed GUI and deregister this callback - embed_gui(true); - _initialised = true; - return false; -} - -bool -NodeModule::show_menu(GdkEventButton* ev) -{ - WidgetFactory::get_widget_derived("object_menu", _menu); - if (!_menu) { - app().log().error("Failed to load object menu widget\n"); - return false; - } - - _menu->init(app(), _block); - _menu->signal_embed_gui.connect( - sigc::mem_fun(this, &NodeModule::on_embed_gui_toggled)); - _menu->signal_popup_gui.connect( - sigc::hide_return(sigc::mem_fun(this, &NodeModule::popup_gui))); - _menu->popup(ev->button, ev->time); - return true; -} - -NodeModule* -NodeModule::create(GraphCanvas& canvas, - SPtr<const BlockModel> block, - bool human) -{ - SPtr<const GraphModel> graph = dynamic_ptr_cast<const GraphModel>(block); - - NodeModule* ret = (graph) - ? new SubgraphModule(canvas, graph) - : new NodeModule(canvas, block); - - for (const auto& p : block->properties()) { - ret->property_changed(p.first, p.second); - } - - for (const auto& p : block->ports()) { - ret->new_port_view(p); - } - - ret->set_stacked(block->polyphonic()); - - if (human) { - ret->show_human_names(human); // FIXME: double port iteration - } - - return ret; -} - -App& -NodeModule::app() const -{ - return ((GraphCanvas*)canvas())->app(); -} - -void -NodeModule::show_human_names(bool b) -{ - const URIs& uris = app().uris(); - - if (b) { - set_label(block()->label().c_str()); - } else { - set_label(block()->symbol().c_str()); - } - - for (iterator i = begin(); i != end(); ++i) { - Ingen::GUI::Port* const port = dynamic_cast<Ingen::GUI::Port*>(*i); - Glib::ustring label(port->model()->symbol().c_str()); - if (b) { - const Atom& name_property = port->model()->get_property(uris.lv2_name); - if (name_property.type() == uris.forge.String) { - label = name_property.ptr<char>(); - } else { - Glib::ustring hn = block()->plugin_model()->port_human_name( - port->model()->index()); - if (!hn.empty()) { - label = hn; - } - } - } - (*i)->set_label(label.c_str()); - } -} - -void -NodeModule::port_activity(uint32_t index, const Atom& value) -{ - const URIs& uris = app().uris(); - if (!_plugin_ui) { - return; - } - - if (_block->get_port(index)->is_a(uris.atom_AtomPort)) { - _plugin_ui->port_event(index, - lv2_atom_total_size(value.atom()), - uris.atom_eventTransfer, - value.atom()); - } -} - -void -NodeModule::port_value_changed(uint32_t index, const Atom& value) -{ - const URIs& uris = app().uris(); - if (!_plugin_ui) { - return; - } - - if (value.type() == uris.atom_Float && - _block->get_port(index)->is_numeric()) { - _plugin_ui->port_event(index, sizeof(float), 0, value.ptr<float>()); - } else { - _plugin_ui->port_event(index, - lv2_atom_total_size(value.atom()), - uris.atom_eventTransfer, - value.atom()); - } -} - -void -NodeModule::plugin_changed() -{ - for (iterator p = begin(); p != end(); ++p) { - dynamic_cast<Ingen::GUI::Port*>(*p)->update_metadata(); - } -} - -void -NodeModule::on_embed_gui_toggled(bool embed) -{ - embed_gui(embed); - app().set_property(_block->uri(), - app().uris().ingen_uiEmbedded, - app().forge().make(embed)); -} - -void -NodeModule::embed_gui(bool embed) -{ - if (embed) { - if (_gui_window) { - app().log().warn("LV2 GUI already popped up, cannot embed\n"); - return; - } - - if (!_plugin_ui) { - _plugin_ui = _block->plugin_model()->ui(app().world(), _block); - } - - if (_plugin_ui) { - _plugin_ui->signal_property_changed().connect( - sigc::mem_fun(app(), &App::set_property)); - - if (!_plugin_ui->instantiate()) { - app().log().error("Failed to instantiate LV2 UI\n"); - } else { - GtkWidget* c_widget = (GtkWidget*)_plugin_ui->get_widget(); - _gui_widget = Glib::wrap(c_widget); - - Gtk::Container* container = new Gtk::EventBox(); - container->set_name("IngenEmbeddedUI"); - container->set_border_width(4.0); - container->add(*_gui_widget); - Ganv::Module::embed(container); - } - } else { - app().log().error("Failed to create LV2 UI\n"); - } - - if (_gui_widget) { - _gui_widget->show_all(); - set_control_values(); - } - - } else { // un-embed - Ganv::Module::embed(nullptr); - _plugin_ui.reset(); - } -} - -void -NodeModule::rename() -{ - if (app().world()->conf().option("port-labels").get<int32_t>() && - !app().world()->conf().option("human-names").get<int32_t>()) { - set_label(_block->path().symbol()); - } -} - -void -NodeModule::new_port_view(SPtr<const PortModel> port) -{ - Port::create(app(), *this, port); - - port->signal_value_changed().connect( - sigc::bind<0>(sigc::mem_fun(this, &NodeModule::port_value_changed), - port->index())); - - port->signal_activity().connect( - sigc::bind<0>(sigc::mem_fun(this, &NodeModule::port_activity), - port->index())); -} - -Port* -NodeModule::port(SPtr<const PortModel> model) -{ - for (iterator p = begin(); p != end(); ++p) { - Port* const port = dynamic_cast<Port*>(*p); - if (port->model() == model) { - return port; - } - } - return nullptr; -} - -void -NodeModule::delete_port_view(SPtr<const PortModel> model) -{ - Port* p = port(model); - if (p) { - delete p; - } else { - app().log().warn(fmt("Failed to find port %1% on module %2%\n") - % model->path() % _block->path()); - } -} - -bool -NodeModule::popup_gui() -{ - if (_block->plugin() && app().uris().lv2_Plugin == _block->plugin_model()->type()) { - if (_plugin_ui) { - app().log().warn("LV2 GUI already embedded, cannot pop up\n"); - return false; - } - - const PluginModel* const plugin = dynamic_cast<const PluginModel*>(_block->plugin()); - assert(plugin); - - _plugin_ui = plugin->ui(app().world(), _block); - - if (_plugin_ui) { - _plugin_ui->signal_property_changed().connect( - sigc::mem_fun(app(), &App::set_property)); - - if (!_plugin_ui->instantiated() && !_plugin_ui->instantiate()) { - app().log().error("Failed to instantiate LV2 UI\n"); - return false; - } - - GtkWidget* c_widget = (GtkWidget*)_plugin_ui->get_widget(); - _gui_widget = Glib::wrap(c_widget); - - _gui_window = new Gtk::Window(); - if (!_plugin_ui->is_resizable()) { - _gui_window->set_resizable(false); - } - _gui_window->set_title(_block->path() + " UI - Ingen"); - _gui_window->set_role("plugin_ui"); - _gui_window->add(*_gui_widget); - _gui_widget->show_all(); - set_control_values(); - - _gui_window->signal_unmap().connect( - sigc::mem_fun(this, &NodeModule::on_gui_window_close)); - _gui_window->present(); - - return true; - } else { - app().log().warn(fmt("No LV2 GUI for %1%\n") % _block->path()); - } - } - - return false; -} - -void -NodeModule::on_gui_window_close() -{ - delete _gui_window; - _gui_window = nullptr; - _plugin_ui.reset(); - _gui_widget = nullptr; -} - -void -NodeModule::set_control_values() -{ - uint32_t index = 0; - for (const auto& p : _block->ports()) { - if (app().can_control(p.get()) && p->value().is_valid()) { - port_value_changed(index, p->value()); - } - ++index; - } -} - -bool -NodeModule::on_double_click(GdkEventButton* event) -{ - popup_gui(); - return true; -} - -bool -NodeModule::on_event(GdkEvent* ev) -{ - if (ev->type == GDK_BUTTON_PRESS && ev->button.button == 3) { - return show_menu(&ev->button); - } else if (ev->type == GDK_2BUTTON_PRESS) { - return on_double_click(&ev->button); - } else if (ev->type == GDK_ENTER_NOTIFY) { - GraphBox* const box = app().window_factory()->graph_box( - dynamic_ptr_cast<const GraphModel>(_block->parent())); - if (box) { - box->object_entered(_block.get()); - } - } else if (ev->type == GDK_LEAVE_NOTIFY) { - GraphBox* const box = app().window_factory()->graph_box( - dynamic_ptr_cast<const GraphModel>(_block->parent())); - if (box) { - box->object_left(_block.get()); - } - } - - return false; -} - -void -NodeModule::store_location(double ax, double ay) -{ - const URIs& uris = app().uris(); - - const Atom x(app().forge().make(static_cast<float>(ax))); - const Atom y(app().forge().make(static_cast<float>(ay))); - - if (x != _block->get_property(uris.ingen_canvasX) || - y != _block->get_property(uris.ingen_canvasY)) - { - app().interface()->put(_block->uri(), {{uris.ingen_canvasX, x}, - {uris.ingen_canvasY, y}}); - } -} - -void -NodeModule::property_changed(const URI& key, const Atom& value) -{ - const URIs& uris = app().uris(); - if (value.type() == uris.forge.Float) { - if (key == uris.ingen_canvasX) { - move_to(value.get<float>(), get_y()); - } else if (key == uris.ingen_canvasY) { - move_to(get_x(), value.get<float>()); - } - } else if (value.type() == uris.forge.Bool) { - if (key == uris.ingen_polyphonic) { - set_stacked(value.get<int32_t>()); - } else if (key == uris.ingen_uiEmbedded && _initialised) { - if (value.get<int32_t>() && !_gui_widget) { - embed_gui(true); - } else if (!value.get<int32_t>() && _gui_widget) { - embed_gui(false); - } - } else if (key == uris.ingen_enabled) { - if (value.get<int32_t>()) { - set_dash_length(0.0); - } else { - set_dash_length(5.0); - } - } - } else if (value.type() == uris.forge.String) { - if (key == uris.lv2_name - && app().world()->conf().option("human-names").get<int32_t>()) { - set_label(value.ptr<char>()); - } - } -} - -bool -NodeModule::on_selected(gboolean selected) -{ - GraphWindow* win = app().window_factory()->parent_graph_window(block()); - if (!win) { - return true; - } - - if (selected && win->documentation_is_visible()) { - GraphWindow* win = app().window_factory()->parent_graph_window(block()); - std::string doc; - bool html = false; -#ifdef HAVE_WEBKIT - html = true; -#endif - if (block()->plugin_model()) { - doc = block()->plugin_model()->documentation(html); - } - win->set_documentation(doc, html); - } - - return true; -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/NodeModule.hpp b/src/gui/NodeModule.hpp deleted file mode 100644 index 863b6ffb..00000000 --- a/src/gui/NodeModule.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_NODEMODULE_HPP -#define INGEN_GUI_NODEMODULE_HPP - -#include "ganv/Module.hpp" -#include "ingen/types.hpp" - -#include "Port.hpp" - -namespace Raul { class Atom; } - -namespace Ingen { namespace Client { -class BlockModel; -class PluginUI; -class PortModel; -} } - -namespace Ingen { -namespace GUI { - -class GraphCanvas; -class Port; -class NodeMenu; - -/** A module in a graphn. - * - * This base class is extended for various types of modules. - * - * \ingroup GUI - */ -class NodeModule : public Ganv::Module -{ -public: - static NodeModule* create( - GraphCanvas& canvas, - SPtr<const Client::BlockModel> block, - bool human); - - virtual ~NodeModule(); - - App& app() const; - - Port* port(SPtr<const Client::PortModel> model); - - void delete_port_view(SPtr<const Client::PortModel> model); - - virtual void store_location(double ax, double ay); - void show_human_names(bool b); - - SPtr<const Client::BlockModel> block() const { return _block; } - -protected: - NodeModule(GraphCanvas& canvas, SPtr<const Client::BlockModel> block); - - virtual bool on_double_click(GdkEventButton* ev); - - bool idle_init(); - bool on_event(GdkEvent* ev); - - void on_embed_gui_toggled(bool embed); - void embed_gui(bool embed); - bool popup_gui(); - void on_gui_window_close(); - bool on_selected(gboolean selected); - - void rename(); - void property_changed(const URI& key, const Atom& value); - - void new_port_view(SPtr<const Client::PortModel> port); - - void port_activity(uint32_t index, const Atom& value); - void port_value_changed(uint32_t index, const Atom& value); - void plugin_changed(); - void set_control_values(); - - bool show_menu(GdkEventButton* ev); - - SPtr<const Client::BlockModel> _block; - NodeMenu* _menu; - SPtr<Client::PluginUI> _plugin_ui; - Gtk::Widget* _gui_widget; - Gtk::Window* _gui_window; ///< iff popped up - bool _initialised; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_NODEMODULE_HPP diff --git a/src/gui/ObjectMenu.cpp b/src/gui/ObjectMenu.cpp deleted file mode 100644 index bfce4248..00000000 --- a/src/gui/ObjectMenu.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <utility> - -#include "ingen/Forge.hpp" -#include "ingen/Interface.hpp" -#include "ingen/client/ObjectModel.hpp" - -#include "App.hpp" -#include "ObjectMenu.hpp" -#include "WidgetFactory.hpp" -#include "WindowFactory.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -ObjectMenu::ObjectMenu(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Gtk::Menu(cobject) - , _app(nullptr) - , _polyphonic_menuitem(nullptr) - , _disconnect_menuitem(nullptr) - , _rename_menuitem(nullptr) - , _destroy_menuitem(nullptr) - , _properties_menuitem(nullptr) - , _enable_signal(false) -{ - xml->get_widget("object_learn_menuitem", _learn_menuitem); - xml->get_widget("object_unlearn_menuitem", _unlearn_menuitem); - xml->get_widget("object_polyphonic_menuitem", _polyphonic_menuitem); - xml->get_widget("object_disconnect_menuitem", _disconnect_menuitem); - xml->get_widget("object_rename_menuitem", _rename_menuitem); - xml->get_widget("object_destroy_menuitem", _destroy_menuitem); - xml->get_widget("object_properties_menuitem", _properties_menuitem); - xml->get_widget("object_menu_separator", _separator_menuitem); -} - -void -ObjectMenu::init(App& app, SPtr<const ObjectModel> object) -{ - _app = &app; - _object = object; - - _polyphonic_menuitem->signal_toggled().connect( - sigc::mem_fun(this, &ObjectMenu::on_menu_polyphonic)); - - _polyphonic_menuitem->set_active(object->polyphonic()); - - _learn_menuitem->signal_activate().connect( - sigc::mem_fun(this, &ObjectMenu::on_menu_learn)); - - _unlearn_menuitem->signal_activate().connect( - sigc::mem_fun(this, &ObjectMenu::on_menu_unlearn)); - - _disconnect_menuitem->signal_activate().connect( - sigc::mem_fun(this, &ObjectMenu::on_menu_disconnect)); - - _rename_menuitem->signal_activate().connect( - sigc::bind(sigc::mem_fun(_app->window_factory(), &WindowFactory::present_rename), - object)); - - _destroy_menuitem->signal_activate().connect( - sigc::mem_fun(this, &ObjectMenu::on_menu_destroy)); - - _properties_menuitem->signal_activate().connect( - sigc::mem_fun(this, &ObjectMenu::on_menu_properties)); - - object->signal_property().connect(sigc::mem_fun(this, &ObjectMenu::property_changed)); - - _learn_menuitem->hide(); - _unlearn_menuitem->hide(); - - _enable_signal = true; -} - -void -ObjectMenu::on_menu_learn() -{ - _app->interface()->set_property(_object->uri(), - _app->uris().midi_binding, - _app->uris().patch_wildcard.urid); -} - -void -ObjectMenu::on_menu_unlearn() -{ - Properties remove; - remove.emplace(_app->uris().midi_binding, - Property(_app->uris().patch_wildcard)); - _app->interface()->delta(_object->uri(), remove, Properties()); -} - -void -ObjectMenu::on_menu_polyphonic() -{ - if (_enable_signal) { - _app->set_property( - _object->uri(), - _app->uris().ingen_polyphonic, - _app->forge().make(bool(_polyphonic_menuitem->get_active()))); - } -} - -void -ObjectMenu::property_changed(const URI& predicate, const Atom& value) -{ - const URIs& uris = _app->uris(); - _enable_signal = false; - if (predicate == uris.ingen_polyphonic && value.type() == uris.forge.Bool) { - _polyphonic_menuitem->set_active(value.get<int32_t>()); - } - _enable_signal = true; -} - -void -ObjectMenu::on_menu_destroy() -{ - _app->interface()->del(_object->uri()); -} - -void -ObjectMenu::on_menu_properties() -{ - _app->window_factory()->present_properties(_object); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/ObjectMenu.hpp b/src/gui/ObjectMenu.hpp deleted file mode 100644 index a9b07fd5..00000000 --- a/src/gui/ObjectMenu.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_OBJECTMENU_HPP -#define INGEN_GUI_OBJECTMENU_HPP - -#include <gtkmm/builder.h> -#include <gtkmm/checkmenuitem.h> -#include <gtkmm/menu.h> -#include <gtkmm/menuitem.h> - -#include "ingen/client/ObjectModel.hpp" -#include "ingen/types.hpp" - -namespace Ingen { -namespace GUI { - -class ObjectControlWindow; -class ObjectPropertiesWindow; -class GraphCanvas; - -/** Menu for a Object. - * - * \ingroup GUI - */ -class ObjectMenu : public Gtk::Menu -{ -public: - ObjectMenu(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - void init(App& app, SPtr<const Client::ObjectModel> object); - - SPtr<const Client::ObjectModel> object() const { return _object; } - App* app() const { return _app; } - -protected: - void on_menu_learn(); - void on_menu_unlearn(); - virtual void on_menu_disconnect() = 0; - void on_menu_polyphonic(); - void on_menu_destroy(); - void on_menu_properties(); - - void property_changed(const URI& predicate, const Atom& value); - - App* _app; - SPtr<const Client::ObjectModel> _object; - Gtk::MenuItem* _learn_menuitem; - Gtk::MenuItem* _unlearn_menuitem; - Gtk::CheckMenuItem* _polyphonic_menuitem; - Gtk::MenuItem* _disconnect_menuitem; - Gtk::MenuItem* _rename_menuitem; - Gtk::MenuItem* _destroy_menuitem; - Gtk::MenuItem* _properties_menuitem; - Gtk::SeparatorMenuItem* _separator_menuitem; - - bool _enable_signal; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_OBJECTMENU_HPP diff --git a/src/gui/PluginMenu.cpp b/src/gui/PluginMenu.cpp deleted file mode 100644 index fc385773..00000000 --- a/src/gui/PluginMenu.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2014-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "PluginMenu.hpp" -#include "ingen/Log.hpp" -#include "ingen/client/PluginModel.hpp" - -namespace Ingen { -namespace GUI { - -PluginMenu::PluginMenu(Ingen::World& world) - : _world(world) - , _classless_menu(nullptr, nullptr) -{ - clear(); -} - -void -PluginMenu::clear() -{ - const LilvWorld* lworld = _world.lilv_world(); - const LilvPluginClass* lv2_plugin = lilv_world_get_plugin_class(lworld); - const LilvPluginClasses* classes = lilv_world_get_plugin_classes(lworld); - - // Empty completely - _classless_menu = MenuRecord(nullptr, nullptr); - _class_menus.clear(); - items().clear(); - - // Build skeleton - LV2Children children; - LILV_FOREACH(plugin_classes, i, classes) { - const LilvPluginClass* c = lilv_plugin_classes_get(classes, i); - const LilvNode* p = lilv_plugin_class_get_parent_uri(c); - if (!p) { - p = lilv_plugin_class_get_uri(lv2_plugin); - } - children.emplace(lilv_node_as_string(p), c); - } - - std::set<const char*> ancestors; - build_plugin_class_menu(this, lv2_plugin, classes, children, ancestors); - - items().push_back(Gtk::Menu_Helpers::MenuElem("_Uncategorized")); - _classless_menu.item = &(items().back()); - _classless_menu.menu = Gtk::manage(new Gtk::Menu()); - _classless_menu.item->set_submenu(*_classless_menu.menu); - _classless_menu.item->hide(); -} - -void -PluginMenu::add_plugin(SPtr<Client::PluginModel> p) -{ - typedef ClassMenus::iterator iterator; - - if (!p->lilv_plugin() || lilv_plugin_is_replaced(p->lilv_plugin())) { - return; - } - - const LilvPluginClass* pc = lilv_plugin_get_class(p->lilv_plugin()); - const LilvNode* class_uri = lilv_plugin_class_get_uri(pc); - const char* class_uri_str = lilv_node_as_string(class_uri); - - std::pair<iterator, iterator> range = _class_menus.equal_range(class_uri_str); - if (range.first == _class_menus.end() || range.first == range.second - || range.first->second.menu == this) { - // Add to uncategorized plugin menu - add_plugin_to_menu(_classless_menu, p); - } else { - // For each menu that represents plugin's class (possibly several) - for (auto i = range.first; i != range.second ; ++i) { - add_plugin_to_menu(i->second, p); - } - } -} - -size_t -PluginMenu::build_plugin_class_menu(Gtk::Menu* menu, - const LilvPluginClass* plugin_class, - const LilvPluginClasses* classes, - const LV2Children& children, - std::set<const char*>& ancestors) -{ - size_t num_items = 0; - const LilvNode* class_uri = lilv_plugin_class_get_uri(plugin_class); - const char* class_uri_str = lilv_node_as_string(class_uri); - - const std::pair<LV2Children::const_iterator, LV2Children::const_iterator> kids - = children.equal_range(class_uri_str); - - if (kids.first == children.end()) { - return 0; - } - - // Add submenus - ancestors.insert(class_uri_str); - for (LV2Children::const_iterator i = kids.first; i != kids.second; ++i) { - const LilvPluginClass* c = i->second; - const char* sub_label_str = lilv_node_as_string(lilv_plugin_class_get_label(c)); - const char* sub_uri_str = lilv_node_as_string(lilv_plugin_class_get_uri(c)); - if (ancestors.find(sub_uri_str) != ancestors.end()) { - _world.log().warn(fmt("Infinite LV2 class recursion: %1% <: %2%\n") - % class_uri_str % sub_uri_str); - return 0; - } - - Gtk::Menu_Helpers::MenuElem menu_elem = Gtk::Menu_Helpers::MenuElem( - std::string("_") + sub_label_str); - menu->items().push_back(menu_elem); - Gtk::MenuItem* menu_item = &(menu->items().back()); - - Gtk::Menu* submenu = Gtk::manage(new Gtk::Menu()); - menu_item->set_submenu(*submenu); - - size_t num_child_items = build_plugin_class_menu( - submenu, c, classes, children, ancestors); - - _class_menus.emplace(sub_uri_str, MenuRecord(menu_item, submenu)); - if (num_child_items == 0) { - menu_item->hide(); - } - - ++num_items; - } - ancestors.erase(class_uri_str); - - return num_items; -} - -void -PluginMenu::add_plugin_to_menu(MenuRecord& menu, SPtr<Client::PluginModel> p) -{ - const URIs& uris = _world.uris(); - LilvWorld* lworld = _world.lilv_world(); - LilvNode* ingen_Graph = lilv_new_uri(lworld, uris.ingen_Graph.c_str()); - LilvNode* rdf_type = lilv_new_uri(lworld, uris.rdf_type.c_str()); - - bool is_graph = lilv_world_ask(lworld, - lilv_plugin_get_uri(p->lilv_plugin()), - rdf_type, - ingen_Graph); - - menu.menu->items().push_back( - Gtk::Menu_Helpers::MenuElem( - std::string("_") + p->human_name() + (is_graph ? " ⚙" : ""), - sigc::bind(sigc::mem_fun(this, &PluginMenu::load_plugin), p))); - - if (!menu.item->is_visible()) { - menu.item->show(); - } - - lilv_node_free(rdf_type); - lilv_node_free(ingen_Graph); -} - -void -PluginMenu::load_plugin(WPtr<Client::PluginModel> weak_plugin) -{ - signal_load_plugin.emit(weak_plugin); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/PluginMenu.hpp b/src/gui/PluginMenu.hpp deleted file mode 100644 index bc654db5..00000000 --- a/src/gui/PluginMenu.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2014-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_PLUGINMENU_HPP -#define INGEN_GUI_PLUGINMENU_HPP - -#include <map> -#include <set> -#include <string> - -#include <gtkmm/menu.h> - -#include "ingen/World.hpp" -#include "ingen/types.hpp" -#include "lilv/lilv.h" - -namespace Ingen { - -namespace Client { class PluginModel; } - -namespace GUI { - -/** - Type-hierarchical plugin menu. - - @ingroup GUI -*/ -class PluginMenu : public Gtk::Menu -{ -public: - PluginMenu(Ingen::World& world); - - void clear(); - void add_plugin(SPtr<Client::PluginModel> p); - - sigc::signal< void, WPtr<Client::PluginModel> > signal_load_plugin; - -private: - struct MenuRecord { - MenuRecord(Gtk::MenuItem* i, Gtk::Menu* m) : item(i), menu(m) {} - Gtk::MenuItem* item; - Gtk::Menu* menu; - }; - - typedef std::multimap<const std::string, const LilvPluginClass*> LV2Children; - typedef std::multimap<const std::string, MenuRecord> ClassMenus; - - /// Recursively add hierarchy rooted at `plugin_class` to `menu`. - size_t build_plugin_class_menu(Gtk::Menu* menu, - const LilvPluginClass* plugin_class, - const LilvPluginClasses* classes, - const LV2Children& children, - std::set<const char*>& ancestors); - - void add_plugin_to_menu(MenuRecord& menu, SPtr<Client::PluginModel> p); - - void load_plugin(WPtr<Client::PluginModel> weak_plugin); - - Ingen::World& _world; - MenuRecord _classless_menu; - ClassMenus _class_menus; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_PLUGINMENU_HPP diff --git a/src/gui/Port.cpp b/src/gui/Port.cpp deleted file mode 100644 index 9742cee3..00000000 --- a/src/gui/Port.cpp +++ /dev/null @@ -1,534 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <string> - -#include "ganv/Module.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/client/GraphModel.hpp" -#include "ingen/client/PortModel.hpp" - -#include "App.hpp" -#include "GraphWindow.hpp" -#include "Port.hpp" -#include "PortMenu.hpp" -#include "RDFS.hpp" -#include "Style.hpp" -#include "WidgetFactory.hpp" -#include "WindowFactory.hpp" -#include "ingen_config.h" -#include "rgba.hpp" - -using namespace Ingen::Client; - -namespace Ingen { -namespace GUI { - -Port* -Port::create(App& app, - Ganv::Module& module, - SPtr<const PortModel> pm, - bool flip) -{ - return new Port(app, module, pm, port_label(app, pm), flip); -} - -/** @param flip Make an input port appear as an output port, and vice versa. - */ -Port::Port(App& app, - Ganv::Module& module, - SPtr<const PortModel> pm, - const std::string& name, - bool flip) - : Ganv::Port(module, name, - flip ? (!pm->is_input()) : pm->is_input(), - app.style()->get_port_color(pm.get())) - , _app(app) - , _port_model(pm) - , _entered(false) - , _flipped(flip) -{ - assert(pm); - - if (app.can_control(pm.get())) { - show_control(); - pm->signal_value_changed().connect( - sigc::mem_fun(this, &Port::value_changed)); - } - - port_properties_changed(); - - pm->signal_property().connect( - sigc::mem_fun(this, &Port::property_changed)); - pm->signal_property_removed().connect( - sigc::mem_fun(this, &Port::property_removed)); - pm->signal_activity().connect( - sigc::mem_fun(this, &Port::activity)); - pm->signal_moved().connect( - sigc::mem_fun(this, &Port::moved)); - - signal_value_changed.connect( - sigc::mem_fun(this, &Port::on_value_changed)); - - signal_event().connect( - sigc::mem_fun(this, &Port::on_event)); - - set_is_controllable(pm->is_numeric() && pm->is_input()); - - Ganv::Port::set_beveled(model()->is_a(_app.uris().lv2_ControlPort) || - model()->has_property(_app.uris().atom_bufferType, - _app.uris().atom_Sequence)); - - for (const auto& p : pm->properties()) { - property_changed(p.first, p.second); - } - - update_metadata(); - value_changed(pm->value()); -} - -Port::~Port() -{ - _app.activity_port_destroyed(this); -} - -std::string -Port::port_label(App& app, SPtr<const PortModel> pm) -{ - if (!pm) { - return ""; - } - - std::string label; - if (app.world()->conf().option("port-labels").get<int32_t>()) { - if (app.world()->conf().option("human-names").get<int32_t>()) { - const Atom& name = pm->get_property(app.uris().lv2_name); - if (name.type() == app.forge().String) { - label = name.ptr<char>(); - } else { - const SPtr<const BlockModel> parent( - dynamic_ptr_cast<const BlockModel>(pm->parent())); - if (parent && parent->plugin_model()) { - label = parent->plugin_model()->port_human_name(pm->index()); - } - } - } else { - label = pm->path().symbol(); - } - } - return label; -} - -void -Port::ensure_label() -{ - if (!get_label()) { - set_label(port_label(_app, _port_model.lock()).c_str()); - } -} - -void -Port::update_metadata() -{ - SPtr<const PortModel> pm = _port_model.lock(); - if (pm && _app.can_control(pm.get()) && pm->is_numeric()) { - SPtr<const BlockModel> parent = dynamic_ptr_cast<const BlockModel>(pm->parent()); - if (parent) { - float min = 0.0f; - float max = 1.0f; - parent->port_value_range(pm, min, max, _app.sample_rate()); - set_control_min(min); - set_control_max(max); - } - } -} - -bool -Port::show_menu(GdkEventButton* ev) -{ - PortMenu* menu = nullptr; - WidgetFactory::get_widget_derived("object_menu", menu); - if (!menu) { - _app.log().error("Failed to load port menu widget\n"); - return false; - } - - menu->init(_app, model(), _flipped); - menu->popup(ev->button, ev->time); - return true; -} - -void -Port::moved() -{ - if (_app.world()->conf().option("port-labels").get<int32_t>() && - !_app.world()->conf().option("human-names").get<int32_t>()) { - set_label(model()->symbol().c_str()); - } -} - -void -Port::on_value_changed(double value) -{ - const URIs& uris = _app.uris(); - const Atom& current_value = model()->value(); - if (current_value.type() != uris.forge.Float) { - return; // Non-float, unsupported - } - - if (current_value.get<float>() == (float)value) { - return; // No change - } - - const Atom atom = _app.forge().make(float(value)); - _app.set_property(model()->uri(), - _app.world()->uris().ingen_value, - atom); - - if (_entered) { - GraphBox* box = get_graph_box(); - if (box) { - box->show_port_status(model().get(), atom); - } - } -} - -void -Port::value_changed(const Atom& value) -{ - if (value.type() == _app.forge().Float && !get_grabbed()) { - Ganv::Port::set_control_value(value.get<float>()); - } -} - -void -Port::on_scale_point_activated(float f) -{ - _app.set_property(model()->uri(), - _app.world()->uris().ingen_value, - _app.world()->forge().make(f)); -} - -Gtk::Menu* -Port::build_enum_menu() -{ - SPtr<const BlockModel> block = dynamic_ptr_cast<BlockModel>(model()->parent()); - Gtk::Menu* menu = Gtk::manage(new Gtk::Menu()); - - PluginModel::ScalePoints points = block->plugin_model()->port_scale_points( - model()->index()); - for (auto i = points.begin(); i != points.end(); ++i) { - menu->items().push_back(Gtk::Menu_Helpers::MenuElem(i->second)); - Gtk::MenuItem* menu_item = &(menu->items().back()); - menu_item->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &Port::on_scale_point_activated), - i->first)); - } - - return menu; -} - -void -Port::on_uri_activated(const URI& uri) -{ - _app.set_property(model()->uri(), - _app.world()->uris().ingen_value, - _app.world()->forge().make_urid( - _app.world()->uri_map().map_uri(uri.c_str()))); -} - -Gtk::Menu* -Port::build_uri_menu() -{ - World* world = _app.world(); - SPtr<const BlockModel> block = dynamic_ptr_cast<BlockModel>(model()->parent()); - Gtk::Menu* menu = Gtk::manage(new Gtk::Menu()); - - // Get the port designation, which should be a rdf:Property - const Atom& designation_atom = model()->get_property( - _app.uris().lv2_designation); - if (!designation_atom.is_valid()) { - return nullptr; - } - - LilvNode* designation = lilv_new_uri( - world->lilv_world(), world->forge().str(designation_atom, false).c_str()); - LilvNode* rdfs_range = lilv_new_uri( - world->lilv_world(), LILV_NS_RDFS "range"); - - // Get every class in the range of the port's property - RDFS::URISet ranges; - LilvNodes* range = lilv_world_find_nodes( - world->lilv_world(), designation, rdfs_range, nullptr); - LILV_FOREACH(nodes, r, range) { - ranges.insert(URI(lilv_node_as_string(lilv_nodes_get(range, r)))); - } - RDFS::classes(world, ranges, false); - - // Get all objects in range - RDFS::Objects values = RDFS::instances(world, ranges); - - // Add a menu item for each such class - for (const auto& v : values) { - if (!v.first.empty()) { - const std::string qname = world->rdf_world()->prefixes().qualify(v.second); - const std::string label = qname + " - " + v.first; - menu->items().push_back(Gtk::Menu_Helpers::MenuElem(label)); - Gtk::MenuItem* menu_item = &(menu->items().back()); - menu_item->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &Port::on_uri_activated), - v.second)); - } - } - - return menu; -} - -bool -Port::on_event(GdkEvent* ev) -{ - GraphBox* box = nullptr; - switch (ev->type) { - case GDK_ENTER_NOTIFY: - _entered = true; - if ((box = get_graph_box())) { - box->object_entered(model().get()); - } - return false; - case GDK_LEAVE_NOTIFY: - _entered = false; - if ((box = get_graph_box())) { - box->object_left(model().get()); - } - return false; - case GDK_BUTTON_PRESS: - if (ev->button.button == 1) { - if (model()->is_enumeration()) { - Gtk::Menu* menu = build_enum_menu(); - menu->popup(ev->button.button, ev->button.time); - return true; - } else if (model()->is_uri()) { - Gtk::Menu* menu = build_uri_menu(); - if (menu) { - menu->popup(ev->button.button, ev->button.time); - return true; - } - } - } else if (ev->button.button == 3) { - return show_menu(&ev->button); - } - break; - default: - break; - } - - return false; -} - -inline static uint32_t -peak_color(float peak) -{ - static const uint32_t min = 0x4A8A0EC0; - static const uint32_t max = 0xFFCE1FC0; - static const uint32_t peak_min = 0xFF561FC0; - static const uint32_t peak_max = 0xFF0A38C0; - - if (peak < 1.0) { - return rgba_interpolate(min, max, peak); - } else { - return rgba_interpolate(peak_min, peak_max, fminf(peak, 2.0f) - 1.0f); - } -} - -void -Port::activity(const Atom& value) -{ - if (model()->is_a(_app.uris().lv2_AudioPort)) { - set_fill_color(peak_color(value.get<float>())); - } else if (_app.can_control(model().get()) && value.type() == _app.uris().atom_Float) { - Ganv::Port::set_control_value(value.get<float>()); - } else { - _app.port_activity(this); - } -} - -GraphBox* -Port::get_graph_box() const -{ - SPtr<const GraphModel> graph = dynamic_ptr_cast<const GraphModel>(model()->parent()); - GraphBox* box = _app.window_factory()->graph_box(graph); - if (!box) { - graph = dynamic_ptr_cast<const GraphModel>(model()->parent()->parent()); - box = _app.window_factory()->graph_box(graph); - } - return box; -} - -void -Port::set_type_tag() -{ - const URIs& uris = _app.uris(); - std::string tag; - if (model()->is_a(_app.uris().lv2_AudioPort)) { - tag = "~"; - } else if (model()->is_a(_app.uris().lv2_CVPort)) { - tag = "ℝ̰"; - } else if (model()->is_a(_app.uris().lv2_ControlPort)) { - if (model()->is_enumeration()) { - tag = "…"; - } else if (model()->is_integer()) { - tag = "ℤ"; - } else if (model()->is_toggle()) { - tag = ((model()->value() != _app.uris().forge.make(0.0f)) - ? "☑" : "☐"); - - } else { - tag = "ℝ"; - } - } else if (model()->is_a(_app.uris().atom_AtomPort)) { - if (model()->supports(_app.uris().atom_Float)) { - if (model()->is_toggle()) { - tag = ((model()->value() != _app.uris().forge.make(0.0f)) - ? "☑" : "☐"); - } else { - tag = "ℝ"; - } - } - if (model()->supports(_app.uris().atom_Int)) { - tag += "ℤ"; - } - if (model()->supports(_app.uris().midi_MidiEvent)) { - tag += "𝕄"; - } - if (model()->supports(_app.uris().patch_Message)) { - if (tag.empty()) { - tag += "="; - } else { - tag += "̿"; - } - } - if (tag.empty()) { - tag = "*"; - } - - if (model()->has_property(uris.atom_bufferType, uris.atom_Sequence)) { - tag += "̤"; - } - } - - if (!tag.empty()) { - set_value_label(tag.c_str()); - } -} - -void -Port::port_properties_changed() -{ - if (model()->is_toggle()) { - set_control_is_toggle(true); - } else if (model()->is_integer()) { - set_control_is_integer(true); - } - set_type_tag(); -} - -void -Port::property_changed(const URI& key, const Atom& value) -{ - const URIs& uris = _app.uris(); - if (value.type() == uris.forge.Float) { - float val = value.get<float>(); - if (key == uris.ingen_value && !get_grabbed()) { - Ganv::Port::set_control_value(val); - if (model()->is_toggle()) { - std::string tag = (val == 0.0f) ? "☐" : "☑"; - if (model()->is_a(_app.uris().lv2_CVPort)) { - tag += "̰"; - } else if (model()->has_property(uris.atom_bufferType, - uris.atom_Sequence)) { - tag += "̤"; - } - set_value_label(tag.c_str()); - } - } else if (key == uris.lv2_minimum) { - if (model()->port_property(uris.lv2_sampleRate)) { - val *= _app.sample_rate(); - } - set_control_min(val); - } else if (key == uris.lv2_maximum) { - if (model()->port_property(uris.lv2_sampleRate)) { - val *= _app.sample_rate(); - } - set_control_max(val); - } - } else if (key == uris.lv2_portProperty) { - port_properties_changed(); - } else if (key == uris.lv2_name) { - if (value.type() == uris.forge.String && - _app.world()->conf().option("port-labels").get<int32_t>() && - _app.world()->conf().option("human-names").get<int32_t>()) { - set_label(value.ptr<char>()); - } - } else if (key == uris.rdf_type || key == uris.atom_bufferType) { - set_fill_color(_app.style()->get_port_color(model().get())); - Ganv::Port::set_beveled(model()->is_a(uris.lv2_ControlPort) || - model()->has_property(uris.atom_bufferType, - uris.atom_Sequence)); - } -} - -void -Port::property_removed(const URI& key, const Atom& value) -{ - const URIs& uris = _app.uris(); - if (key == uris.lv2_minimum || key == uris.lv2_maximum) { - update_metadata(); - } else if (key == uris.rdf_type || key == uris.atom_bufferType) { - Ganv::Port::set_beveled(model()->is_a(uris.lv2_ControlPort) || - model()->has_property(uris.atom_bufferType, - uris.atom_Sequence)); - } -} - -bool -Port::on_selected(gboolean b) -{ - if (b) { - SPtr<const PortModel> pm = _port_model.lock(); - if (pm) { - SPtr<const BlockModel> block = dynamic_ptr_cast<const BlockModel>(pm->parent()); - GraphWindow* win = _app.window_factory()->parent_graph_window(block); - if (win && win->documentation_is_visible() && block->plugin_model()) { - bool html = false; -#ifdef HAVE_WEBKIT - html = true; -#endif - const std::string& doc = block->plugin_model()->port_documentation( - pm->index(), html); - win->set_documentation(doc, html); - } - } - } - - return true; -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/Port.hpp b/src/gui/Port.hpp deleted file mode 100644 index c714feae..00000000 --- a/src/gui/Port.hpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_PORT_HPP -#define INGEN_GUI_PORT_HPP - -#include <cassert> -#include <string> - -#include <gtkmm/menu.h> - -#include "ganv/Port.hpp" -#include "ingen/types.hpp" - -namespace Raul { -class Atom; -} - -namespace Ingen { - -class URI; - -namespace Client { class PortModel; } - -namespace GUI { - -class App; -class GraphBox; - -/** A Port on an Module. - * - * \ingroup GUI - */ -class Port : public Ganv::Port -{ -public: - static Port* create( - App& app, - Ganv::Module& module, - SPtr<const Client::PortModel> pm, - bool flip = false); - - ~Port(); - - SPtr<const Client::PortModel> model() const { return _port_model.lock(); } - - bool show_menu(GdkEventButton* ev); - void update_metadata(); - void ensure_label(); - - void value_changed(const Atom& value); - void activity(const Atom& value); - - bool on_selected(gboolean b); - -private: - Port(App& app, - Ganv::Module& module, - SPtr<const Client::PortModel> pm, - const std::string& name, - bool flip = false); - - static std::string port_label(App& app, SPtr<const Client::PortModel> pm); - - Gtk::Menu* build_enum_menu(); - Gtk::Menu* build_uri_menu(); - GraphBox* get_graph_box() const; - - void property_changed(const URI& key, const Atom& value); - void property_removed(const URI& key, const Atom& value); - void moved(); - - void on_value_changed(double value); - void on_scale_point_activated(float f); - void on_uri_activated(const URI& uri); - bool on_event(GdkEvent* ev); - void port_properties_changed(); - void set_type_tag(); - - App& _app; - WPtr<const Client::PortModel> _port_model; - bool _entered : 1; - bool _flipped : 1; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_PORT_HPP diff --git a/src/gui/PortMenu.cpp b/src/gui/PortMenu.cpp deleted file mode 100644 index c6ec8fa1..00000000 --- a/src/gui/PortMenu.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cmath> - -#include "ingen/Interface.hpp" -#include "ingen/client/GraphModel.hpp" -#include "ingen/client/PortModel.hpp" -#include "ingen/types.hpp" - -#include "App.hpp" -#include "PortMenu.hpp" -#include "WindowFactory.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -PortMenu::PortMenu(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : ObjectMenu(cobject, xml) - , _internal_graph_port(false) -{ - xml->get_widget("object_menu", _port_menu); - xml->get_widget("port_set_min_menuitem", _set_min_menuitem); - xml->get_widget("port_set_max_menuitem", _set_max_menuitem); - xml->get_widget("port_reset_range_menuitem", _reset_range_menuitem); - xml->get_widget("port_expose_menuitem", _expose_menuitem); -} - -void -PortMenu::init(App& app, SPtr<const PortModel> port, bool internal_graph_port) -{ - const URIs& uris = app.uris(); - - ObjectMenu::init(app, port); - _internal_graph_port = internal_graph_port; - - _set_min_menuitem->signal_activate().connect( - sigc::mem_fun(this, &PortMenu::on_menu_set_min)); - - _set_max_menuitem->signal_activate().connect( - sigc::mem_fun(this, &PortMenu::on_menu_set_max)); - - _reset_range_menuitem->signal_activate().connect( - sigc::mem_fun(this, &PortMenu::on_menu_reset_range)); - - _expose_menuitem->signal_activate().connect( - sigc::mem_fun(this, &PortMenu::on_menu_expose)); - - const bool is_control(app.can_control(port.get()) && port->is_numeric()); - const bool is_on_graph(dynamic_ptr_cast<GraphModel>(port->parent())); - const bool is_input(port->is_input()); - - if (!is_on_graph) { - _polyphonic_menuitem->set_sensitive(false); - _rename_menuitem->set_sensitive(false); - _destroy_menuitem->set_sensitive(false); - } - - if (port->is_a(uris.atom_AtomPort)) { - _polyphonic_menuitem->hide(); - } - - _reset_range_menuitem->set_visible(is_input && is_control && !is_on_graph); - _set_max_menuitem->set_visible(is_input && is_control); - _set_min_menuitem->set_visible(is_input && is_control); - _expose_menuitem->set_visible(!is_on_graph); - _learn_menuitem->set_visible(is_input && is_control); - _unlearn_menuitem->set_visible(is_input && is_control); - - if (!is_control && is_on_graph) { - _separator_menuitem->hide(); - } - - _enable_signal = true; -} - -void -PortMenu::on_menu_disconnect() -{ - if (_internal_graph_port) { - _app->interface()->disconnect_all( - _object->parent()->path(), _object->path()); - } else { - _app->interface()->disconnect_all( - _object->parent()->path().parent(), _object->path()); - } -} - -void -PortMenu::on_menu_set_min() -{ - const URIs& uris = _app->uris(); - SPtr<const PortModel> model = dynamic_ptr_cast<const PortModel>(_object); - const Atom& value = model->get_property(uris.ingen_value); - if (value.is_valid()) { - _app->set_property(_object->uri(), uris.lv2_minimum, value); - } -} - -void -PortMenu::on_menu_set_max() -{ - const URIs& uris = _app->uris(); - SPtr<const PortModel> model = dynamic_ptr_cast<const PortModel>(_object); - const Atom& value = model->get_property(uris.ingen_value); - if (value.is_valid()) { - _app->set_property(_object->uri(), uris.lv2_maximum, value); - } -} - -void -PortMenu::on_menu_reset_range() -{ - const URIs& uris = _app->uris(); - SPtr<const PortModel> model = dynamic_ptr_cast<const PortModel>(_object); - - // Remove lv2:minimum and lv2:maximum properties - Properties remove; - remove.insert({uris.lv2_minimum, Property(uris.patch_wildcard)}); - remove.insert({uris.lv2_maximum, Property(uris.patch_wildcard)}); - _app->interface()->delta(_object->uri(), remove, Properties()); -} - -void -PortMenu::on_menu_expose() -{ - const URIs& uris = _app->uris(); - SPtr<const PortModel> port = dynamic_ptr_cast<const PortModel>(_object); - SPtr<const BlockModel> block = dynamic_ptr_cast<const BlockModel>(port->parent()); - - const std::string label = block->label() + " " + block->port_label(port); - const Raul::Path path = Raul::Path(block->path() + Raul::Symbol("_" + port->symbol())); - - Ingen::Resource r(*_object.get()); - r.remove_property(uris.lv2_index, uris.patch_wildcard); - r.set_property(uris.lv2_symbol, _app->forge().alloc(path.symbol())); - r.set_property(uris.lv2_name, _app->forge().alloc(label.c_str())); - - // TODO: Pretty kludgey coordinates - const float block_x = block->get_property(uris.ingen_canvasX).get<float>(); - const float block_y = block->get_property(uris.ingen_canvasY).get<float>(); - const float x_off = (label.length() * 16.0f) * (port->is_input() ? -1 : 1); - const float y_off = port->index() * 32.0f; - r.set_property(uris.ingen_canvasX, _app->forge().make(block_x + x_off)); - r.set_property(uris.ingen_canvasY, _app->forge().make(block_y + y_off)); - - _app->interface()->put(path_to_uri(path), r.properties()); - - if (port->is_input()) { - _app->interface()->connect(path, _object->path()); - } else { - _app->interface()->connect(_object->path(), path); - } -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/PortMenu.hpp b/src/gui/PortMenu.hpp deleted file mode 100644 index db567980..00000000 --- a/src/gui/PortMenu.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_PORTMENU_HPP -#define INGEN_GUI_PORTMENU_HPP - -#include <gtkmm/builder.h> -#include <gtkmm/menu.h> -#include <gtkmm/menushell.h> - -#include "ingen/client/PortModel.hpp" -#include "ingen/types.hpp" - -#include "ObjectMenu.hpp" - -namespace Ingen { -namespace GUI { - -/** Menu for a Port. - * - * \ingroup GUI - */ -class PortMenu : public ObjectMenu -{ -public: - PortMenu(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - void init(App& app, - SPtr<const Client::PortModel> port, - bool internal_graph_port = false); - -private: - void on_menu_disconnect(); - void on_menu_set_min(); - void on_menu_set_max(); - void on_menu_reset_range(); - void on_menu_expose(); - - Gtk::Menu* _port_menu; - Gtk::MenuItem* _set_min_menuitem; - Gtk::MenuItem* _set_max_menuitem; - Gtk::MenuItem* _reset_range_menuitem; - Gtk::MenuItem* _expose_menuitem; - - /// True iff this is a (flipped) port on a GraphPortModule in its graph - bool _internal_graph_port; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_PORTMENU_HPP diff --git a/src/gui/PropertiesWindow.cpp b/src/gui/PropertiesWindow.cpp deleted file mode 100644 index 4d47b3ae..00000000 --- a/src/gui/PropertiesWindow.cpp +++ /dev/null @@ -1,591 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <algorithm> -#include <cassert> -#include <set> - -#include <gtkmm/label.h> -#include <gtkmm/spinbutton.h> - -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/World.hpp" -#include "ingen/client/BlockModel.hpp" -#include "ingen/client/PluginModel.hpp" - -#include "App.hpp" -#include "PropertiesWindow.hpp" -#include "RDFS.hpp" -#include "URIEntry.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -typedef std::set<URI> URISet; - -PropertiesWindow::PropertiesWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Window(cobject) - , _value_type(0) -{ - xml->get_widget("properties_vbox", _vbox); - xml->get_widget("properties_scrolledwindow", _scrolledwindow); - xml->get_widget("properties_table", _table); - xml->get_widget("properties_key_combo", _key_combo); - xml->get_widget("properties_value_bin", _value_bin); - xml->get_widget("properties_add_button", _add_button); - xml->get_widget("properties_cancel_button", _cancel_button); - xml->get_widget("properties_apply_button", _apply_button); - xml->get_widget("properties_ok_button", _ok_button); - - _key_store = Gtk::ListStore::create(_combo_columns); - _key_combo->set_model(_key_store); - _key_combo->pack_start(_combo_columns.label_col); - - _key_combo->signal_changed().connect( - sigc::mem_fun(this, &PropertiesWindow::key_changed)); - - _add_button->signal_clicked().connect( - sigc::mem_fun(this, &PropertiesWindow::add_clicked)); - - _cancel_button->signal_clicked().connect( - sigc::mem_fun(this, &PropertiesWindow::cancel_clicked)); - - _apply_button->signal_clicked().connect( - sigc::mem_fun(this, &PropertiesWindow::apply_clicked)); - - _ok_button->signal_clicked().connect( - sigc::mem_fun(this, &PropertiesWindow::ok_clicked)); -} - -void -PropertiesWindow::reset() -{ - _property_connection.disconnect(); - _property_removed_connection.disconnect(); - - _key_store->clear(); - _records.clear(); - - _model.reset(); - - _table->children().clear(); - _table->resize(1, 3); - _table->property_n_rows() = 1; -} - -void -PropertiesWindow::present(SPtr<const ObjectModel> model) -{ - set_object(model); - Gtk::Window::present(); -} - -void -PropertiesWindow::add_property(const URI& key, const Atom& value) -{ - World* world = _app->world(); - - const unsigned n_rows = _table->property_n_rows() + 1; - _table->property_n_rows() = n_rows; - - // Column 0: Property - LilvNode* prop = lilv_new_uri(world->lilv_world(), key.c_str()); - std::string name = RDFS::label(world, prop); - if (name.empty()) { - name = world->rdf_world()->prefixes().qualify(key); - } - Gtk::Label* label = new Gtk::Label( - std::string("<a href=\"") + key.string() + "\">" + name + "</a>", - 1.0, - 0.5); - label->set_use_markup(true); - _app->set_tooltip(label, prop); - _table->attach(*Gtk::manage(label), 0, 1, n_rows, n_rows + 1, - Gtk::FILL|Gtk::SHRINK, Gtk::SHRINK); - - // Column 1: Value - Gtk::Alignment* align = manage(new Gtk::Alignment(0.0, 0.5, 1.0, 1.0)); - Gtk::CheckButton* present = manage(new Gtk::CheckButton()); - const char* type = _app->world()->uri_map().unmap_uri(value.type()); - Gtk::Widget* val_widget = create_value_widget(key, type, value); - - present->set_active(); - if (val_widget) { - align->add(*Gtk::manage(val_widget)); - _app->set_tooltip(val_widget, prop); - } - - _table->attach(*align, 1, 2, n_rows, n_rows + 1, - Gtk::FILL|Gtk::EXPAND, Gtk::SHRINK); - _table->attach(*present, 2, 3, n_rows, n_rows + 1, - Gtk::FILL, Gtk::SHRINK); - _records.emplace(key, Record(value, align, n_rows, present)); - _table->show_all(); - - lilv_node_free(prop); -} - -bool -PropertiesWindow::datatype_supported(const RDFS::URISet& types, - URI* widget_type) -{ - if (types.find(_app->uris().atom_Int) != types.end()) { - *widget_type = _app->uris().atom_Int; - return true; - } else if (types.find(_app->uris().atom_Float) != types.end()) { - *widget_type = _app->uris().atom_Float; - return true; - } else if (types.find(_app->uris().atom_Bool) != types.end()) { - *widget_type = _app->uris().atom_Bool; - return true; - } else if (types.find(_app->uris().atom_String) != types.end()) { - *widget_type = _app->uris().atom_String; - return true; - } else if (types.find(_app->uris().atom_URID) != types.end()) { - *widget_type = _app->uris().atom_URID; - return true; - } - - return false; -} - -bool -PropertiesWindow::class_supported(const RDFS::URISet& types) -{ - World* world = _app->world(); - LilvNode* rdf_type = lilv_new_uri( - world->lilv_world(), LILV_NS_RDF "type"); - - for (const auto& t : types) { - LilvNode* range = lilv_new_uri(world->lilv_world(), t.c_str()); - LilvNodes* instances = lilv_world_find_nodes( - world->lilv_world(), nullptr, rdf_type, range); - - const bool has_instance = (lilv_nodes_size(instances) > 0); - - lilv_nodes_free(instances); - lilv_node_free(range); - if (has_instance) { - lilv_node_free(rdf_type); - return true; - } - } - - lilv_node_free(rdf_type); - return false; -} - -/** Set the node this window is associated with. - * This function MUST be called before using this object in any way. - */ -void -PropertiesWindow::set_object(SPtr<const ObjectModel> model) -{ - reset(); - _model = model; - - set_title(model->path() + " Properties - Ingen"); - - World* world = _app->world(); - - LilvNode* rdf_type = lilv_new_uri( - world->lilv_world(), LILV_NS_RDF "type"); - LilvNode* rdfs_DataType = lilv_new_uri( - world->lilv_world(), LILV_NS_RDFS "Datatype"); - - // Populate key combo - const URISet props = RDFS::properties(world, model); - std::map<std::string, URI> entries; - for (const auto& p : props) { - LilvNode* prop = lilv_new_uri(world->lilv_world(), p.c_str()); - const std::string label = RDFS::label(world, prop); - URISet ranges = RDFS::range(world, prop, true); - - lilv_node_free(prop); - if (label.empty() || ranges.empty()) { - // Property has no label or range, can't show a widget for it - continue; - } - - LilvNode* range = lilv_new_uri(world->lilv_world(), (*ranges.begin()).c_str()); - if (RDFS::is_a(world, range, rdfs_DataType)) { - // Range is a datatype, show if type or any subtype is supported - RDFS::datatypes(_app->world(), ranges, false); - URI widget_type("urn:nothing"); - if (datatype_supported(ranges, &widget_type)) { - entries.emplace(label, p); - } - } else { - // Range is presumably a class, show if any instances are known - if (class_supported(ranges)) { - entries.emplace(label, p); - } - } - } - - for (const auto& e : entries) { - Gtk::ListStore::iterator ki = _key_store->append(); - Gtk::ListStore::Row row = *ki; - row[_combo_columns.uri_col] = e.second.string(); - row[_combo_columns.label_col] = e.first; - } - - lilv_node_free(rdfs_DataType); - lilv_node_free(rdf_type); - - for (const auto& p : model->properties()) { - add_property(p.first, p.second); - } - - _table->show_all(); - - _property_connection = model->signal_property().connect( - sigc::mem_fun(this, &PropertiesWindow::add_property)); - _property_removed_connection = model->signal_property_removed().connect( - sigc::mem_fun(this, &PropertiesWindow::remove_property)); -} - -Gtk::Widget* -PropertiesWindow::create_value_widget(const URI& key, - const char* type_uri, - const Atom& value) -{ - if (!type_uri || !URI::is_valid(type_uri)) { - return nullptr; - } - - URI type(type_uri); - Ingen::World* world = _app->world(); - LilvWorld* lworld = world->lilv_world(); - - // See if type is a datatype we support - std::set<URI> types{type}; - RDFS::datatypes(_app->world(), types, false); - - URI widget_type("urn:nothing"); - const bool supported = datatype_supported(types, &widget_type); - if (supported) { - type = widget_type; - _value_type = _app->world()->uri_map().map_uri(type); - } - - if (type == _app->uris().atom_Int) { - Gtk::SpinButton* widget = manage(new Gtk::SpinButton(0.0, 0)); - widget->property_numeric() = true; - widget->set_range(INT_MIN, INT_MAX); - widget->set_increments(1, 10); - if (value.is_valid()) { - widget->set_value(value.get<int32_t>()); - } - widget->signal_value_changed().connect( - sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key)); - return widget; - } else if (type == _app->uris().atom_Float) { - Gtk::SpinButton* widget = manage(new Gtk::SpinButton(0.0, 4)); - widget->property_numeric() = true; - widget->set_snap_to_ticks(false); - widget->set_range(-FLT_MAX, FLT_MAX); - widget->set_increments(0.1, 1.0); - if (value.is_valid()) { - widget->set_value(value.get<float>()); - } - widget->signal_value_changed().connect( - sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key)); - return widget; - } else if (type == _app->uris().atom_Bool) { - Gtk::CheckButton* widget = manage(new Gtk::CheckButton()); - if (value.is_valid()) { - widget->set_active(value.get<int32_t>()); - } - widget->signal_toggled().connect( - sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key)); - return widget; - } else if (type == _app->uris().atom_String) { - Gtk::Entry* widget = manage(new Gtk::Entry()); - if (value.is_valid()) { - widget->set_text(value.ptr<char>()); - } - widget->signal_changed().connect( - sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key)); - return widget; - } else if (type == _app->uris().atom_URID) { - const char* str = (value.is_valid() - ? world->uri_map().unmap_uri(value.get<int32_t>()) - : ""); - - LilvNode* pred = lilv_new_uri(lworld, key.c_str()); - URISet ranges = RDFS::range(world, pred, true); - URIEntry* widget = manage(new URIEntry(_app, ranges, str ? str : "")); - widget->signal_changed().connect( - sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key)); - lilv_node_free(pred); - return widget; - } - - LilvNode* type_node = lilv_new_uri(lworld, type.c_str()); - LilvNode* rdfs_Class = lilv_new_uri(lworld, LILV_NS_RDFS "Class"); - const bool is_class = RDFS::is_a(world, type_node, rdfs_Class); - lilv_node_free(rdfs_Class); - lilv_node_free(type_node); - - if (type == _app->uris().atom_URI || - type == _app->uris().rdfs_Class || - is_class) { - LilvNode* pred = lilv_new_uri(lworld, key.c_str()); - URISet ranges = RDFS::range(world, pred, true); - const char* str = value.is_valid() ? value.ptr<const char>() : ""; - URIEntry* widget = manage(new URIEntry(_app, ranges, str)); - widget->signal_changed().connect( - sigc::bind(sigc::mem_fun(this, &PropertiesWindow::on_change), key)); - lilv_node_free(pred); - return widget; - } - - _app->log().error(fmt("No widget for value type %1%\n") % type); - - return nullptr; -} - -void -PropertiesWindow::on_show() -{ - static const int WIN_PAD = 64; - static const int VBOX_PAD = 16; - - int width = 0; - int height = 0; - - for (const auto& c : _vbox->children()) { - const Gtk::Requisition& req = c.get_widget()->size_request(); - - width = std::max(width, req.width); - height += req.height + VBOX_PAD; - } - - const Gtk::Requisition& req = _table->size_request(); - - width = 1.2 * std::max(width, req.width + 128); - height += req.height; - - set_default_size(width + WIN_PAD, height + WIN_PAD); - resize(width + WIN_PAD, height + WIN_PAD); - Gtk::Window::on_show(); -} - -void -PropertiesWindow::change_property(const URI& key, const Atom& value) -{ - auto r = _records.find(key); - if (r == _records.end()) { - add_property(key, value); - _table->show_all(); - return; - } - - Record& record = r->second; - const char* type = _app->world()->uri_map().unmap_uri(value.type()); - Gtk::Widget* val_widget = create_value_widget(key, type, value); - - if (val_widget) { - record.value_widget->remove(); - record.value_widget->add(*Gtk::manage(val_widget)); - val_widget->show_all(); - } - - record.value = value; -} - -void -PropertiesWindow::remove_property(const URI& key, const Atom& value) -{ - // Bleh, there doesn't seem to be an easy way to remove a Gtk::Table row... - _records.clear(); - _table->children().clear(); - _table->resize(1, 3); - _table->property_n_rows() = 1; - - for (const auto& p : _model->properties()) { - add_property(p.first, p.second); - } - _table->show_all(); -} - -Atom -PropertiesWindow::get_value(LV2_URID type, Gtk::Widget* value_widget) -{ - Forge& forge = _app->forge(); - - if (type == forge.Int) { - Gtk::SpinButton* spin = dynamic_cast<Gtk::SpinButton*>(value_widget); - if (spin) { - return _app->forge().make(spin->get_value_as_int()); - } - } else if (type == forge.Float) { - Gtk::SpinButton* spin = dynamic_cast<Gtk::SpinButton*>(value_widget); - if (spin) { - return _app->forge().make(static_cast<float>(spin->get_value())); - } - } else if (type == forge.Bool) { - Gtk::CheckButton* check = dynamic_cast<Gtk::CheckButton*>(value_widget); - if (check) { - return _app->forge().make(check->get_active()); - } - } else if (type == forge.URI || type == forge.URID) { - URIEntry* uri_entry = dynamic_cast<URIEntry*>(value_widget); - if (uri_entry && URI::is_valid(uri_entry->get_text())) { - return _app->forge().make_urid(URI(uri_entry->get_text())); - } else { - _app->log().error(fmt("Invalid URI <%1%>\n") % uri_entry->get_text()); - } - } else if (type == forge.String) { - Gtk::Entry* entry = dynamic_cast<Gtk::Entry*>(value_widget); - if (entry) { - return _app->forge().alloc(entry->get_text()); - } - } - - return Atom(); -} - -void -PropertiesWindow::on_change(const URI& key) -{ - auto r = _records.find(key); - if (r == _records.end()) { - return; - } - - Record& record = r->second; - const Atom value = get_value(record.value.type(), - record.value_widget->get_child()); - - if (value.is_valid()) { - record.value = value; - } else { - _app->log().error(fmt("Failed to get `%1%' value from widget\n") % key); - } -} - -std::string -PropertiesWindow::active_key() const -{ - const Gtk::ListStore::iterator iter = _key_combo->get_active(); - if (!iter) { - return ""; - } - - Glib::ustring prop_uri = (*iter)[_combo_columns.uri_col]; - return prop_uri; -} - -void -PropertiesWindow::key_changed() -{ - _value_bin->remove(); - if (!_key_combo->get_active()) { - return; - } - - LilvWorld* lworld = _app->world()->lilv_world(); - const Gtk::ListStore::Row key_row = *(_key_combo->get_active()); - const Glib::ustring key_uri = key_row[_combo_columns.uri_col]; - LilvNode* prop = lilv_new_uri(lworld, key_uri.c_str()); - - // Try to create a value widget in the range of this property - const URISet ranges = RDFS::range(_app->world(), prop, true); - for (const auto& r : ranges) { - Gtk::Widget* value_widget = create_value_widget( - URI(key_uri), r.c_str(), Atom()); - - if (value_widget) { - _add_button->set_sensitive(true); - _value_bin->remove(); - _value_bin->add(*Gtk::manage(value_widget)); - _value_bin->show_all(); - break; - } - } - - lilv_node_free(prop); -} - -void -PropertiesWindow::add_clicked() -{ - if (!_key_combo->get_active() || !_value_type || !_value_bin->get_child()) { - return; - } - - // Get selected key URI - const Gtk::ListStore::Row key_row = *(_key_combo->get_active()); - const Glib::ustring key_uri = key_row[_combo_columns.uri_col]; - - // Try to get value from value widget - const Atom& value = get_value(_value_type, _value_bin->get_child()); - if (value.is_valid()) { - // Send property to engine - Properties properties; - properties.emplace(URI(key_uri.c_str()), Property(value)); - _app->interface()->put(_model->uri(), properties); - } -} - -void -PropertiesWindow::cancel_clicked() -{ - reset(); - Gtk::Window::hide(); -} - -void -PropertiesWindow::apply_clicked() -{ - Properties remove; - Properties add; - for (const auto& r : _records) { - const URI& uri = r.first; - const Record& record = r.second; - if (record.present_button->get_active()) { - if (!_model->has_property(uri, record.value)) { - add.emplace(uri, record.value); - } - } else { - remove.emplace(uri, record.value); - } - } - - if (remove.empty()) { - _app->interface()->put(_model->uri(), add); - } else { - _app->interface()->delta(_model->uri(), remove, add); - } -} - -void -PropertiesWindow::ok_clicked() -{ - apply_clicked(); - Gtk::Window::hide(); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/PropertiesWindow.hpp b/src/gui/PropertiesWindow.hpp deleted file mode 100644 index f4a8dd0d..00000000 --- a/src/gui/PropertiesWindow.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_PROPERTIES_WINDOW_HPP -#define INGEN_GUI_PROPERTIES_WINDOW_HPP - -#include <map> - -#include <gtkmm/alignment.h> -#include <gtkmm/box.h> -#include <gtkmm/builder.h> -#include <gtkmm/button.h> -#include <gtkmm/checkbutton.h> -#include <gtkmm/combobox.h> -#include <gtkmm/liststore.h> -#include <gtkmm/scrolledwindow.h> -#include <gtkmm/table.h> - -#include "ingen/client/BlockModel.hpp" -#include "ingen/types.hpp" - -#include "Window.hpp" - -namespace Ingen { - -namespace Client { class ObjectModel; } - -namespace GUI { - -/** Object properties window. - * - * Loaded from XML as a derived object. - * - * \ingroup GUI - */ -class PropertiesWindow : public Window -{ -public: - PropertiesWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - void present(SPtr<const Client::ObjectModel> model); - void set_object(SPtr<const Client::ObjectModel> model); - -private: - /** Record of a property (row in the table) */ - struct Record { - Record(const Atom& v, Gtk::Alignment* vw, int r, Gtk::CheckButton* cb) - : value(v), value_widget(vw), row(r), present_button(cb) - {} - Atom value; - Gtk::Alignment* value_widget; - int row; - Gtk::CheckButton* present_button; - }; - - struct ComboColumns : public Gtk::TreeModel::ColumnRecord { - ComboColumns() { - add(label_col); - add(uri_col); - } - Gtk::TreeModelColumn<Glib::ustring> label_col; - Gtk::TreeModelColumn<Glib::ustring> uri_col; - }; - - void add_property(const URI& key, const Atom& value); - void change_property(const URI& key, const Atom& value); - void remove_property(const URI& key, const Atom& value); - void on_change(const URI& key); - - bool datatype_supported(const std::set<URI>& types, - URI* widget_type); - - bool class_supported(const std::set<URI>& types); - - Gtk::Widget* create_value_widget(const URI& key, - const char* type_uri, - const Atom& value = Atom()); - - Atom get_value(LV2_URID type, Gtk::Widget* value_widget); - - void reset(); - void on_show(); - - std::string active_key() const; - - void key_changed(); - void add_clicked(); - void cancel_clicked(); - void apply_clicked(); - void ok_clicked(); - - typedef std::map<URI, Record> Records; - Records _records; - - SPtr<const Client::ObjectModel> _model; - ComboColumns _combo_columns; - Glib::RefPtr<Gtk::ListStore> _key_store; - sigc::connection _property_connection; - sigc::connection _property_removed_connection; - Gtk::VBox* _vbox; - Gtk::ScrolledWindow* _scrolledwindow; - Gtk::Table* _table; - Gtk::ComboBox* _key_combo; - LV2_URID _value_type; - Gtk::Bin* _value_bin; - Gtk::Button* _add_button; - Gtk::Button* _cancel_button; - Gtk::Button* _apply_button; - Gtk::Button* _ok_button; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_PROPERTIES_WINDOW_HPP diff --git a/src/gui/RDFS.cpp b/src/gui/RDFS.cpp deleted file mode 100644 index 71b3441a..00000000 --- a/src/gui/RDFS.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Forge.hpp" -#include "ingen/Log.hpp" -#include "ingen/Resource.hpp" -#include "ingen/World.hpp" -#include "ingen/client/ObjectModel.hpp" -#include "lilv/lilv.h" - -#include "RDFS.hpp" - -namespace Ingen { -namespace GUI { -namespace RDFS { - -std::string -label(World* world, const LilvNode* node) -{ - LilvNode* rdfs_label = lilv_new_uri( - world->lilv_world(), LILV_NS_RDFS "label"); - LilvNodes* labels = lilv_world_find_nodes( - world->lilv_world(), node, rdfs_label, nullptr); - - const LilvNode* first = lilv_nodes_get_first(labels); - std::string label = first ? lilv_node_as_string(first) : ""; - - lilv_nodes_free(labels); - lilv_node_free(rdfs_label); - return label; -} - -std::string -comment(World* world, const LilvNode* node) -{ - LilvNode* rdfs_comment = lilv_new_uri( - world->lilv_world(), LILV_NS_RDFS "comment"); - LilvNodes* comments = lilv_world_find_nodes( - world->lilv_world(), node, rdfs_comment, nullptr); - - const LilvNode* first = lilv_nodes_get_first(comments); - std::string comment = first ? lilv_node_as_string(first) : ""; - - lilv_nodes_free(comments); - lilv_node_free(rdfs_comment); - return comment; -} - -static void -closure(World* world, const LilvNode* pred, URISet& types, bool super) -{ - unsigned added = 0; - do { - added = 0; - URISet klasses; - for (const auto& t : types) { - LilvNode* type = lilv_new_uri(world->lilv_world(), t.c_str()); - LilvNodes* matches = (super) - ? lilv_world_find_nodes( - world->lilv_world(), type, pred, nullptr) - : lilv_world_find_nodes( - world->lilv_world(), nullptr, pred, type); - LILV_FOREACH(nodes, m, matches) { - const LilvNode* klass_node = lilv_nodes_get(matches, m); - if (lilv_node_is_uri(klass_node)) { - URI klass(lilv_node_as_uri(klass_node)); - if (!types.count(klass)) { - ++added; - klasses.insert(klass); - } - } - } - lilv_nodes_free(matches); - lilv_node_free(type); - } - types.insert(klasses.begin(), klasses.end()); - } while (added > 0); -} - -void -classes(World* world, URISet& types, bool super) -{ - LilvNode* rdfs_subClassOf = lilv_new_uri( - world->lilv_world(), LILV_NS_RDFS "subClassOf"); - - closure(world, rdfs_subClassOf, types, super); - - lilv_node_free(rdfs_subClassOf); -} - -void -datatypes(World* world, URISet& types, bool super) -{ - LilvNode* owl_onDatatype = lilv_new_uri( - world->lilv_world(), LILV_NS_OWL "onDatatype"); - - closure(world, owl_onDatatype, types, super); - - lilv_node_free(owl_onDatatype); -} - -URISet -types(World* world, SPtr<const Client::ObjectModel> model) -{ - typedef Properties::const_iterator PropIter; - typedef std::pair<PropIter, PropIter> PropRange; - - // Start with every rdf:type - URISet types; - types.insert(URI(LILV_NS_RDFS "Resource")); - PropRange range = model->properties().equal_range(world->uris().rdf_type); - for (auto t = range.first; t != range.second; ++t) { - if (t->second.type() == world->forge().URI || - t->second.type() == world->forge().URID) { - const URI type(world->forge().str(t->second, false)); - types.insert(type); - if (world->uris().ingen_Graph == type) { - // Add lv2:Plugin as a type for graphs so plugin properties show up - types.insert(world->uris().lv2_Plugin); - } - } else { - world->log().error(fmt("<%1%> has non-URI type\n") % model->uri()); - } - } - - // Add every superclass of every type, recursively - RDFS::classes(world, types, true); - - return types; -} - -URISet -properties(World* world, SPtr<const Client::ObjectModel> model) -{ - URISet properties; - URISet types = RDFS::types(world, model); - - LilvNode* rdf_type = lilv_new_uri(world->lilv_world(), - LILV_NS_RDF "type"); - LilvNode* rdf_Property = lilv_new_uri(world->lilv_world(), - LILV_NS_RDF "Property"); - LilvNode* rdfs_domain = lilv_new_uri(world->lilv_world(), - LILV_NS_RDFS "domain"); - - LilvNodes* props = lilv_world_find_nodes( - world->lilv_world(), nullptr, rdf_type, rdf_Property); - LILV_FOREACH(nodes, p, props) { - const LilvNode* prop = lilv_nodes_get(props, p); - if (lilv_node_is_uri(prop)) { - LilvNodes* domains = lilv_world_find_nodes( - world->lilv_world(), prop, rdfs_domain, nullptr); - unsigned n_matching_domains = 0; - LILV_FOREACH(nodes, d, domains) { - const LilvNode* domain_node = lilv_nodes_get(domains, d); - if (!lilv_node_is_uri(domain_node)) { - // TODO: Blank node domains (e.g. unions) - continue; - } - - const URI domain(lilv_node_as_uri(domain_node)); - if (types.count(domain)) { - ++n_matching_domains; - } - } - - if (lilv_nodes_size(domains) == 0 || ( - n_matching_domains > 0 && - n_matching_domains == lilv_nodes_size(domains))) { - properties.insert(URI(lilv_node_as_uri(prop))); - } - - lilv_nodes_free(domains); - } - } - - lilv_node_free(rdfs_domain); - lilv_node_free(rdf_Property); - lilv_node_free(rdf_type); - - return properties; -} - -Objects -instances(World* world, const URISet& types) -{ - LilvNode* rdf_type = lilv_new_uri( - world->lilv_world(), LILV_NS_RDF "type"); - - Objects result; - for (const auto& t : types) { - LilvNode* type = lilv_new_uri(world->lilv_world(), t.c_str()); - LilvNodes* objects = lilv_world_find_nodes( - world->lilv_world(), nullptr, rdf_type, type); - LILV_FOREACH(nodes, o, objects) { - const LilvNode* object = lilv_nodes_get(objects, o); - if (!lilv_node_is_uri(object)) { - continue; - } - const std::string label = RDFS::label(world, object); - result.emplace(label, URI(lilv_node_as_string(object))); - } - lilv_node_free(type); - } - - lilv_node_free(rdf_type); - return result; -} - -URISet -range(World* world, const LilvNode* prop, bool recursive) -{ - LilvNode* rdfs_range = lilv_new_uri( - world->lilv_world(), LILV_NS_RDFS "range"); - - LilvNodes* nodes = lilv_world_find_nodes( - world->lilv_world(), prop, rdfs_range, nullptr); - - URISet ranges; - LILV_FOREACH(nodes, n, nodes) { - ranges.insert(URI(lilv_node_as_string(lilv_nodes_get(nodes, n)))); - } - - if (recursive) { - RDFS::classes(world, ranges, false); - } - - lilv_nodes_free(nodes); - lilv_node_free(rdfs_range); - return ranges; -} - -bool -is_a(World* world, const LilvNode* inst, const LilvNode* klass) -{ - LilvNode* rdf_type = lilv_new_uri(world->lilv_world(), LILV_NS_RDF "type"); - - const bool is_instance = lilv_world_ask( - world->lilv_world(), inst, rdf_type, klass); - - lilv_node_free(rdf_type); - return is_instance; -} - -} // namespace RDFS -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/RDFS.hpp b/src/gui/RDFS.hpp deleted file mode 100644 index f59bbdf5..00000000 --- a/src/gui/RDFS.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_RDF_HPP -#define INGEN_GUI_RDF_HPP - -#include <map> -#include <set> -#include <string> - -#include "ingen/types.hpp" -#include "lilv/lilv.h" - -namespace Ingen { - -class World; - -namespace Client { class ObjectModel; } - -namespace GUI { - -namespace RDFS { - -/** Set of URIs. */ -typedef std::set<URI> URISet; - -/** Label => Resource map. */ -typedef std::map<std::string, URI> Objects; - -/** Return the label of `node`. */ -std::string label(World* world, const LilvNode* node); - -/** Return the comment of `node`. */ -std::string comment(World* world, const LilvNode* node); - -/** Set `types` to its super/sub class closure. - * @param super If true, find all superclasses, otherwise all subclasses - */ -void classes(World* world, URISet& types, bool super); - -/** Set `types` to its super/sub datatype closure. - * @param super If true, find all supertypes, otherwise all subtypes. - */ -void datatypes(World* world, URISet& types, bool super); - -/** Get all instances of any class in `types`. */ -Objects instances(World* world, const URISet& types); - -/** Get all the types which `model` is an instance of. */ -URISet types(World* world, SPtr<const Client::ObjectModel> model); - -/** Get all the properties with domains appropriate for `model`. */ -URISet properties(World* world, SPtr<const Client::ObjectModel> model); - -/** Return the range (value types) of `prop`. - * @param recursive If true, include all subclasses. - */ -URISet range(World* world, const LilvNode* prop, bool recursive); - -/** Return true iff `inst` is-a `klass`. */ -bool is_a(World* world, const LilvNode* inst, const LilvNode* klass); - -} // namespace RDFS -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_RDF_HPP diff --git a/src/gui/RenameWindow.cpp b/src/gui/RenameWindow.cpp deleted file mode 100644 index c83143d9..00000000 --- a/src/gui/RenameWindow.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <string> - -#include "ingen/Forge.hpp" -#include "ingen/Interface.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/ObjectModel.hpp" -#include "lv2/lv2plug.in/ns/lv2core/lv2.h" - -#include "App.hpp" -#include "RenameWindow.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -RenameWindow::RenameWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml) - : Window(cobject) -{ - xml->get_widget("rename_symbol_entry", _symbol_entry); - xml->get_widget("rename_label_entry", _label_entry); - xml->get_widget("rename_message_label", _message_label); - xml->get_widget("rename_cancel_button", _cancel_button); - xml->get_widget("rename_ok_button", _ok_button); - - _symbol_entry->signal_changed().connect( - sigc::mem_fun(this, &RenameWindow::values_changed)); - _label_entry->signal_changed().connect( - sigc::mem_fun(this, &RenameWindow::values_changed)); - _cancel_button->signal_clicked().connect( - sigc::mem_fun(this, &RenameWindow::cancel_clicked)); - _ok_button->signal_clicked().connect( - sigc::mem_fun(this, &RenameWindow::ok_clicked)); - - _ok_button->property_sensitive() = false; -} - -/** Set the object this window is renaming. - * This function MUST be called before using this object in any way. - */ -void -RenameWindow::set_object(SPtr<const ObjectModel> object) -{ - _object = object; - _symbol_entry->set_text(object->path().symbol()); - const Atom& name_atom = object->get_property(_app->uris().lv2_name); - _label_entry->set_text( - (name_atom.type() == _app->forge().String) ? name_atom.ptr<char>() : ""); -} - -void -RenameWindow::present(SPtr<const ObjectModel> object) -{ - set_object(object); - _symbol_entry->grab_focus(); - Gtk::Window::present(); -} - -void -RenameWindow::values_changed() -{ - const std::string& symbol = _symbol_entry->get_text(); - if (!Raul::Symbol::is_valid(symbol)) { - _message_label->set_text("Invalid symbol"); - _ok_button->property_sensitive() = false; - } else if (_object->symbol() != symbol && - _app->store()->object( - _object->parent()->path().child(Raul::Symbol(symbol)))) { - _message_label->set_text("An object already exists with that path"); - _ok_button->property_sensitive() = false; - } else { - _message_label->set_text(""); - _ok_button->property_sensitive() = true; - } -} - -void -RenameWindow::cancel_clicked() -{ - _symbol_entry->set_text(""); - hide(); -} - -/** Rename the object. - * - * It shouldn't be possible for this to be called with an invalid name set - * (since the Rename button should be deactivated). This is just shinification - * though - the engine will handle invalid names gracefully. - */ -void -RenameWindow::ok_clicked() -{ - const URIs& uris = _app->uris(); - const std::string& symbol_str = _symbol_entry->get_text(); - const std::string& label = _label_entry->get_text(); - Raul::Path path = _object->path(); - const Atom& name_atom = _object->get_property(uris.lv2_name); - - if (!label.empty() && (name_atom.type() != uris.forge.String || - label != name_atom.ptr<char>())) { - _app->set_property(path_to_uri(path), - uris.lv2_name, - _app->forge().alloc(label)); - } - - if (Raul::Symbol::is_valid(symbol_str)) { - const Raul::Symbol symbol(symbol_str); - if (symbol != _object->symbol()) { - path = _object->path().parent().child(symbol); - _app->interface()->move(_object->path(), path); - } - } - - hide(); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/RenameWindow.hpp b/src/gui/RenameWindow.hpp deleted file mode 100644 index 36264879..00000000 --- a/src/gui/RenameWindow.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_RENAMEWINDOW_HPP -#define INGEN_GUI_RENAMEWINDOW_HPP - -#include <gtkmm/builder.h> -#include <gtkmm/button.h> -#include <gtkmm/entry.h> -#include <gtkmm/label.h> - -#include "ingen/client/ObjectModel.hpp" -#include "ingen/types.hpp" - -#include "Window.hpp" - -namespace Ingen { -namespace GUI { - -/** Rename window. Handles renaming of any (Ingen) object. - * - * \ingroup GUI - */ -class RenameWindow : public Window -{ -public: - RenameWindow(BaseObjectType* cobject, - const Glib::RefPtr<Gtk::Builder>& xml); - - void present(SPtr<const Client::ObjectModel> object); - -private: - void set_object(SPtr<const Client::ObjectModel> object); - - void values_changed(); - void cancel_clicked(); - void ok_clicked(); - - SPtr<const Client::ObjectModel> _object; - - Gtk::Entry* _symbol_entry; - Gtk::Entry* _label_entry; - Gtk::Label* _message_label; - Gtk::Button* _cancel_button; - Gtk::Button* _ok_button; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_RENAMEWINDOW_HPP diff --git a/src/gui/Style.cpp b/src/gui/Style.cpp deleted file mode 100644 index 81e6fb6c..00000000 --- a/src/gui/Style.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <cstdlib> -#include <fstream> -#include <map> -#include <string> - -#include "ganv/Port.hpp" -#include "ingen/Log.hpp" -#include "ingen/Parser.hpp" -#include "ingen/client/PluginModel.hpp" -#include "ingen/client/PortModel.hpp" - -#include "App.hpp" -#include "Style.hpp" -#include "Port.hpp" - -namespace Ingen { -namespace GUI { - -using namespace Ingen::Client; - -Style::Style(App& app) - // Colours from the Tango palette with modified V - : _app(app) -#ifdef INGEN_USE_LIGHT_THEME - , _audio_port_color(0xC8E6ABFF) // Green - , _control_port_color(0xAAC0E6FF) // Blue - , _cv_port_color(0xACE6E0FF) // Teal (between audio and control) - , _event_port_color(0xE6ABABFF) // Red - , _string_port_color(0xD8ABE6FF) // Plum -#else - , _audio_port_color(0x4A8A0EFF) // Green - , _control_port_color(0x244678FF) // Blue - , _cv_port_color(0x248780FF) // Teal (between audio and control) - , _event_port_color(0x960909FF) // Red - , _string_port_color(0x5C3566FF) // Plum -#endif -{ -} - -/** Loads settings from the rc file. Passing no parameter will load from - * the default location. - */ -void -Style::load_settings(std::string filename) -{ - /* ... */ -} - -/** Saves settings to rc file. Passing no parameter will save to the - * default location. - */ -void -Style::save_settings(std::string filename) -{ - /* ... */ -} - -/** Applies the current loaded settings to whichever parts of the app - * need updating. - */ -void -Style::apply_settings() -{ - /* ... */ -} - -uint32_t -Style::get_port_color(const Client::PortModel* p) -{ - const URIs& uris = _app.uris(); - if (p->is_a(uris.lv2_AudioPort)) { - return _audio_port_color; - } else if (p->is_a(uris.lv2_ControlPort)) { - return _control_port_color; - } else if (p->is_a(uris.lv2_CVPort)) { - return _cv_port_color; - } else if (p->supports(uris.atom_String)) { - return _string_port_color; - } else if (_app.can_control(p)) { - return _control_port_color; - } else if (p->is_a(uris.atom_AtomPort)) { - return _event_port_color; - } - - return 0x555555FF; -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/Style.hpp b/src/gui/Style.hpp deleted file mode 100644 index 8e628a3d..00000000 --- a/src/gui/Style.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_STYLE_HPP -#define INGEN_GUI_STYLE_HPP - -#include <cstdint> -#include <string> - -namespace Ingen { namespace Client { class PortModel; } } - -namespace Ingen { -namespace GUI { - -class App; -class Port; - -class Style -{ -public: - explicit Style(App& app); - - void load_settings(std::string filename = ""); - void save_settings(std::string filename = ""); - - void apply_settings(); - - uint32_t get_port_color(const Client::PortModel* p); - -private: - App& _app; - - uint32_t _audio_port_color; - uint32_t _control_port_color; - uint32_t _cv_port_color; - uint32_t _event_port_color; - uint32_t _string_port_color; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_STYLE_HPP diff --git a/src/gui/SubgraphModule.cpp b/src/gui/SubgraphModule.cpp deleted file mode 100644 index 6bbcf534..00000000 --- a/src/gui/SubgraphModule.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <utility> - -#include "ingen/Interface.hpp" -#include "ingen/client/GraphModel.hpp" - -#include "App.hpp" -#include "NodeModule.hpp" -#include "GraphCanvas.hpp" -#include "GraphWindow.hpp" -#include "Port.hpp" -#include "SubgraphModule.hpp" -#include "WindowFactory.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -SubgraphModule::SubgraphModule(GraphCanvas& canvas, - SPtr<const GraphModel> graph) - : NodeModule(canvas, graph) - , _graph(graph) -{ - assert(graph); -} - -bool -SubgraphModule::on_double_click(GdkEventButton* event) -{ - assert(_graph); - - SPtr<GraphModel> parent = dynamic_ptr_cast<GraphModel>(_graph->parent()); - - GraphWindow* const preferred = ( (parent && (event->state & GDK_SHIFT_MASK)) - ? nullptr - : app().window_factory()->graph_window(parent) ); - - app().window_factory()->present_graph(_graph, preferred); - return true; -} - -void -SubgraphModule::store_location(double ax, double ay) -{ - const URIs& uris = app().uris(); - - const Atom x(app().forge().make(static_cast<float>(ax))); - const Atom y(app().forge().make(static_cast<float>(ay))); - - if (x != _block->get_property(uris.ingen_canvasX) || - y != _block->get_property(uris.ingen_canvasY)) - { - app().interface()->put(_graph->uri(), - {{uris.ingen_canvasX, x}, - {uris.ingen_canvasY, y}}, - Resource::Graph::EXTERNAL); - } -} - -/** Browse to this graph in current (parent's) window - * (unless an existing window is displaying it) - */ -void -SubgraphModule::browse_to_graph() -{ - assert(_graph->parent()); - - SPtr<GraphModel> parent = dynamic_ptr_cast<GraphModel>(_graph->parent()); - - GraphWindow* const preferred = (parent) - ? app().window_factory()->graph_window(parent) - : nullptr; - - app().window_factory()->present_graph(_graph, preferred); -} - -void -SubgraphModule::menu_remove() -{ - app().interface()->del(_graph->uri()); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/SubgraphModule.hpp b/src/gui/SubgraphModule.hpp deleted file mode 100644 index 1b8df2fa..00000000 --- a/src/gui/SubgraphModule.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_SUBGRAPHMODULE_HPP -#define INGEN_GUI_SUBGRAPHMODULE_HPP - -#include "ingen/types.hpp" - -#include "NodeModule.hpp" -#include "GraphPortModule.hpp" - -namespace Ingen { namespace Client { -class GraphModel; -class GraphWindow; -class PortModel; -} } - -namespace Ingen { -namespace GUI { - -class GraphCanvas; - -/** A module to represent a subgraph - * - * \ingroup GUI - */ -class SubgraphModule : public NodeModule -{ -public: - SubgraphModule(GraphCanvas& canvas, - SPtr<const Client::GraphModel> graph); - - virtual ~SubgraphModule() {} - - bool on_double_click(GdkEventButton* event); - - void store_location(double ax, double ay); - - void browse_to_graph(); - void menu_remove(); - - SPtr<const Client::GraphModel> graph() const { return _graph; } - -protected: - SPtr<const Client::GraphModel> _graph; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_SUBGRAPHMODULE_HPP diff --git a/src/gui/ThreadedLoader.cpp b/src/gui/ThreadedLoader.cpp deleted file mode 100644 index 7a80fa6e..00000000 --- a/src/gui/ThreadedLoader.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <string> - -#include "ingen/Log.hpp" -#include "ingen/Module.hpp" -#include "ingen/World.hpp" -#include "ingen/client/GraphModel.hpp" - -#include "App.hpp" -#include "ThreadedLoader.hpp" - -using boost::optional; - -namespace Ingen { -namespace GUI { - -ThreadedLoader::ThreadedLoader(App& app, SPtr<Interface> engine) - : _app(app) - , _sem(0) - , _engine(std::move(engine)) - , _exit_flag(false) - , _thread(&ThreadedLoader::run, this) -{ - if (!parser()) { - app.log().warn("Parser unavailable, graph loading disabled\n"); - } -} - -ThreadedLoader::~ThreadedLoader() -{ - _exit_flag = true; - _sem.post(); - if (_thread.joinable()) { - _thread.join(); - } -} - -SPtr<Parser> -ThreadedLoader::parser() -{ - return _app.world()->parser(); -} - -void -ThreadedLoader::run() -{ - while (_sem.wait() && !_exit_flag) { - std::lock_guard<std::mutex> lock(_mutex); - while (!_events.empty()) { - _events.front()(); - _events.pop_front(); - } - } -} - -void -ThreadedLoader::load_graph(bool merge, - const FilePath& file_path, - optional<Raul::Path> engine_parent, - optional<Raul::Symbol> engine_symbol, - optional<Properties> engine_data) -{ - std::lock_guard<std::mutex> lock(_mutex); - - Glib::ustring engine_base = ""; - if (engine_parent) { - if (merge) { - engine_base = engine_parent.get(); - } else { - engine_base = engine_parent.get().base(); - } - } - - _events.push_back(sigc::hide_return( - sigc::bind(sigc::mem_fun(this, &ThreadedLoader::load_graph_event), - file_path, - engine_parent, - engine_symbol, - engine_data))); - - _sem.post(); -} - -void -ThreadedLoader::load_graph_event(const FilePath& file_path, - optional<Raul::Path> engine_parent, - optional<Raul::Symbol> engine_symbol, - optional<Properties> engine_data) -{ - std::lock_guard<std::mutex> lock(_app.world()->rdf_mutex()); - - _app.world()->parser()->parse_file(_app.world(), - _app.world()->interface().get(), - file_path, - engine_parent, - engine_symbol, - engine_data); -} - -void -ThreadedLoader::save_graph(SPtr<const Client::GraphModel> model, const URI& uri) -{ - std::lock_guard<std::mutex> lock(_mutex); - - _events.push_back(sigc::hide_return( - sigc::bind(sigc::mem_fun(this, &ThreadedLoader::save_graph_event), - model, - uri))); - - _sem.post(); -} - -void -ThreadedLoader::save_graph_event(SPtr<const Client::GraphModel> model, - const URI& uri) -{ - assert(uri.scheme() == "file"); - if (_app.serialiser()) { - std::lock_guard<std::mutex> lock(_app.world()->rdf_mutex()); - - if (uri.string().find(".ingen") != std::string::npos) { - _app.serialiser()->write_bundle(model, uri); - } else { - _app.serialiser()->start_to_file(model->path(), std::string(uri.path())); - _app.serialiser()->serialise(model); - _app.serialiser()->finish(); - } - } -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/ThreadedLoader.hpp b/src/gui/ThreadedLoader.hpp deleted file mode 100644 index 79ef6466..00000000 --- a/src/gui/ThreadedLoader.hpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_THREADEDLOADER_HPP -#define INGEN_GUI_THREADEDLOADER_HPP - -#include <thread> - -#include <cassert> -#include <list> -#include <mutex> -#include <string> - -#include <boost/optional.hpp> - -#include "ingen/FilePath.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Parser.hpp" -#include "ingen/Serialiser.hpp" -#include "raul/Semaphore.hpp" - -namespace Ingen { - -class URI; - -namespace GUI { - -/** Thread for loading graph files. - * - * This is a seperate thread so it can send all the loading message without - * blocking everything else, so the app can respond to the incoming events - * caused as a result of the graph loading, while the graph loads. - * - * Implemented as a slave with a list of closures (events) which processes - * all events in the (mutex protected) list each time it's whipped. - * - * \ingroup GUI - */ -class ThreadedLoader -{ -public: - ThreadedLoader(App& app, - SPtr<Interface> engine); - - ~ThreadedLoader(); - - void load_graph(bool merge, - const FilePath& file_path, - boost::optional<Raul::Path> engine_parent, - boost::optional<Raul::Symbol> engine_symbol, - boost::optional<Properties> engine_data); - - void save_graph(SPtr<const Client::GraphModel> model, const URI& uri); - - SPtr<Parser> parser(); - -private: - void load_graph_event(const FilePath& file_path, - boost::optional<Raul::Path> engine_parent, - boost::optional<Raul::Symbol> engine_symbol, - boost::optional<Properties> engine_data); - - void save_graph_event(SPtr<const Client::GraphModel> model, - const URI& filename); - - /** Returns nothing and takes no parameters (because they have all been bound) */ - typedef sigc::slot<void> Closure; - - void run(); - - App& _app; - Raul::Semaphore _sem; - SPtr<Interface> _engine; - std::mutex _mutex; - std::list<Closure> _events; - bool _exit_flag; - std::thread _thread; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_LOADERRTHREAD_HPP diff --git a/src/gui/URIEntry.cpp b/src/gui/URIEntry.cpp deleted file mode 100644 index 0b81afd7..00000000 --- a/src/gui/URIEntry.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <unordered_map> - -#include "App.hpp" -#include "RDFS.hpp" -#include "URIEntry.hpp" - -namespace Ingen { -namespace GUI { - -URIEntry::URIEntry(App* app, std::set<URI> types, const std::string& value) - : Gtk::HBox(false, 4) - , _app(app) - , _types(std::move(types)) - , _menu_button(Gtk::manage(new Gtk::Button("≡"))) - , _entry(Gtk::manage(new Gtk::Entry())) -{ - pack_start(*_entry, true, true); - pack_start(*_menu_button, false, true); - - _entry->set_text(value); - - _menu_button->signal_event().connect( - sigc::mem_fun(this, &URIEntry::menu_button_event)); -} - -Gtk::Menu* -URIEntry::build_value_menu() -{ - World* world = _app->world(); - LilvWorld* lworld = world->lilv_world(); - Gtk::Menu* menu = new Gtk::Menu(); - - LilvNode* owl_onDatatype = lilv_new_uri(lworld, LILV_NS_OWL "onDatatype"); - LilvNode* rdf_type = lilv_new_uri(lworld, LILV_NS_RDF "type"); - LilvNode* rdfs_Class = lilv_new_uri(lworld, LILV_NS_RDFS "Class"); - LilvNode* rdfs_Datatype = lilv_new_uri(lworld, LILV_NS_RDFS "Datatype"); - LilvNode* rdfs_subClassOf = lilv_new_uri(lworld, LILV_NS_RDFS "subClassOf"); - - RDFS::Objects values = RDFS::instances(world, _types); - - for (const auto& v : values) { - const LilvNode* inst = lilv_new_uri(lworld, v.second.c_str()); - std::string label = v.first; - if (label.empty()) { - // No label, show raw URI - label = lilv_node_as_string(inst); - } - - if (lilv_world_ask(world->lilv_world(), inst, rdf_type, rdfs_Class) || - lilv_world_ask(world->lilv_world(), inst, rdf_type, rdfs_Datatype)) { - // This value is a class or datatype... - if (!lilv_world_ask(lworld, inst, rdfs_subClassOf, nullptr) && - !lilv_world_ask(lworld, inst, owl_onDatatype, nullptr)) { - // ... which is not a subtype of another, add menu - add_class_menu_item(menu, inst, label); - } - } else { - // Value is not a class, add item - menu->items().push_back( - Gtk::Menu_Helpers::MenuElem( - std::string("_") + label, - sigc::bind(sigc::mem_fun(this, &URIEntry::uri_chosen), - std::string(lilv_node_as_uri(inst))))); - _app->set_tooltip(&menu->items().back(), inst); - } - } - - lilv_node_free(owl_onDatatype); - lilv_node_free(rdf_type); - lilv_node_free(rdfs_Class); - lilv_node_free(rdfs_Datatype); - lilv_node_free(rdfs_subClassOf); - - return menu; -} - -Gtk::Menu* -URIEntry::build_subclass_menu(const LilvNode* klass) -{ - World* world = _app->world(); - LilvWorld* lworld = world->lilv_world(); - - LilvNode* owl_onDatatype = lilv_new_uri(lworld, LILV_NS_OWL "onDatatype"); - LilvNode* rdfs_subClassOf = lilv_new_uri(lworld, LILV_NS_RDFS "subClassOf"); - - LilvNodes* subclasses = lilv_world_find_nodes( - lworld, nullptr, rdfs_subClassOf, klass); - LilvNodes* subtypes = lilv_world_find_nodes( - lworld, nullptr, owl_onDatatype, klass); - - if (lilv_nodes_size(subclasses) == 0 && lilv_nodes_size(subtypes) == 0) { - return nullptr; - } - - Gtk::Menu* menu = new Gtk::Menu(); - - // Add "header" item for choosing this class itself - add_leaf_menu_item(menu, klass, RDFS::label(world, klass)); - menu->items().push_back(Gtk::Menu_Helpers::SeparatorElem()); - - // Put subclasses/types in a map keyed by label (to sort menu) - std::map<std::string, const LilvNode*> entries; - LILV_FOREACH(nodes, s, subclasses) { - const LilvNode* node = lilv_nodes_get(subclasses, s); - entries.emplace(RDFS::label(world, node), node); - } - LILV_FOREACH(nodes, s, subtypes) { - const LilvNode* node = lilv_nodes_get(subtypes, s); - entries.emplace(RDFS::label(world, node), node); - } - - // Add an item (possibly with a submenu) for each subclass/type - for (const auto& e : entries) { - add_class_menu_item(menu, e.second, e.first); - } - - lilv_nodes_free(subtypes); - lilv_nodes_free(subclasses); - lilv_node_free(rdfs_subClassOf); - lilv_node_free(owl_onDatatype); - - return menu; -} - -void -URIEntry::add_leaf_menu_item(Gtk::Menu* menu, - const LilvNode* node, - const std::string& label) -{ - menu->items().push_back( - Gtk::Menu_Helpers::MenuElem( - std::string("_") + label, - sigc::bind(sigc::mem_fun(this, &URIEntry::uri_chosen), - std::string(lilv_node_as_uri(node))))); - - _app->set_tooltip(&menu->items().back(), node); -} - -void -URIEntry::add_class_menu_item(Gtk::Menu* menu, - const LilvNode* klass, - const std::string& label) -{ - Gtk::Menu* submenu = build_subclass_menu(klass); - - if (submenu) { - menu->items().push_back(Gtk::Menu_Helpers::MenuElem(label)); - menu->items().back().set_submenu(*Gtk::manage(submenu)); - } else { - add_leaf_menu_item(menu, klass, label); - } - - _app->set_tooltip(&menu->items().back(), klass); -} - -void -URIEntry::uri_chosen(const std::string& uri) -{ - _entry->set_text(uri); -} - -bool -URIEntry::menu_button_event(GdkEvent* ev) -{ - if (ev->type != GDK_BUTTON_PRESS) { - return false; - } - - Gtk::Menu* menu = Gtk::manage(build_value_menu()); - menu->popup(ev->button.button, ev->button.time); - - return true; -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/URIEntry.hpp b/src/gui/URIEntry.hpp deleted file mode 100644 index 2f55a3d9..00000000 --- a/src/gui/URIEntry.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_URI_ENTRY_HPP -#define INGEN_GUI_URI_ENTRY_HPP - -#include <gtkmm/box.h> -#include <gtkmm/button.h> -#include <gtkmm/entry.h> -#include <gtkmm/menu.h> - -#include "lilv/lilv.h" - -namespace Ingen { -namespace GUI { - -class App; - -class URIEntry : public Gtk::HBox { -public: - /** Create a widget for entering URIs. - * - * If `types` is given, then a menu button will be shown which pops up a - * enu for easily choosing known values with valid types. - */ - URIEntry(App* app, std::set<URI> types, const std::string& value); - - std::string get_text() { return _entry->get_text(); } - Glib::SignalProxy0<void> signal_changed() { return _entry->signal_changed(); } - -private: - Gtk::Menu* build_value_menu(); - Gtk::Menu* build_subclass_menu(const LilvNode* klass); - - void add_leaf_menu_item(Gtk::Menu* menu, - const LilvNode* node, - const std::string& label); - - void add_class_menu_item(Gtk::Menu* menu, - const LilvNode* klass, - const std::string& label); - - void uri_chosen(const std::string& uri); - bool menu_button_event(GdkEvent* ev); - - App* _app; - const std::set<URI> _types; - Gtk::Button* _menu_button; - Gtk::Entry* _entry; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_URI_ENTRY_HPP diff --git a/src/gui/WidgetFactory.cpp b/src/gui/WidgetFactory.cpp deleted file mode 100644 index afb6a07f..00000000 --- a/src/gui/WidgetFactory.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <fstream> -#include <string> - -#include "ingen/Log.hpp" -#include "ingen/runtime_paths.hpp" - -#include "WidgetFactory.hpp" - -namespace Ingen { -namespace GUI { - -Glib::ustring WidgetFactory::ui_filename = ""; - -inline static bool -is_readable(const std::string& filename) -{ - std::ifstream fs(filename.c_str()); - const bool fail = fs.fail(); - fs.close(); - return !fail; -} - -void -WidgetFactory::find_ui_file() -{ - // Try file in bundle (directory where executable resides) - ui_filename = Ingen::bundle_file_path("ingen_gui.ui"); - if (is_readable(ui_filename)) { - return; - } - - // Try ENGINE_UI_PATH from the environment - const char* const env_path = getenv("INGEN_UI_PATH"); - if (env_path && is_readable(env_path)) { - ui_filename = env_path; - return; - } - - // Try the default system installed path - ui_filename = Ingen::data_file_path("ingen_gui.ui"); - if (is_readable(ui_filename)) { - return; - } - - throw std::runtime_error((fmt("Unable to find ingen_gui.ui in %1%\n") - % INGEN_DATA_DIR).str()); -} - -Glib::RefPtr<Gtk::Builder> -WidgetFactory::create(const std::string& toplevel_widget) -{ - if (ui_filename.empty()) { - find_ui_file(); - } - - if (toplevel_widget.empty()) { - return Gtk::Builder::create_from_file(ui_filename); - } else { - return Gtk::Builder::create_from_file(ui_filename, toplevel_widget.c_str()); - } -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/WidgetFactory.hpp b/src/gui/WidgetFactory.hpp deleted file mode 100644 index 92f4dffe..00000000 --- a/src/gui/WidgetFactory.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_GLADEFACTORY_HPP -#define INGEN_GUI_GLADEFACTORY_HPP - -#include <string> - -#include <glibmm.h> -#include <gtkmm/builder.h> - -namespace Ingen { -namespace GUI { - -/** Loads widgets from an XML description. - * Purely static. - * - * \ingroup GUI - */ -class WidgetFactory { -public: - static Glib::RefPtr<Gtk::Builder> - create(const std::string& toplevel_widget=""); - - template<typename T> - static void get_widget(const Glib::ustring& name, T*& widget) { - Glib::RefPtr<Gtk::Builder> xml = create(name); - xml->get_widget(name, widget); - } - - template<typename T> - static void get_widget_derived(const Glib::ustring& name, T*& widget) { - Glib::RefPtr<Gtk::Builder> xml = create(name); - xml->get_widget_derived(name, widget); - } - -private: - static void find_ui_file(); - static Glib::ustring ui_filename; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_GLADEFACTORY_HPP diff --git a/src/gui/Window.hpp b/src/gui/Window.hpp deleted file mode 100644 index 2a5c9843..00000000 --- a/src/gui/Window.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_WINDOW_HPP -#define INGEN_GUI_WINDOW_HPP - -#include <gtkmm/dialog.h> -#include <gtkmm/window.h> - -namespace Ingen { - -namespace GUI { - -class App; - -/** Ingen GUI Window - * \ingroup GUI - */ -class Window : public Gtk::Window -{ -public: - Window() : Gtk::Window(), _app(nullptr) {} - explicit Window(BaseObjectType* cobject) : Gtk::Window(cobject), _app(nullptr) {} - - virtual void init_window(App& app) { _app = &app; } - - bool on_key_press_event(GdkEventKey* event) { - if (event->keyval == GDK_w && event->state & GDK_CONTROL_MASK) { - hide(); - return true; - } - return Gtk::Window::on_key_press_event(event); - } - - static bool key_press_handler(Gtk::Window* win, GdkEventKey* event); - - App* _app; -}; - -/** Ingen GUI Dialog - * \ingroup GUI - */ -class Dialog : public Gtk::Dialog -{ -public: - Dialog() : Gtk::Dialog(), _app(nullptr) {} - explicit Dialog(BaseObjectType* cobject) : Gtk::Dialog(cobject), _app(nullptr) {} - - virtual void init_dialog(App& app) { _app = &app; } - - bool on_key_press_event(GdkEventKey* event) { - if (event->keyval == GDK_w && event->state & GDK_CONTROL_MASK) { - hide(); - return true; - } - return Gtk::Window::on_key_press_event(event); - } - - App* _app; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_WINDOW_HPP diff --git a/src/gui/WindowFactory.cpp b/src/gui/WindowFactory.cpp deleted file mode 100644 index 5dbdbe98..00000000 --- a/src/gui/WindowFactory.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <stdexcept> -#include <string> - -#include "ingen/Log.hpp" -#include "ingen/client/GraphModel.hpp" - -#include "App.hpp" -#include "LoadGraphWindow.hpp" -#include "LoadPluginWindow.hpp" -#include "NewSubgraphWindow.hpp" -#include "GraphView.hpp" -#include "GraphWindow.hpp" -#include "PropertiesWindow.hpp" -#include "RenameWindow.hpp" -#include "WidgetFactory.hpp" -#include "WindowFactory.hpp" - -namespace Ingen { - -using namespace Client; - -namespace GUI { - -WindowFactory::WindowFactory(App& app) - : _app(app) - , _main_box(nullptr) - , _load_plugin_win(nullptr) - , _load_graph_win(nullptr) - , _new_subgraph_win(nullptr) - , _properties_win(nullptr) -{ - WidgetFactory::get_widget_derived("load_plugin_win", _load_plugin_win); - WidgetFactory::get_widget_derived("load_graph_win", _load_graph_win); - WidgetFactory::get_widget_derived("new_subgraph_win", _new_subgraph_win); - WidgetFactory::get_widget_derived("properties_win", _properties_win); - WidgetFactory::get_widget_derived("rename_win", _rename_win); - - if (!(_load_plugin_win && _load_graph_win && _new_subgraph_win - && _properties_win && _rename_win)) { - throw std::runtime_error("failed to load window widgets\n"); - } - - _load_plugin_win->init_window(app); - _load_graph_win->init(app); - _new_subgraph_win->init_window(app); - _properties_win->init_window(app); - _rename_win->init_window(app); -} - -WindowFactory::~WindowFactory() -{ - for (const auto& w : _graph_windows) { - delete w.second; - } -} - -void -WindowFactory::clear() -{ - for (const auto& w : _graph_windows) { - delete w.second; - } - - _graph_windows.clear(); -} - -/** Returns the number of Graph windows currently visible. - */ -size_t -WindowFactory::num_open_graph_windows() -{ - size_t ret = 0; - for (const auto& w : _graph_windows) { - if (w.second->is_visible()) { - ++ret; - } - } - - return ret; -} - -GraphBox* -WindowFactory::graph_box(SPtr<const GraphModel> graph) -{ - GraphWindow* window = graph_window(graph); - if (window) { - return window->box(); - } else { - return _main_box; - } -} - -GraphWindow* -WindowFactory::graph_window(SPtr<const GraphModel> graph) -{ - if (!graph) { - return nullptr; - } - - auto w = _graph_windows.find(graph->path()); - - return (w == _graph_windows.end()) ? nullptr : w->second; -} - -GraphWindow* -WindowFactory::parent_graph_window(SPtr<const BlockModel> block) -{ - if (!block) { - return nullptr; - } - - return graph_window(dynamic_ptr_cast<GraphModel>(block->parent())); -} - -/** Present a GraphWindow for a Graph. - * - * If `preferred` is not NULL, it will be set to display `graph` if the graph - * does not already have a visible window, otherwise that window will be - * presented and `preferred` left unmodified. - */ -void -WindowFactory::present_graph(SPtr<const GraphModel> graph, - GraphWindow* preferred, - SPtr<GraphView> view) -{ - assert(!view || view->graph() == graph); - - auto w = _graph_windows.find(graph->path()); - - if (w != _graph_windows.end()) { - (*w).second->present(); - } else if (preferred) { - w = _graph_windows.find(preferred->graph()->path()); - assert((*w).second == preferred); - - preferred->box()->set_graph(graph, view); - _graph_windows.erase(w); - _graph_windows[graph->path()] = preferred; - preferred->present(); - - } else { - GraphWindow* win = new_graph_window(graph, view); - win->present(); - } -} - -GraphWindow* -WindowFactory::new_graph_window(SPtr<const GraphModel> graph, - SPtr<GraphView> view) -{ - assert(!view || view->graph() == graph); - - GraphWindow* win = nullptr; - WidgetFactory::get_widget_derived("graph_win", win); - if (!win) { - _app.log().error("Failed to load graph window widget\n"); - return nullptr; - } - - win->init_window(_app); - - win->box()->set_graph(graph, view); - _graph_windows[graph->path()] = win; - - win->signal_delete_event().connect( - sigc::bind<0>(sigc::mem_fun(this, &WindowFactory::remove_graph_window), - win)); - - return win; -} - -bool -WindowFactory::remove_graph_window(GraphWindow* win, GdkEventAny* ignored) -{ - if (_graph_windows.size() <= 1) { - return !_app.quit(win); - } - - auto w = _graph_windows.find(win->graph()->path()); - - assert((*w).second == win); - _graph_windows.erase(w); - - delete win; - - return false; -} - -void -WindowFactory::present_load_plugin(SPtr<const GraphModel> graph, - Properties data) -{ - _app.request_plugins_if_necessary(); - - auto w = _graph_windows.find(graph->path()); - - if (w != _graph_windows.end()) { - _load_plugin_win->set_transient_for(*w->second); - } - - _load_plugin_win->set_modal(false); - _load_plugin_win->set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG); - if (w->second) { - int width, height; - w->second->get_size(width, height); - _load_plugin_win->set_default_size(width - width / 8, height / 2); - } - _load_plugin_win->set_title( - std::string("Load Plugin - ") + graph->path() + " - Ingen"); - _load_plugin_win->present(graph, data); -} - -void -WindowFactory::present_load_graph(SPtr<const GraphModel> graph, - Properties data) -{ - auto w = _graph_windows.find(graph->path()); - - if (w != _graph_windows.end()) { - _load_graph_win->set_transient_for(*w->second); - } - - _load_graph_win->present(graph, true, data); -} - -void -WindowFactory::present_load_subgraph(SPtr<const GraphModel> graph, - Properties data) -{ - auto w = _graph_windows.find(graph->path()); - - if (w != _graph_windows.end()) { - _load_graph_win->set_transient_for(*w->second); - } - - _load_graph_win->present(graph, false, data); -} - -void -WindowFactory::present_new_subgraph(SPtr<const GraphModel> graph, - Properties data) -{ - auto w = _graph_windows.find(graph->path()); - - if (w != _graph_windows.end()) { - _new_subgraph_win->set_transient_for(*w->second); - } - - _new_subgraph_win->present(graph, data); -} - -void -WindowFactory::present_rename(SPtr<const ObjectModel> object) -{ - auto w = _graph_windows.find(object->path()); - if (w == _graph_windows.end()) { - w = _graph_windows.find(object->path().parent()); - } - - if (w != _graph_windows.end()) { - _rename_win->set_transient_for(*w->second); - } - - _rename_win->present(object); -} - -void -WindowFactory::present_properties(SPtr<const ObjectModel> object) -{ - auto w = _graph_windows.find(object->path()); - if (w == _graph_windows.end()) { - w = _graph_windows.find(object->path().parent()); - } - if (w == _graph_windows.end()) { - w = _graph_windows.find(object->path().parent().parent()); - } - - if (w != _graph_windows.end()) { - _properties_win->set_transient_for(*w->second); - } - - _properties_win->present(object); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/gui/WindowFactory.hpp b/src/gui/WindowFactory.hpp deleted file mode 100644 index ea8b909b..00000000 --- a/src/gui/WindowFactory.hpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_WINDOWFACTORY_HPP -#define INGEN_GUI_WINDOWFACTORY_HPP - -#include <map> - -#include "ingen/Node.hpp" -#include "ingen/types.hpp" - -namespace Ingen { - -namespace Client { -class BlockModel; -class ObjectModel; -class GraphModel; -} - -namespace GUI { - -class App; -class GraphBox; -class GraphView; -class GraphWindow; -class LoadGraphWindow; -class LoadPluginWindow; -class NewSubgraphWindow; -class PropertiesWindow; -class RenameWindow; - -/** Manager/Factory for all windows. - * - * This serves as a nice centralized spot for all window management issues, - * as well as an enumeration of all windows (the goal being to reduce that - * number as much as possible). - */ -class WindowFactory { -public: - explicit WindowFactory(App& app); - ~WindowFactory(); - - size_t num_open_graph_windows(); - - GraphBox* graph_box(SPtr<const Client::GraphModel> graph); - GraphWindow* graph_window(SPtr<const Client::GraphModel> graph); - GraphWindow* parent_graph_window(SPtr<const Client::BlockModel> block); - - void present_graph( - SPtr<const Client::GraphModel> graph, - GraphWindow* preferred = NULL, - SPtr<GraphView> view = SPtr<GraphView>()); - - void present_load_plugin(SPtr<const Client::GraphModel> graph, Properties data=Properties()); - void present_load_graph(SPtr<const Client::GraphModel> graph, Properties data=Properties()); - void present_load_subgraph(SPtr<const Client::GraphModel> graph, Properties data=Properties()); - void present_new_subgraph(SPtr<const Client::GraphModel> graph, Properties data=Properties()); - void present_rename(SPtr<const Client::ObjectModel> object); - void present_properties(SPtr<const Client::ObjectModel> object); - - bool remove_graph_window(GraphWindow* win, GdkEventAny* ignored = NULL); - - void set_main_box(GraphBox* box) { _main_box = box; } - - void clear(); - -private: - typedef std::map<Raul::Path, GraphWindow*> GraphWindowMap; - - GraphWindow* new_graph_window(SPtr<const Client::GraphModel> graph, - SPtr<GraphView> view); - - App& _app; - GraphBox* _main_box; - GraphWindowMap _graph_windows; - LoadPluginWindow* _load_plugin_win; - LoadGraphWindow* _load_graph_win; - NewSubgraphWindow* _new_subgraph_win; - PropertiesWindow* _properties_win; - RenameWindow* _rename_win; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_WINDOWFACTORY_HPP diff --git a/src/gui/ingen_gui.cpp b/src/gui/ingen_gui.cpp deleted file mode 100644 index 677296fd..00000000 --- a/src/gui/ingen_gui.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2018 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Configuration.hpp" -#include "ingen/Module.hpp" -#include "ingen/QueuedInterface.hpp" -#include "ingen/client/SigClientInterface.hpp" - -#include "App.hpp" - -namespace Ingen { -namespace GUI { - -struct GUIModule : public Module { - using SigClientInterface = Client::SigClientInterface; - - void load(World* world) { - URI uri(world->conf().option("connect").ptr<char>()); - if (!world->interface()) { - world->set_interface( - world->new_interface(URI(uri), make_client(world))); - } else if (!dynamic_ptr_cast<SigClientInterface>( - world->interface()->respondee())) { - world->interface()->set_respondee(make_client(world)); - } - - app = GUI::App::create(world); - } - - void run(World* world) { - app->run(); - } - - SPtr<Interface> make_client(World* const world) { - SPtr<SigClientInterface> sci(new SigClientInterface()); - return world->engine() ? sci : SPtr<Interface>(new QueuedInterface(sci)); - } - - SPtr<GUI::App> app; -}; - -} // namespace GUI -} // namespace Ingen - -extern "C" { - -Ingen::Module* -ingen_module_load() -{ - Glib::thread_init(); - return new Ingen::GUI::GUIModule(); -} - -} // extern "C" diff --git a/src/gui/ingen_gui.gladep b/src/gui/ingen_gui.gladep deleted file mode 100644 index 184ff460..00000000 --- a/src/gui/ingen_gui.gladep +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> -<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd"> - -<glade-project> - <name>Ingen</name> - <program_name>ingen</program_name> - <language>C++</language> - <gnome_support>FALSE</gnome_support> -</glade-project> diff --git a/src/gui/ingen_gui.ui b/src/gui/ingen_gui.ui deleted file mode 100644 index 9e751064..00000000 --- a/src/gui/ingen_gui.ui +++ /dev/null @@ -1,3049 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - <requires lib="gtk+" version="2.24"/> - <!-- interface-naming-policy toplevel-contextual --> - <object class="GtkAboutDialog" id="about_win"> - <property name="can_focus">False</property> - <property name="destroy_with_parent">True</property> - <property name="type_hint">normal</property> - <property name="program_name">Ingen</property> - <property name="version">@INGEN_VERSION@</property> - <property name="copyright" translatable="yes">Copyright 2005-2015 David Robillard <http://drobilla.net></property> - <property name="website">http://drobilla.net/software/ingen</property> - <property name="license" translatable="yes">Licensed under the GNU Affero GPL, Version 3 or later. - -See COPYING file included with this distribution, or http://www.gnu.org/licenses/agpl.txt for more information</property> - <property name="authors">David Robillard <d@drobilla.net></property> - <property name="translator_credits" translatable="yes" comments="TRANSLATORS: Replace this string with your names, one name per line.">translator-credits</property> - <property name="artists">Usability / UI Design: - Thorsten Wilms</property> - <property name="wrap_license">True</property> - <child internal-child="vbox"> - <object class="GtkVBox" id="dialog-vbox3"> - <property name="can_focus">False</property> - <child internal-child="action_area"> - <object class="GtkHButtonBox" id="dialog-action_area3"> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkMenu" id="canvas_menu"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkCheckMenuItem" id="canvas_menu_edit"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Edit</property> - <property name="use_underline">True</property> - <property name="active">True</property> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="menuitem5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="input1"> - <property name="label">_Input</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <child type="submenu"> - <object class="GtkMenu" id="input1_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkMenuItem" id="canvas_menu_add_audio_input"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Audio</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_canvas_menu_add_audio_input_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="canvas_menu_add_cv_input"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">C_V</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_canvas_menu_add_cv_input_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="canvas_menu_add_control_input"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Control</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_canvas_menu_add_control_input_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="canvas_menu_add_event_input"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Event</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_canvas_menu_add_event_input_activate" swapped="no"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="output1"> - <property name="label">_Output</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <child type="submenu"> - <object class="GtkMenu" id="output1_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkMenuItem" id="canvas_menu_add_audio_output"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Audio</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_canvas_menu_add_audio_output_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="canvas_menu_add_cv_output"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">C_V</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_canvas_menu_add_cv_output_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="canvas_menu_add_control_output"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Control</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_canvas_menu_add_control_output_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="canvas_menu_add_event_output"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Event</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_canvas_menu_add_event_output_activate" swapped="no"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="canvas_menu_load_plugin"> - <property name="label">_Find Plugin...</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <signal name="activate" handler="on_canvas_menu_add_plugin_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="canvas_menu_load_graph"> - <property name="label">_Load Graph...</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <signal name="activate" handler="on_canvas_menu_load_graph_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="canvas_menu_new_graph"> - <property name="label">_New Graph...</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <signal name="activate" handler="on_canvas_menu_new_graph_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="menuitem7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="canvas_menu_properties"> - <property name="label">P_roperties...</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <signal name="activate" handler="on_canvas_menu_properties_activate" swapped="no"/> - </object> - </child> - </object> - <object class="GtkWindow" id="config_win"> - <property name="can_focus">False</property> - <property name="border_width">8</property> - <property name="title" translatable="yes">Configuration - Ingen</property> - <child> - <object class="GtkVBox" id="vbox13"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkTable" id="table9"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">2</property> - <property name="n_columns">2</property> - <child> - <object class="GtkLabel" id="label90"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><b>Graph Search Path: </b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkEntry" id="config_path_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkLabel" id="label91"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><i>Example: /foo/bar:/home/user/graphs</i></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkLabel" id="label103"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHButtonBox" id="hbuttonbox2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="config_save_button"> - <property name="label">gtk-save</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="config_cancel_button"> - <property name="label">gtk-cancel</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="config_ok_button"> - <property name="label">gtk-ok</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkDialog" id="connect_win"> - <property name="can_focus">False</property> - <property name="border_width">6</property> - <property name="title" translatable="yes">Engine - Ingen</property> - <property name="resizable">False</property> - <property name="type_hint">dialog</property> - <child internal-child="vbox"> - <object class="GtkVBox" id="dialog-vbox4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child internal-child="action_area"> - <object class="GtkHButtonBox" id="dialog-action_area4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="connect_quit_button"> - <property name="label">gtk-quit</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="connect_disconnect_button"> - <property name="label">gtk-disconnect</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="connect_connect_button"> - <property name="label">gtk-connect</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="vbox19"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox61"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkImage" id="connect_icon"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xpad">12</property> - <property name="stock">gtk-disconnect</property> - <property name="icon-size">3</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="vbox20"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="border_width">5</property> - <property name="homogeneous">True</property> - <child> - <object class="GtkProgressBar" id="connect_progress_bar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="pulse_step">0.10000000149</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="connect_progress_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Not connected</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="padding">4</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkTable" id="table18"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">3</property> - <property name="n_columns">2</property> - <property name="row_spacing">8</property> - <child> - <object class="GtkHBox" id="hbox64"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkSpinButton" id="connect_port_spinbutton"> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <property name="climb_rate">1</property> - <property name="numeric">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options">GTK_FILL</property> - <property name="x_padding">8</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox67"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkEntry" id="connect_url_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="activates_default">True</property> - <property name="width_chars">28</property> - <property name="text" translatable="yes">unix:///tmp/ingen.sock</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - <property name="x_padding">8</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="connect_server_radiobutton"> - <property name="label" translatable="yes">_Connect to engine at: </property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="connect_launch_radiobutton"> - <property name="label" translatable="yes">_Launch separate engine on port: </property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <property name="group">connect_server_radiobutton</property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="connect_internal_radiobutton"> - <property name="label" translatable="yes">Start local _JACK engine</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <property name="group">connect_server_radiobutton</property> - </object> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkLabel" id="label131"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator8"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">3</property> - </packing> - </child> - <child> - <object class="GtkHButtonBox" id="hbuttonbox6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <property name="layout_style">start</property> - <child> - <object class="GtkButton" id="connect_deactivate_button"> - <property name="label" translatable="yes">D_eactivate</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_underline">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="connect_activate_button"> - <property name="label" translatable="yes">_Activate</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_underline">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">6</property> - <property name="position">4</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - </child> - <action-widgets> - <action-widget response="0">connect_quit_button</action-widget> - <action-widget response="-6">connect_disconnect_button</action-widget> - <action-widget response="-6">connect_connect_button</action-widget> - </action-widgets> - </object> - <object class="GtkWindow" id="graph_tree_win"> - <property name="width_request">320</property> - <property name="height_request">340</property> - <property name="can_focus">False</property> - <property name="border_width">8</property> - <property name="title" translatable="yes">Graphs - Ingen</property> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow8"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="border_width">3</property> - <property name="shadow_type">in</property> - <child> - <object class="GtkTreeView" id="graphs_treeview"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="rules_hint">True</property> - </object> - </child> - </object> - </child> - </object> - <object class="GtkWindow" id="graph_win"> - <property name="can_focus">False</property> - <property name="title" translatable="yes">Ingen</property> - <property name="default_width">776</property> - <property name="default_height">480</property> - <child> - <object class="GtkVBox" id="graph_win_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkMenuBar" id="menubar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkMenuItem" id="graph_file_menu"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_File</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="graph_file_menu_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkImageMenuItem" id="graph_import_menuitem"> - <property name="label">_Import...</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <accelerator key="I" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_import_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="separator9"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_save_menuitem"> - <property name="label">gtk-save</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="S" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_file_save_graph_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_save_as_menuitem"> - <property name="label">Save _As...</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <accelerator key="S" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_save_as_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_export_image_menuitem"> - <property name="label">_Export Image...</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <accelerator key="R" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_draw_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="separator11"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_close_menuitem"> - <property name="label">gtk-close</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="W" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_file_close_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_quit_menuitem"> - <property name="label">gtk-quit</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="Q" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_file_quit_nokill_menuitem_activate" swapped="no"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkMenuItem" id="edit2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Edit</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="edit2_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkImageMenuItem" id="graph_undo_menuitem"> - <property name="label">gtk-undo</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="Z" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_undo_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_redo_menuitem"> - <property name="label">gtk-redo</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="Z" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_redo_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="menuitem5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_cut_menuitem"> - <property name="label">gtk-cut</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <signal name="activate" handler="on_graph_cut_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_copy_menuitem"> - <property name="label">gtk-copy</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="C" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_copy_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_paste_menuitem"> - <property name="label">gtk-paste</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="V" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_paste_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_delete_menuitem"> - <property name="label">gtk-delete</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="Delete" signal="activate"/> - <signal name="activate" handler="on_graph_delete_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_select_all_menuitem"> - <property name="label">gtk-select-all</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="A" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_select_all_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="menuitem1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_arrange_menuitem"> - <property name="label">Arrange</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <accelerator key="G" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="menuitem2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_view_control_window_menuitem"> - <property name="label">C_ontrols...</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <accelerator key="O" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_view_control_window_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_properties_menuitem"> - <property name="label">gtk-properties</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="P" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_properties_menuitem_activate" swapped="no"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkMenuItem" id="graph_graph_menu"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_View</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="graph_graph_menu_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkCheckMenuItem" id="graph_animate_signals_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="tooltip_text" translatable="yes">Update control ports as values change.</property> - <property name="label" translatable="yes">Animate Signa_ls</property> - <property name="use_underline">True</property> - <accelerator key="l" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkCheckMenuItem" id="graph_sprung_layout_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Sprung Layou_t</property> - <property name="use_underline">True</property> - <accelerator key="t" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="menuitem6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkCheckMenuItem" id="graph_human_names_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Human names</property> - <property name="use_underline">True</property> - <property name="active">True</property> - <accelerator key="H" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkCheckMenuItem" id="graph_show_port_names_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Port _Names</property> - <property name="use_underline">True</property> - <property name="active">True</property> - <accelerator key="n" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkCheckMenuItem" id="graph_doc_pane_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Documentation Pane</property> - <property name="use_underline">True</property> - <accelerator key="D" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkCheckMenuItem" id="graph_status_bar_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Status Bar</property> - <property name="use_underline">True</property> - <property name="active">True</property> - <accelerator key="b" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="separator1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_zoom_in_menuitem"> - <property name="label">gtk-zoom-in</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="equal" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_zoom_out_menuitem"> - <property name="label">gtk-zoom-out</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="minus" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_zoom_normal_menuitem"> - <property name="label">gtk-zoom-100</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="0" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_zoom_full_menuitem"> - <property name="label">gtk-zoom-fit</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="F" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="menuitem3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="graph_increase_font_size_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Increase Font Size</property> - <property name="use_underline">True</property> - <accelerator key="Up" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="graph_decrease_font_size_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Decrease Font Size</property> - <property name="use_underline">True</property> - <accelerator key="Down" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="graph_normal_font_size_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Normal Font Size</property> - <property name="use_underline">True</property> - <accelerator key="1" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="menuitem4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_parent_menuitem"> - <property name="label">_Parent</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="BackSpace" signal="activate"/> - <signal name="activate" handler="graph_parent_menuitem" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_refresh_menuitem"> - <property name="label">gtk-refresh</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="F5" signal="activate"/> - <signal name="activate" handler="graph_refresh_menuitem" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_fullscreen_menuitem"> - <property name="label">gtk-fullscreen</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="F11" signal="activate"/> - <signal name="activate" handler="graph_fullscreen_menuitem" swapped="no"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkMenuItem" id="view1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Windows</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_view1_activate" swapped="no"/> - <child type="submenu"> - <object class="GtkMenu" id="view1_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkImageMenuItem" id="graph_view_engine_window_menuitem"> - <property name="label">_Engine</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <accelerator key="E" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_view_engine_window_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_view_graph_tree_window_menuitem"> - <property name="label">_Graph Tree</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <accelerator key="T" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_view_tree_window_menuitem_activate" swapped="no"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_view_messages_window_menuitem"> - <property name="label">_Messages</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - <accelerator key="M" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <signal name="activate" handler="on_graph_view_messages_window_menuitem_activate" swapped="no"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkMenuItem" id="help_menu"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Help</property> - <property name="use_underline">True</property> - <signal name="activate" handler="on_help_menu_activate" swapped="no"/> - <child type="submenu"> - <object class="GtkMenu" id="help_menu_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkImageMenuItem" id="right-click_the_canvas_to_add_objects1"> - <property name="label">Right-click the canvas to add objects</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="separator13"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="graph_help_about_menuitem"> - <property name="label">gtk-about</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <signal name="activate" handler="on_about1_activate" swapped="no"/> - </object> - </child> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHPaned" id="graph_documentation_paned"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkAlignment" id="graph_win_alignment"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="resize">True</property> - <property name="shrink">False</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="graph_documentation_scrolledwindow"> - <property name="can_focus">False</property> - <property name="shadow_type">in</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="resize">False</property> - <property name="shrink">True</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkStatusbar" id="graph_win_status_bar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">2</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkFileChooserDialog" id="load_graph_win"> - <property name="can_focus">False</property> - <property name="title" translatable="yes">Load Graph - Ingen</property> - <property name="window_position">center-on-parent</property> - <property name="type_hint">dialog</property> - <child internal-child="vbox"> - <object class="GtkVBox" id="vbox11"> - <property name="can_focus">False</property> - <property name="spacing">24</property> - <child internal-child="action_area"> - <object class="GtkHButtonBox" id="hbuttonbox3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="load_graph_cancel_button"> - <property name="label">gtk-cancel</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="load_graph_ok_button"> - <property name="label">gtk-open</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkAlignment" id="alignment1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xscale">0</property> - <child> - <object class="GtkTable" id="table14"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">3</property> - <property name="n_columns">3</property> - <property name="column_spacing">12</property> - <property name="row_spacing">12</property> - <child> - <object class="GtkLabel" id="load_graph_poly_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Polyphony: </property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="load_graph_poly_from_file_radio"> - <property name="label" translatable="yes">Load from _File</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="has_tooltip">True</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <property name="group">load_graph_poly_voices_radio</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkLabel" id="load_graph_ports_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Ports: </property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="load_graph_insert_ports_radio"> - <property name="label" translatable="yes">_Insert new ports</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="has_tooltip">True</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <property name="group">load_graph_merge_ports_radio</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="load_graph_merge_ports_radio"> - <property name="label" translatable="yes">_Merge with existing ports</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="has_tooltip">True</property> - <property name="use_underline">True</property> - <property name="active">True</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox58"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkRadioButton" id="load_graph_poly_voices_radio"> - <property name="label" translatable="yes">_Voices:</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="has_tooltip">True</property> - <property name="use_underline">True</property> - <property name="active">True</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkSpinButton" id="load_graph_poly_spinbutton"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">●</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <property name="climb_rate">1</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="load_graph_symbol_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">_Symbol: </property> - <property name="use_markup">True</property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">load_graph_symbol_entry</property> - </object> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkEntry" id="load_graph_symbol_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">●</property> - <property name="activates_default">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - </child> - <action-widgets> - <action-widget response="-6">load_graph_cancel_button</action-widget> - <action-widget response="-5">load_graph_ok_button</action-widget> - </action-widgets> - </object> - <object class="GtkWindow" id="load_plugin_win"> - <property name="can_focus">False</property> - <property name="border_width">8</property> - <property name="title" translatable="yes">Load Plugin - Ingen</property> - <property name="window_position">center-on-parent</property> - <property name="destroy_with_parent">True</property> - <property name="type_hint">dialog</property> - <child> - <object class="GtkVBox" id="vbox9"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">1</property> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow3"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="border_width">2</property> - <child> - <object class="GtkTreeView" id="load_plugin_plugins_treeview"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="border_width">2</property> - <property name="reorderable">True</property> - <property name="rules_hint">True</property> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkTable" id="table16"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">3</property> - <property name="n_columns">3</property> - <property name="row_spacing">12</property> - <child> - <object class="GtkLabel" id="label66"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">1</property> - <property name="label" translatable="yes">Node _Symbol:</property> - <property name="use_markup">True</property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">load_plugin_name_entry</property> - </object> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox63"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkEntry" id="load_plugin_name_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="load_plugin_polyphonic_checkbutton"> - <property name="label" translatable="yes">_Polyphonic</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="active">True</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="padding">8</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="y_options">GTK_FILL</property> - <property name="x_padding">6</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="load_plugin_search_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="has_focus">True</property> - <property name="secondary_icon_stock">gtk-clear</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="load_plugin_filter_combo"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkHButtonBox" id="hbuttonbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">4</property> - <child> - <object class="GtkButton" id="load_plugin_close_button"> - <property name="label">gtk-close</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="load_plugin_add_button"> - <property name="label">gtk-add</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkWindow" id="messages_win"> - <property name="width_request">400</property> - <property name="height_request">180</property> - <property name="can_focus">False</property> - <property name="border_width">8</property> - <property name="title" translatable="yes">Messages - Ingen</property> - <child> - <object class="GtkVBox" id="vbox12"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="shadow_type">in</property> - <child> - <object class="GtkTextView" id="messages_textview"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="pixels_above_lines">1</property> - <property name="pixels_below_lines">1</property> - <property name="editable">False</property> - <property name="wrap_mode">word</property> - <property name="left_margin">5</property> - <property name="right_margin">5</property> - <property name="cursor_visible">False</property> - <property name="accepts_tab">False</property> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHButtonBox" id="hbuttonbox8"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="messages_clear_button"> - <property name="label">gtk-clear</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="messages_close_button"> - <property name="label">gtk-close</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkWindow" id="new_subgraph_win"> - <property name="width_request">320</property> - <property name="can_focus">False</property> - <property name="border_width">8</property> - <property name="title" translatable="yes">Create Subgraph - Ingen</property> - <property name="resizable">False</property> - <property name="window_position">center-on-parent</property> - <property name="type_hint">dialog</property> - <child> - <object class="GtkVBox" id="vbox4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkTable" id="table1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">2</property> - <property name="n_columns">2</property> - <child> - <object class="GtkLabel" id="label8"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">_Symbol: </property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">new_subgraph_name_entry</property> - </object> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_EXPAND</property> - <property name="x_padding">5</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label9"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">_Polyphony: </property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">new_subgraph_polyphony_spinbutton</property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_EXPAND</property> - <property name="x_padding">5</property> - </packing> - </child> - <child> - <object class="GtkSpinButton" id="new_subgraph_polyphony_spinbutton"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">●</property> - <property name="activates_default">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <property name="climb_rate">1</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - <property name="y_padding">4</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="new_subgraph_name_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">●</property> - <property name="activates_default">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="y_options"/> - <property name="y_padding">4</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="new_subgraph_message_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHButtonBox" id="hbuttonbox5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">4</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="new_subgraph_cancel_button"> - <property name="label">gtk-cancel</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="new_subgraph_ok_button"> - <property name="label">gtk-ok</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkMenu" id="object_menu"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <child> - <object class="GtkImageMenuItem" id="node_popup_gui_menuitem"> - <property name="label" translatable="yes">Show GUI...</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="use_stock">False</property> - </object> - </child> - <child> - <object class="GtkCheckMenuItem" id="node_embed_gui_menuitem"> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="label" translatable="yes">Embed GUI</property> - </object> - </child> - <child> - <object class="GtkCheckMenuItem" id="node_enabled_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="label" translatable="yes">Enabled</property> - <property name="active">True</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="node_randomize_menuitem"> - <property name="label" translatable="yes">Randomi_ze</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="port_set_min_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Set Value as Mi_nimum</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="port_set_max_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Set Value as Ma_ximum</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="port_reset_range_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Re_set Range</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="port_expose_menuitem"> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Expose</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="object_menu_separator"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkCheckMenuItem" id="object_polyphonic_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="label" translatable="yes">P_olyphonic</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="object_learn_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Learn</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="object_unlearn_menuitem"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Unlearn</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="object_disconnect_menuitem"> - <property name="label">Dis_connect</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="object_destroy_menuitem"> - <property name="label">gtk-delete</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="object_rename_menuitem"> - <property name="label">_Rename...</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="object_properties_menuitem"> - <property name="label">_Properties...</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="use_underline">True</property> - <property name="use_stock">False</property> - </object> - </child> - </object> - <object class="GtkMenu" id="port_control_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkImageMenuItem" id="port_control_menu_properties"> - <property name="label">gtk-properties</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <signal name="activate" handler="on_port_control_menu_properties_activate" swapped="no"/> - </object> - </child> - </object> - <object class="GtkWindow" id="port_properties_win"> - <property name="can_focus">False</property> - <property name="border_width">8</property> - <property name="title" translatable="yes">Port Properties - Ingen</property> - <property name="resizable">False</property> - <property name="window_position">mouse</property> - <child> - <object class="GtkVBox" id="dialog-vbox7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">8</property> - <child> - <object class="GtkHButtonBox" id="dialog-action_area7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="port_properties_cancel_button"> - <property name="label">gtk-cancel</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="port_properties_ok_button"> - <property name="label">gtk-ok</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkTable" id="table20"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">2</property> - <property name="n_columns">2</property> - <property name="column_spacing">2</property> - <property name="row_spacing">4</property> - <child> - <object class="GtkSpinButton" id="port_properties_min_spinner"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <property name="climb_rate">1</property> - <property name="digits">5</property> - <property name="numeric">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkSpinButton" id="port_properties_max_spinner"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <property name="climb_rate">1</property> - <property name="digits">5</property> - <property name="numeric">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkLabel" id="label138"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Minimum Value: </property> - </object> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - <child> - <object class="GtkLabel" id="label139"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Maximum Value: </property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"/> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkWindow" id="properties_win"> - <property name="can_focus">False</property> - <property name="border_width">12</property> - <property name="window_position">center-on-parent</property> - <child> - <object class="GtkVBox" id="properties_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkScrolledWindow" id="properties_scrolledwindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <child> - <object class="GtkViewport" id="viewport2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="resize_mode">queue</property> - <property name="shadow_type">none</property> - <child> - <object class="GtkTable" id="properties_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_columns">3</property> - <property name="column_spacing">12</property> - <property name="row_spacing">6</property> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkComboBox" id="properties_key_combo"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkAlignment" id="properties_value_bin"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="properties_add_button"> - <property name="label">gtk-add</property> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHButtonBox" id="properties_buttonbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="properties_cancel_button"> - <property name="label">gtk-cancel</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="properties_apply_button"> - <property name="label">gtk-apply</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="properties_ok_button"> - <property name="label">gtk-ok</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">1</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkWindow" id="rename_win"> - <property name="width_request">250</property> - <property name="can_focus">False</property> - <property name="title" translatable="yes">Rename</property> - <property name="window_position">center-on-parent</property> - <property name="destroy_with_parent">True</property> - <property name="type_hint">dialog</property> - <child> - <object class="GtkVBox" id="vbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="border_width">5</property> - <child> - <object class="GtkTable" id="table2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">2</property> - <property name="n_columns">2</property> - <property name="row_spacing">8</property> - <child> - <object class="GtkLabel" id="label95"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Symbol: </property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">rename_symbol_entry</property> - </object> - <packing> - <property name="x_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="rename_label_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">●</property> - <property name="activates_default">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Label: </property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">rename_label_entry</property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="rename_symbol_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="has_focus">True</property> - <property name="invisible_char">●</property> - <property name="activates_default">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="rename_message_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHButtonBox" id="hbuttonbox4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">8</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="rename_cancel_button"> - <property name="label">gtk-cancel</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="rename_ok_button"> - <property name="label">gtk-ok</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="receives_default">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - </child> - </object> - <object class="GtkWindow" id="warehouse_win"> - <property name="can_focus">False</property> - <property name="title" translatable="yes">Warehouse - Ingen</property> - <child> - <object class="GtkTable" id="table8"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">6</property> - <property name="column_spacing">12</property> - <property name="row_spacing">12</property> - <child> - <object class="GtkVBox" id="toggle_control"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <child> - <object class="GtkHBox" id="hbox2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <child> - <object class="GtkLabel" id="toggle_control_name_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">4</property> - <property name="label" translatable="yes"><b>Name</b></property> - <property name="use_markup">True</property> - <property name="single_line_mode">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkAlignment" id="alignment7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="yalign">1</property> - <property name="yscale">0</property> - <property name="bottom_padding">1</property> - <property name="left_padding">1</property> - <property name="right_padding">4</property> - <child> - <object class="GtkHSeparator" id="hseparator7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="toggle_control_check"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="y_padding">8</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="control_panel_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkAlignment" id="alignment6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="yalign">0</property> - <child> - <object class="GtkScrolledWindow" id="scrolledwin1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">never</property> - <child> - <object class="GtkViewport" id="viewport1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="shadow_type">none</property> - <child> - <object class="GtkVBox" id="control_panel_controls_box"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - </object> - </child> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - </child> - <child> - <object class="GtkVBox" id="graph_view_box"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox70"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkToolbar" id="toolbar6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="toolbar_style">icons</property> - <property name="icon_size">1</property> - <child> - <object class="GtkToolItem" id="graph_view_breadcrumb_container"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">False</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkToolbar" id="graph_view_toolbar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="toolbar_style">icons</property> - <property name="show_arrow">False</property> - <property name="icon_size">1</property> - <child> - <object class="GtkToggleToolButton" id="graph_view_process_but"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="stock_id">gtk-execute</property> - <property name="active">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="homogeneous">True</property> - </packing> - </child> - <child> - <object class="GtkToolItem" id="toolitem7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkImage" id="image1978"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xpad">4</property> - <property name="stock">gtk-copy</property> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - </packing> - </child> - <child> - <object class="GtkToolItem" id="toolitem10"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkSpinButton" id="graph_view_poly_spin"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <property name="climb_rate">1</property> - <property name="numeric">True</property> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="graph_view_scrolledwindow"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK</property> - <property name="border_width">1</property> - <property name="shadow_type">in</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="control_strip"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <child> - <object class="GtkHBox" id="hbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <child> - <object class="GtkLabel" id="control_strip_name_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">1</property> - <property name="xpad">4</property> - <property name="label" translatable="yes"><b>Name</b></property> - <property name="use_markup">True</property> - <property name="single_line_mode">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkAlignment" id="alignment3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="yalign">1</property> - <property name="yscale">0</property> - <property name="bottom_padding">1</property> - <property name="left_padding">1</property> - <property name="right_padding">4</property> - <child> - <object class="GtkHSeparator" id="hseparator6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkSpinButton" id="control_strip_spinner"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="width_chars">12</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <property name="digits">4</property> - <property name="numeric">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHScale" id="control_strip_slider"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="digits">63</property> - <property name="draw_value">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="y_padding">8</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="string_control"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <child> - <object class="GtkHBox" id="hbox3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <child> - <object class="GtkLabel" id="string_control_name_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">4</property> - <property name="label" translatable="yes"><b>Name</b></property> - <property name="use_markup">True</property> - <property name="single_line_mode">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="string_control_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">●</property> - <property name="caps_lock_warning">False</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="y_padding">8</property> - </packing> - </child> - </object> - </child> - </object> -</interface> diff --git a/src/gui/ingen_gui_lv2.cpp b/src/gui/ingen_gui_lv2.cpp deleted file mode 100644 index 57881741..00000000 --- a/src/gui/ingen_gui_lv2.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/AtomReader.hpp" -#include "ingen/AtomSink.hpp" -#include "ingen/AtomWriter.hpp" -#include "ingen/World.hpp" -#include "ingen/client/ClientStore.hpp" -#include "ingen/client/GraphModel.hpp" -#include "ingen/client/SigClientInterface.hpp" -#include "ingen/ingen.h" -#include "ingen/runtime_paths.hpp" -#include "ingen/types.hpp" -#include "lv2/lv2plug.in/ns/extensions/ui/ui.h" - -#include "App.hpp" -#include "GraphBox.hpp" - -#define INGEN_LV2_UI_URI INGEN_NS "GraphUIGtk2" - -namespace Ingen { - -/** A sink that writes atoms to a port via the UI extension. */ -struct IngenLV2AtomSink : public AtomSink { - IngenLV2AtomSink(URIs& uris, - LV2UI_Write_Function ui_write, - LV2UI_Controller ui_controller) - : _uris(uris) - , _ui_write(ui_write) - , _ui_controller(ui_controller) - {} - - bool write(const LV2_Atom* atom, int32_t default_id) { - _ui_write(_ui_controller, - 0, - lv2_atom_total_size(atom), - _uris.atom_eventTransfer, - atom); - return true; - } - - URIs& _uris; - LV2UI_Write_Function _ui_write; - LV2UI_Controller _ui_controller; -}; - -struct IngenLV2UI { - IngenLV2UI() - : argc(0) - , argv(nullptr) - , forge(nullptr) - , world(nullptr) - , sink(nullptr) - {} - - int argc; - char** argv; - Forge* forge; - World* world; - IngenLV2AtomSink* sink; - SPtr<GUI::App> app; - SPtr<GUI::GraphBox> view; - SPtr<Interface> engine; - SPtr<AtomReader> reader; - SPtr<Client::SigClientInterface> client; -}; - -} // namespace Ingen - -static LV2UI_Handle -instantiate(const LV2UI_Descriptor* descriptor, - const char* plugin_uri, - const char* bundle_path, - LV2UI_Write_Function write_function, - LV2UI_Controller controller, - LV2UI_Widget* widget, - const LV2_Feature* const* features) -{ -#if __cplusplus >= 201103L - using Ingen::SPtr; -#endif - - Ingen::set_bundle_path(bundle_path); - - Ingen::IngenLV2UI* ui = new Ingen::IngenLV2UI(); - - LV2_URID_Map* map = nullptr; - LV2_URID_Unmap* unmap = nullptr; - LV2_Log_Log* log = nullptr; - for (int i = 0; features[i]; ++i) { - if (!strcmp(features[i]->URI, LV2_URID__map)) { - map = (LV2_URID_Map*)features[i]->data; - } else if (!strcmp(features[i]->URI, LV2_URID__unmap)) { - unmap = (LV2_URID_Unmap*)features[i]->data; - } else if (!strcmp(features[i]->URI, LV2_LOG__log)) { - log = (LV2_Log_Log*)features[i]->data; - } - } - - ui->world = new Ingen::World(map, unmap, log); - ui->forge = new Ingen::Forge(ui->world->uri_map()); - - ui->world->load_configuration(ui->argc, ui->argv); - - if (!ui->world->load_module("client")) { - delete ui; - return nullptr; - } - - ui->sink = new Ingen::IngenLV2AtomSink( - ui->world->uris(), write_function, controller); - - // Set up an engine interface that writes LV2 atoms - ui->engine = SPtr<Ingen::Interface>( - new Ingen::AtomWriter( - ui->world->uri_map(), ui->world->uris(), *ui->sink)); - - ui->world->set_interface(ui->engine); - - // Create App and client - ui->app = Ingen::GUI::App::create(ui->world); - ui->client = SPtr<Ingen::Client::SigClientInterface>( - new Ingen::Client::SigClientInterface()); - ui->app->set_is_plugin(true); - ui->app->attach(ui->client); - - ui->reader = SPtr<Ingen::AtomReader>( - new Ingen::AtomReader(ui->world->uri_map(), - ui->world->uris(), - ui->world->log(), - *ui->client.get())); - - // Create empty root graph model - Ingen::Properties props; - props.emplace(ui->app->uris().rdf_type, - Ingen::Property(ui->app->uris().ingen_Graph)); - ui->app->store()->put(Ingen::main_uri(), props); - - // Create a GraphBox for the root and set as the UI widget - SPtr<const Ingen::Client::GraphModel> root = - Ingen::dynamic_ptr_cast<const Ingen::Client::GraphModel>( - ui->app->store()->object(Raul::Path("/"))); - ui->view = Ingen::GUI::GraphBox::create(*ui->app, root); - ui->view->unparent(); - *widget = ui->view->gobj(); - - // Request the actual root graph - ui->world->interface()->get(Ingen::main_uri()); - - return ui; -} - -static void -cleanup(LV2UI_Handle handle) -{ - Ingen::IngenLV2UI* ui = (Ingen::IngenLV2UI*)handle; - delete ui; -} - -static void -port_event(LV2UI_Handle handle, - uint32_t port_index, - uint32_t buffer_size, - uint32_t format, - const void* buffer) -{ - Ingen::IngenLV2UI* ui = (Ingen::IngenLV2UI*)handle; - const LV2_Atom* atom = (const LV2_Atom*)buffer; - ui->reader->write(atom); -} - -static const void* -extension_data(const char* uri) -{ - return nullptr; -} - -static const LV2UI_Descriptor descriptor = { - INGEN_LV2_UI_URI, - instantiate, - cleanup, - port_event, - extension_data -}; - -LV2_SYMBOL_EXPORT -const LV2UI_Descriptor* -lv2ui_descriptor(uint32_t index) -{ - switch (index) { - case 0: - return &descriptor; - default: - return nullptr; - } -} diff --git a/src/gui/ingen_style.rc b/src/gui/ingen_style.rc deleted file mode 100644 index 4763e12a..00000000 --- a/src/gui/ingen_style.rc +++ /dev/null @@ -1,155 +0,0 @@ -style "ingen-default" -{ - GtkMenuItem::selected_shadow_type = out - - GtkWidget::interior_focus = 1 - GtkWidget::focus_padding = 1 - - GtkButton::default_border = { 0, 0, 0, 0 } - GtkButton::default_outside_border = { 0, 0, 0, 0 } - - GtkCheckButton::indicator_size = 12 - GtkExpander::expander_size = 16 - GtkMenuBar::internal-padding = 0 - GtkPaned::handle_size = 6 - GtkRange::slider_width = 15 - GtkRange::stepper_size = 15 - GtkRange::trough_border = 0 - GtkScrollbar::min_slider_length = 30 - GtkTreeView::expander_size = 14 - GtkTreeView::odd_row_color = "#343" - - xthickness = 1 - ythickness = 1 - - fg[NORMAL] = "#B8BBB9" - fg[PRELIGHT] = "#B8BBB9" - fg[ACTIVE] = "#B8BBB9" - fg[SELECTED] = "#B8BBB9" - fg[INSENSITIVE] = "#48494B" - - bg[NORMAL] = "#1E2224" - bg[PRELIGHT] = "#333537" - bg[ACTIVE] = "#333537" - bg[SELECTED] = "#00A150" - bg[INSENSITIVE] = "#1E2224" - - base[NORMAL] = "#111" - base[PRELIGHT] = "#222" - base[ACTIVE] = "#0A2" - base[SELECTED] = "#0A2" - base[INSENSITIVE] = "#444" - - text[NORMAL] = "#FFF" - text[PRELIGHT] = "#FFF" - text[ACTIVE] = "#FFF" - text[SELECTED] = "#FFF" - text[INSENSITIVE] = "#666" - - engine "clearlooks" - { - contrast = 1.0 - } -} - -style "ingen-progressbar" = "ingen-default" -{ - xthickness = 1 - ythickness = 1 -} - -style "ingen-wide" = "ingen-default" -{ - xthickness = 2 - ythickness = 2 -} - -style "ingen-notebook" = "ingen-wide" -{ - bg[NORMAL] = "#383B39" - bg[ACTIVE] = "#383B39" -} - -style "ingen-tasklist" = "ingen-default" -{ - xthickness = 5 - ythickness = 3 -} - -style "ingen-menu" = "ingen-default" -{ - xthickness = 5 - ythickness = 5 - bg[NORMAL] = "#262626" -} - -style "ingen-menu-item" = "ingen-default" -{ - xthickness = 2 - ythickness = 3 -} - -style "ingen-menu-itembar" = "ingen-default" -{ - xthickness = 3 - ythickness = 3 -} - -style "ingen-tree" = "ingen-default" -{ - xthickness = 2 - ythickness = 2 -} - -style "ingen-frame-title" = "ingen-default" -{ - fg[NORMAL] = "#B8BBB9" -} - -style "ingen-panel" = "ingen-default" -{ - xthickness = 3 - ythickness = 3 -} - -style "ingen-tooltips" = "ingen-default" -{ - xthickness = 4 - ythickness = 4 - bg[NORMAL] = "#585B59" -} - -style "ingen-combo" = "ingen-default" -{ - xthickness = 1 - ythickness = 2 -} - -class "*Ingen*GtkWidget" style : highest "ingen-default" -class "*Ingen*GtkButton" style : highest "ingen-wide" -class "*Ingen*GtkRange" style : highest "ingen-wide" -class "*Ingen*GtkFrame" style : highest "ingen-wide" -class "*Ingen*GtkStatusbar" style : highest "ingen-wide" -class "*Ingen*GtkMenu" style : highest "ingen-menu" -class "*Ingen*GtkMenuItem" style : highest "ingen-menu-item" -widget_class "*Ingen*MenuItem.*" style : highest "ingen-menu-item" -widget_class "*Ingen*.GtkAccelMenuItem.*" style : highest "ingen-menu-item" -widget_class "*Ingen*.GtkRadioMenuItem.*" style : highest "ingen-menu-item" -widget_class "*Ingen*.GtkCheckMenuItem.*" style : highest "ingen-menu-item" -widget_class "*Ingen*.GtkImageMenuItem.*" style : highest "ingen-menu-item" -widget_class "*Ingen*.GtkSeparatorMenuItem.*" style : highest "ingen-menu-item" -class "*Ingen*GtkEntry" style : highest "ingen-wide" -widget_class "*Ingen*.tooltips.*.GtkToggleButton" style : highest "ingen-tasklist" -widget_class "*Ingen*.GtkTreeView.GtkButton" style : highest "ingen-tree" -widget_class "*Ingen*.GtkCTree.GtkButton" style : highest "ingen-tree" -widget_class "*Ingen*.GtkList.GtkButton" style : highest "ingen-tree" -widget_class "*Ingen*.GtkCList.GtkButton" style : highest "ingen-tree" -widget_class "*Ingen*.GtkFrame.GtkLabel" style : highest "ingen-frame-title" -widget_class "*Ingen*BasePWidget.GtkEventBox.GtkTable.GtkFrame" style : highest "ingen-panel" -widget "gtk-tooltips" style : highest "ingen-tooltips" -class "*Ingen*GtkNotebook" style : highest "ingen-notebook" -class "*Ingen*GtkProgressBar" style : highest "ingen-progressbar" -widget_class "*Ingen*.GtkComboBox.GtkButton" style : highest "ingen-combo" -widget_class "*Ingen*.GtkCombo.GtkButton" style : highest "ingen-combo" - -widget "*Ingen*" style : highest "ingen-default" diff --git a/src/gui/rgba.hpp b/src/gui/rgba.hpp deleted file mode 100644 index dae3f179..00000000 --- a/src/gui/rgba.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_GUI_RGBA_HPP -#define INGEN_GUI_RGBA_HPP - -#include <cmath> - -namespace Ingen { -namespace GUI { - -static inline uint32_t -rgba_to_uint(uint8_t r, uint8_t g, uint8_t b, uint8_t a) -{ - return ((((uint32_t)(r)) << 24) | - (((uint32_t)(g)) << 16) | - (((uint32_t)(b)) << 8) | - (((uint32_t)(a)))); -} - -static inline uint8_t -mono_interpolate(uint8_t v1, uint8_t v2, float f) -{ - return ((int)rint((v2) * (f) + (v1) * (1 - (f)))); -} - -#define RGBA_R(x) (((uint32_t)(x)) >> 24) -#define RGBA_G(x) ((((uint32_t)(x)) >> 16) & 0xFF) -#define RGBA_B(x) ((((uint32_t)(x)) >> 8) & 0xFF) -#define RGBA_A(x) (((uint32_t)(x)) & 0xFF) - -static inline uint32_t -rgba_interpolate(uint32_t c1, uint32_t c2, float f) -{ - return rgba_to_uint( - mono_interpolate(RGBA_R(c1), RGBA_R(c2), f), - mono_interpolate(RGBA_G(c1), RGBA_G(c2), f), - mono_interpolate(RGBA_B(c1), RGBA_B(c2), f), - mono_interpolate(RGBA_A(c1), RGBA_A(c2), f)); -} - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_GUI_RGBA_HPP diff --git a/src/gui/wscript b/src/gui/wscript deleted file mode 100644 index e4d777ad..00000000 --- a/src/gui/wscript +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env python -import waflib.extras.autowaf as autowaf -import waflib.Utils as Utils -import waflib.Options as Options - -def options(ctx): - opt = ctx.get_option_group('Configuration options') - opt.add_option('--light-theme', action='store_true', dest='light_theme', - help='use light coloured theme') - -def configure(conf): - autowaf.check_pkg(conf, 'glibmm-2.4', uselib_store='GLIBMM', - atleast_version='2.14.0', mandatory=False) - autowaf.check_pkg(conf, 'gthread-2.0', uselib_store='GTHREAD', - atleast_version='2.14.0', mandatory=False) - autowaf.check_pkg(conf, 'gtkmm-2.4', uselib_store='GTKMM', - atleast_version='2.14.0', mandatory=False) - autowaf.check_pkg(conf, 'ganv-1', uselib_store='GANV', - atleast_version='1.5.4', mandatory=False) - if not Options.options.no_webkit: - autowaf.check_pkg(conf, 'webkit-1.0', uselib_store='WEBKIT', - atleast_version='1.4.0', mandatory=False) - - if conf.env.HAVE_GANV and conf.env.HAVE_GTKMM: - autowaf.define(conf, 'INGEN_BUILD_GUI', 1) - - if Options.options.light_theme: - autowaf.define(conf, 'INGEN_USE_LIGHT_THEME', 1) - -def build(bld): - obj = bld(features = 'cxx cxxshlib', - export_includes = ['../..'], - includes = ['../..'], - name = 'libingen_gui', - target = 'ingen_gui', - install_path = '${LIBDIR}', - use = 'libingen libingen_client') - autowaf.use_lib(bld, obj, ''' - GANV - GLADEMM - GLIBMM - GNOMECANVAS - GTKMM - LILV - LV2 - RAUL - SIGCPP - SORD - SOUP - SUIL - WEBKIT - ''') - - obj.source = ''' - App.cpp - Arc.cpp - BreadCrumbs.cpp - ConnectWindow.cpp - GraphBox.cpp - GraphCanvas.cpp - GraphPortModule.cpp - GraphTreeWindow.cpp - GraphView.cpp - GraphWindow.cpp - LoadGraphWindow.cpp - LoadPluginWindow.cpp - MessagesWindow.cpp - NewSubgraphWindow.cpp - NodeMenu.cpp - NodeModule.cpp - ObjectMenu.cpp - PluginMenu.cpp - Port.cpp - PortMenu.cpp - PropertiesWindow.cpp - RDFS.cpp - RenameWindow.cpp - Style.cpp - SubgraphModule.cpp - ThreadedLoader.cpp - URIEntry.cpp - WidgetFactory.cpp - WindowFactory.cpp - ingen_gui.cpp - ''' - - # XML UI definition - bld(features = 'subst', - source = 'ingen_gui.ui', - target = '../../ingen_gui.ui', - install_path = '${DATADIR}/ingen', - chmod = Utils.O755, - INGEN_VERSION = bld.env.INGEN_VERSION) - - # Gtk style - bld(features = 'subst', - is_copy = True, - source = 'ingen_style.rc', - target = '../../ingen_style.rc', - install_path = '${DATADIR}/ingen', - chmod = Utils.O755) - - # LV2 UI - obj = bld(features = 'cxx cxxshlib', - source = 'ingen_gui_lv2.cpp', - includes = ['.', '../..'], - name = 'ingen_gui_lv2', - target = 'ingen_gui_lv2', - install_path = '${LV2DIR}/ingen.lv2/', - use = 'libingen libingen_gui') - autowaf.use_lib(bld, obj, 'LV2 SERD SORD LILV RAUL GLIBMM GTKMM') diff --git a/src/ingen/ingen.cpp b/src/ingen/ingen.cpp deleted file mode 100644 index d812d862..00000000 --- a/src/ingen/ingen.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <signal.h> - -#include <cstdlib> -#include <chrono> -#include <iostream> -#include <memory> -#include <string> - -#include "raul/Path.hpp" - -#include "ingen_config.h" - -#include "ingen/Configuration.hpp" -#include "ingen/EngineBase.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/Parser.hpp" -#include "ingen/World.hpp" -#include "ingen/paths.hpp" -#include "ingen/runtime_paths.hpp" -#include "ingen/types.hpp" -#ifdef HAVE_SOCKET -#include "ingen/client/SocketClient.hpp" -#endif - -using namespace std; -using namespace Ingen; - -class DummyInterface : public Interface -{ - URI uri() const override { return URI("ingen:dummy"); } - void message(const Message& msg) override {} -}; - -unique_ptr<Ingen::World> world; - -static void -ingen_interrupt(int signal) -{ - if (signal == SIGTERM) { - cerr << "ingen: Terminated" << endl; - exit(EXIT_FAILURE); - } else { - cout << "ingen: Interrupted" << endl; - if (world && world->engine()) { - world->engine()->quit(); - } - } -} - -static void -ingen_try(bool cond, const char* msg) -{ - if (!cond) { - cerr << "ingen: error: " << msg << endl; - exit(EXIT_FAILURE); - } -} - -static int -print_version() -{ - cout << "ingen " << INGEN_VERSION - << " <http://drobilla.net/software/ingen>\n" - << "Copyright 2007-2017 David Robillard <http://drobilla.net>.\n" - << "License: <https://www.gnu.org/licenses/agpl-3.0>\n" - << "This is free software; you are free to change and redistribute it.\n" - << "There is NO WARRANTY, to the extent permitted by law." << endl; - return EXIT_SUCCESS; -} - -int -main(int argc, char** argv) -{ - Ingen::set_bundle_path_from_code((void*)&print_version); - - // Create world - try { - world = unique_ptr<Ingen::World>(new Ingen::World(nullptr, NULL, NULL)); - world->load_configuration(argc, argv); - if (argc <= 1) { - world->conf().print_usage("ingen", cout); - return EXIT_FAILURE; - } else if (world->conf().option("help").get<int32_t>()) { - world->conf().print_usage("ingen", cout); - return EXIT_SUCCESS; - } else if (world->conf().option("version").get<int32_t>()) { - return print_version(); - } - } catch (std::exception& e) { - cout << "ingen: error: " << e.what() << endl; - return EXIT_FAILURE; - } - - Configuration& conf = world->conf(); - if (conf.option("uuid").is_valid()) { - world->set_jack_uuid(conf.option("uuid").ptr<char>()); - } - - // Run engine - if (conf.option("engine").get<int32_t>()) { - if (world->conf().option("threads").get<int32_t>() < 1) { - cerr << "ingen: error: threads must be > 0" << endl; - return EXIT_FAILURE; - } - - ingen_try(world->load_module("server"), "Failed to load server module"); - - ingen_try(bool(world->engine()), "Unable to create engine"); - world->engine()->listen(); - } - -#ifdef HAVE_SOCKET - Client::SocketClient::register_factories(world.get()); -#endif - - // Load GUI if requested - if (conf.option("gui").get<int32_t>()) { - ingen_try(world->load_module("client"), "Failed to load client module"); - ingen_try(world->load_module("gui"), "Failed to load GUI module"); - } - - // If we don't have a local engine interface (from the GUI), use network - SPtr<Interface> engine_interface(world->interface()); - SPtr<Interface> dummy_client(new DummyInterface()); - if (!engine_interface) { - const char* const uri = conf.option("connect").ptr<char>(); - ingen_try(URI::is_valid(uri), - (fmt("Invalid URI <%1%>") % uri).str().c_str()); - engine_interface = world->new_interface(URI(uri), dummy_client); - - if (!engine_interface && !conf.option("gui").get<int32_t>()) { - cerr << (fmt("ingen: error: Failed to connect to `%1%'\n") % uri); - return EXIT_FAILURE; - } - - world->set_interface(engine_interface); - } - - // Activate the engine, if we have one - if (world->engine()) { - if (!world->load_module("jack") && !world->load_module("portaudio")) { - cerr << "ingen: error: Failed to load driver module" << endl; - return EXIT_FAILURE; - } - - if (!world->engine()->supports_dynamic_ports() && - !conf.option("load").is_valid()) { - cerr << "ingen: error: Initial graph required for driver" << endl; - return EXIT_FAILURE; - } - } - - // Load a graph - if (conf.option("load").is_valid()) { - boost::optional<Raul::Path> parent; - boost::optional<Raul::Symbol> symbol; - - const Atom& path_option = conf.option("path"); - if (path_option.is_valid()) { - if (Raul::Path::is_valid(path_option.ptr<char>())) { - const Raul::Path p(path_option.ptr<char>()); - if (!p.is_root()) { - parent = p.parent(); - symbol = Raul::Symbol(p.symbol()); - } - } else { - cerr << "Invalid path given: '" << path_option.ptr<char>() << endl; - } - } - - ingen_try(bool(world->parser()), "Failed to create parser"); - - const string graph = conf.option("load").ptr<char>(); - - engine_interface->get(URI("ingen:/plugins")); - engine_interface->get(main_uri()); - - std::lock_guard<std::mutex> lock(world->rdf_mutex()); - world->parser()->parse_file( - world.get(), engine_interface.get(), graph, parent, symbol); - } else if (conf.option("server-load").is_valid()) { - const char* path = conf.option("server-load").ptr<char>(); - if (serd_uri_string_has_scheme((const uint8_t*)path)) { - std::cout << "Loading " << path << " (server side)" << std::endl; - engine_interface->copy(URI(path), main_uri()); - } else { - SerdNode uri = serd_node_new_file_uri( - (const uint8_t*)path, nullptr, nullptr, true); - std::cout << "Loading " << (const char*)uri.buf - << " (server side)" << std::endl; - engine_interface->copy(URI((const char*)uri.buf), main_uri()); - serd_node_free(&uri); - } - } - - // Save the currently loaded graph - if (conf.option("save").is_valid()) { - const char* path = conf.option("save").ptr<char>(); - if (serd_uri_string_has_scheme((const uint8_t*)path)) { - std::cout << "Saving to " << path << std::endl; - engine_interface->copy(main_uri(), URI(path)); - } else { - SerdNode uri = serd_node_new_file_uri( - (const uint8_t*)path, nullptr, nullptr, true); - std::cout << "Saving to " << (const char*)uri.buf << std::endl; - engine_interface->copy(main_uri(), URI((const char*)uri.buf)); - serd_node_free(&uri); - } - } - - // Activate the engine now that the graph is loaded - if (world->engine()) { - world->engine()->flush_events(std::chrono::milliseconds(10)); - world->engine()->activate(); - } - - // Set up signal handlers that will set quit_flag on interrupt - signal(SIGINT, ingen_interrupt); - signal(SIGTERM, ingen_interrupt); - - if (conf.option("gui").get<int32_t>()) { - world->run_module("gui"); - } else if (world->engine()) { - // Run engine main loop until interrupt - while (world->engine()->main_iteration()) { - this_thread::sleep_for(chrono::milliseconds(125)); - } - } - - // Sleep for a half second to allow event queues to drain - this_thread::sleep_for(chrono::milliseconds(500)); - - // Shut down - if (world->engine()) { - world->engine()->deactivate(); - } - - // Save configuration to restore preferences on next run - const std::string path = conf.save( - world->uri_map(), "ingen", "options.ttl", Configuration::GLOBAL); - std::cout << (fmt("Saved configuration to %1%") % path) << std::endl; - - engine_interface.reset(); - - return 0; -} diff --git a/src/ingen/ingen.desktop b/src/ingen/ingen.desktop deleted file mode 100644 index 99192435..00000000 --- a/src/ingen/ingen.desktop +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Encoding=UTF-8 -Name=Ingen -Comment=Create synthesizers and effects in a modular environment -Exec=ingen -eg -Terminal=false -Icon=ingen -Type=Application -Categories=Application;AudioVideo;Sound;Audio; diff --git a/src/ingen/ingen.grind b/src/ingen/ingen.grind deleted file mode 100644 index 9e60915b..00000000 --- a/src/ingen/ingen.grind +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh - -export INGEN_MODULE_PATH="`pwd`/../../libs/engine/.libs:`pwd`/../../libs/gui/.libs:`pwd`/../../libs/client/.libs" -export INGEN_GLADE_PATH="`pwd`/../../libs/gui/ingen_gui.glade" -libtool --mode=execute valgrind ./ingen $@ diff --git a/src/runtime_paths.cpp b/src/runtime_paths.cpp deleted file mode 100644 index 8dbe5c0c..00000000 --- a/src/runtime_paths.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <climits> -#include <cstdlib> -#include <cstdlib> -#include <sstream> -#include <string> - -#include <dlfcn.h> - -#include "ingen/runtime_paths.hpp" -#include "ingen/FilePath.hpp" - -#include "ingen_config.h" - -namespace Ingen { - -static FilePath bundle_path; - -#if defined(__APPLE__) -const char search_path_separator = ':'; -static const char* const library_prefix = "lib"; -static const char* const library_suffix = ".dylib"; -#elif defined(_WIN32) && !defined(__CYGWIN__) -const char search_path_separator = ';'; -static const char* const library_prefix = ""; -static const char* const library_suffix = ".dll"; -#else -const char search_path_separator = ':'; -static const char* const library_prefix = "lib"; -static const char* const library_suffix = ".so"; -#endif - -/** Must be called once at startup, and passed a pointer to a function - * that lives in the 'top level' of the bundle (e.g. the executable). - * Passing a function defined in a module etc. will not work! - */ -void -set_bundle_path_from_code(void* function) -{ - Dl_info dli; - dladdr(function, &dli); - -#ifdef BUNDLE - char bin_loc[PATH_MAX]; - realpath(dli.dli_fname, bin_loc); -#else - const char* bin_loc = dli.dli_fname; -#endif - - bundle_path = FilePath(bin_loc).parent_path(); -} - -void -set_bundle_path(const char* path) -{ - bundle_path = FilePath(path); -} - -/** Return the absolute path of a file in an Ingen LV2 bundle - */ -FilePath -bundle_file_path(const std::string& name) -{ - return bundle_path / name; -} - -/** Return the absolute path of a 'resource' file. - */ -FilePath -data_file_path(const std::string& name) -{ -#ifdef BUNDLE - return bundle_path / INGEN_DATA_DIR / name; -#else - return FilePath(INGEN_DATA_DIR) / name; -#endif -} - -/** Return the absolute path of a module (dynamically loaded shared library). - */ -FilePath -ingen_module_path(const std::string& name, FilePath dir) -{ - FilePath ret; - if (dir.empty()) { -#ifdef BUNDLE - dir = FilePath(bundle_path) / INGEN_MODULE_DIR; -#else - dir = FilePath(INGEN_MODULE_DIR); -#endif - } - - return dir / - (std::string(library_prefix) + "ingen_" + name + library_suffix); -} - -FilePath -user_config_dir() -{ - const char* const xdg_config_home = getenv("XDG_CONFIG_HOME"); - const char* const home = getenv("HOME"); - - if (xdg_config_home) { - return FilePath(xdg_config_home); - } else if (home) { - return FilePath(home) / ".config"; - } - - return FilePath(); -} - -std::vector<FilePath> -system_config_dirs() -{ - const char* const xdg_config_dirs = getenv("XDG_CONFIG_DIRS"); - - std::vector<FilePath> paths; - if (xdg_config_dirs) { - std::istringstream ss(xdg_config_dirs); - std::string entry; - while (std::getline(ss, entry, search_path_separator)) { - paths.emplace_back(entry); - } - } else { - paths.emplace_back("/etc/xdg"); - } - - return paths; -} - -} // namespace Ingen diff --git a/src/server/ArcImpl.cpp b/src/server/ArcImpl.cpp deleted file mode 100644 index 5b96ca03..00000000 --- a/src/server/ArcImpl.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/URIs.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" - -#include "ArcImpl.hpp" -#include "BlockImpl.hpp" -#include "Buffer.hpp" -#include "BufferFactory.hpp" -#include "Engine.hpp" -#include "InputPort.hpp" -#include "OutputPort.hpp" -#include "PortImpl.hpp" - -namespace Ingen { -namespace Server { - -/** Constructor for an arc from a block's output port. - * - * This handles both polyphonic and monophonic blocks, transparently to the - * user (InputPort). - */ -ArcImpl::ArcImpl(PortImpl* tail, PortImpl* head) - : _tail(tail) - , _head(head) -{ - assert(tail != head); - assert(tail->path() != head->path()); -} - -ArcImpl::~ArcImpl() -{ - if (is_linked()) { - InputPort* iport = dynamic_cast<InputPort*>(_head); - if (iport) { - iport->remove_arc(*this); - } - } -} - -const Raul::Path& -ArcImpl::tail_path() const -{ - return _tail->path(); -} - -const Raul::Path& -ArcImpl::head_path() const -{ - return _head->path(); -} - -BufferRef -ArcImpl::buffer(uint32_t voice, SampleCount offset) const -{ - return _tail->buffer(std::min(voice, _tail->poly() - 1)); -} - -bool -ArcImpl::must_mix() const -{ - return (_tail->poly() > _head->poly() || - (_tail->buffer(0)->is_sequence() != _head->buffer(0)->is_sequence())); -} - -bool -ArcImpl::can_connect(const PortImpl* src, const InputPort* dst) -{ - const Ingen::URIs& uris = src->bufs().uris(); - return ( - // (Audio | Control | CV) => (Audio | Control | CV) - ( (src->is_a(PortType::ID::CONTROL) || - src->is_a(PortType::ID::AUDIO) || - src->is_a(PortType::ID::CV)) - && (dst->is_a(PortType::ID::CONTROL) - || dst->is_a(PortType::ID::AUDIO) - || dst->is_a(PortType::ID::CV))) - - // Equal types - || (src->type() == dst->type() && - src->buffer_type() == dst->buffer_type()) - - // Control => atom:Float Value - || (src->is_a(PortType::ID::CONTROL) && dst->supports(uris.atom_Float)) - - // Audio => atom:Sound Value - || (src->is_a(PortType::ID::AUDIO) && dst->supports(uris.atom_Sound)) - - // atom:Float Value => Control - || (src->supports(uris.atom_Float) && dst->is_a(PortType::ID::CONTROL)) - - // atom:Float Value => CV - || (src->supports(uris.atom_Float) && dst->is_a(PortType::ID::CV)) - - // atom:Sound Value => Audio - || (src->supports(uris.atom_Sound) && dst->is_a(PortType::ID::AUDIO))); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/ArcImpl.hpp b/src/server/ArcImpl.hpp deleted file mode 100644 index 40a6d179..00000000 --- a/src/server/ArcImpl.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_ARC_IMPL_HPP -#define INGEN_ENGINE_ARC_IMPL_HPP - -#include <cstdlib> - -#include <boost/intrusive/slist.hpp> - -#include "ingen/Arc.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "raul/Deletable.hpp" - -#include "BufferRef.hpp" -#include "RunContext.hpp" - -namespace Ingen { -namespace Server { - -class PortImpl; -class InputPort; - -/** Represents a single inbound connection for an InputPort. - * - * This can be a group of ports (coming from a polyphonic Block) or - * a single Port. This class exists basically as an abstraction of mixing - * down polyphonic inputs, so InputPort can just deal with mixing down - * multiple connections (oblivious to the polyphonic situation of the - * connection itself). - * - * This is stored in an intrusive slist in InputPort. - * - * \ingroup engine - */ -class ArcImpl - : private Raul::Noncopyable - , public Arc - , public boost::intrusive::slist_base_hook<> -{ -public: - ArcImpl(PortImpl* tail, PortImpl* head); - ~ArcImpl(); - - inline PortImpl* tail() const { return _tail; } - inline PortImpl* head() const { return _head; } - - const Raul::Path& tail_path() const; - const Raul::Path& head_path() const; - - /** Get the buffer for a particular voice. - * An Arc is smart - it knows the destination port requesting the - * buffer, and will return accordingly (e.g. the same buffer for every - * voice in a mono->poly arc). - */ - BufferRef buffer(uint32_t voice, SampleCount offset=0) const; - - /** Whether this arc must mix down voices into a local buffer */ - bool must_mix() const; - - static bool can_connect(const PortImpl* src, const InputPort* dst); - -protected: - PortImpl* const _tail; - PortImpl* const _head; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_ARC_IMPL_HPP diff --git a/src/server/BlockFactory.cpp b/src/server/BlockFactory.cpp deleted file mode 100644 index 7dcfd6af..00000000 --- a/src/server/BlockFactory.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cstdlib> - -#include "lilv/lilv.h" - -#include "ingen/LV2Features.hpp" -#include "ingen/Log.hpp" -#include "ingen/World.hpp" -#include "internals/BlockDelay.hpp" -#include "internals/Controller.hpp" -#include "internals/Note.hpp" -#include "internals/Time.hpp" -#include "internals/Trigger.hpp" - -#include "BlockFactory.hpp" -#include "InternalPlugin.hpp" -#include "LV2Plugin.hpp" -#include "ThreadManager.hpp" - -namespace Ingen { -namespace Server { - -using namespace Internals; - -BlockFactory::BlockFactory(Ingen::World* world) - : _world(world) - , _has_loaded(false) -{ - load_internal_plugins(); -} - -BlockFactory::~BlockFactory() -{ - for (auto& p : _plugins) { - delete p.second; - } - - _plugins.clear(); -} - -const BlockFactory::Plugins& -BlockFactory::plugins() -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - if (!_has_loaded) { - load_lv2_plugins(); - _has_loaded = true; - } - return _plugins; -} - -std::set<PluginImpl*> -BlockFactory::refresh() -{ - // Record current plugins, and those that are currently zombies - const Plugins old_plugins(_plugins); - std::set<PluginImpl*> zombies; - for (const auto& p : _plugins) { - if (p.second->is_zombie()) { - zombies.insert(p.second); - } - } - - // Re-load plugins - load_lv2_plugins(); - - // Add any new plugins to response - std::set<PluginImpl*> new_plugins; - for (const auto& p : _plugins) { - auto o = old_plugins.find(p.first); - if (o == old_plugins.end()) { - new_plugins.insert(p.second); - } - } - - // Add any resurrected plugins to response - for (const auto& z : zombies) { - if (!z->is_zombie()) { - new_plugins.insert(z); - } - } - - return new_plugins; -} - -PluginImpl* -BlockFactory::plugin(const URI& uri) -{ - load_plugin(uri); - const Plugins::const_iterator i = _plugins.find(uri); - return ((i != _plugins.end()) ? i->second : nullptr); -} - -void -BlockFactory::load_internal_plugins() -{ - Ingen::URIs& uris = _world->uris(); - InternalPlugin* block_delay_plug = BlockDelayNode::internal_plugin(uris); - _plugins.emplace(block_delay_plug->uri(), block_delay_plug); - - InternalPlugin* controller_plug = ControllerNode::internal_plugin(uris); - _plugins.emplace(controller_plug->uri(), controller_plug); - - InternalPlugin* note_plug = NoteNode::internal_plugin(uris); - _plugins.emplace(note_plug->uri(), note_plug); - - InternalPlugin* time_plug = TimeNode::internal_plugin(uris); - _plugins.emplace(time_plug->uri(), time_plug); - - InternalPlugin* trigger_plug = TriggerNode::internal_plugin(uris); - _plugins.emplace(trigger_plug->uri(), trigger_plug); -} - -void -BlockFactory::load_plugin(const URI& uri) -{ - if (_has_loaded || _plugins.find(uri) != _plugins.end()) { - return; - } - - LilvNode* node = lilv_new_uri(_world->lilv_world(), uri.c_str()); - const LilvPlugins* plugs = lilv_world_get_all_plugins(_world->lilv_world()); - const LilvPlugin* plug = lilv_plugins_get_by_uri(plugs, node); - if (plug) { - LV2Plugin* const ingen_plugin = new LV2Plugin(_world, plug); - _plugins.emplace(uri, ingen_plugin); - } - lilv_node_free(node); -} - -/** Loads information about all LV2 plugins into internal plugin database. - */ -void -BlockFactory::load_lv2_plugins() -{ - // Build an array of port type nodes for checking compatibility - typedef std::vector< SPtr<LilvNode> > Types; - Types types; - for (unsigned t = PortType::ID::AUDIO; t <= PortType::ID::ATOM; ++t) { - const URI& uri(PortType((PortType::ID)t).uri()); - types.push_back( - SPtr<LilvNode>(lilv_new_uri(_world->lilv_world(), uri.c_str()), - lilv_node_free)); - } - - const LilvPlugins* plugins = lilv_world_get_all_plugins(_world->lilv_world()); - LILV_FOREACH(plugins, i, plugins) { - const LilvPlugin* lv2_plug = lilv_plugins_get(plugins, i); - const URI uri(lilv_node_as_uri(lilv_plugin_get_uri(lv2_plug))); - - // Ignore plugins that require features Ingen doesn't support - LilvNodes* features = lilv_plugin_get_required_features(lv2_plug); - bool supported = true; - LILV_FOREACH(nodes, f, features) { - const char* feature = lilv_node_as_uri(lilv_nodes_get(features, f)); - if (!_world->lv2_features().is_supported(feature)) { - supported = false; - _world->log().warn( - fmt("Ignoring <%1%>; required feature <%2%>\n") - % uri % feature); - break; - } - } - lilv_nodes_free(features); - if (!supported) { - continue; - } - - // Ignore plugins that are missing ports - if (!lilv_plugin_get_port_by_index(lv2_plug, 0)) { - _world->log().warn( - fmt("Ignoring <%1%>; missing or corrupt ports\n") % uri); - continue; - } - - const uint32_t n_ports = lilv_plugin_get_num_ports(lv2_plug); - for (uint32_t p = 0; p < n_ports; ++p) { - const LilvPort* port = lilv_plugin_get_port_by_index(lv2_plug, p); - supported = false; - for (const auto& t : types) { - if (lilv_port_is_a(lv2_plug, port, t.get())) { - supported = true; - break; - } - } - if (!supported && - !lilv_port_has_property(lv2_plug, - port, - _world->uris().lv2_connectionOptional)) { - _world->log().warn( - fmt("Ignoring <%1%>; unsupported port <%2%>\n") - % uri % lilv_node_as_string( - lilv_port_get_symbol(lv2_plug, port))); - break; - } - } - if (!supported) { - continue; - } - - auto p = _plugins.find(uri); - if (p == _plugins.end()) { - LV2Plugin* const plugin = new LV2Plugin(_world, lv2_plug); - _plugins.emplace(uri, plugin); - } else if (lilv_plugin_verify(lv2_plug)) { - p->second->set_is_zombie(false); - } - } - - _world->log().info(fmt("Loaded %1% plugins\n") % _plugins.size()); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/BlockFactory.hpp b/src/server/BlockFactory.hpp deleted file mode 100644 index 25885f75..00000000 --- a/src/server/BlockFactory.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_BLOCKFACTORY_HPP -#define INGEN_ENGINE_BLOCKFACTORY_HPP - -#include <map> -#include <set> - -#include "ingen/World.hpp" -#include "ingen/types.hpp" -#include "raul/Noncopyable.hpp" - -namespace Ingen { -namespace Server { - -class PluginImpl; - -/** Discovers and loads plugin libraries. - * - * \ingroup engine - */ -class BlockFactory : public Raul::Noncopyable -{ -public: - explicit BlockFactory(Ingen::World* world); - ~BlockFactory(); - - /** Reload plugin list. - * - * @return The set of newly loaded plugins. - */ - std::set<PluginImpl*> refresh(); - - void load_plugin(const URI& uri); - - typedef std::map<URI, PluginImpl*> Plugins; - const Plugins& plugins(); - - PluginImpl* plugin(const URI& uri); - -private: - void load_lv2_plugins(); - void load_internal_plugins(); - - Plugins _plugins; - Ingen::World* _world; - bool _has_loaded; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_BLOCKFACTORY_HPP diff --git a/src/server/BlockImpl.cpp b/src/server/BlockImpl.cpp deleted file mode 100644 index e95645f9..00000000 --- a/src/server/BlockImpl.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <cstdint> - -#include "raul/Array.hpp" - -#include "Buffer.hpp" -#include "Engine.hpp" -#include "BlockImpl.hpp" -#include "GraphImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "RunContext.hpp" -#include "ThreadManager.hpp" - -namespace Ingen { -namespace Server { - -BlockImpl::BlockImpl(PluginImpl* plugin, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate) - : NodeImpl(plugin->uris(), parent, symbol) - , _plugin(plugin) - , _polyphony((polyphonic && parent) ? parent->internal_poly() : 1) - , _mark(Mark::UNVISITED) - , _polyphonic(polyphonic) - , _activated(false) - , _enabled(true) -{ - assert(_plugin); - assert(_polyphony > 0); -} - -BlockImpl::~BlockImpl() -{ - if (_activated) { - deactivate(); - } - - if (is_linked()) { - parent_graph()->remove_block(*this); - } -} - -Node* -BlockImpl::port(uint32_t index) const -{ - return (*_ports)[index]; -} - -const Resource* -BlockImpl::plugin() const -{ - return _plugin; -} - -const PluginImpl* -BlockImpl::plugin_impl() const -{ - return _plugin; -} - -void -BlockImpl::activate(BufferFactory& bufs) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - _activated = true; - for (uint32_t p = 0; p < num_ports(); ++p) { - PortImpl* const port = _ports->at(p); - port->activate(bufs); - } -} - -void -BlockImpl::deactivate() -{ - _activated = false; - for (uint32_t p = 0; p < num_ports(); ++p) { - PortImpl* const port = _ports->at(p); - port->deactivate(); - } -} - -bool -BlockImpl::prepare_poly(BufferFactory& bufs, uint32_t poly) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - if (!_polyphonic) { - poly = 1; - } - - if (_ports) { - for (uint32_t i = 0; i < _ports->size(); ++i) { - _ports->at(i)->prepare_poly(bufs, poly); - } - } - - return true; -} - -bool -BlockImpl::apply_poly(RunContext& context, uint32_t poly) -{ - if (!_polyphonic) { - poly = 1; - } - - _polyphony = poly; - - if (_ports) { - for (uint32_t i = 0; i < num_ports(); ++i) { - _ports->at(i)->apply_poly(context, poly); - } - } - - return true; -} - -void -BlockImpl::set_buffer_size(RunContext& context, - BufferFactory& bufs, - LV2_URID type, - uint32_t size) -{ - if (_ports) { - for (uint32_t i = 0; i < _ports->size(); ++i) { - PortImpl* const p = _ports->at(i); - if (p->buffer_type() == type) { - p->set_buffer_size(context, bufs, size); - } - } - } -} - -PortImpl* -BlockImpl::nth_port_by_type(uint32_t n, bool input, PortType type) -{ - uint32_t count = 0; - for (uint32_t i = 0; _ports && i < _ports->size(); ++i) { - PortImpl* const port = _ports->at(i); - if (port->is_input() == input && port->type() == type) { - if (count++ == n) { - return port; - } - } - } - return nullptr; -} - -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 nullptr; -} - -void -BlockImpl::pre_process(RunContext& context) -{ - // Mix down input ports - for (uint32_t i = 0; i < num_ports(); ++i) { - PortImpl* const port = _ports->at(i); - port->pre_process(context); - port->connect_buffers(); - } -} - -void -BlockImpl::bypass(RunContext& context) -{ - if (!_ports) { - return; - } - - // Prepare port buffers for reading, converting/mixing if necessary - for (uint32_t i = 0; i < _ports->size(); ++i) { - _ports->at(i)->connect_buffers(); - _ports->at(i)->pre_run(context); - } - - // Dumb bypass - for (PortType t : { PortType::AUDIO, PortType::CV, PortType::ATOM }) { - for (uint32_t i = 0;; ++i) { - PortImpl* in = nth_port_by_type(i, true, t); - PortImpl* out = nth_port_by_type(i, false, t); - if (!out) { - break; // Finished writing all outputs - } else if (in) { - // Copy corresponding input to output - for (uint32_t v = 0; v < _polyphony; ++v) { - out->buffer(v)->copy(context, in->buffer(v).get()); - } - } else { - // Output but no corresponding input, clear - for (uint32_t v = 0; v < _polyphony; ++v) { - out->buffer(v)->clear(); - } - } - } - } - post_process(context); -} - -void -BlockImpl::process(RunContext& context) -{ - pre_process(context); - - if (!_enabled) { - bypass(context); - post_process(context); - return; - } - - RunContext subcontext(context); - for (SampleCount offset = 0; offset < context.nframes();) { - // Find earliest offset of a value change - SampleCount chunk_end = context.nframes(); - for (uint32_t i = 0; _ports && i < _ports->size(); ++i) { - PortImpl* const port = _ports->at(i); - if (port->type() == PortType::CONTROL && port->is_input()) { - const SampleCount o = port->next_value_offset( - offset, context.nframes()); - if (o < chunk_end) { - chunk_end = o; - } - } - } - - // Slice context into a chunk from now until the next change - subcontext.slice(offset, chunk_end - offset); - - // Prepare port buffers for reading, converting/mixing if necessary - for (uint32_t i = 0; _ports && i < _ports->size(); ++i) { - _ports->at(i)->connect_buffers(offset); - _ports->at(i)->pre_run(subcontext); - } - - // Run the chunk - run(subcontext); - - // Emit control port outputs as events - for (uint32_t i = 0; _ports && i < _ports->size(); ++i) { - PortImpl* const port = _ports->at(i); - if (port->type() == PortType::CONTROL && port->is_output()) { - // TODO: Only emit events when value has actually changed? - for (uint32_t v = 0; v < _polyphony; ++v) { - port->buffer(v)->append_event(offset, port->buffer(v)->value()); - } - } - } - - offset = chunk_end; - subcontext.slice(offset, chunk_end - offset); - } - - post_process(context); -} - -void -BlockImpl::post_process(RunContext& context) -{ - // Write output ports - for (uint32_t i = 0; _ports && i < _ports->size(); ++i) { - _ports->at(i)->post_process(context); - } -} - -void -BlockImpl::set_port_buffer(uint32_t voice, - uint32_t port_num, - BufferRef buf, - SampleCount offset) -{ - /*std::cout << path() << " set port " << port_num << " voice " << voice - << " buffer " << buf << " offset " << offset << std::endl;*/ -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/BlockImpl.hpp b/src/server/BlockImpl.hpp deleted file mode 100644 index d663e319..00000000 --- a/src/server/BlockImpl.hpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_BLOCKIMPL_HPP -#define INGEN_ENGINE_BLOCKIMPL_HPP - -#include <set> - -#include <boost/intrusive/slist.hpp> -#include <boost/optional.hpp> - -#include "lilv/lilv.h" - -#include "raul/Array.hpp" - -#include "BufferRef.hpp" -#include "NodeImpl.hpp" -#include "PluginImpl.hpp" -#include "PortType.hpp" -#include "RunContext.hpp" -#include "types.hpp" - -namespace Raul { -class Maid; -} - -namespace Ingen { -namespace Server { - -class Buffer; -class BufferFactory; -class Engine; -class GraphImpl; -class PluginImpl; -class PortImpl; -class RunContext; -class Worker; - -/** A Block in a Graph (which is also a Block). - * - * This is what is often called a "Module" in modular synthesizers. A Block is - * a unit with input/output ports, a process() method, and some other things. - * - * \ingroup engine - */ -class BlockImpl : public NodeImpl - , public boost::intrusive::slist_base_hook<> // In GraphImpl -{ -public: - typedef Raul::Array<PortImpl*> Ports; - - BlockImpl(PluginImpl* plugin, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate rate); - - virtual ~BlockImpl(); - - virtual GraphType graph_type() const { return GraphType::BLOCK; } - - /** Activate this Block. - * - * This function must be called in a non-realtime thread before it is - * inserted in to a graph. Any non-realtime actions that need to be - * done before the Block is ready for use should be done here. - */ - virtual void activate(BufferFactory& bufs); - - /** Deactivate this Block. - * - * This function must be called in a non-realtime thread after the - * block has been removed from its graph (i.e. processing is finished). - */ - virtual void deactivate(); - - /** Duplicate this Node. */ - virtual BlockImpl* duplicate(Engine& engine, - const Raul::Symbol& symbol, - GraphImpl* parent) { return nullptr; } - - /** Return true iff this block is activated */ - bool activated() const { return _activated; } - - /** Return true iff this block is enabled (not bypassed). */ - bool enabled() const { return _enabled; } - - /** 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 URI& uri) { return nullptr; } - - /** Restore `state`. */ - virtual void apply_state(const UPtr<Worker>& worker, const LilvState* state) {} - - /** Save current state as preset. */ - virtual boost::optional<Resource> - save_preset(const URI& bundle, - const Properties& props) { return boost::optional<Resource>(); } - - /** Learn the next incoming MIDI event (for internals) */ - virtual void learn() {} - - /** Do whatever needs doing in the process thread before process() is called */ - virtual void pre_process(RunContext& context); - - /** Run block for an entire process cycle (calls run()). */ - virtual void process(RunContext& context); - - /** Bypass block for an entire process cycle (called from process()). */ - virtual void bypass(RunContext& context); - - /** Run block for a portion of process cycle (called from process()). */ - virtual void run(RunContext& context) = 0; - - /** Do whatever needs doing in the process thread after process() is called */ - virtual void post_process(RunContext& context); - - /** Set the buffer of a port to a given buffer (e.g. connect plugin to buffer) */ - virtual void set_port_buffer(uint32_t voice, - uint32_t port_num, - BufferRef buf, - SampleCount offset); - - 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::set<BlockImpl*>& providers() { return _providers; } - - /** Blocks that are connected to this Block's outputs. */ - std::set<BlockImpl*>& dependants() { return _dependants; } - - /** Flag block as polyphonic. - * - * Note this will not actually allocate voices etc., prepare_poly - * and apply_poly must be called after this function to truly make - * a block polyphonic. - */ - virtual void set_polyphonic(bool p) { _polyphonic = p; } - - virtual bool prepare_poly(BufferFactory& bufs, uint32_t poly); - virtual bool apply_poly(RunContext& context, uint32_t poly); - - /** Information about the Plugin this Block is an instance of. - * Not the best name - not all blocks come from plugins (ie Graph) - */ - virtual const Resource* plugin() const; - - /** Information about the Plugin this Block is an instance of. - * Not the best name - not all blocks come from plugins (ie Graph) - */ - virtual const PluginImpl* plugin_impl() const; - - virtual void plugin(PluginImpl* pi) { _plugin = pi; } - - virtual void set_buffer_size(RunContext& context, - BufferFactory& bufs, - LV2_URID type, - uint32_t size); - - /** The Graph this Block belongs to. */ - inline GraphImpl* parent_graph() const { return (GraphImpl*)_parent; } - - uint32_t num_ports() const { return _ports ? _ports->size() : 0; } - virtual uint32_t polyphony() const { return _polyphony; } - - /** Mark used during graph compilation */ - enum class Mark { UNVISITED, VISITING, VISITED }; - Mark get_mark() const { return _mark; } - void set_mark(Mark m) { _mark = m; } - -protected: - PortImpl* nth_port_by_type(uint32_t n, bool input, PortType type); - - PluginImpl* _plugin; - MPtr<Ports> _ports; ///< Access in audio thread only - uint32_t _polyphony; - std::set<BlockImpl*> _providers; ///< Blocks connected to this one's input ports - std::set<BlockImpl*> _dependants; ///< Blocks this one's output ports are connected to - Mark _mark; ///< Mark for graph compilation algorithm - bool _polyphonic; - bool _activated; - bool _enabled; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_BLOCKIMPL_HPP diff --git a/src/server/Broadcaster.cpp b/src/server/Broadcaster.cpp deleted file mode 100644 index 00fefddd..00000000 --- a/src/server/Broadcaster.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <utility> - -#include "ingen/Interface.hpp" - -#include "Broadcaster.hpp" -#include "PluginImpl.hpp" -#include "BlockFactory.hpp" - -namespace Ingen { -namespace Server { - -Broadcaster::Broadcaster() - : _must_broadcast(false) - , _bundle_depth(0) -{} - -Broadcaster::~Broadcaster() -{ - std::lock_guard<std::mutex> lock(_clients_mutex); - _clients.clear(); - _broadcastees.clear(); -} - -/** Register a client to receive messages over the notification band. - */ -void -Broadcaster::register_client(SPtr<Interface> client) -{ - std::lock_guard<std::mutex> lock(_clients_mutex); - _clients.insert(client); -} - -/** Remove a client from the list of registered clients. - * - * @return true if client was found and removed. - */ -bool -Broadcaster::unregister_client(SPtr<Interface> client) -{ - std::lock_guard<std::mutex> lock(_clients_mutex); - const size_t erased = _clients.erase(client); - _broadcastees.erase(client); - return (erased > 0); -} - -void -Broadcaster::set_broadcast(SPtr<Interface> client, bool broadcast) -{ - if (broadcast) { - _broadcastees.insert(client); - } else { - _broadcastees.erase(client); - } - _must_broadcast.store(!_broadcastees.empty()); -} - -void -Broadcaster::send_plugins(const BlockFactory::Plugins& plugins) -{ - std::lock_guard<std::mutex> lock(_clients_mutex); - for (const auto& c : _clients) { - send_plugins_to(c.get(), plugins); - } -} - -void -Broadcaster::send_plugins_to(Interface* client, - const BlockFactory::Plugins& plugins) -{ - client->bundle_begin(); - - for (const auto& p : plugins) { - const PluginImpl* const plugin = p.second; - client->put(plugin->uri(), plugin->properties()); - } - - client->bundle_end(); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/Broadcaster.hpp b/src/server/Broadcaster.hpp deleted file mode 100644 index 3981b265..00000000 --- a/src/server/Broadcaster.hpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_BROADCASTER_HPP -#define INGEN_ENGINE_BROADCASTER_HPP - -#include <atomic> -#include <list> -#include <mutex> -#include <set> -#include <string> - -#include "ingen/Interface.hpp" -#include "ingen/types.hpp" - -#include "BlockFactory.hpp" - -namespace Ingen { -namespace Server { - -/** Broadcaster for all clients. - * - * This is an Interface that forwards all messages to all registered - * clients (for updating all clients on state changes in the engine). - * - * \ingroup engine - */ -class Broadcaster : public Interface -{ -public: - Broadcaster(); - ~Broadcaster(); - - void register_client(SPtr<Interface> client); - bool unregister_client(SPtr<Interface> client); - - void set_broadcast(SPtr<Interface> client, bool broadcast); - - /** Ignore a client when broadcasting. - * - * This is used to prevent feeding back updates to the client that - * initiated a property set in the first place. - */ - void set_ignore_client(SPtr<Interface> client) { _ignore_client = client; } - void clear_ignore_client() { _ignore_client.reset(); } - - /** Return true iff there are any clients with broadcasting enabled. - * - * This is used in the audio thread to decide whether or not notifications - * should be calculated and emitted. - */ - bool must_broadcast() const { return _must_broadcast; } - - /** A handle that represents a transfer of possibly several changes. - * - * This object going out of scope signifies the transfer is completed. - * This makes doing the right thing in recursive functions that send - * updates simple (e.g. Event::post_process()). - */ - class Transfer : public Raul::Noncopyable { - public: - explicit Transfer(Broadcaster& b) : broadcaster(b) { - if (++broadcaster._bundle_depth == 1) { - broadcaster.bundle_begin(); - } - } - ~Transfer() { - if (--broadcaster._bundle_depth == 0) { - broadcaster.bundle_end(); - } - } - Broadcaster& broadcaster; - }; - - void send_plugins(const BlockFactory::Plugins& plugins); - void send_plugins_to(Interface*, const BlockFactory::Plugins& plugins); - - void message(const Message& msg) override { - std::lock_guard<std::mutex> lock(_clients_mutex); - for (const auto& c : _clients) { - if (c != _ignore_client) { - c->message(msg); - } - } - } - - URI uri() const override { return URI("ingen:/broadcaster"); } - -private: - friend class Transfer; - - typedef std::set<SPtr<Interface>> Clients; - - std::mutex _clients_mutex; - Clients _clients; - std::set< SPtr<Interface> > _broadcastees; - std::atomic<bool> _must_broadcast; - unsigned _bundle_depth; - SPtr<Interface> _ignore_client; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_BROADCASTER_HPP diff --git a/src/server/Buffer.cpp b/src/server/Buffer.cpp deleted file mode 100644 index 34867fa3..00000000 --- a/src/server/Buffer.cpp +++ /dev/null @@ -1,468 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#define __STDC_LIMIT_MACROS 1 - -#include <cmath> -#include <cstdint> -#include <cstring> -#include <new> - -#ifdef __SSE__ -# include <xmmintrin.h> -#endif - -#include "ingen/URIMap.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" -#include "ingen_config.h" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "ingen/Log.hpp" - -#include "Buffer.hpp" -#include "BufferFactory.hpp" -#include "Engine.hpp" -#include "RunContext.hpp" - -namespace Ingen { -namespace Server { - -Buffer::Buffer(BufferFactory& bufs, - LV2_URID type, - LV2_URID value_type, - uint32_t capacity, - bool external, - void* buf) - : _factory(bufs) - , _next(nullptr) - , _buf(external ? nullptr : aligned_alloc(capacity)) - , _latest_event(0) - , _type(type) - , _value_type(value_type) - , _capacity(capacity) - , _refs(0) - , _external(external) -{ - if (!external && !_buf) { - bufs.engine().log().rt_error("Failed to allocate buffer\n"); - throw std::bad_alloc(); - } - - if (type != bufs.uris().atom_Sound) { - /* Audio buffers are not atoms, the buffer is the start of a float - array which is already silent since the buffer is zeroed. All other - buffers are atoms. */ - if (_buf) { - LV2_Atom* atom = get<LV2_Atom>(); - atom->size = capacity - sizeof(LV2_Atom); - atom->type = type; - - clear(); - } - - if (value_type && value_type != type) { - /* Buffer with a different value type. These buffers (probably - sequences) have a "value" that persists independently of the buffer - contents. This is used to represent things like a Sequence of - Float, which acts like an individual float (has a value), but the - buffer itself only transmits changes and does not necessarily - contain the current value. */ - _value_buffer = bufs.get_buffer(value_type, 0, 0); - } - } -} - -Buffer::~Buffer() -{ - if (!_external) { - free(_buf); - } -} - -void -Buffer::recycle() -{ - _factory.recycle(this); -} - -void -Buffer::set_type(GetFn get, LV2_URID type, LV2_URID value_type) -{ - _type = type; - _value_type = value_type; - if (type == _factory.uris().atom_Sequence && value_type) { - _value_buffer = (_factory.*get)(value_type, 0, 0); - } -} - -void -Buffer::clear() -{ - if (is_audio() && _buf) { - memset(_buf, 0, _capacity); - } else if (is_control()) { - get<LV2_Atom_Float>()->body = 0; - } else if (is_sequence()) { - LV2_Atom_Sequence* seq = get<LV2_Atom_Sequence>(); - seq->atom.type = _factory.uris().atom_Sequence; - seq->atom.size = sizeof(LV2_Atom_Sequence_Body); - seq->body.unit = 0; - seq->body.pad = 0; - _latest_event = 0; - } -} - -void -Buffer::render_sequence(const RunContext& context, const Buffer* src, bool add) -{ - const LV2_URID atom_Float = _factory.uris().atom_Float; - const LV2_Atom_Sequence* seq = src->get<const LV2_Atom_Sequence>(); - const LV2_Atom_Float* init = (const LV2_Atom_Float*)src->value(); - float value = init ? init->body : 0.0f; - SampleCount offset = context.offset(); - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - if (ev->time.frames >= offset && ev->body.type == atom_Float) { - write_block(value, offset, ev->time.frames, add); - value = ((const LV2_Atom_Float*)&ev->body)->body; - offset = ev->time.frames; - } - } - write_block(value, offset, context.offset() + context.nframes(), add); -} - -void -Buffer::copy(const RunContext& context, const Buffer* src) -{ - if (!_buf) { - return; - } else if (_type == src->type()) { - const uint32_t src_size = src->size(); - if (src_size <= _capacity) { - memcpy(_buf, src->_buf, src_size); - } else { - clear(); - } - } else if (src->is_audio() && is_control()) { - samples()[0] = src->samples()[0]; - } else if (src->is_control() && is_audio()) { - set_block(src->samples()[0], 0, context.nframes()); - } else if (src->is_sequence() && is_audio() && - src->value_type() == _factory.uris().atom_Float) { - render_sequence(context, src, false); - } else { - clear(); - } -} - -void -Buffer::resize(uint32_t capacity) -{ - if (!_external) { - _buf = realloc(_buf, capacity); - _capacity = capacity; - clear(); - } else { - _factory.engine().log().error("Attempt to resize external buffer\n"); - } -} - -void* -Buffer::port_data(PortType port_type, SampleCount offset) -{ - switch (port_type.id()) { - case PortType::ID::CONTROL: - return &_value_buffer->get<LV2_Atom_Float>()->body; - case PortType::ID::CV: - case PortType::ID::AUDIO: - if (_type == _factory.uris().atom_Float) { - return &get<LV2_Atom_Float>()->body; - } else if (_type == _factory.uris().atom_Sound) { - return (Sample*)_buf + offset; - } - break; - case PortType::ID::ATOM: - if (_type != _factory.uris().atom_Sound) { - return _buf; - } - default: break; - } - return nullptr; -} - -const void* -Buffer::port_data(PortType port_type, SampleCount offset) const -{ - return const_cast<void*>( - const_cast<Buffer*>(this)->port_data(port_type, offset)); -} - -#ifdef __SSE__ -/** Vector fabsf */ -static inline __m128 -mm_abs_ps(__m128 x) -{ - const __m128 sign_mask = _mm_set1_ps(-0.0f); // -0.0f = 1 << 31 - return _mm_andnot_ps(sign_mask, x); -} -#endif - -float -Buffer::peak(const RunContext& context) const -{ -#ifdef __SSE__ - const __m128* const vbuf = (const __m128*)samples(); - __m128 vpeak = mm_abs_ps(vbuf[0]); - const SampleCount nblocks = context.nframes() / 4; - - // First, find the vector absolute max of the buffer - for (SampleCount i = 1; i < nblocks; ++i) { - vpeak = _mm_max_ps(vpeak, mm_abs_ps(vbuf[i])); - } - - // Now we need the single max of vpeak - // vpeak = ABCD - // tmp = CDAB - __m128 tmp = _mm_shuffle_ps(vpeak, vpeak, _MM_SHUFFLE(2, 3, 0, 1)); - - // vpeak = MAX(A,C) MAX(B,D) MAX(C,A) MAX(D,B) - vpeak = _mm_max_ps(vpeak, tmp); - - // tmp = BADC of the new vpeak - // tmp = MAX(B,D) MAX(A,C) MAX(D,B) MAX(C,A) - tmp = _mm_shuffle_ps(vpeak, vpeak, _MM_SHUFFLE(1, 0, 3, 2)); - - // vpeak = MAX(MAX(A,C), MAX(B,D)), ... - vpeak = _mm_max_ps(vpeak, tmp); - - // peak = vpeak[0] - float peak; - _mm_store_ss(&peak, vpeak); - - return peak; -#else - const Sample* const buf = samples(); - float peak = 0.0f; - for (SampleCount i = 0; i < context.nframes(); ++i) { - peak = fmaxf(peak, fabsf(buf[i])); - } - return peak; -#endif -} - -void -Buffer::prepare_write(RunContext& context) -{ - if (_type == _factory.uris().atom_Sequence) { - LV2_Atom* atom = get<LV2_Atom>(); - - atom->type = (LV2_URID)_factory.uris().atom_Sequence; - atom->size = sizeof(LV2_Atom_Sequence_Body); - _latest_event = 0; - } -} - -void -Buffer::prepare_output_write(RunContext& context) -{ - if (_type == _factory.uris().atom_Sequence) { - LV2_Atom* atom = get<LV2_Atom>(); - - atom->type = (LV2_URID)_factory.uris().atom_Chunk; - atom->size = _capacity - sizeof(LV2_Atom); - _latest_event = 0; - } -} - -bool -Buffer::append_event(int64_t frames, - uint32_t size, - uint32_t type, - const uint8_t* data) -{ - assert(frames >= _latest_event); - - LV2_Atom* atom = get<LV2_Atom>(); - if (atom->type == _factory.uris().atom_Chunk) { - clear(); // Chunk initialized with prepare_output_write(), clear - } - - if (sizeof(LV2_Atom) + atom->size + lv2_atom_pad_size(size) > _capacity) { - return false; - } - - LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)atom; - LV2_Atom_Event* ev = (LV2_Atom_Event*)( - (uint8_t*)seq + lv2_atom_total_size(&seq->atom)); - - ev->time.frames = frames; - ev->body.size = size; - ev->body.type = type; - memcpy(ev + 1, data, size); - - atom->size += sizeof(LV2_Atom_Event) + lv2_atom_pad_size(size); - - _latest_event = frames; - - return true; -} - -bool -Buffer::append_event(int64_t frames, const LV2_Atom* body) -{ - return append_event(frames, body->size, body->type, (const uint8_t*)(body + 1)); -} - -bool -Buffer::append_event_buffer(const Buffer* buf) -{ - LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)get<LV2_Atom>(); - LV2_Atom_Sequence* bseq = (LV2_Atom_Sequence*)buf->get<LV2_Atom>(); - if (seq->atom.type == _factory.uris().atom_Chunk) { - clear(); // Chunk initialized with prepare_output_write(), clear - } - - const uint32_t total_size = lv2_atom_total_size(&seq->atom); - uint8_t* const end = (uint8_t*)seq + total_size; - const uint32_t n_bytes = bseq->atom.size - sizeof(bseq->body); - if (sizeof(LV2_Atom) + total_size + n_bytes >= _capacity) { - return false; // Not enough space - } - - memcpy(end, bseq + 1, n_bytes); - seq->atom.size += n_bytes; - - _latest_event = std::max(_latest_event, buf->_latest_event); - - return true; -} - -SampleCount -Buffer::next_value_offset(SampleCount offset, SampleCount end) const -{ - if (_type == _factory.uris().atom_Sequence && _value_type) { - const LV2_Atom_Sequence* seq = get<const LV2_Atom_Sequence>(); - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - if (ev->time.frames > offset && - ev->time.frames < end && - ev->body.type == _value_type) { - return ev->time.frames; - } - } - } - - /* For CV buffers, it's possible to scan for a value change here, which for - stepped CV would do the right thing, but in the worst case (e.g. with - sine waves), when connected to a control port would split the cycle for - every frame which isn't feasible. Instead, just return end, so the - cycle will not be split. - - A plugin that takes CV and emits discrete change events, possibly with a - maximum rate or fuzz factor, would allow the user to choose which - behaviour, at the cost of some overhead. - */ - - return end; -} - -const LV2_Atom* -Buffer::value() const -{ - return _value_buffer ? _value_buffer->get<const LV2_Atom>() : nullptr; -} - -void -Buffer::set_value(const Atom& value) -{ - if (!value.is_valid() || !_value_buffer) { - return; - } - - const uint32_t total_size = sizeof(LV2_Atom) + value.size(); - if (total_size > _value_buffer->capacity()) { - _value_buffer = _factory.claim_buffer(value.type(), 0, total_size); - } - - memcpy(_value_buffer->get<LV2_Atom*>(), value.atom(), total_size); -} - -void -Buffer::update_value_buffer(SampleCount offset) -{ - if (!_value_buffer || !_value_type) { - return; - } - - LV2_Atom_Sequence* seq = get<LV2_Atom_Sequence>(); - LV2_Atom_Event* latest = nullptr; - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - if (ev->time.frames > offset) { - break; - } else if (ev->body.type == _value_type) { - latest = ev; - } - } - - if (latest) { - memcpy(_value_buffer->get<LV2_Atom>(), - &latest->body, - lv2_atom_total_size(&latest->body)); - } -} - -#ifndef NDEBUG -void -Buffer::dump_cv(const RunContext& context) const -{ - float value = samples()[0]; - fprintf(stderr, "{ 0000: %.02f\n", value); - for (uint32_t i = 0; i < context.nframes(); ++i) { - if (samples()[i] != value) { - value = samples()[i]; - fprintf(stderr, " %4d: %.02f\n", i, value); - } - } - fprintf(stderr, "}\n"); -} -#endif - -void* Buffer::aligned_alloc(size_t size) -{ -#ifdef HAVE_POSIX_MEMALIGN - void* buf; - if (!posix_memalign((void**)&buf, 16, size)) { - memset(buf, 0, size); - return buf; - } -#else - return (LV2_buf*)calloc(1, size); -#endif - return nullptr; -} - -void -intrusive_ptr_add_ref(Buffer* b) -{ - b->ref(); -} - -void -intrusive_ptr_release(Buffer* b) -{ - b->deref(); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/Buffer.hpp b/src/server/Buffer.hpp deleted file mode 100644 index a95fcd3c..00000000 --- a/src/server/Buffer.hpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_BUFFER_HPP -#define INGEN_ENGINE_BUFFER_HPP - -#include <atomic> -#include <cassert> - -#include "ingen/types.hpp" -#include "ingen/ingen.h" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "lv2/lv2plug.in/ns/ext/urid/urid.h" -#include "raul/Deletable.hpp" - -#include "BufferFactory.hpp" -#include "PortType.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Server { - -class BufferFactory; -class Engine; -class RunContext; - -class INGEN_API Buffer -{ -public: - Buffer(BufferFactory& bufs, - LV2_URID type, - LV2_URID value_type, - uint32_t capacity, - bool external = false, - void* buf = nullptr); - - Buffer(const Buffer&) = delete; - Buffer& operator=(const Buffer&) = delete; - - void clear(); - void resize(uint32_t capacity); - void copy(const RunContext& context, const Buffer* src); - void prepare_write(RunContext& context); - - void* port_data(PortType port_type, SampleCount offset); - const void* port_data(PortType port_type, SampleCount offset) const; - - inline LV2_URID type() const { return _type; } - inline LV2_URID value_type() const { return _value_type; } - inline uint32_t capacity() const { return _capacity; } - inline uint32_t size() const { - return is_audio() ? _capacity : sizeof(LV2_Atom) + get<LV2_Atom>()->size; - } - - typedef BufferRef (BufferFactory::*GetFn)(LV2_URID, LV2_URID, uint32_t); - - /** Set the buffer type and optional value type for this buffer. - * - * @param get Called to get auxiliary buffers if necessary. - * @param type Type of buffer. - * @param value_type Type of values in buffer if applicable (for sequences). - */ - void set_type(GetFn get, LV2_URID type, LV2_URID value_type); - - inline bool is_audio() const { - return _type == _factory.uris().atom_Sound; - } - - inline bool is_control() const { - return _type == _factory.uris().atom_Float; - } - - inline bool is_sequence() const { - return _type == _factory.uris().atom_Sequence; - } - - /// Audio or float buffers only - inline const Sample* samples() const { - if (is_control()) { - return (const Sample*)LV2_ATOM_BODY_CONST(get<LV2_Atom_Float>()); - } else if (is_audio()) { - return (const Sample*)_buf; - } - return nullptr; - } - - /// Audio buffers only - inline Sample* samples() { - if (is_control()) { - return (Sample*)LV2_ATOM_BODY(get<LV2_Atom_Float>()); - } else if (is_audio()) { - return (Sample*)_buf; - } - return nullptr; - } - - /// Numeric buffers only - inline Sample value_at(SampleCount offset) const { - if (is_audio() || is_control()) { - return samples()[offset]; - } else if (_value_buffer) { - return ((LV2_Atom_Float*)value())->body; - } - return 0.0f; - } - - inline void set_block(const Sample val, - const SampleCount start, - const SampleCount end) - { - if (is_sequence()) { - append_event(start, sizeof(val), _factory.uris().atom_Float, - reinterpret_cast<const uint8_t*>( - static_cast<const float*>(&val))); - _value_buffer->get<LV2_Atom_Float>()->body = val; - return; - } - - assert(is_audio() || is_control()); - assert(end <= _capacity / sizeof(Sample)); - // Note: Do not change this without ensuring GCC can still vectorize it - Sample* const buf = samples() + start; - for (SampleCount i = 0; i < (end - start); ++i) { - buf[i] = val; - } - } - - inline void add_block(const Sample val, - const SampleCount start, - const SampleCount end) - { - assert(is_audio() || is_control()); - assert(end <= _capacity / sizeof(Sample)); - // Note: Do not change this without ensuring GCC can still vectorize it - Sample* const buf = samples() + start; - for (SampleCount i = 0; i < (end - start); ++i) { - buf[i] += val; - } - } - - inline void write_block(const Sample val, - const SampleCount start, - const SampleCount end, - const bool add) - { - if (add) { - add_block(val, start, end); - } else { - set_block(val, start, end); - } - } - - /// Audio buffers only - float peak(const RunContext& context) const; - - /// Sequence buffers only - void prepare_output_write(RunContext& context); - - /// Sequence buffers only - bool append_event(int64_t frames, - uint32_t size, - uint32_t type, - const uint8_t* data); - - /// Sequence buffers only - bool append_event(int64_t frames, const LV2_Atom* body); - - /// Sequence buffers only - bool append_event_buffer(const Buffer* buf); - - /// Value buffer for numeric sequences - BufferRef value_buffer() { return _value_buffer; } - - /// Return the current value - const LV2_Atom* value() const; - - /// Set/initialise current value in value buffer - void set_value(const Atom& value); - - /// Return offset of the first value change after `offset` - SampleCount next_value_offset(SampleCount offset, SampleCount end) const; - - /// Update value buffer to value as of offset - void update_value_buffer(SampleCount offset); - - /// Set/add to audio buffer from the Sequence of Float in `src` - void render_sequence(const RunContext& context, const Buffer* src, bool add); - -#ifndef NDEBUG - void dump_cv(const RunContext& context) const; -#endif - - void set_capacity(uint32_t capacity) { _capacity = capacity; } - - void set_buffer(void* buf) { assert(_external); _buf = buf; } - - static void* aligned_alloc(size_t size); - - template<typename T> const T* get() const { return reinterpret_cast<const T*>(_buf); } - template<typename T> T* get() { return reinterpret_cast<T*>(_buf); } - - inline void ref() { ++_refs; } - - inline void deref() { - if ((--_refs) == 0) { - recycle(); - } - } - -private: - friend class BufferFactory; - ~Buffer(); - - void recycle(); - - BufferFactory& _factory; - Buffer* _next; ///< Intrusive linked list for BufferFactory - void* _buf; ///< Actual buffer memory - BufferRef _value_buffer; ///< Value buffer for numeric sequences - int64_t _latest_event; - LV2_URID _type; - LV2_URID _value_type; - uint32_t _capacity; - std::atomic<unsigned> _refs; ///< Intrusive reference count - bool _external; ///< Buffer is externally allocated -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_BUFFER_HPP diff --git a/src/server/BufferFactory.cpp b/src/server/BufferFactory.cpp deleted file mode 100644 index d5d947d0..00000000 --- a/src/server/BufferFactory.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Log.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" - -#include "Buffer.hpp" -#include "BufferFactory.hpp" -#include "Engine.hpp" - -namespace Ingen { -namespace Server { - -BufferFactory::BufferFactory(Engine& engine, URIs& uris) - : _free_audio(nullptr) - , _free_control(nullptr) - , _free_sequence(nullptr) - , _free_object(nullptr) - , _engine(engine) - , _uris(uris) - , _seq_size(0) - , _silent_buffer(nullptr) -{ -} - -BufferFactory::~BufferFactory() -{ - _silent_buffer.reset(); - free_list(_free_audio.load()); - free_list(_free_control.load()); - free_list(_free_sequence.load()); - free_list(_free_object.load()); -} - -Forge& -BufferFactory::forge() -{ - return _engine.world()->forge(); -} - -Raul::Maid& -BufferFactory::maid() -{ - return *_engine.maid(); -} - -void -BufferFactory::free_list(Buffer* head) -{ - while (head) { - Buffer* next = head->_next; - delete head; - head = next; - } -} - -void -BufferFactory::set_block_length(SampleCount block_length) -{ - _silent_buffer = create(_uris.atom_Sound, audio_buffer_size(block_length)); -} - -uint32_t -BufferFactory::audio_buffer_size(SampleCount nframes) -{ - return nframes * sizeof(Sample); -} - -uint32_t -BufferFactory::audio_buffer_size() const -{ - return _engine.block_length() * sizeof(Sample); -} - -uint32_t -BufferFactory::default_size(LV2_URID type) const -{ - if (type == _uris.atom_Float) { - return sizeof(LV2_Atom_Float); - } else if (type == _uris.atom_Sound) { - return audio_buffer_size(_engine.block_length()); - } else if (type == _uris.atom_URID) { - return sizeof(LV2_Atom_URID); - } else if (type == _uris.atom_Sequence) { - if (_seq_size == 0) { - return _engine.sequence_size(); - } else { - return _seq_size; - } - } else { - return 0; - } -} - -Buffer* -BufferFactory::try_get_buffer(LV2_URID type) -{ - std::atomic<Buffer*>& head_ptr = free_list(type); - Buffer* head = nullptr; - Buffer* next; - do { - head = head_ptr.load(); - if (!head) { - break; - } - next = head->_next; - } while (!head_ptr.compare_exchange_weak(head, next)); - - return head; -} - -BufferRef -BufferFactory::get_buffer(LV2_URID type, - LV2_URID value_type, - uint32_t capacity) -{ - Buffer* try_head = try_get_buffer(type); - if (!try_head) { - return create(type, value_type, capacity); - } - - try_head->_next = nullptr; - try_head->set_type(&BufferFactory::get_buffer, type, value_type); - try_head->clear(); - return BufferRef(try_head); -} - -BufferRef -BufferFactory::claim_buffer(LV2_URID type, - LV2_URID value_type, - uint32_t capacity) -{ - Buffer* try_head = try_get_buffer(type); - if (!try_head) { - _engine.world()->log().rt_error("Failed to obtain buffer"); - return BufferRef(); - } - - try_head->_next = nullptr; - try_head->set_type(&BufferFactory::claim_buffer, type, value_type); - return BufferRef(try_head); -} - -BufferRef -BufferFactory::silent_buffer() -{ - return _silent_buffer; -} - -BufferRef -BufferFactory::create(LV2_URID type, LV2_URID value_type, uint32_t capacity) -{ - if (capacity == 0) { - capacity = default_size(type); - } else if (type == _uris.atom_Float) { - capacity = std::max(capacity, (uint32_t)sizeof(LV2_Atom_Float)); - } else if (type == _uris.atom_Sound) { - capacity = std::max(capacity, default_size(_uris.atom_Sound)); - } - - return BufferRef(new Buffer(*this, type, value_type, capacity)); -} - -void -BufferFactory::recycle(Buffer* buf) -{ - std::atomic<Buffer*>& head_ptr = free_list(buf->type()); - Buffer* try_head; - do { - try_head = head_ptr.load(); - buf->_next = try_head; - } while (!head_ptr.compare_exchange_weak(try_head, buf)); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/BufferFactory.hpp b/src/server/BufferFactory.hpp deleted file mode 100644 index 8265fc98..00000000 --- a/src/server/BufferFactory.hpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_BUFFERFACTORY_HPP -#define INGEN_ENGINE_BUFFERFACTORY_HPP - -#include <atomic> -#include <map> -#include <mutex> - -#include "ingen/Atom.hpp" -#include "ingen/Forge.hpp" -#include "ingen/URIs.hpp" -#include "ingen/ingen.h" -#include "ingen/types.hpp" -#include "raul/RingBuffer.hpp" - -#include "BufferRef.hpp" -#include "PortType.hpp" -#include "types.hpp" - -namespace Raul { class Maid; } - -namespace Ingen { - -class URIs; - -namespace Server { - -class Engine; - -class INGEN_API BufferFactory { -public: - BufferFactory(Engine& engine, URIs& uris); - ~BufferFactory(); - - static uint32_t audio_buffer_size(SampleCount nframes); - - uint32_t audio_buffer_size() const; - uint32_t default_size(LV2_URID type) const; - - /** Dynamically allocate a new Buffer. */ - BufferRef create(LV2_URID type, - LV2_URID value_type, - uint32_t capacity = 0); - - /** Get a new buffer, reusing if possible, allocating if otherwise. */ - BufferRef get_buffer(LV2_URID type, - LV2_URID value_type, - uint32_t capacity); - - /** Claim an existing buffer, never allocates, real-time safe. */ - BufferRef claim_buffer(LV2_URID type, - LV2_URID value_type, - uint32_t capacity); - - /** Return a reference to a shared silent buffer. */ - BufferRef silent_buffer(); - - void set_block_length(SampleCount block_length); - void set_seq_size(uint32_t seq_size) { _seq_size = seq_size; } - - Forge& forge(); - Raul::Maid& maid(); - - URIs& uris() { return _uris; } - Engine& engine() { return _engine; } - -private: - friend class Buffer; - void recycle(Buffer* buf); - - Buffer* try_get_buffer(LV2_URID type); - - inline std::atomic<Buffer*>& free_list(LV2_URID type) { - if (type == _uris.atom_Float) { - return _free_control; - } else if (type == _uris.atom_Sound) { - return _free_audio; - } else if (type == _uris.atom_Sequence) { - return _free_sequence; - } else { - return _free_object; - } - } - - void free_list(Buffer* head); - - std::atomic<Buffer*> _free_audio; - std::atomic<Buffer*> _free_control; - std::atomic<Buffer*> _free_sequence; - std::atomic<Buffer*> _free_object; - - std::mutex _mutex; - Engine& _engine; - URIs& _uris; - uint32_t _seq_size; - - BufferRef _silent_buffer; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_BUFFERFACTORY_HPP diff --git a/src/server/BufferRef.hpp b/src/server/BufferRef.hpp deleted file mode 100644 index 2a1cbc27..00000000 --- a/src/server/BufferRef.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_BUFFER_REF_HPP -#define INGEN_ENGINE_BUFFER_REF_HPP - -#include <boost/intrusive_ptr.hpp> - -#include "ingen/ingen.h" - -namespace Ingen { -namespace Server { - -class Buffer; - -typedef boost::intrusive_ptr<Buffer> BufferRef; - -// Defined in Buffer.cpp -INGEN_API void intrusive_ptr_add_ref(Buffer* b); -INGEN_API void intrusive_ptr_release(Buffer* b); - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_BUFFER_REF_HPP diff --git a/src/server/ClientUpdate.cpp b/src/server/ClientUpdate.cpp deleted file mode 100644 index 60dd02e3..00000000 --- a/src/server/ClientUpdate.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Interface.hpp" -#include "ingen/URIs.hpp" - -#include "BlockImpl.hpp" -#include "BufferFactory.hpp" -#include "ClientUpdate.hpp" -#include "GraphImpl.hpp" -#include "PortImpl.hpp" - -namespace Ingen { -namespace Server { - -void -ClientUpdate::put(const URI& uri, - const Properties& props, - Resource::Graph ctx) -{ - const ClientUpdate::Put put = { uri, props, ctx }; - puts.push_back(put); -} - -void -ClientUpdate::put_port(const PortImpl* port) -{ - const URIs& uris = port->bufs().uris(); - if (port->is_a(PortType::CONTROL) || port->is_a(PortType::CV)) { - Properties props = port->properties(); - props.erase(uris.ingen_value); - props.emplace(uris.ingen_value, port->value()); - put(port->uri(), props); - } else { - put(port->uri(), port->properties()); - } -} - -void -ClientUpdate::put_block(const BlockImpl* block) -{ - const PluginImpl* const plugin = block->plugin_impl(); - const URIs& uris = plugin->uris(); - - if (uris.ingen_Graph == plugin->type()) { - put_graph((const GraphImpl*)block); - } else { - put(block->uri(), block->properties()); - for (size_t j = 0; j < block->num_ports(); ++j) { - put_port(block->port_impl(j)); - } - } -} - -void -ClientUpdate::put_graph(const GraphImpl* graph) -{ - put(graph->uri(), - graph->properties(Resource::Graph::INTERNAL), - Resource::Graph::INTERNAL); - - put(graph->uri(), - graph->properties(Resource::Graph::EXTERNAL), - Resource::Graph::EXTERNAL); - - // Enqueue blocks - for (const auto& b : graph->blocks()) { - put_block(&b); - } - - // Enqueue ports - for (uint32_t i = 0; i < graph->num_ports_non_rt(); ++i) { - put_port(graph->port_impl(i)); - } - - // Enqueue arcs - for (const auto& a : graph->arcs()) { - const SPtr<const Arc> arc = a.second; - const Connect connect = { arc->tail_path(), arc->head_path() }; - connects.push_back(connect); - } -} - -void -ClientUpdate::put_plugin(PluginImpl* plugin) -{ - put(plugin->uri(), plugin->properties()); - - for (const auto& p : plugin->presets()) { - put_preset(plugin->uris(), plugin->uri(), p.first, p.second); - } -} - -void -ClientUpdate::put_preset(const URIs& uris, - const URI& plugin, - const URI& preset, - const std::string& label) -{ - const Properties props{ - { uris.rdf_type, uris.pset_Preset.urid }, - { uris.rdfs_label, uris.forge.alloc(label) }, - { uris.lv2_appliesTo, uris.forge.make_urid(plugin) }}; - put(preset, props); -} - -void -ClientUpdate::del(const URI& subject) -{ - dels.push_back(subject); -} - -/** Returns true if a is closer to the root than b. */ -static inline bool -put_higher_than(const ClientUpdate::Put& a, const ClientUpdate::Put& b) -{ - return (std::count(a.uri.begin(), a.uri.end(), '/') < - std::count(b.uri.begin(), b.uri.end(), '/')); -} - -void -ClientUpdate::send(Interface& dest) -{ - // Send deletions - for (const URI& subject : dels) { - dest.del(subject); - } - - // Send puts in increasing depth order so parents are sent first - std::stable_sort(puts.begin(), puts.end(), put_higher_than); - for (const ClientUpdate::Put& put : puts) { - dest.put(put.uri, put.properties, put.ctx); - } - - // Send connections - for (const ClientUpdate::Connect& connect : connects) { - dest.connect(connect.tail, connect.head); - } -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/ClientUpdate.hpp b/src/server/ClientUpdate.hpp deleted file mode 100644 index f1a361f7..00000000 --- a/src/server/ClientUpdate.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_CLIENTUPDATE_HPP -#define INGEN_ENGINE_CLIENTUPDATE_HPP - -#include <string> -#include <vector> - -#include "ingen/Resource.hpp" -#include "raul/Path.hpp" - -namespace Ingen { - -class Interface; -class URIs; - -namespace Server { - -class PortImpl; -class BlockImpl; -class GraphImpl; -class PluginImpl; - -/** A sequence of puts/connects/deletes to update clients. - * - * Events like Get construct this in pre_process() and later send it in - * post_process() to avoid the need to lock. - */ -struct ClientUpdate { - void put(const URI& uri, - const Properties& props, - Resource::Graph ctx = Resource::Graph::DEFAULT); - - void put_port(const PortImpl* port); - void put_block(const BlockImpl* block); - void put_graph(const GraphImpl* graph); - void put_plugin(PluginImpl* plugin); - void put_preset(const URIs& uris, - const URI& plugin, - const URI& preset, - const std::string& label); - - void del(const URI& subject); - - void send(Interface& dest); - - struct Put { - URI uri; - Properties properties; - Resource::Graph ctx; - }; - - struct Connect { - Raul::Path tail; - Raul::Path head; - }; - - std::vector<URI> dels; - std::vector<Put> puts; - std::vector<Connect> connects; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_CLIENTUPDATE_HPP diff --git a/src/server/CompiledGraph.cpp b/src/server/CompiledGraph.cpp deleted file mode 100644 index 35b07935..00000000 --- a/src/server/CompiledGraph.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2015-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <algorithm> - -#include "ingen/ColorContext.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/Log.hpp" -#include "ingen/World.hpp" - -#include "CompiledGraph.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "ThreadManager.hpp" - -namespace Ingen { -namespace Server { - -/** Graph contains ambiguous feedback with no delay nodes. */ -class FeedbackException : public std::exception { -public: - FeedbackException(const BlockImpl* node, const BlockImpl* root=nullptr) - : node(node) - , root(root) - {} - - const BlockImpl* node; - const BlockImpl* root; -}; - -static bool -has_provider_with_many_dependants(BlockImpl* n) -{ - for (BlockImpl* p : n->providers()) { - if (p->dependants().size() > 1) { - return true; - } - } - - return false; -} - -CompiledGraph::CompiledGraph(GraphImpl* graph) - : _master(std::unique_ptr<Task>(new Task(Task::Mode::SEQUENTIAL))) -{ - compile_graph(graph); -} - -MPtr<CompiledGraph> -CompiledGraph::compile(Raul::Maid& maid, GraphImpl& graph) -{ - try { - return maid.make_managed<CompiledGraph>(&graph); - } catch (const FeedbackException& e) { - Log& log = graph.engine().log(); - if (e.node && e.root) { - log.error(fmt("Feedback compiling %1% from %2%\n") - % e.node->path() % e.root->path()); - } else { - log.error(fmt("Feedback compiling %1%\n") - % e.node->path()); - } - return MPtr<CompiledGraph>(); - } -} - -static size_t -num_unvisited_dependants(BlockImpl* block) -{ - size_t count = 0; - for (BlockImpl* b : block->dependants()) { - if (b->get_mark() == BlockImpl::Mark::UNVISITED) { - ++count; - } - } - return count; -} - -static size_t -parallel_depth(BlockImpl* block) -{ - if (has_provider_with_many_dependants(block)) { - return 2; - } - - size_t min_provider_depth = std::numeric_limits<size_t>::max(); - for (auto p : block->providers()) { - min_provider_depth = std::min(min_provider_depth, parallel_depth(p)); - } - - return 2 + min_provider_depth; -} - -void -CompiledGraph::compile_graph(GraphImpl* graph) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - // Start with sink nodes (no outputs, or connected only to graph outputs) - std::set<BlockImpl*> blocks; - for (auto& b : graph->blocks()) { - // Mark all blocks as unvisited initially - b.set_mark(BlockImpl::Mark::UNVISITED); - - if (b.dependants().empty()) { - // Block has no dependants, add to initial working set - blocks.insert(&b); - } - } - - // Keep compiling working set until all nodes are visited - while (!blocks.empty()) { - std::set<BlockImpl*> predecessors; - - // Calculate maximum sequential depth to consume this phase - size_t depth = std::numeric_limits<size_t>::max(); - for (auto i : blocks) { - depth = std::min(depth, parallel_depth(i)); - } - - Task par(Task::Mode::PARALLEL); - for (auto b : blocks) { - assert(num_unvisited_dependants(b) == 0); - Task seq(Task::Mode::SEQUENTIAL); - compile_block(b, seq, depth, predecessors); - par.push_front(std::move(seq)); - } - _master->push_front(std::move(par)); - blocks = predecessors; - } - - _master = Task::simplify(std::move(_master)); - - if (graph->engine().world()->conf().option("trace").get<int32_t>()) { - ColorContext ctx(stderr, ColorContext::Color::YELLOW); - dump(graph->path()); - } -} - -/** Throw a FeedbackException iff `dependant` has `root` as a dependency. */ -static void -check_feedback(const BlockImpl* root, BlockImpl* provider) -{ - if (provider == root) { - throw FeedbackException(root); - } - - for (auto p : provider->providers()) { - const BlockImpl::Mark mark = p->get_mark(); - switch (mark) { - case BlockImpl::Mark::UNVISITED: - p->set_mark(BlockImpl::Mark::VISITING); - check_feedback(root, p); - break; - case BlockImpl::Mark::VISITING: - throw FeedbackException(p, root); - case BlockImpl::Mark::VISITED: - break; - } - p->set_mark(mark); - } -} - -void -CompiledGraph::compile_provider(const BlockImpl* root, - BlockImpl* block, - Task& task, - size_t max_depth, - std::set<BlockImpl*>& k) -{ - if (block->dependants().size() > 1) { - /* Provider has other dependants, so this is the tail of a sequential task. - Add provider to future working set and stop traversal. */ - check_feedback(root, block); - if (num_unvisited_dependants(block) == 0) { - k.insert(block); - } - } else if (max_depth > 0) { - // Calling dependant has only this provider, add here - if (task.mode() == Task::Mode::PARALLEL) { - // Inside a parallel task, compile into a new sequential child - Task seq(Task::Mode::SEQUENTIAL); - compile_block(block, seq, max_depth, k); - task.push_front(std::move(seq)); - } else { - // Prepend to given sequential task - compile_block(block, task, max_depth, k); - } - } else { - if (num_unvisited_dependants(block) == 0) { - k.insert(block); - } - } -} - -void -CompiledGraph::compile_block(BlockImpl* n, - Task& task, - size_t max_depth, - std::set<BlockImpl*>& k) -{ - switch (n->get_mark()) { - case BlockImpl::Mark::UNVISITED: - n->set_mark(BlockImpl::Mark::VISITING); - - // Execute this task after the providers to follow - task.push_front(Task(Task::Mode::SINGLE, n)); - - if (n->providers().size() < 2) { - // Single provider, prepend it to this sequential task - for (auto p : n->providers()) { - compile_provider(n, p, task, max_depth - 1, k); - } - } else if (has_provider_with_many_dependants(n)) { - // Stop recursion and enqueue providers for the next round - for (auto p : n->providers()) { - if (num_unvisited_dependants(p) == 0) { - k.insert(p); - } - } - } else { - // Multiple providers with only this node as dependant, - // make a new parallel task to execute them - Task par(Task::Mode::PARALLEL); - for (auto p : n->providers()) { - compile_provider(n, p, par, max_depth - 1, k); - } - task.push_front(std::move(par)); - } - n->set_mark(BlockImpl::Mark::VISITED); - break; - - case BlockImpl::Mark::VISITING: - throw FeedbackException(n); - - case BlockImpl::Mark::VISITED: - break; - } -} - -void -CompiledGraph::run(RunContext& context) -{ - _master->run(context); -} - -void -CompiledGraph::dump(const std::string& name) const -{ - auto sink = [](const std::string& s) { - fwrite(s.c_str(), 1, s.size(), stderr); - }; - - sink("(compiled-graph "); - sink(name); - _master->dump(sink, 2, false); - sink(")\n"); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/CompiledGraph.hpp b/src/server/CompiledGraph.hpp deleted file mode 100644 index 6b802611..00000000 --- a/src/server/CompiledGraph.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_COMPILEDGRAPH_HPP -#define INGEN_ENGINE_COMPILEDGRAPH_HPP - -#include <functional> -#include <set> -#include <vector> - -#include "ingen/types.hpp" -#include "raul/Maid.hpp" -#include "raul/Noncopyable.hpp" - -#include "Task.hpp" - -namespace Ingen { -namespace Server { - -class BlockImpl; -class GraphImpl; -class RunContext; - -/** A graph ``compiled'' into a quickly executable form. - * - * This is a flat sequence of nodes ordered such that the process thread can - * execute the nodes in order and have nodes always executed before any of - * their dependencies. - */ -class CompiledGraph : public Raul::Maid::Disposable - , public Raul::Noncopyable -{ -public: - static MPtr<CompiledGraph> compile(Raul::Maid& maid, GraphImpl& graph); - - void run(RunContext& context); - -private: - friend class Raul::Maid; ///< Allow make_managed to construct - - CompiledGraph(GraphImpl* graph); - - typedef std::set<BlockImpl*> BlockSet; - - void dump(const std::string& name) const; - - void compile_graph(GraphImpl* graph); - - void compile_block(BlockImpl* n, - Task& task, - size_t max_depth, - BlockSet& k); - - void compile_provider(const BlockImpl* root, - BlockImpl* block, - Task& task, - size_t max_depth, - BlockSet& k); - - std::unique_ptr<Task> _master; -}; - -inline MPtr<CompiledGraph> compile(Raul::Maid& maid, GraphImpl& graph) -{ - return CompiledGraph::compile(maid, graph); -} - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_COMPILEDGRAPH_HPP diff --git a/src/server/ControlBindings.cpp b/src/server/ControlBindings.cpp deleted file mode 100644 index 3901d1c2..00000000 --- a/src/server/ControlBindings.cpp +++ /dev/null @@ -1,425 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cmath> - -#include "ingen/Log.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "lv2/lv2plug.in/ns/ext/midi/midi.h" - -#include "Buffer.hpp" -#include "ControlBindings.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "RunContext.hpp" -#include "ThreadManager.hpp" - -namespace Ingen { -namespace Server { - -ControlBindings::ControlBindings(Engine& engine) - : _engine(engine) - , _learn_binding(nullptr) - , _bindings(new Bindings()) - , _feedback(new Buffer(*_engine.buffer_factory(), - engine.world()->uris().atom_Sequence, - 0, - 4096)) // FIXME: capacity? -{ - lv2_atom_forge_init( - &_forge, &engine.world()->uri_map().urid_map_feature()->urid_map); -} - -ControlBindings::~ControlBindings() -{ - _feedback.reset(); - delete _learn_binding.load(); -} - -ControlBindings::Key -ControlBindings::port_binding(PortImpl* port) const -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - const Ingen::URIs& uris = _engine.world()->uris(); - const Atom& binding = port->get_property(uris.midi_binding); - return binding_key(binding); -} - -ControlBindings::Key -ControlBindings::binding_key(const Atom& binding) const -{ - const Ingen::URIs& uris = _engine.world()->uris(); - Key key; - LV2_Atom* num = nullptr; - if (binding.type() == uris.atom_Object) { - const LV2_Atom_Object_Body* obj = (const LV2_Atom_Object_Body*) - binding.get_body(); - if (obj->otype == uris.midi_Bender) { - key = Key(Type::MIDI_BENDER); - } else if (obj->otype == uris.midi_ChannelPressure) { - key = Key(Type::MIDI_CHANNEL_PRESSURE); - } else if (obj->otype == uris.midi_Controller) { - lv2_atom_object_body_get( - binding.size(), obj, (LV2_URID)uris.midi_controllerNumber, &num, NULL); - if (!num) { - _engine.log().rt_error("Controller binding missing number\n"); - } else if (num->type != uris.atom_Int) { - _engine.log().rt_error("Controller number not an integer\n"); - } else { - key = Key(Type::MIDI_CC, ((LV2_Atom_Int*)num)->body); - } - } else if (obj->otype == uris.midi_NoteOn) { - lv2_atom_object_body_get( - binding.size(), obj, (LV2_URID)uris.midi_noteNumber, &num, NULL); - if (!num) { - _engine.log().rt_error("Note binding missing number\n"); - } else if (num->type != uris.atom_Int) { - _engine.log().rt_error("Note number not an integer\n"); - } else { - key = Key(Type::MIDI_NOTE, ((LV2_Atom_Int*)num)->body); - } - } - } else if (binding.type()) { - _engine.log().rt_error("Unknown binding type\n"); - } - return key; -} - -ControlBindings::Key -ControlBindings::midi_event_key(uint16_t size, const uint8_t* buf, uint16_t& value) -{ - switch (lv2_midi_message_type(buf)) { - case LV2_MIDI_MSG_CONTROLLER: - value = static_cast<int8_t>(buf[2]); - return Key(Type::MIDI_CC, static_cast<int8_t>(buf[1])); - case LV2_MIDI_MSG_BENDER: - value = (static_cast<int8_t>(buf[2]) << 7) + static_cast<int8_t>(buf[1]); - return Key(Type::MIDI_BENDER); - case LV2_MIDI_MSG_CHANNEL_PRESSURE: - value = static_cast<int8_t>(buf[1]); - return Key(Type::MIDI_CHANNEL_PRESSURE); - case LV2_MIDI_MSG_NOTE_ON: - value = 1.0f; - return Key(Type::MIDI_NOTE, static_cast<int8_t>(buf[1])); - default: - return Key(); - } -} - -bool -ControlBindings::set_port_binding(RunContext& context, - PortImpl* port, - Binding* binding, - const Atom& value) -{ - const Key key = binding_key(value); - if (!!key) { - binding->key = key; - binding->port = port; - _bindings->insert(*binding); - return true; - } else { - return false; - } -} - -void -ControlBindings::port_value_changed(RunContext& ctx, - PortImpl* port, - Key key, - const Atom& value_atom) -{ - Ingen::World* world = ctx.engine().world(); - const Ingen::URIs& uris = world->uris(); - if (!!key) { - int16_t value = port_value_to_control( - ctx, port, key.type, value_atom); - uint16_t size = 0; - uint8_t buf[4]; - switch (key.type) { - case Type::MIDI_CC: - size = 3; - buf[0] = LV2_MIDI_MSG_CONTROLLER; - buf[1] = key.num; - buf[2] = static_cast<int8_t>(value); - break; - case Type::MIDI_CHANNEL_PRESSURE: - size = 2; - buf[0] = LV2_MIDI_MSG_CHANNEL_PRESSURE; - buf[1] = static_cast<int8_t>(value); - break; - case Type::MIDI_BENDER: - size = 3; - buf[0] = LV2_MIDI_MSG_BENDER; - buf[1] = (value & 0x007F); - buf[2] = (value & 0x7F00) >> 7; - break; - case Type::MIDI_NOTE: - size = 3; - if (value == 1) { - buf[0] = LV2_MIDI_MSG_NOTE_ON; - } else if (value == 0) { - buf[0] = LV2_MIDI_MSG_NOTE_OFF; - } - buf[1] = key.num; - buf[2] = 0x64; // MIDI spec default - break; - default: - break; - } - if (size > 0) { - _feedback->append_event(ctx.nframes() - 1, size, (LV2_URID)uris.midi_MidiEvent, buf); - } - } -} - -void -ControlBindings::start_learn(PortImpl* port) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - Binding* b = _learn_binding.load(); - if (!b) { - _learn_binding = new Binding(Type::NULL_CONTROL, port); - } else { - b->port = port; - } -} - -static void -get_range(RunContext& context, const PortImpl* port, float* min, float* max) -{ - *min = port->minimum().get<float>(); - *max = port->maximum().get<float>(); - if (port->is_sample_rate()) { - *min *= context.engine().sample_rate(); - *max *= context.engine().sample_rate(); - } -} - -float -ControlBindings::control_to_port_value(RunContext& context, - const PortImpl* port, - Type type, - int16_t value) const -{ - float normal = 0.0f; - switch (type) { - case Type::MIDI_CC: - case Type::MIDI_CHANNEL_PRESSURE: - normal = (float)value / 127.0f; - break; - case Type::MIDI_BENDER: - normal = (float)value / 16383.0f; - break; - case Type::MIDI_NOTE: - normal = (value == 0.0f) ? 0.0f : 1.0f; - break; - default: - break; - } - - if (port->is_logarithmic()) { - normal = (expf(normal) - 1.0f) / ((float)M_E - 1.0f); - } - - float min, max; - get_range(context, port, &min, &max); - - return normal * (max - min) + min; -} - -int16_t -ControlBindings::port_value_to_control(RunContext& context, - PortImpl* port, - Type type, - const Atom& value_atom) const -{ - if (value_atom.type() != port->bufs().forge().Float) { - return 0; - } - - float min, max; - get_range(context, port, &min, &max); - - const float value = value_atom.get<float>(); - float normal = (value - min) / (max - min); - - if (normal < 0.0f) { - normal = 0.0f; - } - - if (normal > 1.0f) { - normal = 1.0f; - } - - if (port->is_logarithmic()) { - normal = logf(normal * ((float)M_E - 1.0f) + 1.0); - } - - switch (type) { - case Type::MIDI_CC: - case Type::MIDI_CHANNEL_PRESSURE: - return lrintf(normal * 127.0f); - case Type::MIDI_BENDER: - return lrintf(normal * 16383.0f); - case Type::MIDI_NOTE: - return (value > 0.0f) ? 1 : 0; - default: - return 0; - } -} - -static void -forge_binding(const URIs& uris, - LV2_Atom_Forge* forge, - ControlBindings::Type binding_type, - int32_t value) -{ - LV2_Atom_Forge_Frame frame; - switch (binding_type) { - case ControlBindings::Type::MIDI_CC: - lv2_atom_forge_object(forge, &frame, 0, uris.midi_Controller); - lv2_atom_forge_key(forge, uris.midi_controllerNumber); - lv2_atom_forge_int(forge, value); - break; - case ControlBindings::Type::MIDI_BENDER: - lv2_atom_forge_object(forge, &frame, 0, uris.midi_Bender); - break; - case ControlBindings::Type::MIDI_CHANNEL_PRESSURE: - lv2_atom_forge_object(forge, &frame, 0, uris.midi_ChannelPressure); - break; - case ControlBindings::Type::MIDI_NOTE: - lv2_atom_forge_object(forge, &frame, 0, uris.midi_NoteOn); - lv2_atom_forge_key(forge, uris.midi_noteNumber); - lv2_atom_forge_int(forge, value); - break; - case ControlBindings::Type::MIDI_RPN: // TODO - case ControlBindings::Type::MIDI_NRPN: // TODO - case ControlBindings::Type::NULL_CONTROL: - break; - } -} - -void -ControlBindings::set_port_value(RunContext& context, - PortImpl* port, - Type type, - int16_t value) -{ - float min, max; - get_range(context, port, &min, &max); - - const float val = control_to_port_value(context, port, type, value); - - // TODO: Set port value property so it is saved - port->set_control_value(context, context.start(), val); - - URIs& uris = context.engine().world()->uris(); - context.notify(uris.ingen_value, context.start(), port, - sizeof(float), _forge.Float, &val); -} - -bool -ControlBindings::finish_learn(RunContext& context, Key key) -{ - const Ingen::URIs& uris = context.engine().world()->uris(); - Binding* binding = _learn_binding.exchange(nullptr); - if (!binding || (key.type == Type::MIDI_NOTE && !binding->port->is_toggled())) { - return false; - } - - binding->key = key; - _bindings->insert(*binding); - - LV2_Atom buf[16]; - memset(buf, 0, sizeof(buf)); - lv2_atom_forge_set_buffer(&_forge, (uint8_t*)buf, sizeof(buf)); - forge_binding(uris, &_forge, key.type, key.num); - const LV2_Atom* atom = buf; - context.notify(uris.midi_binding, - context.start(), - binding->port, - atom->size, atom->type, LV2_ATOM_BODY_CONST(atom)); - - return true; -} - -void -ControlBindings::get_all(const Raul::Path& path, std::vector<Binding*>& bindings) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - for (Binding& b : *_bindings) { - if (b.port->path() == path || b.port->path().is_child_of(path)) { - bindings.push_back(&b); - } - } -} - -void -ControlBindings::remove(RunContext& ctx, const std::vector<Binding*>& bindings) -{ - for (Binding* b : bindings) { - _bindings->erase(*b); - } -} - -void -ControlBindings::pre_process(RunContext& ctx, Buffer* buffer) -{ - uint16_t value = 0; - Ingen::World* world = ctx.engine().world(); - const Ingen::URIs& uris = world->uris(); - - _feedback->clear(); - if ((!_learn_binding && _bindings->empty()) || !buffer->get<LV2_Atom>()) { - return; // Don't bother reading input - } - - LV2_Atom_Sequence* seq = buffer->get<LV2_Atom_Sequence>(); - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - if (ev->body.type == uris.midi_MidiEvent) { - const uint8_t* buf = (const uint8_t*)LV2_ATOM_BODY(&ev->body); - const Key key = midi_event_key(ev->body.size, buf, value); - - if (_learn_binding && !!key) { - finish_learn(ctx, key); // Learn new binding - } - - // Set all controls bound to this key - const Binding k = {key, nullptr}; - for (Bindings::const_iterator i = _bindings->lower_bound(k); - i != _bindings->end() && i->key == key; - ++i) { - set_port_value(ctx, i->port, key.type, value); - } - } - } -} - -void -ControlBindings::post_process(RunContext& context, Buffer* buffer) -{ - if (buffer->get<LV2_Atom>()) { - buffer->append_event_buffer(_feedback.get()); - } -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/ControlBindings.hpp b/src/server/ControlBindings.hpp deleted file mode 100644 index 3160f8b2..00000000 --- a/src/server/ControlBindings.hpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_CONTROLBINDINGS_HPP -#define INGEN_ENGINE_CONTROLBINDINGS_HPP - -#include <atomic> -#include <cstdint> -#include <vector> - -#include <boost/intrusive/options.hpp> -#include <boost/intrusive/set.hpp> - -#include "ingen/Atom.hpp" -#include "ingen/types.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/forge.h" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" - -#include "BufferFactory.hpp" - -namespace Ingen { -namespace Server { - -class Engine; -class RunContext; -class PortImpl; - -class ControlBindings { -public: - enum class Type : uint16_t { - NULL_CONTROL, - MIDI_BENDER, - MIDI_CC, - MIDI_RPN, - MIDI_NRPN, - MIDI_CHANNEL_PRESSURE, - MIDI_NOTE - }; - - struct Key { - Key(Type t=Type::NULL_CONTROL, int16_t n=0) : type(t), num(n) {} - inline bool operator<(const Key& other) const { - return ((type < other.type) || - (type == other.type && num < other.num)); - } - inline bool operator==(const Key& other) const { - return type == other.type && num == other.num; - } - inline bool operator!() const { return type == Type::NULL_CONTROL; } - Type type; - int16_t num; - }; - - /** One binding of a controller to a port. */ - struct Binding : public boost::intrusive::set_base_hook<>, - public Raul::Maid::Disposable { - Binding(Key k=Key(), PortImpl* p=nullptr) : key(std::move(k)), port(p) {} - - inline bool operator<(const Binding& rhs) const { return key < rhs.key; } - - Key key; - PortImpl* port; - }; - - /** Comparator for bindings by key. */ - struct BindingLess { - bool operator()(const Binding& lhs, const Binding& rhs) const { - return lhs.key < rhs.key; - } - }; - - explicit ControlBindings(Engine& engine); - ~ControlBindings(); - - Key port_binding(PortImpl* port) const; - Key binding_key(const Atom& binding) const; - - void start_learn(PortImpl* port); - - /** Set the binding for `port` to `binding` and take ownership of it. */ - bool set_port_binding(RunContext& ctx, - PortImpl* port, - Binding* binding, - const Atom& value); - - void port_value_changed(RunContext& ctx, - PortImpl* port, - Key key, - const Atom& value_atom); - - void pre_process(RunContext& ctx, Buffer* buffer); - void post_process(RunContext& ctx, Buffer* buffer); - - /** Get all bindings for `path` or children of `path`. */ - void get_all(const Raul::Path& path, std::vector<Binding*>& bindings); - - /** Remove a set of bindings from an earlier call to get_all(). */ - void remove(RunContext& ctx, const std::vector<Binding*>& bindings); - -private: - typedef boost::intrusive::multiset< - Binding, - boost::intrusive::compare<BindingLess> > Bindings; - - Key midi_event_key(uint16_t size, const uint8_t* buf, uint16_t& value); - - void set_port_value(RunContext& context, - PortImpl* port, - Type type, - int16_t value); - - bool finish_learn(RunContext& context, Key key); - - float control_to_port_value(RunContext& context, - const PortImpl* port, - Type type, - int16_t value) const; - - int16_t port_value_to_control(RunContext& context, - PortImpl* port, - Type type, - const Atom& value_atom) const; - - Engine& _engine; - std::atomic<Binding*> _learn_binding; - SPtr<Bindings> _bindings; - BufferRef _feedback; - LV2_Atom_Forge _forge; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_CONTROLBINDINGS_HPP diff --git a/src/server/DirectDriver.hpp b/src/server/DirectDriver.hpp deleted file mode 100644 index 58b4f898..00000000 --- a/src/server/DirectDriver.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_DIRECT_DRIVER_HPP -#define INGEN_ENGINE_DIRECT_DRIVER_HPP - -#include <boost/intrusive/slist.hpp> - -#include "Driver.hpp" -#include "Engine.hpp" - -namespace Ingen { -namespace Server { - -/** Driver for running Ingen directly as a library. - * \ingroup engine - */ -class DirectDriver : public Driver { -public: - DirectDriver(Engine& engine, - double sample_rate, - SampleCount block_length, - size_t seq_size) - : _engine(engine) - , _sample_rate(sample_rate) - , _block_length(block_length) - , _seq_size(seq_size) - {} - - virtual ~DirectDriver() { - _ports.clear_and_dispose([](EnginePort* p) { delete p; }); - } - - bool dynamic_ports() const { return true; } - - virtual EnginePort* create_port(DuplexPort* graph_port) { - return new EnginePort(graph_port); - } - - virtual EnginePort* get_port(const Raul::Path& path) { - for (auto& p : _ports) { - if (p.graph_port()->path() == path) { - return &p; - } - } - - return nullptr; - } - - virtual void add_port(RunContext& context, EnginePort* port) { - _ports.push_back(*port); - } - - virtual void remove_port(RunContext& context, EnginePort* port) { - _ports.erase(_ports.iterator_to(*port)); - } - - virtual void rename_port(const Raul::Path& old_path, - const Raul::Path& new_path) {} - - virtual void port_property(const Raul::Path& path, - const URI& uri, - const Atom& value) {} - - virtual void register_port(EnginePort& port) {} - virtual void unregister_port(EnginePort& port) {} - - virtual SampleCount block_length() const { return _block_length; } - - virtual size_t seq_size() const { return _seq_size; } - - virtual SampleCount sample_rate() const { return _sample_rate; } - - virtual SampleCount frame_time() const { return _engine.run_context().start(); } - - virtual void append_time_events(RunContext& context, Buffer& buffer) {} - - virtual int real_time_priority() { return 60; } - -private: - typedef boost::intrusive::slist<EnginePort, - boost::intrusive::cache_last<true> - > Ports; - - Engine& _engine; - Ports _ports; - SampleCount _sample_rate; - SampleCount _block_length; - size_t _seq_size; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_DIRECT_DRIVER_HPP diff --git a/src/server/Driver.hpp b/src/server/Driver.hpp deleted file mode 100644 index 9ae4b836..00000000 --- a/src/server/Driver.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_DRIVER_HPP -#define INGEN_ENGINE_DRIVER_HPP - -#include "raul/Noncopyable.hpp" - -#include "DuplexPort.hpp" -#include "EnginePort.hpp" - -namespace Raul { class Path; } - -namespace Ingen { -namespace Server { - -class DuplexPort; -class EnginePort; - -/** Engine driver base class. - * - * A Driver is responsible for managing system ports, and possibly running the - * audio graph. - * - * \ingroup engine - */ -class Driver : public Raul::Noncopyable { -public: - virtual ~Driver() = default; - - /** Activate driver (begin processing graph and events). */ - virtual bool activate() { return true; } - - /** Deactivate driver (stop processing graph and events). */ - virtual void deactivate() {} - - /** Create a port ready to be inserted with add_input (non realtime). - * May return NULL if the Driver can not create the port for some reason. - */ - virtual EnginePort* create_port(DuplexPort* graph_port) = 0; - - /** Find a system port by path. */ - virtual EnginePort* get_port(const Raul::Path& path) = 0; - - /** Add a system visible port (e.g. a port on the root graph). */ - virtual void add_port(RunContext& context, EnginePort* port) = 0; - - /** Remove a system visible port. - * - * This removes the port from the driver in the process thread but does not - * destroy the port. To actually remove the system port, unregister_port() - * must be called later in another thread. - */ - virtual void remove_port(RunContext& context, EnginePort* port) = 0; - - /** Return true iff driver supports dynamic adding/removing of ports. */ - virtual bool dynamic_ports() const { return false; } - - /** Register a system visible port. */ - virtual void register_port(EnginePort& port) = 0; - - /** Register a system visible port. */ - virtual void unregister_port(EnginePort& port) = 0; - - /** Rename a system visible port. */ - virtual void rename_port(const Raul::Path& old_path, - const Raul::Path& new_path) = 0; - - /** Apply a system visible port property. */ - virtual void port_property(const Raul::Path& path, - const URI& uri, - const Atom& value) = 0; - - /** Return the audio buffer size in frames */ - virtual SampleCount block_length() const = 0; - - /** Return the event buffer size in bytes */ - virtual size_t seq_size() const = 0; - - /** Return the sample rate in Hz */ - virtual SampleRate sample_rate() const = 0; - - /** Return the current frame time (running counter) */ - virtual SampleCount frame_time() const = 0; - - /** Append time events for this cycle to `buffer`. */ - virtual void append_time_events(RunContext& context, - Buffer& buffer) = 0; - - /** Return the real-time priority of the audio thread, or -1. */ - virtual int real_time_priority() = 0; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_DRIVER_HPP diff --git a/src/server/DuplexPort.cpp b/src/server/DuplexPort.cpp deleted file mode 100644 index 1b62ff38..00000000 --- a/src/server/DuplexPort.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/URIs.hpp" - -#include "Buffer.hpp" -#include "Driver.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" - -namespace Ingen { -namespace Server { - -DuplexPort::DuplexPort(BufferFactory& bufs, - GraphImpl* parent, - const Raul::Symbol& symbol, - uint32_t index, - bool polyphonic, - PortType type, - LV2_URID buf_type, - size_t buf_size, - const Atom& value, - bool is_output) - : InputPort(bufs, parent, symbol, index, parent->polyphony(), type, buf_type, value, buf_size) -{ - if (polyphonic) { - set_property(bufs.uris().ingen_polyphonic, bufs.forge().make(true)); - } - - if (!parent->parent() || - _poly != parent->parent_graph()->internal_poly()) { - _poly = 1; - } - - // Set default control range - if (!is_output && value.type() == bufs.uris().atom_Float) { - set_property(bufs.uris().lv2_minimum, bufs.forge().make(0.0f)); - set_property(bufs.uris().lv2_maximum, bufs.forge().make(1.0f)); - } - - _is_output = is_output; - if (is_output) { - if (parent->graph_type() != Node::GraphType::GRAPH) { - remove_property(bufs.uris().rdf_type, bufs.uris().lv2_InputPort.urid); - add_property(bufs.uris().rdf_type, bufs.uris().lv2_OutputPort.urid); - } - } - - get_buffers(bufs, &BufferFactory::get_buffer, - _voices, parent->polyphony(), 0); -} - -DuplexPort::~DuplexPort() -{ - if (is_linked()) { - parent_graph()->remove_port(*this); - } -} - -DuplexPort* -DuplexPort::duplicate(Engine& engine, - const Raul::Symbol& symbol, - GraphImpl* parent) -{ - BufferFactory& bufs = *engine.buffer_factory(); - const Atom polyphonic = get_property(bufs.uris().ingen_polyphonic); - - DuplexPort* dup = new DuplexPort( - bufs, parent, symbol, _index, - polyphonic.type() == bufs.uris().atom_Bool && polyphonic.get<int32_t>(), - _type, _buffer_type, _buffer_size, - _value, _is_output); - - dup->set_properties(properties()); - - return dup; -} - -void -DuplexPort::inherit_neighbour(const PortImpl* port, - Properties& remove, - Properties& add) -{ - const URIs& uris = _bufs.uris(); - - /* TODO: This needs to become more sophisticated, and correct the situation - if the port is disconnected. */ - if (_type == PortType::CONTROL || _type == PortType::CV) { - if (port->minimum().get<float>() < _min.get<float>()) { - _min = port->minimum(); - remove.emplace(uris.lv2_minimum, uris.patch_wildcard); - add.emplace(uris.lv2_minimum, port->minimum()); - } - if (port->maximum().get<float>() > _max.get<float>()) { - _max = port->maximum(); - remove.emplace(uris.lv2_maximum, uris.patch_wildcard); - add.emplace(uris.lv2_maximum, port->maximum()); - } - } else if (_type == PortType::ATOM) { - for (auto i = port->properties().find(uris.atom_supports); - i != port->properties().end() && i->first == uris.atom_supports; - ++i) { - set_property(i->first, i->second); - add.insert(*i); - } - } -} - -void -DuplexPort::on_property(const URI& uri, const Atom& value) -{ - _bufs.engine().driver()->port_property(_path, uri, value); -} - -bool -DuplexPort::get_buffers(BufferFactory& bufs, - PortImpl::GetFn get, - const MPtr<Voices>& voices, - uint32_t poly, - size_t num_in_arcs) const -{ - if (!_is_driver_port && is_output()) { - return InputPort::get_buffers(bufs, get, voices, poly, num_in_arcs); - } else if (!_is_driver_port && is_input()) { - return PortImpl::get_buffers(bufs, get, voices, poly, num_in_arcs); - } - return false; -} - -bool -DuplexPort::setup_buffers(RunContext& ctx, BufferFactory& bufs, uint32_t poly) -{ - if (!_is_driver_port && is_output()) { - return InputPort::setup_buffers(ctx, bufs, poly); - } else if (!_is_driver_port && is_input()) { - return PortImpl::setup_buffers(ctx, bufs, poly); - } - return false; -} - -void -DuplexPort::set_is_driver_port(BufferFactory& bufs) -{ - _voices->at(0).buffer = new Buffer(bufs, buffer_type(), _value.type(), 0, true, nullptr); - PortImpl::set_is_driver_port(bufs); -} - -void -DuplexPort::set_driver_buffer(void* buf, uint32_t capacity) -{ - _voices->at(0).buffer->set_buffer(buf); - _voices->at(0).buffer->set_capacity(capacity); -} - -uint32_t -DuplexPort::max_tail_poly(RunContext& context) const -{ - return std::max(_poly, parent_graph()->internal_poly_process()); -} - -bool -DuplexPort::prepare_poly(BufferFactory& bufs, uint32_t poly) -{ - if (!parent()->parent() || - poly != parent()->parent_graph()->internal_poly()) { - return false; - } - - return PortImpl::prepare_poly(bufs, poly); -} - -bool -DuplexPort::apply_poly(RunContext& context, uint32_t poly) -{ - if (!parent()->parent() || - poly != parent()->parent_graph()->internal_poly()) { - return false; - } - - return PortImpl::apply_poly(context, poly); -} - -void -DuplexPort::pre_process(RunContext& context) -{ - if (_is_output) { - /* This is a graph output, which is an input from the internal - perspective. Prepare buffers for write so plugins can deliver to - them */ - for (uint32_t v = 0; v < _poly; ++v) { - _voices->at(v).buffer->prepare_write(context); - } - } else { - /* This is a a graph input, which is an output from the internal - perspective. Do whatever a normal block's input port does to - prepare input for reading. */ - InputPort::pre_process(context); - InputPort::pre_run(context); - } -} - -void -DuplexPort::post_process(RunContext& context) -{ - if (_is_output) { - /* This is a graph output, which is an input from the internal - perspective. Mix down input delivered by plugins so output - (external perspective) is ready. */ - InputPort::pre_process(context); - InputPort::pre_run(context); - } - monitor(context); -} - -SampleCount -DuplexPort::next_value_offset(SampleCount offset, SampleCount end) const -{ - return PortImpl::next_value_offset(offset, end); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/DuplexPort.hpp b/src/server/DuplexPort.hpp deleted file mode 100644 index b0066164..00000000 --- a/src/server/DuplexPort.hpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_DUPLEXPORT_HPP -#define INGEN_ENGINE_DUPLEXPORT_HPP - -#include <boost/intrusive/slist.hpp> - -#include "BufferRef.hpp" -#include "InputPort.hpp" - -namespace Ingen { -namespace Server { - -class BlockImpl; - -/** A duplex Port (both an input and output port on a Graph) - * - * This is used for Graph ports, since they need to appear as both an input and - * an output port based on context. There are no actual duplex ports in Ingen, - * a Port is either an Input or Output. This class only exists to allow Graph - * outputs to appear as inputs from within that Graph, and vice versa. - * - * \ingroup engine - */ -class DuplexPort : public InputPort - , public boost::intrusive::slist_base_hook<> // In GraphImpl -{ -public: - DuplexPort(BufferFactory& bufs, - GraphImpl* parent, - const Raul::Symbol& symbol, - uint32_t index, - bool polyphonic, - PortType type, - LV2_URID buf_type, - size_t buf_size, - const Atom& value, - bool is_output); - - virtual ~DuplexPort(); - - DuplexPort* duplicate(Engine& engine, - const Raul::Symbol& symbol, - GraphImpl* parent); - - void inherit_neighbour(const PortImpl* port, - Properties& remove, - Properties& add); - - void on_property(const URI& uri, const Atom& value); - - uint32_t max_tail_poly(RunContext& context) const; - - bool prepare_poly(BufferFactory& bufs, uint32_t poly); - - bool apply_poly(RunContext& context, uint32_t poly); - - bool get_buffers(BufferFactory& bufs, - PortImpl::GetFn get, - const MPtr<Voices>& voices, - uint32_t poly, - size_t num_in_arcs) const; - - virtual void set_is_driver_port(BufferFactory& bufs); - - /** Set the external driver-provided buffer. - * - * This may only be called in the process thread, after an earlier call to - * prepare_driver_buffer(). - */ - void set_driver_buffer(void* buf, uint32_t capacity); - - bool setup_buffers(RunContext& ctx, BufferFactory& bufs, uint32_t poly); - - void pre_process(RunContext& context); - void post_process(RunContext& context); - - SampleCount next_value_offset(SampleCount offset, SampleCount end) const; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_DUPLEXPORT_HPP diff --git a/src/server/Engine.cpp b/src/server/Engine.cpp deleted file mode 100644 index a7476845..00000000 --- a/src/server/Engine.cpp +++ /dev/null @@ -1,526 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen_config.h" - -#include <sys/mman.h> - -#include <limits> -#include <thread> - -#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h" -#include "lv2/lv2plug.in/ns/ext/state/state.h" - -#include "events/CreateGraph.hpp" -#include "ingen/AtomReader.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/Log.hpp" -#include "ingen/Store.hpp" -#include "ingen/StreamWriter.hpp" -#include "ingen/Tee.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" -#include "ingen/types.hpp" -#include "raul/Maid.hpp" - -#include "BlockFactory.hpp" -#include "Broadcaster.hpp" -#include "BufferFactory.hpp" -#include "ControlBindings.hpp" -#include "DirectDriver.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "Event.hpp" -#include "EventWriter.hpp" -#include "GraphImpl.hpp" -#include "LV2Options.hpp" -#include "PostProcessor.hpp" -#include "PreProcessContext.hpp" -#include "PreProcessor.hpp" -#include "RunContext.hpp" -#include "ThreadManager.hpp" -#include "UndoStack.hpp" -#include "Worker.hpp" -#ifdef HAVE_SOCKET -#include "SocketListener.hpp" -#endif - -namespace Ingen { -namespace Server { - -INGEN_THREAD_LOCAL unsigned ThreadManager::flags(0); -bool ThreadManager::single_threaded(true); - -Engine::Engine(Ingen::World* world) - : _world(world) - , _options(new LV2Options(world->uris())) - , _buffer_factory(new BufferFactory(*this, world->uris())) - , _maid(new Raul::Maid) - , _worker(new Worker(world->log(), event_queue_size())) - , _sync_worker(new Worker(world->log(), event_queue_size(), true)) - , _broadcaster(new Broadcaster()) - , _control_bindings(new ControlBindings(*this)) - , _block_factory(new BlockFactory(world)) - , _undo_stack(new UndoStack(_world->uris(), _world->uri_map())) - , _redo_stack(new UndoStack(_world->uris(), _world->uri_map())) - , _post_processor(new PostProcessor(*this)) - , _pre_processor(new PreProcessor(*this)) - , _event_writer(new EventWriter(*this)) - , _interface(_event_writer) - , _atom_interface( - new AtomReader(world->uri_map(), world->uris(), world->log(), *_interface)) - , _root_graph(nullptr) - , _cycle_start_time(0) - , _rand_engine(0) - , _uniform_dist(0.0f, 1.0f) - , _quit_flag(false) - , _reset_load_flag(false) - , _atomic_bundles(world->conf().option("atomic-bundles").get<int32_t>()) - , _activated(false) -{ - if (!world->store()) { - world->set_store(SPtr<Ingen::Store>(new Store())); - } - - for (int i = 0; i < world->conf().option("threads").get<int32_t>(); ++i) { - Raul::RingBuffer* ring = new Raul::RingBuffer(24 * event_queue_size()); - _notifications.push_back(ring); - _run_contexts.push_back(new RunContext(*this, ring, i, i > 0)); - } - - _world->lv2_features().add_feature(_worker->schedule_feature()); - _world->lv2_features().add_feature(_options); - _world->lv2_features().add_feature( - SPtr<LV2Features::Feature>( - new LV2Features::EmptyFeature(LV2_BUF_SIZE__powerOf2BlockLength))); - _world->lv2_features().add_feature( - SPtr<LV2Features::Feature>( - new LV2Features::EmptyFeature(LV2_BUF_SIZE__fixedBlockLength))); - _world->lv2_features().add_feature( - SPtr<LV2Features::Feature>( - new LV2Features::EmptyFeature(LV2_BUF_SIZE__boundedBlockLength))); - _world->lv2_features().add_feature( - SPtr<LV2Features::Feature>( - new LV2Features::EmptyFeature(LV2_STATE__loadDefaultState))); - - if (world->conf().option("dump").get<int32_t>()) { - _interface = std::make_shared<Tee>( - Tee::Sinks{ - _event_writer, - std::make_shared<StreamWriter>(world->uri_map(), - world->uris(), - URI("ingen:/engine"), - stderr, - ColorContext::Color::MAGENTA)}); - } -} - -Engine::~Engine() -{ - _root_graph = nullptr; - deactivate(); - - // Process all pending events - const FrameTime end = std::numeric_limits<FrameTime>::max(); - RunContext& ctx = run_context(); - locate(ctx.end(), end - ctx.end()); - _post_processor->set_end_time(end); - _post_processor->process(); - while (!_pre_processor->empty()) { - _pre_processor->process(ctx, *_post_processor, 1); - _post_processor->process(); - } - - _atom_interface.reset(); - - // Delete run contexts - _quit_flag = true; - _tasks_available.notify_all(); - for (RunContext* ctx : _run_contexts) { - ctx->join(); - delete ctx; - } - for (Raul::RingBuffer* ring : _notifications) { - delete ring; - } - - const SPtr<Store> store = this->store(); - if (store) { - for (auto& s : *store.get()) { - if (!dynamic_ptr_cast<NodeImpl>(s.second)->parent()) { - s.second.reset(); - } - } - store->clear(); - } - - _world->set_store(SPtr<Ingen::Store>()); -} - -void -Engine::listen() -{ -#ifdef HAVE_SOCKET - _listener = UPtr<SocketListener>(new SocketListener(*this)); -#endif -} - -void -Engine::advance(SampleCount nframes) -{ - for (RunContext* ctx : _run_contexts) { - ctx->locate(ctx->start() + nframes, block_length()); - } -} - -void -Engine::locate(FrameTime s, SampleCount nframes) -{ - for (RunContext* ctx : _run_contexts) { - ctx->locate(s, nframes); - } -} - -void -Engine::set_root_graph(GraphImpl* graph) -{ - _root_graph = graph; -} - -void -Engine::flush_events(const std::chrono::milliseconds& sleep_ms) -{ - bool finished = !pending_events(); - while (!finished) { - // Run one audio block to execute prepared events - run(block_length()); - advance(block_length()); - - // Run one main iteration to post-process events - main_iteration(); - - // Sleep before continuing if there are still events to process - if (!(finished = !pending_events())) { - std::this_thread::sleep_for(sleep_ms); - } - } -} - -void -Engine::emit_notifications(FrameTime end) -{ - for (RunContext* ctx : _run_contexts) { - ctx->emit_notifications(end); - } -} - -bool -Engine::pending_notifications() -{ - for (const RunContext* ctx : _run_contexts) { - if (ctx->pending_notifications()) { - return true; - } - } - return false; -} - -bool -Engine::wait_for_tasks() -{ - if (!_quit_flag) { - std::unique_lock<std::mutex> lock(_tasks_mutex); - _tasks_available.wait(lock); - } - return !_quit_flag; -} - -void -Engine::signal_tasks_available() -{ - _tasks_available.notify_all(); -} - -Task* -Engine::steal_task(unsigned start_thread) -{ - for (unsigned i = 0; i < _run_contexts.size(); ++i) { - const unsigned id = (start_thread + i) % _run_contexts.size(); - RunContext* const ctx = _run_contexts[id]; - Task* par = ctx->task(); - if (par) { - Task* t = par->steal(*ctx); - if (t) { - return t; - } - } - } - return nullptr; -} - -SPtr<Store> -Engine::store() const -{ - return _world->store(); -} - -SampleRate -Engine::sample_rate() const -{ - return _driver->sample_rate(); -} - -SampleCount -Engine::block_length() const -{ - return _driver->block_length(); -} - -size_t -Engine::sequence_size() const -{ - return _driver->seq_size(); -} - -size_t -Engine::event_queue_size() const -{ - return world()->conf().option("queue-size").get<int32_t>(); -} - -void -Engine::quit() -{ - _quit_flag = true; -} - -Properties -Engine::load_properties() const -{ - const Ingen::URIs& uris = world()->uris(); - - return { { uris.ingen_meanRunLoad, - uris.forge.make(floorf(_run_load.mean) / 100.0f) }, - { uris.ingen_minRunLoad, - uris.forge.make(_run_load.min / 100.0f) }, - { uris.ingen_maxRunLoad, - uris.forge.make(_run_load.max / 100.0f) } }; -} - -bool -Engine::main_iteration() -{ - _post_processor->process(); - _maid->cleanup(); - - if (_run_load.changed) { - _broadcaster->put(URI("ingen:/engine"), load_properties()); - _run_load.changed = false; - } - - return !_quit_flag; -} - -void -Engine::set_driver(SPtr<Driver> driver) -{ - _driver = driver; - for (RunContext* ctx : _run_contexts) { - ctx->set_priority(driver->real_time_priority()); - ctx->set_rate(driver->sample_rate()); - } - - _buffer_factory->set_block_length(driver->block_length()); - _options->set(sample_rate(), - block_length(), - buffer_factory()->default_size(_world->uris().atom_Sequence)); -} - -SampleCount -Engine::event_time() -{ - if (ThreadManager::single_threaded) { - return 0; - } - - return _driver->frame_time() + _driver->block_length(); -} - -uint64_t -Engine::current_time() const -{ - return _clock.now_microseconds(); -} - -void -Engine::reset_load() -{ - _reset_load_flag = true; -} - -void -Engine::init(double sample_rate, uint32_t block_length, size_t seq_size) -{ - set_driver(SPtr<Driver>(new DirectDriver(*this, sample_rate, block_length, seq_size))); -} - -bool -Engine::supports_dynamic_ports() const -{ - return !_driver || _driver->dynamic_ports(); -} - -bool -Engine::activate() -{ - if (!_driver) { - return false; - } - - ThreadManager::single_threaded = true; - - const Ingen::URIs& uris = world()->uris(); - - if (!_root_graph) { - // No root graph has been loaded, create an empty one - const Properties properties = { - {uris.rdf_type, uris.ingen_Graph}, - {uris.ingen_polyphony, - Property(_world->forge().make(1), - Resource::Graph::INTERNAL)}}; - - enqueue_event( - new Events::CreateGraph( - *this, SPtr<Interface>(), -1, 0, Raul::Path("/"), properties)); - - flush_events(std::chrono::milliseconds(10)); - if (!_root_graph) { - return false; - } - } - - _driver->activate(); - _root_graph->enable(); - - ThreadManager::single_threaded = false; - _activated = true; - - return true; -} - -void -Engine::deactivate() -{ - if (_driver) { - _driver->deactivate(); - } - - if (_root_graph) { - _root_graph->deactivate(); - } - - ThreadManager::single_threaded = true; - _activated = false; -} - -unsigned -Engine::run(uint32_t sample_count) -{ - RunContext& ctx = run_context(); - _cycle_start_time = current_time(); - - post_processor()->set_end_time(ctx.end()); - - // Process events that came in during the last cycle - // (Aiming for jitter-free 1 block event latency, ideally) - const unsigned n_processed_events = process_events(); - - // Reset load if graph structure has changed - if (_reset_load_flag) { - _run_load = Load(); - _reset_load_flag = false; - } - - // Run root graph - if (_root_graph) { - // Apply control bindings to input - control_bindings()->pre_process( - ctx, _root_graph->port_impl(0)->buffer(0).get()); - - // Run root graph for this cycle - _root_graph->process(ctx); - - // Emit control binding feedback - control_bindings()->post_process( - ctx, _root_graph->port_impl(1)->buffer(0).get()); - } - - // Update load for this cycle - if (ctx.duration() > 0) { - _run_load.update(current_time() - _cycle_start_time, ctx.duration()); - } - - return n_processed_events; -} - -bool -Engine::pending_events() const -{ - return !_pre_processor->empty() || _post_processor->pending(); -} - -void -Engine::enqueue_event(Event* ev, Event::Mode mode) -{ - _pre_processor->event(ev, mode); -} - -unsigned -Engine::process_events() -{ - const size_t MAX_EVENTS_PER_CYCLE = run_context().nframes() / 8; - return _pre_processor->process( - run_context(), *_post_processor, MAX_EVENTS_PER_CYCLE); -} - -unsigned -Engine::process_all_events() -{ - return _pre_processor->process(run_context(), *_post_processor, 0); -} - -Log& -Engine::log() const -{ - return _world->log(); -} - -void -Engine::register_client(SPtr<Interface> client) -{ - log().info(fmt("Registering client <%1%>\n") % client->uri().c_str()); - _broadcaster->register_client(client); -} - -bool -Engine::unregister_client(SPtr<Interface> client) -{ - log().info(fmt("Unregistering client <%1%>\n") % client->uri().c_str()); - return _broadcaster->unregister_client(client); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/Engine.hpp b/src/server/Engine.hpp deleted file mode 100644 index f5ba1feb..00000000 --- a/src/server/Engine.hpp +++ /dev/null @@ -1,221 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_ENGINE_HPP -#define INGEN_ENGINE_ENGINE_HPP - -#include <chrono> -#include <condition_variable> -#include <mutex> -#include <random> - -#include "ingen/Clock.hpp" -#include "ingen/EngineBase.hpp" -#include "ingen/Properties.hpp" -#include "ingen/ingen.h" -#include "ingen/types.hpp" - -#include "Event.hpp" -#include "Load.hpp" - -namespace Raul { -class Maid; -class RingBuffer; -} - -namespace Ingen { - -class AtomReader; -class Interface; -class Log; -class Store; -class World; - -namespace Server { - -class BlockFactory; -class Broadcaster; -class BufferFactory; -class ControlBindings; -class Driver; -class EventWriter; -class GraphImpl; -class LV2Options; -class PostProcessor; -class PreProcessor; -class RunContext; -class SocketListener; -class Task; -class UndoStack; -class Worker; - -/** - The engine which executes the process graph. - - This is a simple class that provides pointers to the various components - that make up the engine implementation. In processes with a local engine, - it can be accessed via the Ingen::World. - - @ingroup engine -*/ -class INGEN_API Engine : public EngineBase -{ -public: - explicit Engine(Ingen::World* world); - virtual ~Engine(); - - Engine(const Engine&) = delete; - Engine& operator=(const Engine&) = delete; - - // EngineBase methods - virtual void init(double sample_rate, uint32_t block_length, size_t seq_size); - virtual bool supports_dynamic_ports() const; - virtual bool activate(); - virtual void deactivate(); - virtual bool pending_events() const; - virtual unsigned run(uint32_t sample_count); - virtual void quit(); - virtual bool main_iteration(); - virtual void register_client(SPtr<Interface> client); - virtual bool unregister_client(SPtr<Interface> client); - - void listen(); - - /** Return a random [0..1] float with uniform distribution */ - float frand() { return _uniform_dist(_rand_engine); } - - void set_driver(SPtr<Driver> driver); - - /** Return the frame time to execute an event that arrived now. - * - * This aims to return a time one cycle from "now", so that events ideally - * have 1 cycle of latency with no jitter. - */ - SampleCount event_time(); - - /** Return the time this cycle began processing in microseconds. - * - * This value is comparable to the value returned by current_time(). - */ - inline uint64_t cycle_start_time(const RunContext& context) const { - return _cycle_start_time; - } - - /** Return the current time in microseconds. */ - uint64_t current_time() const; - - /** Reset the load statistics (when the expected DSP load changes). */ - void reset_load(); - - /** Enqueue an event to be processed (non-realtime threads only). */ - void enqueue_event(Event* ev, Event::Mode mode=Event::Mode::NORMAL); - - /** Process events (process thread only). */ - unsigned process_events(); - - /** Process all events (no RT limits). */ - unsigned process_all_events(); - - Ingen::World* world() const { return _world; } - Log& log() const; - - const SPtr<Interface>& interface() const { return _interface; } - const SPtr<EventWriter>& event_writer() const { return _event_writer; } - const UPtr<AtomReader>& atom_interface() const { return _atom_interface; } - const UPtr<BlockFactory>& block_factory() const { return _block_factory; } - const UPtr<Broadcaster>& broadcaster() const { return _broadcaster; } - const UPtr<BufferFactory>& buffer_factory() const { return _buffer_factory; } - const UPtr<ControlBindings>& control_bindings() const { return _control_bindings; } - const SPtr<Driver>& driver() const { return _driver; } - const UPtr<PostProcessor>& post_processor() const { return _post_processor; } - const UPtr<Raul::Maid>& maid() const { return _maid; } - const UPtr<UndoStack>& undo_stack() const { return _undo_stack; } - const UPtr<UndoStack>& redo_stack() const { return _redo_stack; } - const UPtr<Worker>& worker() const { return _worker; } - const UPtr<Worker>& sync_worker() const { return _sync_worker; } - - GraphImpl* root_graph() const { return _root_graph; } - void set_root_graph(GraphImpl* graph); - - RunContext& run_context() { return *_run_contexts[0]; } - - void flush_events(const std::chrono::milliseconds& sleep_ms); - - void advance(SampleCount nframes); - void locate(FrameTime s, SampleCount nframes); - void emit_notifications(FrameTime end); - bool pending_notifications(); - bool wait_for_tasks(); - void signal_tasks_available(); - Task* steal_task(unsigned start_thread); - - SPtr<Store> store() const; - - SampleRate sample_rate() const; - SampleCount block_length() const; - size_t sequence_size() const; - size_t event_queue_size() const; - - size_t n_threads() const { return _run_contexts.size(); } - bool atomic_bundles() const { return _atomic_bundles; } - bool activated() const { return _activated; } - - Properties load_properties() const; - -private: - Ingen::World* _world; - - SPtr<LV2Options> _options; - UPtr<BufferFactory> _buffer_factory; - UPtr<Raul::Maid> _maid; - SPtr<Driver> _driver; - UPtr<Worker> _worker; - UPtr<Worker> _sync_worker; - UPtr<Broadcaster> _broadcaster; - UPtr<ControlBindings> _control_bindings; - UPtr<BlockFactory> _block_factory; - UPtr<UndoStack> _undo_stack; - UPtr<UndoStack> _redo_stack; - UPtr<PostProcessor> _post_processor; - UPtr<PreProcessor> _pre_processor; - UPtr<SocketListener> _listener; - SPtr<EventWriter> _event_writer; - SPtr<Interface> _interface; - UPtr<AtomReader> _atom_interface; - GraphImpl* _root_graph; - - std::vector<Raul::RingBuffer*> _notifications; - std::vector<RunContext*> _run_contexts; - uint64_t _cycle_start_time; - Load _run_load; - Clock _clock; - - std::mt19937 _rand_engine; - std::uniform_real_distribution<float> _uniform_dist; - - std::condition_variable _tasks_available; - std::mutex _tasks_mutex; - - bool _quit_flag; - bool _reset_load_flag; - bool _atomic_bundles; - bool _activated; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_ENGINE_HPP diff --git a/src/server/EnginePort.hpp b/src/server/EnginePort.hpp deleted file mode 100644 index c14f363c..00000000 --- a/src/server/EnginePort.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_ENGINE_PORT_HPP -#define INGEN_ENGINE_ENGINE_PORT_HPP - -#include "raul/Deletable.hpp" -#include "raul/Noncopyable.hpp" - -#include <boost/intrusive/slist.hpp> - -#include "DuplexPort.hpp" - -namespace Ingen { -namespace Server { - -/** A "system" port (e.g. a Jack port, an external port on Ingen). - * - * @ingroup engine - */ -class EnginePort : public Raul::Noncopyable - , public Raul::Deletable - , public boost::intrusive::slist_base_hook<> -{ -public: - explicit EnginePort(DuplexPort* port) - : _graph_port(port) - , _buffer(nullptr) - , _handle(nullptr) - , _driver_index(0) - {} - - void set_buffer(void* buf) { _buffer = buf; } - void set_handle(void* buf) { _handle = buf; } - void set_driver_index(uint32_t index) { _driver_index = index; } - - void* buffer() const { return _buffer; } - void* handle() const { return _handle; } - uint32_t driver_index() const { return _driver_index; } - DuplexPort* graph_port() const { return _graph_port; } - bool is_input() const { return _graph_port->is_input(); } - -protected: - DuplexPort* _graph_port; - void* _buffer; - void* _handle; - uint32_t _driver_index; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_ENGINE_PORT_HPP diff --git a/src/server/Event.hpp b/src/server/Event.hpp deleted file mode 100644 index d9095def..00000000 --- a/src/server/Event.hpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_EVENT_HPP -#define INGEN_ENGINE_EVENT_HPP - -#include <atomic> - -#include "raul/Deletable.hpp" -#include "raul/Noncopyable.hpp" -#include "raul/Path.hpp" - -#include "ingen/Interface.hpp" -#include "ingen/Node.hpp" -#include "ingen/Status.hpp" -#include "ingen/types.hpp" - -#include "types.hpp" - -namespace Ingen { -namespace Server { - -class Engine; -class RunContext; -class PreProcessContext; - -/** An event (command) to perform some action on Ingen. - * - * Virtually all operations on Ingen are implemented as events. An event has - * three distinct execution phases: - * - * 1) Pre-process: In a non-realtime thread, prepare event for execution - * 2) Execute: In the audio thread, execute (apply) event - * 3) Post-process: In a non-realtime thread, finalize event - * (e.g. clean up and send replies) - * - * \ingroup engine - */ -class Event : public Raul::Deletable, public Raul::Noncopyable -{ -public: - /** Event mode to distinguish normal events from undo events. */ - enum class Mode { NORMAL, UNDO, REDO }; - - /** Execution mode for events that block and unblock preprocessing. */ - enum class Execution { - NORMAL, ///< Normal pipelined execution - ATOMIC, ///< Block pre-processing until this event is executed - BLOCK, ///< Begin atomic block of events - UNBLOCK ///< Finish atomic executed block of events - }; - - /** Pre-process event before execution (non-realtime). */ - virtual bool pre_process(PreProcessContext& ctx) = 0; - - /** Execute this event in the audio thread (realtime). */ - virtual void execute(RunContext& context) = 0; - - /** Post-process event after execution (non-realtime). */ - virtual void post_process() = 0; - - /** Write the inverse of this event to `sink`. */ - virtual void undo(Interface& target) {} - - /** Return true iff this event has been pre-processed. */ - inline bool is_prepared() const { return _status != Status::NOT_PREPARED; } - - /** Return the time stamp of this event. */ - inline SampleCount time() const { return _time; } - - /** Set the time stamp of this event. */ - inline void set_time(SampleCount time) { _time = time; } - - /** Get the next event to be processed after this one. */ - Event* next() const { return _next.load(); } - - /** Set the next event to be processed after this one. */ - void next(Event* ev) { _next = ev; } - - /** Return the status (success or error code) of this event. */ - Status status() const { return _status; } - - /** Return the blocking behaviour of this event (after construction). */ - virtual Execution get_execution() const { return Execution::NORMAL; } - - /** Return undo mode of this event. */ - Mode get_mode() const { return _mode; } - - /** Set the undo mode of this event. */ - void set_mode(Mode mode) { _mode = mode; } - - inline Engine& engine() { return _engine; } - -protected: - Event(Engine& engine, SPtr<Interface> client, int32_t id, FrameTime time) - : _engine(engine) - , _next(nullptr) - , _request_client(std::move(client)) - , _request_id(id) - , _time(time) - , _status(Status::NOT_PREPARED) - , _mode(Mode::NORMAL) - {} - - /** Constructor for internal events only */ - explicit Event(Engine& engine) - : _engine(engine) - , _next(nullptr) - , _request_id(0) - , _time(0) - , _status(Status::NOT_PREPARED) - , _mode(Mode::NORMAL) - {} - - inline bool pre_process_done(Status st) { - _status = st; - return st == Status::SUCCESS; - } - - inline bool pre_process_done(Status st, const URI& subject) { - _err_subject = subject; - return pre_process_done(st); - } - - inline bool pre_process_done(Status st, const Raul::Path& subject) { - return pre_process_done(st, path_to_uri(subject)); - } - - /** Respond to the originating client. */ - inline Status respond() { - if (_request_client && _request_id) { - _request_client->response(_request_id, _status, _err_subject); - } - return _status; - } - - Engine& _engine; - std::atomic<Event*> _next; - SPtr<Interface> _request_client; - int32_t _request_id; - FrameTime _time; - Status _status; - std::string _err_subject; - Mode _mode; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_EVENT_HPP diff --git a/src/server/EventWriter.cpp b/src/server/EventWriter.cpp deleted file mode 100644 index ebdf7562..00000000 --- a/src/server/EventWriter.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <boost/variant/apply_visitor.hpp> - -#include "ingen/URIs.hpp" - -#include "Engine.hpp" -#include "EventWriter.hpp" -#include "events.hpp" - -namespace Ingen { -namespace Server { - -EventWriter::EventWriter(Engine& engine) - : _engine(engine) - , _event_mode(Event::Mode::NORMAL) -{ -} - -SampleCount -EventWriter::now() const -{ - return _engine.event_time(); -} - -void -EventWriter::message(const Message& msg) -{ - boost::apply_visitor(*this, msg); -} - -void -EventWriter::operator()(const BundleBegin& msg) -{ - _engine.enqueue_event(new Events::Mark(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const BundleEnd& msg) -{ - _engine.enqueue_event(new Events::Mark(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const Put& msg) -{ - _engine.enqueue_event(new Events::Delta(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const Delta& msg) -{ - _engine.enqueue_event(new Events::Delta(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const Copy& msg) -{ - _engine.enqueue_event(new Events::Copy(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const Move& msg) -{ - _engine.enqueue_event(new Events::Move(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const Del& msg) -{ - _engine.enqueue_event(new Events::Delete(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const Connect& msg) -{ - _engine.enqueue_event(new Events::Connect(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const Disconnect& msg) -{ - _engine.enqueue_event( - new Events::Disconnect(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const DisconnectAll& msg) -{ - _engine.enqueue_event( - new Events::DisconnectAll(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const SetProperty& msg) -{ - _engine.enqueue_event(new Events::Delta(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const Undo& msg) -{ - _engine.enqueue_event(new Events::Undo(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const Redo& msg) -{ - _engine.enqueue_event(new Events::Undo(_engine, _respondee, now(), msg), - _event_mode); -} - -void -EventWriter::operator()(const Get& msg) -{ - _engine.enqueue_event(new Events::Get(_engine, _respondee, now(), msg), - _event_mode); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/EventWriter.hpp b/src/server/EventWriter.hpp deleted file mode 100644 index 2d4b9724..00000000 --- a/src/server/EventWriter.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_EVENTWRITER_HPP -#define INGEN_ENGINE_EVENTWRITER_HPP - -#include <memory> -#include <string> - -#include "ingen/Interface.hpp" -#include "ingen/Resource.hpp" -#include "ingen/types.hpp" - -#include "Event.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Server { - -class Engine; - -/** An Interface that creates and enqueues Events for the Engine to execute. - */ -class EventWriter : public Interface -{ -public: - explicit EventWriter(Engine& engine); - - URI uri() const override { return URI("ingen:/clients/event_writer"); } - - SPtr<Interface> respondee() const override { - return _respondee; - } - - void set_respondee(SPtr<Interface> respondee) override { - _respondee = respondee; - } - - void message(const Message& msg) override; - - void set_event_mode(Event::Mode mode) { _event_mode = mode; } - Event::Mode get_event_mode() { return _event_mode; } - - void operator()(const BundleBegin&); - void operator()(const BundleEnd&); - void operator()(const Connect&); - void operator()(const Copy&); - void operator()(const Del&); - void operator()(const Delta&); - void operator()(const Disconnect&); - void operator()(const DisconnectAll&); - void operator()(const Error&) {} - void operator()(const Get&); - void operator()(const Move&); - void operator()(const Put&); - void operator()(const Redo&); - void operator()(const Response&) {} - void operator()(const SetProperty&); - void operator()(const Undo&); - -protected: - Engine& _engine; - SPtr<Interface> _respondee; - Event::Mode _event_mode; - -private: - SampleCount now() const; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_EVENTWRITER_HPP diff --git a/src/server/FrameTimer.hpp b/src/server/FrameTimer.hpp deleted file mode 100644 index 367ac900..00000000 --- a/src/server/FrameTimer.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_FRAMETIMER_HPP -#define INGEN_ENGINE_FRAMETIMER_HPP - -#include <chrono> -#include <cmath> -#include <cstdint> - -namespace Ingen { -namespace Server { - -/** Delay-locked loop for monotonic sample time. - * - * See "Using a DLL to filter time" by Fons Adriaensen - * http://kokkinizita.linuxaudio.org/papers/usingdll.pdf - */ -class FrameTimer -{ -public: - static constexpr double PI = 3.14159265358979323846; - static constexpr double bandwidth = 1.0 / 8.0; // Hz - static constexpr double us_per_s = 1000000.0; - - FrameTimer(uint32_t period_size, uint32_t sample_rate) - : tper(((double)period_size / (double)sample_rate) * us_per_s) - , omega(2 * PI * bandwidth / us_per_s * tper) - , b(sqrt(2) * omega) - , c(omega * omega) - , nper(period_size) - { - } - - /** Update the timer for current real time `usec` and frame `frame`. */ - void update(uint64_t usec, uint64_t frame) { - if (!initialized || frame != n1) { - init(usec, frame); - return; - } - - // Calculate loop error - const double e = ((double)usec - t1); - - // Update loop - t0 = t1; - t1 += b * e + e2; - e2 += c * e; - - // Update frame counts - n0 = n1; - n1 += nper; - } - - /** Return an estimate of the frame time for current real time `usec`. */ - uint64_t frame_time(uint64_t usec) const { - if (!initialized) { - return 0; - } - - const double delta = (double)usec - t0; - const double period = t1 - t0; - return n0 + std::round(delta / period * nper); - } - -private: - void init(uint64_t now, uint64_t frame) { - // Init loop - e2 = tper; - t0 = now; - t1 = t0 + e2; - - // Init sample counts - n0 = frame; - n1 = n0 + nper; - - initialized = true; - } - - const double tper; - const double omega; - const double b; - const double c; - - uint64_t nper; - double e2; - double t0; - double t1; - uint64_t n0; - uint64_t n1; - bool initialized; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_FRAMETIMER_HPP diff --git a/src/server/GraphImpl.cpp b/src/server/GraphImpl.cpp deleted file mode 100644 index f9c4cb54..00000000 --- a/src/server/GraphImpl.cpp +++ /dev/null @@ -1,379 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <unordered_map> - -#include "ingen/Log.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" -#include "raul/Maid.hpp" - -#include "ArcImpl.hpp" -#include "BlockImpl.hpp" -#include "BufferFactory.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "GraphPlugin.hpp" -#include "PortImpl.hpp" -#include "ThreadManager.hpp" - -namespace Ingen { -namespace Server { - -GraphImpl::GraphImpl(Engine& engine, - const Raul::Symbol& symbol, - uint32_t poly, - GraphImpl* parent, - SampleRate srate, - uint32_t internal_poly) - : BlockImpl(new GraphPlugin(engine.world()->uris(), - engine.world()->uris().ingen_Graph, - Raul::Symbol("graph"), - "Ingen Graph"), - symbol, poly, parent, srate) - , _engine(engine) - , _poly_pre(internal_poly) - , _poly_process(internal_poly) - , _process(false) -{ - assert(internal_poly >= 1); - assert(internal_poly <= 128); -} - -GraphImpl::~GraphImpl() -{ - delete _plugin; -} - -BlockImpl* -GraphImpl::duplicate(Engine& engine, - const Raul::Symbol& symbol, - GraphImpl* parent) -{ - BufferFactory& bufs = *engine.buffer_factory(); - const SampleRate rate = engine.sample_rate(); - - // Duplicate graph - GraphImpl* dup = new GraphImpl( - engine, symbol, _polyphony, parent, rate, _poly_process); - - Properties props = properties(); - props.erase(bufs.uris().lv2_symbol); - props.insert({bufs.uris().lv2_symbol, bufs.forge().alloc(symbol.c_str())}); - dup->set_properties(props); - - // We need a map of port duplicates to duplicate arcs - typedef std::unordered_map<PortImpl*, PortImpl*> PortMap; - PortMap port_map; - - // Add duplicates of all ports - dup->_ports = bufs.maid().make_managed<Ports>(num_ports(), nullptr); - for (PortList::iterator p = _inputs.begin(); p != _inputs.end(); ++p) { - DuplexPort* p_dup = p->duplicate(engine, p->symbol(), dup); - dup->_inputs.push_front(*p_dup); - (*dup->_ports)[p->index()] = p_dup; - port_map.insert({&*p, p_dup}); - } - for (PortList::iterator p = _outputs.begin(); p != _outputs.end(); ++p) { - DuplexPort* p_dup = p->duplicate(engine, p->symbol(), dup); - dup->_outputs.push_front(*p_dup); - (*dup->_ports)[p->index()] = p_dup; - port_map.insert({&*p, p_dup}); - } - - // Add duplicates of all blocks - for (auto& b : _blocks) { - BlockImpl* b_dup = b.duplicate(engine, b.symbol(), dup); - dup->add_block(*b_dup); - b_dup->activate(*engine.buffer_factory()); - for (uint32_t p = 0; p < b.num_ports(); ++p) { - port_map.insert({b.port_impl(p), b_dup->port_impl(p)}); - } - } - - // Add duplicates of all arcs - for (const auto& a : _arcs) { - SPtr<ArcImpl> arc = dynamic_ptr_cast<ArcImpl>(a.second); - if (arc) { - auto t = port_map.find(arc->tail()); - auto h = port_map.find(arc->head()); - if (t != port_map.end() && h != port_map.end()) { - dup->add_arc(SPtr<ArcImpl>(new ArcImpl(t->second, h->second))); - } - } - } - - return dup; -} - -void -GraphImpl::activate(BufferFactory& bufs) -{ - BlockImpl::activate(bufs); - - for (auto& b : _blocks) { - b.activate(bufs); - } - - assert(_activated); -} - -void -GraphImpl::deactivate() -{ - if (_activated) { - BlockImpl::deactivate(); - - for (auto& b : _blocks) { - if (b.activated()) { - b.deactivate(); - } - } - } -} - -void -GraphImpl::disable(RunContext& context) -{ - _process = false; - for (auto& o : _outputs) { - o.clear_buffers(context); - } -} - -bool -GraphImpl::prepare_internal_poly(BufferFactory& bufs, uint32_t poly) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - // TODO: Subgraph dynamic polyphony (i.e. changing port polyphony) - - for (auto& b : _blocks) { - b.prepare_poly(bufs, poly); - } - - _poly_pre = poly; - return true; -} - -bool -GraphImpl::apply_internal_poly(RunContext& context, - BufferFactory& bufs, - Raul::Maid& maid, - uint32_t poly) -{ - // TODO: Subgraph dynamic polyphony (i.e. changing port polyphony) - - for (auto& b : _blocks) { - b.apply_poly(context, poly); - } - - for (auto& b : _blocks) { - for (uint32_t j = 0; j < b.num_ports(); ++j) { - PortImpl* const port = b.port_impl(j); - if (port->is_input() && dynamic_cast<InputPort*>(port)->direct_connect()) { - port->setup_buffers(context, bufs, port->poly()); - } - port->connect_buffers(); - } - } - - const bool polyphonic = parent_graph() && (poly == parent_graph()->internal_poly_process()); - for (auto& o : _outputs) { - o.setup_buffers(context, bufs, polyphonic ? poly : 1); - } - - _poly_process = poly; - return true; -} - -void -GraphImpl::pre_process(RunContext& context) -{ - // Mix down input ports and connect buffers - for (uint32_t i = 0; i < num_ports(); ++i) { - PortImpl* const port = _ports->at(i); - if (!port->is_driver_port()) { - port->pre_process(context); - port->pre_run(context); - port->connect_buffers(); - } - } -} - -void -GraphImpl::process(RunContext& context) -{ - if (!_process) { - return; - } - - pre_process(context); - run(context); - post_process(context); -} - -void -GraphImpl::run(RunContext& context) -{ - if (_compiled_graph) { - _compiled_graph->run(context); - } -} - -void -GraphImpl::set_buffer_size(RunContext& context, - BufferFactory& bufs, - LV2_URID type, - uint32_t size) -{ - BlockImpl::set_buffer_size(context, bufs, type, size); - - if (_compiled_graph) { - // FIXME - // for (size_t i = 0; i < _compiled_graph->size(); ++i) { - // const CompiledBlock& block = (*_compiled_graph)[i]; - // block.block()->set_buffer_size(context, bufs, type, size); - // } - } -} - -void -GraphImpl::add_block(BlockImpl& block) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - _blocks.push_front(block); -} - -void -GraphImpl::remove_block(BlockImpl& block) -{ - _blocks.erase(_blocks.iterator_to(block)); -} - -void -GraphImpl::add_arc(SPtr<ArcImpl> a) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - _arcs.emplace(std::make_pair(a->tail(), a->head()), a); -} - -SPtr<ArcImpl> -GraphImpl::remove_arc(const PortImpl* tail, const PortImpl* dst_port) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - auto i = _arcs.find(std::make_pair(tail, dst_port)); - if (i != _arcs.end()) { - SPtr<ArcImpl> arc = dynamic_ptr_cast<ArcImpl>(i->second); - _arcs.erase(i); - return arc; - } else { - return SPtr<ArcImpl>(); - } -} - -bool -GraphImpl::has_arc(const PortImpl* tail, const PortImpl* dst_port) const -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - auto i = _arcs.find(std::make_pair(tail, dst_port)); - return (i != _arcs.end()); -} - -void -GraphImpl::set_compiled_graph(MPtr<CompiledGraph>&& cg) -{ - if (_compiled_graph && _compiled_graph != cg) { - _engine.reset_load(); - } - _compiled_graph = std::move(cg); -} - -uint32_t -GraphImpl::num_ports_non_rt() const -{ - ThreadManager::assert_not_thread(THREAD_PROCESS); - return _inputs.size() + _outputs.size(); -} - -bool -GraphImpl::has_port_with_index(uint32_t index) const -{ - BufferFactory& bufs = *_engine.buffer_factory(); - const auto index_atom = bufs.forge().make(int32_t(index)); - - for (auto p = _inputs.begin(); p != _inputs.end(); ++p) { - if (p->has_property(bufs.uris().lv2_index, index_atom)) { - return true; - } - } - - for (auto p = _outputs.begin(); p != _outputs.end(); ++p) { - if (p->has_property(bufs.uris().lv2_index, index_atom)) { - return true; - } - } - - return false; -} - -void -GraphImpl::remove_port(DuplexPort& port) -{ - if (port.is_input()) { - _inputs.erase(_inputs.iterator_to(port)); - } else { - _outputs.erase(_outputs.iterator_to(port)); - } -} - -void -GraphImpl::clear_ports() -{ - _inputs.clear(); - _outputs.clear(); -} - -MPtr<BlockImpl::Ports> -GraphImpl::build_ports_array(Raul::Maid& maid) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - const size_t n = _inputs.size() + _outputs.size(); - MPtr<Ports> result = maid.make_managed<Ports>(n); - - std::map<size_t, DuplexPort*> ports; - for (PortList::iterator p = _inputs.begin(); p != _inputs.end(); ++p) { - ports.emplace(p->index(), &*p); - } - for (PortList::iterator p = _outputs.begin(); p != _outputs.end(); ++p) { - ports.emplace(p->index(), &*p); - } - - size_t i = 0; - for (const auto& p : ports) { - result->at(i++) = p.second; - } - - assert(i == n); - - return result; -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/GraphImpl.hpp b/src/server/GraphImpl.hpp deleted file mode 100644 index 3f11a84a..00000000 --- a/src/server/GraphImpl.hpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_GRAPHIMPL_HPP -#define INGEN_ENGINE_GRAPHIMPL_HPP - -#include <cstdlib> - -#include "ingen/ingen.h" - -#include "BlockImpl.hpp" -#include "CompiledGraph.hpp" -#include "DuplexPort.hpp" -#include "PluginImpl.hpp" -#include "PortType.hpp" -#include "ThreadManager.hpp" - -namespace Ingen { - -class Arc; - -namespace Server { - -class ArcImpl; -class CompiledGraph; -class Engine; -class RunContext; - -/** A group of blocks in a graph, possibly polyphonic. - * - * Note that this is also a Block, just one which contains Blocks. - * Therefore infinite subgraphing is possible, of polyphonic - * graphs of polyphonic blocks etc. etc. - * - * \ingroup engine - */ -class GraphImpl : public BlockImpl -{ -public: - GraphImpl(Engine& engine, - const Raul::Symbol& symbol, - uint32_t poly, - GraphImpl* parent, - SampleRate srate, - uint32_t internal_poly); - - virtual ~GraphImpl(); - - virtual GraphType graph_type() const { return GraphType::GRAPH; } - - BlockImpl* duplicate(Engine& engine, - const Raul::Symbol& symbol, - GraphImpl* parent); - - void activate(BufferFactory& bufs); - void deactivate(); - - void pre_process(RunContext& context); - void process(RunContext& context); - void run(RunContext& context); - - void set_buffer_size(RunContext& context, - BufferFactory& bufs, - LV2_URID type, - uint32_t size); - - /** Prepare for a new (internal) polyphony value. - * - * Pre-process thread, poly is actually applied by apply_internal_poly. - * \return true on success. - */ - bool prepare_internal_poly(BufferFactory& bufs, uint32_t poly); - - /** Apply a new (internal) polyphony value. - * - * Audio thread. - * - * \param context Process context - * \param bufs New set of buffers - * \param poly Must be < the most recent value passed to prepare_internal_poly. - * \param maid Any objects no longer needed will be pushed to this - */ - bool apply_internal_poly(RunContext& context, - BufferFactory& bufs, - Raul::Maid& maid, - uint32_t poly); - - // Graph specific stuff not inherited from Block - - typedef boost::intrusive::slist< - BlockImpl, boost::intrusive::constant_time_size<true> > Blocks; - - /** Add a block to this graph. - * Pre-process thread only. - */ - void add_block(BlockImpl& block); - - /** Remove a block from this graph. - * Pre-process thread only. - */ - void remove_block(BlockImpl& block); - - Blocks& blocks() { return _blocks; } - const Blocks& blocks() const { return _blocks; } - - uint32_t num_ports_non_rt() const; - bool has_port_with_index(uint32_t index) const; - - typedef boost::intrusive::slist< - DuplexPort, boost::intrusive::constant_time_size<true> > PortList; - - void add_input(DuplexPort& port) { - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - assert(port.is_input()); - _inputs.push_front(port); - } - - void add_output(DuplexPort& port) { - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - assert(port.is_output()); - _outputs.push_front(port); - } - - /** Remove port from ports list used in pre-processing thread. - * - * Port is not removed from ports array for process thread (which could be - * simultaneously running). - * - * Pre-processing thread or situations that won't cause races with it only. - */ - void remove_port(DuplexPort& port); - - /** Remove all ports from ports list used in pre-processing thread. - * - * Ports are not removed from ports array for process thread (which could be - * simultaneously running). Returned is a (inputs, outputs) pair. - * - * Pre-processing thread or situations that won't cause races with it only. - */ - void clear_ports(); - - /** Add an arc to this graph. - * Pre-processing thread only. - */ - void add_arc(SPtr<ArcImpl> a); - - /** Remove an arc from this graph. - * Pre-processing thread only. - */ - SPtr<ArcImpl> remove_arc(const PortImpl* tail, const PortImpl* dst_port); - - bool has_arc(const PortImpl* tail, const PortImpl* dst_port) const; - - /** Set a new compiled graph to run, and return the old one. */ - void set_compiled_graph(MPtr<CompiledGraph>&& cg); - - const MPtr<Ports>& external_ports() { return _ports; } - - void set_external_ports(MPtr<Ports>&& pa) { _ports = std::move(pa); } - - MPtr<Ports> build_ports_array(Raul::Maid& maid); - - /** Whether to run this graph's DSP bits in the audio thread */ - bool enabled() const { return _process; } - void enable() { _process = true; } - void disable(RunContext& context); - - uint32_t internal_poly() const { return _poly_pre; } - uint32_t internal_poly_process() const { return _poly_process; } - - Engine& engine() { return _engine; } - -private: - Engine& _engine; - uint32_t _poly_pre; ///< Pre-process thread only - uint32_t _poly_process; ///< Process thread only - MPtr<CompiledGraph> _compiled_graph; ///< Process thread only - PortList _inputs; ///< Pre-process thread only - PortList _outputs; ///< Pre-process thread only - Blocks _blocks; ///< Pre-process thread only - bool _process; ///< True iff graph is enabled -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_GRAPHIMPL_HPP diff --git a/src/server/GraphPlugin.hpp b/src/server/GraphPlugin.hpp deleted file mode 100644 index 308ed91a..00000000 --- a/src/server/GraphPlugin.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_GRAPHPLUGIN_HPP -#define INGEN_ENGINE_GRAPHPLUGIN_HPP - -#include <string> -#include "PluginImpl.hpp" - -namespace Ingen { -namespace Server { - -class BlockImpl; - -/** Implementation of a Graph plugin. - * - * Graphs don't actually work like this yet... - */ -class GraphPlugin : public PluginImpl -{ -public: - GraphPlugin(URIs& uris, - const URI& uri, - const Raul::Symbol& symbol, - const std::string& name) - : PluginImpl(uris, uris.ingen_Graph.urid, uri) - {} - - BlockImpl* instantiate(BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - Engine& engine, - const LilvState* state) - { - return nullptr; - } - - const Raul::Symbol symbol() const { return Raul::Symbol("graph"); } - const std::string name() const { return "Ingen Graph"; } - -private: - const std::string _symbol; - const std::string _name; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_GRAPHPLUGIN_HPP diff --git a/src/server/InputPort.cpp b/src/server/InputPort.cpp deleted file mode 100644 index 2f22491f..00000000 --- a/src/server/InputPort.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cstdlib> -#include <cassert> - -#include "ingen/Log.hpp" -#include "ingen/URIs.hpp" - -#include "ArcImpl.hpp" -#include "BlockImpl.hpp" -#include "Buffer.hpp" -#include "BufferFactory.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "InputPort.hpp" -#include "RunContext.hpp" -#include "mix.hpp" - -namespace Ingen { -namespace Server { - -InputPort::InputPort(BufferFactory& bufs, - BlockImpl* parent, - const Raul::Symbol& symbol, - uint32_t index, - uint32_t poly, - PortType type, - LV2_URID buffer_type, - const Atom& value, - size_t buffer_size) - : PortImpl(bufs, parent, symbol, index, poly, type, buffer_type, value, buffer_size, false) - , _num_arcs(0) -{ - const Ingen::URIs& uris = bufs.uris(); - - if (parent->graph_type() != Node::GraphType::GRAPH) { - add_property(uris.rdf_type, uris.lv2_InputPort.urid); - } -} - -bool -InputPort::apply_poly(RunContext& context, uint32_t poly) -{ - bool ret = PortImpl::apply_poly(context, poly); - if (!ret) { - poly = 1; - } - - assert(_voices->size() >= poly); - - return true; -} - -bool -InputPort::get_buffers(BufferFactory& bufs, - PortImpl::GetFn get, - const MPtr<Voices>& voices, - uint32_t poly, - size_t num_in_arcs) const -{ - if (is_a(PortType::ATOM) && !_value.is_valid()) { - poly = 1; - } - - if (is_a(PortType::AUDIO) && num_in_arcs == 0) { - // Audio input with no arcs, use shared zero buffer - for (uint32_t v = 0; v < poly; ++v) { - voices->at(v).buffer = bufs.silent_buffer(); - } - return false; - } - - // Otherwise, allocate local buffers - for (uint32_t v = 0; v < poly; ++v) { - voices->at(v).buffer.reset(); - voices->at(v).buffer = (bufs.*get)( - buffer_type(), _value.type(), _buffer_size); - voices->at(v).buffer->clear(); - if (_value.is_valid()) { - voices->at(v).buffer->set_value(_value); - } - } - return true; -} - -bool -InputPort::pre_get_buffers(BufferFactory& bufs, - MPtr<Voices>& voices, - uint32_t poly) const -{ - return get_buffers(bufs, &BufferFactory::get_buffer, voices, poly, _num_arcs); -} - -bool -InputPort::setup_buffers(RunContext& ctx, BufferFactory& bufs, uint32_t poly) -{ - if (is_a(PortType::ATOM) && !_value.is_valid()) { - poly = 1; - } - - if (_arcs.size() == 1 && !is_a(PortType::ATOM) && !_arcs.front().must_mix()) { - // Single non-mixing connection, use buffers directly - for (uint32_t v = 0; v < poly; ++v) { - _voices->at(v).buffer = _arcs.front().buffer(v); - } - return false; - } - - return get_buffers(bufs, &BufferFactory::claim_buffer, _voices, poly, _arcs.size()); -} - -void -InputPort::add_arc(RunContext& context, ArcImpl& c) -{ - _arcs.push_front(c); -} - -void -InputPort::remove_arc(ArcImpl& arc) -{ - _arcs.erase(_arcs.iterator_to(arc)); -} - -uint32_t -InputPort::max_tail_poly(RunContext& context) const -{ - return parent_block()->parent_graph()->internal_poly_process(); -} - -void -InputPort::pre_process(RunContext& context) -{ - if (_arcs.empty()) { - // No incoming arcs, just handle user-set value - for (uint32_t v = 0; v < _poly; ++v) { - // Update set state - update_set_state(context, v); - - // Prepare for write in case a set event executes this cycle - if (!_parent->is_main()) { - buffer(v)->prepare_write(context); - } - } - } else if (direct_connect()) { - // Directly connected, use source's buffer directly - for (uint32_t v = 0; v < _poly; ++v) { - _voices->at(v).buffer = _arcs.front().buffer(v); - } - } else { - // Mix down to local buffers in pre_run() - for (uint32_t v = 0; v < _poly; ++v) { - buffer(v)->prepare_write(context); - } - } -} - -void -InputPort::pre_run(RunContext& context) -{ - if ((_user_buffer || !_arcs.empty()) && !direct_connect()) { - const uint32_t src_poly = max_tail_poly(context); - const uint32_t max_n_srcs = _arcs.size() * src_poly + 1; - - for (uint32_t v = 0; v < _poly; ++v) { - if (!buffer(v)->get<void>()) { - continue; - } - - // Get all sources for this voice - const Buffer* srcs[max_n_srcs]; - uint32_t n_srcs = 0; - - if (_user_buffer) { - // Add buffer with user/UI input for this cycle - srcs[n_srcs++] = _user_buffer.get(); - } - - for (const auto& arc : _arcs) { - if (_poly == 1) { - // P -> 1 or 1 -> 1: all tail voices => each head voice - for (uint32_t w = 0; w < arc.tail()->poly(); ++w) { - assert(n_srcs < max_n_srcs); - srcs[n_srcs++] = arc.buffer(w, context.offset()).get(); - assert(srcs[n_srcs - 1]); - } - } else { - // P -> P or 1 -> P: tail voice => corresponding head voice - assert(n_srcs < max_n_srcs); - srcs[n_srcs++] = arc.buffer(v, context.offset()).get(); - assert(srcs[n_srcs - 1]); - } - } - - // Then mix them into our buffer for this voice - mix(context, buffer(v).get(), srcs, n_srcs); - update_values(context.offset(), v); - } - } else if (is_a(PortType::CONTROL)) { - for (uint32_t v = 0; v < _poly; ++v) { - update_values(context.offset(), v); - } - } -} - -SampleCount -InputPort::next_value_offset(SampleCount offset, SampleCount end) const -{ - SampleCount earliest = end; - - if (_user_buffer) { - earliest = _user_buffer->next_value_offset(offset, end); - } - - for (const auto& arc : _arcs) { - const SampleCount o = arc.tail()->next_value_offset(offset, end); - if (o < earliest) { - earliest = o; - } - } - - return earliest; -} - -void -InputPort::post_process(RunContext& context) -{ - if (!_arcs.empty() || _force_monitor_update) { - monitor(context, _force_monitor_update); - _force_monitor_update = false; - } - - /* Finished processing any user/UI messages for this cycle, drop reference - to user buffer. */ - _user_buffer.reset(); -} - -bool -InputPort::direct_connect() const -{ - return _arcs.size() == 1 - && !_parent->is_main() - && !_arcs.front().must_mix() - && buffer(0)->type() != _bufs.uris().atom_Sequence; -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/InputPort.hpp b/src/server/InputPort.hpp deleted file mode 100644 index 708f7ea2..00000000 --- a/src/server/InputPort.hpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_INPUTPORT_HPP -#define INGEN_ENGINE_INPUTPORT_HPP - -#include <cassert> -#include <cstdlib> - -#include <boost/intrusive/slist.hpp> - -#include "ingen/types.hpp" - -#include "ArcImpl.hpp" -#include "PortImpl.hpp" - -namespace Ingen { -namespace Server { - -class ArcImpl; -class BlockImpl; -class RunContext; - -/** An input port on a Block or Graph. - * - * All ports have a Buffer, but the actual contents (data) of that buffer may be - * set directly to the incoming arc's buffer if there's only one inbound - * arc, to eliminate the need to copy/mix. - * - * If a port has multiple arcs, they will be mixed down into the local - * buffer and it will be used. - * - * \ingroup engine - */ -class InputPort : public PortImpl -{ -public: - InputPort(BufferFactory& bufs, - BlockImpl* parent, - const Raul::Symbol& symbol, - uint32_t index, - uint32_t poly, - PortType type, - LV2_URID buffer_type, - const Atom& value, - size_t buffer_size = 0); - - typedef boost::intrusive::slist<ArcImpl, - boost::intrusive::constant_time_size<true> - > Arcs; - - /** Return the maximum polyphony of an output connected to this input. */ - virtual uint32_t max_tail_poly(RunContext& context) const; - - bool apply_poly(RunContext& context, uint32_t poly); - - /** Add an arc. Realtime safe. - * - * The buffer of this port will be set directly to the arc's buffer - * if there is only one arc, since no copying/mixing needs to take place. - * - * setup_buffers() must be called later for the change to take effect. - */ - void add_arc(RunContext& context, ArcImpl& c); - - /** Remove an arc. Realtime safe. - * - * setup_buffers() must be called later for the change to take effect. - */ - void remove_arc(ArcImpl& arc); - - /** Like `get_buffers`, but for the pre-process thread. - * - * This uses the "current" number of arcs fromthe perspective of the - * pre-process thread to allocate buffers for application of a - * connection/disconnection/etc in the next process cycle. - */ - bool pre_get_buffers(BufferFactory& bufs, - MPtr<Voices>& voices, - uint32_t poly) const; - - bool setup_buffers(RunContext& ctx, BufferFactory& bufs, uint32_t poly); - - /** Set up buffer pointers. */ - void pre_process(RunContext& context); - - /** Prepare buffer for access, mixing if necessary. */ - void pre_run(RunContext& context); - - /** Prepare buffer for next process cycle. */ - void post_process(RunContext& context); - - SampleCount next_value_offset(SampleCount offset, SampleCount end) const; - - size_t num_arcs() const { return _num_arcs; } - void increment_num_arcs() { ++_num_arcs; } - void decrement_num_arcs() { --_num_arcs; } - - bool direct_connect() const; - -protected: - bool get_buffers(BufferFactory& bufs, - PortImpl::GetFn get, - const MPtr<Voices>& voices, - uint32_t poly, - size_t num_in_arcs) const; - - size_t _num_arcs; ///< Pre-process thread - Arcs _arcs; ///< Audio thread -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_INPUTPORT_HPP diff --git a/src/server/InternalBlock.cpp b/src/server/InternalBlock.cpp deleted file mode 100644 index 3d8f7390..00000000 --- a/src/server/InternalBlock.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "Buffer.hpp" -#include "Engine.hpp" -#include "InternalBlock.hpp" -#include "InternalPlugin.hpp" -#include "PortImpl.hpp" - -namespace Ingen { -namespace Server { - -InternalBlock::InternalBlock(PluginImpl* plugin, - const Raul::Symbol& symbol, - bool poly, - GraphImpl* parent, - SampleRate rate) - : BlockImpl(plugin, symbol, poly, parent, rate) -{} - -BlockImpl* -InternalBlock::duplicate(Engine& engine, - const Raul::Symbol& symbol, - GraphImpl* parent) -{ - BufferFactory& bufs = *engine.buffer_factory(); - - BlockImpl* copy = reinterpret_cast<InternalPlugin*>(_plugin)->instantiate( - bufs, symbol, _polyphonic, parent_graph(), engine, nullptr); - - for (size_t i = 0; i < num_ports(); ++i) { - const Atom& value = port_impl(i)->value(); - copy->port_impl(i)->set_property(bufs.uris().ingen_value, value); - copy->port_impl(i)->set_value(value); - } - - return copy; -} - -void -InternalBlock::pre_process(RunContext& context) -{ - for (uint32_t i = 0; i < num_ports(); ++i) { - PortImpl* const port = _ports->at(i); - if (port->is_input()) { - port->pre_process(context); - } else if (port->buffer_type() == _plugin->uris().atom_Sequence) { - /* Output sequences are initialized in LV2 format, an atom:Chunk - with size set to the capacity of the buffer. Internal nodes - don't care, so clear to an empty sequences so appending events - results in a valid output. */ - for (uint32_t v = 0; v < port->poly(); ++v) { - port->buffer(v)->clear(); - } - } - } -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/InternalBlock.hpp b/src/server/InternalBlock.hpp deleted file mode 100644 index a57bd89f..00000000 --- a/src/server/InternalBlock.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_INTERNALBLOCK_HPP -#define INGEN_ENGINE_INTERNALBLOCK_HPP - -#include "BlockImpl.hpp" - -namespace Ingen { -namespace Server { - -/** An internal Block implemented inside Ingen. - * - * \ingroup engine - */ -class InternalBlock : public BlockImpl -{ -public: - InternalBlock(PluginImpl* plugin, - const Raul::Symbol& symbol, - bool poly, - GraphImpl* parent, - SampleRate rate); - - BlockImpl* duplicate(Engine& engine, - const Raul::Symbol& symbol, - GraphImpl* parent); - - virtual void pre_process(RunContext& context); -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_BLOCKIMPL_HPP diff --git a/src/server/InternalPlugin.cpp b/src/server/InternalPlugin.cpp deleted file mode 100644 index 6529b9c0..00000000 --- a/src/server/InternalPlugin.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/URIs.hpp" -#include "internals/Controller.hpp" -#include "internals/BlockDelay.hpp" -#include "internals/Note.hpp" -#include "internals/Time.hpp" -#include "internals/Trigger.hpp" - -#include "Engine.hpp" -#include "InternalPlugin.hpp" - -namespace Ingen { -namespace Server { - -using namespace Internals; - -InternalPlugin::InternalPlugin(URIs& uris, - const URI& uri, - const Raul::Symbol& symbol) - : PluginImpl(uris, uris.ingen_Internal.urid, uri) - , _symbol(symbol) -{ - set_property(uris.rdf_type, uris.ingen_Internal); -} - -BlockImpl* -InternalPlugin::instantiate(BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - Engine& engine, - const LilvState* state) -{ - const SampleCount srate = engine.sample_rate(); - - if (uri() == NS_INTERNALS "BlockDelay") { - return new BlockDelayNode(this, bufs, symbol, polyphonic, parent, srate); - } else if (uri() == NS_INTERNALS "Controller") { - return new ControllerNode(this, bufs, symbol, polyphonic, parent, srate); - } else if (uri() == NS_INTERNALS "Note") { - return new NoteNode(this, bufs, symbol, polyphonic, parent, srate); - } else if (uri() == NS_INTERNALS "Time") { - return new TimeNode(this, bufs, symbol, polyphonic, parent, srate); - } else if (uri() == NS_INTERNALS "Trigger") { - return new TriggerNode(this, bufs, symbol, polyphonic, parent, srate); - } else { - return nullptr; - } -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/InternalPlugin.hpp b/src/server/InternalPlugin.hpp deleted file mode 100644 index 79309beb..00000000 --- a/src/server/InternalPlugin.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_INTERNALPLUGIN_HPP -#define INGEN_ENGINE_INTERNALPLUGIN_HPP - -#include "raul/Symbol.hpp" - -#include "PluginImpl.hpp" - -#define NS_INTERNALS "http://drobilla.net/ns/ingen-internals#" - -namespace Ingen { -namespace Server { - -class BlockImpl; -class BufferFactory; - -/** Implementation of an Internal plugin. - */ -class InternalPlugin : public PluginImpl -{ -public: - InternalPlugin(URIs& uris, - const URI& uri, - const Raul::Symbol& symbol); - - BlockImpl* instantiate(BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - Engine& engine, - const LilvState* state); - - const Raul::Symbol symbol() const { return _symbol; } - -private: - const Raul::Symbol _symbol; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_INTERNALPLUGIN_HPP diff --git a/src/server/JackDriver.cpp b/src/server/JackDriver.cpp deleted file mode 100644 index 973e3eb7..00000000 --- a/src/server/JackDriver.cpp +++ /dev/null @@ -1,584 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen_config.h" - -#include <cstdlib> -#include <string> - -#include <jack/midiport.h> -#ifdef INGEN_JACK_SESSION -#include <jack/session.h> -#include <boost/format.hpp> -#include "ingen/Serialiser.hpp" -#endif -#ifdef HAVE_JACK_METADATA -#include <jack/metadata.h> -#include "jackey.h" -#endif - -#include "ingen/Configuration.hpp" -#include "ingen/LV2Features.hpp" -#include "ingen/Log.hpp" -#include "ingen/URI.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/World.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" - -#include "Buffer.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "JackDriver.hpp" -#include "PortImpl.hpp" -#include "ThreadManager.hpp" -#include "util.hpp" - -typedef jack_default_audio_sample_t jack_sample_t; - -namespace Ingen { -namespace Server { - -JackDriver::JackDriver(Engine& engine) - : _engine(engine) - , _sem(0) - , _flag(false) - , _client(nullptr) - , _block_length(0) - , _seq_size(0) - , _sample_rate(0) - , _is_activated(false) - , _old_bpm(120.0f) - , _old_frame(0) - , _old_rolling(false) -{ - _midi_event_type = _engine.world()->uris().midi_MidiEvent; - lv2_atom_forge_init( - &_forge, &engine.world()->uri_map().urid_map_feature()->urid_map); -} - -JackDriver::~JackDriver() -{ - deactivate(); - _ports.clear_and_dispose([](EnginePort* p) { delete p; }); -} - -bool -JackDriver::attach(const std::string& server_name, - const std::string& client_name, - void* jack_client) -{ - assert(!_client); - if (!jack_client) { -#ifdef INGEN_JACK_SESSION - const std::string uuid = _engine.world()->jack_uuid(); - if (!uuid.empty()) { - _client = jack_client_open(client_name.c_str(), - JackSessionID, nullptr, - uuid.c_str()); - _engine.log().info(fmt("Connected to Jack as `%1%' (UUID `%2%')\n") - % client_name.c_str() % uuid); - } -#endif - - // Try supplied server name - if (!_client && !server_name.empty()) { - if ((_client = jack_client_open(client_name.c_str(), - JackServerName, nullptr, - server_name.c_str()))) { - _engine.log().info(fmt("Connected to Jack server `%1%'\n") - % server_name); - } - } - - // Either server name not specified, or supplied server name does not exist - // Connect to default server - if (!_client) { - if ((_client = jack_client_open(client_name.c_str(), JackNullOption, nullptr))) { - _engine.log().info("Connected to default Jack server\n"); - } - } - - // Still failed - if (!_client) { - _engine.log().error("Unable to connect to Jack\n"); - return false; - } - } else { - _client = (jack_client_t*)jack_client; - } - - _sample_rate = jack_get_sample_rate(_client); - _block_length = jack_get_buffer_size(_client); - _seq_size = jack_port_type_get_buffer_size(_client, JACK_DEFAULT_MIDI_TYPE); - - _fallback_buffer = AudioBufPtr( - static_cast<float*>( - Buffer::aligned_alloc(sizeof(float) * _block_length))); - - jack_on_shutdown(_client, shutdown_cb, this); - - jack_set_thread_init_callback(_client, thread_init_cb, this); - jack_set_buffer_size_callback(_client, block_length_cb, this); -#ifdef INGEN_JACK_SESSION - jack_set_session_callback(_client, session_cb, this); -#endif - - for (auto& p : _ports) { - register_port(p); - } - - return true; -} - -bool -JackDriver::activate() -{ - World* world = _engine.world(); - - if (_is_activated) { - _engine.log().warn("Jack driver already activated\n"); - return false; - } - - if (!_client) { - attach(world->conf().option("jack-server").ptr<char>(), - world->conf().option("jack-name").ptr<char>(), nullptr); - } - - if (!_client) { - return false; - } - - jack_set_process_callback(_client, process_cb, this); - - _is_activated = true; - - if (jack_activate(_client)) { - _engine.log().error("Could not activate Jack client, aborting\n"); - return false; - } else { - _engine.log().info(fmt("Activated Jack client `%1%'\n") % - world->conf().option("jack-name").ptr<char>()); - } - return true; -} - -void -JackDriver::deactivate() -{ - if (_is_activated) { - _flag = true; - _is_activated = false; - _sem.timed_wait(std::chrono::seconds(1)); - - for (auto& p : _ports) { - unregister_port(p); - } - - if (_client) { - jack_deactivate(_client); - jack_client_close(_client); - _client = nullptr; - } - - _engine.log().info("Deactivated Jack client\n"); - } -} - -EnginePort* -JackDriver::get_port(const Raul::Path& path) -{ - for (auto& p : _ports) { - if (p.graph_port()->path() == path) { - return &p; - } - } - - return nullptr; -} - -void -JackDriver::add_port(RunContext& context, EnginePort* port) -{ - _ports.push_back(*port); - - DuplexPort* graph_port = port->graph_port(); - if (graph_port->is_a(PortType::AUDIO) || graph_port->is_a(PortType::CV)) { - const SampleCount nframes = context.nframes(); - jack_port_t* jport = (jack_port_t*)port->handle(); - void* jbuf = jack_port_get_buffer(jport, nframes); - - /* Jack fails to return a buffer if this is too soon after registering - the port, so use a silent fallback buffer if necessary. */ - graph_port->set_driver_buffer( - jbuf ? jbuf : _fallback_buffer.get(), - nframes * sizeof(float)); - } -} - -void -JackDriver::remove_port(RunContext& context, EnginePort* port) -{ - _ports.erase(_ports.iterator_to(*port)); -} - -void -JackDriver::register_port(EnginePort& port) -{ - jack_port_t* jack_port = jack_port_register( - _client, - port.graph_port()->path().substr(1).c_str(), - ((port.graph_port()->is_a(PortType::AUDIO) || - port.graph_port()->is_a(PortType::CV)) - ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE), - (port.graph_port()->is_input() - ? JackPortIsInput : JackPortIsOutput), - 0); - - if (!jack_port) { - throw JackDriver::PortRegistrationFailedException(); - } - - port.set_handle(jack_port); - - for (const auto& p : port.graph_port()->properties()) { - port_property_internal(jack_port, p.first, p.second); - } -} - -void -JackDriver::unregister_port(EnginePort& port) -{ - if (jack_port_unregister(_client, (jack_port_t*)port.handle())) { - _engine.log().error("Failed to unregister Jack port\n"); - } - - port.set_handle(nullptr); -} - -void -JackDriver::rename_port(const Raul::Path& old_path, - const Raul::Path& new_path) -{ - EnginePort* eport = get_port(old_path); - if (eport) { -#ifdef HAVE_JACK_PORT_RENAME - jack_port_rename( - _client, (jack_port_t*)eport->handle(), new_path.substr(1).c_str()); -#else - jack_port_set_name((jack_port_t*)eport->handle(), - new_path.substr(1).c_str()); -#endif - } -} - -void -JackDriver::port_property(const Raul::Path& path, - const URI& uri, - const Atom& value) -{ -#ifdef HAVE_JACK_METADATA - EnginePort* eport = get_port(path); - if (eport) { - const jack_port_t* const jport = (const jack_port_t*)eport->handle(); - port_property_internal(jport, uri, value); - } -#endif -} - -void -JackDriver::port_property_internal(const jack_port_t* jport, - const URI& uri, - const Atom& value) -{ -#ifdef HAVE_JACK_METADATA - if (uri == _engine.world()->uris().lv2_name) { - jack_set_property(_client, jack_port_uuid(jport), - JACK_METADATA_PRETTY_NAME, value.ptr<char>(), "text/plain"); - } else if (uri == _engine.world()->uris().lv2_index) { - jack_set_property(_client, jack_port_uuid(jport), - JACKEY_ORDER, std::to_string(value.get<int32_t>()).c_str(), - "http://www.w3.org/2001/XMLSchema#integer"); - } else if (uri == _engine.world()->uris().rdf_type) { - if (value == _engine.world()->uris().lv2_CVPort) { - jack_set_property(_client, jack_port_uuid(jport), - JACKEY_SIGNAL_TYPE, "CV", "text/plain"); - } - } -#endif -} - -EnginePort* -JackDriver::create_port(DuplexPort* graph_port) -{ - EnginePort* eport = nullptr; - if (graph_port->is_a(PortType::AUDIO) || graph_port->is_a(PortType::CV)) { - // Audio buffer port, use Jack buffer directly - eport = new EnginePort(graph_port); - graph_port->set_is_driver_port(*_engine.buffer_factory()); - } else if (graph_port->is_a(PortType::ATOM) && - graph_port->buffer_type() == _engine.world()->uris().atom_Sequence) { - // Sequence port, make Jack port but use internal LV2 format buffer - eport = new EnginePort(graph_port); - } - - if (eport) { - register_port(*eport); - } - - return eport; -} - -void -JackDriver::pre_process_port(RunContext& context, EnginePort* port) -{ - const URIs& uris = context.engine().world()->uris(); - const SampleCount nframes = context.nframes(); - jack_port_t* jack_port = (jack_port_t*)port->handle(); - DuplexPort* graph_port = port->graph_port(); - Buffer* graph_buf = graph_port->buffer(0).get(); - void* jack_buf = jack_port_get_buffer(jack_port, nframes); - - if (graph_port->is_a(PortType::AUDIO) || graph_port->is_a(PortType::CV)) { - graph_port->set_driver_buffer(jack_buf, nframes * sizeof(float)); - if (graph_port->is_input()) { - graph_port->monitor(context); - } else { - graph_port->buffer(0)->clear(); // TODO: Avoid when possible - } - } else if (graph_port->buffer_type() == uris.atom_Sequence) { - graph_buf->prepare_write(context); - if (graph_port->is_input()) { - // Copy events from Jack port buffer into graph port buffer - const jack_nframes_t event_count = jack_midi_get_event_count(jack_buf); - for (jack_nframes_t i = 0; i < event_count; ++i) { - jack_midi_event_t ev; - jack_midi_event_get(&ev, jack_buf, i); - if (!graph_buf->append_event( - ev.time, ev.size, _midi_event_type, ev.buffer)) { - _engine.log().rt_error("Failed to write to MIDI buffer, events lost!\n"); - } - } - } - graph_port->monitor(context); - } -} - -void -JackDriver::post_process_port(RunContext& context, EnginePort* port) -{ - const URIs& uris = context.engine().world()->uris(); - const SampleCount nframes = context.nframes(); - jack_port_t* jack_port = (jack_port_t*)port->handle(); - DuplexPort* graph_port = port->graph_port(); - void* jack_buf = port->buffer(); - - if (port->graph_port()->is_output()) { - if (!jack_buf) { - // First cycle for a new output, so pre_process wasn't called - jack_buf = jack_port_get_buffer(jack_port, nframes); - port->set_buffer(jack_buf); - } - - if (graph_port->buffer_type() == uris.atom_Sequence) { - // Copy LV2 MIDI events to Jack MIDI buffer - Buffer* const graph_buf = graph_port->buffer(0).get(); - LV2_Atom_Sequence* seq = graph_buf->get<LV2_Atom_Sequence>(); - - jack_midi_clear_buffer(jack_buf); - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - const uint8_t* buf = (const uint8_t*)LV2_ATOM_BODY(&ev->body); - if (ev->body.type == _midi_event_type) { - jack_midi_event_write( - jack_buf, ev->time.frames, buf, ev->body.size); - } - } - } - } - - // Reset graph port buffer pointer to no longer point to the Jack buffer - if (graph_port->is_driver_port()) { - graph_port->set_driver_buffer(nullptr, 0); - } -} - -void -JackDriver::append_time_events(RunContext& context, - Buffer& buffer) -{ - const URIs& uris = context.engine().world()->uris(); - const jack_position_t* pos = &_position; - const bool rolling = (_transport_state == JackTransportRolling); - - // Do nothing if there is no unexpected time change (other than rolling) - if (rolling == _old_rolling && - pos->frame == _old_frame && - pos->beats_per_minute == _old_bpm) { - return; - } - - // Update old time information to detect change next cycle - _old_frame = pos->frame; - _old_rolling = rolling; - _old_bpm = pos->beats_per_minute; - - // Build an LV2 position object to append to the buffer - LV2_Atom pos_buf[16]; - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_set_buffer(&_forge, (uint8_t*)pos_buf, sizeof(pos_buf)); - lv2_atom_forge_object(&_forge, &frame, 0, uris.time_Position); - lv2_atom_forge_key(&_forge, uris.time_frame); - lv2_atom_forge_long(&_forge, pos->frame); - lv2_atom_forge_key(&_forge, uris.time_speed); - lv2_atom_forge_float(&_forge, rolling ? 1.0 : 0.0); - if (pos->valid & JackPositionBBT) { - lv2_atom_forge_key(&_forge, uris.time_barBeat); - lv2_atom_forge_float( - &_forge, pos->beat - 1 + (pos->tick / pos->ticks_per_beat)); - lv2_atom_forge_key(&_forge, uris.time_bar); - lv2_atom_forge_long(&_forge, pos->bar - 1); - lv2_atom_forge_key(&_forge, uris.time_beatUnit); - lv2_atom_forge_int(&_forge, pos->beat_type); - lv2_atom_forge_key(&_forge, uris.time_beatsPerBar); - lv2_atom_forge_float(&_forge, pos->beats_per_bar); - lv2_atom_forge_key(&_forge, uris.time_beatsPerMinute); - lv2_atom_forge_float(&_forge, pos->beats_per_minute); - } - - // Append position to buffer at offset 0 (start of this cycle) - LV2_Atom* lpos = (LV2_Atom*)pos_buf; - buffer.append_event( - 0, lpos->size, lpos->type, (const uint8_t*)LV2_ATOM_BODY_CONST(lpos)); -} - -/**** Jack Callbacks ****/ - -/** Jack process callback, drives entire audio thread. - * - * \callgraph - */ -REALTIME int -JackDriver::_process_cb(jack_nframes_t nframes) -{ - if (nframes == 0 || ! _is_activated) { - if (_flag) { - _sem.post(); - } - return 0; - } - - /* Note that Jack may not call this function for a cycle, if overloaded, - so a rolling counter here would not always be correct. */ - const jack_nframes_t start_of_current_cycle = jack_last_frame_time(_client); - - _transport_state = jack_transport_query(_client, &_position); - - _engine.locate(start_of_current_cycle, nframes); - - // Read input - for (auto& p : _ports) { - pre_process_port(_engine.run_context(), &p); - } - - // Process - _engine.run(nframes); - - // Write output - for (auto& p : _ports) { - post_process_port(_engine.run_context(), &p); - } - - // Update expected transport frame for next cycle to detect changes - if (_transport_state == JackTransportRolling) { - _old_frame += nframes; - } - - return 0; -} - -void -JackDriver::_thread_init_cb() -{ - ThreadManager::set_flag(THREAD_PROCESS); - ThreadManager::set_flag(THREAD_IS_REAL_TIME); -} - -void -JackDriver::_shutdown_cb() -{ - _engine.log().info("Jack shutdown, exiting\n"); - _is_activated = false; - _client = nullptr; -} - -int -JackDriver::_block_length_cb(jack_nframes_t nframes) -{ - if (_engine.root_graph()) { - _block_length = nframes; - _seq_size = jack_port_type_get_buffer_size(_client, JACK_DEFAULT_MIDI_TYPE); - _engine.root_graph()->set_buffer_size( - _engine.run_context(), *_engine.buffer_factory(), PortType::AUDIO, - _engine.buffer_factory()->audio_buffer_size(nframes)); - _engine.root_graph()->set_buffer_size( - _engine.run_context(), *_engine.buffer_factory(), PortType::ATOM, - _seq_size); - } - return 0; -} - -#ifdef INGEN_JACK_SESSION -void -JackDriver::_session_cb(jack_session_event_t* event) -{ - _engine.log().info(fmt("Jack session save to %1%\n") % event->session_dir); - - const std::string cmd = ( - boost::format("ingen -eg -n %1% -u %2% -l ${SESSION_DIR}") - % jack_get_client_name(_client) - % event->client_uuid).str(); - - SPtr<Serialiser> serialiser = _engine.world()->serialiser(); - if (serialiser) { - std::lock_guard<std::mutex> lock(_engine.world()->rdf_mutex()); - - SPtr<Node> root(_engine.root_graph(), NullDeleter<Node>); - serialiser->write_bundle(root, - URI(std::string("file://") + event->session_dir)); - } - - event->command_line = (char*)malloc(cmd.size() + 1); - memcpy(event->command_line, cmd.c_str(), cmd.size() + 1); - jack_session_reply(_client, event); - - switch (event->type) { - case JackSessionSave: - break; - case JackSessionSaveAndQuit: - _engine.log().warn("Jack session quit\n"); - _engine.quit(); - break; - case JackSessionSaveTemplate: - break; - } - - jack_session_event_free(event); -} -#endif - -} // namespace Server -} // namespace Ingen diff --git a/src/server/JackDriver.hpp b/src/server/JackDriver.hpp deleted file mode 100644 index 2a21d96e..00000000 --- a/src/server/JackDriver.hpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_JACKAUDIODRIVER_HPP -#define INGEN_ENGINE_JACKAUDIODRIVER_HPP - -#include "ingen_config.h" - -#include <string> -#include <atomic> - -#include <jack/jack.h> -#include <jack/thread.h> -#include <jack/transport.h> -#ifdef INGEN_JACK_SESSION -#include <jack/session.h> -#endif - -#include "ingen/types.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/forge.h" -#include "raul/Semaphore.hpp" - -#include "Driver.hpp" -#include "EnginePort.hpp" - -namespace Raul { class Path; } - -namespace Ingen { -namespace Server { - -class DuplexPort; -class Engine; -class GraphImpl; -class JackDriver; -class PortImpl; - -/** The Jack Driver. - * - * The process callback here drives the entire audio thread by "pulling" - * events from queues, processing them, running the graphs, and passing - * events along to the PostProcessor. - * - * \ingroup engine - */ -class JackDriver : public Driver -{ -public: - explicit JackDriver(Engine& engine); - ~JackDriver(); - - bool attach(const std::string& server_name, - const std::string& client_name, - void* jack_client); - - bool activate(); - void deactivate(); - - bool dynamic_ports() const { return true; } - - EnginePort* create_port(DuplexPort* graph_port); - EnginePort* get_port(const Raul::Path& path); - - void rename_port(const Raul::Path& old_path, const Raul::Path& new_path); - void port_property(const Raul::Path& path, const URI& uri, const Atom& value); - void add_port(RunContext& context, EnginePort* port); - void remove_port(RunContext& context, EnginePort* port); - void register_port(EnginePort& port); - void unregister_port(EnginePort& port); - - /** Transport state for this frame. - * Intended to only be called from the audio thread. */ - inline const jack_position_t* position() { return &_position; } - inline jack_transport_state_t transport_state() { return _transport_state; } - - void append_time_events(RunContext& context, - Buffer& buffer); - - int real_time_priority() { return jack_client_real_time_priority(_client); } - - jack_client_t* jack_client() const { return _client; } - SampleCount block_length() const { return _block_length; } - size_t seq_size() const { return _seq_size; } - SampleCount sample_rate() const { return _sample_rate; } - - inline SampleCount frame_time() const { return _client ? jack_frame_time(_client) : 0; } - - class PortRegistrationFailedException : public std::exception {}; - -private: - friend class JackPort; - - // Static JACK callbacks which call the non-static callbacks (methods) - inline static void thread_init_cb(void* const jack_driver) { - return ((JackDriver*)jack_driver)->_thread_init_cb(); - } - inline static void shutdown_cb(void* const jack_driver) { - return ((JackDriver*)jack_driver)->_shutdown_cb(); - } - inline static int process_cb(jack_nframes_t nframes, void* const jack_driver) { - return ((JackDriver*)jack_driver)->_process_cb(nframes); - } - inline static int block_length_cb(jack_nframes_t nframes, void* const jack_driver) { - return ((JackDriver*)jack_driver)->_block_length_cb(nframes); - } -#ifdef INGEN_JACK_SESSION - inline static void session_cb(jack_session_event_t* event, void* jack_driver) { - ((JackDriver*)jack_driver)->_session_cb(event); - } -#endif - - void pre_process_port(RunContext& context, EnginePort* port); - void post_process_port(RunContext& context, EnginePort* port); - - void port_property_internal(const jack_port_t* jport, - const URI& uri, - const Atom& value); - - // Non static callbacks (methods) - void _thread_init_cb(); - void _shutdown_cb(); - int _process_cb(jack_nframes_t nframes); - int _block_length_cb(jack_nframes_t nframes); -#ifdef INGEN_JACK_SESSION - void _session_cb(jack_session_event_t* event); -#endif - -protected: - typedef boost::intrusive::slist<EnginePort, - boost::intrusive::cache_last<true> - > Ports; - - using AudioBufPtr = UPtr<float, FreeDeleter<float>>; - - Engine& _engine; - Ports _ports; - AudioBufPtr _fallback_buffer; - LV2_Atom_Forge _forge; - Raul::Semaphore _sem; - std::atomic<bool> _flag; - jack_client_t* _client; - jack_nframes_t _block_length; - size_t _seq_size; - jack_nframes_t _sample_rate; - uint32_t _midi_event_type; - bool _is_activated; - jack_position_t _position; - jack_transport_state_t _transport_state; - float _old_bpm; - jack_nframes_t _old_frame; - bool _old_rolling; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_JACKAUDIODRIVER_HPP diff --git a/src/server/LV2Block.cpp b/src/server/LV2Block.cpp deleted file mode 100644 index f4792f39..00000000 --- a/src/server/LV2Block.cpp +++ /dev/null @@ -1,742 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> -#include <cmath> -#include <cstdint> - -#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" - -#include "raul/Maid.hpp" -#include "raul/Array.hpp" - -#include "ingen/FilePath.hpp" -#include "ingen/Log.hpp" -#include "ingen/URI.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" - -#include "Buffer.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "InputPort.hpp" -#include "LV2Block.hpp" -#include "LV2Plugin.hpp" -#include "OutputPort.hpp" -#include "PortImpl.hpp" -#include "RunContext.hpp" -#include "Worker.hpp" - -namespace Ingen { -namespace Server { - -/** Partially construct a LV2Block. - * - * Object is not usable until instantiate() is called with success. - * (It _will_ crash!) - */ -LV2Block::LV2Block(LV2Plugin* plugin, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate) - : BlockImpl(plugin, symbol, polyphonic, parent, srate) - , _lv2_plugin(plugin) - , _worker_iface(nullptr) -{ - assert(_lv2_plugin); -} - -LV2Block::~LV2Block() -{ - // Explicitly drop instances first to prevent reference cycles - drop_instances(_instances); - drop_instances(_prepared_instances); -} - -SPtr<LV2Block::Instance> -LV2Block::make_instance(URIs& uris, - SampleRate rate, - uint32_t voice, - bool preparing) -{ - const Engine& engine = parent_graph()->engine(); - const LilvPlugin* lplug = _lv2_plugin->lilv_plugin(); - LilvInstance* inst = lilv_plugin_instantiate( - lplug, rate, _features->array()); - - if (!inst) { - engine.log().error(fmt("Failed to instantiate <%1%>\n") - % _lv2_plugin->uri().c_str()); - return SPtr<Instance>(); - } - - const LV2_Options_Interface* options_iface = nullptr; - if (lilv_plugin_has_extension_data(lplug, uris.opt_interface)) { - options_iface = (const LV2_Options_Interface*) - lilv_instance_get_extension_data(inst, LV2_OPTIONS__interface); - } - - for (uint32_t p = 0; p < num_ports(); ++p) { - PortImpl* const port = _ports->at(p); - Buffer* const buffer = (preparing) - ? port->prepared_buffer(voice).get() - : port->buffer(voice).get(); - if (port->is_morph() && port->is_a(PortType::CV)) { - if (options_iface) { - const LV2_URID port_type = uris.lv2_CVPort; - const LV2_Options_Option options[] = { - { LV2_OPTIONS_PORT, p, uris.morph_currentType, - sizeof(LV2_URID), uris.atom_URID, &port_type }, - { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, nullptr } - }; - options_iface->set(inst->lv2_handle, options); - } - } - - if (buffer) { - if (port->is_a(PortType::CONTROL)) { - buffer->set_value(port->value()); - } else if (port->is_a(PortType::CV)) { - buffer->set_block(port->value().get<float>(), 0, engine.block_length()); - } else { - buffer->clear(); - } - } - } - - if (options_iface) { - for (uint32_t p = 0; p < num_ports(); ++p) { - PortImpl* const port = _ports->at(p); - if (port->is_auto_morph()) { - LV2_Options_Option options[] = { - { LV2_OPTIONS_PORT, p, uris.morph_currentType, 0, 0, nullptr }, - { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, nullptr } - }; - - options_iface->get(inst->lv2_handle, options); - if (options[0].value) { - LV2_URID type = *(const LV2_URID*)options[0].value; - if (type == _uris.lv2_ControlPort) { - port->set_type(PortType::CONTROL, 0); - } else if (type == _uris.lv2_CVPort) { - port->set_type(PortType::CV, 0); - } else { - parent_graph()->engine().log().error( - fmt("%1% auto-morphed to unknown type %2%\n") - % port->path().c_str() % type); - return SPtr<Instance>(); - } - } else { - parent_graph()->engine().log().error( - fmt("Failed to get auto-morphed type of %1%\n") - % port->path().c_str()); - } - } - } - } - - return std::make_shared<Instance>(inst); -} - -bool -LV2Block::prepare_poly(BufferFactory& bufs, uint32_t poly) -{ - if (!_polyphonic) { - poly = 1; - } - - BlockImpl::prepare_poly(bufs, poly); - - if (_polyphony == poly) { - return true; - } - - const SampleRate rate = bufs.engine().sample_rate(); - assert(!_prepared_instances); - _prepared_instances = bufs.maid().make_managed<Instances>( - poly, *_instances, SPtr<Instance>()); - for (uint32_t i = _polyphony; i < _prepared_instances->size(); ++i) { - SPtr<Instance> inst = make_instance(bufs.uris(), rate, i, true); - if (!inst) { - _prepared_instances.reset(); - return false; - } - - _prepared_instances->at(i) = inst; - - if (_activated) { - lilv_instance_activate(inst->instance); - } - } - - return true; -} - -bool -LV2Block::apply_poly(RunContext& context, uint32_t poly) -{ - if (!_polyphonic) { - poly = 1; - } - - if (_prepared_instances) { - _instances = std::move(_prepared_instances); - } - assert(poly <= _instances->size()); - - return BlockImpl::apply_poly(context, poly); -} - -/** Instantiate self from LV2 plugin descriptor. - * - * Implemented as a seperate function (rather than in the constructor) to - * allow graceful error-catching of broken plugins. - * - * Returns whether or not plugin was successfully instantiated. If return - * value is false, this object may not be used. - */ -bool -LV2Block::instantiate(BufferFactory& bufs, const LilvState* state) -{ - const Ingen::URIs& uris = bufs.uris(); - Ingen::World* world = bufs.engine().world(); - const LilvPlugin* plug = _lv2_plugin->lilv_plugin(); - Ingen::Forge& forge = bufs.forge(); - const uint32_t num_ports = lilv_plugin_get_num_ports(plug); - - LilvNode* lv2_connectionOptional = lilv_new_uri( - bufs.engine().world()->lilv_world(), LV2_CORE__connectionOptional); - - _ports = bufs.maid().make_managed<BlockImpl::Ports>(num_ports, nullptr); - - bool ret = true; - - float* min_values = new float[num_ports]; - float* max_values = new float[num_ports]; - float* def_values = new float[num_ports]; - lilv_plugin_get_port_ranges_float(plug, min_values, max_values, def_values); - uint32_t max_sequence_size = 0; - - // Get all the necessary information about ports - for (uint32_t j = 0; j < num_ports; ++j) { - const LilvPort* id = lilv_plugin_get_port_by_index(plug, j); - - /* LV2 port symbols are guaranteed to be unique, valid C identifiers, - and Lilv guarantees that lilv_port_get_symbol() is valid. */ - const Raul::Symbol port_sym( - lilv_node_as_string(lilv_port_get_symbol(plug, id))); - - // Get port type - Atom val; - PortType port_type = PortType::UNKNOWN; - LV2_URID buffer_type = 0; - bool is_morph = false; - bool is_auto_morph = false; - if (lilv_port_is_a(plug, id, uris.lv2_ControlPort)) { - if (lilv_port_is_a(plug, id, uris.morph_MorphPort)) { - is_morph = true; - LilvNodes* types = lilv_port_get_value( - plug, id, uris.morph_supportsType); - LILV_FOREACH(nodes, i, types) { - const LilvNode* type = lilv_nodes_get(types, i); - if (lilv_node_equals(type, uris.lv2_CVPort)) { - port_type = PortType::CV; - buffer_type = uris.atom_Sound; - } - } - lilv_nodes_free(types); - } - if (port_type == PortType::UNKNOWN) { - port_type = PortType::CONTROL; - buffer_type = uris.atom_Sequence; - val = forge.make(def_values[j]); - } - } else if (lilv_port_is_a(plug, id, uris.lv2_CVPort)) { - port_type = PortType::CV; - buffer_type = uris.atom_Sound; - } else if (lilv_port_is_a(plug, id, uris.lv2_AudioPort)) { - port_type = PortType::AUDIO; - buffer_type = uris.atom_Sound; - } else if (lilv_port_is_a(plug, id, uris.atom_AtomPort)) { - port_type = PortType::ATOM; - } - - if (lilv_port_is_a(plug, id, uris.morph_AutoMorphPort)) { - is_auto_morph = true; - } - - // Get buffer type if necessary (atom ports) - if (!buffer_type) { - LilvNodes* types = lilv_port_get_value( - plug, id, uris.atom_bufferType); - LILV_FOREACH(nodes, i, types) { - const LilvNode* type = lilv_nodes_get(types, i); - if (lilv_node_is_uri(type)) { - buffer_type = bufs.engine().world()->uri_map().map_uri( - lilv_node_as_uri(type)); - } - } - lilv_nodes_free(types); - } - - const bool optional = lilv_port_has_property( - plug, id, lv2_connectionOptional); - - uint32_t port_buffer_size = bufs.default_size(buffer_type); - if (port_buffer_size == 0 && !optional) { - parent_graph()->engine().log().error( - fmt("<%1%> port `%2%' has unknown buffer type\n") - % _lv2_plugin->uri().c_str() % port_sym.c_str()); - ret = false; - break; - } - - if (port_type == PortType::ATOM) { - // Get default value, and its length - LilvNodes* defaults = lilv_port_get_value(plug, id, uris.lv2_default); - LILV_FOREACH(nodes, i, defaults) { - const LilvNode* d = lilv_nodes_get(defaults, i); - if (lilv_node_is_string(d)) { - const char* str_val = lilv_node_as_string(d); - const uint32_t str_val_len = strlen(str_val); - val = forge.alloc(str_val); - port_buffer_size = std::max(port_buffer_size, str_val_len); - } else if (lilv_node_is_uri(d)) { - const char* uri_val = lilv_node_as_uri(d); - val = forge.make_urid( - bufs.engine().world()->uri_map().map_uri(uri_val)); - } - } - lilv_nodes_free(defaults); - - if (!val.type() && buffer_type == _uris.atom_URID) { - val = forge.make_urid(0); - } - - // Get minimum size, if set in data - LilvNodes* sizes = lilv_port_get_value(plug, id, uris.rsz_minimumSize); - LILV_FOREACH(nodes, i, sizes) { - const LilvNode* d = lilv_nodes_get(sizes, i); - if (lilv_node_is_int(d)) { - uint32_t size_val = lilv_node_as_int(d); - port_buffer_size = std::max(port_buffer_size, size_val); - } - } - lilv_nodes_free(sizes); - max_sequence_size = std::max(port_buffer_size, max_sequence_size); - bufs.set_seq_size(max_sequence_size); - } - - enum { UNKNOWN, INPUT, OUTPUT } direction = UNKNOWN; - if (lilv_port_is_a(plug, id, uris.lv2_InputPort)) { - direction = INPUT; - } else if (lilv_port_is_a(plug, id, uris.lv2_OutputPort)) { - direction = OUTPUT; - } - - if ((port_type == PortType::UNKNOWN && !optional) || - direction == UNKNOWN) { - parent_graph()->engine().log().error( - fmt("<%1%> port `%2%' has unknown type or direction\n") - % _lv2_plugin->uri().c_str() % port_sym.c_str()); - ret = false; - break; - } - - if (!val.type() && (port_type != PortType::ATOM)) { - // Ensure numeric ports have a value, use 0 by default - val = forge.make(std::isnan(def_values[j]) ? 0.0f : def_values[j]); - } - - PortImpl* port = (direction == INPUT) - ? static_cast<PortImpl*>( - new InputPort(bufs, this, port_sym, j, _polyphony, - port_type, buffer_type, val)) - : static_cast<PortImpl*>( - new OutputPort(bufs, this, port_sym, j, _polyphony, - port_type, buffer_type, val)); - - port->set_morphable(is_morph, is_auto_morph); - if (direction == INPUT && (port_type == PortType::CONTROL - || port_type == PortType::CV)) { - port->set_value(val); - if (!std::isnan(min_values[j])) { - port->set_minimum(forge.make(min_values[j])); - } - if (!std::isnan(max_values[j])) { - port->set_maximum(forge.make(max_values[j])); - } - } - - // Inherit certain properties from plugin port - const LilvNode* preds[] = { uris.lv2_designation, - uris.lv2_portProperty, - uris.atom_supports, - nullptr }; - for (int p = 0; preds[p]; ++p) { - LilvNodes* values = lilv_port_get_value(plug, id, preds[p]); - LILV_FOREACH(nodes, v, values) { - const LilvNode* val = lilv_nodes_get(values, v); - if (lilv_node_is_uri(val)) { - port->add_property(URI(lilv_node_as_uri(preds[p])), - forge.make_urid(URI(lilv_node_as_uri(val)))); - } - } - lilv_nodes_free(values); - } - - port->cache_properties(); - - _ports->at(j) = port; - } - - delete[] min_values; - delete[] max_values; - delete[] def_values; - - lilv_node_free(lv2_connectionOptional); - - if (!ret) { - _ports.reset(); - return ret; - } - - _features = world->lv2_features().lv2_features(world, this); - - // Actually create plugin instances and port buffers. - const SampleRate rate = bufs.engine().sample_rate(); - _instances = bufs.maid().make_managed<Instances>( - _polyphony, SPtr<Instance>()); - for (uint32_t i = 0; i < _polyphony; ++i) { - _instances->at(i) = make_instance(bufs.uris(), rate, i, false); - if (!_instances->at(i)) { - return false; - } - } - - // Load initial state if no state is explicitly given - LilvState* default_state = nullptr; - if (!state) { - state = default_state = load_preset(_lv2_plugin->uri()); - } - - // Apply state - if (state) { - apply_state(nullptr, state); - } - - if (default_state) { - lilv_state_free(default_state); - } - - // FIXME: Polyphony + worker? - if (lilv_plugin_has_feature(plug, uris.work_schedule)) { - _worker_iface = (const LV2_Worker_Interface*) - lilv_instance_get_extension_data(instance(0), - LV2_WORKER__interface); - } - - return ret; -} - -bool -LV2Block::save_state(const FilePath& dir) const -{ - World* world = _lv2_plugin->world(); - LilvWorld* lworld = world->lilv_world(); - - LilvState* state = lilv_state_new_from_instance( - _lv2_plugin->lilv_plugin(), const_cast<LV2Block*>(this)->instance(0), - &world->uri_map().urid_map_feature()->urid_map, - nullptr, dir.c_str(), dir.c_str(), dir.c_str(), nullptr, nullptr, - LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE, nullptr); - - if (!state) { - return false; - } else if (lilv_state_get_num_properties(state) == 0) { - lilv_state_free(state); - return false; - } - - lilv_state_save(lworld, - &world->uri_map().urid_map_feature()->urid_map, - &world->uri_map().urid_unmap_feature()->urid_unmap, - state, - nullptr, - dir.c_str(), - "state.ttl"); - - lilv_state_free(state); - - return true; -} - -BlockImpl* -LV2Block::duplicate(Engine& engine, - const Raul::Symbol& symbol, - GraphImpl* parent) -{ - const SampleRate rate = engine.sample_rate(); - - // Get current state - LilvState* state = lilv_state_new_from_instance( - _lv2_plugin->lilv_plugin(), instance(0), - &engine.world()->uri_map().urid_map_feature()->urid_map, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, LV2_STATE_IS_NATIVE, nullptr); - - // Duplicate and instantiate block - LV2Block* dup = new LV2Block(_lv2_plugin, symbol, _polyphonic, parent, rate); - if (!dup->instantiate(*engine.buffer_factory(), state)) { - delete dup; - return nullptr; - } - dup->set_properties(properties()); - - // Set duplicate port values and properties to the same as ours - for (uint32_t p = 0; p < num_ports(); ++p) { - const Atom& val = port_impl(p)->value(); - if (val.is_valid()) { - dup->port_impl(p)->set_value(val); - } - dup->port_impl(p)->set_properties(port_impl(p)->properties()); - } - - return dup; -} - -void -LV2Block::activate(BufferFactory& bufs) -{ - BlockImpl::activate(bufs); - - for (uint32_t i = 0; i < _polyphony; ++i) { - lilv_instance_activate(instance(i)); - } -} - -void -LV2Block::deactivate() -{ - BlockImpl::deactivate(); - - for (uint32_t i = 0; i < _polyphony; ++i) { - lilv_instance_deactivate(instance(i)); - } -} - -LV2_Worker_Status -LV2Block::work_respond(LV2_Worker_Respond_Handle handle, - uint32_t size, - const void* data) -{ - LV2Block* block = (LV2Block*)handle; - LV2Block::Response* r = new LV2Block::Response(size, data); - block->_responses.push_back(*r); - return LV2_WORKER_SUCCESS; -} - -LV2_Worker_Status -LV2Block::work(uint32_t size, const void* data) -{ - if (_worker_iface) { - std::lock_guard<std::mutex> lock(_work_mutex); - - LV2_Handle inst = lilv_instance_get_handle(instance(0)); - LV2_Worker_Status st = _worker_iface->work(inst, work_respond, this, size, data); - if (st) { - parent_graph()->engine().log().error( - fmt("Error calling %1% work method\n") % _path); - } - return st; - } - return LV2_WORKER_ERR_UNKNOWN; -} - -void -LV2Block::run(RunContext& context) -{ - for (uint32_t i = 0; i < _polyphony; ++i) { - lilv_instance_run(instance(i), context.nframes()); - } -} - -void -LV2Block::post_process(RunContext& context) -{ - /* Handle any worker responses. Note that this may write to output ports, - so must be done first to prevent clobbering worker responses and - monitored notification ports. */ - if (_worker_iface) { - LV2_Handle inst = lilv_instance_get_handle(instance(0)); - while (!_responses.empty()) { - Response& r = _responses.front(); - _worker_iface->work_response(inst, r.size, r.data); - _responses.pop_front(); - context.engine().maid()->dispose(&r); - } - - if (_worker_iface->end_run) { - _worker_iface->end_run(inst); - } - } - - /* Run cycle truly finished, finalise output ports. */ - BlockImpl::post_process(context); -} - -LilvState* -LV2Block::load_preset(const URI& uri) -{ - World* world = _lv2_plugin->world(); - LilvWorld* lworld = world->lilv_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; -} - -LilvState* -LV2Block::load_state(World* world, const FilePath& path) -{ - LilvWorld* lworld = world->lilv_world(); - const URI uri = URI(path); - LilvNode* subject = lilv_new_uri(lworld, uri.c_str()); - - LilvState* state = lilv_state_new_from_file( - lworld, - &world->uri_map().urid_map_feature()->urid_map, - subject, - path.c_str()); - - lilv_node_free(subject); - return state; -} - -void -LV2Block::apply_state(const UPtr<Worker>& worker, const LilvState* state) -{ - World* world = parent_graph()->engine().world(); - SPtr<LV2_Feature> sched; - if (worker) { - sched = worker->schedule_feature()->feature(world, this); - } - - const LV2_Feature* state_features[2] = { nullptr, nullptr }; - if (sched) { - state_features[0] = sched.get(); - } - - for (uint32_t v = 0; v < _polyphony; ++v) { - lilv_state_restore(state, instance(v), nullptr, nullptr, 0, state_features); - } -} - -static const void* -get_port_value(const char* port_symbol, - void* user_data, - uint32_t* size, - uint32_t* type) -{ - LV2Block* const block = (LV2Block*)user_data; - PortImpl* const port = block->port_by_symbol(port_symbol); - - if (port && port->is_input() && port->value().is_valid()) { - *size = port->value().size(); - *type = port->value().type(); - return port->value().get_body(); - } - - return nullptr; -} - -boost::optional<Resource> -LV2Block::save_preset(const URI& uri, - const Properties& props) -{ - World* world = parent_graph()->engine().world(); - LilvWorld* lworld = _lv2_plugin->world()->lilv_world(); - LV2_URID_Map* lmap = &world->uri_map().urid_map_feature()->urid_map; - LV2_URID_Unmap* lunmap = &world->uri_map().urid_unmap_feature()->urid_unmap; - - const FilePath path = FilePath(uri.path()); - const FilePath dirname = path.parent_path(); - const FilePath basename = path.stem(); - - LilvState* state = lilv_state_new_from_instance( - _lv2_plugin->lilv_plugin(), instance(0), lmap, - nullptr, nullptr, nullptr, path.c_str(), - get_port_value, this, LV2_STATE_IS_NATIVE, nullptr); - - if (state) { - const Properties::const_iterator l = props.find(_uris.rdfs_label); - if (l != props.end() && l->second.type() == _uris.atom_String) { - lilv_state_set_label(state, l->second.ptr<char>()); - } - - lilv_state_save(lworld, lmap, lunmap, state, nullptr, - dirname.c_str(), basename.c_str()); - - const URI uri(lilv_node_as_uri(lilv_state_get_uri(state))); - const std::string label(lilv_state_get_label(state) - ? lilv_state_get_label(state) - : basename); - lilv_state_free(state); - - Resource preset(_uris, uri); - preset.set_property(_uris.rdf_type, _uris.pset_Preset); - preset.set_property(_uris.rdfs_label, world->forge().alloc(label)); - preset.set_property(_uris.lv2_appliesTo, - world->forge().make_urid(_lv2_plugin->uri())); - - const std::string bundle_uri = URI(dirname).string() + '/'; - LilvNode* lbundle = lilv_new_uri(lworld, bundle_uri.c_str()); - lilv_world_load_bundle(lworld, lbundle); - lilv_node_free(lbundle); - - return preset; - } - - return boost::optional<Resource>(); -} - -void -LV2Block::set_port_buffer(uint32_t voice, - uint32_t port_num, - BufferRef buf, - SampleCount offset) -{ - BlockImpl::set_port_buffer(voice, port_num, buf, offset); - lilv_instance_connect_port( - instance(voice), - port_num, - buf ? buf->port_data(_ports->at(port_num)->type(), offset) : nullptr); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/LV2Block.hpp b/src/server/LV2Block.hpp deleted file mode 100644 index f3a59550..00000000 --- a/src/server/LV2Block.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_LV2BLOCK_HPP -#define INGEN_ENGINE_LV2BLOCK_HPP - -#include <mutex> - -#include "lilv/lilv.h" -#include "lv2/lv2plug.in/ns/ext/worker/worker.h" -#include "raul/Maid.hpp" - -#include "BufferRef.hpp" -#include "BlockImpl.hpp" -#include "ingen/LV2Features.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Server { - -class LV2Plugin; - -/** An instance of a LV2 plugin. - * - * \ingroup engine - */ -class LV2Block : public BlockImpl -{ -public: - LV2Block(LV2Plugin* plugin, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate); - - ~LV2Block(); - - bool instantiate(BufferFactory& bufs, const LilvState* state); - - LilvInstance* instance() { return instance(0); } - bool save_state(const FilePath& dir) const; - - BlockImpl* duplicate(Engine& engine, - const Raul::Symbol& symbol, - GraphImpl* parent); - - bool prepare_poly(BufferFactory& bufs, uint32_t poly); - bool apply_poly(RunContext& context, uint32_t poly); - - void activate(BufferFactory& bufs); - void deactivate(); - - LV2_Worker_Status work(uint32_t size, const void* data); - - void run(RunContext& context); - void post_process(RunContext& context); - - LilvState* load_preset(const URI& uri); - - void apply_state(const UPtr<Worker>& worker, const LilvState* state); - - boost::optional<Resource> save_preset(const URI& uri, - const Properties& props); - - void set_port_buffer(uint32_t voice, - uint32_t port_num, - BufferRef buf, - SampleCount offset); - - static LilvState* load_state(World* world, const FilePath& path); - -protected: - struct Instance : public Raul::Noncopyable { - explicit Instance(LilvInstance* i) : instance(i) {} - - ~Instance() { lilv_instance_free(instance); } - - LilvInstance* const instance; - }; - - SPtr<Instance> make_instance(URIs& uris, - SampleRate rate, - uint32_t voice, - bool preparing); - - inline LilvInstance* instance(uint32_t voice) { - return (LilvInstance*)(*_instances)[voice]->instance; - } - - typedef Raul::Array< SPtr<Instance> > Instances; - - void drop_instances(const MPtr<Instances>& instances) { - if (instances) { - for (size_t i = 0; i < instances->size(); ++i) { - (*instances)[i].reset(); - } - } - } - - struct Response : public Raul::Maid::Disposable - , public Raul::Noncopyable - , public boost::intrusive::slist_base_hook<> - { - inline Response(uint32_t s, const void* d) - : size(s) - , data(malloc(s)) - { - memcpy(data, d, s); - } - - ~Response() { - free(data); - } - - const uint32_t size; - void* const data; - }; - - typedef boost::intrusive::slist<Response, - boost::intrusive::cache_last<true>, - boost::intrusive::constant_time_size<false> - > Responses; - - static LV2_Worker_Status work_respond( - LV2_Worker_Respond_Handle handle, uint32_t size, const void* data); - - LV2Plugin* _lv2_plugin; - MPtr<Instances> _instances; - MPtr<Instances> _prepared_instances; - const LV2_Worker_Interface* _worker_iface; - std::mutex _work_mutex; - Responses _responses; - SPtr<LV2Features::FeatureArray> _features; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2BLOCK_HPP diff --git a/src/server/LV2Options.hpp b/src/server/LV2Options.hpp deleted file mode 100644 index ef7c5ec9..00000000 --- a/src/server/LV2Options.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_LV2OPTIONS_HPP -#define INGEN_ENGINE_LV2OPTIONS_HPP - -#include "ingen/LV2Features.hpp" -#include "ingen/URIs.hpp" -#include "lv2/lv2plug.in/ns/ext/options/options.h" - -namespace Ingen { -namespace Server { - -class LV2Options : public Ingen::LV2Features::Feature { -public: - explicit LV2Options(const URIs& uris) - : _uris(uris) - {} - - void set(int32_t sample_rate, int32_t block_length, int32_t seq_size) { - _sample_rate = sample_rate; - _block_length = block_length; - _seq_size = seq_size; - } - - const char* uri() const { return LV2_OPTIONS__options; } - - SPtr<LV2_Feature> feature(World* w, Node* n) { - const LV2_Options_Option options[] = { - { LV2_OPTIONS_INSTANCE, 0, _uris.bufsz_minBlockLength, - sizeof(int32_t), _uris.atom_Int, &_block_length }, - { LV2_OPTIONS_INSTANCE, 0, _uris.bufsz_maxBlockLength, - sizeof(int32_t), _uris.atom_Int, &_block_length }, - { LV2_OPTIONS_INSTANCE, 0, _uris.bufsz_sequenceSize, - sizeof(int32_t), _uris.atom_Int, &_seq_size }, - { LV2_OPTIONS_INSTANCE, 0, _uris.param_sampleRate, - sizeof(int32_t), _uris.atom_Int, &_sample_rate }, - { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, nullptr } - }; - - LV2_Feature* f = (LV2_Feature*)malloc(sizeof(LV2_Feature)); - f->URI = LV2_OPTIONS__options; - f->data = malloc(sizeof(options)); - memcpy(f->data, options, sizeof(options)); - return SPtr<LV2_Feature>(f, &free_feature); - } - -private: - const URIs& _uris; - int32_t _sample_rate; - int32_t _block_length; - int32_t _seq_size; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2OPTIONS_HPP diff --git a/src/server/LV2Plugin.cpp b/src/server/LV2Plugin.cpp deleted file mode 100644 index f56fd4d7..00000000 --- a/src/server/LV2Plugin.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <string> - -#include "ingen/Forge.hpp" -#include "ingen/Log.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" -#include "lv2/lv2plug.in/ns/ext/presets/presets.h" - -#include "Engine.hpp" -#include "LV2Block.hpp" -#include "LV2Plugin.hpp" - -namespace Ingen { -namespace Server { - -LV2Plugin::LV2Plugin(World* world, const LilvPlugin* lplugin) - : PluginImpl(world->uris(), - world->uris().lv2_Plugin.urid, - URI(lilv_node_as_uri(lilv_plugin_get_uri(lplugin)))) - , _world(world) - , _lilv_plugin(lplugin) -{ - set_property(_uris.rdf_type, _uris.lv2_Plugin); - - update_properties(); -} - -void -LV2Plugin::update_properties() -{ - LilvNode* minor = lilv_world_get(_world->lilv_world(), - lilv_plugin_get_uri(_lilv_plugin), - _uris.lv2_minorVersion, - nullptr); - LilvNode* micro = lilv_world_get(_world->lilv_world(), - lilv_plugin_get_uri(_lilv_plugin), - _uris.lv2_microVersion, - nullptr); - - if (lilv_node_is_int(minor) && lilv_node_is_int(micro)) { - set_property(_uris.lv2_minorVersion, - _world->forge().make(lilv_node_as_int(minor))); - set_property(_uris.lv2_microVersion, - _world->forge().make(lilv_node_as_int(micro))); - } - - lilv_node_free(minor); - lilv_node_free(micro); -} - -const Raul::Symbol -LV2Plugin::symbol() const -{ - std::string working = uri(); - if (working.back() == '/') { - working = working.substr(0, working.length() - 1); - } - - while (working.length() > 0) { - size_t last_slash = working.find_last_of("/"); - const std::string symbol = working.substr(last_slash+1); - if ( (symbol[0] >= 'a' && symbol[0] <= 'z') - || (symbol[0] >= 'A' && symbol[0] <= 'Z') ) { - return Raul::Symbol::symbolify(symbol); - } else { - working = working.substr(0, last_slash); - } - } - - return Raul::Symbol("lv2_symbol"); -} - -BlockImpl* -LV2Plugin::instantiate(BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - Engine& engine, - const LilvState* state) -{ - LV2Block* b = new LV2Block( - this, symbol, polyphonic, parent, engine.sample_rate()); - - if (!b->instantiate(bufs, state)) { - delete b; - return nullptr; - } else { - return b; - } -} - -void -LV2Plugin::load_presets() -{ - const URIs& uris = _world->uris(); - LilvWorld* lworld = _world->lilv_world(); - LilvNodes* presets = lilv_plugin_get_related(_lilv_plugin, uris.pset_Preset); - - if (presets) { - LILV_FOREACH(nodes, i, presets) { - const LilvNode* preset = lilv_nodes_get(presets, i); - lilv_world_load_resource(lworld, preset); - - LilvNodes* labels = lilv_world_find_nodes( - lworld, preset, uris.rdfs_label, nullptr); - if (labels) { - const LilvNode* label = lilv_nodes_get_first(labels); - - _presets.emplace(URI(lilv_node_as_uri(preset)), - lilv_node_as_string(label)); - - lilv_nodes_free(labels); - } else { - _world->log().error( - fmt("Preset <%1%> has no rdfs:label\n") - % lilv_node_as_string(lilv_nodes_get(presets, i))); - } - } - - lilv_nodes_free(presets); - } - - PluginImpl::load_presets(); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/LV2Plugin.hpp b/src/server/LV2Plugin.hpp deleted file mode 100644 index 43d0fba9..00000000 --- a/src/server/LV2Plugin.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_LV2PLUGIN_HPP -#define INGEN_ENGINE_LV2PLUGIN_HPP - -#include <cstdlib> - -#include "ingen/types.hpp" -#include "lilv/lilv.h" - -#include "PluginImpl.hpp" - -namespace Ingen { - -class World; - -namespace Server { - -class GraphImpl; -class BlockImpl; - -/** Implementation of an LV2 plugin (loaded shared library). - */ -class LV2Plugin : public PluginImpl -{ -public: - LV2Plugin(World* world, const LilvPlugin* lplugin); - - BlockImpl* instantiate(BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - Engine& engine, - const LilvState* state); - - const Raul::Symbol symbol() const; - - World* world() const { return _world; } - const LilvPlugin* lilv_plugin() const { return _lilv_plugin; } - - void update_properties(); - - void load_presets(); - - URI bundle_uri() const { - const LilvNode* bundle = lilv_plugin_get_bundle_uri(_lilv_plugin); - return URI(lilv_node_as_uri(bundle)); - } - -private: - World* _world; - const LilvPlugin* _lilv_plugin; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2PLUGIN_HPP diff --git a/src/server/LV2ResizeFeature.hpp b/src/server/LV2ResizeFeature.hpp deleted file mode 100644 index f61165ee..00000000 --- a/src/server/LV2ResizeFeature.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_LV2RESIZEFEATURE_HPP -#define INGEN_ENGINE_LV2RESIZEFEATURE_HPP - -#include "ingen/LV2Features.hpp" -#include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h" - -#include "BlockImpl.hpp" -#include "Buffer.hpp" -#include "PortImpl.hpp" - -namespace Ingen { -namespace Server { - -struct ResizeFeature : public Ingen::LV2Features::Feature { - static LV2_Resize_Port_Status resize_port( - LV2_Resize_Port_Feature_Data data, - uint32_t index, - size_t size) { - BlockImpl* block = (BlockImpl*)data; - PortImpl* port = block->port_impl(index); - if (block->context() == Context::ID::MESSAGE) { - port->buffer(0)->resize(size); - port->connect_buffers(); - return LV2_RESIZE_PORT_SUCCESS; - } - return LV2_RESIZE_PORT_ERR_UNKNOWN; - } - - const char* uri() const { return LV2_RESIZE_PORT_URI; } - - SPtr<LV2_Feature> feature(World* w, Node* n) { - BlockImpl* block = dynamic_cast<BlockImpl*>(n); - if (!block) - return SPtr<LV2_Feature>(); - LV2_Resize_Port_Resize* data - = (LV2_Resize_Port_Resize*)malloc(sizeof(LV2_Resize_Port_Resize)); - data->data = block; - data->resize = &resize_port; - LV2_Feature* f = (LV2_Feature*)malloc(sizeof(LV2_Feature)); - f->URI = LV2_RESIZE_PORT_URI; - f->data = data; - return SPtr<LV2_Feature>(f, &free_feature); - } -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2RESIZEFEATURE_HPP diff --git a/src/server/Load.hpp b/src/server/Load.hpp deleted file mode 100644 index ed9ee406..00000000 --- a/src/server/Load.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_LOAD_HPP -#define INGEN_ENGINE_LOAD_HPP - -namespace Ingen { -namespace Server { - -struct Load -{ - void update(uint64_t time, uint64_t available) { - const uint64_t load = time * 100 / available; - if (load < min) { - min = load; - changed = true; - } - if (load > max) { - max = load; - changed = true; - } - if (++n == 1) { - mean = load; - changed = true; - } else { - const float a = mean + ((float)load - mean) / (float)++n; - if (a != mean) { - changed = floorf(a) != floorf(mean); - mean = a; - } - } - } - - uint64_t min = std::numeric_limits<uint64_t>::max(); - uint64_t max = 0; - float mean = 0.0f; - uint64_t n = 0; - bool changed = false; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_LOAD_HPP diff --git a/src/server/NodeImpl.cpp b/src/server/NodeImpl.cpp deleted file mode 100644 index 778ba15a..00000000 --- a/src/server/NodeImpl.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "GraphImpl.hpp" -#include "NodeImpl.hpp" -#include "ThreadManager.hpp" - -namespace Ingen { -namespace Server { - -NodeImpl::NodeImpl(const Ingen::URIs& uris, - NodeImpl* parent, - const Raul::Symbol& symbol) - : Node(uris, parent ? parent->path().child(symbol) : Raul::Path("/")) - , _parent(parent) - , _path(parent ? parent->path().child(symbol) : Raul::Path("/")) - , _symbol(symbol) -{ -} - -const Atom& -NodeImpl::get_property(const URI& key) const -{ - ThreadManager::assert_not_thread(THREAD_PROCESS); - static const Atom null_atom; - auto i = properties().find(key); - return (i != properties().end()) ? i->second : null_atom; -} - -GraphImpl* -NodeImpl::parent_graph() const -{ - return dynamic_cast<GraphImpl*>((BlockImpl*)_parent); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/NodeImpl.hpp b/src/server/NodeImpl.hpp deleted file mode 100644 index 614801eb..00000000 --- a/src/server/NodeImpl.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_NODEIMPL_HPP -#define INGEN_ENGINE_NODEIMPL_HPP - -#include <cassert> -#include <cstddef> -#include <map> - -#include "ingen/Node.hpp" -#include "ingen/Resource.hpp" -#include "raul/Deletable.hpp" -#include "raul/Path.hpp" - -namespace Raul { class Maid; } - -namespace Ingen { - -namespace Shared { class URIs; } - -namespace Server { - -class BufferFactory; -class GraphImpl; -class RunContext; - -/** An object on the audio graph (a Graph, Block, or Port). - * - * Each of these is a Raul::Deletable and so can be deleted in a realtime safe - * way from anywhere, and they all have a map of variable for clients to store - * arbitrary values in (which the engine puts no significance to whatsoever). - * - * \ingroup engine - */ -class NodeImpl : public Node -{ -public: - const Raul::Symbol& symbol() const { return _symbol; } - - Node* graph_parent() const { return _parent; } - NodeImpl* parent() const { return _parent; } - - /** Rename */ - virtual void set_path(const Raul::Path& new_path) { - _path = new_path; - const char* const new_sym = new_path.symbol(); - if (new_sym[0] != '\0') { - _symbol = Raul::Symbol(new_sym); - } - set_uri(path_to_uri(new_path)); - } - - const Atom& get_property(const URI& key) const; - - /** The Graph this object is a child of. */ - virtual GraphImpl* parent_graph() const; - - const Raul::Path& path() const { return _path; } - - /** Prepare for a new (external) polyphony value. - * - * Preprocessor thread, poly is actually applied by apply_poly. - * \return true on success. - */ - virtual bool prepare_poly(BufferFactory& bufs, uint32_t poly) = 0; - - /** Apply a new (external) polyphony value. - * - * \param context Process context (process thread only). - * \param poly Must be <= the most recent value passed to prepare_poly. - */ - virtual bool apply_poly(RunContext& context, uint32_t poly) = 0; - - /** Return true iff this is main (the top level Node). - * - * This is sometimes called "the root graph", but the term "main" is used - * to avoid ambiguity with the root path, since main does not have the path - * "/", but usually "/main" to leave namespace for non-node things. - */ - bool is_main() const { return !_parent; } - -protected: - NodeImpl(const Ingen::URIs& uris, - NodeImpl* parent, - const Raul::Symbol& symbol); - - NodeImpl* _parent; - Raul::Path _path; - Raul::Symbol _symbol; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_NODEIMPL_HPP diff --git a/src/server/OutputPort.hpp b/src/server/OutputPort.hpp deleted file mode 100644 index 1058defb..00000000 --- a/src/server/OutputPort.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_OUTPUTPORT_HPP -#define INGEN_ENGINE_OUTPUTPORT_HPP - -#include "PortImpl.hpp" - -namespace Ingen { -namespace Server { - -/** An output port. - * - * Output ports always have a locally allocated buffer, and buffer() will - * always return that buffer. - * - * \ingroup engine - */ -class OutputPort : public PortImpl -{ -public: - OutputPort(BufferFactory& bufs, - BlockImpl* parent, - const Raul::Symbol& symbol, - uint32_t index, - uint32_t poly, - PortType type, - LV2_URID buffer_type, - const Atom& value, - size_t buffer_size = 0) - : PortImpl(bufs, parent, symbol, index,poly, type, buffer_type, value, buffer_size, true) - {} -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_OUTPUTPORT_HPP diff --git a/src/server/PluginImpl.hpp b/src/server/PluginImpl.hpp deleted file mode 100644 index ebd4b3e5..00000000 --- a/src/server/PluginImpl.hpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_PLUGINIMPL_HPP -#define INGEN_ENGINE_PLUGINIMPL_HPP - -#include <cstdlib> - -#include "ingen/Resource.hpp" -#include "raul/Symbol.hpp" - -namespace Ingen { - -class URIs; - -namespace Server { - -class BlockImpl; -class BufferFactory; -class Engine; -class GraphImpl; - -/** Implementation of a plugin (internal code, or a loaded shared library). - * - * Conceptually, a Block is an instance of this. - */ -class PluginImpl : public Resource -{ -public: - PluginImpl(Ingen::URIs& uris, const Atom& type, const URI& uri) - : Resource(uris, uri) - , _type(type) - , _presets_loaded(false) - , _is_zombie(false) - { - } - - virtual BlockImpl* instantiate(BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - Engine& engine, - const LilvState* state) = 0; - - virtual const Raul::Symbol symbol() const = 0; - - const Atom& type() const { return _type; } - void set_type(const Atom& t) { _type = t; } - bool is_zombie() const { return _is_zombie; } - void set_is_zombie(bool t) { _is_zombie = t; } - - typedef std::pair<URI, std::string> Preset; - typedef std::map<URI, std::string> Presets; - - const Presets& presets(bool force_reload=false) { - if (!_presets_loaded || force_reload) { - load_presets(); - } - - return _presets; - } - - virtual void update_properties() {} - - virtual void load_presets() { _presets_loaded = true; } - - virtual URI bundle_uri() const { return URI("ingen:/"); } - -protected: - Atom _type; - Presets _presets; - bool _presets_loaded; - bool _is_zombie; - -private: - PluginImpl(const PluginImpl&) = delete; - PluginImpl& operator=(const PluginImpl&) = delete; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_PLUGINIMPL_HPP diff --git a/src/server/PortAudioDriver.cpp b/src/server/PortAudioDriver.cpp deleted file mode 100644 index f892c99f..00000000 --- a/src/server/PortAudioDriver.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen_config.h" - -#include <cstdlib> -#include <string> - -#include <portaudio.h> - -#include "ingen/Configuration.hpp" -#include "ingen/LV2Features.hpp" -#include "ingen/Log.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/World.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" - -#include "Buffer.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "PortAudioDriver.hpp" -#include "PortImpl.hpp" -#include "FrameTimer.hpp" -#include "ThreadManager.hpp" -#include "util.hpp" - -namespace Ingen { -namespace Server { - -static bool -pa_error(const char* msg, PaError err) -{ - fprintf(stderr, "error: %s (%s)\n", msg, Pa_GetErrorText(err)); - Pa_Terminate(); - return false; -} - -PortAudioDriver::PortAudioDriver(Engine& engine) - : _engine(engine) - , _sem(0) - , _stream(nullptr) - , _seq_size(4096) - , _block_length(engine.world()->conf().option("buffer-size").get<int32_t>()) - , _sample_rate(48000) - , _n_inputs(0) - , _n_outputs(0) - , _flag(false) - , _is_activated(false) -{ -} - -PortAudioDriver::~PortAudioDriver() -{ - deactivate(); - _ports.clear_and_dispose([](EnginePort* p) { delete p; }); -} - -bool -PortAudioDriver::attach() -{ - PaError st = paNoError; - if ((st = Pa_Initialize())) { - return pa_error("Failed to initialize audio system", st); - } - - // Get default input and output devices - _inputParameters.device = Pa_GetDefaultInputDevice(); - _outputParameters.device = Pa_GetDefaultOutputDevice(); - if (_inputParameters.device == paNoDevice) { - return pa_error("No default input device", paDeviceUnavailable); - } else if (_outputParameters.device == paNoDevice) { - return pa_error("No default output device", paDeviceUnavailable); - } - - const PaDeviceInfo* in_dev = Pa_GetDeviceInfo(_inputParameters.device); - - /* TODO: It looks like it is somehow actually impossible to request the - best/native buffer size then retrieve what it actually is with - PortAudio. How such a glaring useless flaw exists in such a widespread - library is beyond me... */ - - _sample_rate = in_dev->defaultSampleRate; - - _timer = std::unique_ptr<FrameTimer>( - new FrameTimer(_block_length, _sample_rate)); - - return true; -} - -bool -PortAudioDriver::activate() -{ - const PaDeviceInfo* in_dev = Pa_GetDeviceInfo(_inputParameters.device); - const PaDeviceInfo* out_dev = Pa_GetDeviceInfo(_outputParameters.device); - - // Count number of input and output audio ports/channels - _inputParameters.channelCount = 0; - _outputParameters.channelCount = 0; - for (const auto& port : _ports) { - if (port.graph_port()->is_a(PortType::AUDIO)) { - if (port.graph_port()->is_input()) { - ++_inputParameters.channelCount; - } else if (port.graph_port()->is_output()) { - ++_outputParameters.channelCount; - } - } - } - - // Configure audio format - _inputParameters.sampleFormat = paFloat32|paNonInterleaved; - _inputParameters.suggestedLatency = in_dev->defaultLowInputLatency; - _inputParameters.hostApiSpecificStreamInfo = nullptr; - _outputParameters.sampleFormat = paFloat32|paNonInterleaved; - _outputParameters.suggestedLatency = out_dev->defaultLowOutputLatency; - _outputParameters.hostApiSpecificStreamInfo = nullptr; - - // Open stream - PaError st = paNoError; - if ((st = Pa_OpenStream( - &_stream, - _inputParameters.channelCount ? &_inputParameters : nullptr, - _outputParameters.channelCount ? &_outputParameters : nullptr, - in_dev->defaultSampleRate, - _block_length, // paFramesPerBufferUnspecified, // FIXME: ? - 0, - pa_process_cb, - this))) { - return pa_error("Failed to open audio stream", st); - } - - _is_activated = true; - if ((st = Pa_StartStream(_stream))) { - return pa_error("Error starting audio stream", st); - } - - return true; -} - -void -PortAudioDriver::deactivate() -{ - Pa_Terminate(); -} - -SampleCount -PortAudioDriver::frame_time() const -{ - return _timer->frame_time(_engine.current_time()) + _engine.block_length(); -} - -EnginePort* -PortAudioDriver::get_port(const Raul::Path& path) -{ - for (auto& p : _ports) { - if (p.graph_port()->path() == path) { - return &p; - } - } - - return nullptr; -} - -void -PortAudioDriver::add_port(RunContext& context, EnginePort* port) -{ - _ports.push_back(*port); -} - -void -PortAudioDriver::remove_port(RunContext& context, EnginePort* port) -{ - _ports.erase(_ports.iterator_to(*port)); -} - -void -PortAudioDriver::register_port(EnginePort& port) -{ -} - -void -PortAudioDriver::unregister_port(EnginePort& port) -{ -} - -void -PortAudioDriver::rename_port(const Raul::Path& old_path, - const Raul::Path& new_path) -{ -} - -void -PortAudioDriver::port_property(const Raul::Path& path, - const URI& uri, - const Atom& value) -{ -} - -EnginePort* -PortAudioDriver::create_port(DuplexPort* graph_port) -{ - EnginePort* eport = nullptr; - if (graph_port->is_a(PortType::AUDIO) || graph_port->is_a(PortType::CV)) { - // Audio buffer port, use Jack buffer directly - eport = new EnginePort(graph_port); - graph_port->set_is_driver_port(*_engine.buffer_factory()); - } else if (graph_port->is_a(PortType::ATOM) && - graph_port->buffer_type() == _engine.world()->uris().atom_Sequence) { - // Sequence port, make Jack port but use internal LV2 format buffer - eport = new EnginePort(graph_port); - } - - if (graph_port->is_a(PortType::AUDIO)) { - if (graph_port->is_input()) { - eport->set_driver_index(_n_inputs++); - } else { - eport->set_driver_index(_n_outputs++); - } - } - - if (eport) { - register_port(*eport); - } - - return eport; -} - -void -PortAudioDriver::pre_process_port(RunContext& context, - EnginePort* port, - const void* inputs, - void* outputs) -{ - if (!port->graph_port()->is_a(PortType::AUDIO)) { - return; - } - - if (port->is_input()) { - port->set_buffer(((float**)inputs)[port->driver_index()]); - } else { - port->set_buffer(((float**)outputs)[port->driver_index()]); - memset(port->buffer(), 0, _block_length * sizeof(float)); - } - - port->graph_port()->set_driver_buffer( - port->buffer(), _block_length * sizeof(float)); -} - -void -PortAudioDriver::post_process_port(RunContext& context, - EnginePort* port, - const void* inputs, - void* outputs) -{ -} - -int -PortAudioDriver::process_cb(const void* inputs, - void* outputs, - unsigned long nframes, - const PaStreamCallbackTimeInfo* time, - PaStreamCallbackFlags flags) -{ - _engine.advance(nframes); - _timer->update(_engine.current_time(), _engine.run_context().start()); - - // Read input - for (auto& p : _ports) { - pre_process_port(_engine.run_context(), &p, inputs, outputs); - } - - // Process - _engine.run(nframes); - - // Write output - for (auto& p : _ports) { - post_process_port(_engine.run_context(), &p, inputs, outputs); - } - - return 0; -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/PortAudioDriver.hpp b/src/server/PortAudioDriver.hpp deleted file mode 100644 index b1545f64..00000000 --- a/src/server/PortAudioDriver.hpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_PORTAUDIODRIVER_HPP -#define INGEN_ENGINE_PORTAUDIODRIVER_HPP - -#include "ingen_config.h" - -#include <atomic> -#include <memory> -#include <string> - -#include <portaudio.h> - -#include "raul/Semaphore.hpp" - -#include "lv2/lv2plug.in/ns/ext/atom/forge.h" - -#include "Driver.hpp" -#include "EnginePort.hpp" - -namespace Raul { class Path; } - -namespace Ingen { -namespace Server { - -class DuplexPort; -class Engine; -class GraphImpl; -class PortAudioDriver; -class PortImpl; -class FrameTimer; - -class PortAudioDriver : public Driver -{ -public: - explicit PortAudioDriver(Engine& engine); - ~PortAudioDriver(); - - bool attach(); - - bool activate(); - void deactivate(); - - EnginePort* create_port(DuplexPort* graph_port); - EnginePort* get_port(const Raul::Path& path); - - void rename_port(const Raul::Path& old_path, const Raul::Path& new_path); - void port_property(const Raul::Path& path, const URI& uri, const Atom& value); - void add_port(RunContext& context, EnginePort* port); - void remove_port(RunContext& context, EnginePort* port); - void register_port(EnginePort& port); - void unregister_port(EnginePort& port); - - void append_time_events(RunContext& context, Buffer& buffer) {} - - SampleCount frame_time() const; - - int real_time_priority() { return 80; } - - SampleCount block_length() const { return _block_length; } - size_t seq_size() const { return _seq_size; } - SampleCount sample_rate() const { return _sample_rate; } - -private: - friend class PortAudioPort; - - inline static int - pa_process_cb(const void* inputs, - void* outputs, - unsigned long nframes, - const PaStreamCallbackTimeInfo* time, - PaStreamCallbackFlags flags, - void* handle) { - return ((PortAudioDriver*)handle)->process_cb( - inputs, outputs, nframes, time, flags); - } - - int process_cb(const void* inputs, - void* outputs, - unsigned long nframes, - const PaStreamCallbackTimeInfo* time, - PaStreamCallbackFlags flags); - - void pre_process_port(RunContext& context, - EnginePort* port, - const void* inputs, - void* outputs); - - void post_process_port(RunContext& context, - EnginePort* port, - const void* inputs, - void* outputs); - -protected: - typedef boost::intrusive::slist<EnginePort, - boost::intrusive::cache_last<true> - > Ports; - - Engine& _engine; - Ports _ports; - PaStreamParameters _inputParameters; - PaStreamParameters _outputParameters; - Raul::Semaphore _sem; - std::unique_ptr<FrameTimer> _timer; - PaStream* _stream; - size_t _seq_size; - uint32_t _block_length; - uint32_t _sample_rate; - uint32_t _n_inputs; - uint32_t _n_outputs; - std::atomic<bool> _flag; - bool _is_activated; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_PORTAUDIODRIVER_HPP diff --git a/src/server/PortImpl.cpp b/src/server/PortImpl.cpp deleted file mode 100644 index b0ef3c85..00000000 --- a/src/server/PortImpl.cpp +++ /dev/null @@ -1,569 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "raul/Array.hpp" -#include "raul/Maid.hpp" - -#include "BlockImpl.hpp" -#include "Buffer.hpp" -#include "BufferFactory.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "PortType.hpp" -#include "ThreadManager.hpp" - -namespace Ingen { -namespace Server { - -static const uint32_t monitor_rate = 25.0; // Hz - -/** The length of time between monitor updates in frames */ -static inline uint32_t -monitor_period(const Engine& engine) -{ - return std::max(engine.block_length(), - engine.sample_rate() / monitor_rate); -} - -PortImpl::PortImpl(BufferFactory& bufs, - BlockImpl* const block, - const Raul::Symbol& name, - uint32_t index, - uint32_t poly, - PortType type, - LV2_URID buffer_type, - const Atom& value, - size_t buffer_size, - bool is_output) - : NodeImpl(bufs.uris(), block, name) - , _bufs(bufs) - , _index(index) - , _poly(poly) - , _buffer_size(buffer_size) - , _frames_since_monitor(0) - , _monitor_value(0.0f) - , _peak(0.0f) - , _type(type) - , _buffer_type(buffer_type) - , _value(value) - , _min(bufs.forge().make(0.0f)) - , _max(bufs.forge().make(1.0f)) - , _voices(bufs.maid().make_managed<Voices>(poly)) - , _connected_flag(false) - , _monitored(false) - , _force_monitor_update(false) - , _is_morph(false) - , _is_auto_morph(false) - , _is_logarithmic(false) - , _is_sample_rate(false) - , _is_toggled(false) - , _is_driver_port(false) - , _is_output(is_output) -{ - assert(block != nullptr); - assert(_poly > 0); - - const Ingen::URIs& uris = bufs.uris(); - - set_type(type, buffer_type); - - remove_property(uris.lv2_index, uris.patch_wildcard); - set_property(uris.lv2_index, bufs.forge().make((int32_t)index)); - - if (has_value()) { - set_property(uris.ingen_value, value); - } - if (type == PortType::ATOM) { - set_property(uris.atom_bufferType, - bufs.forge().make_urid(buffer_type)); - } - - if (is_output) { - if (_parent->graph_type() != Node::GraphType::GRAPH) { - add_property(bufs.uris().rdf_type, bufs.uris().lv2_OutputPort.urid); - } - } - - get_buffers(bufs, &BufferFactory::get_buffer, _voices, poly, 0); -} - -bool -PortImpl::get_buffers(BufferFactory& bufs, - GetFn get, - const MPtr<Voices>& voices, - uint32_t poly, - size_t num_in_arcs) const -{ - for (uint32_t v = 0; v < poly; ++v) { - voices->at(v).buffer.reset(); - voices->at(v).buffer = (bufs.*get)( - buffer_type(), _value.type(), _buffer_size); - } - - return true; -} - -bool -PortImpl::setup_buffers(RunContext& ctx, BufferFactory& bufs, uint32_t poly) -{ - return get_buffers(bufs, &BufferFactory::claim_buffer, _voices, poly, 0); -} - -void -PortImpl::set_type(PortType port_type, LV2_URID buffer_type) -{ - const Ingen::URIs& uris = _bufs.uris(); - Ingen::World* world = _bufs.engine().world(); - - // Update type properties so clients are aware of current type - remove_property(uris.rdf_type, uris.lv2_AudioPort); - remove_property(uris.rdf_type, uris.lv2_CVPort); - remove_property(uris.rdf_type, uris.lv2_ControlPort); - remove_property(uris.rdf_type, uris.atom_AtomPort); - add_property(uris.rdf_type, world->forge().make_urid(port_type.uri())); - - // Update audio thread types - _type = port_type; - _buffer_type = buffer_type; - if (!_buffer_type) { - switch (_type.id()) { - case PortType::CONTROL: - _buffer_type = uris.atom_Float; - break; - case PortType::AUDIO: - case PortType::CV: - _buffer_type = uris.atom_Sound; - break; - default: - break; - } - } - _buffer_size = std::max(_buffer_size, _bufs.default_size(_buffer_type)); -} - -bool -PortImpl::has_value() const -{ - return (_type == PortType::CONTROL || - _type == PortType::CV || - (_type == PortType::ATOM && - _value.type() == _bufs.uris().atom_Float)); -} - -bool -PortImpl::supports(const URIs::Quark& value_type) const -{ - return has_property(_bufs.uris().atom_supports, value_type); -} - -void -PortImpl::activate(BufferFactory& bufs) -{ - /* Set the time since the last monitor update to a random value within the - monitor period, to spread the load out over time. Otherwise, every - port would try to send an update at exactly the same time, every time. - */ - const double srate = bufs.engine().sample_rate(); - const uint32_t period = srate / monitor_rate; - _frames_since_monitor = bufs.engine().frand() * period; - _monitor_value = 0.0f; - _peak = 0.0f; - - // Trigger buffer re-connect next cycle - _connected_flag.clear(std::memory_order_release); -} - -void -PortImpl::deactivate() -{ - if (is_output() && !_is_driver_port) { - for (uint32_t v = 0; v < _poly; ++v) { - if (_voices->at(v).buffer) { - _voices->at(v).buffer->clear(); - } - } - } - _monitor_value = 0.0f; - _peak = 0.0f; -} - -void -PortImpl::set_voices(RunContext& context, MPtr<Voices>&& voices) -{ - _voices = std::move(voices); - connect_buffers(); -} - -void -PortImpl::cache_properties() -{ - _is_logarithmic = has_property(_bufs.uris().lv2_portProperty, - _bufs.uris().pprops_logarithmic); - _is_sample_rate = has_property(_bufs.uris().lv2_portProperty, - _bufs.uris().lv2_sampleRate); - _is_toggled = has_property(_bufs.uris().lv2_portProperty, - _bufs.uris().lv2_toggled); -} - -void -PortImpl::set_control_value(const RunContext& context, - FrameTime time, - Sample value) -{ - for (uint32_t v = 0; v < _poly; ++v) { - update_set_state(context, v); - set_voice_value(context, v, time, value); - } -} - -void -PortImpl::set_voice_value(const RunContext& context, - uint32_t voice, - FrameTime time, - Sample value) -{ - switch (_type.id()) { - case PortType::CONTROL: - if (buffer(voice)->value()) { - ((LV2_Atom_Float*)buffer(voice)->value())->body = value; - } - _voices->at(voice).set_state.set(context, context.start(), value); - break; - case PortType::AUDIO: - case PortType::CV: { - // Time may be at end so internal blocks can set triggers - assert(time >= context.start()); - assert(time <= context.start() + context.nframes()); - - const FrameTime offset = time - context.start(); - if (offset < context.nframes()) { - buffer(voice)->set_block(value, offset, context.nframes()); - } - /* else, this is a set at context.nframes(), used to reset a CV port's - value for the next block, particularly for triggers on the last - frame of a block (set nframes-1 to 1, then nframes to 0). */ - - _voices->at(voice).set_state.set(context, time, value); - } break; - case PortType::ATOM: - if (buffer(voice)->is_sequence()) { - const FrameTime offset = time - context.start(); - // Same deal as above - if (offset < context.nframes()) { - buffer(voice)->append_event(offset, - sizeof(value), - _bufs.uris().atom_Float, - (const uint8_t*)&value); - } - _voices->at(voice).set_state.set(context, time, value); - } else { -#ifndef NDEBUG - fprintf(stderr, - "error: %s set non-sequence atom port value (buffer type %u)\n", - path().c_str(), buffer(voice)->type()); -#endif - } - default: - break; - } -} - -void -PortImpl::update_set_state(const RunContext& context, uint32_t v) -{ - Voice& voice = _voices->at(v); - SetState& state = voice.set_state; - BufferRef buf = voice.buffer; - switch (state.state) { - case SetState::State::SET: - break; - case SetState::State::SET_CYCLE_1: - if (state.time < context.start() && - buf->is_sequence() && - buf->value_type() == _bufs.uris().atom_Float && - !_parent->is_main()) { - buf->clear(); - state.time = context.start(); - } - state.state = SetState::State::SET; - break; - case SetState::State::HALF_SET_CYCLE_1: - state.state = SetState::State::HALF_SET_CYCLE_2; - break; - case SetState::State::HALF_SET_CYCLE_2: - if (buf->is_sequence()) { - buf->clear(); - buf->append_event( - 0, sizeof(float), _bufs.uris().atom_Float, - (const uint8_t*)&state.value); - } else { - buf->set_block(state.value, 0, context.nframes()); - } - state.state = SetState::State::SET_CYCLE_1; - break; - } -} - -bool -PortImpl::prepare_poly(BufferFactory& bufs, uint32_t poly) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - if (_is_driver_port || _parent->is_main() || - (_type == PortType::ATOM && !_value.is_valid())) { - return false; - } else if (_poly == poly) { - return true; - } else if (_prepared_voices && _prepared_voices->size() != poly) { - _prepared_voices.reset(); - } - - if (!_prepared_voices) { - _prepared_voices = bufs.maid().make_managed<Voices>( - poly, *_voices, Voice()); - } - - get_buffers(bufs, &BufferFactory::get_buffer, - _prepared_voices, _prepared_voices->size(), num_arcs()); - - return true; -} - -bool -PortImpl::apply_poly(RunContext& context, uint32_t poly) -{ - if (_parent->is_main() || - (_type == PortType::ATOM && !_value.is_valid())) { - return false; - } else if (!_prepared_voices) { - return true; - } - - assert(poly == _prepared_voices->size()); - - _poly = poly; - - // Apply a new set of voices from a preceding call to prepare_poly - _voices = std::move(_prepared_voices); - - if (is_a(PortType::CONTROL) || is_a(PortType::CV)) { - set_control_value(context, context.start(), _value.get<float>()); - } - - assert(_voices->size() >= poly); - assert(this->poly() == poly); - assert(!_prepared_voices); - - connect_buffers(); - - return true; -} - -void -PortImpl::set_buffer_size(RunContext& context, BufferFactory& bufs, size_t size) -{ - _buffer_size = size; - - for (uint32_t v = 0; v < _poly; ++v) { - _voices->at(v).buffer->resize(size); - } - - connect_buffers(); -} - -void -PortImpl::connect_buffers(SampleCount offset) -{ - for (uint32_t v = 0; v < _poly; ++v) { - PortImpl::parent_block()->set_port_buffer(v, _index, buffer(v), offset); - } -} - -void -PortImpl::recycle_buffers() -{ - for (uint32_t v = 0; v < _poly; ++v) { - _voices->at(v).buffer = nullptr; - } -} - -void -PortImpl::set_is_driver_port(BufferFactory& bufs) -{ - _is_driver_port = true; -} - -void -PortImpl::clear_buffers(const RunContext& ctx) -{ - switch (_type.id()) { - case PortType::AUDIO: - default: - for (uint32_t v = 0; v < _poly; ++v) { - buffer(v)->clear(); - } - break; - case PortType::CONTROL: - for (uint32_t v = 0; v < _poly; ++v) { - buffer(v)->clear(); - _voices->at(v).set_state.set(ctx, ctx.start(), _value.get<float>()); - } - break; - case PortType::CV: - for (uint32_t v = 0; v < _poly; ++v) { - buffer(v)->set_block(_value.get<float>(), 0, ctx.nframes()); - _voices->at(v).set_state.set(ctx, ctx.start(), _value.get<float>()); - } - break; - } -} - -void -PortImpl::monitor(RunContext& context, bool send_now) -{ - if (!context.must_notify(this)) { - return; - } - - const uint32_t period = monitor_period(context.engine()); - _frames_since_monitor += context.nframes(); - - const bool time_to_send = send_now || _frames_since_monitor >= period; - const bool is_sequence = (_type.id() == PortType::ATOM && - _buffer_type == _bufs.uris().atom_Sequence); - if (!time_to_send && !(is_sequence && _monitored) && (!is_sequence && buffer(0)->value())) { - return; - } - - Forge& forge = context.engine().world()->forge(); - URIs& uris = context.engine().world()->uris(); - LV2_URID key = 0; - float val = 0.0f; - switch (_type.id()) { - case PortType::UNKNOWN: - break; - case PortType::AUDIO: - key = uris.ingen_activity; - val = _peak = std::max(_peak, buffer(0)->peak(context)); - break; - case PortType::CONTROL: - case PortType::CV: - key = uris.ingen_value; - val = buffer(0)->value_at(0); - break; - case PortType::ATOM: - if (_buffer_type == _bufs.uris().atom_Sequence) { - const LV2_Atom* atom = buffer(0)->get<const LV2_Atom>(); - const LV2_Atom* value = buffer(0)->value(); - if (atom->type != _bufs.uris().atom_Sequence) { - /* Buffer contents are not actually a Sequence. Probably an - uninitialized Chunk, so do nothing. */ - } else if (_monitored) { - /* Sequence explicitly monitored, send everything. */ - const LV2_Atom_Sequence* seq = (const LV2_Atom_Sequence*)atom; - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - context.notify(uris.ingen_activity, - context.start() + ev->time.frames, - this, - ev->body.size, - ev->body.type, - LV2_ATOM_BODY(&ev->body)); - } - } else if (value && value->type == _bufs.uris().atom_Float) { - /* Float sequence, monitor as a control. */ - key = uris.ingen_value; - val = ((LV2_Atom_Float*)buffer(0)->value())->body; - } else if (atom->size > sizeof(LV2_Atom_Sequence_Body)) { - /* General sequence, send activity for blinkenlights. */ - const int32_t one = 1; - context.notify(uris.ingen_activity, - context.start(), - this, - sizeof(int32_t), - (LV2_URID)uris.atom_Bool, - &one); - _force_monitor_update = false; - } - } - } - - _frames_since_monitor = _frames_since_monitor % period; - if (key && val != _monitor_value) { - if (context.notify(key, context.start(), this, - sizeof(float), forge.Float, &val)) { - /* Update frames since last update to conceptually zero, but keep - the remainder to preserve load balancing. */ - _frames_since_monitor = _frames_since_monitor % period; - _peak = 0.0f; - _monitor_value = val; - } - // Otherwise failure, leave old value and try again next time - } -} - -BufferRef -PortImpl::value_buffer(uint32_t voice) -{ - return buffer(voice)->value_buffer(); -} - -SampleCount -PortImpl::next_value_offset(SampleCount offset, SampleCount end) const -{ - SampleCount earliest = end; - for (uint32_t v = 0; v < _poly; ++v) { - const SampleCount o = _voices->at(v).buffer->next_value_offset(offset, end); - if (o < earliest) { - earliest = o; - } - } - return earliest; -} - -void -PortImpl::update_values(SampleCount offset, uint32_t voice) -{ - buffer(voice)->update_value_buffer(offset); -} - -void -PortImpl::pre_process(RunContext& context) -{ - if (!_connected_flag.test_and_set(std::memory_order_acquire)) { - connect_buffers(); - clear_buffers(context); - } - - for (uint32_t v = 0; v < _poly; ++v) { - _voices->at(v).buffer->prepare_output_write(context); - } -} - -void -PortImpl::post_process(RunContext& context) -{ - for (uint32_t v = 0; v < _poly; ++v) { - update_set_state(context, v); - update_values(0, v); - } - - monitor(context); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/PortImpl.hpp b/src/server/PortImpl.hpp deleted file mode 100644 index 70d90d0a..00000000 --- a/src/server/PortImpl.hpp +++ /dev/null @@ -1,312 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_PORTIMPL_HPP -#define INGEN_ENGINE_PORTIMPL_HPP - -#include <cstdlib> - -#include "ingen/Atom.hpp" -#include "raul/Array.hpp" - -#include "BufferRef.hpp" -#include "NodeImpl.hpp" -#include "PortType.hpp" -#include "RunContext.hpp" -#include "types.hpp" - -namespace Raul { class Maid; } - -namespace Ingen { -namespace Server { - -class BlockImpl; -class BufferFactory; -class RunContext; - -/** A port (input or output) on a Block. - * - * The base implementation here is general and/or for output ports (which are - * simplest), InputPort and DuplexPort override functions to provide - * specialized behaviour where necessary. - * - * \ingroup engine - */ -class PortImpl : public NodeImpl -{ -public: - struct SetState { - enum class State { - /// Partially set, first cycle: AAAAA => AAABB. - HALF_SET_CYCLE_1, - - /// Partially set, second cycle: AAABB => BBBBB. - HALF_SET_CYCLE_2, - - /// Fully set, first cycle (clear events if necessary). - SET_CYCLE_1, - - /// Fully set, second cycle and onwards (done). - SET - }; - - SetState() : state(State::SET), value(0), time(0) {} - - void set(const RunContext& context, FrameTime t, Sample v) { - time = t; - value = v; - state = (time == context.start() - ? State::SET - : State::HALF_SET_CYCLE_1); - } - - State state; ///< State of buffer for setting control value - Sample value; ///< Value currently being set - FrameTime time; ///< Time value was set - }; - - struct Voice { - Voice() : buffer(nullptr) {} - - SetState set_state; - BufferRef buffer; - }; - - typedef Raul::Array<Voice> Voices; - - PortImpl(BufferFactory& bufs, - BlockImpl* block, - const Raul::Symbol& name, - uint32_t index, - uint32_t poly, - PortType type, - LV2_URID buffer_type, - const Atom& value, - size_t buffer_size = 0, - bool is_output = true); - - virtual GraphType graph_type() const { return GraphType::PORT; } - - /** A port's parent is always a block, so static cast should be safe */ - BlockImpl* parent_block() const { return (BlockImpl*)_parent; } - - /** Set the the voices (buffers) for this port in the audio thread. */ - void set_voices(RunContext& context, MPtr<Voices>&& voices); - - /** Prepare for a new (external) polyphony value. - * - * Preprocessor thread, poly is actually applied by apply_poly. - */ - virtual bool prepare_poly(BufferFactory& bufs, uint32_t poly); - - /** Apply a new polyphony value. - * - * Audio thread. - * \a poly Must be < the most recent value passed to prepare_poly. - */ - virtual bool apply_poly(RunContext& context, uint32_t poly); - - /** Return the number of arcs (pre-process thraed). */ - virtual size_t num_arcs() const { return 0; } - - const Atom& value() const { return _value; } - void set_value(const Atom& v) { _value = v; } - - const Atom& minimum() const { return _min; } - const Atom& maximum() const { return _max; } - - /* The following two methods store the range in variables so it can be - accessed in the process thread, which is required for applying control - bindings from incoming MIDI data. - */ - void set_minimum(const Atom& min) { _min.set_rt(min); } - void set_maximum(const Atom& max) { _max.set_rt(max); } - - inline BufferRef buffer(uint32_t voice) const { - return _voices->at((_poly == 1) ? 0 : voice).buffer; - } - inline BufferRef prepared_buffer(uint32_t voice) const { - return _prepared_voices->at(voice).buffer; - } - - void update_set_state(const RunContext& context, uint32_t v); - - void set_voice_value(const RunContext& context, - uint32_t voice, - FrameTime time, - Sample value); - - void set_control_value(const RunContext& context, - FrameTime time, - Sample value); - - /** Prepare this port to use an external driver-provided buffer. - * - * This will avoid allocating a buffer for the port, instead the driver - * buffer is used directly. This only makes sense for ports on the - * top-level graph, which are monophonic. Non-real-time, must be called - * before using the port, followed by a call to set_driver_buffer() in the - * processing thread. - */ - virtual void set_is_driver_port(BufferFactory& bufs); - - bool is_driver_port() const { return _is_driver_port; } - - /** Called once per process cycle */ - virtual void pre_process(RunContext& context); - virtual void pre_run(RunContext& context) {} - virtual void post_process(RunContext& context); - - /** Clear/silence all buffers */ - virtual void clear_buffers(const RunContext& ctx); - - /** Claim and apply buffers in the real-time thread. */ - virtual bool setup_buffers(RunContext& ctx, BufferFactory& bufs, uint32_t poly); - - void activate(BufferFactory& bufs); - void deactivate(); - - /** - Inherit any properties from a connected neighbour. - - This is used for Graph ports, so e.g. a control input has the range of - all the ports it is connected to. - */ - virtual void inherit_neighbour(const PortImpl* port, - Properties& remove, - Properties& add) {} - - virtual void connect_buffers(SampleCount offset=0); - virtual void recycle_buffers(); - - uint32_t index() const { return _index; } - void set_index(RunContext&, uint32_t index) { _index = index; } - - inline bool is_a(PortType type) const { return _type == type; } - - bool has_value() const; - - PortType type() const { return _type; } - LV2_URID value_type() const { return _value.is_valid() ? _value.type() : 0; } - LV2_URID buffer_type() const { return _buffer_type; } - - bool supports(const URIs::Quark& value_type) const; - - size_t buffer_size() const { return _buffer_size; } - - uint32_t poly() const { - return _poly; - } - uint32_t prepared_poly() const { - return (_prepared_voices) ? _prepared_voices->size() : 1; - } - - void set_buffer_size(RunContext& context, BufferFactory& bufs, size_t size); - - /** Return true iff this port is explicitly monitored. - * - * This is used for plugin UIs which require monitoring for particular - * ports, even if the Ingen client has not requested broadcasting in - * general (e.g. for canvas animation). - */ - bool is_monitored() const { return _monitored; } - - /** Explicitly turn on monitoring for this port. */ - void enable_monitoring(bool monitored) { _monitored = monitored; } - - /** Monitor port value and broadcast to clients periodically. */ - void monitor(RunContext& context, bool send_now=false); - - BufferFactory& bufs() const { return _bufs; } - - BufferRef value_buffer(uint32_t voice); - - BufferRef user_buffer(RunContext&) const { return _user_buffer; } - void set_user_buffer(RunContext&, BufferRef b) { _user_buffer = b; } - - /** Return offset of the first value change after `offset`. */ - virtual SampleCount next_value_offset(SampleCount offset, - SampleCount end) const; - - /** Update value buffer for `voice` to be current as of `offset`. */ - void update_values(SampleCount offset, uint32_t voice); - - void force_monitor_update() { _force_monitor_update = true; } - - void set_morphable(bool is_morph, bool is_auto_morph) { - _is_morph = is_morph; - _is_auto_morph = is_auto_morph; - } - - void set_type(PortType port_type, LV2_URID buffer_type); - - void cache_properties(); - - bool is_input() const { return !_is_output; } - bool is_output() const { return _is_output; } - bool is_morph() const { return _is_morph; } - bool is_auto_morph() const { return _is_auto_morph; } - bool is_logarithmic() const { return _is_logarithmic; } - bool is_sample_rate() const { return _is_sample_rate; } - bool is_toggled() const { return _is_toggled; } - -protected: - typedef BufferRef (BufferFactory::*GetFn)(LV2_URID, LV2_URID, uint32_t); - - /** Set `voices` as the buffers to be used for this port. - * - * This is real-time safe only if `get` is as well, use in the real-time - * thread should pass &BufferFactory::claim_buffer. - * - * @return true iff buffers are locally owned by the port - */ - virtual bool get_buffers(BufferFactory& bufs, - GetFn get, - const MPtr<Voices>& voices, - uint32_t poly, - size_t num_in_arcs) const; - - BufferFactory& _bufs; - uint32_t _index; - uint32_t _poly; - uint32_t _buffer_size; - uint32_t _frames_since_monitor; - float _monitor_value; - float _peak; - PortType _type; - LV2_URID _buffer_type; - Atom _value; - Atom _min; - Atom _max; - MPtr<Voices> _voices; - MPtr<Voices> _prepared_voices; - BufferRef _user_buffer; - std::atomic_flag _connected_flag; - bool _monitored; - bool _force_monitor_update; - bool _is_morph; - bool _is_auto_morph; - bool _is_logarithmic; - bool _is_sample_rate; - bool _is_toggled; - bool _is_driver_port; - bool _is_output; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_PORTIMPL_HPP diff --git a/src/server/PortType.hpp b/src/server/PortType.hpp deleted file mode 100644 index 0b62c5ab..00000000 --- a/src/server/PortType.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_INTERFACE_PORTTYPE_HPP -#define INGEN_INTERFACE_PORTTYPE_HPP - -#include <cassert> - -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "lv2/lv2plug.in/ns/lv2core/lv2.h" - -namespace Ingen { - -/** The type of a port. - * - * This type refers to the type of the port itself (not necessarily the type - * of its contents). Ports with different types can contain the same type of - * data, but may e.g. have different access semantics. - */ -class PortType { -public: - enum ID { - UNKNOWN = 0, - AUDIO = 1, - CONTROL = 2, - CV = 3, - ATOM = 4 - }; - - explicit PortType(const URI& uri) - : _id(UNKNOWN) - { - if (uri == type_uri(AUDIO)) { - _id = AUDIO; - } else if (uri == type_uri(CONTROL)) { - _id = CONTROL; - } else if (uri == type_uri(CV)) { - _id = CV; - } else if (uri == type_uri(ATOM)) { - _id = ATOM; - } - } - - PortType(ID id) : _id(id) {} - - inline const URI& uri() const { return type_uri(_id); } - inline ID id() const { return _id; } - - inline bool operator==(const ID& id) const { return (_id == id); } - inline bool operator!=(const ID& id) const { return (_id != id); } - inline bool operator==(const PortType& type) const { return (_id == type._id); } - inline bool operator!=(const PortType& type) const { return (_id != type._id); } - inline bool operator<(const PortType& type) const { return (_id < type._id); } - - inline bool is_audio() { return _id == AUDIO; } - inline bool is_control() { return _id == CONTROL; } - inline bool is_cv() { return _id == CV; } - inline bool is_atom() { return _id == ATOM; } - -private: - static inline const URI& type_uri(unsigned id_num) { - assert(id_num <= ATOM); - static const URI uris[] = { - URI("http://www.w3.org/2002/07/owl#Nothing"), - URI(LV2_CORE__AudioPort), - URI(LV2_CORE__ControlPort), - URI(LV2_CORE__CVPort), - URI(LV2_ATOM__AtomPort) - }; - return uris[id_num]; - } - - ID _id; -}; - -} // namespace Ingen - -#endif // INGEN_INTERFACE_PORTTYPE_HPP diff --git a/src/server/PostProcessor.cpp b/src/server/PostProcessor.cpp deleted file mode 100644 index b275c36a..00000000 --- a/src/server/PostProcessor.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cassert> - -#include "Engine.hpp" -#include "Event.hpp" -#include "PostProcessor.hpp" -#include "RunContext.hpp" - -namespace Ingen { -namespace Server { - -class Sentinel : public Event { -public: - Sentinel(Engine& engine) : Event(engine) {} - - bool pre_process(PreProcessContext& ctx) { return false; } - void execute(RunContext& context) {} - void post_process() {} -}; - -PostProcessor::PostProcessor(Engine& engine) - : _engine(engine) - , _head(new Sentinel(engine)) - , _tail(_head.load()) - , _max_time(0) -{ -} - -PostProcessor::~PostProcessor() -{ - /* Delete any straggler events (usually at least one since the event list - is never completely emptied by process()). */ - Event* e = _head; - while (e) { - Event* const next = e->next(); - delete e; - e = next; - } -} - -void -PostProcessor::append(RunContext& context, Event* first, Event* last) -{ - assert(first); - assert(last); - assert(!last->next()); - - // The only place where _tail is written or next links are changed - _tail.load()->next(first); - _tail.store(last); -} - -bool -PostProcessor::pending() const -{ - return _head.load()->next() || _engine.pending_notifications(); -} - -void -PostProcessor::process() -{ - const FrameTime end_time = _max_time; - - /* We can never empty the list and set _head = _tail = NULL since this - would cause a race with append. Instead, head is an already - post-processed node, or initially a sentinel. */ - Event* ev = _head.load(); - Event* next = ev->next(); - if (!next || next->time() >= end_time) { - // Process audio thread notifications until end - _engine.emit_notifications(end_time); - return; - } - - do { - // Delete previously post-processed ev and move to next - delete ev; - ev = next; - - // Process audio thread notifications up until this event's time - _engine.emit_notifications(ev->time()); - - // Post-process event - ev->post_process(); - next = ev->next(); // [1] (see below) - } while (next && next->time() < end_time); - - /* Reached the tail (as far as we're concerned). There may be successors - by now if append() has been called since [1], but that's fine. Now, ev - points to the last post-processed event, which will be the new head. */ - assert(ev); - _head = ev; - - // Process remaining audio thread notifications until end - _engine.emit_notifications(end_time); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/PostProcessor.hpp b/src/server/PostProcessor.hpp deleted file mode 100644 index 5a3ffa62..00000000 --- a/src/server/PostProcessor.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_POSTPROCESSOR_HPP -#define INGEN_ENGINE_POSTPROCESSOR_HPP - -#include <atomic> - -#include "ingen/ingen.h" - -#include "types.hpp" - -namespace Ingen { -namespace Server { - -class Engine; -class Event; -class RunContext; - -/** Processor for Events after leaving the audio thread. - * - * The audio thread pushes events to this when it is done with them (which - * is realtime-safe), which signals the processing thread through a semaphore - * to handle the event and pass it on to the Maid. - * - * Update: This is all run from main_iteration now to solve scripting - * thread issues. Not sure if this is permanent/ideal or not... - * - * \ingroup engine - */ -class INGEN_API PostProcessor -{ -public: - explicit PostProcessor(Engine& engine); - ~PostProcessor(); - - /** Push a list of events on to the process queue. - realtime-safe, not thread-safe. - */ - void append(RunContext& context, Event* first, Event* last); - - /** Post-process and delete all pending events */ - void process(); - - /** Return true iff any events are pending */ - bool pending() const; - - /** Set the latest event time that should be post-processed */ - void set_end_time(FrameTime time) { _max_time = time; } - -private: - Engine& _engine; - std::atomic<Event*> _head; - std::atomic<Event*> _tail; - std::atomic<FrameTime> _max_time; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_POSTPROCESSOR_HPP diff --git a/src/server/PreProcessContext.hpp b/src/server/PreProcessContext.hpp deleted file mode 100644 index 1b57c013..00000000 --- a/src/server/PreProcessContext.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_PREPROCESSCONTEXT_HPP -#define INGEN_ENGINE_PREPROCESSCONTEXT_HPP - -#include <unordered_set> - -#include "GraphImpl.hpp" - -namespace Raul { class Maid; } - -namespace Ingen { -namespace Server { - -/** Event pre-processing context. - * - * \ingroup engine - */ -class PreProcessContext -{ -public: - typedef std::unordered_set<GraphImpl*> DirtyGraphs; - - /** Return true iff an atomic bundle is currently being pre-processed. */ - bool in_bundle() const { return _in_bundle; } - - /** Set/unset atomic bundle flag. */ - void set_in_bundle(bool b) { _in_bundle = b; } - - /** Return true iff graph should be compiled now (after a change). - * - * This may return false when an atomic bundle is deferring compilation, in - * which case the graph is flagged as dirty for later compilation. - */ - bool must_compile(GraphImpl& graph) { - if (!graph.enabled()) { - return false; - } else if (_in_bundle) { - _dirty_graphs.insert(&graph); - return false; - } else { - return true; - } - } - - /** Compile graph and return the result if necessary. - * - * This may return null when an atomic bundle is deferring compilation, in - * which case the graph is flagged as dirty for later compilation. - */ - MPtr<CompiledGraph> maybe_compile(Raul::Maid& maid, GraphImpl& graph) { - if (must_compile(graph)) { - return compile(maid, graph); - } - return MPtr<CompiledGraph>(); - } - - /** Return all graphs that require compilation after an atomic bundle. */ - const DirtyGraphs& dirty_graphs() const { return _dirty_graphs; } - DirtyGraphs& dirty_graphs() { return _dirty_graphs; } - -private: - DirtyGraphs _dirty_graphs; - bool _in_bundle = false; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_PREPROCESSCONTEXT_HPP diff --git a/src/server/PreProcessor.cpp b/src/server/PreProcessor.cpp deleted file mode 100644 index f674284e..00000000 --- a/src/server/PreProcessor.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <stdexcept> - -#include "ingen/AtomSink.hpp" -#include "ingen/AtomWriter.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/World.hpp" - -#include "Engine.hpp" -#include "Event.hpp" -#include "PostProcessor.hpp" -#include "PreProcessContext.hpp" -#include "PreProcessor.hpp" -#include "RunContext.hpp" -#include "ThreadManager.hpp" -#include "UndoStack.hpp" - -namespace Ingen { -namespace Server { - -PreProcessor::PreProcessor(Engine& engine) - : _engine(engine) - , _sem(0) - , _head(nullptr) - , _tail(nullptr) - , _block_state(BlockState::UNBLOCKED) - , _exit_flag(false) - , _thread(&PreProcessor::run, this) -{} - -PreProcessor::~PreProcessor() -{ - if (_thread.joinable()) { - _exit_flag = true; - _sem.post(); - _thread.join(); - } -} - -void -PreProcessor::event(Event* const ev, Event::Mode mode) -{ - // TODO: Probably possible to make this lock-free with CAS - ThreadManager::assert_not_thread(THREAD_IS_REAL_TIME); - std::lock_guard<std::mutex> lock(_mutex); - - assert(!ev->is_prepared()); - assert(!ev->next()); - ev->set_mode(mode); - - /* Note that tail is only used here, not in process(). The head must be - checked first here, since if it is NULL the tail pointer is junk. */ - Event* const head = _head.load(); - if (!head) { - _head = ev; - _tail = ev; - } else { - _tail.load()->next(ev); - _tail = ev; - } - - _sem.post(); -} - -unsigned -PreProcessor::process(RunContext& context, PostProcessor& dest, size_t limit) -{ - Event* const head = _head.load(); - size_t n_processed = 0; - Event* ev = head; - Event* last = ev; - while (ev && ev->is_prepared()) { - switch (_block_state.load()) { - case BlockState::UNBLOCKED: - break; - case BlockState::PRE_BLOCKED: - if (ev->get_execution() == Event::Execution::BLOCK) { - _block_state = BlockState::BLOCKED; - } else if (ev->get_execution() == Event::Execution::ATOMIC) { - _block_state = BlockState::PROCESSING; - } - break; - case BlockState::BLOCKED: - break; - case BlockState::PRE_UNBLOCKED: - assert(ev->get_execution() == Event::Execution::BLOCK); - if (ev->get_execution() == Event::Execution::BLOCK) { - _block_state = BlockState::PROCESSING; - } - break; - case BlockState::PROCESSING: - if (ev->get_execution() == Event::Execution::UNBLOCK) { - _block_state = BlockState::UNBLOCKED; - } - } - - if (_block_state == BlockState::BLOCKED) { - break; // Waiting for PRE_UNBLOCKED - } else if (ev->time() < context.start()) { - ev->set_time(context.start()); // Too late, nudge to context start - } else if (_block_state != BlockState::PROCESSING && - ev->time() >= context.end()) { - break; // Event is for a future cycle - } - - // Execute event - ev->execute(context); - ++n_processed; - - // Unblock pre-processing if this is a non-bundled atomic event - if (ev->get_execution() == Event::Execution::ATOMIC) { - assert(_block_state.load() == BlockState::PROCESSING); - _block_state = BlockState::UNBLOCKED; - } - - // Move to next event - last = ev; - ev = ev->next(); - - if (_block_state != BlockState::PROCESSING && - limit && n_processed >= limit) { - break; - } - } - - if (n_processed > 0) { -#ifndef NDEBUG - Engine& engine = context.engine(); - if (engine.world()->conf().option("trace").get<int32_t>()) { - const uint64_t start = engine.cycle_start_time(context); - const uint64_t end = engine.current_time(); - fprintf(stderr, "Processed %zu events in %u us\n", - n_processed, (unsigned)(end - start)); - } -#endif - - Event* next = (Event*)last->next(); - last->next(nullptr); - dest.append(context, head, last); - - // Since _head was not NULL, we know it hasn't been changed since - _head = next; - - /* If next is NULL, then _tail may now be invalid. However, it would cause - a race to reset _tail here. Instead, append() checks only _head for - emptiness, and resets the tail appropriately. */ - } - - return n_processed; -} - -void -PreProcessor::run() -{ - PreProcessContext ctx; - - UndoStack& undo_stack = *_engine.undo_stack(); - UndoStack& redo_stack = *_engine.redo_stack(); - AtomWriter undo_writer( - _engine.world()->uri_map(), _engine.world()->uris(), undo_stack); - AtomWriter redo_writer( - _engine.world()->uri_map(), _engine.world()->uris(), redo_stack); - - ThreadManager::set_flag(THREAD_PRE_PROCESS); - - Event* back = nullptr; - while (!_exit_flag) { - if (!_sem.timed_wait(std::chrono::seconds(1))) { - continue; - } - - if (!back) { - // Ran off end, find new unprepared back - back = _head; - while (back && back->is_prepared()) { - back = back->next(); - } - } - - Event* const ev = back; - if (!ev) { - continue; - } - - // Set block state before enqueueing event - switch (ev->get_execution()) { - case Event::Execution::NORMAL: - break; - case Event::Execution::ATOMIC: - assert(_block_state == BlockState::UNBLOCKED); - _block_state = BlockState::PRE_BLOCKED; - break; - case Event::Execution::BLOCK: - assert(_block_state == BlockState::UNBLOCKED); - _block_state = BlockState::PRE_BLOCKED; - break; - case Event::Execution::UNBLOCK: - wait_for_block_state(BlockState::BLOCKED); - _block_state = BlockState::PRE_UNBLOCKED; - } - - // Prepare event, allowing it to be processed - assert(!ev->is_prepared()); - if (ev->pre_process(ctx)) { - switch (ev->get_mode()) { - case Event::Mode::NORMAL: - case Event::Mode::REDO: - undo_stack.start_entry(); - ev->undo(undo_writer); - undo_stack.finish_entry(); - // undo_stack.save(stderr); - break; - case Event::Mode::UNDO: - redo_stack.start_entry(); - ev->undo(redo_writer); - redo_stack.finish_entry(); - // redo_stack.save(stderr, "redo"); - break; - } - } - assert(ev->is_prepared()); - - // Wait for process() if necessary - if (ev->get_execution() == Event::Execution::ATOMIC) { - wait_for_block_state(BlockState::UNBLOCKED); - } - - back = (Event*)ev->next(); - } -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/PreProcessor.hpp b/src/server/PreProcessor.hpp deleted file mode 100644 index eb72328e..00000000 --- a/src/server/PreProcessor.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_PREPROCESSOR_HPP -#define INGEN_ENGINE_PREPROCESSOR_HPP - -#include <atomic> -#include <thread> -#include <mutex> - -#include "raul/Semaphore.hpp" - -namespace Ingen { -namespace Server { - -class Engine; -class Event; -class PostProcessor; -class RunContext; - -class PreProcessor -{ -public: - explicit PreProcessor(Engine& engine); - - ~PreProcessor(); - - /** Return true iff no events are enqueued. */ - inline bool empty() const { return !_head.load(); } - - /** Enqueue an event. - * This is safe to call from any non-realtime thread (it locks). - */ - void event(Event* ev, Event::Mode mode); - - /** Process events for a cycle. - * @return The number of events processed. - */ - unsigned process(RunContext& context, - PostProcessor& dest, - size_t limit = 0); - -protected: - void run(); - -private: - enum class BlockState { - UNBLOCKED, ///< Normal, unblocked execution - PRE_BLOCKED, ///< Preprocess thread has enqueued blocking event - BLOCKED, ///< Process thread has reached blocking event - PRE_UNBLOCKED, ///< Preprocess thread has enqueued unblocking event - PROCESSING ///< Process thread is executing all events in-between - }; - - void wait_for_block_state(const BlockState state) { - while (_block_state != state) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - } - - Engine& _engine; - std::mutex _mutex; - Raul::Semaphore _sem; - std::atomic<Event*> _head; - std::atomic<Event*> _tail; - std::atomic<BlockState> _block_state; - bool _exit_flag; - std::thread _thread; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_PREPROCESSOR_HPP diff --git a/src/server/RunContext.cpp b/src/server/RunContext.cpp deleted file mode 100644 index 3ab9d15c..00000000 --- a/src/server/RunContext.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Forge.hpp" -#include "ingen/Log.hpp" -#include "ingen/URIMap.hpp" - -#include "Broadcaster.hpp" -#include "BufferFactory.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "RunContext.hpp" -#include "Task.hpp" - -namespace Ingen { -namespace Server { - -struct Notification -{ - inline Notification(PortImpl* p = nullptr, - FrameTime f = 0, - LV2_URID k = 0, - uint32_t s = 0, - LV2_URID t = 0) - : port(p), time(f), key(k), size(s), type(t) - {} - - PortImpl* port; - FrameTime time; - LV2_URID key; - uint32_t size; - LV2_URID type; -}; - -RunContext::RunContext(Engine& engine, - Raul::RingBuffer* event_sink, - unsigned id, - bool threaded) - : _engine(engine) - , _event_sink(event_sink) - , _task(nullptr) - , _thread(threaded ? new std::thread(&RunContext::run, this) : nullptr) - , _id(id) - , _start(0) - , _end(0) - , _offset(0) - , _nframes(0) - , _realtime(true) -{} - -RunContext::RunContext(const RunContext& copy) - : _engine(copy._engine) - , _event_sink(copy._event_sink) - , _task(nullptr) - , _thread(nullptr) - , _id(copy._id) - , _start(copy._start) - , _end(copy._end) - , _offset(copy._offset) - , _nframes(copy._nframes) - , _realtime(copy._realtime) -{} - -bool -RunContext::must_notify(const PortImpl* port) const -{ - return (port->is_monitored() || _engine.broadcaster()->must_broadcast()); -} - -bool -RunContext::notify(LV2_URID key, - FrameTime time, - PortImpl* port, - uint32_t size, - LV2_URID type, - const void* body) -{ - const Notification n(port, time, key, size, type); - if (_event_sink->write_space() < sizeof(n) + size) { - return false; - } - if (_event_sink->write(sizeof(n), &n) != sizeof(n)) { - _engine.log().rt_error("Error writing header to notification ring\n"); - } else if (_event_sink->write(size, body) != size) { - _engine.log().rt_error("Error writing body to notification ring\n"); - } else { - return true; - } - return false; -} - -void -RunContext::emit_notifications(FrameTime end) -{ - const URIs& uris = _engine.buffer_factory()->uris(); - const uint32_t read_space = _event_sink->read_space(); - Notification note; - for (uint32_t i = 0; i < read_space; i += sizeof(note)) { - if (_event_sink->peek(sizeof(note), ¬e) != sizeof(note) || - note.time >= end) { - return; - } - if (_event_sink->read(sizeof(note), ¬e) == sizeof(note)) { - Atom value = _engine.world()->forge().alloc( - note.size, note.type, nullptr); - if (_event_sink->read(note.size, value.get_body()) == note.size) { - i += note.size; - const char* key = _engine.world()->uri_map().unmap_uri(note.key); - if (key) { - _engine.broadcaster()->set_property( - note.port->uri(), URI(key), value); - if (note.port->is_input() && - (note.key == uris.ingen_value || - note.key == uris.midi_binding)) { - // FIXME: not thread safe - note.port->set_property(URI(key), value); - } - } else { - _engine.log().rt_error("Error unmapping notification key URI\n"); - } - } else { - _engine.log().rt_error("Error reading body from notification ring\n"); - } - } else { - _engine.log().rt_error("Error reading header from notification ring\n"); - } - } -} - -void -RunContext::claim_task(Task* task) -{ - if ((_task = task)) { - _engine.signal_tasks_available(); - } -} - -Task* -RunContext::steal_task() const -{ - return _engine.steal_task(_id + 1); -} - -void -RunContext::set_priority(int priority) -{ - if (_thread) { - pthread_t pthread = _thread->native_handle(); - const int policy = (priority > 0) ? SCHED_FIFO : SCHED_OTHER; - sched_param sp; - sp.sched_priority = (priority > 0) ? priority : 0; - if (pthread_setschedparam(pthread, policy, &sp)) { - _engine.log().error( - fmt("Failed to set real-time priority of run thread (%s)\n") - % strerror(errno)); - } - } -} - -void -RunContext::join() -{ - if (_thread) { - if (_thread->joinable()) { - _thread->join(); - } - delete _thread; - } -} - -void -RunContext::run() -{ - while (_engine.wait_for_tasks()) { - for (Task* t; (t = _engine.steal_task(0));) { - t->run(*this); - } - } -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/RunContext.hpp b/src/server/RunContext.hpp deleted file mode 100644 index bb64a250..00000000 --- a/src/server/RunContext.hpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_RUNCONTEXT_HPP -#define INGEN_ENGINE_RUNCONTEXT_HPP - -#include <cstdint> -#include <thread> - -#include "lv2/lv2plug.in/ns/ext/urid/urid.h" -#include "raul/RingBuffer.hpp" - -#include "types.hpp" - -namespace Ingen { -namespace Server { - -class Engine; -class PortImpl; -class Task; - -/** Graph execution context. - * - * This is used to pass whatever information a Node might need to process; such - * as the current time, a sink for generated events, etc. - * - * Note the logical distinction between nframes (jack relative) and start/end - * (timeline relative). If transport speed != 1.0, then end-start != nframes - * (though currently this is never the case, it may be if ingen incorporates - * tempo and varispeed). - * - * \ingroup engine - */ -class RunContext -{ -public: - /** Create a new run context. - * - * @param engine The engine this context is running within. - * @param event_sink Sink for notification events (peaks etc) - * @param id The ID of this context. - * @param threaded If true, then this context is a worker which will launch - * a thread and execute tasks as they become available. - */ - RunContext(Engine& engine, - Raul::RingBuffer* event_sink, - unsigned id, - bool threaded); - - /** Create a sub-context of `parent`. - * - * This is used to subdivide process cycles, the sub-context is - * lightweight and only serves to pass different time attributes. - */ - RunContext(const RunContext& copy); - - /** Return true iff the given port should broadcast its value. - * - * Whether or not broadcasting is actually done is a per-client property, - * this is for use in the audio thread to quickly determine if the - * necessary calculations need to be done at all. - */ - bool must_notify(const PortImpl* port) const; - - /** Send a notification from this run context. - * @return false on failure (ring is full) - */ - bool notify(LV2_URID key = 0, - FrameTime time = 0, - PortImpl* port = nullptr, - uint32_t size = 0, - LV2_URID type = 0, - const void* body = nullptr); - - /** Emit pending notifications in some other non-realtime thread. */ - void emit_notifications(FrameTime end); - - /** Return true iff any notifications are pending. */ - bool pending_notifications() const { return _event_sink->read_space(); } - - /** Return the duration of this cycle in microseconds. - * - * This is the cycle length in frames (nframes) converted to microseconds, - * that is, the amount of real time that this cycle's audio represents. - * Note that this is unrelated to the amount of time available to execute a - * cycle (other than the fact that it must be processed in significantly - * less time to avoid a dropout when running in real time). - */ - inline uint64_t duration() const { - return (uint64_t)_nframes * 1e6 / _rate; - } - - inline void locate(FrameTime s, SampleCount nframes) { - _start = s; - _end = s + nframes; - _nframes = nframes; - } - - inline void slice(SampleCount offset, SampleCount nframes) { - _offset = offset; - _nframes = nframes; - } - - /** Claim a parallel task, and signal others that work is available. */ - void claim_task(Task* task); - - /** Steal a task from some other context if possible. */ - Task* steal_task() const; - - void set_priority(int priority); - void set_rate(SampleCount rate) { _rate = rate; } - - void join(); - - inline Engine& engine() const { return _engine; } - inline Task* task() const { return _task; } - inline unsigned id() const { return _id; } - inline FrameTime start() const { return _start; } - inline FrameTime time() const { return _start + _offset; } - inline FrameTime end() const { return _end; } - inline SampleCount offset() const { return _offset; } - inline SampleCount nframes() const { return _nframes; } - inline SampleCount rate() const { return _rate; } - inline bool realtime() const { return _realtime; } - -protected: - const RunContext& operator=(const RunContext& copy) = delete; - - void run(); - - Engine& _engine; ///< Engine we're running in - Raul::RingBuffer* _event_sink; ///< Port updates from process context - Task* _task; ///< Currently executing task - std::thread* _thread; ///< Thread (NULL for main run context) - unsigned _id; ///< Context ID - - FrameTime _start; ///< Start frame of this cycle, timeline relative - FrameTime _end; ///< End frame of this cycle, timeline relative - SampleCount _offset; ///< Offset into data buffers - SampleCount _nframes; ///< Number of frames past offset to process - SampleCount _rate; ///< Sample rate in Hz - bool _realtime; ///< True iff context is hard realtime -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_RUNCONTEXT_HPP diff --git a/src/server/SocketListener.cpp b/src/server/SocketListener.cpp deleted file mode 100644 index a6faa863..00000000 --- a/src/server/SocketListener.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <poll.h> -#include <signal.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <cerrno> -#include <sstream> -#include <string> -#include <thread> - -#include "ingen/Configuration.hpp" -#include "ingen/Log.hpp" -#include "ingen/Module.hpp" -#include "ingen/World.hpp" -#include "raul/Socket.hpp" - -#include "../server/Engine.hpp" -#include "../server/EventWriter.hpp" - -#include "SocketListener.hpp" -#include "SocketServer.hpp" - -namespace Ingen { -namespace Server { - -static constexpr const char* const unix_scheme = "unix://"; - -static std::string -get_link_target(const char* link_path) -{ - // Stat the link to get the required size for the target path - struct stat link_stat; - if (lstat(link_path, &link_stat)) { - return std::string(); - } - - // Allocate buffer and read link target - char* target = (char*)calloc(1, link_stat.st_size + 1); - if (readlink(link_path, target, link_stat.st_size) != -1) { - const std::string result(target); - free(target); - return result; - } - - return std::string(); -} - -static void ingen_listen(Engine* engine, - Raul::Socket* unix_sock, - Raul::Socket* net_sock); - - -SocketListener::SocketListener(Engine& engine) - : unix_sock(Raul::Socket::Type::UNIX) - , net_sock(Raul::Socket::Type::TCP) - , thread(new std::thread(ingen_listen, &engine, &unix_sock, &net_sock)) -{} - -SocketListener::~SocketListener() { - unix_sock.shutdown(); - net_sock.shutdown(); - thread->join(); - unlink(unix_sock.uri().substr(strlen(unix_scheme)).c_str()); -} - -static void -ingen_listen(Engine* engine, Raul::Socket* unix_sock, Raul::Socket* net_sock) -{ - Ingen::World* world = engine->world(); - - const std::string link_path(world->conf().option("socket").ptr<char>()); - const std::string unix_path(link_path + "." + std::to_string(getpid())); - - // Bind UNIX socket and create PID-less symbolic link - const URI unix_uri(unix_scheme + unix_path); - bool make_link = true; - if (!unix_sock->bind(unix_uri) || !unix_sock->listen()) { - world->log().error("Failed to create UNIX socket\n"); - unix_sock->close(); - make_link = false; - } else { - const std::string old_path = get_link_target(link_path.c_str()); - if (!old_path.empty()) { - const std::string suffix = old_path.substr(old_path.find_last_of(".") + 1); - const pid_t pid = std::stoi(suffix); - if (!kill(pid, 0)) { - make_link = false; - world->log().warn(fmt("Another Ingen instance is running at %1% => %2%\n") - % link_path % old_path); - } else { - world->log().warn(fmt("Replacing old link %1% => %2%\n") - % link_path % old_path); - unlink(link_path.c_str()); - } - } - - if (make_link) { - if (!symlink(unix_path.c_str(), link_path.c_str())) { - world->log().info(fmt("Listening on %1%\n") % - (unix_scheme + link_path)); - } else { - world->log().error(fmt("Failed to link %1% => %2% (%3%)\n") - % link_path % unix_path % strerror(errno)); - } - } else { - world->log().info(fmt("Listening on %1%\n") % unix_uri); - } - } - - // Bind TCP socket - const int port = world->conf().option("engine-port").get<int32_t>(); - std::ostringstream ss; - ss << "tcp://*:" << port; - if (!net_sock->bind(URI(ss.str())) || !net_sock->listen()) { - world->log().error("Failed to create TCP socket\n"); - net_sock->close(); - } else { - world->log().info(fmt("Listening on TCP port %1%\n") % port); - } - - if (unix_sock->fd() == -1 && net_sock->fd() == -1) { - return; // No sockets to listen to, exit thread - } - - struct pollfd pfds[2]; - int nfds = 0; - if (unix_sock->fd() != -1) { - pfds[nfds].fd = unix_sock->fd(); - pfds[nfds].events = POLLIN; - pfds[nfds].revents = 0; - ++nfds; - } - if (net_sock->fd() != -1) { - pfds[nfds].fd = net_sock->fd(); - pfds[nfds].events = POLLIN; - pfds[nfds].revents = 0; - ++nfds; - } - - while (true) { - // Wait for input to arrive at a socket - const int ret = poll(pfds, nfds, -1); - if (ret == -1) { - world->log().error(fmt("Poll error: %1%\n") % strerror(errno)); - break; - } else if (ret == 0) { - world->log().warn("Poll returned with no data\n"); - continue; - } else if ((pfds[0].revents & POLLHUP) || pfds[1].revents & POLLHUP) { - break; - } - - if (pfds[0].revents & POLLIN) { - SPtr<Raul::Socket> conn = unix_sock->accept(); - if (conn) { - new SocketServer(*world, *engine, conn); - } - } - - if (pfds[1].revents & POLLIN) { - SPtr<Raul::Socket> conn = net_sock->accept(); - if (conn) { - new SocketServer(*world, *engine, conn); - } - } - } - - if (make_link) { - unlink(link_path.c_str()); - } -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/SocketListener.hpp b/src/server/SocketListener.hpp deleted file mode 100644 index e74629ad..00000000 --- a/src/server/SocketListener.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <memory> -#include <thread> - -#include "raul/Socket.hpp" - -namespace Ingen { -namespace Server { - -class Engine; - -/** Listens on main sockets and spawns socket servers for new connections. */ -class SocketListener -{ -public: - SocketListener(Engine& engine); - ~SocketListener(); - -private: - Raul::Socket unix_sock; - Raul::Socket net_sock; - std::unique_ptr<std::thread> thread; -}; - -} // namespace Server -} // namespace Ingen diff --git a/src/server/SocketServer.hpp b/src/server/SocketServer.hpp deleted file mode 100644 index dbeb76ea..00000000 --- a/src/server/SocketServer.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_SERVER_SOCKET_SERVER_HPP -#define INGEN_SERVER_SOCKET_SERVER_HPP - -#include "ingen/SocketReader.hpp" -#include "ingen/SocketWriter.hpp" -#include "ingen/StreamWriter.hpp" -#include "ingen/Tee.hpp" -#include "raul/Socket.hpp" - -#include "EventWriter.hpp" - -namespace Ingen { -namespace Server { - -/** The server side of an Ingen socket connection. */ -class SocketServer -{ -public: - SocketServer(World& world, - Server::Engine& engine, - SPtr<Raul::Socket> sock) - : _engine(engine) - , _sink(world.conf().option("dump").get<int32_t>() - ? SPtr<Interface>( - new Tee({SPtr<Interface>(new EventWriter(engine)), - SPtr<Interface>(new StreamWriter(world.uri_map(), - world.uris(), - URI("ingen:/engine"), - stderr, - ColorContext::Color::CYAN))})) - : SPtr<Interface>(new EventWriter(engine))) - , _reader(new SocketReader(world, *_sink.get(), sock)) - , _writer(new SocketWriter(world.uri_map(), - world.uris(), - URI(sock->uri()), - sock)) - { - _sink->set_respondee(_writer); - engine.register_client(_writer); - } - - ~SocketServer() { - if (_writer) { - _engine.unregister_client(_writer); - } - } - -protected: - void on_hangup() { - _engine.unregister_client(_writer); - _writer.reset(); - } - -private: - Server::Engine& _engine; - SPtr<Interface> _sink; - SPtr<SocketReader> _reader; - SPtr<SocketWriter> _writer; -}; - -} // namespace Ingen -} // namespace Socket - -#endif // INGEN_SERVER_SOCKET_SERVER_HPP diff --git a/src/server/Task.cpp b/src/server/Task.cpp deleted file mode 100644 index d2cb2683..00000000 --- a/src/server/Task.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2015-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "BlockImpl.hpp" -#include "Task.hpp" - -namespace Ingen { -namespace Server { - -void -Task::run(RunContext& context) -{ - switch (_mode) { - case Mode::SINGLE: - // fprintf(stderr, "%u run %s\n", context.id(), _block->path().c_str()); - _block->process(context); - break; - case Mode::SEQUENTIAL: - for (const auto& task : _children) { - task->run(context); - } - break; - case Mode::PARALLEL: - // Initialize (not) done state of sub-tasks - for (const auto& task : _children) { - task->set_done(false); - } - - // Grab the first sub-task - _next = 0; - _done_end = 0; - Task* t = steal(context); - - // Allow other threads to steal sub-tasks - context.claim_task(this); - - // Run available tasks until this task is finished - for (; t; t = get_task(context)) { - t->run(context); - } - context.claim_task(nullptr); - break; - } - - set_done(true); -} - -Task* -Task::steal(RunContext& context) -{ - if (_mode == Mode::PARALLEL) { - const unsigned i = _next++; - if (i < _children.size()) { - return _children[i].get(); - } - } - - return nullptr; -} - -Task* -Task::get_task(RunContext& context) -{ - // Attempt to "steal" a task from ourselves - Task* t = steal(context); - if (t) { - return t; - } - - while (true) { - // Push done end index as forward as possible - while (_done_end < _children.size() && _children[_done_end]->done()) { - ++_done_end; - } - - if (_done_end >= _children.size()) { - return nullptr; // All child tasks are finished - } - - // All child tasks claimed, but some are unfinished, steal a task - if ((t = context.steal_task())) { - return t; - } - - /* All child tasks are claimed, and we failed to steal any tasks. Spin - to prevent blocking, though it would probably be wiser to wait for a - signal in non-main threads, and maybe even in the main thread - depending on your real-time safe philosophy... more experimentation - here is needed. */ - } -} - -std::unique_ptr<Task> -Task::simplify(std::unique_ptr<Task>&& task) -{ - if (task->mode() == Mode::SINGLE) { - return std::move(task); - } - - std::unique_ptr<Task> ret = std::unique_ptr<Task>(new Task(task->mode())); - for (auto&& c : task->_children) { - auto child = simplify(std::move(c)); - if (!child->empty()) { - if (child->mode() == task->mode()) { - // Merge child into parent - for (auto&& grandchild : child->_children) { - ret->append(std::move(grandchild)); - } - } else { - // Add child task - ret->append(std::move(child)); - } - } - } - - if (ret->_children.size() == 1) { - return std::move(ret->_children.front()); - } - - return ret; -} - -void -Task::dump(std::function<void (const std::string&)> sink, unsigned indent, bool first) const -{ - if (!first) { - sink("\n"); - for (unsigned i = 0; i < indent; ++i) { - sink(" "); - } - } - - if (_mode == Mode::SINGLE) { - sink(_block->path()); - } else { - sink(((_mode == Mode::SEQUENTIAL) ? "(seq " : "(par ")); - for (size_t i = 0; i < _children.size(); ++i) { - _children[i]->dump(sink, indent + 5, i == 0); - } - sink(")"); - } -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/Task.hpp b/src/server/Task.hpp deleted file mode 100644 index 2cdad71b..00000000 --- a/src/server/Task.hpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_TASK_HPP -#define INGEN_ENGINE_TASK_HPP - -#include <atomic> -#include <cassert> -#include <deque> -#include <functional> -#include <memory> -#include <ostream> - -namespace Ingen { -namespace Server { - -class BlockImpl; -class RunContext; - -class Task { -public: - enum class Mode { - SINGLE, ///< Single block to run - SEQUENTIAL, ///< Elements must be run sequentially in order - PARALLEL ///< Elements may be run in any order in parallel - }; - - Task(Mode mode, BlockImpl* block = nullptr) - : _block(block) - , _mode(mode) - , _done_end(0) - , _next(0) - , _done(false) - { - assert(!(mode == Mode::SINGLE && !block)); - } - - Task(Task&& task) - : _children(std::move(task._children)) - , _block(task._block) - , _mode(task._mode) - , _done_end(task._done_end) - , _next(task._next.load()) - , _done(task._done.load()) - {} - - Task& operator=(Task&& task) - { - _children = std::move(task._children); - _block = task._block; - _mode = task._mode; - _done_end = task._done_end; - _next = task._next.load(); - _done = task._done.load(); - return *this; - } - - /** Run task in the given context. */ - void run(RunContext& context); - - /** Pretty print task to the given stream (recursively). */ - void dump(std::function<void (const std::string&)> sink, unsigned indent, bool first) const; - - /** Return true iff this is an empty task. */ - bool empty() const { return _mode != Mode::SINGLE && _children.empty(); } - - /** Simplify task expression. */ - static std::unique_ptr<Task> simplify(std::unique_ptr<Task>&& task); - - /** Steal a child task from this task (succeeds for PARALLEL only). */ - Task* steal(RunContext& context); - - /** Prepend a child to this task. */ - void push_front(Task&& task) { - _children.emplace_front(std::unique_ptr<Task>(new Task(std::move(task)))); - } - - Mode mode() const { return _mode; } - BlockImpl* block() const { return _block; } - bool done() const { return _done; } - - void set_done(bool done) { _done = done; } - -private: - typedef std::deque<std::unique_ptr<Task>> Children; - - Task(const Task&) = delete; - Task& operator=(const Task&) = delete; - - Task* get_task(RunContext& context); - - void append(std::unique_ptr<Task>&& t) { - _children.emplace_back(std::move(t)); - } - - Children _children; ///< Vector of child tasks - BlockImpl* _block; ///< Used for SINGLE only - Mode _mode; ///< Execution mode - unsigned _done_end; ///< Index of rightmost done sub-task - std::atomic<unsigned> _next; ///< Index of next sub-task - std::atomic<bool> _done; ///< Completion phase -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_TASK_HPP diff --git a/src/server/ThreadManager.hpp b/src/server/ThreadManager.hpp deleted file mode 100644 index 3bcedf30..00000000 --- a/src/server/ThreadManager.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_THREADMANAGER_HPP -#define INGEN_ENGINE_THREADMANAGER_HPP - -#include <cassert> - -#include "ingen/ingen.h" - -#include "util.hpp" - -namespace Ingen { -namespace Server { - -enum ThreadFlag { - THREAD_IS_REAL_TIME = 1, - THREAD_PRE_PROCESS = 1 << 1, - THREAD_PROCESS = 1 << 2, - THREAD_MESSAGE = 1 << 3, -}; - -class INGEN_API ThreadManager { -public: - static inline void set_flag(ThreadFlag f) { -#ifndef NDEBUG - flags = ((unsigned)flags | f); -#endif - } - - static inline void unset_flag(ThreadFlag f) { -#ifndef NDEBUG - flags = ((unsigned)flags & (~f)); -#endif - } - - static inline void assert_thread(ThreadFlag f) { - assert(single_threaded || (flags & f)); - } - - static inline void assert_not_thread(ThreadFlag f) { - assert(single_threaded || !(flags & f)); - } - - /** Set to true during initialisation so ensure_thread doesn't fail. - * Defined in Engine.cpp - */ - static bool single_threaded; - static INGEN_THREAD_LOCAL unsigned flags; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_THREADMANAGER_HPP diff --git a/src/server/UndoStack.cpp b/src/server/UndoStack.cpp deleted file mode 100644 index dad211ad..00000000 --- a/src/server/UndoStack.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <ctime> - -#include "ingen/URIMap.hpp" -#include "ingen/URIs.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "lv2/lv2plug.in/ns/ext/patch/patch.h" -#include "serd/serd.h" -#include "sratom/sratom.h" - -#include "UndoStack.hpp" - -#define NS_RDF (const uint8_t*)"http://www.w3.org/1999/02/22-rdf-syntax-ns#" - -#define USTR(s) ((const uint8_t*)(s)) - -namespace Ingen { -namespace Server { - -int -UndoStack::start_entry() -{ - if (_depth == 0) { - time_t now; - time(&now); - _stack.push_back(Entry(now)); - } - return ++_depth; -} - -bool -UndoStack::write(const LV2_Atom* msg, int32_t default_id) -{ - _stack.back().push_event(msg); - return true; -} - -bool -UndoStack::ignore_later_event(const LV2_Atom* first, - const LV2_Atom* second) const -{ - if (first->type != _uris.atom_Object || first->type != second->type) { - return false; - } - - const LV2_Atom_Object* f = (const LV2_Atom_Object*)first; - const LV2_Atom_Object* s = (const LV2_Atom_Object*)second; - if (f->body.otype == _uris.patch_Set && f->body.otype == s->body.otype) { - const LV2_Atom* f_subject = nullptr; - const LV2_Atom* f_property = nullptr; - const LV2_Atom* s_subject = nullptr; - const LV2_Atom* s_property = nullptr; - lv2_atom_object_get(f, - (LV2_URID)_uris.patch_subject, &f_subject, - (LV2_URID)_uris.patch_property, &f_property, - 0); - lv2_atom_object_get(s, - (LV2_URID)_uris.patch_subject, &s_subject, - (LV2_URID)_uris.patch_property, &s_property, - 0); - return (lv2_atom_equals(f_subject, s_subject) && - lv2_atom_equals(f_property, s_property)); - } - - return false; -} - -int -UndoStack::finish_entry() -{ - if (--_depth > 0) { - return _depth; - } else if (_stack.back().events.empty()) { - // Disregard empty entry - _stack.pop_back(); - } else if (_stack.size() > 1 && _stack.back().events.size() == 1) { - // This entry and the previous one have one event, attempt to merge - auto i = _stack.rbegin(); - ++i; - if (i->events.size() == 1) { - if (ignore_later_event(i->events[0], _stack.back().events[0])) { - _stack.pop_back(); - } - } - } - - return _depth; -} - -UndoStack::Entry -UndoStack::pop() -{ - Entry top; - if (!_stack.empty()) { - top = _stack.back(); - _stack.pop_back(); - } - return top; -} - -struct BlankIDs { - BlankIDs(char c='b') : n(0), c(c) {} - - SerdNode get() { - snprintf(buf, sizeof(buf), "%c%u", c, n++); - return serd_node_from_string(SERD_BLANK, USTR(buf)); - } - - char buf[16]; - unsigned n; - const char c; -}; - -struct ListContext { - explicit ListContext(BlankIDs& ids, unsigned flags, const SerdNode* s, const SerdNode* p) - : ids(ids) - , s(*s) - , p(*p) - , flags(flags | SERD_LIST_O_BEGIN) - {} - - SerdNode start_node(SerdWriter* writer) { - const SerdNode node = ids.get(); - serd_writer_write_statement(writer, flags, nullptr, &s, &p, &node, nullptr, nullptr); - return node; - } - - void append(SerdWriter* writer, unsigned oflags, const SerdNode* value) { - // s p node - const SerdNode node = start_node(writer); - - // node rdf:first value - p = serd_node_from_string(SERD_URI, NS_RDF "first"); - flags = SERD_LIST_CONT; - serd_writer_write_statement(writer, flags|oflags, nullptr, &node, &p, value, nullptr, nullptr); - - end_node(writer, &node); - } - - void end_node(SerdWriter* writer, const SerdNode* node) { - // Prepare for next call: node rdf:rest ... - s = *node; - p = serd_node_from_string(SERD_URI, NS_RDF "rest"); - } - - void end(SerdWriter* writer) { - const SerdNode nil = serd_node_from_string(SERD_URI, NS_RDF "nil"); - serd_writer_write_statement(writer, flags, nullptr, &s, &p, &nil, nullptr, nullptr); - } - - BlankIDs& ids; - SerdNode s; - SerdNode p; - unsigned flags; -}; - -void -UndoStack::write_entry(Sratom* sratom, - SerdWriter* writer, - const SerdNode* const subject, - const UndoStack::Entry& entry) -{ - char time_str[24]; - strftime(time_str, sizeof(time_str), "%FT%T", gmtime(&entry.time)); - - // entry rdf:type ingen:UndoEntry - SerdNode p = serd_node_from_string(SERD_URI, USTR(INGEN_NS "time")); - SerdNode o = serd_node_from_string(SERD_LITERAL, USTR(time_str)); - serd_writer_write_statement(writer, SERD_ANON_CONT, nullptr, subject, &p, &o, nullptr, nullptr); - - p = serd_node_from_string(SERD_URI, USTR(INGEN_NS "events")); - - BlankIDs ids('e'); - ListContext ctx(ids, SERD_ANON_CONT, subject, &p); - - for (const LV2_Atom* atom : entry.events) { - const SerdNode node = ctx.start_node(writer); - - p = serd_node_from_string(SERD_URI, NS_RDF "first"); - ctx.flags = SERD_LIST_CONT; - sratom_write(sratom, &_map.urid_unmap_feature()->urid_unmap, SERD_LIST_CONT, - &node, &p, - atom->type, atom->size, LV2_ATOM_BODY_CONST(atom)); - - ctx.end_node(writer, &node); - } - - ctx.end(writer); -} - -void -UndoStack::save(FILE* stream, const char* name) -{ - SerdEnv* env = serd_env_new(nullptr); - serd_env_set_prefix_from_strings(env, USTR("atom"), USTR(LV2_ATOM_PREFIX)); - serd_env_set_prefix_from_strings(env, USTR("ingen"), USTR(INGEN_NS)); - serd_env_set_prefix_from_strings(env, USTR("patch"), USTR(LV2_PATCH_PREFIX)); - - const SerdNode base = serd_node_from_string(SERD_URI, USTR("ingen:/")); - SerdURI base_uri; - serd_uri_parse(base.buf, &base_uri); - - SerdWriter* writer = serd_writer_new( - SERD_TURTLE, - (SerdStyle)(SERD_STYLE_RESOLVED|SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED), - env, - &base_uri, - serd_file_sink, - stream); - - // Configure sratom to write directly to the writer (and thus the socket) - Sratom* sratom = sratom_new(&_map.urid_map_feature()->urid_map); - sratom_set_sink(sratom, - (const char*)base.buf, - (SerdStatementSink)serd_writer_write_statement, - (SerdEndSink)serd_writer_end_anon, - writer); - - SerdNode s = serd_node_from_string(SERD_BLANK, (const uint8_t*)name); - SerdNode p = serd_node_from_string(SERD_URI, USTR(INGEN_NS "entries")); - - BlankIDs ids('u'); - ListContext ctx(ids, 0, &s, &p); - for (const Entry& e : _stack) { - const SerdNode entry = ids.get(); - ctx.append(writer, SERD_ANON_O_BEGIN, &entry); - write_entry(sratom, writer, &entry, e); - serd_writer_end_anon(writer, &entry); - } - ctx.end(writer); - - sratom_free(sratom); - serd_writer_finish(writer); - serd_writer_free(writer); -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/UndoStack.hpp b/src/server/UndoStack.hpp deleted file mode 100644 index 6ce6475f..00000000 --- a/src/server/UndoStack.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_UNDOSTACK_HPP -#define INGEN_ENGINE_UNDOSTACK_HPP - -#include <ctime> -#include <deque> - -#include "ingen/AtomSink.hpp" -#include "ingen/ingen.h" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "serd/serd.h" -#include "sratom/sratom.h" - -namespace Ingen { - -class URIMap; -class URIs; - -namespace Server { - -class INGEN_API UndoStack : public AtomSink { -public: - struct Entry { - Entry(time_t time=0) : time(time) {} - - Entry(const Entry& copy) - : time(copy.time) - { - for (const LV2_Atom* ev : copy.events) { - push_event(ev); - } - } - - ~Entry() { clear(); } - - Entry& operator=(const Entry& rhs) { - clear(); - time = rhs.time; - for (const LV2_Atom* ev : rhs.events) { - push_event(ev); - } - return *this; - } - - void clear() { - for (LV2_Atom* ev : events) { - free(ev); - } - events.clear(); - } - - void push_event(const LV2_Atom* ev) { - const uint32_t size = lv2_atom_total_size(ev); - LV2_Atom* copy = (LV2_Atom*)malloc(size); - memcpy(copy, ev, size); - events.push_front(copy); - } - - time_t time; - std::deque<LV2_Atom*> events; - }; - - UndoStack(URIs& uris, URIMap& map) : _uris(uris), _map(map), _depth(0) {} - - int start_entry(); - bool write(const LV2_Atom* msg, int32_t default_id=0); - int finish_entry(); - - bool empty() const { return _stack.empty(); } - Entry pop(); - - void save(FILE* stream, const char* name="undo"); - -private: - bool ignore_later_event(const LV2_Atom* first, - const LV2_Atom* second) const; - - void write_entry(Sratom* sratom, - SerdWriter* writer, - const SerdNode* subject, - const Entry& entry); - - URIs& _uris; - URIMap& _map; - std::deque<Entry> _stack; - int _depth; -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_UNDOSTACK_HPP diff --git a/src/server/Worker.cpp b/src/server/Worker.cpp deleted file mode 100644 index 6f60250c..00000000 --- a/src/server/Worker.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/LV2Features.hpp" -#include "ingen/Log.hpp" -#include "lv2/lv2plug.in/ns/ext/worker/worker.h" - -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "LV2Block.hpp" -#include "Worker.hpp" - -namespace Ingen { -namespace Server { - -/// A message in the Worker::_requests ring -struct MessageHeader { - LV2Block* block; ///< Node this message is from - uint32_t size; ///< Size of following data - // `size' bytes of data follow here -}; - -static LV2_Worker_Status -schedule(LV2_Worker_Schedule_Handle handle, - uint32_t size, - const void* data) -{ - LV2Block* block = (LV2Block*)handle; - Engine& engine = block->parent_graph()->engine(); - - return engine.worker()->request(block, size, data); -} - -static LV2_Worker_Status -schedule_sync(LV2_Worker_Schedule_Handle handle, - uint32_t size, - const void* data) -{ - LV2Block* block = (LV2Block*)handle; - Engine& engine = block->parent_graph()->engine(); - - return engine.sync_worker()->request(block, size, data); -} - -LV2_Worker_Status -Worker::request(LV2Block* block, - uint32_t size, - const void* data) -{ - if (_synchronous) { - return block->work(size, data); - } - - Engine& engine = block->parent_graph()->engine(); - if (_requests.write_space() < sizeof(MessageHeader) + size) { - engine.log().error("Work request ring overflow\n"); - return LV2_WORKER_ERR_NO_SPACE; - } - - const MessageHeader msg = { block, size }; - if (_requests.write(sizeof(msg), &msg) != sizeof(msg)) { - engine.log().error("Error writing header to work request ring\n"); - return LV2_WORKER_ERR_UNKNOWN; - } - if (_requests.write(size, data) != size) { - engine.log().error("Error writing body to work request ring\n"); - return LV2_WORKER_ERR_UNKNOWN; - } - - _sem.post(); - - return LV2_WORKER_SUCCESS; -} - -SPtr<LV2_Feature> -Worker::Schedule::feature(World* world, Node* n) -{ - LV2Block* block = dynamic_cast<LV2Block*>(n); - if (!block) { - return SPtr<LV2_Feature>(); - } - - LV2_Worker_Schedule* data = (LV2_Worker_Schedule*)malloc( - sizeof(LV2_Worker_Schedule)); - data->handle = block; - data->schedule_work = synchronous ? schedule_sync : schedule; - - LV2_Feature* f = (LV2_Feature*)malloc(sizeof(LV2_Feature)); - f->URI = LV2_WORKER__schedule; - f->data = data; - - return SPtr<LV2_Feature>(f, &free_feature); -} - -Worker::Worker(Log& log, uint32_t buffer_size, bool synchronous) - : _schedule(new Schedule(synchronous)) - , _log(log) - , _sem(0) - , _requests(buffer_size) - , _responses(buffer_size) - , _buffer((uint8_t*)malloc(buffer_size)) - , _buffer_size(buffer_size) - , _thread(nullptr) - , _exit_flag(false) - , _synchronous(synchronous) -{ - if (!synchronous) { - _thread = new std::thread(&Worker::run, this); - } -} - -Worker::~Worker() -{ - _exit_flag = true; - _sem.post(); - if (_thread) { - _thread->join(); - delete _thread; - } - free(_buffer); -} - -void -Worker::run() -{ - while (_sem.wait() && !_exit_flag) { - MessageHeader msg; - if (_requests.read_space() > sizeof(msg)) { - if (_requests.read(sizeof(msg), &msg) != sizeof(msg)) { - _log.error("Error reading header from work request ring\n"); - continue; - } - - if (msg.size >= _buffer_size - sizeof(msg)) { - _log.error("Corrupt work request ring\n"); - return; - } - - if (_requests.read(msg.size, _buffer) != msg.size) { - _log.error("Error reading body from work request ring\n"); - continue; - } - - msg.block->work(msg.size, _buffer); - } - } -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/Worker.hpp b/src/server/Worker.hpp deleted file mode 100644 index 0a3fdeaf..00000000 --- a/src/server/Worker.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_WORKER_HPP -#define INGEN_ENGINE_WORKER_HPP - -#include <thread> - -#include "ingen/LV2Features.hpp" -#include "lv2/lv2plug.in/ns/ext/worker/worker.h" -#include "raul/RingBuffer.hpp" -#include "raul/Semaphore.hpp" - -namespace Ingen { - -class Log; - -namespace Server { - -class LV2Block; - -class Worker -{ -public: - Worker(Log& log, uint32_t buffer_size, bool synchronous=false); - ~Worker(); - - struct Schedule : public LV2Features::Feature { - Schedule(bool sync) : synchronous(sync) {} - - const char* uri() const { return LV2_WORKER__schedule; } - - SPtr<LV2_Feature> feature(World* world, Node* n); - - const bool synchronous; - }; - - LV2_Worker_Status request(LV2Block* block, - uint32_t size, - const void* data); - - SPtr<Schedule> schedule_feature() { return _schedule; } - -private: - SPtr<Schedule> _schedule; - - Log& _log; - Raul::Semaphore _sem; - Raul::RingBuffer _requests; - Raul::RingBuffer _responses; - uint8_t* const _buffer; - const uint32_t _buffer_size; - std::thread* _thread; - bool _exit_flag; - bool _synchronous; - - void run(); -}; - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_WORKER_HPP diff --git a/src/server/events.hpp b/src/server/events.hpp deleted file mode 100644 index 5f77b431..00000000 --- a/src/server/events.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_EVENTS_HPP -#define INGEN_ENGINE_EVENTS_HPP - -#include "events/Connect.hpp" -#include "events/Copy.hpp" -#include "events/CreateBlock.hpp" -#include "events/CreateGraph.hpp" -#include "events/CreatePort.hpp" -#include "events/Delete.hpp" -#include "events/Delta.hpp" -#include "events/Disconnect.hpp" -#include "events/DisconnectAll.hpp" -#include "events/Get.hpp" -#include "events/Mark.hpp" -#include "events/Move.hpp" -#include "events/SetPortValue.hpp" -#include "events/Undo.hpp" - -#endif // INGEN_ENGINE_EVENTS_HPP diff --git a/src/server/events/Connect.cpp b/src/server/events/Connect.cpp deleted file mode 100644 index 8937b327..00000000 --- a/src/server/events/Connect.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Store.hpp" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" - -#include "ArcImpl.hpp" -#include "Broadcaster.hpp" -#include "BufferFactory.hpp" -#include "Connect.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "InputPort.hpp" -#include "PortImpl.hpp" -#include "PreProcessContext.hpp" -#include "internals/BlockDelay.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -Connect::Connect(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Connect& msg) - : Event(engine, client, msg.seq, timestamp) - , _msg(msg) - , _graph(nullptr) - , _head(nullptr) -{} - -bool -Connect::pre_process(PreProcessContext& ctx) -{ - std::lock_guard<Store::Mutex> lock(_engine.store()->mutex()); - - Node* tail = _engine.store()->get(_msg.tail); - if (!tail) { - return Event::pre_process_done(Status::NOT_FOUND, _msg.tail); - } - - Node* head = _engine.store()->get(_msg.head); - if (!head) { - return Event::pre_process_done(Status::NOT_FOUND, _msg.head); - } - - PortImpl* tail_output = dynamic_cast<PortImpl*>(tail); - _head = dynamic_cast<InputPort*>(head); - if (!tail_output || !_head) { - return Event::pre_process_done(Status::BAD_REQUEST, _msg.head); - } - - BlockImpl* const tail_block = tail_output->parent_block(); - BlockImpl* const head_block = _head->parent_block(); - if (!tail_block || !head_block) { - return Event::pre_process_done(Status::PARENT_NOT_FOUND, _msg.head); - } - - if (tail_block->parent() != head_block->parent() - && tail_block != head_block->parent() - && tail_block->parent() != head_block) { - return Event::pre_process_done(Status::PARENT_DIFFERS, _msg.head); - } - - if (!ArcImpl::can_connect(tail_output, _head)) { - return Event::pre_process_done(Status::TYPE_MISMATCH, _msg.head); - } - - if (tail_block->parent_graph() != head_block->parent_graph()) { - // Arc to a graph port from inside the graph - assert(tail_block->parent() == head_block || head_block->parent() == tail_block); - if (tail_block->parent() == head_block) { - _graph = dynamic_cast<GraphImpl*>(head_block); - } else { - _graph = dynamic_cast<GraphImpl*>(tail_block); - } - } else if (tail_block == head_block && dynamic_cast<GraphImpl*>(tail_block)) { - // Arc from a graph input to a graph output (pass through) - _graph = dynamic_cast<GraphImpl*>(tail_block); - } else { - // Normal arc between blocks with the same parent - _graph = tail_block->parent_graph(); - } - - if (_graph->has_arc(tail_output, _head)) { - return Event::pre_process_done(Status::EXISTS, _msg.head); - } - - _arc = SPtr<ArcImpl>(new ArcImpl(tail_output, _head)); - - /* Need to be careful about graph port arcs here and adding a - block's parent as a dependant/provider, or adding a graph as its own - provider... - */ - if (tail_block != head_block && tail_block->parent() == head_block->parent()) { - // Connection is between blocks inside a graph, compile graph - - // The tail block is now a dependency (provider) of the head block - head_block->providers().insert(tail_block); - - if (!dynamic_cast<Internals::BlockDelayNode*>(tail_block)) { - /* Arcs leaving a delay node are ignored for the purposes of - compilation, since the output is from the previous cycle and - does not affect execution order. Otherwise, the head block is - now a dependant of the head block. */ - tail_block->dependants().insert(head_block); - } - - if (ctx.must_compile(*_graph)) { - if (!(_compiled_graph = compile(*_engine.maid(), *_graph))) { - head_block->providers().erase(tail_block); - tail_block->dependants().erase(head_block); - return Event::pre_process_done(Status::COMPILATION_FAILED); - } - } - } - - _graph->add_arc(_arc); - _head->increment_num_arcs(); - - if (!_head->is_driver_port()) { - BufferFactory& bufs = *_engine.buffer_factory(); - _voices = bufs.maid().make_managed<PortImpl::Voices>(_head->poly()); - _head->pre_get_buffers(bufs, _voices, _head->poly()); - } - - tail_output->inherit_neighbour(_head, _tail_remove, _tail_add); - _head->inherit_neighbour(tail_output, _head_remove, _head_add); - - return Event::pre_process_done(Status::SUCCESS); -} - -void -Connect::execute(RunContext& context) -{ - if (_status == Status::SUCCESS) { - _head->add_arc(context, *_arc.get()); - if (!_head->is_driver_port()) { - _head->set_voices(context, std::move(_voices)); - } - _head->connect_buffers(); - if (_compiled_graph) { - _graph->set_compiled_graph(std::move(_compiled_graph)); - } - } -} - -void -Connect::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS) { - _engine.broadcaster()->message(_msg); - if (!_tail_remove.empty() || !_tail_add.empty()) { - _engine.broadcaster()->delta( - path_to_uri(_msg.tail), _tail_remove, _tail_add); - } - if (!_tail_remove.empty() || !_tail_add.empty()) { - _engine.broadcaster()->delta( - path_to_uri(_msg.tail), _tail_remove, _tail_add); - } - } -} - -void -Connect::undo(Interface& target) -{ - target.disconnect(_msg.tail, _msg.head); -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/Connect.hpp b/src/server/events/Connect.hpp deleted file mode 100644 index 8a42b984..00000000 --- a/src/server/events/Connect.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_CONNECT_HPP -#define INGEN_EVENTS_CONNECT_HPP - -#include "raul/Path.hpp" - -#include "CompiledGraph.hpp" -#include "Event.hpp" -#include "PortImpl.hpp" -#include "types.hpp" - -namespace Raul { -template <typename T> class Array; -} - -namespace Ingen { -namespace Server { - -class ArcImpl; -class GraphImpl; -class InputPort; - -namespace Events { - -/** Make an Arc between two Ports. - * - * \ingroup engine - */ -class Connect : public Event -{ -public: - Connect(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Connect& msg); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - void undo(Interface& target); - -private: - const Ingen::Connect _msg; - GraphImpl* _graph; - InputPort* _head; - MPtr<CompiledGraph> _compiled_graph; - SPtr<ArcImpl> _arc; - MPtr<PortImpl::Voices> _voices; - Properties _tail_remove; - Properties _tail_add; - Properties _head_remove; - Properties _head_add; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_CONNECT_HPP diff --git a/src/server/events/Copy.cpp b/src/server/events/Copy.cpp deleted file mode 100644 index fc9d40f7..00000000 --- a/src/server/events/Copy.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Parser.hpp" -#include "ingen/Serialiser.hpp" -#include "ingen/Store.hpp" -#include "raul/Path.hpp" - -#include "BlockImpl.hpp" -#include "Broadcaster.hpp" -#include "Engine.hpp" -#include "EnginePort.hpp" -#include "GraphImpl.hpp" -#include "PreProcessContext.hpp" -#include "events/Copy.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -Copy::Copy(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Copy& msg) - : Event(engine, client, msg.seq, timestamp) - , _msg(msg) - , _old_block(nullptr) - , _parent(nullptr) - , _block(nullptr) -{} - -bool -Copy::pre_process(PreProcessContext& ctx) -{ - std::lock_guard<Store::Mutex> lock(_engine.store()->mutex()); - - if (uri_is_path(_msg.old_uri)) { - // Old URI is a path within the engine - const Raul::Path old_path = uri_to_path(_msg.old_uri); - - // Find the old node - const Store::iterator i = _engine.store()->find(old_path); - if (i == _engine.store()->end()) { - return Event::pre_process_done(Status::NOT_FOUND, old_path); - } - - // Ensure it is a block (ports are not supported for now) - if (!(_old_block = dynamic_ptr_cast<BlockImpl>(i->second))) { - return Event::pre_process_done(Status::BAD_OBJECT_TYPE, old_path); - } - - if (uri_is_path(_msg.new_uri)) { - // Copy to path within the engine - return engine_to_engine(ctx); - } else if (_msg.new_uri.scheme() == "file") { - // Copy to filesystem path (i.e. save) - return engine_to_filesystem(ctx); - } else { - return Event::pre_process_done(Status::BAD_REQUEST); - } - } else if (_msg.old_uri.scheme() == "file") { - if (uri_is_path(_msg.new_uri)) { - return filesystem_to_engine(ctx); - } else { - // Ingen is not your file manager - return Event::pre_process_done(Status::BAD_REQUEST); - } - } - - return Event::pre_process_done(Status::BAD_URI); -} - -bool -Copy::engine_to_engine(PreProcessContext& ctx) -{ - // Only support a single source for now - const Raul::Path new_path = uri_to_path(_msg.new_uri); - if (!Raul::Symbol::is_valid(new_path.symbol())) { - return Event::pre_process_done(Status::BAD_REQUEST); - } - - // Ensure the new node doesn't already exist - if (_engine.store()->find(new_path) != _engine.store()->end()) { - return Event::pre_process_done(Status::EXISTS, new_path); - } - - // Find new parent graph - const Raul::Path parent_path = new_path.parent(); - const Store::iterator p = _engine.store()->find(parent_path); - if (p == _engine.store()->end()) { - return Event::pre_process_done(Status::NOT_FOUND, parent_path); - } - if (!(_parent = dynamic_cast<GraphImpl*>(p->second.get()))) { - return Event::pre_process_done(Status::BAD_OBJECT_TYPE, parent_path); - } - - // Create new block - if (!(_block = dynamic_cast<BlockImpl*>( - _old_block->duplicate(_engine, Raul::Symbol(new_path.symbol()), _parent)))) { - return Event::pre_process_done(Status::INTERNAL_ERROR); - } - - _block->activate(*_engine.buffer_factory()); - - // Add block to the store and the graph's pre-processor only block list - _parent->add_block(*_block); - _engine.store()->add(_block); - - // Compile graph with new block added for insertion in audio thread - _compiled_graph = ctx.maybe_compile(*_engine.maid(), *_parent); - - return Event::pre_process_done(Status::SUCCESS); -} - -static bool -ends_with(const std::string& str, const std::string& end) -{ - if (str.length() >= end.length()) { - return !str.compare(str.length() - end.length(), end.length(), end); - } - return false; -} - -bool -Copy::engine_to_filesystem(PreProcessContext& ctx) -{ - // Ensure source is a graph - SPtr<GraphImpl> graph = dynamic_ptr_cast<GraphImpl>(_old_block); - if (!graph) { - return Event::pre_process_done(Status::BAD_OBJECT_TYPE, _msg.old_uri); - } - - if (!_engine.world()->serialiser()) { - return Event::pre_process_done(Status::INTERNAL_ERROR); - } - - std::lock_guard<std::mutex> lock(_engine.world()->rdf_mutex()); - - if (ends_with(_msg.new_uri, ".ingen") || ends_with(_msg.new_uri, ".ingen/")) { - _engine.world()->serialiser()->write_bundle(graph, URI(_msg.new_uri)); - } else { - _engine.world()->serialiser()->start_to_file(graph->path(), _msg.new_uri); - _engine.world()->serialiser()->serialise(graph); - _engine.world()->serialiser()->finish(); - } - - return Event::pre_process_done(Status::SUCCESS); -} - -bool -Copy::filesystem_to_engine(PreProcessContext& ctx) -{ - if (!_engine.world()->parser()) { - return Event::pre_process_done(Status::INTERNAL_ERROR); - } - - std::lock_guard<std::mutex> lock(_engine.world()->rdf_mutex()); - - // Old URI is a filesystem path and new URI is a path within the engine - const std::string src_path(_msg.old_uri.path()); - const Raul::Path dst_path = uri_to_path(_msg.new_uri); - boost::optional<Raul::Path> dst_parent; - boost::optional<Raul::Symbol> dst_symbol; - if (!dst_path.is_root()) { - dst_parent = dst_path.parent(); - dst_symbol = Raul::Symbol(dst_path.symbol()); - } - - _engine.world()->parser()->parse_file( - _engine.world(), _engine.world()->interface().get(), src_path, - dst_parent, dst_symbol); - - return Event::pre_process_done(Status::SUCCESS); -} - -void -Copy::execute(RunContext& context) -{ - if (_block && _compiled_graph) { - _parent->set_compiled_graph(std::move(_compiled_graph)); - } -} - -void -Copy::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS) { - _engine.broadcaster()->message(_msg); - } -} - -void -Copy::undo(Interface& target) -{ - if (uri_is_path(_msg.new_uri)) { - target.del(_msg.new_uri); - } -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/Copy.hpp b/src/server/events/Copy.hpp deleted file mode 100644 index 5216b56e..00000000 --- a/src/server/events/Copy.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_COPY_HPP -#define INGEN_EVENTS_COPY_HPP - -#include <list> - -#include "ingen/Store.hpp" -#include "raul/Path.hpp" - -#include "CompiledGraph.hpp" -#include "Event.hpp" - -namespace Ingen { -namespace Server { - -class BlockImpl; -class GraphImpl; - -namespace Events { - -/** Copy a graph object to a new path. - * \ingroup engine - */ -class Copy : public Event -{ -public: - Copy(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Copy& msg); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - void undo(Interface& target); - -private: - bool engine_to_engine(PreProcessContext& ctx); - bool engine_to_filesystem(PreProcessContext& ctx); - bool filesystem_to_engine(PreProcessContext& ctx); - - const Ingen::Copy _msg; - SPtr<BlockImpl> _old_block; - GraphImpl* _parent; - BlockImpl* _block; - MPtr<CompiledGraph> _compiled_graph; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_COPY_HPP diff --git a/src/server/events/CreateBlock.cpp b/src/server/events/CreateBlock.cpp deleted file mode 100644 index d678bea3..00000000 --- a/src/server/events/CreateBlock.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Forge.hpp" -#include "ingen/Store.hpp" -#include "ingen/URIs.hpp" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" - -#include "BlockFactory.hpp" -#include "BlockImpl.hpp" -#include "Broadcaster.hpp" -#include "CreateBlock.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "PreProcessContext.hpp" -#include "LV2Block.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -CreateBlock::CreateBlock(Engine& engine, - SPtr<Interface> client, - int32_t id, - SampleCount timestamp, - const Raul::Path& path, - Properties& properties) - : Event(engine, client, id, timestamp) - , _path(path) - , _properties(properties) - , _graph(nullptr) - , _block(nullptr) -{} - -bool -CreateBlock::pre_process(PreProcessContext& ctx) -{ - typedef Properties::const_iterator iterator; - - const Ingen::URIs& uris = _engine.world()->uris(); - const SPtr<Store> store = _engine.store(); - - // Check sanity of target path - if (_path.is_root()) { - return Event::pre_process_done(Status::BAD_URI, _path); - } else if (store->get(_path)) { - return Event::pre_process_done(Status::EXISTS, _path); - } else if (!(_graph = dynamic_cast<GraphImpl*>(store->get(_path.parent())))) { - return Event::pre_process_done(Status::PARENT_NOT_FOUND, _path.parent()); - } - - // Map old ingen:prototype to new lv2:prototype - auto range = _properties.equal_range(uris.ingen_prototype); - for (auto i = range.first; i != range.second;) { - const auto value = i->second; - auto next = i; - next = _properties.erase(i); - _properties.emplace(uris.lv2_prototype, value); - i = next; - } - - // Get prototype - iterator t = _properties.find(uris.lv2_prototype); - if (t == _properties.end() || !uris.forge.is_uri(t->second)) { - // Missing/invalid prototype - return Event::pre_process_done(Status::BAD_REQUEST); - } - - const URI prototype(uris.forge.str(t->second, false)); - - // Find polyphony - const iterator p = _properties.find(uris.ingen_polyphonic); - const bool polyphonic = (p != _properties.end() && - p->second.type() == uris.forge.Bool && - p->second.get<int32_t>()); - - // Find and instantiate/duplicate prototype (plugin/existing node) - if (uri_is_path(prototype)) { - // Prototype is an existing block - BlockImpl* const ancestor = dynamic_cast<BlockImpl*>( - store->get(uri_to_path(prototype))); - if (!ancestor) { - return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype); - } else if (!(_block = ancestor->duplicate( - _engine, Raul::Symbol(_path.symbol()), _graph))) { - return Event::pre_process_done(Status::CREATION_FAILED, _path); - } - - /* Replace prototype with the ancestor's. This is less informative, - but the client expects an actual LV2 plugin as prototype. */ - _properties.erase(uris.ingen_prototype); - _properties.erase(uris.lv2_prototype); - _properties.emplace(uris.lv2_prototype, - uris.forge.make_urid(ancestor->plugin()->uri())); - } else { - // Prototype is a plugin - PluginImpl* const plugin = _engine.block_factory()->plugin(prototype); - if (!plugin) { - return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype); - } - - // Load state from directory if given in properties - LilvState* state = nullptr; - auto s = _properties.find(uris.state_state); - if (s != _properties.end() && s->second.type() == uris.forge.Path) { - state = LV2Block::load_state( - _engine.world(), FilePath(s->second.ptr<char>())); - } - - // Instantiate plugin - if (!(_block = plugin->instantiate(*_engine.buffer_factory(), - Raul::Symbol(_path.symbol()), - polyphonic, - _graph, - _engine, - state))) { - return Event::pre_process_done(Status::CREATION_FAILED, _path); - } - } - - // Activate block - _block->properties().insert(_properties.begin(), _properties.end()); - _block->activate(*_engine.buffer_factory()); - - // Add block to the store and the graph's pre-processor only block list - _graph->add_block(*_block); - store->add(_block); - - /* Compile graph with new block added for insertion in audio thread - TODO: Since the block is not connected at this point, a full compilation - could be avoided and the block simply appended. */ - _compiled_graph = ctx.maybe_compile(*_engine.maid(), *_graph); - - _update.put_block(_block); - - return Event::pre_process_done(Status::SUCCESS); -} - -void -CreateBlock::execute(RunContext& context) -{ - if (_status == Status::SUCCESS && _compiled_graph) { - _graph->set_compiled_graph(std::move(_compiled_graph)); - } -} - -void -CreateBlock::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS) { - _update.send(*_engine.broadcaster()); - } -} - -void -CreateBlock::undo(Interface& target) -{ - target.del(_block->uri()); -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/CreateBlock.hpp b/src/server/events/CreateBlock.hpp deleted file mode 100644 index 0a29e68c..00000000 --- a/src/server/events/CreateBlock.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_CREATEBLOCK_HPP -#define INGEN_EVENTS_CREATEBLOCK_HPP - -#include "ingen/Resource.hpp" - -#include "ClientUpdate.hpp" -#include "CompiledGraph.hpp" -#include "Event.hpp" - -namespace Ingen { -namespace Server { - -class BlockImpl; -class GraphImpl; - -namespace Events { - -/** An event to load a Block and insert it into a Graph. - * - * \ingroup engine - */ -class CreateBlock : public Event -{ -public: - CreateBlock(Engine& engine, - SPtr<Interface> client, - int32_t id, - SampleCount timestamp, - const Raul::Path& path, - Properties& properties); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - void undo(Interface& target); - -private: - Raul::Path _path; - Properties& _properties; - ClientUpdate _update; - GraphImpl* _graph; - BlockImpl* _block; - MPtr<CompiledGraph> _compiled_graph; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_CREATEBLOCK_HPP diff --git a/src/server/events/CreateGraph.cpp b/src/server/events/CreateGraph.cpp deleted file mode 100644 index 390fdd9a..00000000 --- a/src/server/events/CreateGraph.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Forge.hpp" -#include "ingen/Store.hpp" -#include "ingen/URIs.hpp" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" - -#include "Broadcaster.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "PreProcessContext.hpp" -#include "events/CreateGraph.hpp" -#include "events/CreatePort.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -CreateGraph::CreateGraph(Engine& engine, - SPtr<Interface> client, - int32_t id, - SampleCount timestamp, - const Raul::Path& path, - const Properties& properties) - : Event(engine, client, id, timestamp) - , _path(path) - , _properties(properties) - , _graph(nullptr) - , _parent(nullptr) -{} - -CreateGraph::~CreateGraph() -{ - for (Event* ev : _child_events) { - delete ev; - } -} - -void -CreateGraph::build_child_events() -{ - const Ingen::URIs& uris = _engine.world()->uris(); - - // Properties common to both ports - Properties control_properties; - control_properties.put(uris.atom_bufferType, uris.atom_Sequence); - control_properties.put(uris.atom_supports, uris.patch_Message); - control_properties.put(uris.lv2_designation, uris.lv2_control); - control_properties.put(uris.lv2_portProperty, uris.lv2_connectionOptional); - control_properties.put(uris.rdf_type, uris.atom_AtomPort); - control_properties.put(uris.rsz_minimumSize, uris.forge.make(4096)); - - // Add control port (message receive) - Properties in_properties(control_properties); - in_properties.put(uris.lv2_index, uris.forge.make(0)); - in_properties.put(uris.lv2_name, uris.forge.alloc("Control")); - in_properties.put(uris.rdf_type, uris.lv2_InputPort); - in_properties.put(uris.ingen_canvasX, uris.forge.make(32.0f), - Resource::Graph::EXTERNAL); - in_properties.put(uris.ingen_canvasY, uris.forge.make(32.0f), - Resource::Graph::EXTERNAL); - - _child_events.push_back( - new Events::CreatePort( - _engine, _request_client, -1, _time, - _path.child(Raul::Symbol("control")), - in_properties)); - - // Add notify port (message respond) - Properties out_properties(control_properties); - out_properties.put(uris.lv2_index, uris.forge.make(1)); - out_properties.put(uris.lv2_name, uris.forge.alloc("Notify")); - out_properties.put(uris.rdf_type, uris.lv2_OutputPort); - out_properties.put(uris.ingen_canvasX, uris.forge.make(128.0f), - Resource::Graph::EXTERNAL); - out_properties.put(uris.ingen_canvasY, uris.forge.make(32.0f), - Resource::Graph::EXTERNAL); - - _child_events.push_back( - new Events::CreatePort(_engine, _request_client, -1, _time, - _path.child(Raul::Symbol("notify")), - out_properties)); -} - -bool -CreateGraph::pre_process(PreProcessContext& ctx) -{ - if (_engine.store()->get(_path)) { - return Event::pre_process_done(Status::EXISTS, _path); - } - - if (!_path.is_root()) { - const Raul::Path up(_path.parent()); - if (!(_parent = dynamic_cast<GraphImpl*>(_engine.store()->get(up)))) { - return Event::pre_process_done(Status::PARENT_NOT_FOUND, up); - } - } - - const Ingen::URIs& uris = _engine.world()->uris(); - - typedef Properties::const_iterator iterator; - - uint32_t ext_poly = 1; - uint32_t int_poly = 1; - iterator p = _properties.find(uris.ingen_polyphony); - if (p != _properties.end() && p->second.type() == uris.forge.Int) { - int_poly = p->second.get<int32_t>(); - } - - if (int_poly < 1 || int_poly > 128) { - return Event::pre_process_done(Status::INVALID_POLY, _path); - } - - if (!_parent || int_poly == _parent->internal_poly()) { - ext_poly = int_poly; - } - - const Raul::Symbol symbol(_path.is_root() ? "graph" : _path.symbol()); - - // Get graph prototype - iterator t = _properties.find(uris.lv2_prototype); - if (t == _properties.end()) { - t = _properties.find(uris.lv2_prototype); - } - - if (t != _properties.end() && - uris.forge.is_uri(t->second) && - URI::is_valid(uris.forge.str(t->second, false)) && - uri_is_path(URI(uris.forge.str(t->second, false)))) { - // Create a duplicate of an existing graph - const URI prototype(uris.forge.str(t->second, false)); - GraphImpl* ancestor = dynamic_cast<GraphImpl*>( - _engine.store()->get(uri_to_path(prototype))); - if (!ancestor) { - return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype); - } else if (!(_graph = dynamic_cast<GraphImpl*>( - ancestor->duplicate(_engine, symbol, _parent)))) { - return Event::pre_process_done(Status::CREATION_FAILED, _path); - } - } else { - // Create a new graph - _graph = new GraphImpl(_engine, symbol, ext_poly, _parent, - _engine.sample_rate(), int_poly); - _graph->add_property(uris.rdf_type, uris.ingen_Graph.urid); - _graph->add_property(uris.rdf_type, - Property(uris.ingen_Block, - Resource::Graph::EXTERNAL)); - } - - _graph->set_properties(_properties); - - if (_parent) { - // Add graph to parent - _parent->add_block(*_graph); - if (_parent->enabled()) { - _graph->enable(); - } - _compiled_graph = ctx.maybe_compile(*_engine.maid(), *_parent); - } - - _graph->activate(*_engine.buffer_factory()); - - // Insert into store and build update to send to clients - _engine.store()->add(_graph); - _update.put_graph(_graph); - for (BlockImpl& block : _graph->blocks()) { - _engine.store()->add(&block); - } - - // Build and pre-process child events to create standard ports - build_child_events(); - for (Event* ev : _child_events) { - ev->pre_process(ctx); - } - - return Event::pre_process_done(Status::SUCCESS); -} - -void -CreateGraph::execute(RunContext& context) -{ - if (_graph) { - if (_parent) { - if (_compiled_graph) { - _parent->set_compiled_graph(std::move(_compiled_graph)); - } - } else { - _engine.set_root_graph(_graph); - _graph->enable(); - } - - for (Event* ev : _child_events) { - ev->execute(context); - } - } -} - -void -CreateGraph::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS) { - _update.send(*_engine.broadcaster()); - } - - if (_graph) { - for (Event* ev : _child_events) { - ev->post_process(); - } - } -} - -void -CreateGraph::undo(Interface& target) -{ - target.del(_graph->uri()); -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/CreateGraph.hpp b/src/server/events/CreateGraph.hpp deleted file mode 100644 index 564d553b..00000000 --- a/src/server/events/CreateGraph.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_CREATEGRAPH_HPP -#define INGEN_EVENTS_CREATEGRAPH_HPP - -#include <list> - -#include "ingen/Resource.hpp" - -#include "CompiledGraph.hpp" -#include "Event.hpp" -#include "events/Get.hpp" - -namespace Ingen { -namespace Server { - -class GraphImpl; - -namespace Events { - -/** Creates a new Graph. - * - * \ingroup engine - */ -class CreateGraph : public Event -{ -public: - CreateGraph(Engine& engine, - SPtr<Interface> client, - int32_t id, - SampleCount timestamp, - const Raul::Path& path, - const Properties& properties); - - ~CreateGraph(); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - void undo(Interface& target); - - GraphImpl* graph() { return _graph; } - -private: - void build_child_events(); - - const Raul::Path _path; - Properties _properties; - ClientUpdate _update; - GraphImpl* _graph; - GraphImpl* _parent; - MPtr<CompiledGraph> _compiled_graph; - std::list<Event*> _child_events; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_CREATEGRAPH_HPP diff --git a/src/server/events/CreatePort.cpp b/src/server/events/CreatePort.cpp deleted file mode 100644 index e17b8b01..00000000 --- a/src/server/events/CreatePort.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <utility> - -#include "ingen/Atom.hpp" -#include "ingen/Store.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/URIs.hpp" -#include "raul/Array.hpp" -#include "raul/Path.hpp" - -#include "Broadcaster.hpp" -#include "BufferFactory.hpp" -#include "CreatePort.hpp" -#include "Driver.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "PortImpl.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -CreatePort::CreatePort(Engine& engine, - SPtr<Interface> client, - int32_t id, - SampleCount timestamp, - const Raul::Path& path, - const Properties& properties) - : Event(engine, client, id, timestamp) - , _path(path) - , _port_type(PortType::UNKNOWN) - , _buf_type(0) - , _graph(nullptr) - , _graph_port(nullptr) - , _engine_port(nullptr) - , _properties(properties) -{ - const Ingen::URIs& uris = _engine.world()->uris(); - - typedef Properties::const_iterator Iterator; - typedef std::pair<Iterator, Iterator> Range; - - const Range types = properties.equal_range(uris.rdf_type); - for (Iterator i = types.first; i != types.second; ++i) { - const Atom& type = i->second; - if (type == uris.lv2_AudioPort) { - _port_type = PortType::AUDIO; - } else if (type == uris.lv2_ControlPort) { - _port_type = PortType::CONTROL; - } else if (type == uris.lv2_CVPort) { - _port_type = PortType::CV; - } else if (type == uris.atom_AtomPort) { - _port_type = PortType::ATOM; - } else if (type == uris.lv2_InputPort) { - _flow = Flow::INPUT; - } else if (type == uris.lv2_OutputPort) { - _flow = Flow::OUTPUT; - } - } - - const Range buffer_types = properties.equal_range(uris.atom_bufferType); - for (Iterator i = buffer_types.first; i != buffer_types.second; ++i) { - if (uris.forge.is_uri(i->second)) { - _buf_type = _engine.world()->uri_map().map_uri( - uris.forge.str(i->second, false)); - } - } -} - -bool -CreatePort::pre_process(PreProcessContext& ctx) -{ - if (_port_type == PortType::UNKNOWN) { - return Event::pre_process_done(Status::UNKNOWN_TYPE, _path); - } else if (!_flow) { - return Event::pre_process_done(Status::UNKNOWN_TYPE, _path); - } else if (_path.is_root()) { - return Event::pre_process_done(Status::BAD_URI, _path); - } else if (_engine.store()->get(_path)) { - return Event::pre_process_done(Status::EXISTS, _path); - } - - const Raul::Path parent_path = _path.parent(); - Node* const parent = _engine.store()->get(parent_path); - if (!parent) { - return Event::pre_process_done(Status::PARENT_NOT_FOUND, parent_path); - } else if (!(_graph = dynamic_cast<GraphImpl*>(parent))) { - return Event::pre_process_done(Status::INVALID_PARENT, parent_path); - } else if (!_graph->parent() && _engine.activated() && - !_engine.driver()->dynamic_ports()) { - return Event::pre_process_done(Status::CREATION_FAILED, _path); - } - - const URIs& uris = _engine.world()->uris(); - BufferFactory& bufs = *_engine.buffer_factory(); - const uint32_t buf_size = bufs.default_size(_buf_type); - const int32_t old_n_ports = _graph->num_ports_non_rt(); - - typedef Properties::const_iterator PropIter; - - PropIter index_i = _properties.find(uris.lv2_index); - int32_t index = 0; - if (index_i != _properties.end()) { - // Ensure given index is sane and not taken - if (index_i->second.type() != uris.forge.Int) { - return Event::pre_process_done(Status::BAD_REQUEST); - } - - index = index_i->second.get<int32_t>(); - if (_graph->has_port_with_index(index)) { - return Event::pre_process_done(Status::BAD_INDEX); - } - } else { - // No index given, append - index = old_n_ports; - index_i = _properties.emplace(uris.lv2_index, - _engine.world()->forge().make(index)); - } - - const PropIter poly_i = _properties.find(uris.ingen_polyphonic); - const bool polyphonic = (poly_i != _properties.end() && - poly_i->second.type() == uris.forge.Bool && - poly_i->second.get<int32_t>()); - - // Create 0 value if the port requires one - Atom value; - if (_port_type == PortType::CONTROL || _port_type == PortType::CV) { - value = bufs.forge().make(0.0f); - } - - // Create port - _graph_port = new DuplexPort(bufs, _graph, Raul::Symbol(_path.symbol()), - index, - polyphonic, - _port_type, _buf_type, buf_size, - value, _flow == Flow::OUTPUT); - assert((_flow == Flow::OUTPUT && _graph_port->is_output()) || - (_flow == Flow::INPUT && _graph_port->is_input())); - _graph_port->properties().insert(_properties.begin(), _properties.end()); - - _engine.store()->add(_graph_port); - if (_flow == Flow::OUTPUT) { - _graph->add_output(*_graph_port); - } else { - _graph->add_input(*_graph_port); - } - - if (!_graph->parent()) { - _engine_port = _engine.driver()->create_port(_graph_port); - } - - _ports_array = bufs.maid().make_managed<GraphImpl::Ports>( - old_n_ports + 1, nullptr); - - _update = _graph_port->properties(); - - assert(_graph_port->index() == (uint32_t)index_i->second.get<int32_t>()); - assert(_graph->num_ports_non_rt() == (uint32_t)old_n_ports + 1); - assert(_ports_array->size() == _graph->num_ports_non_rt()); - assert(_graph_port->index() < _ports_array->size()); - return Event::pre_process_done(Status::SUCCESS); -} - -void -CreatePort::execute(RunContext& context) -{ - if (_status == Status::SUCCESS) { - const MPtr<GraphImpl::Ports>& old_ports = _graph->external_ports(); - if (old_ports) { - for (uint32_t i = 0; i < old_ports->size(); ++i) { - const auto* const old_port = (*old_ports)[i]; - assert(old_port->index() < _ports_array->size()); - (*_ports_array)[old_port->index()] = (*old_ports)[i]; - } - } - assert(!(*_ports_array)[_graph_port->index()]); - (*_ports_array)[_graph_port->index()] = _graph_port; - _graph->set_external_ports(std::move(_ports_array)); - - if (_engine_port) { - _engine.driver()->add_port(context, _engine_port); - } - } -} - -void -CreatePort::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS) { - _engine.broadcaster()->put(path_to_uri(_path), _update); - } -} - -void -CreatePort::undo(Interface& target) -{ - target.del(_graph_port->uri()); -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/CreatePort.hpp b/src/server/events/CreatePort.hpp deleted file mode 100644 index a2ea7682..00000000 --- a/src/server/events/CreatePort.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_CREATEPORT_HPP -#define INGEN_EVENTS_CREATEPORT_HPP - -#include <boost/optional.hpp> - -#include "ingen/Resource.hpp" -#include "lv2/lv2plug.in/ns/ext/urid/urid.h" -#include "raul/Array.hpp" -#include "raul/Path.hpp" - -#include "BlockImpl.hpp" -#include "Event.hpp" -#include "PortType.hpp" - -namespace Ingen { -namespace Server { - -class DuplexPort; -class EnginePort; -class GraphImpl; -class PortImpl; - -namespace Events { - -/** An event to add a Port to a Graph. - * - * \ingroup engine - */ -class CreatePort : public Event -{ -public: - CreatePort(Engine& engine, - SPtr<Interface> client, - int32_t id, - SampleCount timestamp, - const Raul::Path& path, - const Properties& properties); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - void undo(Interface& target); - -private: - enum class Flow { - INPUT, - OUTPUT - }; - - Raul::Path _path; - PortType _port_type; - LV2_URID _buf_type; - GraphImpl* _graph; - DuplexPort* _graph_port; - MPtr<BlockImpl::Ports> _ports_array; ///< New external port array for Graph - EnginePort* _engine_port; ///< Driver port if on the root - Properties _properties; - Properties _update; - boost::optional<Flow> _flow; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_CREATEPORT_HPP diff --git a/src/server/events/Delete.cpp b/src/server/events/Delete.cpp deleted file mode 100644 index e8f9582c..00000000 --- a/src/server/events/Delete.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Store.hpp" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" - -#include "BlockImpl.hpp" -#include "Broadcaster.hpp" -#include "ControlBindings.hpp" -#include "Delete.hpp" -#include "DisconnectAll.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "EnginePort.hpp" -#include "GraphImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "PreProcessContext.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -Delete::Delete(Engine& engine, - SPtr<Interface> client, - FrameTime timestamp, - const Ingen::Del& msg) - : Event(engine, client, msg.seq, timestamp) - , _msg(msg) - , _engine_port(nullptr) - , _disconnect_event(nullptr) -{ - if (uri_is_path(msg.uri)) { - _path = uri_to_path(msg.uri); - } -} - -Delete::~Delete() -{ - delete _disconnect_event; - for (ControlBindings::Binding* b : _removed_bindings) { - delete b; - } -} - -bool -Delete::pre_process(PreProcessContext& ctx) -{ - const Ingen::URIs& uris = _engine.world()->uris(); - if (_path.is_root() || _path == "/control" || _path == "/notify") { - return Event::pre_process_done(Status::NOT_DELETABLE, _path); - } - - _engine.control_bindings()->get_all(_path, _removed_bindings); - - auto iter = _engine.store()->find(_path); - if (iter == _engine.store()->end()) { - return Event::pre_process_done(Status::NOT_FOUND, _path); - } - - if (!(_block = dynamic_ptr_cast<BlockImpl>(iter->second))) { - _port = dynamic_ptr_cast<DuplexPort>(iter->second); - } - - if ((!_block && !_port) || (_port && !_engine.driver()->dynamic_ports())) { - return Event::pre_process_done(Status::NOT_DELETABLE, _path); - } - - GraphImpl* parent = _block ? _block->parent_graph() : _port->parent_graph(); - if (!parent) { - return Event::pre_process_done(Status::INTERNAL_ERROR, _path); - } - - // Take a writer lock while we modify the store - std::lock_guard<Store::Mutex> lock(_engine.store()->mutex()); - - _engine.store()->remove(iter, _removed_objects); - - if (_block) { - parent->remove_block(*_block); - _disconnect_event = new DisconnectAll(_engine, parent, _block.get()); - _disconnect_event->pre_process(ctx); - _compiled_graph = ctx.maybe_compile(*_engine.maid(), *parent); - } else if (_port) { - parent->remove_port(*_port); - _disconnect_event = new DisconnectAll(_engine, parent, _port.get()); - _disconnect_event->pre_process(ctx); - - _compiled_graph = ctx.maybe_compile(*_engine.maid(), *parent); - if (parent->enabled()) { - _ports_array = parent->build_ports_array(*_engine.maid()); - assert(_ports_array->size() == parent->num_ports_non_rt()); - - // Adjust port indices if necessary and record changes for later - for (size_t i = 0; i < _ports_array->size(); ++i) { - PortImpl* const port = _ports_array->at(i); - if (port->index() != i) { - _port_index_changes.emplace( - port->path(), std::make_pair(port->index(), i)); - port->remove_property(uris.lv2_index, uris.patch_wildcard); - port->set_property( - uris.lv2_index, - _engine.buffer_factory()->forge().make((int32_t)i)); - } - } - } - - if (!parent->parent()) { - _engine_port = _engine.driver()->get_port(_port->path()); - } - } - - return Event::pre_process_done(Status::SUCCESS); -} - -void -Delete::execute(RunContext& context) -{ - if (_status != Status::SUCCESS) { - return; - } - - if (_disconnect_event) { - _disconnect_event->execute(context); - } - - if (!_removed_bindings.empty()) { - _engine.control_bindings()->remove(context, _removed_bindings); - } - - GraphImpl* parent = _block ? _block->parent_graph() : nullptr; - if (_port) { - // Adjust port indices if necessary - for (size_t i = 0; i < _ports_array->size(); ++i) { - PortImpl* const port = _ports_array->at(i); - if (port->index() != i) { - port->set_index(context, i); - } - } - - // Replace ports array in graph - parent = _port->parent_graph(); - parent->set_external_ports(std::move(_ports_array)); - - if (_engine_port) { - _engine.driver()->remove_port(context, _engine_port); - } - } - - if (parent && _compiled_graph) { - parent->set_compiled_graph(std::move(_compiled_graph)); - } -} - -void -Delete::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS && (_block || _port)) { - if (_block) { - _block->deactivate(); - } - - _engine.broadcaster()->message(_msg); - } - - if (_engine_port) { - _engine.driver()->unregister_port(*_engine_port); - delete _engine_port; - } -} - -void -Delete::undo(Interface& target) -{ - const Ingen::URIs& uris = _engine.world()->uris(); - Ingen::Forge& forge = _engine.buffer_factory()->forge(); - - auto i = _removed_objects.find(_path); - if (i != _removed_objects.end()) { - // Undo disconnect - if (_disconnect_event) { - _disconnect_event->undo(target); - } - - // Put deleted item back - target.put(_msg.uri, i->second->properties()); - - // Adjust port indices - for (const auto& c : _port_index_changes) { - if (c.first != _msg.uri.path()) { - target.set_property(path_to_uri(c.first), - uris.lv2_index, - forge.make(int32_t(c.second.first))); - } - } - } -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/Delete.hpp b/src/server/events/Delete.hpp deleted file mode 100644 index 8b2a0a91..00000000 --- a/src/server/events/Delete.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_DELETE_HPP -#define INGEN_EVENTS_DELETE_HPP - -#include <map> -#include <vector> - -#include "ingen/Store.hpp" - -#include "CompiledGraph.hpp" -#include "ControlBindings.hpp" -#include "Event.hpp" -#include "GraphImpl.hpp" - -namespace Raul { -template<typename T> class Array; -} - -namespace Ingen { -namespace Server { - -class BlockImpl; -class DuplexPort; -class EnginePort; -class PortImpl; - -namespace Events { - -class DisconnectAll; - -/** Delete a graph object. - * \ingroup engine - */ -class Delete : public Event -{ -public: - Delete(Engine& engine, - SPtr<Interface> client, - FrameTime timestamp, - const Ingen::Del& msg); - - ~Delete(); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - void undo(Interface& target); - -private: - using IndexChange = std::pair<uint32_t, uint32_t>; - using IndexChanges = std::map<Raul::Path, IndexChange>; - - const Ingen::Del _msg; - Raul::Path _path; - SPtr<BlockImpl> _block; ///< Non-NULL iff a block - SPtr<DuplexPort> _port; ///< Non-NULL iff a port - EnginePort* _engine_port; - MPtr<GraphImpl::Ports> _ports_array; ///< New (external) ports for Graph - MPtr<CompiledGraph> _compiled_graph; ///< Graph's new process order - DisconnectAll* _disconnect_event; - Store::Objects _removed_objects; - IndexChanges _port_index_changes; - - std::vector<ControlBindings::Binding*> _removed_bindings; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_DELETE_HPP diff --git a/src/server/events/Delta.cpp b/src/server/events/Delta.cpp deleted file mode 100644 index b23ae884..00000000 --- a/src/server/events/Delta.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <vector> -#include <thread> - -#include "ingen/Log.hpp" -#include "ingen/Store.hpp" -#include "ingen/URIs.hpp" -#include "raul/Maid.hpp" - -#include "Broadcaster.hpp" -#include "ControlBindings.hpp" -#include "CreateBlock.hpp" -#include "CreateGraph.hpp" -#include "CreatePort.hpp" -#include "Delta.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "PortType.hpp" -#include "SetPortValue.hpp" -#include "events/Get.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -Delta::Delta(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Put& msg) - : Event(engine, client, msg.seq, timestamp) - , _create_event(nullptr) - , _subject(msg.uri) - , _properties(msg.properties) - , _object(nullptr) - , _graph(nullptr) - , _binding(nullptr) - , _state(nullptr) - , _context(msg.ctx) - , _type(Type::PUT) - , _block(false) -{ - init(); -} - -Delta::Delta(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Delta& msg) - : Event(engine, client, msg.seq, timestamp) - , _create_event(nullptr) - , _subject(msg.uri) - , _properties(msg.add) - , _remove(msg.remove) - , _object(nullptr) - , _graph(nullptr) - , _binding(nullptr) - , _state(nullptr) - , _context(msg.ctx) - , _type(Type::PATCH) - , _block(false) -{ - init(); -} - -Delta::Delta(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::SetProperty& msg) - : Event(engine, client, msg.seq, timestamp) - , _create_event(nullptr) - , _subject(msg.subject) - , _properties{{msg.predicate, msg.value}} - , _object(nullptr) - , _graph(nullptr) - , _binding(nullptr) - , _state(nullptr) - , _context(msg.ctx) - , _type(Type::SET) - , _block(false) -{ - init(); -} - -Delta::~Delta() -{ - for (auto& s : _set_events) { - delete s; - } - - delete _create_event; -} - -void -Delta::init() -{ - if (_context != Resource::Graph::DEFAULT) { - for (auto& p : _properties) { - p.second.set_context(_context); - } - } - - // Set atomic execution if polyphony is to be changed - const Ingen::URIs& uris = _engine.world()->uris(); - if (_properties.count(uris.ingen_polyphonic) || - _properties.count(uris.ingen_polyphony)) { - _block = true; - } -} - -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), false, true); - - _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); -} - -static LilvNode* -get_file_node(LilvWorld* lworld, const URIs& uris, const Atom& value) -{ - if (value.type() == uris.atom_Path) { - return lilv_new_file_uri(lworld, nullptr, value.ptr<char>()); - } else if (uris.forge.is_uri(value)) { - const std::string str = uris.forge.str(value, false); - if (str.substr(0, 5) == "file:") { - return lilv_new_uri(lworld, value.ptr<char>()); - } - } - return nullptr; -} - -bool -Delta::pre_process(PreProcessContext& ctx) -{ - const Ingen::URIs& uris = _engine.world()->uris(); - - const bool is_graph_object = uri_is_path(_subject); - const bool is_client = (_subject == "ingen:/clients/this"); - const bool is_engine = (_subject == "ingen:/"); - const bool is_file = (_subject.scheme() == "file"); - - if (_type == Type::PUT && is_file) { - // Ensure type is Preset, the only supported file put - const auto t = _properties.find(uris.rdf_type); - if (t == _properties.end() || t->second != uris.pset_Preset) { - return Event::pre_process_done(Status::BAD_REQUEST, _subject); - } - - // Get "prototype" for preset (node to save state for) - const auto p = _properties.find(uris.lv2_prototype); - if (p == _properties.end()) { - return Event::pre_process_done(Status::BAD_REQUEST, _subject); - } else if (!_engine.world()->forge().is_uri(p->second)) { - return Event::pre_process_done(Status::BAD_REQUEST, _subject); - } - - const URI prot(_engine.world()->forge().str(p->second, false)); - if (!uri_is_path(prot)) { - return Event::pre_process_done(Status::BAD_URI, _subject); - } - - Node* node = _engine.store()->get(uri_to_path(prot)); - if (!node) { - return Event::pre_process_done(Status::NOT_FOUND, prot); - } - - BlockImpl* block = dynamic_cast<BlockImpl*>(node); - if (!block) { - return Event::pre_process_done(Status::BAD_OBJECT_TYPE, prot); - } - - if ((_preset = block->save_preset(_subject, _properties))) { - return Event::pre_process_done(Status::SUCCESS); - } else { - return Event::pre_process_done(Status::FAILURE); - } - } - - std::lock_guard<Store::Mutex> lock(_engine.store()->mutex()); - - _object = is_graph_object - ? static_cast<Ingen::Resource*>(_engine.store()->get(uri_to_path(_subject))) - : static_cast<Ingen::Resource*>(_engine.block_factory()->plugin(_subject)); - - if (!_object && !is_client && !is_engine && - (!is_graph_object || _type != Type::PUT)) { - return Event::pre_process_done(Status::NOT_FOUND, _subject); - } - - if (is_graph_object && !_object) { - Raul::Path path(uri_to_path(_subject)); - bool is_graph = false, is_block = false, is_port = false, is_output = false; - Ingen::Resource::type(uris, _properties, is_graph, is_block, is_port, is_output); - - if (is_graph) { - _create_event = new CreateGraph( - _engine, _request_client, _request_id, _time, path, _properties); - } else if (is_block) { - _create_event = new CreateBlock( - _engine, _request_client, _request_id, _time, path, _properties); - } else if (is_port) { - _create_event = new CreatePort( - _engine, _request_client, _request_id, _time, - path, _properties); - } - if (_create_event) { - if (_create_event->pre_process(ctx)) { - _object = _engine.store()->get(path); // Get object for setting - } else { - return Event::pre_process_done(Status::CREATION_FAILED, _subject); - } - } else { - return Event::pre_process_done(Status::BAD_OBJECT_TYPE, _subject); - } - } - - _types.reserve(_properties.size()); - - NodeImpl* obj = dynamic_cast<NodeImpl*>(_object); - - // Remove any properties removed in delta - for (const auto& r : _remove) { - const URI& key = r.first; - const Atom& value = r.second; - if (key == uris.midi_binding && value == uris.patch_wildcard) { - PortImpl* port = dynamic_cast<PortImpl*>(_object); - if (port) { - _engine.control_bindings()->get_all(port->path(), _removed_bindings); - } - } - if (_object) { - _removed.emplace(key, value); - _object->remove_property(key, value); - } else if (is_engine && key == uris.ingen_loadedBundle) { - LilvWorld* lworld = _engine.world()->lilv_world(); - LilvNode* bundle = get_file_node(lworld, uris, value); - if (bundle) { - for (const auto& p : _engine.block_factory()->plugins()) { - if (p.second->bundle_uri() == lilv_node_as_string(bundle)) { - p.second->set_is_zombie(true); - _update.del(p.second->uri()); - } - } - lilv_world_unload_bundle(lworld, bundle); - _engine.block_factory()->refresh(); - lilv_node_free(bundle); - } else { - _status = Status::BAD_VALUE; - } - } - } - - // Remove all added properties if this is a put or set - if (_object && (_type == Type::PUT || _type == Type::SET)) { - for (auto p = _properties.begin(); - p != _properties.end(); - p = _properties.upper_bound(p->first)) { - for (auto q = _object->properties().find(p->first); - q != _object->properties().end() && q->first == p->first;) { - auto next = q; - ++next; - - if (!_properties.contains(q->first, q->second)) { - const auto r = std::make_pair(q->first, q->second); - _object->properties().erase(q); - _object->on_property_removed(r.first, r.second); - _removed.insert(r); - } - - q = next; - } - } - } - - for (const auto& p : _properties) { - const URI& key = p.first; - const Property& value = p.second; - SpecialType op = SpecialType::NONE; - if (obj) { - Resource& resource = *obj; - if (value != uris.patch_wildcard) { - if (resource.add_property(key, value, value.context())) { - _added.emplace(key, value); - } - } - - BlockImpl* block = nullptr; - PortImpl* port = dynamic_cast<PortImpl*>(_object); - if (port) { - if (key == uris.ingen_broadcast) { - if (value.type() == uris.forge.Bool) { - op = SpecialType::ENABLE_BROADCAST; - } else { - _status = Status::BAD_VALUE_TYPE; - } - } else if (key == uris.ingen_value || key == uris.ingen_activity) { - SetPortValue* ev = new SetPortValue( - _engine, _request_client, _request_id, _time, port, value, - key == uris.ingen_activity); - _set_events.push_back(ev); - } else if (key == uris.midi_binding) { - if (port->is_a(PortType::CONTROL) || port->is_a(PortType::CV)) { - if (value == uris.patch_wildcard) { - _engine.control_bindings()->start_learn(port); - } else if (value.type() == uris.atom_Object) { - op = SpecialType::CONTROL_BINDING; - _binding = new ControlBindings::Binding(); - } else { - _status = Status::BAD_VALUE_TYPE; - } - } else { - _status = Status::BAD_OBJECT_TYPE; - } - } else if (key == uris.lv2_index) { - op = SpecialType::PORT_INDEX; - port->set_property(key, value); - } - } else if ((block = dynamic_cast<BlockImpl*>(_object))) { - if (key == uris.midi_binding && value == uris.patch_wildcard) { - op = SpecialType::CONTROL_BINDING; // Internal block learn - } else if (key == uris.ingen_enabled) { - if (value.type() == uris.forge.Bool) { - op = SpecialType::ENABLE; - } else { - _status = Status::BAD_VALUE_TYPE; - } - } else if (key == uris.pset_preset) { - URI uri; - if (uris.forge.is_uri(value)) { - const std::string uri_str = uris.forge.str(value, false); - if (URI::is_valid(uri_str)) { - uri = URI(uri_str); - } - } else if (value.type() == uris.forge.Path) { - uri = URI(FilePath(value.ptr<char>())); - } - - if (!uri.empty()) { - op = SpecialType::PRESET; - if ((_state = block->load_preset(uri))) { - lilv_state_emit_port_values( - _state, s_add_set_event, this); - } else { - _engine.log().warn(fmt("Failed to load preset <%1%>\n") % uri); - } - } else { - _status = Status::BAD_VALUE; - } - } - } - - if ((_graph = dynamic_cast<GraphImpl*>(_object))) { - if (key == uris.ingen_enabled) { - if (value.type() == uris.forge.Bool) { - op = SpecialType::ENABLE; - // FIXME: defer this until all other metadata has been processed - if (value.get<int32_t>() && !_graph->enabled()) { - if (!(_compiled_graph = compile(*_engine.maid(), *_graph))) { - _status = Status::COMPILATION_FAILED; - } - } - } else { - _status = Status::BAD_VALUE_TYPE; - } - } else if (key == uris.ingen_polyphony) { - if (value.type() == uris.forge.Int) { - if (value.get<int32_t>() < 1 || value.get<int32_t>() > 128) { - _status = Status::INVALID_POLY; - } else { - op = SpecialType::POLYPHONY; - _graph->prepare_internal_poly( - *_engine.buffer_factory(), value.get<int32_t>()); - } - } else { - _status = Status::BAD_VALUE_TYPE; - } - } - } - - if (!_create_event && key == uris.ingen_polyphonic) { - GraphImpl* parent = dynamic_cast<GraphImpl*>(obj->parent()); - if (!parent) { - _status = Status::BAD_OBJECT_TYPE; - } else if (value.type() != uris.forge.Bool) { - _status = Status::BAD_VALUE_TYPE; - } else { - op = SpecialType::POLYPHONIC; - obj->set_property(key, value, value.context()); - BlockImpl* block = dynamic_cast<BlockImpl*>(obj); - if (block) { - block->set_polyphonic(value.get<int32_t>()); - } - if (value.get<int32_t>()) { - obj->prepare_poly(*_engine.buffer_factory(), parent->internal_poly()); - } else { - obj->prepare_poly(*_engine.buffer_factory(), 1); - } - } - } - } else if (is_client && key == uris.ingen_broadcast) { - _engine.broadcaster()->set_broadcast( - _request_client, value.get<int32_t>()); - } else if (is_engine && key == uris.ingen_loadedBundle) { - LilvWorld* lworld = _engine.world()->lilv_world(); - LilvNode* bundle = get_file_node(lworld, uris, value); - if (bundle) { - lilv_world_load_bundle(lworld, bundle); - const std::set<PluginImpl*> new_plugins = - _engine.block_factory()->refresh(); - - for (PluginImpl* p : new_plugins) { - if (p->bundle_uri() == lilv_node_as_string(bundle)) { - _update.put_plugin(p); - } - } - lilv_node_free(bundle); - } else { - _status = Status::BAD_VALUE; - } - } - - if (_status != Status::NOT_PREPARED) { - break; - } - - _types.push_back(op); - } - - for (auto& s : _set_events) { - s->pre_process(ctx); - } - - return Event::pre_process_done( - _status == Status::NOT_PREPARED ? Status::SUCCESS : _status, - _subject); -} - -void -Delta::execute(RunContext& context) -{ - if (_status != Status::SUCCESS || _preset) { - return; - } - - const Ingen::URIs& uris = _engine.world()->uris(); - - if (_create_event) { - _create_event->set_time(_time); - _create_event->execute(context); - } - - for (auto& s : _set_events) { - s->set_time(_time); - s->execute(context); - } - - if (!_removed_bindings.empty()) { - _engine.control_bindings()->remove(context, _removed_bindings); - } - - NodeImpl* const object = dynamic_cast<NodeImpl*>(_object); - BlockImpl* const block = dynamic_cast<BlockImpl*>(_object); - PortImpl* const port = dynamic_cast<PortImpl*>(_object); - - std::vector<SpecialType>::const_iterator t = _types.begin(); - for (const auto& p : _properties) { - const URI& key = p.first; - const Atom& value = p.second; - switch (*t++) { - case SpecialType::ENABLE_BROADCAST: - if (port) { - port->enable_monitoring(value.get<int32_t>()); - } - break; - case SpecialType::ENABLE: - if (_graph) { - if (value.get<int32_t>()) { - if (_compiled_graph) { - _graph->set_compiled_graph(std::move(_compiled_graph)); - } - _graph->enable(); - } else { - _graph->disable(context); - } - } else if (block) { - block->set_enabled(value.get<int32_t>()); - } - break; - case SpecialType::POLYPHONIC: { - GraphImpl* parent = reinterpret_cast<GraphImpl*>(object->parent()); - if (value.get<int32_t>()) { - object->apply_poly(context, parent->internal_poly_process()); - } else { - object->apply_poly(context, 1); - } - } break; - case SpecialType::POLYPHONY: - if (!_graph->apply_internal_poly(context, - *_engine.buffer_factory(), - *_engine.maid(), - value.get<int32_t>())) { - _status = Status::INTERNAL_ERROR; - } - break; - case SpecialType::PORT_INDEX: - if (port) { - port->set_index(context, value.get<int32_t>()); - } - break; - case SpecialType::CONTROL_BINDING: - if (port) { - if (!_engine.control_bindings()->set_port_binding(context, port, _binding, value)) { - _status = Status::BAD_VALUE; - } - } else if (block) { - if (uris.ingen_Internal == block->plugin_impl()->type()) { - block->learn(); - } - } - break; - case SpecialType::PRESET: - block->set_enabled(false); - break; - case SpecialType::NONE: - if (port) { - if (key == uris.lv2_minimum) { - port->set_minimum(value); - } else if (key == uris.lv2_maximum) { - port->set_maximum(value); - } - } - case SpecialType::LOADED_BUNDLE: - break; - } - } -} - -void -Delta::post_process() -{ - if (_state) { - BlockImpl* block = dynamic_cast<BlockImpl*>(_object); - if (block) { - block->apply_state(_engine.sync_worker(), _state); - block->set_enabled(true); - } - lilv_state_free(_state); - } - - Broadcaster::Transfer t(*_engine.broadcaster()); - - if (_create_event) { - _create_event->post_process(); - if (_create_event->status() != Status::SUCCESS) { - return; // Creation failed, nothing else to do - } - } - - for (auto& s : _set_events) { - if (s->synthetic() || s->status() != Status::SUCCESS) { - s->post_process(); // Set failed, report error - } - } - - if (respond() == Status::SUCCESS) { - _update.send(*_engine.broadcaster()); - - switch (_type) { - case Type::SET: - /* Kludge to avoid feedback for set events only. The GUI - depends on put responses to e.g. initially place blocks. - Some more sensible way of controlling this is needed. */ - if (_mode == Mode::NORMAL) { - _engine.broadcaster()->set_ignore_client(_request_client); - } - _engine.broadcaster()->set_property( - _subject, - _properties.begin()->first, - _properties.begin()->second); - if (_mode == Mode::NORMAL) { - _engine.broadcaster()->clear_ignore_client(); - } - break; - case Type::PUT: - if (_type == Type::PUT && _subject.scheme() == "file") { - // Preset save - ClientUpdate response; - response.put(_preset->uri(), _preset->properties()); - response.send(*_engine.broadcaster()); - } else { - // Graph object put - _engine.broadcaster()->put(_subject, _properties, _context); - } - break; - case Type::PATCH: - _engine.broadcaster()->delta(_subject, _remove, _properties, _context); - break; - } - } -} - -void -Delta::undo(Interface& target) -{ - if (_create_event) { - _create_event->undo(target); - } else if (_type == Type::PATCH) { - target.delta(_subject, _added, _removed, _context); - } else if (_type == Type::SET || _type == Type::PUT) { - if (_removed.size() == 1) { - target.set_property(_subject, - _removed.begin()->first, - _removed.begin()->second, - _context); - } else if (_removed.empty()) { - target.delta(_subject, _added, {}, _context); - } else { - target.put(_subject, _removed, _context); - } - } -} - -Event::Execution -Delta::get_execution() const -{ - return _block ? Execution::ATOMIC : Execution::NORMAL; -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/Delta.hpp b/src/server/events/Delta.hpp deleted file mode 100644 index af337b57..00000000 --- a/src/server/events/Delta.hpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_DELTA_HPP -#define INGEN_EVENTS_DELTA_HPP - -#include <vector> - -#include <boost/optional.hpp> - -#include "lilv/lilv.h" - -#include "CompiledGraph.hpp" -#include "ControlBindings.hpp" -#include "Event.hpp" -#include "PluginImpl.hpp" - -namespace Ingen { - -class Resource; - -namespace Server { - -class Engine; -class GraphImpl; -class RunContext; - -namespace Events { - -class SetPortValue; - -/** Set properties of a graph object. - * \ingroup engine - */ -class Delta : public Event -{ -public: - Delta(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Put& msg); - - Delta(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Delta& msg); - - Delta(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::SetProperty& msg); - - ~Delta(); - - void add_set_event(const char* port_symbol, - const void* value, - uint32_t size, - uint32_t type); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - void undo(Interface& target); - - Execution get_execution() const; - -private: - enum class Type { - SET, - PUT, - PATCH - }; - - enum class SpecialType { - NONE, - ENABLE, - ENABLE_BROADCAST, - POLYPHONY, - POLYPHONIC, - PORT_INDEX, - CONTROL_BINDING, - PRESET, - LOADED_BUNDLE - }; - - typedef std::vector<SetPortValue*> SetEvents; - - void init(); - - Event* _create_event; - SetEvents _set_events; - std::vector<SpecialType> _types; - std::vector<SpecialType> _remove_types; - URI _subject; - Properties _properties; - Properties _remove; - ClientUpdate _update; - Ingen::Resource* _object; - GraphImpl* _graph; - MPtr<CompiledGraph> _compiled_graph; - ControlBindings::Binding* _binding; - LilvState* _state; - Resource::Graph _context; - Type _type; - - Properties _added; - Properties _removed; - - std::vector<ControlBindings::Binding*> _removed_bindings; - - boost::optional<Resource> _preset; - - bool _block; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_DELTA_HPP diff --git a/src/server/events/Disconnect.cpp b/src/server/events/Disconnect.cpp deleted file mode 100644 index 4553c8a2..00000000 --- a/src/server/events/Disconnect.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <set> - -#include "ingen/Store.hpp" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" - -#include "ArcImpl.hpp" -#include "Broadcaster.hpp" -#include "Buffer.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "InputPort.hpp" -#include "PortImpl.hpp" -#include "PreProcessContext.hpp" -#include "RunContext.hpp" -#include "ThreadManager.hpp" -#include "events/Disconnect.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -Disconnect::Disconnect(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Disconnect& msg) - : Event(engine, client, msg.seq, timestamp) - , _msg(msg) - , _graph(nullptr) - , _impl(nullptr) -{ -} - -Disconnect::~Disconnect() -{ - delete _impl; -} - -Disconnect::Impl::Impl(Engine& e, - GraphImpl* graph, - PortImpl* t, - InputPort* h) - : _engine(e) - , _tail(t) - , _head(h) - , _arc(graph->remove_arc(_tail, _head)) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - BlockImpl* const tail_block = _tail->parent_block(); - BlockImpl* const head_block = _head->parent_block(); - - // Remove tail from head's providers - auto hp = head_block->providers().find(tail_block); - if (hp != head_block->providers().end()) { - head_block->providers().erase(hp); - } - - // Remove head from tail's providers - auto td = tail_block->dependants().find(head_block); - if (td != tail_block->dependants().end()) { - tail_block->dependants().erase(td); - } - - _head->decrement_num_arcs(); - - if (_head->num_arcs() == 0) { - if (!_head->is_driver_port()) { - BufferFactory& bufs = *_engine.buffer_factory(); - _voices = bufs.maid().make_managed<PortImpl::Voices>(_head->poly()); - _head->pre_get_buffers(bufs, _voices, _head->poly()); - - if (_head->is_a(PortType::CONTROL) || - _head->is_a(PortType::CV)) { - // Reset buffer to control value - const float value = _head->value().get<float>(); - for (uint32_t i = 0; i < _voices->size(); ++i) { - Buffer* buf = _voices->at(i).buffer.get(); - buf->set_block(value, 0, e.block_length()); - } - } else { - for (uint32_t i = 0; i < _voices->size(); ++i) { - _voices->at(i).buffer->clear(); - } - } - } - } -} - -bool -Disconnect::pre_process(PreProcessContext& ctx) -{ - std::lock_guard<Store::Mutex> lock(_engine.store()->mutex()); - - if (_msg.tail.parent().parent() != _msg.head.parent().parent() - && _msg.tail.parent() != _msg.head.parent().parent() - && _msg.tail.parent().parent() != _msg.head.parent()) { - return Event::pre_process_done(Status::PARENT_DIFFERS, _msg.head); - } - - PortImpl* tail = dynamic_cast<PortImpl*>(_engine.store()->get(_msg.tail)); - if (!tail) { - return Event::pre_process_done(Status::PORT_NOT_FOUND, _msg.tail); - } - - PortImpl* head = dynamic_cast<PortImpl*>(_engine.store()->get(_msg.head)); - if (!head) { - return Event::pre_process_done(Status::PORT_NOT_FOUND, _msg.head); - } - - BlockImpl* const tail_block = tail->parent_block(); - BlockImpl* const head_block = head->parent_block(); - - if (tail_block->parent_graph() != head_block->parent_graph()) { - // Arc to a graph port from inside the graph - assert(tail_block->parent() == head_block || head_block->parent() == tail_block); - if (tail_block->parent() == head_block) { - _graph = dynamic_cast<GraphImpl*>(head_block); - } else { - _graph = dynamic_cast<GraphImpl*>(tail_block); - } - } else if (tail_block == head_block && dynamic_cast<GraphImpl*>(tail_block)) { - // Arc from a graph input to a graph output (pass through) - _graph = dynamic_cast<GraphImpl*>(tail_block); - } else { - // Normal arc between blocks with the same parent - _graph = tail_block->parent_graph(); - } - - if (!_graph) { - return Event::pre_process_done(Status::INTERNAL_ERROR, _msg.head); - } else if (!_graph->has_arc(tail, head)) { - return Event::pre_process_done(Status::NOT_FOUND, _msg.head); - } - - if (tail_block == nullptr || head_block == nullptr) { - return Event::pre_process_done(Status::PARENT_NOT_FOUND, _msg.head); - } - - _impl = new Impl(_engine, - _graph, - dynamic_cast<PortImpl*>(tail), - dynamic_cast<InputPort*>(head)); - - _compiled_graph = ctx.maybe_compile(*_engine.maid(), *_graph); - - return Event::pre_process_done(Status::SUCCESS); -} - -bool -Disconnect::Impl::execute(RunContext& context, bool set_head_buffers) -{ - if (!_arc) { - return false; - } - - _head->remove_arc(*_arc.get()); - if (_head->is_driver_port()) { - return true; - } - - if (set_head_buffers) { - if (_voices) { - _head->set_voices(context, std::move(_voices)); - } else { - _head->setup_buffers(context, *_engine.buffer_factory(), _head->poly()); - } - _head->connect_buffers(); - } else { - _head->recycle_buffers(); - } - - return true; -} - -void -Disconnect::execute(RunContext& context) -{ - if (_status == Status::SUCCESS) { - if (_impl->execute(context, true)) { - if (_compiled_graph) { - _graph->set_compiled_graph(std::move(_compiled_graph)); - } - } else { - _status = Status::NOT_FOUND; - } - } -} - -void -Disconnect::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS) { - _engine.broadcaster()->message(_msg); - } -} - -void -Disconnect::undo(Interface& target) -{ - target.connect(_msg.tail, _msg.head); -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/Disconnect.hpp b/src/server/events/Disconnect.hpp deleted file mode 100644 index 44290d7c..00000000 --- a/src/server/events/Disconnect.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_DISCONNECT_HPP -#define INGEN_EVENTS_DISCONNECT_HPP - -#include "raul/Path.hpp" - -#include "BufferFactory.hpp" -#include "CompiledGraph.hpp" -#include "Event.hpp" -#include "GraphImpl.hpp" -#include "types.hpp" - -namespace Raul { -template <typename T> class Array; -} - -namespace Ingen { -namespace Server { - -class InputPort; -class PortImpl; - -namespace Events { - -/** Remove an Arc between two Ports. - * - * \ingroup engine - */ -class Disconnect : public Event -{ -public: - Disconnect(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Disconnect& msg); - - ~Disconnect(); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - void undo(Interface& target); - - class Impl { - public: - Impl(Engine& e, GraphImpl* graph, PortImpl* t, InputPort* h); - - bool execute(RunContext& context, bool set_head_buffers); - - inline PortImpl* tail() { return _tail; } - inline InputPort* head() { return _head; } - - private: - Engine& _engine; - PortImpl* _tail; - InputPort* _head; - SPtr<ArcImpl> _arc; - MPtr<PortImpl::Voices> _voices; - }; - -private: - const Ingen::Disconnect _msg; - GraphImpl* _graph; - Impl* _impl; - MPtr<CompiledGraph> _compiled_graph; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_DISCONNECT_HPP diff --git a/src/server/events/DisconnectAll.cpp b/src/server/events/DisconnectAll.cpp deleted file mode 100644 index 11311d12..00000000 --- a/src/server/events/DisconnectAll.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <set> - -#include "ingen/Store.hpp" -#include "raul/Array.hpp" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" - -#include "ArcImpl.hpp" -#include "BlockImpl.hpp" -#include "Broadcaster.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "InputPort.hpp" -#include "PortImpl.hpp" -#include "PreProcessContext.hpp" -#include "events/Disconnect.hpp" -#include "events/DisconnectAll.hpp" -#include "util.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -DisconnectAll::DisconnectAll(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::DisconnectAll& msg) - : Event(engine, client, msg.seq, timestamp) - , _msg(msg) - , _parent(nullptr) - , _block(nullptr) - , _port(nullptr) - , _deleting(false) -{ -} - -/** Internal version for use by other events. - */ -DisconnectAll::DisconnectAll(Engine& engine, - GraphImpl* parent, - Node* object) - : Event(engine) - , _msg{0, parent->path(), object->path()} - , _parent(parent) - , _block(dynamic_cast<BlockImpl*>(object)) - , _port(dynamic_cast<PortImpl*>(object)) - , _deleting(true) -{ -} - -DisconnectAll::~DisconnectAll() -{ - for (auto& i : _impls) { - delete i; - } -} - -bool -DisconnectAll::pre_process(PreProcessContext& ctx) -{ - std::unique_lock<Store::Mutex> lock(_engine.store()->mutex(), - std::defer_lock); - - if (!_deleting) { - lock.lock(); - - _parent = dynamic_cast<GraphImpl*>(_engine.store()->get(_msg.graph)); - if (!_parent) { - return Event::pre_process_done(Status::PARENT_NOT_FOUND, - _msg.graph); - } - - NodeImpl* const object = dynamic_cast<NodeImpl*>( - _engine.store()->get(_msg.path)); - if (!object) { - return Event::pre_process_done(Status::NOT_FOUND, _msg.path); - } - - if (object->parent_graph() != _parent - && object->parent()->parent_graph() != _parent) { - return Event::pre_process_done(Status::INVALID_PARENT, _msg.graph); - } - - // Only one of these will succeed - _block = dynamic_cast<BlockImpl*>(object); - _port = dynamic_cast<PortImpl*>(object); - - if (!_block && !_port) { - return Event::pre_process_done(Status::INTERNAL_ERROR, _msg.path); - } - } - - // Find set of arcs to remove - std::set<ArcImpl*> to_remove; - for (const auto& a : _parent->arcs()) { - ArcImpl* const arc = (ArcImpl*)a.second.get(); - if (_block) { - if (arc->tail()->parent_block() == _block - || arc->head()->parent_block() == _block) { - to_remove.insert(arc); - } - } else if (_port) { - if (arc->tail() == _port || arc->head() == _port) { - to_remove.insert(arc); - } - } - } - - // Create disconnect events (which erases from _parent->arcs()) - for (const auto& a : to_remove) { - _impls.push_back(new Disconnect::Impl( - _engine, _parent, - dynamic_cast<PortImpl*>(a->tail()), - dynamic_cast<InputPort*>(a->head()))); - } - - if (!_deleting && ctx.must_compile(*_parent)) { - if (!(_compiled_graph = compile(*_engine.maid(), *_parent))) { - return Event::pre_process_done(Status::COMPILATION_FAILED); - } - } - - return Event::pre_process_done(Status::SUCCESS); -} - -void -DisconnectAll::execute(RunContext& context) -{ - if (_status == Status::SUCCESS) { - for (auto& i : _impls) { - i->execute(context, - !_deleting || (i->head()->parent_block() != _block)); - } - } - - if (_compiled_graph) { - _parent->set_compiled_graph(std::move(_compiled_graph)); - } -} - -void -DisconnectAll::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS) { - _engine.broadcaster()->message(_msg); - } -} - -void -DisconnectAll::undo(Interface& target) -{ - for (auto& i : _impls) { - target.connect(i->tail()->path(), i->head()->path()); - } -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/DisconnectAll.hpp b/src/server/events/DisconnectAll.hpp deleted file mode 100644 index 947e538f..00000000 --- a/src/server/events/DisconnectAll.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_DISCONNECTALL_HPP -#define INGEN_EVENTS_DISCONNECTALL_HPP - -#include <list> - -#include "raul/Path.hpp" - -#include "CompiledGraph.hpp" -#include "Disconnect.hpp" -#include "Event.hpp" - -namespace Ingen { -namespace Server { - -class BlockImpl; -class GraphImpl; -class PortImpl; - -namespace Events { - -class Disconnect; - -/** An event to disconnect all connections to a Block. - * - * \ingroup engine - */ -class DisconnectAll : public Event -{ -public: - DisconnectAll(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::DisconnectAll& msg); - - DisconnectAll(Engine& engine, - GraphImpl* parent, - Node* object); - - ~DisconnectAll(); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - void undo(Interface& target); - -private: - typedef std::list<Disconnect::Impl*> Impls; - - const Ingen::DisconnectAll _msg; - GraphImpl* _parent; - BlockImpl* _block; - PortImpl* _port; - Impls _impls; - MPtr<CompiledGraph> _compiled_graph; - bool _deleting; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_DISCONNECTALL_HPP diff --git a/src/server/events/Get.cpp b/src/server/events/Get.cpp deleted file mode 100644 index e53e8c41..00000000 --- a/src/server/events/Get.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <utility> - -#include "ingen/Interface.hpp" -#include "ingen/Node.hpp" -#include "ingen/Store.hpp" - -#include "BlockImpl.hpp" -#include "Broadcaster.hpp" -#include "BufferFactory.hpp" -#include "Engine.hpp" -#include "Get.hpp" -#include "GraphImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -Get::Get(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Get& msg) - : Event(engine, client, msg.seq, timestamp) - , _msg(msg) - , _object(nullptr) - , _plugin(nullptr) -{} - -bool -Get::pre_process(PreProcessContext& ctx) -{ - std::lock_guard<Store::Mutex> lock(_engine.store()->mutex()); - - const auto& uri = _msg.subject; - if (uri == "ingen:/plugins") { - _plugins = _engine.block_factory()->plugins(); - return Event::pre_process_done(Status::SUCCESS); - } else if (uri == "ingen:/engine") { - return Event::pre_process_done(Status::SUCCESS); - } else if (uri_is_path(uri)) { - if ((_object = _engine.store()->get(uri_to_path(uri)))) { - const BlockImpl* block = nullptr; - const GraphImpl* graph = nullptr; - const PortImpl* port = nullptr; - if ((graph = dynamic_cast<const GraphImpl*>(_object))) { - _response.put_graph(graph); - } else if ((block = dynamic_cast<const BlockImpl*>(_object))) { - _response.put_block(block); - } else if ((port = dynamic_cast<const PortImpl*>(_object))) { - _response.put_port(port); - } else { - return Event::pre_process_done(Status::BAD_OBJECT_TYPE, uri); - } - return Event::pre_process_done(Status::SUCCESS); - } - return Event::pre_process_done(Status::NOT_FOUND, uri); - } else if ((_plugin = _engine.block_factory()->plugin(uri))) { - _response.put_plugin(_plugin); - return Event::pre_process_done(Status::SUCCESS); - } else { - return Event::pre_process_done(Status::NOT_FOUND, uri); - } -} - -void -Get::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS && _request_client) { - if (_msg.subject == "ingen:/plugins") { - _engine.broadcaster()->send_plugins_to(_request_client.get(), _plugins); - } else if (_msg.subject == "ingen:/engine") { - // TODO: Keep a proper RDF model of the engine - URIs& uris = _engine.world()->uris(); - Properties props = { - { uris.param_sampleRate, - uris.forge.make(int32_t(_engine.sample_rate())) }, - { uris.bufsz_maxBlockLength, - uris.forge.make(int32_t(_engine.block_length())) }, - { uris.ingen_numThreads, - uris.forge.make(int32_t(_engine.n_threads())) } }; - - const Properties load_props = _engine.load_properties(); - props.insert(load_props.begin(), load_props.end()); - _request_client->put(URI("ingen:/engine"), props); - } else { - _response.send(*_request_client); - } - } -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/Get.hpp b/src/server/events/Get.hpp deleted file mode 100644 index 7392550f..00000000 --- a/src/server/events/Get.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_GET_HPP -#define INGEN_EVENTS_GET_HPP - -#include <vector> - -#include "BlockFactory.hpp" -#include "ClientUpdate.hpp" -#include "Event.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Server { - -class BlockImpl; -class GraphImpl; -class PluginImpl; -class PortImpl; - -namespace Events { - -/** A request from a client to send an object. - * - * \ingroup engine - */ -class Get : public Event -{ -public: - Get(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Get& msg); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context) {} - void post_process(); - -private: - const Ingen::Get _msg; - const Node* _object; - PluginImpl* _plugin; - BlockFactory::Plugins _plugins; - ClientUpdate _response; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_GET_HPP diff --git a/src/server/events/Mark.cpp b/src/server/events/Mark.cpp deleted file mode 100644 index 3c0dfaaf..00000000 --- a/src/server/events/Mark.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "Engine.hpp" -#include "PreProcessContext.hpp" -#include "UndoStack.hpp" -#include "events/Mark.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -Mark::Mark(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::BundleBegin& msg) - : Event(engine, client, msg.seq, timestamp) - , _type(Type::BUNDLE_BEGIN) - , _depth(0) -{} - -Mark::Mark(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::BundleEnd& msg) - : Event(engine, client, msg.seq, timestamp) - , _type(Type::BUNDLE_END) - , _depth(0) -{} - -bool -Mark::pre_process(PreProcessContext& ctx) -{ - const UPtr<UndoStack>& stack = ((_mode == Mode::UNDO) - ? _engine.redo_stack() - : _engine.undo_stack()); - - switch (_type) { - case Type::BUNDLE_BEGIN: - ctx.set_in_bundle(true); - _depth = stack->start_entry(); - break; - case Type::BUNDLE_END: - _depth = stack->finish_entry(); - ctx.set_in_bundle(false); - if (!ctx.dirty_graphs().empty()) { - for (GraphImpl* g : ctx.dirty_graphs()) { - MPtr<CompiledGraph> cg = compile(*_engine.maid(), *g); - if (cg) { - _compiled_graphs.emplace(g, std::move(cg)); - } - } - ctx.dirty_graphs().clear(); - } - break; - } - - return Event::pre_process_done(Status::SUCCESS); -} - -void -Mark::execute(RunContext& context) -{ - for (auto& g : _compiled_graphs) { - g.first->set_compiled_graph(std::move(g.second)); - } -} - -void -Mark::post_process() -{ - respond(); -} - -Event::Execution -Mark::get_execution() const -{ - if (!_engine.atomic_bundles()) { - return Execution::NORMAL; - } - - switch (_type) { - case Type::BUNDLE_BEGIN: - if (_depth == 1) { - return Execution::BLOCK; - } - break; - case Type::BUNDLE_END: - if (_depth == 0) { - return Execution::UNBLOCK; - } - break; - } - return Execution::NORMAL; -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/Mark.hpp b/src/server/events/Mark.hpp deleted file mode 100644 index eaeb9332..00000000 --- a/src/server/events/Mark.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_MARK_HPP -#define INGEN_EVENTS_MARK_HPP - -#include "Event.hpp" - -namespace Ingen { -namespace Server { - -class Engine; - -namespace Events { - -/** Delineate the start or end of a bundle of events. - * - * This is used to mark the ends of an undo transaction, so a single undo can - * undo the effects of many events (such as a paste or a graph load). - * - * \ingroup engine - */ -class Mark : public Event -{ -public: - Mark(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::BundleBegin& msg); - - Mark(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::BundleEnd& msg); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - - Execution get_execution() const; - -private: - enum class Type { BUNDLE_BEGIN, BUNDLE_END }; - - typedef std::map<GraphImpl*, MPtr<CompiledGraph>> CompiledGraphs; - - CompiledGraphs _compiled_graphs; - Type _type; - int _depth; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_MARK_HPP diff --git a/src/server/events/Move.cpp b/src/server/events/Move.cpp deleted file mode 100644 index b0935675..00000000 --- a/src/server/events/Move.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Store.hpp" -#include "raul/Path.hpp" - -#include "BlockImpl.hpp" -#include "Broadcaster.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "EnginePort.hpp" -#include "GraphImpl.hpp" -#include "events/Move.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -Move::Move(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Move& msg) - : Event(engine, client, msg.seq, timestamp) - , _msg(msg) -{ -} - -bool -Move::pre_process(PreProcessContext& ctx) -{ - std::lock_guard<Store::Mutex> lock(_engine.store()->mutex()); - - if (!_msg.old_path.parent().is_parent_of(_msg.new_path)) { - return Event::pre_process_done(Status::PARENT_DIFFERS, _msg.new_path); - } - - const Store::iterator i = _engine.store()->find(_msg.old_path); - if (i == _engine.store()->end()) { - return Event::pre_process_done(Status::NOT_FOUND, _msg.old_path); - } - - if (_engine.store()->find(_msg.new_path) != _engine.store()->end()) { - return Event::pre_process_done(Status::EXISTS, _msg.new_path); - } - - EnginePort* eport = _engine.driver()->get_port(_msg.old_path); - if (eport) { - _engine.driver()->rename_port(_msg.old_path, _msg.new_path); - } - - _engine.store()->rename(i, _msg.new_path); - - return Event::pre_process_done(Status::SUCCESS); -} - -void -Move::execute(RunContext& context) -{ -} - -void -Move::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS) { - _engine.broadcaster()->message(_msg); - } -} - -void -Move::undo(Interface& target) -{ - target.move(_msg.new_path, _msg.old_path); -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/Move.hpp b/src/server/events/Move.hpp deleted file mode 100644 index 459d2709..00000000 --- a/src/server/events/Move.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_MOVE_HPP -#define INGEN_EVENTS_MOVE_HPP - -#include "ingen/Store.hpp" -#include "raul/Path.hpp" - -#include "Event.hpp" - -namespace Ingen { -namespace Server { - -class GraphImpl; -class PortImpl; - -namespace Events { - -/** Move a graph object to a new path. - * \ingroup engine - */ -class Move : public Event -{ -public: - Move(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Move& msg); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - void undo(Interface& target); - -private: - const Ingen::Move _msg; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_MOVE_HPP diff --git a/src/server/events/SetPortValue.cpp b/src/server/events/SetPortValue.cpp deleted file mode 100644 index 62f2def6..00000000 --- a/src/server/events/SetPortValue.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/LV2Features.hpp" -#include "ingen/Store.hpp" -#include "ingen/URIs.hpp" -#include "ingen/World.hpp" - -#include "BlockImpl.hpp" -#include "Broadcaster.hpp" -#include "Buffer.hpp" -#include "ControlBindings.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "RunContext.hpp" -#include "SetPortValue.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -/** Internal */ -SetPortValue::SetPortValue(Engine& engine, - SPtr<Interface> client, - int32_t id, - SampleCount timestamp, - PortImpl* port, - const Atom& value, - bool activity, - bool synthetic) - : Event(engine, client, id, timestamp) - , _port(port) - , _value(value) - , _activity(activity) - , _synthetic(synthetic) -{ -} - -bool -SetPortValue::pre_process(PreProcessContext& ctx) -{ - Ingen::URIs& uris = _engine.world()->uris(); - if (_port->is_output()) { - return Event::pre_process_done(Status::DIRECTION_MISMATCH, _port->path()); - } - - if (!_activity) { - // Set value metadata (does not affect buffers) - _port->set_value(_value); - _port->set_property(_engine.world()->uris().ingen_value, _value); - } - - _binding = _engine.control_bindings()->port_binding(_port); - - if (_port->buffer_type() == uris.atom_Sequence) { - _buffer = _engine.buffer_factory()->get_buffer( - _port->buffer_type(), - _value.type() == uris.atom_Float ? _value.type() : 0, - _engine.buffer_factory()->default_size(_port->buffer_type())); - } - - return Event::pre_process_done(Status::SUCCESS); -} - -void -SetPortValue::execute(RunContext& context) -{ - assert(_time >= context.start() && _time <= context.end()); - apply(context); - _engine.control_bindings()->port_value_changed(context, _port, _binding, _value); -} - -void -SetPortValue::apply(RunContext& context) -{ - if (_status != Status::SUCCESS) { - return; - } - - Ingen::URIs& uris = _engine.world()->uris(); - Buffer* buf = _port->buffer(0).get(); - - if (_buffer) { - if (_port->user_buffer(context)) { - buf = _port->user_buffer(context).get(); - } else { - _port->set_user_buffer(context, _buffer); - buf = _buffer.get(); - } - } - - if (buf->type() == uris.atom_Sound || buf->type() == uris.atom_Float) { - if (_value.type() == uris.forge.Float) { - _port->set_control_value(context, _time, _value.get<float>()); - } else { - _status = Status::TYPE_MISMATCH; - } - } else if (buf->type() == uris.atom_Sequence) { - if (!buf->append_event(_time - context.start(), - _value.size(), - _value.type(), - (const uint8_t*)_value.get_body())) { - _status = Status::NO_SPACE; - } - } else if (buf->type() == uris.atom_URID) { - buf->get<LV2_Atom_URID>()->body = _value.get<int32_t>(); - } else { - _status = Status::BAD_VALUE_TYPE; - } -} - -void -SetPortValue::post_process() -{ - Broadcaster::Transfer t(*_engine.broadcaster()); - if (respond() == Status::SUCCESS && !_activity) { - _engine.broadcaster()->set_property( - _port->uri(), - _engine.world()->uris().ingen_value, - _value); - } -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/SetPortValue.hpp b/src/server/events/SetPortValue.hpp deleted file mode 100644 index 4df60019..00000000 --- a/src/server/events/SetPortValue.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_SETPORTVALUE_HPP -#define INGEN_EVENTS_SETPORTVALUE_HPP - -#include "ingen/Atom.hpp" - -#include "BufferRef.hpp" -#include "ControlBindings.hpp" -#include "Event.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Server { - -class PortImpl; - -namespace Events { - -/** An event to change the value of a port. - * - * \ingroup engine - */ -class SetPortValue : public Event -{ -public: - SetPortValue(Engine& engine, - SPtr<Interface> client, - int32_t id, - SampleCount timestamp, - PortImpl* port, - const Atom& value, - bool activity, - bool synthetic = false); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - - bool synthetic() const { return _synthetic; } - -private: - void apply(RunContext& context); - - PortImpl* _port; - const Atom _value; - BufferRef _buffer; - ControlBindings::Key _binding; - bool _activity; - bool _synthetic; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_SETPORTVALUE_HPP diff --git a/src/server/events/Undo.cpp b/src/server/events/Undo.cpp deleted file mode 100644 index e06a5951..00000000 --- a/src/server/events/Undo.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/AtomReader.hpp" - -#include "Engine.hpp" -#include "EventWriter.hpp" -#include "Undo.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -Undo::Undo(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Undo& msg) - : Event(engine, client, msg.seq, timestamp) - , _is_redo(false) -{} - -Undo::Undo(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Redo& msg) - : Event(engine, client, msg.seq, timestamp) - , _is_redo(true) -{} - -bool -Undo::pre_process(PreProcessContext& ctx) -{ - const UPtr<UndoStack>& stack = _is_redo ? _engine.redo_stack() : _engine.undo_stack(); - const Event::Mode mode = _is_redo ? Event::Mode::REDO : Event::Mode::UNDO; - - if (stack->empty()) { - return Event::pre_process_done(Status::NOT_FOUND); - } - - const Event::Mode orig_mode = _engine.event_writer()->get_event_mode(); - _entry = stack->pop(); - _engine.event_writer()->set_event_mode(mode); - if (_entry.events.size() > 1) { - _engine.interface()->bundle_begin(); - } - - for (const LV2_Atom* ev : _entry.events) { - _engine.atom_interface()->write(ev); - } - - if (_entry.events.size() > 1) { - _engine.interface()->bundle_end(); - } - _engine.event_writer()->set_event_mode(orig_mode); - - return Event::pre_process_done(Status::SUCCESS); -} - -void -Undo::execute(RunContext& context) -{ -} - -void -Undo::post_process() -{ - respond(); -} - -} // namespace Events -} // namespace Server -} // namespace Ingen diff --git a/src/server/events/Undo.hpp b/src/server/events/Undo.hpp deleted file mode 100644 index af4b0d65..00000000 --- a/src/server/events/Undo.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_EVENTS_UNDO_HPP -#define INGEN_EVENTS_UNDO_HPP - -#include "Event.hpp" -#include "UndoStack.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Server { -namespace Events { - -/** A request to undo the last change to the engine. - * - * \ingroup engine - */ -class Undo : public Event -{ -public: - Undo(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Undo& msg); - - Undo(Engine& engine, - SPtr<Interface> client, - SampleCount timestamp, - const Ingen::Redo& msg); - - bool pre_process(PreProcessContext& ctx); - void execute(RunContext& context); - void post_process(); - -private: - UndoStack::Entry _entry; - bool _is_redo; -}; - -} // namespace Events -} // namespace Server -} // namespace Ingen - -#endif // INGEN_EVENTS_UNDO_HPP diff --git a/src/server/ingen_engine.cpp b/src/server/ingen_engine.cpp deleted file mode 100644 index 3409f1bf..00000000 --- a/src/server/ingen_engine.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/Module.hpp" -#include "ingen/World.hpp" -#include "Engine.hpp" -#include "EventWriter.hpp" -#include "util.hpp" - -using namespace Ingen; - -struct IngenEngineModule : public Ingen::Module { - virtual void load(Ingen::World* world) { - Server::set_denormal_flags(world->log()); - SPtr<Server::Engine> engine(new Server::Engine(world)); - world->set_engine(engine); - if (!world->interface()) { - world->set_interface(engine->interface()); - } - } -}; - -extern "C" { - -Ingen::Module* -ingen_module_load() -{ - return new IngenEngineModule(); -} - -} // extern "C" diff --git a/src/server/ingen_jack.cpp b/src/server/ingen_jack.cpp deleted file mode 100644 index a897f130..00000000 --- a/src/server/ingen_jack.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <string> - -#include "ingen/Atom.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/Log.hpp" -#include "ingen/Module.hpp" -#include "ingen/World.hpp" - -#include "JackDriver.hpp" -#include "Engine.hpp" - -using namespace Ingen; - -struct IngenJackModule : public Ingen::Module { - void load(Ingen::World* world) { - if (((Server::Engine*)world->engine().get())->driver()) { - world->log().warn("Engine already has a driver\n"); - return; - } - - Server::JackDriver* driver = new Server::JackDriver( - *(Server::Engine*)world->engine().get()); - const Atom& s = world->conf().option("jack-server"); - const std::string server_name = s.is_valid() ? s.ptr<char>() : ""; - driver->attach(server_name, - world->conf().option("jack-name").ptr<char>(), - nullptr); - ((Server::Engine*)world->engine().get())->set_driver( - SPtr<Server::Driver>(driver)); - } -}; - -extern "C" { - -Ingen::Module* -ingen_module_load() -{ - return new IngenJackModule(); -} - -} // extern "C" diff --git a/src/server/ingen_lv2.cpp b/src/server/ingen_lv2.cpp deleted file mode 100644 index b2806ab6..00000000 --- a/src/server/ingen_lv2.cpp +++ /dev/null @@ -1,850 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cstdlib> -#include <string> -#include <thread> -#include <vector> - -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h" -#include "lv2/lv2plug.in/ns/ext/log/log.h" -#include "lv2/lv2plug.in/ns/ext/log/logger.h" -#include "lv2/lv2plug.in/ns/ext/options/options.h" -#include "lv2/lv2plug.in/ns/ext/state/state.h" -#include "lv2/lv2plug.in/ns/ext/urid/urid.h" -#include "lv2/lv2plug.in/ns/lv2core/lv2.h" - -#include "ingen/AtomReader.hpp" -#include "ingen/AtomWriter.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/Interface.hpp" -#include "ingen/Log.hpp" -#include "ingen/Parser.hpp" -#include "ingen/Serialiser.hpp" -#include "ingen/Store.hpp" -#include "ingen/URI.hpp" -#include "ingen/World.hpp" -#include "ingen/ingen.h" -#include "ingen/runtime_paths.hpp" -#include "ingen/types.hpp" -#include "raul/Semaphore.hpp" - -#include "Buffer.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "EnginePort.hpp" -#include "EventWriter.hpp" -#include "GraphImpl.hpp" -#include "PostProcessor.hpp" -#include "RunContext.hpp" -#include "ThreadManager.hpp" - -#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" -#define NS_RDFS "http://www.w3.org/2000/01/rdf-schema#" - -namespace Ingen { - -/** Record of a graph in this bundle. */ -struct LV2Graph : public Parser::ResourceRecord { - LV2Graph(Parser::ResourceRecord record); - - LV2_Descriptor descriptor; -}; - -/** Ingen LV2 library. */ -class Lib { -public: - explicit Lib(const char* bundle_path); - - typedef std::vector< SPtr<const LV2Graph> > Graphs; - - Graphs graphs; -}; - -namespace Server { - -class LV2Driver; - -void signal_main(RunContext& context, LV2Driver* driver); - -inline size_t -ui_ring_size(SampleCount block_length) -{ - return std::max((size_t)8192, (size_t)block_length * 16); -} - -class LV2Driver : public Ingen::Server::Driver - , public Ingen::AtomSink -{ -public: - LV2Driver(Engine& engine, - SampleCount block_length, - size_t seq_size, - SampleCount sample_rate) - : _engine(engine) - , _main_sem(0) - , _reader(engine.world()->uri_map(), - engine.world()->uris(), - engine.world()->log(), - *engine.world()->interface().get()) - , _writer(engine.world()->uri_map(), - engine.world()->uris(), - *this) - , _from_ui(ui_ring_size(block_length)) - , _to_ui(ui_ring_size(block_length)) - , _root_graph(nullptr) - , _notify_capacity(0) - , _block_length(block_length) - , _seq_size(seq_size) - , _sample_rate(sample_rate) - , _frame_time(0) - , _to_ui_overflow_sem(0) - , _to_ui_overflow(false) - , _instantiated(false) - {} - - virtual bool dynamic_ports() const { return !_instantiated; } - - void pre_process_port(RunContext& context, EnginePort* port) { - const URIs& uris = _engine.world()->uris(); - const SampleCount nframes = context.nframes(); - DuplexPort* graph_port = port->graph_port(); - Buffer* graph_buf = graph_port->buffer(0).get(); - void* lv2_buf = port->buffer(); - - if (graph_port->is_a(PortType::AUDIO) || graph_port->is_a(PortType::CV)) { - graph_port->set_driver_buffer(lv2_buf, nframes * sizeof(float)); - } else if (graph_port->buffer_type() == uris.atom_Sequence) { - graph_port->set_driver_buffer(lv2_buf, lv2_atom_total_size((LV2_Atom*)lv2_buf)); - if (graph_port->symbol() == "control") { // TODO: Safe to use index? - LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)lv2_buf; - bool enqueued = false; - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - if (AtomReader::is_message(uris, &ev->body)) { - enqueued = enqueue_message(&ev->body) || enqueued; - } - } - - if (enqueued) { - // Enqueued a message for processing, raise semaphore - _main_sem.post(); - } - } - } - - if (graph_port->is_input()) { - graph_port->monitor(context); - } else { - graph_buf->prepare_write(context); - } - } - - void post_process_port(RunContext& context, EnginePort* port) { - DuplexPort* graph_port = port->graph_port(); - - // No copying necessary, host buffers are used directly - // Reset graph port buffer pointer to no longer point to the Jack buffer - if (graph_port->is_driver_port()) { - graph_port->set_driver_buffer(nullptr, 0); - } - } - - void run(uint32_t nframes) { - _engine.locate(_frame_time, nframes); - - // Notify buffer is a Chunk with size set to the available space - _notify_capacity = ((LV2_Atom_Sequence*)_ports[1]->buffer())->atom.size; - - for (auto& p : _ports) { - pre_process_port(_engine.run_context(), p); - } - - _engine.run(nframes); - if (_engine.post_processor()->pending()) { - _main_sem.post(); - } - - flush_to_ui(_engine.run_context()); - - for (auto& p : _ports) { - post_process_port(_engine.run_context(), p); - } - - _frame_time += nframes; - } - - virtual void deactivate() { - _engine.quit(); - _main_sem.post(); - } - - virtual void set_root_graph(GraphImpl* graph) { _root_graph = graph; } - virtual GraphImpl* root_graph() { return _root_graph; } - - virtual EnginePort* get_port(const Raul::Path& path) { - for (auto& p : _ports) { - if (p->graph_port()->path() == path) { - return p; - } - } - - return nullptr; - } - - /** Add a port. Called only during init or restore. */ - virtual void add_port(RunContext& context, EnginePort* port) { - const uint32_t index = port->graph_port()->index(); - if (_ports.size() <= index) { - _ports.resize(index + 1); - } - _ports[index] = port; - } - - /** Remove a port. Called only during init or restore. */ - virtual void remove_port(RunContext& context, EnginePort* port) { - const uint32_t index = port->graph_port()->index(); - _ports[index] = nullptr; - } - - /** Unused since LV2 has no dynamic ports. */ - virtual void register_port(EnginePort& port) {} - - /** Unused since LV2 has no dynamic ports. */ - virtual void unregister_port(EnginePort& port) {} - - /** Unused since LV2 has no dynamic ports. */ - virtual void rename_port(const Raul::Path& old_path, - const Raul::Path& new_path) {} - - /** Unused since LV2 has no dynamic ports. */ - virtual void port_property(const Raul::Path& path, - const URI& uri, - const Atom& value) {} - - virtual EnginePort* create_port(DuplexPort* graph_port) { - graph_port->set_is_driver_port(*_engine.buffer_factory()); - return new EnginePort(graph_port); - } - - virtual void append_time_events(RunContext& context, - Buffer& buffer) - { - const URIs& uris = _engine.world()->uris(); - LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)_ports[0]->buffer(); - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - if (ev->body.type == uris.atom_Object) { - const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body; - if (obj->body.otype == uris.time_Position) { - buffer.append_event(ev->time.frames, - ev->body.size, - ev->body.type, - (const uint8_t*)(&ev->body + 1)); - } - } - } - } - - virtual int real_time_priority() { return 60; } - - /** Called in run thread for events received at control input port. */ - bool enqueue_message(const LV2_Atom* atom) { - if (_from_ui.write(lv2_atom_total_size(atom), atom) == 0) { -#ifndef NDEBUG - _engine.log().error("Control input buffer overflow\n"); -#endif - return false; - } - return true; - } - - Raul::Semaphore& main_sem() { return _main_sem; } - - /** AtomSink::write implementation called by the PostProcessor in the main - * thread to write responses to the UI. - */ - bool write(const LV2_Atom* atom, int32_t default_id) { - // Called from post-processor in main thread - while (_to_ui.write(lv2_atom_total_size(atom), atom) == 0) { - // Overflow, wait until ring is drained next cycle - _to_ui_overflow = true; - _to_ui_overflow_sem.wait(); - _to_ui_overflow = false; - } - return true; - } - - void consume_from_ui() { - const uint32_t read_space = _from_ui.read_space(); - void* buf = nullptr; - for (uint32_t read = 0; read < read_space;) { - LV2_Atom atom; - if (!_from_ui.read(sizeof(LV2_Atom), &atom)) { - _engine.log().rt_error("Error reading head from from-UI ring\n"); - break; - } - - buf = realloc(buf, sizeof(LV2_Atom) + atom.size); - memcpy(buf, &atom, sizeof(LV2_Atom)); - - if (!_from_ui.read(atom.size, (char*)buf + sizeof(LV2_Atom))) { - _engine.log().rt_error("Error reading body from from-UI ring\n"); - break; - } - - _reader.write((LV2_Atom*)buf); - read += sizeof(LV2_Atom) + atom.size; - } - free(buf); - } - - void flush_to_ui(RunContext& context) { - if (_ports.size() < 2) { - _engine.log().rt_error("Standard control ports are not present\n"); - return; - } - - LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)_ports[1]->buffer(); - if (!seq) { - _engine.log().rt_error("Notify output not connected\n"); - return; - } - - // Initialise output port buffer to an empty Sequence - seq->atom.type = _engine.world()->uris().atom_Sequence; - seq->atom.size = sizeof(LV2_Atom_Sequence_Body); - - const uint32_t read_space = _to_ui.read_space(); - for (uint32_t read = 0; read < read_space;) { - LV2_Atom atom; - if (!_to_ui.peek(sizeof(LV2_Atom), &atom)) { - _engine.log().rt_error("Error reading head from to-UI ring\n"); - break; - } - - if (seq->atom.size + lv2_atom_pad_size( - sizeof(LV2_Atom_Event) + atom.size) - > _notify_capacity) { - break; // Output port buffer full, resume next time - } - - LV2_Atom_Event* ev = (LV2_Atom_Event*)( - (uint8_t*)seq + lv2_atom_total_size(&seq->atom)); - - ev->time.frames = 0; // TODO: Time? - ev->body = atom; - - _to_ui.skip(sizeof(LV2_Atom)); - if (!_to_ui.read(ev->body.size, LV2_ATOM_BODY(&ev->body))) { - _engine.log().rt_error("Error reading body from to-UI ring\n"); - break; - } - - read += lv2_atom_total_size(&ev->body); - seq->atom.size += lv2_atom_pad_size( - sizeof(LV2_Atom_Event) + ev->body.size); - } - - if (_to_ui_overflow) { - _to_ui_overflow_sem.post(); - } - } - - virtual SampleCount block_length() const { return _block_length; } - virtual size_t seq_size() const { return _seq_size; } - virtual SampleCount sample_rate() const { return _sample_rate; } - virtual SampleCount frame_time() const { return _frame_time; } - - AtomReader& reader() { return _reader; } - AtomWriter& writer() { return _writer; } - - typedef std::vector<EnginePort*> Ports; - - Ports& ports() { return _ports; } - - void set_instantiated(bool instantiated) { _instantiated = instantiated; } - -private: - Engine& _engine; - Ports _ports; - Raul::Semaphore _main_sem; - AtomReader _reader; - AtomWriter _writer; - Raul::RingBuffer _from_ui; - Raul::RingBuffer _to_ui; - GraphImpl* _root_graph; - uint32_t _notify_capacity; - SampleCount _block_length; - size_t _seq_size; - SampleCount _sample_rate; - SampleCount _frame_time; - Raul::Semaphore _to_ui_overflow_sem; - bool _to_ui_overflow; - bool _instantiated; -}; - -} // namespace Server -} // namespace Ingen - -extern "C" { - -using namespace Ingen; -using namespace Ingen::Server; - -static void -ingen_lv2_main(SPtr<Engine> engine, const SPtr<LV2Driver>& driver) -{ - while (true) { - // Wait until there is work to be done - driver->main_sem().wait(); - - // Convert pending messages to events and push to pre processor - driver->consume_from_ui(); - - // Run post processor and maid to finalise events from last time - if (!engine->main_iteration()) { - return; - } - } -} - -struct IngenPlugin { - IngenPlugin() - : world(nullptr) - , main(nullptr) - , map(nullptr) - , argc(0) - , argv(nullptr) - {} - - Ingen::World* world; - SPtr<Engine> engine; - std::thread* main; - LV2_URID_Map* map; - int argc; - char** argv; -}; - -static Lib::Graphs -find_graphs(const URI& manifest_uri) -{ - Sord::World world; - Parser parser; - - const std::set<Parser::ResourceRecord> resources = parser.find_resources( - world, - manifest_uri, - URI(INGEN__Graph)); - - Lib::Graphs graphs; - for (const auto& r : resources) { - graphs.push_back(SPtr<const LV2Graph>(new LV2Graph(r))); - } - - return graphs; -} - -static LV2_Handle -ingen_instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* bundle_path, - const LV2_Feature*const* features) -{ - // Get features from features array - LV2_URID_Map* map = nullptr; - LV2_URID_Unmap* unmap = nullptr; - LV2_Log_Log* log = nullptr; - const LV2_Options_Option* options = nullptr; - for (int i = 0; features[i]; ++i) { - if (!strcmp(features[i]->URI, LV2_URID__map)) { - map = (LV2_URID_Map*)features[i]->data; - } else if (!strcmp(features[i]->URI, LV2_URID__unmap)) { - unmap = (LV2_URID_Unmap*)features[i]->data; - } else if (!strcmp(features[i]->URI, LV2_LOG__log)) { - log = (LV2_Log_Log*)features[i]->data; - } else if (!strcmp(features[i]->URI, LV2_OPTIONS__options)) { - options = (const LV2_Options_Option*)features[i]->data; - } - } - - LV2_Log_Logger logger; - lv2_log_logger_init(&logger, map, log); - - if (!map) { - lv2_log_error(&logger, "host did not provide URI map feature\n"); - return nullptr; - } else if (!unmap) { - lv2_log_error(&logger, "host did not provide URI unmap feature\n"); - return nullptr; - } - - set_bundle_path(bundle_path); - const std::string manifest_path = Ingen::bundle_file_path("manifest.ttl"); - SerdNode manifest_node = serd_node_new_file_uri( - (const uint8_t*)manifest_path.c_str(), nullptr, nullptr, true); - - Lib::Graphs graphs = find_graphs(URI((const char*)manifest_node.buf)); - serd_node_free(&manifest_node); - - const LV2Graph* graph = nullptr; - for (const auto& g : graphs) { - if (g->uri == descriptor->URI) { - graph = g.get(); - break; - } - } - - if (!graph) { - lv2_log_error(&logger, "could not find graph <%s>\n", descriptor->URI); - return nullptr; - } - - IngenPlugin* plugin = new IngenPlugin(); - plugin->map = map; - plugin->world = new Ingen::World(map, unmap, log); - plugin->world->load_configuration(plugin->argc, plugin->argv); - - LV2_URID bufsz_max = map->map(map->handle, LV2_BUF_SIZE__maxBlockLength); - LV2_URID bufsz_seq = map->map(map->handle, LV2_BUF_SIZE__sequenceSize); - LV2_URID atom_Int = map->map(map->handle, LV2_ATOM__Int); - int32_t block_length = 0; - int32_t seq_size = 0; - if (options) { - for (const LV2_Options_Option* o = options; o->key; ++o) { - if (o->key == bufsz_max && o->type == atom_Int) { - block_length = *(const int32_t*)o->value; - } else if (o->key == bufsz_seq && o->type == atom_Int) { - seq_size = *(const int32_t*)o->value; - } - } - } - if (block_length == 0) { - block_length = 4096; - plugin->world->log().warn("No maximum block length given\n"); - } - if (seq_size == 0) { - seq_size = 16384; - plugin->world->log().warn("No maximum sequence size given\n"); - } - - plugin->world->log().info( - fmt("Block: %1% frames, Sequence: %2% bytes\n") - % block_length % seq_size); - plugin->world->conf().set( - "queue-size", - plugin->world->forge().make(std::max(block_length, seq_size) * 4)); - - SPtr<Server::Engine> engine(new Server::Engine(plugin->world)); - plugin->engine = engine; - plugin->world->set_engine(engine); - - SPtr<Interface> interface = engine->interface(); - - plugin->world->set_interface(interface); - - Server::ThreadManager::set_flag(Server::THREAD_PRE_PROCESS); - Server::ThreadManager::single_threaded = true; - - LV2Driver* driver = new LV2Driver(*engine.get(), block_length, seq_size, rate); - engine->set_driver(SPtr<Ingen::Server::Driver>(driver)); - - engine->activate(); - Server::ThreadManager::single_threaded = true; - - std::lock_guard<std::mutex> lock(plugin->world->rdf_mutex()); - - // Locate to time 0 to process initialization events - engine->locate(0, block_length); - engine->post_processor()->set_end_time(block_length); - - // Parse graph, filling the queue with events to create it - plugin->world->interface()->bundle_begin(); - plugin->world->parser()->parse_file(plugin->world, - plugin->world->interface().get(), - graph->filename); - plugin->world->interface()->bundle_end(); - - // Drain event queue - while (engine->pending_events()) { - engine->process_all_events(); - engine->post_processor()->process(); - engine->maid()->cleanup(); - } - - /* Register client after loading graph so the to-ui ring does not overflow. - Since we are not yet rolling, it won't be drained, causing a deadlock. */ - SPtr<Interface> client(&driver->writer(), NullDeleter<Interface>); - interface->set_respondee(client); - engine->register_client(client); - - driver->set_instantiated(true); - return (LV2_Handle)plugin; -} - -static void -ingen_connect_port(LV2_Handle instance, uint32_t port, void* data) -{ - using namespace Ingen::Server; - - IngenPlugin* me = (IngenPlugin*)instance; - Server::Engine* engine = (Server::Engine*)me->world->engine().get(); - const SPtr<LV2Driver>& driver = static_ptr_cast<LV2Driver>(engine->driver()); - if (port < driver->ports().size()) { - driver->ports().at(port)->set_buffer(data); - } else { - engine->log().rt_error("Connect to non-existent port\n"); - } -} - -static void -ingen_activate(LV2_Handle instance) -{ - IngenPlugin* me = (IngenPlugin*)instance; - SPtr<Server::Engine> engine = static_ptr_cast<Server::Engine>(me->world->engine()); - const SPtr<LV2Driver>& driver = static_ptr_cast<LV2Driver>(engine->driver()); - engine->activate(); - me->main = new std::thread(ingen_lv2_main, engine, driver); -} - -static void -ingen_run(LV2_Handle instance, uint32_t sample_count) -{ - IngenPlugin* me = (IngenPlugin*)instance; - SPtr<Server::Engine> engine = static_ptr_cast<Server::Engine>(me->world->engine()); - const SPtr<LV2Driver>& driver = static_ptr_cast<LV2Driver>(engine->driver()); - - Server::ThreadManager::set_flag(Ingen::Server::THREAD_PROCESS); - Server::ThreadManager::set_flag(Ingen::Server::THREAD_IS_REAL_TIME); - - driver->run(sample_count); -} - -static void -ingen_deactivate(LV2_Handle instance) -{ - IngenPlugin* me = (IngenPlugin*)instance; - me->world->engine()->deactivate(); - if (me->main) { - me->main->join(); - delete me->main; - me->main = nullptr; - } -} - -static void -ingen_cleanup(LV2_Handle instance) -{ - IngenPlugin* me = (IngenPlugin*)instance; - me->world->set_engine(SPtr<Ingen::Server::Engine>()); - me->world->set_interface(SPtr<Ingen::Interface>()); - if (me->main) { - me->main->join(); - delete me->main; - } - - World* world = me->world; - delete me; - delete world; -} - -static void -get_state_features(const LV2_Feature* const* features, - LV2_State_Map_Path** map, - LV2_State_Make_Path** make) -{ - for (int i = 0; features[i]; ++i) { - if (map && !strcmp(features[i]->URI, LV2_STATE__mapPath)) { - *map = (LV2_State_Map_Path*)features[i]->data; - } else if (make && !strcmp(features[i]->URI, LV2_STATE__makePath)) { - *make = (LV2_State_Make_Path*)features[i]->data; - } - } -} - -static LV2_State_Status -ingen_save(LV2_Handle instance, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - IngenPlugin* plugin = (IngenPlugin*)instance; - - LV2_State_Map_Path* map_path = nullptr; - LV2_State_Make_Path* make_path = nullptr; - get_state_features(features, &map_path, &make_path); - if (!map_path || !make_path || !plugin->map) { - plugin->world->log().error("Missing state:mapPath, state:makePath, or urid:Map\n"); - return LV2_STATE_ERR_NO_FEATURE; - } - - LV2_URID ingen_file = plugin->map->map(plugin->map->handle, INGEN__file); - LV2_URID atom_Path = plugin->map->map(plugin->map->handle, - LV2_ATOM__Path); - - char* real_path = make_path->path(make_path->handle, "main.ttl"); - char* state_path = map_path->abstract_path(map_path->handle, real_path); - - auto root = plugin->world->store()->find(Raul::Path("/")); - - { - std::lock_guard<std::mutex> lock(plugin->world->rdf_mutex()); - - plugin->world->serialiser()->start_to_file(root->second->path(), real_path); - plugin->world->serialiser()->serialise(root->second); - plugin->world->serialiser()->finish(); - } - - store(handle, - ingen_file, - state_path, - strlen(state_path) + 1, - atom_Path, - LV2_STATE_IS_POD); - - free(state_path); - free(real_path); - return LV2_STATE_SUCCESS; -} - -static LV2_State_Status -ingen_restore(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature* const* features) -{ - IngenPlugin* plugin = (IngenPlugin*)instance; - - LV2_State_Map_Path* map_path = nullptr; - get_state_features(features, &map_path, nullptr); - if (!map_path) { - plugin->world->log().error("Missing state:mapPath\n"); - return LV2_STATE_ERR_NO_FEATURE; - } - - LV2_URID ingen_file = plugin->map->map(plugin->map->handle, INGEN__file); - size_t size; - uint32_t type; - uint32_t valflags; - - // Get abstract path to graph file - const char* path = (const char*)retrieve( - handle, ingen_file, &size, &type, &valflags); - if (!path) { - return LV2_STATE_ERR_NO_PROPERTY; - } - - // Convert to absolute path - char* real_path = map_path->absolute_path(map_path->handle, path); - if (!real_path) { - return LV2_STATE_ERR_UNKNOWN; - } - -#if 0 - // Remove existing root graph contents - SPtr<Engine> engine = plugin->engine; - for (const auto& b : engine->root_graph()->blocks()) { - plugin->world->interface()->del(b.uri()); - } - - const uint32_t n_ports = engine->root_graph()->num_ports_non_rt(); - for (int32_t i = n_ports - 1; i >= 0; --i) { - PortImpl* port = engine->root_graph()->port_impl(i); - if (port->symbol() != "control" && port->symbol() != "notify") { - plugin->world->interface()->del(port->uri()); - } - } -#endif - - // Load new graph - std::lock_guard<std::mutex> lock(plugin->world->rdf_mutex()); - plugin->world->parser()->parse_file( - plugin->world, plugin->world->interface().get(), real_path); - - free(real_path); - return LV2_STATE_SUCCESS; -} - -static const void* -ingen_extension_data(const char* uri) -{ - static const LV2_State_Interface state = { ingen_save, ingen_restore }; - if (!strcmp(uri, LV2_STATE__interface)) { - return &state; - } - return nullptr; -} - -LV2Graph::LV2Graph(Parser::ResourceRecord record) - : Parser::ResourceRecord(std::move(record)) -{ - descriptor.URI = uri.c_str(); - descriptor.instantiate = ingen_instantiate; - descriptor.connect_port = ingen_connect_port; - descriptor.activate = ingen_activate; - descriptor.run = ingen_run; - descriptor.deactivate = ingen_deactivate; - descriptor.cleanup = ingen_cleanup; - descriptor.extension_data = ingen_extension_data; -} - -Lib::Lib(const char* bundle_path) -{ - Ingen::set_bundle_path(bundle_path); - const std::string manifest_path = Ingen::bundle_file_path("manifest.ttl"); - SerdNode manifest_node = serd_node_new_file_uri( - (const uint8_t*)manifest_path.c_str(), nullptr, nullptr, true); - - graphs = find_graphs(URI((const char*)manifest_node.buf)); - - serd_node_free(&manifest_node); -} - -static void -lib_cleanup(LV2_Lib_Handle handle) -{ - Lib* lib = (Lib*)handle; - delete lib; -} - -static const LV2_Descriptor* -lib_get_plugin(LV2_Lib_Handle handle, uint32_t index) -{ - Lib* lib = (Lib*)handle; - return index < lib->graphs.size() ? &lib->graphs[index]->descriptor : nullptr; -} - -/** LV2 plugin library entry point */ -LV2_SYMBOL_EXPORT -const LV2_Lib_Descriptor* -lv2_lib_descriptor(const char* bundle_path, - const LV2_Feature*const* features) -{ - static const uint32_t desc_size = sizeof(LV2_Lib_Descriptor); - Lib* lib = new Lib(bundle_path); - - // FIXME: memory leak. I think the LV2_Lib_Descriptor API is botched :( - LV2_Lib_Descriptor* desc = (LV2_Lib_Descriptor*)malloc(desc_size); - desc->handle = lib; - desc->size = desc_size; - desc->cleanup = lib_cleanup; - desc->get_plugin = lib_get_plugin; - - return desc; -} - -} // extern "C" diff --git a/src/server/ingen_portaudio.cpp b/src/server/ingen_portaudio.cpp deleted file mode 100644 index e4065342..00000000 --- a/src/server/ingen_portaudio.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <string> - -#include "ingen/Atom.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/Configuration.hpp" -#include "ingen/Log.hpp" -#include "ingen/Module.hpp" -#include "ingen/World.hpp" - -#include "PortAudioDriver.hpp" -#include "Engine.hpp" - -using namespace Ingen; - -struct IngenPortAudioModule : public Ingen::Module { - void load(Ingen::World* world) { - if (((Server::Engine*)world->engine().get())->driver()) { - world->log().warn("Engine already has a driver\n"); - return; - } - - Server::PortAudioDriver* driver = new Server::PortAudioDriver( - *(Server::Engine*)world->engine().get()); - driver->attach(); - ((Server::Engine*)world->engine().get())->set_driver( - SPtr<Server::Driver>(driver)); - } -}; - -extern "C" { - -Ingen::Module* -ingen_module_load() -{ - return new IngenPortAudioModule(); -} - -} // extern "C" diff --git a/src/server/internals/BlockDelay.cpp b/src/server/internals/BlockDelay.cpp deleted file mode 100644 index 6b27ed83..00000000 --- a/src/server/internals/BlockDelay.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <climits> - -#include <cmath> - -#include "ingen/URIs.hpp" -#include "raul/Array.hpp" -#include "raul/Maid.hpp" - -#include "Buffer.hpp" -#include "InputPort.hpp" -#include "InternalPlugin.hpp" -#include "OutputPort.hpp" -#include "RunContext.hpp" -#include "internals/BlockDelay.hpp" - -namespace Ingen { -namespace Server { -namespace Internals { - -InternalPlugin* BlockDelayNode::internal_plugin(URIs& uris) { - return new InternalPlugin( - uris, URI(NS_INTERNALS "BlockDelay"), Raul::Symbol("blockDelay")); -} - -BlockDelayNode::BlockDelayNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate) - : InternalBlock(plugin, symbol, polyphonic, parent, srate) -{ - const Ingen::URIs& uris = bufs.uris(); - _ports = bufs.maid().make_managed<Ports>(2); - - _in_port = new InputPort(bufs, this, Raul::Symbol("in"), 0, 1, - PortType::AUDIO, 0, bufs.forge().make(0.0f)); - _in_port->set_property(uris.lv2_name, bufs.forge().alloc("In")); - _ports->at(0) = _in_port; - - _out_port = new OutputPort(bufs, this, Raul::Symbol("out"), 0, 1, - PortType::AUDIO, 0, bufs.forge().make(0.0f)); - _out_port->set_property(uris.lv2_name, bufs.forge().alloc("Out")); - _ports->at(1) = _out_port; -} - -BlockDelayNode::~BlockDelayNode() -{ - _buffer.reset(); -} - -void -BlockDelayNode::activate(BufferFactory& bufs) -{ - _buffer = bufs.create( - bufs.uris().atom_Sound, 0, bufs.audio_buffer_size()); - - BlockImpl::activate(bufs); -} - -void -BlockDelayNode::run(RunContext& context) -{ - // Copy buffer from last cycle to output - _out_port->buffer(0)->copy(context, _buffer.get()); - - // Copy input from this cycle to buffer - _buffer->copy(context, _in_port->buffer(0).get()); -} - -} // namespace Internals -} // namespace Server -} // namespace Ingen diff --git a/src/server/internals/BlockDelay.hpp b/src/server/internals/BlockDelay.hpp deleted file mode 100644 index e1ef5311..00000000 --- a/src/server/internals/BlockDelay.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_INTERNALS_BLOCKDELAY_HPP -#define INGEN_INTERNALS_BLOCKDELAY_HPP - -#include "BufferRef.hpp" -#include "InternalBlock.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Server { - -class InputPort; -class OutputPort; -class InternalPlugin; -class BufferFactory; - -namespace Internals { - -class BlockDelayNode : public InternalBlock -{ -public: - BlockDelayNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate); - - ~BlockDelayNode(); - - void activate(BufferFactory& bufs); - - void run(RunContext& context); - - static InternalPlugin* internal_plugin(URIs& uris); - -private: - InputPort* _in_port; - OutputPort* _out_port; - BufferRef _buffer; -}; - -} // namespace Server -} // namespace Ingen -} // namespace Internals - -#endif // INGEN_INTERNALS_BLOCKDELAY_HPP diff --git a/src/server/internals/Controller.cpp b/src/server/internals/Controller.cpp deleted file mode 100644 index 4c1cf45a..00000000 --- a/src/server/internals/Controller.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cmath> - -#include "ingen/URIs.hpp" -#include "internals/Controller.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "lv2/lv2plug.in/ns/ext/midi/midi.h" - -#include "Buffer.hpp" -#include "Engine.hpp" -#include "InputPort.hpp" -#include "InternalPlugin.hpp" -#include "OutputPort.hpp" -#include "PostProcessor.hpp" -#include "RunContext.hpp" -#include "util.hpp" - -namespace Ingen { -namespace Server { -namespace Internals { - -InternalPlugin* ControllerNode::internal_plugin(URIs& uris) { - return new InternalPlugin( - uris, URI(NS_INTERNALS "Controller"), Raul::Symbol("controller")); -} - -ControllerNode::ControllerNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate) - : InternalBlock(plugin, symbol, false, parent, srate) - , _learning(false) -{ - const Ingen::URIs& uris = bufs.uris(); - _ports = bufs.maid().make_managed<Ports>(7); - - const Atom zero = bufs.forge().make(0.0f); - const Atom one = bufs.forge().make(1.0f); - const Atom atom_Float = bufs.forge().make_urid(URI(LV2_ATOM__Float)); - - _midi_in_port = new InputPort(bufs, this, Raul::Symbol("input"), 0, 1, - PortType::ATOM, uris.atom_Sequence, Atom()); - _midi_in_port->set_property(uris.lv2_name, bufs.forge().alloc("Input")); - _midi_in_port->set_property(uris.atom_supports, - bufs.forge().make_urid(uris.midi_MidiEvent)); - _ports->at(0) = _midi_in_port; - - _midi_out_port = new OutputPort(bufs, this, Raul::Symbol("event"), 1, 1, - PortType::ATOM, uris.atom_Sequence, Atom()); - _midi_out_port->set_property(uris.lv2_name, bufs.forge().alloc("Event")); - _midi_out_port->set_property(uris.atom_supports, - bufs.forge().make_urid(uris.midi_MidiEvent)); - _ports->at(1) = _midi_out_port; - - _param_port = new InputPort(bufs, this, Raul::Symbol("controller"), 2, 1, - PortType::ATOM, uris.atom_Sequence, zero); - _param_port->set_property(uris.atom_supports, atom_Float); - _param_port->set_property(uris.lv2_minimum, zero); - _param_port->set_property(uris.lv2_maximum, bufs.forge().make(127.0f)); - _param_port->set_property(uris.lv2_portProperty, uris.lv2_integer); - _param_port->set_property(uris.lv2_name, bufs.forge().alloc("Controller")); - _ports->at(2) = _param_port; - - _log_port = new InputPort(bufs, this, Raul::Symbol("logarithmic"), 3, 1, - PortType::ATOM, uris.atom_Sequence, zero); - _log_port->set_property(uris.atom_supports, atom_Float); - _log_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); - _log_port->set_property(uris.lv2_name, bufs.forge().alloc("Logarithmic")); - _ports->at(3) = _log_port; - - _min_port = new InputPort(bufs, this, Raul::Symbol("minimum"), 4, 1, - PortType::ATOM, uris.atom_Sequence, zero); - _min_port->set_property(uris.atom_supports, atom_Float); - _min_port->set_property(uris.lv2_name, bufs.forge().alloc("Minimum")); - _ports->at(4) = _min_port; - - _max_port = new InputPort(bufs, this, Raul::Symbol("maximum"), 5, 1, - PortType::ATOM, uris.atom_Sequence, one); - _max_port->set_property(uris.atom_supports, atom_Float); - _max_port->set_property(uris.lv2_name, bufs.forge().alloc("Maximum")); - _ports->at(5) = _max_port; - - _audio_port = new OutputPort(bufs, this, Raul::Symbol("output"), 6, 1, - PortType::ATOM, uris.atom_Sequence, zero); - _audio_port->set_property(uris.atom_supports, atom_Float); - _audio_port->set_property(uris.lv2_name, bufs.forge().alloc("Output")); - _ports->at(6) = _audio_port; -} - -void -ControllerNode::run(RunContext& context) -{ - const BufferRef midi_in = _midi_in_port->buffer(0); - LV2_Atom_Sequence* seq = midi_in->get<LV2_Atom_Sequence>(); - const BufferRef midi_out = _midi_out_port->buffer(0); - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - const uint8_t* buf = (const uint8_t*)LV2_ATOM_BODY(&ev->body); - if (ev->body.type == _midi_in_port->bufs().uris().midi_MidiEvent && - ev->body.size >= 3 && - lv2_midi_message_type(buf) == LV2_MIDI_MSG_CONTROLLER) { - if (control(context, buf[1], buf[2], ev->time.frames + context.start())) { - midi_out->append_event(ev->time.frames, &ev->body); - } - } - } -} - -bool -ControllerNode::control(RunContext& context, uint8_t control_num, uint8_t val, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - const uint32_t offset = time - context.start(); - - const Sample nval = (val / 127.0f); // normalized [0, 1] - - if (_learning) { - _param_port->set_control_value(context, time, control_num); - _param_port->force_monitor_update(); - _learning = false; - } else { - _param_port->update_values(offset, 0); - } - - if (control_num != _param_port->buffer(0)->value_at(offset)) { - return false; - } - - for (const auto& port : { _min_port, _max_port, _log_port }) { - port->update_values(offset, 0); - } - - const Sample min_port_val = _min_port->buffer(0)->value_at(offset); - const Sample max_port_val = _max_port->buffer(0)->value_at(offset); - const Sample log_port_val = _log_port->buffer(0)->value_at(offset); - - Sample scaled_value; - if (log_port_val > 0.0f) { - // haaaaack, stupid negatives and logarithms - Sample log_offset = 0; - if (min_port_val < 0) { - log_offset = fabs(min_port_val); - } - const Sample min = log(min_port_val + 1 + log_offset); - const Sample max = log(max_port_val + 1 + log_offset); - scaled_value = expf(nval * (max - min) + min) - 1 - log_offset; - } else { - scaled_value = ((nval) * (max_port_val - min_port_val)) + min_port_val; - } - - _audio_port->set_control_value(context, time, scaled_value); - - return true; -} - -} // namespace Internals -} // namespace Server -} // namespace Ingen diff --git a/src/server/internals/Controller.hpp b/src/server/internals/Controller.hpp deleted file mode 100644 index 720f78c0..00000000 --- a/src/server/internals/Controller.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_INTERNALS_CONTROLLER_HPP -#define INGEN_INTERNALS_CONTROLLER_HPP - -#include "InternalBlock.hpp" - -namespace Ingen { -namespace Server { - -class InputPort; -class OutputPort; -class InternalPlugin; - -namespace Internals { - -/** MIDI control input block. - * - * Creating one of these nodes is how a user makes "MIDI Bindings". Note that - * this node will always be monophonic, the poly parameter is ignored. - * - * \ingroup engine - */ -class ControllerNode : public InternalBlock -{ -public: - ControllerNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate); - - void run(RunContext& context); - - bool control(RunContext& context, uint8_t control_num, uint8_t val, FrameTime time); - - void learn() { _learning = true; } - - static InternalPlugin* internal_plugin(URIs& uris); - -private: - InputPort* _midi_in_port; - OutputPort* _midi_out_port; - InputPort* _param_port; - InputPort* _log_port; - InputPort* _min_port; - InputPort* _max_port; - OutputPort* _audio_port; - bool _learning; -}; - -} // namespace Server -} // namespace Ingen -} // namespace Internals - -#endif // INGEN_INTERNALS_CONTROLLER_HPP diff --git a/src/server/internals/Note.cpp b/src/server/internals/Note.cpp deleted file mode 100644 index b39dd1d4..00000000 --- a/src/server/internals/Note.cpp +++ /dev/null @@ -1,420 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cmath> - -#include "ingen/URIs.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "lv2/lv2plug.in/ns/ext/midi/midi.h" -#include "raul/Array.hpp" -#include "raul/Maid.hpp" - -#include "Buffer.hpp" -#include "GraphImpl.hpp" -#include "InputPort.hpp" -#include "InternalPlugin.hpp" -#include "OutputPort.hpp" -#include "RunContext.hpp" -#include "ingen_config.h" -#include "internals/Note.hpp" -#include "util.hpp" - -// #define NOTE_DEBUG 1 - -namespace Ingen { -namespace Server { -namespace Internals { - -InternalPlugin* NoteNode::internal_plugin(URIs& uris) { - return new InternalPlugin( - uris, URI(NS_INTERNALS "Note"), Raul::Symbol("note")); -} - -NoteNode::NoteNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate) - : InternalBlock(plugin, symbol, polyphonic, parent, srate) - , _voices(bufs.maid().make_managed<Voices>(_polyphony)) - , _sustain(false) -{ - const Ingen::URIs& uris = bufs.uris(); - _ports = bufs.maid().make_managed<Ports>(8); - - const Atom zero = bufs.forge().make(0.0f); - const Atom one = bufs.forge().make(1.0f); - - _midi_in_port = new InputPort(bufs, this, Raul::Symbol("input"), 0, 1, - PortType::ATOM, uris.atom_Sequence, Atom()); - _midi_in_port->set_property(uris.lv2_name, bufs.forge().alloc("Input")); - _midi_in_port->set_property(uris.atom_supports, - bufs.forge().make_urid(uris.midi_MidiEvent)); - _ports->at(0) = _midi_in_port; - - _freq_port = new OutputPort(bufs, this, Raul::Symbol("frequency"), 1, _polyphony, - PortType::ATOM, uris.atom_Sequence, - bufs.forge().make(440.0f)); - _freq_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _freq_port->set_property(uris.lv2_name, bufs.forge().alloc("Frequency")); - _freq_port->set_property(uris.lv2_minimum, bufs.forge().make(16.0f)); - _freq_port->set_property(uris.lv2_maximum, bufs.forge().make(25088.0f)); - _ports->at(1) = _freq_port; - - _num_port = new OutputPort(bufs, this, Raul::Symbol("number"), 1, _polyphony, - PortType::ATOM, uris.atom_Sequence, zero); - _num_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _num_port->set_property(uris.lv2_minimum, zero); - _num_port->set_property(uris.lv2_maximum, bufs.forge().make(127.0f)); - _num_port->set_property(uris.lv2_portProperty, uris.lv2_integer); - _num_port->set_property(uris.lv2_name, bufs.forge().alloc("Number")); - _ports->at(2) = _num_port; - - _vel_port = new OutputPort(bufs, this, Raul::Symbol("velocity"), 2, _polyphony, - PortType::ATOM, uris.atom_Sequence, zero); - _vel_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _vel_port->set_property(uris.lv2_minimum, zero); - _vel_port->set_property(uris.lv2_maximum, one); - _vel_port->set_property(uris.lv2_name, bufs.forge().alloc("Velocity")); - _ports->at(3) = _vel_port; - - _gate_port = new OutputPort(bufs, this, Raul::Symbol("gate"), 3, _polyphony, - PortType::ATOM, uris.atom_Sequence, zero); - _gate_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _gate_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); - _gate_port->set_property(uris.lv2_name, bufs.forge().alloc("Gate")); - _ports->at(4) = _gate_port; - - _trig_port = new OutputPort(bufs, this, Raul::Symbol("trigger"), 4, _polyphony, - PortType::ATOM, uris.atom_Sequence, zero); - _trig_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _trig_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); - _trig_port->set_property(uris.lv2_name, bufs.forge().alloc("Trigger")); - _ports->at(5) = _trig_port; - - _bend_port = new OutputPort(bufs, this, Raul::Symbol("bend"), 5, _polyphony, - PortType::ATOM, uris.atom_Sequence, zero); - _bend_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _bend_port->set_property(uris.lv2_name, bufs.forge().alloc("Bender")); - _bend_port->set_property(uris.lv2_default, zero); - _bend_port->set_property(uris.lv2_minimum, bufs.forge().make(-1.0f)); - _bend_port->set_property(uris.lv2_maximum, one); - _ports->at(6) = _bend_port; - - _pressure_port = new OutputPort(bufs, this, Raul::Symbol("pressure"), 6, _polyphony, - PortType::ATOM, uris.atom_Sequence, zero); - _pressure_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _pressure_port->set_property(uris.lv2_name, bufs.forge().alloc("Pressure")); - _pressure_port->set_property(uris.lv2_default, zero); - _pressure_port->set_property(uris.lv2_minimum, zero); - _pressure_port->set_property(uris.lv2_maximum, one); - _ports->at(7) = _pressure_port; -} - -bool -NoteNode::prepare_poly(BufferFactory& bufs, uint32_t poly) -{ - if (!_polyphonic) { - return true; - } - - BlockImpl::prepare_poly(bufs, poly); - - if (_prepared_voices && poly <= _prepared_voices->size()) { - return true; - } - - _prepared_voices = bufs.maid().make_managed<Voices>( - poly, *_voices, Voice()); - - return true; -} - -bool -NoteNode::apply_poly(RunContext& context, uint32_t poly) -{ - if (!BlockImpl::apply_poly(context, poly)) { - return false; - } - - if (_prepared_voices) { - assert(_polyphony <= _prepared_voices->size()); - _voices = std::move(_prepared_voices); - } - assert(_polyphony <= _voices->size()); - - return true; -} - -void -NoteNode::run(RunContext& context) -{ - Buffer* const midi_in = _midi_in_port->buffer(0).get(); - LV2_Atom_Sequence* seq = midi_in->get<LV2_Atom_Sequence>(); - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - const uint8_t* buf = (const uint8_t*)LV2_ATOM_BODY_CONST(&ev->body); - const FrameTime time = context.start() + (FrameTime)ev->time.frames; - if (ev->body.type == _midi_in_port->bufs().uris().midi_MidiEvent && - ev->body.size >= 3) { - switch (lv2_midi_message_type(buf)) { - case LV2_MIDI_MSG_NOTE_ON: - if (buf[2] == 0) { - note_off(context, buf[1], time); - } else { - note_on(context, buf[1], buf[2], time); - } - break; - case LV2_MIDI_MSG_NOTE_OFF: - note_off(context, buf[1], time); - break; - case LV2_MIDI_MSG_CONTROLLER: - switch (buf[1]) { - case LV2_MIDI_CTL_ALL_NOTES_OFF: - case LV2_MIDI_CTL_ALL_SOUNDS_OFF: - all_notes_off(context, time); - break; - case LV2_MIDI_CTL_SUSTAIN: - if (buf[2] > 63) { - sustain_on(context, time); - } else { - sustain_off(context, time); - } - break; - } - break; - case LV2_MIDI_MSG_BENDER: - bend(context, time, (((((uint16_t)buf[2] << 7) | buf[1]) - 8192.0f) - / 8192.0f)); - break; - case LV2_MIDI_MSG_CHANNEL_PRESSURE: - channel_pressure(context, time, buf[1] / 127.0f); - break; - case LV2_MIDI_MSG_NOTE_PRESSURE: - note_pressure(context, time, buf[1], buf[2] / 127.0f); - break; - default: - break; - } - } - } -} - -static inline float -note_to_freq(uint8_t num) -{ - static const float A4 = 440.0f; - return A4 * powf(2.0f, (float)(num - 57.0f) / 12.0f); -} - -void -NoteNode::note_on(RunContext& context, uint8_t note_num, uint8_t velocity, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - assert(note_num <= 127); - - Key* key = &_keys[note_num]; - Voice* voice = nullptr; - uint32_t voice_num = 0; - - if (key->state != Key::State::OFF) { - return; - } - - // Look for free voices - for (uint32_t i=0; i < _polyphony; ++i) { - if ((*_voices)[i].state == Voice::State::FREE) { - voice = &(*_voices)[i]; - voice_num = i; - break; - } - } - - // If we didn't find a free one, steal the oldest - if (voice == nullptr) { - voice_num = 0; - voice = &(*_voices)[0]; - FrameTime oldest_time = (*_voices)[0].time; - for (uint32_t i=1; i < _polyphony; ++i) { - if ((*_voices)[i].time < oldest_time) { - voice = &(*_voices)[i]; - voice_num = i; - oldest_time = voice->time; - } - } - } - assert(voice != nullptr); - assert(voice == &(*_voices)[voice_num]); - - // Update stolen key, if applicable - if (voice->state == Voice::State::ACTIVE) { - assert(_keys[voice->note].state == Key::State::ON_ASSIGNED); - assert(_keys[voice->note].voice == voice_num); - _keys[voice->note].state = Key::State::ON_UNASSIGNED; - } - - // Store key information for later reallocation on note off - key->state = Key::State::ON_ASSIGNED; - key->voice = voice_num; - key->time = time; - - // Check if we just triggered this voice at the same time - // (Double note-on at the same sample on the same voice) - const bool double_trigger = (voice->state == Voice::State::ACTIVE && - voice->time == time); - - // Trigger voice - voice->state = Voice::State::ACTIVE; - voice->note = note_num; - voice->time = time; - - assert(_keys[voice->note].state == Key::State::ON_ASSIGNED); - assert(_keys[voice->note].voice == voice_num); - - _freq_port->set_voice_value(context, voice_num, time, note_to_freq(note_num)); - _num_port->set_voice_value(context, voice_num, time, (float)note_num); - _vel_port->set_voice_value(context, voice_num, time, velocity / 127.0f); - _gate_port->set_voice_value(context, voice_num, time, 1.0f); - if (!double_trigger) { - _trig_port->set_voice_value(context, voice_num, time, 1.0f); - _trig_port->set_voice_value(context, voice_num, time + 1, 0.0f); - } - - assert(key->state == Key::State::ON_ASSIGNED); - assert(voice->state == Voice::State::ACTIVE); - assert(key->voice == voice_num); - assert((*_voices)[key->voice].note == note_num); -} - -void -NoteNode::note_off(RunContext& context, uint8_t note_num, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - - Key* key = &_keys[note_num]; - - if (key->state == Key::State::ON_ASSIGNED) { - // Assigned key, turn off voice and key - if ((*_voices)[key->voice].state == Voice::State::ACTIVE) { - assert((*_voices)[key->voice].note == note_num); - if ( ! _sustain) { - free_voice(context, key->voice, time); - } else { - (*_voices)[key->voice].state = Voice::State::HOLDING; - } - } - } - - key->state = Key::State::OFF; -} - -void -NoteNode::free_voice(RunContext& context, uint32_t voice, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - - // Find a key to reassign to the freed voice (the newest, if there is one) - Key* replace_key = nullptr; - uint8_t replace_key_num = 0; - - for (uint8_t i = 0; i <= 127; ++i) { - if (_keys[i].state == Key::State::ON_UNASSIGNED) { - if (replace_key == nullptr || _keys[i].time > replace_key->time) { - replace_key = &_keys[i]; - replace_key_num = i; - } - } - } - - if (replace_key != nullptr) { // Found a key to assign to freed voice - assert(&_keys[replace_key_num] == replace_key); - assert(replace_key->state == Key::State::ON_UNASSIGNED); - - // Change the freq but leave the gate high and don't retrigger - _freq_port->set_voice_value(context, voice, time, note_to_freq(replace_key_num)); - _num_port->set_voice_value(context, voice, time, replace_key_num); - - replace_key->state = Key::State::ON_ASSIGNED; - replace_key->voice = voice; - _keys[(*_voices)[voice].note].state = Key::State::ON_UNASSIGNED; - (*_voices)[voice].note = replace_key_num; - (*_voices)[voice].state = Voice::State::ACTIVE; - } else { - // No new note for voice, deactivate (set gate low) - _gate_port->set_voice_value(context, voice, time, 0.0f); - (*_voices)[voice].state = Voice::State::FREE; - } -} - -void -NoteNode::all_notes_off(RunContext& context, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - - // FIXME: set all keys to Key::OFF? - - for (uint32_t i = 0; i < _polyphony; ++i) { - _gate_port->set_voice_value(context, i, time, 0.0f); - (*_voices)[i].state = Voice::State::FREE; - } -} - -void -NoteNode::sustain_on(RunContext& context, FrameTime time) -{ - _sustain = true; -} - -void -NoteNode::sustain_off(RunContext& context, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - - _sustain = false; - - for (uint32_t i=0; i < _polyphony; ++i) { - if ((*_voices)[i].state == Voice::State::HOLDING) { - free_voice(context, i, time); - } - } -} - -void -NoteNode::bend(RunContext& context, FrameTime time, float amount) -{ - _bend_port->set_control_value(context, time, amount); -} - -void -NoteNode::note_pressure(RunContext& context, FrameTime time, uint8_t note_num, float amount) -{ - for (uint32_t i=0; i < _polyphony; ++i) { - if ((*_voices)[i].state != Voice::State::FREE && (*_voices)[i].note == note_num) { - _pressure_port->set_voice_value(context, i, time, amount); - return; - } - } -} - -void -NoteNode::channel_pressure(RunContext& context, FrameTime time, float amount) -{ - _pressure_port->set_control_value(context, time, amount); -} - -} // namespace Internals -} // namespace Server -} // namespace Ingen diff --git a/src/server/internals/Note.hpp b/src/server/internals/Note.hpp deleted file mode 100644 index 1e60c130..00000000 --- a/src/server/internals/Note.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_INTERNALS_NOTE_HPP -#define INGEN_INTERNALS_NOTE_HPP - -#include "InternalBlock.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Server { - -class InputPort; -class OutputPort; -class InternalPlugin; - -namespace Internals { - -/** MIDI note input block. - * - * For pitched instruments like keyboard, etc. - * - * \ingroup engine - */ -class NoteNode : public InternalBlock -{ -public: - NoteNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate); - - bool prepare_poly(BufferFactory& bufs, uint32_t poly); - bool apply_poly(RunContext& context, uint32_t poly); - - void run(RunContext& context); - - void note_on(RunContext& context, uint8_t note_num, uint8_t velocity, FrameTime time); - void note_off(RunContext& context, uint8_t note_num, FrameTime time); - void all_notes_off(RunContext& context, FrameTime time); - - void sustain_on(RunContext& context, FrameTime time); - void sustain_off(RunContext& context, FrameTime time); - - void bend(RunContext& context, FrameTime time, float amount); - void note_pressure(RunContext& context, FrameTime time, uint8_t note_num, float amount); - void channel_pressure(RunContext& context, FrameTime time, float amount); - - static InternalPlugin* internal_plugin(URIs& uris); - -private: - /** Key, one for each key on the keyboard */ - struct Key { - enum class State { OFF, ON_ASSIGNED, ON_UNASSIGNED }; - Key() : state(State::OFF), voice(0), time(0) {} - State state; - uint32_t voice; - SampleCount time; - }; - - /** Voice, one of these always exists for each voice */ - struct Voice { - enum class State { FREE, ACTIVE, HOLDING }; - Voice() : state(State::FREE), note(0), time(0) {} - State state; - uint8_t note; - SampleCount time; - }; - - typedef Raul::Array<Voice> Voices; - - void free_voice(RunContext& context, uint32_t voice, FrameTime time); - - MPtr<Voices> _voices; - MPtr<Voices> _prepared_voices; - - Key _keys[128]; - bool _sustain; ///< Whether or not hold pedal is depressed - - InputPort* _midi_in_port; - OutputPort* _freq_port; - OutputPort* _num_port; - OutputPort* _vel_port; - OutputPort* _gate_port; - OutputPort* _trig_port; - OutputPort* _bend_port; - OutputPort* _pressure_port; -}; - -} // namespace Server -} // namespace Ingen -} // namespace Internals - -#endif // INGEN_INTERNALS_NOTE_HPP diff --git a/src/server/internals/Time.cpp b/src/server/internals/Time.cpp deleted file mode 100644 index 5474bf21..00000000 --- a/src/server/internals/Time.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen/URIs.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "lv2/lv2plug.in/ns/ext/midi/midi.h" - -#include "Buffer.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "InternalPlugin.hpp" -#include "OutputPort.hpp" -#include "RunContext.hpp" -#include "internals/Time.hpp" -#include "util.hpp" - -namespace Ingen { -namespace Server { -namespace Internals { - -InternalPlugin* TimeNode::internal_plugin(URIs& uris) { - return new InternalPlugin( - uris, URI(NS_INTERNALS "Time"), Raul::Symbol("time")); -} - -TimeNode::TimeNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate) - : InternalBlock(plugin, symbol, false, parent, srate) -{ - const Ingen::URIs& uris = bufs.uris(); - _ports = bufs.maid().make_managed<Ports>(1); - - _notify_port = new OutputPort( - bufs, this, Raul::Symbol("notify"), 0, 1, - PortType::ATOM, uris.atom_Sequence, Atom(), 1024); - _notify_port->set_property(uris.lv2_name, bufs.forge().alloc("Notify")); - _notify_port->set_property(uris.atom_supports, - bufs.forge().make_urid(uris.time_Position)); - _ports->at(0) = _notify_port; -} - -void -TimeNode::run(RunContext& context) -{ - BufferRef buf = _notify_port->buffer(0); - LV2_Atom_Sequence* seq = buf->get<LV2_Atom_Sequence>(); - - // Initialise output to the empty sequence - seq->atom.type = _notify_port->bufs().uris().atom_Sequence; - seq->atom.size = sizeof(LV2_Atom_Sequence_Body); - seq->body.unit = 0; - seq->body.pad = 0; - - // Ask the driver to append any time events for this cycle - context.engine().driver()->append_time_events( - context, *_notify_port->buffer(0)); -} - -} // namespace Internals -} // namespace Server -} // namespace Ingen diff --git a/src/server/internals/Time.hpp b/src/server/internals/Time.hpp deleted file mode 100644 index 1a063f8d..00000000 --- a/src/server/internals/Time.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_INTERNALS_TIME_HPP -#define INGEN_INTERNALS_TIME_HPP - -#include "InternalBlock.hpp" - -namespace Ingen { -namespace Server { - -class InputPort; -class OutputPort; -class InternalPlugin; - -namespace Internals { - -/** Time information block. - * - * This sends messages whenever the transport speed or tempo changes. - * - * \ingroup engine - */ -class TimeNode : public InternalBlock -{ -public: - TimeNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate); - - void run(RunContext& context); - - static InternalPlugin* internal_plugin(URIs& uris); - -private: - OutputPort* _notify_port; -}; - -} // namespace Server -} // namespace Ingen -} // namespace Internals - -#endif // INGEN_INTERNALS_TIME_HPP diff --git a/src/server/internals/Trigger.cpp b/src/server/internals/Trigger.cpp deleted file mode 100644 index 69967877..00000000 --- a/src/server/internals/Trigger.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <cmath> - -#include "ingen/URIs.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" -#include "lv2/lv2plug.in/ns/ext/midi/midi.h" - -#include "Buffer.hpp" -#include "Engine.hpp" -#include "InputPort.hpp" -#include "InternalPlugin.hpp" -#include "OutputPort.hpp" -#include "RunContext.hpp" -#include "ingen_config.h" -#include "internals/Trigger.hpp" -#include "util.hpp" - -namespace Ingen { -namespace Server { -namespace Internals { - -InternalPlugin* TriggerNode::internal_plugin(URIs& uris) { - return new InternalPlugin( - uris, URI(NS_INTERNALS "Trigger"), Raul::Symbol("trigger")); -} - -TriggerNode::TriggerNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate) - : InternalBlock(plugin, symbol, false, parent, srate) - , _learning(false) -{ - const Ingen::URIs& uris = bufs.uris(); - _ports = bufs.maid().make_managed<Ports>(6); - - const Atom zero = bufs.forge().make(0.0f); - - _midi_in_port = new InputPort(bufs, this, Raul::Symbol("input"), 0, 1, - PortType::ATOM, uris.atom_Sequence, Atom()); - _midi_in_port->set_property(uris.lv2_name, bufs.forge().alloc("Input")); - _midi_in_port->set_property(uris.atom_supports, - bufs.forge().make_urid(uris.midi_MidiEvent)); - _ports->at(0) = _midi_in_port; - - _midi_out_port = new OutputPort(bufs, this, Raul::Symbol("event"), 1, 1, - PortType::ATOM, uris.atom_Sequence, Atom()); - _midi_out_port->set_property(uris.lv2_name, bufs.forge().alloc("Event")); - _midi_out_port->set_property(uris.atom_supports, - bufs.forge().make_urid(uris.midi_MidiEvent)); - _ports->at(1) = _midi_out_port; - - _note_port = new InputPort(bufs, this, Raul::Symbol("note"), 2, 1, - PortType::ATOM, uris.atom_Sequence, - bufs.forge().make(60.0f)); - _note_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _note_port->set_property(uris.lv2_minimum, zero); - _note_port->set_property(uris.lv2_maximum, bufs.forge().make(127.0f)); - _note_port->set_property(uris.lv2_portProperty, uris.lv2_integer); - _note_port->set_property(uris.lv2_name, bufs.forge().alloc("Note")); - _ports->at(2) = _note_port; - - _gate_port = new OutputPort(bufs, this, Raul::Symbol("gate"), 3, 1, - PortType::ATOM, uris.atom_Sequence, zero); - _gate_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _gate_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); - _gate_port->set_property(uris.lv2_name, bufs.forge().alloc("Gate")); - _ports->at(3) = _gate_port; - - _trig_port = new OutputPort(bufs, this, Raul::Symbol("trigger"), 4, 1, - PortType::ATOM, uris.atom_Sequence, zero); - _trig_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _trig_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); - _trig_port->set_property(uris.lv2_name, bufs.forge().alloc("Trigger")); - _ports->at(4) = _trig_port; - - _vel_port = new OutputPort(bufs, this, Raul::Symbol("velocity"), 5, 1, - PortType::ATOM, uris.atom_Sequence, zero); - _vel_port->set_property(uris.atom_supports, bufs.uris().atom_Float); - _vel_port->set_property(uris.lv2_minimum, zero); - _vel_port->set_property(uris.lv2_maximum, bufs.forge().make(1.0f)); - _vel_port->set_property(uris.lv2_name, bufs.forge().alloc("Velocity")); - _ports->at(5) = _vel_port; -} - -void -TriggerNode::run(RunContext& context) -{ - const BufferRef midi_in = _midi_in_port->buffer(0); - LV2_Atom_Sequence* const seq = midi_in->get<LV2_Atom_Sequence>(); - const BufferRef midi_out = _midi_out_port->buffer(0); - - // Initialise output to the empty sequence - midi_out->prepare_write(context); - - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - const int64_t t = ev->time.frames; - const uint8_t* buf = (const uint8_t*)LV2_ATOM_BODY(&ev->body); - bool emit = false; - if (ev->body.type == _midi_in_port->bufs().uris().midi_MidiEvent && - ev->body.size >= 3) { - const FrameTime time = context.start() + t; - switch (lv2_midi_message_type(buf)) { - case LV2_MIDI_MSG_NOTE_ON: - if (buf[2] == 0) { - emit = note_off(context, buf[1], time); - } else { - emit = note_on(context, buf[1], buf[2], time); - } - break; - case LV2_MIDI_MSG_NOTE_OFF: - emit = note_off(context, buf[1], time); - break; - case LV2_MIDI_MSG_CONTROLLER: - switch (buf[1]) { - case LV2_MIDI_CTL_ALL_NOTES_OFF: - case LV2_MIDI_CTL_ALL_SOUNDS_OFF: - _gate_port->set_control_value(context, time, 0.0f); - emit = true; - } - default: - break; - } - } - - if (emit) { - midi_out->append_event(t, &ev->body); - } - } -} - -bool -TriggerNode::note_on(RunContext& context, uint8_t note_num, uint8_t velocity, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - const uint32_t offset = time - context.start(); - - if (_learning) { - _note_port->set_control_value(context, time, (float)note_num); - _note_port->force_monitor_update(); - _learning = false; - } - - if (note_num == lrintf(_note_port->buffer(0)->value_at(offset))) { - _gate_port->set_control_value(context, time, 1.0f); - _trig_port->set_control_value(context, time, 1.0f); - _trig_port->set_control_value(context, time + 1, 0.0f); - _vel_port->set_control_value(context, time, velocity / 127.0f); - return true; - } - return false; -} - -bool -TriggerNode::note_off(RunContext& context, uint8_t note_num, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - const uint32_t offset = time - context.start(); - - if (note_num == lrintf(_note_port->buffer(0)->value_at(offset))) { - _gate_port->set_control_value(context, time, 0.0f); - return true; - } - - return false; -} - -} // namespace Internals -} // namespace Server -} // namespace Ingen diff --git a/src/server/internals/Trigger.hpp b/src/server/internals/Trigger.hpp deleted file mode 100644 index 4d67395a..00000000 --- a/src/server/internals/Trigger.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2016 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_INTERNALS_TRIGGER_HPP -#define INGEN_INTERNALS_TRIGGER_HPP - -#include "InternalBlock.hpp" - -namespace Ingen { -namespace Server { - -class InputPort; -class OutputPort; -class InternalPlugin; - -namespace Internals { - -/** MIDI trigger input block. - * - * Just has a gate, for drums etc. A control port is used to select - * which note number is responded to. - * - * Note that this block is always monophonic, the poly parameter is ignored. - * (Should that change?) - * - * \ingroup engine - */ -class TriggerNode : public InternalBlock -{ -public: - TriggerNode(InternalPlugin* plugin, - BufferFactory& bufs, - const Raul::Symbol& symbol, - bool polyphonic, - GraphImpl* parent, - SampleRate srate); - - void run(RunContext& context); - - bool note_on(RunContext& context, uint8_t note_num, uint8_t velocity, FrameTime time); - bool note_off(RunContext& context, uint8_t note_num, FrameTime time); - - void learn() { _learning = true; } - - static InternalPlugin* internal_plugin(URIs& uris); - -private: - bool _learning; - - InputPort* _midi_in_port; - OutputPort* _midi_out_port; - InputPort* _note_port; - OutputPort* _gate_port; - OutputPort* _trig_port; - OutputPort* _vel_port; -}; - -} // namespace Server -} // namespace Ingen -} // namespace Internals - -#endif // INGEN_INTERNALS_TRIGGER_HPP diff --git a/src/server/jackey.h b/src/server/jackey.h deleted file mode 100644 index fc31d73c..00000000 --- a/src/server/jackey.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - Copyright 2014-2015 David Robillard <http://drobilla.net> - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - The supported event types of an event port. - - This is a kludge around Jack only supporting MIDI, particularly for OSC. - This property is a comma-separated list of event types, currently "MIDI" or - "OSC". If this contains "OSC", the port may carry OSC bundles (first byte - '#') or OSC messages (first byte '/'). Note that the "status byte" of both - OSC events is not a valid MIDI status byte, so MIDI clients that check the - status byte will gracefully ignore OSC messages if the user makes an - inappropriate connection. -*/ -#define JACKEY_EVENT_TYPES "http://jackaudio.org/metadata/event-types" - -/** - The type of an audio signal. - - This property allows audio ports to be tagged with a "meaning". The value - is a simple string. Currently, the only type is "CV", for "control voltage" - ports. Hosts SHOULD be take care to not treat CV ports as audibile and send - their output directly to speakers. In particular, CV ports are not - necessarily periodic at all and may have very high DC. -*/ -#define JACKEY_SIGNAL_TYPE "http://jackaudio.org/metadata/signal-type" - -/** - The name of the icon for the subject (typically client). - - This is used for looking up icons on the system, possibly with many sizes or - themes. Icons should be searched for according to the freedesktop Icon - Theme Specification: - - http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html -*/ -#define JACKEY_ICON_NAME "http://jackaudio.org/metadata/icon-name" - -/** - Channel designation for a port. - - This allows ports to be tagged with a meaningful designation like "left", - "right", "lfe", etc. - - The value MUST be a URI. An extensive set of URIs for designating audio - channels can be found at http://lv2plug.in/ns/ext/port-groups -*/ -#define JACKEY_DESIGNATION "http://lv2plug.in/ns/lv2core#designation" - -/** - Order for a port. - - This is used to specify the best order to show ports in user interfaces. - The value MUST be an integer. There are no other requirements, so there may - be gaps in the orders for several ports. Applications should compare the - orders of ports to determine their relative order, but must not assign any - other relevance to order values. -*/ -#define JACKEY_ORDER "http://jackaudio.org/metadata/order" diff --git a/src/server/mix.cpp b/src/server/mix.cpp deleted file mode 100644 index 3e7634fe..00000000 --- a/src/server/mix.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "lv2/lv2plug.in/ns/ext/atom/util.h" - -#include "Buffer.hpp" -#include "RunContext.hpp" -#include "mix.hpp" - -namespace Ingen { -namespace Server { - -static inline bool -is_end(const Buffer* buf, const LV2_Atom_Event* ev) -{ - const LV2_Atom* atom = buf->get<const LV2_Atom>(); - return lv2_atom_sequence_is_end( - (const LV2_Atom_Sequence_Body*)LV2_ATOM_BODY_CONST(atom), - atom->size, - ev); -} - -void -mix(const RunContext& context, - Buffer* dst, - const Buffer*const* srcs, - uint32_t num_srcs) -{ - if (num_srcs == 1) { - dst->copy(context, srcs[0]); - } else if (dst->is_control()) { - Sample* const out = dst->samples(); - out[0] = srcs[0]->value_at(0); - for (uint32_t i = 1; i < num_srcs; ++i) { - out[0] += srcs[i]->value_at(0); - } - } else if (dst->is_audio()) { - // Copy the first source - dst->copy(context, srcs[0]); - - // Mix in the rest - Sample* __restrict const out = dst->samples(); - const SampleCount end = context.nframes(); - for (uint32_t i = 1; i < num_srcs; ++i) { - const Sample* __restrict const in = srcs[i]->samples(); - if (srcs[i]->is_control()) { // control => audio - for (SampleCount i = 0; i < end; ++i) { - out[i] += in[0]; - } - } else if (srcs[i]->is_audio()) { // audio => audio - for (SampleCount i = 0; i < end; ++i) { - out[i] += in[i]; - } - } else if (srcs[i]->is_sequence()) { // sequence => audio - dst->render_sequence(context, srcs[i], true); - } - } - } else if (dst->is_sequence()) { - const LV2_Atom_Event* iters[num_srcs]; - for (uint32_t i = 0; i < num_srcs; ++i) { - iters[i] = nullptr; - if (srcs[i]->is_sequence()) { - const LV2_Atom_Sequence* seq = srcs[i]->get<const LV2_Atom_Sequence>(); - iters[i] = lv2_atom_sequence_begin(&seq->body); - if (is_end(srcs[i], iters[i])) { - iters[i] = nullptr; - } - } - } - - while (true) { - const LV2_Atom_Event* first = nullptr; - uint32_t first_i = 0; - for (uint32_t i = 0; i < num_srcs; ++i) { - const LV2_Atom_Event* const ev = iters[i]; - if (!first || (ev && ev->time.frames < first->time.frames)) { - first = ev; - first_i = i; - } - } - - if (first) { - dst->append_event( - first->time.frames, first->body.size, first->body.type, - (const uint8_t*)LV2_ATOM_BODY_CONST(&first->body)); - - iters[first_i] = lv2_atom_sequence_next(first); - if (is_end(srcs[first_i], iters[first_i])) { - iters[first_i] = nullptr; - } - } else { - break; - } - } - } -} - -} // namespace Server -} // namespace Ingen diff --git a/src/server/mix.hpp b/src/server/mix.hpp deleted file mode 100644 index 3d8880db..00000000 --- a/src/server/mix.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_MIX_HPP -#define INGEN_ENGINE_MIX_HPP - -#include <cstdint> - -namespace Ingen { - -class URIs; - -namespace Server { - -class Buffer; -class RunContext; - -void -mix(const RunContext& context, - Buffer* dst, - const Buffer*const* srcs, - uint32_t num_srcs); - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_MIX_HPP diff --git a/src/server/types.hpp b/src/server/types.hpp deleted file mode 100644 index e7dae117..00000000 --- a/src/server/types.hpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_TYPES_HPP -#define INGEN_ENGINE_TYPES_HPP - -#include <cstdint> - -typedef float Sample; -typedef uint32_t SampleCount; -typedef uint32_t SampleRate; -typedef uint32_t FrameTime; - -#endif // INGEN_ENGINE_TYPES_HPP diff --git a/src/server/util.hpp b/src/server/util.hpp deleted file mode 100644 index 7d30cc8f..00000000 --- a/src/server/util.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef INGEN_ENGINE_UTIL_HPP -#define INGEN_ENGINE_UTIL_HPP - -#include <cstdlib> - -#include "ingen/Log.hpp" -#include "raul/Path.hpp" - -#include "ingen_config.h" - -#include <fenv.h> -#ifdef __SSE__ -#include <xmmintrin.h> -#endif - -#ifdef __clang__ -# define REALTIME __attribute__((annotate("realtime"))) -#else -# define REALTIME -#endif - -#if defined(INGEN_HAVE_THREAD_LOCAL) -# define INGEN_THREAD_LOCAL thread_local -#elif defined(INGEN_HAVE_THREAD_BUILTIN) -# define INGEN_THREAD_LOCAL __thread -#else -# define INGEN_THREAD_LOCAL -#endif - -namespace Ingen { -namespace Server { - -/** Set flags to disable denormal processing. - */ -inline void -set_denormal_flags(Ingen::Log& log) -{ -#ifdef __SSE__ - _mm_setcsr(_mm_getcsr() | 0x8040); - log.info("Set SSE denormal-are-zero and flush-to-zero flags\n"); -#endif -} - -} // namespace Server -} // namespace Ingen - -#endif // INGEN_ENGINE_UTIL_HPP diff --git a/src/server/wscript b/src/server/wscript deleted file mode 100644 index 8d1ec90d..00000000 --- a/src/server/wscript +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python -from waflib.extras import autowaf as autowaf - -def build(bld): - core_source = ''' - ArcImpl.cpp - BlockFactory.cpp - BlockImpl.cpp - Broadcaster.cpp - Buffer.cpp - BufferFactory.cpp - CompiledGraph.cpp - ClientUpdate.cpp - ControlBindings.cpp - DuplexPort.cpp - Engine.cpp - EventWriter.cpp - GraphImpl.cpp - InputPort.cpp - InternalBlock.cpp - InternalPlugin.cpp - LV2Block.cpp - LV2Plugin.cpp - NodeImpl.cpp - PortImpl.cpp - PostProcessor.cpp - PreProcessor.cpp - RunContext.cpp - SocketListener.cpp - Task.cpp - UndoStack.cpp - Worker.cpp - events/Connect.cpp - events/Copy.cpp - events/CreateBlock.cpp - events/CreateGraph.cpp - events/CreatePort.cpp - events/Delete.cpp - events/Delta.cpp - events/Disconnect.cpp - events/DisconnectAll.cpp - events/Get.cpp - events/Mark.cpp - events/Move.cpp - events/SetPortValue.cpp - events/Undo.cpp - ingen_engine.cpp - internals/BlockDelay.cpp - internals/Controller.cpp - internals/Note.cpp - internals/Time.cpp - internals/Trigger.cpp - mix.cpp - ''' - - obj = bld(features = 'cxx cxxshlib', - source = core_source, - export_includes = ['../..'], - includes = ['.', '../..'], - name = 'libingen_server', - target = 'ingen_server', - install_path = '${LIBDIR}', - use = 'libingen libingen_socket', - cxxflags = bld.env.PTHREAD_CFLAGS + bld.env.INGEN_TEST_CXXFLAGS, - linkflags = bld.env.PTHREAD_LINKFLAGS + bld.env.INGEN_TEST_LINKFLAGS) - core_libs = 'LV2 LILV RAUL SERD SORD' - autowaf.use_lib(bld, obj, core_libs) - - if bld.env.HAVE_JACK: - obj = bld(features = 'cxx cxxshlib', - source = 'JackDriver.cpp ingen_jack.cpp', - includes = ['.', '../..'], - name = 'libingen_jack', - target = 'ingen_jack', - install_path = '${LIBDIR}', - use = 'libingen_server', - cxxflags = bld.env.PTHREAD_CFLAGS, - linkflags = bld.env.PTHREAD_LINKFLAGS) - autowaf.use_lib(bld, obj, core_libs + ' JACK') - - if bld.env.HAVE_PORTAUDIO: - obj = bld(features = 'cxx cxxshlib', - source = 'PortAudioDriver.cpp ingen_portaudio.cpp', - includes = ['.', '../..'], - name = 'libingen_portaudio', - target = 'ingen_portaudio', - install_path = '${LIBDIR}', - use = 'libingen_server', - cxxflags = bld.env.PTHREAD_CFLAGS, - linkflags = bld.env.PTHREAD_LINKFLAGS) - autowaf.use_lib(bld, obj, core_libs + ' PORTAUDIO') - - # Ingen LV2 wrapper - if bld.env.INGEN_BUILD_LV2: - obj = bld(features = 'cxx cxxshlib', - source = ' ingen_lv2.cpp ', - includes = ['.', '../..'], - name = 'libingen_lv2', - target = 'ingen_lv2', - install_path = '${LV2DIR}/ingen.lv2/', - use = 'libingen libingen_server', - cxxflags = bld.env.PTHREAD_CFLAGS, - linkflags = bld.env.PTHREAD_LINKFLAGS) - autowaf.use_lib(bld, obj, core_libs) diff --git a/src/wscript b/src/wscript deleted file mode 100644 index 07379b83..00000000 --- a/src/wscript +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -from waflib.extras import autowaf as autowaf - -def build(bld): - sources = [ - 'AtomReader.cpp', - 'AtomWriter.cpp', - 'ClashAvoider.cpp', - 'ColorContext.cpp', - 'Configuration.cpp', - 'FilePath.cpp', - 'Forge.cpp', - 'LV2Features.cpp', - 'Library.cpp', - 'Log.cpp', - 'Parser.cpp', - 'Resource.cpp', - 'Serialiser.cpp', - 'Store.cpp', - 'StreamWriter.cpp', - 'TurtleWriter.cpp', - 'URI.cpp', - 'URIMap.cpp', - 'URIs.cpp', - 'World.cpp', - 'runtime_paths.cpp' - ] - if bld.is_defined('HAVE_SOCKET'): - sources += [ 'SocketReader.cpp', 'SocketWriter.cpp' ] - - lib = [] - if bld.is_defined('HAVE_LIBDL'): - lib += ['dl'] - - obj = bld(features = 'cxx cxxshlib', - source = sources, - export_includes = ['..'], - includes = ['..'], - name = 'libingen', - target = 'ingen', - vnum = '0.0.0', - install_path = '${LIBDIR}', - lib = lib, - cxxflags = bld.env.PTHREAD_CFLAGS + bld.env.INGEN_TEST_CXXFLAGS, - linkflags = bld.env.PTHREAD_LINKFLAGS + bld.env.INGEN_TEST_LINKFLAGS) - autowaf.use_lib(bld, obj, 'LV2 LILV RAUL SERD SORD SRATOM') |