From 3d89115a67a9c947a28539ffdd2399808a53279b Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 16 Nov 2009 00:30:35 +0000 Subject: Rework objects extension to have "value ports" and "message ports". Make audio and control buffers in ingen actually object buffers (towards interop). Overhaul the hell out of ingen buffer and mixing stuff. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@2266 a436a847-0d15-0410-975c-d299462d15a1 --- src/engine/AudioBuffer.cpp | 229 +++++++++++----------------------- src/engine/AudioBuffer.hpp | 72 ++++++----- src/engine/Buffer.cpp | 12 +- src/engine/Buffer.hpp | 26 ++-- src/engine/BufferFactory.cpp | 124 ++++++++++++++++++ src/engine/BufferFactory.hpp | 69 ++++++++++ src/engine/ConnectionImpl.cpp | 179 ++++---------------------- src/engine/ConnectionImpl.hpp | 66 +++++----- src/engine/DuplexPort.cpp | 53 ++------ src/engine/DuplexPort.hpp | 3 +- src/engine/Engine.cpp | 8 +- src/engine/Engine.hpp | 3 + src/engine/EventBuffer.cpp | 38 ++---- src/engine/EventBuffer.hpp | 11 +- src/engine/InputPort.cpp | 191 ++++++++++------------------ src/engine/InputPort.hpp | 11 +- src/engine/InternalPlugin.cpp | 12 +- src/engine/InternalPlugin.hpp | 4 +- src/engine/JackAudioDriver.cpp | 47 ++++--- src/engine/JackAudioDriver.hpp | 4 +- src/engine/JackMidiDriver.cpp | 4 +- src/engine/LADSPANode.cpp | 33 +++-- src/engine/LADSPANode.hpp | 6 +- src/engine/LADSPAPlugin.cpp | 5 +- src/engine/LADSPAPlugin.hpp | 3 +- src/engine/LV2Node.cpp | 25 ++-- src/engine/LV2Node.hpp | 6 +- src/engine/LV2Plugin.cpp | 5 +- src/engine/LV2Plugin.hpp | 3 +- src/engine/NodeBase.cpp | 16 +-- src/engine/NodeBase.hpp | 6 +- src/engine/NodeFactory.cpp | 25 ++-- src/engine/NodeImpl.hpp | 12 +- src/engine/ObjectBuffer.cpp | 97 +++++++------- src/engine/ObjectBuffer.hpp | 17 ++- src/engine/ObjectSender.cpp | 2 +- src/engine/OutputPort.cpp | 14 ++- src/engine/OutputPort.hpp | 3 +- src/engine/PatchImpl.cpp | 16 +-- src/engine/PatchImpl.hpp | 6 +- src/engine/PatchPlugin.hpp | 3 +- src/engine/PluginImpl.cpp | 1 - src/engine/PluginImpl.hpp | 4 +- src/engine/PortImpl.cpp | 57 ++++----- src/engine/PortImpl.hpp | 38 +++--- src/engine/QueuedEngineInterface.cpp | 4 +- src/engine/events/Connect.cpp | 2 +- src/engine/events/CreateNode.cpp | 2 +- src/engine/events/CreatePort.cpp | 2 +- src/engine/events/RequestMetadata.cpp | 4 +- src/engine/events/SetMetadata.cpp | 2 +- src/engine/events/SetPortValue.cpp | 16 +-- src/engine/internals/Controller.cpp | 37 +++--- src/engine/internals/Controller.hpp | 2 +- src/engine/internals/Note.cpp | 39 +++--- src/engine/internals/Note.hpp | 4 +- src/engine/internals/Transport.cpp | 158 ----------------------- src/engine/internals/Transport.hpp | 47 ------- src/engine/internals/Trigger.cpp | 34 ++--- src/engine/internals/Trigger.hpp | 2 +- src/engine/wscript | 3 +- src/shared/LV2URIMap.cpp | 1 + src/shared/LV2URIMap.hpp | 1 + 63 files changed, 813 insertions(+), 1116 deletions(-) create mode 100644 src/engine/BufferFactory.cpp create mode 100644 src/engine/BufferFactory.hpp delete mode 100644 src/engine/internals/Transport.cpp delete mode 100644 src/engine/internals/Transport.hpp (limited to 'src') diff --git a/src/engine/AudioBuffer.cpp b/src/engine/AudioBuffer.cpp index 53924ae4..1b5a582e 100644 --- a/src/engine/AudioBuffer.cpp +++ b/src/engine/AudioBuffer.cpp @@ -18,9 +18,13 @@ #include #include #include +#include "raul/SharedPtr.hpp" +#include "object.lv2/object.h" #include "ingen-config.h" #include "AudioBuffer.hpp" #include "ProcessContext.hpp" +#include "LV2Features.hpp" +#include "LV2URIMap.hpp" using namespace std; @@ -32,96 +36,61 @@ namespace Ingen { using namespace Shared; -AudioBuffer::AudioBuffer(size_t size) - : Buffer((size == 1) ? DataType::CONTROL : DataType::AUDIO, size) - , _data(NULL) - , _local_data(NULL) - , _filled_size(0) +AudioBuffer::AudioBuffer(Shared::DataType type, size_t size) + : ObjectBuffer(size + sizeof(LV2_Object) + + (type == DataType::AUDIO ? sizeof(LV2_Vector_Body) : 0)) + , _port_type(type) , _state(OK) , _set_value(0) , _set_time(0) { - assert(_size > 0); - allocate(); + assert(size >= sizeof(Sample)); + assert(this->size() > size); assert(data()); -} - - -void -AudioBuffer::alloc_local_data(size_t size) -{ -#ifdef HAVE_POSIX_MEMALIGN - const int ret = posix_memalign((void**)&_local_data, 16, size * sizeof(Sample)); -#else - _local_data = (Sample*)malloc(size * sizeof(Sample)); - int ret = (_local_data != NULL) ? 0 : -1; -#endif - if (ret != 0) { - cerr << "[Buffer] Failed to allocate buffer. Aborting." << endl; - exit(EXIT_FAILURE); + _type = type; + + // Control port / Single float object + if (type == DataType::CONTROL) { + object()->type = 0;//map->float_type; + + // Audio port / Vector of float + } else { + assert(type == DataType::AUDIO); + object()->type = 0;//map->vector_type; + LV2_Vector_Body* body = (LV2_Vector_Body*)object()->body; + body->elem_count = size / sizeof(Sample); + body->elem_type = 0;//map->float_type; } -} -void -AudioBuffer::resize(size_t size) -{ - _size = size; - - Sample* const old_data = _data; - - const bool using_local_data = (_data == _local_data); - - deallocate(); - alloc_local_data(_size * sizeof(Sample)); - assert(_local_data); - - if (using_local_data) - _data = _local_data; - else - _data = old_data; - - set_block(0, 0, _size - 1); + /*cout << "Created Audio Buffer" << endl + << "\tobject @ " << (void*)object() << endl + << "\tbody @ " << (void*)object()->body + << "\t(offset " << (char*)object()->body - (char*)object() << ")" << endl + << "\tdata @ " << (void*)data() + << "\t(offset " << (char*)data() - (char*)object() << ")" + << endl;*/ } -/** Allocate and use a locally managed buffer (data). - */ void -AudioBuffer::allocate() +AudioBuffer::resize(size_t size) { - assert(!_joined_buf); - assert(_local_data == NULL); - assert(_size > 0); - - alloc_local_data(_size * sizeof(Sample)); - assert(_local_data); - - _data = _local_data; - + if (_port_type == DataType::AUDIO) { + ObjectBuffer::resize(size + sizeof(LV2_Vector_Body)); + vector()->elem_count = size / sizeof(Sample); + } clear(); } -/** Free locally allocated buffer. - */ -void -AudioBuffer::deallocate() -{ - assert(!_joined_buf); - free(_local_data); - _local_data = NULL; - _data = NULL; -} - - /** Empty (ie zero) the buffer. */ void AudioBuffer::clear() { - set_block(0, 0, _size - 1); + assert(nframes() != 0); + set_block(0, 0, nframes() - 1); _state = OK; - _filled_size = 0; } @@ -134,14 +103,15 @@ AudioBuffer::clear() void AudioBuffer::set_value(Sample val, FrameTime cycle_start, FrameTime time) { - if (_size == 1) + if (_port_type == DataType::CONTROL) time = cycle_start; - FrameTime offset = time - cycle_start; - assert(offset <= _size); + const FrameTime offset = time - cycle_start; + assert(nframes() != 0); + assert(offset <= nframes()); - if (offset < _size) { - set_block(val, offset, _size - 1); + if (offset < nframes()) { + set_block(val, offset, nframes() - 1); if (offset > 0) _state = HALF_SET_CYCLE_1; @@ -160,7 +130,7 @@ void AudioBuffer::set_block(Sample val, size_t start_offset, size_t end_offset) { assert(end_offset >= start_offset); - assert(end_offset < _size); + assert(end_offset < nframes()); Sample* const buf = data(); assert(buf); @@ -170,52 +140,34 @@ AudioBuffer::set_block(Sample val, size_t start_offset, size_t end_offset) } -/** Scale a block of buffer by @a val. - * - * @a start_sample and @a end_sample define the inclusive range to be set. - */ -void -AudioBuffer::scale(Sample val, size_t start_sample, size_t end_sample) -{ - assert(end_sample >= start_sample); - assert(end_sample < _size); - - Sample* const buf = data(); - assert(buf); - - for (size_t i=start_sample; i <= end_sample; ++i) - buf[i] *= val; -} - - /** Copy a block of @a src into buffer. * * @a start_sample and @a end_sample define the inclusive range to be set. * This function only copies the same range in one buffer to another. */ void -AudioBuffer::copy(const Buffer* src, size_t start_sample, size_t end_sample) +AudioBuffer::copy(const Sample* src, size_t start_sample, size_t end_sample) { assert(end_sample >= start_sample); - assert(src); - assert(src->type() == DataType::CONTROL || DataType::AUDIO); + assert(nframes() != 0); Sample* const buf = data(); assert(buf); - const Sample* const src_buf = ((AudioBuffer*)src)->data(); - assert(src_buf); - - const size_t to_copy = std::min(end_sample, _size - 1); - for (size_t i = start_sample; i <= to_copy; ++i) - buf[i] = src_buf[i]; + const size_t copy_end = std::min(end_sample, (size_t)nframes() - 1); + for (size_t i = start_sample; i <= copy_end; ++i) + buf[i] = src[i]; } void AudioBuffer::copy(Context& context, const Buffer* src) { - copy(src, context.start(), std::min(size(), src->size())); + if (_type == src->type()) { + ObjectBuffer::copy(context, src); + } else if (_type == DataType::AUDIO && src->type() == DataType::CONTROL) { + set_block(((AudioBuffer*)src)->data()[0], 0, nframes()); + } } @@ -225,66 +177,42 @@ AudioBuffer::copy(Context& context, const Buffer* src) * This function only adds the same range in one buffer to another. */ void -AudioBuffer::accumulate(const AudioBuffer* const src, size_t start_sample, size_t end_sample) -{ - assert(end_sample >= start_sample); - assert(src); - assert(src->type() == DataType::CONTROL || DataType::AUDIO); - - Sample* const buf = data(); - assert(buf); - - const Sample* const src_buf = src->data(); - assert(src_buf); - - const size_t to_copy = std::min(end_sample, _size - 1); - for (size_t i = start_sample; i <= to_copy; ++i) - buf[i] += src_buf[i]; - -} - - -/** Use another buffer's data instead of the local one. - * - * This buffer will essentially be identical to @a buf after this call. - */ -bool -AudioBuffer::join(Buffer* buf) +AudioBuffer::mix(Context& context, const Buffer* const src) { - assert(buf != this); - AudioBuffer* abuf = dynamic_cast(buf); - if (!abuf) - return false; + if (src->type() != DataType::CONTROL && src->type() != DataType::AUDIO) + return; - assert(abuf->size() >= _size); + AudioBuffer* src_abuf = (AudioBuffer*)src; - _joined_buf = abuf; - _filled_size = abuf->filled_size(); + Sample* const buf = data(); + const Sample* const src_buf = src_abuf->data(); - assert(_filled_size <= _size); - - return true; -} + const size_t frames = std::min(nframes(), src_abuf->nframes()); + assert(frames != 0); + // Mix initial portions + SampleCount i = 0; + for (; i < frames; ++i) + buf[i] += src_buf[i]; -void -AudioBuffer::unjoin() -{ - _joined_buf = NULL; - _data = _local_data; + // Extend/Mix the final sample of src if it is shorter + const Sample last = src_buf[i - 1]; + while (i < frames) + buf[i++] = last; } void AudioBuffer::prepare_read(Context& context) { + assert(nframes() != 0); switch (_state) { case HALF_SET_CYCLE_1: if (context.start() > _set_time) _state = HALF_SET_CYCLE_2; break; case HALF_SET_CYCLE_2: - set_block(_set_value, 0, _size - 1); + set_block(_set_value, 0, nframes() - 1); _state = OK; break; default: @@ -293,17 +221,4 @@ AudioBuffer::prepare_read(Context& context) } -/** Set the buffer (data) used. - * - * This is only to be used by Drivers (to provide zero-copy processing). - */ -void -AudioBuffer::set_data(Sample* buf) -{ - assert(buf); - assert(!_joined_buf); - _data = buf; -} - - } // namespace Ingen diff --git a/src/engine/AudioBuffer.hpp b/src/engine/AudioBuffer.hpp index d072ddb3..7a8d119d 100644 --- a/src/engine/AudioBuffer.hpp +++ b/src/engine/AudioBuffer.hpp @@ -18,64 +18,74 @@ #ifndef AUDIOBUFFER_H #define AUDIOBUFFER_H +#include #include #include #include #include "types.hpp" -#include "Buffer.hpp" +#include "ObjectBuffer.hpp" + +using namespace std; namespace Ingen { -class AudioBuffer : public Buffer +class AudioBuffer : public ObjectBuffer { public: - AudioBuffer(size_t capacity); + AudioBuffer(Shared::DataType type, size_t capacity); void clear(); + void set_value(Sample val, FrameTime cycle_start, FrameTime time); void set_block(Sample val, size_t start_offset, size_t end_offset); - void scale(Sample val, size_t start_sample, size_t end_sample); - void copy(const Buffer* src, size_t start_sample, size_t end_sample); + void copy(const Sample* src, size_t start_sample, size_t end_sample); void copy(Context& context, const Buffer* src); - void accumulate(const AudioBuffer* src, size_t start_sample, size_t end_sample); - - bool join(Buffer* buf); - void unjoin(); - - /** For driver use only!! */ - void set_data(Sample* data); - - inline const void* raw_data() const { return _data; } - inline void* raw_data() { return _data; } - - inline Sample* data() const { return _data; } + void mix(Context& context, const Buffer* src); + + inline Sample* data() const { + switch (_port_type.symbol()) { + case Shared::DataType::CONTROL: + return (Sample*)object()->body; + case Shared::DataType::AUDIO: + return (Sample*)(object()->body + sizeof(LV2_Vector_Body)); + default: + return NULL; + } + } + + inline SampleCount nframes() const { + SampleCount ret = 0; + switch (_port_type.symbol()) { + case Shared::DataType::CONTROL: + ret = 1; + break; + case Shared::DataType::AUDIO: + ret = (_size - sizeof(LV2_Object) - sizeof(LV2_Vector_Body)) / sizeof(Sample); + break; + default: + ret = 0; + } + return ret; + } inline Sample& value_at(size_t offset) const - { assert(offset < _size); return data()[offset]; } + { assert(offset < nframes()); return data()[offset]; } void prepare_read(Context& context); void prepare_write(Context& context) {} void resize(size_t size); - void filled_size(size_t size) { _filled_size = size; } - size_t filled_size() const { return _filled_size; } - size_t size() const { return _size; } - private: enum State { OK, HALF_SET_CYCLE_1, HALF_SET_CYCLE_2 }; - void alloc_local_data(size_t size); - void allocate(); - void deallocate(); + LV2_Vector_Body* vector() { return(LV2_Vector_Body*)object()->body; } - Sample* _data; ///< Used data pointer (probably same as _local_data) - Sample* _local_data; ///< Locally allocated buffer (possibly unused if joined or set_data used) - size_t _filled_size; ///< Usable buffer size (for MIDI ports etc) - State _state; ///< State of buffer for setting values next cycle - Sample _set_value; ///< Value set by set_value (for completing the set next cycle) - FrameTime _set_time; ///< Time _set_value was set (to reset next cycle) + Shared::DataType _port_type; ///< Type of port this buffer is for + State _state; ///< State of buffer for setting values next cycle + Sample _set_value; ///< Value set by set_value (for completing the set next cycle) + FrameTime _set_time; ///< Time _set_value was set (to reset next cycle) }; diff --git a/src/engine/Buffer.cpp b/src/engine/Buffer.cpp index 07498b36..b473c194 100644 --- a/src/engine/Buffer.cpp +++ b/src/engine/Buffer.cpp @@ -15,6 +15,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include "AudioBuffer.hpp" #include "EventBuffer.hpp" #include "ObjectBuffer.hpp" @@ -23,17 +24,20 @@ namespace Ingen { using namespace Shared; +class Engine; + Buffer* -Buffer::create(DataType type, size_t size) +Buffer::create(Engine& engine, Shared::DataType type, size_t size) { if (type.is_control()) - return new AudioBuffer(1); + return new AudioBuffer(type, size); else if (type.is_audio()) - return new AudioBuffer(size); + return new AudioBuffer(type, size); else if (type.is_events()) return new EventBuffer(size); else if (type.is_value()) - return new ObjectBuffer(size); + return new ObjectBuffer(std::max(size, + sizeof(LV2_Object) + sizeof(void*))); else throw; } diff --git a/src/engine/Buffer.hpp b/src/engine/Buffer.hpp index adde37f2..f566f9b6 100644 --- a/src/engine/Buffer.hpp +++ b/src/engine/Buffer.hpp @@ -22,12 +22,14 @@ #include #include #include "raul/Deletable.hpp" +#include "raul/SharedPtr.hpp" #include "types.hpp" #include "interface/DataType.hpp" namespace Ingen { class Context; +class Engine; class Buffer : public boost::noncopyable, public Raul::Deletable { @@ -35,42 +37,38 @@ public: Buffer(Shared::DataType type, size_t size) : _type(type) , _size(size) - , _joined_buf(NULL) {} - virtual ~Buffer() {} - - static Buffer* create(Shared::DataType type, size_t size); - /** Clear contents and reset state */ virtual void clear() = 0; virtual void resize(size_t size) { _size = size; } - virtual void* raw_data() = 0; - virtual const void* raw_data() const = 0; + virtual void* port_data(Shared::DataType port_type) = 0; + virtual const void* port_data(Shared::DataType port_type) const = 0; /** Rewind (ie reset read pointer), but leave contents unchanged */ virtual void rewind() const {} virtual void copy(Context& context, const Buffer* src) = 0; + virtual void mix(Context& context, const Buffer* src) = 0; virtual void prepare_read(Context& context) {} virtual void prepare_write(Context& context) {} - bool is_joined() const { return (_joined_buf != NULL); } - Buffer* joined_buffer() const { return _joined_buf; } - - virtual bool join(Buffer* buf) = 0; - virtual void unjoin() = 0; - Shared::DataType type() const { return _type; } size_t size() const { return _size; } protected: Shared::DataType _type; size_t _size; - Buffer* _joined_buf; + bool _local; + + friend class BufferFactory; + virtual ~Buffer() {} + +private: + Buffer* _next; ///< Intrusive linked list for BufferFactory }; diff --git a/src/engine/BufferFactory.cpp b/src/engine/BufferFactory.cpp new file mode 100644 index 00000000..63fd31ae --- /dev/null +++ b/src/engine/BufferFactory.cpp @@ -0,0 +1,124 @@ +/* This file is part of Ingen. + * Copyright (C) 2007-2009 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "shared/LV2URIMap.hpp" +#include "AudioBuffer.hpp" +#include "EventBuffer.hpp" +#include "ObjectBuffer.hpp" +#include "BufferFactory.hpp" +#include "Engine.hpp" +#include "AudioDriver.hpp" +#include "ThreadManager.hpp" + +namespace Ingen { + +using namespace Shared; + +BufferFactory::BufferFactory(Engine& engine, SharedPtr map) + : _engine(engine) + , _map(map) +{ +} + +struct BufferDeleter { + BufferDeleter(BufferFactory& bf) : _factory(bf) {} + void operator()(void* ptr) { + _factory.recycle((Buffer*)ptr); + } + BufferFactory& _factory; +}; + + +SharedPtr +BufferFactory::get(Shared::DataType type, size_t size) +{ + Raul::AtomicPtr& head_ptr = free_list(type); + Buffer* try_head; + Buffer* next; + do { + try_head = head_ptr.get(); + if (!try_head) + break; + next = try_head->_next; + } while (!head_ptr.compare_and_exchange(try_head, next)); + + if (!try_head) { + if (ThreadManager::current_thread_id() != THREAD_PROCESS) { + return create(type, size); + } else { + cerr << "ERROR: Failed to obtain buffer" << endl; + return SharedPtr(); + } + } + + try_head->_next = NULL; + return SharedPtr(try_head, BufferDeleter(*this)); +} + + +SharedPtr +BufferFactory::create(Shared::DataType type, size_t size) +{ + assert(ThreadManager::current_thread_id() != THREAD_PROCESS); + + Buffer* buffer = NULL; + + if (type.is_control()) { + if (size == 0) + size = sizeof(LV2_Object) + sizeof(float); + AudioBuffer* ret = new AudioBuffer(type, size); + ret->object()->type = _map->object_class_vector; + ((LV2_Vector_Body*)ret->object()->body)->elem_type = _map->object_class_float32; + buffer = ret; + } else if (type.is_audio()) { + if (size == 0) + size = sizeof(LV2_Object) + sizeof(LV2_Vector_Body) + + _engine.audio_driver()->buffer_size() * sizeof(float); + AudioBuffer* ret = new AudioBuffer(type, size); + ret->object()->type = _map->object_class_float32; + buffer = ret; + } else if (type.is_events()) { + if (size == 0) + size = _engine.audio_driver()->buffer_size() * 4; // FIXME + buffer = new EventBuffer(size); + } else if (type.is_value()) { + if (size == 0) + size = 32; // FIXME + buffer = new ObjectBuffer(std::max(size, sizeof(LV2_Object) + sizeof(void*))); + } else { + cout << "ERROR: Failed to create buffer of unknown type" << endl; + return SharedPtr(); + } + + return SharedPtr(buffer, BufferDeleter(*this)); +} + +void +BufferFactory::recycle(Buffer* buf) +{ + Raul::AtomicPtr& head_ptr = free_list(buf->type()); + Buffer* try_head; + do { + try_head = head_ptr.get(); + buf->_next = try_head; + } while (!head_ptr.compare_and_exchange(try_head, buf)); +} + + +} // namespace Ingen diff --git a/src/engine/BufferFactory.hpp b/src/engine/BufferFactory.hpp new file mode 100644 index 00000000..dcc1e03f --- /dev/null +++ b/src/engine/BufferFactory.hpp @@ -0,0 +1,69 @@ +/* This file is part of Ingen. + * Copyright (C) 2009 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BUFFER_FACTORY_H +#define BUFFER_FACTORY_H + +#include +#include "interface/DataType.hpp" +#include "glibmm/thread.h" +#include "raul/RingBuffer.hpp" +#include "raul/AtomicPtr.hpp" + +namespace Ingen { + +using namespace Shared; + +class Engine; + +namespace Shared { class LV2URIMap; } + +class BufferFactory { +public: + BufferFactory(Engine& engine, SharedPtr map); + + SharedPtr get(Shared::DataType type, size_t size=0); + +private: + friend class BufferDeleter; + void recycle(Buffer* buf); + + SharedPtr create(Shared::DataType type, size_t size=0); + + inline Raul::AtomicPtr& free_list(Shared::DataType type) { + switch (type.symbol()) { + case DataType::AUDIO: return _free_audio; + case DataType::CONTROL: return _free_control; + case DataType::EVENTS: return _free_event; + case DataType::VALUE: return _free_object; + default: throw; + } + } + + Raul::AtomicPtr _free_audio; + Raul::AtomicPtr _free_control; + Raul::AtomicPtr _free_event; + Raul::AtomicPtr _free_object; + + Glib::Mutex _mutex; + Engine& _engine; + SharedPtr _map; +}; + +} // namespace Ingen + +#endif // BUFFER_FACTORY_H diff --git a/src/engine/ConnectionImpl.cpp b/src/engine/ConnectionImpl.cpp index 6c7a63f0..715b1984 100644 --- a/src/engine/ConnectionImpl.cpp +++ b/src/engine/ConnectionImpl.cpp @@ -22,6 +22,8 @@ #include "PortImpl.hpp" #include "AudioBuffer.hpp" #include "ProcessContext.hpp" +#include "InputPort.hpp" +#include "BufferFactory.hpp" namespace Ingen { @@ -32,12 +34,10 @@ using namespace Shared; * This handles both polyphonic and monophonic nodes, transparently to the * user (InputPort). */ -ConnectionImpl::ConnectionImpl(PortImpl* src_port, PortImpl* dst_port) - : _mode(DIRECT) +ConnectionImpl::ConnectionImpl(BufferFactory& bufs, PortImpl* src_port, PortImpl* dst_port) + : _bufs(bufs) , _src_port(src_port) , _dst_port(dst_port) - , _local_buffer(NULL) - , _buffer_size(dst_port->buffer_size()) , _pending_disconnection(false) { assert(src_port); @@ -51,83 +51,34 @@ ConnectionImpl::ConnectionImpl(PortImpl* src_port, PortImpl* dst_port) /*assert((src_port->parent_node()->poly() == dst_port->parent_node()->poly()) || (src_port->parent_node()->poly() == 1 || dst_port->parent_node()->poly() == 1));*/ - set_mode(); - if (need_buffer()) - _local_buffer = Buffer::create(dst_port->type(), _buffer_size); - - /* FIXME: 1->1 connections with a destination with fixed buffers copies unecessarily */ - //cerr << src_port->path() << " -> " << dst_port->path() << " must mix: " << _must_mix << endl; -} - - -ConnectionImpl::~ConnectionImpl() -{ - delete _local_buffer; -} - - -void -ConnectionImpl::set_mode() -{ if (must_mix()) - _mode = MIX; - else if (must_copy()) - _mode = COPY; - else if (must_extend()) - _mode = EXTEND; - - if (_mode == MIX && type() == DataType::EVENTS) - _mode = COPY; - - if (type() == DataType::VALUE) - _mode = DIRECT; + _local_buffer = bufs.get(dst_port->type(), dst_port->buffer_size()); } void -ConnectionImpl::set_buffer_size(size_t size) -{ - if (_mode == MIX || _mode == EXTEND) { - assert(_local_buffer); - delete _local_buffer; - - _local_buffer = Buffer::create(_dst_port->type(), _dst_port->buffer(0)->size()); - } - - _buffer_size = size; -} - - -bool -ConnectionImpl::must_copy() const -{ - return (_dst_port->fixed_buffers() && (_src_port->poly() != _dst_port->poly())); -} - - -bool -ConnectionImpl::must_mix() const +ConnectionImpl::dump() const { - return ( (_src_port->polyphonic() && !_dst_port->polyphonic()) - || (_src_port->parent()->polyphonic() && !_dst_port->parent()->polyphonic()) - || (_dst_port->fixed_buffers()) ); + cerr << _src_port->path() << " -> " << _dst_port->path() + << (must_mix() ? " MIX" : " DIRECT") << endl; } -bool -ConnectionImpl::must_extend() const +void +ConnectionImpl::set_buffer_size(BufferFactory& bufs, size_t size) { - return (_src_port->buffer_size() != _dst_port->buffer_size()); + if (must_mix()) + _local_buffer = bufs.get(_dst_port->type(), _dst_port->buffer(0)->size()); } void -ConnectionImpl::prepare_poly(uint32_t poly) +ConnectionImpl::prepare_poly(BufferFactory& bufs, uint32_t poly) { - _src_port->prepare_poly(poly); + _src_port->prepare_poly(bufs, poly); - if (need_buffer() && !_local_buffer) - _local_buffer = Buffer::create(_dst_port->type(), _dst_port->buffer(0)->size()); + if (must_mix()) + _local_buffer = bufs.get(_dst_port->type(), _dst_port->buffer(0)->size()); } @@ -135,101 +86,25 @@ void ConnectionImpl::apply_poly(Raul::Maid& maid, uint32_t poly) { _src_port->apply_poly(maid, poly); - set_mode(); - - /*cerr << "CONNECTION APPLY: " << src_port()->path() << " * " << src_port()->poly() - << " -> " << dst_port()->path() << " * " << dst_port()->poly() - << "\t\tmust mix: " << must_mix() << ", extend: " << must_extend() - << ", poly " << poly << endl;*/ // Recycle buffer if it's no longer needed - if (_local_buffer && !need_buffer()) { - maid.push(_local_buffer); - _local_buffer = NULL; - } + if (!must_mix() && _local_buffer) + _local_buffer.reset(); } void ConnectionImpl::process(Context& context) { - /* Thought: A poly output port can be connected to multiple mono input - * ports, which means this mix down would have to happen many times. - * Adding a method to OutputPort that mixes down all it's outputs into - * a buffer (if it hasn't been done already this cycle) and returns that - * would avoid having to mix multiple times. Probably not a very common - * case, but it would be faster anyway. */ - - std::cerr << src_port()->path() << " * " << src_port()->poly() - << " -> " << dst_port()->path() << " * " << dst_port()->poly() - << "\t\tmode: " << (int)_mode << std::endl; - - if (_mode == COPY) { - assert(src_port()->poly() == dst_port()->poly()); - for (uint32_t i = 0; i < src_port()->poly(); ++i) - dst_port()->buffer(i)->copy(context, src_port()->buffer(i)); - - } else if (_mode == MIX) { - assert(type() == DataType::AUDIO || type() == DataType::CONTROL); - - const AudioBuffer* const src_buffer = (AudioBuffer*)src_port()->buffer(0); - AudioBuffer* mix_buf = (AudioBuffer*)_local_buffer; - - assert(mix_buf); - - const size_t copy_size = std::min(src_buffer->size(), mix_buf->size()); - - // Copy src buffer to start of mix buffer - mix_buf->copy(context, src_buffer); - - // Write last value of src buffer to remainder of dst buffer, if necessary - if (copy_size < mix_buf->size()) - mix_buf->set_block(src_buffer->value_at(copy_size-1), copy_size, mix_buf->size()-1); - - // Accumulate the source's voices into local buffer starting at the second - // voice (buffer is already set to first voice above) - for (uint32_t j=1; j < src_port()->poly(); ++j) { - mix_buf->accumulate((AudioBuffer*)src_port()->buffer(j), 0, copy_size-1); - } - - // Find the summed value and write it to the remainder of dst buffer - if (copy_size < mix_buf->size()) { - float src_value = src_buffer->value_at(copy_size-1); - for (uint32_t j=1; j < src_port()->poly(); ++j) - src_value += ((AudioBuffer*)src_port()->buffer(j))->value_at(copy_size-1); - - mix_buf->set_block(src_value, copy_size, mix_buf->size()-1); - } - - // Scale the buffer down. - if (src_port()->poly() > 1) - mix_buf->scale(1.0f/(float)src_port()->poly(), 0, mix_buf->size()-1); - - } else if (_mode == EXTEND) { - assert(type() == DataType::AUDIO || type() == DataType::CONTROL); - assert(src_port()->poly() == 1 || src_port()->poly() == dst_port()->poly()); - - const uint32_t poly = dst_port()->poly(); - const uint32_t copy_size = std::min(src_port()->buffer(0)->size(), - dst_port()->buffer(0)->size()); - - for (uint32_t i = 0; i < poly; ++i) { - uint32_t src_voice = std::min(i, src_port()->poly() - 1); - AudioBuffer* src_buf = (AudioBuffer*)src_port()->buffer(src_voice); - AudioBuffer* dst_buf = (AudioBuffer*)dst_port()->buffer(i); - - // Copy src to start of dst - dst_buf->copy(src_buf, 0, copy_size-1); - - // Write last value of src buffer to remainder of dst buffer, if necessary - if (copy_size < dst_buf->size()) - dst_buf->set_block(src_buf->value_at(copy_size - 1), copy_size, dst_buf->size() - 1); - } - - } else if (_mode == DIRECT) { - for (uint32_t j=0; j < src_port()->poly(); ++j) - src_port()->buffer(j)->prepare_read(context); - } + if (!must_mix()) + return; + + // Copy the first voice + _local_buffer->copy(context, src_port()->buffer(0).get()); + + // Mix in the rest + for (uint32_t v = 0; v < src_port()->poly(); ++v) + _local_buffer->mix(context, src_port()->buffer(v).get()); } diff --git a/src/engine/ConnectionImpl.hpp b/src/engine/ConnectionImpl.hpp index a4ad281b..c9101304 100644 --- a/src/engine/ConnectionImpl.hpp +++ b/src/engine/ConnectionImpl.hpp @@ -18,17 +18,22 @@ #ifndef CONNECTIONIMPL_H #define CONNECTIONIMPL_H +#include #include #include #include "raul/Deletable.hpp" #include "interface/DataType.hpp" #include "interface/Connection.hpp" #include "PortImpl.hpp" +#include "PortImpl.hpp" + +using namespace std; namespace Ingen { class PortImpl; class Buffer; +class BufferFactory; /** Represents a single inbound connection for an InputPort. @@ -44,8 +49,7 @@ class Buffer; class ConnectionImpl : public Raul::Deletable, public Shared::Connection { public: - ConnectionImpl(PortImpl* src_port, PortImpl* dst_port); - virtual ~ConnectionImpl(); + ConnectionImpl(BufferFactory& bufs, PortImpl* src_port, PortImpl* dst_port); PortImpl* src_port() const { return _src_port; } PortImpl* dst_port() const { return _dst_port; } @@ -61,49 +65,35 @@ public: /** Get the buffer for a particular voice. * A Connection is smart - it knows the destination port requesting the - * buffer, and will return accordingly (ie the same buffer for every voice - * in a mono->poly connection). + * buffer, and will return accordingly (e.g. the same buffer for every + * voice in a mono->poly connection). */ - inline Buffer* buffer(uint32_t voice) const; - - inline size_t buffer_size() const { return _buffer_size; } + inline SharedPtr buffer(uint32_t voice) const { + if (must_mix()) { + return _local_buffer; + } else if ( ! _src_port->polyphonic()) { + return _src_port->buffer(0); + } else { + return _src_port->buffer(voice); + } + } - void set_buffer_size(size_t size); - void prepare_poly(uint32_t poly); + void set_buffer_size(BufferFactory& bufs, size_t size); + void prepare_poly(BufferFactory& bufs, uint32_t poly); void apply_poly(Raul::Maid& maid, uint32_t poly); - inline bool need_buffer() const { return must_mix(); } - inline bool can_direct() const { return _mode == DIRECT; } - - Shared::DataType type() const { return _src_port->type(); } + /** Returns true if this connection must mix down voices into a local buffer */ + inline bool must_mix() const { return _src_port->poly() > _dst_port->poly(); } protected: - enum { DIRECT, MIX, COPY, EXTEND } _mode; - void set_mode(); - - bool must_copy() const; - bool must_mix() const; - bool must_extend() const; - - PortImpl* const _src_port; - PortImpl* const _dst_port; - Buffer* _local_buffer; - size_t _buffer_size; - bool _pending_disconnection; -}; - + void dump() const; -inline Buffer* -ConnectionImpl::buffer(uint32_t voice) const -{ - if (_mode == MIX) { - return _local_buffer; - } else if ( ! _src_port->polyphonic()) { - return _src_port->buffer(0); - } else { - return _src_port->buffer(voice); - } -} + BufferFactory& _bufs; + PortImpl* const _src_port; + PortImpl* const _dst_port; + SharedPtr _local_buffer; + bool _pending_disconnection; +}; } // namespace Ingen diff --git a/src/engine/DuplexPort.cpp b/src/engine/DuplexPort.cpp index 78a2bb94..6cf1d908 100644 --- a/src/engine/DuplexPort.cpp +++ b/src/engine/DuplexPort.cpp @@ -34,6 +34,7 @@ using namespace Shared; DuplexPort::DuplexPort( + BufferFactory& bufs, NodeImpl* parent, const string& name, uint32_t index, @@ -42,13 +43,12 @@ DuplexPort::DuplexPort( const Raul::Atom& value, size_t buffer_size, bool is_output) - : PortImpl(parent, name, index, poly, type, value, buffer_size) - , InputPort(parent, name, index, poly, type, value, buffer_size) - , OutputPort(parent, name, index, poly, type, value, buffer_size) + : PortImpl(bufs, parent, name, index, poly, type, value, buffer_size) + , InputPort(bufs, parent, name, index, poly, type, value, buffer_size) + , OutputPort(bufs, parent, name, index, poly, type, value, buffer_size) , _is_output(is_output) { assert(PortImpl::_parent == parent); - _fixed_buffers = true; } @@ -56,30 +56,17 @@ DuplexPort::DuplexPort( void DuplexPort::pre_process(Context& context) { - /*cerr << endl << "{ duplex pre" << endl; - cerr << path() << " duplex pre: fixed buffers: " << fixed_buffers() << endl; - cerr << path() << " duplex pre: buffer: " << buffer(0) << endl; - cerr << path() << " duplex pre: is_output: " << _is_output << " { " << endl;*/ - - // If we're a patch output, prepare buffers for write (so plugins can deliver to them) + // If we're a patch output, we're an input from the internal perspective. + // Prepare buffers for write (so plugins can deliver to them) if (_is_output) { - for (uint32_t i=0; i < _poly; ++i) - if (!_buffers->at(i)->is_joined()) - _buffers->at(i)->prepare_write(context); + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v)->prepare_write(context); - // Otherwise, we're a patch input, do whatever a normal node's input port does - // (mix down inputs from an outside "patch is a node" perspective) + // If we're a patch input, were an output from the internal perspective. + // Do whatever a normal node's input port does to prepare input for reading. } else { InputPort::pre_process(context); } - - /*if (type() == DataType::EVENT) - for (uint32_t i=0; i < _poly; ++i) - cerr << path() << " (" << buffer(i) << ") # events: " - << ((EventBuffer*)buffer(i))->event_count() - << ", joined: " << _buffers->at(i)->is_joined() - << ", is_output: " << _is_output << endl;*/ - //cerr << "} duplex pre " << path() << endl; } @@ -87,30 +74,14 @@ DuplexPort::pre_process(Context& context) void DuplexPort::post_process(Context& context) { - /*cerr << endl << "{ duplex post" << endl; - cerr << path() << " duplex post: fixed buffers: " << fixed_buffers() << endl; - cerr << path() << " duplex post: buffer: " << buffer(0) << endl; - cerr << path() << " duplex post: is_output: " << _is_output << " { " << endl; - - if (type() == DataType::EVENT) - for (uint32_t i=0; i < _poly; ++i) - cerr << path() << " (" << buffer(i) << ") # events: " - << ((EventBuffer*)buffer(i))->event_count() - << ", joined: " << _buffers->at(i)->is_joined() << endl;*/ - - // If we're a patch output - - + // If we're a patch output, we're an input from the internal perspective. + // Mix down input delivered by plugins so output (external perspective) is ready. if (_is_output) { - // Then we've been delivered to as an input (internal perspective) - // Mix down this input so patch output ports (external perspective) are ready InputPort::pre_process(context); if (_broadcast) broadcast_value(context, false); } - - //cerr << "} duplex post " << path() << endl; } diff --git a/src/engine/DuplexPort.hpp b/src/engine/DuplexPort.hpp index 882bab73..621d0c62 100644 --- a/src/engine/DuplexPort.hpp +++ b/src/engine/DuplexPort.hpp @@ -39,7 +39,8 @@ class NodeImpl; class DuplexPort : public InputPort, public OutputPort { public: - DuplexPort(NodeImpl* parent, + DuplexPort(BufferFactory& bufs, + NodeImpl* parent, const std::string& name, uint32_t index, uint32_t poly, diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 619ead5e..1c17bf74 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -25,7 +25,12 @@ #include "common/interface/EventType.hpp" #include "events/CreatePatch.hpp" #include "module/World.hpp" +#include "shared/LV2Features.hpp" +#include "shared/LV2URIMap.hpp" +#include "shared/Store.hpp" +#include "uri-map.lv2/uri-map.h" #include "AudioDriver.hpp" +#include "BufferFactory.hpp" #include "ClientBroadcaster.hpp" #include "Engine.hpp" #include "EngineStore.hpp" @@ -41,7 +46,6 @@ #include "ProcessSlave.hpp" #include "QueuedEventSource.hpp" #include "ThreadManager.hpp" -#include "shared/Store.hpp" #include "tuning.hpp" using namespace std; @@ -60,6 +64,8 @@ Engine::Engine(Ingen::Shared::World* world) , _broadcaster(new ClientBroadcaster()) , _node_factory(new NodeFactory(world)) , _message_context(new MessageContext(*this)) + , _buffer_factory(new BufferFactory(*this, PtrCast( + world->lv2_features->feature(LV2_URI_MAP_URI)))) , _quit_flag(false) , _activated(false) { diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index 16635561..93162ff4 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -47,6 +47,7 @@ class Driver; class ProcessSlave; class ProcessContext; class MessageContext; +class BufferFactory; /** The main class for the Engine. @@ -88,6 +89,7 @@ public: ClientBroadcaster* broadcaster() const { return _broadcaster; } NodeFactory* node_factory() const { return _node_factory; } MessageContext* message_context() const { return _message_context; } + BufferFactory* buffer_factory() const { return _buffer_factory; } SharedPtr engine_store() const; @@ -120,6 +122,7 @@ private: ClientBroadcaster* _broadcaster; NodeFactory* _node_factory; MessageContext* _message_context; + BufferFactory* _buffer_factory; bool _quit_flag; bool _activated; diff --git a/src/engine/EventBuffer.cpp b/src/engine/EventBuffer.cpp index d92ecae1..380fe3b6 100644 --- a/src/engine/EventBuffer.cpp +++ b/src/engine/EventBuffer.cpp @@ -35,39 +35,11 @@ using namespace Shared; */ EventBuffer::EventBuffer(size_t capacity) : Buffer(DataType(DataType::EVENTS), capacity) - , _local_buf(new LV2EventBuffer(capacity)) + , _buf(new LV2EventBuffer(capacity)) { - _buf = _local_buf; clear(); - //cerr << "Creating MIDI Buffer " << _buf << ", capacity = " << _buf->capacity << endl; -} - - -/** Use another buffer's data instead of the local one. - * - * This buffer will essentially be identical to @a buf after this call. - */ -bool -EventBuffer::join(Buffer* buf) -{ - assert(buf != this); - EventBuffer* ebuf = dynamic_cast(buf); - if (!ebuf) - return false; - - _buf = ebuf->_local_buf; - _joined_buf = ebuf; - - return true; -} - - -void -EventBuffer::unjoin() -{ - _joined_buf = NULL; - _buf = _local_buf; + //cerr << "Creating Event Buffer " << _buf << ", capacity = " << _buf->capacity << endl; } @@ -101,6 +73,12 @@ EventBuffer::copy(Context& context, const Buffer* src_buf) } +void +EventBuffer::mix(Context& context, const Buffer* src) +{ +} + + /** Clear, and merge \a a and \a b into this buffer. * * FIXME: This is slow. diff --git a/src/engine/EventBuffer.hpp b/src/engine/EventBuffer.hpp index cafa71bd..b5287f63 100644 --- a/src/engine/EventBuffer.hpp +++ b/src/engine/EventBuffer.hpp @@ -31,13 +31,10 @@ class EventBuffer : public Buffer { public: EventBuffer(size_t capacity); - bool join(Buffer* buf); - void unjoin(); - void clear() { _buf->reset(); } - void* raw_data() { return _buf; } - const void* raw_data() const { return _buf; } + void* port_data(Shared::DataType port_type) { return _buf; } + const void* port_data(Shared::DataType port_type) const { return _buf; } void rewind() const { _buf->rewind(); } @@ -45,6 +42,7 @@ public: void prepare_write(Context& context); void copy(Context& context, const Buffer* src); + void mix(Context& contect, const Buffer* src); bool merge(const EventBuffer& a, const EventBuffer& b); bool increment() const { return _buf->increment(); } @@ -75,8 +73,7 @@ public: } private: - LV2EventBuffer* _buf; ///< Contents (maybe belong to _joined_buf) - LV2EventBuffer* _local_buf; ///< Local contents + LV2EventBuffer* _buf; ///< Contents }; diff --git a/src/engine/InputPort.cpp b/src/engine/InputPort.cpp index 5d3a0627..4182e862 100644 --- a/src/engine/InputPort.cpp +++ b/src/engine/InputPort.cpp @@ -27,6 +27,7 @@ #include "OutputPort.hpp" #include "ProcessContext.hpp" #include "ThreadManager.hpp" +#include "BufferFactory.hpp" #include "util.hpp" using namespace std; @@ -36,14 +37,15 @@ namespace Ingen { namespace Shared { class Patch; } using namespace Shared; -InputPort::InputPort(NodeImpl* parent, +InputPort::InputPort(BufferFactory& bufs, + NodeImpl* parent, const string& name, uint32_t index, uint32_t poly, DataType type, const Raul::Atom& value, size_t buffer_size) - : PortImpl(parent, name, index, poly, type, value, buffer_size) + : PortImpl(bufs, parent, name, index, poly, type, value, buffer_size) { if (!dynamic_cast(parent)) add_property("rdf:type", Raul::Atom(Raul::Atom::URI, "lv2:InputPort")); @@ -53,28 +55,32 @@ InputPort::InputPort(NodeImpl* parent, bool InputPort::can_direct() const { - return _connections.size() == 1 && _connections.front()->can_direct(); + return _connections.size() == 1 + && _connections.front()->src_port()->poly() == poly() + && (_connections.front()->src_port()->type() == type() + || (_connections.front()->src_port()->type() == DataType::AUDIO + && type() == DataType::CONTROL)); } void -InputPort::set_buffer_size(size_t size) +InputPort::set_buffer_size(BufferFactory& bufs, size_t size) { - PortImpl::set_buffer_size(size); + PortImpl::set_buffer_size(bufs, size); assert(_buffer_size = size); for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) - ((ConnectionImpl*)c->get())->set_buffer_size(size); + ((ConnectionImpl*)c->get())->set_buffer_size(bufs, size); } bool -InputPort::prepare_poly(uint32_t poly) +InputPort::prepare_poly(BufferFactory& bufs, uint32_t poly) { - PortImpl::prepare_poly(poly); + PortImpl::prepare_poly(bufs, poly); for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) - ((ConnectionImpl*)c->get())->prepare_poly(poly); + ((ConnectionImpl*)c->get())->prepare_poly(bufs, poly); return true; } @@ -92,47 +98,51 @@ InputPort::apply_poly(Raul::Maid& maid, uint32_t poly) PortImpl::apply_poly(maid, poly); assert(this->poly() == poly); - if (can_direct()) { + if (_connections.size() == 1) { ConnectionImpl* c = _connections.begin()->get(); - for (uint32_t i=_poly; i < poly; ++i) - _buffers->at(i)->join(c->buffer(i)); + for (uint32_t v = _poly; v < poly; ++v) + _buffers->at(v) = c->buffer(v); } - for (uint32_t i=0; i < _poly; ++i) + for (uint32_t i = 0; i < _poly; ++i) PortImpl::parent_node()->set_port_buffer(i, _index, buffer(i)); return true; } +/** Connect buffers to the appropriate locations based on the current connections */ +void +InputPort::connect_buffers() +{ + // Single connection now, use it directly + if (_connections.size() == 1) { + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v) = _connections.front()->buffer(v); + + // Use local buffers + } else { + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v) = _bufs.get(type()); // Use local buffer + } + + // Connect node to buffers + PortImpl::connect_buffers(); +} + + /** Add a connection. Realtime safe. * * The buffer of this port will be set directly to the connection's buffer - * if there is only one connection, since no mixing needs to take place. + * if there is only one connection, since no copying/mixing needs to take place. */ void InputPort::add_connection(Connections::Node* const c) { assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - const bool could_direct = can_direct(); - _connections.push_back(c); - - if (!_fixed_buffers) { - if (can_direct()) { - // Use buffer directly to avoid copying - for (uint32_t i=0; i < _poly; ++i) { - _buffers->at(i)->join(c->elem()->buffer(i)); - } - } else if (could_direct) { - // Used to directly use single connection's buffer(s), - // but now there's two so use the local ones again and mix down - for (uint32_t i=0; i < _poly; ++i) { - _buffers->at(i)->unjoin(); - } - } - } + connect_buffers(); // Automatically broadcast connected control inputs if (_type == DataType::CONTROL) @@ -146,38 +156,23 @@ InputPort::remove_connection(const OutputPort* src_port) { assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - bool found = false; Connections::Node* connection = NULL; - for (Connections::iterator i = _connections.begin(); i != _connections.end(); ++i) { - if ((*i)->src_port() == src_port) { + for (Connections::iterator i = _connections.begin(); i != _connections.end(); ++i) + if ((*i)->src_port() == src_port) connection = _connections.erase(i); - found = true; - } - } - if ( ! found) { - cerr << "WARNING: [InputPort::remove_connection] Connection not found !" << endl; - exit(EXIT_FAILURE); - } else { - if (_connections.size() == 0) { - for (uint32_t i=0; i < _poly; ++i) { - // Use a local buffer - if (!_fixed_buffers) - _buffers->at(i)->unjoin(); - _buffers->at(i)->clear(); // Write silence - } - } else if (_connections.size() == 1 && !_fixed_buffers && can_direct()) { - // Share a buffer - for (uint32_t i=0; i < _poly; ++i) { - _buffers->at(i)->join(_connections.front()->buffer(i)); - } - } + if ( ! connection) { + cerr << "ERROR: [InputPort::remove_connection] Connection not found!" << endl; + return NULL; } - if (!_fixed_buffers) - PortImpl::connect_buffers(); + connect_buffers(); - // Turn off broadcasting if we're not connected any more (FIXME: not quite right..) + if (_connections.size() == 0) + for (uint32_t v = 0; v < _poly; ++v) + buffer(v)->clear(); + + // Turn off broadcasting if we're no longer connected if (_type == DataType::CONTROL && _connections.size() == 0) _broadcast = false; @@ -190,74 +185,31 @@ InputPort::remove_connection(const OutputPort* src_port) void InputPort::pre_process(Context& context) { - // If value has been set (e.g. events pushed) by the user, - // don't do anything this cycle to avoid smashing the value + // If value has been set (e.g. events pushed) by the user, don't smash it if (_set_by_user) return; - // No connections, just prepare buffers for reading by our node - if (_connections.size() == 0) { - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_read(context); - return; - } + //connect_buffers(); + // Process connections (mix down polyphony, if necessary) for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) (*c)->process(context); - if (!_fixed_buffers) { - // Single (matching) connection, use buffer(s) directly (zero copy) - if (can_direct()) { - for (uint32_t i=0; i < _poly; ++i) { - _buffers->at(i)->join(_connections.front()->buffer(i)); - _buffers->at(i)->prepare_read(context); - } - connect_buffers(); - return; - } - - connect_buffers(); - } - - /*cerr << path() << " poly = " << _poly << ", fixed buffers: " << _fixed_buffers - << ", joined: " << _buffers->at(0)->is_joined() - << " to " << _buffers->at(0)->joined_buffer() << endl;*/ - - /*if (type() == DataType::EVENT) - for (uint32_t i=0; i < _poly; ++i) - if (((EventBuffer*)buffer(i))->event_count() > 0) - cerr << path() << " (" << buffer(i) << ") # events: " - << ((EventBuffer*)buffer(i))->event_count() - << ", joined: " << _buffers->at(i)->is_joined() << endl;*/ - - // Mix down all incoming connection to buffers - - if (_type == DataType::CONTROL || _type == DataType::AUDIO) { - for (uint32_t voice=0; voice < _poly; ++voice) { + // Multiple connections, mix them all into our local buffers + if (_connections.size() > 1) { + for (uint32_t v = 0; v < _poly; ++v) { // Copy first connection - buffer(voice)->copy(context, _connections.front()->buffer(voice)); - - // Accumulate the rest - if (_connections.size() > 1) { - Connections::iterator c = _connections.begin(); - for (++c; c != _connections.end(); ++c) - ((AudioBuffer*)buffer(voice))->accumulate( - ((AudioBuffer*)(*c)->buffer(voice)), 0, _buffer_size-1); - } - } - } else { - assert(_poly == 1); + buffer(v)->copy(context, _connections.front()->buffer(v).get()); - // FIXME - if (_connections.size() > 1) - cerr << "WARNING: MIDI mixing not implemented, only first connection used." << endl; - - // Copy first connection - buffer(0)->copy(context, _connections.front()->buffer(0)); + // Mix in the rest + Connections::iterator c = _connections.begin(); + for (++c; c != _connections.end(); ++c) + buffer(v)->mix(context, (*c)->buffer(v).get()); + } } - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_read(context); + for (uint32_t v = 0; v < _poly; ++v) + buffer(v)->prepare_read(context); if (_broadcast) broadcast_value(context, false); @@ -269,17 +221,10 @@ InputPort::post_process(Context& context) { // Prepare buffers for next cycle if (!can_direct()) - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_write(context); + for (uint32_t v = 0; v < _poly; ++v) + buffer(v)->prepare_write(context); _set_by_user = false; - - /*if (_broadcast && (_type == DataType::CONTROL)) { - const Sample value = ((AudioBuffer*)(*_buffers)[0])->value_at(0); - - cerr << path() << " input post: buffer: " << buffer(0) << " value = " - << value << " (last " << _last_broadcasted_value << ")" < 0); } + size_t num_connections() const { return _connections.size(); } bool is_input() const { return true; } bool is_output() const { return false; } - virtual void set_buffer_size(size_t size); - protected: + void connect_buffers(); bool can_direct() const; Connections _connections; diff --git a/src/engine/InternalPlugin.cpp b/src/engine/InternalPlugin.cpp index 99061e6c..a3d22e10 100644 --- a/src/engine/InternalPlugin.cpp +++ b/src/engine/InternalPlugin.cpp @@ -20,7 +20,6 @@ #include "internals/Note.hpp" #include "internals/Trigger.hpp" #include "internals/Controller.hpp" -#include "internals/Transport.hpp" #include "Engine.hpp" #include "AudioDriver.hpp" @@ -40,7 +39,8 @@ InternalPlugin::InternalPlugin(const std::string& uri, const std::string& symbol NodeImpl* -InternalPlugin::instantiate(const string& name, +InternalPlugin::instantiate(BufferFactory& bufs, + const string& name, bool polyphonic, Ingen::PatchImpl* parent, Engine& engine) @@ -52,13 +52,11 @@ InternalPlugin::instantiate(const string& name, const string uri_str = uri().str(); if (uri_str == NS_INTERNALS "Note") { - return new NoteNode(name, polyphonic, parent, srate, buffer_size); + return new NoteNode(bufs, name, polyphonic, parent, srate, buffer_size); } else if (uri_str == NS_INTERNALS "Trigger") { - return new TriggerNode(name, polyphonic, parent, srate, buffer_size); + return new TriggerNode(bufs, name, polyphonic, parent, srate, buffer_size); } else if (uri_str == NS_INTERNALS "Controller") { - return new ControllerNode(name, polyphonic, parent, srate, buffer_size); - } else if (uri_str == NS_INTERNALS "Transport") { - return new TransportNode(name, polyphonic, parent, srate, buffer_size); + return new ControllerNode(bufs, name, polyphonic, parent, srate, buffer_size); } else { return NULL; } diff --git a/src/engine/InternalPlugin.hpp b/src/engine/InternalPlugin.hpp index d9d6b0b2..b7797b49 100644 --- a/src/engine/InternalPlugin.hpp +++ b/src/engine/InternalPlugin.hpp @@ -37,6 +37,7 @@ namespace Ingen { class NodeImpl; +class BufferFactory; /** Implementation of an Internal plugin. @@ -46,7 +47,8 @@ class InternalPlugin : public PluginImpl public: InternalPlugin(const std::string& uri, const std::string& symbol); - NodeImpl* instantiate(const std::string& name, + NodeImpl* instantiate(BufferFactory& bufs, + const std::string& name, bool polyphonic, Ingen::PatchImpl* parent, Engine& engine); diff --git a/src/engine/JackAudioDriver.cpp b/src/engine/JackAudioDriver.cpp index 667b6d91..86f946da 100644 --- a/src/engine/JackAudioDriver.cpp +++ b/src/engine/JackAudioDriver.cpp @@ -50,14 +50,12 @@ JackAudioPort::JackAudioPort(JackAudioDriver* driver, DuplexPort* patch_port) , Raul::List::Node(this) , _driver(driver) , _jack_port(NULL) - , _jack_buffer(NULL) { assert(patch_port->poly() == 1); create(); patch_port->buffer(0)->clear(); - patch_port->fixed_buffers(true); } @@ -93,19 +91,28 @@ JackAudioPort::destroy() void -JackAudioPort::prepare_buffer(jack_nframes_t nframes) +JackAudioPort::pre_process(jack_nframes_t nframes) { + if (!is_input()) + return; + jack_sample_t* jack_buf = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes); - AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0); + AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0).get(); - //cerr << "[JACK] " << _patch_port->path() << " buffer: " << patch_buf << endl; + patch_buf->copy(jack_buf, 0, nframes - 1); +} - if (jack_buf != _jack_buffer) { - patch_buf->set_data(jack_buf); - _jack_buffer = jack_buf; - } - assert(patch_buf->data() == jack_buf); +void +JackAudioPort::post_process(jack_nframes_t nframes) +{ + if (is_input()) + return; + + jack_sample_t* jack_buf = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes); + AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0).get(); + + memcpy(jack_buf, patch_buf->data(), nframes * sizeof(Sample)); } @@ -172,7 +179,7 @@ JackAudioDriver::attach(const std::string& server_name, _local_client = (jack_client == NULL); - _buffer_size = jack_get_buffer_size(_client); + _buffer_size = jack_get_buffer_size(_client) * sizeof(Sample); _sample_rate = jack_get_sample_rate(_client); jack_on_shutdown(_client, shutdown_cb, this); @@ -334,7 +341,7 @@ JackAudioDriver::_process_cb(jack_nframes_t nframes) // FIXME: all of this time stuff is screwy // FIXME: support nframes != buffer_size, even though that never damn well happens - assert(nframes == _buffer_size); + assert(nframes == _buffer_size / sizeof(Sample)); // Jack can elect to not call this function for a cycle, if overloaded // FIXME: this doesn't make sense, and the start time isn't used anyway @@ -360,11 +367,9 @@ JackAudioDriver::_process_cb(jack_nframes_t nframes) // (Aiming for jitter-free 1 block event latency, ideally) _engine.process_events(_process_context); - // Set buffers of patch ports to Jack port buffers (zero-copy processing) - for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) { - assert(*i); - (*i)->prepare_buffer(nframes); - } + // Read input + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) + (*i)->pre_process(nframes); if (_engine.midi_driver()) _engine.midi_driver()->pre_process(_process_context); @@ -376,6 +381,10 @@ JackAudioDriver::_process_cb(jack_nframes_t nframes) if (_engine.midi_driver()) _engine.midi_driver()->post_process(_process_context); + // Write output + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) + (*i)->post_process(nframes); + _engine.post_processor()->set_end_time(_process_context.end()); return 0; @@ -420,8 +429,8 @@ int JackAudioDriver::_buffer_size_cb(jack_nframes_t nframes) { if (_root_patch) { - _root_patch->set_buffer_size(nframes); - _buffer_size = nframes; + _buffer_size = nframes * sizeof(Sample); + _root_patch->set_buffer_size(*_engine.buffer_factory(), _buffer_size); } return 0; } diff --git a/src/engine/JackAudioDriver.hpp b/src/engine/JackAudioDriver.hpp index c4f7990c..0e062b22 100644 --- a/src/engine/JackAudioDriver.hpp +++ b/src/engine/JackAudioDriver.hpp @@ -55,14 +55,14 @@ public: void set_name(const std::string& name) { jack_port_set_name(_jack_port, name.c_str()); }; - void prepare_buffer(jack_nframes_t nframes); + void pre_process(jack_nframes_t nframes); + void post_process(jack_nframes_t nframes); jack_port_t* jack_port() const { return _jack_port; } private: JackAudioDriver* _driver; jack_port_t* _jack_port; - jack_sample_t* _jack_buffer; ///< Cached for output ports }; diff --git a/src/engine/JackMidiDriver.cpp b/src/engine/JackMidiDriver.cpp index 1cb46c3e..6de97f68 100644 --- a/src/engine/JackMidiDriver.cpp +++ b/src/engine/JackMidiDriver.cpp @@ -100,7 +100,7 @@ JackMidiPort::pre_process(ProcessContext& context) assert(_patch_port->poly() == 1); - EventBuffer* patch_buf = dynamic_cast(_patch_port->buffer(0)); + EventBuffer* patch_buf = dynamic_cast(_patch_port->buffer(0).get()); assert(patch_buf); void* jack_buffer = jack_port_get_buffer(_jack_port, context.nframes()); @@ -134,7 +134,7 @@ JackMidiPort::post_process(ProcessContext& context) if (is_input()) return; - EventBuffer* patch_buf = dynamic_cast(_patch_port->buffer(0)); + EventBuffer* patch_buf = dynamic_cast(_patch_port->buffer(0).get()); void* jack_buf = jack_port_get_buffer(_jack_port, context.nframes()); assert(_patch_port->poly() == 1); diff --git a/src/engine/LADSPANode.cpp b/src/engine/LADSPANode.cpp index e761ff60..04ffa3f9 100644 --- a/src/engine/LADSPANode.cpp +++ b/src/engine/LADSPANode.cpp @@ -62,9 +62,9 @@ LADSPANode::~LADSPANode() bool -LADSPANode::prepare_poly(uint32_t poly) +LADSPANode::prepare_poly(BufferFactory& bufs, uint32_t poly) { - NodeBase::prepare_poly(poly); + NodeBase::prepare_poly(bufs, poly); if ( (!_polyphonic) || (_prepared_instances && poly <= _prepared_instances->size()) ) { @@ -82,7 +82,7 @@ LADSPANode::prepare_poly(uint32_t poly) // Initialize the values of new ports for (unsigned long j=0; j < num_ports(); ++j) { PortImpl* const port = _ports->at(j); - Buffer *buffer = port->prepared_buffer(i); + Buffer* buffer = port->prepared_buffer(i).get(); // FIXME: Preserve individual voice values if (port->type() == DataType::CONTROL) { @@ -142,7 +142,7 @@ nameify_if_invalid(const string& name) * value is false, this object may not be used. */ bool -LADSPANode::instantiate() +LADSPANode::instantiate(BufferFactory& bufs) { if (!_ports) _ports = new Raul::Array(_descriptor->PortCount); @@ -193,11 +193,11 @@ LADSPANode::instantiate() Path port_path(path().child(port_name)); DataType type = DataType::AUDIO; - port_buffer_size = _buffer_size; + port_buffer_size = _buffer_size * sizeof(Sample); if (LADSPA_IS_PORT_CONTROL(_descriptor->PortDescriptors[j])) { type = DataType::CONTROL; - port_buffer_size = 1; + port_buffer_size = sizeof(Sample); } else { assert(LADSPA_IS_PORT_AUDIO(_descriptor->PortDescriptors[j])); } @@ -210,10 +210,10 @@ LADSPANode::instantiate() const float value = default_val ? default_val.get() : 0.0f; if (LADSPA_IS_PORT_INPUT(_descriptor->PortDescriptors[j])) { - port = new InputPort(this, port_name, j, _polyphony, type, value, port_buffer_size); + port = new InputPort(bufs, this, port_name, j, _polyphony, type, value, port_buffer_size); _ports->at(j) = port; } else if (LADSPA_IS_PORT_OUTPUT(_descriptor->PortDescriptors[j])) { - port = new OutputPort(this, port_name, j, _polyphony, type, value, port_buffer_size); + port = new OutputPort(bufs, this, port_name, j, _polyphony, type, value, port_buffer_size); _ports->at(j) = port; } @@ -236,7 +236,7 @@ LADSPANode::instantiate() // Set initial/default value if (port->buffer_size() == 1) { for (uint32_t i=0; i < _polyphony; ++i) - ((AudioBuffer*)port->buffer(i))->set_value(value, 0, 0); + ((AudioBuffer*)port->buffer(i).get())->set_value(value, 0, 0); } if (port->is_input() && port->buffer_size() == 1) { @@ -265,9 +265,9 @@ LADSPANode::activate() set_port_buffer(i, j, port->buffer(i)); if (port->type() == DataType::CONTROL) { - ((AudioBuffer*)port->buffer(i))->set_value(port->value().get_float(), 0, 0); + ((AudioBuffer*)port->buffer(i).get())->set_value(port->value().get_float(), 0, 0); } else if (port->type() == DataType::AUDIO) { - ((AudioBuffer*)port->buffer(i))->set_value(0.0f, 0, 0); + ((AudioBuffer*)port->buffer(i).get())->set_value(0.0f, 0, 0); } } if (_descriptor->activate != NULL) @@ -300,16 +300,11 @@ LADSPANode::process(ProcessContext& context) void -LADSPANode::set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) +LADSPANode::set_port_buffer(uint32_t voice, uint32_t port_num, SharedPtr buf) { assert(voice < _polyphony); - - AudioBuffer* audio_buffer = dynamic_cast(buf); - assert(audio_buffer); - - if (port_num < _descriptor->PortCount) - _descriptor->connect_port((*_instances)[voice], port_num, - audio_buffer->data()); + _descriptor->connect_port((*_instances)[voice], port_num, + (LADSPA_Data*)buf->port_data(_ports->at(port_num)->type())); } diff --git a/src/engine/LADSPANode.hpp b/src/engine/LADSPANode.hpp index 008319b0..29f8fc5b 100644 --- a/src/engine/LADSPANode.hpp +++ b/src/engine/LADSPANode.hpp @@ -45,9 +45,9 @@ public: ~LADSPANode(); - bool instantiate(); + bool instantiate(BufferFactory& bufs); - bool prepare_poly(uint32_t poly); + bool prepare_poly(BufferFactory& bufs, uint32_t poly); bool apply_poly(Raul::Maid& maid, uint32_t poly); void activate(); @@ -55,7 +55,7 @@ public: void process(ProcessContext& context); - void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf); + void set_port_buffer(uint32_t voice, uint32_t port_num, SharedPtr buf); protected: void get_port_limits(unsigned long port_index, diff --git a/src/engine/LADSPAPlugin.cpp b/src/engine/LADSPAPlugin.cpp index 44df77b0..dd95bfbe 100644 --- a/src/engine/LADSPAPlugin.cpp +++ b/src/engine/LADSPAPlugin.cpp @@ -58,7 +58,8 @@ LADSPAPlugin::get_property(const Raul::URI& uri) const NodeImpl* -LADSPAPlugin::instantiate(const string& name, +LADSPAPlugin::instantiate(BufferFactory& bufs, + const string& name, bool polyphonic, Ingen::PatchImpl* parent, Engine& engine) @@ -97,7 +98,7 @@ LADSPAPlugin::instantiate(const string& name, if (descriptor != NULL) { n = new LADSPANode(this, name, polyphonic, parent, descriptor, srate, buffer_size); - if ( ! n->instantiate() ) { + if ( ! n->instantiate(bufs) ) { delete n; n = NULL; } diff --git a/src/engine/LADSPAPlugin.hpp b/src/engine/LADSPAPlugin.hpp index 0a9bbdd7..332bdd8e 100644 --- a/src/engine/LADSPAPlugin.hpp +++ b/src/engine/LADSPAPlugin.hpp @@ -44,7 +44,8 @@ public: const std::string& label, const std::string& name); - NodeImpl* instantiate(const std::string& name, + NodeImpl* instantiate(BufferFactory& bufs, + const std::string& name, bool polyphonic, Ingen::PatchImpl* parent, Engine& engine); diff --git a/src/engine/LV2Node.cpp b/src/engine/LV2Node.cpp index 1515344a..9ac478a8 100644 --- a/src/engine/LV2Node.cpp +++ b/src/engine/LV2Node.cpp @@ -70,9 +70,9 @@ LV2Node::~LV2Node() bool -LV2Node::prepare_poly(uint32_t poly) +LV2Node::prepare_poly(BufferFactory& bufs, uint32_t poly) { - NodeBase::prepare_poly(poly); + NodeBase::prepare_poly(bufs, poly); if ( (!_polyphonic) || (_prepared_instances && poly <= _prepared_instances->size()) ) { @@ -93,7 +93,7 @@ LV2Node::prepare_poly(uint32_t poly) // Initialize the values of new ports for (unsigned long j=0; j < num_ports(); ++j) { PortImpl* const port = _ports->at(j); - Buffer *buffer = port->prepared_buffer(i); + Buffer* buffer = port->prepared_buffer(i).get(); // FIXME: Preserve individual voice values if (port->type() == DataType::CONTROL) { @@ -140,7 +140,7 @@ LV2Node::apply_poly(Raul::Maid& maid, uint32_t poly) * value is false, this object may not be used. */ bool -LV2Node::instantiate() +LV2Node::instantiate(BufferFactory& bufs) { SharedPtr info = _lv2_plugin->lv2_info(); SLV2Plugin plug = _lv2_plugin->slv2_plugin(); @@ -210,7 +210,7 @@ LV2Node::instantiate() DataType data_type = DataType::UNKNOWN; if (slv2_port_is_a(plug, id, info->control_class)) { data_type = DataType::CONTROL; - port_buffer_size = 1; + port_buffer_size = sizeof(Sample); } else if (slv2_port_is_a(plug, id, info->audio_class)) { data_type = DataType::AUDIO; port_buffer_size = _buffer_size; @@ -266,12 +266,12 @@ LV2Node::instantiate() val = isnan(def_values[j]) ? 0.0f : def_values[j]; if (direction == INPUT) - port = new InputPort(this, port_name, j, _polyphony, data_type, val, port_buffer_size); + port = new InputPort(bufs, this, port_name, j, _polyphony, data_type, val, port_buffer_size); else - port = new OutputPort(this, port_name, j, _polyphony, data_type, val, port_buffer_size); + port = new OutputPort(bufs, this, port_name, j, _polyphony, data_type, val, port_buffer_size); if (direction == INPUT && data_type == DataType::CONTROL) - ((AudioBuffer*)port->buffer(0))->set_value(val.get_float(), 0, 0); + ((AudioBuffer*)port->buffer(0).get())->set_value(val.get_float(), 0, 0); SLV2Values contexts = slv2_port_get_value(plug, id, context_pred); for (uint32_t i = 0; i < slv2_values_size(contexts); ++i) { @@ -312,9 +312,9 @@ LV2Node::activate() set_port_buffer(i, j, port->buffer(i)); if (port->type() == DataType::CONTROL) { - ((AudioBuffer*)port->buffer(i))->set_value(port->value().get_float(), 0, 0); + ((AudioBuffer*)port->buffer(i).get())->set_value(port->value().get_float(), 0, 0); } else if (port->type() == DataType::AUDIO) { - ((AudioBuffer*)port->buffer(i))->set_value(0.0f, 0, 0); + ((AudioBuffer*)port->buffer(i).get())->set_value(0.0f, 0, 0); } } slv2_instance_activate((*_instances)[i]); @@ -361,10 +361,11 @@ LV2Node::process(ProcessContext& context) void -LV2Node::set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) +LV2Node::set_port_buffer(uint32_t voice, uint32_t port_num, SharedPtr buf) { assert(voice < _polyphony); - slv2_instance_connect_port((*_instances)[voice], port_num, buf->raw_data()); + slv2_instance_connect_port((*_instances)[voice], port_num, + buf->port_data(_ports->at(port_num)->type())); } diff --git a/src/engine/LV2Node.hpp b/src/engine/LV2Node.hpp index 8a543f2d..e971b1d0 100644 --- a/src/engine/LV2Node.hpp +++ b/src/engine/LV2Node.hpp @@ -46,9 +46,9 @@ public: ~LV2Node(); - bool instantiate(); + bool instantiate(BufferFactory& bufs); - bool prepare_poly(uint32_t poly); + bool prepare_poly(BufferFactory& bufs, uint32_t poly); bool apply_poly(Raul::Maid& maid, uint32_t poly); void activate(); @@ -58,7 +58,7 @@ public: void process(ProcessContext& context); - void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf); + void set_port_buffer(uint32_t voice, uint32_t port_num, SharedPtr buf); protected: LV2Plugin* _lv2_plugin; diff --git a/src/engine/LV2Plugin.cpp b/src/engine/LV2Plugin.cpp index 7b289c32..3f3e25c2 100644 --- a/src/engine/LV2Plugin.cpp +++ b/src/engine/LV2Plugin.cpp @@ -60,7 +60,8 @@ LV2Plugin::symbol() const NodeImpl* -LV2Plugin::instantiate(const string& name, +LV2Plugin::instantiate(BufferFactory& bufs, + const string& name, bool polyphonic, Ingen::PatchImpl* parent, Engine& engine) @@ -73,7 +74,7 @@ LV2Plugin::instantiate(const string& name, Glib::Mutex::Lock lock(engine.world()->rdf_world->mutex()); LV2Node* n = new LV2Node(this, name, polyphonic, parent, srate, buffer_size); - if ( ! n->instantiate() ) { + if ( ! n->instantiate(bufs) ) { delete n; n = NULL; } diff --git a/src/engine/LV2Plugin.hpp b/src/engine/LV2Plugin.hpp index 29c987d4..fdb90a22 100644 --- a/src/engine/LV2Plugin.hpp +++ b/src/engine/LV2Plugin.hpp @@ -48,7 +48,8 @@ class LV2Plugin : public PluginImpl public: LV2Plugin(SharedPtr lv2_info, const std::string& uri); - NodeImpl* instantiate(const std::string& name, + NodeImpl* instantiate(BufferFactory& bufs, + const std::string& name, bool polyphonic, Ingen::PatchImpl* parent, Engine& engine); diff --git a/src/engine/NodeBase.cpp b/src/engine/NodeBase.cpp index 25660c37..ab8b7054 100644 --- a/src/engine/NodeBase.cpp +++ b/src/engine/NodeBase.cpp @@ -102,7 +102,7 @@ NodeBase::deactivate() bool -NodeBase::prepare_poly(uint32_t poly) +NodeBase::prepare_poly(BufferFactory& bufs, uint32_t poly) { assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); @@ -111,7 +111,7 @@ NodeBase::prepare_poly(uint32_t poly) if (_ports) for (size_t i=0; i < _ports->size(); ++i) - _ports->at(i)->prepare_poly(poly); + _ports->at(i)->prepare_poly(bufs, poly); return true; } @@ -139,7 +139,7 @@ NodeBase::apply_poly(Raul::Maid& maid, uint32_t poly) void -NodeBase::set_buffer_size(size_t size) +NodeBase::set_buffer_size(BufferFactory& bufs, size_t size) { assert(ThreadManager::current_thread_id() == THREAD_PROCESS); @@ -147,7 +147,7 @@ NodeBase::set_buffer_size(size_t size) if (_ports) for (size_t i=0; i < _ports->size(); ++i) - _ports->at(i)->set_buffer_size(size); + _ports->at(i)->set_buffer_size(bufs, size); } @@ -209,8 +209,8 @@ NodeBase::pre_process(Context& context) { assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - // Mix down any ports with multiple inputs - for (size_t i=0; i < num_ports(); ++i) { + // Mix down input ports + for (uint32_t i = 0; i < num_ports(); ++i) { PortImpl* const port = _ports->at(i); if (port->context() == Context::AUDIO) port->pre_process(context); @@ -225,8 +225,8 @@ NodeBase::post_process(Context& context) { assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - /* Write output ports */ - for (size_t i=0; _ports && i < _ports->size(); ++i) { + // Write output ports + for (size_t i = 0; _ports && i < _ports->size(); ++i) { PortImpl* const port = _ports->at(i); if (port->context() == Context::AUDIO) _ports->at(i)->post_process(context); diff --git a/src/engine/NodeBase.hpp b/src/engine/NodeBase.hpp index 15772551..7a7db8e1 100644 --- a/src/engine/NodeBase.hpp +++ b/src/engine/NodeBase.hpp @@ -60,7 +60,7 @@ public: virtual void deactivate(); bool activated() { return _activated; } - virtual bool prepare_poly(uint32_t poly); + virtual bool prepare_poly(BufferFactory& bufs, uint32_t poly); virtual bool apply_poly(Raul::Maid& maid, uint32_t poly); virtual void reset_input_ready(); @@ -82,9 +82,9 @@ public: virtual void process(ProcessContext& context) = 0; virtual void post_process(Context& context); - virtual void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) {} + virtual void set_port_buffer(uint32_t voice, uint32_t port_num, SharedPtr buf) {} - virtual void set_buffer_size(size_t size); + virtual void set_buffer_size(BufferFactory& bufs, size_t size); SampleRate sample_rate() const { return _srate; } size_t buffer_size() const { return _buffer_size; } diff --git a/src/engine/NodeFactory.cpp b/src/engine/NodeFactory.cpp index 8b2e93d9..c6dca64b 100644 --- a/src/engine/NodeFactory.cpp +++ b/src/engine/NodeFactory.cpp @@ -24,14 +24,14 @@ #include "redlandmm/World.hpp" #include "raul/Atom.hpp" #include "module/World.hpp" -#include "NodeFactory.hpp" -#include "ThreadManager.hpp" #include "internals/Note.hpp" #include "internals/Trigger.hpp" #include "internals/Controller.hpp" -#include "internals/Transport.hpp" -#include "PatchImpl.hpp" +#include "Engine.hpp" #include "InternalPlugin.hpp" +#include "NodeFactory.hpp" +#include "PatchImpl.hpp" +#include "ThreadManager.hpp" #ifdef HAVE_LADSPA_H #include "LADSPANode.hpp" #include "LADSPAPlugin.hpp" @@ -137,21 +137,22 @@ NodeFactory::load_plugins() void NodeFactory::load_internal_plugins() { - // This is a touch gross... + // FIXME: This is a touch gross... + + const SampleRate r = 48000; + const size_t s = sizeof(Sample); - PatchImpl* parent = new PatchImpl(*_world->local_engine, "dummy", 1, NULL, 1, 1, 1); + Engine& engine = *_world->local_engine; + PatchImpl* parent = new PatchImpl(engine, "dummy", 1, NULL, r, s, 1); NodeImpl* n = NULL; - n = new NoteNode("foo", 1, parent, 1, 1); - _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); - delete n; - n = new TriggerNode("foo", 1, parent, 1, 1); + n = new NoteNode(*engine.buffer_factory(), "foo", 1, parent, r, s); _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); delete n; - n = new ControllerNode("foo", 1, parent, 1, 1); + n = new TriggerNode(*engine.buffer_factory(), "foo", 1, parent, r, s); _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); delete n; - n = new TransportNode("foo", 1, parent, 1, 1); + n = new ControllerNode(*engine.buffer_factory(), "foo", 1, parent, r, s); _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); delete n; diff --git a/src/engine/NodeImpl.hpp b/src/engine/NodeImpl.hpp index 67ab0898..55ff5417 100644 --- a/src/engine/NodeImpl.hpp +++ b/src/engine/NodeImpl.hpp @@ -29,10 +29,12 @@ namespace Ingen { namespace Shared { class Plugin; class Node; class Port; } class Buffer; -class PluginImpl; +class BufferFactory; +class Engine; +class MessageContext; class PatchImpl; +class PluginImpl; class PortImpl; -class MessageContext; class ProcessContext; @@ -69,7 +71,7 @@ public: * Preprocessor thread, poly is actually applied by apply_poly. * \return true on success. */ - virtual bool prepare_poly(uint32_t poly) = 0; + virtual bool prepare_poly(BufferFactory& bufs, uint32_t poly) = 0; /** Apply a new (external) polyphony value. * @@ -131,7 +133,7 @@ public: */ virtual void process(ProcessContext& context) = 0; - virtual void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) = 0; + virtual void set_port_buffer(uint32_t voice, uint32_t port_num, SharedPtr buf) = 0; virtual uint32_t num_ports() const = 0; @@ -167,7 +169,7 @@ public: */ virtual const Shared::Plugin* plugin() const = 0; - virtual void set_buffer_size(size_t size) = 0; + virtual void set_buffer_size(BufferFactory& bufs, size_t size) = 0; }; diff --git a/src/engine/ObjectBuffer.cpp b/src/engine/ObjectBuffer.cpp index a26eaea5..f96c910f 100644 --- a/src/engine/ObjectBuffer.cpp +++ b/src/engine/ObjectBuffer.cpp @@ -34,56 +34,41 @@ namespace Ingen { using namespace Shared; -/** Allocate a new string buffer. - * \a capacity is in bytes. +/** Allocate a new object buffer. + * \a capacity is in bytes, including LV2_Object header */ ObjectBuffer::ObjectBuffer(size_t capacity) : Buffer(DataType(DataType::VALUE), capacity) { - capacity = std::max(capacity, (size_t)32); - cerr << "Creating Object Buffer " << _buf << " capacity = " << capacity << endl; - _local_buf = (LV2_Object*)malloc(sizeof(LV2_Object) + capacity); - _buf = _local_buf; - clear(); + //cerr << "Creating Object Buffer capacity = " << capacity << endl; + assert(capacity >= sizeof(LV2_Object)); + +#ifdef HAVE_POSIX_MEMALIGN + const int ret = posix_memalign((void**)&_buf, 16, capacity); +#else + _buf = (LV2_Object*)malloc(capacity); + const int ret = (_buf != NULL) ? 0 : -1; +#endif + + if (ret != 0) { + cerr << "Failed to allocate buffer. Aborting." << endl; + exit(EXIT_FAILURE); + } + + object()->type = 0; + object()->size = capacity; } void ObjectBuffer::clear() { - // nil + // null _buf->type = 0; _buf->size = 0; } -/** Use another buffer's data instead of the local one. - * - * This buffer will essentially be identical to @a buf after this call. - */ -bool -ObjectBuffer::join(Buffer* buf) -{ - assert(buf != this); - ObjectBuffer* sbuf = dynamic_cast(buf); - if (!sbuf) - return false; - - _buf = sbuf->_local_buf; - _joined_buf = sbuf; - - return true; -} - - -void -ObjectBuffer::unjoin() -{ - _joined_buf = NULL; - _buf = _local_buf; -} - - void ObjectBuffer::copy(Context& context, const Buffer* src_buf) { @@ -91,27 +76,51 @@ ObjectBuffer::copy(Context& context, const Buffer* src_buf) if (!src || src == this || src->_buf == _buf) return; - // Copy if src is a POD object only, that fits - if (src->_buf->type != 0 && src->_buf->size <= size()) - memcpy(_buf, src->_buf, sizeof(LV2_Object) + src->_buf->size); + // Copy only if src is a POD object that fits + if (src->_buf->type != 0 && src_buf->size() <= size()) + memcpy(_buf, src->_buf, sizeof(LV2_Object) + src_buf->size()); } void ObjectBuffer::resize(size_t size) { - const bool using_local_data = (_buf == _local_buf); - const uint32_t contents_size = sizeof(LV2_Object) + _buf->size; + const uint32_t contents_size = sizeof(LV2_Object) + _buf->size; - _local_buf = (LV2_Object*)realloc(_buf, sizeof(LV2_Object) + size); - _size = size; + _buf = (LV2_Object*)realloc(_buf, sizeof(LV2_Object) + size); + _size = size; // If we shrunk and chopped the current contents, clear corrupt data if (size < contents_size) clear(); +} + - if (using_local_data) - _buf = _local_buf; +void* +ObjectBuffer::port_data(DataType port_type) +{ + switch (port_type.symbol()) { + case DataType::CONTROL: + return object()->body; + case DataType::AUDIO: + return ((LV2_Vector_Body*)object()->body)->elems; + default: + return _buf; + } +} + + +const void* +ObjectBuffer::port_data(DataType port_type) const +{ + switch (port_type.symbol()) { + case DataType::CONTROL: + return _buf + sizeof(LV2_Object); + case DataType::AUDIO: + return _buf + sizeof(LV2_Object) + sizeof(LV2_Vector_Body); + default: + return _buf; + } } diff --git a/src/engine/ObjectBuffer.hpp b/src/engine/ObjectBuffer.hpp index a1d198e9..ec28453b 100644 --- a/src/engine/ObjectBuffer.hpp +++ b/src/engine/ObjectBuffer.hpp @@ -33,22 +33,19 @@ public: void clear(); - void* raw_data() { return (void*)_buf; } - const void* raw_data() const { return (void*)_buf; } - - LV2_Object* data() { return _buf; } - const LV2_Object* data() const { return _buf; } - - bool join(Buffer* buf); - void unjoin(); + void* port_data(Shared::DataType port_type); + const void* port_data(Shared::DataType port_type) const; void copy(Context& context, const Buffer* src); + void mix(Context& context, const Buffer* src) {} void resize(size_t size); + LV2_Object* object() { return _buf; } + const LV2_Object* object() const { return _buf; } + private: - LV2_Object* _buf; ///< Contents (_local_buf or belongs to _joined_buf) - LV2_Object* _local_buf; ///< Local contents + LV2_Object* _buf; ///< Contents }; diff --git a/src/engine/ObjectSender.cpp b/src/engine/ObjectSender.cpp index 072d6c52..02886be5 100644 --- a/src/engine/ObjectSender.cpp +++ b/src/engine/ObjectSender.cpp @@ -142,7 +142,7 @@ ObjectSender::send_port(ClientInterface* client, const PortImpl* port, bool bund // Send control value if (port->type() == DataType::CONTROL) { - const Sample& value = dynamic_cast(port->buffer(0))->value_at(0); + const Sample& value = PtrCast(port->buffer(0))->value_at(0); client->set_port_value(port->path(), value); } diff --git a/src/engine/OutputPort.cpp b/src/engine/OutputPort.cpp index 8f6b7751..30d29fa0 100644 --- a/src/engine/OutputPort.cpp +++ b/src/engine/OutputPort.cpp @@ -29,14 +29,15 @@ namespace Ingen { namespace Shared { class Patch; } using namespace Shared; -OutputPort::OutputPort(NodeImpl* parent, +OutputPort::OutputPort(BufferFactory& bufs, + NodeImpl* parent, const string& name, uint32_t index, uint32_t poly, DataType type, const Raul::Atom& value, size_t buffer_size) - : PortImpl(parent, name, index, poly, type, value, buffer_size) + : PortImpl(bufs, parent, name, index, poly, type, value, buffer_size) { if (!dynamic_cast(parent)) add_property("rdf:type", Raul::Atom(Raul::Atom::URI, "lv2:OutputPort")); @@ -49,16 +50,17 @@ OutputPort::OutputPort(NodeImpl* parent, void OutputPort::pre_process(Context& context) { - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_write(context); + connect_buffers(); + for (uint32_t v = 0; v < _poly; ++v) + buffer(v)->prepare_write(context); } void OutputPort::post_process(Context& context) { - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_read(context); + for (uint32_t v = 0; v < _poly; ++v) + buffer(v)->prepare_read(context); //cerr << path() << " output post: buffer: " << buffer(0) << endl; diff --git a/src/engine/OutputPort.hpp b/src/engine/OutputPort.hpp index 15382332..baaa6680 100644 --- a/src/engine/OutputPort.hpp +++ b/src/engine/OutputPort.hpp @@ -39,7 +39,8 @@ namespace Ingen { class OutputPort : virtual public PortImpl { public: - OutputPort(NodeImpl* parent, + OutputPort(BufferFactory& bufs, + NodeImpl* parent, const std::string& name, uint32_t index, uint32_t poly, diff --git a/src/engine/PatchImpl.cpp b/src/engine/PatchImpl.cpp index b4fb8c31..2e02355f 100644 --- a/src/engine/PatchImpl.cpp +++ b/src/engine/PatchImpl.cpp @@ -98,17 +98,17 @@ PatchImpl::disable() bool -PatchImpl::prepare_internal_poly(uint32_t poly) +PatchImpl::prepare_internal_poly(BufferFactory& bufs, uint32_t poly) { assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); /* TODO: ports? internal/external poly? */ for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) - (*i)->prepare_poly(poly); + (*i)->prepare_poly(bufs, poly); for (Connections::iterator i = _connections.begin(); i != _connections.end(); ++i) - ((ConnectionImpl*)i->get())->prepare_poly(poly); + ((ConnectionImpl*)i->get())->prepare_poly(bufs, poly); /* FIXME: Deal with failure */ @@ -242,15 +242,15 @@ PatchImpl::process_single(ProcessContext& context) void -PatchImpl::set_buffer_size(size_t size) +PatchImpl::set_buffer_size(BufferFactory& bufs, size_t size) { - NodeBase::set_buffer_size(size); + NodeBase::set_buffer_size(bufs, size); assert(_buffer_size == size); CompiledPatch* const cp = _compiled_patch; for (size_t i=0; i < cp->size(); ++i) - (*cp)[i].node()->set_buffer_size(size); + (*cp)[i].node()->set_buffer_size(bufs, size); } @@ -342,7 +342,7 @@ PatchImpl::num_ports() const /** Create a port. Not realtime safe. */ PortImpl* -PatchImpl::create_port(const string& name, DataType type, size_t buffer_size, bool is_output) +PatchImpl::create_port(BufferFactory& bufs, const string& name, DataType type, size_t buffer_size, bool is_output) { if (type == DataType::UNKNOWN) { cerr << "[PatchImpl::create_port] Unknown port type " << type.uri() << endl; @@ -351,7 +351,7 @@ PatchImpl::create_port(const string& name, DataType type, size_t buffer_size, bo assert( !(type == DataType::UNKNOWN) ); - return new DuplexPort(this, name, num_ports(), _polyphony, type, Raul::Atom(), buffer_size, is_output); + return new DuplexPort(bufs, this, name, num_ports(), _polyphony, type, Raul::Atom(), buffer_size, is_output); } diff --git a/src/engine/PatchImpl.hpp b/src/engine/PatchImpl.hpp index 8bdbd21a..67f35869 100644 --- a/src/engine/PatchImpl.hpp +++ b/src/engine/PatchImpl.hpp @@ -63,14 +63,14 @@ public: void process(ProcessContext& context); - void set_buffer_size(size_t size); + void set_buffer_size(BufferFactory& bufs, size_t size); /** Prepare for a new (internal) polyphony value. * * Preprocessor thread, poly is actually applied by apply_internal_poly. * \return true on success. */ - bool prepare_internal_poly(uint32_t poly); + bool prepare_internal_poly(BufferFactory& bufs, uint32_t poly); /** Apply a new (internal) polyphony value. * @@ -96,7 +96,7 @@ public: uint32_t num_ports() const; - PortImpl* create_port(const std::string& name, Shared::DataType type, size_t buffer_size, bool is_output); + PortImpl* create_port(BufferFactory& bufs, const std::string& name, Shared::DataType type, size_t buffer_size, bool is_output); void add_input(Raul::List::Node* port) { _input_ports.push_back(port); } ///< Preprocesser thread void add_output(Raul::List::Node* port) { _output_ports.push_back(port); } ///< Preprocessor thread Raul::List::Node* remove_port(const std::string& name); diff --git a/src/engine/PatchPlugin.hpp b/src/engine/PatchPlugin.hpp index 4a5a39e9..99c9359f 100644 --- a/src/engine/PatchPlugin.hpp +++ b/src/engine/PatchPlugin.hpp @@ -41,7 +41,8 @@ public: : PluginImpl(Plugin::Patch, uri) {} - NodeImpl* instantiate(const std::string& name, + NodeImpl* instantiate(BufferFactory& bufs, + const std::string& name, bool polyphonic, Ingen::PatchImpl* parent, Engine& engine) diff --git a/src/engine/PluginImpl.cpp b/src/engine/PluginImpl.cpp index 7a481c55..cdc6ffb2 100644 --- a/src/engine/PluginImpl.cpp +++ b/src/engine/PluginImpl.cpp @@ -20,7 +20,6 @@ #include "internals/Note.hpp" #include "internals/Trigger.hpp" #include "internals/Controller.hpp" -#include "internals/Transport.hpp" using namespace std; diff --git a/src/engine/PluginImpl.hpp b/src/engine/PluginImpl.hpp index 38b6ad53..6dcf77a5 100644 --- a/src/engine/PluginImpl.hpp +++ b/src/engine/PluginImpl.hpp @@ -32,6 +32,7 @@ namespace Ingen { class PatchImpl; class NodeImpl; class Engine; +class BufferFactory; /** Implementation of a plugin (internal code, or a loaded shared library). @@ -50,7 +51,8 @@ public: , _module(NULL) {} - virtual NodeImpl* instantiate(const std::string& name, + virtual NodeImpl* instantiate(BufferFactory& bufs, + const std::string& name, bool polyphonic, Ingen::PatchImpl* parent, Engine& engine) = 0; diff --git a/src/engine/PortImpl.cpp b/src/engine/PortImpl.cpp index ecb0d20b..169c7289 100644 --- a/src/engine/PortImpl.cpp +++ b/src/engine/PortImpl.cpp @@ -24,6 +24,7 @@ #include "AudioBuffer.hpp" #include "EventBuffer.hpp" #include "Engine.hpp" +#include "BufferFactory.hpp" #include "LV2Object.hpp" #include "NodeImpl.hpp" #include "ObjectBuffer.hpp" @@ -38,7 +39,8 @@ namespace Ingen { using namespace Shared; -PortImpl::PortImpl(NodeImpl* const node, +PortImpl::PortImpl(BufferFactory& bufs, + NodeImpl* const node, const string& name, uint32_t index, uint32_t poly, @@ -46,23 +48,27 @@ PortImpl::PortImpl(NodeImpl* const node, const Atom& value, size_t buffer_size) : GraphObjectImpl(node, name, (type == DataType::AUDIO || type == DataType::CONTROL)) + , _bufs(bufs) , _index(index) , _poly(poly) , _buffer_size(buffer_size) , _type(type) , _value(value) - , _fixed_buffers(false) , _broadcast(false) , _set_by_user(false) , _last_broadcasted_value(_value.type() == Atom::FLOAT ? _value.get_float() : 0.0f) // default? , _context(Context::AUDIO) - , _buffers(new Array(poly)) + , _buffers(new Array< SharedPtr >(poly)) , _prepared_buffers(NULL) { assert(node != NULL); assert(_poly > 0); - allocate_buffers(); + _buffers->alloc(_poly); + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v) = bufs.get(_type, _buffer_size); + + _prepared_buffers = _buffers; if (node->parent() == NULL) _polyphonic = false; @@ -80,9 +86,8 @@ PortImpl::PortImpl(NodeImpl* const node, PortImpl::~PortImpl() { - if (!_fixed_buffers) - for (uint32_t i=0; i < _poly; ++i) - delete _buffers->at(i); + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v).reset(); delete _buffers; } @@ -99,16 +104,16 @@ PortImpl::set_polyphonic(Maid& maid, bool p) bool -PortImpl::prepare_poly(uint32_t poly) +PortImpl::prepare_poly(BufferFactory& bufs, uint32_t poly) { if (!_polyphonic || !_parent->polyphonic()) return true; /* FIXME: poly never goes down, harsh on memory.. */ if (poly > _poly) { - _prepared_buffers = new Array(poly, *_buffers); + _prepared_buffers = new Array< SharedPtr >(poly, *_buffers); for (uint32_t i = _poly; i < _prepared_buffers->size(); ++i) - _prepared_buffers->at(i) = Buffer::create(_type, _buffer_size); + _prepared_buffers->at(i) = bufs.get(_type, _buffer_size); } return true; @@ -138,24 +143,12 @@ PortImpl::apply_poly(Maid& maid, uint32_t poly) void -PortImpl::allocate_buffers() -{ - _buffers->alloc(_poly); - - for (uint32_t i=0; i < _poly; ++i) - _buffers->at(i) = Buffer::create(_type, _buffer_size); - - _prepared_buffers = _buffers; -} - - -void -PortImpl::set_buffer_size(size_t size) +PortImpl::set_buffer_size(BufferFactory& bufs, size_t size) { _buffer_size = size; - for (uint32_t i=0; i < _poly; ++i) - _buffers->at(i)->resize(size); + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v)->resize(size); connect_buffers(); } @@ -164,16 +157,16 @@ PortImpl::set_buffer_size(size_t size) void PortImpl::connect_buffers() { - for (uint32_t i=0; i < _poly; ++i) - PortImpl::parent_node()->set_port_buffer(i, _index, buffer(i)); + for (uint32_t v = 0; v < _poly; ++v) + PortImpl::parent_node()->set_port_buffer(v, _index, buffer(v)); } void PortImpl::clear_buffers() { - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->clear(); + for (uint32_t v = 0; v < _poly; ++v) + buffer(v)->clear(); } @@ -186,16 +179,16 @@ PortImpl::broadcast_value(Context& context, bool force) break; case DataType::AUDIO: case DataType::CONTROL: - val = ((AudioBuffer*)buffer(0))->value_at(0); + val = ((AudioBuffer*)buffer(0).get())->value_at(0); break; case DataType::EVENTS: - if (((EventBuffer*)buffer(0))->event_count() > 0) { + if (((EventBuffer*)buffer(0).get())->event_count() > 0) { const Events::SendPortActivity ev(context.engine(), context.start(), this); context.event_sink().write(sizeof(ev), &ev); } break; case DataType::VALUE: - LV2Object::to_atom(context.engine().world(), ((ObjectBuffer*)buffer(0))->data(), val); + LV2Object::to_atom(context.engine().world(), ((ObjectBuffer*)buffer(0).get())->object(), val); break; } diff --git a/src/engine/PortImpl.hpp b/src/engine/PortImpl.hpp index b9c8dc52..6b8c1b56 100644 --- a/src/engine/PortImpl.hpp +++ b/src/engine/PortImpl.hpp @@ -36,6 +36,7 @@ namespace Ingen { class NodeImpl; class Buffer; class ProcessContext; +class BufferFactory; /** A port on a Node. @@ -49,8 +50,6 @@ class ProcessContext; class PortImpl : public GraphObjectImpl, public Ingen::Shared::Port { public: - virtual ~PortImpl(); - /** A port's parent is always a node, so static cast should be safe */ NodeImpl* parent_node() const { return (NodeImpl*)_parent; } @@ -60,7 +59,7 @@ public: * * Preprocessor thread, poly is actually applied by apply_poly. */ - virtual bool prepare_poly(uint32_t poly); + virtual bool prepare_poly(BufferFactory& bufs, uint32_t poly); /** Apply a new polyphony value. * @@ -73,16 +72,10 @@ public: const Raul::Atom& value() const { return _value; } void set_value(const Raul::Atom& v) { _value = v; } - inline Buffer* buffer(uint32_t voice) const { - Buffer* const buf = _buffers->at(voice); - if (buf->is_joined()) { - assert(buf->joined_buffer()); - return buf->joined_buffer(); - } else { - return buf; - } + inline SharedPtr buffer(uint32_t voice) const { + return _buffers->at(voice); } - inline Buffer* prepared_buffer(uint32_t voice) const { + inline SharedPtr prepared_buffer(uint32_t voice) const { return _prepared_buffers->at(voice); } @@ -105,10 +98,7 @@ public: return (_type == Shared::DataType::CONTROL) ? 1 : _buffer_size; } - virtual void set_buffer_size(size_t size); - - void fixed_buffers(bool b) { _fixed_buffers = b; } - bool fixed_buffers() { return _fixed_buffers; } + void set_buffer_size(BufferFactory& factory, size_t size); void broadcast(bool b) { _broadcast = b; } bool broadcast() { return _broadcast; } @@ -121,7 +111,8 @@ public: void set_context(Context::ID c) { _context = c; } protected: - PortImpl(NodeImpl* node, + PortImpl(BufferFactory& bufs, + NodeImpl* node, const std::string& name, uint32_t index, uint32_t poly, @@ -129,23 +120,24 @@ protected: const Raul::Atom& value, size_t buffer_size); - virtual void allocate_buffers(); - + BufferFactory& _bufs; uint32_t _index; uint32_t _poly; uint32_t _buffer_size; Shared::DataType _type; Raul::Atom _value; - bool _fixed_buffers; bool _broadcast; bool _set_by_user; Raul::Atom _last_broadcasted_value; - Context::ID _context; - Raul::Array* _buffers; + Context::ID _context; + Raul::Array< SharedPtr >* _buffers; // Dynamic polyphony - Raul::Array* _prepared_buffers; + Raul::Array< SharedPtr >* _prepared_buffers; + + friend class Engine; + virtual ~PortImpl(); }; diff --git a/src/engine/QueuedEngineInterface.cpp b/src/engine/QueuedEngineInterface.cpp index 55e02206..d2ff74df 100644 --- a/src/engine/QueuedEngineInterface.cpp +++ b/src/engine/QueuedEngineInterface.cpp @@ -158,11 +158,11 @@ QueuedEngineInterface::put(const URI& uri, bool meta = uri.substr(0, 6) == "meta:#"; URI subject(meta ? (string("path:/") + uri.substr(6)) : uri.str()); - cerr << "ENGINE PUT " << subject << " {" << endl; + /*cerr << "ENGINE PUT " << subject << " {" << endl; typedef Resource::Properties::const_iterator iterator; for (iterator i = properties.begin(); i != properties.end(); ++i) cerr << "\t" << i->first << " = " << i->second << " :: " << i->second.type() << endl; - cerr << "}" << endl; + cerr << "}" << endl;*/ push_queued(new Events::SetMetadata(_engine, _responder, now(), this, true, meta, subject, properties)); } diff --git a/src/engine/events/Connect.cpp b/src/engine/events/Connect.cpp index 6c7bf2fe..62e32916 100644 --- a/src/engine/events/Connect.cpp +++ b/src/engine/events/Connect.cpp @@ -133,7 +133,7 @@ Connect::pre_process() return; } - _connection = SharedPtr(new ConnectionImpl(_src_port, _dst_port)); + _connection = SharedPtr(new ConnectionImpl(*_engine.buffer_factory(), _src_port, _dst_port)); _patch_listnode = new PatchImpl::Connections::Node(_connection); _port_listnode = new InputPort::Connections::Node(_connection); diff --git a/src/engine/events/CreateNode.cpp b/src/engine/events/CreateNode.cpp index 6c70d481..9a9e0826 100644 --- a/src/engine/events/CreateNode.cpp +++ b/src/engine/events/CreateNode.cpp @@ -90,7 +90,7 @@ CreateNode::pre_process() if (_patch && _plugin) { - _node = _plugin->instantiate(_path.name(), _polyphonic, _patch, _engine); + _node = _plugin->instantiate(*_engine.buffer_factory(), _path.name(), _polyphonic, _patch, _engine); if (_node != NULL) { _node->properties().insert(_properties.begin(), _properties.end()); diff --git a/src/engine/events/CreatePort.cpp b/src/engine/events/CreatePort.cpp index 5ea78fb0..848092b1 100644 --- a/src/engine/events/CreatePort.cpp +++ b/src/engine/events/CreatePort.cpp @@ -98,7 +98,7 @@ CreatePort::pre_process() const uint32_t old_num_ports = _patch->num_ports(); - _patch_port = _patch->create_port(_path.name(), _data_type, buffer_size, _is_output); + _patch_port = _patch->create_port(*_engine.buffer_factory(), _path.name(), _data_type, buffer_size, _is_output); if (_patch->parent()) _patch_port->set_property("rdf:instanceOf", Atom(Atom::URI, _patch_port->meta_uri().str())); diff --git a/src/engine/events/RequestMetadata.cpp b/src/engine/events/RequestMetadata.cpp index e6ba7f5d..2ef3edda 100644 --- a/src/engine/events/RequestMetadata.cpp +++ b/src/engine/events/RequestMetadata.cpp @@ -95,10 +95,10 @@ RequestMetadata::execute(ProcessContext& context) PortImpl* port = dynamic_cast(_resource); if (port) { if (port->type() == DataType::CONTROL || port->type() == DataType::AUDIO) - _value = ((AudioBuffer*)port->buffer(0))->value_at(0); // TODO: offset + _value = ((AudioBuffer*)port->buffer(0).get())->value_at(0); // TODO: offset else if (port->type() == DataType::VALUE) LV2Object::to_atom(context.engine().world(), - ((ObjectBuffer*)port->buffer(0))->data(), _value); + ((ObjectBuffer*)port->buffer(0).get())->object(), _value); } else { _resource = 0; } diff --git a/src/engine/events/SetMetadata.cpp b/src/engine/events/SetMetadata.cpp index 3d62781d..caed4909 100644 --- a/src/engine/events/SetMetadata.cpp +++ b/src/engine/events/SetMetadata.cpp @@ -162,7 +162,7 @@ SetMetadata::pre_process() } else if (key.str() == "ingen:polyphony") { if (value.type() == Atom::INT) { op = POLYPHONY; - _patch->prepare_internal_poly(value.get_int32()); + _patch->prepare_internal_poly(*_engine.buffer_factory(), value.get_int32()); } else { _error = BAD_TYPE; } diff --git a/src/engine/events/SetPortValue.cpp b/src/engine/events/SetPortValue.cpp index 6b9d8d9e..2a33b798 100644 --- a/src/engine/events/SetPortValue.cpp +++ b/src/engine/events/SetPortValue.cpp @@ -151,7 +151,7 @@ SetPortValue::apply(Context& context) /*} else if (_port->buffer(0)->capacity() < _data_size) { _error = NO_SPACE;*/ } else { - Buffer* const buf = _port->buffer(0); + Buffer* const buf = _port->buffer(0).get(); AudioBuffer* const abuf = dynamic_cast(buf); if (abuf) { if (_value.type() != Atom::FLOAT) { @@ -160,12 +160,12 @@ SetPortValue::apply(Context& context) } if (_omni) { - for (uint32_t i=0; i < _port->poly(); ++i) - ((AudioBuffer*)_port->buffer(i))->set_value( + for (uint32_t v = 0; v < _port->poly(); ++v) + ((AudioBuffer*)_port->buffer(v).get())->set_value( _value.get_float(), start, _time); } else { if (_voice_num < _port->poly()) - ((AudioBuffer*)_port->buffer(_voice_num))->set_value( + ((AudioBuffer*)_port->buffer(_voice_num).get())->set_value( _value.get_float(), start, _time); else _error = ILLEGAL_VOICE; @@ -198,10 +198,10 @@ SetPortValue::apply(Context& context) ObjectBuffer* const obuf = dynamic_cast(buf); if (obuf) { - obuf->data()->size = obuf->size() - sizeof(LV2_Object); - if (LV2Object::from_atom(_engine.world(), _value, obuf->data())) { - cout << "Converted atom " << _value << " :: " << obuf->data()->type - << " * " << obuf->data()->size << " @ " << obuf->data() << endl; + obuf->object()->size = obuf->size() - sizeof(LV2_Object); + if (LV2Object::from_atom(_engine.world(), _value, obuf->object())) { + cout << "Converted atom " << _value << " :: " << obuf->object()->type + << " * " << obuf->object()->size << " @ " << obuf->object() << endl; return; } else { cerr << "WARNING: Failed to convert atom to LV2 object" << endl; diff --git a/src/engine/internals/Controller.cpp b/src/engine/internals/Controller.cpp index 9788206e..3d003a43 100644 --- a/src/engine/internals/Controller.cpp +++ b/src/engine/internals/Controller.cpp @@ -38,36 +38,37 @@ using namespace Shared; static InternalPlugin controller_plugin(NS_INTERNALS "Controller", "controller"); -ControllerNode::ControllerNode(const string& path, - bool polyphonic, - PatchImpl* parent, - SampleRate srate, - size_t buffer_size) +ControllerNode::ControllerNode(BufferFactory& bufs, + const string& path, + bool polyphonic, + PatchImpl* parent, + SampleRate srate, + size_t buffer_size) : NodeBase(&controller_plugin, path, false, parent, srate, buffer_size) , _learning(false) { _ports = new Raul::Array(6); - _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENTS, Raul::Atom(), _buffer_size); + _midi_in_port = new InputPort(bufs, this, "input", 0, 1, DataType::EVENTS, Raul::Atom(), _buffer_size); _ports->at(0) = _midi_in_port; - _param_port = new InputPort(this, "controller", 1, 1, DataType::CONTROL, 0.0f, 1); + _param_port = new InputPort(bufs, this, "controller", 1, 1, DataType::CONTROL, 0.0f, sizeof(Sample)); _param_port->set_property("lv2:minimum", 0.0f); _param_port->set_property("lv2:maximum", 127.0f); _param_port->set_property("lv2:integer", true); _ports->at(1) = _param_port; - _log_port = new InputPort(this, "logarithmic", 2, 1, DataType::CONTROL, 0.0f, 1); + _log_port = new InputPort(bufs, this, "logarithmic", 2, 1, DataType::CONTROL, 0.0f, sizeof(Sample)); _log_port->set_property("lv2:toggled", true); _ports->at(2) = _log_port; - _min_port = new InputPort(this, "minimum", 3, 1, DataType::CONTROL, 0.0f, 1); + _min_port = new InputPort(bufs, this, "minimum", 3, 1, DataType::CONTROL, 0.0f, sizeof(Sample)); _ports->at(3) = _min_port; - _max_port = new InputPort(this, "maximum", 4, 1, DataType::CONTROL, 1.0f, 1); + _max_port = new InputPort(bufs, this, "maximum", 4, 1, DataType::CONTROL, 1.0f, sizeof(Sample)); _ports->at(4) = _max_port; - _audio_port = new OutputPort(this, "ar_output", 5, 1, DataType::AUDIO, 0.0f, _buffer_size); + _audio_port = new OutputPort(bufs, this, "ar_output", 5, 1, DataType::AUDIO, 0.0f, _buffer_size); _ports->at(5) = _audio_port; } @@ -83,7 +84,7 @@ ControllerNode::process(ProcessContext& context) uint16_t size = 0; uint8_t* buf = NULL; - EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); + EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0).get(); midi_in->rewind(); @@ -110,15 +111,15 @@ ControllerNode::control(ProcessContext& context, uint8_t control_num, uint8_t va if (_learning) { _param_port->set_value(control_num); - ((AudioBuffer*)_param_port->buffer(0))->set_value( + ((AudioBuffer*)_param_port->buffer(0).get())->set_value( (float)control_num, context.start(), context.end()); _param_port->broadcast_value(context, true); _learning = false; } - const Sample min_port_val = ((AudioBuffer*)_min_port->buffer(0))->value_at(0); - const Sample max_port_val = ((AudioBuffer*)_max_port->buffer(0))->value_at(0); - const Sample log_port_val = ((AudioBuffer*)_log_port->buffer(0))->value_at(0); + const Sample min_port_val = ((AudioBuffer*)_min_port->buffer(0).get())->value_at(0); + const Sample max_port_val = ((AudioBuffer*)_max_port->buffer(0).get())->value_at(0); + const Sample log_port_val = ((AudioBuffer*)_log_port->buffer(0).get())->value_at(0); if (log_port_val > 0.0f) { // haaaaack, stupid negatives and logarithms @@ -132,8 +133,8 @@ ControllerNode::control(ProcessContext& context, uint8_t control_num, uint8_t va scaled_value = ((nval) * (max_port_val - min_port_val)) + min_port_val; } - if (control_num == ((AudioBuffer*)_param_port->buffer(0))->value_at(0)) - ((AudioBuffer*)_audio_port->buffer(0))->set_value(scaled_value, context.start(), time); + if (control_num == ((AudioBuffer*)_param_port->buffer(0).get())->value_at(0)) + ((AudioBuffer*)_audio_port->buffer(0).get())->set_value(scaled_value, context.start(), time); } diff --git a/src/engine/internals/Controller.hpp b/src/engine/internals/Controller.hpp index 4f02a9ca..bca26ed5 100644 --- a/src/engine/internals/Controller.hpp +++ b/src/engine/internals/Controller.hpp @@ -39,7 +39,7 @@ namespace Internals { class ControllerNode : public NodeBase { public: - ControllerNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); + ControllerNode(BufferFactory& bufs, const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); void process(ProcessContext& context); diff --git a/src/engine/internals/Note.cpp b/src/engine/internals/Note.cpp index 4dc11891..ffe9ceca 100644 --- a/src/engine/internals/Note.cpp +++ b/src/engine/internals/Note.cpp @@ -40,7 +40,7 @@ using namespace Shared; static InternalPlugin note_plugin(NS_INTERNALS "Note", "note"); -NoteNode::NoteNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) +NoteNode::NoteNode(BufferFactory& bufs, const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) : NodeBase(¬e_plugin, path, polyphonic, parent, srate, buffer_size) , _voices(new Raul::Array(_polyphony)) , _prepared_voices(NULL) @@ -48,22 +48,22 @@ NoteNode::NoteNode(const string& path, bool polyphonic, PatchImpl* parent, Sampl { _ports = new Raul::Array(5); - _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENTS, Raul::Atom(), _buffer_size); + _midi_in_port = new InputPort(bufs, this, "input", 0, 1, DataType::EVENTS, Raul::Atom(), _buffer_size); _ports->at(0) = _midi_in_port; - _freq_port = new OutputPort(this, "frequency", 1, _polyphony, DataType::AUDIO, 440.0f, _buffer_size); + _freq_port = new OutputPort(bufs, this, "frequency", 1, _polyphony, DataType::AUDIO, 440.0f, _buffer_size); _ports->at(1) = _freq_port; - _vel_port = new OutputPort(this, "velocity", 2, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); + _vel_port = new OutputPort(bufs, this, "velocity", 2, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); _vel_port->set_property("lv2:minimum", 0.0f); _vel_port->set_property("lv2:maximum", 1.0f); _ports->at(2) = _vel_port; - _gate_port = new OutputPort(this, "gate", 3, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); + _gate_port = new OutputPort(bufs, this, "gate", 3, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); _gate_port->set_property("lv2:toggled", true); _ports->at(3) = _gate_port; - _trig_port = new OutputPort(this, "trigger", 4, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); + _trig_port = new OutputPort(bufs, this, "trigger", 4, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); _trig_port->set_property("lv2:toggled", true); _ports->at(4) = _trig_port; } @@ -76,12 +76,12 @@ NoteNode::~NoteNode() bool -NoteNode::prepare_poly(uint32_t poly) +NoteNode::prepare_poly(BufferFactory& bufs, uint32_t poly) { if (!_polyphonic) return true; - NodeBase::prepare_poly(poly); + NodeBase::prepare_poly(bufs, poly); if (_prepared_voices && poly <= _prepared_voices->size()) return true; @@ -117,7 +117,7 @@ NoteNode::apply_poly(Raul::Maid& maid, uint32_t poly) void NoteNode::process(ProcessContext& context) { - EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); + EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0).get(); NodeBase::pre_process(context); uint32_t frames = 0; @@ -248,13 +248,18 @@ NoteNode::note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, F assert(_keys[voice->note].state == Key::Key::ON_ASSIGNED); assert(_keys[voice->note].voice == voice_num); - ((AudioBuffer*)_freq_port->buffer(voice_num))->set_value(note_to_freq(note_num), context.start(), time); - ((AudioBuffer*)_vel_port->buffer(voice_num))->set_value(velocity/127.0, context.start(), time); - ((AudioBuffer*)_gate_port->buffer(voice_num))->set_value(1.0f, context.start(), time); + ((AudioBuffer*)_freq_port->buffer(voice_num).get())->set_value( + note_to_freq(note_num), context.start(), time); + ((AudioBuffer*)_vel_port->buffer(voice_num).get())->set_value( + velocity/127.0, context.start(), time); + ((AudioBuffer*)_gate_port->buffer(voice_num).get())->set_value( + 1.0f, context.start(), time); // trigger (one sample) - ((AudioBuffer*)_trig_port->buffer(voice_num))->set_value(1.0f, context.start(), time); - ((AudioBuffer*)_trig_port->buffer(voice_num))->set_value(0.0f, context.start(), time + 1); + ((AudioBuffer*)_trig_port->buffer(voice_num).get())->set_value( + 1.0f, context.start(), time); + ((AudioBuffer*)_trig_port->buffer(voice_num).get())->set_value( + 0.0f, context.start(), time + 1); assert(key->state == Key::Key::ON_ASSIGNED); assert(voice->state == Voice::Voice::ACTIVE); @@ -321,7 +326,7 @@ NoteNode::free_voice(ProcessContext& context, uint32_t voice, FrameTime time) assert(replace_key->state == Key::ON_UNASSIGNED); // Change the freq but leave the gate high and don't retrigger - ((AudioBuffer*)_freq_port->buffer(voice))->set_value(note_to_freq(replace_key_num), context.start(), time); + ((AudioBuffer*)_freq_port->buffer(voice).get())->set_value(note_to_freq(replace_key_num), context.start(), time); replace_key->state = Key::ON_ASSIGNED; replace_key->voice = voice; @@ -331,7 +336,7 @@ NoteNode::free_voice(ProcessContext& context, uint32_t voice, FrameTime time) } else { // No new note for voice, deactivate (set gate low) //cerr << "[NoteNode] Note off. Key " << (int)note_num << ", Voice " << voice << " Killed" << endl; - ((AudioBuffer*)_gate_port->buffer(voice))->set_value(0.0f, context.start(), time); + ((AudioBuffer*)_gate_port->buffer(voice).get())->set_value(0.0f, context.start(), time); (*_voices)[voice].state = Voice::FREE; } } @@ -348,7 +353,7 @@ NoteNode::all_notes_off(ProcessContext& context, FrameTime time) // FIXME: set all keys to Key::OFF? for (uint32_t i = 0; i < _polyphony; ++i) { - ((AudioBuffer*)_gate_port->buffer(i))->set_value(0.0f, context.start(), time); + ((AudioBuffer*)_gate_port->buffer(i).get())->set_value(0.0f, context.start(), time); (*_voices)[i].state = Voice::FREE; } } diff --git a/src/engine/internals/Note.hpp b/src/engine/internals/Note.hpp index d4c4c851..7f091231 100644 --- a/src/engine/internals/Note.hpp +++ b/src/engine/internals/Note.hpp @@ -39,10 +39,10 @@ namespace Internals { class NoteNode : public NodeBase { public: - NoteNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); + NoteNode(BufferFactory& bufs, const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); ~NoteNode(); - bool prepare_poly(uint32_t poly); + bool prepare_poly(BufferFactory& bufs, uint32_t poly); bool apply_poly(Raul::Maid& maid, uint32_t poly); void process(ProcessContext& context); diff --git a/src/engine/internals/Transport.cpp b/src/engine/internals/Transport.cpp deleted file mode 100644 index 8fccc436..00000000 --- a/src/engine/internals/Transport.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007-2009 Dave Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "internals/Transport.hpp" -#include -#include "OutputPort.hpp" -#include "InternalPlugin.hpp" -#include "JackAudioDriver.hpp" -#include "PortImpl.hpp" -#include "util.hpp" -//#include "Engine.hpp" - -using namespace std; - -namespace Ingen { -namespace Internals { - -static InternalPlugin transport_plugin(NS_INTERNALS "Transport", "transport"); - -TransportNode::TransportNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) - : NodeBase(&transport_plugin, path, false, parent, srate, buffer_size) -{ -#if 0 - _num_ports = 10; - _ports.alloc(_num_ports); - - OutputPort* spb_port = new OutputPort(this, "Seconds per Beat", 0, 1, - // new PortInfo("Seconds per Beat", CONTROL, OUTPUT, 0, 0, 1), 1); - _ports.at(0) = spb_port; - - OutputPort* bpb_port = new OutputPort(this, "Beats per Bar", 1, 1, - // new PortInfo("Beats per Bar", CONTROL, OUTPUT, 0, 0, 1), 1); - _ports.at(1) = bpb_port; - - OutputPort* bar_port = new OutputPort(this, "Bar", 3, 1, -// new PortInfo("Bar", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(2) = bar_port; - - OutputPort* beat_port = new OutputPort(this, "Beat", 3, 1, - // new PortInfo("Beat", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(3) = beat_port; - - OutputPort* frame_port = new OutputPort(this, "Frame", 3, 1, - // new PortInfo("Frame", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(4) = frame_port; - - OutputPort* hour_port = new OutputPort(this, "Hour", 3, 1, - // new PortInfo("Hour", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(5) = hour_port; - - OutputPort* minute_port = new OutputPort(this, "Minute", 3, 1, - // new PortInfo("Minute", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(6) = minute_port; - - OutputPort* second_port = new OutputPort(this, "Second", 3, 1, - // new PortInfo("Second", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(7) = second_port; - - OutputPort* trg_port = new OutputPort(this, "Beat Tick", 2, 1, - // new PortInfo("Beat Tick", AUDIO, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(8) = trg_port; - - OutputPort* bar_trig_port = new OutputPort(this, "Bar Tick", 3, 1, - // new PortInfo("Bar Tick", AUDIO, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(9) = bar_trig_port; -#endif -} - - -void -TransportNode::process(ProcessContext& context) -{ - NodeBase::pre_process(context); -#if 0 - - // FIXME: this will die horribly with any driver other than jack (in theory) - const jack_position_t* const position = ((JackAudioDriver*)Engine::instance().audio_driver())->position(); - jack_transport_state_t state = ((JackAudioDriver*)Engine::instance().audio_driver())->transport_state(); - double bpm = position->beats_per_minute; - float bpb = position->beats_per_bar; - float spb = 60.0 / bpm; - - //cerr << "bpm = " << bpm << endl; - //cerr << "spb = " << spb << endl; - - if (position->valid & JackPositionBBT) { - cerr << "bar: " << position->bar << endl; - cerr << "beat: " << position->beat << endl; - cerr << "tick: " << position->tick << endl; - } else { - cerr << "No BBT" << endl; - } - - if (position->valid & JackBBTFrameOffset) { - cerr << "bbt_offset: " << position->bbt_offset << endl; - } else { - cerr << "No BBT offset" << endl; - } - - if (position->valid & JackPositionTimecode) { - double time = position->frame_time; - cerr << "Seconds: " << time << " : " << endl; - /*time /= 60.0; - cerr << "Minutes: " << time << " : "; - time /= 60.0; - cerr << "Hours: " << time << " : ";*/ - } else { - cerr << "No timecode." << endl; - } - - - ((OutputPort*)_ports.at(0))->buffer(0)->set(spb, 0, 0); - ((OutputPort*)_ports.at(1))->buffer(0)->set(bpb, 0, 0); - - // fill the trigger buffers with zeros - ((OutputPort*)_ports.at(2))->buffer(0)->set(0.0f, 0, nframes - 1); - ((OutputPort*)_ports.at(3))->buffer(0)->set(0.0f, 0, nframes - 1); - - // if the transport is rolling, add triggers at the right frame positions - if ((position->valid & JackTransportBBT) && (state == JackTransportRolling)) { - double frames_per_beat = position->frame_rate * spb; - double first_beat = (1.0f - position->tick / position->ticks_per_beat) * frames_per_beat; - int first_beat_no = position->beat; - if (first_beat >= frames_per_beat) { - first_beat -= frames_per_beat; - --first_beat_no; - } - for ( ; first_beat < nframes; first_beat += frames_per_beat) { - ((OutputPort*)_ports.at(2))->buffer(0)->set(1.0f, size_t(first_beat)); - if (first_beat_no % int(bpb) == 0) { - ((OutputPort*)_ports.at(3))->buffer(0)->set(1.0f, size_t(first_beat)); - ++first_beat_no; - } - } - } - #endif - - NodeBase::post_process(context); -} - - -} // namespace Ingen -} // namespace Internals - diff --git a/src/engine/internals/Transport.hpp b/src/engine/internals/Transport.hpp deleted file mode 100644 index 78e12f4f..00000000 --- a/src/engine/internals/Transport.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007-2009 Dave Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef TRANSPORTNODE_H -#define TRANSPORTNODE_H - -#include -#include -#include "NodeBase.hpp" - -namespace Ingen { -namespace Internals { - - -/** Transport Node, brings timing information into patches. - * - * This node uses the Jack transport API to get information about BPM, time - * signature, etc.. all sample accurate. Using this you can do - * tempo-synced effects or even synthesis, etc. - */ -class TransportNode : public NodeBase -{ -public: - TransportNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); - - virtual void process(ProcessContext& context); -}; - - -} // namespace Ingen -} // namespace Internals - -#endif // TRANSPORTNODE_H diff --git a/src/engine/internals/Trigger.cpp b/src/engine/internals/Trigger.cpp index 428a1b8f..15251f88 100644 --- a/src/engine/internals/Trigger.cpp +++ b/src/engine/internals/Trigger.cpp @@ -35,30 +35,30 @@ using namespace Shared; static InternalPlugin trigger_plugin(NS_INTERNALS "Trigger", "trigger"); -TriggerNode::TriggerNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) +TriggerNode::TriggerNode(BufferFactory& bufs, const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) : NodeBase(&trigger_plugin, path, false, parent, srate, buffer_size) , _learning(false) { _ports = new Raul::Array(5); - _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENTS, Raul::Atom(), _buffer_size); + _midi_in_port = new InputPort(bufs, this, "input", 0, 1, DataType::EVENTS, Raul::Atom(), _buffer_size); _ports->at(0) = _midi_in_port; - _note_port = new InputPort(this, "note", 1, 1, DataType::CONTROL, 60.0f, 1); + _note_port = new InputPort(bufs, this, "note", 1, 1, DataType::CONTROL, 60.0f, sizeof(Sample)); _note_port->set_property("lv2:minimum", 0.0f); _note_port->set_property("lv2:maximum", 127.0f); _note_port->set_property("lv2:integer", true); _ports->at(1) = _note_port; - _gate_port = new OutputPort(this, "gate", 2, 1, DataType::AUDIO, 0.0f, _buffer_size); + _gate_port = new OutputPort(bufs, this, "gate", 2, 1, DataType::AUDIO, 0.0f, _buffer_size); _gate_port->set_property("lv2:toggled", true); _ports->at(2) = _gate_port; - _trig_port = new OutputPort(this, "trigger", 3, 1, DataType::AUDIO, 0.0f, _buffer_size); + _trig_port = new OutputPort(bufs, this, "trigger", 3, 1, DataType::AUDIO, 0.0f, _buffer_size); _trig_port->set_property("lv2:toggled", true); _ports->at(3) = _trig_port; - _vel_port = new OutputPort(this, "velocity", 4, 1, DataType::AUDIO, 0.0f, _buffer_size); + _vel_port = new OutputPort(bufs, this, "velocity", 4, 1, DataType::AUDIO, 0.0f, _buffer_size); _vel_port->set_property("lv2:minimum", 0.0f); _vel_port->set_property("lv2:maximum", 1.0f); _ports->at(4) = _vel_port; @@ -76,7 +76,7 @@ TriggerNode::process(ProcessContext& context) uint16_t size = 0; uint8_t* buf = NULL; - EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); + EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0).get(); midi_in->rewind(); @@ -97,7 +97,7 @@ TriggerNode::process(ProcessContext& context) case MIDI_CMD_CONTROL: if (buf[1] == MIDI_CTL_ALL_NOTES_OFF || buf[1] == MIDI_CTL_ALL_SOUNDS_OFF) - ((AudioBuffer*)_gate_port->buffer(0))->set_value(0.0f, context.start(), time); + ((AudioBuffer*)_gate_port->buffer(0).get())->set_value(0.0f, context.start(), time); default: break; } @@ -118,7 +118,7 @@ TriggerNode::note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity if (_learning) { _note_port->set_value(note_num); - ((AudioBuffer*)_note_port->buffer(0))->set_value( + ((AudioBuffer*)_note_port->buffer(0).get())->set_value( (float)note_num, context.start(), context.end()); _note_port->broadcast_value(context, true); _learning = false; @@ -126,13 +126,13 @@ TriggerNode::note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity /*cerr << "[TriggerNode] " << path() << " Note " << (int)note_num << " on @ " << time << endl;*/ - Sample filter_note = ((AudioBuffer*)_note_port->buffer(0))->value_at(0); + Sample filter_note = ((AudioBuffer*)_note_port->buffer(0).get())->value_at(0); if (filter_note >= 0.0 && filter_note < 127.0 && (note_num == (uint8_t)filter_note)) { - ((AudioBuffer*)_gate_port->buffer(0))->set_value(1.0f, context.start(), time); - ((AudioBuffer*)_trig_port->buffer(0))->set_value(1.0f, context.start(), time); - ((AudioBuffer*)_trig_port->buffer(0))->set_value(0.0f, context.start(), time + 1); - ((AudioBuffer*)_vel_port->buffer(0))->set_value(velocity / 127.0f, context.start(), time); - assert(((AudioBuffer*)_trig_port->buffer(0))->data()[time - context.start()] == 1.0f); + ((AudioBuffer*)_gate_port->buffer(0).get())->set_value(1.0f, context.start(), time); + ((AudioBuffer*)_trig_port->buffer(0).get())->set_value(1.0f, context.start(), time); + ((AudioBuffer*)_trig_port->buffer(0).get())->set_value(0.0f, context.start(), time + 1); + ((AudioBuffer*)_vel_port->buffer(0).get())->set_value(velocity / 127.0f, context.start(), time); + assert(((AudioBuffer*)_trig_port->buffer(0).get())->data()[time - context.start()] == 1.0f); } } @@ -143,8 +143,8 @@ TriggerNode::note_off(ProcessContext& context, uint8_t note_num, FrameTime time) assert(time >= context.start() && time <= context.end()); assert(time - context.start() < _buffer_size); - if (note_num == lrintf(((AudioBuffer*)_note_port->buffer(0))->value_at(0))) - ((AudioBuffer*)_gate_port->buffer(0))->set_value(0.0f, context.start(), time); + if (note_num == lrintf(((AudioBuffer*)_note_port->buffer(0).get())->value_at(0))) + ((AudioBuffer*)_gate_port->buffer(0).get())->set_value(0.0f, context.start(), time); } diff --git a/src/engine/internals/Trigger.hpp b/src/engine/internals/Trigger.hpp index dfb4f899..84281ac7 100644 --- a/src/engine/internals/Trigger.hpp +++ b/src/engine/internals/Trigger.hpp @@ -42,7 +42,7 @@ namespace Internals { class TriggerNode : public NodeBase { public: - TriggerNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); + TriggerNode(BufferFactory& bufs, const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); void process(ProcessContext& context); diff --git a/src/engine/wscript b/src/engine/wscript index 232fb281..faee5517 100644 --- a/src/engine/wscript +++ b/src/engine/wscript @@ -4,7 +4,7 @@ import autowaf def build(bld): core_source = ''' AudioBuffer.cpp - Buffer.cpp + BufferFactory.cpp ClientBroadcaster.cpp ConnectionImpl.cpp DuplexPort.cpp @@ -34,7 +34,6 @@ def build(bld): ingen_engine.cpp internals/Controller.cpp internals/Note.cpp - internals/Transport.cpp internals/Trigger.cpp ''' diff --git a/src/shared/LV2URIMap.cpp b/src/shared/LV2URIMap.cpp index 65e3aa30..e401f6cb 100644 --- a/src/shared/LV2URIMap.cpp +++ b/src/shared/LV2URIMap.cpp @@ -35,6 +35,7 @@ LV2URIMap::LV2URIMap() , object_class_string(uri_to_id(NULL, LV2_OBJECT_URI "#String")) , object_class_int32(uri_to_id(NULL, LV2_OBJECT_URI "#Int32")) , object_class_float32(uri_to_id(NULL, LV2_OBJECT_URI "#Float32")) + , object_class_vector(uri_to_id(NULL, LV2_OBJECT_URI "#Vector")) , ui_format_events(uri_to_id(NULL, "http://lv2plug.in/ns/extensions/ui#Events")) , midi_event(uri_to_id(NULL, "http://lv2plug.in/ns/ext/midi#MidiEvent")) , string_transfer(uri_to_id(NULL, "http://lv2plug.in/ns/dev/string-port#StringTransfer")) diff --git a/src/shared/LV2URIMap.hpp b/src/shared/LV2URIMap.hpp index 0dd6b0cb..30823ef0 100644 --- a/src/shared/LV2URIMap.hpp +++ b/src/shared/LV2URIMap.hpp @@ -63,6 +63,7 @@ public: const uint32_t object_class_string; const uint32_t object_class_int32; const uint32_t object_class_float32; + const uint32_t object_class_vector; const uint32_t ui_format_events; const uint32_t midi_event; const uint32_t string_transfer; -- cgit v1.2.1