(3.0) fix compile error related to Controllable/shared_ptr confusion post-merge
[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/route.h>
32 #include <ardour/session.h>
33
34 #include "generic_midi_control_protocol.h"
35 #include "midicontrollable.h"
36
37 using namespace ARDOUR;
38 using namespace PBD;
39
40 #include "i18n.h"
41
42 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
43         : ControlProtocol  (s, _("Generic MIDI"))
44 {
45         MIDI::Manager* mm = MIDI::Manager::instance();
46
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.
49         */
50         
51         _port = mm->port (X_("control"));
52
53         if (_port == 0) {
54                 error << _("no MIDI port named \"control\" exists - generic MIDI control disabled") << endmsg;
55                 throw failed_constructor();
56         }
57
58         do_feedback = false;
59         _feedback_interval = 10000; // microseconds
60         last_feedback_time = 0;
61
62         auto_binding = FALSE;
63
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));
67         
68         Controllable::CreateBinding.connect (mem_fun (*this, &GenericMidiControlProtocol::create_binding));
69         Controllable::DeleteBinding.connect (mem_fun (*this, &GenericMidiControlProtocol::delete_binding));
70
71         Session::AutoBindingOn.connect (mem_fun (*this, &GenericMidiControlProtocol::auto_binding_on));
72         Session::AutoBindingOff.connect (mem_fun (*this, &GenericMidiControlProtocol::auto_binding_off));
73 }
74
75 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
76 {
77 }
78
79 int
80 GenericMidiControlProtocol::set_active (bool yn)
81 {
82         /* start/stop delivery/outbound thread */
83         return 0;
84 }
85
86 void
87 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
88 {
89         _feedback_interval = ms;
90 }
91
92 void 
93 GenericMidiControlProtocol::send_feedback ()
94 {
95         if (!do_feedback) {
96                 return;
97         }
98
99         microseconds_t now = get_microseconds ();
100
101         if (last_feedback_time != 0) {
102                 if ((now - last_feedback_time) < _feedback_interval) {
103                         return;
104                 }
105         }
106
107         _send_feedback ();
108         
109         last_feedback_time = now;
110 }
111
112 void 
113 GenericMidiControlProtocol::_send_feedback ()
114 {
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;
119         
120         for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
121                 end = (*r)->write_feedback (end, bsize);
122         }
123         
124         if (end == buf) {
125                 return;
126         } 
127
128         // FIXME
129         //_port->write (buf, (int32_t) (end - buf));
130 }
131
132 bool
133 GenericMidiControlProtocol::start_learning (Controllable* c)
134 {
135         if (c == 0) {
136                 return false;
137         }
138
139         MIDIControllables::iterator tmp;
140         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
141                 tmp = i;
142                 ++tmp;
143                 if (&(*i)->get_controllable() == c) {
144                         delete (*i);
145                         controllables.erase (i);
146                 }
147                 i = tmp;
148         }
149
150         MIDIPendingControllables::iterator ptmp;
151         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
152                 ptmp = i;
153                 ++ptmp;
154                 if (&((*i).first)->get_controllable() == c) {
155                         (*i).second.disconnect();
156                         delete (*i).first;
157                         pending_controllables.erase (i);
158                 }
159                 i = ptmp;
160         }
161
162
163         MIDIControllable* mc = 0;
164
165         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
166                 if ((*i)->get_controllable().id() == c->id()) {
167                         mc = *i;
168                         break;
169                 }
170         }
171
172         if (!mc) {
173                 mc = new MIDIControllable (*_port, *c);
174         }
175         
176         {
177                 Glib::Mutex::Lock lm (pending_lock);
178
179                 std::pair<MIDIControllable *, sigc::connection> element;
180                 element.first = mc;
181                 element.second = c->LearningFinished.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc));
182
183                 pending_controllables.push_back (element);
184         }
185
186         mc->learn_about_external_control ();
187         return true;
188 }
189
190 void
191 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
192 {
193         Glib::Mutex::Lock lm (pending_lock);
194         Glib::Mutex::Lock lm2 (controllables_lock);
195         
196         MIDIPendingControllables::iterator tmp;
197
198         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
199                 tmp = i;
200                 ++tmp;
201
202                 if ( (*i).first == mc) {
203                         (*i).second.disconnect();
204                         pending_controllables.erase(i);
205                 }
206
207                 i = tmp;
208         }
209
210         controllables.insert (mc);
211 }
212
213 void
214 GenericMidiControlProtocol::stop_learning (Controllable* c)
215 {
216         Glib::Mutex::Lock lm (pending_lock);
217         Glib::Mutex::Lock lm2 (controllables_lock);
218         MIDIControllable* dptr = 0;
219
220         /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
221            relevant MIDIControllable and remove it from the pending list.
222         */
223
224         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
225                 if (&((*i).first)->get_controllable() == c) {
226                         (*i).first->stop_learning ();
227                         dptr = (*i).first;
228                         (*i).second.disconnect();
229
230                         pending_controllables.erase (i);
231                         break;
232                 }
233         }
234         
235         if (dptr) {
236                 delete dptr;
237         }
238 }
239
240 void
241 GenericMidiControlProtocol::delete_binding ( PBD::Controllable* control )
242 {
243         if( control != 0 ) {
244                 Glib::Mutex::Lock lm2 (controllables_lock);
245                 
246                 for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
247                         MIDIControllable* existingBinding = (*iter);
248                         
249                         if( control == &(existingBinding->get_controllable()) ) {
250                                 delete existingBinding;
251                                 controllables.erase (iter);
252                         }
253                         
254                 } // end for midi controllables
255         } // end null check
256 }
257 void
258 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
259 {
260         if( control != NULL ) {
261                 Glib::Mutex::Lock lm2 (controllables_lock);
262                 
263                 MIDI::channel_t channel = (pos & 0xf);
264                 MIDI::byte value = control_number;
265                 
266                 // Create a MIDIControllable::
267                 MIDIControllable* mc = new MIDIControllable (*_port, *control);
268                 
269                 // Remove any old binding for this midi channel/type/value pair
270                 // Note:  can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
271                 for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
272                         MIDIControllable* existingBinding = (*iter);
273                         
274                         if( (existingBinding->get_control_channel() & 0xf ) == channel &&
275                             existingBinding->get_control_additional() == value &&
276                             (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller ) {
277                                 
278                                 delete existingBinding;
279                                 controllables.erase (iter);
280                         }
281                         
282                 } // end for midi controllables
283                 
284                 
285                 // Update the MIDI Controllable based on the the pos param
286                 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
287                 mc->bind_midi( channel, MIDI::controller, value );
288                 
289                 controllables.insert (mc);
290         } // end null test
291 }
292
293 void
294 GenericMidiControlProtocol::auto_binding_on()
295 {
296         auto_binding = TRUE;
297 }
298
299 void
300 GenericMidiControlProtocol::auto_binding_off()
301 {
302         auto_binding = FALSE;
303 }
304
305 XMLNode&
306 GenericMidiControlProtocol::get_state () 
307 {
308         XMLNode* node = new XMLNode ("Protocol"); 
309         char buf[32];
310
311         node->add_property (X_("name"), _name);
312         node->add_property (X_("feedback"), do_feedback ? "1" : "0");
313         snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
314         node->add_property (X_("feedback_interval"), buf);
315
316         XMLNode* children = new XMLNode (X_("controls"));
317
318         node->add_child_nocopy (*children);
319
320         Glib::Mutex::Lock lm2 (controllables_lock);
321         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
322                 children->add_child_nocopy ((*i)->get_state());
323         }
324
325         return *node;
326 }
327
328 int
329 GenericMidiControlProtocol::set_state (const XMLNode& node)
330 {
331         XMLNodeList nlist;
332         XMLNodeConstIterator niter;
333         const XMLProperty* prop;
334
335         if ((prop = node.property ("feedback")) != 0) {
336                 do_feedback = (bool) atoi (prop->value().c_str());
337         } else {
338                 do_feedback = false;
339         }
340
341         if ((prop = node.property ("feedback_interval")) != 0) {
342                 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
343                         _feedback_interval = 10000;
344                 }
345         } else {
346                 _feedback_interval = 10000;
347         }
348
349         // Are we using the autobinding feature?  If so skip this part
350         if ( !auto_binding ) {
351                 
352                 boost::shared_ptr<Controllable> c;
353                 
354                 {
355                         Glib::Mutex::Lock lm (pending_lock);
356                         pending_controllables.clear ();
357                 }
358                 
359                 Glib::Mutex::Lock lm2 (controllables_lock);
360                 controllables.clear ();
361                 nlist = node.children(); // "controls"
362                 
363                 if (nlist.empty()) {
364                         return 0;
365                 }
366                 
367                 nlist = nlist.front()->children ();
368                 
369                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
370                         
371                         if ((prop = (*niter)->property ("id")) != 0) {
372
373                                 ID id = prop->value ();
374                                 c = session->controllable_by_id (id);
375                                 
376                                 if (c) {
377                                         MIDIControllable* mc = new MIDIControllable (*_port, *c);
378                                         if (mc->set_state (**niter) == 0) {
379                                                 controllables.insert (mc);
380                                         }
381                                         
382                                 } else {
383                                         warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),
384                                                                    id)
385                                                 << endmsg;
386                                 }
387                         }
388                 }
389         } // end autobinding check
390         return 0;
391 }
392
393 int
394 GenericMidiControlProtocol::set_feedback (bool yn)
395 {
396         do_feedback = yn;
397         last_feedback_time = 0;
398         return 0;
399 }
400
401 bool
402 GenericMidiControlProtocol::get_feedback () const
403 {
404         return do_feedback;
405 }