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 = 0;
131 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
132 if ((*i)->get_controllable().id() == c->id()) {
139 mc = new MIDIControllable (*_port, *c);
143 Glib::Mutex::Lock lm (pending_lock);
144 std::pair<MIDIControllables::iterator,bool> result;
145 result = pending_controllables.insert (mc);
147 c->LearningFinished.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc));
151 mc->learn_about_external_control ();
156 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
158 Glib::Mutex::Lock lm (pending_lock);
159 Glib::Mutex::Lock lm2 (controllables_lock);
161 MIDIControllables::iterator i = find (pending_controllables.begin(), pending_controllables.end(), mc);
163 if (i != pending_controllables.end()) {
164 pending_controllables.erase (i);
167 controllables.insert (mc);
171 GenericMidiControlProtocol::stop_learning (Controllable* c)
173 Glib::Mutex::Lock lm (pending_lock);
175 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
176 relevant MIDIControllable and remove it from the pending list.
179 for (MIDIControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
180 if (&(*i)->get_controllable() == c) {
181 (*i)->stop_learning ();
183 pending_controllables.erase (i);
190 GenericMidiControlProtocol::get_state ()
192 XMLNode* node = new XMLNode ("Protocol");
195 node->add_property (X_("name"), _name);
196 node->add_property (X_("feedback"), do_feedback ? "1" : "0");
197 snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
198 node->add_property (X_("feedback_interval"), buf);
200 XMLNode* children = new XMLNode (X_("controls"));
202 node->add_child_nocopy (*children);
204 Glib::Mutex::Lock lm2 (controllables_lock);
205 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
206 children->add_child_nocopy ((*i)->get_state());
213 GenericMidiControlProtocol::set_state (const XMLNode& node)
216 XMLNodeConstIterator niter;
217 const XMLProperty* prop;
219 if ((prop = node.property ("feedback")) != 0) {
220 do_feedback = (bool) atoi (prop->value().c_str());
225 if ((prop = node.property ("feedback_interval")) != 0) {
226 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
227 _feedback_interval = 10000;
230 _feedback_interval = 10000;
236 Glib::Mutex::Lock lm (pending_lock);
237 pending_controllables.clear ();
240 Glib::Mutex::Lock lm2 (controllables_lock);
242 controllables.clear ();
244 nlist = node.children(); // "controls"
250 nlist = nlist.front()->children ();
252 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
254 if ((prop = (*niter)->property ("id")) != 0) {
256 ID id = prop->value ();
258 c = session->controllable_by_id (id);
261 MIDIControllable* mc = new MIDIControllable (*_port, *c);
262 if (mc->set_state (**niter) == 0) {
263 controllables.insert (mc);
267 warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),
278 GenericMidiControlProtocol::set_feedback (bool yn)
281 last_feedback_time = 0;
286 GenericMidiControlProtocol::get_feedback () const