summaryrefslogtreecommitdiffstats
path: root/src/server/GraphImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/GraphImpl.cpp')
-rw-r--r--src/server/GraphImpl.cpp379
1 files changed, 379 insertions, 0 deletions
diff --git a/src/server/GraphImpl.cpp b/src/server/GraphImpl.cpp
new file mode 100644
index 00000000..ae9b8f6e
--- /dev/null
+++ b/src/server/GraphImpl.cpp
@@ -0,0 +1,379 @@
+/*
+ This file is part of Ingen.
+ Copyright 2007-2016 David Robillard <http://drobilla.net/>
+
+ Ingen is free software: you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or any later version.
+
+ Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Ingen. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cassert>
+#include <unordered_map>
+
+#include "ingen/Log.hpp"
+#include "ingen/URIs.hpp"
+#include "ingen/World.hpp"
+#include "raul/Maid.hpp"
+
+#include "ArcImpl.hpp"
+#include "BlockImpl.hpp"
+#include "BufferFactory.hpp"
+#include "DuplexPort.hpp"
+#include "Engine.hpp"
+#include "GraphImpl.hpp"
+#include "GraphPlugin.hpp"
+#include "PortImpl.hpp"
+#include "ThreadManager.hpp"
+
+namespace ingen {
+namespace server {
+
+GraphImpl::GraphImpl(Engine& engine,
+ const Raul::Symbol& symbol,
+ uint32_t poly,
+ GraphImpl* parent,
+ SampleRate srate,
+ uint32_t internal_poly)
+ : BlockImpl(new GraphPlugin(engine.world()->uris(),
+ engine.world()->uris().ingen_Graph,
+ Raul::Symbol("graph"),
+ "Ingen Graph"),
+ symbol, poly, parent, srate)
+ , _engine(engine)
+ , _poly_pre(internal_poly)
+ , _poly_process(internal_poly)
+ , _process(false)
+{
+ assert(internal_poly >= 1);
+ assert(internal_poly <= 128);
+}
+
+GraphImpl::~GraphImpl()
+{
+ delete _plugin;
+}
+
+BlockImpl*
+GraphImpl::duplicate(Engine& engine,
+ const Raul::Symbol& symbol,
+ GraphImpl* parent)
+{
+ BufferFactory& bufs = *engine.buffer_factory();
+ const SampleRate rate = engine.sample_rate();
+
+ // Duplicate graph
+ GraphImpl* dup = new GraphImpl(
+ engine, symbol, _polyphony, parent, rate, _poly_process);
+
+ Properties props = properties();
+ props.erase(bufs.uris().lv2_symbol);
+ props.insert({bufs.uris().lv2_symbol, bufs.forge().alloc(symbol.c_str())});
+ dup->set_properties(props);
+
+ // We need a map of port duplicates to duplicate arcs
+ typedef std::unordered_map<PortImpl*, PortImpl*> PortMap;
+ PortMap port_map;
+
+ // Add duplicates of all ports
+ dup->_ports = bufs.maid().make_managed<Ports>(num_ports(), nullptr);
+ for (PortList::iterator p = _inputs.begin(); p != _inputs.end(); ++p) {
+ DuplexPort* p_dup = p->duplicate(engine, p->symbol(), dup);
+ dup->_inputs.push_front(*p_dup);
+ (*dup->_ports)[p->index()] = p_dup;
+ port_map.insert({&*p, p_dup});
+ }
+ for (PortList::iterator p = _outputs.begin(); p != _outputs.end(); ++p) {
+ DuplexPort* p_dup = p->duplicate(engine, p->symbol(), dup);
+ dup->_outputs.push_front(*p_dup);
+ (*dup->_ports)[p->index()] = p_dup;
+ port_map.insert({&*p, p_dup});
+ }
+
+ // Add duplicates of all blocks
+ for (auto& b : _blocks) {
+ BlockImpl* b_dup = b.duplicate(engine, b.symbol(), dup);
+ dup->add_block(*b_dup);
+ b_dup->activate(*engine.buffer_factory());
+ for (uint32_t p = 0; p < b.num_ports(); ++p) {
+ port_map.insert({b.port_impl(p), b_dup->port_impl(p)});
+ }
+ }
+
+ // Add duplicates of all arcs
+ for (const auto& a : _arcs) {
+ SPtr<ArcImpl> arc = dynamic_ptr_cast<ArcImpl>(a.second);
+ if (arc) {
+ auto t = port_map.find(arc->tail());
+ auto h = port_map.find(arc->head());
+ if (t != port_map.end() && h != port_map.end()) {
+ dup->add_arc(SPtr<ArcImpl>(new ArcImpl(t->second, h->second)));
+ }
+ }
+ }
+
+ return dup;
+}
+
+void
+GraphImpl::activate(BufferFactory& bufs)
+{
+ BlockImpl::activate(bufs);
+
+ for (auto& b : _blocks) {
+ b.activate(bufs);
+ }
+
+ assert(_activated);
+}
+
+void
+GraphImpl::deactivate()
+{
+ if (_activated) {
+ BlockImpl::deactivate();
+
+ for (auto& b : _blocks) {
+ if (b.activated()) {
+ b.deactivate();
+ }
+ }
+ }
+}
+
+void
+GraphImpl::disable(RunContext& context)
+{
+ _process = false;
+ for (auto& o : _outputs) {
+ o.clear_buffers(context);
+ }
+}
+
+bool
+GraphImpl::prepare_internal_poly(BufferFactory& bufs, uint32_t poly)
+{
+ ThreadManager::assert_thread(THREAD_PRE_PROCESS);
+
+ // TODO: Subgraph dynamic polyphony (i.e. changing port polyphony)
+
+ for (auto& b : _blocks) {
+ b.prepare_poly(bufs, poly);
+ }
+
+ _poly_pre = poly;
+ return true;
+}
+
+bool
+GraphImpl::apply_internal_poly(RunContext& context,
+ BufferFactory& bufs,
+ Raul::Maid& maid,
+ uint32_t poly)
+{
+ // TODO: Subgraph dynamic polyphony (i.e. changing port polyphony)
+
+ for (auto& b : _blocks) {
+ b.apply_poly(context, poly);
+ }
+
+ for (auto& b : _blocks) {
+ for (uint32_t j = 0; j < b.num_ports(); ++j) {
+ PortImpl* const port = b.port_impl(j);
+ if (port->is_input() && dynamic_cast<InputPort*>(port)->direct_connect()) {
+ port->setup_buffers(context, bufs, port->poly());
+ }
+ port->connect_buffers();
+ }
+ }
+
+ const bool polyphonic = parent_graph() && (poly == parent_graph()->internal_poly_process());
+ for (auto& o : _outputs) {
+ o.setup_buffers(context, bufs, polyphonic ? poly : 1);
+ }
+
+ _poly_process = poly;
+ return true;
+}
+
+void
+GraphImpl::pre_process(RunContext& context)
+{
+ // Mix down input ports and connect buffers
+ for (uint32_t i = 0; i < num_ports(); ++i) {
+ PortImpl* const port = _ports->at(i);
+ if (!port->is_driver_port()) {
+ port->pre_process(context);
+ port->pre_run(context);
+ port->connect_buffers();
+ }
+ }
+}
+
+void
+GraphImpl::process(RunContext& context)
+{
+ if (!_process) {
+ return;
+ }
+
+ pre_process(context);
+ run(context);
+ post_process(context);
+}
+
+void
+GraphImpl::run(RunContext& context)
+{
+ if (_compiled_graph) {
+ _compiled_graph->run(context);
+ }
+}
+
+void
+GraphImpl::set_buffer_size(RunContext& context,
+ BufferFactory& bufs,
+ LV2_URID type,
+ uint32_t size)
+{
+ BlockImpl::set_buffer_size(context, bufs, type, size);
+
+ if (_compiled_graph) {
+ // FIXME
+ // for (size_t i = 0; i < _compiled_graph->size(); ++i) {
+ // const CompiledBlock& block = (*_compiled_graph)[i];
+ // block.block()->set_buffer_size(context, bufs, type, size);
+ // }
+ }
+}
+
+void
+GraphImpl::add_block(BlockImpl& block)
+{
+ ThreadManager::assert_thread(THREAD_PRE_PROCESS);
+ _blocks.push_front(block);
+}
+
+void
+GraphImpl::remove_block(BlockImpl& block)
+{
+ _blocks.erase(_blocks.iterator_to(block));
+}
+
+void
+GraphImpl::add_arc(SPtr<ArcImpl> a)
+{
+ ThreadManager::assert_thread(THREAD_PRE_PROCESS);
+ _arcs.emplace(std::make_pair(a->tail(), a->head()), a);
+}
+
+SPtr<ArcImpl>
+GraphImpl::remove_arc(const PortImpl* tail, const PortImpl* dst_port)
+{
+ ThreadManager::assert_thread(THREAD_PRE_PROCESS);
+ auto i = _arcs.find(std::make_pair(tail, dst_port));
+ if (i != _arcs.end()) {
+ SPtr<ArcImpl> arc = dynamic_ptr_cast<ArcImpl>(i->second);
+ _arcs.erase(i);
+ return arc;
+ } else {
+ return SPtr<ArcImpl>();
+ }
+}
+
+bool
+GraphImpl::has_arc(const PortImpl* tail, const PortImpl* dst_port) const
+{
+ ThreadManager::assert_thread(THREAD_PRE_PROCESS);
+ auto i = _arcs.find(std::make_pair(tail, dst_port));
+ return (i != _arcs.end());
+}
+
+void
+GraphImpl::set_compiled_graph(MPtr<CompiledGraph>&& cg)
+{
+ if (_compiled_graph && _compiled_graph != cg) {
+ _engine.reset_load();
+ }
+ _compiled_graph = std::move(cg);
+}
+
+uint32_t
+GraphImpl::num_ports_non_rt() const
+{
+ ThreadManager::assert_not_thread(THREAD_PROCESS);
+ return _inputs.size() + _outputs.size();
+}
+
+bool
+GraphImpl::has_port_with_index(uint32_t index) const
+{
+ BufferFactory& bufs = *_engine.buffer_factory();
+ const auto index_atom = bufs.forge().make(int32_t(index));
+
+ for (auto p = _inputs.begin(); p != _inputs.end(); ++p) {
+ if (p->has_property(bufs.uris().lv2_index, index_atom)) {
+ return true;
+ }
+ }
+
+ for (auto p = _outputs.begin(); p != _outputs.end(); ++p) {
+ if (p->has_property(bufs.uris().lv2_index, index_atom)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+GraphImpl::remove_port(DuplexPort& port)
+{
+ if (port.is_input()) {
+ _inputs.erase(_inputs.iterator_to(port));
+ } else {
+ _outputs.erase(_outputs.iterator_to(port));
+ }
+}
+
+void
+GraphImpl::clear_ports()
+{
+ _inputs.clear();
+ _outputs.clear();
+}
+
+MPtr<BlockImpl::Ports>
+GraphImpl::build_ports_array(Raul::Maid& maid)
+{
+ ThreadManager::assert_thread(THREAD_PRE_PROCESS);
+
+ const size_t n = _inputs.size() + _outputs.size();
+ MPtr<Ports> result = maid.make_managed<Ports>(n);
+
+ std::map<size_t, DuplexPort*> ports;
+ for (PortList::iterator p = _inputs.begin(); p != _inputs.end(); ++p) {
+ ports.emplace(p->index(), &*p);
+ }
+ for (PortList::iterator p = _outputs.begin(); p != _outputs.end(); ++p) {
+ ports.emplace(p->index(), &*p);
+ }
+
+ size_t i = 0;
+ for (const auto& p : ports) {
+ result->at(i++) = p.second;
+ }
+
+ assert(i == n);
+
+ return result;
+}
+
+} // namespace server
+} // namespace ingen