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));
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);
251 } // end for midi controllables
255 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
257 if( control != NULL ) {
258 Glib::Mutex::Lock lm2 (controllables_lock);
260 MIDI::channel_t channel = (pos & 0xf);
261 MIDI::byte value = control_number;
263 // Create a MIDIControllable::
264 MIDIControllable* mc = new MIDIControllable (*_port, *control);
266 // Remove any old binding for this midi channel/type/value pair
267 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
268 for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
269 MIDIControllable* existingBinding = (*iter);
271 if( (existingBinding->get_control_channel() & 0xf ) == channel &&
272 existingBinding->get_control_additional() == value &&
273 (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller ) {
275 delete existingBinding;
276 controllables.erase (iter);
279 } // end for midi controllables
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)
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 // Are we using the autobinding feature? If so skip this part
347 if ( !auto_binding ) {
352 Glib::Mutex::Lock lm (pending_lock);
353 pending_controllables.clear ();
356 Glib::Mutex::Lock lm2 (controllables_lock);
357 controllables.clear ();
358 nlist = node.children(); // "controls"
364 nlist = nlist.front()->children ();
366 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
368 if ((prop = (*niter)->property ("id")) != 0) {
370 ID id = prop->value ();
371 c = session->controllable_by_id (id);
374 MIDIControllable* mc = new MIDIControllable (*_port, *c);
375 if (mc->set_state (**niter) == 0) {
376 controllables.insert (mc);
380 warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),
386 } // end autobinding check
391 GenericMidiControlProtocol::set_feedback (bool yn)
394 last_feedback_time = 0;
399 GenericMidiControlProtocol::get_feedback () const