Logo Search packages:      
Sourcecode: ladcca version File versions  Download package

jack_mgr.c

/*
 *   LADCCA
 *    
 *   Copyright (C) 2002 Robert Ham <rah@bash.sh>
 *    
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#define _GNU_SOURCE

#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <pthread.h>
#include <stdarg.h>

#include <jack/jack.h>
#include <ladcca/ladcca.h>
#include <ladcca/internal_headers.h>

#include "jack_mgr_client.h"
#include "jack_mgr.h"
#include "server_event.h"
#include "server.h"
#include "jack_fport.h"

#define BACKUP_INTERVAL ((time_t)(30))

static void * jack_mgr_callback_run (void * data);
static void jack_mgr_new_client_port (jack_mgr_t * jack_mgr, const char * port_name_port, jack_mgr_client_t * client);

const char *
get_jack_port_name_only (const char * port_name)
{
  static char * name = NULL;
  char * ptr;
  
  ptr = strchr (port_name, ':');
  if (!ptr)
    return NULL;
  ptr++;
  
  if (name)
    free (name);
  
  name = cca_strdup (ptr);
  
  return name;  
}


const char *
get_jack_client_name_only (const char * port_name)
{
  static char * name = NULL;
  char * ptr;
  
  if (name)
    free (name);
  
  name = cca_strdup (port_name);

  ptr = strchr (name, ':');
  if (!ptr)
    return NULL;
    
  *ptr = '\0';
  
  return name;  
}

static jack_mgr_client_t *
jack_mgr_find_client (jack_mgr_t * jack_mgr, uuid_t id)
{
  cca_list_t * list;
  jack_mgr_client_t * client;
  
  for (list = jack_mgr->clients; list; list = cca_list_next (list))
    {
      client = (jack_mgr_client_t *) list->data;
      
      if (uuid_compare (id, client->id) == 0)
        return client;
    }
  
  return NULL;
}

static void
jack_mgr_jack_error (const char * message)
{
  fprintf  (stderr, "JACK error: %s\n", message);
}

static void
jack_mgr_shutdown_cb (void * data)
{
  server_t * server;
  
  server = (server_t *) data;
  
  fprintf (stderr, "%s: JACK server shut us down; telling server to quit\n", __FUNCTION__);
  
  server->quit = 1;
  pthread_cond_signal (&server->server_event_cond);
}

static void
jack_mgr_port_registration_cb (jack_port_id_t port_id, int registered, void * data)
{
  jack_mgr_t * jack_mgr;
  ssize_t err;
  char port_notify = 1;
  char reg = registered;
  
  jack_mgr = (jack_mgr_t *) data;
  
  err = write (jack_mgr->callback_write_socket, &port_notify, sizeof (port_notify));
  if (err == -1)
    {
      fprintf (stderr, "%s: could not send data to jack manager callback, aborting: %s\n",
               __FUNCTION__, strerror (errno));
      abort ();
    }

  err = write (jack_mgr->callback_write_socket, &port_id, sizeof (port_id));
  if (err == -1)
    {
      fprintf (stderr, "%s: could not send data to jack manager callback, aborting: %s\n",
               __FUNCTION__, strerror (errno));
      abort ();
    }

  err = write (jack_mgr->callback_write_socket, &reg, sizeof (reg));
  if (err == -1)
    {
      fprintf (stderr, "%s: could not send data to jack manager callback, aborting: %s\n",
               __FUNCTION__, strerror (errno));
      abort ();
    }
  
}

static int
jack_mgr_graph_reorder_cb (void * data)
{
  jack_mgr_t * jack_mgr;
  ssize_t err;
  char port_notify = 0;
  
  jack_mgr = (jack_mgr_t *) data;

  err = write (jack_mgr->callback_write_socket, &port_notify, sizeof (port_notify));
  if (err == -1)
    {
      fprintf (stderr, "%s: could not send data to jack manager callback, aborting: %s\n",
               __FUNCTION__, strerror (errno));
      abort ();
    }
  
  return 0;
}

static void
jack_mgr_init_jack (jack_mgr_t * jack_mgr)
{
  int err;
  const char * client_name = "LADCCA_Server";
  
  jack_set_error_function (jack_mgr_jack_error);

  jack_mgr->jack_client = jack_client_new (client_name);
  if (!jack_mgr->jack_client)
    {
      fprintf (stderr, "%s: could not connect to jackd, exiting\n", __FUNCTION__);
      exit (1);
    }

  printf ("Connected to JACK server with client name '%s'\n", client_name);
  
  err = jack_set_port_registration_callback (jack_mgr->jack_client, jack_mgr_port_registration_cb, jack_mgr);
  if (err)
    {
      fprintf (stderr, "%s: could not set jack port registration callback\n", __FUNCTION__);
      abort ();
    }

  err = jack_set_graph_order_callback (jack_mgr->jack_client, jack_mgr_graph_reorder_cb, jack_mgr);
  if (err)
    {
      fprintf (stderr, "%s: could not set jack graph order callback\n", __FUNCTION__);
      abort ();
    }

  jack_on_shutdown (jack_mgr->jack_client, jack_mgr_shutdown_cb, jack_mgr->server);

  err = jack_activate (jack_mgr->jack_client);
  if (err)
    {
      fprintf (stderr, "%s: could not activate jack client\n", __FUNCTION__);
      abort ();
    }
}

jack_mgr_t *
jack_mgr_new (server_t * server)
{
  jack_mgr_t * jack_mgr;
  int sockets[2];
  int err;
  
  jack_mgr = cca_malloc (sizeof (jack_mgr_t));
  jack_mgr->server = server;
  jack_mgr->quit = 0;
  jack_mgr->clients = NULL;
  jack_mgr->foreign_ports = NULL;
  
  pthread_mutex_init (&jack_mgr->lock, NULL);
  
  err = socketpair (AF_UNIX, SOCK_STREAM, 0, sockets);
  if (err == -1)
    {
      fprintf (stderr, "%s: could not create unix socket pair: %s\n",
               __FUNCTION__, strerror (errno));
      abort ();
    }
  
  jack_mgr->callback_read_socket  = sockets[0];
  jack_mgr->callback_write_socket = sockets[1];
  
  jack_mgr_init_jack (jack_mgr);
  
  pthread_create (&jack_mgr->callback_thread, NULL, jack_mgr_callback_run, jack_mgr);
  
  return jack_mgr;
}


void
jack_mgr_destroy (jack_mgr_t * jack_mgr)
{
  int err;
  char dummy;

  jack_mgr->quit = 1;

  err = jack_deactivate (jack_mgr->jack_client);
  if (err)
    {
      fprintf (stderr, "%s: could not deactivate jack client\n", __FUNCTION__);
    }
  
  CCA_DEBUG ("stopping");
  err = write (jack_mgr->callback_write_socket, &dummy, sizeof (dummy));
  if (err == -1)
    {
      fprintf (stderr, "%s: could not send data to jack manager callback, aborting: %s\n",
               __FUNCTION__, strerror (errno));
      abort ();
    }
  
  err = pthread_join (jack_mgr->callback_thread, NULL);
  if (err)
    {
      fprintf (stderr, "%s: error joining callback thread: %s\n", __FUNCTION__, strerror (errno));
      return;
    }
  CCA_DEBUG ("stopped");

  jack_client_close (jack_mgr->jack_client);

/* FIXME: free all the data */
  
  free (jack_mgr);
}

