git-svn-id: svn+ssh://jackaudio.org/trunk/jack@348 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
@@ -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; | |||
} | |||
} | |||
@@ -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]); | |||
@@ -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); | |||
} | |||
@@ -66,7 +66,6 @@ struct _jack_engine { | |||
JSList *clients; | |||
JSList *clients_waiting; | |||
JSList *aliases; | |||
struct _jack_port_internal *internal_ports; | |||
@@ -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 (); | |||
@@ -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__ */ | |||
@@ -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; | |||
@@ -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; | |||
@@ -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; | |||
} | |||
} |
@@ -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. | |||
*/ | |||
@@ -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 | |||
@@ -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; | |||
} | |||
@@ -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__ */ |
@@ -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 | |||
} | |||
}; | |||