Browse Source

remove HUP handling, enable port_get_connections() for internal clients, add metering API, more

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@348 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.109.0
pbd 22 years ago
parent
commit
7cb8250ea2
14 changed files with 1063 additions and 936 deletions
  1. +42
    -10
      drivers/alsa/alsa_driver.c
  2. +1
    -1
      example-clients/lsp.c
  3. +4
    -4
      example-clients/simple_client.c
  4. +0
    -1
      jack/engine.h
  5. +3
    -11
      jack/internal.h
  6. +104
    -100
      jack/jack.h
  7. +35
    -20
      jack/port.h
  8. +2
    -2
      jack/types.h
  9. +122
    -159
      jackd/engine.c
  10. +5
    -1
      jackd/jackd.c
  11. +6
    -1
      libjack/Makefile.am
  12. +35
    -626
      libjack/client.c
  13. +32
    -0
      libjack/local.h
  14. +672
    -0
      libjack/port.c

+ 42
- 10
drivers/alsa/alsa_driver.c View File

@@ -1147,7 +1147,8 @@ alsa_driver_process (alsa_driver_t *driver, jack_nframes_t nframes)
/* oh well, the engine can't run, so we'll just throw away this
cycle's data ...
*/
snd_pcm_mmap_commit (driver->capture_handle, capture_offset, contiguous);
if (driver->capture_handle)
snd_pcm_mmap_commit (driver->capture_handle, capture_offset, contiguous);
}

