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 | /* oh well, the engine can't run, so we'll just throw away this | ||||
cycle's data ... | 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 */ | /* Now handle input monitoring */ | ||||
@@ -1453,19 +1454,50 @@ alsa_driver_new (char *name, char *alsa_device, | |||||
driver->clock_sync_listeners = 0; | driver->clock_sync_listeners = 0; | ||||
if (playing) { | 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 (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]); | printf (" %s\n", connections[j]); | ||||
} | } | ||||
free (connections); | free (connections); | ||||
} | |||||
} | |||||
} | } | ||||
if (show_latency) { | if (show_latency) { | ||||
jack_port_t *port = jack_port_by_name (client, ports[i]); | 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); | 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); | memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes); | ||||
return 0; | return 0; | ||||
} | } | ||||
@@ -32,7 +32,7 @@ srate (jack_nframes_t nframes, void *arg) | |||||
void | void | ||||
error (const char *desc) | error (const char *desc) | ||||
{ | { | ||||
printf ("JACK experienced an error: %s\n", desc); | |||||
fprintf (stderr, "JACK error: %s\n", desc); | |||||
} | } | ||||
void | void | ||||
@@ -115,7 +115,7 @@ main (int argc, char *argv[]) | |||||
if ((ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == NULL) { | 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); | exit(1); | ||||
} | } | ||||
@@ -126,7 +126,7 @@ main (int argc, char *argv[]) | |||||
free (ports); | free (ports); | ||||
if ((ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == NULL) { | 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); | exit(1); | ||||
} | } | ||||
@@ -66,7 +66,6 @@ struct _jack_engine { | |||||
JSList *clients; | JSList *clients; | ||||
JSList *clients_waiting; | JSList *clients_waiting; | ||||
JSList *aliases; | |||||
struct _jack_port_internal *internal_ports; | struct _jack_port_internal *internal_ports; | ||||
@@ -259,8 +259,6 @@ typedef enum { | |||||
SetClientCapabilities = 9, | SetClientCapabilities = 9, | ||||
GetPortConnections = 10, | GetPortConnections = 10, | ||||
GetPortNConnections = 11, | GetPortNConnections = 11, | ||||
AddAlias = 12, | |||||
RemoveAlias = 13, | |||||
} RequestType; | } RequestType; | ||||
struct _jack_request { | struct _jack_request { | ||||
@@ -280,21 +278,15 @@ struct _jack_request { | |||||
char destination_port[JACK_PORT_NAME_SIZE+1]; | char destination_port[JACK_PORT_NAME_SIZE+1]; | ||||
} connect; | } connect; | ||||
struct { | 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_client_id_t client_id; | ||||
jack_nframes_t nframes; | jack_nframes_t nframes; | ||||
unsigned int nports; | |||||
} x; | } x; | ||||
int status; | 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_shm (); | ||||
extern void jack_cleanup_files (); | 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 | * NOTE: clients do not need to call this. It exists only | ||||
* to help more complex clients understand what is going | * 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); | 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); | 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); | 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 *); | 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. | * 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); | 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 | * A client may call this on a pair of its own ports to | ||||
* semi-permanently wire them together. This means that | * 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); | 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 | * If JackPortCanMonitor is set for a port, then these 2 functions will | ||||
* turn on/off input monitoring for the port. If JackPortCanMonitor | * 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); | 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 | * This returns the sample rate of the jack system, as set by the user when | ||||
* jackd was started. | * jackd was started. | ||||
@@ -542,28 +566,6 @@ float jack_cpu_load (jack_client_t *client); | |||||
*/ | */ | ||||
void jack_set_server_dir (const char *path); | 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 | * Return the pthread ID of the thread running the JACK client | ||||
* side code. | * side code. | ||||
@@ -575,3 +577,5 @@ pthread_t jack_client_thread_id (jack_client_t *); | |||||
#endif | #endif | ||||
#endif /* __jack_h__ */ | #endif /* __jack_h__ */ | ||||
@@ -33,26 +33,38 @@ | |||||
typedef unsigned long jack_client_id_t; | typedef unsigned long jack_client_id_t; | ||||
typedef struct _jack_port_type_info { | 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; | } jack_port_type_info_t; | ||||
/* This is the data structure allocated in shared memory | /* 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 jack_nframes_t total_latency; | ||||
volatile unsigned char monitor_requests; | 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 in_use : 1; | ||||
char locked : 1; | char locked : 1; | ||||
struct _jack_port *tied; | struct _jack_port *tied; | ||||
@@ -44,8 +44,8 @@ typedef struct _jack_port jack_port_t; | |||||
typedef struct _jack_client jack_client_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; | typedef unsigned long jack_port_id_t; | ||||
@@ -60,6 +60,8 @@ | |||||
*/ | */ | ||||
#define JACKD_SOFT_MODE_TIMEOUT 500 | #define JACKD_SOFT_MODE_TIMEOUT 500 | ||||
#define JACK_ERROR_WITH_SOCKETS 10000000 | |||||
typedef struct { | typedef struct { | ||||
jack_port_internal_t *source; | 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 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 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_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_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); | 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 (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_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_unregister (jack_engine_t *engine, jack_request_t *); | ||||
static int jack_port_do_register (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); | 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 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_use_driver (jack_engine_t *engine, jack_driver_t *driver); | ||||
static int *jack_shm_registry; | static int *jack_shm_registry; | ||||
@@ -333,7 +331,7 @@ jack_cleanup_files () | |||||
while ((dirent = readdir (dir)) != NULL) { | while ((dirent = readdir (dir)) != NULL) { | ||||
if (strncmp (dirent->d_name, "jack-", 5) == 0 || strncmp (dirent->d_name, "jack_", 5) == 0) { | if (strncmp (dirent->d_name, "jack-", 5) == 0 || strncmp (dirent->d_name, "jack_", 5) == 0) { | ||||
char fullpath[PATH_MAX+1]; | 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); | unlink (fullpath); | ||||
} | } | ||||
} | } | ||||
@@ -646,7 +644,12 @@ jack_engine_post_process (jack_engine_t *engine) | |||||
if (!jack_client_is_internal (client) && ctl->process) { | if (!jack_client_is_internal (client) && ctl->process) { | ||||
if (ctl->awake_at != 0 && ctl->state > NotTriggered && ctl->state != Finished && ctl->timed_out++) { | 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; | client = (jack_client_internal_t *) node->data; | ||||
if (client->error) { | 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; | need_sort = TRUE; | ||||
} | } | ||||
@@ -1301,7 +1321,7 @@ jack_set_timebase (jack_engine_t *engine, jack_client_id_t client) | |||||
} | } | ||||
static int | 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; | jack_client_internal_t *client = 0; | ||||
JSList *node; | 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) { | if (((jack_client_internal_t *) node->data)->request_fd == fd) { | ||||
client = (jack_client_internal_t *) node->data; | client = (jack_client_internal_t *) node->data; | ||||
client->error++; | |||||
if (client->error < JACK_ERROR_WITH_SOCKETS) { | |||||
client->error += JACK_ERROR_WITH_SOCKETS; | |||||
} | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
@@ -1374,24 +1396,12 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd) | |||||
case GetPortConnections: | case GetPortConnections: | ||||
case GetPortNConnections: | 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; | break; | ||||
case AddAlias: | |||||
req->status = jack_do_add_alias (engine, req); | |||||
break; | |||||
case RemoveAlias: | |||||
req->status = jack_do_remove_alias (engine, req); | |||||
break; | |||||
default: | default: | ||||
/* some requests are handled entirely on the client side, | /* some requests are handled entirely on the client side, | ||||
by adjusting the shared memory area(s) | by adjusting the shared memory area(s) | ||||
@@ -1557,7 +1567,7 @@ jack_server_thread (void *arg) | |||||
} | } | ||||
if (pfd[i].revents & ~POLLIN) { | 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) { | } else if (pfd[i].revents & POLLIN) { | ||||
if (handle_external_client_request (engine, pfd[i].fd)) { | if (handle_external_client_request (engine, pfd[i].fd)) { | ||||
jack_error ("bad hci\n"); | 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); | pthread_mutex_init (&engine->request_lock, 0); | ||||
engine->clients = 0; | engine->clients = 0; | ||||
engine->aliases = 0; | |||||
engine->port_segments = 0; | engine->port_segments = 0; | ||||
engine->port_buffer_freelist = 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)) { | 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); | delayed_usecs, WORK_SCALE * engine->spare_usecs); | ||||
if (++consecutive_excessive_delays > 10) { | 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->type = req->type; | ||||
client->control->active = 0; | client->control->active = 0; | ||||
client->control->dead = 0; | |||||
client->control->dead = FALSE; | |||||
client->control->timed_out = 0; | client->control->timed_out = 0; | ||||
client->control->id = engine->next_client_id++; | client->control->id = engine->next_client_id++; | ||||
strcpy ((char *) client->control->name, req->name); | 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 | 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) { | 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 */ | /* 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 */ | /* this stops jack_deliver_event() from doing anything */ | ||||
client->control->dead = TRUE; | client->control->dead = TRUE; | ||||
if (client == engine->timebase_client) { | if (client == engine->timebase_client) { | ||||
engine->timebase_client = 0; | engine->timebase_client = 0; | ||||
engine->control->current_time.frame = 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_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) { | 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); | jack_client_delete (engine, client); | ||||
} | } | ||||
static void | static void | ||||
jack_client_delete (jack_engine_t *engine, jack_client_internal_t *client) | 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); | free ((char *) client->control); | ||||
} else { | } else { | ||||
shmdt ((void *) client->control); | shmdt ((void *) client->control); | ||||
shmctl(client->shm_id,IPC_RMID,0); | |||||
} | } | ||||
free (client); | free (client); | ||||
@@ -3022,7 +3036,7 @@ jack_get_fifo_fd (jack_engine_t *engine, unsigned int which_fifo) | |||||
char path[PATH_MAX+1]; | char path[PATH_MAX+1]; | ||||
struct stat statbuf; | 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); | 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; | jack_port_internal_t *port; | ||||
JSList *node; | JSList *node; | ||||
unsigned int i; | |||||
int ret = -1; | int ret = -1; | ||||
int internal = FALSE; | |||||
jack_lock_graph (engine); | 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); | 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; | 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; | port_id = ((jack_connection_internal_t *) node->data)->destination->shared->id; | ||||
} | |||||
else | |||||
{ | |||||
} else { | |||||
port_id = ((jack_connection_internal_t *) node->data)->source->shared->id; | 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; | 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; | sigset_t allsignals; | ||||
struct sigaction action; | struct sigaction action; | ||||
/* remove any existing files from a previous instance */ | |||||
jack_cleanup_files (); | |||||
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); | pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); | ||||
/* what's this for? | /* what's this for? | ||||
@@ -251,7 +255,7 @@ jack_main (int argc, char **argv) | |||||
if (sig != SIGSEGV) { | 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 | this will help prod developers not to lose sight | ||||
of bugs that cause segfaults etc. during shutdown. | of bugs that cause segfaults etc. during shutdown. | ||||
*/ | */ | ||||
@@ -1,6 +1,11 @@ | |||||
MAINTAINERCLEANFILES = Makefile.in | 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 | lib_LTLIBRARIES = libjack.la | ||||
@@ -44,6 +44,8 @@ | |||||
#include <jack/jslist.h> | #include <jack/jslist.h> | ||||
#include <jack/version.h> | #include <jack/version.h> | ||||
#include "local.h" | |||||
#ifdef WITH_TIMESTAMPS | #ifdef WITH_TIMESTAMPS | ||||
#include <jack/timestamps.h> | #include <jack/timestamps.h> | ||||
#endif /* WITH_TIMESTAMPS */ | #endif /* WITH_TIMESTAMPS */ | ||||
@@ -56,40 +58,11 @@ jack_set_server_dir (const char *path) | |||||
jack_server_dir = strdup (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_mutex_t client_lock; | ||||
static pthread_cond_t client_ready; | static pthread_cond_t client_ready; | ||||
void *jack_zero_filled_buffer = 0; | 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 event_fd pollfd[0].fd | ||||
#define graph_wait_fd pollfd[1].fd | #define graph_wait_fd pollfd[1].fd | ||||
@@ -101,13 +74,12 @@ typedef struct { | |||||
void | void | ||||
jack_error (const char *fmt, ...) | jack_error (const char *fmt, ...) | ||||
{ | { | ||||
va_list ap; | va_list ap; | ||||
char buffer[300]; | char buffer[300]; | ||||
va_start (ap, fmt); | va_start (ap, fmt); | ||||
vsnprintf (buffer, 300, fmt, ap); | |||||
vsnprintf (buffer, sizeof(buffer), fmt, ap); | |||||
jack_error_callback (buffer); | jack_error_callback (buffer); | ||||
va_end (ap); | va_end (ap); | ||||
} | } | ||||
@@ -136,8 +108,8 @@ oop_client_deliver_request (void *ptr, jack_request_t *req) | |||||
return req->status; | 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 | /* indirect through the function pointer that was set | ||||
either by jack_client_new() (external) or handle_new_client() | either by jack_client_new() (external) or handle_new_client() | ||||
@@ -194,47 +166,8 @@ jack_client_free (jack_client_t *client) | |||||
free (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 | static void | ||||
jack_client_invalidate_port_buffers (jack_client_t *client) | jack_client_invalidate_port_buffers (jack_client_t *client) | ||||
{ | { | ||||
JSList *node; | JSList *node; | ||||
jack_port_t *port; | jack_port_t *port; | ||||
@@ -352,7 +285,6 @@ jack_handle_reorder (jack_client_t *client, jack_event_t *event) | |||||
static int | static int | ||||
server_connect (int which) | server_connect (int which) | ||||
{ | { | ||||
int fd; | int fd; | ||||
struct sockaddr_un addr; | struct sockaddr_un addr; | ||||
@@ -376,7 +308,6 @@ server_connect (int which) | |||||
static int | static int | ||||
server_event_connect (jack_client_t *client) | server_event_connect (jack_client_t *client) | ||||
{ | { | ||||
int fd; | int fd; | ||||
struct sockaddr_un addr; | struct sockaddr_un addr; | ||||
@@ -727,11 +658,7 @@ jack_client_thread (void *arg) | |||||
} | } | ||||
if (client->pollfd[0].revents & ~POLLIN || client->control->dead) { | 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) { | 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"); | DEBUG("process cycle fully complete\n"); | ||||
#ifdef WITH_TIMESTAMPS | #ifdef WITH_TIMESTAMPS | ||||
@@ -871,6 +804,19 @@ jack_client_thread (void *arg) | |||||
} | } | ||||
return (void *) ((intptr_t)err); | 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 | static int | ||||
@@ -1061,7 +1007,7 @@ jack_activate (jack_client_t *client) | |||||
req.type = SetClientCapabilities; | req.type = SetClientCapabilities; | ||||
req.x.client_id = client->control->id; | req.x.client_id = client->control->id; | ||||
deliver_request (client, &req); | |||||
jack_client_deliver_request (client, &req); | |||||
if (req.status) { | if (req.status) { | ||||
@@ -1107,7 +1053,7 @@ jack_activate (jack_client_t *client) | |||||
req.type = ActivateClient; | req.type = ActivateClient; | ||||
req.x.client_id = client->control->id; | req.x.client_id = client->control->id; | ||||
return deliver_request (client, &req); | |||||
return jack_client_deliver_request (client, &req); | |||||
} | } | ||||
int | int | ||||
@@ -1119,7 +1065,7 @@ jack_deactivate (jack_client_t *client) | |||||
req.type = DeactivateClient; | req.type = DeactivateClient; | ||||
req.x.client_id = client->control->id; | req.x.client_id = client->control->id; | ||||
return deliver_request (client, &req); | |||||
return jack_client_deliver_request (client, &req); | |||||
} | } | ||||
int | int | ||||
@@ -1179,118 +1125,6 @@ unsigned long jack_get_sample_rate (jack_client_t *client) | |||||
return client->engine->current_time.frame_rate; | 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 | int | ||||
jack_connect (jack_client_t *client, const char *source_port, const char *destination_port) | 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.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); | 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 | int | ||||
@@ -1322,7 +1156,7 @@ jack_port_disconnect (jack_client_t *client, jack_port_t *port) | |||||
req.type = DisconnectPort; | req.type = DisconnectPort; | ||||
req.x.port_info.port_id = port->shared->id; | req.x.port_info.port_id = port->shared->id; | ||||
return deliver_request (client, &req); | |||||
return jack_client_deliver_request (client, &req); | |||||
} | } | ||||
int | 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.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); | 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 | int | ||||
@@ -1347,7 +1181,7 @@ jack_engine_takeover_timebase (jack_client_t *client) | |||||
req.type = SetTimeBaseClient; | req.type = SetTimeBaseClient; | ||||
req.x.client_id = client->control->id; | req.x.client_id = client->control->id; | ||||
return deliver_request (client, &req); | |||||
return jack_client_deliver_request (client, &req); | |||||
} | } | ||||
void | void | ||||
@@ -1356,111 +1190,6 @@ jack_set_error_function (void (*func) (const char *)) | |||||
jack_error_callback = func; | 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 | int | ||||
jack_set_graph_order_callback (jack_client_t *client, JackGraphOrderCallback callback, void *arg) | 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 | int | ||||
jack_set_buffer_size_callback (jack_client_t *client, JackBufferSizeCallback callback, void *arg) | 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; | return 0; | ||||
} | } | ||||
@@ -1543,133 +1272,6 @@ jack_get_process_done_fd (jack_client_t *client) | |||||
return client->graph_next_fd; | 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 | void | ||||
jack_on_shutdown (jack_client_t *client, void (*function)(void *arg), void *arg) | 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; | 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 */ | /* TRANSPORT CONTROL */ | ||||
@@ -2005,12 +1442,6 @@ jack_set_transport_info (jack_client_t *client, | |||||
return 0; | return 0; | ||||
} | } | ||||
jack_nframes_t | |||||
jack_port_get_total_latency (jack_client_t *client, jack_port_t *port) | |||||
{ | |||||
return port->shared->total_latency; | |||||
} | |||||
int | int | ||||
jack_get_mhz (void) | jack_get_mhz (void) | ||||
{ | { | ||||
@@ -2053,31 +1484,9 @@ jack_cpu_load (jack_client_t *client) | |||||
return client->engine->cpu_load; | 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 | pthread_t | ||||
jack_client_thread_id (jack_client_t *client) | jack_client_thread_id (jack_client_t *client) | ||||
{ | { | ||||
return client->thread_id; | 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 | |||||
} | |||||
}; | |||||