2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #define __STDC_FORMAT_MACROS 1
25 #include <pbd/error.h>
26 #include <pbd/failed_constructor.h>
28 #include <midi++/port.h>
29 #include <midi++/manager.h>
31 #include <ardour/route.h>
32 #include <ardour/session.h>
34 #include "generic_midi_control_protocol.h"
35 #include "midicontrollable.h"
37 using namespace ARDOUR;
42 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
43 : ControlProtocol (s, _("Generic MIDI"))
45 MIDI::Manager* mm = MIDI::Manager::instance();
47 /* XXX it might be nice to run "control" through i18n, but thats a bit tricky because
48 the name is defined in ardour.rc which is likely not internationalized.
51 _port = mm->port (X_("control"));
54 error << _("no MIDI port named \"control\" exists - generic MIDI control disabled") << endmsg;
55 throw failed_constructor();
59 _feedback_interval = 10000; // microseconds
60 last_feedback_time = 0;
64 Controllable::StartLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::start_learning));
65 Controllable::StopLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::stop_learning));
66 Session::SendFeedback.connect (mem_fun (*this, &GenericMidiControlProtocol::send_feedback));
68 Controllable::CreateBinding.connect (mem_fun (*this, &GenericMidiControlProtocol::create_binding));
69 Controllable::DeleteBinding.connect (mem_fun (*this, &GenericMidiControlProtocol::delete_binding));
71 Session::AutoBindingOn.connect (mem_fun (*this, &GenericMidiControlProtocol::auto_binding_on));
72 Session::AutoBindingOff.connect (mem_fun (*this, &GenericMidiControlProtocol::auto_binding_off));
75 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
80 GenericMidiControlProtocol::set_active (bool /*yn*/)
82 /* start/stop delivery/outbound thread */
87 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
89 _feedback_interval = ms;
93 GenericMidiControlProtocol::send_feedback ()
99 microseconds_t now = get_microseconds ();
101 if (last_feedback_time != 0) {
102 if ((now - last_feedback_time) < _feedback_interval) {
109 last_feedback_time = now;
113 GenericMidiControlProtocol::_send_feedback ()
115 const int32_t bufsize = 16 * 1024; /* XXX too big */
116 MIDI::byte buf[bufsize];
117 int32_t bsize = bufsize;
118 MIDI::byte* end = buf;
120 for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
121 end = (*r)->write_feedback (end, bsize);
128 _port->write (buf, (int32_t) (end - buf), 0);
132 GenericMidiControlProtocol::start_learning (Controllable* c)
138 MIDIControllables::iterator tmp;
139 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
142 if (&(*i)->get_controllable() == c) {
144 controllables.erase (i);
149 MIDIPendingControllables::iterator ptmp;
150 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
153 if (&((*i).first)->get_controllable() == c) {
154 (*i).second.disconnect();
156 pending_controllables.erase (i);
162 MIDIControllable* mc = 0;
164 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
165 if ((*i)->get_controllable().id() == c->id()) {
172 mc = new MIDIControllable (*_port, *c);
176 Glib::Mutex::Lock lm (pending_lock);
178 std::pair<MIDIControllable *, sigc::connection> element;
180 element.second = c->LearningFinished.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc));
182 pending_controllables.push_back (element);
185 mc->learn_about_external_control ();
190 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
192 Glib::Mutex::Lock lm (pending_lock);
193 Glib::Mutex::Lock lm2 (controllables_lock);
195 MIDIPendingControllables::iterator tmp;
197 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
201 if ( (*i).first == mc) {
202 (*i).second.disconnect();
203 pending_controllables.erase(i);
209 controllables.insert (mc);
213 GenericMidiControlProtocol::stop_learning (Controllable* c)
215 Glib::Mutex::Lock lm (pending_lock);
216 Glib::Mutex::Lock lm2 (controllables_lock);
217 MIDIControllable* dptr = 0;
219 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
220 relevant MIDIControllable and remove it from the pending list.
223 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
224 if (&((*i).first)->get_controllable() == c) {
225 (*i).first->stop_learning ();
227 (*i).second.disconnect();
229 pending_controllables.erase (i);
238 GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
241 Glib::Mutex::Lock lm2 (controllables_lock);
243 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
244 MIDIControllable* existingBinding = (*iter);
246 if (control == &(existingBinding->get_controllable())) {
247 delete existingBinding;
248 controllables.erase (iter);
256 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
258 if (control != NULL) {
259 Glib::Mutex::Lock lm2 (controllables_lock);
261 MIDI::channel_t channel = (pos & 0xf);
262 MIDI::byte value = control_number;
264 // Create a MIDIControllable
265 MIDIControllable* mc = new MIDIControllable (*_port, *control);
267 // Remove any old binding for this midi channel/type/value pair
268 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
269 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
270 MIDIControllable* existingBinding = (*iter);
272 if ((existingBinding->get_control_channel() & 0xf ) == channel &&
273 existingBinding->get_control_additional() == value &&
274 (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
276 delete existingBinding;
277 controllables.erase (iter);
282 // Update the MIDI Controllable based on the the pos param
283 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
284 mc->bind_midi(channel, MIDI::controller, value);
286 controllables.insert (mc);
291 GenericMidiControlProtocol::auto_binding_on()
297 GenericMidiControlProtocol::auto_binding_off()
299 auto_binding = FALSE;
303 GenericMidiControlProtocol::get_state ()
305 XMLNode* node = new XMLNode ("Protocol");
308 node->add_property (X_("name"), _name);
309 node->add_property (X_("feedback"), do_feedback ? "1" : "0");
310 snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
311 node->add_property (X_("feedback_interval"), buf);
313 XMLNode* children = new XMLNode (X_("controls"));
315 node->add_child_nocopy (*children);
317 Glib::Mutex::Lock lm2 (controllables_lock);
318 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
319 children->add_child_nocopy ((*i)->get_state());
326 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
329 XMLNodeConstIterator niter;
330 const XMLProperty* prop;
332 if ((prop = node.property ("feedback")) != 0) {
333 do_feedback = (bool) atoi (prop->value().c_str());
338 if ((prop = node.property ("feedback_interval")) != 0) {
339 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
340 _feedback_interval = 10000;
343 _feedback_interval = 10000;
346 if ( !auto_binding ) {
348 boost::shared_ptr<Controllable> c;
351 Glib::Mutex::Lock lm (pending_lock);
352 pending_controllables.clear ();
355 Glib::Mutex::Lock lm2 (controllables_lock);
356 controllables.clear ();
357 nlist = node.children(); // "controls"
363 nlist = nlist.front()->children ();
365 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
367 if ((prop = (*niter)->property ("id")) != 0) {
369 ID id = prop->value ();
370 c = session->controllable_by_id (id);
373 MIDIControllable* mc = new MIDIControllable (*_port, *c);
374 if (mc->set_state (**niter) == 0) {
375 controllables.insert (mc);
379 warning << string_compose (
380 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
390 GenericMidiControlProtocol::set_feedback (bool yn)
393 last_feedback_time = 0;
398 GenericMidiControlProtocol::get_feedback () const