/* Now handle input monitoring */
@@ -1453,19 +1454,50 @@ alsa_driver_new (char *name, char *alsa_device,
driver->clock_sync_listeners = 0;

if (playing) {
if ((err = snd_pcm_open (&driver->playback_handle, alsa_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
jack_error ("ALSA: Cannot open PCM device %s/%s", name, alsa_device);
free (driver);
return 0;
if (snd_pcm_open (&driver->playback_handle, alsa_device, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
driver->playback_handle = NULL;
}
}

if (capturing) {
if ((err = snd_pcm_open (&driver->capture_handle, alsa_device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
snd_pcm_close (driver->playback_handle);
jack_error ("ALSA: Cannot open PCM device %s", name);
free (driver);
return 0;
if (snd_pcm_open (&driver->capture_handle, alsa_device, SND_PCM_STREAM_CAPTURE, 0) < 0) {
driver->capture_handle = NULL;
}
}

if (driver->playback_handle == NULL) {
if (playing) {

/* they asked for playback, but we can't do it */

jack_error ("ALSA: Cannot open PCM device %s for playback. Falling back to capture-only mode",
name);

if (driver->capture_handle == NULL) {
/* can't do anything */
free (driver);
return 0;
}
playing = FALSE;
}
}

if (driver->capture_handle == NULL) {
if (capturing) {

/* they asked for capture, but we can't do it */
jack_error ("ALSA: Cannot open PCM device %s for capture. Falling back to playback-only mode",
name);
if (driver->playback_handle == NULL) {
/* can't do anything */
free (driver);
return 0;
}

capturing = FALSE;
}
}



+ 1
- 1
example-clients/lsp.c View File

@@ -56,7 +56,7 @@ main (int argc, char *argv[])
printf (" %s\n", connections[j]);
}
free (connections);
}
}
}
if (show_latency) {
jack_port_t *port = jack_port_by_name (client, ports[i]);


+ 4
- 4
example-clients/simple_client.c View File

@@ -17,7 +17,7 @@ process (jack_nframes_t nframes, void *arg)
jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) jack_port_get_buffer (input_port, nframes);

memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes);
return 0;
}

@@ -32,7 +32,7 @@ srate (jack_nframes_t nframes, void *arg)
void
error (const char *desc)
{
printf ("JACK experienced an error: %s\n", desc);
fprintf (stderr, "JACK error: %s\n", desc);
}

void
@@ -115,7 +115,7 @@ main (int argc, char *argv[])


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

@@ -126,7 +126,7 @@ main (int argc, char *argv[])
free (ports);
if ((ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == NULL) {
fprintf(stderr, "Cannot find any physical playback ports");
fprintf(stderr, "Cannot find any physical playback ports\n");
exit(1);
}



+ 0
- 1
jack/engine.h View File

@@ -66,7 +66,6 @@ struct _jack_engine {

JSList *clients;
JSList *clients_waiting;
JSList *aliases;

struct _jack_port_internal *internal_ports;



+ 3
- 11
jack/internal.h View File

@@ -259,8 +259,6 @@ typedef enum {
SetClientCapabilities = 9,
GetPortConnections = 10,
GetPortNConnections = 11,
AddAlias = 12,
RemoveAlias = 13,
} RequestType;

struct _jack_request {
@@ -280,21 +278,15 @@ struct _jack_request {
char destination_port[JACK_PORT_NAME_SIZE+1];
} connect;
struct {
char port[JACK_PORT_NAME_SIZE+1];
char alias[JACK_PORT_NAME_SIZE+1];
} alias;
unsigned int nports;
const char **ports;
} port_connections;
jack_client_id_t client_id;
jack_nframes_t nframes;
unsigned int nports;
} x;
int status;
};

typedef struct _jack_port_alias {
char port[JACK_PORT_NAME_SIZE+1];
char alias[JACK_PORT_NAME_SIZE+1];
} jack_port_alias_t;

extern void jack_cleanup_shm ();
extern void jack_cleanup_files ();



+ 104
- 100
jack/jack.h View File

@@ -79,7 +79,7 @@ void jack_internal_client_close (const char *client_name);
*
* NOTE: clients do not need to call this. It exists only
* to help more complex clients understand what is going
* on. If called, it must be called before jack_client_activate().
* on. If called, it should be called before jack_client_activate().
*/
void jack_on_shutdown (jack_client_t *client, void (*function)(void *arg), void *arg);

@@ -101,7 +101,11 @@ void jack_on_shutdown (jack_client_t *client, void (*function)(void *arg), void
int jack_set_process_callback (jack_client_t *, JackProcessCallback process_callback, void *arg);

/**
* <b>Note!</b> This function is deprecated.
* Tell the Jack server to call 'bufsize_callback' whenever the size of the
* the buffer that will be passed to the process callback changes,
* passing 'arg' as the second argument.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_set_buffer_size_callback (jack_client_t *, JackBufferSizeCallback bufsize_callback, void *arg);

@@ -190,6 +194,22 @@ jack_port_t *jack_port_register (jack_client_t *,
*/
int jack_port_unregister (jack_client_t *, jack_port_t *);

/**
* This returns a pointer to the memory area associated with the
* specified port. For an output port, it will be a memory area
* that can be written to; for an input port, it will be an area
* containing the data from the port's connection(s), or
* zero-filled. if there are multiple inbound connections, the data
* will be mixed appropriately.
*
* You may cache the value returned, but only between calls to
* your "blocksize" callback. For this reason alone, you should
* either never cache the return value or ensure you have
* a "blocksize" callback and be sure to invalidate the cached
* address from there.
*/
void *jack_port_get_buffer (jack_port_t *, jack_nframes_t);

/**
* Returns the name of the jack_port_t.
*/
@@ -265,82 +285,6 @@ const char ** jack_port_get_connections (const jack_port_t *port);
*/
const char ** jack_port_get_all_connections (const jack_client_t *client, const jack_port_t *port);

/**
* This modifies a port's name, and may be called at any time.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_port_set_name (jack_port_t *port, const char *name);

/**
* This returns a pointer to the memory area associated with the
* specified port. For an output port, it will be a memory area
* that can be written to; for an input port, it will be an area
* containing the data from the port's connection(s), or
* zero-filled. if there are multiple inbound connections, the data
* will be mixed appropriately.
*
* You may cache the value returned, but only between calls to
* your "blocksize" callback. For this reason alone, you should
* either never cache the return value or ensure you have
* a "blocksize" callback and be sure to invalidate the cached
* address from there.
*/
void *jack_port_get_buffer (jack_port_t *, jack_nframes_t);

/**
* Establishes a connection between two ports.
*
* When a connection exists, data written to the source port will
* be available to be read at the destination port.
*
* @pre The types of both ports must be identical to establish a connection.
* @pre The flags of the source port must include PortIsOutput.
* @pre The flags of the destination port must include PortIsInput.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_connect (jack_client_t *,
const char *source_port,
const char *destination_port);

/**
* Removes a connection between two ports.
*
* @pre The types of both ports must be identical to establish a connection.
* @pre The flags of the source port must include PortIsOutput.
* @pre The flags of the destination port must include PortIsInput.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_disconnect (jack_client_t *,
const char *source_port,
const char *destination_port);

/**
* Performs the exact same function as jack_connect(), but it uses
* port handles rather than names, which avoids the name lookup inherent
* in the name-based version.
*
* It is envisaged that clients connecting their own ports will use these
* two, whereas generic connection clients (e.g. patchbays) will use the
* name-based versions.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_port_connect (jack_client_t *, jack_port_t *src, jack_port_t *dst);

/**
* Performs the exact same function as jack_disconnect(), but it uses
* port handles rather than names, which avoids the name lookup inherent
* in the name-based version.
*
* It is envisaged that clients disconnecting their own ports will use these
* two, whereas generic connection clients (e.g. patchbays) will use the
* name-based versions.
*/
int jack_port_disconnect (jack_client_t *, jack_port_t *);

/**
* A client may call this on a pair of its own ports to
* semi-permanently wire them together. This means that
@@ -415,6 +359,33 @@ jack_nframes_t jack_port_get_total_latency (jack_client_t *, jack_port_t *port);
*/
void jack_port_set_latency (jack_port_t *, jack_nframes_t);

/**
* This modifies a port's name, and may be called at any time.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_port_set_name (jack_port_t *port, const char *name);

/**
*/

double jack_port_get_peak (jack_port_t*, jack_nframes_t);

/**
*/

double jack_port_get_power (jack_port_t*, jack_nframes_t);

/**
*/

void jack_port_set_peak_function (jack_port_t *, double (*func)(jack_port_t*, jack_nframes_t));

/**
*/

void jack_port_set_power_function (jack_port_t *, double (*func)(jack_port_t*, jack_nframes_t));

/**
* If JackPortCanMonitor is set for a port, then these 2 functions will
* turn on/off input monitoring for the port. If JackPortCanMonitor
@@ -447,6 +418,59 @@ int jack_port_ensure_monitor (jack_port_t *port, int onoff);
*/
int jack_port_monitoring_input (jack_port_t *port);

/**
* Establishes a connection between two ports.
*
* When a connection exists, data written to the source port will
* be available to be read at the destination port.
*
* @pre The types of both ports must be identical to establish a connection.
* @pre The flags of the source port must include PortIsOutput.
* @pre The flags of the destination port must include PortIsInput.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_connect (jack_client_t *,
const char *source_port,
const char *destination_port);

/**
* Removes a connection between two ports.
*
* @pre The types of both ports must be identical to establish a connection.
* @pre The flags of the source port must include PortIsOutput.
* @pre The flags of the destination port must include PortIsInput.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_disconnect (jack_client_t *,
const char *source_port,
const char *destination_port);

/**
* Performs the exact same function as jack_connect(), but it uses
* port handles rather than names, which avoids the name lookup inherent
* in the name-based version.
*
* It is envisaged that clients connecting their own ports will use these
* two, whereas generic connection clients (e.g. patchbays) will use the
* name-based versions.
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_port_connect (jack_client_t *, jack_port_t *src, jack_port_t *dst);

/**
* Performs the exact same function as jack_disconnect(), but it uses
* port handles rather than names, which avoids the name lookup inherent
* in the name-based version.
*
* It is envisaged that clients disconnecting their own ports will use these
* two, whereas generic connection clients (e.g. patchbays) will use the
* name-based versions.
*/
int jack_port_disconnect (jack_client_t *, jack_port_t *);

/**
* This returns the sample rate of the jack system, as set by the user when
* jackd was started.
@@ -542,28 +566,6 @@ float jack_cpu_load (jack_client_t *client);
*/
void jack_set_server_dir (const char *path);

/**
* Create an alias for a port. Returns zero if
* successful, non-zero otherwise. After a successful
* return, `alias' may be used to refer to a port
* instead of the port's actual name. the naming
* scheme is "alias:<alias>", so if the port alias
* was "left", and the port name was "foo:out1",
* then "alias:left" will refer to "foo:out1".
*
* @return 0 on success, otherwise a non-zero error code
*/
int jack_add_alias (jack_client_t *, const char *portname, const char *alias);

/**
* Remove `alias' from a JACK system.
*
* @return zero if successful, less than zero if the alias
* did not exist, greater than zero if the alias could not
* be removed.
*/
int jack_remove_alias (jack_client_t *, const char *alias);

/**
* Return the pthread ID of the thread running the JACK client
* side code.
@@ -575,3 +577,5 @@ pthread_t jack_client_thread_id (jack_client_t *);
#endif

#endif /* __jack_h__ */



+ 35
- 20
jack/port.h View File

@@ -33,26 +33,38 @@
typedef unsigned long jack_client_id_t;

typedef struct _jack_port_type_info {
const char type_name[JACK_PORT_TYPE_SIZE]; /* what do you think ? */

void (*mixdown)(jack_port_t *, jack_nframes_t); /* function to mixdown multiple inputs to a buffer. can be
NULL, indicating that multiple input connections
are not legal for this data type.
*/

long buffer_scale_factor; /* If == 1, then a buffer to handle nframes worth of
data is sizeof(jack_default_audio_sample_t) * nframes bytes large.
If anything other than 1, the buffer allocated
for input mixing will be this value times
sizeof (jack_default_audio_sample_t) * nframes bytes in size.

Obviously, for non-audio data types, it may have
a different value.

if < 0, then the value should be ignored, and
port->shared->buffer_size should be used.
*/
const char type_name[JACK_PORT_TYPE_SIZE];

void (*mixdown)(jack_port_t *, jack_nframes_t); /* function to mixdown multiple inputs to a buffer. can be
NULL, indicating that multiple input connections
are not legal for this data type.
*/
double (*peak)(jack_port_t *, jack_nframes_t); /* function to compute a peak value for a buffer. can be
NULL, indicating that the computation has no meaning.
the return value is normalized to a [0..1] range.
*/

double (*power)(jack_port_t *, jack_nframes_t); /* function to compute a power value for a buffer. can be
NULL, indicating that the computation has no meaning.

the return value is normalized to a [0..1] range.
*/
long buffer_scale_factor; /* If == 1, then a buffer to handle nframes worth of
data is sizeof(jack_default_audio_sample_t) * nframes bytes large.
If anything other than 1, the buffer allocated
for input mixing will be this value times
sizeof (jack_default_audio_sample_t) * nframes bytes in size.
Obviously, for non-audio data types, it may have
a different value.
if < 0, then the value should be ignored, and
port->shared->buffer_size should be used.
*/
} jack_port_type_info_t;

/* This is the data structure allocated in shared memory
@@ -74,6 +86,9 @@ typedef struct _jack_port_shared {
volatile jack_nframes_t total_latency;
volatile unsigned char monitor_requests;

double (*peak)(jack_port_t*,jack_nframes_t);
double (*power)(jack_port_t*,jack_nframes_t);

char in_use : 1;
char locked : 1;
struct _jack_port *tied;


+ 2
- 2
jack/types.h View File

@@ -44,8 +44,8 @@ typedef struct _jack_port jack_port_t;
typedef struct _jack_client jack_client_t;

/**
* Ports have unique ids. You will very rarely need to know them, however,
* except in the case of the port registration callback.
* Ports have unique ids. A port registration callback is the only
* place you ever need to know their value.
*/
typedef unsigned long jack_port_id_t;



+ 122
- 159
jackd/engine.c View File

@@ -60,6 +60,8 @@
*/
#define JACKD_SOFT_MODE_TIMEOUT 500

#define JACK_ERROR_WITH_SOCKETS 10000000

typedef struct {

jack_port_internal_t *source;
@@ -98,8 +100,9 @@ typedef struct _jack_driver_info {
static int jack_port_assign_buffer (jack_engine_t *, jack_port_internal_t *);
static jack_port_internal_t *jack_get_port_by_name (jack_engine_t *, const char *name);

static void jack_client_delete (jack_engine_t *, jack_client_internal_t *);
static void jack_zombify_client (jack_engine_t *engine, jack_client_internal_t *client);
static void jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client);
static void jack_client_delete (jack_engine_t *, jack_client_internal_t *);

static jack_client_internal_t *jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_request_t *);
static jack_client_internal_t *jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id);
@@ -113,9 +116,6 @@ static int jack_port_do_connect (jack_engine_t *engine, const char *source_port
static int jack_port_do_disconnect (jack_engine_t *engine, const char *source_port, const char *destination_port);
static int jack_port_do_disconnect_all (jack_engine_t *engine, jack_port_id_t);

static int jack_do_add_alias (jack_engine_t *engine, jack_request_t *);
static int jack_do_remove_alias (jack_engine_t *engine, jack_request_t *);

static int jack_port_do_unregister (jack_engine_t *engine, jack_request_t *);
static int jack_port_do_register (jack_engine_t *engine, jack_request_t *);
static int jack_do_get_port_connections (jack_engine_t *engine, jack_request_t *req, int reply_fd);
@@ -135,8 +135,6 @@ static int jack_engine_post_process (jack_engine_t *);

static int internal_client_request (void*, jack_request_t *);

static const char *jack_lookup_alias (jack_engine_t *engine, const char *alias);

static int jack_use_driver (jack_engine_t *engine, jack_driver_t *driver);

static int *jack_shm_registry;
@@ -333,7 +331,7 @@ jack_cleanup_files ()
while ((dirent = readdir (dir)) != NULL) {
if (strncmp (dirent->d_name, "jack-", 5) == 0 || strncmp (dirent->d_name, "jack_", 5) == 0) {
char fullpath[PATH_MAX+1];
sprintf (fullpath, "%s/%s", jack_server_dir, dirent->d_name);
snprintf (fullpath, sizeof (fullpath), "%s/%s", jack_server_dir, dirent->d_name);
unlink (fullpath);
}
}
@@ -646,7 +644,12 @@ jack_engine_post_process (jack_engine_t *engine)

if (!jack_client_is_internal (client) && ctl->process) {
if (ctl->awake_at != 0 && ctl->state > NotTriggered && ctl->state != Finished && ctl->timed_out++) {
client->error = TRUE;
fprintf (stderr, "client %s error: awake_at = %Lu state = %d timed_out = %d\n",
ctl->name,
ctl->awake_at,
ctl->state,
ctl->timed_out);
client->error++;
}
}

@@ -669,14 +672,31 @@ jack_engine_post_process (jack_engine_t *engine)
client = (jack_client_internal_t *) node->data;
if (client->error) {
if (engine->verbose) {
fprintf (stderr, "removing failed client %s state = %s errors = %d\n",
client->control->name, client_state_names[client->control->state],
client->error);

/* if we have a communication problem with the client,
remove it. otherwise, turn it into a zombie. the client
will/should realize this and will close it sockets.
then we'll end up back here again and will finally
remove the client.
*/

if (client->error >= JACK_ERROR_WITH_SOCKETS) {
if (engine->verbose) {
fprintf (stderr, "removing failed client %s state = %s errors = %d\n",
client->control->name, client_state_names[client->control->state],
client->error);
}
jack_remove_client (engine, (jack_client_internal_t *) node->data);
} else {
if (engine->verbose) {
fprintf (stderr, "zombifying failed client %s state = %s errors = %d\n",
client->control->name, client_state_names[client->control->state],
client->error);
}
jack_zombify_client (engine, (jack_client_internal_t *) node->data);
client->error = 0;
}
jack_remove_client (engine, (jack_client_internal_t *) node->data);

need_sort = TRUE;
}
@@ -1301,7 +1321,7 @@ jack_set_timebase (jack_engine_t *engine, jack_client_id_t client)
}

static int
handle_client_jack_error (jack_engine_t *engine, int fd)
handle_client_socket_error (jack_engine_t *engine, int fd)
{
jack_client_internal_t *client = 0;
JSList *node;
@@ -1316,7 +1336,9 @@ handle_client_jack_error (jack_engine_t *engine, int fd)

if (((jack_client_internal_t *) node->data)->request_fd == fd) {
client = (jack_client_internal_t *) node->data;
client->error++;
if (client->error < JACK_ERROR_WITH_SOCKETS) {
client->error += JACK_ERROR_WITH_SOCKETS;
}
break;
}
}
@@ -1374,24 +1396,12 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd)
case GetPortConnections:
case GetPortNConnections:
if (reply_fd) {
req->status = jack_do_get_port_connections (engine, req, *reply_fd);
if (req->status != 0) {
*reply_fd = -1;
}
} else {
req->status = -1;
if ((req->status = jack_do_get_port_connections (engine, req, *reply_fd)) == 0) {
/* we have already replied, don't do it again */
*reply_fd = -1;
}
break;

case AddAlias:
req->status = jack_do_add_alias (engine, req);
break;

case RemoveAlias:
req->status = jack_do_remove_alias (engine, req);
break;

default:
/* some requests are handled entirely on the client side,
by adjusting the shared memory area(s)
@@ -1557,7 +1567,7 @@ jack_server_thread (void *arg)
}

if (pfd[i].revents & ~POLLIN) {
handle_client_jack_error (engine, pfd[i].fd);
handle_client_socket_error (engine, pfd[i].fd);
} else if (pfd[i].revents & POLLIN) {
if (handle_external_client_request (engine, pfd[i].fd)) {
jack_error ("bad hci\n");
@@ -1608,8 +1618,6 @@ jack_engine_new (int realtime, int rtpriority, int verbose)
pthread_mutex_init (&engine->request_lock, 0);

engine->clients = 0;
engine->aliases = 0;

engine->port_segments = 0;
engine->port_buffer_freelist = 0;

@@ -1902,7 +1910,7 @@ jack_main_thread (void *arg)

if (engine->control->real_time != 0 && engine->spare_usecs && ((WORK_SCALE * engine->spare_usecs) <= delayed_usecs)) {
printf ("delay of %.3f usecs exceeds estimated spare time of %.3f; restart ...\n",
fprintf (stderr, "delay of %.3f usecs exceeds estimated spare time of %.3f; restart ...\n",
delayed_usecs, WORK_SCALE * engine->spare_usecs);

if (++consecutive_excessive_delays > 10) {
@@ -2099,7 +2107,7 @@ jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_req

client->control->type = req->type;
client->control->active = 0;
client->control->dead = 0;
client->control->dead = FALSE;
client->control->timed_out = 0;
client->control->id = engine->next_client_id++;
strcpy ((char *) client->control->name, req->name);
@@ -2147,13 +2155,10 @@ jack_port_clear_connections (jack_engine_t *engine, jack_port_internal_t *port)
}

static void
jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
jack_zombify_client (jack_engine_t *engine, jack_client_internal_t *client)
{
JSList *node;
unsigned int i;

if (engine->verbose) {
fprintf (stderr, "adios senor %s\n", client->control->name);
fprintf (stderr, "senor %s - you are a zombie\n", client->control->name);
}

/* caller must hold the client_lock */
@@ -2161,7 +2166,7 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
/* this stops jack_deliver_event() from doing anything */

client->control->dead = TRUE;
if (client == engine->timebase_client) {
engine->timebase_client = 0;
engine->control->current_time.frame = 0;
@@ -2171,33 +2176,32 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
}

jack_client_disconnect (engine, client);
jack_client_do_deactivate (engine, client, FALSE);
}

/* try to force the server thread to return from poll */

close (client->event_fd);
close (client->request_fd);
static void
jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
{
unsigned int i;
JSList *node;

/* if the client is stuck in its process() callback, its not going to
notice that we closed the pipes. give it a little help ... though
this could prove fatal to some clients.
*/
/* caller must hold the client_lock */

if (client->control->pid > 0) {
if (engine->verbose) {
fprintf (stderr, "sending SIGHUP to client %s at %d\n", client->control->name, client->control->pid);
}
kill (client->control->pid, SIGHUP);
if (engine->verbose) {
fprintf (stderr, "adios senor %s\n", client->control->name);
}

for (node = engine->clients; node; node = jack_slist_next (node)) {
if (((jack_client_internal_t *) node->data)->control->id == client->control->id) {
engine->clients = jack_slist_remove_link (engine->clients, node);
jack_slist_free_1 (node);
break;
}
/* if its not already a zombie, make it so */

if (!client->control->dead) {
jack_zombify_client (engine, client);
}

/* try to force the server thread to return from poll */

close (client->event_fd);
close (client->request_fd);
jack_client_do_deactivate (engine, client, FALSE);

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

@@ -2215,9 +2219,18 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
}
}

for (node = engine->clients; node; node = jack_slist_next (node)) {
if (((jack_client_internal_t *) node->data)->control->id == client->control->id) {
engine->clients = jack_slist_remove_link (engine->clients, node);
jack_slist_free_1 (node);
break;
}
}
jack_client_delete (engine, client);
}


static void
jack_client_delete (jack_engine_t *engine, jack_client_internal_t *client)
{
@@ -2226,6 +2239,7 @@ jack_client_delete (jack_engine_t *engine, jack_client_internal_t *client)
free ((char *) client->control);
} else {
shmdt ((void *) client->control);
shmctl(client->shm_id,IPC_RMID,0);
}

free (client);
@@ -3022,7 +3036,7 @@ jack_get_fifo_fd (jack_engine_t *engine, unsigned int which_fifo)
char path[PATH_MAX+1];
struct stat statbuf;

sprintf (path, "%s-%d", engine->fifo_prefix, which_fifo);
snprintf (path, sizeof (path), "%s-%d", engine->fifo_prefix, which_fifo);

DEBUG ("%s", path);

@@ -3272,7 +3286,9 @@ jack_do_get_port_connections (jack_engine_t *engine, jack_request_t *req, int re
{
jack_port_internal_t *port;
JSList *node;
unsigned int i;
int ret = -1;
int internal = FALSE;

jack_lock_graph (engine);

@@ -3280,30 +3296,57 @@ jack_do_get_port_connections (jack_engine_t *engine, jack_request_t *req, int re

DEBUG ("Getting connections for port '%s'.", port->shared->name);

req->x.nports = jack_slist_length (port->connections);
req->x.port_connections.nports = jack_slist_length (port->connections);
req->status = 0;

if (write (reply_fd, req, sizeof (*req)) < (ssize_t) sizeof (req)) {
jack_error ("cannot write GetPortConnections result to client");
goto out;
/* figure out if this is an internal or external client */

for (node = engine->clients; node; node = jack_slist_next (node)) {
if (((jack_client_internal_t *) node->data)->request_fd == reply_fd) {
internal = jack_client_is_internal((jack_client_internal_t *) node->data);
break;
}
}

if (req->type == GetPortConnections)
{
for (node = port->connections; node; node = jack_slist_next (node) ) {
if (!internal) {
if (write (reply_fd, req, sizeof (*req)) < (ssize_t) sizeof (req)) {
jack_error ("cannot write GetPortConnections result to client via fd = %d (%s)",
reply_fd, strerror (errno));
goto out;
}
} else {
req->x.port_connections.ports = (const char **) malloc (sizeof (char *) * req->x.port_connections.nports);
}

if (req->type == GetPortConnections) {
for (i = 0, node = port->connections; node; node = jack_slist_next (node), ++i) {

jack_port_id_t port_id;
if (((jack_connection_internal_t *) node->data)->source == port)
{
if (((jack_connection_internal_t *) node->data)->source == port) {
port_id = ((jack_connection_internal_t *) node->data)->destination->shared->id;
}
else
{
} else {
port_id = ((jack_connection_internal_t *) node->data)->source->shared->id;
}
if (write (reply_fd, &port_id, sizeof (port_id)) < (ssize_t) sizeof (port_id)) {
jack_error ("cannot write port id to client");
goto out;
if (internal) {

/* internal client asking for names. store in malloc'ed space, client frees
*/

req->x.port_connections.ports[i] = engine->control->ports[port_id].name;

} else {

/* external client asking for names. we write the port id's to the reply fd.
*/

if (write (reply_fd, &port_id, sizeof (port_id)) < (ssize_t) sizeof (port_id)) {
jack_error ("cannot write port id to client");
goto out;
}
}
}
}
@@ -3457,84 +3500,4 @@ jack_set_asio_mode (jack_engine_t *engine, int yn)
engine->asio_mode = yn;
}

int
jack_do_add_alias (jack_engine_t *engine, jack_request_t *req)
{
JSList *list;
jack_port_alias_t *alias;
int ret = -1;

jack_lock_graph (engine);

for (list = engine->aliases; list; list = jack_slist_next (list)) {

alias = (jack_port_alias_t *) list->data;

if (strcmp (alias->port, req->x.alias.port) == 0 && strcmp (alias->alias, req->x.alias.alias) == 0) {
break;
}
}
if (list == NULL) {
alias = (jack_port_alias_t *) malloc (sizeof (jack_port_alias_t));
strcpy (alias->port, req->x.alias.port);
strcpy (alias->alias, req->x.alias.alias);
engine->aliases = jack_slist_append (engine->aliases, alias);
ret = 0;
}

jack_unlock_graph (engine);
return ret;
}

int
jack_do_remove_alias (jack_engine_t *engine, jack_request_t *req)
{
JSList *list;
jack_port_alias_t *alias;
int ret = -1;

jack_lock_graph (engine);

for (list = engine->aliases; list; list = jack_slist_next (list)) {

alias = (jack_port_alias_t *) list->data;

if (strcmp (alias->port, req->x.alias.port) == 0 && strcmp (alias->alias, req->x.alias.alias) == 0) {
engine->aliases = jack_slist_remove_link (engine->aliases, list);
jack_slist_free_1 (list);
free (alias);
ret = 0;
}
}

jack_unlock_graph (engine);
return ret;
}

const char *
jack_lookup_alias (jack_engine_t *engine, const char *alias_name)
{
JSList *list;
jack_port_alias_t *alias;

jack_lock_graph (engine);

for (list = engine->aliases; list; list = jack_slist_next (list)) {

alias = (jack_port_alias_t *) list->data;

if (strcmp (alias->alias, alias_name) == 0) {
break;
}
}

jack_unlock_graph (engine);

if (list) {
return ((jack_port_alias_t *) list->data)->port;
} else {
return 0;
}
}

+ 5
- 1
jackd/jackd.c View File

@@ -140,6 +140,10 @@ jack_main (int argc, char **argv)
sigset_t allsignals;
struct sigaction action;

/* remove any existing files from a previous instance */
jack_cleanup_files ();

pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

/* what's this for?
@@ -251,7 +255,7 @@ jack_main (int argc, char **argv)

if (sig != SIGSEGV) {

/* unblock signals so we can see them during shutdown.
/* unblock signals so we can see them during shutdown.
this will help prod developers not to lose sight
of bugs that cause segfaults etc. during shutdown.
*/


+ 6
- 1
libjack/Makefile.am View File

@@ -1,6 +1,11 @@
MAINTAINERCLEANFILES = Makefile.in

SOURCE_FILES = client.c pool.c driver.c timestamps.c
SOURCE_FILES = \
client.c \
pool.c \
port.c \
driver.c \
timestamps.c

lib_LTLIBRARIES = libjack.la



+ 35
- 626
libjack/client.c View File

@@ -44,6 +44,8 @@
#include <jack/jslist.h>
#include <jack/version.h>

#include "local.h"

#ifdef WITH_TIMESTAMPS
#include <jack/timestamps.h>
#endif /* WITH_TIMESTAMPS */
@@ -56,40 +58,11 @@ jack_set_server_dir (const char *path)
jack_server_dir = strdup (path);
}

static jack_port_t *jack_port_new (const jack_client_t *client, jack_port_id_t port_id, jack_control_t *control);

static pthread_mutex_t client_lock;
static pthread_cond_t client_ready;
void *jack_zero_filled_buffer = 0;

static void jack_audio_port_mixdown (jack_port_t *port, jack_nframes_t nframes);

jack_port_type_info_t builtin_port_types[] = {
{ JACK_DEFAULT_AUDIO_TYPE, jack_audio_port_mixdown, 1 },
{ "", NULL }
};

struct _jack_client {

jack_control_t *engine;
jack_client_control_t *control;
struct pollfd *pollfd;
int pollmax;
int graph_next_fd;
int request_fd;
JSList *port_segments;
JSList *ports;
pthread_t thread;
char fifo_prefix[PATH_MAX+1];
void (*on_shutdown)(void *arg);
void *on_shutdown_arg;
char thread_ok : 1;
char first_active : 1;
float cpu_mhz;
pthread_t thread_id;

};

#define event_fd pollfd[0].fd
#define graph_wait_fd pollfd[1].fd

@@ -101,13 +74,12 @@ typedef struct {

void
jack_error (const char *fmt, ...)

{
va_list ap;
char buffer[300];

va_start (ap, fmt);
vsnprintf (buffer, 300, fmt, ap);
vsnprintf (buffer, sizeof(buffer), fmt, ap);
jack_error_callback (buffer);
va_end (ap);
}
@@ -136,8 +108,8 @@ oop_client_deliver_request (void *ptr, jack_request_t *req)
return req->status;
}

static int
deliver_request (const jack_client_t *client, jack_request_t *req)
int
jack_client_deliver_request (const jack_client_t *client, jack_request_t *req)
{
/* indirect through the function pointer that was set
either by jack_client_new() (external) or handle_new_client()
@@ -194,47 +166,8 @@ jack_client_free (jack_client_t *client)
free (client);
}

jack_port_t *
jack_port_by_id (const jack_client_t *client, jack_port_id_t id)
{
JSList *node;

for (node = client->ports; node; node = jack_slist_next (node)) {
if (((jack_port_t *) node->data)->shared->id == id) {
return (jack_port_t *) node->data;
}
}

if (id >= client->engine->port_max)
return NULL;

if (client->engine->ports[id].in_use)
return jack_port_new (client, id, client->engine);

return NULL;
}

jack_port_t *
jack_port_by_name (jack_client_t *client, const char *port_name)
{
unsigned long i, limit;
jack_port_shared_t *port;
limit = client->engine->port_max;
port = &client->engine->ports[0];
for (i = 0; i < limit; i++) {
if (port[i].in_use && strcmp (port[i].name, port_name) == 0) {
return jack_port_new (client, port[i].id, client->engine);
}
}

return NULL;
}

static void
jack_client_invalidate_port_buffers (jack_client_t *client)

{
JSList *node;
jack_port_t *port;
@@ -352,7 +285,6 @@ jack_handle_reorder (jack_client_t *client, jack_event_t *event)
static int
server_connect (int which)

{
int fd;
struct sockaddr_un addr;
@@ -376,7 +308,6 @@ server_connect (int which)

static int
server_event_connect (jack_client_t *client)

{
int fd;
struct sockaddr_un addr;
@@ -727,11 +658,7 @@ jack_client_thread (void *arg)
}

if (client->pollfd[0].revents & ~POLLIN || client->control->dead) {
jack_error ("engine has shut down socket; thread exiting");
if (client->on_shutdown) {
client->on_shutdown (client->on_shutdown_arg);
}
pthread_exit (0);
goto zombie;
}

if (client->pollfd[0].revents & POLLIN) {
@@ -861,6 +788,12 @@ jack_client_thread (void *arg)
*/
}

/* check if we were killed during the process cycle (or whatever) */

if (client->control->dead) {
goto zombie;
}

DEBUG("process cycle fully complete\n");

#ifdef WITH_TIMESTAMPS
@@ -871,6 +804,19 @@ jack_client_thread (void *arg)
}
return (void *) ((intptr_t)err);

zombie:
if (client->on_shutdown) {
jack_error ("zombified - calling shutdown handler");
client->on_shutdown (client->on_shutdown_arg);
} else {
jack_error ("zombified - exiting from JACK");
jack_client_close (client);
}

pthread_exit (0);
/*NOTREACHED*/
return 0;
}

static int
@@ -1061,7 +1007,7 @@ jack_activate (jack_client_t *client)
req.type = SetClientCapabilities;
req.x.client_id = client->control->id;
deliver_request (client, &req);
jack_client_deliver_request (client, &req);

if (req.status) {

@@ -1107,7 +1053,7 @@ jack_activate (jack_client_t *client)
req.type = ActivateClient;
req.x.client_id = client->control->id;

return deliver_request (client, &req);
return jack_client_deliver_request (client, &req);
}

int
@@ -1119,7 +1065,7 @@ jack_deactivate (jack_client_t *client)
req.type = DeactivateClient;
req.x.client_id = client->control->id;

return deliver_request (client, &req);
return jack_client_deliver_request (client, &req);
}

int
@@ -1179,118 +1125,6 @@ unsigned long jack_get_sample_rate (jack_client_t *client)
return client->engine->current_time.frame_rate;
}

static jack_port_t *
jack_port_new (const jack_client_t *client, jack_port_id_t port_id, jack_control_t *control)
{
jack_port_t *port;
jack_port_shared_t *shared;
jack_port_segment_info_t *si;
JSList *node;

shared = &control->ports[port_id];

port = (jack_port_t *) malloc (sizeof (jack_port_t));

port->client_segment_base = 0;
port->shared = shared;
pthread_mutex_init (&port->connection_lock, NULL);
port->connections = 0;
port->shared->tied = NULL;

si = NULL;
for (node = client->port_segments; node; node = jack_slist_next (node)) {
si = (jack_port_segment_info_t *) node->data;
if (si->shm_key == port->shared->shm_key) {
break;
}
}
if (si == NULL) {
jack_error ("cannot find port segment to match newly registered port\n");
return NULL;
}
port->client_segment_base = si->address;
return port;
}

jack_port_t *
jack_port_register (jack_client_t *client,
const char *port_name,
const char *port_type,
unsigned long flags,
unsigned long buffer_size)
{
jack_request_t req;
jack_port_t *port = 0;
jack_port_type_info_t *type_info;
int n;

req.type = RegisterPort;

strcpy ((char *) req.x.port_info.name, (const char *) client->control->name);
strcat ((char *) req.x.port_info.name, ":");
strcat ((char *) req.x.port_info.name, port_name);

snprintf (req.x.port_info.type, sizeof (req.x.port_info.type), "%s", port_type);
req.x.port_info.flags = flags;
req.x.port_info.buffer_size = buffer_size;
req.x.port_info.client_id = client->control->id;

if (deliver_request (client, &req)) {
return NULL;
}

port = jack_port_new (client, req.x.port_info.port_id, client->engine);

type_info = NULL;

for (n = 0; builtin_port_types[n].type_name[0]; n++) {
if (strcmp (req.x.port_info.type, builtin_port_types[n].type_name) == 0) {
type_info = &builtin_port_types[n];
break;
}
}

if (type_info == NULL) {
/* not a builtin type, so allocate a new type_info structure,
and fill it appropriately.
*/
type_info = (jack_port_type_info_t *) malloc (sizeof (jack_port_type_info_t));

snprintf ((char *) type_info->type_name, sizeof (type_info->type_name), req.x.port_info.type);

type_info->mixdown = NULL; /* we have no idea how to mix this */
type_info->buffer_scale_factor = -1; /* use specified port buffer size */
}

memcpy (&port->shared->type_info, type_info, sizeof (jack_port_type_info_t));

client->ports = jack_slist_prepend (client->ports, port);

return port;
}

int
jack_port_unregister (jack_client_t *client, jack_port_t *port)

{
jack_request_t req;

req.type = UnRegisterPort;
req.x.port_info.port_id = port->shared->id;
req.x.port_info.client_id = client->control->id;

return deliver_request (client, &req);
}

int
jack_connect (jack_client_t *client, const char *source_port, const char *destination_port)

@@ -1302,7 +1136,7 @@ jack_connect (jack_client_t *client, const char *source_port, const char *destin
snprintf (req.x.connect.source_port, sizeof (req.x.connect.source_port), "%s", source_port);
snprintf (req.x.connect.destination_port, sizeof (req.x.connect.destination_port), "%s", destination_port);

return deliver_request (client, &req);
return jack_client_deliver_request (client, &req);
}

int
@@ -1322,7 +1156,7 @@ jack_port_disconnect (jack_client_t *client, jack_port_t *port)
req.type = DisconnectPort;
req.x.port_info.port_id = port->shared->id;

return deliver_request (client, &req);
return jack_client_deliver_request (client, &req);
}

int
@@ -1335,7 +1169,7 @@ jack_disconnect (jack_client_t *client, const char *source_port, const char *des
snprintf (req.x.connect.source_port, sizeof (req.x.connect.source_port), "%s", source_port);
snprintf (req.x.connect.destination_port, sizeof (req.x.connect.destination_port), "%s", destination_port);
return deliver_request (client, &req);
return jack_client_deliver_request (client, &req);
}

int
@@ -1347,7 +1181,7 @@ jack_engine_takeover_timebase (jack_client_t *client)
req.type = SetTimeBaseClient;
req.x.client_id = client->control->id;

return deliver_request (client, &req);
return jack_client_deliver_request (client, &req);
}

void
@@ -1356,111 +1190,6 @@ jack_set_error_function (void (*func) (const char *))
jack_error_callback = func;
}

jack_nframes_t
jack_port_get_latency (jack_port_t *port)
{
return port->shared->latency;
}

void
jack_port_set_latency (jack_port_t *port, jack_nframes_t nframes)
{
port->shared->latency = nframes;
}

void *
jack_port_get_buffer (jack_port_t *port, jack_nframes_t nframes)

{
JSList *node, *next;

/* Output port. The buffer was assigned by the engine
when the port was registered.
*/

if (port->shared->flags & JackPortIsOutput) {
if (port->shared->tied) {
return jack_port_get_buffer (port->shared->tied, nframes);
}
return jack_port_buffer (port);
}

/* Input port.
*/

/* since this can only be called from the process() callback,
and since no connections can be made/broken during this
phase (enforced by the jack server), there is no need
to take the connection lock here
*/

if ((node = port->connections) == NULL) {
/* no connections; return a zero-filled buffer */

return jack_zero_filled_buffer;
}

if ((next = jack_slist_next (node)) == NULL) {

/* one connection: use zero-copy mode - just pass
the buffer of the connected (output) port.
*/

return jack_port_get_buffer (((jack_port_t *) node->data), nframes);
}

/* multiple connections. use a local buffer and mixdown
the incoming data to that buffer. we have already
established the existence of a mixdown function
during the connection process.

no port can have an offset of 0 - that offset refers
to the zero-filled area at the start of a shared port
segment area. so, use the offset to store the location
of a locally allocated buffer, and reset the client_segment_base
so that the jack_port_buffer() computation works correctly.
*/

if (port->shared->offset == 0) {
port->shared->offset = (size_t) jack_pool_alloc (port->shared->type_info.buffer_scale_factor *
sizeof (jack_default_audio_sample_t) * nframes);
port->client_segment_base = 0;
}

port->shared->type_info.mixdown (port, nframes);
return (jack_default_audio_sample_t *) port->shared->offset;
}

int
jack_port_tie (jack_port_t *src, jack_port_t *dst)

{
if (dst->shared->client_id != src->shared->client_id) {
jack_error ("cannot tie ports not owned by the same client");
return -1;
}

if (dst->shared->flags & JackPortIsOutput) {
jack_error ("cannot tie an input port");
return -1;
}

dst->shared->tied = src;
return 0;
}

int
jack_port_untie (jack_port_t *port)

{
if (port->shared->tied == NULL) {
jack_error ("port \"%s\" is not tied", port->shared->name);
return -1;
}
port->shared->tied = NULL;
return 0;
}

int
jack_set_graph_order_callback (jack_client_t *client, JackGraphOrderCallback callback, void *arg)
@@ -1490,8 +1219,8 @@ jack_set_process_callback (jack_client_t *client, JackProcessCallback callback,
int
jack_set_buffer_size_callback (jack_client_t *client, JackBufferSizeCallback callback, void *arg)
{
jack_error("\n*** libjack: WARNING! Use of function jack_set_buffer_size_callback() is deprecated! ***\n\n");
client->control->bufsize_arg = arg;
client->control->bufsize = callback;
return 0;
}

@@ -1543,133 +1272,6 @@ jack_get_process_done_fd (jack_client_t *client)
return client->graph_next_fd;
}

int
jack_port_request_monitor_by_name (jack_client_t *client, const char *port_name, int onoff)

{
jack_port_t *port;
unsigned long i, limit;
jack_port_shared_t *ports;

limit = client->engine->port_max;
ports = &client->engine->ports[0];
for (i = 0; i < limit; i++) {
if (ports[i].in_use && strcmp (ports[i].name, port_name) == 0) {
port = jack_port_new (client, ports[i].id, client->engine);
return jack_port_request_monitor (port, onoff);
free (port);
return 0;
}
}

return -1;
}

int
jack_port_request_monitor (jack_port_t *port, int onoff)

{
if (onoff) {
port->shared->monitor_requests++;
} else if (port->shared->monitor_requests) {
port->shared->monitor_requests--;
}

if ((port->shared->flags & JackPortIsOutput) == 0) {

JSList *node;

/* this port is for input, so recurse over each of the
connected ports.
*/

pthread_mutex_lock (&port->connection_lock);
for (node = port->connections; node; node = jack_slist_next (node)) {
/* drop the lock because if there is a feedback loop,
we will deadlock. XXX much worse things will
happen if there is a feedback loop !!!
*/

pthread_mutex_unlock (&port->connection_lock);
jack_port_request_monitor ((jack_port_t *) node->data, onoff);
pthread_mutex_lock (&port->connection_lock);
}
pthread_mutex_unlock (&port->connection_lock);
}

return 0;
}
int
jack_ensure_port_monitor_input (jack_port_t *port, int yn)
{
if (yn) {
if (port->shared->monitor_requests == 0) {
port->shared->monitor_requests++;
}
} else {
if (port->shared->monitor_requests == 1) {
port->shared->monitor_requests--;
}
}

return 0;
}

int
jack_port_monitoring_input (jack_port_t *port)
{
return port->shared->monitor_requests > 0;
}

const char *
jack_port_name (const jack_port_t *port)
{
return port->shared->name;
}

const char *
jack_port_short_name (const jack_port_t *port)
{
/* we know there is always a colon, because we put
it there ...
*/

return strchr (port->shared->name, ':') + 1;
}

int
jack_port_is_mine (const jack_client_t *client, const jack_port_t *port)
{
return port->shared->client_id == client->control->id;
}

int
jack_port_flags (const jack_port_t *port)
{
return port->shared->flags;
}

const char *
jack_port_type (const jack_port_t *port)
{
return port->shared->type_info.type_name;
}

int
jack_port_set_name (jack_port_t *port, const char *new_name)
{
char *colon;
int len;

colon = strchr (port->shared->name, ':');
len = sizeof (port->shared->name) - ((int) (colon - port->shared->name)) - 2;
snprintf (colon+1, len, "%s", new_name);
return 0;
}

void
jack_on_shutdown (jack_client_t *client, void (*function)(void *arg), void *arg)
@@ -1793,171 +1395,6 @@ jack_frame_time (const jack_client_t *client)
return current.frames + elapsed;
}

