/* This file is part of Ingen. Copyright 2007-2015 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 "BlockImpl.hpp" #include "Buffer.hpp" #include "GraphImpl.hpp" #include "PluginImpl.hpp" #include "PortImpl.hpp" #include "PortType.hpp" #include "RunContext.hpp" #include "ThreadManager.hpp" #include "lv2/urid/urid.h" #include "raul/Array.hpp" #include "raul/Symbol.hpp" #include #include #include #include namespace ingen::server { BlockImpl::BlockImpl(PluginImpl* plugin, const raul::Symbol& symbol, bool polyphonic, GraphImpl* parent, SampleRate) : NodeImpl(plugin->uris(), parent, symbol) , _plugin(plugin) , _polyphony((polyphonic && parent) ? parent->internal_poly() : 1) , _polyphonic(polyphonic) { assert(_plugin); assert(_polyphony > 0); } BlockImpl::~BlockImpl() { assert(!_activated); if (is_linked()) { reinterpret_cast(_parent)->remove_block(*this); } } Node* BlockImpl::port(uint32_t index) const { return (*_ports)[index]; } const Resource* BlockImpl::plugin() const { return _plugin; } const PluginImpl* BlockImpl::plugin_impl() const { return _plugin; } void BlockImpl::activate(BufferFactory& bufs) { ThreadManager::assert_thread(THREAD_PRE_PROCESS); _activated = true; for (uint32_t p = 0; p < num_ports(); ++p) { PortImpl* const port = _ports->at(p); port->activate(bufs); } } void BlockImpl::deactivate() { _activated = false; for (uint32_t p = 0; p < num_ports(); ++p) { PortImpl* const port = _ports->at(p); port->deactivate(); } } bool BlockImpl::prepare_poly(BufferFactory& bufs, uint32_t poly) { ThreadManager::assert_thread(THREAD_PRE_PROCESS); if (!_polyphonic) { poly = 1; } if (_ports) { for (uint32_t i = 0; i < _ports->size(); ++i) { _ports->at(i)->prepare_poly(bufs, poly); } } return true; } bool BlockImpl::apply_poly(RunContext& ctx, uint32_t poly) { if (!_polyphonic) { poly = 1; } _polyphony = poly; if (_ports) { for (uint32_t i = 0; i < num_ports(); ++i) { _ports->at(i)->apply_poly(ctx, poly); } } return true; } void BlockImpl::set_buffer_size(RunContext& ctx, BufferFactory& bufs, LV2_URID type, uint32_t size) { if (_ports) { for (uint32_t i = 0; i < _ports->size(); ++i) { PortImpl* const p = _ports->at(i); if (p->buffer_type() == type) { p->set_buffer_size(ctx, bufs, size); } } } } PortImpl* BlockImpl::nth_port_by_type(uint32_t n, bool input, PortType type) { uint32_t count = 0; for (uint32_t i = 0; _ports && i < _ports->size(); ++i) { PortImpl* const port = _ports->at(i); if (port->is_input() == input && port->type() == type) { if (count++ == n) { return port; } } } return nullptr; } PortImpl* BlockImpl::port_by_symbol(const char* symbol) { for (uint32_t p = 0; _ports && p < _ports->size(); ++p) { if (_ports->at(p)->symbol() == symbol) { return _ports->at(p); } } return nullptr; } void BlockImpl::pre_process(RunContext& ctx) { // Mix down input ports for (uint32_t i = 0; i < num_ports(); ++i) { PortImpl* const port = _ports->at(i); port->pre_process(ctx); port->connect_buffers(); } } void BlockImpl::bypass(RunContext& ctx) { if (!_ports) { return; } // Prepare port buffers for reading, converting/mixing if necessary for (uint32_t i = 0; i < _ports->size(); ++i) { _ports->at(i)->connect_buffers(); _ports->at(i)->pre_run(ctx); } // Dumb bypass for (const PortType t : { PortType::AUDIO, PortType::CV, PortType::ATOM }) { for (uint32_t i = 0;; ++i) { const PortImpl* in = nth_port_by_type(i, true, t); const PortImpl* out = nth_port_by_type(i, false, t); if (!out) { break; // Finished writing all outputs } if (in) { // Copy corresponding input to output for (uint32_t v = 0; v < _polyphony; ++v) { out->buffer(v)->copy(ctx, in->buffer(v).get()); } } else { // Output but no corresponding input, clear for (uint32_t v = 0; v < _polyphony; ++v) { out->buffer(v)->clear(); } } } } post_process(ctx); } void BlockImpl::process(RunContext& ctx) { pre_process(ctx); if (!_enabled) { bypass(ctx); post_process(ctx); return; } RunContext subcontext(ctx); for (SampleCount offset = 0; offset < ctx.nframes();) { // Find earliest offset of a value change SampleCount chunk_end = ctx.nframes(); for (uint32_t i = 0; _ports && i < _ports->size(); ++i) { const PortImpl* const port = _ports->at(i); if (port->type() == PortType::CONTROL && port->is_input()) { const SampleCount o = port->next_value_offset( offset, ctx.nframes()); if (o < chunk_end) { chunk_end = o; } } } // Slice context into a chunk from now until the next change subcontext.slice(offset, chunk_end - offset); // Prepare port buffers for reading, converting/mixing if necessary for (uint32_t i = 0; _ports && i < _ports->size(); ++i) { _ports->at(i)->connect_buffers(offset); _ports->at(i)->pre_run(subcontext); } // Run the chunk run(subcontext); // Emit control port outputs as events for (uint32_t i = 0; _ports && i < _ports->size(); ++i) { const PortImpl* const port = _ports->at(i); if (port->type() == PortType::CONTROL && port->is_output()) { // TODO: Only emit events when value has actually changed? for (uint32_t v = 0; v < _polyphony; ++v) { port->buffer(v)->append_event(offset, port->buffer(v)->value()); } } } offset = chunk_end; subcontext.slice(offset, chunk_end - offset); } post_process(ctx); } void BlockImpl::post_process(RunContext& ctx) { // Write output ports for (uint32_t i = 0; _ports && i < _ports->size(); ++i) { _ports->at(i)->post_process(ctx); } } void BlockImpl::set_port_buffer(uint32_t, uint32_t, const BufferRef&, SampleCount) { /*std::cout << path() << " set port " << port_num << " voice " << voice << " buffer " << buf << " offset " << offset << std::endl;*/ } } // namespace ingen::server