Browse Source

add session notification infrastructure

squashed rebase.

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@3969 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.120.1
torben 16 years ago
parent
commit
cf530808c9
19 changed files with 2047 additions and 40 deletions
  1. +5
    -0
      example-clients/Makefile.am
  2. +193
    -0
      example-clients/simple_session_client.c
  3. +1
    -0
      jack/Makefile.am
  4. +13
    -0
      jack/engine.h
  5. +25
    -7
      jack/internal.h
  6. +0
    -1
      jack/jack.h
  7. +229
    -0
      jack/session.h
  8. +13
    -8
      jack/types.h
  9. +3
    -0
      jack/varargs.h
  10. +89
    -10
      jackd/clientengine.c
  11. +236
    -6
      jackd/engine.c
  12. +225
    -2
      libjack/client.c
  13. +4
    -1
      libjack/local.h
  14. +363
    -0
      python/libjack.py
  15. +277
    -0
      python/sessionmanager.py
  16. +136
    -0
      python/state.py
  17. +5
    -0
      tools/Makefile.am
  18. +50
    -5
      tools/connect.c
  19. +180
    -0
      tools/session_notify.c

+ 5
- 0
example-clients/Makefile.am View File

@@ -13,6 +13,7 @@ dist-check-sndfile:
endif

bin_PROGRAMS = jack_simple_client \
jack_simple_session_client \
jack_transport_client \
jack_impulse_grabber \
jack_metro \
@@ -33,6 +34,10 @@ jack_simple_client_SOURCES = simple_client.c
jack_simple_client_LDFLAGS = @OS_LDFLAGS@
jack_simple_client_LDADD = $(top_builddir)/libjack/libjack.la

jack_simple_session_client_SOURCES = simple_session_client.c
jack_simple_session_client_LDFLAGS = @OS_LDFLAGS@
jack_simple_session_client_LDADD = $(top_builddir)/libjack/libjack.la

jack_transport_client_SOURCES = transport_client.c
jack_transport_client_LDFLAGS = @OS_LDFLAGS@
jack_transport_client_LDADD = $(top_builddir)/libjack/libjack.la


+ 193
- 0
example-clients/simple_session_client.c View File

@@ -0,0 +1,193 @@
/** @file simple_session_client.c
*
* @brief This simple client demonstrates the most basic features of JACK
* as they would be used by many applications.
* this version also adds session manager functionality.
*/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <jack/jack.h>
#include <jack/session.h>

jack_port_t *input_port;
jack_port_t *output_port;
jack_client_t *client;

int simple_quit = 0;

/**
* The process callback for this JACK application is called in a
* special realtime thread once for each audio cycle.
*
* This client does nothing more than copy data from its input
* port to its output port. It will exit when stopped by
* the user (e.g. using Ctrl-C on a unix-ish operating system)
*/
int
process (jack_nframes_t nframes, void *arg)
{
jack_default_audio_sample_t *in, *out;

in = jack_port_get_buffer (input_port, nframes);
out = jack_port_get_buffer (output_port, nframes);
memcpy (out, in,
sizeof (jack_default_audio_sample_t) * nframes);

return 0;
}

void
session_callback (jack_session_event_t *event, void *arg)
{
char retval[100];
printf ("session notification\n");
printf ("path %s, uuid %s, type: %s\n", event->session_dir, event->client_uuid, event->type == JackSessionSave ? "save" : "quit");


snprintf (retval, 100, "jack_simple_client %s", event->client_uuid);
event->command_line = strdup (retval);

jack_session_reply( client, event );

if (event->type == JackSessionSaveAndQuit) {
simple_quit = 1;
}

jack_session_event_free (event);
}

/**
* JACK calls this shutdown_callback if the server ever shuts down or
* decides to disconnect the client.
*/
void
jack_shutdown (void *arg)
{
exit (1);
}

int
main (int argc, char *argv[])
{
const char **ports;
const char *client_name = "simple";
jack_status_t status;

/* open a client connection to the JACK server */

if( argc == 1 )
client = jack_client_open (client_name, JackNullOption, &status );
else if( argc == 2 )
client = jack_client_open (client_name, JackSessionID, &status, argv[1] );

if (client == NULL) {
fprintf (stderr, "jack_client_open() failed, "
"status = 0x%2.0x\n", status);
if (status & JackServerFailed) {
fprintf (stderr, "Unable to connect to JACK server\n");
}
exit (1);
}
if (status & JackServerStarted) {
fprintf (stderr, "JACK server started\n");
}
if (status & JackNameNotUnique) {
client_name = jack_get_client_name(client);
fprintf (stderr, "unique name `%s' assigned\n", client_name);
}

/* tell the JACK server to call `process()' whenever
there is work to be done.
*/

jack_set_process_callback (client, process, 0);

/* tell the JACK server to call `jack_shutdown()' if
it ever shuts down, either entirely, or if it
just decides to stop calling us.
*/

jack_on_shutdown (client, jack_shutdown, 0);

/* tell the JACK server to call `session_callback()' if
the session is saved.
*/

jack_set_session_callback (client, session_callback, NULL);

/* display the current sample rate.
*/

printf ("engine sample rate: %" PRIu32 "\n",
jack_get_sample_rate (client));

/* create two ports */

input_port = jack_port_register (client, "input",
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsInput, 0);
output_port = jack_port_register (client, "output",
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);

if ((input_port == NULL) || (output_port == NULL)) {
fprintf(stderr, "no more JACK ports available\n");
exit (1);
}

/* Tell the JACK server that we are ready to roll. Our
* process() callback will start running now. */

if (jack_activate (client)) {
fprintf (stderr, "cannot activate client");
exit (1);
}

/* Connect the ports. You can't do this before the client is
* activated, because we can't make connections to clients
* that aren't running. Note the confusing (but necessary)
* orientation of the driver backend ports: playback ports are
* "input" to the backend, and capture ports are "output" from
* it.
*/

ports = jack_get_ports (client, NULL, NULL,
JackPortIsPhysical|JackPortIsOutput);
if (ports == NULL) {
fprintf(stderr, "no physical capture ports\n");
exit (1);
}

if (jack_connect (client, ports[0], jack_port_name (input_port))) {
fprintf (stderr, "cannot connect input ports\n");
}

free (ports);

ports = jack_get_ports (client, NULL, NULL,
JackPortIsPhysical|JackPortIsInput);
if (ports == NULL) {
fprintf(stderr, "no physical playback ports\n");
exit (1);
}

if (jack_connect (client, jack_port_name (output_port), ports[0])) {
fprintf (stderr, "cannot connect output ports\n");
}

free (ports);

/* keep running until until we get a quit event */

while (!simple_quit)
sleep(1);

jack_client_close (client);
exit (0);
}

+ 1
- 0
jack/Makefile.am View File

@@ -7,6 +7,7 @@ libjackinclude_HEADERS = \
jack.h \
ringbuffer.h \
statistics.h \
session.h \
thread.h \
timestamps.h \
transport.h \


+ 13
- 0
jack/engine.h View File

@@ -51,6 +51,11 @@ typedef struct _jack_port_buffer_list {
jack_port_buffer_info_t *info; /* jack_buffer_info_t array */
} jack_port_buffer_list_t;

typedef struct _jack_reserved_name {
jack_client_id_t uuid;
char name[JACK_CLIENT_NAME_SIZE];
} jack_reserved_name_t;

#define JACKD_WATCHDOG_TIMEOUT 10000
#define JACKD_CLIENT_EVENT_TIMEOUT 2000

