diff options
Diffstat (limited to 'ingen')
54 files changed, 5057 insertions, 0 deletions
diff --git a/ingen/Arc.hpp b/ingen/Arc.hpp new file mode 100644 index 00000000..043ffc0b --- /dev/null +++ b/ingen/Arc.hpp @@ -0,0 +1,40 @@ +/* + 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_ARC_HPP +#define INGEN_ARC_HPP + +#include "ingen/ingen.h" +#include "raul/Deletable.hpp" + +namespace Raul { class Path; } + +namespace Ingen { + +/** A connection between two ports. + * + * @ingroup Ingen + */ +class INGEN_API Arc : public Raul::Deletable +{ +public: + virtual const Raul::Path& tail_path() const = 0; + virtual const Raul::Path& head_path() const = 0; +}; + +} // namespace Ingen + +#endif // INGEN_ARC_HPP diff --git a/ingen/Atom.hpp b/ingen/Atom.hpp new file mode 100644 index 00000000..5e90903a --- /dev/null +++ b/ingen/Atom.hpp @@ -0,0 +1,178 @@ +/* + 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_ATOM_HPP +#define INGEN_ATOM_HPP + +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <cstring> + +#include "ingen/ingen.h" +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" + +namespace Ingen { + +/** + A generic typed data container. + + An Atom holds a value with some type and size, both specified by a uint32_t. + Values with size less than sizeof(void*) are stored inline: no dynamic + allocation occurs so Atoms may be created in hard real-time threads. + Otherwise, if the size is larger than sizeof(void*), the value will be + dynamically allocated in a separate chunk of memory. + + In either case, the data is stored in a binary compatible format to LV2_Atom + (i.e., if the value is dynamically allocated, the header is repeated there). +*/ +class INGEN_API Atom { +public: + Atom() noexcept { _atom.size = 0; _atom.type = 0; _body.ptr = nullptr; } + ~Atom() { dealloc(); } + + /** Construct a raw atom. + * + * Typically this is not used directly, use Forge methods to make atoms. + */ + Atom(uint32_t size, LV2_URID type, const void* body) { + _atom.size = size; + _atom.type = type; + _body.ptr = nullptr; + if (is_reference()) { + _body.ptr = (LV2_Atom*)malloc(sizeof(LV2_Atom) + size); + memcpy(_body.ptr, &_atom, sizeof(LV2_Atom)); + } + if (body) { + memcpy(get_body(), body, size); + } + } + + Atom(const Atom& copy) + : _atom(copy._atom) + { + if (is_reference()) { + _body.ptr = (LV2_Atom*)malloc(sizeof(LV2_Atom) + _atom.size); + memcpy(_body.ptr, copy._body.ptr, sizeof(LV2_Atom) + _atom.size); + } else { + _body.val = copy._body.val; + } + } + + Atom& operator=(const Atom& other) { + if (&other == this) { + return *this; + } + dealloc(); + _atom = other._atom; + if (is_reference()) { + _body.ptr = (LV2_Atom*)malloc(sizeof(LV2_Atom) + _atom.size); + memcpy(_body.ptr, other._body.ptr, sizeof(LV2_Atom) + _atom.size); + } else { + _body.val = other._body.val; + } + return *this; + } + + inline bool operator==(const Atom& other) const { + if (_atom.type != other._atom.type || + _atom.size != other._atom.size) { + return false; + } + return is_reference() + ? !memcmp(_body.ptr, other._body.ptr, sizeof(LV2_Atom) + _atom.size) + : _body.val == other._body.val; + } + + inline bool operator!=(const Atom& other) const { + return !operator==(other); + } + + inline bool operator<(const Atom& other) const { + if (_atom.type == other._atom.type) { + const uint32_t min_size = std::min(_atom.size, other._atom.size); + const int cmp = is_reference() + ? memcmp(_body.ptr, other._body.ptr, min_size) + : memcmp(&_body.val, &other._body.val, min_size); + return cmp < 0 || (cmp == 0 && _atom.size < other._atom.size); + } + return type() < other.type(); + } + + /** Like assignment, but only works for value atoms (not references). + * Always real-time safe. + * @return true iff set succeeded. + */ + inline bool set_rt(const Atom& other) { + if (is_reference()) { + return false; + } else { + _atom = other._atom; + _body.val = other._body.val; + return true; + } + } + + inline uint32_t size() const { return _atom.size; } + inline LV2_URID type() const { return _atom.type; } + inline bool is_valid() const { return _atom.type; } + + inline const void* get_body() const { + return is_reference() ? (void*)(_body.ptr + 1) : &_body.val; + } + + inline void* get_body() { + return is_reference() ? (void*)(_body.ptr + 1) : &_body.val; + } + + template <typename T> const T& get() const { + assert(size() == sizeof(T)); + return *static_cast<const T*>(get_body()); + } + + template <typename T> const T* ptr() const { + return static_cast<const T*>(get_body()); + } + + const LV2_Atom* atom() const { + return is_reference() ? _body.ptr : &_atom; + } + +private: + /** Free dynamically allocated value, if applicable. */ + inline void dealloc() { + if (is_reference()) { + free(_body.ptr); + } + } + + /** Return true iff this value is dynamically allocated. */ + inline bool is_reference() const { + return _atom.size > sizeof(_body.val); + } + + LV2_Atom _atom; + union { + intptr_t val; + LV2_Atom* ptr; + } _body; +}; + +} // namespace Ingen + +#endif // INGEN_ATOM_HPP diff --git a/ingen/AtomForgeSink.hpp b/ingen/AtomForgeSink.hpp new file mode 100644 index 00000000..8c070701 --- /dev/null +++ b/ingen/AtomForgeSink.hpp @@ -0,0 +1,102 @@ +/* + 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_ATOMFORGESINK_HPP +#define INGEN_ATOMFORGESINK_HPP + +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <cstring> + +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "lv2/lv2plug.in/ns/ext/atom/forge.h" +#include "lv2/lv2plug.in/ns/ext/atom/util.h" + +namespace Ingen { + +/** A resizing sink for LV2_Atom_Forge. */ +class AtomForgeSink +{ +public: + explicit AtomForgeSink(LV2_Atom_Forge* forge = nullptr) + : _capacity(8 * sizeof(LV2_Atom)) + , _size(0) + , _buf((LV2_Atom*)calloc(8, sizeof(LV2_Atom))) + { + if (forge) { + set_forge_sink(forge); + } + } + + ~AtomForgeSink() { free(_buf); } + + void set_forge_sink(LV2_Atom_Forge* forge) { + lv2_atom_forge_set_sink(forge, c_append, c_deref, this); + } + + /** Append some data and return a reference to its start. */ + intptr_t append(const void* buf, uint32_t len) { + // Record offset of the start of this write (+1 to avoid NULL) + const intptr_t ref = _size + 1; + + // Update size and reallocate if necessary + if (lv2_atom_pad_size(_size + len) > _capacity) { + _capacity = lv2_atom_pad_size(_size + len); + _buf = (LV2_Atom*)realloc(_buf, _capacity); + } + + // Append new data + memcpy((uint8_t*)_buf + _size, buf, len); + _size += len; + return ref; + } + + /** Dereference a reference previously returned by append. */ + LV2_Atom* deref(intptr_t ref) { + /* Make some assumptions and do unnecessary math to appease + -Wcast-align. This is questionable at best, though the forge should + only dereference references to aligned atoms. */ + assert((ref - 1) % sizeof(LV2_Atom) == 0); + return (LV2_Atom*)(_buf + (ref - 1) / sizeof(LV2_Atom)); + + // Alternatively: + // return (LV2_Atom*)((uint8_t*)_buf + ref - 1); + } + + const LV2_Atom* atom() const { return _buf; } + + void clear() { _buf->type = 0; _buf->size = 0; _size = 0; } + + static LV2_Atom_Forge_Ref + c_append(void* handle, const void* buf, uint32_t len) { + return ((AtomForgeSink*)handle)->append(buf, len); + } + + static LV2_Atom* + c_deref(void* handle, LV2_Atom_Forge_Ref ref) { + return ((AtomForgeSink*)handle)->deref(ref); + } + +private: + size_t _capacity; + size_t _size; + LV2_Atom* _buf; +}; + +} // namespace Ingen + +#endif // INGEN_ATOMFORGESINK_HPP diff --git a/ingen/AtomReader.hpp b/ingen/AtomReader.hpp new file mode 100644 index 00000000..7ea4654a --- /dev/null +++ b/ingen/AtomReader.hpp @@ -0,0 +1,76 @@ +/* + 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_ATOMREADER_HPP +#define INGEN_ATOMREADER_HPP + +#include <cstdint> + +#include <boost/optional/optional.hpp> + +#include "ingen/AtomSink.hpp" +#include "ingen/Resource.hpp" +#include "ingen/ingen.h" +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" + +namespace Raul { +class Path; +} + +namespace Ingen { + +class URI; +class Atom; +class Interface; +class Log; +class Properties; +class URIMap; +class URIs; + +/** An AtomSink that calls methods on an Interface. + * @ingroup IngenShared + */ +class INGEN_API AtomReader : public AtomSink +{ +public: + AtomReader(URIMap& map, + URIs& uris, + Log& log, + Interface& iface); + + static bool is_message(const URIs& uris, const LV2_Atom* msg); + + bool write(const LV2_Atom* msg, int32_t default_id=0) override; + +private: + void get_atom(const LV2_Atom* in, Atom& out); + + boost::optional<URI> atom_to_uri(const LV2_Atom* atom); + boost::optional<Raul::Path> atom_to_path(const LV2_Atom* atom); + Resource::Graph atom_to_context(const LV2_Atom* atom); + + void get_props(const LV2_Atom_Object* obj, + Ingen::Properties& props); + + URIMap& _map; + URIs& _uris; + Log& _log; + Interface& _iface; +}; + +} // namespace Ingen + +#endif // INGEN_ATOMREADER_HPP diff --git a/ingen/AtomSink.hpp b/ingen/AtomSink.hpp new file mode 100644 index 00000000..0cfa027a --- /dev/null +++ b/ingen/AtomSink.hpp @@ -0,0 +1,47 @@ +/* + 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_ATOMSINK_HPP +#define INGEN_ATOMSINK_HPP + +#include <cstdint> + +#include "ingen/ingen.h" +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" + +namespace Ingen { + +/** A sink for LV2 Atoms. + * @ingroup IngenShared + */ +class INGEN_API AtomSink { +public: + virtual ~AtomSink() = default; + + /** Write an Atom to the sink. + * + * @param msg The atom to write. + * @param default_id The default response ID to use if no + * patch:sequenceNumber property is present on the message. + * + * @return True on success. + */ + virtual bool write(const LV2_Atom* msg, int32_t default_id=0) = 0; +}; + +} // namespace Ingen + +#endif // INGEN_ATOMSINK_HPP diff --git a/ingen/AtomWriter.hpp b/ingen/AtomWriter.hpp new file mode 100644 index 00000000..5e765c75 --- /dev/null +++ b/ingen/AtomWriter.hpp @@ -0,0 +1,86 @@ +/* + 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_ATOMWRITER_HPP +#define INGEN_ATOMWRITER_HPP + +#include <cstdint> + +#include "ingen/AtomForgeSink.hpp" +#include "ingen/Interface.hpp" +#include "ingen/Message.hpp" +#include "ingen/Properties.hpp" +#include "ingen/Resource.hpp" +#include "ingen/ingen.h" +#include "lv2/lv2plug.in/ns/ext/atom/forge.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" + +namespace Raul { class Path; } + +namespace Ingen { + +class AtomSink; +class URIMap; +class URIs; + +/** An Interface that writes LV2 atoms to an AtomSink. */ +class INGEN_API AtomWriter : public Interface +{ +public: + using result_type = void; ///< For boost::apply_visitor + + AtomWriter(URIMap& map, URIs& uris, AtomSink& sink); + + URI uri() const override { return URI("ingen:/clients/atom_writer"); } + + void message(const Message& message) override; + + 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&); + +private: + void forge_uri(const URI& uri); + void forge_properties(const Properties& properties); + void forge_arc(const Raul::Path& tail, const Raul::Path& head); + void forge_request(LV2_Atom_Forge_Frame* frame, LV2_URID type, int32_t id); + void forge_context(Resource::Graph ctx); + + void finish_msg(); + + URIMap& _map; + URIs& _uris; + AtomSink& _sink; + AtomForgeSink _out; + LV2_Atom_Forge _forge; +}; + +} // namespace Ingen + +#endif // INGEN_ATOMWRITER_HPP diff --git a/ingen/ClashAvoider.hpp b/ingen/ClashAvoider.hpp new file mode 100644 index 00000000..069c8fef --- /dev/null +++ b/ingen/ClashAvoider.hpp @@ -0,0 +1,55 @@ +/* + 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_CLASHAVOIDER_HPP +#define INGEN_CLASHAVOIDER_HPP + +#include <map> + +#include "ingen/ingen.h" +#include "raul/Path.hpp" + +namespace Ingen { + +class Store; +class URI; + +/** Maps paths so they do not clash with an existing object in a store. + * + * @ingroup ingen + */ +class INGEN_API ClashAvoider +{ +public: + explicit ClashAvoider(const Store& store); + + const URI map_uri(const URI& in); + const Raul::Path map_path(const Raul::Path& in); + + bool exists(const Raul::Path& path) const; + +private: + typedef std::map<Raul::Path, unsigned> Offsets; + typedef std::map<Raul::Path, Raul::Path> SymbolMap; + + const Store& _store; + Offsets _offsets; + SymbolMap _symbol_map; +}; + +} // namespace Ingen + +#endif // INGEN_CLASHAVOIDER_HPP diff --git a/ingen/Clock.hpp b/ingen/Clock.hpp new file mode 100644 index 00000000..69d5eb17 --- /dev/null +++ b/ingen/Clock.hpp @@ -0,0 +1,63 @@ +/* + This file is part of Ingen. + Copyright 2016-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_CLOCK_HPP +#define INGEN_ENGINE_CLOCK_HPP + +#include <cstdint> + +#ifdef __MACH__ +# include <mach/mach.h> +# include <mach/mach_time.h> +#else +# include <time.h> +# include <sys/time.h> +#endif + +namespace Ingen { + +class Clock { +public: +#ifdef __MACH__ + + Clock() { mach_timebase_info(&_timebase); } + + inline uint64_t now_microseconds() const { + const uint64_t now = mach_absolute_time(); + return now * _timebase.numer / _timebase.denom / 1e3; + } + +private: + mach_timebase_info_data_t _timebase; + +#else + + inline uint64_t now_microseconds() const { + struct timespec time; +# if defined(CLOCK_MONOTONIC_RAW) + clock_gettime(CLOCK_MONOTONIC_RAW, &time); +# else + clock_gettime(CLOCK_MONOTONIC, &time); +# endif + return (uint64_t)time.tv_sec * 1e6 + (uint64_t)time.tv_nsec / 1e3; + } + +#endif +}; + +} // namespace Ingen + +#endif // INGEN_ENGINE_CLOCK_HPP diff --git a/ingen/ColorContext.hpp b/ingen/ColorContext.hpp new file mode 100644 index 00000000..81e0e062 --- /dev/null +++ b/ingen/ColorContext.hpp @@ -0,0 +1,37 @@ +/* + 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_COLORCONTEXT_HPP +#define INGEN_COLORCONTEXT_HPP + +#include <cstdio> + +namespace Ingen { + +class ColorContext { +public: + enum class Color { RED = 31, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; + + ColorContext(FILE* stream, Color color); + ~ColorContext(); + +private: + FILE* _stream; +}; + +} // namespace Ingen + +#endif // INGEN_COLORCONTEXT_HPP diff --git a/ingen/Configuration.hpp b/ingen/Configuration.hpp new file mode 100644 index 00000000..6759b4b0 --- /dev/null +++ b/ingen/Configuration.hpp @@ -0,0 +1,160 @@ +/* + 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_CONFIGURATION_HPP +#define INGEN_CONFIGURATION_HPP + +#include <cstdlib> +#include <list> +#include <map> +#include <ostream> +#include <string> + +#include "ingen/Atom.hpp" +#include "ingen/ingen.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "raul/Exception.hpp" + +namespace Ingen { + +class FilePath; +class Forge; +class URIMap; + +/** Ingen configuration (command line options and/or configuration file). + * @ingroup IngenShared + */ +class INGEN_API Configuration { +public: + explicit Configuration(Forge& forge); + + /** The scope of a configuration option. + * + * This controls when and where an option will be saved or restored. + */ + enum Scope { + GLOBAL = 1, ///< Applies to any Ingen instance + SESSION = 1<<1, ///< Applies to this Ingen instance only + GUI = 1<<2 ///< Persistent GUI settings saved at exit + }; + + /** Add a configuration option. + * + * @param key URI local name, in camelCase + * @param name Long option name (without leading "--") + * @param letter Short option name (without leading "-") + * @param desc Description + * @param scope Scope of option + * @param type Type + * @param value Default value + */ + 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); + + void print_usage(const std::string& program, std::ostream& os); + + struct OptionError : public Raul::Exception { + explicit OptionError(const std::string& m) : Exception(m) {} + }; + + struct FileError : public Raul::Exception { + explicit FileError(const std::string& m) : Exception(m) {} + }; + + /** Parse a command line. + * + * @throw OptionError + */ + void parse(int argc, char **argv); + + /** Load a specific file. */ + bool load(const FilePath& path); + + /** Save configuration to a file. + * + * @param uri_map URI map. + * + * @param app Application name. + * + * @param filename If absolute, the configuration will be saved to this + * path. Otherwise the configuration will be saved to the user + * configuration directory (e.g. ~/.config/ingen/filename). + * + * @param scopes Bitwise OR of Scope values. Only options which match the + * given scopes will be saved. + * + * @return The absolute path of the saved configuration file. + */ + FilePath save(URIMap& uri_map, + const std::string& app, + const FilePath& filename, + unsigned scopes); + + /** Load files from the standard configuration directories for the app. + * + * The system configuration file(s), e.g. /etc/xdg/appname/filename, + * will be loaded before the user's, e.g. ~/.config/appname/filename, + * so the user options will override the system options. + */ + std::list<FilePath> load_default(const std::string& app, + const FilePath& filename); + + const Atom& option(const std::string& long_name) const; + bool set(const std::string& long_name, const Atom& value); + +private: + struct Option { + std::string key; + std::string name; + char letter; + std::string desc; + Scope scope; + LV2_URID type; + Atom value; + }; + + struct OptionNameOrder { + inline bool operator()(const Option& a, const Option& b) { + return a.name < b.name; + } + }; + + typedef std::map<std::string, Option> Options; + typedef std::map<char, std::string> ShortNames; + typedef std::map<std::string, std::string> Keys; + + std::string variable_string(LV2_URID type) const; + + int set_value_from_string(Configuration::Option& option, + const std::string& value); + + Forge& _forge; + const std::string _shortdesc; + const std::string _desc; + Options _options; + Keys _keys; + ShortNames _short_names; + size_t _max_name_length; +}; + +} // namespace Ingen + +#endif // INGEN_CONFIGURATION_HPP diff --git a/ingen/DataAccess.hpp b/ingen/DataAccess.hpp new file mode 100644 index 00000000..cabb5345 --- /dev/null +++ b/ingen/DataAccess.hpp @@ -0,0 +1,68 @@ +/* + 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_DATAACCESS_HPP +#define INGEN_ENGINE_DATAACCESS_HPP + +#include <cstdlib> +#include <utility> + +#include "ingen/LV2Features.hpp" +#include "ingen/Node.hpp" +#include "ingen/Store.hpp" +#include "ingen/World.hpp" +#include "ingen/types.hpp" +#include "lilv/lilv.h" + +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" +#include "lv2/lv2plug.in/ns/ext/data-access/data-access.h" + +namespace Ingen { + +struct DataAccess : public Ingen::LV2Features::Feature +{ + static void delete_feature(LV2_Feature* feature) { + free(feature->data); + delete feature; + } + + const char* uri() const override { return "http://lv2plug.in/ns/ext/data-access"; } + + SPtr<LV2_Feature> feature(World* world, Node* node) override { + Node* store_node = world->store()->get(node->path()); + if (!store_node) { + return SPtr<LV2_Feature>(); + } + + LilvInstance* inst = store_node->instance(); + if (!inst) { + return SPtr<LV2_Feature>(); + } + + const LV2_Descriptor* desc = lilv_instance_get_descriptor(inst); + LV2_Extension_Data_Feature* data = (LV2_Extension_Data_Feature*) + malloc(sizeof(LV2_Extension_Data_Feature)); + + data->data_access = desc->extension_data; + + return SPtr<LV2_Feature>( + new LV2_Feature{ "http://lv2plug.in/ns/ext/data-access", data }); + } +}; + +} // namespace Ingen + +#endif // INGEN_ENGINE_DATAACCESS_HPP diff --git a/ingen/EngineBase.hpp b/ingen/EngineBase.hpp new file mode 100644 index 00000000..00436c66 --- /dev/null +++ b/ingen/EngineBase.hpp @@ -0,0 +1,145 @@ +/* + 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_ENGINEBASE_HPP +#define INGEN_ENGINEBASE_HPP + +#include <chrono> +#include <cstddef> +#include <cstdint> + +#include "ingen/ingen.h" +#include "ingen/types.hpp" + +namespace Ingen { + +class Interface; + +/** + The audio engine which executes the graph. + + @ingroup Ingen +*/ +class INGEN_API EngineBase +{ +public: + virtual ~EngineBase() = default; + + /** + Initialise the engine for local use (e.g. without a Jack driver). + @param sample_rate Audio sampling rate in Hz. + @param block_length Audio block length (i.e. buffer size) in frames. + @param seq_size Sequence buffer size in bytes. + */ + virtual void init(double sample_rate, + uint32_t block_length, + size_t seq_size) = 0; + + /** + Return true iff the engine and driver supports dynamic ports. + + This returns false in situations where top level ports can not be + created once the driver is running, which is the case for most + environments outside Jack. + */ + virtual bool supports_dynamic_ports() const = 0; + + /** + Activate the engine. + */ + virtual bool activate() = 0; + + /** + Deactivate the engine. + */ + virtual void deactivate() = 0; + + /** + Begin listening on network sockets. + */ + virtual void listen() = 0; + + /** + Return true iff events are waiting to be processed. + */ + virtual bool pending_events() const = 0; + + /** + Flush any pending events. + + This function is only safe to call in sequential contexts, and runs both + process thread and main iterations in lock-step. + + @param sleep_ms Interval in milliseconds to sleep between each block. + */ + virtual void flush_events(const std::chrono::milliseconds& sleep_ms) = 0; + + /** + Advance audio time by the given number of frames. + */ + virtual void advance(uint32_t nframes) = 0; + + /** + Locate to a given audio position. + */ + virtual void locate(uint32_t start, uint32_t sample_count) = 0; + + /** + Process audio for `sample_count` frames. + + If the return value is non-zero, events have been processed and are + awaiting to be finalised (including responding and announcing any changes + to clients) via a call to main_iteration(). + + @return The number of events processed. + */ + virtual unsigned run(uint32_t sample_count) = 0; + + /** + Indicate that a quit is desired. + + This function simply sets a flag which affects the return value of + main_iteration, it does not actually force the engine to stop running or + block. The code driving the engine is responsible for stopping and + cleaning up when main_iteration returns false. + */ + virtual void quit() = 0; + + /** + Run a single iteration of the main context. + + The main context post-processes events and performs housekeeping duties + like collecting garbage. This should be called regularly, e.g. a few + times per second. The return value indicates whether execution should + continue; i.e. if false is returned, a quit has been requested and the + caller should cease calling main_iteration() and stop the engine. + */ + virtual bool main_iteration() = 0; + + /** + Register a client to receive updates about engine changes. + */ + virtual void register_client(SPtr<Interface> client) = 0; + + /** + Unregister a client. + */ + virtual bool unregister_client(SPtr<Interface> client) = 0; +}; + +} // namespace Ingen + +#endif // INGEN_ENGINEBASE_HPP diff --git a/ingen/FilePath.hpp b/ingen/FilePath.hpp new file mode 100644 index 00000000..6bdd6044 --- /dev/null +++ b/ingen/FilePath.hpp @@ -0,0 +1,123 @@ +/* + 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/>. +*/ + +#ifndef INGEN_FILE_PATH_HPP +#define INGEN_FILE_PATH_HPP + +#include <iosfwd> +#include <type_traits> +#include <utility> + +#include <boost/utility/string_view.hpp> + +#if defined(_WIN32) && !defined(__CYGWIN__) +#define USE_WINDOWS_FILE_PATHS 1 +#endif + +namespace Ingen { + +/** A path to a file. + * + * This is a minimal subset of the std::filesystem::path interface in C++17. + * Support for Windows paths is only partial and there is no support for + * character encoding conversion at all. + */ +class FilePath +{ +public: +#ifdef USE_WINDOWS_FILE_PATHS + typedef wchar_t value_type; + static constexpr value_type preferred_separator = L'\\'; +#else + typedef char value_type; + static constexpr value_type preferred_separator = '/'; +#endif + + typedef std::basic_string<value_type> string_type; + + FilePath() = default; + FilePath(const FilePath&) = default; + FilePath(FilePath&&) = default; + + FilePath(string_type&& str) : _str(std::move(str)) {} + FilePath(const string_type& str) : _str(str) {} + FilePath(const value_type* str) : _str(str) {} + FilePath(const boost::basic_string_view<value_type>& sv) + : _str(sv.data(), sv.length()) + {} + + ~FilePath() = default; + + FilePath& operator=(const FilePath& path) = default; + FilePath& operator=(FilePath&& path) noexcept; + FilePath& operator=(string_type&& str); + + FilePath& operator/=(const FilePath& path); + + FilePath& operator+=(const FilePath& path); + FilePath& operator+=(const string_type& str); + FilePath& operator+=(const value_type* str); + FilePath& operator+=(value_type chr); + FilePath& operator+=(boost::basic_string_view<value_type> sv); + + void clear() noexcept { _str.clear(); } + + const string_type& native() const noexcept { return _str; } + const string_type& string() const noexcept { return _str; } + const value_type* c_str() const noexcept { return _str.c_str(); } + + operator string_type() const { return _str; } + + FilePath root_name() const; + FilePath root_directory() const; + FilePath root_path() const; + FilePath relative_path() const; + FilePath parent_path() const; + FilePath filename() const; + FilePath stem() const; + FilePath extension() const; + + bool empty() const noexcept { return _str.empty(); } + + bool is_absolute() const; + bool is_relative() const { return !is_absolute(); } + +private: + std::size_t find_first_sep() const; + std::size_t find_last_sep() const; + + string_type _str; +}; + +bool operator==(const FilePath& lhs, const FilePath& rhs) noexcept; +bool operator!=(const FilePath& lhs, const FilePath& rhs) noexcept; +bool operator<(const FilePath& lhs, const FilePath& rhs) noexcept; +bool operator<=(const FilePath& lhs, const FilePath& rhs) noexcept; +bool operator>(const FilePath& lhs, const FilePath& rhs) noexcept; +bool operator>=(const FilePath& lhs, const FilePath& rhs) noexcept; + +FilePath operator/(const FilePath& lhs, const FilePath& rhs); + +template <typename Char, typename Traits> +std::basic_ostream<Char, Traits>& +operator<<(std::basic_ostream<Char, Traits>& os, const FilePath& path) +{ + return os << path.string(); +} + +} // namespace Ingen + +#endif // INGEN_FILE_PATH_HPP diff --git a/ingen/Forge.hpp b/ingen/Forge.hpp new file mode 100644 index 00000000..32822f89 --- /dev/null +++ b/ingen/Forge.hpp @@ -0,0 +1,86 @@ +/* + 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_FORGE_HPP +#define INGEN_FORGE_HPP + +#include <cstdint> +#include <cstring> +#include <string> + +#include "ingen/Atom.hpp" +#include "ingen/ingen.h" +#include "lv2/lv2plug.in/ns/ext/atom/forge.h" + +namespace Ingen { + +class URIMap; +class URI; + +/** Forge for Atoms. + * @ingroup IngenShared + */ +class INGEN_API Forge : public LV2_Atom_Forge { +public: + explicit Forge(URIMap& map); + + std::string str(const Atom& atom, bool quoted); + + bool is_uri(const Atom& atom) const { + return atom.type() == URI || atom.type() == URID; + } + + Atom make() { return Atom(); } + Atom make(int32_t v) { return Atom(sizeof(v), Int, &v); } + Atom make(float v) { return Atom(sizeof(v), Float, &v); } + Atom make(bool v) { + const int32_t iv = v ? 1 : 0; + return Atom(sizeof(int32_t), Bool, &iv); + } + + Atom make_urid(int32_t v) { return Atom(sizeof(int32_t), URID, &v); } + + Atom make_urid(const Ingen::URI& u); + + Atom alloc(uint32_t size, uint32_t type, const void* val) { + return Atom(size, type, val); + } + + Atom alloc(const char* v) { + const size_t len = strlen(v); + return Atom(len + 1, String, v); + } + + Atom alloc(const std::string& v) { + return Atom(v.length() + 1, String, v.c_str()); + } + + Atom alloc_uri(const char* v) { + const size_t len = strlen(v); + return Atom(len + 1, URI, v); + } + + Atom alloc_uri(const std::string& v) { + return Atom(v.length() + 1, URI, v.c_str()); + } + +private: + URIMap& _map; +}; + +} // namespace Ingen + +#endif // INGEN_FORGE_HPP diff --git a/ingen/InstanceAccess.hpp b/ingen/InstanceAccess.hpp new file mode 100644 index 00000000..a93edc66 --- /dev/null +++ b/ingen/InstanceAccess.hpp @@ -0,0 +1,56 @@ +/* + 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_INSTANCEACCESS_HPP +#define INGEN_ENGINE_INSTANCEACCESS_HPP + +#include <utility> + +#include "ingen/LV2Features.hpp" +#include "ingen/Node.hpp" +#include "ingen/Store.hpp" +#include "ingen/World.hpp" +#include "ingen/types.hpp" +#include "lilv/lilv.h" + +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" + +namespace Ingen { + +struct InstanceAccess : public Ingen::LV2Features::Feature +{ + const char* uri() const override { return "http://lv2plug.in/ns/ext/instance-access"; } + + SPtr<LV2_Feature> feature(World* world, Node* node) override { + Node* store_node = world->store()->get(node->path()); + if (!store_node) { + return SPtr<LV2_Feature>(); + } + + LilvInstance* instance = store_node->instance(); + if (!instance) { + return SPtr<LV2_Feature>(); + } + + return SPtr<LV2_Feature>( + new LV2_Feature{ "http://lv2plug.in/ns/ext/instance-access", + lilv_instance_get_handle(instance) }); + } +}; + +} // namespace Ingen + +#endif // INGEN_ENGINE_INSTANCEACCESS_HPP diff --git a/ingen/Interface.hpp b/ingen/Interface.hpp new file mode 100644 index 00000000..a6654afe --- /dev/null +++ b/ingen/Interface.hpp @@ -0,0 +1,149 @@ +/* + 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/>. +*/ + +/** + @defgroup Ingen Core Interfaces +*/ + +#ifndef INGEN_INTERFACE_HPP +#define INGEN_INTERFACE_HPP + +#include <cstdint> +#include <string> + +#include "ingen/Message.hpp" +#include "ingen/Properties.hpp" +#include "ingen/Resource.hpp" +#include "ingen/Status.hpp" +#include "ingen/ingen.h" +#include "ingen/types.hpp" + +namespace Raul { +class Path; +} + +namespace Ingen { + +class Atom; +class URI; + +/** Abstract interface for Ingen servers and clients. + * + * @ingroup Ingen + */ +class INGEN_API Interface +{ +public: + using result_type = void; + + Interface() : _seq(0) {} + + virtual ~Interface() = default; + + virtual URI uri() const = 0; + + virtual SPtr<Interface> respondee() const { return SPtr<Interface>(); } + + virtual void set_respondee(SPtr<Interface> respondee) {} + + virtual void message(const Message& msg) = 0; + + /** @name Convenience API + * @{ + */ + + inline void operator()(const Message& msg) { message(msg); } + + inline void set_response_id(int32_t id) { _seq = id; } + + inline void bundle_begin() { message(BundleBegin{_seq++}); } + inline void bundle_end() { message(BundleEnd{_seq++}); } + + inline void put(const URI& uri, + const Properties& properties, + Resource::Graph ctx = Resource::Graph::DEFAULT) + { + message(Put{_seq++, uri, properties, ctx}); + } + + inline void delta(const URI& uri, + const Properties& remove, + const Properties& add, + Resource::Graph ctx = Resource::Graph::DEFAULT) + { + message(Delta{_seq++, uri, remove, add, ctx}); + } + + inline void copy(const URI& old_uri, const URI& new_uri) + { + message(Copy{_seq++, old_uri, new_uri}); + } + + inline void move(const Raul::Path& old_path, const Raul::Path& new_path) + { + message(Move{_seq++, old_path, new_path}); + } + + inline void del(const URI& uri) { message(Del{_seq++, uri}); } + + inline void connect(const Raul::Path& tail, const Raul::Path& head) + { + message(Connect{_seq++, tail, head}); + } + + inline void disconnect(const Raul::Path& tail, const Raul::Path& head) + { + message(Disconnect{_seq++, tail, head}); + } + + inline void disconnect_all(const Raul::Path& graph, const Raul::Path& path) + { + message(DisconnectAll{_seq++, graph, path}); + } + + inline void set_property(const URI& subject, + const URI& predicate, + const Atom& value, + Resource::Graph ctx = Resource::Graph::DEFAULT) + { + message(SetProperty{_seq++, subject, predicate, value, ctx}); + } + + inline void undo() { message(Undo{_seq++}); } + + inline void redo() { message(Redo{_seq++}); } + + inline void get(const URI& uri) { message(Get{_seq++, uri}); } + + inline void response(int32_t id, Status status, const std::string& subject) + { + message(Response{id, status, subject}); + } + + inline void error(const std::string& error_message) + { + message(Error{_seq++, error_message}); + } + + /** @} */ + +private: + int32_t _seq; +}; + +} // namespace Ingen + +#endif // INGEN_INTERFACE_HPP diff --git a/ingen/LV2Features.hpp b/ingen/LV2Features.hpp new file mode 100644 index 00000000..9995ff47 --- /dev/null +++ b/ingen/LV2Features.hpp @@ -0,0 +1,95 @@ +/* + 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_LV2FEATURES_HPP +#define INGEN_LV2FEATURES_HPP + +#include <string> +#include <utility> +#include <vector> + +#include "ingen/ingen.h" +#include "ingen/types.hpp" +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" +#include "raul/Noncopyable.hpp" + +namespace Ingen { + +class Node; +class World; + +/** Features for use by LV2 plugins. + * @ingroup IngenShared + */ +class INGEN_API LV2Features { +public: + LV2Features(); + + class Feature { + public: + virtual ~Feature() = default; + + virtual const char* uri() const = 0; + + virtual SPtr<LV2_Feature> feature(World* world, + Node* block) = 0; + +protected: + static void free_feature(LV2_Feature* feature); + }; + + class EmptyFeature : public Feature { + public: + explicit EmptyFeature(const char* uri) : _uri(uri) {} + + const char* uri() const override { return _uri; } + + SPtr<LV2_Feature> feature(World* world, Node* block) override { + return SPtr<LV2_Feature>(); + } + + const char* _uri; + }; + + class FeatureArray : public Raul::Noncopyable { + public: + typedef std::vector< SPtr<LV2_Feature> > FeatureVector; + + explicit FeatureArray(FeatureVector& features); + + ~FeatureArray(); + + LV2_Feature** array() { return _array; } + + private: + FeatureVector _features; + LV2_Feature** _array; + }; + + void add_feature(SPtr<Feature> feature); + bool is_supported(const std::string& uri) const; + + SPtr<FeatureArray> lv2_features(World* world, + Node* node) const; + +private: + typedef std::vector< SPtr<Feature> > Features; + Features _features; +}; + +} // namespace Ingen + +#endif // INGEN_LV2FEATURES_HPP diff --git a/ingen/Library.hpp b/ingen/Library.hpp new file mode 100644 index 00000000..71257900 --- /dev/null +++ b/ingen/Library.hpp @@ -0,0 +1,48 @@ +/* + 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/>. +*/ + +#ifndef INGEN_LIBRARY_HPP +#define INGEN_LIBRARY_HPP + +#include "ingen/FilePath.hpp" +#include "ingen/ingen.h" + +namespace Ingen { + +/** A dynamically loaded library (module, plugin). */ +class INGEN_API Library { +public: + Library(const FilePath& path); + ~Library(); + + Library(const Library&) = delete; + Library& operator=(const Library&) = delete; + + using VoidFuncPtr = void (*)(void); + + VoidFuncPtr get_function(const char* const name); + + static const char* get_last_error(); + + operator bool() const { return _lib; } + +private: + void* _lib; +}; + +} // namespace Ingen + +#endif // INGEN_LIBRARY_HPP diff --git a/ingen/Log.hpp b/ingen/Log.hpp new file mode 100644 index 00000000..54369551 --- /dev/null +++ b/ingen/Log.hpp @@ -0,0 +1,88 @@ +/* + 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_LOG_HPP +#define INGEN_LOG_HPP + +#include <cstdarg> +#include <cstdio> +#include <functional> +#include <string> + +#include <boost/format.hpp> + +#include "ingen/LV2Features.hpp" +#include "ingen/ingen.h" +#include "lv2/lv2plug.in/ns/ext/log/log.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" + +namespace Ingen { + +typedef boost::basic_format<char> fmt; + +class Node; +class URIs; +class World; + +class INGEN_API Log { +public: + typedef std::function<int(LV2_URID, const char*, va_list)> Sink; + + Log(LV2_Log_Log* log, URIs& uris); + + struct Feature : public LV2Features::Feature { + const char* uri() const override { return LV2_LOG__log; } + + SPtr<LV2_Feature> feature(World* world, Node* block) override; + + struct Handle { + LV2_Log_Log lv2_log; + Log* log; + Node* node; + }; + }; + + void rt_error(const char* msg); + + void error(const std::string& msg); + void info(const std::string& msg); + void warn(const std::string& msg); + void trace(const std::string& msg); + + inline void error(const fmt& fmt) { error(fmt.str()); } + inline void info(const fmt& fmt) { info(fmt.str()); } + inline void warn(const fmt& fmt) { warn(fmt.str()); } + + int vtprintf(LV2_URID type, const char* fmt, va_list args); + + void set_flush(bool f) { _flush = f; } + void set_trace(bool f) { _trace = f; } + void set_sink(Sink s) { _sink = s; } + +private: + void print(FILE* stream, const std::string& msg); + + LV2_Log_Log* _log; + URIs& _uris; + Sink _sink; + bool _flush; + bool _trace; +}; + +} // namespace Ingen + +#endif // INGEN_LOG_HPP diff --git a/ingen/Message.hpp b/ingen/Message.hpp new file mode 100644 index 00000000..515a2e1f --- /dev/null +++ b/ingen/Message.hpp @@ -0,0 +1,158 @@ +/* + 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_MESSAGE_HPP +#define INGEN_MESSAGE_HPP + +#include <cstdint> +#include <string> + +#include <boost/variant/variant.hpp> + +#include "ingen/Atom.hpp" +#include "ingen/Properties.hpp" +#include "ingen/Resource.hpp" +#include "ingen/Status.hpp" +#include "raul/Path.hpp" + +namespace Ingen { + +struct BundleBegin +{ + int32_t seq; +}; + +struct BundleEnd +{ + int32_t seq; +}; + +struct Connect +{ + int32_t seq; + Raul::Path tail; + Raul::Path head; +}; + +struct Copy +{ + int32_t seq; + URI old_uri; + URI new_uri; +}; + +struct Del +{ + int32_t seq; + URI uri; +}; + +struct Delta +{ + int32_t seq; + URI uri; + Properties remove; + Properties add; + Resource::Graph ctx; +}; + +struct Disconnect +{ + int32_t seq; + Raul::Path tail; + Raul::Path head; +}; + +struct DisconnectAll +{ + int32_t seq; + Raul::Path graph; + Raul::Path path; +}; + +struct Error +{ + int32_t seq; + std::string message; +}; + +struct Get +{ + int32_t seq; + URI subject; +}; + +struct Move +{ + int32_t seq; + Raul::Path old_path; + Raul::Path new_path; +}; + +struct Put +{ + int32_t seq; + URI uri; + Properties properties; + Resource::Graph ctx; +}; + +struct Redo +{ + int32_t seq; +}; + +struct Response +{ + int32_t id; + Status status; + std::string subject; +}; + +struct SetProperty +{ + int32_t seq; + URI subject; + URI predicate; + Atom value; + Resource::Graph ctx; +}; + +struct Undo +{ + int32_t seq; +}; + +using Message = boost::variant<BundleBegin, + BundleEnd, + Connect, + Copy, + Del, + Delta, + Disconnect, + DisconnectAll, + Error, + Get, + Move, + Put, + Redo, + Response, + SetProperty, + Undo>; + +} // namespace Ingen + +#endif // INGEN_MESSAGE_HPP diff --git a/ingen/Module.hpp b/ingen/Module.hpp new file mode 100644 index 00000000..df47b70b --- /dev/null +++ b/ingen/Module.hpp @@ -0,0 +1,64 @@ +/* + 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_MODULE_HPP +#define INGEN_MODULE_HPP + +#include <memory> + +#include "ingen/FilePath.hpp" +#include "ingen/Library.hpp" +#include "ingen/ingen.h" + +namespace Ingen { + +class World; + +/** A dynamically loaded Ingen module. + * + * All components of Ingen reside in one of these. + * @ingroup IngenShared + */ +class INGEN_API Module { +public: + Module() : library(nullptr) {} + virtual ~Module() = default; + + Module(const Module&) = delete; + Module& operator=(const Module&) = delete; + + virtual void load(Ingen::World* world) = 0; + virtual void run(Ingen::World* world) {} + + /** Library implementing this module. + * + * This is managed by the World and not this class, since closing the library + * in this destructor could possibly reference code from the library + * afterwards and cause a segfault on exit. + */ + std::unique_ptr<Library> library; +}; + +} // namespace Ingen + +extern "C" { + +/** Prototype for the ingen_module_load() entry point in an ingen module. */ +INGEN_API Ingen::Module* ingen_module_load(); + +} + +#endif // INGEN_MODULE_HPP diff --git a/ingen/Node.hpp b/ingen/Node.hpp new file mode 100644 index 00000000..ca78aa3d --- /dev/null +++ b/ingen/Node.hpp @@ -0,0 +1,106 @@ +/* + 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_NODE_HPP +#define INGEN_NODE_HPP + +#include <cstdint> +#include <map> +#include <string> +#include <utility> + +#include "ingen/Resource.hpp" +#include "ingen/ingen.h" +#include "ingen/paths.hpp" +#include "ingen/types.hpp" +#include "lilv/lilv.h" + +namespace Raul { +class Path; +class Symbol; +} + +namespace Ingen { + +class Arc; +class FilePath; +class Store; +class URIs; + +/** A node in the audio graph. + * + * The key property of nodes is that all nodes have a path and a symbol, as + * well as a URI. + * + * To avoid ugly inheritance issues and the need for excessive use of + * dynamic_cast, this class contains some members which are only applicable to + * certain types of node. There is a type tag which can be used to determine + * the type of any Node. + * + * @ingroup Ingen + */ +class INGEN_API Node : public Resource +{ +public: + enum class GraphType { + GRAPH, + BLOCK, + PORT + }; + + typedef std::pair<const Node*, const Node*> ArcsKey; + typedef std::map< ArcsKey, SPtr<Arc> > Arcs; + + // Graphs only + Arcs& arcs() { return _arcs; } + const Arcs& arcs() const { return _arcs; } + + // Blocks and graphs only + virtual uint32_t num_ports() const { return 0; } + virtual Node* port(uint32_t index) const { return nullptr; } + virtual const Resource* plugin() const { return nullptr; } + + // Plugin blocks only + virtual LilvInstance* instance() { return nullptr; } + virtual bool save_state(const FilePath& dir) const { return false; } + + // All objects + virtual GraphType graph_type() const = 0; + virtual const Raul::Path& path() const = 0; + virtual const Raul::Symbol& symbol() const = 0; + virtual Node* graph_parent() const = 0; + + URI base_uri() const { + if (uri().string()[uri().string().size() - 1] == '/') { + return uri(); + } + return URI(uri().string() + '/'); + } + +protected: + friend class Store; + virtual void set_path(const Raul::Path& p) = 0; + + Node(const URIs& uris, const Raul::Path& path) + : Resource(uris, path_to_uri(path)) + {} + + Arcs _arcs; ///< Graphs only +}; + +} // namespace Ingen + +#endif // INGEN_NODE_HPP diff --git a/ingen/Parser.hpp b/ingen/Parser.hpp new file mode 100644 index 00000000..5a361cd3 --- /dev/null +++ b/ingen/Parser.hpp @@ -0,0 +1,99 @@ +/* + 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_PARSER_HPP +#define INGEN_PARSER_HPP + +#include <set> +#include <string> +#include <utility> + +#include <boost/optional/optional.hpp> + +#include "ingen/FilePath.hpp" +#include "ingen/Properties.hpp" +#include "ingen/URI.hpp" +#include "ingen/ingen.h" +#include "raul/Path.hpp" +#include "raul/Symbol.hpp" + +namespace Sord { class World; } + +namespace Ingen { + +class Interface; +class World; + +/** + Parser for reading graphs from Turtle files or strings. + + @ingroup Ingen +*/ +class INGEN_API Parser { +public: + explicit Parser() = default; + + virtual ~Parser() = default; + + /** Record of a resource listed in a bundle manifest. */ + struct ResourceRecord { + inline ResourceRecord(URI u, FilePath f) + : uri(std::move(u)), filename(std::move(f)) + {} + + inline bool operator<(const ResourceRecord& r) const { + return uri < r.uri; + } + + URI uri; ///< URI of resource (e.g. a Graph) + FilePath filename; ///< Path of describing file (seeAlso) + }; + + /** Find all resources of a given type listed in a manifest file. */ + virtual std::set<ResourceRecord> find_resources(Sord::World& world, + const URI& manifest_uri, + const URI& type_uri); + + /** Parse a graph from RDF into a Interface (engine or client). + * + * If `path` is a file path, then the graph is loaded from that + * file. If it is a directory, then the manifest.ttl from that directory + * is used instead. In either case, any rdfs:seeAlso files are loaded and + * the graph parsed from the resulting combined model. + * + * @return whether or not load was successful. + */ + virtual bool parse_file( + World* world, + Interface* target, + const FilePath& path, + 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>()); + + virtual boost::optional<URI> parse_string( + World* world, + Interface* target, + const std::string& str, + const URI& base_uri, + 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>()); +}; + +} // namespace Ingen + +#endif // INGEN_PARSER_HPP diff --git a/ingen/Properties.hpp b/ingen/Properties.hpp new file mode 100644 index 00000000..e148c542 --- /dev/null +++ b/ingen/Properties.hpp @@ -0,0 +1,90 @@ +/* + 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_PROPERTIES_HPP +#define INGEN_PROPERTIES_HPP + +#include <initializer_list> +#include <map> +#include <utility> + +#include "ingen/Atom.hpp" +#include "ingen/URIs.hpp" + +namespace Ingen { + +/** A property value (an Atom with a context). */ +class Property : public Atom { +public: + enum class Graph { + DEFAULT, ///< Default context for "universal" properties + EXTERNAL, ///< Externally visible graph properties + INTERNAL ///< Internally visible graph properties + }; + + Property(const Atom& atom, Graph ctx=Graph::DEFAULT) + : Atom(atom) + , _ctx(ctx) + {} + + Property(const URIs::Quark& quark, Graph ctx=Graph::DEFAULT) + : Atom(quark.urid) + , _ctx(ctx) + {} + + Graph context() const { return _ctx; } + void set_context(Graph ctx) { _ctx = ctx; } + +private: + Graph _ctx; +}; + +class Properties : public std::multimap<URI, Property> { +public: + using Graph = Property::Graph; + + Properties() = default; + Properties(const Properties& copy) = default; + + Properties(std::initializer_list<value_type> l) + : std::multimap<URI, Property>(l) + {} + + void put(const URI& key, + const Atom& value, + Graph ctx = Graph::DEFAULT) { + emplace(key, Property(value, ctx)); + } + + void put(const URI& key, + const URIs::Quark& value, + Graph ctx = Graph::DEFAULT) { + emplace(key, Property(value, ctx)); + } + + bool contains(const URI& key, const Atom& value) { + for (const_iterator i = find(key); i != end() && i->first == key; ++i) { + if (i->second == value) { + return true; + } + } + return false; + } +}; + +} // namespace Ingen + +#endif // INGEN_PROPERTIES_HPP diff --git a/ingen/QueuedInterface.hpp b/ingen/QueuedInterface.hpp new file mode 100644 index 00000000..bf424edd --- /dev/null +++ b/ingen/QueuedInterface.hpp @@ -0,0 +1,66 @@ +/* + 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/>. +*/ + +#ifndef INGEN_ENGINE_QUEUEDINTERFACE_HPP +#define INGEN_ENGINE_QUEUEDINTERFACE_HPP + +#include <mutex> +#include <vector> + +#include "ingen/Interface.hpp" +#include "ingen/Message.hpp" + +namespace Ingen { + +/** Stores all messages and emits them to a sink on demand. + * + * This can be used to make an interface thread-safe. + */ +class QueuedInterface : public Interface +{ +public: + explicit QueuedInterface(SPtr<Interface> sink) : _sink(std::move(sink)) {} + + URI uri() const override { return URI("ingen:/QueuedInterface"); } + + void message(const Message& message) override { + std::lock_guard<std::mutex> lock(_mutex); + _messages.emplace_back(message); + } + + void emit() { + std::vector<Message> messages; + { + std::lock_guard<std::mutex> lock(_mutex); + _messages.swap(messages); + } + + for (const auto& i : messages) { + _sink->message(i); + } + } + + const SPtr<Interface>& sink() const { return _sink; } + +private: + std::mutex _mutex; + SPtr<Interface> _sink; + std::vector<Message> _messages; +}; + +} // namespace Ingen + +#endif // INGEN_ENGINE_QUEUEDINTERFACE_HPP diff --git a/ingen/Resource.hpp b/ingen/Resource.hpp new file mode 100644 index 00000000..9fc854c7 --- /dev/null +++ b/ingen/Resource.hpp @@ -0,0 +1,205 @@ +/* + 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_RESOURCE_HPP +#define INGEN_RESOURCE_HPP + +#include <cassert> +#include <string> + +#include "ingen/Properties.hpp" +#include "ingen/URI.hpp" +#include "ingen/URIs.hpp" +#include "ingen/ingen.h" +#include "raul/Deletable.hpp" + +namespace Ingen { + +class Atom; + +/** A resource with a URI described by properties. + * + * This is the base class for most things in Ingen, including graphs, blocks, + * ports, and the engine itself. + * + * @ingroup Ingen + */ +class INGEN_API Resource : public Raul::Deletable +{ +public: + using Graph = Property::Graph; + + Resource(const URIs& uris, const URI& uri) + : _uris(uris) + , _uri(uri) + {} + + Resource& operator=(const Resource& rhs) { + assert(&rhs._uris == &_uris); + if (&rhs != this) { + _uri = rhs._uri; + _properties = rhs._properties; + } + return *this; + } + + static URI graph_to_uri(Graph g) { + switch (g) { + case Graph::EXTERNAL: return URI(INGEN_NS "externalContext"); + case Graph::INTERNAL: return URI(INGEN_NS "internalContext"); + default: return URI(INGEN_NS "defaultContext"); + } + } + + static Graph uri_to_graph(const URI& uri) { + if (uri == INGEN_NS "externalContext") { + return Graph::EXTERNAL; + } else if (uri == INGEN_NS "internalContext") { + return Graph::INTERNAL; + } + return Graph::DEFAULT; + } + + /** Get a single property value. + * + * This is only useful for properties with a single value. If the + * requested property has several values, the first will be returned. + */ + virtual const Atom& get_property(const URI& uri) const; + + /** Set (replace) a property value. + * + * This will first erase any properties with the given `uri`, so after + * this call exactly one property with predicate `uri` will be set. + */ + virtual const Atom& set_property(const URI& uri, + const Atom& value, + Graph ctx = Graph::DEFAULT); + + /** Set (replace) a property value. + * + * This will first erase any properties with the given `uri`, so after + * this call exactly one property with predicate `uri` will be set. + */ + virtual const Atom& set_property(const URI& uri, + const URIs::Quark& value, + Graph ctx = Graph::DEFAULT); + + /** Add a property value. + * + * This will not remove any existing values, so if properties with + * predicate `uri` and values other than `value` exist, this will result + * in multiple values for the property. + * + * @return True iff a new property was added. + */ + virtual bool add_property(const URI& uri, + const Atom& value, + Graph ctx = Graph::DEFAULT); + + /** Remove a property. + * + * If `value` is patch:wildcard then any property with `uri` for a + * predicate will be removed. + */ + virtual void remove_property(const URI& uri, + const Atom& value); + + /** Remove a property. + * + * If `value` is patch:wildcard then any property with `uri` for a + * predicate will be removed. + */ + virtual void remove_property(const URI& uri, + const URIs::Quark& value); + + /** Return true iff a property is set with a specific value. */ + virtual bool has_property(const URI& uri, + const Atom& value) const; + + /** Return true iff a property is set with a specific value. */ + virtual bool has_property(const URI& uri, + const URIs::Quark& value) const; + + /** Set (replace) several properties at once. + * + * This will erase all properties with keys in `p`, though multiple values + * for one property may exist in `p` will all be set (unlike simply + * calling set_property in a loop which would only set one value). + */ + void set_properties(const Properties& props); + + /** Add several properties at once. */ + void add_properties(const Properties& props); + + /** Remove several properties at once. + * + * This removes all matching properties (both key and value), or all + * properties with a matching key if the value in `p` is patch:wildcard. + */ + void remove_properties(const Properties& props); + + /** Hook called whenever a property is added. + * + * This can be used by derived classes to implement special behaviour for + * particular properties (e.g. ingen:value for ports). + */ + virtual void on_property(const URI& uri, const Atom& value) {} + + /** Hook called whenever a property value is removed. + * + * If all values for a key are removed, then value will be the wildcard. + * + * This can be used by derived classes to implement special behaviour for + * particular properties (e.g. ingen:value for ports). + */ + virtual void on_property_removed(const URI& uri, const Atom& value) {} + + /** Get the ingen type from a set of Properties. + * + * If some coherent ingen type is found, true is returned and the appropriate + * output parameter set to true. Otherwise false is returned. + */ + static bool type(const URIs& uris, + const Properties& properties, + bool& graph, + bool& block, + bool& port, + bool& is_output); + + virtual void set_uri(const URI& uri) { _uri = uri; } + + /** Get all the properties with a given context. */ + Properties properties(Resource::Graph ctx) const; + + const URIs& uris() const { return _uris; } + const URI& uri() const { return _uri; } + const Properties& properties() const { return _properties; } + Properties& properties() { return _properties; } + +protected: + const Atom& set_property(const URI& uri, const Atom& value) const; + + const URIs& _uris; + +private: + URI _uri; + mutable Properties _properties; +}; + +} // namespace Ingen + +#endif // INGEN_RESOURCE_HPP diff --git a/ingen/Serialiser.hpp b/ingen/Serialiser.hpp new file mode 100644 index 00000000..fcfb40a7 --- /dev/null +++ b/ingen/Serialiser.hpp @@ -0,0 +1,103 @@ +/* + 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_SERIALISER_HPP +#define INGEN_SERIALISER_HPP + +#include <stdexcept> +#include <string> + +#include "ingen/ingen.h" +#include "ingen/types.hpp" +#include "sord/sordmm.hpp" + +namespace Raul { class Path; } + +namespace Ingen { + +class Arc; +class Node; +class URI; +class World; + +/** + Serialiser for writing graphs to Turtle files or strings. + + @ingroup Ingen +*/ +class INGEN_API Serialiser +{ +public: + explicit Serialiser(World& world); + virtual ~Serialiser(); + + /** Write a graph and all its contents as a complete bundle. */ + virtual void write_bundle(SPtr<const Node> graph, + const URI& uri); + + /** Begin a serialization to a string. + * + * This must be called before any serializing methods. + * + * The results of the serialization will be returned by the finish() method after + * the desired objects have been serialised. + * + * All serialized paths will have the root path chopped from their prefix + * (therefore all serialized paths must be descendants of the root) + */ + virtual void start_to_string(const Raul::Path& root, + const URI& base_uri); + + /** Begin a serialization to a file. + * + * This must be called before any serializing methods. + * + * All serialized paths will have the root path chopped from their prefix + * (therefore all serialized paths must be descendants of the root) + */ + virtual void start_to_file(const Raul::Path& root, + const std::string& filename); + + /** Serialize an object (graph, block, or port). + * + * @throw std::logic_error + */ + virtual void serialise(SPtr<const Node> object); + + /** Serialize an arc. + * + * @throw std::logic_error + */ + virtual void serialise_arc(const Sord::Node& parent, + SPtr<const Arc> arc); + + /** Finish serialization. + * + * If this is a file serialization, this must be called to finish and close + * the output file, and the empty string is returned. + * + * If this is a string serialization, the serialized result is returned. + */ + virtual std::string finish(); + +private: + struct Impl; + Impl* me; +}; + +} // namespace Ingen + +#endif // INGEN_SERIALISER_HPP diff --git a/ingen/SocketReader.hpp b/ingen/SocketReader.hpp new file mode 100644 index 00000000..37ec8567 --- /dev/null +++ b/ingen/SocketReader.hpp @@ -0,0 +1,78 @@ +/* + 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_SOCKET_READER_HPP +#define INGEN_SOCKET_READER_HPP + +#include <thread> + +#include "ingen/ingen.h" +#include "ingen/types.hpp" +#include "serd/serd.h" +#include "sord/sord.h" + +namespace Raul { class Socket; } + +namespace Ingen { + +class Interface; +class World; + +/** Calls Interface methods based on Turtle messages received via socket. */ +class INGEN_API SocketReader +{ +public: + SocketReader(World& world, + Interface& iface, + SPtr<Raul::Socket> sock); + + virtual ~SocketReader(); + +protected: + virtual void on_hangup() {} + +private: + void run(); + + static SerdStatus set_base_uri(SocketReader* iface, + const SerdNode* uri_node); + + static SerdStatus set_prefix(SocketReader* iface, + const SerdNode* name, + const SerdNode* uri_node); + + static SerdStatus 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); + + World& _world; + Interface& _iface; + SerdEnv* _env; + SordInserter* _inserter; + SordNode* _msg_node; + SPtr<Raul::Socket> _socket; + bool _exit_flag; + std::thread _thread; +}; + +} // namespace Ingen + +#endif // INGEN_SOCKET_READER_HPP diff --git a/ingen/SocketWriter.hpp b/ingen/SocketWriter.hpp new file mode 100644 index 00000000..1dcc077b --- /dev/null +++ b/ingen/SocketWriter.hpp @@ -0,0 +1,57 @@ +/* + This file is part of Ingen. + Copyright 2012-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_SOCKET_WRITER_HPP +#define INGEN_SOCKET_WRITER_HPP + +#include <cstddef> +#include <cstdint> + +#include "ingen/TurtleWriter.hpp" +#include "ingen/ingen.h" + +namespace Raul { +class Socket; +} + +namespace Ingen { + +class URI; +class URIMap; +class URIs; + +/** An Interface that writes Turtle messages to a socket. + */ +class INGEN_API SocketWriter : public TurtleWriter +{ +public: + SocketWriter(URIMap& map, + URIs& uris, + const URI& uri, + SPtr<Raul::Socket> sock); + + size_t text_sink(const void* buf, size_t len) override; + + /** Override of bundle_end to terminate bundles in the stream. */ + void bundle_end(); + +protected: + SPtr<Raul::Socket> _socket; +}; + +} // namespace Ingen + +#endif // INGEN_SOCKET_WRITER_HPP diff --git a/ingen/Status.hpp b/ingen/Status.hpp new file mode 100644 index 00000000..c1002a17 --- /dev/null +++ b/ingen/Status.hpp @@ -0,0 +1,92 @@ +/* + 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_STATUS_HPP +#define INGEN_STATUS_HPP + +namespace Ingen { + +enum class Status { + SUCCESS, + FAILURE, + + BAD_INDEX, + BAD_OBJECT_TYPE, + BAD_REQUEST, + BAD_URI, + BAD_VALUE_TYPE, + BAD_VALUE, + CLIENT_NOT_FOUND, + CREATION_FAILED, + DIRECTION_MISMATCH, + EXISTS, + INTERNAL_ERROR, + INVALID_PARENT, + INVALID_POLY, + NOT_DELETABLE, + NOT_FOUND, + NOT_MOVABLE, + NOT_PREPARED, + NO_SPACE, + PARENT_DIFFERS, + PARENT_NOT_FOUND, + PROTOTYPE_NOT_FOUND, + PORT_NOT_FOUND, + TYPE_MISMATCH, + UNKNOWN_TYPE, + COMPILATION_FAILED +}; + +static inline const char* +ingen_status_string(Status st) +{ + switch (st) { + case Status::SUCCESS: return "Success"; + case Status::FAILURE: return "Failure"; + + case Status::BAD_INDEX: return "Invalid index"; + case Status::BAD_OBJECT_TYPE: return "Invalid object type"; + case Status::BAD_REQUEST: return "Invalid request"; + case Status::BAD_URI: return "Invalid URI"; + case Status::BAD_VALUE_TYPE: return "Invalid value type"; + case Status::BAD_VALUE: return "Invalid value"; + case Status::CLIENT_NOT_FOUND: return "Client not found"; + case Status::CREATION_FAILED: return "Creation failed"; + case Status::DIRECTION_MISMATCH: return "Direction mismatch"; + case Status::EXISTS: return "Object exists"; + case Status::INTERNAL_ERROR: return "Internal error"; + case Status::INVALID_PARENT: return "Invalid parent"; + case Status::INVALID_POLY: return "Invalid polyphony"; + case Status::NOT_DELETABLE: return "Object not deletable"; + case Status::NOT_FOUND: return "Object not found"; + case Status::NOT_MOVABLE: return "Object not movable"; + case Status::NOT_PREPARED: return "Not prepared"; + case Status::NO_SPACE: return "Insufficient space"; + case Status::PARENT_DIFFERS: return "Parent differs"; + case Status::PARENT_NOT_FOUND: return "Parent not found"; + case Status::PROTOTYPE_NOT_FOUND: return "Prototype not found"; + case Status::PORT_NOT_FOUND: return "Port not found"; + case Status::TYPE_MISMATCH: return "Type mismatch"; + case Status::UNKNOWN_TYPE: return "Unknown type"; + case Status::COMPILATION_FAILED: return "Graph compilation failed"; + } + + return "Unknown error"; +} + +} // namespace Ingen + +#endif // INGEN_STATUS_HPP diff --git a/ingen/Store.hpp b/ingen/Store.hpp new file mode 100644 index 00000000..52c9012b --- /dev/null +++ b/ingen/Store.hpp @@ -0,0 +1,89 @@ +/* + 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_STORE_HPP +#define INGEN_STORE_HPP + +#include <cstddef> +#include <map> +#include <mutex> +#include <utility> + +#include "ingen/ingen.h" +#include "ingen/types.hpp" +#include "raul/Deletable.hpp" +#include "raul/Noncopyable.hpp" +#include "raul/Path.hpp" + +namespace Raul { class Symbol; } + +namespace Ingen { + +class Node; + +/** Store of objects in the graph hierarchy. + * @ingroup IngenShared + */ +class INGEN_API Store : public Raul::Noncopyable + , public Raul::Deletable + , public std::map< const Raul::Path, SPtr<Node> > { +public: + void add(Node* o); + + Node* get(const Raul::Path& path) { + const iterator i = find(path); + return (i == end()) ? nullptr : i->second.get(); + } + + typedef std::pair<const_iterator, const_iterator> const_range; + + typedef std::map< Raul::Path, SPtr<Node> > Objects; + + iterator find_descendants_end(Store::iterator parent); + const_iterator find_descendants_end(Store::const_iterator parent) const; + + const_range children_range(SPtr<const Node> o) const; + + /** Remove the object at `top` and all its children from the store. + * + * @param top Iterator to first (topmost parent) object to remove. + * + * @param removed Filled with all the removed objects. Note this may be + * many objects since any children will be removed as well. + */ + void remove(iterator top, Objects& removed); + + /** Rename (move) the object at `top` to `new_path`. + * + * Note this invalidates `i`. + */ + void rename(iterator top, const Raul::Path& new_path); + + unsigned child_name_offset(const Raul::Path& parent, + const Raul::Symbol& symbol, + bool allow_zero=true) const; + + typedef std::recursive_mutex Mutex; + + Mutex& mutex() { return _mutex; } + +private: + Mutex _mutex; +}; + +} // namespace Ingen + +#endif // INGEN_STORE_HPP diff --git a/ingen/StreamWriter.hpp b/ingen/StreamWriter.hpp new file mode 100644 index 00000000..66a03216 --- /dev/null +++ b/ingen/StreamWriter.hpp @@ -0,0 +1,52 @@ +/* + 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/>. +*/ + +#ifndef INGEN_STREAMWRITER_HPP +#define INGEN_STREAMWRITER_HPP + +#include <cstdio> + +#include "ingen/ingen.h" +#include "ingen/ColorContext.hpp" +#include "ingen/TurtleWriter.hpp" + +namespace Ingen { + +class URI; +class URIMap; +class URIs; + +/** An Interface that writes Turtle messages to a stream. + */ +class INGEN_API StreamWriter : public TurtleWriter +{ +public: + StreamWriter(URIMap& map, + URIs& uris, + const URI& uri, + FILE* stream, + ColorContext::Color color); + + size_t text_sink(const void* buf, size_t len) override; + +protected: + FILE* _stream; + ColorContext::Color _color; +}; + +} // namespace Ingen + +#endif // INGEN_STREAMWRITER_HPP diff --git a/ingen/Tee.hpp b/ingen/Tee.hpp new file mode 100644 index 00000000..2a6f00df --- /dev/null +++ b/ingen/Tee.hpp @@ -0,0 +1,63 @@ +/* + 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_TEE_HPP +#define INGEN_ENGINE_TEE_HPP + +#include <cstddef> +#include <mutex> +#include <set> +#include <utility> + +#include "ingen/Interface.hpp" +#include "ingen/Message.hpp" +#include "ingen/types.hpp" + +namespace Ingen { + +/** Interface that forwards all calls to several sinks. */ +class Tee : public Interface +{ +public: + typedef std::set< SPtr<Interface> > Sinks; + + explicit Tee(Sinks sinks) : _sinks(std::move(sinks)) {} + + SPtr<Interface> respondee() const override { + return (*_sinks.begin())->respondee(); + } + + void set_respondee(SPtr<Interface> respondee) override { + (*_sinks.begin())->set_respondee(respondee); + } + + void message(const Message& message) override { + std::lock_guard<std::mutex> lock(_sinks_mutex); + for (const auto& s : _sinks) { + s->message(message); + } + } + + URI uri() const override { return URI("ingen:/tee"); } + +private: + std::mutex _sinks_mutex; + Sinks _sinks; +}; + +} // namespace Ingen + +#endif // INGEN_ENGINE_TEE_HPP diff --git a/ingen/TurtleWriter.hpp b/ingen/TurtleWriter.hpp new file mode 100644 index 00000000..8cf7ceb8 --- /dev/null +++ b/ingen/TurtleWriter.hpp @@ -0,0 +1,68 @@ +/* + 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/>. +*/ + +#ifndef INGEN_TURTLE_WRITER_HPP +#define INGEN_TURTLE_WRITER_HPP + +#include <cstddef> +#include <cstdint> + +#include "ingen/AtomSink.hpp" +#include "ingen/AtomWriter.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; + +/** An Interface that writes Turtle messages to a sink method. + * + * Derived classes must implement text_sink() to do something with the + * serialized messages. + */ +class INGEN_API TurtleWriter : public AtomWriter, public AtomSink +{ +public: + TurtleWriter(URIMap& map, URIs& uris, const URI& uri); + + ~TurtleWriter() override; + + /** AtomSink method which receives calls serialized to LV2 atoms. */ + bool write(const LV2_Atom* msg, int32_t default_id=0) override; + + /** Pure virtual text sink which receives calls serialized to Turtle. */ + virtual size_t text_sink(const void* buf, size_t len) = 0; + + URI uri() const override { return _uri; } + +protected: + URIMap& _map; + Sratom* _sratom; + SerdNode _base; + SerdURI _base_uri; + SerdEnv* _env; + SerdWriter* _writer; + URI _uri; + bool _wrote_prefixes; +}; + +} // namespace Ingen + +#endif // INGEN_TURTLE_WRITER_HPP diff --git a/ingen/URI.hpp b/ingen/URI.hpp new file mode 100644 index 00000000..3c6d38d1 --- /dev/null +++ b/ingen/URI.hpp @@ -0,0 +1,160 @@ +/* + 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/>. +*/ + +#ifndef INGEN_URI_HPP +#define INGEN_URI_HPP + +#include <iosfwd> +#include <string> + +#include <boost/utility/string_view.hpp> + +#include "ingen/FilePath.hpp" +#include "ingen/ingen.h" +#include "serd/serd.h" +#include "sord/sordmm.hpp" + +namespace Ingen { + +class INGEN_API URI +{ +public: + using Chunk = boost::string_view; + + URI(); + explicit URI(const std::string& str); + explicit URI(const char* str); + URI(const std::string& str, const URI& base); + URI(const Sord::Node& node); + URI(SerdNode node); + explicit URI(const FilePath& path); + + URI(const URI& uri); + URI& operator=(const URI& uri); + + URI(URI&& uri); + URI& operator=(URI&& uri); + + ~URI(); + + URI make_relative(const URI& base) const; + + bool empty() const { return !_node.buf; } + + std::string string() const { return std::string(c_str(), _node.n_bytes); } + size_t length() const { return _node.n_bytes; } + const char* c_str() const { return (const char*)_node.buf; } + + FilePath file_path() const { + return scheme() == "file" ? FilePath(path()) : FilePath(); + } + + operator std::string() const { return string(); } + + const char* begin() const { return (const char*)_node.buf; } + const char* end() const { return (const char*)_node.buf + _node.n_bytes; } + + Chunk scheme() const { return make_chunk(_uri.scheme); } + Chunk authority() const { return make_chunk(_uri.authority); } + Chunk path() const { return make_chunk(_uri.path); } + Chunk query() const { return make_chunk(_uri.query); } + Chunk fragment() const { return make_chunk(_uri.fragment); } + + static bool is_valid(const char* str) { + return serd_uri_string_has_scheme((const uint8_t*)str); + } + + static bool is_valid(const std::string& str) + { + return is_valid(str.c_str()); + } + +private: + URI(SerdNode node, SerdURI uri); + + static Chunk make_chunk(const SerdChunk& chunk) { + return Chunk((const char*)chunk.buf, chunk.len); + } + + SerdNode _node; + SerdURI _uri; +}; + +inline bool operator==(const URI& lhs, const URI& rhs) +{ + return lhs.string() == rhs.string(); +} + +inline bool operator==(const URI& lhs, const std::string& rhs) +{ + return lhs.string() == rhs; +} + +inline bool operator==(const URI& lhs, const char* rhs) +{ + return lhs.string() == rhs; +} + +inline bool operator==(const URI& lhs, const Sord::Node& rhs) +{ + return rhs.type() == Sord::Node::URI && lhs.string() == rhs.to_string(); +} + +inline bool operator==(const Sord::Node& lhs, const URI& rhs) +{ + return rhs == lhs; +} + +inline bool operator!=(const URI& lhs, const URI& rhs) +{ + return lhs.string() != rhs.string(); +} + +inline bool operator!=(const URI& lhs, const std::string& rhs) +{ + return lhs.string() != rhs; +} + +inline bool operator!=(const URI& lhs, const char* rhs) +{ + return lhs.string() != rhs; +} + +inline bool operator!=(const URI& lhs, const Sord::Node& rhs) +{ + return !(lhs == rhs); +} + +inline bool operator!=(const Sord::Node& lhs, const URI& rhs) +{ + return !(lhs == rhs); +} + +inline bool operator<(const URI& lhs, const URI& rhs) +{ + return lhs.string() < rhs.string(); +} + +template <typename Char, typename Traits> +inline std::basic_ostream<Char, Traits>& +operator<<(std::basic_ostream<Char, Traits>& os, const URI& uri) +{ + return os << uri.string(); +} + +} // namespace Ingen + +#endif // INGEN_URI_HPP diff --git a/ingen/URIMap.hpp b/ingen/URIMap.hpp new file mode 100644 index 00000000..90c5f0ac --- /dev/null +++ b/ingen/URIMap.hpp @@ -0,0 +1,100 @@ +/* + 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_URIMAP_HPP +#define INGEN_URIMAP_HPP + +#include <cstdint> +#include <mutex> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +#include "ingen/LV2Features.hpp" +#include "ingen/ingen.h" +#include "ingen/types.hpp" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" +#include "raul/Noncopyable.hpp" + +namespace Ingen { + +class Log; +class Node; +class World; + +/** URI to integer map and implementation of LV2 URID extension. + * @ingroup IngenShared + */ +class INGEN_API URIMap : public Raul::Noncopyable { +public: + URIMap(Log& log, LV2_URID_Map* map, LV2_URID_Unmap* unmap); + + uint32_t map_uri(const char* uri); + uint32_t map_uri(const std::string& uri) { return map_uri(uri.c_str()); } + const char* unmap_uri(uint32_t urid) const; + + class Feature : public LV2Features::Feature { + public: + Feature(const char* URI, void* data) { + _feature.URI = URI; + _feature.data = data; + } + + const char* uri() const override { return _feature.URI; } + + SPtr<LV2_Feature> feature(World*, Node*) override { + return SPtr<LV2_Feature>(&_feature, NullDeleter<LV2_Feature>); + } + + private: + LV2_Feature _feature; + }; + + struct URIDMapFeature : public Feature { + URIDMapFeature(URIMap* map, LV2_URID_Map* impl, Log& log); + LV2_URID map(const char* uri); + static LV2_URID default_map(LV2_URID_Map_Handle h, const char* c_uri); + LV2_URID_Map urid_map; + Log& log; + }; + + struct URIDUnmapFeature : public Feature { + URIDUnmapFeature(URIMap* map, LV2_URID_Unmap* impl); + const char* unmap(const LV2_URID urid); + static const char* default_unmap(LV2_URID_Map_Handle h, LV2_URID urid); + LV2_URID_Unmap urid_unmap; + }; + + SPtr<URIDMapFeature> urid_map_feature() { return _urid_map_feature; } + SPtr<URIDUnmapFeature> urid_unmap_feature() { return _urid_unmap_feature; } + +private: + friend struct URIDMapFeature; + friend struct URIDUnMapFeature; + + SPtr<URIDMapFeature> _urid_map_feature; + SPtr<URIDUnmapFeature> _urid_unmap_feature; + + std::mutex _mutex; + std::unordered_map<std::string, LV2_URID> _map; + std::vector<std::string> _unmap; +}; + +} // namespace Ingen + +#endif // INGEN_URIMAP_HPP diff --git a/ingen/URIs.hpp b/ingen/URIs.hpp new file mode 100644 index 00000000..71c14d14 --- /dev/null +++ b/ingen/URIs.hpp @@ -0,0 +1,234 @@ +/* + 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_URIS_HPP +#define INGEN_URIS_HPP + +#include "ingen/Atom.hpp" +#include "ingen/URI.hpp" +#include "ingen/ingen.h" +#include "lilv/lilv.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "raul/Noncopyable.hpp" + +namespace Ingen { + +class Forge; +class URIMap; + +/** Frequently used interned URIs. + * + * This class initially maps all the special URIs used throughout the code + * using the URIMap so they can be used quickly with the performance of + * integers, but still be dynamic. + * + * @ingroup ingen + */ +class INGEN_API URIs : public Raul::Noncopyable { +public: + URIs(Ingen::Forge& forge, URIMap* map, LilvWorld* lworld); + + struct Quark : public URI { + Quark(Ingen::Forge& forge, + URIMap* map, + LilvWorld* lworld, + const char* str); + + Quark(const Quark& copy); + + ~Quark(); + + operator LV2_URID() const { return urid.get<LV2_URID>(); } + explicit operator Atom() const { return urid; } + operator const LilvNode*() const { return lnode; } + + Atom urid; + Atom uri; + LilvNode* lnode; + }; + + Ingen::Forge& forge; + + const Quark atom_AtomPort; + const Quark atom_Bool; + const Quark atom_Chunk; + const Quark atom_Float; + const Quark atom_Int; + const Quark atom_Object; + const Quark atom_Path; + const Quark atom_Sequence; + const Quark atom_Sound; + const Quark atom_String; + const Quark atom_URI; + const Quark atom_URID; + const Quark atom_bufferType; + const Quark atom_eventTransfer; + const Quark atom_supports; + const Quark bufsz_maxBlockLength; + const Quark bufsz_minBlockLength; + const Quark bufsz_sequenceSize; + const Quark doap_name; + const Quark ingen_Arc; + const Quark ingen_Block; + const Quark ingen_BundleEnd; + const Quark ingen_BundleStart; + const Quark ingen_Graph; + const Quark ingen_GraphPrototype; + const Quark ingen_Internal; + const Quark ingen_Redo; + const Quark ingen_Undo; + const Quark ingen_activity; + const Quark ingen_arc; + const Quark ingen_block; + const Quark ingen_broadcast; + const Quark ingen_canvasX; + const Quark ingen_canvasY; + const Quark ingen_enabled; + const Quark ingen_externalContext; + const Quark ingen_file; + const Quark ingen_head; + const Quark ingen_incidentTo; + const Quark ingen_internalContext; + const Quark ingen_loadedBundle; + const Quark ingen_maxRunLoad; + const Quark ingen_meanRunLoad; + const Quark ingen_minRunLoad; + const Quark ingen_numThreads; + const Quark ingen_polyphonic; + const Quark ingen_polyphony; + const Quark ingen_prototype; + const Quark ingen_sprungLayout; + const Quark ingen_tail; + const Quark ingen_uiEmbedded; + const Quark ingen_value; + const Quark log_Error; + const Quark log_Note; + const Quark log_Trace; + const Quark log_Warning; + const Quark lv2_AudioPort; + const Quark lv2_CVPort; + const Quark lv2_ControlPort; + const Quark lv2_InputPort; + const Quark lv2_OutputPort; + const Quark lv2_Plugin; + const Quark lv2_appliesTo; + const Quark lv2_binary; + const Quark lv2_connectionOptional; + const Quark lv2_control; + const Quark lv2_default; + const Quark lv2_designation; + const Quark lv2_enumeration; + const Quark lv2_extensionData; + const Quark lv2_index; + const Quark lv2_integer; + const Quark lv2_maximum; + const Quark lv2_microVersion; + const Quark lv2_minimum; + const Quark lv2_minorVersion; + const Quark lv2_name; + const Quark lv2_port; + const Quark lv2_portProperty; + const Quark lv2_prototype; + const Quark lv2_sampleRate; + const Quark lv2_scalePoint; + const Quark lv2_symbol; + const Quark lv2_toggled; + const Quark midi_Bender; + const Quark midi_ChannelPressure; + const Quark midi_Controller; + const Quark midi_MidiEvent; + const Quark midi_NoteOn; + const Quark midi_binding; + const Quark midi_controllerNumber; + const Quark midi_noteNumber; + const Quark morph_AutoMorphPort; + const Quark morph_MorphPort; + const Quark morph_currentType; + const Quark morph_supportsType; + const Quark opt_interface; + const Quark param_sampleRate; + const Quark patch_Copy; + const Quark patch_Delete; + const Quark patch_Get; + const Quark patch_Message; + const Quark patch_Move; + const Quark patch_Patch; + const Quark patch_Put; + const Quark patch_Response; + const Quark patch_Set; + const Quark patch_add; + const Quark patch_body; + const Quark patch_context; + const Quark patch_destination; + const Quark patch_property; + const Quark patch_remove; + const Quark patch_sequenceNumber; + const Quark patch_subject; + const Quark patch_value; + const Quark patch_wildcard; + const Quark pprops_logarithmic; + const Quark pset_Preset; + const Quark pset_preset; + const Quark rdf_type; + const Quark rdfs_Class; + const Quark rdfs_label; + const Quark rdfs_seeAlso; + const Quark rsz_minimumSize; + const Quark state_loadDefaultState; + const Quark state_state; + const Quark time_Position; + const Quark time_bar; + const Quark time_barBeat; + const Quark time_beatUnit; + const Quark time_beatsPerBar; + const Quark time_beatsPerMinute; + const Quark time_frame; + const Quark time_speed; + const Quark work_schedule; +}; + +inline bool +operator==(const URIs::Quark& lhs, const Atom& rhs) +{ + if (rhs.type() == lhs.urid.type()) { + return rhs == lhs.urid; + } else if (rhs.type() == lhs.uri.type()) { + return rhs == lhs.uri; + } + return false; +} + +inline bool +operator==(const Atom& lhs, const URIs::Quark& rhs) +{ + return rhs == lhs; +} + +inline bool +operator!=(const Atom& lhs, const URIs::Quark& rhs) +{ + return !(lhs == rhs); +} + +inline bool +operator!=(const URIs::Quark& lhs, const Atom& rhs) +{ + return !(lhs == rhs); +} + +} // namespace Ingen + +#endif // INGEN_URIS_HPP diff --git a/ingen/World.hpp b/ingen/World.hpp new file mode 100644 index 00000000..13bd7001 --- /dev/null +++ b/ingen/World.hpp @@ -0,0 +1,150 @@ +/* + 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_WORLD_HPP +#define INGEN_WORLD_HPP + +#include <mutex> +#include <string> + +#include "ingen/ingen.h" +#include "ingen/types.hpp" +#include "lv2/lv2plug.in/ns/ext/log/log.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "raul/Noncopyable.hpp" + +typedef struct LilvWorldImpl LilvWorld; + +namespace Sord { class World; } + +namespace Ingen { + +class Configuration; +class EngineBase; +class Forge; +class Interface; +class LV2Features; +class Log; +class Parser; +class Serialiser; +class Store; +class URI; +class URIMap; +class URIs; + +/** The "world" all Ingen modules share. + * + * This is the root to which all components of Ingen are connected. It + * contains all necessary shared data (including the world for libraries like + * Sord and Lilv) and holds references to components. + * + * Some functionality in Ingen is implemented in dynamically loaded modules, + * which are loaded using this interface. When loaded, those modules add + * facilities to the World which can then be used throughout the code. + * + * The world is used in any process which uses the Ingen as a library, both + * client and server (e.g. the world may not actually contain an Engine, since + * it maybe running in another process or even on a different machine). + * + * @ingroup IngenShared + */ +class INGEN_API World : public Raul::Noncopyable { +public: + /** Construct a new Ingen world. + * @param map LV2 URID map implementation, or NULL to use internal. + * @param unmap LV2 URID unmap implementation, or NULL to use internal. + * @param log LV2 log implementation, or NULL to use internal. + */ + World(LV2_URID_Map* map, LV2_URID_Unmap* unmap, LV2_Log_Log* log); + + virtual ~World(); + + /** Load configuration from files and command line. + * @param argc Argument count (as in C main()) + * @param argv Argument vector (as in C main()) + */ + virtual void load_configuration(int& argc, char**& argv); + + /** Load an Ingen module by name (e.g. "server", "gui", etc.) + * @return True on success. + */ + virtual bool load_module(const char* name); + + /** Run a loaded module (modules that "run" only, e.g. gui). + * @return True on success. + */ + virtual bool run_module(const char* name); + + /** A function to create a new remote Interface. */ + typedef SPtr<Interface> (*InterfaceFactory)(World* world, + const URI& engine_uri, + SPtr<Interface> respondee); + + /** Register an InterfaceFactory (for module implementations). */ + virtual void add_interface_factory(const std::string& scheme, + InterfaceFactory factory); + + /** Return a new Interface to control a server. + * @param engine_uri The URI of the possibly remote server to control. + * @param respondee The Interface that will receive responses to commands + * and broadcasts, if applicable. + */ + virtual SPtr<Interface> new_interface(const URI& engine_uri, + SPtr<Interface> respondee); + + /** Run a script. */ + virtual bool run(const std::string& mime_type, + const std::string& filename); + + virtual void set_engine(SPtr<EngineBase> e); + virtual void set_interface(SPtr<Interface> i); + virtual void set_store(SPtr<Store> s); + + virtual SPtr<EngineBase> engine(); + virtual SPtr<Interface> interface(); + virtual SPtr<Parser> parser(); + virtual SPtr<Serialiser> serialiser(); + virtual SPtr<Store> store(); + + virtual int& argc(); + virtual char**& argv(); + virtual Configuration& conf(); + + /** Lock for rdf_world() or lilv_world(). */ + virtual std::mutex& rdf_mutex(); + + virtual Sord::World* rdf_world(); + virtual LilvWorld* lilv_world(); + + virtual LV2Features& lv2_features(); + virtual Ingen::Forge& forge(); + virtual URIMap& uri_map(); + virtual URIs& uris(); + + virtual void set_jack_uuid(const std::string& uuid); + virtual std::string jack_uuid(); + + virtual Log& log(); + +private: + class Impl; + + Impl* _impl; +}; + +} // namespace Ingen + +#endif // INGEN_WORLD_HPP diff --git a/ingen/client/ArcModel.hpp b/ingen/client/ArcModel.hpp new file mode 100644 index 00000000..8b129a00 --- /dev/null +++ b/ingen/client/ArcModel.hpp @@ -0,0 +1,68 @@ +/* + 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_CLIENT_ARCMODEL_HPP +#define INGEN_CLIENT_ARCMODEL_HPP + +#include <cassert> + +#include "ingen/types.hpp" +#include "raul/Path.hpp" + +#include "ingen/Arc.hpp" +#include "ingen/client/PortModel.hpp" +#include "ingen/ingen.h" + +namespace Ingen { +namespace Client { + +class ClientStore; + +/** Class to represent a port->port connections in the engine. + * + * @ingroup IngenClient + */ +class INGEN_API ArcModel : public Arc +{ +public: + SPtr<PortModel> tail() const { return _tail; } + SPtr<PortModel> head() const { return _head; } + + const Raul::Path& tail_path() const { return _tail->path(); } + const Raul::Path& head_path() const { return _head->path(); } + +private: + friend class ClientStore; + + ArcModel(SPtr<PortModel> tail, SPtr<PortModel> head) + : _tail(std::move(tail)) + , _head(std::move(head)) + { + assert(_tail); + assert(_head); + assert(_tail->parent()); + assert(_head->parent()); + assert(_tail->path() != _head->path()); + } + + const SPtr<PortModel> _tail; + const SPtr<PortModel> _head; +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_ARCMODEL_HPP diff --git a/ingen/client/BlockModel.hpp b/ingen/client/BlockModel.hpp new file mode 100644 index 00000000..38e8987e --- /dev/null +++ b/ingen/client/BlockModel.hpp @@ -0,0 +1,118 @@ +/* + 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_CLIENT_BLOCKMODEL_HPP +#define INGEN_CLIENT_BLOCKMODEL_HPP + +#include <cstdlib> +#include <string> +#include <vector> + +#include "ingen/Node.hpp" +#include "ingen/client/ObjectModel.hpp" +#include "ingen/client/PluginModel.hpp" +#include "ingen/client/PortModel.hpp" +#include "ingen/ingen.h" +#include "ingen/types.hpp" + +namespace Raul { class Path; } + +namespace Ingen { + +class URIs; + +namespace Client { + +class PluginModel; +class ClientStore; + +/** Block model class, used by the client to store engine's state. + * + * @ingroup IngenClient + */ +class INGEN_API BlockModel : public ObjectModel +{ +public: + BlockModel(const BlockModel& copy); + virtual ~BlockModel(); + + GraphType graph_type() const { return Node::GraphType::BLOCK; } + + typedef std::vector< SPtr<const PortModel> > Ports; + + SPtr<const PortModel> get_port(const Raul::Symbol& symbol) const; + SPtr<const PortModel> get_port(uint32_t index) const; + + Node* port(uint32_t index) const; + + const URI& plugin_uri() const { return _plugin_uri; } + const Resource* plugin() const { return _plugin.get(); } + Resource* plugin() { return _plugin.get(); } + SPtr<PluginModel> plugin_model() const { return _plugin; } + uint32_t num_ports() const { return _ports.size(); } + const Ports& ports() const { return _ports; } + + void default_port_value_range(SPtr<const PortModel> port, + float& min, + float& max, + uint32_t srate = 1) const; + + void port_value_range(SPtr<const PortModel> port, + float& min, + float& max, + uint32_t srate = 1) const; + + std::string label() const; + std::string port_label(SPtr<const PortModel> port) const; + + // Signals + INGEN_SIGNAL(new_port, void, SPtr<const PortModel>); + INGEN_SIGNAL(removed_port, void, SPtr<const PortModel>); + +protected: + friend class ClientStore; + + BlockModel(URIs& uris, + const URI& plugin_uri, + const Raul::Path& path); + BlockModel(URIs& uris, + SPtr<PluginModel> plugin, + const Raul::Path& path); + explicit BlockModel(const Raul::Path& path); + + void add_child(SPtr<ObjectModel> c); + bool remove_child(SPtr<ObjectModel> c); + void add_port(SPtr<PortModel> pm); + void remove_port(SPtr<PortModel> port); + void remove_port(const Raul::Path& port_path); + void set(SPtr<ObjectModel> model); + + virtual void clear(); + + Ports _ports; ///< Vector of ports + URI _plugin_uri; ///< Plugin URI (if PluginModel is unknown) + SPtr<PluginModel> _plugin; ///< The plugin this block is an instance of + +private: + mutable uint32_t _num_values; ///< Size of _min_values and _max_values + mutable float* _min_values; ///< Port min values (cached for LV2) + mutable float* _max_values; ///< Port max values (cached for LV2) +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_BLOCKMODEL_HPP diff --git a/ingen/client/ClientStore.hpp b/ingen/client/ClientStore.hpp new file mode 100644 index 00000000..797052ef --- /dev/null +++ b/ingen/client/ClientStore.hpp @@ -0,0 +1,127 @@ +/* + 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_CLIENT_CLIENTSTORE_HPP +#define INGEN_CLIENT_CLIENTSTORE_HPP + +#include <cassert> +#include <list> +#include <string> + +#include "ingen/Interface.hpp" +#include "ingen/Store.hpp" +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" +#include "ingen/types.hpp" +#include "raul/Path.hpp" + +namespace Raul { class Atom; } + +namespace Ingen { + +class Log; +class Node; +class URIs; + +namespace Client { + +class BlockModel; +class GraphModel; +class ObjectModel; +class PluginModel; +class PortModel; +class SigClientInterface; + +/** Automatically manages models of objects in the engine. + * + * @ingroup IngenClient + */ +class INGEN_API ClientStore : public Store + , public Interface + , public INGEN_TRACKABLE { +public: + ClientStore( + URIs& uris, + Log& log, + SPtr<SigClientInterface> emitter = SPtr<SigClientInterface>()); + + URI uri() const override { return URI("ingen:/clients/store"); } + + SPtr<const ObjectModel> object(const Raul::Path& path) const; + SPtr<const PluginModel> plugin(const URI& uri) const; + SPtr<const Resource> resource(const URI& uri) const; + + void clear(); + + typedef std::map< const URI, SPtr<PluginModel> > Plugins; + SPtr<const Plugins> plugins() const { return _plugins; } + SPtr<Plugins> plugins() { return _plugins; } + void set_plugins(SPtr<Plugins> p) { _plugins = p; } + + URIs& uris() { return _uris; } + + void message(const Message& msg) override; + + 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&) {} + + INGEN_SIGNAL(new_object, void, SPtr<ObjectModel>); + INGEN_SIGNAL(new_plugin, void, SPtr<PluginModel>); + INGEN_SIGNAL(plugin_deleted, void, URI); + +private: + SPtr<ObjectModel> _object(const Raul::Path& path); + SPtr<PluginModel> _plugin(const URI& uri); + SPtr<PluginModel> _plugin(const Atom& uri); + SPtr<Resource> _resource(const URI& uri); + + void add_object(SPtr<ObjectModel> object); + SPtr<ObjectModel> remove_object(const Raul::Path& path); + + void add_plugin(SPtr<PluginModel> pm); + + SPtr<GraphModel> connection_graph(const Raul::Path& tail_path, + const Raul::Path& head_path); + + // Slots for SigClientInterface signals + bool attempt_connection(const Raul::Path& tail_path, + const Raul::Path& head_path); + + URIs& _uris; + Log& _log; + SPtr<SigClientInterface> _emitter; + + SPtr<Plugins> _plugins; ///< Map, keyed by plugin URI +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_CLIENTSTORE_HPP diff --git a/ingen/client/GraphModel.hpp b/ingen/client/GraphModel.hpp new file mode 100644 index 00000000..ef072d87 --- /dev/null +++ b/ingen/client/GraphModel.hpp @@ -0,0 +1,74 @@ +/* + 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_CLIENT_GRAPHMODEL_HPP +#define INGEN_CLIENT_GRAPHMODEL_HPP + +#include "ingen/client/BlockModel.hpp" +#include "ingen/ingen.h" +#include "ingen/types.hpp" + +namespace Ingen { +namespace Client { + +class ArcModel; +class ClientStore; + +/** Client's model of a graph. + * + * @ingroup IngenClient + */ +class INGEN_API GraphModel : public BlockModel +{ +public: + /* WARNING: Copy constructor creates a shallow copy WRT connections */ + + GraphType graph_type() const { return Node::GraphType::GRAPH; } + + SPtr<ArcModel> get_arc(const Ingen::Node* tail, + const Ingen::Node* head); + + bool enabled() const; + bool polyphonic() const; + uint32_t internal_poly() const; + + // Signals + INGEN_SIGNAL(new_block, void, SPtr<BlockModel>); + INGEN_SIGNAL(removed_block, void, SPtr<BlockModel>); + INGEN_SIGNAL(new_arc, void, SPtr<ArcModel>); + INGEN_SIGNAL(removed_arc, void, SPtr<ArcModel>); + +private: + friend class ClientStore; + + GraphModel(URIs& uris, const Raul::Path& graph_path) + : BlockModel(uris, uris.ingen_Graph, graph_path) + {} + + void clear(); + void add_child(SPtr<ObjectModel> c); + bool remove_child(SPtr<ObjectModel> o); + void remove_arcs_on(SPtr<PortModel> p); + + void add_arc(SPtr<ArcModel> arc); + void remove_arc(const Ingen::Node* tail, + const Ingen::Node* head); +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_GRAPHMODEL_HPP diff --git a/ingen/client/ObjectModel.hpp b/ingen/client/ObjectModel.hpp new file mode 100644 index 00000000..a5a68f1e --- /dev/null +++ b/ingen/client/ObjectModel.hpp @@ -0,0 +1,103 @@ +/* + 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/>. +*/ + +/** + @defgroup IngenClient Client-Side Models and Utilities +*/ + +#ifndef INGEN_CLIENT_OBJECTMODEL_HPP +#define INGEN_CLIENT_OBJECTMODEL_HPP + +#include <algorithm> +#include <cassert> +#include <cstdlib> + +#include "ingen/types.hpp" +#include "raul/Path.hpp" + +#include "ingen/Node.hpp" +#include "ingen/Resource.hpp" +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" + +namespace Ingen { + +class URIs; + +namespace Client { + +class ClientStore; + +/** Base class for all Node models (BlockModel, GraphModel, PortModel). + * + * There are no non-const public methods intentionally, models are not allowed + * to be manipulated directly by anything (but the Store) because of the + * asynchronous nature of engine control. To change something, use the + * controller (which the model probably shouldn't have a reference to but oh + * well, it reduces Collection Hell) and wait for the result (as a signal + * from this Model). + * + * @ingroup IngenClient + */ +class INGEN_API ObjectModel : public Node +{ +public: + bool is_a(const URIs::Quark& type) const; + + const Atom& get_property(const URI& key) const; + + void on_property(const URI& uri, const Atom& value); + void on_property_removed(const URI& uri, const Atom& value); + + const Raul::Path& path() const { return _path; } + const Raul::Symbol& symbol() const { return _symbol; } + SPtr<ObjectModel> parent() const { return _parent; } + bool polyphonic() const; + + Node* graph_parent() const { return _parent.get(); } + + // Signals + INGEN_SIGNAL(new_child, void, SPtr<ObjectModel>); + INGEN_SIGNAL(removed_child, void, SPtr<ObjectModel>); + INGEN_SIGNAL(property, void, const URI&, const Atom&); + INGEN_SIGNAL(property_removed, void, const URI&, const Atom&); + INGEN_SIGNAL(destroyed, void); + INGEN_SIGNAL(moved, void); + +protected: + friend class ClientStore; + + ObjectModel(URIs& uris, const Raul::Path& path); + ObjectModel(const ObjectModel& copy); + + virtual void set_path(const Raul::Path& p); + virtual void set_parent(SPtr<ObjectModel> p); + virtual void add_child(SPtr<ObjectModel> c) {} + virtual bool remove_child(SPtr<ObjectModel> c) { return true; } + + virtual void set(SPtr<ObjectModel> o); + + SPtr<ObjectModel> _parent; + +private: + Raul::Path _path; + Raul::Symbol _symbol; +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_OBJECTMODEL_HPP diff --git a/ingen/client/PluginModel.hpp b/ingen/client/PluginModel.hpp new file mode 100644 index 00000000..61de0f1a --- /dev/null +++ b/ingen/client/PluginModel.hpp @@ -0,0 +1,128 @@ +/* + 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_CLIENT_PLUGINMODEL_HPP +#define INGEN_CLIENT_PLUGINMODEL_HPP + +#include <list> +#include <map> +#include <string> +#include <utility> + +#include "ingen/Forge.hpp" +#include "ingen/Resource.hpp" +#include "ingen/World.hpp" +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" +#include "ingen/types.hpp" +#include "lilv/lilv.h" +#include "raul/Symbol.hpp" +#include "sord/sordmm.hpp" + +namespace Ingen { + +class URIs; + +namespace Client { + +class GraphModel; +class BlockModel; +class PluginUI; + +/** Model for a plugin available for loading. + * + * @ingroup IngenClient + */ +class INGEN_API PluginModel : public Ingen::Resource +{ +public: + PluginModel(URIs& uris, + const URI& uri, + const Atom& type, + const Ingen::Properties& properties); + + const Atom& type() const { return _type; } + + const URI type_uri() const + { + return URI(_type.is_valid() ? _uris.forge.str(_type, false) + : "http://www.w3.org/2002/07/owl#Nothing"); + } + + virtual const Atom& get_property(const URI& key) const; + + Raul::Symbol default_block_symbol() const; + std::string human_name() const; + std::string port_human_name(uint32_t i) const; + + typedef std::map<float, std::string> ScalePoints; + ScalePoints port_scale_points(uint32_t i) const; + + typedef std::map<URI, std::string> Presets; + const Presets& presets() const { return _presets; } + + static LilvWorld* lilv_world() { return _lilv_world; } + const LilvPlugin* lilv_plugin() const { return _lilv_plugin; } + + const LilvPort* lilv_port(uint32_t index) const; + + static void set_lilv_world(LilvWorld* world); + + bool has_ui() const; + + SPtr<PluginUI> ui(Ingen::World* world, + SPtr<const BlockModel> block) const; + + std::string documentation(bool html) const; + std::string port_documentation(uint32_t index, bool html) const; + + static void set_rdf_world(Sord::World& world) { + _rdf_world = &world; + } + + static Sord::World* rdf_world() { return _rdf_world; } + + // Signals + INGEN_SIGNAL(changed, void); + INGEN_SIGNAL(property, void, const URI&, const Atom&); + INGEN_SIGNAL(preset, void, const URI&, const std::string&); + + bool fetched() const { return _fetched; } + void set_fetched(bool f) { _fetched = f; } + +protected: + friend class ClientStore; + void set(SPtr<PluginModel> p); + + void add_preset(const URI& uri, const std::string& label); + +private: + std::string get_documentation(const LilvNode* subject, bool html) const; + + static Sord::World* _rdf_world; + static LilvWorld* _lilv_world; + static const LilvPlugins* _lilv_plugins; + + Atom _type; + const LilvPlugin* _lilv_plugin; + Presets _presets; + bool _fetched; +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_PLUGINMODEL_HPP diff --git a/ingen/client/PluginUI.hpp b/ingen/client/PluginUI.hpp new file mode 100644 index 00000000..a98df61d --- /dev/null +++ b/ingen/client/PluginUI.hpp @@ -0,0 +1,111 @@ +/* + 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_CLIENT_PLUGINUI_HPP +#define INGEN_CLIENT_PLUGINUI_HPP + +#include <set> + +#include "ingen/LV2Features.hpp" +#include "ingen/ingen.h" +#include "ingen/types.hpp" +#include "lilv/lilv.h" +#include "suil/suil.h" + +namespace Ingen { + +class Interface; +class World; + +namespace Client { + +class BlockModel; + +/** Custom user interface for a plugin. + * + * @ingroup IngenClient + */ +class INGEN_API PluginUI { +public: + ~PluginUI(); + + /** Create a UI for the given block and plugin. + * + * This does not actually instantiate the UI itself, so signals can be + * connected first. The caller should connect to signal_property_changed, + * then call instantiate(). + */ + static SPtr<PluginUI> create(Ingen::World* world, + SPtr<const BlockModel> block, + const LilvPlugin* plugin); + + /** Instantiate the UI. + * + * If true is returned, instantiation was successfull and the widget can be + * obtained with get_widget(). Otherwise, instantiation failed, so there is + * no widget and the UI can not be used. + */ + bool instantiate(); + bool instantiated () { return _instance != nullptr; } + + SuilWidget get_widget(); + + void port_event(uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer); + + bool is_resizable() const; + + /** Signal emitted when the UI sets a property. + * + * The application must connect to this signal to communicate with the + * engine and/or update itself as necessary. + */ + INGEN_SIGNAL(property_changed, void, + const URI&, // Subject + const URI&, // Predicate + const Atom&, // Object + Resource::Graph); // Context + + Ingen::World* world() const { return _world; } + SPtr<const BlockModel> block() const { return _block; } + +private: + PluginUI(Ingen::World* world, + SPtr<const BlockModel> block, + LilvUIs* uis, + const LilvUI* ui, + const LilvNode* ui_type); + + Ingen::World* _world; + SPtr<const BlockModel> _block; + SuilInstance* _instance; + LilvUIs* _uis; + const LilvUI* _ui; + LilvNode* _ui_node; + LilvNode* _ui_type; + std::set<uint32_t> _subscribed_ports; + + static SuilHost* ui_host; + + SPtr<LV2Features::FeatureArray> _features; +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_PLUGINUI_HPP diff --git a/ingen/client/PortModel.hpp b/ingen/client/PortModel.hpp new file mode 100644 index 00000000..9ad37378 --- /dev/null +++ b/ingen/client/PortModel.hpp @@ -0,0 +1,97 @@ +/* + 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_CLIENT_PORTMODEL_HPP +#define INGEN_CLIENT_PORTMODEL_HPP + +#include <cstdlib> +#include <string> + +#include "ingen/client/ObjectModel.hpp" +#include "ingen/ingen.h" +#include "ingen/types.hpp" +#include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" + +namespace Raul { class Path; } + +namespace Ingen { +namespace Client { + +/** Model of a port. + * + * @ingroup IngenClient + */ +class INGEN_API PortModel : public ObjectModel +{ +public: + enum class Direction { INPUT, OUTPUT }; + + GraphType graph_type() const { return Node::GraphType::PORT; } + + bool supports(const URIs::Quark& value_type) const; + + inline uint32_t index() const { return _index; } + inline const Atom& value() const { return get_property(_uris.ingen_value); } + inline bool is_input() const { return (_direction == Direction::INPUT); } + inline bool is_output() const { return (_direction == Direction::OUTPUT); } + + bool port_property(const URIs::Quark& uri) const; + + bool is_logarithmic() const { return port_property(_uris.pprops_logarithmic); } + bool is_enumeration() const { return port_property(_uris.lv2_enumeration); } + bool is_integer() const { return port_property(_uris.lv2_integer); } + bool is_toggle() const { return port_property(_uris.lv2_toggled); } + bool is_numeric() const { + return ObjectModel::is_a(_uris.lv2_ControlPort) + || ObjectModel::is_a(_uris.lv2_CVPort); + } + bool is_uri() const; + + inline bool operator==(const PortModel& pm) const { return (path() == pm.path()); } + + void on_property(const URI& uri, const Atom& value); + + // Signals + INGEN_SIGNAL(value_changed, void, const Atom&); + INGEN_SIGNAL(voice_changed, void, uint32_t, const Atom&); + INGEN_SIGNAL(activity, void, const Atom&); + +private: + friend class ClientStore; + + PortModel(URIs& uris, + const Raul::Path& path, + uint32_t index, + Direction dir) + : ObjectModel(uris, path) + , _index(index) + , _direction(dir) + {} + + void add_child(SPtr<ObjectModel> c) { throw; } + bool remove_child(SPtr<ObjectModel> c) { throw; } + + void set(SPtr<ObjectModel> model); + + uint32_t _index; + Direction _direction; +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_PORTMODEL_HPP diff --git a/ingen/client/SigClientInterface.hpp b/ingen/client/SigClientInterface.hpp new file mode 100644 index 00000000..8ac8dca4 --- /dev/null +++ b/ingen/client/SigClientInterface.hpp @@ -0,0 +1,64 @@ +/* + 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_CLIENT_SIGCLIENTINTERFACE_HPP +#define INGEN_CLIENT_SIGCLIENTINTERFACE_HPP + +#include <cstdint> +#include <string> + +#include "raul/Path.hpp" + +#include "ingen/Interface.hpp" +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" + +namespace Ingen { +namespace Client { + +/** A LibSigC++ signal emitting interface for clients to use. + * + * This simply emits a signal for every event that comes from the engine. + * For a higher level model based view of the engine, use ClientStore. + * + * The signals here match the calls to ClientInterface exactly. See the + * documentation for ClientInterface for meanings of signal parameters. + * + * @ingroup IngenClient + */ +class INGEN_API SigClientInterface : public Ingen::Interface, + public INGEN_TRACKABLE +{ +public: + SigClientInterface() {} + + URI uri() const override { return URI("ingen:/clients/sig"); } + + INGEN_SIGNAL(message, void, Message) + + /** Fire pending signals. Only does anything on derived classes (that may queue) */ + virtual bool emit_signals() { return false; } + +protected: + void message(const Message& msg) override { + _signal_message(msg); + } +}; + +} // namespace Client +} // namespace Ingen + +#endif diff --git a/ingen/client/SocketClient.hpp b/ingen/client/SocketClient.hpp new file mode 100644 index 00000000..8236200b --- /dev/null +++ b/ingen/client/SocketClient.hpp @@ -0,0 +1,80 @@ +/* + This file is part of Ingen. + Copyright 2012-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_CLIENT_SOCKET_CLIENT_HPP +#define INGEN_CLIENT_SOCKET_CLIENT_HPP + +#include "ingen/SocketReader.hpp" +#include "ingen/SocketWriter.hpp" +#include "ingen/ingen.h" +#include "raul/Socket.hpp" + +namespace Ingen { +namespace Client { + +/** The client side of an Ingen socket connection. */ +class INGEN_API SocketClient : public SocketWriter +{ +public: + SocketClient(World& world, + const URI& uri, + SPtr<Raul::Socket> sock, + SPtr<Interface> respondee) + : SocketWriter(world.uri_map(), world.uris(), uri, sock) + , _respondee(respondee) + , _reader(world, *respondee.get(), sock) + {} + + SPtr<Interface> respondee() const override { + return _respondee; + } + + void set_respondee(SPtr<Interface> respondee) override { + _respondee = respondee; + } + + static SPtr<Ingen::Interface> + new_socket_interface(Ingen::World* world, + const URI& uri, + SPtr<Ingen::Interface> respondee) + { + const Raul::Socket::Type type = (uri.scheme() == "unix" + ? Raul::Socket::Type::UNIX + : Raul::Socket::Type::TCP); + + SPtr<Raul::Socket> sock(new Raul::Socket(type)); + if (!sock->connect(uri)) { + world->log().error(fmt("Failed to connect <%1%> (%2%)\n") + % sock->uri() % strerror(errno)); + return SPtr<Interface>(); + } + return SPtr<Interface>(new SocketClient(*world, uri, sock, respondee)); + } + + static void register_factories(World* world) { + world->add_interface_factory("unix", &new_socket_interface); + world->add_interface_factory("tcp", &new_socket_interface); + } + +private: + SPtr<Interface> _respondee; + SocketReader _reader; +}; + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_SOCKET_CLIENT_HPP diff --git a/ingen/client/signal.hpp b/ingen/client/signal.hpp new file mode 100644 index 00000000..ba5b017b --- /dev/null +++ b/ingen/client/signal.hpp @@ -0,0 +1,31 @@ +/* + 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_CLIENT_SIGNAL_HPP +#define INGEN_CLIENT_SIGNAL_HPP + +#include <sigc++/sigc++.h> + +#define INGEN_SIGNAL(name, ...) \ +protected: \ +sigc::signal<__VA_ARGS__> _signal_##name; \ +public: \ +sigc::signal<__VA_ARGS__> signal_##name() const { return _signal_##name; } \ +sigc::signal<__VA_ARGS__>& signal_##name() { return _signal_##name; } + +#define INGEN_TRACKABLE sigc::trackable + +#endif // INGEN_CLIENT_SIGNAL_HPP diff --git a/ingen/filesystem.hpp b/ingen/filesystem.hpp new file mode 100644 index 00000000..5c7d7568 --- /dev/null +++ b/ingen/filesystem.hpp @@ -0,0 +1,86 @@ +/* + 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_FILESYSTEM_HPP +#define INGEN_FILESYSTEM_HPP + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 + +#include <cerrno> +#include <climits> +#include <cstdlib> +#include <memory> +#include <vector> + +#include <sys/stat.h> +#include <sys/types.h> + +#ifdef _WIN32 +# include <windows.h> +# include <io.h> +# define F_OK 0 +# define mkdir(path, flags) _mkdir(path) +#endif + +#include "ingen/FilePath.hpp" + +/* A minimal subset of the std::filesystem API from C++17. */ + +namespace Ingen { +namespace filesystem { + +inline bool exists(const FilePath& path) +{ + return !access(path.c_str(), F_OK); +} + +inline bool is_directory(const FilePath& path) +{ + struct stat info; + stat(path.c_str(), &info); + return S_ISDIR(info.st_mode); +} + +inline bool create_directories(const FilePath& path) +{ + std::vector<FilePath> paths; + for (FilePath p = path; p != path.root_directory(); p = p.parent_path()) { + paths.emplace_back(p); + } + + for (auto p = paths.rbegin(); p != paths.rend(); ++p) { + if (mkdir(p->c_str(), 0755) && errno != EEXIST) { + return false; + } + } + + return true; +} + +inline FilePath current_path() +{ + struct Freer { void operator()(char* const ptr) { free(ptr); } }; + + std::unique_ptr<char, Freer> cpath(realpath(".", NULL)); + const FilePath path(cpath.get()); + return path; +} + +} // namespace filesystem +} // namespace Ingen + +#endif // INGEN_FILESYSTEM_HPP diff --git a/ingen/ingen.h b/ingen/ingen.h new file mode 100644 index 00000000..05b9e7b2 --- /dev/null +++ b/ingen/ingen.h @@ -0,0 +1,75 @@ +/* + This file is part of Ingen. + Copyright 2014-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_H +#define INGEN_H + +#ifdef INGEN_SHARED +# ifdef _WIN32 +# define INGEN_LIB_IMPORT __declspec(dllimport) +# define INGEN_LIB_EXPORT __declspec(dllexport) +# else +# define INGEN_LIB_IMPORT __attribute__((visibility("default"))) +# define INGEN_LIB_EXPORT __attribute__((visibility("default"))) +# endif +# ifdef INGEN_INTERNAL +# define INGEN_API INGEN_LIB_EXPORT +# else +# define INGEN_API INGEN_LIB_IMPORT +# endif +#else +# define INGEN_API +#endif + +#define INGEN_NS "http://drobilla.net/ns/ingen#" + +#define INGEN__Arc INGEN_NS "Arc" +#define INGEN__Block INGEN_NS "Block" +#define INGEN__BundleEnd INGEN_NS "BundleEnd" +#define INGEN__BundleStart INGEN_NS "BundleStart" +#define INGEN__Graph INGEN_NS "Graph" +#define INGEN__GraphPrototype INGEN_NS "GraphPrototype" +#define INGEN__Internal INGEN_NS "Internal" +#define INGEN__Node INGEN_NS "Node" +#define INGEN__Plugin INGEN_NS "Plugin" +#define INGEN__Redo INGEN_NS "Redo" +#define INGEN__Undo INGEN_NS "Undo" +#define INGEN__activity INGEN_NS "activity" +#define INGEN__arc INGEN_NS "arc" +#define INGEN__block INGEN_NS "block" +#define INGEN__broadcast INGEN_NS "broadcast" +#define INGEN__canvasX INGEN_NS "canvasX" +#define INGEN__canvasY INGEN_NS "canvasY" +#define INGEN__enabled INGEN_NS "enabled" +#define INGEN__externalContext INGEN_NS "externalContext" +#define INGEN__file INGEN_NS "file" +#define INGEN__head INGEN_NS "head" +#define INGEN__incidentTo INGEN_NS "incidentTo" +#define INGEN__internalContext INGEN_NS "internalContext" +#define INGEN__loadedBundle INGEN_NS "loadedBundle" +#define INGEN__maxRunLoad INGEN_NS "maxRunLoad" +#define INGEN__meanRunLoad INGEN_NS "meanRunLoad" +#define INGEN__minRunLoad INGEN_NS "minRunLoad" +#define INGEN__numThreads INGEN_NS "numThreads" +#define INGEN__polyphonic INGEN_NS "polyphonic" +#define INGEN__polyphony INGEN_NS "polyphony" +#define INGEN__prototype INGEN_NS "prototype" +#define INGEN__sprungLayout INGEN_NS "sprungLayout" +#define INGEN__tail INGEN_NS "tail" +#define INGEN__uiEmbedded INGEN_NS "uiEmbedded" +#define INGEN__value INGEN_NS "value" + +#endif // INGEN_H diff --git a/ingen/paths.hpp b/ingen/paths.hpp new file mode 100644 index 00000000..e75e71e0 --- /dev/null +++ b/ingen/paths.hpp @@ -0,0 +1,55 @@ +/* + 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_PATHS_HPP +#define INGEN_PATHS_HPP + +#include <cstddef> +#include <string> + +#include "ingen/URI.hpp" +#include "raul/Path.hpp" + +namespace Ingen { + +inline URI main_uri() { return URI("ingen:/main"); } + +inline bool uri_is_path(const URI& uri) +{ + const size_t root_len = main_uri().string().length(); + if (uri == main_uri()) { + return true; + } else { + return uri.string().substr(0, root_len + 1) == + main_uri().string() + "/"; + } +} + +inline Raul::Path uri_to_path(const URI& uri) +{ + return (uri == main_uri()) + ? Raul::Path("/") + : Raul::Path(uri.string().substr(main_uri().string().length())); +} + +inline URI path_to_uri(const Raul::Path& path) +{ + return URI(main_uri().string() + path.c_str()); +} + +} // namespace Ingen + +#endif // INGEN_PATHS_HPP diff --git a/ingen/runtime_paths.hpp b/ingen/runtime_paths.hpp new file mode 100644 index 00000000..1a8bc2c2 --- /dev/null +++ b/ingen/runtime_paths.hpp @@ -0,0 +1,42 @@ +/* + 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_RUNTIME_PATHS_HPP +#define INGEN_RUNTIME_PATHS_HPP + +#include <string> +#include <vector> + +#include "ingen/ingen.h" +#include "ingen/FilePath.hpp" + +namespace Ingen { + +extern const char search_path_separator; + +INGEN_API void set_bundle_path(const char* path); +INGEN_API void set_bundle_path_from_code(void* function); + +INGEN_API FilePath bundle_file_path(const std::string& name); +INGEN_API FilePath data_file_path(const std::string& name); +INGEN_API FilePath ingen_module_path(const std::string& name, FilePath dir={}); + +INGEN_API FilePath user_config_dir(); +INGEN_API std::vector<FilePath> system_config_dirs(); + +} // namespace Ingen + +#endif // INGEN_RUNTIME_PATHS_HPP diff --git a/ingen/types.hpp b/ingen/types.hpp new file mode 100644 index 00000000..7cd1c386 --- /dev/null +++ b/ingen/types.hpp @@ -0,0 +1,62 @@ +/* + 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_TYPES_HPP +#define INGEN_TYPES_HPP + +#include <cstdlib> +#include <memory> + +#include "raul/Maid.hpp" + +namespace Ingen { + +template <class T> +void NullDeleter(T* ptr) {} + +template <class T> +struct FreeDeleter { void operator()(T* const ptr) { free(ptr); } }; + +template <class T, class Deleter = std::default_delete<T>> +using UPtr = std::unique_ptr<T, Deleter>; + +template <class T> +using SPtr = std::shared_ptr<T>; + +template <class T> +using WPtr = std::weak_ptr<T>; + +template <class T> +using MPtr = Raul::managed_ptr<T>; + +template<class T, class U> +SPtr<T> static_ptr_cast(const SPtr<U>& r) { + return std::static_pointer_cast<T>(r); +} + +template<class T, class U> +SPtr<T> dynamic_ptr_cast(const SPtr<U>& r) { + return std::dynamic_pointer_cast<T>(r); +} + +template<class T, class U> +SPtr<T> const_ptr_cast(const SPtr<U>& r) { + return std::const_pointer_cast<T>(r); +} + +} // namespace Ingen + +#endif // INGEN_TYPES_HPP |