Thanks to Timo Wischer for the initial worktags/v1.9.12
| @@ -1364,6 +1364,17 @@ SERVER_EXPORT bool jackctl_server_unload_internal( | |||||
| } | } | ||||
| } | } | ||||
| SERVER_EXPORT bool jackctl_server_load_session_file( | |||||
| jackctl_server * server_ptr, | |||||
| const char * file) | |||||
| { | |||||
| if (!server_ptr || !file || !server_ptr->engine) { | |||||
| return false; | |||||
| } | |||||
| return (server_ptr->engine->LoadInternalSessionFile(file) >= 0); | |||||
| } | |||||
| SERVER_EXPORT bool jackctl_server_add_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) | SERVER_EXPORT bool jackctl_server_add_slave(jackctl_server * server_ptr, jackctl_driver * driver_ptr) | ||||
| { | { | ||||
| if (server_ptr && server_ptr->engine) { | if (server_ptr && server_ptr->engine) { | ||||
| @@ -236,6 +236,10 @@ SERVER_EXPORT bool jackctl_server_unload_internal( | |||||
| jackctl_server * server, | jackctl_server * server, | ||||
| jackctl_internal * internal); | jackctl_internal * internal); | ||||
| SERVER_EXPORT bool jackctl_server_load_session_file( | |||||
| jackctl_server * server_ptr, | |||||
| const char * file); | |||||
| SERVER_EXPORT bool jackctl_server_add_slave(jackctl_server_t * server, | SERVER_EXPORT bool jackctl_server_add_slave(jackctl_server_t * server, | ||||
| jackctl_driver_t * driver); | jackctl_driver_t * driver); | ||||
| @@ -0,0 +1,176 @@ | |||||
| /* | |||||
| Copyright (C) 2017 Timo Wischer | |||||
| This program is free software; you can redistribute it and/or modify | |||||
| it under the terms of the GNU Lesser General Public License as published by | |||||
| the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. | |||||
| You should have received a copy of the GNU Lesser General Public License | |||||
| along with this program; if not, write to the Free Software | |||||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
| */ | |||||
| #include <fstream> | |||||
| #include "JackInternalSessionLoader.h" | |||||
| #include "JackLockedEngine.h" | |||||
| namespace Jack | |||||
| { | |||||
| JackInternalSessionLoader::JackInternalSessionLoader(JackServer* const server) : | |||||
| fServer(server) | |||||
| { | |||||
| } | |||||
| int JackInternalSessionLoader::Load(const char* file) | |||||
| { | |||||
| std::ifstream infile(file); | |||||
| if (!infile.is_open()) { | |||||
| jack_error("JACK internal session file %s does not exist or cannot be opened for reading.", file); | |||||
| 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("con") == 0) ) { | |||||
| ConnectPorts(iss, linenr); | |||||
| } else if ( (command.compare("l") == 0) || (command.compare("load") == 0) ) { | |||||
| LoadClient(iss, linenr); | |||||
| #if 0 | |||||
| /* NOTE: c++11 only */ | |||||
| } else if (command.front() == '#') { | |||||
| #else | |||||
| } else if (command[0] == '#') { | |||||
| #endif | |||||
| /* ignoring commented lines. | |||||
| * The # can be followed by non spaces. | |||||
| * Therefore only compare the first letter of the command. | |||||
| */ | |||||
| } else { | |||||
| jack_error("JACK internal session file %s line %u contains unkown command '%s'. Ignoring the line!", file, linenr, line.c_str()); | |||||
| } | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| void JackInternalSessionLoader::LoadClient(std::istringstream& iss, const int linenr) | |||||
| { | |||||
| std::string client_name; | |||||
| if ( !(iss >> client_name) ) { | |||||
| jack_error("Cannot read client name from internal session 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 internal session 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 internal session 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 JackInternalSessionLoader::ConnectPorts(std::istringstream& iss, const int linenr) | |||||
| { | |||||
| std::string src_port; | |||||
| if ( !(iss >> src_port) ) { | |||||
| jack_error("Cannot read first port from internal session 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 internal session 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 internal session 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 internal session file line %u '%s'.\n" | |||||
| "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()); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,46 @@ | |||||
| /* | |||||
| Copyright (C) 2017 Timo Wischer | |||||
| This program is free software; you can redistribute it and/or modify | |||||
| it under the terms of the GNU Lesser General Public License as published by | |||||
| the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. | |||||
| You should have received a copy of the GNU Lesser General Public License | |||||
| along with this program; if not, write to the Free Software | |||||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
| */ | |||||
| #ifndef __JackInternalSessionLoader__ | |||||
| #define __JackInternalSessionLoader__ | |||||
| #include <string> | |||||
| #include <sstream> | |||||
| #include "JackServer.h" | |||||
| namespace Jack | |||||
| { | |||||
| class JackInternalSessionLoader | |||||
| { | |||||
| public: | |||||
| JackInternalSessionLoader(JackServer* const server); | |||||
| int Load(const char* 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 | |||||
| @@ -33,6 +33,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
| #include "JackInternalClient.h" | #include "JackInternalClient.h" | ||||
| #include "JackError.h" | #include "JackError.h" | ||||
| #include "JackMessageBuffer.h" | #include "JackMessageBuffer.h" | ||||
| #include "JackInternalSessionLoader.h" | |||||
| const char * jack_get_self_connect_mode_description(char mode); | const char * jack_get_self_connect_mode_description(char mode); | ||||
| @@ -225,6 +226,16 @@ int JackServer::InternalClientLoadAux(JackLoadableInternalClient* client, const | |||||
| } | } | ||||
| } | } | ||||
| //----------------------- | |||||
| // Internal session file | |||||
| //----------------------- | |||||
| int JackServer::LoadInternalSessionFile(const char* file) | |||||
| { | |||||
| JackInternalSessionLoader loader(this); | |||||
| return loader.Load(file); | |||||
| } | |||||
| //--------------------------- | //--------------------------- | ||||
| // From request thread : API | // From request thread : API | ||||
| //--------------------------- | //--------------------------- | ||||
| @@ -86,6 +86,9 @@ class SERVER_EXPORT JackServer | |||||
| // Internals clients | // 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 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 InternalClientLoad2(const char* client_name, const char* so_name, const JSList * parameters, int options, int* int_ref, int uuid, int* status); | ||||
| // Internal session file | |||||
| int LoadInternalSessionFile(const char* file); | |||||
| // Transport management | // Transport management | ||||
| int ReleaseTimebase(int refnum); | int ReleaseTimebase(int refnum); | ||||
| @@ -162,7 +162,7 @@ static void print_server_drivers(jackctl_server_t *server, FILE* file) | |||||
| const JSList * node_ptr = jackctl_server_get_drivers_list(server); | const JSList * node_ptr = jackctl_server_get_drivers_list(server); | ||||
| fprintf(file, "Available backends:\n"); | fprintf(file, "Available backends:\n"); | ||||
| while (node_ptr) { | while (node_ptr) { | ||||
| jackctl_driver_t* driver = (jackctl_driver_t *)node_ptr->data; | jackctl_driver_t* driver = (jackctl_driver_t *)node_ptr->data; | ||||
| fprintf(file, " %s (%s)\n", jackctl_driver_get_name(driver), (jackctl_driver_get_type(driver) == JackMaster) ? "master" : "slave"); | fprintf(file, " %s (%s)\n", jackctl_driver_get_name(driver), (jackctl_driver_get_type(driver) == JackMaster) ? "master" : "slave"); | ||||
| @@ -176,7 +176,7 @@ static void print_server_internals(jackctl_server_t *server, FILE* file) | |||||
| const JSList * node_ptr = jackctl_server_get_internals_list(server); | const JSList * node_ptr = jackctl_server_get_internals_list(server); | ||||
| fprintf(file, "Available internals:\n"); | fprintf(file, "Available internals:\n"); | ||||
| while (node_ptr) { | while (node_ptr) { | ||||
| jackctl_internal_t* internal = (jackctl_internal_t *)node_ptr->data; | jackctl_internal_t* internal = (jackctl_internal_t *)node_ptr->data; | ||||
| fprintf(file, " %s\n", jackctl_internal_get_name(internal)); | fprintf(file, " %s\n", jackctl_internal_get_name(internal)); | ||||
| @@ -202,6 +202,7 @@ static void usage(FILE* file, jackctl_server_t *server, bool full = true) | |||||
| " [ --port-max OR -p maximum-number-of-ports]\n" | " [ --port-max OR -p maximum-number-of-ports]\n" | ||||
| " [ --slave-backend OR -X slave-backend-name ]\n" | " [ --slave-backend OR -X slave-backend-name ]\n" | ||||
| " [ --internal-client OR -I internal-client-name ]\n" | " [ --internal-client OR -I internal-client-name ]\n" | ||||
| " [ --internal-session-file OR -C internal-session-file ]\n" | |||||
| " [ --verbose OR -v ]\n" | " [ --verbose OR -v ]\n" | ||||
| #ifdef __linux__ | #ifdef __linux__ | ||||
| " [ --clocksource OR -c [ h(pet) | s(ystem) ]\n" | " [ --clocksource OR -c [ h(pet) | s(ystem) ]\n" | ||||
| @@ -232,7 +233,7 @@ static void usage(FILE* file, jackctl_server_t *server, bool full = true) | |||||
| " -d master-backend-name [ ... master-backend args ... ]\n" | " -d master-backend-name [ ... master-backend args ... ]\n" | ||||
| " jackdmp -d master-backend-name --help\n" | " jackdmp -d master-backend-name --help\n" | ||||
| " to display options for each master backend\n\n"); | " to display options for each master backend\n\n"); | ||||
| if (full) { | if (full) { | ||||
| print_server_drivers(server, file); | print_server_drivers(server, file); | ||||
| print_server_internals(server, file); | print_server_internals(server, file); | ||||
| @@ -265,7 +266,7 @@ int main(int argc, char** argv) | |||||
| print_version(); | print_version(); | ||||
| } | } | ||||
| } | } | ||||
| const char *options = "-d:X:I:P:uvshrRL:STFl:t:mn:p:" | |||||
| const char *options = "-d:X:I:P:uvshrRL:STFl:t:mn:p:C:" | |||||
| "a:" | "a:" | ||||
| #ifdef __linux__ | #ifdef __linux__ | ||||
| "c:" | "c:" | ||||
| @@ -276,6 +277,7 @@ int main(int argc, char** argv) | |||||
| #ifdef __linux__ | #ifdef __linux__ | ||||
| { "clock-source", 1, 0, 'c' }, | { "clock-source", 1, 0, 'c' }, | ||||
| #endif | #endif | ||||
| { "internal-session-file", 1, 0, 'C' }, | |||||
| { "loopback-driver", 1, 0, 'L' }, | { "loopback-driver", 1, 0, 'L' }, | ||||
| { "audio-driver", 1, 0, 'd' }, | { "audio-driver", 1, 0, 'd' }, | ||||
| { "midi-driver", 1, 0, 'X' }, | { "midi-driver", 1, 0, 'X' }, | ||||
| @@ -301,6 +303,7 @@ int main(int argc, char** argv) | |||||
| int i,opt = 0; | int i,opt = 0; | ||||
| int option_index = 0; | int option_index = 0; | ||||
| char* internal_session_file = NULL; | |||||
| char* master_driver_name = NULL; | char* master_driver_name = NULL; | ||||
| char** master_driver_args = NULL; | char** master_driver_args = NULL; | ||||
| int master_driver_nargs = 1; | int master_driver_nargs = 1; | ||||
| @@ -443,6 +446,10 @@ int main(int argc, char** argv) | |||||
| } | } | ||||
| break; | break; | ||||
| case 'C': | |||||
| internal_session_file = optarg; | |||||
| break; | |||||
| case 'P': | case 'P': | ||||
| param = jackctl_get_parameter(server_parameters, "realtime-priority"); | param = jackctl_get_parameter(server_parameters, "realtime-priority"); | ||||
| if (param != NULL) { | if (param != NULL) { | ||||
| @@ -606,6 +613,13 @@ int main(int argc, char** argv) | |||||
| } | } | ||||
| } | } | ||||
| if (internal_session_file != NULL) { | |||||
| if (!jackctl_server_load_session_file(server_ctl, internal_session_file)) { | |||||
| fprintf(stderr, "Internal session file %s cannot be loaded!\n", internal_session_file); | |||||
| goto stop_server; | |||||
| } | |||||
| } | |||||
| notify_server_start(server_name); | notify_server_start(server_name); | ||||
| notify_sent = true; | notify_sent = true; | ||||
| return_value = 0; | return_value = 0; | ||||
| @@ -256,6 +256,20 @@ jackctl_server_unload_internal( | |||||
| jackctl_server_t * server, | jackctl_server_t * server, | ||||
| jackctl_internal_t * internal); | jackctl_internal_t * internal); | ||||
| /** | |||||
| * Call this function to load a session file. | |||||
| * (can be used when the server is running) | |||||
| * | |||||
| * @param server server object handle | |||||
| * @param file the session file to load, containing a list of | |||||
| * internal clients and connections to be made. | |||||
| * | |||||
| * @return success status: true - success, false - fail | |||||
| */ | |||||
| bool jackctl_server_load_session_file( | |||||
| jackctl_server * server_ptr, | |||||
| const char * file); | |||||
| /** | /** | ||||
| * Call this function to add a slave in the driver slave list. | * Call this function to add a slave in the driver slave list. | ||||
| * (cannot be used when the server is running that is between | * (cannot be used when the server is running that is between | ||||
| @@ -221,6 +221,7 @@ def build(bld): | |||||
| 'JackExternalClient.cpp', | 'JackExternalClient.cpp', | ||||
| 'JackFreewheelDriver.cpp', | 'JackFreewheelDriver.cpp', | ||||
| 'JackInternalClient.cpp', | 'JackInternalClient.cpp', | ||||
| 'JackInternalSessionLoader.cpp', | |||||
| 'JackServer.cpp', | 'JackServer.cpp', | ||||
| 'JackThreadedDriver.cpp', | 'JackThreadedDriver.cpp', | ||||
| 'JackRestartThreadedDriver.cpp', | 'JackRestartThreadedDriver.cpp', | ||||
| @@ -244,7 +245,7 @@ def build(bld): | |||||
| 'JackMidiReceiveQueue.cpp', | 'JackMidiReceiveQueue.cpp', | ||||
| 'JackMidiSendQueue.cpp', | 'JackMidiSendQueue.cpp', | ||||
| 'JackMidiUtil.cpp', | 'JackMidiUtil.cpp', | ||||
| 'JackMidiWriteQueue.cpp' | |||||
| 'JackMidiWriteQueue.cpp', | |||||
| ] | ] | ||||
| if bld.env['IS_LINUX']: | if bld.env['IS_LINUX']: | ||||
| @@ -90,6 +90,33 @@ Prevent JACK from ever kicking out clients because they were too slow. | |||||
| This cancels the effect any specified timeout value, but JACK and its clients are | This cancels the effect any specified timeout value, but JACK and its clients are | ||||
| still subject to the supervision of the watchdog thread or its equivalent. | still subject to the supervision of the watchdog thread or its equivalent. | ||||
| .TP | .TP | ||||
| \fB\-C, \-\-internal-session-file \fIinternal-session-file\fR | |||||
| .br | |||||
| Load internal clients and connections from \fIinternal-session-file\fR. | |||||
| Each line of this configuration file starts with a command. | |||||
| The following commands are available: | |||||
| .br | |||||
| \fBl(oad)\fR \fIclient-name lib-name client-args\fR | |||||
| .br | |||||
| With this command an internal JACK client will be instantiated. | |||||
| \fIclient-name\fR and \fIlib-name\fR cannot contain spaces. | |||||
| The rest of the line will be interpreted as \fIclient-args\fR and | |||||
| sent to the client library. | |||||
| .br | |||||
| \fBc(on)\fR \fIsource-port destination-port\fR | |||||
| .br | |||||
| With this command a source port will be connected to a destination port. | |||||
| \fIsource-port\fR and \fIdestination-port\fR cannot contain spaces. | |||||
| .br | |||||
| Comments are allowed, they start with \fB#\fR. | |||||
| .br | |||||
| An example configuration could look like the following: | |||||
| .br | |||||
| l inprocess1 inprocess | |||||
| l amp1 jalv http://lv2plug.in/plugins/eg-amp | |||||
| .br | |||||
| c amp:out system:playback_1 | |||||
| .TP | |||||
| \fB\-u, \-\-unlock\fR | \fB\-u, \-\-unlock\fR | ||||
| .br | .br | ||||
| Unlock libraries GTK+, QT, FLTK, Wine. | Unlock libraries GTK+, QT, FLTK, Wine. | ||||