summaryrefslogtreecommitdiffstats
path: root/src/server
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2014-08-31 01:24:57 +0000
committerDavid Robillard <d@drobilla.net>2014-08-31 01:24:57 +0000
commita2792bd09212eed55bba1aa30dc09043a6955486 (patch)
treed4262f760d4522bc6f1c99778987aada332b9e7e /src/server
parente3ecb2b439bd03d27b5e11efe430c24f0ebe6283 (diff)
downloadingen-a2792bd09212eed55bba1aa30dc09043a6955486.tar.gz
ingen-a2792bd09212eed55bba1aa30dc09043a6955486.tar.bz2
ingen-a2792bd09212eed55bba1aa30dc09043a6955486.zip
Use float sequences for sample-accurate control ports.
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@5462 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/server')
-rw-r--r--src/server/ArcImpl.cpp25
-rw-r--r--src/server/ArcImpl.hpp2
-rw-r--r--src/server/BlockImpl.cpp46
-rw-r--r--src/server/BlockImpl.hpp14
-rw-r--r--src/server/Buffer.cpp114
-rw-r--r--src/server/Buffer.hpp73
-rw-r--r--src/server/BufferFactory.cpp9
-rw-r--r--src/server/BufferFactory.hpp3
-rw-r--r--src/server/Context.cpp38
-rw-r--r--src/server/Context.hpp20
-rw-r--r--src/server/ControlBindings.cpp1
-rw-r--r--src/server/DuplexPort.cpp16
-rw-r--r--src/server/DuplexPort.hpp3
-rw-r--r--src/server/Event.hpp3
-rw-r--r--src/server/GraphImpl.cpp22
-rw-r--r--src/server/GraphImpl.hpp2
-rw-r--r--src/server/InputPort.cpp103
-rw-r--r--src/server/InputPort.hpp8
-rw-r--r--src/server/LV2Block.cpp28
-rw-r--r--src/server/LV2Block.hpp10
-rw-r--r--src/server/OutputPort.cpp24
-rw-r--r--src/server/OutputPort.hpp7
-rw-r--r--src/server/PortImpl.cpp101
-rw-r--r--src/server/PortImpl.hpp25
-rw-r--r--src/server/ProcessContext.hpp2
-rw-r--r--src/server/internals/Controller.cpp31
-rw-r--r--src/server/internals/Controller.hpp2
-rw-r--r--src/server/internals/Delay.cpp6
-rw-r--r--src/server/internals/Delay.hpp2
-rw-r--r--src/server/internals/Note.cpp28
-rw-r--r--src/server/internals/Note.hpp2
-rw-r--r--src/server/internals/Time.cpp6
-rw-r--r--src/server/internals/Time.hpp2
-rw-r--r--src/server/internals/Trigger.cpp30
-rw-r--r--src/server/internals/Trigger.hpp2
-rw-r--r--src/server/mix.cpp2
36 files changed, 614 insertions, 198 deletions
diff --git a/src/server/ArcImpl.cpp b/src/server/ArcImpl.cpp
index 8b8a23b4..e53f1880 100644
--- a/src/server/ArcImpl.cpp
+++ b/src/server/ArcImpl.cpp
@@ -55,21 +55,31 @@ ArcImpl::head_path() const
}
BufferRef
-ArcImpl::buffer(uint32_t voice) const
+ArcImpl::buffer(uint32_t voice, SampleCount offset) const
{
- assert(!must_mix());
assert(_tail->poly() == 1 || _tail->poly() > voice);
if (_tail->poly() == 1) {
- return _tail->buffer(0);
- } else {
- return _tail->buffer(voice);
+ voice = 0;
}
+
+ if (_tail->buffer(0)->is_sequence()) {
+ if (_head->type() == PortType::CONTROL) {
+ _tail->update_values(offset); // Update value buffer
+ return _tail->value_buffer(voice); // Return value buffer
+ } else if (_head->type() == PortType::CV) {
+ _tail->update_values(offset); // Update initial value
+ // Return full tail buffer below
+ }
+ }
+
+ return _tail->buffer(voice);
}
bool
ArcImpl::must_mix() const
{
- return _tail->poly() > _head->poly();
+ return (_tail->poly() > _head->poly() ||
+ (_tail->buffer(0)->is_sequence() != _head->buffer(0)->is_sequence()));
}
bool
@@ -98,6 +108,9 @@ ArcImpl::can_connect(const OutputPort* src, const InputPort* dst)
// atom:Float Value => Control
|| (src->supports(uris.atom_Float) && dst->is_a(PortType::ID::CONTROL))
+ // atom:Float Value => CV
+ || (src->supports(uris.atom_Float) && dst->is_a(PortType::ID::CV))
+
// atom:Sound Value => Audio
|| (src->supports(uris.atom_Sound) && dst->is_a(PortType::ID::AUDIO)));
}
diff --git a/src/server/ArcImpl.hpp b/src/server/ArcImpl.hpp
index 9ea103a5..63ed8430 100644
--- a/src/server/ArcImpl.hpp
+++ b/src/server/ArcImpl.hpp
@@ -67,7 +67,7 @@ public:
* buffer, and will return accordingly (e.g. the same buffer for every
* voice in a mono->poly arc).
*/
- BufferRef buffer(uint32_t voice) const;
+ BufferRef buffer(uint32_t voice, SampleCount offset=0) const;
/** Whether this arc must mix down voices into a local buffer */
bool must_mix() const;
diff --git a/src/server/BlockImpl.cpp b/src/server/BlockImpl.cpp
index 0cae55a2..069ecdfa 100644
--- a/src/server/BlockImpl.cpp
+++ b/src/server/BlockImpl.cpp
@@ -156,6 +156,45 @@ BlockImpl::pre_process(ProcessContext& context)
}
void
+BlockImpl::process(ProcessContext& context)
+{
+ pre_process(context);
+
+ ProcessContext subcontext(context);
+ for (SampleCount offset = 0; offset < context.nframes();) {
+ // Find earliest offset of a value change
+ SampleCount chunk_end = context.nframes();
+ for (uint32_t i = 0; _ports && i < _ports->size(); ++i) {
+ PortImpl* const port = _ports->at(i);
+ if (port->type() == PortType::CONTROL && port->is_input()) {
+ const SampleCount o = port->next_value_offset(
+ offset, context.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);
+
+ offset = chunk_end;
+ subcontext.slice(offset, chunk_end - offset);
+ }
+
+ post_process(context);
+}
+
+void
BlockImpl::post_process(ProcessContext& context)
{
// Write output ports
@@ -165,9 +204,10 @@ BlockImpl::post_process(ProcessContext& context)
}
void
-BlockImpl::set_port_buffer(uint32_t voice,
- uint32_t port_num,
- BufferRef buf)
+BlockImpl::set_port_buffer(uint32_t voice,
+ uint32_t port_num,
+ BufferRef buf,
+ SampleCount offset)
{
/*std::cout << path() << " set port " << port_num << " voice " << voice
<< " buffer " << buf << " offset " << offset << std::endl;*/
diff --git a/src/server/BlockImpl.hpp b/src/server/BlockImpl.hpp
index cfffcb96..58ced1fe 100644
--- a/src/server/BlockImpl.hpp
+++ b/src/server/BlockImpl.hpp
@@ -92,16 +92,20 @@ public:
/** Do whatever needs doing in the process thread before process() is called */
virtual void pre_process(ProcessContext& context);
- /** Run for the block of time specified by `context`. */
- virtual void process(ProcessContext& context) = 0;
+ /** Run block for an entire process cycle (calls run()). */
+ virtual void process(ProcessContext& context);
+
+ /** Run block for a portion of process cycle (called from process()). */
+ virtual void run(ProcessContext& context) = 0;
/** Do whatever needs doing in the process thread after process() is called */
virtual void post_process(ProcessContext& context);
/** Set the buffer of a port to a given buffer (e.g. connect plugin to buffer) */
- virtual void set_port_buffer(uint32_t voice,
- uint32_t port_num,
- BufferRef buf);
+ virtual void set_port_buffer(uint32_t voice,
+ uint32_t port_num,
+ BufferRef buf,
+ SampleCount offset);
virtual Node* port(uint32_t index) const;
virtual PortImpl* port_impl(uint32_t index) const { return (*_ports)[index]; }
diff --git a/src/server/Buffer.cpp b/src/server/Buffer.cpp
index ebab53fd..ac77be63 100644
--- a/src/server/Buffer.cpp
+++ b/src/server/Buffer.cpp
@@ -39,10 +39,15 @@
namespace Ingen {
namespace Server {
-Buffer::Buffer(BufferFactory& bufs, LV2_URID type, uint32_t capacity)
+Buffer::Buffer(BufferFactory& bufs,
+ LV2_URID type,
+ LV2_URID value_type,
+ uint32_t capacity)
: _factory(bufs)
, _type(type)
+ , _value_type(value_type)
, _capacity(capacity)
+ , _latest_event(0)
, _next(NULL)
, _refs(0)
{
@@ -69,6 +74,10 @@ Buffer::Buffer(BufferFactory& bufs, LV2_URID type, uint32_t capacity)
vec->body.child_type = bufs.uris().atom_Float;
}
+ if (type == bufs.uris().atom_Sequence && value_type) {
+ _value_buffer = bufs.get_buffer(value_type, 0, 0, false);
+ }
+
clear();
}
@@ -84,6 +93,15 @@ Buffer::recycle()
}
void
+Buffer::set_type(LV2_URID type, LV2_URID value_type)
+{
+ _type = type;
+ if (type == _factory.uris().atom_Sequence && value_type) {
+ _value_buffer = _factory.get_buffer(value_type, 0, 0, false);
+ }
+}
+
+void
Buffer::clear()
{
if (is_audio() || is_control()) {
@@ -95,18 +113,43 @@ Buffer::clear()
_atom->size = sizeof(LV2_Atom_Sequence_Body);
seq->body.unit = 0;
seq->body.pad = 0;
+ _latest_event = 0;
}
}
void
+Buffer::render_sequence(const Context& context, const Buffer* src, bool add)
+{
+ const LV2_URID atom_Float = _factory.uris().atom_Float;
+ const LV2_Atom_Sequence* seq = (const LV2_Atom_Sequence*)src->atom();
+ const LV2_Atom_Float* init = (const LV2_Atom_Float*)src->value();
+ float value = init ? init->body : 0.0f;
+ SampleCount offset = context.offset();
+ LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
+ if (ev->time.frames >= offset && ev->body.type == atom_Float) {
+ write_block(value, offset, ev->time.frames, add);
+ value = ((const LV2_Atom_Float*)&ev->body)->body;
+ offset = ev->time.frames;
+ }
+ }
+ write_block(value, offset, context.offset() + context.nframes(), add);
+}
+
+void
Buffer::copy(const Context& context, const Buffer* src)
{
if (_type == src->type() && src->_atom->size + sizeof(LV2_Atom) <= _capacity) {
memcpy(_atom, src->_atom, sizeof(LV2_Atom) + src->_atom->size);
+ if (value() && src->value()) {
+ memcpy(value(), src->value(), lv2_atom_total_size(src->value()));
+ }
} else if (src->is_audio() && is_control()) {
samples()[0] = src->samples()[0];
} else if (src->is_control() && is_audio()) {
set_block(src->samples()[0], 0, context.nframes());
+ } else if (src->is_sequence() && is_audio() &&
+ src->value_type() == _factory.uris().atom_Float) {
+ render_sequence(context, src, false);
} else {
clear();
}
@@ -121,7 +164,7 @@ Buffer::resize(uint32_t capacity)
}
void*
-Buffer::port_data(PortType port_type)
+Buffer::port_data(PortType port_type, SampleCount offset)
{
switch (port_type.id()) {
case PortType::ID::CONTROL:
@@ -130,7 +173,7 @@ Buffer::port_data(PortType port_type)
if (_atom->type == _factory.uris().atom_Float) {
return (float*)LV2_ATOM_BODY(_atom);
} else if (_atom->type == _factory.uris().atom_Sound) {
- return (float*)LV2_ATOM_CONTENTS(LV2_Atom_Vector, _atom);
+ return (float*)LV2_ATOM_CONTENTS(LV2_Atom_Vector, _atom) + offset;
}
break;
default:
@@ -140,10 +183,10 @@ Buffer::port_data(PortType port_type)
}
const void*
-Buffer::port_data(PortType port_type) const
+Buffer::port_data(PortType port_type, SampleCount offset) const
{
return const_cast<void*>(
- const_cast<Buffer*>(this)->port_data(port_type));
+ const_cast<Buffer*>(this)->port_data(port_type, offset));
}
#ifdef __SSE__
@@ -203,7 +246,9 @@ void
Buffer::prepare_write(Context& context)
{
if (_type == _factory.uris().atom_Sequence) {
- _atom->size = sizeof(LV2_Atom_Sequence_Body);
+ _atom->type = (LV2_URID)_factory.uris().atom_Sequence;
+ _atom->size = sizeof(LV2_Atom_Sequence_Body);
+ _latest_event = 0;
}
}
@@ -211,8 +256,9 @@ void
Buffer::prepare_output_write(Context& context)
{
if (_type == _factory.uris().atom_Sequence) {
- _atom->type = (LV2_URID)_factory.uris().atom_Chunk;
- _atom->size = _capacity - sizeof(LV2_Atom);
+ _atom->type = (LV2_URID)_factory.uris().atom_Chunk;
+ _atom->size = _capacity - sizeof(LV2_Atom);
+ _latest_event = 0;
}
}
@@ -222,6 +268,12 @@ Buffer::append_event(int64_t frames,
uint32_t type,
const uint8_t* data)
{
+ assert(frames >= _latest_event);
+ if (_atom->type == _factory.uris().atom_Chunk) {
+ // Chunk initialized with prepare_output_write(), clear
+ clear();
+ }
+
if (sizeof(LV2_Atom) + _atom->size + lv2_atom_pad_size(size) > _capacity) {
return false;
}
@@ -237,9 +289,55 @@ Buffer::append_event(int64_t frames,
_atom->size += sizeof(LV2_Atom_Event) + lv2_atom_pad_size(size);
+ _latest_event = frames;
+
return true;
}
+SampleCount
+Buffer::next_value_offset(SampleCount offset, SampleCount end) const
+{
+ SampleCount earliest = end;
+ LV2_ATOM_SEQUENCE_FOREACH((LV2_Atom_Sequence*)_atom, ev) {
+ if (ev->time.frames > offset &&
+ ev->time.frames < earliest &&
+ ev->body.type == _value_type) {
+ earliest = ev->time.frames;
+ break;
+ }
+ }
+ return earliest;
+}
+
+const LV2_Atom*
+Buffer::value() const
+{
+ return _value_buffer ? _value_buffer->atom() : NULL;
+}
+
+LV2_Atom*
+Buffer::value()
+{
+ return _value_buffer ? _value_buffer->atom() : NULL;
+}
+
+void
+Buffer::update_value_buffer(SampleCount offset)
+{
+ if (!_value_buffer) {
+ return;
+ }
+
+ LV2_ATOM_SEQUENCE_FOREACH((LV2_Atom_Sequence*)_atom, ev) {
+ if (ev->time.frames <= offset && ev->body.type == _value_type) {
+ memcpy(_value_buffer->atom(),
+ &ev->body,
+ lv2_atom_total_size(&ev->body));
+ break;
+ }
+ }
+}
+
void
intrusive_ptr_add_ref(Buffer* b)
{
diff --git a/src/server/Buffer.hpp b/src/server/Buffer.hpp
index 14984de4..cf36bc21 100644
--- a/src/server/Buffer.hpp
+++ b/src/server/Buffer.hpp
@@ -41,20 +41,24 @@ class BufferFactory;
class Buffer : public boost::noncopyable
{
public:
- Buffer(BufferFactory& bufs, LV2_URID type, uint32_t capacity);
+ Buffer(BufferFactory& bufs,
+ LV2_URID type,
+ LV2_URID value_type,
+ uint32_t capacity);
void clear();
void resize(uint32_t size);
void copy(const Context& context, const Buffer* src);
void prepare_write(Context& context);
- void* port_data(PortType port_type);
- const void* port_data(PortType port_type) const;
+ void* port_data(PortType port_type, SampleCount offset);
+ const void* port_data(PortType port_type, SampleCount offset) const;
- inline LV2_URID type() const { return _type; }
- inline uint32_t capacity() const { return _capacity; }
+ inline LV2_URID type() const { return _type; }
+ inline LV2_URID value_type() const { return _value_type; }
+ inline uint32_t capacity() const { return _capacity; }
- inline void set_type(LV2_URID t) { _type = t; }
+ void set_type(LV2_URID type, LV2_URID value_type);
inline bool is_audio() const {
return _type == _factory.uris().atom_Sound;
@@ -68,6 +72,12 @@ public:
return _type == _factory.uris().atom_Sequence;
}
+ inline bool empty() const {
+ return (_atom->type != _type ||
+ (_type == _factory.uris().atom_Sequence &&
+ _atom->size <= sizeof(LV2_Atom_Sequence_Body)));
+ }
+
/// Audio buffers only
inline const Sample* samples() const {
if (is_control()) {
@@ -98,18 +108,21 @@ public:
return 0;
}
- /// Audio buffers only
+ /// Numeric buffers only
inline Sample value_at(SampleCount offset) const {
if (is_audio() || is_control()) {
return samples()[offset];
+ } else if (_value_buffer) {
+ return ((LV2_Atom_Float*)value())->body;
}
return 0.0f;
}
- inline void set_block(Sample val,
+ inline void set_block(const Sample val,
const SampleCount start,
const SampleCount end)
{
+ assert(is_audio() || is_control());
assert(end <= nframes());
// Note: Do not change this without ensuring GCC can still vectorize it
Sample* const buf = samples() + start;
@@ -118,6 +131,30 @@ public:
}
}
+ inline void add_block(const Sample val,
+ const SampleCount start,
+ const SampleCount end)
+ {
+ assert(is_audio() || is_control());
+ assert(end <= nframes());
+ // Note: Do not change this without ensuring GCC can still vectorize it
+ Sample* const buf = samples() + start;
+ for (SampleCount i = 0; i < (end - start); ++i) {
+ buf[i] += val;
+ }
+ }
+
+ inline void write_block(const Sample val,
+ const SampleCount start,
+ const SampleCount end,
+ const bool add)
+ {
+ if (add) {
+ add_block(val, start, end);
+ }
+ set_block(val, start, end);
+ }
+
/// Audio buffers only
float peak(const Context& context) const;
@@ -130,6 +167,22 @@ public:
uint32_t type,
const uint8_t* data);
+ /// Value buffer for numeric sequences
+ BufferRef value_buffer() { return _value_buffer; }
+ const BufferRef value_buffer() const { return _value_buffer; }
+
+ const LV2_Atom* value() const;
+ LV2_Atom* value();
+
+ /// Return offset of the first value change after `offset`.
+ SampleCount next_value_offset(SampleCount offset, SampleCount end) const;
+
+ /// Update value buffer to value as of offset
+ void update_value_buffer(SampleCount offset);
+
+ /// Set/add to audio buffer from the Sequence of Float in `src`
+ void render_sequence(const Context& context, const Buffer* src, bool add);
+
LV2_Atom* atom() { return _atom; }
const LV2_Atom* atom() const { return _atom; }
@@ -147,7 +200,11 @@ protected:
BufferFactory& _factory;
LV2_Atom* _atom;
LV2_URID _type;
+ LV2_URID _value_type;
uint32_t _capacity;
+ int64_t _latest_event;
+
+ BufferRef _value_buffer; ///< Value buffer for numeric sequences
friend class BufferFactory;
~Buffer();
diff --git a/src/server/BufferFactory.cpp b/src/server/BufferFactory.cpp
index 6390a36b..88ec195b 100644
--- a/src/server/BufferFactory.cpp
+++ b/src/server/BufferFactory.cpp
@@ -97,6 +97,7 @@ BufferFactory::default_size(LV2_URID type) const
BufferRef
BufferFactory::get_buffer(LV2_URID type,
+ LV2_URID value_type,
uint32_t capacity,
bool real_time,
bool force_create)
@@ -116,7 +117,7 @@ BufferFactory::get_buffer(LV2_URID type,
if (!try_head) {
if (!real_time) {
- return create(type, capacity);
+ return create(type, value_type, capacity);
} else {
_engine.world()->log().error("Failed to obtain buffer");
return BufferRef();
@@ -124,7 +125,7 @@ BufferFactory::get_buffer(LV2_URID type,
}
try_head->_next = NULL;
- try_head->set_type(type);
+ try_head->set_type(type, value_type);
return BufferRef(try_head);
}
@@ -135,7 +136,7 @@ BufferFactory::silent_buffer()
}
BufferRef
-BufferFactory::create(LV2_URID type, uint32_t capacity)
+BufferFactory::create(LV2_URID type, LV2_URID value_type, uint32_t capacity)
{
if (capacity == 0) {
capacity = default_size(type);
@@ -145,7 +146,7 @@ BufferFactory::create(LV2_URID type, uint32_t capacity)
capacity = std::max(capacity, default_size(_uris.atom_Sound));
}
- return BufferRef(new Buffer(*this, type, capacity));
+ return BufferRef(new Buffer(*this, type, value_type, capacity));
}
void
diff --git a/src/server/BufferFactory.hpp b/src/server/BufferFactory.hpp
index 54c02c77..662c8149 100644
--- a/src/server/BufferFactory.hpp
+++ b/src/server/BufferFactory.hpp
@@ -48,6 +48,7 @@ public:
uint32_t default_size(LV2_URID type) const;
BufferRef get_buffer(LV2_URID type,
+ LV2_URID value_type,
uint32_t capacity,
bool real_time,
bool force_create = false);
@@ -65,7 +66,7 @@ private:
friend class Buffer;
void recycle(Buffer* buf);
- BufferRef create(LV2_URID type, uint32_t capacity=0);
+ BufferRef create(LV2_URID type, LV2_URID value_type, uint32_t capacity=0);
inline std::atomic<Buffer*>& free_list(LV2_URID type) {
if (type == _uris.atom_Float) {
diff --git a/src/server/Context.cpp b/src/server/Context.cpp
index 67a247e1..ecf078e1 100644
--- a/src/server/Context.cpp
+++ b/src/server/Context.cpp
@@ -47,13 +47,35 @@ struct Notification
Context::Context(Engine& engine, ID id)
: _engine(engine)
, _id(id)
- , _event_sink(engine.event_queue_size() * sizeof(Notification))
+ , _event_sink(
+ new Raul::RingBuffer(engine.event_queue_size() * sizeof(Notification)))
, _start(0)
, _end(0)
+ , _offset(0)
, _nframes(0)
, _realtime(true)
+ , _copy(false)
{}
+Context::Context(const Context& copy)
+ : _engine(copy._engine)
+ , _id(copy._id)
+ , _event_sink(copy._event_sink)
+ , _start(copy._start)
+ , _end(copy._end)
+ , _offset(copy._offset)
+ , _nframes(copy._nframes)
+ , _realtime(copy._realtime)
+ , _copy(true)
+{}
+
+Context::~Context()
+{
+ if (!_copy) {
+ delete _event_sink;
+ }
+}
+
bool
Context::must_notify(const PortImpl* port) const
{
@@ -69,12 +91,12 @@ Context::notify(LV2_URID key,
const void* body)
{
const Notification n(port, time, key, size, type);
- if (_event_sink.write_space() < sizeof(n) + size) {
+ if (_event_sink->write_space() < sizeof(n) + size) {
return false;
}
- if (_event_sink.write(sizeof(n), &n) != sizeof(n)) {
+ if (_event_sink->write(sizeof(n), &n) != sizeof(n)) {
_engine.log().error("Error writing header to notification ring\n");
- } else if (_event_sink.write(size, body) != size) {
+ } else if (_event_sink->write(size, body) != size) {
_engine.log().error("Error writing body to notification ring\n");
} else {
return true;
@@ -86,17 +108,17 @@ void
Context::emit_notifications(FrameTime end)
{
const URIs& uris = _engine.buffer_factory()->uris();
- const uint32_t read_space = _event_sink.read_space();
+ const uint32_t read_space = _event_sink->read_space();
Notification note;
for (uint32_t i = 0; i < read_space; i += sizeof(note)) {
- if (_event_sink.peek(sizeof(note), &note) != sizeof(note) ||
+ if (_event_sink->peek(sizeof(note), &note) != sizeof(note) ||
note.time >= end) {
return;
}
- if (_event_sink.read(sizeof(note), &note) == sizeof(note)) {
+ if (_event_sink->read(sizeof(note), &note) == sizeof(note)) {
Atom value = _engine.world()->forge().alloc(
note.size, note.type, NULL);
- if (_event_sink.read(note.size, value.get_body()) == note.size) {
+ if (_event_sink->read(note.size, value.get_body()) == note.size) {
i += note.size;
const char* key = _engine.world()->uri_map().unmap_uri(note.key);
if (key) {
diff --git a/src/server/Context.hpp b/src/server/Context.hpp
index bea43943..a11a5e8c 100644
--- a/src/server/Context.hpp
+++ b/src/server/Context.hpp
@@ -50,8 +50,9 @@ public:
};
Context(Engine& engine, ID id);
+ Context(const Context& copy);
- virtual ~Context() {}
+ virtual ~Context();
/** Return true iff the given port should broadcast its value.
*
@@ -75,7 +76,7 @@ public:
void emit_notifications(FrameTime end);
/** Return true iff any notifications are pending. */
- bool pending_notifications() const { return _event_sink.read_space(); }
+ bool pending_notifications() const { return _event_sink->read_space(); }
inline ID id() const { return _id; }
@@ -91,22 +92,33 @@ public:
_nframes = other._nframes;
}
+ inline void slice(SampleCount offset, SampleCount nframes) {
+ _offset = offset;
+ _nframes = nframes;
+ }
+
inline Engine& engine() const { return _engine; }
inline FrameTime start() const { return _start; }
+ inline FrameTime time() const { return _start + _offset; }
inline FrameTime end() const { return _end; }
+ inline SampleCount offset() const { return _offset; }
inline SampleCount nframes() const { return _nframes; }
inline bool realtime() const { return _realtime; }
protected:
+ const Context& operator=(const Context& copy) = delete;
+
Engine& _engine; ///< Engine we're running in
ID _id; ///< Fast ID for this context
- Raul::RingBuffer _event_sink; ///< Port updates from process context
+ Raul::RingBuffer* _event_sink; ///< Port updates from process context
FrameTime _start; ///< Start frame of this cycle, timeline relative
FrameTime _end; ///< End frame of this cycle, timeline relative
- SampleCount _nframes; ///< Length of this cycle in frames
+ SampleCount _offset; ///< Offset into data buffers
+ SampleCount _nframes; ///< Number of frames past offset to process
bool _realtime; ///< True iff context is hard realtime
+ bool _copy; ///< True iff this is a copy (shared event_sink)
};
} // namespace Server
diff --git a/src/server/ControlBindings.cpp b/src/server/ControlBindings.cpp
index 3c6d26df..6ffb5c7d 100644
--- a/src/server/ControlBindings.cpp
+++ b/src/server/ControlBindings.cpp
@@ -42,6 +42,7 @@ ControlBindings::ControlBindings(Engine& engine)
, _bindings(new Bindings())
, _feedback(new Buffer(*_engine.buffer_factory(),
engine.world()->uris().atom_Sequence,
+ 0,
4096)) // FIXME: capacity?
{
lv2_atom_forge_init(
diff --git a/src/server/DuplexPort.cpp b/src/server/DuplexPort.cpp
index 20f8f1b7..0713c35a 100644
--- a/src/server/DuplexPort.cpp
+++ b/src/server/DuplexPort.cpp
@@ -49,11 +49,10 @@ DuplexPort::DuplexPort(BufferFactory& bufs,
}
// Set default control range
- if (!is_output && (type == PortType::CONTROL || type == PortType::CV)) {
+ if (!is_output && value.type() == bufs.uris().atom_Float) {
set_property(bufs.uris().lv2_minimum, bufs.forge().make(0.0f));
set_property(bufs.uris().lv2_maximum, bufs.forge().make(1.0f));
}
-
}
DuplexPort::~DuplexPort()
@@ -148,10 +147,23 @@ DuplexPort::post_process(Context& context)
perspective. Mix down input delivered by plugins so output
(external perspective) is ready. */
InputPort::pre_process(context);
+ InputPort::pre_run(context);
} else {
monitor(context);
}
}
+SampleCount
+DuplexPort::next_value_offset(SampleCount offset, SampleCount end) const
+{
+ return OutputPort::next_value_offset(offset, end);
+}
+
+void
+DuplexPort::update_values(SampleCount offset)
+{
+ return OutputPort::update_values(offset);
+}
+
} // namespace Server
} // namespace Ingen
diff --git a/src/server/DuplexPort.hpp b/src/server/DuplexPort.hpp
index ba9658fb..d231cebd 100644
--- a/src/server/DuplexPort.hpp
+++ b/src/server/DuplexPort.hpp
@@ -72,6 +72,9 @@ public:
void pre_process(Context& context);
void post_process(Context& context);
+ SampleCount next_value_offset(SampleCount offset, SampleCount end) const;
+ void update_values(SampleCount offset);
+
bool is_input() const { return !_is_output; }
bool is_output() const { return _is_output; }
diff --git a/src/server/Event.hpp b/src/server/Event.hpp
index 29a20c18..8ffbbe0a 100644
--- a/src/server/Event.hpp
+++ b/src/server/Event.hpp
@@ -105,9 +105,8 @@ protected:
}
inline bool pre_process_done(Status st, const Raul::URI& subject) {
- _status = st;
_err_subject = subject;
- return st == Status::SUCCESS;
+ return pre_process_done(st);
}
inline bool pre_process_done(Status st, const Raul::Path& subject) {
diff --git a/src/server/GraphImpl.cpp b/src/server/GraphImpl.cpp
index 2bb99dc8..a8a44cf1 100644
--- a/src/server/GraphImpl.cpp
+++ b/src/server/GraphImpl.cpp
@@ -143,21 +143,37 @@ GraphImpl::apply_internal_poly(ProcessContext& context,
}
void
+GraphImpl::pre_process(ProcessContext& context)
+{
+ // Mix down input ports and connect buffers
+ for (uint32_t i = 0; i < num_ports(); ++i) {
+ PortImpl* const port = _ports->at(i);
+ port->pre_process(context);
+ port->pre_run(context);
+ port->connect_buffers();
+ }
+}
+
+void
GraphImpl::process(ProcessContext& context)
{
if (!_process)
return;
- BlockImpl::pre_process(context);
+ pre_process(context);
+ run(context);
+ post_process(context);
+}
+void
+GraphImpl::run(ProcessContext& context)
+{
if (_compiled_graph && _compiled_graph->size() > 0) {
// Run all blocks
for (size_t i = 0; i < _compiled_graph->size(); ++i) {
(*_compiled_graph)[i].block()->process(context);
}
}
-
- BlockImpl::post_process(context);
}
void
diff --git a/src/server/GraphImpl.hpp b/src/server/GraphImpl.hpp
index 0aba78bd..61bbdba4 100644
--- a/src/server/GraphImpl.hpp
+++ b/src/server/GraphImpl.hpp
@@ -63,7 +63,9 @@ public:
void activate(BufferFactory& bufs);
void deactivate();
+ void pre_process(ProcessContext& context);
void process(ProcessContext& context);
+ void run(ProcessContext& context);
void set_buffer_size(Context& context,
BufferFactory& bufs,
diff --git a/src/server/InputPort.cpp b/src/server/InputPort.cpp
index e0406f05..3e345f8d 100644
--- a/src/server/InputPort.cpp
+++ b/src/server/InputPort.cpp
@@ -75,6 +75,10 @@ InputPort::get_buffers(BufferFactory& bufs,
{
const size_t num_arcs = real_time ? _arcs.size() : _num_arcs;
+ if (is_a(PortType::ATOM) && !_value.is_valid()) {
+ poly = 1;
+ }
+
if (is_a(PortType::AUDIO) && num_arcs == 0) {
// Audio input with no arcs, use shared zero buffer
for (uint32_t v = 0; v < poly; ++v) {
@@ -97,7 +101,8 @@ InputPort::get_buffers(BufferFactory& bufs,
// Otherwise, allocate local buffers
for (uint32_t v = 0; v < poly; ++v) {
voices->at(v).buffer.reset();
- voices->at(v).buffer = bufs.get_buffer(buffer_type(), _buffer_size, real_time);
+ voices->at(v).buffer = bufs.get_buffer(
+ buffer_type(), _value.type(), _buffer_size, real_time);
voices->at(v).buffer->clear();
}
return true;
@@ -135,34 +140,16 @@ InputPort::max_tail_poly(Context& context) const
return parent_block()->parent_graph()->internal_poly_process();
}
-static void
-get_sources(const Context& context,
- const ArcImpl& arc,
- uint32_t voice,
- const Buffer** srcs,
- uint32_t max_num_srcs,
- uint32_t& num_srcs)
-{
- if (arc.must_mix()) {
- // Mixing down voices: all tail voices => one head voice
- for (uint32_t v = 0; v < arc.tail()->poly(); ++v) {
- assert(num_srcs < max_num_srcs);
- srcs[num_srcs++] = arc.tail()->buffer(v).get();
- }
- } else {
- // Matching polyphony: each tail voice => corresponding head voice
- assert(arc.tail()->poly() == arc.head()->poly());
- assert(num_srcs < max_num_srcs);
- srcs[num_srcs++] = arc.tail()->buffer(voice).get();
- }
-}
-
void
InputPort::pre_process(Context& context)
{
- // If value has been set (e.g. events pushed) by the user, don't smash it
- if (_set_by_user)
+ if (_set_by_user) {
+ // Value has been set (e.g. events pushed) by the user, don't smash it
+ for (uint32_t v = 0; v < _poly; ++v) {
+ buffer(v)->update_value_buffer(context.offset());
+ }
return;
+ }
if (_arcs.empty()) {
for (uint32_t v = 0; v < _poly; ++v) {
@@ -173,30 +160,74 @@ InputPort::pre_process(Context& context)
_voices->at(v).buffer = _arcs.front().buffer(v);
}
} else {
- const uint32_t src_poly = max_tail_poly(context);
- const uint32_t max_num_srcs = _arcs.size() * src_poly;
+ // Mix down to local buffers in pre_run()
+ for (uint32_t v = 0; v < _poly; ++v) {
+ buffer(v)->prepare_write(context);
+ }
+ }
+}
+
+void
+InputPort::pre_run(Context& context)
+{
+ if (!_set_by_user && !_arcs.empty() && !direct_connect()) {
+ const uint32_t src_poly = max_tail_poly(context);
+ const uint32_t max_n_srcs = _arcs.size() * src_poly;
- const Buffer* srcs[max_num_srcs];
for (uint32_t v = 0; v < _poly; ++v) {
- // Get all the sources for this voice
- uint32_t num_srcs = 0;
- for (const auto& a : _arcs) {
- get_sources(context, a, v, srcs, max_num_srcs, num_srcs);
+ // Get all sources for this voice
+ const Buffer* srcs[max_n_srcs];
+ uint32_t n_srcs = 0;
+ for (const auto& arc : _arcs) {
+ if (_poly == 1) {
+ // P -> 1 or 1 -> 1: all tail voices => each head voice
+ for (uint32_t w = 0; w < arc.tail()->poly(); ++w) {
+ assert(n_srcs < max_n_srcs);
+ srcs[n_srcs++] = arc.buffer(w, context.offset()).get();
+ assert(srcs[n_srcs - 1]);
+ }
+ } else {
+ // P -> P or 1 -> P: tail voice => corresponding head voice
+ assert(n_srcs < max_n_srcs);
+ srcs[n_srcs++] = arc.buffer(v, context.offset()).get();
+ assert(srcs[n_srcs - 1]);
+ }
}
- // Then mix them into out buffer for this voice
- mix(context, buffer(v).get(), srcs, num_srcs);
+ // Then mix them into our buffer for this voice
+ mix(context, buffer(v).get(), srcs, n_srcs);
}
}
+}
- if (!_arcs.empty()) {
- monitor(context);
+SampleCount
+InputPort::next_value_offset(SampleCount offset, SampleCount end) const
+{
+ SampleCount earliest = end;
+ for (const auto& arc : _arcs) {
+ if (arc.tail()->type() != this->type()) {
+ const SampleCount o = arc.tail()->next_value_offset(offset, end);
+ if (o < earliest) {
+ earliest = o;
+ }
+ }
}
+ return earliest;
+}
+
+void
+InputPort::update_values(SampleCount offset)
+{
}
void
InputPort::post_process(Context& context)
{
+ if (!_arcs.empty() || _force_monitor_update) {
+ monitor(context, _force_monitor_update);
+ _force_monitor_update = false;
+ }
+
if (_set_by_user) {
if (_buffer_type == _bufs.uris().atom_Sequence) {
// Clear events received via a SetPortValue
diff --git a/src/server/InputPort.hpp b/src/server/InputPort.hpp
index 12d28b04..d3bcd0be 100644
--- a/src/server/InputPort.hpp
+++ b/src/server/InputPort.hpp
@@ -96,12 +96,18 @@ public:
uint32_t poly,
bool real_time) const;
- /** Prepare buffer for access, mixing if necessary. */
+ /** Set up buffer pointers. */
void pre_process(Context& context);
+ /** Prepare buffer for access, mixing if necessary. */
+ void pre_run(Context& context);
+
/** Prepare buffer for next process cycle. */
void post_process(Context& context);
+ SampleCount next_value_offset(SampleCount offset, SampleCount end) const;
+ void update_values(SampleCount offset);
+
size_t num_arcs() const { return _num_arcs; } ///< Pre-process thread
void increment_num_arcs() { ++_num_arcs; }
void decrement_num_arcs() { --_num_arcs; }
diff --git a/src/server/LV2Block.cpp b/src/server/LV2Block.cpp
index 952b1d7e..deafdcfe 100644
--- a/src/server/LV2Block.cpp
+++ b/src/server/LV2Block.cpp
@@ -351,7 +351,7 @@ LV2Block::instantiate(BufferFactory& bufs)
break;
}
- if (!val.type() && port_type != PortType::ATOM) {
+ if (!val.type()) {
val = forge.make(isnan(def_values[j]) ? 0.0f : def_values[j]);
}
@@ -473,12 +473,16 @@ LV2Block::work(uint32_t size, const void* data)
}
void
-LV2Block::process(ProcessContext& context)
+LV2Block::run(ProcessContext& context)
{
- BlockImpl::pre_process(context);
-
for (uint32_t i = 0; i < _polyphony; ++i)
lilv_instance_run(instance(i), context.nframes());
+}
+
+void
+LV2Block::post_process(ProcessContext& context)
+{
+ BlockImpl::post_process(context);
if (_worker_iface) {
LV2_Handle inst = lilv_instance_get_handle(instance(0));
@@ -493,19 +497,19 @@ LV2Block::process(ProcessContext& context)
_worker_iface->end_run(inst);
}
}
-
- BlockImpl::post_process(context);
}
void
-LV2Block::set_port_buffer(uint32_t voice,
- uint32_t port_num,
- BufferRef buf)
+LV2Block::set_port_buffer(uint32_t voice,
+ uint32_t port_num,
+ BufferRef buf,
+ SampleCount offset)
{
- BlockImpl::set_port_buffer(voice, port_num, buf);
+ BlockImpl::set_port_buffer(voice, port_num, buf, offset);
lilv_instance_connect_port(
- instance(voice), port_num,
- buf ? buf->port_data(_ports->at(port_num)->type()) : NULL);
+ instance(voice),
+ port_num,
+ buf ? buf->port_data(_ports->at(port_num)->type(), offset) : NULL);
}
} // namespace Server
diff --git a/src/server/LV2Block.hpp b/src/server/LV2Block.hpp
index 321fcd9c..35a2d5c3 100644
--- a/src/server/LV2Block.hpp
+++ b/src/server/LV2Block.hpp
@@ -56,11 +56,13 @@ public:
void work(uint32_t size, const void* data);
- void process(ProcessContext& context);
+ void run(ProcessContext& context);
+ void post_process(ProcessContext& context);
- void set_port_buffer(uint32_t voice,
- uint32_t port_num,
- BufferRef buf);
+ void set_port_buffer(uint32_t voice,
+ uint32_t port_num,
+ BufferRef buf,
+ SampleCount offset);
protected:
SPtr<LilvInstance> make_instance(URIs& uris,
diff --git a/src/server/OutputPort.cpp b/src/server/OutputPort.cpp
index 9f97930e..395885ee 100644
--- a/src/server/OutputPort.cpp
+++ b/src/server/OutputPort.cpp
@@ -52,7 +52,8 @@ OutputPort::get_buffers(BufferFactory& bufs,
bool real_time) const
{
for (uint32_t v = 0; v < poly; ++v)
- voices->at(v).buffer = bufs.get_buffer(buffer_type(), _buffer_size, real_time);
+ voices->at(v).buffer = bufs.get_buffer(
+ buffer_type(), _value.type(), _buffer_size, real_time);
return true;
}
@@ -64,6 +65,26 @@ OutputPort::pre_process(Context& context)
_voices->at(v).buffer->prepare_output_write(context);
}
+SampleCount
+OutputPort::next_value_offset(SampleCount offset, SampleCount end) const
+{
+ SampleCount earliest = end;
+ for (uint32_t v = 0; v < _poly; ++v) {
+ const SampleCount o = _voices->at(v).buffer->next_value_offset(offset, end);
+ if (o < earliest) {
+ earliest = o;
+ }
+ }
+ return earliest;
+}
+
+void
+OutputPort::update_values(SampleCount offset)
+{
+ for (uint32_t v = 0; v < _poly; ++v)
+ _voices->at(v).buffer->update_value_buffer(offset);
+}
+
void
OutputPort::post_process(Context& context)
{
@@ -71,6 +92,7 @@ OutputPort::post_process(Context& context)
update_set_state(context, v);
}
+ update_values(0);
monitor(context);
}
diff --git a/src/server/OutputPort.hpp b/src/server/OutputPort.hpp
index 92ed8428..24017059 100644
--- a/src/server/OutputPort.hpp
+++ b/src/server/OutputPort.hpp
@@ -29,10 +29,6 @@ namespace Server {
* Output ports always have a locally allocated buffer, and buffer() will
* always return that buffer. (This is very different from InputPort)
*
- * This class actually adds no functionality to Port whatsoever right now,
- * it will in the future when more advanced port types exist, and it makes
- * things clearer throughout the engine.
- *
* \ingroup engine
*/
class OutputPort : virtual public PortImpl
@@ -58,6 +54,9 @@ public:
void pre_process(Context& context);
void post_process(Context& context);
+ SampleCount next_value_offset(SampleCount offset, SampleCount end) const;
+ void update_values(SampleCount offset);
+
bool is_input() const { return false; }
bool is_output() const { return true; }
};
diff --git a/src/server/PortImpl.cpp b/src/server/PortImpl.cpp
index c9afd307..ade8c74a 100644
--- a/src/server/PortImpl.cpp
+++ b/src/server/PortImpl.cpp
@@ -68,6 +68,7 @@ PortImpl::PortImpl(BufferFactory& bufs,
, _voices(new Raul::Array<Voice>(static_cast<size_t>(poly)))
, _prepared_voices(NULL)
, _monitored(false)
+ , _force_monitor_update(false)
, _set_by_user(false)
, _is_morph(false)
, _is_auto_morph(false)
@@ -83,13 +84,13 @@ PortImpl::PortImpl(BufferFactory& bufs,
set_type(type, buffer_type);
set_property(uris.lv2_index, bufs.forge().make((int32_t)index));
- if ((type == PortType::CONTROL || type == PortType::CV) && value.is_valid()) {
+ if (has_value()) {
set_property(uris.ingen_value, value);
}
- // if (type == PortType::ATOM) {
- // set_property(uris.atom_bufferType,
- // bufs.forge().make_urid(buffer_type));
- // }
+ if (type == PortType::ATOM) {
+ set_property(uris.atom_bufferType,
+ bufs.forge().make_urid(buffer_type));
+ }
}
PortImpl::~PortImpl()
@@ -131,6 +132,15 @@ PortImpl::set_type(PortType port_type, LV2_URID buffer_type)
}
bool
+PortImpl::has_value() const
+{
+ return (_type == PortType::CONTROL ||
+ _type == PortType::CV ||
+ (_type == PortType::ATOM &&
+ _value.type() == _bufs.uris().atom_Float));
+}
+
+bool
PortImpl::supports(const Raul::URI& value_type) const
{
return has_property(_bufs.uris().atom_supports,
@@ -213,7 +223,7 @@ PortImpl::set_voice_value(const Context& context,
switch (_type.id()) {
case PortType::CONTROL:
buffer(voice)->samples()[0] = value;
- _voices->at(voice).set_state.state = SetState::State::SET;
+ _voices->at(voice).set_state.set(context, context.start(), value);
break;
case PortType::AUDIO:
case PortType::CV: {
@@ -229,13 +239,20 @@ PortImpl::set_voice_value(const Context& context,
value for the next block, particularly for triggers on the last
frame of a block (set nframes-1 to 1, then nframes to 0). */
- SetState& state = _voices->at(voice).set_state;
- state.state = (offset == 0)
- ? SetState::State::SET
- : SetState::State::HALF_SET_CYCLE_1;
- state.time = time;
- state.value = value;
- }
+ _voices->at(voice).set_state.set(context, time, value);
+ } break;
+ case PortType::ATOM:
+ if (buffer(voice)->is_sequence()) {
+ buffer(voice)->append_event(time - context.start(),
+ sizeof(value),
+ _bufs.uris().atom_Float,
+ (const uint8_t*)&value);
+ _voices->at(voice).set_state.set(context, time, value);
+ } else {
+ fprintf(stderr,
+ "error: %s set non-sequence atom port value (buffer type %u)\n",
+ path().c_str(), buffer(voice)->type());
+ }
default:
break;
}
@@ -247,12 +264,25 @@ PortImpl::update_set_state(Context& context, uint32_t voice)
SetState& state = _voices->at(voice).set_state;
switch (state.state) {
case SetState::State::SET:
+ if (state.time < context.start() &&
+ buffer(voice)->is_sequence() &&
+ !_parent->path().is_root()) {
+ buffer(voice)->clear();
+ state.time = context.start();
+ }
break;
case SetState::State::HALF_SET_CYCLE_1:
state.state = SetState::State::HALF_SET_CYCLE_2;
break;
case SetState::State::HALF_SET_CYCLE_2:
- buffer(voice)->set_block(state.value, 0, context.nframes());
+ if (buffer(voice)->is_sequence()) {
+ buffer(voice)->clear();
+ buffer(voice)->append_event(
+ 0, sizeof(float), _bufs.uris().atom_Float,
+ (const uint8_t*)&state.value);
+ } else {
+ buffer(voice)->set_block(state.value, 0, context.nframes());
+ }
state.state = SetState::State::SET;
break;
}
@@ -262,9 +292,8 @@ bool
PortImpl::prepare_poly(BufferFactory& bufs, uint32_t poly)
{
ThreadManager::assert_thread(THREAD_PRE_PROCESS);
- if (_type != PortType::CONTROL &&
- _type != PortType::CV &&
- _type != PortType::AUDIO) {
+ if (_parent->path().is_root() ||
+ (_type == PortType::ATOM && !_value.is_valid())) {
return false;
}
@@ -291,9 +320,8 @@ PortImpl::prepare_poly(BufferFactory& bufs, uint32_t poly)
bool
PortImpl::apply_poly(ProcessContext& context, Raul::Maid& maid, uint32_t poly)
{
- if (_type != PortType::CONTROL &&
- _type != PortType::CV &&
- _type != PortType::AUDIO) {
+ if (_parent->path().is_root() ||
+ (_type == PortType::ATOM && !_value.is_valid())) {
return false;
}
@@ -333,10 +361,10 @@ PortImpl::set_buffer_size(Context& context, BufferFactory& bufs, size_t size)
}
void
-PortImpl::connect_buffers()
+PortImpl::connect_buffers(SampleCount offset)
{
for (uint32_t v = 0; v < _poly; ++v)
- PortImpl::parent_block()->set_port_buffer(v, _index, buffer(v));
+ PortImpl::parent_block()->set_port_buffer(v, _index, buffer(v), offset);
}
void
@@ -382,7 +410,7 @@ PortImpl::monitor(Context& context, bool send_now)
const bool time_to_send = send_now || _frames_since_monitor >= period;
const bool is_sequence = (_type.id() == PortType::ATOM &&
_buffer_type == _bufs.uris().atom_Sequence);
- if (!time_to_send && !(is_sequence && _monitored)) {
+ if (!time_to_send && (!is_sequence || _monitored || buffer(0)->value())) {
return;
}
@@ -404,9 +432,14 @@ PortImpl::monitor(Context& context, bool send_now)
break;
case PortType::ATOM:
if (_buffer_type == _bufs.uris().atom_Sequence) {
- LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)buffer(0)->atom();
- if (_monitored) {
+ const LV2_Atom* atom = buffer(0)->atom();
+ if (buffer(0)->value() && !_monitored) {
+ // Value sequence not fully monitored, monitor as control
+ key = uris.ingen_value;
+ val = ((LV2_Atom_Float*)buffer(0)->value())->body;
+ } else if (_monitored && !buffer(0)->empty()) {
// Monitoring explictly enabled, send everything
+ const LV2_Atom_Sequence* seq = (const LV2_Atom_Sequence*)atom;
LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
context.notify(uris.ingen_activity,
context.start() + ev->time.frames,
@@ -415,8 +448,8 @@ PortImpl::monitor(Context& context, bool send_now)
ev->body.type,
LV2_ATOM_BODY(&ev->body));
}
- } else if (seq->atom.size > sizeof(LV2_Atom_Sequence_Body)) {
- // Just sending for blinkenlights, send one
+ } else if (!buffer(0)->empty()) {
+ // Just send activity for blinkenlights
const int32_t one = 1;
context.notify(uris.ingen_activity,
context.start(),
@@ -424,10 +457,11 @@ PortImpl::monitor(Context& context, bool send_now)
sizeof(int32_t),
(LV2_URID)uris.atom_Bool,
&one);
+ _force_monitor_update = false;
}
}
}
-
+
_frames_since_monitor = _frames_since_monitor % period;
if (key && val != _monitor_value) {
if (context.notify(key, context.start(), this,
@@ -440,7 +474,18 @@ PortImpl::monitor(Context& context, bool send_now)
}
// Otherwise failure, leave old value and try again next time
}
+}
+BufferRef
+PortImpl::value_buffer(uint32_t voice)
+{
+ return buffer(voice)->value_buffer();
+}
+
+SampleCount
+PortImpl::next_value_offset(SampleCount offset, SampleCount end) const
+{
+ return end;
}
} // namespace Server
diff --git a/src/server/PortImpl.hpp b/src/server/PortImpl.hpp
index 4dd1afb3..ccefa0f1 100644
--- a/src/server/PortImpl.hpp
+++ b/src/server/PortImpl.hpp
@@ -48,6 +48,14 @@ public:
SetState() : state(State::SET), value(0), time(0) {}
+ void set(const Context& 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
@@ -122,6 +130,7 @@ public:
/** Called once per process cycle */
virtual void pre_process(Context& context) = 0;
+ virtual void pre_run(Context& context) {}
virtual void post_process(Context& context) = 0;
/** Empty buffer contents completely (ie silence) */
@@ -150,7 +159,7 @@ public:
Resource::Properties& remove,
Resource::Properties& add) {}
- virtual void connect_buffers();
+ virtual void connect_buffers(SampleCount offset=0);
virtual void recycle_buffers();
virtual bool is_input() const = 0;
@@ -160,6 +169,8 @@ public:
inline bool is_a(PortType type) const { return _type == type; }
+ bool has_value() const;
+
PortType type() const { return _type; }
LV2_URID buffer_type() const { return _buffer_type; }
@@ -194,6 +205,17 @@ public:
BufferFactory& bufs() const { return _bufs; }
+ BufferRef value_buffer(uint32_t voice);
+
+ /** Return offset of the first value change after `offset`. */
+ virtual SampleCount next_value_offset(SampleCount offset,
+ SampleCount end) const;
+
+ /** Update value buffer to be current as of `offset`. */
+ virtual void update_values(SampleCount offset) = 0;
+
+ 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;
@@ -235,6 +257,7 @@ protected:
Raul::Array<Voice>* _voices;
Raul::Array<Voice>* _prepared_voices;
bool _monitored;
+ bool _force_monitor_update;
bool _set_by_user;
bool _is_morph;
bool _is_auto_morph;
diff --git a/src/server/ProcessContext.hpp b/src/server/ProcessContext.hpp
index 729bf4c4..78ed9344 100644
--- a/src/server/ProcessContext.hpp
+++ b/src/server/ProcessContext.hpp
@@ -33,6 +33,8 @@ class ProcessContext : public Context
{
public:
explicit ProcessContext(Engine& engine) : Context(engine, ID::AUDIO) {}
+
+ explicit ProcessContext(const ProcessContext& copy) : Context(copy) {}
};
} // namespace Server
diff --git a/src/server/internals/Controller.cpp b/src/server/internals/Controller.cpp
index 1f4f75cc..5f084f6f 100644
--- a/src/server/internals/Controller.cpp
+++ b/src/server/internals/Controller.cpp
@@ -53,6 +53,10 @@ ControllerNode::ControllerNode(InternalPlugin* plugin,
const Ingen::URIs& uris = bufs.uris();
_ports = new Raul::Array<PortImpl*>(6);
+ const Atom zero = bufs.forge().make(0.0f);
+ const Atom one = bufs.forge().make(1.0f);
+ const Atom atom_Float = bufs.forge().alloc_uri(LV2_ATOM__Float);
+
_midi_in_port = new InputPort(bufs, this, Raul::Symbol("input"), 0, 1,
PortType::ATOM, uris.atom_Sequence, Atom());
_midi_in_port->set_property(uris.lv2_name, bufs.forge().alloc("Input"));
@@ -61,40 +65,43 @@ ControllerNode::ControllerNode(InternalPlugin* plugin,
_ports->at(0) = _midi_in_port;
_param_port = new InputPort(bufs, this, Raul::Symbol("controller"), 1, 1,
- PortType::CONTROL, 0, bufs.forge().make(0.0f));
- _param_port->set_property(uris.lv2_minimum, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _param_port->set_property(uris.atom_supports, atom_Float);
+ _param_port->set_property(uris.lv2_minimum, zero);
_param_port->set_property(uris.lv2_maximum, bufs.forge().make(127.0f));
_param_port->set_property(uris.lv2_portProperty, uris.lv2_integer);
_param_port->set_property(uris.lv2_name, bufs.forge().alloc("Controller"));
_ports->at(1) = _param_port;
_log_port = new InputPort(bufs, this, Raul::Symbol("logarithmic"), 2, 1,
- PortType::CONTROL, 0, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _log_port->set_property(uris.atom_supports, atom_Float);
_log_port->set_property(uris.lv2_portProperty, uris.lv2_toggled);
_log_port->set_property(uris.lv2_name, bufs.forge().alloc("Logarithmic"));
_ports->at(2) = _log_port;
_min_port = new InputPort(bufs, this, Raul::Symbol("minimum"), 3, 1,
- PortType::CONTROL, 0, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _min_port->set_property(uris.atom_supports, atom_Float);
_min_port->set_property(uris.lv2_name, bufs.forge().alloc("Minimum"));
_ports->at(3) = _min_port;
_max_port = new InputPort(bufs, this, Raul::Symbol("maximum"), 4, 1,
- PortType::CONTROL, 0, bufs.forge().make(1.0f));
+ PortType::ATOM, uris.atom_Sequence, one);
+ _max_port->set_property(uris.atom_supports, atom_Float);
_max_port->set_property(uris.lv2_name, bufs.forge().alloc("Maximum"));
_ports->at(4) = _max_port;
_audio_port = new OutputPort(bufs, this, Raul::Symbol("output"), 5, 1,
- PortType::CV, 0, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _audio_port->set_property(uris.atom_supports, atom_Float);
_audio_port->set_property(uris.lv2_name, bufs.forge().alloc("Output"));
_ports->at(5) = _audio_port;
}
void
-ControllerNode::process(ProcessContext& context)
+ControllerNode::run(ProcessContext& context)
{
- BlockImpl::pre_process(context);
-
Buffer* const midi_in = _midi_in_port->buffer(0).get();
LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)midi_in->atom();
LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
@@ -105,8 +112,6 @@ ControllerNode::process(ProcessContext& context)
control(context, buf[1], buf[2], ev->time.frames + context.start());
}
}
-
- BlockImpl::post_process(context);
}
void
@@ -117,10 +122,8 @@ ControllerNode::control(ProcessContext& context, uint8_t control_num, uint8_t va
const Sample nval = (val / 127.0f); // normalized [0, 1]
if (_learning) {
- // FIXME: not thread safe
- _param_port->set_value(context.engine().world()->forge().make(control_num));
_param_port->set_control_value(context, time, control_num);
- _param_port->monitor(context, true);
+ _param_port->force_monitor_update();
_learning = false;
}
diff --git a/src/server/internals/Controller.hpp b/src/server/internals/Controller.hpp
index 6cb78397..a5fc5ef2 100644
--- a/src/server/internals/Controller.hpp
+++ b/src/server/internals/Controller.hpp
@@ -45,7 +45,7 @@ public:
GraphImpl* parent,
SampleRate srate);
- void process(ProcessContext& context);
+ void run(ProcessContext& context);
void control(ProcessContext& context, uint8_t control_num, uint8_t val, FrameTime time);
diff --git a/src/server/internals/Delay.cpp b/src/server/internals/Delay.cpp
index 78d2118c..92bbe45b 100644
--- a/src/server/internals/Delay.cpp
+++ b/src/server/internals/Delay.cpp
@@ -138,14 +138,12 @@ static inline float cube_interp(const float fr, const float inm1, const float
}
void
-DelayNode::process(ProcessContext& context)
+DelayNode::run(ProcessContext& context)
{
Buffer* const delay_buf = _delay_port->buffer(0).get();
Buffer* const in_buf = _in_port->buffer(0).get();
Buffer* const out_buf = _out_port->buffer(0).get();
- BlockImpl::pre_process(context);
-
DelayNode* plugin_data = this;
const float* const in = in_buf->samples();
@@ -200,8 +198,6 @@ DelayNode::process(ProcessContext& context)
}
_write_phase = write_phase;
-
- BlockImpl::post_process(context);
}
} // namespace Internals
diff --git a/src/server/internals/Delay.hpp b/src/server/internals/Delay.hpp
index 0dc5fb21..9df7349f 100644
--- a/src/server/internals/Delay.hpp
+++ b/src/server/internals/Delay.hpp
@@ -46,7 +46,7 @@ public:
void activate(BufferFactory& bufs);
- void process(ProcessContext& context);
+ void run(ProcessContext& context);
static InternalPlugin* internal_plugin(URIs& uris);
diff --git a/src/server/internals/Note.cpp b/src/server/internals/Note.cpp
index e2ead4b7..7558334e 100644
--- a/src/server/internals/Note.cpp
+++ b/src/server/internals/Note.cpp
@@ -60,6 +60,8 @@ NoteNode::NoteNode(InternalPlugin* plugin,
const Ingen::URIs& uris = bufs.uris();
_ports = new Raul::Array<PortImpl*>(6);
+ const Atom zero = bufs.forge().make(0.0f);
+
_midi_in_port = new InputPort(bufs, this, Raul::Symbol("input"), 0, 1,
PortType::ATOM, uris.atom_Sequence, Atom());
_midi_in_port->set_property(uris.lv2_name, bufs.forge().alloc("Input"));
@@ -68,35 +70,41 @@ NoteNode::NoteNode(InternalPlugin* plugin,
_ports->at(0) = _midi_in_port;
_freq_port = new OutputPort(bufs, this, Raul::Symbol("frequency"), 1, _polyphony,
- PortType::CV, 0, bufs.forge().make(440.0f));
+ PortType::ATOM, uris.atom_Sequence,
+ bufs.forge().make(440.0f));
+ _freq_port->set_property(uris.atom_supports, bufs.uris().atom_Float);
_freq_port->set_property(uris.lv2_name, bufs.forge().alloc("Frequency"));
_freq_port->set_property(uris.lv2_minimum, bufs.forge().make(16.0f));
_freq_port->set_property(uris.lv2_maximum, bufs.forge().make(25088.0f));
_ports->at(1) = _freq_port;
_num_port = new OutputPort(bufs, this, Raul::Symbol("number"), 1, _polyphony,
- PortType::CV, 0, bufs.forge().make(0.0f));
- _num_port->set_property(uris.lv2_minimum, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _num_port->set_property(uris.atom_supports, bufs.uris().atom_Float);
+ _num_port->set_property(uris.lv2_minimum, zero);
_num_port->set_property(uris.lv2_maximum, bufs.forge().make(127.0f));
_num_port->set_property(uris.lv2_portProperty, uris.lv2_integer);
_num_port->set_property(uris.lv2_name, bufs.forge().alloc("Number"));
_ports->at(2) = _num_port;
_vel_port = new OutputPort(bufs, this, Raul::Symbol("velocity"), 2, _polyphony,
- PortType::CV, 0, bufs.forge().make(0.0f));
- _vel_port->set_property(uris.lv2_minimum, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _vel_port->set_property(uris.atom_supports, bufs.uris().atom_Float);
+ _vel_port->set_property(uris.lv2_minimum, zero);
_vel_port->set_property(uris.lv2_maximum, bufs.forge().make(1.0f));
_vel_port->set_property(uris.lv2_name, bufs.forge().alloc("Velocity"));
_ports->at(3) = _vel_port;
_gate_port = new OutputPort(bufs, this, Raul::Symbol("gate"), 3, _polyphony,
- PortType::CV, 0, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _gate_port->set_property(uris.atom_supports, bufs.uris().atom_Float);
_gate_port->set_property(uris.lv2_portProperty, uris.lv2_toggled);
_gate_port->set_property(uris.lv2_name, bufs.forge().alloc("Gate"));
_ports->at(4) = _gate_port;
_trig_port = new OutputPort(bufs, this, Raul::Symbol("trigger"), 4, _polyphony,
- PortType::CV, 0, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _trig_port->set_property(uris.atom_supports, bufs.uris().atom_Float);
_trig_port->set_property(uris.lv2_portProperty, uris.lv2_toggled);
_trig_port->set_property(uris.lv2_name, bufs.forge().alloc("Trigger"));
_ports->at(5) = _trig_port;
@@ -141,10 +149,8 @@ NoteNode::apply_poly(ProcessContext& context, Raul::Maid& maid, uint32_t poly)
}
void
-NoteNode::process(ProcessContext& context)
+NoteNode::run(ProcessContext& context)
{
- BlockImpl::pre_process(context);
-
Buffer* const midi_in = _midi_in_port->buffer(0).get();
LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)midi_in->atom();
LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
@@ -182,8 +188,6 @@ NoteNode::process(ProcessContext& context)
}
}
}
-
- BlockImpl::post_process(context);
}
static inline float
diff --git a/src/server/internals/Note.hpp b/src/server/internals/Note.hpp
index 027912b6..3b77cf12 100644
--- a/src/server/internals/Note.hpp
+++ b/src/server/internals/Note.hpp
@@ -50,7 +50,7 @@ public:
bool prepare_poly(BufferFactory& bufs, uint32_t poly);
bool apply_poly(ProcessContext& context, Raul::Maid& maid, uint32_t poly);
- void process(ProcessContext& context);
+ void run(ProcessContext& context);
void note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, FrameTime time);
void note_off(ProcessContext& context, uint8_t note_num, FrameTime time);
diff --git a/src/server/internals/Time.cpp b/src/server/internals/Time.cpp
index 6f090257..54901172 100644
--- a/src/server/internals/Time.cpp
+++ b/src/server/internals/Time.cpp
@@ -57,10 +57,8 @@ TimeNode::TimeNode(InternalPlugin* plugin,
}
void
-TimeNode::process(ProcessContext& context)
+TimeNode::run(ProcessContext& context)
{
- BlockImpl::pre_process(context);
-
BufferRef buf = _notify_port->buffer(0);
LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)buf->atom();
@@ -73,8 +71,6 @@ TimeNode::process(ProcessContext& context)
// Ask the driver to append any time events for this cycle
context.engine().driver()->append_time_events(
context, *_notify_port->buffer(0));
-
- BlockImpl::post_process(context);
}
} // namespace Internals
diff --git a/src/server/internals/Time.hpp b/src/server/internals/Time.hpp
index 7efe5d6a..8c0114b7 100644
--- a/src/server/internals/Time.hpp
+++ b/src/server/internals/Time.hpp
@@ -44,7 +44,7 @@ public:
GraphImpl* parent,
SampleRate srate);
- void process(ProcessContext& context);
+ void run(ProcessContext& context);
static InternalPlugin* internal_plugin(URIs& uris);
diff --git a/src/server/internals/Trigger.cpp b/src/server/internals/Trigger.cpp
index abef29ca..650126d4 100644
--- a/src/server/internals/Trigger.cpp
+++ b/src/server/internals/Trigger.cpp
@@ -53,6 +53,8 @@ TriggerNode::TriggerNode(InternalPlugin* plugin,
const Ingen::URIs& uris = bufs.uris();
_ports = new Raul::Array<PortImpl*>(5);
+ const Atom zero = bufs.forge().make(0.0f);
+
_midi_in_port = new InputPort(bufs, this, Raul::Symbol("input"), 0, 1,
PortType::ATOM, uris.atom_Sequence, Atom());
_midi_in_port->set_property(uris.lv2_name, bufs.forge().alloc("Input"));
@@ -61,38 +63,41 @@ TriggerNode::TriggerNode(InternalPlugin* plugin,
_ports->at(0) = _midi_in_port;
_note_port = new InputPort(bufs, this, Raul::Symbol("note"), 1, 1,
- PortType::CONTROL, 0, bufs.forge().make(60.0f));
- _note_port->set_property(uris.lv2_minimum, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence,
+ bufs.forge().make(60.0f));
+ _note_port->set_property(uris.atom_supports, bufs.uris().atom_Float);
+ _note_port->set_property(uris.lv2_minimum, zero);
_note_port->set_property(uris.lv2_maximum, bufs.forge().make(127.0f));
_note_port->set_property(uris.lv2_portProperty, uris.lv2_integer);
_note_port->set_property(uris.lv2_name, bufs.forge().alloc("Note"));
_ports->at(1) = _note_port;
_gate_port = new OutputPort(bufs, this, Raul::Symbol("gate"), 2, 1,
- PortType::CV, 0, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _gate_port->set_property(uris.atom_supports, bufs.uris().atom_Float);
_gate_port->set_property(uris.lv2_portProperty, uris.lv2_toggled);
_gate_port->set_property(uris.lv2_name, bufs.forge().alloc("Gate"));
_ports->at(2) = _gate_port;
_trig_port = new OutputPort(bufs, this, Raul::Symbol("trigger"), 3, 1,
- PortType::CV, 0, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _trig_port->set_property(uris.atom_supports, bufs.uris().atom_Float);
_trig_port->set_property(uris.lv2_portProperty, uris.lv2_toggled);
_trig_port->set_property(uris.lv2_name, bufs.forge().alloc("Trigger"));
_ports->at(3) = _trig_port;
_vel_port = new OutputPort(bufs, this, Raul::Symbol("velocity"), 4, 1,
- PortType::CV, 0, bufs.forge().make(0.0f));
- _vel_port->set_property(uris.lv2_minimum, bufs.forge().make(0.0f));
+ PortType::ATOM, uris.atom_Sequence, zero);
+ _vel_port->set_property(uris.atom_supports, bufs.uris().atom_Float);
+ _vel_port->set_property(uris.lv2_minimum, zero);
_vel_port->set_property(uris.lv2_maximum, bufs.forge().make(1.0f));
_vel_port->set_property(uris.lv2_name, bufs.forge().alloc("Velocity"));
_ports->at(4) = _vel_port;
}
void
-TriggerNode::process(ProcessContext& context)
+TriggerNode::run(ProcessContext& context)
{
- BlockImpl::pre_process(context);
-
Buffer* const midi_in = _midi_in_port->buffer(0).get();
LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)midi_in->atom();
LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
@@ -122,8 +127,6 @@ TriggerNode::process(ProcessContext& context)
}
}
}
-
- BlockImpl::post_process(context);
}
void
@@ -132,10 +135,8 @@ TriggerNode::note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity
assert(time >= context.start() && time <= context.end());
if (_learning) {
- // FIXME: not thread safe
- _note_port->set_value(context.engine().world()->forge().make((float)note_num));
_note_port->set_control_value(context, time, (float)note_num);
- _note_port->monitor(context, true);
+ _note_port->force_monitor_update();
_learning = false;
}
@@ -145,7 +146,6 @@ TriggerNode::note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity
_trig_port->set_control_value(context, time, 1.0f);
_trig_port->set_control_value(context, time + 1, 0.0f);
_vel_port->set_control_value(context, time, velocity / 127.0f);
- assert(_trig_port->buffer(0)->samples()[time - context.start()] == 1.0f);
}
}
diff --git a/src/server/internals/Trigger.hpp b/src/server/internals/Trigger.hpp
index b9ae4849..041bc801 100644
--- a/src/server/internals/Trigger.hpp
+++ b/src/server/internals/Trigger.hpp
@@ -48,7 +48,7 @@ public:
GraphImpl* parent,
SampleRate srate);
- void process(ProcessContext& context);
+ void run(ProcessContext& context);
void note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, FrameTime time);
void note_off(ProcessContext& context, uint8_t note_num, FrameTime time);
diff --git a/src/server/mix.cpp b/src/server/mix.cpp
index 22ae60b0..afece4f6 100644
--- a/src/server/mix.cpp
+++ b/src/server/mix.cpp
@@ -63,6 +63,8 @@ mix(const Context& context,
for (SampleCount i = 0; i < end; ++i) {
out[i] += in[i];
}
+ } else if (srcs[i]->is_sequence()) { // sequence => audio
+ dst->render_sequence(context, srcs[i], true);
}
}
} else if (dst->is_sequence()) {