diff options
Diffstat (limited to 'include')
55 files changed, 5140 insertions, 0 deletions
diff --git a/include/ingen/Arc.hpp b/include/ingen/Arc.hpp new file mode 100644 index 00000000..b254e3f3 --- /dev/null +++ b/include/ingen/Arc.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_ARC_HPP +#define INGEN_ARC_HPP + +#include "ingen/ingen.h" +#include "raul/Deletable.hpp" + +namespace raul { +class Path; +} // namespace raul + +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/include/ingen/Atom.hpp b/include/ingen/Atom.hpp new file mode 100644 index 00000000..0c4ac8c5 --- /dev/null +++ b/include/ingen/Atom.hpp @@ -0,0 +1,186 @@ +/* + 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 "ingen/ingen.h" +#include "lv2/atom/atom.h" +#include "lv2/urid/urid.h" + +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <cstring> + +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 = default; + + ~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, type} + { + if (is_reference()) { + _body.ptr = static_cast<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 = + static_cast<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 = + static_cast<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; + } + + 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; + } + + bool operator!=(const Atom& other) const { + return !operator==(other); + } + + 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. + */ + bool set_rt(const Atom& other) { + if (is_reference()) { + return false; + } + + _atom = other._atom; + _body.val = other._body.val; + return true; + } + + uint32_t size() const { return _atom.size; } + LV2_URID type() const { return _atom.type; } + bool is_valid() const { return _atom.type; } + + const void* get_body() const { + return is_reference() ? static_cast<void*>(_body.ptr + 1) : &_body.val; + } + + void* get_body() { + return is_reference() ? static_cast<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. */ + void dealloc() { + if (is_reference()) { + free(_body.ptr); + } + } + + /** Return true iff this value is dynamically allocated. */ + bool is_reference() const { + return _atom.size > sizeof(_body.val); + } + + LV2_Atom _atom = {0, 0}; + union + { + intptr_t val; + LV2_Atom* ptr; + } _body = {}; +}; + +} // namespace ingen + +#endif // INGEN_ATOM_HPP diff --git a/include/ingen/AtomForge.hpp b/include/ingen/AtomForge.hpp new file mode 100644 index 00000000..cf5a759d --- /dev/null +++ b/include/ingen/AtomForge.hpp @@ -0,0 +1,126 @@ +/* + 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_ATOMFORGE_HPP +#define INGEN_ATOMFORGE_HPP + +#include "ingen/memory.hpp" +#include "lv2/atom/atom.h" +#include "lv2/atom/forge.h" +#include "lv2/atom/util.h" +#include "lv2/urid/urid.h" +#include "sord/sord.h" +#include "sord/sordmm.hpp" +#include "sratom/sratom.h" + +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <memory> + +namespace ingen { + +/// An atom forge that writes to an automatically-resized memory buffer +class AtomForge : public LV2_Atom_Forge +{ +public: + explicit AtomForge(LV2_URID_Map& map) + : LV2_Atom_Forge{} + , _sratom{sratom_new(&map)} + , _buf{static_cast<LV2_Atom*>(calloc(8, sizeof(LV2_Atom)))} + { + lv2_atom_forge_init(this, &map); + lv2_atom_forge_set_sink(this, c_append, c_deref, this); + } + + /// Forge an atom from `node` in `model` + void read(Sord::World& world, SordModel* model, const SordNode* node) + { + sratom_read(_sratom.get(), this, world.c_obj(), model, node); + } + + /// Return the top-level atom that has been forged + const LV2_Atom* atom() const { return _buf.get(); } + + /// Clear the atom buffer and reset the forge + void clear() + { + lv2_atom_forge_set_sink(this, c_append, c_deref, this); + _size = 0; + *_buf = {0U, 0U}; + } + + /// Return the internal atom serialiser + Sratom& sratom() { return *_sratom; } + +private: + struct SratomDeleter { void operator()(Sratom* s) { sratom_free(s); } }; + + using AtomPtr = std::unique_ptr<LV2_Atom, FreeDeleter<LV2_Atom>>; + using SratomPtr = std::unique_ptr<Sratom, SratomDeleter>; + + /// Append some data and return a reference to its start + intptr_t append(const void* data, uint32_t len) { + // Record offset of the start of this write (+1 to avoid null) + const auto ref = static_cast<intptr_t>(_size + 1U); + + // Update size and reallocate if necessary + if (lv2_atom_pad_size(_size + len) > _capacity) { + _capacity = lv2_atom_pad_size(_size + len); + + _buf = AtomPtr{static_cast<LV2_Atom*>( + realloc(_buf.release(), _capacity)), + FreeDeleter<LV2_Atom>{}}; + } + + // Append new data + memcpy(reinterpret_cast<uint8_t*>(_buf.get()) + _size, data, 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 static_cast<LV2_Atom*>(_buf.get() + (ref - 1) / sizeof(LV2_Atom)); + + // Alternatively: + // return (LV2_Atom*)((uint8_t*)_buf + ref - 1); + } + + static LV2_Atom_Forge_Ref + c_append(void* self, const void* data, uint32_t len) { + return static_cast<AtomForge*>(self)->append(data, len); + } + + static LV2_Atom* + c_deref(void* self, LV2_Atom_Forge_Ref ref) { + return static_cast<AtomForge*>(self)->deref(ref); + } + + size_t _size{0}; ///< Current atom size + size_t _capacity{8 * sizeof(LV2_Atom)}; ///< Allocated size of buffer + SratomPtr _sratom; ///< Atom serialiser + AtomPtr _buf; ///< Atom buffer +}; + +} // namespace ingen + +#endif // INGEN_ATOMFORGE_HPP diff --git a/include/ingen/AtomReader.hpp b/include/ingen/AtomReader.hpp new file mode 100644 index 00000000..6e5d83fd --- /dev/null +++ b/include/ingen/AtomReader.hpp @@ -0,0 +1,75 @@ +/* + 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 "ingen/AtomSink.hpp" +#include "ingen/Resource.hpp" +#include "ingen/ingen.h" +#include "lv2/atom/atom.h" + +#include <cstdint> +#include <optional> + +namespace raul { +class Path; +} // namespace raul + +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); + + std::optional<URI> atom_to_uri(const LV2_Atom* atom); + std::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/include/ingen/AtomSink.hpp b/include/ingen/AtomSink.hpp new file mode 100644 index 00000000..abe1c196 --- /dev/null +++ b/include/ingen/AtomSink.hpp @@ -0,0 +1,48 @@ +/* + 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 "ingen/ingen.h" +#include "lv2/atom/atom.h" + +#include <cstdint> + +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/include/ingen/AtomWriter.hpp b/include/ingen/AtomWriter.hpp new file mode 100644 index 00000000..43ee08b1 --- /dev/null +++ b/include/ingen/AtomWriter.hpp @@ -0,0 +1,88 @@ +/* + 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 "ingen/AtomForge.hpp" +#include "ingen/Interface.hpp" +#include "ingen/Message.hpp" +#include "ingen/Properties.hpp" +#include "ingen/Resource.hpp" +#include "ingen/URI.hpp" +#include "ingen/ingen.h" +#include "lv2/atom/forge.h" +#include "lv2/urid/urid.h" + +#include <cstdint> + +namespace raul { +class Path; +} // namespace raul + +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; + AtomForge _forge; +}; + +} // namespace ingen + +#endif // INGEN_ATOMWRITER_HPP diff --git a/include/ingen/ClashAvoider.hpp b/include/ingen/ClashAvoider.hpp new file mode 100644 index 00000000..c1d62754 --- /dev/null +++ b/include/ingen/ClashAvoider.hpp @@ -0,0 +1,66 @@ +/* + 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 "ingen/ingen.h" +#include "raul/Path.hpp" + +#include <map> +#include <string> + +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); + + URI map_uri(const URI& in); + raul::Path map_path(const raul::Path& in); + + bool exists(const raul::Path& path) const; + + /** Adjust a new label by increasing the numeric suffix if any. + * + * @param old_path The old path that was mapped with `map_path()` + * @param new_path The new path that `old_path` was mapped to + * @param name The old name. + */ + static std::string adjust_name(const raul::Path& old_path, + const raul::Path& new_path, + std::string name); + +private: + using Offsets = std::map<raul::Path, unsigned>; + using SymbolMap = std::map<raul::Path, raul::Path>; + + const Store& _store; + Offsets _offsets; + SymbolMap _symbol_map; +}; + +} // namespace ingen + +#endif // INGEN_CLASHAVOIDER_HPP diff --git a/include/ingen/Clock.hpp b/include/ingen/Clock.hpp new file mode 100644 index 00000000..75575aa5 --- /dev/null +++ b/include/ingen/Clock.hpp @@ -0,0 +1,66 @@ +/* + 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_CLOCK_HPP +#define INGEN_CLOCK_HPP + +#ifdef __MACH__ +# include <mach/mach.h> +# include <mach/mach_time.h> +#else +# include <ctime> +#endif + +#include <cstdint> + +namespace ingen { + +class Clock +{ +public: +#ifdef __MACH__ + + Clock() { mach_timebase_info(&_timebase); } + + 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 + + uint64_t now_microseconds() const { + struct timespec time{}; + clock_gettime(_clock, &time); + return static_cast<uint64_t>(time.tv_sec) * 1000000U + + static_cast<uint64_t>(time.tv_nsec) / 100U; + } + +private: +# if defined(CLOCK_MONOTONIC_RAW) + const clockid_t _clock = CLOCK_MONOTONIC_RAW; +# else + const clockid_t _clock = CLOCK_MONOTONIC; +# endif +#endif +}; + +} // namespace ingen + +#endif // INGEN_CLOCK_HPP diff --git a/include/ingen/ColorContext.hpp b/include/ingen/ColorContext.hpp new file mode 100644 index 00000000..c1486b5c --- /dev/null +++ b/include/ingen/ColorContext.hpp @@ -0,0 +1,45 @@ +/* + 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 "ingen/ingen.h" + +#include <cstdio> + +namespace ingen { + +class INGEN_API ColorContext +{ +public: + enum class Color { RED = 31, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; + + ColorContext(FILE* stream, Color color); + ~ColorContext(); + + ColorContext(const ColorContext&) = delete; + ColorContext& operator=(const ColorContext&) = delete; + ColorContext(ColorContext&&) = delete; + ColorContext& operator=(ColorContext&&) = delete; + +private: + FILE* _stream; +}; + +} // namespace ingen + +#endif // INGEN_COLORCONTEXT_HPP diff --git a/include/ingen/Configuration.hpp b/include/ingen/Configuration.hpp new file mode 100644 index 00000000..ba68950c --- /dev/null +++ b/include/ingen/Configuration.hpp @@ -0,0 +1,161 @@ +/* + 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 "ingen/Atom.hpp" +#include "ingen/FilePath.hpp" +#include "ingen/ingen.h" +#include "lv2/urid/urid.h" +#include "raul/Exception.hpp" + +#include <cstdio> +#include <list> +#include <map> +#include <ostream> +#include <string> + +namespace ingen { + +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, + 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 { + bool operator()(const Option& a, const Option& b) { + return a.name < b.name; + } + }; + + using Options = std::map<std::string, Option>; + using ShortNames = std::map<char, std::string>; + using Keys = std::map<std::string, std::string>; + + 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{0}; +}; + +} // namespace ingen + +#endif // INGEN_CONFIGURATION_HPP diff --git a/include/ingen/DataAccess.hpp b/include/ingen/DataAccess.hpp new file mode 100644 index 00000000..59bd80f9 --- /dev/null +++ b/include/ingen/DataAccess.hpp @@ -0,0 +1,65 @@ +/* + 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_DATAACCESS_HPP +#define INGEN_DATAACCESS_HPP + +#include "ingen/LV2Features.hpp" +#include "ingen/Node.hpp" +#include "ingen/Store.hpp" +#include "ingen/World.hpp" +#include "lilv/lilv.h" +#include "lv2/core/lv2.h" +#include "lv2/data-access/data-access.h" + +#include <cstdlib> +#include <memory> + +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"; } + + std::shared_ptr<LV2_Feature> feature(World& world, Node* node) override { + Node* store_node = world.store()->get(node->path()); + if (!store_node) { + return nullptr; + } + + LilvInstance* inst = store_node->instance(); + if (!inst) { + return nullptr; + } + + const LV2_Descriptor* desc = lilv_instance_get_descriptor(inst); + auto* data = static_cast<LV2_Extension_Data_Feature*>( + malloc(sizeof(LV2_Extension_Data_Feature))); + + data->data_access = desc->extension_data; + + return std::make_shared<LV2_Feature>( + LV2_Feature{"http://lv2plug.in/ns/ext/data-access", data}); + } +}; + +} // namespace ingen + +#endif // INGEN_DATAACCESS_HPP diff --git a/include/ingen/EngineBase.hpp b/include/ingen/EngineBase.hpp new file mode 100644 index 00000000..1b6b105a --- /dev/null +++ b/include/ingen/EngineBase.hpp @@ -0,0 +1,146 @@ +/* + 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 "ingen/ingen.h" + +#include <chrono> +#include <cstddef> +#include <cstdint> +#include <memory> + +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(const std::shared_ptr<Interface>& client) = 0; + + /** + Unregister a client. + */ + virtual bool + unregister_client(const std::shared_ptr<Interface>& client) = 0; +}; + +} // namespace ingen + +#endif // INGEN_ENGINEBASE_HPP diff --git a/include/ingen/FilePath.hpp b/include/ingen/FilePath.hpp new file mode 100644 index 00000000..ce157d90 --- /dev/null +++ b/include/ingen/FilePath.hpp @@ -0,0 +1,28 @@ +/* + This file is part of Ingen. + Copyright 2018-2020 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + 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_FILEPATH_HPP +#define INGEN_FILEPATH_HPP + +#include <filesystem> + +namespace ingen { + +using FilePath = std::filesystem::path; + +} // namespace ingen + +#endif // INGEN_FILEPATH_HPP diff --git a/include/ingen/Forge.hpp b/include/ingen/Forge.hpp new file mode 100644 index 00000000..fdd53276 --- /dev/null +++ b/include/ingen/Forge.hpp @@ -0,0 +1,87 @@ +/* + 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 "ingen/Atom.hpp" +#include "ingen/ingen.h" +#include "lv2/atom/forge.h" + +#include <cstdint> +#include <cstring> +#include <string> + +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; + } + + static Atom make() { return {}; } + Atom make(int32_t v) { return {sizeof(v), Int, &v}; } + Atom make(float v) { return {sizeof(v), Float, &v}; } + Atom make(bool v) { + const int32_t iv = v ? 1 : 0; + return {sizeof(int32_t), Bool, &iv}; + } + + Atom make_urid(int32_t v) { return {sizeof(int32_t), URID, &v}; } + + Atom make_urid(const ingen::URI& u); + + static Atom alloc(uint32_t s, uint32_t t, const void* v) { + return {s, t, v}; + } + + Atom alloc(const char* v) { + const auto len = static_cast<uint32_t>(strlen(v)); + return {len + 1U, String, v}; + } + + Atom alloc(const std::string& v) { + return {static_cast<uint32_t>(v.length()) + 1U, String, v.c_str()}; + } + + Atom alloc_uri(const char* v) { + const auto len = static_cast<uint32_t>(strlen(v)); + return {len + 1U, URI, v}; + } + + Atom alloc_uri(const std::string& v) { + return {static_cast<uint32_t>(v.length()) + 1U, URI, v.c_str()}; + } + +private: + URIMap& _map; +}; + +} // namespace ingen + +#endif // INGEN_FORGE_HPP diff --git a/include/ingen/InstanceAccess.hpp b/include/ingen/InstanceAccess.hpp new file mode 100644 index 00000000..b3fcff35 --- /dev/null +++ b/include/ingen/InstanceAccess.hpp @@ -0,0 +1,53 @@ +/* + 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_INSTANCEACCESS_HPP +#define INGEN_INSTANCEACCESS_HPP + +#include "ingen/LV2Features.hpp" +#include "ingen/Node.hpp" +#include "ingen/Store.hpp" +#include "ingen/World.hpp" +#include "lilv/lilv.h" +#include "lv2/core/lv2.h" + +#include <memory> + +namespace ingen { + +struct InstanceAccess : public ingen::LV2Features::Feature { + const char* uri() const override { return "http://lv2plug.in/ns/ext/instance-access"; } + + std::shared_ptr<LV2_Feature> feature(World& world, Node* node) override { + Node* store_node = world.store()->get(node->path()); + if (!store_node) { + return nullptr; + } + + LilvInstance* instance = store_node->instance(); + if (!instance) { + return nullptr; + } + + return std::make_shared<LV2_Feature>( + LV2_Feature{"http://lv2plug.in/ns/ext/instance-access", + lilv_instance_get_handle(instance)}); + } +}; + +} // namespace ingen + +#endif // INGEN_INSTANCEACCESS_HPP diff --git a/include/ingen/Interface.hpp b/include/ingen/Interface.hpp new file mode 100644 index 00000000..79cfad63 --- /dev/null +++ b/include/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 "ingen/Message.hpp" +#include "ingen/Properties.hpp" +#include "ingen/Resource.hpp" +#include "ingen/Status.hpp" +#include "ingen/ingen.h" + +#include <cstdint> +#include <memory> +#include <string> + +namespace raul { +class Path; +} // namespace raul + +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() = default; + + virtual ~Interface() = default; + + virtual URI uri() const = 0; + + virtual std::shared_ptr<Interface> respondee() const { return nullptr; } + + virtual void set_respondee(const std::shared_ptr<Interface>& respondee) {} + + virtual void message(const Message& msg) = 0; + + /** @name Convenience API + * @{ + */ + + void operator()(const Message& msg) { message(msg); } + + void set_response_id(int32_t id) { _seq = id; } + + void bundle_begin() { message(BundleBegin{_seq++}); } + void bundle_end() { message(BundleEnd{_seq++}); } + + void put(const URI& uri, + const Properties& properties, + Resource::Graph ctx = Resource::Graph::DEFAULT) + { + message(Put{_seq++, uri, properties, ctx}); + } + + void delta(const URI& uri, + const Properties& remove, + const Properties& add, + Resource::Graph ctx = Resource::Graph::DEFAULT) + { + message(Delta{_seq++, uri, remove, add, ctx}); + } + + void copy(const URI& old_uri, const URI& new_uri) + { + message(Copy{_seq++, old_uri, new_uri}); + } + + void move(const raul::Path& old_path, const raul::Path& new_path) + { + message(Move{_seq++, old_path, new_path}); + } + + void del(const URI& uri) { message(Del{_seq++, uri}); } + + void connect(const raul::Path& tail, const raul::Path& head) + { + message(Connect{_seq++, tail, head}); + } + + void disconnect(const raul::Path& tail, const raul::Path& head) + { + message(Disconnect{_seq++, tail, head}); + } + + void disconnect_all(const raul::Path& graph, const raul::Path& path) + { + message(DisconnectAll{_seq++, graph, path}); + } + + 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}); + } + + void undo() { message(Undo{_seq++}); } + + void redo() { message(Redo{_seq++}); } + + void get(const URI& uri) { message(Get{_seq++, uri}); } + + void response(int32_t id, Status status, const std::string& subject) + { + message(Response{id, status, subject}); + } + + void error(const std::string& error_message) + { + message(Error{_seq++, error_message}); + } + + /** @} */ + +private: + int32_t _seq = 0; +}; + +} // namespace ingen + +#endif // INGEN_INTERFACE_HPP diff --git a/include/ingen/LV2Features.hpp b/include/ingen/LV2Features.hpp new file mode 100644 index 00000000..5726feb9 --- /dev/null +++ b/include/ingen/LV2Features.hpp @@ -0,0 +1,98 @@ +/* + 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 "ingen/ingen.h" +#include "lv2/core/lv2.h" +#include "raul/Noncopyable.hpp" + +#include <memory> +#include <string> +#include <vector> + +namespace ingen { + +class Node; +class World; + +/** Features for use by LV2 plugins. + * @ingroup IngenShared + */ +class INGEN_API LV2Features +{ +public: + LV2Features() = default; + + class Feature + { + public: + virtual ~Feature() = default; + + virtual const char* uri() const = 0; + + virtual std::shared_ptr<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) noexcept : _uri(uri) {} + + const char* uri() const override { return _uri; } + + std::shared_ptr<LV2_Feature> feature(World& world, Node* block) override + { + return nullptr; + } + + const char* _uri; + }; + + class FeatureArray : public raul::Noncopyable + { + public: + using FeatureVector = std::vector<std::shared_ptr<LV2_Feature>>; + + explicit FeatureArray(FeatureVector& features); + + ~FeatureArray(); + + LV2_Feature** array() { return _array; } + + private: + FeatureVector _features; + LV2_Feature** _array; + }; + + void add_feature(const std::shared_ptr<Feature>& feature); + bool is_supported(const std::string& uri) const; + + std::shared_ptr<FeatureArray> lv2_features(World& world, Node* node) const; + +private: + using Features = std::vector<std::shared_ptr<Feature>>; + Features _features; +}; + +} // namespace ingen + +#endif // INGEN_LV2FEATURES_HPP diff --git a/include/ingen/Library.hpp b/include/ingen/Library.hpp new file mode 100644 index 00000000..4cee985f --- /dev/null +++ b/include/ingen/Library.hpp @@ -0,0 +1,51 @@ +/* + 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; + Library(Library&&) = delete; + Library& operator=(Library&&) = delete; + + using VoidFuncPtr = void (*)(); + + VoidFuncPtr get_function(const char* name); + + static const char* get_last_error(); + + operator bool() const { return _lib; } + +private: + void* _lib; +}; + +} // namespace ingen + +#endif // INGEN_LIBRARY_HPP diff --git a/include/ingen/Log.hpp b/include/ingen/Log.hpp new file mode 100644 index 00000000..8d1b420c --- /dev/null +++ b/include/ingen/Log.hpp @@ -0,0 +1,109 @@ +/* + 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 "ingen/LV2Features.hpp" +#include "ingen/fmt.hpp" // IWYU pragma: export +#include "ingen/ingen.h" +#include "lv2/core/lv2.h" +#include "lv2/log/log.h" +#include "lv2/urid/urid.h" + +#include <cstdarg> +#include <cstdio> +#include <functional> +#include <memory> +#include <string> +#include <utility> + +namespace ingen { + +class Node; +class URIs; +class World; + +class INGEN_API Log +{ +public: + using Sink = std::function<int(LV2_URID, const char*, va_list)>; + + Log(LV2_Log_Log* log, URIs& uris); + + struct Feature : public LV2Features::Feature { + const char* uri() const override { return LV2_LOG__log; } + + std::shared_ptr<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); + + template <typename... Args> + void error(const char* format, Args&&... args) + { + error(fmt(format, std::forward<Args>(args)...)); + } + + template <typename... Args> + void info(const char* format, Args&&... args) + { + info(fmt(format, std::forward<Args>(args)...)); + } + + template <typename... Args> + void warn(const char* format, Args&&... args) + { + warn(fmt(format, std::forward<Args>(args)...)); + } + + template <typename... Args> + void trace(const char* format, Args&&... args) + { + trace(fmt(format, std::forward<Args>(args)...)); + } + + 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 = std::move(s); } + +private: + void print(FILE* stream, const std::string& msg) const; + + LV2_Log_Log* _log; + URIs& _uris; + Sink _sink; + bool _flush{false}; + bool _trace{false}; +}; + +} // namespace ingen + +#endif // INGEN_LOG_HPP diff --git a/include/ingen/Message.hpp b/include/ingen/Message.hpp new file mode 100644 index 00000000..de62f459 --- /dev/null +++ b/include/ingen/Message.hpp @@ -0,0 +1,142 @@ +/* + 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 "ingen/Atom.hpp" +#include "ingen/Properties.hpp" +#include "ingen/Resource.hpp" +#include "ingen/Status.hpp" +#include "ingen/URI.hpp" +#include "raul/Path.hpp" + +#include <cstdint> +#include <string> +#include <variant> + +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 = std::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/include/ingen/Module.hpp b/include/ingen/Module.hpp new file mode 100644 index 00000000..90f2f930 --- /dev/null +++ b/include/ingen/Module.hpp @@ -0,0 +1,71 @@ +/* + 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 "ingen/Library.hpp" +#include "ingen/ingen.h" + +#include <memory> + +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() noexcept : 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" { + +#ifdef _WIN32 +# define INGEN_MODULE_EXPORT __declspec(dllexport) +#else +# define INGEN_MODULE_EXPORT __attribute__((visibility("default"))) +#endif + +/** Prototype for the ingen_module_load() entry point in an ingen module. */ +INGEN_MODULE_EXPORT ingen::Module* ingen_module_load(); + +} + +#endif // INGEN_MODULE_HPP diff --git a/include/ingen/Node.hpp b/include/ingen/Node.hpp new file mode 100644 index 00000000..3e07df2f --- /dev/null +++ b/include/ingen/Node.hpp @@ -0,0 +1,110 @@ +/* + 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 "ingen/Resource.hpp" +#include "ingen/URI.hpp" +#include "ingen/ingen.h" +#include "ingen/paths.hpp" +#include "lilv/lilv.h" + +#include <cstdint> +#include <filesystem> +#include <map> +#include <memory> +#include <string> +#include <utility> + +namespace raul { +class Path; +class Symbol; +} // namespace raul + +namespace ingen { + +class Arc; +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 + }; + + using ArcsKey = std::pair<const Node*, const Node*>; + using Arcs = std::map<ArcsKey, std::shared_ptr<Arc>>; + + // Graphs only + Arcs& arcs() { return _graph_arcs; } + const Arcs& arcs() const { return _graph_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 std::filesystem::path& 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 _graph_arcs; ///< Graphs only +}; + +} // namespace ingen + +#endif // INGEN_NODE_HPP diff --git a/include/ingen/Parser.hpp b/include/ingen/Parser.hpp new file mode 100644 index 00000000..16ee4070 --- /dev/null +++ b/include/ingen/Parser.hpp @@ -0,0 +1,101 @@ +/* + 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 "ingen/FilePath.hpp" +#include "ingen/Properties.hpp" // IWYU pragma: keep +#include "ingen/URI.hpp" +#include "ingen/ingen.h" +#include "raul/Path.hpp" // IWYU pragma: keep +#include "raul/Symbol.hpp" // IWYU pragma: keep + +#include <optional> +#include <set> +#include <string> +#include <utility> + +namespace Sord { +class World; +} // namespace Sord + +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 { + ResourceRecord(URI u, FilePath f) + : uri(std::move(u)), filename(std::move(f)) + {} + + 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, + const std::optional<raul::Path>& parent = std::optional<raul::Path>(), + const std::optional<raul::Symbol>& symbol = std::optional<raul::Symbol>(), + const std::optional<Properties>& data = std::optional<Properties>()); + + virtual std::optional<URI> parse_string( + World& world, + Interface& target, + const std::string& str, + const URI& base_uri, + const std::optional<raul::Path>& parent = std::optional<raul::Path>(), + const std::optional<raul::Symbol>& symbol = std::optional<raul::Symbol>(), + const std::optional<Properties>& data = std::optional<Properties>()); +}; + +} // namespace ingen + +#endif // INGEN_PARSER_HPP diff --git a/include/ingen/Properties.hpp b/include/ingen/Properties.hpp new file mode 100644 index 00000000..9151372b --- /dev/null +++ b/include/ingen/Properties.hpp @@ -0,0 +1,98 @@ +/* + 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 "ingen/Atom.hpp" +#include "ingen/URI.hpp" +#include "ingen/URIs.hpp" + +#include <initializer_list> +#include <map> +#include <utility> + +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_atom()) + , _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&) = default; + Properties& operator=(const Properties&) = default; + + Properties(Properties&&) = default; + Properties& operator=(Properties&&) = 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 (auto 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/include/ingen/QueuedInterface.hpp b/include/ingen/QueuedInterface.hpp new file mode 100644 index 00000000..4bb6baea --- /dev/null +++ b/include/ingen/QueuedInterface.hpp @@ -0,0 +1,71 @@ +/* + 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_QUEUEDINTERFACE_HPP +#define INGEN_QUEUEDINTERFACE_HPP + +#include "ingen/Interface.hpp" +#include "ingen/Message.hpp" +#include "ingen/URI.hpp" + +#include <memory> +#include <mutex> +#include <utility> +#include <vector> + +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(std::shared_ptr<Interface> sink) + : _sink(std::move(sink)) + {} + + URI uri() const override { return URI("ingen:/QueuedInterface"); } + + void message(const Message& message) override { + const std::lock_guard<std::mutex> lock{_mutex}; + _messages.emplace_back(message); + } + + void emit() { + std::vector<Message> messages; + { + const std::lock_guard<std::mutex> lock{_mutex}; + _messages.swap(messages); + } + + for (const auto& i : messages) { + _sink->message(i); + } + } + + const std::shared_ptr<Interface>& sink() const { return _sink; } + +private: + std::mutex _mutex; + std::shared_ptr<Interface> _sink; + std::vector<Message> _messages; +}; + +} // namespace ingen + +#endif // INGEN_QUEUEDINTERFACE_HPP diff --git a/include/ingen/Resource.hpp b/include/ingen/Resource.hpp new file mode 100644 index 00000000..8b96a27b --- /dev/null +++ b/include/ingen/Resource.hpp @@ -0,0 +1,210 @@ +/* + 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 "ingen/Properties.hpp" +#include "ingen/URI.hpp" +#include "ingen/URIs.hpp" +#include "ingen/ingen.h" +#include "raul/Deletable.hpp" + +#include <cassert> +#include <utility> + +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, URI uri) + : _uris(uris) + , _uri(std::move(uri)) + {} + + Resource(const Resource& resource) = default; + + 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; + } + + 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/include/ingen/Serialiser.hpp b/include/ingen/Serialiser.hpp new file mode 100644 index 00000000..f7f04317 --- /dev/null +++ b/include/ingen/Serialiser.hpp @@ -0,0 +1,109 @@ +/* + 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 "ingen/FilePath.hpp" +#include "ingen/Properties.hpp" +#include "ingen/ingen.h" +#include "sord/sordmm.hpp" + +#include <memory> +#include <string> + +namespace raul { +class Path; +} // namespace raul + +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(const std::shared_ptr<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 FilePath& filename); + + /** Serialize an object (graph, block, or port). + * + * @throw std::logic_error + */ + virtual void serialise(const std::shared_ptr<const Node>& object, + Property::Graph context = Property::Graph::DEFAULT); + + /** Serialize an arc. + * + * @throw std::logic_error + */ + virtual void serialise_arc(const Sord::Node& parent, + const std::shared_ptr<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; + + std::unique_ptr<Impl> me; +}; + +} // namespace ingen + +#endif // INGEN_SERIALISER_HPP diff --git a/include/ingen/SocketReader.hpp b/include/ingen/SocketReader.hpp new file mode 100644 index 00000000..5e7bc373 --- /dev/null +++ b/include/ingen/SocketReader.hpp @@ -0,0 +1,88 @@ +/* + 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_SOCKETREADER_HPP +#define INGEN_SOCKETREADER_HPP + +#include "ingen/ingen.h" +#include "serd/serd.h" +#include "sord/sord.h" + +#include <cstddef> +#include <memory> +#include <thread> + +namespace raul { +class Socket; +} // namespace raul + +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, + std::shared_ptr<raul::Socket> sock); + + virtual ~SocketReader(); + +protected: + virtual void on_hangup() {} + +private: + /// Serd source function for reading from socket + static size_t c_recv(void* buf, size_t size, size_t nmemb, void* stream); + + /// Serd error function for getting socket error status + static int c_err(void* stream); + + 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{nullptr}; + SordInserter* _inserter{nullptr}; + SordNode* _msg_node{nullptr}; + std::shared_ptr<raul::Socket> _socket; + int _socket_error{0}; + bool _exit_flag{false}; + std::thread _thread; +}; + +} // namespace ingen + +#endif // INGEN_SOCKETREADER_HPP diff --git a/include/ingen/SocketWriter.hpp b/include/ingen/SocketWriter.hpp new file mode 100644 index 00000000..7edaa13c --- /dev/null +++ b/include/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_SOCKETWRITER_HPP +#define INGEN_SOCKETWRITER_HPP + +#include "ingen/Message.hpp" +#include "ingen/TurtleWriter.hpp" +#include "ingen/ingen.h" + +#include <cstddef> +#include <memory> + +namespace raul { +class Socket; +} // namespace raul + +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, + std::shared_ptr<raul::Socket> sock); + + void message(const Message& message) override; + + size_t text_sink(const void* buf, size_t len) override; + +protected: + std::shared_ptr<raul::Socket> _socket; +}; + +} // namespace ingen + +#endif // INGEN_SOCKETWRITER_HPP diff --git a/include/ingen/Status.hpp b/include/ingen/Status.hpp new file mode 100644 index 00000000..fbd23dc0 --- /dev/null +++ b/include/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 +}; + +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/include/ingen/Store.hpp b/include/ingen/Store.hpp new file mode 100644 index 00000000..a7a1ec28 --- /dev/null +++ b/include/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 "ingen/ingen.h" +#include "raul/Deletable.hpp" +#include "raul/Noncopyable.hpp" +#include "raul/Path.hpp" + +#include <map> +#include <memory> +#include <mutex> +#include <utility> + +namespace raul { +class Symbol; +} // namespace raul + +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, std::shared_ptr<Node>> +{ +public: + void add(Node* o); + + Node* get(const raul::Path& path) { + const auto i = find(path); + return (i == end()) ? nullptr : i->second.get(); + } + + using const_range = std::pair<const_iterator, const_iterator>; + using Objects = std::map<raul::Path, std::shared_ptr<Node>>; + using Mutex = std::recursive_mutex; + + iterator find_descendants_end(Store::iterator parent); + const_iterator find_descendants_end(Store::const_iterator parent) const; + + const_range children_range(const std::shared_ptr<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; + + Mutex& mutex() { return _mutex; } + +private: + Mutex _mutex; +}; + +} // namespace ingen + +#endif // INGEN_STORE_HPP diff --git a/include/ingen/StreamWriter.hpp b/include/ingen/StreamWriter.hpp new file mode 100644 index 00000000..9fafe571 --- /dev/null +++ b/include/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 "ingen/ColorContext.hpp" +#include "ingen/TurtleWriter.hpp" +#include "ingen/ingen.h" + +#include <cstdio> + +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/include/ingen/Tee.hpp b/include/ingen/Tee.hpp new file mode 100644 index 00000000..562ff298 --- /dev/null +++ b/include/ingen/Tee.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_TEE_HPP +#define INGEN_TEE_HPP + +#include "ingen/Interface.hpp" +#include "ingen/Message.hpp" +#include "ingen/URI.hpp" + +#include <memory> +#include <mutex> +#include <utility> +#include <vector> + +namespace ingen { + +/** Interface that forwards all calls to several sinks. */ +class Tee : public Interface +{ +public: + using Sinks = std::vector<std::shared_ptr<Interface>>; + + explicit Tee(Sinks sinks) noexcept : _sinks(std::move(sinks)) {} + + std::shared_ptr<Interface> respondee() const override { + return _sinks.front()->respondee(); + } + + void set_respondee(const std::shared_ptr<Interface>& respondee) override + { + _sinks.front()->set_respondee(respondee); + } + + void message(const Message& message) override { + const 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_TEE_HPP diff --git a/include/ingen/TurtleWriter.hpp b/include/ingen/TurtleWriter.hpp new file mode 100644 index 00000000..780d9f7b --- /dev/null +++ b/include/ingen/TurtleWriter.hpp @@ -0,0 +1,69 @@ +/* + 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_TURTLEWRITER_HPP +#define INGEN_TURTLEWRITER_HPP + +#include "ingen/AtomSink.hpp" +#include "ingen/AtomWriter.hpp" +#include "ingen/URI.hpp" +#include "ingen/ingen.h" +#include "lv2/atom/atom.h" +#include "serd/serd.h" +#include "sratom/sratom.h" + +#include <cstddef> +#include <cstdint> + +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, 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{SERD_URI_NULL}; + SerdEnv* _env; + SerdWriter* _writer; + URI _uri; + bool _wrote_prefixes{false}; +}; + +} // namespace ingen + +#endif // INGEN_TURTLEWRITER_HPP diff --git a/include/ingen/URI.hpp b/include/ingen/URI.hpp new file mode 100644 index 00000000..a45fb268 --- /dev/null +++ b/include/ingen/URI.hpp @@ -0,0 +1,175 @@ +/* + 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 "ingen/FilePath.hpp" +#include "ingen/ingen.h" +#include "serd/serd.h" +#include "sord/sordmm.hpp" + +#include <cstddef> +#include <cstdint> +#include <ostream> +#include <string> +#include <string_view> + +namespace ingen { + +class INGEN_API URI +{ +public: + using Chunk = std::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) noexcept; + URI& operator=(URI&& uri) noexcept; + + ~URI(); + + URI make_relative(const URI& base) const; + URI make_relative(const URI& base, const URI& root) const; + + bool empty() const { return !_node.buf; } + + std::string string() const { return {c_str(), _node.n_bytes}; } + size_t length() const { return _node.n_bytes; } + + const char* c_str() const + { + return reinterpret_cast<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 reinterpret_cast<const char*>(_node.buf); + } + + const char* end() const + { + return reinterpret_cast<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( + reinterpret_cast<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 {reinterpret_cast<const char*>(chunk.buf), chunk.len}; + } + + SerdURI _uri; + SerdNode _node; +}; + +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/include/ingen/URIMap.hpp b/include/ingen/URIMap.hpp new file mode 100644 index 00000000..de137af2 --- /dev/null +++ b/include/ingen/URIMap.hpp @@ -0,0 +1,129 @@ +/* + 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 "ingen/LV2Features.hpp" +#include "ingen/ingen.h" +#include "ingen/memory.hpp" +#include "lv2/core/lv2.h" +#include "lv2/urid/urid.h" +#include "raul/Noncopyable.hpp" + +#include <cstdint> +#include <memory> +#include <mutex> +#include <string> +#include <unordered_map> +#include <vector> + +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, data} {} + + const char* uri() const override { return _feature.URI; } + + std::shared_ptr<LV2_Feature> feature(World&, Node*) override + { + return {&_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& data() { return _urid_map; } + const LV2_URID_Map& data() const { return _urid_map; } + + private: + LV2_URID_Map _urid_map; + Log& _log; + }; + + struct URIDUnmapFeature : public Feature { + URIDUnmapFeature(URIMap* map, LV2_URID_Unmap* impl); + + const char* unmap(LV2_URID urid) const; + static const char* default_unmap(LV2_URID_Map_Handle h, LV2_URID urid); + + LV2_URID_Unmap& data() { return _urid_unmap; } + const LV2_URID_Unmap& data() const { return _urid_unmap; } + + private: + LV2_URID_Unmap _urid_unmap; + }; + + const LV2_URID_Map& urid_map() const { return _urid_map_feature->data(); } + LV2_URID_Map& urid_map() { return _urid_map_feature->data(); } + + const LV2_URID_Unmap& urid_unmap() const + { + return _urid_unmap_feature->data(); + } + + LV2_URID_Unmap& urid_unmap() { return _urid_unmap_feature->data(); } + + std::shared_ptr<URIDMapFeature> urid_map_feature() + { + return _urid_map_feature; + } + + std::shared_ptr<URIDUnmapFeature> urid_unmap_feature() + { + return _urid_unmap_feature; + } + +private: + friend struct URIDMapFeature; + friend struct URIDUnMapFeature; + + std::shared_ptr<URIDMapFeature> _urid_map_feature; + std::shared_ptr<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/include/ingen/URIs.hpp b/include/ingen/URIs.hpp new file mode 100644 index 00000000..1b29bcd5 --- /dev/null +++ b/include/ingen/URIs.hpp @@ -0,0 +1,246 @@ +/* + 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/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& ingen_forge, URIMap* map, LilvWorld* lworld); + + struct Quark : public URI { + Quark(ingen::Forge& ingen_forge, + URIMap* map, + LilvWorld* lworld, + const char* str); + + Quark(const Quark& copy); + + ~Quark(); + + const Atom& urid_atom() const { return _urid_atom; } + const Atom& uri_atom() const { return _uri_atom; } + + LV2_URID urid() const { return _urid_atom.get<LV2_URID>(); } + const LilvNode* node() const { return _lilv_node; } + + operator LV2_URID() const { return _urid_atom.get<LV2_URID>(); } + explicit operator Atom() const { return _urid_atom; } + operator const LilvNode*() const { return _lilv_node; } + + private: + Atom _urid_atom; + Atom _uri_atom; + LilvNode* _lilv_node; + }; + + ingen::Forge& forge; + + Quark atom_AtomPort; + Quark atom_Bool; + Quark atom_Chunk; + Quark atom_Float; + Quark atom_Int; + Quark atom_Object; + Quark atom_Path; + Quark atom_Sequence; + Quark atom_Sound; + Quark atom_String; + Quark atom_URI; + Quark atom_URID; + Quark atom_bufferType; + Quark atom_eventTransfer; + Quark atom_supports; + Quark bufsz_maxBlockLength; + Quark bufsz_minBlockLength; + Quark bufsz_sequenceSize; + Quark doap_name; + Quark ingen_Arc; + Quark ingen_Block; + Quark ingen_BundleEnd; + Quark ingen_BundleStart; + Quark ingen_Graph; + Quark ingen_GraphPrototype; + Quark ingen_Internal; + Quark ingen_Redo; + Quark ingen_Undo; + Quark ingen_activity; + Quark ingen_arc; + Quark ingen_block; + Quark ingen_broadcast; + Quark ingen_canvasX; + Quark ingen_canvasY; + Quark ingen_enabled; + Quark ingen_externalContext; + Quark ingen_file; + Quark ingen_head; + Quark ingen_incidentTo; + Quark ingen_internalContext; + Quark ingen_loadedBundle; + Quark ingen_maxRunLoad; + Quark ingen_meanRunLoad; + Quark ingen_minRunLoad; + Quark ingen_numThreads; + Quark ingen_polyphonic; + Quark ingen_polyphony; + Quark ingen_prototype; + Quark ingen_sprungLayout; + Quark ingen_tail; + Quark ingen_uiEmbedded; + Quark ingen_value; + Quark log_Error; + Quark log_Note; + Quark log_Trace; + Quark log_Warning; + Quark lv2_AudioPort; + Quark lv2_CVPort; + Quark lv2_ControlPort; + Quark lv2_InputPort; + Quark lv2_OutputPort; + Quark lv2_Plugin; + Quark lv2_appliesTo; + Quark lv2_binary; + Quark lv2_connectionOptional; + Quark lv2_control; + Quark lv2_default; + Quark lv2_designation; + Quark lv2_enumeration; + Quark lv2_extensionData; + Quark lv2_index; + Quark lv2_integer; + Quark lv2_maximum; + Quark lv2_microVersion; + Quark lv2_minimum; + Quark lv2_minorVersion; + Quark lv2_name; + Quark lv2_port; + Quark lv2_portProperty; + Quark lv2_prototype; + Quark lv2_sampleRate; + Quark lv2_scalePoint; + Quark lv2_symbol; + Quark lv2_toggled; + Quark midi_Bender; + Quark midi_ChannelPressure; + Quark midi_Controller; + Quark midi_MidiEvent; + Quark midi_NoteOn; + Quark midi_binding; + Quark midi_controllerNumber; + Quark midi_noteNumber; + Quark midi_channel; + Quark morph_AutoMorphPort; + Quark morph_MorphPort; + Quark morph_currentType; + Quark morph_supportsType; + Quark opt_interface; + Quark param_sampleRate; + Quark patch_Copy; + Quark patch_Delete; + Quark patch_Get; + Quark patch_Message; + Quark patch_Move; + Quark patch_Patch; + Quark patch_Put; + Quark patch_Response; + Quark patch_Set; + Quark patch_add; + Quark patch_body; + Quark patch_context; + Quark patch_destination; + Quark patch_property; + Quark patch_remove; + Quark patch_sequenceNumber; + Quark patch_subject; + Quark patch_value; + Quark patch_wildcard; + Quark pprops_logarithmic; + Quark pset_Preset; + Quark pset_preset; + Quark rdf_type; + Quark rdfs_Class; + Quark rdfs_label; + Quark rdfs_seeAlso; + Quark rsz_minimumSize; + Quark state_loadDefaultState; + Quark state_state; + Quark time_Position; + Quark time_bar; + Quark time_barBeat; + Quark time_beatUnit; + Quark time_beatsPerBar; + Quark time_beatsPerMinute; + Quark time_frame; + Quark time_speed; + Quark work_schedule; +}; + +inline bool +operator==(const URIs::Quark& lhs, const Atom& rhs) +{ + if (rhs.type() == lhs.urid_atom().type()) { + return rhs == lhs.urid_atom(); + } + + if (rhs.type() == lhs.uri_atom().type()) { + return rhs == lhs.uri_atom(); + } + + 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/include/ingen/World.hpp b/include/ingen/World.hpp new file mode 100644 index 00000000..0b8690ad --- /dev/null +++ b/include/ingen/World.hpp @@ -0,0 +1,155 @@ +/* + 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 "ingen/ingen.h" +#include "lv2/log/log.h" +#include "lv2/urid/urid.h" +#include "raul/Noncopyable.hpp" + +#include <memory> +#include <mutex> +#include <string> + +using LilvWorld = struct LilvWorldImpl; + +namespace Sord { +class World; +} // namespace Sord + +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. */ + using InterfaceFactory = std::shared_ptr<Interface> (*)( + World& world, + const URI& engine_uri, + const std::shared_ptr<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 std::shared_ptr<Interface> + new_interface(const URI& engine_uri, + const std::shared_ptr<Interface>& respondee); + + /** Run a script. */ + virtual bool run(const std::string& mime_type, + const std::string& filename); + + virtual void set_engine(const std::shared_ptr<EngineBase>& e); + virtual void set_interface(const std::shared_ptr<Interface>& i); + virtual void set_store(const std::shared_ptr<Store>& s); + + virtual std::shared_ptr<EngineBase> engine(); + virtual std::shared_ptr<Interface> interface(); + virtual std::shared_ptr<Parser> parser(); + virtual std::shared_ptr<Serialiser> serialiser(); + virtual std::shared_ptr<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/include/ingen/client/ArcModel.hpp b/include/ingen/client/ArcModel.hpp new file mode 100644 index 00000000..8104b188 --- /dev/null +++ b/include/ingen/client/ArcModel.hpp @@ -0,0 +1,65 @@ +/* + 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 "ingen/Arc.hpp" +#include "ingen/client/PortModel.hpp" +#include "ingen/ingen.h" +#include "raul/Path.hpp" + +#include <cassert> +#include <memory> +#include <string> +#include <utility> + +namespace ingen::client { + +/** Class to represent a port->port connections in the engine. + * + * @ingroup IngenClient + */ +class INGEN_API ArcModel : public Arc +{ +public: + std::shared_ptr<PortModel> tail() const { return _tail; } + std::shared_ptr<PortModel> head() const { return _head; } + + const raul::Path& tail_path() const override { return _tail->path(); } + const raul::Path& head_path() const override { return _head->path(); } + +private: + friend class ClientStore; + + ArcModel(std::shared_ptr<PortModel> tail, std::shared_ptr<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 std::shared_ptr<PortModel> _tail; + const std::shared_ptr<PortModel> _head; +}; + +} // namespace ingen::client + +#endif // INGEN_CLIENT_ARCMODEL_HPP diff --git a/include/ingen/client/BlockModel.hpp b/include/ingen/client/BlockModel.hpp new file mode 100644 index 00000000..d2641f77 --- /dev/null +++ b/include/ingen/client/BlockModel.hpp @@ -0,0 +1,124 @@ +/* + 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 "ingen/Node.hpp" +#include "ingen/URI.hpp" +#include "ingen/client/ObjectModel.hpp" +#include "ingen/client/PluginModel.hpp" // IWYU pragma: keep +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" + +#include <cstdint> +#include <memory> +#include <string> +#include <vector> + +// IWYU pragma: no_include <algorithm> + +namespace raul { +class Path; +class Symbol; +} // namespace raul + +namespace ingen { + +class Resource; +class URIs; + +namespace client { + +class PortModel; + +/** 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); + ~BlockModel() override; + + GraphType graph_type() const override { return Node::GraphType::BLOCK; } + + using Ports = std::vector<std::shared_ptr<const PortModel>>; + + std::shared_ptr<const PortModel> get_port(const raul::Symbol& symbol) const; + std::shared_ptr<const PortModel> get_port(uint32_t index) const; + + Node* port(uint32_t index) const override; + + const URI& plugin_uri() const { return _plugin_uri; } + const Resource* plugin() const override { return _plugin.get(); } + Resource* plugin() { return _plugin.get(); } + std::shared_ptr<PluginModel> plugin_model() const { return _plugin; } + uint32_t num_ports() const override { return _ports.size(); } + const Ports& ports() const { return _ports; } + + void default_port_value_range(const std::shared_ptr<const PortModel>& port, + float& min, + float& max, + uint32_t srate = 1) const; + + void port_value_range(const std::shared_ptr<const PortModel>& port, + float& min, + float& max, + uint32_t srate = 1) const; + + std::string label() const; + std::string port_label(const std::shared_ptr<const PortModel>& port) const; + + // Signals + INGEN_SIGNAL(new_port, void, std::shared_ptr<const PortModel>) + INGEN_SIGNAL(removed_port, void, std::shared_ptr<const PortModel>) + +protected: + friend class ClientStore; + + BlockModel(URIs& uris, URI plugin_uri, const raul::Path& path); + + BlockModel(URIs& uris, + const std::shared_ptr<PluginModel>& plugin, + const raul::Path& path); + + explicit BlockModel(const raul::Path& path); + + void add_child(const std::shared_ptr<ObjectModel>& c) override; + bool remove_child(const std::shared_ptr<ObjectModel>& c) override; + void add_port(const std::shared_ptr<PortModel>& pm); + void remove_port(const std::shared_ptr<PortModel>& port); + void remove_port(const raul::Path& port_path); + void set(const std::shared_ptr<ObjectModel>& model) override; + + virtual void clear(); + + Ports _ports; ///< Vector of ports + URI _plugin_uri; ///< Plugin URI (if PluginModel is unknown) + std::shared_ptr<PluginModel> _plugin; ///< Plugin this 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/include/ingen/client/ClientStore.hpp b/include/ingen/client/ClientStore.hpp new file mode 100644 index 00000000..83cce726 --- /dev/null +++ b/include/ingen/client/ClientStore.hpp @@ -0,0 +1,130 @@ +/* + 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 "ingen/Interface.hpp" +#include "ingen/Message.hpp" +#include "ingen/Store.hpp" +#include "ingen/URI.hpp" +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" + +#include <map> +#include <memory> +#include <utility> + +namespace raul { +class Path; +} // namespace raul + +namespace ingen { + +class Atom; +class Log; +class Resource; +class URIs; + +namespace client { + +class GraphModel; +class ObjectModel; +class PluginModel; +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, + const std::shared_ptr<SigClientInterface>& emitter = + std::shared_ptr<SigClientInterface>()); + + URI uri() const override { return URI("ingen:/clients/store"); } + + std::shared_ptr<const ObjectModel> object(const raul::Path& path) const; + std::shared_ptr<const PluginModel> plugin(const URI& uri) const; + std::shared_ptr<const Resource> resource(const URI& uri) const; + + void clear(); + + using Plugins = std::map<const URI, std::shared_ptr<PluginModel>>; + + std::shared_ptr<const Plugins> plugins() const { return _plugins; } + std::shared_ptr<Plugins> plugins() { return _plugins; } + + void set_plugins(std::shared_ptr<Plugins> p) { _plugins = std::move(p); } + + URIs& uris() { return _uris; } + + void message(const Message& msg) override; + + void operator()(const BundleBegin&) noexcept {} + void operator()(const BundleEnd&) noexcept {} + 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&) noexcept {} + void operator()(const Get&) noexcept {} + void operator()(const Move&); + void operator()(const Put&); + void operator()(const Redo&) noexcept {} + void operator()(const Response&) noexcept {} + void operator()(const SetProperty&); + void operator()(const Undo&) noexcept {} + + INGEN_SIGNAL(new_object, void, std::shared_ptr<ObjectModel>) + INGEN_SIGNAL(new_plugin, void, std::shared_ptr<PluginModel>) + INGEN_SIGNAL(plugin_deleted, void, URI) + +private: + std::shared_ptr<ObjectModel> _object(const raul::Path& path); + std::shared_ptr<PluginModel> _plugin(const URI& uri); + std::shared_ptr<PluginModel> _plugin(const Atom& uri); + std::shared_ptr<Resource> _resource(const URI& uri); + + void add_object(const std::shared_ptr<ObjectModel>& object); + std::shared_ptr<ObjectModel> remove_object(const raul::Path& path); + + void add_plugin(const std::shared_ptr<PluginModel>& pm); + + std::shared_ptr<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; + std::shared_ptr<SigClientInterface> _emitter; + + std::shared_ptr<Plugins> _plugins; ///< Map, keyed by plugin URI +}; + +} // namespace client +} // namespace ingen + +#endif // INGEN_CLIENT_CLIENTSTORE_HPP diff --git a/include/ingen/client/GraphModel.hpp b/include/ingen/client/GraphModel.hpp new file mode 100644 index 00000000..0ae756d8 --- /dev/null +++ b/include/ingen/client/GraphModel.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_CLIENT_GRAPHMODEL_HPP +#define INGEN_CLIENT_GRAPHMODEL_HPP + +#include "ingen/Node.hpp" +#include "ingen/URIs.hpp" +#include "ingen/client/BlockModel.hpp" +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" + +#include <cstdint> +#include <memory> + +namespace raul { +class Path; +} // namespace raul + +namespace ingen { + +class URI; + +namespace client { + +class ArcModel; +class ObjectModel; +class PortModel; + +/** 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 override { return Node::GraphType::GRAPH; } + + std::shared_ptr<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, std::shared_ptr<BlockModel>) + INGEN_SIGNAL(removed_block, void, std::shared_ptr<BlockModel>) + INGEN_SIGNAL(new_arc, void, std::shared_ptr<ArcModel>) + INGEN_SIGNAL(removed_arc, void, std::shared_ptr<ArcModel>) + +private: + friend class ClientStore; + + GraphModel(URIs& uris, const raul::Path& graph_path) + : BlockModel(uris, + static_cast<const URI&>(uris.ingen_Graph), + graph_path) + {} + + void clear() override; + void add_child(const std::shared_ptr<ObjectModel>& c) override; + bool remove_child(const std::shared_ptr<ObjectModel>& o) override; + void remove_arcs_on(const std::shared_ptr<PortModel>& p); + + void add_arc(const std::shared_ptr<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/include/ingen/client/ObjectModel.hpp b/include/ingen/client/ObjectModel.hpp new file mode 100644 index 00000000..e92618f8 --- /dev/null +++ b/include/ingen/client/ObjectModel.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/>. +*/ + +/** + @defgroup IngenClient Client-Side Models and Utilities +*/ + +#ifndef INGEN_CLIENT_OBJECTMODEL_HPP +#define INGEN_CLIENT_OBJECTMODEL_HPP + +#include "ingen/Node.hpp" +#include "ingen/URI.hpp" +#include "ingen/URIs.hpp" +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" +#include "raul/Path.hpp" +#include "raul/Symbol.hpp" + +#include <memory> + +namespace ingen { + +class Atom; + +namespace client { + +/** 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 override; + + void on_property(const URI& uri, const Atom& value) override; + void on_property_removed(const URI& uri, const Atom& value) override; + + const raul::Path& path() const override { return _path; } + const raul::Symbol& symbol() const override { return _symbol; } + + std::shared_ptr<ObjectModel> parent() const { return _parent; } + bool polyphonic() const; + + Node* graph_parent() const override { return _parent.get(); } + + // Signals + INGEN_SIGNAL(new_child, void, std::shared_ptr<ObjectModel>) + INGEN_SIGNAL(removed_child, void, std::shared_ptr<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); + + void set_path(const raul::Path& p) override; + virtual void set_parent(const std::shared_ptr<ObjectModel>& p); + virtual void add_child(const std::shared_ptr<ObjectModel>& c) {} + virtual bool remove_child(const std::shared_ptr<ObjectModel>& c) { return true; } + + virtual void set(const std::shared_ptr<ObjectModel>& o); + + std::shared_ptr<ObjectModel> _parent; + +private: + raul::Path _path; + raul::Symbol _symbol; +}; + +} // namespace client +} // namespace ingen + +#endif // INGEN_CLIENT_OBJECTMODEL_HPP diff --git a/include/ingen/client/PluginModel.hpp b/include/ingen/client/PluginModel.hpp new file mode 100644 index 00000000..7f86f680 --- /dev/null +++ b/include/ingen/client/PluginModel.hpp @@ -0,0 +1,133 @@ +/* + 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 "ingen/Atom.hpp" +#include "ingen/Forge.hpp" +#include "ingen/Properties.hpp" +#include "ingen/Resource.hpp" +#include "ingen/URI.hpp" +#include "ingen/URIs.hpp" +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" +#include "lilv/lilv.h" +#include "raul/Symbol.hpp" + +#include <cstdint> +#include <map> +#include <memory> +#include <string> + +namespace Sord { +class World; +} // namespace Sord + +namespace ingen { + +class World; + +namespace client { + +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; } + + URI type_uri() const + { + return URI(_type.is_valid() ? _uris.forge.str(_type, false) + : "http://www.w3.org/2002/07/owl#Nothing"); + } + + const Atom& get_property(const URI& key) const override; + + raul::Symbol default_block_symbol() const; + std::string human_name() const; + std::string port_human_name(uint32_t index) const; + + using ScalePoints = std::map<float, std::string>; + ScalePoints port_scale_points(uint32_t index) const; + + using Presets = std::map<URI, std::string>; + 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; + + std::shared_ptr<PluginUI> + ui(ingen::World& world, + const std::shared_ptr<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(const std::shared_ptr<PluginModel>& p); + + void add_preset(const URI& uri, const std::string& label); + +private: + static std::string get_documentation(const LilvNode* subject, bool html); + + static Sord::World* _rdf_world; + static LilvWorld* _lilv_world; + static const LilvPlugins* _lilv_plugins; + + Atom _type; + const LilvPlugin* _lilv_plugin; + Presets _presets; + bool _fetched{false}; +}; + +} // namespace client +} // namespace ingen + +#endif // INGEN_CLIENT_PLUGINMODEL_HPP diff --git a/include/ingen/client/PluginUI.hpp b/include/ingen/client/PluginUI.hpp new file mode 100644 index 00000000..b291d521 --- /dev/null +++ b/include/ingen/client/PluginUI.hpp @@ -0,0 +1,119 @@ +/* + 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 "signal.hpp" + +#include "ingen/LV2Features.hpp" +#include "ingen/Resource.hpp" +#include "ingen/ingen.h" +#include "lilv/lilv.h" +#include "suil/suil.h" + +#include <cstdint> +#include <memory> +#include <set> + +namespace ingen { + +class Atom; +class URI; +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 std::shared_ptr<PluginUI> + create(ingen::World& world, + const std::shared_ptr<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; } + std::shared_ptr<const BlockModel> block() const { return _block; } + +private: + PluginUI(ingen::World& world, + std::shared_ptr<const BlockModel> block, + LilvUIs* uis, + const LilvUI* ui, + const LilvNode* ui_type); + + ingen::World& _world; + std::shared_ptr<const BlockModel> _block; + SuilInstance* _instance{nullptr}; + LilvUIs* _uis{nullptr}; + const LilvUI* _ui{nullptr}; + LilvNode* _ui_node{nullptr}; + LilvNode* _ui_type{nullptr}; + std::set<uint32_t> _subscribed_ports; + + static SuilHost* ui_host; + + std::shared_ptr<LV2Features::FeatureArray> _features; +}; + +} // namespace client +} // namespace ingen + +#endif // INGEN_CLIENT_PLUGINUI_HPP diff --git a/include/ingen/client/PortModel.hpp b/include/ingen/client/PortModel.hpp new file mode 100644 index 00000000..903a435d --- /dev/null +++ b/include/ingen/client/PortModel.hpp @@ -0,0 +1,101 @@ +/* + 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 "ingen/Node.hpp" +#include "ingen/URI.hpp" +#include "ingen/URIs.hpp" +#include "ingen/client/ObjectModel.hpp" +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" +#include "raul/Path.hpp" + +#include <cstdint> +#include <memory> +#include <string> + +namespace ingen { + +class Atom; + +namespace client { + +/** Model of a port. + * + * @ingroup IngenClient + */ +class INGEN_API PortModel : public ObjectModel +{ +public: + enum class Direction { INPUT, OUTPUT }; + + GraphType graph_type() const override { return Node::GraphType::PORT; } + + bool supports(const URIs::Quark& value_type) const; + + uint32_t index() const { return _index; } + const Atom& value() const { return get_property(_uris.ingen_value); } + bool is_input() const { return (_direction == Direction::INPUT); } + 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; + + bool operator==(const PortModel& pm) const { return (path() == pm.path()); } + + void on_property(const URI& uri, const Atom& value) override; + + // 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(const std::shared_ptr<ObjectModel>& c) override { throw; } + bool remove_child(const std::shared_ptr<ObjectModel>& c) override { throw; } + + void set(const std::shared_ptr<ObjectModel>& model) override; + + uint32_t _index; + Direction _direction; +}; + +} // namespace client +} // namespace ingen + +#endif // INGEN_CLIENT_PORTMODEL_HPP diff --git a/include/ingen/client/SigClientInterface.hpp b/include/ingen/client/SigClientInterface.hpp new file mode 100644 index 00000000..6bff2e33 --- /dev/null +++ b/include/ingen/client/SigClientInterface.hpp @@ -0,0 +1,59 @@ +/* + 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 "ingen/Interface.hpp" +#include "ingen/Message.hpp" +#include "ingen/URI.hpp" +#include "ingen/client/signal.hpp" +#include "ingen/ingen.h" + +namespace ingen::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() = default; + + 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 ingen::client + +#endif diff --git a/include/ingen/client/SocketClient.hpp b/include/ingen/client/SocketClient.hpp new file mode 100644 index 00000000..7434da90 --- /dev/null +++ b/include/ingen/client/SocketClient.hpp @@ -0,0 +1,92 @@ +/* + 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_SOCKETCLIENT_HPP +#define INGEN_CLIENT_SOCKETCLIENT_HPP + +#include "ingen/Log.hpp" +#include "ingen/SocketReader.hpp" +#include "ingen/SocketWriter.hpp" +#include "ingen/URI.hpp" +#include "ingen/World.hpp" +#include "ingen/ingen.h" +#include "raul/Socket.hpp" + +#include <cerrno> +#include <cstring> +#include <memory> + +namespace ingen { + +class Interface; + +namespace client { + +/** The client side of an Ingen socket connection. */ +class INGEN_API SocketClient : public SocketWriter +{ +public: + SocketClient(World& world, + const URI& uri, + const std::shared_ptr<raul::Socket>& sock, + const std::shared_ptr<Interface>& respondee) + : SocketWriter(world.uri_map(), world.uris(), uri, sock) + , _respondee(respondee) + , _reader(world, *respondee, sock) + {} + + std::shared_ptr<Interface> respondee() const override { + return _respondee; + } + + void set_respondee(const std::shared_ptr<Interface>& respondee) override + { + _respondee = respondee; + } + + static std::shared_ptr<ingen::Interface> + new_socket_interface(ingen::World& world, + const URI& uri, + const std::shared_ptr<ingen::Interface>& respondee) + { + const raul::Socket::Type type = (uri.scheme() == "unix" + ? raul::Socket::Type::UNIX + : raul::Socket::Type::TCP); + + const std::shared_ptr<raul::Socket> sock{new raul::Socket(type)}; + if (!sock->connect(uri)) { + world.log().error("Failed to connect <%1%> (%2%)\n", + sock->uri(), strerror(errno)); + return nullptr; + } + return std::shared_ptr<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: + std::shared_ptr<Interface> _respondee; + SocketReader _reader; +}; + +} // namespace client +} // namespace ingen + +#endif // INGEN_CLIENT_SOCKETCLIENT_HPP diff --git a/include/ingen/client/client.h b/include/ingen/client/client.h new file mode 100644 index 00000000..6f7ac9b5 --- /dev/null +++ b/include/ingen/client/client.h @@ -0,0 +1,31 @@ +/* + This file is part of Ingen. + Copyright 2014-2022 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + 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_CLIENT_H +#define INGEN_CLIENT_CLIENT_H + +#if defined(_WIN32) && !defined(INGEN_CLIENT_STATIC) && \ + defined(INGEN_CLIENT_INTERNAL) +# define INGEN_CLIENT_API __declspec(dllexport) +#elif defined(_WIN32) && !defined(INGEN_CLIENT_STATIC) +# define INGEN_CLIENT_API __declspec(dllimport) +#elif defined(__GNUC__) +# define INGEN_CLIENT_API __attribute__((visibility("default"))) +#else +# define INGEN_CLIENT_API +#endif + +#endif // INGEN_CLIENT_CLIENT_H diff --git a/include/ingen/client/signal.hpp b/include/ingen/client/signal.hpp new file mode 100644 index 00000000..ea382549 --- /dev/null +++ b/include/ingen/client/signal.hpp @@ -0,0 +1,34 @@ +/* + 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 + +// IWYU pragma: begin_exports +#include <sigc++/signal.h> +#include <sigc++/trackable.h> +// IWYU pragma: end_exports + +#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/include/ingen/fmt.hpp b/include/ingen/fmt.hpp new file mode 100644 index 00000000..7ca5de9f --- /dev/null +++ b/include/ingen/fmt.hpp @@ -0,0 +1,40 @@ +/* + This file is part of Ingen. + Copyright 2007-2023 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + 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_FMT_HPP +#define INGEN_FMT_HPP + +#include <boost/format.hpp> // IWYU pragma: export + +#include <initializer_list> +#include <string> + +namespace ingen { +template <typename... Args> +std::string +fmt(const char* fmt, Args&&... args) +{ + boost::format f{fmt}; // NOLINT(misc-const-correctness) + const std::initializer_list<char> l{ + (static_cast<void>(f % args), char{})...}; + + (void)l; + return boost::str(f); +} + +} // namespace ingen + +#endif // INGEN_FMT_HPP diff --git a/include/ingen/ingen.h b/include/ingen/ingen.h new file mode 100644 index 00000000..9292de46 --- /dev/null +++ b/include/ingen/ingen.h @@ -0,0 +1,68 @@ +/* + 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_INGEN_H +#define INGEN_INGEN_H + +#if defined(_WIN32) && !defined(INGEN_STATIC) && defined(INGEN_INTERNAL) +# define INGEN_API __declspec(dllexport) +#elif defined(_WIN32) && !defined(INGEN_STATIC) +# define INGEN_API __declspec(dllimport) +#elif defined(__GNUC__) +# define INGEN_API __attribute__((visibility("default"))) +#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_INGEN_H diff --git a/include/ingen/memory.hpp b/include/ingen/memory.hpp new file mode 100644 index 00000000..a1dba436 --- /dev/null +++ b/include/ingen/memory.hpp @@ -0,0 +1,32 @@ +/* + This file is part of Ingen. + Copyright 2007-2020 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + 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_MEMORY_HPP +#define INGEN_MEMORY_HPP + +#include <cstdlib> + +namespace ingen { + +template <class T> +void NullDeleter(T* ptr) noexcept {} + +template <class T> +struct FreeDeleter { void operator()(T* const ptr) noexcept { free(ptr); } }; + +} // namespace ingen + +#endif // INGEN_MEMORY_HPP diff --git a/include/ingen/paths.hpp b/include/ingen/paths.hpp new file mode 100644 index 00000000..ad079390 --- /dev/null +++ b/include/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 "ingen/URI.hpp" +#include "raul/Path.hpp" + +#include <cstddef> +#include <string> + +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; + } + + 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/include/ingen/runtime_paths.hpp b/include/ingen/runtime_paths.hpp new file mode 100644 index 00000000..30e877fb --- /dev/null +++ b/include/ingen/runtime_paths.hpp @@ -0,0 +1,51 @@ +/* + 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 "ingen/FilePath.hpp" +#include "ingen/ingen.h" + +#include <string> +#include <vector> + +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 +find_in_search_path(const std::string& name, + const std::vector<FilePath>& search_path); + +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); + +INGEN_API FilePath user_config_dir(); +INGEN_API FilePath user_data_dir(); +INGEN_API std::vector<FilePath> system_config_dirs(); +INGEN_API std::vector<FilePath> system_data_dirs(); +INGEN_API std::vector<FilePath> config_dirs(); +INGEN_API std::vector<FilePath> data_dirs(); +INGEN_API std::vector<FilePath> ingen_module_dirs(); + +} // namespace ingen + +#endif // INGEN_RUNTIME_PATHS_HPP |