int
jack_port_lock (jack_client_t *client, jack_port_t *port)
{
if (port) {
port->shared->locked = 1;
return 0;
}
return -1;
}

int
jack_port_unlock (jack_client_t *client, jack_port_t *port)
{
if (port) {
port->shared->locked = 0;
return 0;
}
return -1;
}

static void
jack_audio_port_mixdown (jack_port_t *port, jack_nframes_t nframes)
{
JSList *node;
jack_port_t *input;
jack_nframes_t n;
jack_default_audio_sample_t *buffer;
jack_default_audio_sample_t *dst, *src;

/* by the time we've called this, we've already established
the existence of more than 1 connection to this input port.
*/

/* no need to take connection lock, since this is called
from the process() callback, and the jack server
ensures that no changes to connections happen
during this time.
*/

node = port->connections;
input = (jack_port_t *) node->data;
buffer = jack_port_buffer (port);

memcpy (buffer, jack_port_buffer (input), sizeof (jack_default_audio_sample_t) * nframes);

for (node = jack_slist_next (node); node; node = jack_slist_next (node)) {

input = (jack_port_t *) node->data;

n = nframes;
dst = buffer;
src = jack_port_buffer (input);

while (n--) {
*dst++ += *src++;
}
}
}

/* LOCAL (in-client) connection querying only */

