diff options
Diffstat (limited to 'src/engine')
50 files changed, 622 insertions, 367 deletions
diff --git a/src/engine/AudioBuffer.cpp b/src/engine/AudioBuffer.cpp index 02ebea57..53924ae4 100644 --- a/src/engine/AudioBuffer.cpp +++ b/src/engine/AudioBuffer.cpp @@ -20,6 +20,7 @@ #include <stdlib.h> #include "ingen-config.h" #include "AudioBuffer.hpp" +#include "ProcessContext.hpp" using namespace std; @@ -211,6 +212,13 @@ AudioBuffer::copy(const Buffer* src, size_t start_sample, size_t end_sample) } +void +AudioBuffer::copy(Context& context, const Buffer* src) +{ + copy(src, context.start(), std::min(size(), src->size())); +} + + /** Accumulate a block of @a src into buffer. * * @a start_sample and @a end_sample define the inclusive range to be accumulated. @@ -268,15 +276,11 @@ AudioBuffer::unjoin() void -AudioBuffer::prepare_read(FrameTime start, SampleCount nframes) +AudioBuffer::prepare_read(Context& context) { - // FIXME: nframes parameter doesn't actually work, - // writing starts from 0 every time - assert(_size == 1 || nframes == _size); - switch (_state) { case HALF_SET_CYCLE_1: - if (start > _set_time) + if (context.start() > _set_time) _state = HALF_SET_CYCLE_2; break; case HALF_SET_CYCLE_2: diff --git a/src/engine/AudioBuffer.hpp b/src/engine/AudioBuffer.hpp index aff990ec..d072ddb3 100644 --- a/src/engine/AudioBuffer.hpp +++ b/src/engine/AudioBuffer.hpp @@ -37,6 +37,7 @@ public: 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(Context& context, const Buffer* src); void accumulate(const AudioBuffer* src, size_t start_sample, size_t end_sample); bool join(Buffer* buf); @@ -53,8 +54,8 @@ public: inline Sample& value_at(size_t offset) const { assert(offset < _size); return data()[offset]; } - void prepare_read(FrameTime start, SampleCount nframes); - void prepare_write(FrameTime start, SampleCount nframes) {} + void prepare_read(Context& context); + void prepare_write(Context& context) {} void resize(size_t size); diff --git a/src/engine/Buffer.cpp b/src/engine/Buffer.cpp index 14d97314..65e46736 100644 --- a/src/engine/Buffer.cpp +++ b/src/engine/Buffer.cpp @@ -17,7 +17,7 @@ #include "AudioBuffer.hpp" #include "EventBuffer.hpp" -#include "StringBuffer.hpp" +#include "ObjectBuffer.hpp" namespace Ingen { @@ -30,10 +30,10 @@ Buffer::create(DataType type, size_t size) return new AudioBuffer(1); else if (type.is_audio()) return new AudioBuffer(size); - else if (type.is_event()) + else if (type.is_events()) return new EventBuffer(size); - else if (type.is_string()) - return new StringBuffer(size); + else if (type.is_object()) + return new ObjectBuffer(size); else throw; } diff --git a/src/engine/Buffer.hpp b/src/engine/Buffer.hpp index a1a56725..adde37f2 100644 --- a/src/engine/Buffer.hpp +++ b/src/engine/Buffer.hpp @@ -27,6 +27,7 @@ namespace Ingen { +class Context; class Buffer : public boost::noncopyable, public Raul::Deletable { @@ -52,10 +53,10 @@ public: /** Rewind (ie reset read pointer), but leave contents unchanged */ virtual void rewind() const {} - virtual void copy(const Buffer* src, size_t start_sample, size_t end_sample) = 0; + virtual void copy(Context& context, const Buffer* src) = 0; - virtual void prepare_read(FrameTime start, SampleCount nframes) = 0; - virtual void prepare_write(FrameTime start, SampleCount nframes) = 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; } diff --git a/src/engine/ConnectionImpl.cpp b/src/engine/ConnectionImpl.cpp index 66592bfe..f33c612b 100644 --- a/src/engine/ConnectionImpl.cpp +++ b/src/engine/ConnectionImpl.cpp @@ -76,8 +76,11 @@ ConnectionImpl::set_mode() else if (must_extend()) _mode = EXTEND; - if (_mode == MIX && type() == DataType::EVENT) + if (_mode == MIX && type() == DataType::EVENTS) _mode = COPY; + + if (type() == DataType::OBJECT) + _mode = DIRECT; } @@ -123,10 +126,6 @@ ConnectionImpl::prepare_poly(uint32_t poly) { _src_port->prepare_poly(poly); - /*cerr << "CONNECTION PREPARE: " << src_port()->path() << " * " << src_port()->poly() - << " -> " << dst_port()->path() << " * " << dst_port()->poly() - << "\t\tmust mix: " << must_mix() << " at poly " << poly << endl;*/ - if (need_buffer() && !_local_buffer) _local_buffer = Buffer::create(_dst_port->type(), _dst_port->buffer(0)->size()); } @@ -152,11 +151,8 @@ ConnectionImpl::apply_poly(Raul::Maid& maid, uint32_t poly) void -ConnectionImpl::process(ProcessContext& context) +ConnectionImpl::process(Context& context) { - // FIXME: nframes parameter not used - assert(_buffer_size == 1 || _buffer_size == context.nframes()); - /* 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 @@ -164,15 +160,15 @@ ConnectionImpl::process(ProcessContext& context) * 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() + std::cerr << src_port()->path() << " * " << src_port()->poly() << " -> " << dst_port()->path() << " * " << dst_port()->poly() - << "\t\tmode: " << (int)_mode << std::endl;*/ + << "\t\tmode: " << (int)_mode << std::endl; if (_mode == COPY) { assert(src_port()->poly() == dst_port()->poly()); - const size_t copy_size = std::min(src_port()->buffer_size(), dst_port()->buffer_size()); - for (uint32_t i=0; i < src_port()->poly(); ++i) - dst_port()->buffer(i)->copy(src_port()->buffer(i), 0, copy_size-1); + 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); @@ -184,7 +180,7 @@ ConnectionImpl::process(ProcessContext& context) const size_t copy_size = std::min(src_buffer->size(), mix_buf->size()); // Copy src buffer to start of mix buffer - mix_buf->copy((AudioBuffer*)src_port()->buffer(0), 0, copy_size-1); + 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()) @@ -229,9 +225,10 @@ ConnectionImpl::process(ProcessContext& context) 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.start(), context.nframes()); + src_port()->buffer(j)->prepare_read(context); } } diff --git a/src/engine/ConnectionImpl.hpp b/src/engine/ConnectionImpl.hpp index dd8c6a2c..a4ad281b 100644 --- a/src/engine/ConnectionImpl.hpp +++ b/src/engine/ConnectionImpl.hpp @@ -57,7 +57,7 @@ public: bool pending_disconnection() { return _pending_disconnection; } void pending_disconnection(bool b) { _pending_disconnection = b; } - void process(ProcessContext& context); + void process(Context& context); /** Get the buffer for a particular voice. * A Connection is smart - it knows the destination port requesting the diff --git a/src/engine/Context.hpp b/src/engine/Context.hpp index db19d5f9..8d3085b8 100644 --- a/src/engine/Context.hpp +++ b/src/engine/Context.hpp @@ -18,6 +18,8 @@ #ifndef CONTEXT_H #define CONTEXT_H +#include "EventSink.hpp" + namespace Ingen { class Engine; @@ -33,15 +35,32 @@ public: Context(Engine& engine, ID id) : _id(id) , _engine(engine) + , _event_sink(engine, 1024) // FIXME: size? + , _start(0) + , _realtime(true) {} virtual ~Context() {} - inline Engine& engine() const { return _engine; } + ID id() const { return _id; } + + void locate(FrameTime s) { _start = s; } + + inline Engine& engine() const { return _engine; } + inline FrameTime start() const { return _start; } + inline bool realtime() const { return _realtime; } + + inline const EventSink& event_sink() const { return _event_sink; } + inline EventSink& event_sink() { return _event_sink; } protected: - ID _id; ///< Fast ID for this context - Engine& _engine; ///< Engine we're running in + ID _id; ///< Fast ID for this context + Engine& _engine; ///< Engine we're running in + +private: + EventSink _event_sink; ///< Sink for events generated in a realtime context + FrameTime _start; ///< Start frame of this cycle, timeline relative + bool _realtime; ///< True iff context is hard realtime }; diff --git a/src/engine/DuplexPort.cpp b/src/engine/DuplexPort.cpp index 2c4e81b9..78a2bb94 100644 --- a/src/engine/DuplexPort.cpp +++ b/src/engine/DuplexPort.cpp @@ -54,7 +54,7 @@ DuplexPort::DuplexPort( /** Prepare for the execution of parent patch */ void -DuplexPort::pre_process(ProcessContext& context) +DuplexPort::pre_process(Context& context) { /*cerr << endl << "{ duplex pre" << endl; cerr << path() << " duplex pre: fixed buffers: " << fixed_buffers() << endl; @@ -65,7 +65,7 @@ DuplexPort::pre_process(ProcessContext& context) if (_is_output) { for (uint32_t i=0; i < _poly; ++i) if (!_buffers->at(i)->is_joined()) - _buffers->at(i)->prepare_write(context.start(), context.nframes()); + _buffers->at(i)->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) @@ -85,7 +85,7 @@ DuplexPort::pre_process(ProcessContext& context) /** Finalize after the execution of parent patch (deliver outputs) */ void -DuplexPort::post_process(ProcessContext& context) +DuplexPort::post_process(Context& context) { /*cerr << endl << "{ duplex post" << endl; cerr << path() << " duplex post: fixed buffers: " << fixed_buffers() << endl; diff --git a/src/engine/DuplexPort.hpp b/src/engine/DuplexPort.hpp index 97816acb..882bab73 100644 --- a/src/engine/DuplexPort.hpp +++ b/src/engine/DuplexPort.hpp @@ -50,8 +50,8 @@ public: virtual ~DuplexPort() {} - void pre_process(ProcessContext& context); - void post_process(ProcessContext& context); + void pre_process(Context& context); + void post_process(Context& context); bool is_input() const { return !_is_output; } bool is_output() const { return _is_output; } diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 8f119248..619ead5e 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -105,7 +105,7 @@ Engine::driver(DataType type, EventType event_type) { if (type == DataType::AUDIO) { return _audio_driver.get(); - } else if (type == DataType::EVENT) { + } else if (type == DataType::EVENTS) { if (event_type == EventType::MIDI) { return _midi_driver; } else if (event_type == EventType::OSC) { diff --git a/src/engine/EventBuffer.cpp b/src/engine/EventBuffer.cpp index f8b97595..d92ecae1 100644 --- a/src/engine/EventBuffer.cpp +++ b/src/engine/EventBuffer.cpp @@ -18,10 +18,11 @@ #define __STDC_LIMIT_MACROS 1 #include <stdint.h> #include <iostream> -#include "ingen-config.h" -#include "EventBuffer.hpp" #include "event.lv2/event.h" #include "event.lv2/event-helpers.h" +#include "ingen-config.h" +#include "EventBuffer.hpp" +#include "ProcessContext.hpp" using namespace std; @@ -33,11 +34,11 @@ using namespace Shared; * \a capacity is in bytes (not number of events). */ EventBuffer::EventBuffer(size_t capacity) - : Buffer(DataType(DataType::EVENT), capacity) + : Buffer(DataType(DataType::EVENTS), capacity) , _local_buf(new LV2EventBuffer(capacity)) { _buf = _local_buf; - reset(0); + clear(); //cerr << "Creating MIDI Buffer " << _buf << ", capacity = " << _buf->capacity << endl; } @@ -71,22 +72,21 @@ EventBuffer::unjoin() void -EventBuffer::prepare_read(FrameTime start, SampleCount nframes) +EventBuffer::prepare_read(Context& context) { rewind(); - _this_nframes = nframes; } void -EventBuffer::prepare_write(FrameTime start, SampleCount nframes) +EventBuffer::prepare_write(Context& context) { - reset(nframes); + clear(); } -/** FIXME: parameters ignored */ + void -EventBuffer::copy(const Buffer* src_buf, size_t start_sample, size_t end_sample) +EventBuffer::copy(Context& context, const Buffer* src_buf) { const EventBuffer* src = dynamic_cast<const EventBuffer*>(src_buf); assert(src); @@ -97,7 +97,6 @@ EventBuffer::copy(const Buffer* src_buf, size_t start_sample, size_t end_sample) src->rewind(); _buf->copy(*src->_buf); - _this_nframes = end_sample - start_sample; assert(event_count() == src->event_count()); } @@ -114,7 +113,7 @@ EventBuffer::merge(const EventBuffer& a, const EventBuffer& b) // Die if a merge isn't necessary as it's expensive assert(a.size() > 0 && b.size() > 0); - reset(_this_nframes); + clear(); a.rewind(); b.rewind(); diff --git a/src/engine/EventBuffer.hpp b/src/engine/EventBuffer.hpp index f89705b0..cafa71bd 100644 --- a/src/engine/EventBuffer.hpp +++ b/src/engine/EventBuffer.hpp @@ -34,17 +34,17 @@ public: bool join(Buffer* buf); void unjoin(); - void clear() { reset(_this_nframes); } + void clear() { _buf->reset(); } void* raw_data() { return _buf; } const void* raw_data() const { return _buf; } void rewind() const { _buf->rewind(); } - void prepare_read(FrameTime start, SampleCount nframes); - void prepare_write(FrameTime start, SampleCount nframes); + void prepare_read(Context& context); + void prepare_write(Context& context); - void copy(const Buffer* src, size_t start_sample, size_t end_sample); + void copy(Context& context, const Buffer* src); bool merge(const EventBuffer& a, const EventBuffer& b); bool increment() const { return _buf->increment(); } @@ -52,14 +52,8 @@ public: inline uint32_t latest_frames() const { return _buf->latest_frames(); } inline uint32_t latest_subframes() const { return _buf->latest_subframes(); } - inline uint32_t this_nframes() const { return _this_nframes; } inline uint32_t event_count() const { return _buf->event_count(); } - inline void reset(SampleCount nframes) { - _this_nframes = nframes; - _buf->reset(); - } - inline bool get_event(uint32_t* frames, uint32_t* subframes, uint16_t* type, @@ -81,9 +75,8 @@ public: } private: - LV2EventBuffer* _buf; ///< Contents (maybe belong to _joined_buf) - LV2EventBuffer* _local_buf; ///< Local contents - uint32_t _this_nframes; ///< Current cycle nframes + LV2EventBuffer* _buf; ///< Contents (maybe belong to _joined_buf) + LV2EventBuffer* _local_buf; ///< Local contents }; diff --git a/src/engine/GraphObjectImpl.hpp b/src/engine/GraphObjectImpl.hpp index c8b0c13c..66bda3e2 100644 --- a/src/engine/GraphObjectImpl.hpp +++ b/src/engine/GraphObjectImpl.hpp @@ -32,6 +32,7 @@ namespace Raul { class Maid; } namespace Ingen { class PatchImpl; +class Context; class ProcessContext; diff --git a/src/engine/InputPort.cpp b/src/engine/InputPort.cpp index 31cb3e92..5d3a0627 100644 --- a/src/engine/InputPort.cpp +++ b/src/engine/InputPort.cpp @@ -188,7 +188,7 @@ InputPort::remove_connection(const OutputPort* src_port) /** Prepare buffer for access, mixing if necessary. Realtime safe. */ void -InputPort::pre_process(ProcessContext& context) +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 @@ -198,7 +198,7 @@ InputPort::pre_process(ProcessContext& context) // 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.start(), context.nframes()); + buffer(i)->prepare_read(context); return; } @@ -210,7 +210,7 @@ InputPort::pre_process(ProcessContext& context) 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.start(), context.nframes()); + _buffers->at(i)->prepare_read(context); } connect_buffers(); return; @@ -235,8 +235,7 @@ InputPort::pre_process(ProcessContext& context) if (_type == DataType::CONTROL || _type == DataType::AUDIO) { for (uint32_t voice=0; voice < _poly; ++voice) { // Copy first connection - buffer(voice)->copy( - _connections.front()->buffer(voice), 0, _buffer_size-1); + buffer(voice)->copy(context, _connections.front()->buffer(voice)); // Accumulate the rest if (_connections.size() > 1) { @@ -254,11 +253,11 @@ InputPort::pre_process(ProcessContext& context) cerr << "WARNING: MIDI mixing not implemented, only first connection used." << endl; // Copy first connection - buffer(0)->copy(_connections.front()->buffer(0), 0, _buffer_size-1); + buffer(0)->copy(context, _connections.front()->buffer(0)); } for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_read(context.start(), context.nframes()); + buffer(i)->prepare_read(context); if (_broadcast) broadcast_value(context, false); @@ -266,12 +265,12 @@ InputPort::pre_process(ProcessContext& context) void -InputPort::post_process(ProcessContext& context) +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.start(), context.nframes()); + buffer(i)->prepare_write(context); _set_by_user = false; diff --git a/src/engine/InputPort.hpp b/src/engine/InputPort.hpp index 4f27835a..81702b34 100644 --- a/src/engine/InputPort.hpp +++ b/src/engine/InputPort.hpp @@ -64,8 +64,8 @@ public: bool prepare_poly(uint32_t poly); bool apply_poly(Raul::Maid& maid, uint32_t poly); - void pre_process(ProcessContext& context); - void post_process(ProcessContext& context); + void pre_process(Context& context); + void post_process(Context& context); bool is_connected() const { return (_connections.size() > 0); } diff --git a/src/engine/JackMidiDriver.cpp b/src/engine/JackMidiDriver.cpp index 61aacc06..1cb46c3e 100644 --- a/src/engine/JackMidiDriver.cpp +++ b/src/engine/JackMidiDriver.cpp @@ -106,7 +106,7 @@ JackMidiPort::pre_process(ProcessContext& context) void* jack_buffer = jack_port_get_buffer(_jack_port, context.nframes()); const jack_nframes_t event_count = jack_midi_get_event_count(jack_buffer); - patch_buf->prepare_write(context.start(), context.nframes()); + patch_buf->prepare_write(context); // Copy events from Jack port buffer into patch port buffer for (jack_nframes_t i=0; i < event_count; ++i) { @@ -140,7 +140,7 @@ JackMidiPort::post_process(ProcessContext& context) assert(_patch_port->poly() == 1); assert(patch_buf); - patch_buf->prepare_read(context.start(), context.nframes()); + patch_buf->prepare_read(context); jack_midi_clear_buffer(jack_buf); uint32_t frames = 0; @@ -173,8 +173,8 @@ JackMidiDriver::JackMidiDriver(Engine& engine) , _is_activated(false) , _is_enabled(false) { - const Shared::LV2Features::Feature* f = engine.world()->lv2_features->feature(LV2_URI_MAP_URI); - Shared::LV2URIMap* map = (Shared::LV2URIMap*)f->controller; + SharedPtr<Shared::LV2URIMap> map = PtrCast<Shared::LV2URIMap>( + _engine.world()->lv2_features->feature(LV2_URI_MAP_URI)); _midi_event_type = map->uri_to_id(NULL, "http://lv2plug.in/ns/ext/midi#MidiEvent"); } diff --git a/src/engine/LV2BlobFeature.hpp b/src/engine/LV2BlobFeature.hpp new file mode 100644 index 00000000..c672b7ac --- /dev/null +++ b/src/engine/LV2BlobFeature.hpp @@ -0,0 +1,64 @@ +/* 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 LV2_BLOB_FEATURE_H +#define LV2_BLOB_FEATURE_H + +#include "shared/LV2Features.hpp" + +namespace Ingen { + +struct BlobFeature : public Shared::LV2Features::Feature { + BlobFeature() { + LV2_Blob_Support* data = (LV2_Blob_Support*)malloc(sizeof(LV2_Blob_Support)); + data->data = NULL; + data->reference_size = sizeof(LV2_Blob*); + data->lv2_blob_new = &blob_new; + data->lv2_reference_get = &reference_get; + data->lv2_reference_copy = &reference_copy; + data->lv2_reference_reset = &reference_reset; + _feature.URI = LV2_BLOB_SUPPORT_URI; + _feature.data = data; + } + + static void blob_new(LV2_Blob_Support_Data data, + LV2_Reference* reference, + LV2_Blob_Destroy destroy_func, + uint32_t type, + uint32_t size) {} + + static LV2_Blob* reference_get(LV2_Blob_Support_Data data, + LV2_Reference* ref) { return 0; } + + static void reference_copy(LV2_Blob_Support_Data data, + LV2_Reference* dst, + LV2_Reference* src) {} + + static void reference_reset(LV2_Blob_Support_Data data, + LV2_Reference* ref) {} + + SharedPtr<LV2_Feature> feature(Shared::Node*) { + return SharedPtr<LV2_Feature>(&_feature, NullDeleter<LV2_Feature>); + } + +private: + LV2_Feature _feature; +}; + +} // namespace Ingen + +#endif // LV2_BLOB_FEATURE_H diff --git a/src/engine/LV2EventFeature.hpp b/src/engine/LV2EventFeature.hpp new file mode 100644 index 00000000..b2baaeec --- /dev/null +++ b/src/engine/LV2EventFeature.hpp @@ -0,0 +1,51 @@ +/* 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 LV2_EVENT_FEATURE_H +#define LV2_EVENT_FEATURE_H + +#include "shared/LV2Features.hpp" + +namespace Ingen { + +struct EventFeature : public Shared::LV2Features::Feature { + EventFeature() { + LV2_Event_Feature* data = (LV2_Event_Feature*)malloc(sizeof(LV2_Event_Feature)); + data->lv2_event_ref = &event_ref; + data->lv2_event_unref = &event_unref; + data->callback_data = this; + _feature.URI = LV2_EVENT_URI; + _feature.data = data; + } + + static uint32_t event_ref(LV2_Event_Callback_Data callback_data, + LV2_Event* event) { return 0; } + + static uint32_t event_unref(LV2_Event_Callback_Data callback_data, + LV2_Event* event) { return 0; } + + SharedPtr<LV2_Feature> feature(Shared::Node*) { + return SharedPtr<LV2_Feature>(&_feature, NullDeleter<LV2_Feature>); + } + +private: + LV2_Feature _feature; +}; + +} // namespace Ingen + +#endif // LV2_EVENT_FEATURE_H diff --git a/src/engine/LV2Info.cpp b/src/engine/LV2Info.cpp index 10541d0c..e94deaad 100644 --- a/src/engine/LV2Info.cpp +++ b/src/engine/LV2Info.cpp @@ -19,8 +19,13 @@ #include <cassert> #include <iostream> #include <stdint.h> +#include "object.lv2/object.h" #include "LV2Info.hpp" #include "module/World.hpp" +#include "LV2Features.hpp" +#include "LV2EventFeature.hpp" +#include "LV2BlobFeature.hpp" +#include "LV2ResizeFeature.hpp" using namespace std; @@ -32,24 +37,19 @@ LV2Info::LV2Info(Ingen::Shared::World* world) , control_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_CONTROL)) , audio_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_AUDIO)) , event_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_EVENT)) - , string_class(slv2_value_new_uri(world->slv2_world, - "http://lv2plug.in/ns/dev/string-port#StringPort")) + , object_port_class(slv2_value_new_uri(world->slv2_world, LV2_OBJECT_URI "#ObjectPort")) , _world(world) { assert(world); - LV2_Event_Feature* ev_data = (LV2_Event_Feature*)malloc(sizeof(LV2_Event_Feature)); - ev_data->lv2_event_ref = &LV2Info::event_ref; - ev_data->lv2_event_unref = &LV2Info::event_ref; - ev_data->callback_data = this; - LV2_Feature* ev_feature = (LV2_Feature*)malloc(sizeof(LV2_Event_Feature)); - ev_feature->URI = LV2_EVENT_URI; - ev_feature->data = ev_data; - - world->lv2_features->add_feature(LV2_EVENT_URI, ev_feature, ev_data); + world->lv2_features->add_feature(LV2_EVENT_URI, + SharedPtr<Shared::LV2Features::Feature>(new EventFeature())); + world->lv2_features->add_feature(LV2_BLOB_SUPPORT_URI, + SharedPtr<Shared::LV2Features::Feature>(new BlobFeature())); + world->lv2_features->add_feature(LV2_RESIZE_PORT_URI, + SharedPtr<Shared::LV2Features::Feature>(new ResizeFeature())); } - LV2Info::~LV2Info() { slv2_value_free(input_class); @@ -57,17 +57,7 @@ LV2Info::~LV2Info() slv2_value_free(control_class); slv2_value_free(audio_class); slv2_value_free(event_class); - slv2_value_free(string_class); -} - - -uint32_t -LV2Info::event_ref(LV2_Event_Callback_Data callback_data, - LV2_Event* event) -{ - return 0; + slv2_value_free(object_port_class); } - - } // namespace Ingen diff --git a/src/engine/LV2Info.hpp b/src/engine/LV2Info.hpp index 6f34dc57..8d4478aa 100644 --- a/src/engine/LV2Info.hpp +++ b/src/engine/LV2Info.hpp @@ -31,13 +31,16 @@ #include "shared/LV2Features.hpp" #include "uri-map.lv2/uri-map.h" #include "event.lv2/event.h" +#include "object.lv2/object.h" +#include "resize-port.lv2/resize-port.h" namespace Ingen { +namespace Shared { class Node; } /** Stuff that may need to be passed to an LV2 plugin (i.e. LV2 features). */ -class LV2Info : public Shared::LV2URIMap { +class LV2Info { public: LV2Info(Ingen::Shared::World* world); ~LV2Info(); @@ -47,16 +50,11 @@ public: SLV2Value control_class; SLV2Value audio_class; SLV2Value event_class; - SLV2Value string_class; + SLV2Value object_port_class; Ingen::Shared::World& world() { return *_world; } SLV2World lv2_world() { return _world->slv2_world; } - static uint32_t event_ref(LV2_Event_Callback_Data callback_data, - LV2_Event* event); - - LV2_Feature** lv2_features() const { return _world->lv2_features->lv2_features(); } - private: Ingen::Shared::World* _world; }; diff --git a/src/engine/LV2Node.cpp b/src/engine/LV2Node.cpp index 70ee2e08..c12171e9 100644 --- a/src/engine/LV2Node.cpp +++ b/src/engine/LV2Node.cpp @@ -28,6 +28,7 @@ #include "EventBuffer.hpp" #include "OutputPort.hpp" #include "ProcessContext.hpp" +#include "MessageContext.hpp" using namespace std; using namespace Raul; @@ -81,9 +82,8 @@ LV2Node::prepare_poly(uint32_t poly) SharedPtr<LV2Info> info = _lv2_plugin->lv2_info(); _prepared_instances = new Raul::Array<SLV2Instance>(poly, *_instances); for (uint32_t i = _polyphony; i < _prepared_instances->size(); ++i) { - // FIXME: features array (in NodeFactory) must be passed! _prepared_instances->at(i) = slv2_plugin_instantiate( - _lv2_plugin->slv2_plugin(), _srate, info->lv2_features()); + _lv2_plugin->slv2_plugin(), _srate, _features->array()); if (_prepared_instances->at(i) == NULL) { cerr << "Failed to instantiate plugin!" << endl; @@ -151,11 +151,13 @@ LV2Node::instantiate() _ports = new Raul::Array<PortImpl*>(num_ports, NULL); _instances = new Raul::Array<SLV2Instance>(_polyphony, NULL); + _features = info->world().lv2_features->lv2_features(this); + uint32_t port_buffer_size = 0; SLV2Value ctx_ext_uri = slv2_value_new_uri(info->lv2_world(), LV2_CONTEXT_MESSAGE); for (uint32_t i=0; i < _polyphony; ++i) { - (*_instances)[i] = slv2_plugin_instantiate(plug, _srate, info->lv2_features()); + (*_instances)[i] = slv2_plugin_instantiate(plug, _srate, _features->array()); if ((*_instances)[i] == NULL) { cerr << "Failed to instantiate plugin!" << endl; return false; @@ -213,10 +215,10 @@ LV2Node::instantiate() data_type = DataType::AUDIO; port_buffer_size = _buffer_size; } else if (slv2_port_is_a(plug, id, info->event_class)) { - data_type = DataType::EVENT; + data_type = DataType::EVENTS; port_buffer_size = _buffer_size; - } else if (slv2_port_is_a(plug, id, info->string_class)) { - data_type = DataType::STRING; + } else if (slv2_port_is_a(plug, id, info->object_port_class)) { + data_type = DataType::OBJECT; port_buffer_size = 0; // Get default value, and its length @@ -275,7 +277,7 @@ LV2Node::instantiate() for (uint32_t i = 0; i < slv2_values_size(contexts); ++i) { SLV2Value c = slv2_values_get_at(contexts, i); const char* context = slv2_value_as_string(c); - if (!strcmp("http://lv2plug.in/ns/dev/contexts#MessageContext", context)) { + if (!strcmp(LV2_CONTEXT_MESSAGE, context)) { cerr << _lv2_plugin->uri() << " port " << i << " has message context" << endl; if (!_message_funcs) { cerr << _lv2_plugin->uri() @@ -331,13 +333,18 @@ LV2Node::deactivate() void -LV2Node::message_process(MessageContext& context, uint32_t* inputs, uint32_t* outputs) +LV2Node::message_run(MessageContext& context) { - // FIXME: voice - if (_message_funcs) - (*_message_funcs->message_run)((*_instances)[0]->lv2_handle, inputs, outputs); + for (size_t i = 0; i < num_ports(); ++i) { + PortImpl* const port = _ports->at(i); + if (port->context() == Context::MESSAGE) + port->pre_process(context); + } - /* MESSAGE PROCESS */ + if (!_valid_ports) + _valid_ports = calloc(num_ports() / 8, 1); + if (_message_funcs) + (*_message_funcs->message_run)((*_instances)[0]->lv2_handle, _valid_ports, _valid_ports); } @@ -357,15 +364,7 @@ void LV2Node::set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) { assert(voice < _polyphony); - slv2_instance_connect_port((*_instances)[voice], port_num, buf->raw_data()); - if ((*_ports).at(port_num)->context() == Context::MESSAGE) { - assert(_message_funcs); - assert(_message_funcs->message_connect_port); - (*_message_funcs->message_connect_port)((*_instances)[voice]->lv2_handle, port_num, buf->raw_data()); - } else { - slv2_instance_connect_port((*_instances)[voice], port_num, buf->raw_data()); - } } diff --git a/src/engine/LV2Node.hpp b/src/engine/LV2Node.hpp index dd4c80c5..8a543f2d 100644 --- a/src/engine/LV2Node.hpp +++ b/src/engine/LV2Node.hpp @@ -23,6 +23,7 @@ #include "lv2_contexts.h" #include "types.hpp" #include "NodeBase.hpp" +#include "LV2Features.hpp" namespace Ingen { @@ -53,7 +54,7 @@ public: void activate(); void deactivate(); - void message_process(MessageContext& context, uint32_t* ins, uint32_t* outs); + void message_run(MessageContext& context); void process(ProcessContext& context); @@ -65,6 +66,8 @@ protected: Raul::Array<SLV2Instance>* _prepared_instances; LV2MessageContext* _message_funcs; + + SharedPtr<Shared::LV2Features::FeatureArray> _features; }; diff --git a/src/engine/LV2ResizeFeature.hpp b/src/engine/LV2ResizeFeature.hpp new file mode 100644 index 00000000..90452552 --- /dev/null +++ b/src/engine/LV2ResizeFeature.hpp @@ -0,0 +1,68 @@ +/* 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 LV2_RESIZE_FEATURE_H +#define LV2_RESIZE_FEATURE_H + +#include <iostream> +#include "shared/LV2Features.hpp" +#include "NodeImpl.hpp" +#include "PortImpl.hpp" + +using namespace std; + +namespace Ingen { + +struct ResizeFeature : public Shared::LV2Features::Feature { + static bool resize_port(LV2_Resize_Port_Feature_Data data, + uint32_t index, + size_t size) { + NodeImpl* node = (NodeImpl*)data; + PortImpl* port = node->port_impl(index); + if (port->context() == Context::MESSAGE) { + cout << "Resizing " << port->path() << " to " << size << " bytes" << endl; + port->buffer(0)->resize(size); + port->connect_buffers(); + return true; + } else { + return false; + } + } + + static void delete_feature(LV2_Feature* feature) { + free(feature->data); + free(feature); + } + + SharedPtr<LV2_Feature> feature(Shared::Node* n) { + NodeImpl* node = dynamic_cast<NodeImpl*>(n); + if (!node) + return SharedPtr<LV2_Feature>(); + LV2_Resize_Port_Feature* data + = (LV2_Resize_Port_Feature*)malloc(sizeof(LV2_Resize_Port_Feature)); + data->data = node; + data->resize_port = &resize_port; + LV2_Feature* f = (LV2_Feature*)malloc(sizeof(LV2_Feature)); + f->URI = LV2_RESIZE_PORT_URI; + f->data = data; + return SharedPtr<LV2_Feature>(f, &delete_feature); + } +}; + +} // namespace Ingen + +#endif // LV2_RESIZE_FEATURE_H diff --git a/src/engine/MessageContext.cpp b/src/engine/MessageContext.cpp index 355a4361..c0f4d3a6 100644 --- a/src/engine/MessageContext.cpp +++ b/src/engine/MessageContext.cpp @@ -15,19 +15,46 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <iostream> #include "raul/Atom.hpp" +#include "ConnectionImpl.hpp" +#include "Engine.hpp" #include "MessageContext.hpp" #include "NodeImpl.hpp" +#include "PatchImpl.hpp" +#include "PortImpl.hpp" +#include "ProcessContext.hpp" + +using namespace std; namespace Ingen { void MessageContext::run(NodeImpl* node) { - uint32_t inputs, outputs; - node->message_process(*this, &inputs, &outputs); + node->message_run(*this); + + void* valid_ports = node->valid_ports(); + PatchImpl* patch = node->parent_patch(); + + cout << "MESSAGE RUN " << node->path() << " {" << endl; + for (uint32_t i = 0; i < node->num_ports(); ++i) { + PortImpl* p = node->port_impl(i); + if (p->is_output() && p->context() == Context::MESSAGE && + lv2_contexts_port_is_valid(valid_ports, i)) { + PatchImpl::Connections& wires = patch->connections(); + for (PatchImpl::Connections::iterator c = wires.begin(); c != wires.end(); ++c) { + ConnectionImpl* ci = dynamic_cast<ConnectionImpl*>(c->get()); + if (ci->src_port() == p) { + ci->dst_port()->pre_process(*_engine.message_context()); + run(ci->dst_port()->parent_node()); + } + } + } + } + cout << "}" << endl; - // Don't care what the plugin output, yet... + node->reset_valid_ports(); } } // namespace Ingen diff --git a/src/engine/MessageContext.hpp b/src/engine/MessageContext.hpp index 6275f1d3..9d17d920 100644 --- a/src/engine/MessageContext.hpp +++ b/src/engine/MessageContext.hpp @@ -24,7 +24,7 @@ namespace Ingen { class NodeImpl; -/** Context of a message_process() call. +/** Context of a message_run() call. * * The message context is a non-hard-realtime thread used to execute things * that can take too long to execute in an audio thread, and do sloppy timed diff --git a/src/engine/MidiDriver.hpp b/src/engine/MidiDriver.hpp index eb27463a..86b54986 100644 --- a/src/engine/MidiDriver.hpp +++ b/src/engine/MidiDriver.hpp @@ -36,7 +36,7 @@ class AudioDriver; class MidiDriver : public Driver { public: - MidiDriver() : Driver(Shared::DataType::EVENT) {} + MidiDriver() : Driver(Shared::DataType::EVENTS) {} virtual void attach(AudioDriver& master) {} diff --git a/src/engine/NodeBase.cpp b/src/engine/NodeBase.cpp index bdacfe80..25660c37 100644 --- a/src/engine/NodeBase.cpp +++ b/src/engine/NodeBase.cpp @@ -40,14 +40,15 @@ NodeBase::NodeBase(PluginImpl* plugin, const string& name, bool polyphonic, Patc , _polyphony((polyphonic && parent) ? parent->internal_polyphony() : 1) , _srate(srate) , _buffer_size(buffer_size) - , _activated(false) - , _traversed(false) + , _valid_ports(NULL) , _input_ready(1) , _process_lock(0) , _n_inputs_ready(0) , _ports(NULL) , _providers(new Raul::List<NodeImpl*>()) , _dependants(new Raul::List<NodeImpl*>()) + , _activated(false) + , _traversed(false) { assert(_plugin); assert(_polyphony > 0); @@ -62,6 +63,8 @@ NodeBase::~NodeBase() delete _providers; delete _dependants; + + free(_valid_ports); } @@ -202,27 +205,59 @@ NodeBase::signal_input_ready() /** Prepare to run a cycle (in the audio thread) */ void -NodeBase::pre_process(ProcessContext& context) +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) - _ports->at(i)->pre_process(context); + for (size_t i=0; i < num_ports(); ++i) { + PortImpl* const port = _ports->at(i); + if (port->context() == Context::AUDIO) + port->pre_process(context); + } } /** Prepare to run a cycle (in the audio thread) */ void -NodeBase::post_process(ProcessContext& context) +NodeBase::post_process(Context& context) { assert(ThreadManager::current_thread_id() == THREAD_PROCESS); /* Write output ports */ - if (_ports) - for (size_t i=0; i < _ports->size(); ++i) + 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); + } +} + + +/** Flag a port as set (for message context) + */ +void +NodeBase::set_port_valid(uint32_t port_index) +{ + // Allocate enough space for one bit per port + if (!_valid_ports) + _valid_ports = calloc(num_ports() / 8, 1); + lv2_contexts_set_port_valid(_valid_ports, port_index); +} + + +void* +NodeBase::valid_ports() +{ + return _valid_ports; +} + + +void +NodeBase::reset_valid_ports() +{ + if (_valid_ports) + memset(_valid_ports, '\0', num_ports() / 8); } diff --git a/src/engine/NodeBase.hpp b/src/engine/NodeBase.hpp index 2537039b..15772551 100644 --- a/src/engine/NodeBase.hpp +++ b/src/engine/NodeBase.hpp @@ -27,6 +27,7 @@ #include "raul/Atom.hpp" #include "interface/Port.hpp" #include "NodeImpl.hpp" +#include "contexts.lv2/contexts.h" namespace Ingen { @@ -70,11 +71,16 @@ public: virtual void learn() {} - virtual void message_process(MessageContext& context, uint32_t* ins, uint32_t* outs) {} + virtual void message_run(MessageContext& context) {} - virtual void pre_process(ProcessContext& context); + virtual void set_port_valid(uint32_t port_index); + + virtual void* valid_ports(); + virtual void reset_valid_ports(); + + virtual void pre_process(Context& context); virtual void process(ProcessContext& context) = 0; - virtual void post_process(ProcessContext& context); + virtual void post_process(Context& context); virtual void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) {} @@ -115,15 +121,18 @@ protected: uint32_t _polyphony; SampleRate _srate; size_t _buffer_size; - bool _activated; - bool _traversed; ///< Flag for process order algorithm + void* _valid_ports; ///< Valid port flags for message context + Raul::Semaphore _input_ready; ///< Parallelism: input ready signal Raul::AtomicInt _process_lock; ///< Parallelism: Waiting on inputs 'lock' Raul::AtomicInt _n_inputs_ready; ///< Parallelism: # input ready signals this cycle Raul::Array<PortImpl*>* _ports; ///< Access in audio thread only Raul::List<NodeImpl*>* _providers; ///< Nodes connected to this one's input ports Raul::List<NodeImpl*>* _dependants; ///< Nodes this one's output ports are connected to + + bool _activated; + bool _traversed; ///< Flag for process order algorithm }; diff --git a/src/engine/NodeImpl.hpp b/src/engine/NodeImpl.hpp index 7f3e243a..67ab0898 100644 --- a/src/engine/NodeImpl.hpp +++ b/src/engine/NodeImpl.hpp @@ -33,6 +33,7 @@ class PluginImpl; class PatchImpl; class PortImpl; class MessageContext; +class ProcessContext; /** A Node (or "module") in a Patch (which is also a Node). @@ -112,7 +113,16 @@ public: /** Run the node for one instant in the message thread. */ - virtual void message_process(MessageContext& context, uint32_t* ins, uint32_t* outs) = 0; + virtual void message_run(MessageContext& context) = 0; + + /** Flag a port as valid (for message context) */ + virtual void set_port_valid(uint32_t index) = 0; + + /** Return a bit vector of which ports are valid */ + virtual void* valid_ports() = 0; + + /** Clear all bits in valid_ports() */ + virtual void reset_valid_ports() = 0; /** Run the node for @a nframes input/output. * diff --git a/src/engine/OSCDriver.hpp b/src/engine/OSCDriver.hpp index 38eacd48..1c838210 100644 --- a/src/engine/OSCDriver.hpp +++ b/src/engine/OSCDriver.hpp @@ -32,7 +32,7 @@ namespace Ingen { class OSCDriver : public Driver { public: - OSCDriver() : Driver(Shared::DataType::EVENT) {} + OSCDriver() : Driver(Shared::DataType::EVENTS) {} /** Prepare events (however neccessary) for the specified block (realtime safe) */ virtual void prepare_block(const SampleCount block_start, const SampleCount block_end) = 0; diff --git a/src/engine/ObjectBuffer.cpp b/src/engine/ObjectBuffer.cpp new file mode 100644 index 00000000..0f40ea9a --- /dev/null +++ b/src/engine/ObjectBuffer.cpp @@ -0,0 +1,119 @@ +/* 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 + */ + +#define __STDC_LIMIT_MACROS 1 +#include <string.h> +#include <stdint.h> +#include <algorithm> +#include <iostream> +#include "uri-map.lv2/uri-map.h" +#include "ingen-config.h" +#include "shared/LV2Features.hpp" +#include "shared/LV2URIMap.hpp" +#include "ObjectBuffer.hpp" +#include "Engine.hpp" + +using namespace std; + +namespace Ingen { + +using namespace Shared; + + +/** Allocate a new string buffer. + * \a capacity is in bytes. + */ +ObjectBuffer::ObjectBuffer(size_t capacity) + : Buffer(DataType(DataType::OBJECT), 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(); +} + + +void +ObjectBuffer::clear() +{ + // nil + _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) +{ + const ObjectBuffer* src = dynamic_cast<const ObjectBuffer*>(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); +} + + +void +ObjectBuffer::resize(size_t size) +{ + const bool using_local_data = (_buf == _local_buf); + const uint32_t contents_size = sizeof(LV2_Object) + _buf->size; + + _local_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; +} + + +} // namespace Ingen + diff --git a/src/engine/StringBuffer.hpp b/src/engine/ObjectBuffer.hpp index 79d83f7a..a1d198e9 100644 --- a/src/engine/StringBuffer.hpp +++ b/src/engine/ObjectBuffer.hpp @@ -15,45 +15,43 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef STRINGBUFFER_H -#define STRINGBUFFER_H +#ifndef OBJECTBUFFER_H +#define OBJECTBUFFER_H -#include "string-port.lv2/string-port.h" +#include "raul/Atom.hpp" +#include "object.lv2/object.h" #include "interface/DataType.hpp" #include "Buffer.hpp" namespace Ingen { +class Context; -class StringBuffer : public Buffer { +class ObjectBuffer : public Buffer { public: - StringBuffer(size_t capacity); + ObjectBuffer(size_t capacity); void clear(); - void* raw_data() { return (void*)&_buf; } - const void* raw_data() const { return (void*)&_buf; } + void* raw_data() { return (void*)_buf; } + const void* raw_data() const { return (void*)_buf; } - char* data() { return _buf->data; } - const char* data() const { return _buf->data; } - - void prepare_read(FrameTime start, SampleCount nframes); - void prepare_write(FrameTime start, SampleCount nframes); + LV2_Object* data() { return _buf; } + const LV2_Object* data() const { return _buf; } bool join(Buffer* buf); void unjoin(); - void copy(const Buffer* src, size_t start_sample, size_t end_sample); + void copy(Context& context, const Buffer* src); void resize(size_t size); private: - LV2_String_Data* _buf; ///< Contents (_local_buf or belongs to _joined_buf) - LV2_String_Data _local_buf; ///< Local contents - uint32_t _this_nframes; ///< Current cycle nframes + LV2_Object* _buf; ///< Contents (_local_buf or belongs to _joined_buf) + LV2_Object* _local_buf; ///< Local contents }; } // namespace Ingen -#endif // STRINGBUFFER_H +#endif // OBJECTBUFFER_H diff --git a/src/engine/OutputPort.cpp b/src/engine/OutputPort.cpp index 023d5a21..8f6b7751 100644 --- a/src/engine/OutputPort.cpp +++ b/src/engine/OutputPort.cpp @@ -47,18 +47,18 @@ OutputPort::OutputPort(NodeImpl* parent, void -OutputPort::pre_process(ProcessContext& context) +OutputPort::pre_process(Context& context) { for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_write(context.start(), context.nframes()); + buffer(i)->prepare_write(context); } void -OutputPort::post_process(ProcessContext& context) +OutputPort::post_process(Context& context) { for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_read(context.start(), context.nframes()); + buffer(i)->prepare_read(context); //cerr << path() << " output post: buffer: " << buffer(0) << endl; diff --git a/src/engine/OutputPort.hpp b/src/engine/OutputPort.hpp index e5c4ed28..15382332 100644 --- a/src/engine/OutputPort.hpp +++ b/src/engine/OutputPort.hpp @@ -47,8 +47,8 @@ public: const Raul::Atom& value, size_t buffer_size); - void pre_process(ProcessContext& context); - void post_process(ProcessContext& context); + void pre_process(Context& context); + void post_process(Context& context); virtual ~OutputPort() {} diff --git a/src/engine/PatchImpl.hpp b/src/engine/PatchImpl.hpp index 86133971..8bdbd21a 100644 --- a/src/engine/PatchImpl.hpp +++ b/src/engine/PatchImpl.hpp @@ -34,6 +34,7 @@ namespace Shared { class Connection; } class ConnectionImpl; class Engine; class CompiledPatch; +class ProcessContext; /** A group of nodes in a graph, possibly polyphonic. diff --git a/src/engine/PortImpl.cpp b/src/engine/PortImpl.cpp index 7559ae02..20d0b682 100644 --- a/src/engine/PortImpl.cpp +++ b/src/engine/PortImpl.cpp @@ -23,10 +23,12 @@ #include "events/SendPortActivity.hpp" #include "AudioBuffer.hpp" #include "EventBuffer.hpp" +#include "Engine.hpp" +#include "LV2Object.hpp" #include "NodeImpl.hpp" +#include "ObjectBuffer.hpp" #include "PortImpl.hpp" #include "ProcessContext.hpp" -#include "StringBuffer.hpp" using namespace std; using namespace Raul; @@ -69,7 +71,7 @@ PortImpl::PortImpl(NodeImpl* const node, add_property("rdf:type", Atom(Atom::URI, type.uri())); - if (type == DataType::EVENT) + if (type == DataType::EVENTS) _broadcast = true; // send activity blips assert(_buffers->size() > 0); @@ -176,7 +178,7 @@ PortImpl::clear_buffers() void -PortImpl::broadcast_value(ProcessContext& context, bool force) +PortImpl::broadcast_value(Context& context, bool force) { Raul::Atom val; switch (_type.symbol()) { @@ -186,14 +188,14 @@ PortImpl::broadcast_value(ProcessContext& context, bool force) case DataType::CONTROL: val = ((AudioBuffer*)buffer(0))->value_at(0); break; - case DataType::EVENT: + case DataType::EVENTS: if (((EventBuffer*)buffer(0))->event_count() > 0) { const Events::SendPortActivity ev(context.engine(), context.start(), this); context.event_sink().write(sizeof(ev), &ev); } break; - case DataType::STRING: - val = Raul::Atom(((StringBuffer*)buffer(0))->data()); + case DataType::OBJECT: + LV2Object::to_atom(context.engine().world(), ((ObjectBuffer*)buffer(0))->data(), val); break; } diff --git a/src/engine/PortImpl.hpp b/src/engine/PortImpl.hpp index 7d587165..b9c8dc52 100644 --- a/src/engine/PortImpl.hpp +++ b/src/engine/PortImpl.hpp @@ -87,12 +87,13 @@ public: } /** Called once per process cycle */ - virtual void pre_process(ProcessContext& context) = 0; + virtual void pre_process(Context& context) = 0; virtual void process(ProcessContext& context) {}; - virtual void post_process(ProcessContext& context) = 0; + virtual void post_process(Context& context) = 0; /** Empty buffer contents completely (ie silence) */ virtual void clear_buffers(); + virtual void connect_buffers(); virtual bool is_input() const = 0; virtual bool is_output() const = 0; @@ -112,7 +113,7 @@ public: void broadcast(bool b) { _broadcast = b; } bool broadcast() { return _broadcast; } - void broadcast_value(ProcessContext& context, bool force=false); + void broadcast_value(Context& context, bool force=false); void raise_set_by_user_flag() { _set_by_user = true; } @@ -129,7 +130,6 @@ protected: size_t buffer_size); virtual void allocate_buffers(); - virtual void connect_buffers(); uint32_t _index; uint32_t _poly; diff --git a/src/engine/ProcessContext.hpp b/src/engine/ProcessContext.hpp index a5c8149a..743b72c4 100644 --- a/src/engine/ProcessContext.hpp +++ b/src/engine/ProcessContext.hpp @@ -42,26 +42,22 @@ class ProcessContext : public Context public: ProcessContext(Engine& engine) : Context(engine, AUDIO) - , _event_sink(engine, 1024) // FIXME: size? + , _nframes(0) + , _end(0) {} void set_time_slice(SampleCount nframes, FrameTime start, FrameTime end) { + locate(start); _nframes = nframes; - _start = start; _end = end; } - inline SampleCount nframes() const { return _nframes; } - inline FrameTime start() const { return _start; } - inline FrameTime end() const { return _end; } - inline const EventSink& event_sink() const { return _event_sink; } - inline EventSink& event_sink() { return _event_sink; } + inline SampleCount nframes()const { return _nframes; } + inline FrameTime end() const { return _end; } private: - SampleCount _nframes; ///< Number of actual time (Jack) frames this cycle - FrameTime _start; ///< Start frame of this cycle, timeline relative - FrameTime _end; ///< End frame of this cycle, timeline relative - EventSink _event_sink; ///< Sink for events generated in the audio thread + SampleCount _nframes; ///< Length of this cycle in frames + FrameTime _end; ///< End frame of this cycle, timeline relative }; diff --git a/src/engine/QueuedEngineInterface.cpp b/src/engine/QueuedEngineInterface.cpp index d2ff74df..55e02206 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/StringBuffer.cpp b/src/engine/StringBuffer.cpp deleted file mode 100644 index a6cec9f1..00000000 --- a/src/engine/StringBuffer.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* 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 - */ - -#define __STDC_LIMIT_MACROS 1 -#include <string.h> -#include <stdint.h> -#include <iostream> -#include "ingen-config.h" -#include "StringBuffer.hpp" - -using namespace std; - -namespace Ingen { - -using namespace Shared; - -/** Allocate a new string buffer. - * \a capacity is in bytes. - */ -StringBuffer::StringBuffer(size_t capacity) - : Buffer(DataType(DataType::EVENT), capacity) -{ - memset(&_local_buf, '\0', sizeof(LV2_String_Data)); - _local_buf.data = (char*)malloc(capacity); - _local_buf.storage = capacity; - - _buf = &_local_buf; - clear(); - - cerr << "Creating String Buffer " << _buf << ", capacity = " << capacity << endl; -} - - -void -StringBuffer::clear() -{ - static const string default_val("2\n0 1\n1 1\n"); - if (_buf && _buf->data && _buf->storage > default_val.length()) - strncpy(_buf->data, default_val.c_str(), default_val.length()); -} - - -/** Use another buffer's data instead of the local one. - * - * This buffer will essentially be identical to @a buf after this call. - */ -bool -StringBuffer::join(Buffer* buf) -{ - assert(buf != this); - StringBuffer* sbuf = dynamic_cast<StringBuffer*>(buf); - if (!sbuf) - return false; - - _buf = &sbuf->_local_buf; - _joined_buf = sbuf; - - return true; -} - - -void -StringBuffer::unjoin() -{ - _joined_buf = NULL; - _buf = &_local_buf; -} - - -void -StringBuffer::prepare_read(FrameTime start, SampleCount nframes) -{ - _this_nframes = nframes; -} - - -void -StringBuffer::prepare_write(FrameTime start, SampleCount nframes) -{ -} - - -void -StringBuffer::copy(const Buffer* src_buf, size_t start_sample, size_t end_sample) -{ - const StringBuffer* src = dynamic_cast<const StringBuffer*>(src_buf); - assert(src); - assert(src != this); - assert(src->_buf != _buf); - assert(src->_buf->data != _buf->data); - - strncpy(_buf->data, src->_buf->data, std::min(_buf->len, src->_buf->len)); - _this_nframes = end_sample - start_sample; -} - - -void -StringBuffer::resize(size_t size) -{ - _buf->data = (char*)realloc(_buf->data, size); - _buf->storage = size; - _size = size; -} - - -} // namespace Ingen - diff --git a/src/engine/events/ClearPatch.cpp b/src/engine/events/ClearPatch.cpp index 3957294e..92c0156c 100644 --- a/src/engine/events/ClearPatch.cpp +++ b/src/engine/events/ClearPatch.cpp @@ -117,7 +117,7 @@ ClearPatch::execute(ProcessContext& context) if (port && port->type() == DataType::AUDIO) { _driver_ports->push_back( _engine.audio_driver()->remove_port(port->path())); - } else if (port && port->type() == DataType::EVENT) { + } else if (port && port->type() == DataType::EVENTS) { _driver_ports->push_back( _engine.midi_driver()->remove_port(port->path())); } diff --git a/src/engine/events/Delete.cpp b/src/engine/events/Delete.cpp index d8a8bef9..aa773176 100644 --- a/src/engine/events/Delete.cpp +++ b/src/engine/events/Delete.cpp @@ -155,7 +155,7 @@ Delete::execute(ProcessContext& context) if ( ! _port->parent_patch()->parent()) { if (_port->type() == DataType::AUDIO) _driver_port = _engine.audio_driver()->remove_port(_port->path()); - else if (_port->type() == DataType::EVENT) + else if (_port->type() == DataType::EVENTS) _driver_port = _engine.midi_driver()->remove_port(_port->path()); // Apparently this needs to be called in post_process?? diff --git a/src/engine/events/Move.cpp b/src/engine/events/Move.cpp index 1b12819e..9782e0d6 100644 --- a/src/engine/events/Move.cpp +++ b/src/engine/events/Move.cpp @@ -108,7 +108,7 @@ Move::execute(ProcessContext& context) if (port->type() == DataType::AUDIO) driver_port = _engine.audio_driver()->driver_port(_new_path); - else if (port->type() == DataType::EVENT) + else if (port->type() == DataType::EVENTS) driver_port = _engine.midi_driver()->driver_port(_new_path); if (driver_port) diff --git a/src/engine/events/RequestMetadata.cpp b/src/engine/events/RequestMetadata.cpp index e633ca10..06069f74 100644 --- a/src/engine/events/RequestMetadata.cpp +++ b/src/engine/events/RequestMetadata.cpp @@ -17,15 +17,17 @@ #include "interface/ClientInterface.hpp" #include "events/RequestMetadata.hpp" -#include "Responder.hpp" +#include "shared/LV2Object.hpp" +#include "AudioBuffer.hpp" +#include "ClientBroadcaster.hpp" #include "Engine.hpp" -#include "GraphObjectImpl.hpp" #include "EngineStore.hpp" -#include "ClientBroadcaster.hpp" -#include "PortImpl.hpp" +#include "GraphObjectImpl.hpp" +#include "ObjectBuffer.hpp" #include "PluginImpl.hpp" -#include "AudioBuffer.hpp" -#include "StringBuffer.hpp" +#include "PortImpl.hpp" +#include "ProcessContext.hpp" +#include "Responder.hpp" using namespace std; using namespace Raul; @@ -94,8 +96,9 @@ RequestMetadata::execute(ProcessContext& context) if (port) { if (port->type() == DataType::CONTROL || port->type() == DataType::AUDIO) _value = ((AudioBuffer*)port->buffer(0))->value_at(0); // TODO: offset - else if (port->type() == DataType::STRING) - _value = (char*)((StringBuffer*)port->buffer(0))->data(); + else if (port->type() == DataType::OBJECT) + LV2Object::to_atom(context.engine().world(), + ((ObjectBuffer*)port->buffer(0))->data(), _value); } else { _resource = 0; } diff --git a/src/engine/events/SetPortValue.cpp b/src/engine/events/SetPortValue.cpp index 1964880d..6b9d8d9e 100644 --- a/src/engine/events/SetPortValue.cpp +++ b/src/engine/events/SetPortValue.cpp @@ -19,6 +19,7 @@ #include "event.lv2/event.h" #include "shared/LV2URIMap.hpp" #include "shared/LV2Features.hpp" +#include "shared/LV2Object.hpp" #include "module/World.hpp" #include "AudioBuffer.hpp" #include "ClientBroadcaster.hpp" @@ -27,11 +28,11 @@ #include "EventBuffer.hpp" #include "MessageContext.hpp" #include "NodeImpl.hpp" +#include "ObjectBuffer.hpp" #include "PortImpl.hpp" #include "ProcessContext.hpp" #include "Responder.hpp" #include "SetPortValue.hpp" -#include "StringBuffer.hpp" using namespace std; using namespace Raul; @@ -115,7 +116,8 @@ SetPortValue::pre_process() // Port is a message context port, set its value and // call the plugin's message run function once if (_port && _port->context() == Context::MESSAGE) { - apply(0, 0); + apply(*_engine.message_context()); + _port->parent_node()->set_port_valid(_port->index()); _engine.message_context()->run(_port->parent_node()); } @@ -132,13 +134,14 @@ SetPortValue::execute(ProcessContext& context) if (_port && _port->context() == Context::MESSAGE) return; - apply(context.start(), context.nframes()); + apply(context); } void -SetPortValue::apply(uint32_t start, uint32_t nframes) +SetPortValue::apply(Context& context) { + uint32_t start = context.start(); if (_error == NO_ERROR && !_port) _port = _engine.engine_store()->find_port(_port_path); @@ -170,15 +173,12 @@ SetPortValue::apply(uint32_t start, uint32_t nframes) return; } - const LV2Features::Feature* f = _engine.world()->lv2_features->feature(LV2_URI_MAP_URI); - LV2URIMap* map = (LV2URIMap*)f->controller; + SharedPtr<LV2URIMap> map = PtrCast<LV2URIMap>( + _engine.world()->lv2_features->feature(LV2_URI_MAP_URI)); - // TODO: eliminate lookups EventBuffer* const ebuf = dynamic_cast<EventBuffer*>(buf); if (ebuf) { - const uint32_t frames = std::max( - uint32_t(_time - start), - ebuf->latest_frames()); + const uint32_t frames = std::max(uint32_t(_time - start), ebuf->latest_frames()); // Size 0 event, pass it along to the plugin as a typed but empty event if (_value.data_size() == 0) { @@ -188,34 +188,27 @@ SetPortValue::apply(uint32_t start, uint32_t nframes) return; } else if (!strcmp(_value.get_blob_type(), "lv2midi:MidiEvent")) { - const uint32_t type_id = map->uri_to_id(NULL, - "http://lv2plug.in/ns/ext/midi#MidiEvent"); - - ebuf->prepare_write(start, nframes); - // FIXME: use OSC midi type? avoid MIDI over OSC entirely? - ebuf->append(frames, 0, type_id, _value.data_size(), + ebuf->prepare_write(context); + ebuf->append(frames, 0, map->midi_event, _value.data_size(), (const uint8_t*)_value.get_blob()); _port->raise_set_by_user_flag(); return; } } - StringBuffer* const sbuf = dynamic_cast<StringBuffer*>(buf); - if (sbuf) { - if (_value.type() != Atom::STRING) { - _error = TYPE_MISMATCH; + 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; return; + } else { + cerr << "WARNING: Failed to convert atom to LV2 object" << endl; } - strncpy(sbuf->data(), _value.get_string(), - std::min(sbuf->size(), strlen(_value.get_string()))); - return; } - - if (_value.type() == Atom::BLOB) - cerr << "WARNING: Unknown value blob type " << _value.get_blob_type() << endl; - else - cerr << "WARNING: Unknown value type " << (int)_value.type() << endl; + cerr << "WARNING: Unknown value type " << (int)_value.type() << endl; } } diff --git a/src/engine/events/SetPortValue.hpp b/src/engine/events/SetPortValue.hpp index 87f4f6f6..aab59811 100644 --- a/src/engine/events/SetPortValue.hpp +++ b/src/engine/events/SetPortValue.hpp @@ -77,7 +77,7 @@ private: TYPE_MISMATCH }; - void apply(uint32_t start, uint32_t nframes); + void apply(Context& context); bool _queued; bool _omni; diff --git a/src/engine/internals/Controller.cpp b/src/engine/internals/Controller.cpp index 70d1e7a9..9788206e 100644 --- a/src/engine/internals/Controller.cpp +++ b/src/engine/internals/Controller.cpp @@ -48,7 +48,7 @@ ControllerNode::ControllerNode(const string& path, { _ports = new Raul::Array<PortImpl*>(6); - _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENT, Raul::Atom(), _buffer_size); + _midi_in_port = new InputPort(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); @@ -84,7 +84,6 @@ ControllerNode::process(ProcessContext& context) uint8_t* buf = NULL; EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); - //assert(midi_in->this_nframes() == context.nframes()); midi_in->rewind(); diff --git a/src/engine/internals/Note.cpp b/src/engine/internals/Note.cpp index 1a7d3f5e..4dc11891 100644 --- a/src/engine/internals/Note.cpp +++ b/src/engine/internals/Note.cpp @@ -48,7 +48,7 @@ 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::EVENT, Raul::Atom(), _buffer_size); + _midi_in_port = new InputPort(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); @@ -126,8 +126,6 @@ NoteNode::process(ProcessContext& context) uint16_t size = 0; uint8_t* buf = NULL; - //assert(midi_in->this_nframes() == context.nframes()); - midi_in->rewind(); if (midi_in->event_count() > 0) diff --git a/src/engine/internals/Trigger.cpp b/src/engine/internals/Trigger.cpp index d30873c9..428a1b8f 100644 --- a/src/engine/internals/Trigger.cpp +++ b/src/engine/internals/Trigger.cpp @@ -41,7 +41,7 @@ TriggerNode::TriggerNode(const string& path, bool polyphonic, PatchImpl* parent, { _ports = new Raul::Array<PortImpl*>(5); - _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENT, Raul::Atom(), _buffer_size); + _midi_in_port = new InputPort(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); @@ -77,7 +77,6 @@ TriggerNode::process(ProcessContext& context) uint8_t* buf = NULL; EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); - //assert(midi_in->this_nframes() == context.nframes()); midi_in->rewind(); diff --git a/src/engine/wscript b/src/engine/wscript index 0b3888f0..232fb281 100644 --- a/src/engine/wscript +++ b/src/engine/wscript @@ -20,6 +20,7 @@ def build(bld): MessageContext.cpp NodeBase.cpp NodeFactory.cpp + ObjectBuffer.cpp ObjectSender.cpp OutputPort.cpp PatchImpl.cpp @@ -28,7 +29,6 @@ def build(bld): PostProcessor.cpp ProcessSlave.cpp QueuedEvent.cpp - StringBuffer.cpp events/SendPortActivity.cpp events/SendPortValue.cpp ingen_engine.cpp |