/*
This file is part of Ingen.
Copyright 2007-2016 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 .
*/
#ifndef INGEN_ENGINE_PORTIMPL_HPP
#define INGEN_ENGINE_PORTIMPL_HPP
#include "BufferFactory.hpp"
#include "BufferRef.hpp"
#include "NodeImpl.hpp"
#include "RunContext.hpp"
#include "server.h"
#include "types.hpp"
#include "ingen/Atom.hpp"
#include "ingen/URIs.hpp"
#include "lv2/urid/urid.h"
#include "raul/Array.hpp"
#include "raul/Maid.hpp"
#include
#include
#include
#include
namespace raul {
class Symbol;
} // namespace raul
namespace ingen {
enum class PortType;
class Properties;
namespace server {
class BlockImpl;
/** 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 INGEN_SERVER_API 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() = default;
void set(const RunContext& ctx, FrameTime t, Sample v) {
time = t;
value = v;
state = (time == ctx.start()
? State::SET
: State::HALF_SET_CYCLE_1);
}
State state = State::SET; ///< State for setting control value
Sample value = 0; ///< Value currently being set
FrameTime time = 0; ///< Time value was set
};
struct Voice {
SetState set_state;
BufferRef buffer{nullptr};
};
using Voices = raul::Array;
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 reinterpret_cast(_parent); }
/** Set the the voices (buffers) for this port in the audio thread. */
void set_voices(RunContext& ctx, raul::managed_ptr&& 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& ctx, 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); }
BufferRef buffer(uint32_t voice) const {
return _voices->at((_poly == 1) ? 0 : voice).buffer;
}
BufferRef prepared_buffer(uint32_t voice) const {
return _prepared_voices->at(voice).buffer;
}
void update_set_state(const RunContext& ctx, uint32_t v);
void set_voice_value(const RunContext& ctx,
uint32_t voice,
FrameTime time,
Sample value);
void set_control_value(const RunContext& ctx,
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& ctx);
virtual void pre_run(RunContext& ctx);
virtual void post_process(RunContext& ctx);
/** 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; }
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& ctx, 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& ctx, bool send_now=false);
BufferFactory& bufs() const { return _bufs; }
BufferRef value_buffer(uint32_t voice) const;
BufferRef user_buffer(RunContext&) const { return _user_buffer; }
void set_user_buffer(RunContext&, BufferRef b)
{
_user_buffer = std::move(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) const;
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:
using GetFn = BufferRef (BufferFactory::*)(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 raul::managed_ptr& 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{0};
float _monitor_value{0.0f};
float _peak{0.0f};
PortType _type;
LV2_URID _buffer_type;
Atom _value;
Atom _min;
Atom _max;
raul::managed_ptr _voices;
raul::managed_ptr _prepared_voices;
BufferRef _user_buffer;
std::atomic_flag _connected_flag{false};
bool _monitored{false};
bool _force_monitor_update{false};
bool _is_morph{false};
bool _is_auto_morph{false};
bool _is_logarithmic{false};
bool _is_sample_rate{false};
bool _is_toggled{false};
bool _is_driver_port{false};
bool _is_output;
};
} // namespace server
} // namespace ingen
#endif // INGEN_ENGINE_PORTIMPL_HPP