diff options
63 files changed, 813 insertions, 1116 deletions
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 <iostream> #include <cassert> #include <stdlib.h> +#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<AudioBuffer*>(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 <iostream> #include <cstddef> #include <cassert> #include <boost/utility.hpp> #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 <algorithm> #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 <cassert> #include <boost/utility.hpp> #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 <http://drobilla.net> + * + * 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 <algorithm> +#include <iostream> +#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<Shared::LV2URIMap> map) + : _engine(engine) + , _map(map) +{ +} + +struct BufferDeleter { + BufferDeleter(BufferFactory& bf) : _factory(bf) {} + void operator()(void* ptr) { + _factory.recycle((Buffer*)ptr); + } + BufferFactory& _factory; +}; + + +SharedPtr<Buffer> +BufferFactory::get(Shared::DataType type, size_t size) +{ + Raul::AtomicPtr<Buffer>& 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<Buffer>(); + } + } + + try_head->_next = NULL; + return SharedPtr<Buffer>(try_head, BufferDeleter(*this)); +} + + +SharedPtr<Buffer> +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<Buffer>(); + } + + return SharedPtr<Buffer>(buffer, BufferDeleter(*this)); +} + +void +BufferFactory::recycle(Buffer* buf) +{ + Raul::AtomicPtr<Buffer>& 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 <http://drobilla.net> + * + * 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 <map> +#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<Shared::LV2URIMap> map); + + SharedPtr<Buffer> get(Shared::DataType type, size_t size=0); + +private: + friend class BufferDeleter; + void recycle(Buffer* buf); + + SharedPtr<Buffer> create(Shared::DataType type, size_t size=0); + + inline Raul::AtomicPtr<Buffer>& 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<Buffer> _free_audio; + Raul::AtomicPtr<Buffer> _free_control; + Raul::AtomicPtr<Buffer> _free_event; + Raul::AtomicPtr<Buffer> _free_object; + + Glib::Mutex _mutex; + Engine& _engine; + SharedPtr<Shared::LV2URIMap> _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 <iostream> #include <cstdlib> #include <boost/utility.hpp> #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> 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<Buffer> _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<LV2URIMap>( + 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<EngineStore> 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<EventBuffer*>(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<Patch*>(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 << ")" <<endl; - }*/ } diff --git a/src/engine/InputPort.hpp b/src/engine/InputPort.hpp index 81702b34..971a8747 100644 --- a/src/engine/InputPort.hpp +++ b/src/engine/InputPort.hpp @@ -46,7 +46,8 @@ class NodeImpl; class InputPort : virtual public PortImpl { public: - InputPort(NodeImpl* parent, + InputPort(BufferFactory& bufs, + NodeImpl* parent, const std::string& name, uint32_t index, uint32_t poly, @@ -61,20 +62,20 @@ public: void add_connection(Connections::Node* c); Connections::Node* remove_connection(const OutputPort* src_port); - bool prepare_poly(uint32_t poly); + void set_buffer_size(BufferFactory& bufs, size_t size); + bool prepare_poly(BufferFactory& bufs, uint32_t poly); bool apply_poly(Raul::Maid& maid, uint32_t poly); void pre_process(Context& context); void post_process(Context& context); - bool is_connected() const { return (_connections.size() > 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<JackAudioPort*>::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<JackAudioPort*>::iterator i = _ports.begin(); i != _ports.end(); ++i) { - assert(*i); - (*i)->prepare_buffer(nframes); - } + // Read input + for (Raul::List<JackAudioPort*>::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<JackAudioPort*>::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<EventBuffer*>(_patch_port->buffer(0)); + EventBuffer* patch_buf = dynamic_cast<EventBuffer*>(_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<EventBuffer*>(_patch_port->buffer(0)); + EventBuffer* patch_buf = dynamic_cast<EventBuffer*>(_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<PortImpl*>(_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<Buffer> buf) { assert(voice < _polyphony); - - AudioBuffer* audio_buffer = dynamic_cast<AudioBuffer*>(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<Buffer> 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<LV2Info> 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<Buffer> 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<Buffer> 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<LV2Info> 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<Buffer> 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<Buffer> 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<ObjectBuffer*>(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<const AudioBuffer*>(port->buffer(0))->value_at(0); + const Sample& value = PtrCast<const AudioBuffer>(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<Patch*>(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<NodeImpl*>::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<PortImpl*>::Node* port) { _input_ports.push_back(port); } ///< Preprocesser thread void add_output(Raul::List<PortImpl*>::Node* port) { _output_ports.push_back(port); } ///< Preprocessor thread Raul::List<PortImpl*>::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<Buffer*>(poly)) + , _buffers(new Array< SharedPtr<Buffer> >(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<Buffer*>(poly, *_buffers); + _prepared_buffers = new Array< SharedPtr<Buffer> >(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> buffer(uint32_t voice) const { + return _buffers->at(voice); } - inline Buffer* prepared_buffer(uint32_t voice) const { + inline SharedPtr<Buffer> 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<Buffer*>* _buffers; + Context::ID _context; + Raul::Array< SharedPtr<Buffer> >* _buffers; // Dynamic polyphony - Raul::Array<Buffer*>* _prepared_buffers; + Raul::Array< SharedPtr<Buffer> >* _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<ConnectionImpl>(new ConnectionImpl(_src_port, _dst_port)); + _connection = SharedPtr<ConnectionImpl>(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<PortImpl*>(_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<AudioBuffer*>(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<ObjectBuffer*>(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<PortImpl*>(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<Voice>(_polyphony)) , _prepared_voices(NULL) @@ -48,22 +48,22 @@ NoteNode::NoteNode(const string& path, bool polyphonic, PatchImpl* parent, Sampl { _ports = new Raul::Array<PortImpl*>(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 <http://drobilla.net> - * - * 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 <jack/transport.h> -#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<Sample>* spb_port = new OutputPort<Sample>(this, "Seconds per Beat", 0, 1, - // new PortInfo("Seconds per Beat", CONTROL, OUTPUT, 0, 0, 1), 1); - _ports.at(0) = spb_port; - - OutputPort<Sample>* bpb_port = new OutputPort<Sample>(this, "Beats per Bar", 1, 1, - // new PortInfo("Beats per Bar", CONTROL, OUTPUT, 0, 0, 1), 1); - _ports.at(1) = bpb_port; - - OutputPort<Sample>* bar_port = new OutputPort<Sample>(this, "Bar", 3, 1, -// new PortInfo("Bar", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(2) = bar_port; - - OutputPort<Sample>* beat_port = new OutputPort<Sample>(this, "Beat", 3, 1, - // new PortInfo("Beat", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(3) = beat_port; - - OutputPort<Sample>* frame_port = new OutputPort<Sample>(this, "Frame", 3, 1, - // new PortInfo("Frame", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(4) = frame_port; - - OutputPort<Sample>* hour_port = new OutputPort<Sample>(this, "Hour", 3, 1, - // new PortInfo("Hour", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(5) = hour_port; - - OutputPort<Sample>* minute_port = new OutputPort<Sample>(this, "Minute", 3, 1, - // new PortInfo("Minute", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(6) = minute_port; - - OutputPort<Sample>* second_port = new OutputPort<Sample>(this, "Second", 3, 1, - // new PortInfo("Second", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(7) = second_port; - - OutputPort<Sample>* trg_port = new OutputPort<Sample>(this, "Beat Tick", 2, 1, - // new PortInfo("Beat Tick", AUDIO, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(8) = trg_port; - - OutputPort<Sample>* bar_trig_port = new OutputPort<Sample>(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<Sample>*)_ports.at(0))->buffer(0)->set(spb, 0, 0); - ((OutputPort<Sample>*)_ports.at(1))->buffer(0)->set(bpb, 0, 0); - - // fill the trigger buffers with zeros - ((OutputPort<Sample>*)_ports.at(2))->buffer(0)->set(0.0f, 0, nframes - 1); - ((OutputPort<Sample>*)_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<Sample>*)_ports.at(2))->buffer(0)->set(1.0f, size_t(first_beat)); - if (first_beat_no % int(bpb) == 0) { - ((OutputPort<Sample>*)_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 <http://drobilla.net> - * - * 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 <string> -#include <jack/transport.h> -#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<PortImpl*>(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; |