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 | |||
| } | |||
| }; | |||