|
|
@@ -24,6 +24,7 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
#define _GNU_SOURCE |
|
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter" |
|
|
|
|
|
|
|
#include <string.h> |
|
|
|
#include <sys/stat.h> |
|
|
@@ -42,16 +43,19 @@ |
|
|
|
|
|
|
|
#include <lo/lo.h> |
|
|
|
|
|
|
|
static const char **all_ports = NULL; |
|
|
|
#include <pthread.h> |
|
|
|
|
|
|
|
jack_client_t *client; |
|
|
|
|
|
|
|
pthread_mutex_t port_lock; |
|
|
|
|
|
|
|
lo_server losrv; |
|
|
|
lo_address nsm_addr; |
|
|
|
int nsm_is_active; |
|
|
|
|
|
|
|
char *project_file; |
|
|
|
|
|
|
|
#undef VERSION |
|
|
|
#define APP_TITLE "JACKPatch" |
|
|
|
#define VERSION "0.2" |
|
|
|
|
|
|
@@ -60,9 +64,20 @@ struct patch_record { |
|
|
|
char *client; |
|
|
|
char *port; |
|
|
|
} src , dst; |
|
|
|
struct patch_record *next; |
|
|
|
int active; /* true if patch has already been activate (by us) */ |
|
|
|
struct patch_record *next; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
struct port_record { |
|
|
|
char *port; |
|
|
|
int reg; /* true if registered, false if unregistered */ |
|
|
|
struct port_record *next; |
|
|
|
}; |
|
|
|
|
|
|
|
static struct port_record *new_ports = NULL; |
|
|
|
static struct port_record *known_ports = NULL; |
|
|
|
|
|
|
|
static struct patch_record *patch_list = NULL; |
|
|
|
|
|
|
|
/** |
|
|
@@ -76,7 +91,6 @@ print_patch ( struct patch_record *pr, int mode ) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
enqueue ( struct patch_record *p ) |
|
|
|
{ |
|
|
@@ -98,6 +112,71 @@ dequeue ( struct patch_record *pr ) |
|
|
|
free( pr ); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
enqueue_port ( struct port_record **q, const char *port, int reg ) |
|
|
|
{ |
|
|
|
struct port_record *p = malloc( sizeof( struct port_record )); |
|
|
|
|
|
|
|
p->port = strdup( port ); |
|
|
|
p->reg = reg; |
|
|
|
p->next = *q; |
|
|
|
*q = p; |
|
|
|
} |
|
|
|
|
|
|
|
struct port_record * |
|
|
|
dequeue_port ( struct port_record **q ) |
|
|
|
{ |
|
|
|
if ( *q ) |
|
|
|
{ |
|
|
|
struct port_record *p = *q; |
|
|
|
|
|
|
|
*q = p->next; |
|
|
|
|
|
|
|
return p; |
|
|
|
} |
|
|
|
|
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
void enqueue_known_port ( const char *port ) |
|
|
|
{ |
|
|
|
enqueue_port( &known_ports, port, 1 ); |
|
|
|
} |
|
|
|
|
|
|
|
const char * find_known_port ( const char *port ) |
|
|
|
{ |
|
|
|
struct port_record *pr; |
|
|
|
|
|
|
|
for ( pr = known_ports; pr; pr = pr->next ) |
|
|
|
if ( !strcmp( port, pr->port ) ) |
|
|
|
return pr->port; |
|
|
|
|
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
enqueue_new_port ( const char *port, int reg ) |
|
|
|
{ |
|
|
|
pthread_mutex_lock( &port_lock ); |
|
|
|
|
|
|
|
enqueue_port( &new_ports, port, reg ); |
|
|
|
|
|
|
|
pthread_mutex_unlock( &port_lock ); |
|
|
|
} |
|
|
|
|
|
|
|
struct port_record * |
|
|
|
dequeue_new_port ( void ) |
|
|
|
{ |
|
|
|
pthread_mutex_lock( &port_lock ); |
|
|
|
|
|
|
|
struct port_record *p = dequeue_port( &new_ports ); |
|
|
|
|
|
|
|
pthread_mutex_unlock( &port_lock ); |
|
|
|
return p; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int |
|
|
|
process_patch ( const char *patch ) |
|
|
|
{ |
|
|
@@ -175,6 +254,7 @@ process_patch ( const char *patch ) |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
pr->active = 0; |
|
|
|
|
|
|
|
print_patch( pr, 1 ); |
|
|
|
|
|
|
@@ -202,7 +282,6 @@ read_config ( const char *file ) |
|
|
|
{ |
|
|
|
FILE *fp; |
|
|
|
int i = 0; |
|
|
|
struct patch_record *pr; |
|
|
|
|
|
|
|
if ( NULL == ( fp = fopen( file, "r" ) ) ) |
|
|
|
return 0; |
|
|
@@ -212,7 +291,7 @@ read_config ( const char *file ) |
|
|
|
while ( !feof( fp ) && !ferror( fp ) ) |
|
|
|
{ |
|
|
|
int retval; |
|
|
|
int k; |
|
|
|
unsigned int k; |
|
|
|
char buf[BUFSIZ]; |
|
|
|
|
|
|
|
i++; |
|
|
@@ -255,7 +334,7 @@ read_config ( const char *file ) |
|
|
|
|
|
|
|
/* returns 0 if connection failed, true if succeeded. Already connected |
|
|
|
* is not considered failure */ |
|
|
|
int |
|
|
|
void |
|
|
|
connect_path ( struct patch_record *pr ) |
|
|
|
{ |
|
|
|
int r = 0; |
|
|
@@ -266,6 +345,19 @@ connect_path ( struct patch_record *pr ) |
|
|
|
snprintf( srcport, 512, "%s:%s", pr->src.client, pr->src.port ); |
|
|
|
snprintf( dstport, 512, "%s:%s", pr->dst.client, pr->dst.port ); |
|
|
|
|
|
|
|
if ( pr->active ) |
|
|
|
{ |
|
|
|
/* patch is already active, don't bother JACK with it... */ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if ( ! ( find_known_port( srcport ) && find_known_port( dstport ) ) ) |
|
|
|
{ |
|
|
|
/* one of the ports doesn't exist yet... don't attempt |
|
|
|
* connection, jack will just complain. */ |
|
|
|
printf( "Not attempting connection because one of the ports is missing.\n" ); |
|
|
|
} |
|
|
|
|
|
|
|
printf( "Connecting %s |> %s\n", srcport, dstport ); |
|
|
|
|
|
|
|
r = jack_connect( client, srcport, dstport ); |
|
|
@@ -273,110 +365,105 @@ connect_path ( struct patch_record *pr ) |
|
|
|
print_patch( pr, r ); |
|
|
|
|
|
|
|
if ( r == 0 || r == EEXIST ) |
|
|
|
return 1; |
|
|
|
{ |
|
|
|
pr->active = 1; |
|
|
|
return; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
printf( "Error is %i", r ); |
|
|
|
return 0; |
|
|
|
pr->active = 0; |
|
|
|
printf( "Error is %i\n", r ); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int |
|
|
|
activate_patch ( const char *portname ) |
|
|
|
void |
|
|
|
do_for_matching_patches ( const char *portname, void (*func)( struct patch_record * ) ) |
|
|
|
{ |
|
|
|
struct patch_record *pr; |
|
|
|
int r = 0; |
|
|
|
|
|
|
|
char client[512]; |
|
|
|
char port[512]; |
|
|
|
|
|
|
|
sscanf( portname, "%[^:]:%s", client, port ); |
|
|
|
sscanf( portname, "%[^:]:%[^\n]", client, port ); |
|
|
|
|
|
|
|
for ( pr = patch_list; pr; pr = pr->next ) |
|
|
|
{ |
|
|
|
// printf( "checking %s:%s agains %s:%s\n", pr->src.client, pr->src.port, client, port ); |
|
|
|
|
|
|
|
if ( ( !strcmp( client, pr->src.client ) && !strcmp( port, pr->src.port ) ) || |
|
|
|
( !strcmp( client, pr->dst.client ) && !strcmp( port, pr->dst.port ) ) ) |
|
|
|
{ |
|
|
|
return connect_path( pr ); |
|
|
|
func( pr ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Attempt to activate all connections in patch list |
|
|
|
*/ |
|
|
|
void |
|
|
|
activate_all_patches ( void ) |
|
|
|
inactivate_path ( struct patch_record *pr ) |
|
|
|
{ |
|
|
|
struct patch_record *pr; |
|
|
|
pr->active = 0; |
|
|
|
} |
|
|
|
|
|
|
|
for ( pr = patch_list; pr; pr = pr->next ) |
|
|
|
connect_path( pr ); |
|
|
|
void |
|
|
|
inactivate_patch ( const char *portname ) |
|
|
|
{ |
|
|
|
do_for_matching_patches( portname, inactivate_path ); |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
find_port ( const char *portname ) |
|
|
|
void |
|
|
|
activate_patch ( const char *portname ) |
|
|
|
{ |
|
|
|
if ( ! all_ports || ! portname ) |
|
|
|
return 0; |
|
|
|
do_for_matching_patches( portname, connect_path ); |
|
|
|
} |
|
|
|
|
|
|
|
const char **port; |
|
|
|
for ( port = all_ports; *port; port++ ) |
|
|
|
void remove_known_port ( const char *port ) |
|
|
|
{ |
|
|
|
/* remove it from the list of known ports */ |
|
|
|
{ |
|
|
|
if ( 0 == strcmp( *port, portname ) ) |
|
|
|
return 1; |
|
|
|
struct port_record *pr; |
|
|
|
struct port_record *lp = NULL; |
|
|
|
|
|
|
|
for ( pr = known_ports; pr; lp = pr, pr = pr->next ) |
|
|
|
if ( !strcmp( port, pr->port ) ) |
|
|
|
{ |
|
|
|
if ( lp ) |
|
|
|
lp->next = pr->next; |
|
|
|
else |
|
|
|
known_ports = pr->next; |
|
|
|
|
|
|
|
free( pr->port ); |
|
|
|
free( pr ); |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
/* now mark all patches including this port as inactive */ |
|
|
|
inactivate_patch ( port ); |
|
|
|
} |
|
|
|
|
|
|
|
/** called for every new port */ |
|
|
|
int |
|
|
|
handle_new_port ( const char *portname ) |
|
|
|
{ |
|
|
|
printf( "New endpoint '%s' registered.\n", portname ); |
|
|
|
/* this is a new port */ |
|
|
|
return activate_patch( portname ); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Attempt to activate all connections in patch list |
|
|
|
*/ |
|
|
|
void |
|
|
|
wipe_ports ( void ) |
|
|
|
activate_all_patches ( void ) |
|
|
|
{ |
|
|
|
if ( all_ports ) |
|
|
|
free( all_ports ); |
|
|
|
struct patch_record *pr; |
|
|
|
|
|
|
|
all_ports = NULL; |
|
|
|
for ( pr = patch_list; pr; pr = pr->next ) |
|
|
|
connect_path( pr ); |
|
|
|
} |
|
|
|
|
|
|
|
/** called for every new port */ |
|
|
|
void |
|
|
|
check_for_new_ports ( void ) |
|
|
|
handle_new_port ( const char *portname ) |
|
|
|
{ |
|
|
|
const char **port; |
|
|
|
const char **ports = jack_get_ports( client, NULL, NULL, 0 ); |
|
|
|
|
|
|
|
if ( ! ports ) |
|
|
|
{ |
|
|
|
printf( "error, no ports" ); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
for ( port = ports; *port; port++ ) |
|
|
|
if ( ! find_port( *port ) ) |
|
|
|
if ( ! handle_new_port( *port ) ) |
|
|
|
{ |
|
|
|
/* failed to connect. Probably because client is inactive. Mark it as invalid and try again later. */ |
|
|
|
// *port = "RETRY"; |
|
|
|
} |
|
|
|
|
|
|
|
if ( all_ports ) |
|
|
|
free( all_ports ); |
|
|
|
enqueue_known_port( portname ); |
|
|
|
|
|
|
|
all_ports = ports; |
|
|
|
printf( "New endpoint '%s' registered.\n", portname ); |
|
|
|
/* this is a new port */ |
|
|
|
activate_patch( portname ); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
@@ -463,6 +550,9 @@ set_traps ( void ) |
|
|
|
int |
|
|
|
osc_announce_error ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) |
|
|
|
{ |
|
|
|
if ( strcmp( types, "sis" ) ) |
|
|
|
return -1; |
|
|
|
|
|
|
|
if ( strcmp( "/nsm/server/announce", &argv[0]->s ) ) |
|
|
|
return -1; |
|
|
|
|
|
|
@@ -501,7 +591,7 @@ int |
|
|
|
osc_open ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data ) |
|
|
|
{ |
|
|
|
const char *new_path = &argv[0]->s; |
|
|
|
const char *display_name = &argv[1]->s; |
|
|
|
// const char *display_name = &argv[1]->s; |
|
|
|
|
|
|
|
char *new_filename; |
|
|
|
|
|
|
@@ -514,9 +604,9 @@ osc_open ( const char *path, const char *types, lo_arg **argv, int argc, lo_mess |
|
|
|
if ( read_config( new_filename ) ) |
|
|
|
{ |
|
|
|
printf( "Reading patch definitions from: %s\n", new_filename ); |
|
|
|
wipe_ports(); |
|
|
|
check_for_new_ports(); |
|
|
|
// activate_all_patches(); |
|
|
|
/* wipe_ports(); */ |
|
|
|
/* check_for_new_ports(); */ |
|
|
|
activate_all_patches(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
@@ -576,6 +666,33 @@ init_osc ( const char *osc_port ) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
check_for_new_ports ( void ) |
|
|
|
{ |
|
|
|
struct port_record *p = NULL; |
|
|
|
|
|
|
|
while ( ( p = dequeue_new_port() ) ) |
|
|
|
{ |
|
|
|
if ( p->reg ) |
|
|
|
handle_new_port( p->port ); |
|
|
|
else |
|
|
|
remove_known_port( p->port ); |
|
|
|
|
|
|
|
free( p->port ); |
|
|
|
free( p ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
port_registration_callback( jack_port_id_t id, int reg, void *arg ) |
|
|
|
{ |
|
|
|
jack_port_t *p = jack_port_by_id( client, id ); |
|
|
|
|
|
|
|
const char *port = jack_port_name( p ); |
|
|
|
|
|
|
|
enqueue_new_port( port, reg ); |
|
|
|
} |
|
|
|
|
|
|
|
/* */ |
|
|
|
|
|
|
|
int |
|
|
@@ -588,17 +705,19 @@ main ( int argc, char **argv ) |
|
|
|
|
|
|
|
client = jack_client_open( APP_TITLE, JackNullOption, &status ); |
|
|
|
|
|
|
|
jack_set_port_registration_callback( client, port_registration_callback, NULL ); |
|
|
|
|
|
|
|
if ( ! client ) |
|
|
|
{ |
|
|
|
fprintf( stderr, "Could not register JACK client\n" ); |
|
|
|
exit(1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
pthread_mutex_init( &port_lock, NULL ); |
|
|
|
|
|
|
|
jack_activate( client ); |
|
|
|
|
|
|
|
// activate_all_patches(); |
|
|
|
|
|
|
|
set_traps(); |
|
|
|
|
|
|
|
if ( argc > 1 ) |
|
|
@@ -618,8 +737,8 @@ main ( int argc, char **argv ) |
|
|
|
printf( "Monitoring...\n" ); |
|
|
|
for ( ;; ) |
|
|
|
{ |
|
|
|
check_for_new_ports(); |
|
|
|
usleep( 50000 ); |
|
|
|
check_for_new_ports(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|