|
- /*
- 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/jslist.h>
-
- #include "local.h"
-
- static void jack_audio_port_mixdown (jack_port_t *port,
- jack_nframes_t nframes);
-
- /* These function pointers are local to each address space. For
- * internal clients they reside within jackd; for external clients in
- * the application process. */
- jack_port_functions_t jack_builtin_audio_functions = {
- .mixdown = jack_audio_port_mixdown,
- };
-
- jack_port_functions_t jack_builtin_NULL_functions = {
- .mixdown = NULL,
- };
-
- /* Only the Audio port type is currently built in. */
- jack_port_type_info_t jack_builtin_port_types[] = {
- { .type_name = JACK_DEFAULT_AUDIO_TYPE,
- .buffer_scale_factor = 1,
- },
- { .type_name = "", }
- };
-
- jack_port_t *
- jack_port_new (const jack_client_t *client, jack_port_id_t port_id,
- jack_control_t *control)
- {
- jack_port_shared_t *shared = &control->ports[port_id];
- jack_port_type_id_t ptid = shared->ptype_id;
- jack_port_t *port = (jack_port_t *) malloc (sizeof (jack_port_t));
-
- port->mix_buffer = NULL;
- port->client_segment_base = NULL;
- port->shared = shared;
- port->type_info = &client->engine->port_types[ptid];
- pthread_mutex_init (&port->connection_lock, NULL);
- port->connections = 0;
- port->tied = NULL;
-
- if (client->control->id == port->shared->client_id) {
-
- /* It's our port, so initialize the pointers to port
- * functions within this address space. These builtin
- * definitions can be overridden by the client. */
-
- if (ptid == JACK_AUDIO_PORT_TYPE) {
-
- port->fptr = jack_builtin_audio_functions;
- port->shared->has_mixdown = TRUE;
-
- } else { /* no other builtin functions */
-
- port->fptr = jack_builtin_NULL_functions;
- port->shared->has_mixdown = FALSE;
- }
- }
-
- /* set up a base address so that port->offset can be used to
- compute the correct location. we don't store the location
- directly, because port->client_segment_base and/or
- port->offset can change if the buffer size or port counts
- are changed.
- */
- port->client_segment_base =
- (void *) &client->port_segment[ptid].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;
- int length ;
-
- req.type = RegisterPort;
-
- length = strlen ( (const char *) client->control->name )
- + 1 + strlen ( port_name ) ;
- if ( length >= sizeof (req.x.port_info.name) ) {
- jack_error ("\"%s:%s\" is too long to be used as a JACK port name.\n"
- "Please use %lu characters or less.",
- client->control->name ,
- port_name ,
- sizeof (req.x.port_info.name) - 1);
- return NULL ;
- }
-
- 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;
- }
-
- if ((port = jack_port_new (client, req.x.port_info.port_id,
- client->engine)) == NULL) {
- return NULL;
- }
-
- 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->tied) {
- return jack_port_get_buffer (port->tied, nframes);
- }
- return jack_output_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 mix the
- incoming data into that buffer. We have already
- established the existence of a mixdown function during the
- connection process.
- */
- if (port->mix_buffer == NULL) {
- port->mix_buffer =
- jack_pool_alloc (
- port->type_info->buffer_scale_factor
- * sizeof (jack_default_audio_sample_t)
- * nframes);
- }
- port->fptr.mixdown (port, nframes);
- return (jack_default_audio_sample_t *) port->mix_buffer;
- }
-
- 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->tied = src;
- return 0;
- }
-
- int
- jack_port_untie (jack_port_t *port)
-
- {
- if (port->tied == NULL) {
- jack_error ("port \"%s\" is not tied", port->shared->name);
- return -1;
- }
- port->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 > 0) {
- port->shared->monitor_requests = 0;
- }
- }
-
- 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->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;
- }
-
- /* 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 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 one connection to this input
- port and allocated a mix_buffer.
- */
-
- /* 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 = port->mix_buffer;
-
- memcpy (buffer, jack_output_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_output_port_buffer (input);
-
- while (n--) {
- *dst++ += *src++;
- }
- }
- }
|