diff options
Diffstat (limited to 'src/server/events/CreateBlock.cpp')
-rw-r--r-- | src/server/events/CreateBlock.cpp | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/src/server/events/CreateBlock.cpp b/src/server/events/CreateBlock.cpp new file mode 100644 index 00000000..fabdbd85 --- /dev/null +++ b/src/server/events/CreateBlock.cpp @@ -0,0 +1,180 @@ +/* + This file is part of Ingen. + Copyright 2007-2016 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "ingen/Forge.hpp" +#include "ingen/Store.hpp" +#include "ingen/URIs.hpp" +#include "raul/Maid.hpp" +#include "raul/Path.hpp" + +#include "BlockFactory.hpp" +#include "BlockImpl.hpp" +#include "Broadcaster.hpp" +#include "CreateBlock.hpp" +#include "Engine.hpp" +#include "GraphImpl.hpp" +#include "PluginImpl.hpp" +#include "PortImpl.hpp" +#include "PreProcessContext.hpp" +#include "LV2Block.hpp" + +namespace ingen { +namespace server { +namespace events { + +CreateBlock::CreateBlock(Engine& engine, + SPtr<Interface> client, + int32_t id, + SampleCount timestamp, + const Raul::Path& path, + Properties& properties) + : Event(engine, client, id, timestamp) + , _path(path) + , _properties(properties) + , _graph(nullptr) + , _block(nullptr) +{} + +bool +CreateBlock::pre_process(PreProcessContext& ctx) +{ + typedef Properties::const_iterator iterator; + + const ingen::URIs& uris = _engine.world()->uris(); + const SPtr<Store> store = _engine.store(); + + // Check sanity of target path + if (_path.is_root()) { + return Event::pre_process_done(Status::BAD_URI, _path); + } else if (store->get(_path)) { + return Event::pre_process_done(Status::EXISTS, _path); + } else if (!(_graph = dynamic_cast<GraphImpl*>(store->get(_path.parent())))) { + return Event::pre_process_done(Status::PARENT_NOT_FOUND, _path.parent()); + } + + // Map old ingen:prototype to new lv2:prototype + auto range = _properties.equal_range(uris.ingen_prototype); + for (auto i = range.first; i != range.second;) { + const auto value = i->second; + auto next = i; + next = _properties.erase(i); + _properties.emplace(uris.lv2_prototype, value); + i = next; + } + + // Get prototype + iterator t = _properties.find(uris.lv2_prototype); + if (t == _properties.end() || !uris.forge.is_uri(t->second)) { + // Missing/invalid prototype + return Event::pre_process_done(Status::BAD_REQUEST); + } + + const URI prototype(uris.forge.str(t->second, false)); + + // Find polyphony + const iterator p = _properties.find(uris.ingen_polyphonic); + const bool polyphonic = (p != _properties.end() && + p->second.type() == uris.forge.Bool && + p->second.get<int32_t>()); + + // Find and instantiate/duplicate prototype (plugin/existing node) + if (uri_is_path(prototype)) { + // Prototype is an existing block + BlockImpl* const ancestor = dynamic_cast<BlockImpl*>( + store->get(uri_to_path(prototype))); + if (!ancestor) { + return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype); + } else if (!(_block = ancestor->duplicate( + _engine, Raul::Symbol(_path.symbol()), _graph))) { + return Event::pre_process_done(Status::CREATION_FAILED, _path); + } + + /* Replace prototype with the ancestor's. This is less informative, + but the client expects an actual LV2 plugin as prototype. */ + _properties.erase(uris.ingen_prototype); + _properties.erase(uris.lv2_prototype); + _properties.emplace(uris.lv2_prototype, + uris.forge.make_urid(ancestor->plugin()->uri())); + } else { + // Prototype is a plugin + PluginImpl* const plugin = _engine.block_factory()->plugin(prototype); + if (!plugin) { + return Event::pre_process_done(Status::PROTOTYPE_NOT_FOUND, prototype); + } + + // Load state from directory if given in properties + LilvState* state = nullptr; + auto s = _properties.find(uris.state_state); + if (s != _properties.end() && s->second.type() == uris.forge.Path) { + state = LV2Block::load_state( + _engine.world(), FilePath(s->second.ptr<char>())); + } + + // Instantiate plugin + if (!(_block = plugin->instantiate(*_engine.buffer_factory(), + Raul::Symbol(_path.symbol()), + polyphonic, + _graph, + _engine, + state))) { + return Event::pre_process_done(Status::CREATION_FAILED, _path); + } + } + + // Activate block + _block->properties().insert(_properties.begin(), _properties.end()); + _block->activate(*_engine.buffer_factory()); + + // Add block to the store and the graph's pre-processor only block list + _graph->add_block(*_block); + store->add(_block); + + /* Compile graph with new block added for insertion in audio thread + TODO: Since the block is not connected at this point, a full compilation + could be avoided and the block simply appended. */ + _compiled_graph = ctx.maybe_compile(*_engine.maid(), *_graph); + + _update.put_block(_block); + + return Event::pre_process_done(Status::SUCCESS); +} + +void +CreateBlock::execute(RunContext& context) +{ + if (_status == Status::SUCCESS && _compiled_graph) { + _graph->set_compiled_graph(std::move(_compiled_graph)); + } +} + +void +CreateBlock::post_process() +{ + Broadcaster::Transfer t(*_engine.broadcaster()); + if (respond() == Status::SUCCESS) { + _update.send(*_engine.broadcaster()); + } +} + +void +CreateBlock::undo(Interface& target) +{ + target.del(_block->uri()); +} + +} // namespace events +} // namespace server +} // namespace ingen |