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.
21 #define __STDC_FORMAT_MACROS 1
26 #include <pbd/error.h>
27 #include <pbd/failed_constructor.h>
29 #include <midi++/port.h>
30 #include <midi++/manager.h>
31 #include <midi++/port_request.h>
33 #include <ardour/route.h>
34 #include <ardour/session.h>
36 #include "generic_midi_control_protocol.h"
37 #include "midicontrollable.h"
39 using namespace ARDOUR;
44 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
45 : ControlProtocol (s, _("Generic MIDI"))
47 MIDI::Manager* mm = MIDI::Manager::instance();
49 /* XXX it might be nice to run "control" through i18n, but thats a bit tricky because
50 the name is defined in ardour.rc which is likely not internationalized.
53 _port = mm->port (X_("control"));
56 error << _("no MIDI port named \"control\" exists - generic MIDI control disabled") << endmsg;
57 throw failed_constructor();
61 _feedback_interval = 10000; // microseconds
62 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));
69 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
74 GenericMidiControlProtocol::set_active (bool yn)
76 /* start/stop delivery/outbound thread */
81 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
83 _feedback_interval = ms;
87 GenericMidiControlProtocol::send_feedback ()
93 microseconds_t now = get_microseconds ();
95 if (last_feedback_time != 0) {
96 if ((now - last_feedback_time) < _feedback_interval) {
103 last_feedback_time = now;
107 GenericMidiControlProtocol::_send_feedback ()
109 const int32_t bufsize = 16 * 1024; /* XXX too big */
110 MIDI::byte buf[bufsize];
111 int32_t bsize = bufsize;
112 MIDI::byte* end = buf;
114 for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
115 end = (*r)->write_feedback (end, bsize);
123 //_port->write (buf, (int32_t) (end - buf));
127 GenericMidiControlProtocol::start_learning (Controllable* c)
133 MIDIControllable* mc = new MIDIControllable (*_port, *c);
136 Glib::Mutex::Lock lm (pending_lock);
137 std::pair<MIDIControllables::iterator,bool> result;
138 result = pending_controllables.insert (mc);
140 c->LearningFinished.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc));
144 mc->learn_about_external_control ();
149 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
151 Glib::Mutex::Lock lm (pending_lock);
152 Glib::Mutex::Lock lm2 (controllables_lock);
154 MIDIControllables::iterator i = find (pending_controllables.begin(), pending_controllables.end(), mc);
156 if (i != pending_controllables.end()) {
157 pending_controllables.erase (i);
160 controllables.insert (mc);
164 GenericMidiControlProtocol::stop_learning (Controllable* c)
166 Glib::Mutex::Lock lm (pending_lock);
168 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
169 relevant MIDIControllable and remove it from the pending list.
172 for (MIDIControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
173 if (&(*i)->get_controllable() == c) {
174 (*i)->stop_learning ();
176 pending_controllables.erase (i);
183 GenericMidiControlProtocol::get_state ()
185 XMLNode* node = new XMLNode ("Protocol");
188 node->add_property (X_("name"), _name);
189 node->add_property (X_("feedback"), do_feedback ? "1" : "0");
190 snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
191 node->add_property (X_("feedback_interval"), buf);
193 XMLNode* children = new XMLNode (X_("controls"));
195 node->add_child_nocopy (*children);
197 Glib::Mutex::Lock lm2 (controllables_lock);
198 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
199 children->add_child_nocopy ((*i)->get_state());
206 GenericMidiControlProtocol::set_state (const XMLNode& node)
209 XMLNodeConstIterator niter;
210 const XMLProperty* prop;
212 if ((prop = node.property ("feedback")) != 0) {
213 do_feedback = (bool) atoi (prop->value().c_str());
218 if ((prop = node.property ("feedback_interval")) != 0) {
219 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
220 _feedback_interval = 10000;
223 _feedback_interval = 10000;
229 Glib::Mutex::Lock lm (pending_lock);
230 pending_controllables.clear ();
233 Glib::Mutex::Lock lm2 (controllables_lock);
235 controllables.clear ();
237 nlist = node.children(); // "controls"
243 nlist = nlist.front()->children ();
245 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
247 if ((prop = (*niter)->property ("id")) != 0) {
249 ID id = prop->value ();
251 c = session->controllable_by_id (id);
254 MIDIControllable* mc = new MIDIControllable (*_port, *c);
255 if (mc->set_state (**niter) == 0) {
256 controllables.insert (mc);
260 warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),
271 GenericMidiControlProtocol::set_feedback (bool yn)
274 last_feedback_time = 0;
279 GenericMidiControlProtocol::get_feedback () const