int
jack_port_connected (const jack_port_t *port)
{
return jack_slist_length (port->connections);
}

int
jack_port_connected_to (const jack_port_t *port, const char *portname)
{
JSList *node;
int ret = FALSE;

/* XXX this really requires a cross-process lock
so that ports/connections cannot go away
while we are checking for them. that's hard,
and has a non-trivial performance impact
for jackd.
*/

pthread_mutex_lock (&((jack_port_t *) port)->connection_lock);

for (node = port->connections; node; node = jack_slist_next (node)) {
jack_port_t *other_port = (jack_port_t *) node->data;
if (strcmp (other_port->shared->name, portname) == 0) {
ret = TRUE;
break;
}
}

pthread_mutex_unlock (&((jack_port_t *) port)->connection_lock);
return ret;
}

const char **
jack_port_get_connections (const jack_port_t *port)
{
const char **ret = NULL;
JSList *node;
unsigned int n;

/* XXX this really requires a cross-process lock
so that ports/connections cannot go away
while we are checking for them. that's hard,
and has a non-trivial performance impact
for jackd.
*/

pthread_mutex_lock (&((jack_port_t *) port)->connection_lock);

if (port->connections != NULL) {

ret = (const char **) malloc (sizeof (char *) * (jack_slist_length (port->connections) + 1));
for (n = 0, node = port->connections; node; node = jack_slist_next (node), ++n) {
ret[n] = ((jack_port_t *) node->data)->shared->name;
}
ret[n] = NULL;
}

pthread_mutex_unlock (&((jack_port_t *) port)->connection_lock);
return ret;
}

