From 8facc4a559bcf4007992dbdd354cdd6bdd9bf3eb Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Mon, 10 Jul 2017 13:16:09 +0200 Subject: [PATCH] Load internal JACK clients and port connections from configuration list file Signed-off-by: Timo Wischer --- common/JackControlAPI.cpp | 11 +++ common/JackControlAPI.h | 4 ++ common/JackLoader.cpp | 148 ++++++++++++++++++++++++++++++++++++++ common/JackLoader.h | 27 +++++++ common/JackServer.cpp | 7 ++ common/JackServer.h | 1 + common/Jackdmp.cpp | 5 ++ common/jack/control.h | 15 ++++ common/wscript | 3 +- 9 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 common/JackLoader.cpp create mode 100644 common/JackLoader.h diff --git a/common/JackControlAPI.cpp b/common/JackControlAPI.cpp index a733f5d9..a62f49f9 100644 --- a/common/JackControlAPI.cpp +++ b/common/JackControlAPI.cpp @@ -1347,6 +1347,17 @@ SERVER_EXPORT bool jackctl_server_load_internal( } } +SERVER_EXPORT bool jackctl_server_load_conf_file( + jackctl_server * const server_ptr, + const char* const file) +{ + if (!server_ptr || !file || !server_ptr->engine) { + return false; + } + + return (server_ptr->engine->InternalLoadFromConfFile(file) >= 0); +} + SERVER_EXPORT bool jackctl_server_unload_internal( jackctl_server * server_ptr, jackctl_internal * internal) diff --git a/common/JackControlAPI.h b/common/JackControlAPI.h index d9529388..c7c1059a 100644 --- a/common/JackControlAPI.h +++ b/common/JackControlAPI.h @@ -232,6 +232,10 @@ SERVER_EXPORT bool jackctl_server_load_internal( jackctl_server * server, jackctl_internal * internal); +SERVER_EXPORT bool jackctl_server_load_conf_file( + jackctl_server * const server_ptr, + const char* const file); + SERVER_EXPORT bool jackctl_server_unload_internal( jackctl_server * server, jackctl_internal * internal); diff --git a/common/JackLoader.cpp b/common/JackLoader.cpp new file mode 100644 index 00000000..12526c2f --- /dev/null +++ b/common/JackLoader.cpp @@ -0,0 +1,148 @@ +#include +#include "JackLoader.h" +#include "JackLockedEngine.h" + + +namespace Jack +{ + +JackLoader::JackLoader(JackServer* const server) : + fServer(server) +{ +} + +int JackLoader::Load(const std::string file) +{ + std::ifstream infile(file); + + if (!infile.is_open()) { + jack_error("JACK configuration file %s does not exist or cannot be opened for reading.", file.c_str()); + return -1; + } + + + std::string line; + int linenr = -1; + while (std::getline(infile, line)) + { + linenr++; + + std::istringstream iss(line); + + std::string command; + if ( !(iss >> command) ) { + /* ignoring empty line or line only filled with spaces */ + continue; + } + + /* convert command to lower case to accept any case of the letters in the command */ + std::transform(command.begin(), command.end(), command.begin(), ::tolower); + + if ( (command.compare("c") == 0) || (command.compare("connect") == 0) ) { + ConnectPorts(iss, linenr); + } else if ( (command.compare("l") == 0) || (command.compare("load") == 0) ) { + LoadClient(iss, linenr); + } else if (command.front() == '#') { + /* ignoring commented lines. + * The # can be followed by non spaces. + * Therefore only compare the first letter of the command. + */ + } else { + jack_error("JACK configuration file %s line %u contains unkown command '%s'. Ignoring the line!", file.c_str(), linenr, line.c_str()); + } + } + + return 0; +} + +void JackLoader::LoadClient(std::istringstream& iss, const int linenr) +{ + std::string client_name; + if ( !(iss >> client_name) ) { + jack_error("Cannot read client name from configuration file line %u '%s'. Ignoring the line!", linenr, iss.str().c_str()); + return; + } + + std::string lib_name; + if ( !(iss >> lib_name) ) { + jack_error("Cannot read client library name from configuration file line %u '%s'. Ignoring the line!", linenr, iss.str().c_str()); + return; + } + + /* get the rest of the line */ + std::string parameters; + if ( std::getline(iss, parameters) ) { + /* remove the leading spaces */ + const std::size_t start = parameters.find_first_not_of(" \t"); + if (start == std::string::npos) { + /* Parameters containing only spaces. + * Use empty parameter string. + */ + parameters = ""; + } else { + parameters = parameters.substr(start); + } + } + + + /* jackctl_server_load_internal() can not be used + * because it calls jack_internal_initialize() + * instead of jack_initialize() + */ + int status = 0; + int refnum = 0; + if (fServer->InternalClientLoad1(client_name.c_str(), lib_name.c_str(), parameters.c_str(), (JackLoadName|JackUseExactName|JackLoadInit), &refnum, -1, &status) < 0) { + /* Due to the JackUseExactName option JackNameNotUnique will always handled as a failure. + * See JackEngine::ClientCheck(). + */ + if (status & JackNameNotUnique) { + jack_error("Internal client name `%s' not unique", client_name.c_str()); + } + /* An error message for JackVersionError will already + * be printed by JackInternalClient::Open(). + * Therefore no need to handle it here. + */ + + jack_error("Cannot load client %s from configuration file line %u. Ignoring the line!", client_name.c_str(), linenr); + return; + } + + /* status has not to be checked for JackFailure + * because JackServer::InternalClientLoad1() will return a value < 0 + * and this is handled by the previouse if-clause. + */ + + jack_info("Internal client %s successfully loaded", client_name.c_str()); +} + +void JackLoader::ConnectPorts(std::istringstream& iss, const int linenr) +{ + std::string src_port; + if ( !(iss >> src_port) ) { + jack_error("Cannot read first port from configuration file line %u '%s'. Ignoring the line!", linenr, iss.str().c_str()); + return; + } + + std::string dst_port; + if ( !(iss >> dst_port) ) { + jack_error("Cannot read second port from configuration file line %u '%s'. Ignoring the line!", linenr, iss.str().c_str()); + return; + } + + /* use the client reference of the source port */ + const jack_port_id_t src_port_index = fServer->GetGraphManager()->GetPort(src_port.c_str()); + if (src_port_index >= NO_PORT) { + jack_error("Source port %s does not exist! Ignoring configuration file line %u '%s'.", src_port.c_str(), linenr, iss.str().c_str()); + return; + } + const int src_refnum = fServer->GetGraphManager()->GetOutputRefNum(src_port_index); + + if (fServer->GetEngine()->PortConnect(src_refnum, src_port.c_str(), dst_port.c_str()) < 0) { + jack_error("Cannot connect ports of configuration file line %u '%s'.Possibly the destination port does not exist. Ignoring the line!", linenr, iss.str().c_str()); + return; + } + + jack_info("Ports connected: %s -> %s", src_port.c_str(), dst_port.c_str()); +} + +} diff --git a/common/JackLoader.h b/common/JackLoader.h new file mode 100644 index 00000000..ce78a3d4 --- /dev/null +++ b/common/JackLoader.h @@ -0,0 +1,27 @@ +#ifndef JACKLOADER_H +#define JACKLOADER_H + +#include +#include +#include "JackServer.h" + + +namespace Jack +{ + +class JackLoader +{ +public: + JackLoader(JackServer* const server); + int Load(const std::string file); + +private: + void LoadClient(std::istringstream& iss, const int linenr); + void ConnectPorts(std::istringstream& iss, const int linenr); + + JackServer* const fServer; +}; + +} // end of namespace + +#endif // JACKLOADER_H diff --git a/common/JackServer.cpp b/common/JackServer.cpp index 1fd26754..4815432f 100644 --- a/common/JackServer.cpp +++ b/common/JackServer.cpp @@ -33,6 +33,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackInternalClient.h" #include "JackError.h" #include "JackMessageBuffer.h" +#include "JackLoader.h" const char * jack_get_self_connect_mode_description(char mode); @@ -225,6 +226,12 @@ int JackServer::InternalClientLoadAux(JackLoadableInternalClient* client, const } } +int JackServer::InternalLoadFromConfFile(const char* const file) +{ + JackLoader loader(this); + return loader.Load(file); +} + //--------------------------- // From request thread : API //--------------------------- diff --git a/common/JackServer.h b/common/JackServer.h index 473f88ff..95a2f61e 100644 --- a/common/JackServer.h +++ b/common/JackServer.h @@ -86,6 +86,7 @@ class SERVER_EXPORT JackServer // Internals clients int InternalClientLoad1(const char* client_name, const char* so_name, const char* objet_data, int options, int* int_ref, int uuid, int* status); int InternalClientLoad2(const char* client_name, const char* so_name, const JSList * parameters, int options, int* int_ref, int uuid, int* status); + int InternalLoadFromConfFile(const char* const file); // Transport management int ReleaseTimebase(int refnum); diff --git a/common/Jackdmp.cpp b/common/Jackdmp.cpp index 7eea2815..16c9a454 100644 --- a/common/Jackdmp.cpp +++ b/common/Jackdmp.cpp @@ -606,6 +606,11 @@ int main(int argc, char** argv) } } + if (!jackctl_server_load_conf_file(server_ctl, "/etc/jackd.conf")) { + fprintf(stderr, "Configuration file %s can not be loaded\n", "TODO"); + goto stop_server; + } + notify_server_start(server_name); notify_sent = true; return_value = 0; diff --git a/common/jack/control.h b/common/jack/control.h index 33cd77b1..bbd7ad74 100644 --- a/common/jack/control.h +++ b/common/jack/control.h @@ -242,6 +242,21 @@ jackctl_server_load_internal( jackctl_server_t * server, jackctl_internal_t * internal); +/** + * Call this function to load one internal client. + * (can be used when the server is running) + * + * @param server server object handle + * @param file the configuration file + * which contains the clients and connections + * which should be loaded + * + * @return success status: true - success, false - fail + */ +bool jackctl_server_load_conf_file( + jackctl_server * const server_ptr, + const char* const file); + /** * Call this function to unload one internal client. * (can be used when the server is running) diff --git a/common/wscript b/common/wscript index 860cbace..529a883a 100644 --- a/common/wscript +++ b/common/wscript @@ -244,7 +244,8 @@ def build(bld): 'JackMidiReceiveQueue.cpp', 'JackMidiSendQueue.cpp', 'JackMidiUtil.cpp', - 'JackMidiWriteQueue.cpp' + 'JackMidiWriteQueue.cpp', + 'JackLoader.cpp' ] if bld.env['IS_LINUX']: