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();
57 _feedback_interval = 10000; // microseconds
58 last_feedback_time = 0;
60 Controllable::StartLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::start_learning));
61 Controllable::StopLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::stop_learning));
62 Session::SendFeedback.connect (mem_fun (*this, &GenericMidiControlProtocol::send_feedback));
65 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
70 GenericMidiControlProtocol::set_active (bool yn)
72 /* start/stop delivery/outbound thread */
77 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
79 _feedback_interval = ms;
83 GenericMidiControlProtocol::send_feedback ()
85 microseconds_t now = get_microseconds ();
87 if (last_feedback_time != 0) {
88 if ((now - last_feedback_time) < _feedback_interval) {
95 last_feedback_time = now;
99 GenericMidiControlProtocol::_send_feedback ()
101 const int32_t bufsize = 16 * 1024;
102 MIDI::byte buf[bufsize];
103 int32_t bsize = bufsize;
104 MIDI::byte* end = buf;
106 for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
107 end = (*r)->write_feedback (end, bsize);
114 _port->write (buf, (int32_t) (end - buf));
118 GenericMidiControlProtocol::start_learning (Controllable* c)
124 MIDIControllable* mc = new MIDIControllable (*_port, *c);
127 Glib::Mutex::Lock lm (pending_lock);
128 std::pair<MIDIControllables::iterator,bool> result;
129 result = pending_controllables.insert (mc);
131 c->LearningFinished.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc));
135 mc->learn_about_external_control ();
140 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
142 Glib::Mutex::Lock lm (pending_lock);
143 Glib::Mutex::Lock lm2 (controllables_lock);
145 MIDIControllables::iterator i = find (pending_controllables.begin(), pending_controllables.end(), mc);
147 if (i != pending_controllables.end()) {
148 pending_controllables.erase (i);
151 controllables.insert (mc);
155 GenericMidiControlProtocol::stop_learning (Controllable* c)
157 Glib::Mutex::Lock lm (pending_lock);
159 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
160 relevant MIDIControllable and remove it from the pending list.
163 for (MIDIControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
164 if (&(*i)->get_controllable() == c) {
165 (*i)->stop_learning ();
167 pending_controllables.erase (i);
174 GenericMidiControlProtocol::get_state ()
176 XMLNode* node = new XMLNode ("Protocol");
178 node->add_property (X_("name"), _name);
180 XMLNode* children = new XMLNode (X_("controls"));
182 node->add_child_nocopy (*children);
184 Glib::Mutex::Lock lm2 (controllables_lock);
185 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
186 children->add_child_nocopy ((*i)->get_state());
193 GenericMidiControlProtocol::set_state (const XMLNode& node)
196 XMLNodeConstIterator niter;
200 Glib::Mutex::Lock lm (pending_lock);
201 pending_controllables.clear ();
204 Glib::Mutex::Lock lm2 (controllables_lock);
206 controllables.clear ();
208 nlist = node.children();
214 nlist = nlist.front()->children ();
216 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
220 if ((prop = (*niter)->property ("id")) != 0) {
222 ID id = prop->value ();
224 c = session->controllable_by_id (id);
227 MIDIControllable* mc = new MIDIControllable (*_port, *c);
228 if (mc->set_state (**niter) == 0) {
229 controllables.insert (mc);
233 warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),