/* SERVER-SIDE (all) connection querying */

const char **
jack_port_get_all_connections (const jack_client_t *client, const jack_port_t *port)
{
const char **ret;
jack_request_t req;
unsigned int i;

req.type = GetPortConnections;

req.x.port_info.name[0] = '\0';
req.x.port_info.type[0] = '\0';
req.x.port_info.flags = 0;
req.x.port_info.buffer_size = 0;
req.x.port_info.client_id = 0;
req.x.port_info.port_id = port->shared->id;

deliver_request (client, &req);

if (req.status != 0 || req.x.nports == 0) {
return NULL;
}

ret = (const char **) malloc (sizeof (char *) * (req.x.nports + 1));

for ( i=0; i<req.x.nports; i++ ) {
jack_port_id_t port_id;
if (read (client->request_fd, &port_id, sizeof (port_id)) != sizeof (port_id)) {
jack_error ("cannot read port id from server");
return 0;
}
ret[i] = jack_port_by_id (client, port_id)->shared->name;
}

ret[i] = NULL;

return ret;
}

/* TRANSPORT CONTROL */

@@ -2005,12 +1442,6 @@ jack_set_transport_info (jack_client_t *client,
return 0;
}

jack_nframes_t
jack_port_get_total_latency (jack_client_t *client, jack_port_t *port)
{
return port->shared->total_latency;
}

