
/*
#    Sfront, a SAOL to C translator    
#    This file: Time synchronization routines for real-time input.
#    Copyright (C) 1999  Regents of the University of California
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License (Version 2) as
#    published by the Free Software Foundation.
#
#    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#    Primary Author: John Lazzaro, lazzaro@cs.berkeley.edu
*/


/******************************************************************/
/*                                                                */
/* This file defines two functions ksyncinit(), called before     */
/* the first ipass begins, and ksync(), called at the start      */
/* of every ipass. These routines act to ensure that each         */
/* sequence of IPASS/KPASS/APASS takes at least KUTIME. ksync     */
/* returns an estimate of cpuload, range [0.0:1.0], which codes   */
/* what fraction of the kcycle was used for computing audio.      */
/*                                                                */
/*                                                                */
/* Rules for writing your own timesync sync.c file:               */
/*                                                                */
/* (1) all globals and functions must begin with sync_, all defs  */
/*     must begin with SYNC_.                                     */
/* (2) Define both ksync() and ksyncinit().                       */
/*                                                                */
/* Note that:                                                     */
/*  KUTIME: a long,  defines k-cycle length in microseconds       */
/*  KMTIME: a float, defines k-cycle length in milliseconds       */
/*  KTIME:  a float, defines k-cycle length in seconds            */
/******************************************************************/


#include <sys/time.h>

#define SYNC_ONESECOND 1000000L
#define SYNC_SELDELAY 1000L
#define SYNC_NORM (1.0F/KUTIME)

struct timeval sync_last, sync_this, sync_delay;

/***********************************************************/
/*         synchronizes on k-rate boundaries               */
/***********************************************************/

float ksync()

{
  float ret = 1.0F;

  if ((sync_last.tv_usec += KUTIME) < SYNC_ONESECOND) /* non-rollover case */
    {
      gettimeofday(&sync_this, NULL);
      if (((sync_delay.tv_usec = sync_last.tv_usec - sync_this.tv_usec) >= 0)
	  && (sync_last.tv_sec == sync_this.tv_sec))
	{
	  ret -= SYNC_NORM*sync_delay.tv_usec;
	  ret = (ret > 0.0F) ? ret : 0.0F;

	  if ((sync_delay.tv_usec -= SYNC_SELDELAY) > 0)
	    select(0, NULL, NULL, NULL, &sync_delay);

	  do 
	    gettimeofday(&sync_this, NULL);
	  while (((sync_this.tv_sec == sync_last.tv_sec))
		 &&(sync_last.tv_usec > sync_this.tv_usec));
	}
      return ret;
    }

  sync_last.tv_sec++;
  sync_last.tv_usec %= SYNC_ONESECOND;

  gettimeofday(&sync_this, NULL);
  if ((sync_last.tv_sec > sync_this.tv_sec) ||
      ((sync_last.tv_sec == sync_this.tv_sec) &&
       ((sync_delay.tv_usec = sync_last.tv_usec - sync_this.tv_usec) >= 0)))
    {
      if (sync_last.tv_sec > sync_this.tv_sec)
	sync_delay.tv_usec = SYNC_ONESECOND  - sync_this.tv_usec
	  + sync_last.tv_usec;

      ret -= SYNC_NORM*sync_delay.tv_usec;
      ret = (ret > 0.0F) ? ret : 0.0F;

      if ((sync_delay.tv_usec -= SYNC_SELDELAY) > 0)
	select(0, NULL, NULL, NULL, &sync_delay);

      do
	gettimeofday(&sync_this, NULL);
      while ((sync_last.tv_sec > sync_this.tv_sec) || 
	     ((sync_last.tv_sec == sync_this.tv_sec) &&
	      (sync_last.tv_usec > sync_this.tv_usec)));
    }
  return ret;

}

/***********************************************************/
/*         initializes k-rate boundaries sync              */
/***********************************************************/

void ksyncinit()

{
  sync_delay.tv_sec = 0;
  gettimeofday(&sync_last, NULL);
}


