67e940d905c81465bbc8cc086b02b681264dafc3
[ardour.git] / libs / surfaces / generic_midi / generic_midi_control_protocol.cc
1 /*
2     Copyright (C) 2006 Paul Davis
3  
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.
8
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.
13
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.
17
18 */
19
20 #define __STDC_FORMAT_MACROS 1
21 #include <stdint.h>
22
23 #include <algorithm>
24
25 #include "pbd/error.h"
26 #include "pbd/failed_constructor.h"
27
28 #include "midi++/port.h"
29 #include "midi++/manager.h"
30
31 #include "ardour/session.h"
32 #include "ardour/route.h"
33 #include "ardour/midi_ui.h"
34
35 #include "generic_midi_control_protocol.h"
36 #include "midicontrollable.h"
37 #include "midifunction.h"
38
39 using namespace ARDOUR;
40 using namespace PBD;
41 using namespace std;
42
43 #include "i18n.h"
44
45 #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
46 #define ui_bind(x) boost::protect (boost::bind ((x)))
47
48 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
49         : ControlProtocol (s, _("Generic MIDI"), midi_ui_context())
50 {
51         MIDI::Manager* mm = MIDI::Manager::instance();
52
53         /* XXX it might be nice to run "control" through i18n, but thats a bit tricky because
54            the name is defined in ardour.rc which is likely not internationalized.
55         */
56         
57         _port = mm->port (X_("control"));
58
59         if (_port == 0) {
60                 error << _("no MIDI port named \"control\" exists - generic MIDI control disabled") << endmsg;
61                 throw failed_constructor();
62         }
63
64         do_feedback = false;
65         _feedback_interval = 10000; // microseconds
66         last_feedback_time = 0;
67
68         /* XXX is it right to do all these in the same thread as whatever emits the signal? */
69
70         Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1));
71         Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
72         Controllable::CreateBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding, this, _1, _2, _3));
73         Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
74
75         Session::SendFeedback.connect (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
76
77         std::string xmlpath = "/tmp/midi.map";
78
79         load_bindings (xmlpath);
80         reset_controllables ();
81 }
82
83 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
84 {
85         Glib::Mutex::Lock lm (pending_lock);
86         Glib::Mutex::Lock lm2 (controllables_lock);
87
88         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
89                 delete *i;
90         }
91         controllables.clear ();
92
93         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
94                 delete *i;
95         }
96         pending_controllables.clear ();
97
98         for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
99                 delete *i;
100         }
101         functions.clear ();
102 }
103
104 int
105 GenericMidiControlProtocol::set_active (bool /*yn*/)
106 {
107         /* start/stop delivery/outbound thread */
108         return 0;
109 }
110
111 void
112 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
113 {
114         _feedback_interval = ms;
115 }
116
117 void 
118 GenericMidiControlProtocol::send_feedback ()
119 {
120         if (!do_feedback) {
121                 return;
122         }
123
124         microseconds_t now = get_microseconds ();
125
126         if (last_feedback_time != 0) {
127                 if ((now - last_feedback_time) < _feedback_interval) {
128                         return;
129                 }
130         }
131
132         _send_feedback ();
133         
134         last_feedback_time = now;
135 }
136
137 void 
138 GenericMidiControlProtocol::_send_feedback ()
139 {
140         const int32_t bufsize = 16 * 1024; /* XXX too big */
141         MIDI::byte buf[bufsize];
142         int32_t bsize = bufsize;
143         MIDI::byte* end = buf;
144         
145         for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
146                 end = (*r)->write_feedback (end, bsize);
147         }
148         
149         if (end == buf) {
150                 return;
151         } 
152
153         _port->write (buf, (int32_t) (end - buf), 0);
154 }
155
156 bool
157 GenericMidiControlProtocol::start_learning (Controllable* c)
158 {
159         if (c == 0) {
160                 return false;
161         }
162
163         Glib::Mutex::Lock lm (pending_lock);
164         Glib::Mutex::Lock lm2 (controllables_lock);
165
166         MIDIControllables::iterator tmp;
167         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
168                 tmp = i;
169                 ++tmp;
170                 if ((*i)->get_controllable() == c) {
171                         delete (*i);
172                         controllables.erase (i);
173                 }
174                 i = tmp;
175         }
176
177         MIDIPendingControllables::iterator ptmp;
178         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
179                 ptmp = i;
180                 ++ptmp;
181                 if (((*i)->first)->get_controllable() == c) {
182                         (*i)->second.disconnect();
183                         delete (*i)->first;
184                         delete *i;
185                         pending_controllables.erase (i);
186                 }
187                 i = ptmp;
188         }
189
190
191         MIDIControllable* mc = 0;
192
193         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
194                 if ((*i)->get_controllable()->id() == c->id()) {
195                         mc = *i;
196                         break;
197                 }
198         }
199
200         if (!mc) {
201                 mc = new MIDIControllable (*_port, *c);
202         }
203         
204         {
205                 Glib::Mutex::Lock lm (pending_lock);
206
207                 MIDIPendingControllable* element = new MIDIPendingControllable;
208                 element->first = mc;
209                 c->LearningFinished.connect_same_thread (element->second, boost::bind (&GenericMidiControlProtocol::learning_stopped, this, mc));
210
211                 pending_controllables.push_back (element);
212         }
213
214         mc->learn_about_external_control ();
215         return true;
216 }
217
218 void
219 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
220 {
221         Glib::Mutex::Lock lm (pending_lock);
222         Glib::Mutex::Lock lm2 (controllables_lock);
223         
224         MIDIPendingControllables::iterator tmp;
225
226         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
227                 tmp = i;
228                 ++tmp;
229
230                 if ( (*i)->first == mc) {
231                         (*i)->second.disconnect();
232                         delete *i;
233                         pending_controllables.erase(i);
234                 }
235
236                 i = tmp;
237         }
238
239         controllables.insert (mc);
240 }
241
242 void
243 GenericMidiControlProtocol::stop_learning (Controllable* c)
244 {
245         Glib::Mutex::Lock lm (pending_lock);
246         Glib::Mutex::Lock lm2 (controllables_lock);
247         MIDIControllable* dptr = 0;
248
249         /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
250            relevant MIDIControllable and remove it from the pending list.
251         */
252
253         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
254                 if (((*i)->first)->get_controllable() == c) {
255                         (*i)->first->stop_learning ();
256                         dptr = (*i)->first;
257                         (*i)->second.disconnect();
258
259                         delete *i;
260                         pending_controllables.erase (i);
261                         break;
262                 }
263         }
264         
265         delete dptr;
266 }
267
268 void
269 GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
270 {
271         if (control != 0) {
272                 Glib::Mutex::Lock lm2 (controllables_lock);
273                 
274                 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
275                         MIDIControllable* existingBinding = (*iter);
276                         
277                         if (control == (existingBinding->get_controllable())) {
278                                 delete existingBinding;
279                                 controllables.erase (iter);
280                         }
281                         
282                 }
283         }
284 }
285
286 void
287 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
288 {
289         if (control != NULL) {
290                 Glib::Mutex::Lock lm2 (controllables_lock);
291                 
292                 MIDI::channel_t channel = (pos & 0xf);
293                 MIDI::byte value = control_number;
294                 
295                 // Create a MIDIControllable
296                 MIDIControllable* mc = new MIDIControllable (*_port, *control);
297                 
298                 // Remove any old binding for this midi channel/type/value pair
299                 // Note:  can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
300                 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
301                         MIDIControllable* existingBinding = (*iter);
302                         
303                         if ((existingBinding->get_control_channel() & 0xf ) == channel &&
304                             existingBinding->get_control_additional() == value &&
305                             (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
306                                 
307                                 delete existingBinding;
308                                 controllables.erase (iter);
309                         }
310                         
311                 }
312                 
313                 // Update the MIDI Controllable based on the the pos param
314                 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
315                 mc->bind_midi(channel, MIDI::controller, value);
316                 
317                 controllables.insert (mc);
318         }
319 }
320
321 XMLNode&
322 GenericMidiControlProtocol::get_state () 
323 {
324         XMLNode* node = new XMLNode ("Protocol"); 
325         char buf[32];
326
327         node->add_property (X_("name"), _name);
328         node->add_property (X_("feedback"), do_feedback ? "1" : "0");
329         snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
330         node->add_property (X_("feedback_interval"), buf);
331
332         XMLNode* children = new XMLNode (X_("controls"));
333
334         node->add_child_nocopy (*children);
335
336         Glib::Mutex::Lock lm2 (controllables_lock);
337         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
338                 children->add_child_nocopy ((*i)->get_state());
339         }
340
341         return *node;
342 }
343
344 int
345 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
346 {
347         XMLNodeList nlist;
348         XMLNodeConstIterator niter;
349         const XMLProperty* prop;
350
351         if ((prop = node.property ("feedback")) != 0) {
352                 do_feedback = (bool) atoi (prop->value().c_str());
353         } else {
354                 do_feedback = false;
355         }
356
357         if ((prop = node.property ("feedback_interval")) != 0) {
358                 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
359                         _feedback_interval = 10000;
360                 }
361         } else {
362                 _feedback_interval = 10000;
363         }
364
365         boost::shared_ptr<Controllable> c;
366         
367         {
368                 Glib::Mutex::Lock lm (pending_lock);
369                 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
370                         delete *i;
371                 }
372                 pending_controllables.clear ();
373         }
374         
375         Glib::Mutex::Lock lm2 (controllables_lock);
376         controllables.clear ();
377         nlist = node.children(); // "controls"
378         
379         if (nlist.empty()) {
380                 return 0;
381         }
382         
383         nlist = nlist.front()->children ();
384                 
385         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
386                 
387                 if ((prop = (*niter)->property ("id")) != 0) {
388                         
389                         ID id = prop->value ();
390                         c = session->controllable_by_id (id);
391                         
392                         if (c) {
393                                 MIDIControllable* mc = new MIDIControllable (*_port, *c);
394                                 if (mc->set_state (**niter, version) == 0) {
395                                         controllables.insert (mc);
396                                 }
397                                 
398                         } else {
399                                 warning << string_compose (
400                                         _("Generic MIDI control: controllable %1 not found in session (ignored)"),
401                                         id) << endmsg;
402                         }
403                 }
404         }
405         return 0;
406 }
407
408 int
409 GenericMidiControlProtocol::set_feedback (bool yn)
410 {
411         do_feedback = yn;
412         last_feedback_time = 0;
413         return 0;
414 }
415
416 bool
417 GenericMidiControlProtocol::get_feedback () const
418 {
419         return do_feedback;
420 }
421
422 int
423 GenericMidiControlProtocol::load_bindings (const string& xmlpath)
424 {
425         XMLTree state_tree;
426
427         if (!state_tree.read (xmlpath.c_str())) {
428                 error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
429                 return -1;
430         }
431
432         XMLNode* root = state_tree.root();
433
434         if (root->name() != X_("ArdourMIDIBindings")) {
435                 error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
436                 return -1;
437         }
438
439         const XMLProperty* prop;
440
441         if ((prop = root->property ("version")) == 0) {
442                 return -1;
443         } else {
444                 int major;
445                 int minor;
446                 int micro;
447
448                 sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, &micro);
449                 Stateful::loading_state_version = (major * 1000) + minor;
450         }
451         
452         const XMLNodeList& children (root->children());
453         XMLNodeConstIterator citer;
454         XMLNodeConstIterator gciter;
455
456         MIDIControllable* mc;
457
458         for (citer = children.begin(); citer != children.end(); ++citer) {
459                 if ((*citer)->name() == "Binding") {
460                         const XMLNode* child = *citer;
461
462                         if (child->property ("uri")) {
463                                 /* controllable */
464                                 
465                                 if ((mc = create_binding (*child)) != 0) {
466                                         Glib::Mutex::Lock lm2 (controllables_lock);
467                                         controllables.insert (mc);
468                                 }
469
470                         } else if (child->property ("function")) {
471
472                                 /* function */
473                                 MIDIFunction* mf;
474
475                                 if ((mf = create_function (*child)) != 0) {
476                                         functions.push_back (mf);
477                                 }
478                         }
479                 }
480         }
481
482         return 0;
483 }
484
485 MIDIControllable*
486 GenericMidiControlProtocol::create_binding (const XMLNode& node)
487 {
488         const XMLProperty* prop;
489         int detail;
490         int channel;
491         string uri;
492         MIDI::eventType ev;
493
494         if ((prop = node.property (X_("channel"))) == 0) {
495                 return 0;
496         }
497         
498         if (sscanf (prop->value().c_str(), "%d", &channel) != 1) {
499                 return 0;
500         }
501
502         if ((prop = node.property (X_("ctl"))) != 0) {
503                 ev = MIDI::controller;
504         } else if ((prop = node.property (X_("note"))) != 0) {
505                 ev = MIDI::on;
506         } else if ((prop = node.property (X_("pgm"))) != 0) {
507                 ev = MIDI::program;
508         } else {
509                 return 0;
510         }
511
512         if (sscanf (prop->value().c_str(), "%d", &detail) != 1) {
513                 return 0;
514         }
515
516         prop = node.property (X_("uri"));
517         uri = prop->value();
518
519         MIDIControllable* mc = new MIDIControllable (*_port, uri, false);
520         mc->bind_midi (channel, ev, detail);
521
522         cerr << "New MC with URI = " << uri << endl;
523
524         return mc;
525 }
526
527 void
528 GenericMidiControlProtocol::reset_controllables ()
529 {
530         Glib::Mutex::Lock lm2 (controllables_lock);
531         
532         for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
533                 MIDIControllable* existingBinding = (*iter);
534                 
535                 boost::shared_ptr<Controllable> c = session->controllable_by_uri (existingBinding->current_uri());
536                 existingBinding->set_controllable (c.get());
537         }
538 }
539
540 MIDIFunction*
541 GenericMidiControlProtocol::create_function (const XMLNode& node)
542 {
543         const XMLProperty* prop;
544         int detail;
545         int channel;
546         string uri;
547         MIDI::eventType ev;
548
549         if ((prop = node.property (X_("channel"))) == 0) {
550                 return 0;
551         }
552         
553         if (sscanf (prop->value().c_str(), "%d", &channel) != 1) {
554                 return 0;
555         }
556
557         if ((prop = node.property (X_("ctl"))) != 0) {
558                 ev = MIDI::controller;
559         } else if ((prop = node.property (X_("note"))) != 0) {
560                 ev = MIDI::on;
561         } else if ((prop = node.property (X_("pgm"))) != 0) {
562                 ev = MIDI::program;
563         } else {
564                 return 0;
565         }
566
567         if (sscanf (prop->value().c_str(), "%d", &detail) != 1) {
568                 return 0;
569         }
570
571         prop = node.property (X_("function"));
572         
573         MIDIFunction* mf = new MIDIFunction (*_port);
574         
575         if (mf->init (*this, prop->value())) {
576                 delete mf;
577                 return 0;
578         }
579
580         mf->bind_midi (channel, ev, detail);
581         
582         cerr << "New MF with function = " << prop->value() << endl;
583
584         return mf;
585 }