Logo Search packages:      
Sourcecode: jack-audio-connection-kit version File versions  Download package

hammerfall.c

/*
    Copyright (C) 2001 Paul Davis 

    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.

    $Id: hammerfall.c,v 1.5 2003/08/29 00:06:30 trutkin Exp $
*/

#include <jack/hardware.h>
#include "alsa_driver.h"
#include "hammerfall.h"
#include <jack/internal.h>

/* Set this to 1 if you want this compile error:
 *   warning: `hammerfall_monitor_controls' defined but not used */
#define HAMMERFALL_MONITOR_CONTROLS 0

static void 
set_control_id (snd_ctl_elem_id_t *ctl, const char *name)
{
      snd_ctl_elem_id_set_name (ctl, name);
      snd_ctl_elem_id_set_numid (ctl, 0);
      snd_ctl_elem_id_set_interface (ctl, SND_CTL_ELEM_IFACE_PCM);
      snd_ctl_elem_id_set_device (ctl, 0);
      snd_ctl_elem_id_set_subdevice (ctl, 0);
      snd_ctl_elem_id_set_index (ctl, 0);
}

#if HAMMERFALL_MONITOR_CONTROLS
static void
hammerfall_broadcast_channel_status_change (hammerfall_t *h, int lock, int sync, channel_t lowchn, channel_t highchn)

{
      channel_t chn;
      ClockSyncStatus status = 0;

      if (lock) {
            status |= Lock;
      } else {
            status |= NoLock;
      }

      if (sync) {
            status |= Sync;
      } else {
            status |= NoSync;
      }

      for (chn = lowchn; chn < highchn; chn++) {
            alsa_driver_set_clock_sync_status (h->driver, chn, status);
      }
}

static void
hammerfall_check_sync_state (hammerfall_t *h, int val, int adat_id)

{
      int lock;
      int sync;

      /* S/PDIF channel is always locked and synced, but we only
         need tell people once that this is TRUE.

         XXX - maybe need to make sure that the rate matches our
         idea of the current rate ?
      */

      if (!h->said_that_spdif_is_fine) {
            ClockSyncStatus status;
            
            status = Lock|Sync;

            /* XXX broken! fix for hammerfall light ! */

            alsa_driver_set_clock_sync_status (h->driver, 24, status);
            alsa_driver_set_clock_sync_status (h->driver, 25, status);

            h->said_that_spdif_is_fine = TRUE;
      }

      lock = (val & 0x1) ? TRUE : FALSE;
      sync = (val & 0x2) ? TRUE : FALSE;
      
      if (h->lock_status[adat_id] != lock ||
          h->sync_status[adat_id] != sync) {
            hammerfall_broadcast_channel_status_change (h, lock, sync, adat_id*8, (adat_id*8)+8);
      }

      h->lock_status[adat_id] = lock;
      h->sync_status[adat_id] = sync;
}

static void
hammerfall_check_sync (hammerfall_t *h, snd_ctl_elem_value_t *ctl)

{
      const char *name;
      int val;
      snd_ctl_elem_id_t *ctl_id;
      
      printf ("check sync\n");

      snd_ctl_elem_id_alloca (&ctl_id);
      snd_ctl_elem_value_get_id (ctl, ctl_id);

      name = snd_ctl_elem_id_get_name (ctl_id);

      if (strcmp (name, "ADAT1 Sync Check") == 0) {
            val = snd_ctl_elem_value_get_enumerated (ctl, 0);
            hammerfall_check_sync_state (h, val, 0);
      } else if (strcmp (name, "ADAT2 Sync Check") == 0) {
            val = snd_ctl_elem_value_get_enumerated (ctl, 0);
            hammerfall_check_sync_state (h, val, 1);
      } else if (strcmp (name, "ADAT3 Sync Check") == 0) {
            val = snd_ctl_elem_value_get_enumerated (ctl, 0);
            hammerfall_check_sync_state (h, val, 2);
      } else {
            jack_error ("Hammerfall: unknown control \"%s\"", name);
      }
}
#endif /* HAMMERFALL_MONITOR_CONTROLS */

static int 
hammerfall_set_input_monitor_mask (jack_hardware_t *hw, unsigned long mask)
{
      hammerfall_t *h = (hammerfall_t *) hw->private;
      snd_ctl_elem_value_t *ctl;
      snd_ctl_elem_id_t *ctl_id;
      int err;
      int i;
      
      snd_ctl_elem_value_alloca (&ctl);
      snd_ctl_elem_id_alloca (&ctl_id);
      set_control_id (ctl_id, "Channels Thru");
      snd_ctl_elem_value_set_id (ctl, ctl_id);
      
      for (i = 0; i < 26; i++) {
            snd_ctl_elem_value_set_integer (ctl, i, (mask & (1<<i)) ? 1 : 0);
      }
      
      if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) != 0) {
            jack_error ("ALSA/Hammerfall: cannot set input monitoring (%s)", snd_strerror (err));
            return -1;
      }
      
      hw->input_monitor_mask = mask;

      return 0;
}

