add new control protocol related files
authorPaul Davis <paul@linuxaudiosystems.com>
Wed, 5 Apr 2006 00:24:57 +0000 (00:24 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Wed, 5 Apr 2006 00:24:57 +0000 (00:24 +0000)
git-svn-id: svn://localhost/trunk/ardour2@443 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/control_protocol_manager.h [new file with mode: 0644]
libs/ardour/control_protocol.cc [new file with mode: 0644]
libs/ardour/control_protocol_manager.cc [new file with mode: 0644]

diff --git a/libs/ardour/ardour/control_protocol_manager.h b/libs/ardour/ardour/control_protocol_manager.h
new file mode 100644 (file)
index 0000000..f0b7846
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef ardour_control_protocol_manager_h
+#define ardour_control_protocol_manager_h
+
+#include <string>
+#include <list>
+
+#include <pbd/lockmonitor.h>
+
+namespace ARDOUR {
+
+class ControlProtocol;
+class ControlProtocolDescriptor;
+
+struct ControlProtocolInfo {
+    ControlProtocolDescriptor* descriptor;
+    ControlProtocol* protocol;
+    std::string name;
+    std::string path;
+};
+
+class ControlProtocolManager
+{
+  public:
+       ControlProtocolManager ();
+       ~ControlProtocolManager ();
+
+       static ControlProtocolManager& instance() { return *_instance; }
+
+       void discover_control_protocols (std::string search_path);
+       void startup (Session&);
+
+       ControlProtocol* instantiate (Session&, std::string protocol_name);
+       int              teardown (std::string protocol_name);
+
+  private:
+       static ControlProtocolManager* _instance;
+
+       PBD::Lock protocols_lock;
+       std::list<ControlProtocolInfo*> control_protocol_info;
+       std::list<ControlProtocol*>    control_protocols;
+
+       int control_protocol_discover (std::string path);
+       ControlProtocolDescriptor* get_descriptor (std::string path);
+};
+
+} // namespace
+
+#endif // ardour_control_protocol_manager_h
diff --git a/libs/ardour/control_protocol.cc b/libs/ardour/control_protocol.cc
new file mode 100644 (file)
index 0000000..9e1cbfd
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+    Copyright (C) 2006 Paul Davis 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    $Id$
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <pbd/pthread_utils.h>
+#include <pbd/error.h>
+#include <ardour/control_protocol.h>
+#include <ardour/configuration.h>
+#include <ardour/session.h>
+
+using namespace ARDOUR;
+using namespace std;
+
+#include "i18n.h"
+
+ControlProtocol::ControlProtocol (Session& s, string str)
+       : session (s), 
+         _name (str)
+{
+       active_thread = 1;
+}
+
+ControlProtocol::~ControlProtocol ()
+{
+}
+
+void
+ControlProtocol::set_send (SendWhat sw)
+{
+       _send = sw;
+}
+
+int
+ControlProtocol::init_thread ()
+{
+       if (pipe (thread_request_pipe) != 0) {
+               error << string_compose (_("%1: cannot create thread request pipe (%1)"), _name, strerror (errno))
+                     << endmsg;
+               return -1;
+       }
+
+       if (fcntl (thread_request_pipe[0], F_SETFL, O_NONBLOCK)) {
+               error << string_compose(_("%1: cannot set O_NONBLOCK on read pipe (%2)"), _name, strerror (errno)) << endmsg;
+               return -1;
+       }
+
+       if (fcntl (thread_request_pipe[1], F_SETFL, O_NONBLOCK)) {
+               error << string_compose(_("%1: cannot set O_NONBLOCK on signal write pipe (%2)"), _name, strerror (errno)) << endmsg;
+               return -1;
+       }
+
+       if (pthread_create_and_store ("tranzport delivery", &_thread, 0, _thread_work, this)) {
+               error << string_compose (_("%1: could not create thread"), _name) << endmsg;
+               return -1;
+       }
+
+       return 0;
+}      
+
+int
+ControlProtocol::poke_thread (ThreadRequest::Type why)
+{
+       char c = (char) why;
+       return !(write (thread_request_pipe[1], &c, 1) == 1);
+}
+
+int
+ControlProtocol::start_thread ()
+{
+       return poke_thread (ThreadRequest::Start);
+}
+
+int
+ControlProtocol::stop_thread ()
+{
+       return poke_thread (ThreadRequest::Stop);
+}
+
+void
+ControlProtocol::set_active (bool yn)
+{
+       if (yn != active_thread) {
+
+               if (yn) {
+                       /* make sure the feedback thread is alive */
+                       start_thread ();
+               } else {
+                       /* maybe put the feedback thread to sleep */
+                       stop_thread ();
+               }
+               
+               ActiveChanged ();
+       }
+}
+
+void
+ControlProtocol::terminate_thread ()
+{
+       void* status;
+       poke_thread (ThreadRequest::Quit);
+       pthread_join (_thread, &status);
+}
+
+void*
+ControlProtocol::_thread_work (void* arg)
+{
+       return static_cast<ControlProtocol*> (arg)->thread_work ();
+}
+
+void*
+ControlProtocol::thread_work ()
+{
+       PBD::ThreadCreated (pthread_self(), _name);
+
+       struct pollfd pfd[1];
+       int timeout;
+
+       struct sched_param rtparam;
+       int err;
+
+       memset (&rtparam, 0, sizeof (rtparam));
+       rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */
+       
+       if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
+               // do we care? not particularly.
+               info << string_compose (_("%1: delivery thread not running with realtime scheduling (%2)"), _name, strerror (errno)) << endmsg;
+       } 
+
+       pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
+       pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
+
+
+       if (active_thread) {
+               timeout = max (5, (int) Config->get_feedback_interval_ms());
+       } else {
+               timeout = -1;
+       }
+
+       while (1) {
+
+               pfd[0].fd = thread_request_pipe[0];
+               pfd[0].events = POLLIN|POLLHUP|POLLERR;
+
+               if (poll (pfd, 1, timeout) < 0) {
+                       if (errno == EINTR) {
+                               continue;
+                       }
+                       error << string_compose (_("Protocol \"%1\" thread: poll failed (%2)"), _name, strerror (errno))
+                             << endmsg;
+                       break;
+               }
+
+               if (pfd[0].revents & ~POLLIN) {
+                       error << string_compose (_("Error thread request pipe for protocol \"%1\""), _name) << endmsg;
+                       break;
+               }
+
+               if (pfd[0].revents & POLLIN) {
+
+                       char req;
+                       
+                       /* empty the pipe of all current requests */
+
+                       while (1) {
+                               size_t nread = read (thread_request_pipe[0], &req, sizeof (req));
+
+                               if (nread == 1) {
+                                       switch ((ThreadRequest::Type) req) {
+                                       
+                                       case ThreadRequest::Start:
+                                               timeout = max (5, (int) Config->get_feedback_interval_ms());
+                                               active_thread++;
+                                               break;
+                                               
+                                       case ThreadRequest::Stop:
+                                               timeout = -1;
+                                               if (active_thread) {
+                                                       active_thread--;
+                                               } 
+                                               break;
+                                               
+                                       case ThreadRequest::Quit:
+                                               pthread_exit_pbd (0);
+                                               /*NOTREACHED*/
+                                               break;
+                                               
+                                       default:
+                                               break;
+                                       }
+
+                               } else if (nread == 0) {
+                                       break;
+                               } else if (errno == EAGAIN) {
+                                       break;
+                               } else {
+                                       fatal << string_compose (_("Error reading from thread request pipe for protocol \"%1\""), _name) << endmsg;
+                                       /*NOTREACHED*/
+                               }
+                       }
+               }
+               
+               if (!active_thread) {
+                       continue;
+               }
+
+               cerr << ".\n";
+
+               if (send()) {
+                       
+                       list<Route*> routes = session.get_routes(); /* copies the routes */
+                       
+                       if (send_route_feedback ()) {
+                               send_route_feedback (routes);
+                       }
+
+                       send_global_feedback ();
+               }
+       }
+
+       return 0;
+}
diff --git a/libs/ardour/control_protocol_manager.cc b/libs/ardour/control_protocol_manager.cc
new file mode 100644 (file)
index 0000000..4944816
--- /dev/null
@@ -0,0 +1,211 @@
+#include <dlfcn.h>
+
+#include <pbd/compose.h>
+#include <pbd/error.h>
+#include <pbd/pathscanner.h>
+
+#include <ardour/control_protocol.h>
+#include <ardour/control_protocol_manager.h>
+
+using namespace ARDOUR;
+using namespace PBD;
+using namespace std;
+
+#include "i18n.h"
+
+ControlProtocolManager* ControlProtocolManager::_instance = 0;
+
+ControlProtocolManager::ControlProtocolManager ()
+{
+       if (_instance == 0) {
+               _instance = this;
+       }
+}
+
+ControlProtocolManager::~ControlProtocolManager()
+{
+}
+
+void
+ControlProtocolManager::startup (Session& s)
+{
+       list<ControlProtocolInfo *>::iterator i;
+       
+       for (i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+
+               ControlProtocolInfo* cpi = (*i);
+
+               if (cpi->name == "Tranzport") {
+               
+                       cpi->descriptor = get_descriptor ((*i)->path);
+                       
+                       if (cpi->descriptor == 0) {
+                               error << string_compose (_("control protocol name \"%1\" has no descriptor"), cpi->name) << endmsg;
+                               continue;
+                       }
+                       
+                       if ((cpi->protocol = cpi->descriptor->initialize (cpi->descriptor, &s)) == 0) {
+                               error << string_compose (_("control protocol name \"%1\" could not be initialized"), cpi->name) << endmsg;
+                               continue;
+                       }
+                       
+                       {
+                               LockMonitor lm (protocols_lock, __LINE__, __FILE__);
+                               control_protocols.push_back (cpi->protocol);
+                       }
+                       
+                       cerr << "start " << cpi->name << endl;
+                       cpi->protocol->init ();
+                       
+                       cerr << "activate " << cpi->name << endl;
+                       cpi->protocol->set_active (true);
+                       
+                       cerr << cpi->name << " now running\n";
+               }
+       }
+}
+
+ControlProtocol*
+ControlProtocolManager::instantiate (Session& session, string name)
+{
+       list<ControlProtocolInfo *>::iterator i;
+
+       for (i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+               if ((*i)->name == name) {
+                       break;
+               }
+       }
+
+       if (i == control_protocol_info.end()) {
+               error << string_compose (_("control protocol name \"%1\" is unknown"), name) << endmsg;
+               return 0;
+       }
+
+       ControlProtocolInfo* cpi = (*i);
+
+       cpi->descriptor = get_descriptor ((*i)->path);
+
+       if (cpi->descriptor == 0) {
+               error << string_compose (_("control protocol name \"%1\" has no descriptor"), name) << endmsg;
+               return 0;
+       }
+
+       if ((cpi->protocol = cpi->descriptor->initialize (cpi->descriptor, &session)) == 0) {
+               error << string_compose (_("control protocol name \"%1\" could not be initialized"), name) << endmsg;
+               return 0;
+       }
+
+       LockMonitor lm (protocols_lock, __LINE__, __FILE__);
+       control_protocols.push_back (cpi->protocol);
+       return cpi->protocol;
+}
+
+int
+ControlProtocolManager::teardown (string name)
+{
+       for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+               ControlProtocolInfo* cpi = *i;
+
+               if (cpi->name == name && cpi->descriptor && cpi->protocol) {
+                       cpi->descriptor->destroy (cpi->descriptor, cpi->protocol);
+                       
+                       {
+                               LockMonitor lm (protocols_lock, __LINE__, __FILE__);
+                               list<ControlProtocol*>::iterator p = find (control_protocols.begin(), control_protocols.end(), cpi->protocol);
+                               if (p != control_protocols.end()) {
+                                       control_protocols.erase (p);
+                               }
+                       }
+
+                       cpi->protocol = 0;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+static bool protocol_filter (const string& str, void *arg)
+{
+       /* Not a dotfile, has a prefix before a period, suffix is "so" */
+       
+       return str[0] != '.' && (str.length() > 3 && str.find (".so") == (str.length() - 3));
+}
+
+void
+ControlProtocolManager::discover_control_protocols (string path)
+{
+       vector<string *> *found;
+       PathScanner scanner;
+
+       cerr << "CP Manager looking for surfaces\n";
+
+       found = scanner (path, protocol_filter, 0, false, true);
+
+       for (vector<string*>::iterator i = found->begin(); i != found->end(); ++i) {
+               cerr << "CP Manager looking at " << **i << endl;
+               control_protocol_discover (**i);
+               delete *i;
+       }
+
+       delete found;
+}
+
+int
+ControlProtocolManager::control_protocol_discover (string path)
+{
+       ControlProtocolDescriptor* descriptor;
+
+       if ((descriptor = get_descriptor (path)) != 0) {
+
+               ControlProtocolInfo* info = new ControlProtocolInfo ();
+
+               info->descriptor = descriptor;
+               info->name = descriptor->name;
+               info->path = path;
+               
+               control_protocol_info.push_back (info);
+
+               cerr << "Found \"" << info->name << "\"\n";
+
+               dlclose (descriptor->module);
+
+       } else {
+               cerr << "no descriptor\n";
+       }
+
+       return 0;
+}
+
+ControlProtocolDescriptor*
+ControlProtocolManager::get_descriptor (string path)
+{
+       void *module;
+       ControlProtocolDescriptor *descriptor = 0;
+       ControlProtocolDescriptor* (*dfunc)(void);
+       const char *errstr;
+
+       if ((module = dlopen (path.c_str(), RTLD_NOW)) == 0) {
+               error << string_compose(_("ControlProtocolManager: cannot load module \"%1\" (%2)"), path, dlerror()) << endmsg;
+               return 0;
+       }
+
+
+       dfunc = (ControlProtocolDescriptor* (*)(void)) dlsym (module, "protocol_descriptor");
+
+       if ((errstr = dlerror()) != 0) {
+               error << string_compose(_("ControlProtocolManager: module \"%1\" has no descriptor function."), path) << endmsg;
+               error << errstr << endmsg;
+               dlclose (module);
+               return 0;
+       }
+
+       descriptor = dfunc();
+       if (descriptor) {
+               descriptor->module = module;
+       } else {
+               dlclose (module);
+       }
+
+       return descriptor;
+}