From 1b964e850bbe3207fe9a65849520634955d141f0 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 29 Jan 2010 04:01:29 +0000 Subject: Send binding information to client. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@2392 a436a847-0d15-0410-975c-d299462d15a1 --- src/bindings/Client.hpp | 1 + src/client/ClientStore.cpp | 9 +++ src/client/ClientStore.hpp | 3 +- src/client/SigClientInterface.hpp | 4 + src/client/ThreadedSigClientInterface.hpp | 6 ++ src/common/interface/ClientInterface.hpp | 3 + src/common/interface/MessageType.hpp | 120 ++++++++++++++++++++++++++++++ src/engine/ClientBroadcaster.hpp | 4 + src/engine/ControlBindings.cpp | 26 +++++-- src/engine/ControlBindings.hpp | 3 +- src/engine/HTTPClientSender.cpp | 7 ++ src/engine/HTTPClientSender.hpp | 2 + src/engine/OSCClientSender.cpp | 20 +++++ src/engine/OSCClientSender.hpp | 2 + src/engine/events/SendBinding.cpp | 39 ++++++++++ src/engine/events/SendBinding.hpp | 68 +++++++++++++++++ src/engine/wscript | 1 + 17 files changed, 310 insertions(+), 8 deletions(-) create mode 100644 src/common/interface/MessageType.hpp create mode 100644 src/engine/events/SendBinding.cpp create mode 100644 src/engine/events/SendBinding.hpp diff --git a/src/bindings/Client.hpp b/src/bindings/Client.hpp index 690d2214..d23166e9 100644 --- a/src/bindings/Client.hpp +++ b/src/bindings/Client.hpp @@ -27,4 +27,5 @@ public: void set_port_value(const Raul::Path& port_path, const Raul::Atom& value) {} void set_voice_value(const Raul::Path& port_path, uint32_t voice, const Raul::Atom& value) {} void activity(const Raul::Path& port_path) {} + void binding(const Raul::Path& path, const MessageType& type) {} }; diff --git a/src/client/ClientStore.cpp b/src/client/ClientStore.cpp index 73d2b23e..40c11323 100644 --- a/src/client/ClientStore.cpp +++ b/src/client/ClientStore.cpp @@ -17,6 +17,7 @@ #include "raul/log.hpp" #include "raul/PathTable.hpp" +#include "interface/MessageType.hpp" #include "ClientStore.hpp" #include "ObjectModel.hpp" #include "PatchModel.hpp" @@ -53,6 +54,7 @@ ClientStore::ClientStore(SharedPtr engine, SharedPtrsignal_port_value.connect(sigc::mem_fun(this, &ClientStore::set_port_value)); emitter->signal_voice_value.connect(sigc::mem_fun(this, &ClientStore::set_voice_value)); emitter->signal_activity.connect(sigc::mem_fun(this, &ClientStore::activity)); + emitter->signal_binding.connect(sigc::mem_fun(this, &ClientStore::binding)); } @@ -383,6 +385,13 @@ ClientStore::activity(const Path& path) } +void +ClientStore::binding(const Path& path, const Shared::MessageType& type) +{ + LOG(info) << "Bind " << path << " : " << type << endl; +} + + SharedPtr ClientStore::connection_patch(const Path& src_port_path, const Path& dst_port_path) { diff --git a/src/client/ClientStore.hpp b/src/client/ClientStore.hpp index ba32b362..63b03dae 100644 --- a/src/client/ClientStore.hpp +++ b/src/client/ClientStore.hpp @@ -33,7 +33,7 @@ namespace Raul { class Atom; } namespace Ingen { -namespace Shared { class GraphObject; } +namespace Shared { class GraphObject; class MessageType; } namespace Client { @@ -96,6 +96,7 @@ private: // Slots for SigClientInterface signals void object_moved(const Raul::Path& old_path, const Raul::Path& new_path); void activity(const Raul::Path& path); + void binding(const Raul::Path& path, const Shared::MessageType& type); bool attempt_connection(const Raul::Path& src_port_path, const Raul::Path& dst_port_path); diff --git a/src/client/SigClientInterface.hpp b/src/client/SigClientInterface.hpp index 785f6ac8..3a3ce9d4 100644 --- a/src/client/SigClientInterface.hpp +++ b/src/client/SigClientInterface.hpp @@ -60,6 +60,7 @@ public: sigc::signal signal_port_value; sigc::signal signal_voice_value; sigc::signal signal_activity; + sigc::signal signal_binding; /** Fire pending signals. Only does anything on derived classes (that may queue) */ virtual bool emit_signals() { return false; } @@ -114,6 +115,9 @@ protected: void activity(const Raul::Path& port_path) { EMIT(activity, port_path); } + + void binding(const Raul::Path& path, const Shared::MessageType& type) + { EMIT(binding, path, type); } }; diff --git a/src/client/ThreadedSigClientInterface.hpp b/src/client/ThreadedSigClientInterface.hpp index 901fd4a6..456aa67f 100644 --- a/src/client/ThreadedSigClientInterface.hpp +++ b/src/client/ThreadedSigClientInterface.hpp @@ -24,6 +24,7 @@ #include #include "raul/Atom.hpp" #include "interface/ClientInterface.hpp" +#include "interface/MessageType.hpp" #include "SigClientInterface.hpp" #include "raul/SRSWQueue.hpp" @@ -60,6 +61,7 @@ public: , property_change_slot(signal_property_change.make_slot()) , port_value_slot(signal_port_value.make_slot()) , activity_slot(signal_activity.make_slot()) + , binding_slot(signal_binding.make_slot()) { } @@ -110,6 +112,9 @@ public: void activity(const Raul::Path& port_path) { push_sig(sigc::bind(activity_slot, port_path)); } + void binding(const Raul::Path& path, const Shared::MessageType& type) + { push_sig(sigc::bind(binding_slot, path, type)); } + /** Process all queued events - Called from GTK thread to emit signals. */ bool emit_signals(); @@ -139,6 +144,7 @@ private: sigc::slot port_value_slot; sigc::slot voice_value_slot; sigc::slot activity_slot; + sigc::slot binding_slot; }; diff --git a/src/common/interface/ClientInterface.hpp b/src/common/interface/ClientInterface.hpp index 619c48b8..9ec875ac 100644 --- a/src/common/interface/ClientInterface.hpp +++ b/src/common/interface/ClientInterface.hpp @@ -28,6 +28,7 @@ namespace Raul { class Path; class URI; } namespace Ingen { namespace Shared { +class MessageType; /** The (only) interface the engine uses to communicate with clients. * Purely virtual (except for the destructor). @@ -55,6 +56,8 @@ public: virtual void error(const std::string& msg) = 0; virtual void activity(const Raul::Path& path) = 0; + + virtual void binding(const Raul::Path& path, const MessageType& type) = 0; }; diff --git a/src/common/interface/MessageType.hpp b/src/common/interface/MessageType.hpp new file mode 100644 index 00000000..ea6bc726 --- /dev/null +++ b/src/common/interface/MessageType.hpp @@ -0,0 +1,120 @@ +/* This file is part of Ingen. + * Copyright (C) 2007-2009 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MESSAGE_TYPE_HPP +#define MESSAGE_TYPE_HPP + +#include +#include +#include + +namespace Ingen { +namespace Shared { + + +/** A type of control message that could be bound to a control port. + * + * \ingroup interface + */ +class MessageType +{ +public: + enum Type { + MIDI_PITCH, + MIDI_CC, + MIDI_RPN, + MIDI_NRPN + }; + + MessageType(Type type, int16_t num) + : _type(type) + { + switch (type) { + case MIDI_PITCH: + break; + case MIDI_CC: + assert(num >= 0 && num < 128); + _id.midi_cc = num; + break; + case MIDI_RPN: + assert(num >= 0 && num < 16384); + _id.midi_pn = num; + break; + case MIDI_NRPN: + assert(num >= 0 && num < 16384); + _id.midi_pn = num; + break; + } + } + + inline Type type() const { return _type; } + inline int8_t midi_cc_num() const { assert(_type == MIDI_CC); return _id.midi_cc; } + inline int8_t midi_rpn_num() const { assert(_type == MIDI_RPN); return _id.midi_pn; } + inline int8_t midi_nrpn_num() const { assert(_type == MIDI_NRPN); return _id.midi_pn; } + + inline int num() const { + switch (_type) { + case MIDI_CC: + return _id.midi_cc; + case MIDI_RPN: + case MIDI_NRPN: + return _id.midi_pn; + default: + return -1; + } + } + + inline const char* type_uri() const { + switch (_type) { + case MIDI_PITCH: + return "midi:PitchBend"; + case MIDI_CC: + return "midi:Control"; + case MIDI_RPN: + return "midi:RPN"; + case MIDI_NRPN: + return "midi:NRPN"; + } + } + +private: + union { + int8_t midi_cc; ///< Controller number [0..2^7) + int16_t midi_pn; ///< RPN or NRPN number [0..2^14) + } _id; + + Type _type; +}; + + +} // namespace Shared +} // namespace Ingen + + +static inline std::ostream& operator<<(std::ostream& os, const Ingen::Shared::MessageType& type) +{ + using namespace Ingen::Shared; + switch (type.type()) { + case MessageType::MIDI_PITCH: return os << "MIDI Pitch Bender"; + case MessageType::MIDI_CC: return os << "MIDI CC " << type.num(); + case MessageType::MIDI_RPN: return os << "MIDI RPN " << type.num(); + case MessageType::MIDI_NRPN: return os << "MIDI NRPN " << type.num(); + } + return os; +} + +#endif // MESSAGE_TYPE_HPP diff --git a/src/engine/ClientBroadcaster.hpp b/src/engine/ClientBroadcaster.hpp index 9de7aeff..fa44bc43 100644 --- a/src/engine/ClientBroadcaster.hpp +++ b/src/engine/ClientBroadcaster.hpp @@ -123,6 +123,10 @@ public: void error(const std::string& msg) { BROADCAST(error, msg); } void activity(const Raul::Path& path) { BROADCAST(activity, path); } + void binding(const Raul::Path& path, const Shared::MessageType& type) { + BROADCAST(binding, path, type); + } + private: typedef std::map Clients; Clients _clients; diff --git a/src/engine/ControlBindings.cpp b/src/engine/ControlBindings.cpp index 00241b54..b74a3124 100644 --- a/src/engine/ControlBindings.cpp +++ b/src/engine/ControlBindings.cpp @@ -18,6 +18,7 @@ #include "raul/log.hpp" #include "raul/midi_events.h" #include "events/SendPortValue.hpp" +#include "events/SendBinding.hpp" #include "ControlBindings.hpp" #include "EventBuffer.hpp" #include "PortImpl.hpp" @@ -41,20 +42,34 @@ ControlBindings::learn(PortImpl* port) void -ControlBindings::set_port_value(ProcessContext& context, PortImpl* port, int8_t value) +ControlBindings::set_port_value(ProcessContext& context, PortImpl* port, int8_t cc_value) { + // TODO: cache these to avoid the lookup float min = port->get_property("lv2:minimum").get_float(); float max = port->get_property("lv2:maximum").get_float(); - Raul::Atom scaled_value(static_cast(((float)value / 127.0) * (max - min) + min)); - port->set_value(scaled_value); + Raul::Atom value(static_cast(((float)cc_value / 127.0) * (max - min) + min)); + port->set_value(value); const Events::SendPortValue ev(context.engine(), context.start(), port, true, 0, - scaled_value.get_float()); + value.get_float()); context.event_sink().write(sizeof(ev), &ev); } +void +ControlBindings::bind(ProcessContext& context, int8_t cc_num) +{ + _bindings.insert(make_pair(cc_num, _learn_port)); + + const Events::SendBinding ev(context.engine(), context.start(), _learn_port, + MessageType(MessageType::MIDI_CC, cc_num)); + context.event_sink().write(sizeof(ev), &ev); + + _learn_port = NULL; +} + + void ControlBindings::process(ProcessContext& context, EventBuffer* buffer) { @@ -69,8 +84,7 @@ ControlBindings::process(ProcessContext& context, EventBuffer* buffer) while (buffer->get_event(&frames, &subframes, &type, &size, &buf)) { if (type == _map->midi_event && (buf[0] & 0xF0) == MIDI_CMD_CONTROL) { const int8_t controller = static_cast(buf[1]); - _bindings.insert(make_pair(controller, _learn_port)); - _learn_port = NULL; + bind(context, controller); break; } buffer->increment(); diff --git a/src/engine/ControlBindings.hpp b/src/engine/ControlBindings.hpp index 5e1e123d..8477a0fa 100644 --- a/src/engine/ControlBindings.hpp +++ b/src/engine/ControlBindings.hpp @@ -46,7 +46,8 @@ private: SharedPtr _map; PortImpl* _learn_port; - void set_port_value(ProcessContext& context, PortImpl* port, int8_t value); + void set_port_value(ProcessContext& context, PortImpl* port, int8_t cc_value); + void bind(ProcessContext& context, int8_t cc_num); typedef std::map Bindings; Bindings _bindings; diff --git a/src/engine/HTTPClientSender.cpp b/src/engine/HTTPClientSender.cpp index 8804c65c..0c14b06f 100644 --- a/src/engine/HTTPClientSender.cpp +++ b/src/engine/HTTPClientSender.cpp @@ -142,6 +142,13 @@ HTTPClientSender::activity(const Path& path) } +void +HTTPClientSender::binding(const Path& path, const MessageType& type) +{ + warn << "TODO: HTTP binding" << endl; +} + + void HTTPClientSender::move(const Path& old_path, const Path& new_path) { diff --git a/src/engine/HTTPClientSender.hpp b/src/engine/HTTPClientSender.hpp index d3bc9505..e7120e81 100644 --- a/src/engine/HTTPClientSender.hpp +++ b/src/engine/HTTPClientSender.hpp @@ -93,6 +93,8 @@ public: virtual void activity(const Raul::Path& path); + virtual void binding(const Raul::Path& path, const Shared::MessageType& type); + private: Engine& _engine; std::string _url; diff --git a/src/engine/OSCClientSender.cpp b/src/engine/OSCClientSender.cpp index 13147a42..f07668ca 100644 --- a/src/engine/OSCClientSender.cpp +++ b/src/engine/OSCClientSender.cpp @@ -20,6 +20,7 @@ #include "raul/log.hpp" #include "raul/AtomLiblo.hpp" #include "interface/ClientInterface.hpp" +#include "interface/MessageType.hpp" #include "EngineStore.hpp" #include "NodeImpl.hpp" #include "OSCClientSender.hpp" @@ -246,4 +247,23 @@ OSCClientSender::activity(const Path& path) } +/** \page client_osc_namespace + *