@@ -109,6 +114,11 @@ struct _jack_engine {
char fifo_prefix[PATH_MAX+1];
int *fifo;
unsigned long fifo_size;

/* session handling */
int session_reply_fd;
int session_pending_replies;

unsigned long external_client_cnt;
int rtpriority;
volatile char freewheeling;
@@ -130,6 +140,7 @@ struct _jack_engine {
/* these lists are protected by `client_lock' */
JSList *clients;
JSList *clients_waiting;
JSList *reserved_client_names;

jack_port_internal_t *internal_ports;
jack_client_internal_t *timebase_client;
@@ -234,5 +245,7 @@ void jack_port_registration_notify (jack_engine_t *, jack_port_id_t, int);
void jack_port_release (jack_engine_t *engine, jack_port_internal_t *);
void jack_sort_graph (jack_engine_t *engine);
int jack_stop_freewheeling (jack_engine_t* engine, int engine_exiting);
jack_client_internal_t *
jack_client_by_name (jack_engine_t *engine, const char *name);

#endif /* __jack_engine_h__ */

+ 25
- 7
jack/internal.h View File

@@ -58,6 +58,7 @@ extern void jack_info (const char *fmt, ...);
#include <jack/types.h>
#include <jack/port.h>
#include <jack/transport.h>
#include <jack/session.h>
#include <jack/thread.h>

extern jack_thread_creator_t jack_thread_creator;
@@ -217,14 +218,15 @@ typedef enum {
StartFreewheel,
StopFreewheel,
ClientRegistered,
ClientUnregistered
ClientUnregistered,
SaveSession
} JackEventType;

typedef struct {
JackEventType type;
union {
uint32_t n;
char name[JACK_CLIENT_NAME_SIZE];
char name[JACK_PORT_NAME_SIZE];
jack_port_id_t port_id;
jack_port_id_t self_id;
} x;
@@ -252,9 +254,12 @@ typedef enum {
typedef volatile struct {

volatile jack_client_id_t id; /* w: engine r: engine and client */
volatile jack_client_id_t uid; /* w: engine r: engine and client */
volatile jack_nframes_t nframes; /* w: engine r: client */
volatile jack_client_state_t state; /* w: engine and client r: engine */
volatile char name[JACK_CLIENT_NAME_SIZE];
volatile char session_command[JACK_PORT_NAME_SIZE];
volatile jack_session_flags_t session_flags;
volatile ClientType type; /* w: engine r: engine and client */
volatile int8_t active; /* w: engine r: engine and client */
volatile int8_t dead; /* r/w: engine */
@@ -292,6 +297,7 @@ typedef volatile struct {
volatile uint8_t freewheel_cb_cbset;
volatile uint8_t client_register_cbset;
volatile uint8_t thread_cb_cbset;
volatile uint8_t session_cbset;

} POST_PACKED_STRUCTURE jack_client_control_t;

@@ -301,6 +307,7 @@ typedef struct {
int32_t load;
ClientType type;
jack_options_t options;
jack_client_id_t uuid;

char name[JACK_CLIENT_NAME_SIZE];
char object_path[PATH_MAX+1];
@@ -370,7 +377,11 @@ typedef enum {
IntClientName = 21,
IntClientUnload = 22,
RecomputeTotalLatencies = 23,
RecomputeTotalLatency = 24
RecomputeTotalLatency = 24,
SessionNotify = 25,
GetClientByUUID = 26,
ReserveName = 30,
SessionReply = 31
} RequestType;

struct _jack_request {
@@ -390,6 +401,11 @@ struct _jack_request {
char source_port[JACK_PORT_NAME_SIZE];
char destination_port[JACK_PORT_NAME_SIZE];
} POST_PACKED_STRUCTURE connect;
struct {
char path[JACK_PORT_NAME_SIZE];
jack_session_event_type_t type;
char target[JACK_CLIENT_NAME_SIZE];
} POST_PACKED_STRUCTURE session;
struct {
int32_t nports;
const char **ports; /* this is only exposed to internal clients, so there
@@ -407,6 +423,10 @@ struct _jack_request {
jack_client_id_t client_id;
int32_t conditional;
} POST_PACKED_STRUCTURE timebase;
struct {
char name[JACK_CLIENT_NAME_SIZE];
jack_client_id_t uuid;
} POST_PACKED_STRUCTURE reservename;
struct {
//jack_options_t options;
uint32_t options;
@@ -447,6 +467,8 @@ typedef struct _jack_client_internal {
int (*initialize)(jack_client_t*, const char*); /* int. clients only */
void (*finish)(void *); /* internal clients only */
int error;

int session_reply_pending;
#ifdef JACK_USE_MACH_THREADS
/* specific resources for server/client real-time thread communication */
@@ -456,10 +478,6 @@ typedef struct _jack_client_internal {
int portnum;
#endif /* JACK_USE_MACH_THREADS */
/* external clients: set by libjack
* internal clients: set by engine */
//int (*deliver_request)(void*, jack_request_t*); /* JOQ: 64/32 bug! */
//void *deliver_arg;
jack_client_t *private_client;
} jack_client_internal_t;



+ 0
- 1
jack/jack.h View File

@@ -406,7 +406,6 @@ int jack_set_graph_order_callback (jack_client_t *,
*/
int jack_set_xrun_callback (jack_client_t *,
JackXRunCallback xrun_callback, void *arg) JACK_OPTIONAL_WEAK_EXPORT;

/*@}*/

/**


+ 229
- 0
jack/session.h View File

@@ -0,0 +1,229 @@
/*
Copyright (C) 2001 Paul Davis
Copyright (C) 2004 Jack O'Quin
Copyright (C) 2010 Torben Hohn
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 __jack_session_h__
#define __jack_session_h__

#ifdef __cplusplus
extern "C" {
#endif

#include <jack/types.h>
#include <jack/weakmacros.h>

/**
* @defgroup SessionClientFunctions Session API for clients.
* @{
*/


/**
* session event types.
*
* if a client cant save templates, i might just do a normal save.
*
* the rationale, why there is no quit without save, is that a client
* might refuse to quit when it has unsaved data.
* however some other clients might have already quit.
* this results in too much confusion, so we just dont support that.
* the session manager can check, if the saved state is different from a previous
* save, and just remove the saved stuff.
*
* (an inquiry function, whether a quit is ok, followed by a quit event
* would have a race)
*/
enum JackSessionEventType {
JackSessionSave = 1,
JackSessionSaveAndQuit = 2,
JackSessionSaveTemplate = 3
};

typedef enum JackSessionEventType jack_session_event_type_t;

enum JackSessionFlags {
/**
* an error occured while saving.
*/
JackSessionSaveError = 0x01,

/**
* this reply indicates that a client is part of a multiclient application.
* the command reply is left empty. but the session manager should still
* consider this client part of a session. it will come up due to invocation of another
* client.
*/
JackSessionChildClient = 0x02
};

typedef enum JackSessionFlags jack_session_flags_t;

struct _jack_session_event {
/**
* the actual type of this session event.
*/
jack_session_event_type_t type;

/**
* session_directory with trailing separator
* this is per client. so the client can do whatever it likes in here.
*/
const char *session_dir;

/**
* client_uuid which must be specified to jack_client_open on session reload.
* client can specify it in the returned commandline as an option, or just save it
* with the state file.
*/
const char *client_uuid;

/**
* the command_line is the reply of the client.
* it specifies in a platform dependent way, how the client must be restarted upon session reload.
*
* probably it should contain ${SESSION_DIR} instead of the actual session dir.
* this would basically make the session dir moveable.
*
* ownership of the memory is handed to jack.
* initially set to NULL by jack;
*/
char *command_line;

/**
* flags to be set by the client. normally left 0.
*/
jack_session_flags_t flags;
};

typedef struct _jack_session_event jack_session_event_t;

/**
* Prototype for the client supplied function that is called
* whenever a session notification is sent via jack_session_notify().
*
* The session_id must be passed to jack_client_open on session reload (this can be
* done by specifying it somehow on the returned command line).
*
* @param event the event_structure.
* @param arg pointer to a client supplied structure
*/
typedef void (*JackSessionCallback)(jack_session_event_t *event, void *arg);

/**
* Tell the JACK server to call @a save_callback the session handler wants
* to save.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_session_callback(jack_client_t *client,
JackSessionCallback session_callback,
void *arg) JACK_WEAK_EXPORT;

/**
* reply to a session_event
*
* this can either be called directly from the callback, or later from a different thread.
* so its possible to just stick the event pointer into a pipe and execute the save code
* from the gui thread.
*
* @return 0 on success, otherwise a non-zero error code
*/

int jack_session_reply( jack_client_t *client, jack_session_event_t *event ) JACK_WEAK_EXPORT;


/**
* free memory used by a jack_session_event_t
* this also frees the memory used by the command_line pointer.
* if its non NULL.
*/

void jack_session_event_free (jack_session_event_t *event);

/*@}*/


/**
* @defgroup JackSessionManagerAPI this API is intended for a sessionmanager.
* this API could be server specific. if we dont reach consensus here,
* we can just drop it.
* i know its a bit clumsy.
* but this api isnt required to be as stable as the client api.
* @{
*/

typedef struct {
const char *uuid;
const char *client_name;
const char *command;
jack_session_flags_t flags;
} jack_session_command_t;

/**
* send a save or quit event, to all clients listening for session
* callbacks. the returned strings of the clients are accumulated and
* returned as an array of jack_session_command_t.
* its terminated by ret[i].uuid == NULL
* target == NULL means send to all interested clients. otherwise a clientname
*/

jack_session_command_t *jack_session_notify (jack_client_t* client,
const char *target,
jack_session_event_type_t type,
const char *path ) JACK_WEAK_EXPORT;

/**
* free the memory allocated by a session command.
*/

void jack_session_commands_free (jack_session_command_t *cmds) JACK_WEAK_EXPORT;

/**
* get the sessionid for a client name.
* the sessionmanager needs this to reassociate a client_name to the session_id.
*/

char *jack_get_uuid_for_client_name( jack_client_t *client, const char *client_name ) JACK_WEAK_EXPORT;

/**
* get the client name for a session_id.
* in order to snapshot the graph connections, the sessionmanager needs to map
* session_ids to client names.
*/

char *jack_get_client_name_by_uuid( jack_client_t *client, const char *client_uuid ) JACK_WEAK_EXPORT;

/**
* reserve a client name and associate it to a uuid.
* when a client later call jack_client_open() and specifies the uuid,
* jackd will assign the reserved name.
* this allows a session manager to know in advance under which client name
* its managed clients will appear.
*
* @return 0 on success, otherwise a non-zero error code
*/

int
jack_reserve_client_name( jack_client_t *client, const char *name, const char *uuid ) JACK_WEAK_EXPORT;

#ifdef __cplusplus
}
#endif
#endif

+ 13
- 8
jack/types.h View File

@@ -113,11 +113,16 @@ enum JackOptions {
* Pass optional <em>(char *) load_init</em> string to the
* jack_initialize() entry point of an internal client.
*/
JackLoadInit = 0x10
JackLoadInit = 0x10,

/**
* pass a SessionID Token this allows the sessionmanager to identify the client again.
*/
JackSessionID = 0x20
};

/** Valid options for opening an external client. */
#define JackOpenOptions (JackServerName|JackNoStartServer|JackUseExactName)
#define JackOpenOptions (JackSessionID|JackServerName|JackNoStartServer|JackUseExactName)

/** Valid options for loading an internal client. */
#define JackLoadOptions (JackLoadInit|JackLoadName|JackUseExactName)
@@ -331,14 +336,14 @@ typedef void (*JackPortConnectCallback)(jack_port_id_t a, jack_port_id_t b, int
*
* @param starting non-zero if we start starting to freewheel, zero otherwise
* @param arg pointer to a client supplied structure
*/
*/
typedef void (*JackFreewheelCallback)(int starting, void *arg);

typedef void *(*JackThreadCallback)(void* arg);

/**
* Prototype for the client supplied function that is called
* whenever jackd is shutdown. Note that after server shutdown,
* whenever jackd is shutdown. Note that after server shutdown,
* the client pointer is *not* deallocated by libjack,
* the application is responsible to properly use jack_client_close()
* to release client ressources. Warning: jack_client_close() cannot be
@@ -351,15 +356,15 @@ typedef void (*JackShutdownCallback)(void *arg);

/**
* Prototype for the client supplied function that is called
* whenever jackd is shutdown. Note that after server shutdown,
* whenever jackd is shutdown. Note that after server shutdown,
* the client pointer is *not* deallocated by libjack,
* the application is responsible to properly use jack_client_close()
* to release client ressources. Warning: jack_client_close() cannot be
* safely used inside the shutdown callback and has to be called outside of
* the callback context.
* @param code a shuntdown code
* @param reason a string discribing the shuntdown reason (backend failure, server crash... etc...)
*
* @param code a shutdown code
* @param reason a string describing the shutdown reason (backend failure, server crash... etc...)
* @param arg pointer to a client supplied structure
*/
typedef void (*JackInfoShutdownCallback)(jack_status_t code, const char* reason, void *arg);


+ 3
- 0
jack/varargs.h View File

@@ -29,6 +29,7 @@ typedef struct {
char *server_name; /* server name */
char *load_name; /* load module name */
char *load_init; /* initialization string */
char *sess_uuid;
} jack_varargs_t;

static inline void
@@ -53,6 +54,8 @@ jack_varargs_parse (jack_options_t options, va_list ap, jack_varargs_t *va)
va->load_name = va_arg(ap, char *);
if ((options & JackLoadInit))
va->load_init = va_arg(ap, char *);
if ((options & JackSessionID))
va->sess_uuid = va_arg(ap, char *);
}

#ifdef __cplusplus


+ 89
- 10
jackd/clientengine.c View File

@@ -109,6 +109,7 @@ static void
jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
{
JSList *node;
jack_client_id_t finalizer=0;

/* caller must write-hold the client lock */

@@ -120,6 +121,20 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
jack_zombify_client (engine, client);
}

if (client->session_reply_pending) {
engine->session_pending_replies -= 1;

if (engine->session_pending_replies == 0) {
if (write (engine->session_reply_fd, &finalizer, sizeof (finalizer))
< (ssize_t) sizeof (finalizer)) {
jack_error ("cannot write SessionNotify result "
"to client via fd = %d (%s)",
engine->session_reply_fd, strerror (errno));
}
engine->session_reply_fd = -1;
}
}

if (client->control->type == ClientExternal) {

/* try to force the server thread to return from poll */
@@ -342,7 +357,7 @@ jack_client_unload (jack_client_internal_t *client)
}
}

static jack_client_internal_t *
jack_client_internal_t *
jack_client_by_name (jack_engine_t *engine, const char *name)
{
jack_client_internal_t *client = NULL;
@@ -406,6 +421,18 @@ jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id)
return client;
}

int
jack_client_name_reserved( jack_engine_t *engine, const char *name )
{
JSList *node;
for (node = engine->reserved_client_names; node; node = jack_slist_next (node)) {
jack_reserved_name_t *reservation = (jack_reserved_name_t *) node->data;
if( !strcmp( reservation->name, name ) )
return 1;
}
return 0;
}

/* generate a unique client name
*
* returns 0 if successful, updates name in place
@@ -428,7 +455,7 @@ jack_generate_unique_name (jack_engine_t *engine, char *name)
name[tens] = '0';
name[ones] = '1';
name[length] = '\0';
while (jack_client_by_name (engine, name)) {
while (jack_client_by_name (engine, name) || jack_client_name_reserved( engine, name )) {
if (name[ones] == '9') {
if (name[tens] == '9') {
jack_error ("client %s has 99 extra"
@@ -456,7 +483,7 @@ jack_client_name_invalid (jack_engine_t *engine, char *name,
* startup. There are no other clients at that point, anyway.
*/

if (jack_client_by_name (engine, name)) {
if (jack_client_by_name (engine, name) || jack_client_name_reserved(engine, name )) {

*status |= JackNameNotUnique;

@@ -480,7 +507,7 @@ jack_client_name_invalid (jack_engine_t *engine, char *name,
* internal and external clients. */
static jack_client_internal_t *
jack_setup_client_control (jack_engine_t *engine, int fd,
ClientType type, const char *name)
ClientType type, const char *name, jack_client_id_t uuid)
{
jack_client_internal_t *client;

@@ -530,10 +557,13 @@ jack_setup_client_control (jack_engine_t *engine, int fd,
client->control->dead = FALSE;
client->control->timed_out = 0;
client->control->id = engine->next_client_id++;
client->control->uid = uuid;
strcpy ((char *) client->control->name, name);
client->subgraph_start_fd = -1;
client->subgraph_wait_fd = -1;

client->session_reply_pending = FALSE;

client->control->process_cbset = FALSE;
client->control->bufsize_cbset = FALSE;
client->control->srate_cbset = FALSE;
@@ -543,6 +573,7 @@ jack_setup_client_control (jack_engine_t *engine, int fd,
client->control->graph_order_cbset = FALSE;
client->control->client_register_cbset = FALSE;
client->control->thread_cb_cbset = FALSE;
client->control->session_cbset = FALSE;

#if 0
if (type != ClientExternal) {
@@ -578,9 +609,23 @@ jack_setup_client_control (jack_engine_t *engine, int fd,
return client;
}

static void
jack_ensure_uuid_unique (jack_engine_t *engine, jack_client_id_t uuid)
{
JSList *node;

jack_lock_graph (engine);
for (node=engine->clients; node; node=jack_slist_next (node)) {
jack_client_internal_t *client = (jack_client_internal_t *) node->data;
if (client->control->uid == uuid)
client->control->uid = 0;
}
jack_unlock_graph (engine);
}

/* set up all types of clients */
static jack_client_internal_t *
setup_client (jack_engine_t *engine, ClientType type, char *name,
setup_client (jack_engine_t *engine, ClientType type, char *name, jack_client_id_t uuid,
jack_options_t options, jack_status_t *status, int client_fd,
const char *object_path, const char *object_data)
{
@@ -591,9 +636,12 @@ setup_client (jack_engine_t *engine, ClientType type, char *name,
if (jack_client_name_invalid (engine, name, options, status))
return NULL;

if (uuid != 0)
jack_ensure_uuid_unique (engine, uuid);

/* create a client struct for this name */
if ((client = jack_setup_client_control (engine, client_fd,
type, name)) == NULL) {
type, name, uuid )) == NULL) {
*status |= (JackFailure|JackInitFailure);
jack_error ("cannot create new client object");
return NULL;
@@ -687,7 +735,7 @@ jack_create_driver_client (jack_engine_t *engine, char *name)
snprintf (req.name, sizeof (req.name), "%s", name);

pthread_mutex_lock (&engine->request_lock);
client = setup_client (engine, ClientDriver, name, JackUseExactName,
client = setup_client (engine, ClientDriver, name, 0, JackUseExactName,
&status, -1, NULL, NULL);
pthread_mutex_unlock (&engine->request_lock);

@@ -715,6 +763,22 @@ handle_unload_client (jack_engine_t *engine, jack_client_id_t id)
return status;
}

static char *
jack_get_reserved_name( jack_engine_t *engine, jack_client_id_t uuid )
{
JSList *node;
for (node = engine->reserved_client_names; node; node = jack_slist_next (node)) {
jack_reserved_name_t *reservation = (jack_reserved_name_t *) node->data;
if( reservation->uuid== uuid ) {
char *retval = strdup( reservation->name );
free( reservation );
engine->reserved_client_names =
jack_slist_remove( engine->reserved_client_names, reservation );
return retval;
}
}
return 0;
}
int
jack_client_create (jack_engine_t *engine, int client_fd)
{
@@ -762,7 +826,14 @@ jack_client_create (jack_engine_t *engine, int client_fd)
}
pthread_mutex_lock (&engine->request_lock);
client = setup_client (engine, req.type, req.name,
if( req.uuid ) {
char *res_name = jack_get_reserved_name( engine, req.uuid );
if( res_name ) {
snprintf( req.name, sizeof(req.name), "%s", res_name );
free(res_name);
}
}
client = setup_client (engine, req.type, req.name, req.uuid,
req.options, &res.status, client_fd,
req.object_path, req.object_data);
pthread_mutex_unlock (&engine->request_lock);
@@ -818,7 +889,7 @@ int
jack_client_activate (jack_engine_t *engine, jack_client_id_t id)
{
jack_client_internal_t *client;
JSList *node;
JSList *node, *node2;
int ret = -1;

jack_lock_graph (engine);
@@ -843,11 +914,18 @@ jack_client_activate (jack_engine_t *engine, jack_client_id_t id)
++engine->external_client_cnt);
jack_sort_graph (engine);

// send delayed notifications for ports.
for (node2 = client->ports; node2; node2 = jack_slist_next (node2)) {
jack_port_internal_t *port = (jack_port_internal_t *) node2->data;
jack_port_registration_notify (engine, port->shared->id, TRUE);
}

ret = 0;
break;
}
}


jack_unlock_graph (engine);
return ret;
}
@@ -886,6 +964,7 @@ jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id)
return ret;
}


int
jack_mark_client_socket_error (jack_engine_t *engine, int fd)
{
@@ -968,7 +1047,7 @@ jack_intclient_load_request (jack_engine_t *engine, jack_request_t *req)
req->x.intclient.path, req->x.intclient.init,
req->x.intclient.options);

client = setup_client (engine, ClientInternal, req->x.intclient.name,
client = setup_client (engine, ClientInternal, req->x.intclient.name, 0,
req->x.intclient.options, &status, -1,
req->x.intclient.path, req->x.intclient.init);



+ 236
- 6
jackd/engine.c View File

@@ -134,7 +134,12 @@ static void jack_check_acyclic (jack_engine_t* engine);
static void jack_compute_all_port_total_latencies (jack_engine_t *engine);
static void jack_compute_port_total_latency (jack_engine_t *engine, jack_port_shared_t*);
static void jack_engine_signal_problems (jack_engine_t* engine);
static int jack_do_session_notify (jack_engine_t *engine, jack_request_t *req, int reply_fd );
static int jack_check_client_status (jack_engine_t* engine);
static void jack_do_get_client_by_uuid ( jack_engine_t *engine, jack_request_t *req);
static void jack_do_reserve_name ( jack_engine_t *engine, jack_request_t *req);
static void jack_do_session_reply (jack_engine_t *engine, jack_request_t *req );


static inline int
jack_rolling_interval (jack_time_t period_usecs)
@@ -1353,6 +1358,31 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd)
req->status = 0;
break;

case GetClientByUUID:
jack_rdlock_graph (engine);
jack_do_get_client_by_uuid (engine, req);
jack_unlock_graph (engine);
break;
case ReserveName:
jack_rdlock_graph (engine);
jack_do_reserve_name (engine, req);
jack_unlock_graph (engine);
break;
case SessionReply:
jack_rdlock_graph (engine);
jack_do_session_reply (engine, req);
jack_unlock_graph (engine);
break;
case SessionNotify:
jack_rdlock_graph (engine);
if ((req->status =
jack_do_session_notify (engine, req, *reply_fd))
>= 0) {
/* we have already replied, don't do it again */
*reply_fd = -1;
}
jack_unlock_graph (engine);
break;
default:
/* some requests are handled entirely on the client
* side, by adjusting the shared memory area(s) */
@@ -1757,6 +1787,9 @@ jack_engine_new (int realtime, int rtpriority, int do_mlock, int do_unlock,
engine->nozombies = nozombies;
engine->removing_clients = 0;

engine->session_reply_fd = -1;
engine->session_pending_replies = 0;

engine->audio_out_cnt = 0;
engine->audio_in_cnt = 0;
engine->midi_out_cnt = 0;
@@ -1771,6 +1804,7 @@ jack_engine_new (int realtime, int rtpriority, int do_mlock, int do_unlock,
pthread_mutex_init (&engine->problem_lock, 0);

engine->clients = 0;
engine->reserved_client_names = 0;

engine->pfd_size = 0;
engine->pfd_max = 0;
@@ -2492,6 +2526,201 @@ jack_deliver_event_to_all (jack_engine_t *engine, jack_event_t *event)
jack_unlock_graph (engine);
}

static jack_client_id_t jack_engine_get_max_uuid( jack_engine_t *engine )
{
JSList *node;
jack_client_id_t retval = 0;
for (node = engine->clients; node; node = jack_slist_next (node)) {
jack_client_internal_t* client = (jack_client_internal_t*) node->data;
if( client->control->uid > retval )
retval = client->control->uid;
}
return retval;
}

static void jack_do_get_client_by_uuid ( jack_engine_t *engine, jack_request_t *req)
{
JSList *node;
req->status = -1;
for (node = engine->clients; node; node = jack_slist_next (node)) {
jack_client_internal_t* client = (jack_client_internal_t*) node->data;
if( client->control->uid == req->x.client_id ) {
snprintf( req->x.port_info.name, sizeof(req->x.port_info.name), "%s", client->control->name );
req->status = 0;
return;
}
}
}

static void jack_do_reserve_name ( jack_engine_t *engine, jack_request_t *req)
{
jack_reserved_name_t *reservation;
JSList *node;
// check is name is free...
for (node = engine->clients; node; node = jack_slist_next (node)) {
jack_client_internal_t* client = (jack_client_internal_t*) node->data;
if( !strcmp( (char *)client->control->name, req->x.reservename.name )) {
req->status = -1;
return;
}
}

reservation = malloc( sizeof( jack_reserved_name_t ) );
if( reservation == NULL ) {
req->status = -1;
return;
}

snprintf( reservation->name, sizeof( reservation->name ), "%s", req->x.reservename.name );
reservation->uuid = req->x.reservename.uuid;
engine->reserved_client_names = jack_slist_append( engine->reserved_client_names, reservation );

req->status = 0;
}

static int jack_send_session_reply ( jack_engine_t *engine, jack_client_internal_t *client )
{
if (write (engine->session_reply_fd, (const void *) &client->control->uid, sizeof (client->control->uid))
< (ssize_t) sizeof (client->control->uid)) {
jack_error ("cannot write SessionNotify result "
"to client via fd = %d (%s)",
engine->session_reply_fd, strerror (errno));
return -1;
}
if (write (engine->session_reply_fd, (const void *) client->control->name, sizeof (client->control->name))
< (ssize_t) sizeof (client->control->name)) {
jack_error ("cannot write SessionNotify result "
"to client via fd = %d (%s)",
engine->session_reply_fd, strerror (errno));
return -1;
}
if (write (engine->session_reply_fd, (const void *) client->control->session_command,
sizeof (client->control->session_command))
< (ssize_t) sizeof (client->control->session_command)) {
jack_error ("cannot write SessionNotify result "
"to client via fd = %d (%s)",
engine->session_reply_fd, strerror (errno));
return -1;
}
if (write (engine->session_reply_fd, (const void *) ( & client->control->session_flags ),
sizeof (client->control->session_flags))
< (ssize_t) sizeof (client->control->session_flags)) {
jack_error ("cannot write SessionNotify result "
"to client via fd = %d (%s)",
engine->session_reply_fd, strerror (errno));
return -1;
}

return 0;
}

static int
jack_do_session_notify (jack_engine_t *engine, jack_request_t *req, int reply_fd )
{
JSList *node;
jack_event_t event;
int reply;
jack_client_id_t finalizer=0;

if (engine->session_reply_fd != -1) {
// we should have a notion of busy or somthing.
// just sending empty reply now.
goto send_final;
}

engine->session_reply_fd = reply_fd;
engine->session_pending_replies = 0;

event.type = SaveSession;
event.y.n = req->x.session.type;
/* GRAPH MUST BE LOCKED : see callers of jack_send_connection_notification()
*/

for (node = engine->clients; node; node = jack_slist_next (node)) {
jack_client_internal_t* client = (jack_client_internal_t*) node->data;
if (client->control->session_cbset) {
if( client->control->uid == 0 ) {
client->control->uid=jack_engine_get_max_uuid( engine ) + 1;
}

// in case we only want to send to a special client.
// uuid assign is still complete. not sure if thats necessary.
if( (req->x.session.target[0] != 0) && strcmp(req->x.session.target, (char *)client->control->name) )
continue;

snprintf (event.x.name, sizeof (event.x.name), "%s%s/", req->x.session.path, client->control->name );
mkdir (event.x.name, 0777 );
reply = jack_deliver_event (engine, client, &event);

if (reply == 1) {
// delayed reply
engine->session_pending_replies += 1;
client->session_reply_pending = TRUE;
} else if (reply == 2) {
// immediate reply
if (jack_send_session_reply (engine, client))
goto error_out;
}
}
}

if (engine->session_pending_replies != 0)
return 0;

send_final:
if (write (reply_fd, &finalizer, sizeof (finalizer))
< (ssize_t) sizeof (finalizer)) {
jack_error ("cannot write SessionNotify result "
"to client via fd = %d (%s)",
reply_fd, strerror (errno));
goto error_out;
}

engine->session_reply_fd = -1;
return 0;
error_out:
return -3;
}

static void jack_do_session_reply (jack_engine_t *engine, jack_request_t *req )
{
jack_client_id_t client_id = req->x.client_id;
jack_client_internal_t *client = jack_client_internal_by_id (engine, client_id);
jack_client_id_t finalizer=0;

req->status = 0;

client->session_reply_pending = 0;

if (engine->session_reply_fd == -1) {
jack_error ("spurious Session Reply");
return;
}

engine->session_pending_replies -= 1;

if (jack_send_session_reply (engine, client)) {
// maybe need to fix all client pendings.
// but we will just get a set of spurious replies now.
engine->session_reply_fd = -1;
return;
}

if (engine->session_pending_replies == 0) {
if (write (engine->session_reply_fd, &finalizer, sizeof (finalizer))
< (ssize_t) sizeof (finalizer)) {
jack_error ("cannot write SessionNotify result "
"to client via fd = %d (%s)",
engine->session_reply_fd, strerror (errno));
req->status = -1;
}
engine->session_reply_fd = -1;
}
}

static void
jack_notify_all_port_interested_clients (jack_engine_t *engine, jack_client_id_t src, jack_client_id_t dst, jack_port_id_t a, jack_port_id_t b, int connected)
{
@@ -2522,7 +2751,7 @@ static int
jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
jack_event_t *event)
{
char status;
char status=0;

/* caller must hold the graph lock */

@@ -2607,7 +2836,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
}

if (client->error) {
status = 1;
status = -1;
} else {
// then we check whether there really is an error.... :)
@@ -2672,7 +2901,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
client->event_fd,
pfd[0].revents,
poll_timeout);
status = 1;
status = -2;
#ifdef __linux
}
#endif
@@ -2697,7 +2926,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
event->type);
}

if (status) {
if (status<0) {
client->error += JACK_ERROR_WITH_SOCKETS;
jack_engine_signal_problems (engine);
}
@@ -2705,7 +2934,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client,
}
DEBUG ("event delivered");

return 0;
return status;
}

int
@@ -3919,7 +4148,8 @@ next:
}

client->ports = jack_slist_prepend (client->ports, port);
jack_port_registration_notify (engine, port_id, TRUE);
if( client->control->active )
jack_port_registration_notify (engine, port_id, TRUE);
jack_unlock_graph (engine);

VERBOSE (engine, "registered port %s, offset = %u",


+ 225
- 2
libjack/client.c View File

@@ -514,6 +514,32 @@ jack_client_handle_port_connection (jack_client_t *client, jack_event_t *event)
return 0;
}

int
jack_client_handle_session_callback (jack_client_t *client, jack_event_t *event)
{
char prefix[32];
jack_session_event_t *s_event;

if (! client->control->session_cbset) {
return -1;
}

snprintf( prefix, sizeof(prefix), "%d", client->control->uid );

s_event = malloc( sizeof(jack_session_event_t) );
s_event->type = event->y.n;
s_event->session_dir = strdup( event->x.name );
s_event->client_uuid = strdup( prefix );
s_event->command_line = NULL;

client->session_cb_immediate_reply = 0;
client->session_cb ( s_event, client->session_cb_arg);

if (client->session_cb_immediate_reply) {
return 2;
}
return 1;
}
#if JACK_USE_MACH_THREADS

static int
@@ -874,6 +900,10 @@ jack_request_client (ClientType type,

/* format connection request */

if( va->sess_uuid )
req.uuid = atoi( va->sess_uuid );
else
req.uuid = 0;
req.protocol_v = jack_protocol_version;
req.load = TRUE;
req.type = type;
@@ -1116,6 +1146,11 @@ jack_client_open_aux (const char *client_name,
client->deliver_request = oop_client_deliver_request;
client->deliver_arg = client;

if( va.sess_uuid )
client->control->uid = atoi( va.sess_uuid );
else
client->control->uid = 0U;

if ((ev_fd = server_event_connect (client, va.server_name)) < 0) {
goto fail;
}
@@ -1302,6 +1337,144 @@ jack_set_freewheel (jack_client_t* client, int onoff)
return jack_client_deliver_request (client, &request);
}

int
jack_session_reply (jack_client_t *client, jack_session_event_t *event )
{
int retval = 0;

if (event->command_line) {
snprintf ((char *)client->control->session_command,
sizeof(client->control->session_command),
"%s", event->command_line);
client->control->session_flags = event->flags;

} else {
retval = -1;
}

if (pthread_self() == client->thread_id) {
client->session_cb_immediate_reply = 1;
} else {
jack_request_t request;
request.type = SessionReply;
request.x.client_id = client->control->id;

retval = jack_client_deliver_request(client, &request);
}

return retval;
}

void
jack_session_event_free (jack_session_event_t *event)
{
if (event->command_line)
free (event->command_line);

free ((char *)event->session_dir);
free ((char *)event->client_uuid);
free (event);
}

void
jack_session_commands_free (jack_session_command_t *cmds)
{
int i=0;
while(1) {
if (cmds[i].client_name)
free ((char *)cmds[i].client_name);
if (cmds[i].command)
free ((char *)cmds[i].command);
if (cmds[i].uuid)
free ((char *)cmds[i].uuid);
else
break;

i += 1;
}

free(cmds);
}

jack_session_command_t *
jack_session_notify (jack_client_t* client, const char *target, jack_session_event_type_t code, const char *path )
{
jack_request_t request;

jack_session_command_t *retval = NULL;
int num_replies = 0;
request.type = SessionNotify;
if( path )
snprintf( request.x.session.path, sizeof( request.x.session.path ), "%s", path );
else
request.x.session.path[0] = '\0';

if( target )
snprintf( request.x.session.target, sizeof( request.x.session.target ), "%s", target );
else
request.x.session.target[0] = '\0';

request.x.session.type = code;
if( (write (client->request_fd, &request, sizeof (request))
!= sizeof (request)) ) {
jack_error ("cannot send request type %d to server",
request.type);
goto out;
}

while( 1 ) {
jack_client_id_t uid;
if (read (client->request_fd, &uid, sizeof (uid)) != sizeof (uid)) {
jack_error ("cannot read result for request type %d from"
" server (%s)", request.type, strerror (errno));
goto out;
}

num_replies += 1;
retval = realloc( retval, (num_replies)*sizeof(jack_session_command_t) );
retval[num_replies-1].client_name = malloc (JACK_CLIENT_NAME_SIZE);
retval[num_replies-1].command = malloc (JACK_PORT_NAME_SIZE);
retval[num_replies-1].uuid = malloc (16);

if ( (retval[num_replies-1].client_name == NULL)
||(retval[num_replies-1].command == NULL)
||(retval[num_replies-1].uuid == NULL) )
goto out;

if( uid == 0 )
break;


if (read (client->request_fd, (char *)retval[num_replies-1].client_name, JACK_CLIENT_NAME_SIZE)
!= JACK_CLIENT_NAME_SIZE) {
jack_error ("cannot read result for request type %d from"
" server (%s)", request.type, strerror (errno));
goto out;
}
if (read (client->request_fd, (char *)retval[num_replies-1].command, JACK_PORT_NAME_SIZE)
!= JACK_PORT_NAME_SIZE) {
jack_error ("cannot read result for request type %d from"
" server (%s)", request.type, strerror (errno));
goto out;
}
if (read (client->request_fd, & retval[num_replies-1].flags, sizeof(retval[num_replies-1].flags) )
!= sizeof(retval[num_replies-1].flags) ) {
jack_error ("cannot read result for request type %d from"
" server (%s)", request.type, strerror (errno));
goto out;
}
snprintf( (char *)retval[num_replies-1].uuid, 16, "%d", uid );
}
free((char *)retval[num_replies-1].uuid);
retval[num_replies-1].uuid = NULL;
return retval;
out:
if( retval )
jack_session_commands_free(retval);
return NULL;
}

void
jack_start_freewheel (jack_client_t* client)
{
@@ -1473,6 +1646,9 @@ jack_client_process_events (jack_client_t* client)
case StopFreewheel:
jack_stop_freewheel (client);
break;
case SaveSession:
status = jack_client_handle_session_callback (client, &event );
break;
}
DEBUG ("client has dealt with the event, writing "
@@ -2005,11 +2181,11 @@ jack_activate (jack_client_t *client)
* usage in jack_start_thread())
*/
char buf[JACK_THREAD_STACK_TOUCH];
//char buf[JACK_THREAD_STACK_TOUCH];
int i;

for (i = 0; i < JACK_THREAD_STACK_TOUCH; i++) {
buf[i] = (char) (i & 0xff);
//buf[i] = (char) (i & 0xff);
}

if (client->control->type == ClientInternal ||
@@ -2449,6 +2625,20 @@ jack_set_process_thread(jack_client_t* client, JackThreadCallback callback, void
return 0;
}

int
jack_set_session_callback(jack_client_t* client, JackSessionCallback callback, void *arg)
{
if (client->control->active) {
jack_error ("You cannot set callbacks on an active client.");
return -1;
}

client->session_cb_arg = arg;
client->session_cb = callback;
client->control->session_cbset = (callback != NULL);
return 0;
}

int
jack_get_process_done_fd (jack_client_t *client)
{
@@ -2469,6 +2659,39 @@ jack_on_info_shutdown (jack_client_t *client, void (*function)(jack_status_t, co
client->on_info_shutdown_arg = arg;
}

char *
jack_get_client_name_by_uuid( jack_client_t *client, const char *uuid )
{
jack_request_t request;

char *end_ptr;
jack_client_id_t uuid_int = strtol( uuid, &end_ptr, 10 );
if( *end_ptr != '\0' )
return NULL;
request.type = GetClientByUUID;
request.x.client_id = uuid_int;
if( jack_client_deliver_request( client, &request ) )
return NULL;

return strdup( request.x.port_info.name );
}

int
jack_reserve_client_name( jack_client_t *client, const char *name, const char *uuid )
{
jack_request_t request;

char *end_ptr;
jack_client_id_t uuid_int = strtol( uuid, &end_ptr, 10 );
if( *end_ptr != '\0' )
return -1;
request.type = ReserveName;
snprintf( request.x.reservename.name, sizeof( request.x.reservename.name ),
"%s", name );
request.x.reservename.uuid = uuid_int;
return jack_client_deliver_request( client, &request );
}

const char **
jack_get_ports (jack_client_t *client,
const char *port_name_pattern,


+ 4
- 1
libjack/local.h View File

@@ -35,6 +35,7 @@ struct _jack_client {
char first_active : 1;
pthread_t thread_id;
char name[JACK_CLIENT_NAME_SIZE];
int session_cb_immediate_reply;

#ifdef JACK_USE_MACH_THREADS
/* specific ressources for server/client real-time thread communication */
@@ -70,8 +71,10 @@ struct _jack_client {
void *freewheel_arg;
JackClientRegistrationCallback client_register;
void *client_register_arg;
JackThreadCallback thread_cb;
JackThreadCallback thread_cb;
void *thread_cb_arg;
JackSessionCallback session_cb;
void *session_cb_arg;

/* external clients: set by libjack
* internal clients: set by engine */


+ 363
- 0
python/libjack.py View File

@@ -0,0 +1,363 @@

from ctypes import *
import string
from Queue import Queue

class jack_client_t(Structure):
pass

class jack_port_t(Structure):
pass

client_p = POINTER(jack_client_t)
port_p = POINTER(jack_port_t)

libjack = cdll.LoadLibrary( "libjack.so" )
client_new = libjack.jack_client_new
client_new.argtypes = [ c_char_p ]
client_new.restype = client_p

client_open = libjack.jack_client_open
client_open.argtypes = [ c_char_p, c_uint, POINTER( c_uint ) ]
client_open.restype = client_p

client_close = libjack.jack_client_close
client_close.argtypes = [ client_p ]
client_close.restype = None

activate = libjack.jack_activate
activate.argtypes = [ client_p ]
activate.restype = None

deactivate = libjack.jack_deactivate
deactivate.argtypes = [ client_p ]
deactivate.restype = None

get_ports = libjack.jack_get_ports
get_ports.argtypes = [ client_p, c_char_p, c_char_p, c_ulong ]
get_ports.restype = POINTER( c_char_p )

port_by_name = libjack.jack_port_by_name
port_by_name.argtypes = [ client_p, c_char_p ]
port_by_name.restype = port_p

port_get_all_connections = libjack.jack_port_get_all_connections
port_get_all_connections.argtypes = [ client_p, port_p ]
port_get_all_connections.restype = POINTER( c_char_p )

jack_free = libjack.jack_free
jack_free.argtypes = [ c_void_p ]
jack_free.restype = None

rename_client = libjack.jack_rename_client
rename_client.argtypes = [ client_p, c_char_p, c_char_p ]
rename_client.restype = c_int

reserve_client_name = libjack.jack_reserve_client_name
reserve_client_name.argtypes = [ client_p, c_char_p, c_char_p ]
reserve_client_name.restype = c_int

class jack_session_command_t( Structure ):
_fields_ = [ ("uuid", c_char_p ), ("clientname", c_char_p), ("command", c_char_p ), ("flags", c_int) ]

session_notify = libjack.jack_session_notify
session_notify.argtypes = [ client_p, c_char_p, c_uint, c_char_p ]
session_notify.restype = POINTER( jack_session_command_t )

session_commands_free = libjack.jack_session_commands_free
session_commands_free.argtypes = [ POINTER( jack_session_command_t ) ]
session_commands_free.restype = None

get_client_name_by_uuid = libjack.jack_get_client_name_by_uuid
get_client_name_by_uuid.argtypes = [ client_p, c_char_p ]
get_client_name_by_uuid.restype = c_char_p

connect = libjack.jack_connect
connect.argtypes = [ client_p, c_char_p, c_char_p ]
connect.restype = c_int

disconnect = libjack.jack_disconnect
disconnect.argtypes = [ client_p, c_char_p, c_char_p ]
disconnect.restype = c_int

PortRegistrationCallback = CFUNCTYPE( None, c_uint, c_int, c_void_p )

set_port_registration_callback = libjack.jack_set_port_registration_callback
set_port_registration_callback.argtypes = [ client_p, PortRegistrationCallback, c_void_p ]
set_port_registration_callback.restype = c_int

port_by_id = libjack.jack_port_by_id
port_by_id.argtypes = [ client_p, c_uint ]
port_by_id.restype = port_p

port_name = libjack.jack_port_name
port_name.argtypes = [ port_p ]
port_name.restype = c_char_p

port_type = libjack.jack_port_type
port_type.argtypes = [ port_p ]
port_type.restype = c_char_p

port_flags = libjack.jack_port_flags
port_flags.argtypes = [ port_p ]
port_flags.restype = c_int

JACK_DEFAULT_AUDIO_TYPE="32 bit float mono audio"
JACK_DEFAULT_MIDI_TYPE="8 bit raw midi"

JackPortIsInput = 0x1
JackPortIsOutput = 0x2
JackPortIsPhysical = 0x4
JackPortCanMonitor = 0x8
JackPortIsTerminal = 0x10

JackSessionSave = 1
JackSessionQuit = 2



class Port( object ):
def __init__( self, client, name ):
self.client = client
self.name = name
self.portname = name.split(':')[1]
self.port_p = port_by_name( self.client, name )
self.conns = self.get_live_connections()

def get_connections( self ):
return self.conns

def get_live_connections( self ):
conns = port_get_all_connections( self.client, self.port_p )
if not conns:
return []

i=0
retval = []
while conns[i]:
retval.append( conns[i] )
i+=1
jack_free(conns)

return retval

def connect( self, other ):
connect( self.client, self.name, other )

def disconnect( self, other ):
disconnect( self.client, self.name, other )

def is_input( self ):
return (port_flags( self.port_p ) & JackPortIsInput) != 0

def is_output( self ):
return (port_flags( self.port_p ) & JackPortIsOutput) != 0

def is_audio( self ):
return (port_type( self.port_p ) == JACK_DEFAULT_AUDIO_TYPE)

def is_midi( self ):
return (port_type( self.port_p ) == JACK_DEFAULT_MIDI_TYPE)


class Client( object ):
def __init__( self, client, name ):
self.client = client
self.name = name
self.ports = []
self.commandline = None
self.isinfra = False
self.uuid = None

def get_commandline( self ):
if self.commandline:
return self.commandline
else:
return ""

def set_commandline( self, cmdline ):
self.commandline = cmdline

def get_uuid( self ):
return self.uuid

def set_uuid( self, uuid ):
self.uuid = uuid

def add_port( self, portname ):
self.ports.append( Port( self.client, portname ) )

def rename( self, newname ):
rename_client( self.client, self.name, newname )
self.ports = []
ports = get_ports( self.client, newname+":.*", None, 0 )
self.name = newname

i=0
while(ports[i]):
self.add_port( ports[i] )
i+=1

jack_free( ports )

def set_infra( self, cmdline ):
self.isinfra = True
self.commandline = cmdline



class JackGraph( object ):
def __init__( self, client, ports, uuids=[] ):
self.client = client
self.clients = {}
self.reserved_names = []

i=0
while(ports[i]):
port_split = ports[i].split(':')
if not self.clients.has_key(port_split[0]):
self.clients[port_split[0]] = Client( client, port_split[0] )

self.clients[port_split[0]].add_port( ports[i] )
i+=1

def get_client( self, name ):
return self.clients[name]

def get_port_list( self ):
retval = []
for c in self.clients.values():
for p in c.ports:
retval.append( p.name )
return retval



def check_client_name( self, client ):
if not client.name in self.reserved_names:
return

oldname = client.name
newname = self.get_free_name( client.name )

client.rename( newname )
del self.clients[oldname]
self.clients[newname] = client

def get_free_name( self, oldname, other_names=[] ):
cname_split = oldname.split('-')
if len(cname_split) == 1:
cname_prefix = cname_split[0]
else:
cname_prefix = string.join( cname_split[:-1], '-' )

num = 1
while ("%s-%d"%(cname_prefix,num)) in (self.clients.keys()+self.reserved_names+other_names):
num+=1

return ("%s-%d"%(cname_prefix,num))



def remove_client( self, name ):
del self.clients[name]
for c in self.clients.values():
for p in c.ports:
for conn in p.get_connections():
if conn.startswith(name+":"):
p.conns.remove( conn )

def remove_client_only( self, name ):
del self.clients[name]

def ensure_clientnames( self, names ):
self.reserved_names = names
for c in self.clients.values():
self.check_client_name( c )

def get_taken_names( self ):
return self.clients.keys() + self.reserved_names

def reserve_name( self, uuid, name ):
if reserve_client_name( self.client, name, uuid ):
raise Exception( "reservation failure" )
self.reserved_names.append( name )


class NotifyReply(object):
def __init__( self, uuid, clientname, commandline ):
self.uuid = uuid
self.clientname = clientname
self.commandline = commandline


class JackClient(object):
def __init__( self, name ):
self.client = client_open( name, 0, None )
if not self.client:
raise Exception( "got no client name" )
self.reg_cb = PortRegistrationCallback( self.port_registration_cb )
set_port_registration_callback( self.client, self.reg_cb, None )
self.port_queue = Queue()

activate( self.client )

def close( self ):
client_close( self.client )

def get_graph( self ):
ports = get_ports( self.client, None, None, 0 )
retval = JackGraph( self.client, ports )
jack_free( ports )
return retval

def rename_client( self, old, new ):
if rename_client( self.client, old, new ):
raise Exception

def port_registration_cb( self, port_id, reg, arg ):
port_p = port_by_id( self.client, port_id )
self.port_queue.put( (port_p,reg) )


def session_save( self, path ):
commands = session_notify( self.client, None, JackSessionSave, path )
i=0
retval = []
while( commands[i].uuid ):
retval.append( NotifyReply( commands[i].uuid, commands[i].clientname, commands[i].command ) )
i+=1
session_commands_free( commands )

return retval

def session_save_and_quit( self, path ):
commands = session_notify( self.client, None, JackSessionQuit, path )
i=0
retval = []
while( commands[i].uuid ):
retval.append( NotifyReply( commands[i].uuid, commands[i].clientname, commands[i].command ) )
i+=1
session_commands_free( commands )

return retval

def connect( self, a, b ):
portA_p = port_by_name( self.client, a )
if( port_flags( portA_p ) & JackPortIsOutput ):
connect( self.client, a, b )
else:
connect( self.client, b, a )









+ 277
- 0
python/sessionmanager.py View File

@@ -0,0 +1,277 @@
#!/usr/bin/env python

import libjack
import state
import subprocess
from optparse import OptionParser
from ConfigParser import SafeConfigParser
import os

try:
import dbus.service
import gobject
have_dbus = True
except:
have_dbus = False


SESSION_PATH=os.path.join(os.getenv("HOME"), "jackSessions")
defaults = { "jackclientname": "sessionmanager", "sessiondir": "~/jackSessions" }

class SessionManager( object ):
def __init__( self ):
self.config = SafeConfigParser( defaults )
self.config.read( os.path.expanduser( "~/.jacksessionrc" ) )
self.jackname = self.config.get( "DEFAULT", "jackclientname" )
self.cl = libjack.JackClient( self.jackname )
if self.config.has_section( "infra" ):
self.infra_clients = {}
for inf in self.config.items( "infra" ):
self.infra_clients[inf[0]] = inf[1]
else:
self.infra_clients = { "a2j": "a2jmidid" }
self.implicit_clients = [ "system" ]

self.sessiondir = os.path.expanduser( self.config.get( "DEFAULT", "sessiondir" ) ) + "/"
if not os.path.exists( self.sessiondir ):
print "Sessiondir %s does not exist. Creating it..."%self.sessiondir
os.mkdir( self.sessiondir )

def list_projects( self ):
files = os.listdir( self.sessiondir )
files = filter( lambda x: os.path.isdir( os.path.join( self.sessiondir, x ) ), files )
return files

def load_session( self, name ):
if not os.path.exists( self.sessiondir+name+"/session.xml" ):
print "Session %s does not exist"%name
return -1

sd = state.SessionDom( self.sessiondir+name+"/session.xml" )

g=self.cl.get_graph()

children = []

for ic in sd.get_infra_clients():
if not ic[0] in g.clients.keys():
children.append( subprocess.Popen( ic[1], shell=True ) )


print sd.get_client_names()

if opt.renames:
g.ensure_clientnames( sd.get_reg_client_names() )
# get graph again... renaming isnt prefect yet.
g=self.cl.get_graph()

# fixup names, doing this unconditionally, because
# a client rename might have failed.
sd.fixup_client_names( g )

# now we have mangled all the names, lets reserve them.
for (uuid, clientname) in sd.get_uuid_client_pairs():
print "reserving name %s"%clientname
g.reserve_name( uuid, clientname )

# build up list of port connections
conns = []
for p in sd.get_port_names():
for c in sd.get_connections_for_port( p ):
conns.append( (p,c) )
print conns

# now fire up the processes
for cname in sd.get_reg_client_names():
cmd = sd.get_commandline_for_client( cname )
children.append( subprocess.Popen( cmd, shell=True ) )

avail_ports = g.get_port_list()
# wait for ports to appear, and connect em.

while len(conns) > 0:
p = self.cl.port_queue.get()
print p[0],p[1]
pname = libjack.port_name(p[0])
for c1 in conns:
if c1[0]==pname:
if c1[1] in avail_ports:
self.cl.connect( pname, c1[1] )

conns.remove( c1 )
if (c1[1],c1[0]) in conns:
conns.remove( (c1[1],c1[0]) )

break

if c1[1]==pname:
if c1[0] in avail_ports:
self.cl.connect( pname, c1[0] )

conns.remove( c1 )
if (c1[1],c1[0]) in conns:
conns.remove( (c1[1],c1[0]) )
break

avail_ports.append( pname )

print "session restored..."
return 0

def quit_session( self, name ):
self.save_session( name, True )


def save_session( self, name, quit=False ):
if os.path.exists( self.sessiondir+name ):
print "session %s already exists"%name
return -1
os.mkdir( self.sessiondir+name )
g=self.cl.get_graph()
if quit:
notify = self.cl.session_save_and_quit( self.sessiondir+name+"/" )
else:
notify = self.cl.session_save( self.sessiondir+name+"/" )

for n in notify:
c = g.get_client( n.clientname )
c.set_commandline( n.commandline )
c.set_uuid( n.uuid )

sd = state.SessionDom()

for c in g.clients.values():
if c.get_commandline() == "":
if not c.name in self.infra_clients.keys()+self.implicit_clients:
g.remove_client( c.name )
elif c.name in self.implicit_clients:
g.remove_client_only( c.name )
else:
c.set_infra( self.infra_clients[c.name] )


for i in g.clients.values():
sd.add_client(i)

f = file( self.sessiondir+name+"/session.xml", "w" )
f.write( sd.get_xml() )
f.close()

print sd.get_xml()
return 0

def exit( self ):
self.cl.close()

if have_dbus:
class DbusSM( dbus.service.Object ):
def __init__( self, sm ):
self.sm = sm
dbus.service.Object.__init__( self, None,
"/org/jackaudio/sessionmanager",
dbus.service.BusName( "org.jackaudio.sessionmanager", bus=dbus.SessionBus() ) )
@dbus.service.method( dbus_interface="org.jackaudio.sessionmanager",
in_signature="s", out_signature="i" )
def save_as( self, name ):
return self.sm.save_session( name )

@dbus.service.method( dbus_interface="org.jackaudio.sessionmanager",
in_signature="s", out_signature="i" )
def quit_as( self, name ):
return self.sm.quit_session( name )

@dbus.service.method( dbus_interface="org.jackaudio.sessionmanager",
in_signature="s", out_signature="i" )
def load( self, name ):
return self.sm.load_session( name )

@dbus.service.method( dbus_interface="org.jackaudio.sessionmanager",
in_signature="", out_signature="as" )
def list( self ):
return self.sm.list_projects()

@dbus.service.method( dbus_interface="org.jackaudio.sessionmanager",
in_signature="", out_signature="" )
def daemon_quit( self ):
loop.quit()

oparser = OptionParser()
oparser.add_option( "--nodbus", action="store_false", dest="dbus", default=have_dbus,
help="Dont use DBUS to issue commands to a running instance" )
oparser.add_option( "--daemon", action="store_true", dest="daemon", default=False,
help="Start in daemon mode, and listen for dbus commands" )
#oparser.add_option( "--save", action="store_true", dest="save", default=False,
# help="Tell SessionManger to save." )
oparser.add_option( "--saveas", action="store", type="string", dest="saveas",
help="Save Session As <name>" )

#oparser.add_option( "--quit", action="store_true", dest="quit", default=False,
# help="Tell SessionManager to Save And Quit" )

oparser.add_option( "--list", action="store_true", dest="list", default=False,
help="List Projects" )
oparser.add_option( "--quitdaemon", action="store_true", dest="quitdaemon", default=False,
help="Tell SessionManager Daemon to Exit" )
oparser.add_option( "--quitas", action="store", dest="quitas", type="string",
help="SaveAs And Quit" )
oparser.add_option( "--load", action="store", dest="load", type="string",
help="Load Session with <name>" )
oparser.add_option( "--renames", action="store_true", dest="renames", default=False,
help="Allow renaming offending clients" )

(opt,args) = oparser.parse_args()

if not opt.dbus:
sm = SessionManager()
try:
if opt.saveas:
sm.save_session( opt.saveas )

if opt.load:
sm.load_session( opt.load )

if opt.quitas:
sm.quit_session( opt.quitas )
except:
sm.exit()
raise

sm.exit()
else:
if opt.daemon:
try:
sm = SessionManager()
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
dbsm = DbusSM( sm )
loop = gobject.MainLoop()
loop.run()
except:
sm.exit()
raise

sm.exit()
else:
session_bus = dbus.SessionBus()
sm_proxy = session_bus.get_object( "org.jackaudio.sessionmanager", "/org/jackaudio/sessionmanager" )
sm_iface = dbus.Interface( sm_proxy, "org.jackaudio.sessionmanager" )
if opt.saveas:
sm_iface.save_as( opt.saveas )
if opt.quitas:
sm_iface.quit_as( opt.quitas )
if opt.load:
sm_iface.load( opt.load )
if opt.list:
projects = sm_iface.list()
for i in projects:
print i

if opt.quitdaemon:
sm_iface.quit()






+ 136
- 0
python/state.py View File

@@ -0,0 +1,136 @@

from xml.dom.minidom import getDOMImplementation, parse, Element
import string

impl = getDOMImplementation()

class SessionDom( object ):
def __init__( self, filename=None ):
if filename:
self.dom = parse( filename )
else:
self.dom = impl.createDocument(None,"jacksession",None)
def add_client( self, client ):
cl_elem = Element( "jackclient" )
cl_elem.setAttribute( "cmdline", client.get_commandline() )
cl_elem.setAttribute( "jackname", client.name )
if client.get_uuid():
cl_elem.setAttribute( "uuid", client.get_uuid() )
if client.isinfra:
cl_elem.setAttribute( "infra", "True" )
else:
cl_elem.setAttribute( "infra", "False" )

for p in client.ports:
po_elem = Element( "port" )
po_elem.setAttribute( "name", p.name )
po_elem.setAttribute( "shortname", p.portname )
for c in p.get_connections():
c_elem = Element( "conn" )
c_elem.setAttribute( "dst", c )

po_elem.appendChild( c_elem )

cl_elem.appendChild( po_elem )
self.dom.documentElement.appendChild( cl_elem )

def get_xml(self):
return self.dom.toprettyxml()

def get_client_names(self):
retval = []
doc = self.dom.documentElement
for c in doc.getElementsByTagName( "jackclient" ):
retval.append( c.getAttribute( "jackname" ) )

return retval

def get_reg_client_names(self):
retval = []
doc = self.dom.documentElement
for c in doc.getElementsByTagName( "jackclient" ):
if c.getAttribute( "infra" ) != "True":
retval.append( c.getAttribute( "jackname" ) )

return retval

def get_infra_clients(self):
retval = []
doc = self.dom.documentElement
for c in doc.getElementsByTagName( "jackclient" ):
if c.getAttribute( "infra" ) == "True":
retval.append( (c.getAttribute( "jackname" ), c.getAttribute( "cmdline" ) ) )

return retval
def get_port_names(self):
retval = []
doc = self.dom.documentElement
for c in doc.getElementsByTagName( "port" ):
retval.append( c.getAttribute( "name" ) )
return retval

def get_connections_for_port( self, portname ):
retval = []
doc = self.dom.documentElement
for c in doc.getElementsByTagName( "port" ):
if c.getAttribute( "name" ) == portname:
for i in c.getElementsByTagName( "conn" ):
retval.append( i.getAttribute( "dst" ) )
return retval

def get_commandline_for_client( self, name ):
doc = self.dom.documentElement
for c in doc.getElementsByTagName( "jackclient" ):
if c.getAttribute( "jackname" ) == name:
return c.getAttribute( "cmdline" )

def get_uuid_client_pairs( self ):
retval = []
doc = self.dom.documentElement
for c in doc.getElementsByTagName( "jackclient" ):
if c.getAttribute( "infra" ) != "True":
retval.append( (c.getAttribute( "uuid" ), c.getAttribute( "jackname" )) )

return retval

def renameclient( self, celem, newname ):
doc = self.dom.documentElement
celem.setAttribute( "jackname", newname )
for pelem in celem.getElementsByTagName( "port" ):
old_pname = pelem.getAttribute( "name" )
pname_split = old_pname.split(":")
pname_split[0] = newname
new_pname = string.join( pname_split, ":" )
pelem.setAttribute( "name", new_pname )
for dst in doc.getElementsByTagName( "conn" ):
if dst.getAttribute( "dst" ) == old_pname:
dst.setAttribute( "dst", new_pname )


def fixup_client_names( self, graph ):
doc = self.dom.documentElement
for c in doc.getElementsByTagName( "jackclient" ):
if c.getAttribute( "infra" ) == "True":
continue
cname = c.getAttribute( "jackname" )
if cname in graph.get_taken_names():
free_name = graph.get_free_name( cname, self.get_reg_client_names() )
print "name taken %s.. reallocate to %s"%(cname, free_name )
self.renameclient( c, free_name )











+ 5
- 0
tools/Makefile.am View File

@@ -38,6 +38,7 @@ bin_PROGRAMS = jack_load \
jack_alias \
jack_bufsize \
jack_samplerate \
jack_session_notify \
jack_wait \
$(JACK_TRANSPORT) \
$(NETJACK_TOOLS)
@@ -96,6 +97,10 @@ jack_samplerate_SOURCES = samplerate.c
jack_samplerate_LDFLAGS = @OS_LDFLAGS@
jack_samplerate_LDADD = $(top_builddir)/libjack/libjack.la

jack_session_notify_SOURCES = session_notify.c
jack_session_notify_LDFLAGS = @OS_LDFLAGS@
jack_session_notify_LDADD = $(top_builddir)/libjack/libjack.la

if HAVE_READLINE
jack_transport_SOURCES = transport.c
jack_transport_LDFLAGS = -lreadline @READLINE_DEPS@ @OS_LDFLAGS@


+ 50
- 5
tools/connect.c View File

@@ -27,6 +27,7 @@
#include <config.h>

#include <jack/jack.h>
#include <jack/session.h>

#define TRUE 1
#define FALSE 0
@@ -49,6 +50,7 @@ show_usage (char *my_name)
fprintf (stderr, "For more information see http://jackaudio.org/\n");
}


int
main (int argc, char *argv[])
{
@@ -63,6 +65,9 @@ main (int argc, char *argv[])
jack_port_t *dst_port = 0;
jack_port_t *port1 = 0;
jack_port_t *port2 = 0;
char portA[300];
char portB[300];
int use_uuid=0;
int connecting, disconnecting;
int port1_flags, port2_flags;
int rc = 1;
@@ -71,16 +76,20 @@ main (int argc, char *argv[])
{ "server", 1, 0, 's' },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ "uuid", 0, 0, 'u' },
{ 0, 0, 0, 0 }
};

while ((c = getopt_long (argc, argv, "s:hv", long_options, &option_index)) >= 0) {
while ((c = getopt_long (argc, argv, "s:hvu", long_options, &option_index)) >= 0) {
switch (c) {
case 's':
server_name = (char *) malloc (sizeof (char) * strlen(optarg));
strcpy (server_name, optarg);
options |= JackServerName;
break;
case 'u':
use_uuid = 1;
break;
case 'h':
show_usage (my_name);
return 1;
@@ -123,12 +132,48 @@ main (int argc, char *argv[])

/* find the two ports */

if ((port1 = jack_port_by_name(client, argv[argc-1])) == 0) {
fprintf (stderr, "ERROR %s not a valid port\n", argv[argc-1]);
if( use_uuid ) {
char *tmpname;
char *clientname;
char *portname;
tmpname = strdup( argv[argc-1] );
portname = strchr( tmpname, ':' );
portname[0] = '\0';
portname+=1;
clientname = jack_get_client_name_by_uuid( client, tmpname );
if( clientname ) {

snprintf( portA, sizeof(portA), "%s:%s", clientname, portname );
jack_free( clientname );
} else {
snprintf( portA, sizeof(portA), "%s", argv[argc-1] );
}
free( tmpname );

tmpname = strdup( argv[argc-2] );
portname = strchr( tmpname, ':' );
portname[0] = '\0';
portname+=1;
clientname = jack_get_client_name_by_uuid( client, tmpname );
if( clientname ) {
snprintf( portB, sizeof(portB), "%s:%s", clientname, portname );
jack_free( clientname );
} else {
snprintf( portB, sizeof(portB), "%s", argv[argc-2] );
}

free( tmpname );

} else {
snprintf( portA, sizeof(portA), "%s", argv[argc-1] );
snprintf( portB, sizeof(portB), "%s", argv[argc-2] );
}
if ((port1 = jack_port_by_name(client, portA)) == 0) {
fprintf (stderr, "ERROR %s not a valid port\n", portA);
goto exit;
}
if ((port2 = jack_port_by_name(client, argv[argc-2])) == 0) {
fprintf (stderr, "ERROR %s not a valid port\n", argv[argc-2]);
if ((port2 = jack_port_by_name(client, portB)) == 0) {
fprintf (stderr, "ERROR %s not a valid port\n", portB);
goto exit;
}



+ 180
- 0
tools/session_notify.c View File

@@ -0,0 +1,180 @@
/*
* session_notify.c -- ultra minimal session manager
*
* Copyright (C) 2010 Torben Hohn.
*
* 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.
*/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <jack/jack.h>
#include <jack/jslist.h>
#include <jack/transport.h>
#include <jack/session.h>

char *package; /* program name */
jack_client_t *client;

jack_session_event_type_t notify_type;
char *save_path = NULL;

void jack_shutdown(void *arg)
{
fprintf(stderr, "JACK shut down, exiting ...\n");
exit(1);
}

void signal_handler(int sig)
{
jack_client_close(client);
fprintf(stderr, "signal received, exiting ...\n");
exit(0);
}

void parse_arguments(int argc, char *argv[])
{

/* basename $0 */
package = strrchr(argv[0], '/');
if (package == 0)
package = argv[0];
else
package++;

if (argc==2) {
if( !strcmp( argv[1], "quit" ) ) {
notify_type = JackSessionSaveAndQuit;
return;
}
}
if (argc==3) {
if( !strcmp( argv[1], "save" ) ) {
notify_type = JackSessionSave;
save_path = argv[2];
return;
}

}
fprintf(stderr, "usage: %s quit|save [path]\n", package);
exit(9);
}

typedef struct {
char name[32];
char uuid[16];
} uuid_map_t;

JSList *uuid_map = NULL;

void add_uuid_mapping( const char *uuid ) {
char *clientname = jack_get_client_name_by_uuid( client, uuid );
if( !clientname ) {
printf( "error... cant find client for uuid" );
return;
}

uuid_map_t *mapping = malloc( sizeof(uuid_map_t) );
snprintf( mapping->uuid, sizeof(mapping->uuid), "%s", uuid );
snprintf( mapping->name, sizeof(mapping->name), "%s", clientname );
uuid_map = jack_slist_append( uuid_map, mapping );
}

char *map_port_name_to_uuid_port( const char *port_name )
{
JSList *node;
char retval[300];
char *port_component = strchr( port_name,':' );
char *client_component = strdup( port_name );
strchr( client_component, ':' )[0] = '\0';

sprintf( retval, "%s", port_name );

for( node=uuid_map; node; node=jack_slist_next(node) ) {
uuid_map_t *mapping = node->data;
if( !strcmp( mapping->name, client_component ) ) {
sprintf( retval, "%s%s", mapping->uuid, port_component );
break;
}
}

return strdup(retval);
}

int main(int argc, char *argv[])
{
parse_arguments(argc, argv);
jack_session_command_t *retval;
int k,i,j;


/* become a JACK client */
if ((client = jack_client_open(package, JackNullOption, NULL)) == 0) {
fprintf(stderr, "JACK server not running?\n");
exit(1);
}

signal(SIGQUIT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGHUP, signal_handler);
signal(SIGINT, signal_handler);

jack_on_shutdown(client, jack_shutdown, 0);

jack_activate(client);


retval = jack_session_notify( client, NULL, notify_type, save_path );
for(i=0; retval[i].uuid; i++ ) {
printf( "%s &\n", retval[i].command );
add_uuid_mapping(retval[i].uuid);
}

printf( "sleep 10\n" );

for(k=0; retval[k].uuid; k++ ) {

char* port_regexp = alloca( jack_client_name_size()+3 );
char* client_name = jack_get_client_name_by_uuid( client, retval[k].uuid );
snprintf( port_regexp, jack_client_name_size()+3, "%s:.*", client_name );
jack_free(client_name);
const char **ports = jack_get_ports( client, port_regexp, NULL, 0 );
if( !ports ) {
continue;
}
for (i = 0; ports[i]; ++i) {
const char **connections;
if ((connections = jack_port_get_all_connections (client, jack_port_by_name(client, ports[i]))) != 0) {
for (j = 0; connections[j]; j++) {
char *src = map_port_name_to_uuid_port( ports[i] );
char *dst = map_port_name_to_uuid_port( connections[j] );
printf( "jack_connect -u \"%s\" \"%s\"\n", src, dst );
}
jack_free (connections);
}
}
jack_free(ports);

}
jack_session_commands_free(retval);

jack_client_close(client);

return 0;
}

Loading…
Cancel
Save