int
jack_get_mhz (void)
{
@@ -2053,31 +1484,9 @@ jack_cpu_load (jack_client_t *client)
return client->engine->cpu_load;
}

int
jack_add_alias (jack_client_t *client, const char *portname, const char *alias)
{
jack_request_t req;
req.type = AddAlias;
snprintf (req.x.alias.port, sizeof (req.x.alias.port), "%s", portname);
snprintf (req.x.alias.alias, sizeof (req.x.alias.alias), "%s", alias);
return deliver_request (client, &req);
}

int
jack_remove_alias (jack_client_t *client, const char *alias)
{
jack_request_t req;
req.type = RemoveAlias;
snprintf (req.x.alias.alias, sizeof (req.x.alias.alias), "%s", alias);
return deliver_request (client, &req);
}

pthread_t
jack_client_thread_id (jack_client_t *client)
{
return client->thread_id;
}


+ 32
- 0
libjack/local.h View File

@@ -0,0 +1,32 @@
#ifndef __jack_libjack_local_h__
#define __jack_libjack_local_h__

struct _jack_client {

jack_control_t *engine;
jack_client_control_t *control;
struct pollfd *pollfd;
int pollmax;
int graph_next_fd;
int request_fd;
JSList *port_segments;
JSList *ports;
pthread_t thread;
char fifo_prefix[PATH_MAX+1];
void (*on_shutdown)(void *arg);
void *on_shutdown_arg;
char thread_ok : 1;
char first_active : 1;
float cpu_mhz;
pthread_t thread_id;

};

