summaryrefslogtreecommitdiffstats
path: root/src/server/ControlBindings.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/ControlBindings.cpp')
-rw-r--r--src/server/ControlBindings.cpp425
1 files changed, 0 insertions, 425 deletions
diff --git a/src/server/ControlBindings.cpp b/src/server/ControlBindings.cpp
deleted file mode 100644
index 3901d1c2..00000000
--- a/src/server/ControlBindings.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- This file is part of Ingen.
- Copyright 2007-2017 David Robillard <http://drobilla.net/>
-
- Ingen is free software: you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free
- Software Foundation, either version 3 of the License, or 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 Affero General Public License for details.
-
- You should have received a copy of the GNU Affero General Public License
- along with Ingen. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <cmath>
-
-#include "ingen/Log.hpp"
-#include "ingen/URIMap.hpp"
-#include "ingen/URIs.hpp"
-#include "ingen/World.hpp"
-#include "lv2/lv2plug.in/ns/ext/atom/util.h"
-#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
-
-#include "Buffer.hpp"
-#include "ControlBindings.hpp"
-#include "Engine.hpp"
-#include "PortImpl.hpp"
-#include "RunContext.hpp"
-#include "ThreadManager.hpp"
-
-namespace Ingen {
-namespace Server {
-
-ControlBindings::ControlBindings(Engine& engine)
- : _engine(engine)
- , _learn_binding(nullptr)
- , _bindings(new Bindings())
- , _feedback(new Buffer(*_engine.buffer_factory(),
- engine.world()->uris().atom_Sequence,
- 0,
- 4096)) // FIXME: capacity?
-{
- lv2_atom_forge_init(
- &_forge, &engine.world()->uri_map().urid_map_feature()->urid_map);
-}
-
-ControlBindings::~ControlBindings()
-{
- _feedback.reset();
- delete _learn_binding.load();
-}
-
-ControlBindings::Key
-ControlBindings::port_binding(PortImpl* port) const
-{
- ThreadManager::assert_thread(THREAD_PRE_PROCESS);
- const Ingen::URIs& uris = _engine.world()->uris();
- const Atom& binding = port->get_property(uris.midi_binding);
- return binding_key(binding);
-}
-
-ControlBindings::Key
-ControlBindings::binding_key(const Atom& binding) const
-{
- const Ingen::URIs& uris = _engine.world()->uris();
- Key key;
- LV2_Atom* num = nullptr;
- if (binding.type() == uris.atom_Object) {
- const LV2_Atom_Object_Body* obj = (const LV2_Atom_Object_Body*)
- binding.get_body();
- if (obj->otype == uris.midi_Bender) {
- key = Key(Type::MIDI_BENDER);
- } else if (obj->otype == uris.midi_ChannelPressure) {
- key = Key(Type::MIDI_CHANNEL_PRESSURE);
- } else if (obj->otype == uris.midi_Controller) {
- lv2_atom_object_body_get(
- binding.size(), obj, (LV2_URID)uris.midi_controllerNumber, &num, NULL);
- if (!num) {
- _engine.log().rt_error("Controller binding missing number\n");
- } else if (num->type != uris.atom_Int) {
- _engine.log().rt_error("Controller number not an integer\n");
- } else {
- key = Key(Type::MIDI_CC, ((LV2_Atom_Int*)num)->body);
- }
- } else if (obj->otype == uris.midi_NoteOn) {
- lv2_atom_object_body_get(
- binding.size(), obj, (LV2_URID)uris.midi_noteNumber, &num, NULL);
- if (!num) {
- _engine.log().rt_error("Note binding missing number\n");
- } else if (num->type != uris.atom_Int) {
- _engine.log().rt_error("Note number not an integer\n");
- } else {
- key = Key(Type::MIDI_NOTE, ((LV2_Atom_Int*)num)->body);
- }
- }
- } else if (binding.type()) {
- _engine.log().rt_error("Unknown binding type\n");
- }
- return key;
-}
-
-ControlBindings::Key
-ControlBindings::midi_event_key(uint16_t size, const uint8_t* buf, uint16_t& value)
-{
- switch (lv2_midi_message_type(buf)) {
- case LV2_MIDI_MSG_CONTROLLER:
- value = static_cast<int8_t>(buf[2]);
- return Key(Type::MIDI_CC, static_cast<int8_t>(buf[1]));
- case LV2_MIDI_MSG_BENDER:
- value = (static_cast<int8_t>(buf[2]) << 7) + static_cast<int8_t>(buf[1]);
- return Key(Type::MIDI_BENDER);
- case LV2_MIDI_MSG_CHANNEL_PRESSURE:
- value = static_cast<int8_t>(buf[1]);
- return Key(Type::MIDI_CHANNEL_PRESSURE);
- case LV2_MIDI_MSG_NOTE_ON:
- value = 1.0f;
- return Key(Type::MIDI_NOTE, static_cast<int8_t>(buf[1]));
- default:
- return Key();
- }
-}
-
-bool
-ControlBindings::set_port_binding(RunContext& context,
- PortImpl* port,
- Binding* binding,
- const Atom& value)
-{
- const Key key = binding_key(value);
- if (!!key) {
- binding->key = key;
- binding->port = port;
- _bindings->insert(*binding);
- return true;
- } else {
- return false;
- }
-}
-
-void
-ControlBindings::port_value_changed(RunContext& ctx,
- PortImpl* port,
- Key key,
- const Atom& value_atom)
-{
- Ingen::World* world = ctx.engine().world();
- const Ingen::URIs& uris = world->uris();
- if (!!key) {
- int16_t value = port_value_to_control(
- ctx, port, key.type, value_atom);
- uint16_t size = 0;
- uint8_t buf[4];
- switch (key.type) {
- case Type::MIDI_CC:
- size = 3;
- buf[0] = LV2_MIDI_MSG_CONTROLLER;
- buf[1] = key.num;
- buf[2] = static_cast<int8_t>(value);
- break;
- case Type::MIDI_CHANNEL_PRESSURE:
- size = 2;
- buf[0] = LV2_MIDI_MSG_CHANNEL_PRESSURE;
- buf[1] = static_cast<int8_t>(value);
- break;
- case Type::MIDI_BENDER:
- size = 3;
- buf[0] = LV2_MIDI_MSG_BENDER;
- buf[1] = (value & 0x007F);
- buf[2] = (value & 0x7F00) >> 7;
- break;
- case Type::MIDI_NOTE:
- size = 3;
- if (value == 1) {
- buf[0] = LV2_MIDI_MSG_NOTE_ON;
- } else if (value == 0) {
- buf[0] = LV2_MIDI_MSG_NOTE_OFF;
- }
- buf[1] = key.num;
- buf[2] = 0x64; // MIDI spec default
- break;
- default:
- break;
- }
- if (size > 0) {
- _feedback->append_event(ctx.nframes() - 1, size, (LV2_URID)uris.midi_MidiEvent, buf);
- }
- }
-}
-
-void
-ControlBindings::start_learn(PortImpl* port)
-{
- ThreadManager::assert_thread(THREAD_PRE_PROCESS);
- Binding* b = _learn_binding.load();
- if (!b) {
- _learn_binding = new Binding(Type::NULL_CONTROL, port);
- } else {
- b->port = port;
- }
-}
-
-static void
-get_range(RunContext& context, const PortImpl* port, float* min, float* max)
-{
- *min = port->minimum().get<float>();
- *max = port->maximum().get<float>();
- if (port->is_sample_rate()) {
- *min *= context.engine().sample_rate();
- *max *= context.engine().sample_rate();
- }
-}
-
-float
-ControlBindings::control_to_port_value(RunContext& context,
- const PortImpl* port,
- Type type,
- int16_t value) const
-{
- float normal = 0.0f;
- switch (type) {
- case Type::MIDI_CC:
- case Type::MIDI_CHANNEL_PRESSURE:
- normal = (float)value / 127.0f;
- break;
- case Type::MIDI_BENDER:
- normal = (float)value / 16383.0f;
- break;
- case Type::MIDI_NOTE:
- normal = (value == 0.0f) ? 0.0f : 1.0f;
- break;
- default:
- break;
- }
-
- if (port->is_logarithmic()) {
- normal = (expf(normal) - 1.0f) / ((float)M_E - 1.0f);
- }
-
- float min, max;
- get_range(context, port, &min, &max);
-
- return normal * (max - min) + min;
-}
-
-int16_t
-ControlBindings::port_value_to_control(RunContext& context,
- PortImpl* port,
- Type type,
- const Atom& value_atom) const
-{
- if (value_atom.type() != port->bufs().forge().Float) {
- return 0;
- }
-
- float min, max;
- get_range(context, port, &min, &max);
-
- const float value = value_atom.get<float>();
- float normal = (value - min) / (max - min);
-
- if (normal < 0.0f) {
- normal = 0.0f;
- }
-
- if (normal > 1.0f) {
- normal = 1.0f;
- }
-
- if (port->is_logarithmic()) {
- normal = logf(normal * ((float)M_E - 1.0f) + 1.0);
- }
-
- switch (type) {
- case Type::MIDI_CC:
- case Type::MIDI_CHANNEL_PRESSURE:
- return lrintf(normal * 127.0f);
- case Type::MIDI_BENDER:
- return lrintf(normal * 16383.0f);
- case Type::MIDI_NOTE:
- return (value > 0.0f) ? 1 : 0;
- default:
- return 0;
- }
-}
-
-static void
-forge_binding(const URIs& uris,
- LV2_Atom_Forge* forge,
- ControlBindings::Type binding_type,
- int32_t value)
-{
- LV2_Atom_Forge_Frame frame;
- switch (binding_type) {
- case ControlBindings::Type::MIDI_CC:
- lv2_atom_forge_object(forge, &frame, 0, uris.midi_Controller);
- lv2_atom_forge_key(forge, uris.midi_controllerNumber);
- lv2_atom_forge_int(forge, value);
- break;
- case ControlBindings::Type::MIDI_BENDER:
- lv2_atom_forge_object(forge, &frame, 0, uris.midi_Bender);
- break;
- case ControlBindings::Type::MIDI_CHANNEL_PRESSURE:
- lv2_atom_forge_object(forge, &frame, 0, uris.midi_ChannelPressure);
- break;
- case ControlBindings::Type::MIDI_NOTE:
- lv2_atom_forge_object(forge, &frame, 0, uris.midi_NoteOn);
- lv2_atom_forge_key(forge, uris.midi_noteNumber);
- lv2_atom_forge_int(forge, value);
- break;
- case ControlBindings::Type::MIDI_RPN: // TODO
- case ControlBindings::Type::MIDI_NRPN: // TODO
- case ControlBindings::Type::NULL_CONTROL:
- break;
- }
-}
-
-void
-ControlBindings::set_port_value(RunContext& context,
- PortImpl* port,
- Type type,
- int16_t value)
-{
- float min, max;
- get_range(context, port, &min, &max);
-
- const float val = control_to_port_value(context, port, type, value);
-
- // TODO: Set port value property so it is saved
- port->set_control_value(context, context.start(), val);
-
- URIs& uris = context.engine().world()->uris();
- context.notify(uris.ingen_value, context.start(), port,
- sizeof(float), _forge.Float, &val);
-}
-
-bool
-ControlBindings::finish_learn(RunContext& context, Key key)
-{
- const Ingen::URIs& uris = context.engine().world()->uris();
- Binding* binding = _learn_binding.exchange(nullptr);
- if (!binding || (key.type == Type::MIDI_NOTE && !binding->port->is_toggled())) {
- return false;
- }
-
- binding->key = key;
- _bindings->insert(*binding);
-
- LV2_Atom buf[16];
- memset(buf, 0, sizeof(buf));
- lv2_atom_forge_set_buffer(&_forge, (uint8_t*)buf, sizeof(buf));
- forge_binding(uris, &_forge, key.type, key.num);
- const LV2_Atom* atom = buf;
- context.notify(uris.midi_binding,
- context.start(),
- binding->port,
- atom->size, atom->type, LV2_ATOM_BODY_CONST(atom));
-
- return true;
-}
-
-void
-ControlBindings::get_all(const Raul::Path& path, std::vector<Binding*>& bindings)
-{
- ThreadManager::assert_thread(THREAD_PRE_PROCESS);
-
- for (Binding& b : *_bindings) {
- if (b.port->path() == path || b.port->path().is_child_of(path)) {
- bindings.push_back(&b);
- }
- }
-}
-
-void
-ControlBindings::remove(RunContext& ctx, const std::vector<Binding*>& bindings)
-{
- for (Binding* b : bindings) {
- _bindings->erase(*b);
- }
-}
-
-void
-ControlBindings::pre_process(RunContext& ctx, Buffer* buffer)
-{
- uint16_t value = 0;
- Ingen::World* world = ctx.engine().world();
- const Ingen::URIs& uris = world->uris();
-
- _feedback->clear();
- if ((!_learn_binding && _bindings->empty()) || !buffer->get<LV2_Atom>()) {
- return; // Don't bother reading input
- }
-
- LV2_Atom_Sequence* seq = buffer->get<LV2_Atom_Sequence>();
- LV2_ATOM_SEQUENCE_FOREACH(seq, ev) {
- if (ev->body.type == uris.midi_MidiEvent) {
- const uint8_t* buf = (const uint8_t*)LV2_ATOM_BODY(&ev->body);
- const Key key = midi_event_key(ev->body.size, buf, value);
-
- if (_learn_binding && !!key) {
- finish_learn(ctx, key); // Learn new binding
- }
-
- // Set all controls bound to this key
- const Binding k = {key, nullptr};
- for (Bindings::const_iterator i = _bindings->lower_bound(k);
- i != _bindings->end() && i->key == key;
- ++i) {
- set_port_value(ctx, i->port, key.type, value);
- }
- }
- }
-}
-
-void
-ControlBindings::post_process(RunContext& context, Buffer* buffer)
-{
- if (buffer->get<LV2_Atom>()) {
- buffer->append_event_buffer(_feedback.get());
- }
-}
-
-} // namespace Server
-} // namespace Ingen