/ingen/binding

+ * \arg \b path (string) - Path of object + * \arg \b type (string) - Type of message (URI) + * \arg \b id (int) - Controller number (if applicable) + * + * Notification of "activity" (e.g. port message blinkenlights). + */ +void +OSCClientSender::binding(const Path& path, const MessageType& type) +{ + if (!_enabled) + return; + + lo_send(_address, "/ingen/binding", "ssi", + path.c_str(), type.type_uri(), type.num(), LO_ARGS_END); +} + + } // namespace Ingen diff --git a/src/engine/OSCClientSender.hpp b/src/engine/OSCClientSender.hpp index c5317a14..1ce58d45 100644 --- a/src/engine/OSCClientSender.hpp +++ b/src/engine/OSCClientSender.hpp @@ -93,6 +93,8 @@ public: virtual void activity(const Raul::Path& path); + virtual void binding(const Raul::Path& path, const Shared::MessageType& type); + private: Raul::URI _url; }; diff --git a/src/engine/events/SendBinding.cpp b/src/engine/events/SendBinding.cpp new file mode 100644 index 00000000..7168b142 --- /dev/null +++ b/src/engine/events/SendBinding.cpp @@ -0,0 +1,39 @@ +/* This file is part of Ingen. + * Copyright (C) 2007-2009 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "events/SendBinding.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "ClientBroadcaster.hpp" + +using namespace std; + +namespace Ingen { +namespace Events { + + +void +SendBinding::post_process() +{ + _engine.broadcaster()->binding(_port->path(), _type); +} + + +} // namespace Ingen +} // namespace Events + diff --git a/src/engine/events/SendBinding.hpp b/src/engine/events/SendBinding.hpp new file mode 100644 index 00000000..303e29fa --- /dev/null +++ b/src/engine/events/SendBinding.hpp @@ -0,0 +1,68 @@ +/* This file is part of Ingen. + * Copyright (C) 2007-2009 Dave Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SENDBINDINGEVENT_H +#define SENDBINDINGEVENT_H + +#include "raul/Atom.hpp" +#include "interface/MessageType.hpp" +#include "engine/Event.hpp" +#include "engine/types.hpp" + +namespace Ingen { + +class PortImpl; + +namespace Events { + + +/** A special event used internally to send control bindings from the audio thread. + * + * See SendPortValue documentation for details. + * + * \ingroup engine + */ +class SendBinding : public Event +{ +public: + inline SendBinding( + Engine& engine, + SampleCount timestamp, + PortImpl* port, + const Shared::MessageType& type) + : Event(engine, SharedPtr(), timestamp) + , _port(port) + , _type(type) + {} + + inline void operator=(const SendBinding& ev) { + _port = ev._port; + _type = ev._type; + } + + void post_process(); + +private: + PortImpl* _port; + Shared::MessageType _type; +}; + + +} // namespace Ingen +} // namespace Events + +#endif // SENDBINDINGEVENT_H diff --git a/src/engine/wscript b/src/engine/wscript index e0ac6d69..7e630387 100644 --- a/src/engine/wscript +++ b/src/engine/wscript @@ -51,6 +51,7 @@ def build(bld): events/RequestAllObjects.cpp events/RequestMetadata.cpp events/RequestPlugins.cpp + events/SendBinding.cpp events/SendPortActivity.cpp events/SendPortValue.cpp events/SetMetadata.cpp -- cgit v1.2.1