void
jack_mgr_lock (jack_mgr_t * jack_mgr)
{
  pthread_mutex_lock (&jack_mgr->lock);
}

void
jack_mgr_unlock (jack_mgr_t * jack_mgr)
{
  pthread_mutex_unlock (&jack_mgr->lock);
}



/* caller must gold clients lock */
void
jack_mgr_check_client_ports (jack_mgr_t * jack_mgr, jack_mgr_client_t * client)
{
  char * port_name;
  cca_list_t * list;
  jack_fport_t * fport;
  
  for (list = jack_mgr->foreign_ports; list;)
    {
      fport = (jack_fport_t *) list->data;
      list = cca_list_next (list);
      
      if (strcmp (client->name, get_jack_client_name_only (fport->name)) == 0)
        {
          port_name = cca_strdup (get_jack_port_name_only (fport->name));
          CCA_DEBUGARGS ("resuming previously foreign port '%s'", fport->name);
          jack_mgr_new_client_port (jack_mgr, port_name, client);
          free (port_name);
          
          jack_mgr->foreign_ports = cca_list_remove (jack_mgr->foreign_ports, fport);
          jack_fport_destroy (fport);
        }
    }
}

void
jack_mgr_add_client (jack_mgr_t * jack_mgr,
                     uuid_t id,
                     const char * jack_client_name,
                     cca_list_t * jack_patches)
{
  jack_mgr_client_t * client;
  
  CCA_DEBUGARGS ("adding client '%s'", jack_client_name);
  
  client = jack_mgr_client_new ();
  jack_mgr_client_set_id (client, id);
  jack_mgr_client_set_name (client, jack_client_name);
  client->old_patches = jack_patches;
  
  jack_mgr->clients = cca_list_append (jack_mgr->clients, client);
  
  /* check if it's registered some ports already */
  jack_mgr_check_client_ports (jack_mgr, client);

  CCA_DEBUG ("client added");
}

