fix abort-capture path, including many subtle issues with shared_ptr<>; remove old...
[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     $Id$
19 */
20
21 #include <algorithm>
22
23 #include <pbd/error.h>
24 #include <pbd/failed_constructor.h>
25
26 #include <midi++/port.h>
27 #include <midi++/manager.h>
28 #include <midi++/port_request.h>
29
30 #include <ardour/route.h>
31 #include <ardour/session.h>
32
33 #include "generic_midi_control_protocol.h"
34 #include "midicontrollable.h"
35
36 using namespace ARDOUR;
37 using namespace PBD;
38
39 #include "i18n.h"
40
41 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
42         : ControlProtocol  (s, _("Generic MIDI"))
43 {
44         MIDI::Manager* mm = MIDI::Manager::instance();
45
46         /* XXX it might be nice to run "control" through i18n, but thats a bit tricky because
47            the name is defined in ardour.rc which is likely not internationalized.
48         */
49         
50         _port = mm->port (X_("control"));
51
52         if (_port == 0) {
53                 error << _("no MIDI port named \"control\" exists - generic MIDI control disabled") << endmsg;
54                 throw failed_constructor();
55         }
56
57         do_feedback = false;
58         _feedback_interval = 10000; // microseconds
59         last_feedback_time = 0;
60
61         Controllable::StartLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::start_learning));
62         Controllable::StopLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::stop_learning));
63         Session::SendFeedback.connect (mem_fun (*this, &GenericMidiControlProtocol::send_feedback));
64 }
65
66 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
67 {
68 }
69
70 int
71 GenericMidiControlProtocol::set_active (bool yn)
72 {
73         /* start/stop delivery/outbound thread */
74         return 0;
75 }
76
77 void
78 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
79 {
80         _feedback_interval = ms;
81 }
82
83 void 
84 GenericMidiControlProtocol::send_feedback ()
85 {
86         if (!do_feedback) {
87                 return;
88         }
89
90         microseconds_t now = get_microseconds ();
91
92         if (last_feedback_time != 0) {
93                 if ((now - last_feedback_time) < _feedback_interval) {
94                         return;
95                 }
96         }
97
98         _send_feedback ();
99         
100         last_feedback_time = now;
101 }
102
103 void 
104 GenericMidiControlProtocol::_send_feedback ()
105 {
106         const int32_t bufsize = 16 * 1024; /* XXX too big */
107         MIDI::byte buf[bufsize];
108         int32_t bsize = bufsize;
109         MIDI::byte* end = buf;
110         
111         for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
112                 end = (*r)->write_feedback (end, bsize);
113         }
114         
115         if (end == buf) {
116                 return;
117         } 
118         
119         _port->write (buf, (int32_t) (end - buf));
120 }
121
122 bool
123 GenericMidiControlProtocol::start_learning (Controllable* c)
124 {
125         if (c == 0) {
126                 return false;
127         }
128
129         MIDIControllable* mc = new MIDIControllable (*_port, *c);
130         
131         {
132                 Glib::Mutex::Lock lm (pending_lock);
133                 std::pair<MIDIControllables::iterator,bool> result;
134                 result = pending_controllables.insert (mc);
135                 if (result.second) {
136                         c->LearningFinished.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc));
137                 }
138         }
139
140         mc->learn_about_external_control ();
141         return true;
142 }
143
144 void
145 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
146 {
147         Glib::Mutex::Lock lm (pending_lock);
148         Glib::Mutex::Lock lm2 (controllables_lock);
149         
150         MIDIControllables::iterator i = find (pending_controllables.begin(), pending_controllables.end(), mc);
151
152         if (i != pending_controllables.end()) {
153                 pending_controllables.erase (i);
154         }
155
156         controllables.insert (mc);
157 }
158
159 void
160 GenericMidiControlProtocol::stop_learning (Controllable* c)
161 {
162         Glib::Mutex::Lock lm (pending_lock);
163
164         /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
165            relevant MIDIControllable and remove it from the pending list.
166         */
167
168         for (MIDIControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
169                 if (&(*i)->get_controllable() == c) {
170                         (*i)->stop_learning ();
171                         delete (*i);
172                         pending_controllables.erase (i);
173                         break;
174                 }
175         }
176 }
177
178 XMLNode&
179 GenericMidiControlProtocol::get_state () 
180 {
181         XMLNode* node = new XMLNode ("Protocol"); 
182         char buf[32];
183
184         node->add_property (X_("name"), _name);
185         node->add_property (X_("feedback"), do_feedback ? "1" : "0");
186         snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
187         node->add_property (X_("feedback_interval"), buf);
188
189         XMLNode* children = new XMLNode (X_("controls"));
190
191         node->add_child_nocopy (*children);
192
193         Glib::Mutex::Lock lm2 (controllables_lock);
194         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
195                 children->add_child_nocopy ((*i)->get_state());
196         }
197
198         return *node;
199 }
200
201 int
202 GenericMidiControlProtocol::set_state (const XMLNode& node)
203 {
204         XMLNodeList nlist;
205         XMLNodeConstIterator niter;
206         const XMLProperty* prop;
207
208         if ((prop = node.property ("feedback")) != 0) {
209                 do_feedback = (bool) atoi (prop->value().c_str());
210         } else {
211                 do_feedback = false;
212         }
213
214         if ((prop = node.property ("feedback_interval")) != 0) {
215                 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
216                         _feedback_interval = 10000;
217                 }
218         } else {
219                 _feedback_interval = 10000;
220         }
221
222         Controllable* c;
223
224         {
225                 Glib::Mutex::Lock lm (pending_lock);
226                 pending_controllables.clear ();
227         }
228
229         Glib::Mutex::Lock lm2 (controllables_lock);
230
231         controllables.clear ();
232
233         nlist = node.children();
234
235         if (nlist.empty()) {
236                 return 0;
237         }
238
239         nlist = nlist.front()->children ();
240
241         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
242
243                 if ((prop = (*niter)->property ("id")) != 0) {
244                         
245                         ID id = prop->value ();
246                         
247                         c = session->controllable_by_id (id);
248                         
249                         if (c) {
250                                 MIDIControllable* mc = new MIDIControllable (*_port, *c);
251                                 if (mc->set_state (**niter) == 0) {
252                                         controllables.insert (mc);
253                                 }
254                                 
255                         } else {
256                                 warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),
257                                                            id)
258                                         << endmsg;
259                         }
260                 }
261         }
262
263         return 0;
264 }
265
266 int
267 GenericMidiControlProtocol::set_feedback (bool yn)
268 {
269         do_feedback = yn;
270         last_feedback_time = 0;
271         return 0;
272 }
273
274 bool
275 GenericMidiControlProtocol::get_feedback () const
276 {
277         return do_feedback;
278 }