extern int jack_client_deliver_request (const jack_client_t *client, jack_request_t *req);
extern jack_port_t *jack_port_new (const jack_client_t *client, jack_port_id_t port_id, jack_control_t *control);

extern void* jack_zero_filled_buffer;
extern jack_port_type_info_t jack_builtin_port_types[];


#endif /* __jack_libjack_local_h__ */

+ 672
- 0
libjack/port.c View File

@@ -0,0 +1,672 @@
/*
Copyright (C) 2001-2003 Paul Davis
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.

$Id$
*/

#include <string.h>
#include <stdio.h>
#include <math.h>

#include <config.h>

#include <jack/jack.h>
#include <jack/types.h>
#include <jack/internal.h>
#include <jack/engine.h>
#include <jack/pool.h>
#include <jack/port.h>
#include <jack/error.h>
#include <jack/jslist.h>

#include "local.h"

jack_port_t *
jack_port_new (const jack_client_t *client, jack_port_id_t port_id, jack_control_t *control)
{
jack_port_t *port;
jack_port_shared_t *shared;
jack_port_segment_info_t *si;
JSList *node;

shared = &control->ports[port_id];

port = (jack_port_t *) malloc (sizeof (jack_port_t));

port->client_segment_base = 0;
port->shared = shared;
pthread_mutex_init (&port->connection_lock, NULL);
port->connections = 0;
port->shared->tied = NULL;
port->shared->peak = port->shared->type_info.peak;
port->shared->power = port->shared->type_info.power;
si = NULL;
for (node = client->port_segments; node; node = jack_slist_next (node)) {
si = (jack_port_segment_info_t *) node->data;
if (si->shm_key == port->shared->shm_key) {
break;
}
}
if (si == NULL) {
jack_error ("cannot find port segment to match newly registered port\n");
return NULL;
}
port->client_segment_base = si->address;
return port;
}

jack_port_t *
jack_port_register (jack_client_t *client,
const char *port_name,
const char *port_type,
unsigned long flags,
unsigned long buffer_size)
{
jack_request_t req;
jack_port_t *port = 0;
jack_port_type_info_t *type_info;
int n;

req.type = RegisterPort;

strcpy ((char *) req.x.port_info.name, (const char *) client->control->name);
strcat ((char *) req.x.port_info.name, ":");
strcat ((char *) req.x.port_info.name, port_name);

snprintf (req.x.port_info.type, sizeof (req.x.port_info.type), "%s", port_type);
req.x.port_info.flags = flags;
req.x.port_info.buffer_size = buffer_size;
req.x.port_info.client_id = client->control->id;

if (jack_client_deliver_request (client, &req)) {
return NULL;
}

port = jack_port_new (client, req.x.port_info.port_id, client->engine);

type_info = NULL;

for (n = 0; jack_builtin_port_types[n].type_name[0]; n++) {
if (strcmp (req.x.port_info.type, jack_builtin_port_types[n].type_name) == 0) {
type_info = &jack_builtin_port_types[n];
break;
}
}

if (type_info == NULL) {
/* not a builtin type, so allocate a new type_info structure,
and fill it appropriately.
*/
type_info = (jack_port_type_info_t *) malloc (sizeof (jack_port_type_info_t));

snprintf ((char *) type_info->type_name, sizeof (type_info->type_name), req.x.port_info.type);

type_info->mixdown = NULL; /* we have no idea how to mix this */
type_info->buffer_scale_factor = -1; /* use specified port buffer size */
}

memcpy (&port->shared->type_info, type_info, sizeof (jack_port_type_info_t));

client->ports = jack_slist_prepend (client->ports, port);

return port;
}

int
jack_port_unregister (jack_client_t *client, jack_port_t *port)
{
jack_request_t req;

req.type = UnRegisterPort;
req.x.port_info.port_id = port->shared->id;
req.x.port_info.client_id = client->control->id;

return jack_client_deliver_request (client, &req);
}

int
jack_port_lock (jack_client_t *client, jack_port_t *port)
{
if (port) {
port->shared->locked = 1;
return 0;
}
return -1;
}

int
jack_port_unlock (jack_client_t *client, jack_port_t *port)
{
if (port) {
port->shared->locked = 0;
return 0;
}
return -1;
}

/* LOCAL (in-client) connection querying only */

int
jack_port_connected (const jack_port_t *port)
{
return jack_slist_length (port->connections);
}

int
jack_port_connected_to (const jack_port_t *port, const char *portname)
{
JSList *node;
int ret = FALSE;

/* XXX this really requires a cross-process lock
so that ports/connections cannot go away
while we are checking for them. that's hard,
and has a non-trivial performance impact
for jackd.
*/

pthread_mutex_lock (&((jack_port_t *) port)->connection_lock);

for (node = port->connections; node; node = jack_slist_next (node)) {
jack_port_t *other_port = (jack_port_t *) node->data;
if (strcmp (other_port->shared->name, portname) == 0) {
ret = TRUE;
break;
}
}

pthread_mutex_unlock (&((jack_port_t *) port)->connection_lock);
return ret;
}

const char **
jack_port_get_connections (const jack_port_t *port)
{
const char **ret = NULL;
JSList *node;
unsigned int n;

/* XXX this really requires a cross-process lock
so that ports/connections cannot go away
while we are checking for them. that's hard,
and has a non-trivial performance impact
for jackd.
*/

pthread_mutex_lock (&((jack_port_t *) port)->connection_lock);

if (port->connections != NULL) {

ret = (const char **) malloc (sizeof (char *) * (jack_slist_length (port->connections) + 1));
for (n = 0, node = port->connections; node; node = jack_slist_next (node), ++n) {
ret[n] = ((jack_port_t *) node->data)->shared->name;
}
ret[n] = NULL;
}

pthread_mutex_unlock (&((jack_port_t *) port)->connection_lock);
return ret;
}

/* SERVER-SIDE (all) connection querying */

const char **
jack_port_get_all_connections (const jack_client_t *client, const jack_port_t *port)
{
const char **ret;
jack_request_t req;
unsigned int i;

if (port == NULL) {
return NULL;
}

req.type = GetPortConnections;

req.x.port_info.name[0] = '\0';
req.x.port_info.type[0] = '\0';
req.x.port_info.flags = 0;
req.x.port_info.buffer_size = 0;
req.x.port_info.client_id = 0;
req.x.port_info.port_id = port->shared->id;

jack_client_deliver_request (client, &req);

if (req.status != 0 || req.x.port_connections.nports == 0) {
return NULL;
}

if (client->request_fd < 0) {
/* internal client */
return req.x.port_connections.ports;
}

ret = (const char **) malloc (sizeof (char *) * (req.x.port_connections.nports + 1));

for (i = 0; i < req.x.port_connections.nports; ++i ) {
jack_port_id_t port_id;
if (read (client->request_fd, &port_id, sizeof (port_id)) != sizeof (port_id)) {
jack_error ("cannot read port id from server");
return 0;
}
ret[i] = jack_port_by_id (client, port_id)->shared->name;
}

ret[i] = NULL;

return ret;
}

