Browse Source

NSM: For lack of a better place to put it, add 'jackpatch' program to the repository.

tags/non-daw-v1.1.0
Jonathan Moore Liles 13 years ago
parent
commit
677d4976b2
4 changed files with 661 additions and 6 deletions
  1. +2
    -0
      session-manager/Makefile
  2. +1
    -1
      session-manager/configure
  3. +8
    -5
      session-manager/makefile.inc
  4. +650
    -0
      session-manager/src/jackpatch.c

+ 2
- 0
session-manager/Makefile View File

@@ -111,6 +111,7 @@ install: all
@ echo -n "Installing..."
@ install src/nsmd "$(DESTDIR)$(prefix)"/bin/nsmd
@ install src/session-manager "$(DESTDIR)$(prefix)"/bin/non-session-manager
@ install src/jackpatch "$(DESTDIR)$(prefix)"/bin/jackpatch
@ mkdir -p "$(DESTDIR)$(SYSTEM_PATH)"/non-session-manager
@ $(MAKE) -s -C doc install
@ install -d "$(DESTDIR)$(PIXMAP_PATH)/non-session-manager"
@@ -122,6 +123,7 @@ install: all
@ echo "$(DONE)"
ifneq ($(USE_DEBUG),yes)
@ echo -n "Stripping..."
@ strip "$(DESTDIR)$(prefix)"/bin/jackpatch
@ strip "$(DESTDIR)$(prefix)"/bin/nsmd
@ strip "$(DESTDIR)$(prefix)"/bin/non-session-manager
@ echo "$(DONE)"


+ 1
- 1
session-manager/configure View File

@@ -18,7 +18,7 @@ require_FLTK 1.1.7 images
require_command FLUID fluid
require_command ar ar
require_command makedepend makedepend
# require_package JACK 0.103.0 jack
suggest_package JACK 0.103.0 jack
require_package liblo 0.23 liblo
require_package sigcpp 2.0.0 sigc++-2.0
suggest_package XPM 2.0.0 xpm


+ 8
- 5
session-manager/makefile.inc View File

@@ -14,16 +14,19 @@ Session_CFLAGS := $(LIBLO_CFLAGS) $(SIGCPP_CFLAGS) $(XPM_CFLAGS) $(FLTK_CLFAGS)

src/nsmd: src/nsmd.o nonlib/libnonlib.a
@ echo -n Linking session handler.
@ $(CXX) $(CXXFLAGS) $(Session_LIBS) src/nsmd.o -o $@ -Lnonlib -lnonlib -ldl && echo $(DONE)
@ $(CXX) $(CXXFLAGS) $(Session_LIBS) src/nsmd.o -o $@ -Lnonlib -lnonlib && echo $(DONE)

src/session-manager: src/session-manager.o nonlib/libnonlib.a
@ echo -n Linking session handler.
@ $(CXX) $(CXXFLAGS) $(FLTK_LIBS) $(XPM_LIBS) $(Session_LIBS) src/session-manager.o -o $@ -Lnonlib -lnonlib -ldl && echo $(DONE)
@ $(CXX) $(CXXFLAGS) $(FLTK_LIBS) $(XPM_LIBS) $(Session_LIBS) src/session-manager.o -o $@ -Lnonlib -lnonlib && echo $(DONE)

src/send_osc: src/send_osc.o nonlib/libnonlib.a
@ $(CXX) $(CXXFLAGS) $(Session_LIBS) src/send_osc.o -o $@ -Lnonlib -lnonlib -ddl && echo $(DONE)
@ $(CXX) $(CXXFLAGS) $(Session_LIBS) src/send_osc.o -o $@ -Lnonlib -lnonlib && echo $(DONE)

Session: src/send_osc src/nsmd src/session-manager
src/jackpatch: src/jackpatch.o
@ $(CXX) $(CXXFLAGS) $(JACK_LIBS) $(LIBLO_LIBS) src/jackpatch.o -o $@ && echo $(DONE)

Session: src/send_osc src/nsmd src/session-manager src/jackpatch

Session_clean:
rm -f $(Session_OBJS) src/nsmd
rm -f $(Session_OBJS) src/nsmd src/session-manager src/jackpatch

+ 650
- 0
session-manager/src/jackpatch.c View File

@@ -0,0 +1,650 @@

/*******************************************************************************/
/* Copyright (C) 2008 Jonathan Moore Liles */
/* */
/* 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; see the file COPYING. If not,write to the Free Software */
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/*******************************************************************************/

/* jackpatch.c

This program is just like ASSPatch, except that it works with Jack ports (audio and MIDI).

*/

#define _GNU_SOURCE

#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>

#include <jack/jack.h>

#include <lo/lo.h>

static const char **all_ports = NULL;

jack_client_t *client;

lo_server losrv;
lo_address nsm_addr;
int nsm_is_active;

char *project_file;

#define APP_TITLE "JACKPatch"
#define VERSION "0.2"

