From 64bd557e75113743f179086b365ea7d97b72ee3e Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 12 Nov 2009 06:56:26 +0000 Subject: String port support. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@2255 a436a847-0d15-0410-975c-d299462d15a1 --- src/client/PluginUI.cpp | 9 ++- src/common/interface/DataType.hpp | 15 +++-- src/common/string-port.lv2 | 1 + src/engine/AudioBuffer.cpp | 1 - src/engine/AudioBuffer.hpp | 2 - src/engine/Buffer.cpp | 3 + src/engine/Buffer.hpp | 12 ++-- src/engine/EventBuffer.hpp | 15 ++--- src/engine/LV2Info.cpp | 3 + src/engine/LV2Info.hpp | 1 + src/engine/LV2Node.cpp | 70 +++++++++++++++----- src/engine/PortImpl.cpp | 29 ++++---- src/engine/PortImpl.hpp | 2 +- src/engine/StringBuffer.cpp | 121 ++++++++++++++++++++++++++++++++++ src/engine/StringBuffer.hpp | 59 +++++++++++++++++ src/engine/events/RequestMetadata.cpp | 13 ++-- src/engine/events/RequestMetadata.hpp | 10 +-- src/engine/events/SendPortValue.hpp | 21 +++--- src/engine/events/SetPortValue.cpp | 24 +++++-- src/engine/internals/Note.cpp | 6 +- src/engine/wscript | 1 + src/gui/Configuration.cpp | 7 +- src/gui/Configuration.hpp | 1 + src/gui/ControlPanel.cpp | 22 +++++-- src/gui/ControlPanel.hpp | 7 +- src/gui/Controls.cpp | 80 +++++++++++++++++++++- src/gui/Controls.hpp | 25 +++++++ src/gui/NodeModule.cpp | 23 ++++--- src/gui/PortMenu.cpp | 2 +- src/gui/ingen_gui.glade | 63 +++++++++++++++++- src/ingen/main.cpp | 29 ++++---- src/serialisation/Serialiser.cpp | 2 - src/shared/ResourceImpl.cpp | 3 + 33 files changed, 559 insertions(+), 123 deletions(-) create mode 120000 src/common/string-port.lv2 create mode 100644 src/engine/StringBuffer.cpp create mode 100644 src/engine/StringBuffer.hpp diff --git a/src/client/PluginUI.cpp b/src/client/PluginUI.cpp index 405dd099..3007e723 100644 --- a/src/client/PluginUI.cpp +++ b/src/client/PluginUI.cpp @@ -17,6 +17,7 @@ #include #include "event.lv2/event-helpers.h" +#include "string-port.lv2/string-port.h" #include "shared/LV2Features.hpp" #include "shared/LV2URIMap.hpp" #include "PluginUI.hpp" @@ -67,7 +68,7 @@ lv2_ui_write(LV2UI_Controller controller, ui->world()->engine->set_port_value(port->path(), Atom(*(float*)buffer)); - // FIXME: slow, need to cache ID + // FIXME: this is slow, cache ID } else if (format == map->uri_to_id(NULL, "http://lv2plug.in/ns/extensions/ui#Events")) { uint32_t midi_event_type = map->uri_to_id(NULL, "http://lv2plug.in/ns/ext/midi#MidiEvent"); LV2_Event_Buffer* buf = (LV2_Event_Buffer*)buffer; @@ -87,6 +88,12 @@ lv2_ui_write(LV2UI_Controller controller, lv2_event_increment(&iter); } + + // FIXME: this is slow, cache ID + } else if (format == map->uri_to_id(NULL, "http://lv2plug.in/ns/dev/string-port#StringTransfer")) { + LV2_String_Data* buf = (LV2_String_Data*)buffer; + ui->world()->engine->set_port_value(port->path(), buf->data); + } else { cerr << "WARNING: Unknown value format " << format << ", either plugin " << ui->node()->plugin()->uri() << " is broken" diff --git a/src/common/interface/DataType.hpp b/src/common/interface/DataType.hpp index aae7f52a..4590a9a1 100644 --- a/src/common/interface/DataType.hpp +++ b/src/common/interface/DataType.hpp @@ -39,7 +39,10 @@ public: UNKNOWN = 0, AUDIO = 1, CONTROL = 2, - EVENT = 3 + EVENT = 3, + //MIDI = 4, + //OSC = 5, + STRING = 6 }; DataType(const Raul::URI& uri) @@ -49,8 +52,10 @@ public: _symbol = AUDIO; } else if (uri.str() == type_uri(CONTROL)) { _symbol = CONTROL; - } else if (uri.str() == type_uri(EVENT) || uri.str() == "lv2ev:EventPort") { + } else if (uri.str() == type_uri(EVENT)) { _symbol = EVENT; + } else if (uri.str() == type_uri(STRING)) { + _symbol = STRING; } } @@ -69,6 +74,7 @@ public: inline bool is_audio() { return _symbol == AUDIO; } inline bool is_control() { return _symbol == CONTROL; } inline bool is_event() { return _symbol == EVENT; } + inline bool is_string() { return _symbol == STRING; } private: @@ -77,8 +83,9 @@ private: case 1: return "lv2:AudioPort"; case 2: return "lv2:ControlPort"; case 3: return "lv2ev:EventPort"; - case 4: return "lv2ev:EventPort"; // MIDI - case 5: return "lv2ev:EventPort"; // OSC + case 4: return "lv2ev:EventPort"; // MIDI (no longer used) + case 5: return "lv2ev:EventPort"; // OSC (no longer used) + case 6: return "sp:StringPort"; default: return ""; } } diff --git a/src/common/string-port.lv2 b/src/common/string-port.lv2 new file mode 120000 index 00000000..3615878f --- /dev/null +++ b/src/common/string-port.lv2 @@ -0,0 +1 @@ +../../../lv2/dev/string-port.lv2 \ No newline at end of file diff --git a/src/engine/AudioBuffer.cpp b/src/engine/AudioBuffer.cpp index dc7efee5..02ebea57 100644 --- a/src/engine/AudioBuffer.cpp +++ b/src/engine/AudioBuffer.cpp @@ -35,7 +35,6 @@ AudioBuffer::AudioBuffer(size_t size) : Buffer((size == 1) ? DataType::CONTROL : DataType::AUDIO, size) , _data(NULL) , _local_data(NULL) - , _size(size) , _filled_size(0) , _state(OK) , _set_value(0) diff --git a/src/engine/AudioBuffer.hpp b/src/engine/AudioBuffer.hpp index 732ff0db..aff990ec 100644 --- a/src/engine/AudioBuffer.hpp +++ b/src/engine/AudioBuffer.hpp @@ -56,7 +56,6 @@ public: void prepare_read(FrameTime start, SampleCount nframes); void prepare_write(FrameTime start, SampleCount nframes) {} - void rewind() const {} void resize(size_t size); void filled_size(size_t size) { _filled_size = size; } @@ -72,7 +71,6 @@ private: 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 _size; ///< Allocated buffer size 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) diff --git a/src/engine/Buffer.cpp b/src/engine/Buffer.cpp index a8b091e0..14d97314 100644 --- a/src/engine/Buffer.cpp +++ b/src/engine/Buffer.cpp @@ -17,6 +17,7 @@ #include "AudioBuffer.hpp" #include "EventBuffer.hpp" +#include "StringBuffer.hpp" namespace Ingen { @@ -31,6 +32,8 @@ Buffer::create(DataType type, size_t size) return new AudioBuffer(size); else if (type.is_event()) return new EventBuffer(size); + else if (type.is_string()) + return new StringBuffer(size); else throw; } diff --git a/src/engine/Buffer.hpp b/src/engine/Buffer.hpp index c6b6dd1b..a1a56725 100644 --- a/src/engine/Buffer.hpp +++ b/src/engine/Buffer.hpp @@ -44,25 +44,25 @@ public: /** 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; /** Rewind (ie reset read pointer), but leave contents unchanged */ - virtual void rewind() const = 0; + virtual void rewind() const {} + + virtual void copy(const Buffer* src, size_t start_sample, size_t end_sample) = 0; virtual void prepare_read(FrameTime start, SampleCount nframes) = 0; virtual void prepare_write(FrameTime start, SampleCount nframes) = 0; - bool is_joined() const { return (_joined_buf != NULL); } + 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; - virtual void copy(const Buffer* src, size_t start_sample, size_t end_sample) = 0; - - virtual void resize(size_t size) { _size = size; } - Shared::DataType type() const { return _type; } size_t size() const { return _size; } diff --git a/src/engine/EventBuffer.hpp b/src/engine/EventBuffer.hpp index b35fb13e..f89705b0 100644 --- a/src/engine/EventBuffer.hpp +++ b/src/engine/EventBuffer.hpp @@ -31,17 +31,20 @@ class EventBuffer : public Buffer { public: EventBuffer(size_t capacity); - void prepare_read(FrameTime start, SampleCount nframes); - void prepare_write(FrameTime start, SampleCount nframes); - bool join(Buffer* buf); void unjoin(); + void clear() { reset(_this_nframes); } + void* raw_data() { return _buf; } const void* raw_data() const { return _buf; } - void copy(const Buffer* src, size_t start_sample, size_t end_sample); + void rewind() const { _buf->rewind(); } + void prepare_read(FrameTime start, SampleCount nframes); + void prepare_write(FrameTime start, SampleCount nframes); + + void copy(const Buffer* src, size_t start_sample, size_t end_sample); bool merge(const EventBuffer& a, const EventBuffer& b); bool increment() const { return _buf->increment(); } @@ -52,10 +55,6 @@ public: inline uint32_t this_nframes() const { return _this_nframes; } inline uint32_t event_count() const { return _buf->event_count(); } - inline void rewind() const { _buf->rewind(); } - - inline void clear() { reset(_this_nframes); } - inline void reset(SampleCount nframes) { _this_nframes = nframes; _buf->reset(); diff --git a/src/engine/LV2Info.cpp b/src/engine/LV2Info.cpp index 91db3e7b..10541d0c 100644 --- a/src/engine/LV2Info.cpp +++ b/src/engine/LV2Info.cpp @@ -32,6 +32,8 @@ 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")) , _world(world) { assert(world); @@ -55,6 +57,7 @@ LV2Info::~LV2Info() slv2_value_free(control_class); slv2_value_free(audio_class); slv2_value_free(event_class); + slv2_value_free(string_class); } diff --git a/src/engine/LV2Info.hpp b/src/engine/LV2Info.hpp index 2486efca..6f34dc57 100644 --- a/src/engine/LV2Info.hpp +++ b/src/engine/LV2Info.hpp @@ -47,6 +47,7 @@ public: SLV2Value control_class; SLV2Value audio_class; SLV2Value event_class; + SLV2Value string_class; Ingen::Shared::World& world() { return *_world; } SLV2World lv2_world() { return _world->slv2_world; } diff --git a/src/engine/LV2Node.cpp b/src/engine/LV2Node.cpp index df2ccbb2..70ee2e08 100644 --- a/src/engine/LV2Node.cpp +++ b/src/engine/LV2Node.cpp @@ -43,11 +43,11 @@ using namespace Shared; * (It _will_ crash!) */ LV2Node::LV2Node(LV2Plugin* plugin, - const string& name, - bool polyphonic, - PatchImpl* parent, - SampleRate srate, - size_t buffer_size) + const string& name, + bool polyphonic, + PatchImpl* parent, + SampleRate srate, + size_t buffer_size) : NodeBase(plugin, name, polyphonic, parent, srate, buffer_size) , _lv2_plugin(plugin) , _instances(NULL) @@ -168,7 +168,7 @@ LV2Node::instantiate() (*_instances)[i], LV2_CONTEXT_MESSAGE); if (i == 0 && ctx_ext) { - cerr << "HAS CONTEXT EXTENSION" << endl; + cerr << _lv2_plugin->uri() << " has message context" << endl; assert(!_message_funcs); _message_funcs = (LV2MessageContext*)ctx_ext; } @@ -184,9 +184,17 @@ LV2Node::instantiate() float* def_values = new float[num_ports]; slv2_plugin_get_port_ranges_float(plug, 0, 0, def_values); - SLV2Value pred = slv2_value_new_uri(info->lv2_world(), + SLV2Value context_pred = slv2_value_new_uri(info->lv2_world(), "http://lv2plug.in/ns/dev/contexts#context"); + // FIXME: Why doesn't this just use lv2:default? + SLV2Value default_pred = slv2_value_new_uri(info->lv2_world(), + "http://lv2plug.in/ns/dev/string-port#default"); + + // FIXME: Make this a separate extension + SLV2Value size_pred = slv2_value_new_uri(info->lv2_world(), + "http://lv2plug.in/ns/dev/string-port#requiredSpace"); + for (uint32_t j=0; j < num_ports; ++j) { SLV2Port id = slv2_plugin_get_port_by_index(plug, j); @@ -196,6 +204,7 @@ LV2Node::instantiate() port_path = path().child(port_name); + Raul::Atom val; DataType data_type = DataType::UNKNOWN; if (slv2_port_is_a(plug, id, info->control_class)) { data_type = DataType::CONTROL; @@ -206,6 +215,34 @@ LV2Node::instantiate() } else if (slv2_port_is_a(plug, id, info->event_class)) { data_type = DataType::EVENT; port_buffer_size = _buffer_size; + } else if (slv2_port_is_a(plug, id, info->string_class)) { + data_type = DataType::STRING; + port_buffer_size = 0; + + // Get default value, and its length + SLV2Values defaults = slv2_port_get_value(plug, id, default_pred); + for (uint32_t i = 0; i < slv2_values_size(defaults); ++i) { + SLV2Value d = slv2_values_get_at(defaults, i); + if (slv2_value_is_string(d)) { + const char* str_val = slv2_value_as_string(d); + const size_t str_val_len = strlen(str_val); + if (str_val_len >= port_buffer_size) { + val = str_val; + port_buffer_size = str_val_len; + } + } + } + + // Get minimum size, if set in data + SLV2Values sizes = slv2_port_get_value(plug, id, size_pred); + for (uint32_t i = 0; i < slv2_values_size(sizes); ++i) { + SLV2Value d = slv2_values_get_at(sizes, i); + if (slv2_value_is_int(d)) { + size_t size_val = slv2_value_as_int(d); + if (size_val > port_buffer_size) + port_buffer_size = size_val; + } + } } enum { UNKNOWN, INPUT, OUTPUT } direction = UNKNOWN; @@ -223,31 +260,30 @@ LV2Node::instantiate() return false; } - // FIXME: need nice type preserving SLV2Value -> Raul::Atom conversion - const float def = isnan(def_values[j]) ? 0.0f : def_values[j]; - const Raul::Atom defatm = def; + if (val.type() == Atom::NIL) + val = isnan(def_values[j]) ? 0.0f : def_values[j]; if (direction == INPUT) - port = new InputPort(this, port_name, j, _polyphony, data_type, defatm, port_buffer_size); + port = new InputPort(this, port_name, j, _polyphony, data_type, val, port_buffer_size); else - port = new OutputPort(this, port_name, j, _polyphony, data_type, defatm, port_buffer_size); + port = new OutputPort(this, port_name, j, _polyphony, data_type, val, port_buffer_size); if (direction == INPUT && data_type == DataType::CONTROL) - ((AudioBuffer*)port->buffer(0))->set_value(def, 0, 0); + ((AudioBuffer*)port->buffer(0))->set_value(val.get_float(), 0, 0); - SLV2Values contexts = slv2_port_get_value(plug, id, pred); + SLV2Values contexts = slv2_port_get_value(plug, id, context_pred); 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)) { - cout << "MESSAGE CONTEXT!" << endl; + cerr << _lv2_plugin->uri() << " port " << i << " has message context" << endl; if (!_message_funcs) { - cerr << "Plugin " << _lv2_plugin->uri() + cerr << _lv2_plugin->uri() << " has a message port, but no context extension data." << endl; } port->set_context(Context::MESSAGE); } else { - cout << "UNKNOWN CONTEXT: " + cout << _lv2_plugin->uri() << " port " << i << " has unknown context " << slv2_value_as_string(slv2_values_get_at(contexts, i)) << endl; } diff --git a/src/engine/PortImpl.cpp b/src/engine/PortImpl.cpp index 05fcfe67..7559ae02 100644 --- a/src/engine/PortImpl.cpp +++ b/src/engine/PortImpl.cpp @@ -18,14 +18,15 @@ #include #include "raul/Array.hpp" #include "raul/Maid.hpp" -#include "PortImpl.hpp" -#include "NodeImpl.hpp" #include "interface/DataType.hpp" +#include "events/SendPortValue.hpp" +#include "events/SendPortActivity.hpp" #include "AudioBuffer.hpp" #include "EventBuffer.hpp" +#include "NodeImpl.hpp" +#include "PortImpl.hpp" #include "ProcessContext.hpp" -#include "events/SendPortValue.hpp" -#include "events/SendPortActivity.hpp" +#include "StringBuffer.hpp" using namespace std; using namespace Raul; @@ -177,19 +178,13 @@ PortImpl::clear_buffers() void PortImpl::broadcast_value(ProcessContext& context, bool force) { + Raul::Atom val; switch (_type.symbol()) { case DataType::UNKNOWN: break; case DataType::AUDIO: case DataType::CONTROL: - { - const Sample val = ((AudioBuffer*)buffer(0))->value_at(0); - if (force || val != _last_broadcasted_value) { - _last_broadcasted_value = val; - const Events::SendPortValue ev(context.engine(), context.start(), this, false, 0, val); - context.event_sink().write(sizeof(ev), &ev); - } - } + val = ((AudioBuffer*)buffer(0))->value_at(0); break; case DataType::EVENT: if (((EventBuffer*)buffer(0))->event_count() > 0) { @@ -197,7 +192,17 @@ PortImpl::broadcast_value(ProcessContext& context, bool force) context.event_sink().write(sizeof(ev), &ev); } break; + case DataType::STRING: + val = Raul::Atom(((StringBuffer*)buffer(0))->data()); + break; } + + if (val.type() == Atom::FLOAT && (force || val != _last_broadcasted_value)) { + _last_broadcasted_value = val; + const Events::SendPortValue ev(context.engine(), context.start(), this, false, 0, val); + context.event_sink().write(sizeof(ev), &ev); + } + } diff --git a/src/engine/PortImpl.hpp b/src/engine/PortImpl.hpp index 986e8233..7d587165 100644 --- a/src/engine/PortImpl.hpp +++ b/src/engine/PortImpl.hpp @@ -139,7 +139,7 @@ protected: bool _fixed_buffers; bool _broadcast; bool _set_by_user; - Sample _last_broadcasted_value; + Raul::Atom _last_broadcasted_value; Context::ID _context; Raul::Array* _buffers; diff --git a/src/engine/StringBuffer.cpp b/src/engine/StringBuffer.cpp new file mode 100644 index 00000000..a6cec9f1 --- /dev/null +++ b/src/engine/StringBuffer.cpp @@ -0,0 +1,121 @@ +/* This file is part of Ingen. + * Copyright (C) 2009 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define __STDC_LIMIT_MACROS 1 +#include +#include +#include +#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(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(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/StringBuffer.hpp b/src/engine/StringBuffer.hpp new file mode 100644 index 00000000..79d83f7a --- /dev/null +++ b/src/engine/StringBuffer.hpp @@ -0,0 +1,59 @@ +/* This file is part of Ingen. + * Copyright (C) 2009 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef STRINGBUFFER_H +#define STRINGBUFFER_H + +#include "string-port.lv2/string-port.h" +#include "interface/DataType.hpp" +#include "Buffer.hpp" + +namespace Ingen { + + +class StringBuffer : public Buffer { +public: + StringBuffer(size_t capacity); + + void clear(); + + 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); + + bool join(Buffer* buf); + void unjoin(); + + void copy(const Buffer* src, size_t start_sample, size_t end_sample); + + 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 +}; + + +} // namespace Ingen + +#endif // STRINGBUFFER_H diff --git a/src/engine/events/RequestMetadata.cpp b/src/engine/events/RequestMetadata.cpp index 145260ef..e633ca10 100644 --- a/src/engine/events/RequestMetadata.cpp +++ b/src/engine/events/RequestMetadata.cpp @@ -25,6 +25,7 @@ #include "PortImpl.hpp" #include "PluginImpl.hpp" #include "AudioBuffer.hpp" +#include "StringBuffer.hpp" using namespace std; using namespace Raul; @@ -36,11 +37,11 @@ using namespace Shared; RequestMetadata::RequestMetadata(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - bool is_meta, - const URI& subject, - const URI& key) + SharedPtr responder, + SampleCount timestamp, + bool is_meta, + const URI& subject, + const URI& key) : QueuedEvent(engine, responder, timestamp) , _error(NO_ERROR) , _special_type(NONE) @@ -93,6 +94,8 @@ 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 { _resource = 0; } diff --git a/src/engine/events/RequestMetadata.hpp b/src/engine/events/RequestMetadata.hpp index ba860aaf..0777c79a 100644 --- a/src/engine/events/RequestMetadata.hpp +++ b/src/engine/events/RequestMetadata.hpp @@ -45,11 +45,11 @@ class RequestMetadata : public QueuedEvent { public: RequestMetadata(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - bool meta, - const Raul::URI& subject, - const Raul::URI& key); + SharedPtr responder, + SampleCount timestamp, + bool meta, + const Raul::URI& subject, + const Raul::URI& key); void pre_process(); void execute(ProcessContext& context); diff --git a/src/engine/events/SendPortValue.hpp b/src/engine/events/SendPortValue.hpp index d3ac61f7..b942398c 100644 --- a/src/engine/events/SendPortValue.hpp +++ b/src/engine/events/SendPortValue.hpp @@ -18,6 +18,7 @@ #ifndef SENDPORTVALUEEVENT_H #define SENDPORTVALUEEVENT_H +#include "raul/Atom.hpp" #include "engine/Event.hpp" #include "engine/types.hpp" @@ -43,12 +44,12 @@ class SendPortValue : public Event { public: inline SendPortValue( - Engine& engine, - SampleCount timestamp, - PortImpl* port, - bool omni, - uint32_t voice_num, - Sample value) + Engine& engine, + SampleCount timestamp, + PortImpl* port, + bool omni, + uint32_t voice_num, + const Raul::Atom& value) : Event(engine, SharedPtr(), timestamp) , _port(port) , _omni(omni) @@ -67,10 +68,10 @@ public: void post_process(); private: - PortImpl* _port; - bool _omni; - uint32_t _voice_num; - Sample _value; + PortImpl* _port; + bool _omni; + uint32_t _voice_num; + Raul::Atom _value; }; diff --git a/src/engine/events/SetPortValue.cpp b/src/engine/events/SetPortValue.cpp index eacf172b..1964880d 100644 --- a/src/engine/events/SetPortValue.cpp +++ b/src/engine/events/SetPortValue.cpp @@ -31,6 +31,7 @@ #include "ProcessContext.hpp" #include "Responder.hpp" #include "SetPortValue.hpp" +#include "StringBuffer.hpp" using namespace std; using namespace Raul; @@ -169,21 +170,18 @@ SetPortValue::apply(uint32_t start, uint32_t nframes) return; } - EventBuffer* const ebuf = dynamic_cast(buf); - const LV2Features::Feature* f = _engine.world()->lv2_features->feature(LV2_URI_MAP_URI); LV2URIMap* map = (LV2URIMap*)f->controller; - // FIXME: eliminate lookups - // FIXME: need a proper prefix system - if (ebuf && _value.type() == Atom::BLOB) { + // TODO: eliminate lookups + EventBuffer* const ebuf = dynamic_cast(buf); + if (ebuf) { const uint32_t frames = std::max( - (uint32_t)(_time - start), + 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) { - cout << "BANG!" << endl; const uint32_t type_id = map->uri_to_id(NULL, _value.get_blob_type()); ebuf->append(frames, 0, type_id, 0, NULL); _port->raise_set_by_user_flag(); @@ -202,6 +200,18 @@ SetPortValue::apply(uint32_t start, uint32_t nframes) } } + StringBuffer* const sbuf = dynamic_cast(buf); + if (sbuf) { + if (_value.type() != Atom::STRING) { + _error = TYPE_MISMATCH; + return; + } + 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 diff --git a/src/engine/internals/Note.cpp b/src/engine/internals/Note.cpp index 4b42807b..1a7d3f5e 100644 --- a/src/engine/internals/Note.cpp +++ b/src/engine/internals/Note.cpp @@ -131,7 +131,8 @@ NoteNode::process(ProcessContext& context) midi_in->rewind(); if (midi_in->event_count() > 0) - while (midi_in->get_event(&frames, &subframes, &type, &size, &buf)) { + for (midi_in->rewind(); midi_in->get_event(&frames, &subframes, &type, &size, &buf); + midi_in->increment()) { /*cout << "EVENT TYPE " << type << " @ " << frames << "." << subframes << ": "; for (uint16_t i = 0; i < size; ++i) @@ -178,9 +179,6 @@ NoteNode::process(ProcessContext& context) } else { //fprintf(stderr, "Unknown (size %d) MIDI event %X\n", size, buf[0]); } - - if (midi_in->increment() == midi_in->this_nframes()) - break; } NodeBase::post_process(context); diff --git a/src/engine/wscript b/src/engine/wscript index b4d0dc7a..0b3888f0 100644 --- a/src/engine/wscript +++ b/src/engine/wscript @@ -28,6 +28,7 @@ def build(bld): PostProcessor.cpp ProcessSlave.cpp QueuedEvent.cpp + StringBuffer.cpp events/SendPortActivity.cpp events/SendPortValue.cpp ingen_engine.cpp diff --git a/src/gui/Configuration.cpp b/src/gui/Configuration.cpp index de0a0e94..c7a5b19c 100644 --- a/src/gui/Configuration.cpp +++ b/src/gui/Configuration.cpp @@ -41,6 +41,7 @@ Configuration::Configuration() , _control_port_color(0x4A8A0EC0) , _event_port_color( 0x960909C0) // , _osc_port_color( 0x5C3566C0) + , _string_port_color( 0x00000000) { } @@ -91,9 +92,11 @@ Configuration::get_port_color(const PortModel* p) return _audio_port_color; } else if (p->type().is_event()) { return _event_port_color; - } /*else if (p->type().is_osc()) { + /*} else if (p->type().is_osc()) { return _osc_port_color; - }*/ + */} else if (p->type().is_string()) { + return _string_port_color; + } cerr << "[Configuration] Unknown port type " << p->type().uri() << ", port will appear black." << endl; diff --git a/src/gui/Configuration.hpp b/src/gui/Configuration.hpp index b3d64ff8..480ed6d1 100644 --- a/src/gui/Configuration.hpp +++ b/src/gui/Configuration.hpp @@ -68,6 +68,7 @@ private: uint32_t _audio_port_color; uint32_t _control_port_color; uint32_t _event_port_color; + uint32_t _string_port_color; }; diff --git a/src/gui/ControlPanel.cpp b/src/gui/ControlPanel.cpp index 99b66899..1e7d177b 100644 --- a/src/gui/ControlPanel.cpp +++ b/src/gui/ControlPanel.cpp @@ -111,22 +111,29 @@ ControlPanel::add_port(SharedPtr pm) if (find_port(pm->path()) != NULL) return; - // Add port - if (pm->type().is_control() && pm->is_input()) { - Control* control = NULL; + Control* control = NULL; - if (pm->is_toggle()) { + // Add port + if (pm->is_input()) { + if (pm->type().is_control() && pm->is_toggle()) { ToggleControl* tc; Glib::RefPtr xml = GladeFactory::new_glade_reference("toggle_control"); xml->get_widget_derived("toggle_control", tc); control = tc; + } else if (pm->type().is_string()) { + StringControl* sc; + Glib::RefPtr xml = GladeFactory::new_glade_reference("string_control"); + xml->get_widget_derived("string_control", sc); + control = sc; } else { SliderControl* sc; Glib::RefPtr xml = GladeFactory::new_glade_reference("control_strip"); xml->get_widget_derived("control_strip", sc); control = sc; } + } + if (control) { control->init(this, pm); if (_controls.size() > 0) @@ -223,20 +230,21 @@ ControlPanel::disable_port(const Path& path) /** Callback for Controls to notify this of a change. */ void -ControlPanel::value_changed(SharedPtr port, float val) +ControlPanel::value_changed_atom(SharedPtr port, const Raul::Atom& val) { if (_callback_enabled) { if (_all_voices_radio->get_active()) { - App::instance().engine()->set_port_value(port->path(), Atom(val)); + App::instance().engine()->set_port_value(port->path(), val); port->value(val); } else { int voice = _voice_spinbutton->get_value_as_int(); - App::instance().engine()->set_voice_value(port->path(), voice, Atom(val)); + App::instance().engine()->set_voice_value(port->path(), voice, val); port->value(val); } } } + void ControlPanel::all_voices_selected() { diff --git a/src/gui/ControlPanel.hpp b/src/gui/ControlPanel.hpp index 146173dd..375c0a72 100644 --- a/src/gui/ControlPanel.hpp +++ b/src/gui/ControlPanel.hpp @@ -66,7 +66,12 @@ public: std::pair ideal_size() const { return _ideal_size; } // Callback for Control - void value_changed(SharedPtr port_path, float val); + void value_changed_atom(SharedPtr port, const Raul::Atom& val); + + template + void value_changed(SharedPtr port, T val) { + this->value_changed_atom(port, Raul::Atom(val)); + } private: void all_voices_selected(); diff --git a/src/gui/Controls.cpp b/src/gui/Controls.cpp index a6a2ead3..39c18a88 100644 --- a/src/gui/Controls.cpp +++ b/src/gui/Controls.cpp @@ -279,7 +279,6 @@ SliderControl::update_value_from_spinner() _control_panel->value_changed(_port_model, value); - //m_port_model->value(value); _enable_signal = true; } } @@ -469,5 +468,84 @@ ToggleControl::toggled() } +// ///////////// StringControl ////////////// // + + +StringControl::StringControl(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Control(cobject, xml) +{ + xml->get_widget("string_control_name_label", _name_label); + xml->get_widget("string_control_entry", _entry); +} + + +void +StringControl::init(ControlPanel* panel, SharedPtr pm) +{ + _enable_signal = false; + + Control::init(panel, pm); + + assert(_name_label); + assert(_entry); + + set_name(pm->path().name()); + + _entry->signal_activate().connect(sigc::mem_fun(*this, &StringControl::activated)); + set_value(pm->value()); + + _enable_signal = true; + show_all(); +} + + +void +StringControl::set_name(const string& name) +{ + string name_label = ""; + name_label += name + ""; + _name_label->set_markup(name_label); +} + + +void +StringControl::set_value(const Atom& val) +{ + _enable_signal = false; + if (val.type() == Atom::STRING) + _entry->set_text(val.get_string()); + else + cerr << "ERROR: Non-string value for string port" << endl; + _enable_signal = true; +} + + +void +StringControl::enable() +{ + _entry->property_sensitive() = true; + _name_label->property_sensitive() = true; +} + + +void +StringControl::disable() +{ + _entry->property_sensitive() = false; + _name_label->property_sensitive() = false; +} + + +void +StringControl::activated() +{ + if (_enable_signal) { + const string& value = _entry->get_text(); + cerr << "String control activated: " << value << endl; + _control_panel->value_changed(_port_model, value.c_str()); + } +} + + } // namespace GUI } // namespace Ingen diff --git a/src/gui/Controls.hpp b/src/gui/Controls.hpp index 9a6a13f5..0f0dbd79 100644 --- a/src/gui/Controls.hpp +++ b/src/gui/Controls.hpp @@ -157,6 +157,31 @@ private: }; +/** A text entry for string controls. + * + * \ingroup GUI + */ +class StringControl : public Control +{ +public: + StringControl(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void init(ControlPanel* panel, SharedPtr pm); + + void enable(); + void disable(); + +private: + void set_name(const std::string& name); + void set_value(const Raul::Atom& value); + + void activated(); + + Gtk::Label* _name_label; + Gtk::Entry* _entry; +}; + + } // namespace GUI } // namespace Ingen diff --git a/src/gui/NodeModule.cpp b/src/gui/NodeModule.cpp index fbc89fc3..4df32263 100644 --- a/src/gui/NodeModule.cpp +++ b/src/gui/NodeModule.cpp @@ -144,20 +144,23 @@ NodeModule::show_human_names(bool b) void NodeModule::value_changed(uint32_t index, const Atom& value) { - float control = 0.0f; + if (!_plugin_ui) + return; + + float float_val = 0.0f; + SLV2UIInstance inst = _plugin_ui->instance(); + const LV2UI_Descriptor* ui_desc = slv2_ui_instance_get_descriptor(inst); + LV2UI_Handle ui = slv2_ui_instance_get_handle(inst); + switch (value.type()) { case Atom::FLOAT: - control = value.get_float(); - if (_plugin_ui) { - SLV2UIInstance inst = _plugin_ui->instance(); - const LV2UI_Descriptor* ui_descriptor = slv2_ui_instance_get_descriptor(inst); - LV2UI_Handle ui_handle = slv2_ui_instance_get_handle(inst); - if (ui_descriptor->port_event) - ui_descriptor->port_event(ui_handle, index, 4, 0, &control); - } + float_val = value.get_float(); + if (ui_desc->port_event) + ui_desc->port_event(ui, index, 4, 0, &float_val); break; case Atom::STRING: - cout << "Port value type is a string? (\"" << value.get_string() << "\")" << endl; + if (ui_desc->port_event) + ui_desc->port_event(ui, index, strlen(value.get_string()), 0, value.get_string()); break; default: break; diff --git a/src/gui/PortMenu.cpp b/src/gui/PortMenu.cpp index 834d1a32..c6229361 100644 --- a/src/gui/PortMenu.cpp +++ b/src/gui/PortMenu.cpp @@ -48,7 +48,7 @@ PortMenu::init(SharedPtr port, bool patch_port) _destroy_menuitem->hide(); } - if (port->type() == DataType::EVENT) + if (port->type() == DataType::EVENT || port->type() == DataType::STRING) _polyphonic_menuitem->hide(); _enable_signal = true; diff --git a/src/gui/ingen_gui.glade b/src/gui/ingen_gui.glade index d1054c7e..371c7bd0 100644 --- a/src/gui/ingen_gui.glade +++ b/src/gui/ingen_gui.glade @@ -1230,7 +1230,7 @@ True - 5 + 6 2 @@ -1244,7 +1244,6 @@ True 0 - 1 4 <b>Name</b> True @@ -1714,6 +1713,66 @@ 8 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + vertical + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + 0 + 4 + <b>Name</b> + True + True + + + False + False + 0 + + + + + True + True + automatic + automatic + + + True + True + + False + + + + + 1 + + + + + False + False + 0 + + + + + 5 + 6 + 8 + + + + + diff --git a/src/ingen/main.cpp b/src/ingen/main.cpp index caf65e78..3fa1c7ce 100644 --- a/src/ingen/main.cpp +++ b/src/ingen/main.cpp @@ -109,16 +109,17 @@ main(int argc, char** argv) Ingen::Shared::World* world = Ingen::Shared::get_world(); /* Set up RDF world */ - world->rdf_world->add_prefix("xsd", "http://www.w3.org/2001/XMLSchema#"); - world->rdf_world->add_prefix("ingen", "http://drobilla.net/ns/ingen#"); + world->rdf_world->add_prefix("dc", "http://purl.org/dc/elements/1.1/"); + world->rdf_world->add_prefix("doap", "http://usefulinc.com/ns/doap#"); + world->rdf_world->add_prefix("ingen", "http://drobilla.net/ns/ingen#"); world->rdf_world->add_prefix("ingenuity", "http://drobilla.net/ns/ingenuity#"); - world->rdf_world->add_prefix("lv2", "http://lv2plug.in/ns/lv2core#"); - world->rdf_world->add_prefix("lv2ev", "http://lv2plug.in/ns/ext/event#"); - world->rdf_world->add_prefix("lv2midi", "http://lv2plug.in/ns/ext/midi"); - world->rdf_world->add_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#"); - world->rdf_world->add_prefix("owl", "http://www.w3.org/2002/07/owl#"); - world->rdf_world->add_prefix("doap", "http://usefulinc.com/ns/doap#"); - world->rdf_world->add_prefix("dc", "http://purl.org/dc/elements/1.1/"); + world->rdf_world->add_prefix("lv2", "http://lv2plug.in/ns/lv2core#"); + world->rdf_world->add_prefix("lv2ev", "http://lv2plug.in/ns/ext/event#"); + world->rdf_world->add_prefix("lv2midi", "http://lv2plug.in/ns/ext/midi"); + world->rdf_world->add_prefix("owl", "http://www.w3.org/2002/07/owl#"); + world->rdf_world->add_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#"); + world->rdf_world->add_prefix("sp", "http://lv2plug.in/ns/dev/string-port#"); + world->rdf_world->add_prefix("xsd", "http://www.w3.org/2001/XMLSchema#"); /* Run engine */ if (args.engine_flag) { @@ -298,10 +299,10 @@ main(int argc, char** argv) if (run_gui) gui_run(); - /* Run a script */ - if (args.run_given) { + /* Run a script */ + if (args.run_given) { #ifdef WITH_BINDINGS - bool (*run_script)(Ingen::Shared::World*, const char*) = NULL; + bool (*run_script)(Ingen::Shared::World*, const char*) = NULL; SharedPtr bindings_module = Ingen::Shared::load_module("ingen_bindings"); if (bindings_module) { bindings_module->make_resident(); @@ -314,14 +315,14 @@ main(int argc, char** argv) cerr << "FAILED: " << Glib::Module::get_last_error() << endl; } } else { - cerr << Glib::Module::get_last_error() << endl; + cerr << Glib::Module::get_last_error() << endl; } #else cerr << "This build of ingen does not support scripting." << endl; #endif /* Listen to OSC and do our own main thing. */ - } else if (engine && !run_gui) { + } else if (engine && !run_gui) { signal(SIGINT, catch_int); signal(SIGTERM, catch_int); engine->main(); diff --git a/src/serialisation/Serialiser.cpp b/src/serialisation/Serialiser.cpp index a2e92454..d3583124 100644 --- a/src/serialisation/Serialiser.cpp +++ b/src/serialisation/Serialiser.cpp @@ -496,8 +496,6 @@ Serialiser::serialise_properties( const Redland::Resource key(_model->world(), v->first.str()); const Redland::Node value = AtomRDF::atom_to_node(_model->world(), v->second); if (value.is_valid()) { - cerr << "serialise property '" << v->first << "' = " << value << " :: " - << (int)v->second.type() << endl; _model->add_statement(subject, key, value); } else { cerr << "WARNING: can not serialise variable '" << v->first << "' :: " diff --git a/src/shared/ResourceImpl.cpp b/src/shared/ResourceImpl.cpp index e35bb7ad..b4b40116 100644 --- a/src/shared/ResourceImpl.cpp +++ b/src/shared/ResourceImpl.cpp @@ -112,6 +112,9 @@ ResourceImpl::type( } else if (!strcmp(atom.get_uri(), "lv2ev:EventPort")) { data_type = DataType::EVENT; port = true; + } else if (!strcmp(atom.get_uri(), "sp:StringPort")) { + data_type = DataType::STRING; + port = true; } } } -- cgit v1.2.1