diff options
Diffstat (limited to 'src/server/PortImpl.hpp')
-rw-r--r-- | src/server/PortImpl.hpp | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/src/server/PortImpl.hpp b/src/server/PortImpl.hpp new file mode 100644 index 00000000..d9b81a16 --- /dev/null +++ b/src/server/PortImpl.hpp @@ -0,0 +1,312 @@ +/* + 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_ENGINE_PORTIMPL_HPP +#define INGEN_ENGINE_PORTIMPL_HPP + +#include <cstdlib> + +#include "ingen/Atom.hpp" +#include "raul/Array.hpp" + +#include "BufferRef.hpp" +#include "NodeImpl.hpp" +#include "PortType.hpp" +#include "RunContext.hpp" +#include "types.hpp" + +namespace Raul { class Maid; } + +namespace ingen { +namespace server { + +class BlockImpl; +class BufferFactory; +class RunContext; + +/** A port (input or output) on a Block. + * + * The base implementation here is general and/or for output ports (which are + * simplest), InputPort and DuplexPort override functions to provide + * specialized behaviour where necessary. + * + * \ingroup engine + */ +class PortImpl : public NodeImpl +{ +public: + struct SetState { + enum class State { + /// Partially set, first cycle: AAAAA => AAABB. + HALF_SET_CYCLE_1, + + /// Partially set, second cycle: AAABB => BBBBB. + HALF_SET_CYCLE_2, + + /// Fully set, first cycle (clear events if necessary). + SET_CYCLE_1, + + /// Fully set, second cycle and onwards (done). + SET + }; + + SetState() : state(State::SET), value(0), time(0) {} + + void set(const RunContext& context, FrameTime t, Sample v) { + time = t; + value = v; + state = (time == context.start() + ? State::SET + : State::HALF_SET_CYCLE_1); + } + + State state; ///< State of buffer for setting control value + Sample value; ///< Value currently being set + FrameTime time; ///< Time value was set + }; + + struct Voice { + Voice() : buffer(nullptr) {} + + SetState set_state; + BufferRef buffer; + }; + + typedef Raul::Array<Voice> Voices; + + PortImpl(BufferFactory& bufs, + BlockImpl* block, + const Raul::Symbol& name, + uint32_t index, + uint32_t poly, + PortType type, + LV2_URID buffer_type, + const Atom& value, + size_t buffer_size = 0, + bool is_output = true); + + GraphType graph_type() const override { return GraphType::PORT; } + + /** A port's parent is always a block, so static cast should be safe */ + BlockImpl* parent_block() const { return (BlockImpl*)_parent; } + + /** Set the the voices (buffers) for this port in the audio thread. */ + void set_voices(RunContext& context, MPtr<Voices>&& voices); + + /** Prepare for a new (external) polyphony value. + * + * Preprocessor thread, poly is actually applied by apply_poly. + */ + bool prepare_poly(BufferFactory& bufs, uint32_t poly) override; + + /** Apply a new polyphony value. + * + * Audio thread. + * \a poly Must be < the most recent value passed to prepare_poly. + */ + bool apply_poly(RunContext& context, uint32_t poly) override; + + /** Return the number of arcs (pre-process thraed). */ + virtual size_t num_arcs() const { return 0; } + + const Atom& value() const { return _value; } + void set_value(const Atom& v) { _value = v; } + + const Atom& minimum() const { return _min; } + const Atom& maximum() const { return _max; } + + /* The following two methods store the range in variables so it can be + accessed in the process thread, which is required for applying control + bindings from incoming MIDI data. + */ + void set_minimum(const Atom& min) { _min.set_rt(min); } + void set_maximum(const Atom& max) { _max.set_rt(max); } + + inline BufferRef buffer(uint32_t voice) const { + return _voices->at((_poly == 1) ? 0 : voice).buffer; + } + inline BufferRef prepared_buffer(uint32_t voice) const { + return _prepared_voices->at(voice).buffer; + } + + void update_set_state(const RunContext& context, uint32_t v); + + void set_voice_value(const RunContext& context, + uint32_t voice, + FrameTime time, + Sample value); + + void set_control_value(const RunContext& context, + FrameTime time, + Sample value); + + /** Prepare this port to use an external driver-provided buffer. + * + * This will avoid allocating a buffer for the port, instead the driver + * buffer is used directly. This only makes sense for ports on the + * top-level graph, which are monophonic. Non-real-time, must be called + * before using the port, followed by a call to set_driver_buffer() in the + * processing thread. + */ + virtual void set_is_driver_port(BufferFactory& bufs); + + bool is_driver_port() const { return _is_driver_port; } + + /** Called once per process cycle */ + virtual void pre_process(RunContext& context); + virtual void pre_run(RunContext& context) {} + virtual void post_process(RunContext& context); + + /** Clear/silence all buffers */ + virtual void clear_buffers(const RunContext& ctx); + + /** Claim and apply buffers in the real-time thread. */ + virtual bool setup_buffers(RunContext& ctx, BufferFactory& bufs, uint32_t poly); + + void activate(BufferFactory& bufs); + void deactivate(); + + /** + Inherit any properties from a connected neighbour. + + This is used for Graph ports, so e.g. a control input has the range of + all the ports it is connected to. + */ + virtual void inherit_neighbour(const PortImpl* port, + Properties& remove, + Properties& add) {} + + virtual void connect_buffers(SampleCount offset=0); + virtual void recycle_buffers(); + + uint32_t index() const { return _index; } + void set_index(RunContext&, uint32_t index) { _index = index; } + + inline bool is_a(PortType type) const { return _type == type; } + + bool has_value() const; + + PortType type() const { return _type; } + LV2_URID value_type() const { return _value.is_valid() ? _value.type() : 0; } + LV2_URID buffer_type() const { return _buffer_type; } + + bool supports(const URIs::Quark& value_type) const; + + size_t buffer_size() const { return _buffer_size; } + + uint32_t poly() const { + return _poly; + } + uint32_t prepared_poly() const { + return (_prepared_voices) ? _prepared_voices->size() : 1; + } + + void set_buffer_size(RunContext& context, BufferFactory& bufs, size_t size); + + /** Return true iff this port is explicitly monitored. + * + * This is used for plugin UIs which require monitoring for particular + * ports, even if the Ingen client has not requested broadcasting in + * general (e.g. for canvas animation). + */ + bool is_monitored() const { return _monitored; } + + /** Explicitly turn on monitoring for this port. */ + void enable_monitoring(bool monitored) { _monitored = monitored; } + + /** Monitor port value and broadcast to clients periodically. */ + void monitor(RunContext& context, bool send_now=false); + + BufferFactory& bufs() const { return _bufs; } + + BufferRef value_buffer(uint32_t voice); + + BufferRef user_buffer(RunContext&) const { return _user_buffer; } + void set_user_buffer(RunContext&, BufferRef b) { _user_buffer = b; } + + /** Return offset of the first value change after `offset`. */ + virtual SampleCount next_value_offset(SampleCount offset, + SampleCount end) const; + + /** Update value buffer for `voice` to be current as of `offset`. */ + void update_values(SampleCount offset, uint32_t voice); + + void force_monitor_update() { _force_monitor_update = true; } + + void set_morphable(bool is_morph, bool is_auto_morph) { + _is_morph = is_morph; + _is_auto_morph = is_auto_morph; + } + + void set_type(PortType port_type, LV2_URID buffer_type); + + void cache_properties(); + + bool is_input() const { return !_is_output; } + bool is_output() const { return _is_output; } + bool is_morph() const { return _is_morph; } + bool is_auto_morph() const { return _is_auto_morph; } + bool is_logarithmic() const { return _is_logarithmic; } + bool is_sample_rate() const { return _is_sample_rate; } + bool is_toggled() const { return _is_toggled; } + +protected: + typedef BufferRef (BufferFactory::*GetFn)(LV2_URID, LV2_URID, uint32_t); + + /** Set `voices` as the buffers to be used for this port. + * + * This is real-time safe only if `get` is as well, use in the real-time + * thread should pass &BufferFactory::claim_buffer. + * + * @return true iff buffers are locally owned by the port + */ + virtual bool get_buffers(BufferFactory& bufs, + GetFn get, + const MPtr<Voices>& voices, + uint32_t poly, + size_t num_in_arcs) const; + + BufferFactory& _bufs; + uint32_t _index; + uint32_t _poly; + uint32_t _buffer_size; + uint32_t _frames_since_monitor; + float _monitor_value; + float _peak; + PortType _type; + LV2_URID _buffer_type; + Atom _value; + Atom _min; + Atom _max; + MPtr<Voices> _voices; + MPtr<Voices> _prepared_voices; + BufferRef _user_buffer; + std::atomic_flag _connected_flag; + bool _monitored; + bool _force_monitor_update; + bool _is_morph; + bool _is_auto_morph; + bool _is_logarithmic; + bool _is_sample_rate; + bool _is_toggled; + bool _is_driver_port; + bool _is_output; +}; + +} // namespace server +} // namespace ingen + +#endif // INGEN_ENGINE_PORTIMPL_HPP |