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.
23 #include <pbd/error.h>
24 #include <pbd/failed_constructor.h>
26 #include <midi++/port.h>
27 #include <midi++/manager.h>
28 #include <midi++/port_request.h>
30 #include <ardour/route.h>
31 #include <ardour/session.h>
33 #include "generic_midi_control_protocol.h"
34 #include "midicontrollable.h"
36 using namespace ARDOUR;
41 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
42 : ControlProtocol (s, _("Generic MIDI"))
44 MIDI::Manager* mm = MIDI::Manager::instance();
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.
50 _port = mm->port (X_("control"));
53 error << _("no MIDI port named \"control\" exists - generic MIDI control disabled") << endmsg;
54 throw failed_constructor();
58 _feedback_interval = 10000; // microseconds
59 last_feedback_time = 0;
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));
66 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
71 GenericMidiControlProtocol::set_active (bool yn)
73 /* start/stop delivery/outbound thread */
78 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
80 _feedback_interval = ms;
84 GenericMidiControlProtocol::send_feedback ()
90 microseconds_t now = get_microseconds ();
92 if (last_feedback_time != 0) {
93 if ((now - last_feedback_time) < _feedback_interval) {
100 last_feedback_time = now;
104 GenericMidiControlProtocol::_send_feedback ()
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;
111 for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
112 end = (*r)->write_feedback (end, bsize);
119 _port->write (buf, (int32_t) (end - buf));
123 GenericMidiControlProtocol::start_learning (Controllable* c)
129 MIDIControllable* mc = new MIDIControllable (*_port, *c);
132 Glib::Mutex::Lock lm (pending_lock);
133 std::pair<MIDIControllables::iterator,bool> result;
134 result = pending_controllables.insert (mc);
136 c->LearningFinished.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc));
140 mc->learn_about_external_control ();
145 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
147 Glib::Mutex::Lock lm (pending_lock);
148 Glib::Mutex::Lock lm2 (controllables_lock);
150 MIDIControllables::iterator i = find (pending_controllables.begin(), pending_controllables.end(), mc);
152 if (i != pending_controllables.end()) {
153 pending_controllables.erase (i);
156 controllables.insert (mc);
160 GenericMidiControlProtocol::stop_learning (Controllable* c)
162 Glib::Mutex::Lock lm (pending_lock);
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.
168 for (MIDIControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
169 if (&(*i)->get_controllable() == c) {
170 (*i)->stop_learning ();
172 pending_controllables.erase (i);
179 GenericMidiControlProtocol::get_state ()
181 XMLNode* node = new XMLNode ("Protocol");
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);
189 XMLNode* children = new XMLNode (X_("controls"));
191 node->add_child_nocopy (*children);
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());
202 GenericMidiControlProtocol::set_state (const XMLNode& node)
205 XMLNodeConstIterator niter;
206 const XMLProperty* prop;
208 if ((prop = node.property ("feedback")) != 0) {
209 do_feedback = (bool) atoi (prop->value().c_str());
214 if ((prop = node.property ("feedback_interval")) != 0) {
215 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
216 _feedback_interval = 10000;
219 _feedback_interval = 10000;
225 Glib::Mutex::Lock lm (pending_lock);
226 pending_controllables.clear ();
229 Glib::Mutex::Lock lm2 (controllables_lock);
231 controllables.clear ();
233 nlist = node.children();
239 nlist = nlist.front()->children ();
241 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
243 if ((prop = (*niter)->property ("id")) != 0) {
245 ID id = prop->value ();
247 c = session->controllable_by_id (id);
250 MIDIControllable* mc = new MIDIControllable (*_port, *c);
251 if (mc->set_state (**niter) == 0) {
252 controllables.insert (mc);
256 warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),
267 GenericMidiControlProtocol::set_feedback (bool yn)
270 last_feedback_time = 0;
275 GenericMidiControlProtocol::get_feedback () const