cca_list_t *
jack_mgr_remove_client (jack_mgr_t * jack_mgr, uuid_t id)
{
  jack_mgr_client_t * client;
  cca_list_t * backup_patches;
  
  CCA_DEBUG ("removing client");
  
  client = jack_mgr_find_client (jack_mgr, id);
  if (!client)
    {
      fprintf (stderr, "%s: unknown client\n", __FUNCTION__);
      return NULL;
    }
    
  jack_mgr->clients = cca_list_remove (jack_mgr->clients, client);
  
  CCA_DEBUGARGS ("removed client '%s'", client->name);
  
  backup_patches = client->backup_patches;
  client->backup_patches = NULL;
  
  jack_mgr_client_destroy (client);
  
  return backup_patches;
}

cca_list_t *
jack_mgr_get_client_patches (jack_mgr_t * jack_mgr, uuid_t id)
{
  jack_mgr_client_t * client;
  cca_list_t * node;
  cca_list_t * uniq_node;
  cca_list_t * patches;
  cca_list_t * uniq_patches = NULL;
  jack_patch_t * patch;
  jack_patch_t * uniq_patch;
  
  client = jack_mgr_find_client (jack_mgr, id);
  if (!client)
    {
      fprintf (stderr, "%s: unknown client\n", __FUNCTION__);
      return NULL;
    }

  patches = jack_mgr_client_dup_patches (client);

  
  for (node = patches; node; node = cca_list_next (node))
    {
      patch = (jack_patch_t *) node->data;

      jack_patch_set (patch, jack_mgr->clients);

      for (uniq_node = uniq_patches; uniq_node; uniq_node = cca_list_next (uniq_node))
      {
        uniq_patch = (jack_patch_t *) uniq_node->data;

        if (strcmp (patch->src_client, uniq_patch->src_client) == 0 &&
            strcmp (patch->src_port, uniq_patch->src_port) == 0 &&
            strcmp (patch->dest_client, uniq_patch->dest_client) == 0 &&
            strcmp (patch->dest_port, uniq_patch->dest_port) == 0)
          break;
      }

      if (uniq_node)
      jack_patch_destroy (patch);
      else
      uniq_patches = cca_list_append (uniq_patches, patch);
    }
  
  cca_list_free (patches);
  
  return uniq_patches;
}

