diff options
author | David Robillard <d@drobilla.net> | 2014-08-31 01:24:57 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2014-08-31 01:24:57 +0000 |
commit | a2792bd09212eed55bba1aa30dc09043a6955486 (patch) | |
tree | d4262f760d4522bc6f1c99778987aada332b9e7e /src | |
parent | e3ecb2b439bd03d27b5e11efe430c24f0ebe6283 (diff) | |
download | ingen-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')
40 files changed, 630 insertions, 200 deletions
diff --git a/src/gui/NodeModule.cpp b/src/gui/NodeModule.cpp index b9798339..0bacb70b 100644 --- a/src/gui/NodeModule.cpp +++ b/src/gui/NodeModule.cpp @@ -186,7 +186,8 @@ NodeModule::port_value_changed(uint32_t index, const Atom& value) return; } - if (value.type() == uris.atom_Float) { + if (value.type() == uris.atom_Float && + _block->get_port(index)->is_numeric()) { _plugin_ui->port_event(index, sizeof(float), 0, value.ptr<float>()); } else { _plugin_ui->port_event(index, diff --git a/src/gui/Port.cpp b/src/gui/Port.cpp index b04e93b5..e123a571 100644 --- a/src/gui/Port.cpp +++ b/src/gui/Port.cpp @@ -83,6 +83,7 @@ Port::Port(App& app, assert(pm); set_border_width(1.0); + set_dash_length(app.style()->get_port_dash_length(pm.get())); if (app.can_control(pm.get())) { show_control(); @@ -367,6 +368,8 @@ Port::activity(const Atom& value) { if (model()->is_a(_app.uris().lv2_AudioPort)) { set_fill_color(peak_color(value.get<float>())); + } else if (_app.can_control(model().get()) && value.type() == _app.uris().atom_Float) { + Ganv::Port::set_control_value(value.get<float>()); } else { _app.port_activity(this); } diff --git a/src/gui/Style.cpp b/src/gui/Style.cpp index 1c61d995..287a1496 100644 --- a/src/gui/Style.cpp +++ b/src/gui/Style.cpp @@ -82,7 +82,6 @@ Style::apply_settings() uint32_t Style::get_port_color(const Client::PortModel* p) { - assert(p != NULL); const URIs& uris = _app.uris(); if (p->is_a(uris.lv2_AudioPort)) { return _audio_port_color; @@ -102,5 +101,15 @@ Style::get_port_color(const Client::PortModel* p) return 0x666666FF; } +uint32_t +Style::get_port_dash_length(const Client::PortModel* p) +{ + const URIs& uris = _app.uris(); + if (p->is_a(uris.atom_AtomPort)) { + return 4.0; + } + return 0.0; +} + } // namespace GUI } // namespace Ingen diff --git a/src/gui/Style.hpp b/src/gui/Style.hpp index 13fa0546..a52a3d01 100644 --- a/src/gui/Style.hpp +++ b/src/gui/Style.hpp @@ -41,6 +41,7 @@ public: void apply_settings(); uint32_t get_port_color(const Client::PortModel* p); + uint32_t get_port_dash_length(const Client::PortModel* p); private: App& _app; 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), ¬e) != sizeof(note) || + if (_event_sink->peek(sizeof(note), ¬e) != sizeof(note) || note.time >= end) { return; } - if (_event_sink.read(sizeof(note), ¬e) == sizeof(note)) { + if (_event_sink->read(sizeof(note), ¬e) == 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()) { |