Add support for internal session files
Thanks to Timo Wischer for the initial work
This commit is contained in:
parent
9c929bef39
commit
2ad48419a7
|
@ -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)
|
||||
{
|
||||
if (server_ptr && server_ptr->engine) {
|
||||
|
|
|
@ -236,6 +236,10 @@ SERVER_EXPORT bool jackctl_server_unload_internal(
|
|||
jackctl_server * server,
|
||||
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,
|
||||
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 "JackError.h"
|
||||
#include "JackMessageBuffer.h"
|
||||
#include "JackInternalSessionLoader.h"
|
||||
|
||||
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
|
||||
//---------------------------
|
||||
|
|
|
@ -86,6 +86,9 @@ 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);
|
||||
|
||||
// Internal session file
|
||||
int LoadInternalSessionFile(const char* file);
|
||||
|
||||
// Transport management
|
||||
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);
|
||||
|
||||
fprintf(file, "Available backends:\n");
|
||||
|
||||
|
||||
while (node_ptr) {
|
||||
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");
|
||||
|
@ -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);
|
||||
|
||||
fprintf(file, "Available internals:\n");
|
||||
|
||||
|
||||
while (node_ptr) {
|
||||
jackctl_internal_t* internal = (jackctl_internal_t *)node_ptr->data;
|
||||
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"
|
||||
" [ --slave-backend OR -X slave-backend-name ]\n"
|
||||
" [ --internal-client OR -I internal-client-name ]\n"
|
||||
" [ --internal-session-file OR -C internal-session-file ]\n"
|
||||
" [ --verbose OR -v ]\n"
|
||||
#ifdef __linux__
|
||||
" [ --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"
|
||||
" jackdmp -d master-backend-name --help\n"
|
||||
" to display options for each master backend\n\n");
|
||||
|
||||
|
||||
if (full) {
|
||||
print_server_drivers(server, file);
|
||||
print_server_internals(server, file);
|
||||
|
@ -265,7 +266,7 @@ int main(int argc, char** argv)
|
|||
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:"
|
||||
#ifdef __linux__
|
||||
"c:"
|
||||
|
@ -276,6 +277,7 @@ int main(int argc, char** argv)
|
|||
#ifdef __linux__
|
||||
{ "clock-source", 1, 0, 'c' },
|
||||
#endif
|
||||
{ "internal-session-file", 1, 0, 'C' },
|
||||
{ "loopback-driver", 1, 0, 'L' },
|
||||
{ "audio-driver", 1, 0, 'd' },
|
||||
{ "midi-driver", 1, 0, 'X' },
|
||||
|
@ -301,6 +303,7 @@ int main(int argc, char** argv)
|
|||
|
||||
int i,opt = 0;
|
||||
int option_index = 0;
|
||||
char* internal_session_file = NULL;
|
||||
char* master_driver_name = NULL;
|
||||
char** master_driver_args = NULL;
|
||||
int master_driver_nargs = 1;
|
||||
|
@ -443,6 +446,10 @@ int main(int argc, char** argv)
|
|||
}
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
internal_session_file = optarg;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
param = jackctl_get_parameter(server_parameters, "realtime-priority");
|
||||
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_sent = true;
|
||||
return_value = 0;
|
||||
|
|
|
@ -256,6 +256,20 @@ jackctl_server_unload_internal(
|
|||
jackctl_server_t * server,
|
||||
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.
|
||||
* (cannot be used when the server is running that is between
|
||||
|
|
|
@ -221,6 +221,7 @@ def build(bld):
|
|||
'JackExternalClient.cpp',
|
||||
'JackFreewheelDriver.cpp',
|
||||
'JackInternalClient.cpp',
|
||||
'JackInternalSessionLoader.cpp',
|
||||
'JackServer.cpp',
|
||||
'JackThreadedDriver.cpp',
|
||||
'JackRestartThreadedDriver.cpp',
|
||||
|
@ -244,7 +245,7 @@ def build(bld):
|
|||
'JackMidiReceiveQueue.cpp',
|
||||
'JackMidiSendQueue.cpp',
|
||||
'JackMidiUtil.cpp',
|
||||
'JackMidiWriteQueue.cpp'
|
||||
'JackMidiWriteQueue.cpp',
|
||||
]
|
||||
|
||||
if bld.env['IS_LINUX']:
|
||||
|
|
27
man/jackd.0
27
man/jackd.0
|
@ -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
|
||||
still subject to the supervision of the watchdog thread or its equivalent.
|
||||
.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
|
||||
.br
|
||||
Unlock libraries GTK+, QT, FLTK, Wine.
|
||||
|
|
Loading…
Reference in New Issue