static int 
hammerfall_change_sample_clock (jack_hardware_t *hw, SampleClockMode mode) 
{
      hammerfall_t *h = (hammerfall_t *) hw->private;
      snd_ctl_elem_value_t *ctl;
      snd_ctl_elem_id_t *ctl_id;
      int err;

      snd_ctl_elem_value_alloca (&ctl);
      snd_ctl_elem_id_alloca (&ctl_id);
      set_control_id (ctl_id, "Sync Mode");
      snd_ctl_elem_value_set_id (ctl, ctl_id);

      switch (mode) {
      case AutoSync:
            snd_ctl_elem_value_set_enumerated (ctl, 0, 0);
            break;
      case ClockMaster:
            snd_ctl_elem_value_set_enumerated (ctl, 0, 1);
            break;
      case WordClock:
            snd_ctl_elem_value_set_enumerated (ctl, 0, 2);
            break;
      }

      if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) < 0) {
            jack_error ("ALSA-Hammerfall: cannot set clock mode");
      }

      return 0;
}

static void
hammerfall_release (jack_hardware_t *hw)

{
      hammerfall_t *h = (hammerfall_t *) hw->private;
      void *status;

      if (h == 0) {
            return;
      }

      pthread_cancel (h->monitor_thread);
      pthread_join (h->monitor_thread, &status);

      free (h);
}

#if HAMMERFALL_MONITOR_CONTROLS
static void *
hammerfall_monitor_controls (void *arg)
{
      jack_hardware_t *hw = (jack_hardware_t *) arg;
      hammerfall_t *h = (hammerfall_t *) hw->private;
      snd_ctl_elem_id_t *switch_id[3];
      snd_ctl_elem_value_t *sw[3];

      pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

      snd_ctl_elem_id_malloc (&switch_id[0]);
      snd_ctl_elem_id_malloc (&switch_id[1]);
      snd_ctl_elem_id_malloc (&switch_id[2]);

      snd_ctl_elem_value_malloc (&sw[0]);
      snd_ctl_elem_value_malloc (&sw[1]);
      snd_ctl_elem_value_malloc (&sw[2]);

      set_control_id (switch_id[0], "ADAT1 Sync Check");
      set_control_id (switch_id[1], "ADAT2 Sync Check");
      set_control_id (switch_id[2], "ADAT3 Sync Check");

      snd_ctl_elem_value_set_id (sw[0], switch_id[0]);
      snd_ctl_elem_value_set_id (sw[1], switch_id[1]);
      snd_ctl_elem_value_set_id (sw[2], switch_id[2]);

      while (1) {
            if (snd_ctl_elem_read (h->driver->ctl_handle, sw[0])) {
                  jack_error ("cannot read control switch 0 ...");
            }
            hammerfall_check_sync (h, sw[0]);

            if (snd_ctl_elem_read (h->driver->ctl_handle, sw[1])) {
                  jack_error ("cannot read control switch 0 ...");
            }
            hammerfall_check_sync (h, sw[1]);

            if (snd_ctl_elem_read (h->driver->ctl_handle, sw[2])) {
                  jack_error ("cannot read control switch 0 ...");
            }
            hammerfall_check_sync (h, sw[2]);
            
            if (nanosleep (&h->monitor_interval, 0)) {
                  break;
            }
      }

      pthread_exit (0);
}
#endif /* HAMMERFALL_MONITOR_CONTROLS */

jack_hardware_t *
jack_alsa_hammerfall_hw_new (alsa_driver_t *driver)

{
      jack_hardware_t *hw;
      hammerfall_t *h;

      hw = (jack_hardware_t *) malloc (sizeof (jack_hardware_t));

      hw->capabilities = Cap_HardwareMonitoring|Cap_AutoSync|Cap_WordClock|Cap_ClockMaster|Cap_ClockLockReporting;
      hw->input_monitor_mask = 0;
      hw->private = 0;

      hw->set_input_monitor_mask = hammerfall_set_input_monitor_mask;
      hw->change_sample_clock = hammerfall_change_sample_clock;
      hw->release = hammerfall_release;

      h = (hammerfall_t *) malloc (sizeof (hammerfall_t));

      h->lock_status[0] = FALSE;
      h->sync_status[0] = FALSE;
      h->lock_status[1] = FALSE;
      h->sync_status[1] = FALSE;
      h->lock_status[2] = FALSE;
      h->sync_status[2] = FALSE;
      h->said_that_spdif_is_fine = FALSE;
      h->driver = driver;

      h->monitor_interval.tv_sec = 1;
      h->monitor_interval.tv_nsec = 0;

      hw->private = h;

#if 0
      if (pthread_create (&h->monitor_thread, 0, hammerfall_monitor_controls, hw)) {
            jack_error ("ALSA/Hammerfall: cannot create sync monitor thread");
      }
#endif

      return hw;
}

Generated by  Doxygen 1.6.0   Back to index