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);
129 //_port->write (buf, (int32_t) (end - buf));
133 GenericMidiControlProtocol::start_learning (Controllable* c)
139 MIDIControllables::iterator tmp;
140 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
143 if (&(*i)->get_controllable() == c) {
145 controllables.erase (i);
150 MIDIPendingControllables::iterator ptmp;
151 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
154 if (&((*i).first)->get_controllable() == c) {
155 (*i).second.disconnect();
157 pending_controllables.erase (i);
163 MIDIControllable* mc = 0;
165 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
166 if ((*i)->get_controllable().id() == c->id()) {
173 mc = new MIDIControllable (*_port, *c);
177 Glib::Mutex::Lock lm (pending_lock);
179 std::pair<MIDIControllable *, sigc::connection> element;
181 element.second = c->LearningFinished.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc));
183 pending_controllables.push_back (element);
186 mc->learn_about_external_control ();
191 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
193 Glib::Mutex::Lock lm (pending_lock);
194 Glib::Mutex::Lock lm2 (controllables_lock);
196 MIDIPendingControllables::iterator tmp;
198 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
202 if ( (*i).first == mc) {
203 (*i).second.disconnect();
204 pending_controllables.erase(i);
210 controllables.insert (mc);
214 GenericMidiControlProtocol::stop_learning (Controllable* c)
216 Glib::Mutex::Lock lm (pending_lock);
217 Glib::Mutex::Lock lm2 (controllables_lock);
218 MIDIControllable* dptr = 0;
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.
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 ();
228 (*i).second.disconnect();
230 pending_controllables.erase (i);
239 GenericMidiControlProtocol::delete_binding ( PBD::Controllable* control )
242 Glib::Mutex::Lock lm2 (controllables_lock);
244 for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
245 MIDIControllable* existingBinding = (*iter);
247 if( control == &(existingBinding->get_controllable()) ) {
248 delete existingBinding;
249 controllables.erase (iter);
252 } // end for midi controllables
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);
280 } // end for midi controllables
283 // Update the MIDI Controllable based on the the pos param
284 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
285 mc->bind_midi( channel, MIDI::controller, value );
287 controllables.insert (mc);
292 GenericMidiControlProtocol::auto_binding_on()
298 GenericMidiControlProtocol::auto_binding_off()
300 auto_binding = FALSE;
304 GenericMidiControlProtocol::get_state ()
306 XMLNode* node = new XMLNode ("Protocol");
309 node->add_property (X_("name"), _name);
310 node->add_property (X_("feedback"), do_feedback ? "1" : "0");
311 snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
312 node->add_property (X_("feedback_interval"), buf);
314 XMLNode* children = new XMLNode (X_("controls"));
316 node->add_child_nocopy (*children);
318 Glib::Mutex::Lock lm2 (controllables_lock);
319 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
320 children->add_child_nocopy ((*i)->get_state());
327 GenericMidiControlProtocol::set_state (const XMLNode& node)
330 XMLNodeConstIterator niter;
331 const XMLProperty* prop;
333 if ((prop = node.property ("feedback")) != 0) {
334 do_feedback = (bool) atoi (prop->value().c_str());
339 if ((prop = node.property ("feedback_interval")) != 0) {
340 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
341 _feedback_interval = 10000;
344 _feedback_interval = 10000;
347 // Are we using the autobinding feature? If so skip this part
348 if ( !auto_binding ) {
350 boost::shared_ptr<Controllable> c;
353 Glib::Mutex::Lock lm (pending_lock);
354 pending_controllables.clear ();
357 Glib::Mutex::Lock lm2 (controllables_lock);
358 controllables.clear ();
359 nlist = node.children(); // "controls"
365 nlist = nlist.front()->children ();
367 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
369 if ((prop = (*niter)->property ("id")) != 0) {
371 ID id = prop->value ();
372 c = session->controllable_by_id (id);
375 MIDIControllable* mc = new MIDIControllable (*_port, *c);
376 if (mc->set_state (**niter) == 0) {
377 controllables.insert (mc);
381 warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),
387 } // end autobinding check
392 GenericMidiControlProtocol::set_feedback (bool yn)
395 last_feedback_time = 0;
400 GenericMidiControlProtocol::get_feedback () const