/*
This file is part of Ingen.
Copyright 2007-2012 David Robillard
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or any later version.
Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
You should have received a copy of the GNU Affero General Public License
along with Ingen. If not, see .
*/
#include
#include
#include
#include "raul/Maid.hpp"
#include "raul/Path.hpp"
#include "Broadcaster.hpp"
#include "Connect.hpp"
#include "EdgeImpl.hpp"
#include "DuplexPort.hpp"
#include "Engine.hpp"
#include "EngineStore.hpp"
#include "InputPort.hpp"
#include "OutputPort.hpp"
#include "PatchImpl.hpp"
#include "PortImpl.hpp"
#include "ProcessContext.hpp"
#include "types.hpp"
namespace Ingen {
namespace Server {
namespace Events {
Connect::Connect(Engine& engine,
Interface* client,
int32_t id,
SampleCount timestamp,
const Raul::Path& tail_path,
const Raul::Path& head_path)
: Event(engine, client, id, timestamp)
, _tail_path(tail_path)
, _head_path(head_path)
, _patch(NULL)
, _src_output_port(NULL)
, _dst_input_port(NULL)
, _compiled_patch(NULL)
, _buffers(NULL)
{}
bool
Connect::pre_process()
{
Glib::RWLock::ReaderLock rlock(_engine.engine_store()->lock());
PortImpl* tail = _engine.engine_store()->find_port(_tail_path);
PortImpl* head = _engine.engine_store()->find_port(_head_path);
if (!tail || !head) {
return Event::pre_process_done(PORT_NOT_FOUND);
}
_dst_input_port = dynamic_cast(head);
_src_output_port = dynamic_cast(tail);
if (!_dst_input_port || !_src_output_port) {
return Event::pre_process_done(DIRECTION_MISMATCH);
}
NodeImpl* const src_node = tail->parent_node();
NodeImpl* const dst_node = head->parent_node();
if (!src_node || !dst_node) {
return Event::pre_process_done(PARENT_NOT_FOUND);
}
if (src_node->parent() != dst_node->parent()
&& src_node != dst_node->parent()
&& src_node->parent() != dst_node) {
return Event::pre_process_done(PARENT_DIFFERS);
}
if (!EdgeImpl::can_connect(_src_output_port, _dst_input_port)) {
return Event::pre_process_done(TYPE_MISMATCH);
}
if (src_node->parent_patch() != dst_node->parent_patch()) {
// Edge to a patch port from inside the patch
assert(src_node->parent() == dst_node || dst_node->parent() == src_node);
if (src_node->parent() == dst_node) {
_patch = dynamic_cast(dst_node);
} else {
_patch = dynamic_cast(src_node);
}
} else if (src_node == dst_node && dynamic_cast(src_node)) {
// Edge from a patch input to a patch output (pass through)
_patch = dynamic_cast(src_node);
} else {
// Normal edge between nodes with the same parent
_patch = src_node->parent_patch();
}
if (_patch->has_edge(_src_output_port, _dst_input_port)) {
return Event::pre_process_done(EXISTS);
}
_edge = SharedPtr(
new EdgeImpl(_src_output_port, _dst_input_port));
rlock.release();
{
Glib::RWLock::ReaderLock wlock(_engine.engine_store()->lock());
/* Need to be careful about patch port edges here and adding a
node's parent as a dependant/provider, or adding a patch as its own
provider...
*/
if (src_node != dst_node && src_node->parent() == dst_node->parent()) {
dst_node->providers().push_back(src_node);
src_node->dependants().push_back(dst_node);
}
_patch->add_edge(_edge);
_dst_input_port->increment_num_edges();
}
_buffers = new Raul::Array(_dst_input_port->poly());
_dst_input_port->get_buffers(_engine.message_context(),
*_engine.buffer_factory(),
_buffers, _dst_input_port->poly());
if (_patch->enabled()) {
_compiled_patch = _patch->compile();
}
return Event::pre_process_done(SUCCESS);
}
void
Connect::execute(ProcessContext& context)
{
if (!_status) {
_dst_input_port->add_edge(context, _edge.get());
_engine.maid()->push(_dst_input_port->set_buffers(context, _buffers));
_dst_input_port->connect_buffers();
_engine.maid()->push(_patch->compiled_patch());
_patch->compiled_patch(_compiled_patch);
}
}
void
Connect::post_process()
{
respond(_status);
if (!_status) {
_engine.broadcaster()->connect(_tail_path, _head_path);
}
}
} // namespace Events
} // namespace Server
} // namespace Ingen