/* caller must hold client lock */
static void
jack_mgr_add_foreign_port (jack_mgr_t * jack_mgr, jack_fport_t * port)
{
  jack_mgr->foreign_ports = cca_list_append (jack_mgr->foreign_ports, port);
}

static void
jack_mgr_remove_foreign_port (jack_mgr_t * jack_mgr, jack_port_id_t id)
{
  jack_fport_t * port;
  cca_list_t * list;
  
  for (list = jack_mgr->foreign_ports; list; list = cca_list_next (list))
    {
      port = (jack_fport_t *) list->data;
      
      if (id == port->id)
        {
          jack_mgr->foreign_ports = cca_list_remove (jack_mgr->foreign_ports, port);
          
          CCA_DEBUGARGS ("removed foreign port '%s'", port->name);
          jack_fport_destroy (port);
          
          return;
        }
    }
}

static int
jack_mgr_resume_patch (jack_mgr_t * jack_mgr, jack_patch_t * patch)
{
  int err;
  char * src;
  char * dest;

  src = cca_strdup (jack_patch_get_src (patch));
  dest = cca_strdup (jack_patch_get_dest (patch));

  err = jack_connect (jack_mgr->jack_client, src, dest);
  if (err)
    {
      CCA_DEBUGARGS ("could not (yet?) resume patch %s", jack_patch_get_desc (patch));
    }
  else
    printf ("Connected JACK port '%s' to '%s'\n", src, dest);

  free (src);
  free (dest);

  return err;
}

/*
 * Here we have to go through each patch, see if it's the specified port,
 * unset it, see if we can connect it, and remove it.  Other clients will
 * have connections for this one if they're not registered yet.
 */
static void
jack_mgr_new_client_port (jack_mgr_t * jack_mgr, const char * port, jack_mgr_client_t * client)
{
  cca_list_t * node;
  jack_patch_t * patch, * unset_patch = NULL;
  int err;
  
  for (node = client->old_patches; node; )
    {
      patch = (jack_patch_t *) node->data;
      node = cca_list_next (node);

      CCA_DEBUGARGS ("checking patch %s", jack_patch_get_desc (patch));

      unset_patch = jack_patch_dup (patch);
      err = jack_patch_unset (unset_patch, jack_mgr->clients);
      if (err)
        {
          CCA_DEBUGARGS ("could not resume jack patch '%s' as the other client hasn't registered yet",
                         jack_patch_get_desc (patch));
          client->old_patches = cca_list_remove (client->old_patches, patch);
          jack_patch_destroy (patch);
        jack_patch_destroy (unset_patch);
          continue;
        }

      if ( (strcmp (client->name, unset_patch->src_client) == 0 &&
            strcmp (port, unset_patch->src_port) == 0             ) ||
         (strcmp (client->name, unset_patch->dest_client) == 0 &&
            strcmp (port, unset_patch->dest_port) == 0             ))
        {
        CCA_DEBUGARGS ("attempting to resume patch %s", jack_patch_get_desc (unset_patch));
          err = jack_mgr_resume_patch (jack_mgr, unset_patch);
        if (!err)
          {
            client->old_patches = cca_list_remove (client->old_patches, patch);
            jack_patch_destroy (patch);
          }
        }

      jack_patch_destroy (unset_patch);
    }
}