struct patch_record {
struct {
char *client;
char *port;
} src , dst;
struct patch_record *next;
};

static struct patch_record *patch_list = NULL;

/**
* Pretty-print patch relationship of /pr/
*/
void
print_patch ( struct patch_record *pr, int mode )
{
printf( "%s from '%s:%s' to '%s:%s'\n", mode ? ">>" : "::",
pr->src.client, pr->src.port, pr->dst.client, pr->dst.port );

}


void
enqueue ( struct patch_record *p )
{
p->next = patch_list;
patch_list = p;
}

void
dequeue ( struct patch_record *pr )
{
if ( !pr )
return;

free( pr->src.port );
free( pr->dst.port );
free( pr->src.client );
free( pr->dst.client );

free( pr );
}

int
process_patch ( const char *patch )
{
struct patch_record *pr;
char *leftc, *rightc, *leftp, *rightp;
char dir[3];

int retval;

retval = sscanf( patch, " %a[^:]:%a[^|] |%1[<>|] %a[^:]:%a[^\n]",
&leftc, &leftp, dir, &rightc, &rightp );

if ( retval == EOF )
return -1;

if ( retval != 5 )
return 0;

/* trim space */
int j;
for ( j = strlen( leftp ) - 1; j > 0; j-- )
{
if ( leftp[j] == ' ' || leftp[j] == '\t' )
leftp[j] = 0;
else
break;
}

dir[2] = 0;

pr = malloc( sizeof( struct patch_record ) );

switch ( *dir )
{
case '<':
pr->src.client = rightc;
pr->src.port = rightp;

pr->dst.client = leftc;
pr->dst.port = leftp;

enqueue( pr );
break;
case '>':
pr->src.client = leftc;
pr->src.port = leftp;

pr->dst.client = rightc;
pr->dst.port = rightp;

enqueue( pr );
break;
case '|':
pr->src.client = rightc;
pr->src.port = rightp;

pr->dst.client = leftc;
pr->dst.port = leftp;

enqueue( pr );

pr = malloc( sizeof( struct patch_record ) );

pr->src.client = strdup( leftc );
pr->src.port = strdup( leftp );

pr->dst.client = strdup( rightc );
pr->dst.port = strdup( rightp );

enqueue( pr );
break;
default:
// fprintf( stderr, "Invalid token '|%s' at line %i of %s!", dir, i, file );
free( pr );
return 0;
}


print_patch( pr, 1 );

return 1;
}

void
clear_all_patches ( )
{
struct patch_record *pr;

while ( patch_list )
{
pr = patch_list;
patch_list = pr->next;
dequeue( pr );
}
}

/**
* Crudely parse configuration file named by /file/ using fscanf
*/
int
read_config ( const char *file )
{
FILE *fp;
int i = 0;
struct patch_record *pr;

if ( NULL == ( fp = fopen( file, "r" ) ) )
return 0;

clear_all_patches();

while ( !feof( fp ) && !ferror( fp ) )
{
int retval;
int k;
char buf[BUFSIZ];

i++;

for ( k = 0; k < sizeof( buf ) - 1; k++ )
{
retval = fread( buf + k, 1, 1, fp );
if ( retval != 1 )
break;
if ( buf[k] == '\n' )
{
if ( k == 0 )
continue;
else
break;
}
}

if ( retval == 0 )
break;

retval = process_patch( buf );

if ( retval < 0 )
break;

if ( retval == 0 )
{
printf( "bad line %i.\n", i );
continue;
}
}

fclose( fp );

return 1;
}



/* returns 0 if connection failed, true if succeeded. Already connected
* is not considered failure */
int
connect_path ( struct patch_record *pr )
{
int r = 0;

char srcport[512];
char dstport[512];

snprintf( srcport, 512, "%s:%s", pr->src.client, pr->src.port );
snprintf( dstport, 512, "%s:%s", pr->dst.client, pr->dst.port );

printf( "Connecting %s |> %s\n", srcport, dstport );

r = jack_connect( client, srcport, dstport );

print_patch( pr, r );

if ( r == 0 || r == EEXIST )
return 1;
else
{
printf( "Error is %i", r );
return 0;
}
}


int
activate_patch ( const char *portname )
{
struct patch_record *pr;
int r = 0;

char client[512];
char port[512];

sscanf( portname, "%[^:]:%s", 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 );
}
}

return 0;
}

/**
* Attempt to activate all connections in patch list
*/
void
activate_all_patches ( void )
{
struct patch_record *pr;

for ( pr = patch_list; pr; pr = pr->next )
connect_path( pr );
}

int
find_port ( const char *portname )
{
if ( ! all_ports || ! portname )
return 0;

const char **port;
for ( port = all_ports; *port; port++ )
{
if ( 0 == strcmp( *port, portname ) )
return 1;
}

return 0;
}

