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);
241 GenericMidiControlProtocol::delete_binding ( PBD::Controllable* control )
244 Glib::Mutex::Lock lm2 (controllables_lock);
246 for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
247 MIDIControllable* existingBinding = (*iter);
249 if( control == &(existingBinding->get_controllable()) ) {
250 delete existingBinding;
251 controllables.erase (iter);
254 } // end for midi controllables
258 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
260 if( control != NULL ) {
261 Glib::Mutex::Lock lm2 (controllables_lock);
263 MIDI::channel_t channel = (pos & 0xf);
264 MIDI::byte value = control_number;
266 // Create a MIDIControllable::
267 MIDIControllable* mc = new MIDIControllable (*_port, *control);
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);
274 if( (existingBinding->get_control_channel() & 0xf ) == channel &&
275 existingBinding->get_control_additional() == value &&
276 (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller ) {
278 delete existingBinding;
279 controllables.erase (iter);
282 } // end for midi controllables
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 );
289 controllables.insert (mc);
294 GenericMidiControlProtocol::auto_binding_on()
300 GenericMidiControlProtocol::auto_binding_off()
302 auto_binding = FALSE;
306 GenericMidiControlProtocol::get_state ()
308 XMLNode* node = new XMLNode ("Protocol");
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);
316 XMLNode* children = new XMLNode (X_("controls"));
318 node->add_child_nocopy (*children);
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());
329 GenericMidiControlProtocol::set_state (const XMLNode& node)
332 XMLNodeConstIterator niter;
333 const XMLProperty* prop;
335 if ((prop = node.property ("feedback")) != 0) {
336 do_feedback = (bool) atoi (prop->value().c_str());
341 if ((prop = node.property ("feedback_interval")) != 0) {
342 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
343 _feedback_interval = 10000;
346 _feedback_interval = 10000;
349 // Are we using the autobinding feature? If so skip this part
350 if ( !auto_binding ) {
352 boost::shared_ptr<Controllable> c;
355 Glib::Mutex::Lock lm (pending_lock);
356 pending_controllables.clear ();
359 Glib::Mutex::Lock lm2 (controllables_lock);
360 controllables.clear ();
361 nlist = node.children(); // "controls"
367 nlist = nlist.front()->children ();
369 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
371 if ((prop = (*niter)->property ("id")) != 0) {
373 ID id = prop->value ();
374 c = session->controllable_by_id (id);
377 MIDIControllable* mc = new MIDIControllable (*_port, *c);
378 if (mc->set_state (**niter) == 0) {
379 controllables.insert (mc);
383 warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),
389 } // end autobinding check
394 GenericMidiControlProtocol::set_feedback (bool yn)
397 last_feedback_time = 0;
402 GenericMidiControlProtocol::get_feedback () const