static void
jack_mgr_port_notification (jack_mgr_t * jack_mgr, jack_port_id_t port_id)
{
  jack_mgr_client_t * client;
  jack_port_t * jack_port;
  char * client_name;
  cca_list_t * list;
  char * ptr;
  
  /* get the client name */
  jack_port = jack_port_by_id (jack_mgr->jack_client, port_id);
  if (!jack_port)
    {
      fprintf (stderr, "%s: jack port registration notification for non-existant port\n", __FUNCTION__);
      return;
    }
  
  CCA_DEBUGARGS ("recieved port registration notification for port '%u'", port_id);
    
  client_name = cca_strdup (jack_port_name (jack_port));
  ptr = strchr (client_name, ':');
  if (!ptr)
    {
      fprintf (stderr, "%s: port name '%s' contains no client name/port name seperator\n",
               __FUNCTION__, client_name);
      return;
    }
  *ptr = '\0';
  
  for (list = jack_mgr->clients; list; list = cca_list_next (list))
    {
      client = (jack_mgr_client_t *) list->data;
      
      if (strcmp (jack_mgr_client_get_name (client), client_name) == 0)
        break;
    }

  if (!list)
    {
      jack_fport_t * fport;

      CCA_DEBUGARGS ("adding foreign port '%s'", jack_port_name (jack_port));
      fport = jack_fport_new_with_id (port_id);
      jack_fport_set_name (fport, jack_port_name (jack_port));
      
      jack_mgr_add_foreign_port (jack_mgr, fport);
      
      return;
    }
  
  ptr++;
  jack_mgr_new_client_port (jack_mgr, ptr, client);

  
  free (client_name);
}

cca_list_t *
jack_mgr_get_patches_with_type (const char * client_name, jack_client_t * jack_client, int type_flags)
{
  jack_patch_t * patch;
  char * port_name_regex;
  jack_port_t * port;
  char ** port_names;
  char ** connected_port_names;
  int i, j;
  cca_list_t * list = NULL;
  
  port_name_regex = cca_malloc (strlen (client_name) + 4);
  sprintf (port_name_regex, "%s:.*", client_name);
  
  port_names = (char **) jack_get_ports (jack_client, port_name_regex, NULL, type_flags);
  free (port_name_regex);
  
  if (!port_names)
    return NULL;
  
  for (i = 0; port_names[i]; i++)
    {
      port = jack_port_by_name (jack_client, port_names[i]);
      
      connected_port_names = (char **) jack_port_get_all_connections (jack_client, port);
      
      if (!connected_port_names)
        continue;
      
      for (j = 0; connected_port_names[j]; j++)
        {
          patch = jack_patch_new ();
          
          jack_patch_set_src (patch, port_names[i]);
          jack_patch_set_dest (patch, connected_port_names[j]);
          if (type_flags & JackPortIsInput)
            jack_patch_switch_clients (patch);
          
          list = cca_list_append (list, patch);
          CCA_DEBUGARGS ("found patch: '%s'/'%s'", jack_patch_get_src (patch),  jack_patch_get_dest (patch));
        }
      
/*      free (connected_port_names); */
    }
  
/*  free (port_names); */
  
  return list;
}


static void
jack_mgr_get_patches (jack_mgr_t * jack_mgr, jack_mgr_client_t * client)
{
  cca_list_t * patches = NULL;

  jack_mgr_client_free_patches (client);
  
  patches = jack_mgr_get_patches_with_type (
              client->name,
              jack_mgr->jack_client,
              JackPortIsInput);
  client->patches = patches;

  patches = jack_mgr_get_patches_with_type (
              client->name,
              jack_mgr->jack_client,
              JackPortIsOutput);
  
  client->patches = cca_list_concat (client->patches, patches);
}

static void
jack_mgr_check_foreign_ports (jack_mgr_t * jack_mgr)
{
  cca_list_t * list;
  jack_fport_t * fport;
  jack_port_t * port;

  for (list = jack_mgr->foreign_ports; list;)
    {
      fport = (jack_fport_t *) list->data;
      list = cca_list_next (list);
      
      port = jack_port_by_id (jack_mgr->jack_client, jack_fport_get_id (fport));
      
      if (!port)
        {
          jack_mgr->foreign_ports = cca_list_remove (jack_mgr->foreign_ports, fport);
          CCA_DEBUGARGS ("removed foreign port '%s'", fport->name);

          jack_fport_destroy (fport);
        }
    }
}

