From c412d819699995c84bfb55bc57e8ee26d471bc98 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 5 Apr 2006 00:24:57 +0000 Subject: [PATCH] add new control protocol related files git-svn-id: svn://localhost/trunk/ardour2@443 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/control_protocol_manager.h | 48 ++++ libs/ardour/control_protocol.cc | 242 ++++++++++++++++++ libs/ardour/control_protocol_manager.cc | 211 +++++++++++++++ 3 files changed, 501 insertions(+) create mode 100644 libs/ardour/ardour/control_protocol_manager.h create mode 100644 libs/ardour/control_protocol.cc create mode 100644 libs/ardour/control_protocol_manager.cc diff --git a/libs/ardour/ardour/control_protocol_manager.h b/libs/ardour/ardour/control_protocol_manager.h new file mode 100644 index 0000000000..f0b7846978 --- /dev/null +++ b/libs/ardour/ardour/control_protocol_manager.h @@ -0,0 +1,48 @@ +#ifndef ardour_control_protocol_manager_h +#define ardour_control_protocol_manager_h + +#include +#include + +#include + +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 control_protocol_info; + std::list 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 index 0000000000..9e1cbfd4fd --- /dev/null +++ b/libs/ardour/control_protocol.cc @@ -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 +#include +#include +#include + +#include +#include +#include +#include +#include + +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 (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 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 index 0000000000..4944816177 --- /dev/null +++ b/libs/ardour/control_protocol_manager.cc @@ -0,0 +1,211 @@ +#include + +#include +#include +#include + +#include +#include + +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::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::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::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::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 *found; + PathScanner scanner; + + cerr << "CP Manager looking for surfaces\n"; + + found = scanner (path, protocol_filter, 0, false, true); + + for (vector::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; +}