jack_port_t *
jack_port_by_id (const jack_client_t *client, jack_port_id_t id)
{
JSList *node;

for (node = client->ports; node; node = jack_slist_next (node)) {
if (((jack_port_t *) node->data)->shared->id == id) {
return (jack_port_t *) node->data;
}
}

if (id >= client->engine->port_max)
return NULL;

if (client->engine->ports[id].in_use)
return jack_port_new (client, id, client->engine);

return NULL;
}

jack_port_t *
jack_port_by_name (jack_client_t *client, const char *port_name)
{
unsigned long i, limit;
jack_port_shared_t *port;
limit = client->engine->port_max;
port = &client->engine->ports[0];
for (i = 0; i < limit; i++) {
if (port[i].in_use && strcmp (port[i].name, port_name) == 0) {
return jack_port_new (client, port[i].id, client->engine);
}
}

return NULL;
}

jack_nframes_t
jack_port_get_latency (jack_port_t *port)
{
return port->shared->latency;
}

jack_nframes_t
jack_port_get_total_latency (jack_client_t *client, jack_port_t *port)
{
return port->shared->total_latency;
}

void
jack_port_set_latency (jack_port_t *port, jack_nframes_t nframes)
{
port->shared->latency = nframes;
}

void *
jack_port_get_buffer (jack_port_t *port, jack_nframes_t nframes)
{
JSList *node, *next;

/* Output port. The buffer was assigned by the engine
when the port was registered.
*/

if (port->shared->flags & JackPortIsOutput) {
if (port->shared->tied) {
return jack_port_get_buffer (port->shared->tied, nframes);
}
return jack_port_buffer (port);
}

/* Input port.
*/

/* since this can only be called from the process() callback,
and since no connections can be made/broken during this
phase (enforced by the jack server), there is no need
to take the connection lock here
*/

if ((node = port->connections) == NULL) {
/* no connections; return a zero-filled buffer */

return jack_zero_filled_buffer;
}

if ((next = jack_slist_next (node)) == NULL) {

/* one connection: use zero-copy mode - just pass
the buffer of the connected (output) port.
*/

return jack_port_get_buffer (((jack_port_t *) node->data), nframes);
}

/* multiple connections. use a local buffer and mixdown
the incoming data to that buffer. we have already
established the existence of a mixdown function
during the connection process.

no port can have an offset of 0 - that offset refers
to the zero-filled area at the start of a shared port
segment area. so, use the offset to store the location
of a locally allocated buffer, and reset the client_segment_base
so that the jack_port_buffer() computation works correctly.
*/

if (port->shared->offset == 0) {
port->shared->offset = (size_t) jack_pool_alloc (port->shared->type_info.buffer_scale_factor *
sizeof (jack_default_audio_sample_t) * nframes);
port->client_segment_base = 0;
}

port->shared->type_info.mixdown (port, nframes);
return (jack_default_audio_sample_t *) port->shared->offset;
}

int
jack_port_tie (jack_port_t *src, jack_port_t *dst)

{
if (dst->shared->client_id != src->shared->client_id) {
jack_error ("cannot tie ports not owned by the same client");
return -1;
}

if (dst->shared->flags & JackPortIsOutput) {
jack_error ("cannot tie an input port");
return -1;
}

dst->shared->tied = src;
return 0;
}

int
jack_port_untie (jack_port_t *port)

{
if (port->shared->tied == NULL) {
jack_error ("port \"%s\" is not tied", port->shared->name);
return -1;
}
port->shared->tied = NULL;
return 0;
}

int
jack_port_request_monitor (jack_port_t *port, int onoff)

{
if (onoff) {
port->shared->monitor_requests++;
} else if (port->shared->monitor_requests) {
port->shared->monitor_requests--;
}

if ((port->shared->flags & JackPortIsOutput) == 0) {

JSList *node;

/* this port is for input, so recurse over each of the
connected ports.
*/

pthread_mutex_lock (&port->connection_lock);
for (node = port->connections; node; node = jack_slist_next (node)) {
/* drop the lock because if there is a feedback loop,
we will deadlock. XXX much worse things will
happen if there is a feedback loop !!!
*/

pthread_mutex_unlock (&port->connection_lock);
jack_port_request_monitor ((jack_port_t *) node->data, onoff);
pthread_mutex_lock (&port->connection_lock);
}
pthread_mutex_unlock (&port->connection_lock);
}

return 0;
}
int
jack_port_request_monitor_by_name (jack_client_t *client, const char *port_name, int onoff)

{
jack_port_t *port;
unsigned long i, limit;
jack_port_shared_t *ports;

limit = client->engine->port_max;
ports = &client->engine->ports[0];
for (i = 0; i < limit; i++) {
if (ports[i].in_use && strcmp (ports[i].name, port_name) == 0) {
port = jack_port_new (client, ports[i].id, client->engine);
return jack_port_request_monitor (port, onoff);
free (port);
return 0;
}
}

return -1;
}

int
jack_ensure_port_monitor_input (jack_port_t *port, int yn)
{
if (yn) {
if (port->shared->monitor_requests == 0) {
port->shared->monitor_requests++;
}
} else {
if (port->shared->monitor_requests == 1) {
port->shared->monitor_requests--;
}
}

return 0;
}

int
jack_port_monitoring_input (jack_port_t *port)
{
return port->shared->monitor_requests > 0;
}

const char *
jack_port_name (const jack_port_t *port)
{
return port->shared->name;
}

const char *
jack_port_short_name (const jack_port_t *port)
{
/* we know there is always a colon, because we put
it there ...
*/

return strchr (port->shared->name, ':') + 1;
}

int
jack_port_is_mine (const jack_client_t *client, const jack_port_t *port)
{
return port->shared->client_id == client->control->id;
}

int
jack_port_flags (const jack_port_t *port)
{
return port->shared->flags;
}

const char *
jack_port_type (const jack_port_t *port)
{
return port->shared->type_info.type_name;
}

int
jack_port_set_name (jack_port_t *port, const char *new_name)
{
char *colon;
int len;

colon = strchr (port->shared->name, ':');
len = sizeof (port->shared->name) - ((int) (colon - port->shared->name)) - 2;
snprintf (colon+1, len, "%s", new_name);
return 0;
}

void
jack_port_set_peak_function (jack_port_t *port, double (*func)(jack_port_t* port, jack_nframes_t))
{
port->shared->peak = func;
}

void
jack_port_set_power_function (jack_port_t *port, double (*func)(jack_port_t* port, jack_nframes_t))
{
port->shared->power = func;
}

double
jack_port_get_peak (jack_port_t* port, jack_nframes_t nframes)
{
if (port->shared->peak (port, nframes)) {
return port->shared->peak (port, nframes);
} else {
return 0;
}
}

double
jack_port_get_power (jack_port_t* port, jack_nframes_t nframes)
{
if (port->shared->power) {
return port->shared->power (port, nframes);
} else {
return 0;
}
}

/* AUDIO PORT SUPPORT */

static inline float f_max(float x, float a)
{
x -= a;
x += fabs (x);
x *= 0.5;
x += a;

return (x);
}

static double
jack_audio_port_peak (jack_port_t *port, jack_nframes_t nframes)
{
jack_nframes_t n;
jack_default_audio_sample_t *buf = (jack_default_audio_sample_t *) jack_port_get_buffer (port, nframes);
float max = 0;

for (n = 0; n < nframes; ++n) {
max = f_max (buf[n], max);
}

return max;
}

static void
jack_audio_port_mixdown (jack_port_t *port, jack_nframes_t nframes)
{
JSList *node;
jack_port_t *input;
jack_nframes_t n;
jack_default_audio_sample_t *buffer;
jack_default_audio_sample_t *dst, *src;

/* by the time we've called this, we've already established
the existence of more than 1 connection to this input port.
*/

/* no need to take connection lock, since this is called
from the process() callback, and the jack server
ensures that no changes to connections happen
during this time.
*/

node = port->connections;
input = (jack_port_t *) node->data;
buffer = jack_port_buffer (port);

memcpy (buffer, jack_port_buffer (input), sizeof (jack_default_audio_sample_t) * nframes);

for (node = jack_slist_next (node); node; node = jack_slist_next (node)) {

input = (jack_port_t *) node->data;

n = nframes;
dst = buffer;
src = jack_port_buffer (input);

while (n--) {
*dst++ += *src++;
}
}
}

jack_port_type_info_t jack_builtin_port_types[] = {
{ .type_name = JACK_DEFAULT_AUDIO_TYPE,
.mixdown = jack_audio_port_mixdown,
.peak = jack_audio_port_peak,
.power = NULL,
.buffer_scale_factor = 1
},
{ .type_name = "",
.mixdown = NULL
}
};


Loading…
Cancel
Save