static void
jack_mgr_graph_reorder_notification (jack_mgr_t * jack_mgr)
{
  cca_list_t * list;
  jack_mgr_client_t * client;

  for (list = jack_mgr->clients; list; list = cca_list_next (list))
    {
      client = (jack_mgr_client_t *) list->data;
      
      jack_mgr_get_patches (jack_mgr, client);
    }
  
  jack_mgr_check_foreign_ports (jack_mgr);
}


static void
jack_mgr_read_callback (jack_mgr_t * jack_mgr)
{
  char port_notify;
  char registered;
  jack_port_id_t port_id;
  int err;

  err = read (jack_mgr->callback_read_socket, &port_notify, sizeof (port_notify));
  if (err == -1)
    {
      fprintf (stderr, "%s: could not read data from callback socket; aborting: %s\n",
               __FUNCTION__, strerror (errno));
      abort ();
    }
    
  if (port_notify)
    {
      CCA_DEBUG ("jack port reg callback happened");

      err = read (jack_mgr->callback_read_socket, &port_id, sizeof (port_id));
      if (err == -1)
        {
          fprintf (stderr, "%s: could not read data from callback socket; aborting: %s\n",
                   __FUNCTION__, strerror (errno));
          abort ();
        }

      err = read (jack_mgr->callback_read_socket, &registered, sizeof (registered));
      if (err == -1)
        {
          fprintf (stderr, "%s: could not read data from callback socket; aborting: %s\n",
                   __FUNCTION__, strerror (errno));
          abort ();
        }
      
      if (registered)
        jack_mgr_port_notification (jack_mgr, port_id);
      else
        jack_mgr_remove_foreign_port (jack_mgr, port_id);
    }
  else
    {
      CCA_DEBUG ("jack graph order callback happened");

      jack_mgr_graph_reorder_notification (jack_mgr);
    }
}

static void
jack_mgr_backup_patches (jack_mgr_t * jack_mgr)
{
  static time_t last_backup = 0;
  time_t now;
  cca_list_t * list, * patches;
  jack_mgr_client_t * client;
  jack_patch_t * patch;
  
  now = time (NULL);
  
  if (now - last_backup < BACKUP_INTERVAL)
    return;
    
  for (list = jack_mgr->clients; list; list = cca_list_next (list))
    {
      client = (jack_mgr_client_t *) list->data;
      
      jack_mgr_client_free_backup_patches (client);
      client->backup_patches = jack_mgr_client_dup_patches (client);
      
      
      for (patches = client->backup_patches; patches; patches = cca_list_next (patches))
        {
          patch = (jack_patch_t *) patches->data;
          
          jack_patch_set (patch, jack_mgr->clients);
        }
    }
  
  last_backup = now;
}

static void *
jack_mgr_callback_run (void * data)
{
  jack_mgr_t * jack_mgr;
  fd_set socket_set;
  fd_set select_set;
  struct timeval timeout;
  int err;
  
  jack_mgr = (jack_mgr_t *) data;
  
  FD_ZERO (&socket_set);
  FD_SET (jack_mgr->callback_read_socket, &socket_set);
  
  while (!jack_mgr->quit)
    {
      /* backup timeout */
      timeout.tv_sec  = BACKUP_INTERVAL;
      timeout.tv_usec = 0;
      
      select_set = socket_set;
      
      err = select (jack_mgr->callback_read_socket + 1, &select_set, NULL, NULL, &timeout);
      
      if (err == -1)
        {
          if (errno == EINTR)
            continue;

          fprintf (stderr, "%s: error calling select(): %s\n", __FUNCTION__, strerror (errno));
        }
      
      if (jack_mgr->quit)
        break;
      

      jack_mgr_lock (jack_mgr);

      if (FD_ISSET (jack_mgr->callback_read_socket, &select_set))
        jack_mgr_read_callback (jack_mgr);
      
      jack_mgr_backup_patches (jack_mgr);

      jack_mgr_unlock (jack_mgr);
    }
    
  CCA_DEBUG ("finished");
  return NULL;
}

/* EOF */

Generated by  Doxygen 1.6.0   Back to index