/** 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 );
}

void
wipe_ports ( void )
{
if ( all_ports )
free( all_ports );

all_ports = NULL;
}

void
check_for_new_ports ( void )
{
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 );

all_ports = ports;
}

void
snapshot ( const char *file )
{
FILE *fp;

const char **port;
const char **ports = jack_get_ports( client, NULL, NULL, JackPortIsOutput );

if ( ! ports )
return;

if ( NULL == ( fp = fopen( file, "w" ) ) )
{
fprintf( stderr, "Error opening snapshot file for writing" );
return;
}

for ( port = ports; *port; port++ )
{
jack_port_t *p;

p = jack_port_by_name( client, *port );

const char **connections;
const char **connection;

connections = jack_port_get_all_connections( client, p );

if ( ! connections )
continue;

for ( connection = connections; *connection; connection++ )
{
fprintf( fp, "%-40s |> %s\n", *port, *connection );
printf( "++ %s |> %s\n", *port, *connection );
}

free( connections );
}

free( ports );

fclose( fp );
}

static int die_now = 0;

void
signal_handler ( int x )
{
die_now = 1;
}

void
die ( void )
{
jack_deactivate( client );
jack_client_close( client );
client = NULL;
exit( 0 );
}

/** set_traps
*
* Handle signals
*/
void
set_traps ( void )
{
// signal( SIGHUP, signal_handler );
signal( SIGINT, signal_handler );
// signal( SIGQUIT, signal_handler );
// signal( SIGSEGV, signal_handler );
// signal( SIGPIPE, signal_handler );
signal( SIGTERM, signal_handler );
}

/****************/
/* OSC HANDLERS */
/****************/

int
osc_announce_error ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
{
if ( strcmp( "/nsm/server/announce", &argv[0]->s ) )
return -1;

printf( "Failed to register with NSM: %s\n", &argv[2]->s );
nsm_is_active = 0;

return 0;
}


int
osc_announce_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
{
if ( strcmp( "/nsm/server/announce", &argv[0]->s ) )
return -1;

printf( "Successfully registered. NSM says: %s", &argv[1]->s );
nsm_is_active = 1;
nsm_addr = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) ) );
return 0;
}

int
osc_save ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
{
snapshot( project_file );

lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/reply", "ss", path, "OK" );

return 0;
}

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;

char *new_filename;
asprintf( &new_filename, "%s.jackpatch", new_path );

struct stat st;

if ( 0 == stat( new_filename, &st ) )
{
if ( read_config( new_filename ) )
{
printf( "Reading patch definitions from: %s\n", new_filename );
wipe_ports();
check_for_new_ports();
// activate_all_patches();
}
else
{
lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/error", "sis", path, -1, "Could not open file" );
return 0;
}
}
else
{
clear_all_patches();
}

if ( project_file )
free( project_file );
project_file = new_filename;

lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/reply", "ss", path, "OK" );

return 0;
}

void
announce ( const char *nsm_url, const char *client_name, const char *process_name )
{
printf( "Announcing to NSM\n" );

lo_address to = lo_address_new_from_url( nsm_url );

int pid = (int)getpid();

lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii",
client_name,
":switch:",
process_name,
0, /* api_major_version */
8, /* api_minor_version */
pid );

lo_address_free( to );
}

void
init_osc ( const char *osc_port )
{
losrv = lo_server_new( osc_port, NULL );
//error_handler );

char *url = lo_server_get_url(losrv);
printf("OSC: %s\n",url);
free(url);

lo_server_add_method( losrv, "/nsm/client/save", "", osc_save, NULL );
lo_server_add_method( losrv, "/nsm/client/open", "sss", osc_open, NULL );
lo_server_add_method( losrv, "/error", "sis", osc_announce_error, NULL );
lo_server_add_method( losrv, "/reply", "ssss", osc_announce_reply, NULL );
}


/* */

int
main ( int argc, char **argv )
{

/* get_args( argc, argv ); */

jack_status_t status;

client = jack_client_open( APP_TITLE, JackNullOption, &status );

if ( ! client )
{
fprintf( stderr, "Could not register JACK client\n" );
exit(1);
}
jack_activate( client );

// activate_all_patches();

set_traps();

if ( argc > 1 )
{
if ( ! strcmp( argv[1], "--save" ) )
{
if ( argc > 2 )
{
printf( "Saving current graph to: %s\n", argv[2] );
snapshot( argv[2] );
exit(0);
}
}
else
{
read_config( argv[1] );
printf( "Monitoring...\n" );
for ( ;; )
{
check_for_new_ports();
usleep( 50000 );
}
}
}

init_osc( NULL );

const char *nsm_url = getenv( "NSM_URL" );

if ( nsm_url )
{
announce( nsm_url, APP_TITLE, argv[0] );
}
else
{
fprintf( stderr, "Could not register as NSM client.\n" );
exit(1);
}

for ( ;; )
{
lo_server_recv_noblock( losrv, 500 );

check_for_new_ports();

if ( die_now )
die();
}
}

Loading…
Cancel
Save