summaryrefslogtreecommitdiffstats
path: root/src/engine/ControlBindings.cpp
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2010-02-04 01:29:34 +0000
committerDavid Robillard <d@drobilla.net>2010-02-04 01:29:34 +0000
commit36dc6bdb6d9b96e2a18acbe6c681a842d1590fb6 (patch)
treeeb01898024d18a0d1463a03cbcfaeb0a7e3956c4 /src/engine/ControlBindings.cpp
parentaef3c9348218029a077f40081fd078a9e2ee85ca (diff)
downloadingen-36dc6bdb6d9b96e2a18acbe6c681a842d1590fb6.tar.gz
ingen-36dc6bdb6d9b96e2a18acbe6c681a842d1590fb6.tar.bz2
ingen-36dc6bdb6d9b96e2a18acbe6c681a842d1590fb6.zip
MIDI feedback.
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@2418 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/engine/ControlBindings.cpp')
-rw-r--r--src/engine/ControlBindings.cpp306
1 files changed, 205 insertions, 101 deletions
diff --git a/src/engine/ControlBindings.cpp b/src/engine/ControlBindings.cpp
index 6fb1c6a8..7f58c057 100644
--- a/src/engine/ControlBindings.cpp
+++ b/src/engine/ControlBindings.cpp
@@ -15,8 +15,10 @@
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <math.h>
#include "raul/log.hpp"
#include "raul/midi_events.h"
+#include "raul/IntrusivePtr.hpp"
#include "events/SendPortValue.hpp"
#include "events/SendBinding.hpp"
#include "ControlBindings.hpp"
@@ -34,32 +36,122 @@ using namespace Raul;
namespace Ingen {
-void
-ControlBindings::update_port(ProcessContext& context, PortImpl* port)
+ControlBindings::ControlBindings(Engine& engine, SharedPtr<Shared::LV2URIMap> map)
+ : _engine(engine)
+ , _map(map)
+ , _learn_port(NULL)
+ , _bindings(new Bindings())
+ , _feedback(new EventBuffer(*_engine.buffer_factory(), 1024)) // FIXME: size
{
- const Shared::LV2URIMap& uris = Shared::LV2URIMap::instance();
- const Raul::Atom& binding = port->get_property(uris.ingen_controlBinding);
+}
+
+
+ControlBindings::~ControlBindings()
+{
+ delete _feedback;
+}
+
+
+ControlBindings::Key
+ControlBindings::port_binding(PortImpl* port)
+{
+ const Shared::LV2URIMap& uris = Shared::LV2URIMap::instance();
+ const Raul::Atom& binding = port->get_property(uris.ingen_controlBinding);
+ Key key;
if (binding.type() == Atom::DICT) {
- Atom::DictValue::const_iterator t = binding.get_dict().find(uris.rdf_type);
+ const Atom::DictValue& dict = binding.get_dict();
+ Atom::DictValue::const_iterator t = dict.find(uris.rdf_type);
Atom::DictValue::const_iterator n;
- if (t != binding.get_dict().end()) {
- if (t->second == uris.midi_Bender) {
- _bindings->insert(make_pair(Key(MIDI_BENDER), port));
- } else if (t->second == uris.midi_Bender) {
- _bindings->insert(make_pair(Key(MIDI_BENDER), port));
- } else if (t->second == uris.midi_ChannelPressure) {
- _bindings->insert(make_pair(Key(MIDI_CHANNEL_PRESSURE), port));
- } else if (t->second == uris.midi_Controller) {
- n = binding.get_dict().find(uris.midi_controllerNumber);
- if (n != binding.get_dict().end())
- _bindings->insert(make_pair(Key(MIDI_CC, n->second.get_int32()), port));
- } else if (t->second == uris.midi_Note) {
- n = binding.get_dict().find(uris.midi_noteNumber);
- if (n != binding.get_dict().end())
- _bindings->insert(make_pair(Key(MIDI_NOTE, n->second.get_int32()), port));
- }
+ if (t == dict.end()) {
+ return key;
+ } else if (t->second == uris.midi_Bender) {
+ key = Key(MIDI_BENDER);
+ } else if (t->second == uris.midi_ChannelPressure) {
+ key = Key(MIDI_CHANNEL_PRESSURE);
+ } else if (t->second == uris.midi_Controller) {
+ if ((n = dict.find(uris.midi_controllerNumber)) != dict.end())
+ key = Key(MIDI_CC, n->second.get_int32());
+ } else if (t->second == uris.midi_Note) {
+ if ((n = dict.find(uris.midi_noteNumber)) != dict.end())
+ key = Key(MIDI_NOTE, n->second.get_int32());
}
}
+ return key;
+}
+
+
+ControlBindings::Key
+ControlBindings::midi_event_key(uint16_t size, uint8_t* buf, uint16_t& value)
+{
+ switch (buf[0] & 0xF0) {
+ case MIDI_CMD_CONTROL:
+ value = static_cast<const int8_t>(buf[2]);
+ return Key(MIDI_CC, static_cast<const int8_t>(buf[1]));
+ case MIDI_CMD_BENDER:
+ value = (static_cast<int8_t>(buf[2]) << 7) + static_cast<int8_t>(buf[1]);
+ return Key(MIDI_BENDER);
+ case MIDI_CMD_CHANNEL_PRESSURE:
+ value = static_cast<const int8_t>(buf[1]);
+ return Key(MIDI_CHANNEL_PRESSURE);
+ case MIDI_CMD_NOTE_ON:
+ value = 1.0f;
+ return Key(MIDI_NOTE, static_cast<const int8_t>(buf[1]));
+ default:
+ return Key();
+ }
+}
+
+
+void
+ControlBindings::port_binding_changed(ProcessContext& context, PortImpl* port)
+{
+ Key key = port_binding(port);
+ if (key)
+ _bindings->insert(make_pair(key, port));
+}
+
+
+void
+ControlBindings::port_value_changed(ProcessContext& context, PortImpl* port)
+{
+ Key key = port_binding(port);
+ if (key) {
+ int16_t value = port_value_to_control(port, key.type);
+ uint16_t size = 0;
+ uint8_t buf[4];
+ switch (key.type) {
+ case MIDI_CC:
+ size = 3;
+ buf[0] = MIDI_CMD_CONTROL;
+ buf[1] = key.num;
+ buf[2] = static_cast<int8_t>(value);
+ break;
+ case MIDI_CHANNEL_PRESSURE:
+ size = 2;
+ buf[0] = MIDI_CMD_CHANNEL_PRESSURE;
+ buf[1] = static_cast<int8_t>(value);
+ break;
+ case MIDI_BENDER:
+ size = 3;
+ buf[0] = MIDI_CMD_BENDER;
+ buf[1] = (value & 0x007F);
+ buf[2] = (value & 0x7F00) >> 7;
+ break;
+ case MIDI_NOTE:
+ size = 3;
+ if (value == 1)
+ buf[0] = MIDI_CMD_NOTE_ON;
+ else if (value == 0)
+ buf[0] = MIDI_CMD_NOTE_OFF;
+ buf[1] = key.num;
+ buf[2] = 0x64; // MIDI spec default
+ break;
+ default:
+ break;
+ }
+ if (size > 0)
+ _feedback->append(0, 0, _map->midi_event.id, size, buf);
+ }
}
@@ -71,14 +163,15 @@ ControlBindings::learn(PortImpl* port)
}
-void
-ControlBindings::set_port_value(ProcessContext& context, PortImpl* port, Type type, int16_t value)
+Raul::Atom
+ControlBindings::control_to_port_value(PortImpl* port, Type type, int16_t value)
{
- // TODO: cache these to avoid the lookup
const Shared::LV2URIMap& uris = Shared::LV2URIMap::instance();
- float min = port->get_property(uris.lv2_minimum).get_float();
- float max = port->get_property(uris.lv2_maximum).get_float();
- bool toggled = port->has_property(uris.lv2_portProperty, uris.lv2_toggled);
+
+ // TODO: cache these to avoid the lookup
+ float min = port->get_property(uris.lv2_minimum).get_float();
+ float max = port->get_property(uris.lv2_maximum).get_float();
+ bool toggled = port->has_property(uris.lv2_portProperty, uris.lv2_toggled);
float normal;
switch (type) {
@@ -100,29 +193,63 @@ ControlBindings::set_port_value(ProcessContext& context, PortImpl* port, Type ty
if (toggled)
scaled_value = (scaled_value < 0.5) ? 0.0 : 1.0;
- Raul::Atom atom(scaled_value);
- port->set_value(atom);
+ return Raul::Atom(scaled_value);
+}
+
+
+int16_t
+ControlBindings::port_value_to_control(PortImpl* port, Type type)
+{
+ if (port->value().type() != Atom::FLOAT)
+ return 0;
+
+ const Shared::LV2URIMap& uris = Shared::LV2URIMap::instance();
+
+ // TODO: cache these to avoid the lookup
+ float min = port->get_property(uris.lv2_minimum).get_float();
+ float max = port->get_property(uris.lv2_maximum).get_float();
+ //bool toggled = port->has_property(uris.lv2_portProperty, uris.lv2_toggled);
+ float value = port->value().get_float();
+ float normal = (value - min) / (max - min);
+
+ assert(normal >= 0.0f && normal <= 1.0f);
+ switch (type) {
+ case MIDI_CC:
+ case MIDI_CHANNEL_PRESSURE:
+ return lrintf(normal * 127.0f);
+ case MIDI_BENDER:
+ return lrintf(normal * 16383.0f);
+ case MIDI_NOTE:
+ return (value > 0.0f) ? 1 : 0;
+ default:
+ return 0;
+ }
+}
+
+void
+ControlBindings::set_port_value(ProcessContext& context, PortImpl* port, Type type, int16_t value)
+{
const Events::SendPortValue ev(context.engine(), context.start(), port, true, 0,
- atom.get_float());
+ control_to_port_value(port, type, value));
context.event_sink().write(sizeof(ev), &ev);
}
bool
-ControlBindings::bind(ProcessContext& context, Type type, int16_t num)
+ControlBindings::bind(ProcessContext& context, Key key)
{
const Shared::LV2URIMap& uris = Shared::LV2URIMap::instance();
assert(_learn_port);
- if (type == MIDI_NOTE) {
+ if (key.type == MIDI_NOTE) {
bool toggled = _learn_port->has_property(uris.lv2_portProperty, uris.lv2_toggled);
if (!toggled)
return false;
}
- _bindings->insert(make_pair(Key(type, num), _learn_port));
+ _bindings->insert(make_pair(key, _learn_port));
- const Events::SendBinding ev(context.engine(), context.start(), _learn_port, type, num);
+ const Events::SendBinding ev(context.engine(), context.start(), _learn_port, key.type, key.num);
context.event_sink().write(sizeof(ev), &ev);
_learn_port = NULL;
@@ -154,88 +281,65 @@ ControlBindings::remove(const Raul::Path& path)
void
-ControlBindings::process(ProcessContext& context, EventBuffer* buffer)
+ControlBindings::pre_process(ProcessContext& context, EventBuffer* buffer)
{
uint32_t frames = 0;
uint32_t subframes = 0;
uint16_t type = 0;
uint16_t size = 0;
uint8_t* buf = NULL;
+ uint16_t value = 0;
SharedPtr<Bindings> bindings = _bindings;
+ _feedback->clear();
+ // Learn from input if necessary
if (_learn_port) {
- buffer->rewind();
- int16_t num;
- while (buffer->get_event(&frames, &subframes, &type, &size, &buf)) {
- if (type == _map->midi_event.id) {
- switch (buf[0] & 0xF0) {
- case MIDI_CMD_CONTROL:
- num = static_cast<const int8_t>(buf[1]);
- bind(context, MIDI_CC, num);
- break;
- case MIDI_CMD_BENDER:
- bind(context, MIDI_BENDER);
- break;
- case MIDI_CMD_CHANNEL_PRESSURE:
- bind(context, MIDI_CHANNEL_PRESSURE);
- break;
- case MIDI_CMD_NOTE_ON:
- num = static_cast<const int8_t>(buf[1]);
- bind(context, MIDI_NOTE, num);
- break;
- default:
- break;
- }
- }
- if (!_learn_port)
+ for (buffer->rewind();
+ buffer->get_event(&frames, &subframes, &type, &size, &buf);
+ buffer->increment()) {
+ if (type != _map->midi_event.id)
+ continue;
+
+ const Key key = midi_event_key(size, buf, value);
+ if (key && bind(context, key))
break;
- buffer->increment();
}
}
- if (!bindings->empty()) {
- buffer->rewind();
- while (buffer->get_event(&frames, &subframes, &type, &size, &buf)) {
- if (type == _map->midi_event.id) {
- if ((buf[0] & 0xF0) == MIDI_CMD_CONTROL) {
- const int8_t controller = static_cast<const int8_t>(buf[1]);
- Bindings::const_iterator i = bindings->find(Key(MIDI_CC, controller));
- if (i != bindings->end()) {
- const int8_t value = static_cast<const int8_t>(buf[2]);
- set_port_value(context, i->second, MIDI_CC, value);
- }
- } else if ((buf[0] & 0xF0) == MIDI_CMD_BENDER) {
- Bindings::const_iterator i = bindings->find(Key(MIDI_BENDER));
- if (i != bindings->end()) {
- const int8_t lsb = static_cast<const int8_t>(buf[1]);
- const int8_t msb = static_cast<const int8_t>(buf[2]);
- const int16_t value = (msb << 7) + lsb;
- set_port_value(context, i->second, MIDI_BENDER, value);
- }
- } else if ((buf[0] & 0xF0) == MIDI_CMD_CHANNEL_PRESSURE) {
- Bindings::const_iterator i = bindings->find(Key(MIDI_CHANNEL_PRESSURE));
- if (i != bindings->end()) {
- const int8_t value = static_cast<const int8_t>(buf[1]);
- set_port_value(context, i->second, MIDI_CHANNEL_PRESSURE, value);
- }
- } else if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_ON) {
- const int8_t num = static_cast<const int8_t>(buf[1]);
- Bindings::const_iterator i = bindings->find(Key(MIDI_NOTE, num));
- if (i != bindings->end()) {
- set_port_value(context, i->second, MIDI_NOTE, 1.0f);
- }
- } else if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_OFF) {
- const int8_t num = static_cast<const int8_t>(buf[1]);
- Bindings::const_iterator i = bindings->find(Key(MIDI_NOTE, num));
- if (i != bindings->end()) {
- set_port_value(context, i->second, MIDI_NOTE, 0.0f);
- }
- }
- }
- buffer->increment();
- }
+ // If bindings are empty, no sense reading input
+ if (bindings->empty())
+ return;
+
+ // Read input and apply control values
+ for (buffer->rewind();
+ buffer->get_event(&frames, &subframes, &type, &size, &buf);
+ buffer->increment()) {
+ if (type != _map->midi_event.id)
+ continue;
+
+ const Key key = midi_event_key(size, buf, value);
+ if (!key)
+ continue;
+
+ Bindings::const_iterator i = bindings->find(key);
+ if (i == bindings->end())
+ continue;
+
+ set_port_value(context, i->second, key.type, value);
}
}
+
+void
+ControlBindings::post_process(ProcessContext& context, EventBuffer* buffer)
+{
+ if (_feedback->event_count() > 0) {
+ // TODO: merge buffer's existing contents (anything send to it in the patch)
+ _feedback->rewind();
+ buffer->copy(context, _feedback);
+ }
}
+
+
+} // namespace Ingen