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

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> 
#include <errno.h>
extern int errno;
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>        /* For inet_ntoa. */
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "../include/string.h"
#include "../include/strexp.h"
#include "../include/disk.h"
#include "ymode.h"
#include "yhost.h"
#include "ynet.h"
#include "ymixer.h"
#include "rcfile.h"
#include "yiff.h"
#include "options.h"
#include "config.h"


int YAntiShift(int in);

void YiffSignalHandler(int sig);
void YiffResetMixer(void);

int YiffInit(int argc, char **argv);
void YiffShutdown(void);

int YiffCheckNewConnection(int socket);
void YiffCloseConnection(YConnectionNumber con_num);
void YiffManageConnections(void);

int YiffReadNextDSP(
      PlayStack *ps_ptr, int bytes_per_cycle
);
int YiffCreatePlaystack(
      const char *path,   
      YConnectionNumber owner,
      YID yid,  
      YDataPosition pos,
      YVolumeStruct *volume,
      int sample_rate,
      int repeats
);
void YiffDestroyPlaystack(int n);
void YiffManageSound(void);

void YiffUpdateTimers(void);
void YiffResetTimers(void);


#define MIN(a,b)        ((a) < (b) ? (a) : (b))
#define MAX(a,b)        ((a) > (b) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define ABSOLUTE(x)     (((x) < 0) ? ((x) * -1) : (x))

#define MINSC(a,b)      ((a < b) ? (char)127 : (char)(a))
#define MAXSC(a,b)      ((a > b) ? (char)-128 : (char)(a))


PlayStack **playstack;
int total_playstacks;

SoundPath **soundpath;
int total_soundpaths;

YConnection **yconnection;
int total_yconnections;

YMode **ymode;
int total_ymodes;

YHost **yhost;   
int total_yhosts;

yiff_option_struct option;
yiff_stats_struct ystats;
yiff_fname_struct fname;

int runlevel;
YTime cur_time;

yiff_next_struct next;

Recorder *recorder;

int listen_socket;



/*
 *    Local timings:
 */
#define MIN_NEXT_SOUND_DELTA  100   /* In microseconds. */
static time_t next_sound_delta_us;  /* In microseconds. */


/*
 *      Segfault counter.
 */
static int segfault_count;


/*
 *    Anti shift macro:
 */
int YAntiShift(int in)
{
      int s, i;

      for(s = 0, i = in; i; i >>= 1)
          s++;

      return(s);
}


/*
 *    Procedure to perform when a SIGHUP signal is recieved.
 */
void YiffHangupHandle(void)
{
      int i;

      /* Reload YIFF sound server configuration. */
      RCLoadFromFile(fname.rcfile);

      /* Delete all playstacks and notify owner connections about it. */
      for(i = 0; i < total_playstacks; i++)
          YiffDestroyPlaystack(i);

      /* Notify all connections about YSHM sound closing. */
      YNetDoNotifyAllYSHMSoundClose();

      /* Close sound device. */
      YSoundShutdown(recorder);


      /*   Do not free recorder (other functions may be reffering to it
       *   during threaded execution of this function.
       */


      /* Initialize recorder. */
      if(YSoundInit(recorder, &option.audio))
      {
          fprintf(stderr,
"Error: Unable to initialize recorder.\n\
To try again type: `kill -s HUP %i'\n\
To terminate type: `kill -s INT %i'\n",
            ystats.pid,
            ystats.pid
          );
          return;
      }

      /* Load mixer settings from file. */
      if(MixerRCLoadFromFile(fname.mixerrcfile, recorder))
      {
          /* Could not load settings from file, so reset mixer
           * values to defaults.
           */
          YiffResetMixer();
      }

      /* Update timers (to help sync audio device). */
      YiffUpdateTimers(); 

      /* Need to sync audio device right after initialization. */
      YSoundSync(recorder, 0);  

      /* Reset global next sound delta (use min). */
      next_sound_delta_us = MIN_NEXT_SOUND_DELTA;


      /* Schedual next refresh. */
      next.refresh.ms = cur_time.ms + option.refresh_int.ms;
      next.refresh.us = 0;


      return;
}

/*
 *    Signal handler.
 */
void YiffSignalHandler(int sig)
{
      switch(sig)
      {
        case SIGHUP:
          signal(SIGHUP, YiffSignalHandler);
          YiffHangupHandle();
          break;

        case SIGINT:
          runlevel = 1;
          break;

        case SIGKILL:  
          runlevel = 1;
          break;

        case SIGPIPE:
          signal(SIGPIPE, YiffSignalHandler);
          break;

        case SIGSEGV:
          /* Increment segfault counter. */
          segfault_count++;
          if(segfault_count >= 2)
            exit(-1);

          runlevel = 1;
          break;

        case SIGTERM:
          runlevel = 1;
          break;

        default:
          break;
      }

      return;
}

/*
 *    Procedure to reset mixer values.
 */
void YiffResetMixer(void)
{
      Coefficient value[YMixerValues];


      if(recorder == NULL)
          return;

      value[0] = 0.5; value[1] = 0.5; value[2] = 0.5; value[3] = 0.5;
      YMixerSet(recorder, YMixerCodeBass, value);
      value[0] = 0.75; value[1] = 0.75; value[2] = 0.75; value[3] = 0.75;
      YMixerSet(recorder, YMixerCodeCD, value);
      YMixerSet(recorder, YMixerCodeGainIn, value);
      YMixerSet(recorder, YMixerCodeGainOut, value);

      value[0] = 0.75; value[1] = 0.75; value[2] = 0.75; value[3] = 0.75;
      YMixerSet(recorder, YMixerCodeLine, value);
      YMixerSet(recorder, YMixerCodeLine1, value);
      YMixerSet(recorder, YMixerCodeLine2, value);
      YMixerSet(recorder, YMixerCodeLine3, value);

      value[0] = 0.15; value[1] = 0.15; value[2] = 0.15; value[3] = 0.15;
      YMixerSet(recorder, YMixerCodeMic, value);
      value[0] = 0.75; value[1] = 0.75; value[2] = 0.75; value[3] = 0.75;
      YMixerSet(recorder, YMixerCodeMix, value);
      YMixerSet(recorder, YMixerCodePCM, value);
      YMixerSet(recorder, YMixerCodePCM2, value);

      value[0] = 0.15; value[1] = 0.15; value[2] = 0.15; value[3] = 0.15;
      YMixerSet(recorder, YMixerCodeRec, value);
      value[0] = 0.75; value[1] = 0.75; value[2] = 0.75; value[3] = 0.75;
      YMixerSet(recorder, YMixerCodeSpeaker, value);
      YMixerSet(recorder, YMixerCodeSynth, value);
      value[0] = 0.5; value[1] = 0.5; value[2] = 0.5; value[3] = 0.5;
      YMixerSet(recorder, YMixerCodeTreble, value);
      value[0] = 0.75; value[1] = 0.75; value[2] = 0.75; value[3] = 0.75;
      YMixerSet(recorder, YMixerCodeVolume, value);
}


/*
 *    Initializes the YIFF Sound Server.
 *
 *    Return values:
 *
 *    0     Success.
 *    -1    General error.
 *
 *    -4    Help or version print, do not run.
 */
int YiffInit(int argc, char **argv)
{
      int i, n, status;
      char *strptr, *arg_ptr;
      struct sockaddr_in addr;
      char cwd[PATH_MAX];
      struct stat stat_buf;

      int override_device = 0;
      char device[PATH_MAX + NAME_MAX];
      int override_mixer = 0;
      char mixer[PATH_MAX + NAME_MAX];
      int override_port = 0;
      int port = -1;
      YIPUnion ip;


      /* First thing to do on initialization is to reset the
       * global segfault counter.
       */
      segfault_count = 0;  


      /* Handle basic command line arguments first, reset status
       * to 1. If any basic command line arguments were handled that
       * would require foreground running, set status to 0.
       */
      for(i = 1, status = 1; i < argc; i++)
      {
          arg_ptr = argv[i];
          if(arg_ptr == NULL)
            continue;

          if(!strcasecmp(arg_ptr, "--foreground") ||
             !strcasecmp(arg_ptr, "--fg") ||
             !strcasecmp(arg_ptr, "-foreground") ||
             !strcasecmp(arg_ptr, "-fg") ||
             strcasepfx(arg_ptr,"--h") ||
             strcasepfx(arg_ptr,"-h") ||
             strcasepfx(arg_ptr,"-?") ||
             strcasepfx(arg_ptr,"/?") ||
             strcasepfx(arg_ptr, "--ver") ||
             strcasepfx(arg_ptr, "-ver")
          )
          {
            status = 0;
            break;
          }
      }
      /* If no basic command line arguments were handled and was
       * not specified to run in foreground then status will be 1.
       */
      if(status)
      {
          /* Fork process into background. */
          switch(fork())
          {
            case -1:
            perror("fork");
            return(-1);
            break;

            case 0:   /* Child. */
            break;

            default:  /* Parent. */
            exit(0);
            break;
          }
      }


      /* Get current working dir. */
      getcwd(cwd, PATH_MAX);
      cwd[PATH_MAX - 1] = '\0';


      /* Reset global variables. */

      /* Options. */
      option.debug = False;
      option.port = DEF_PORT;

      option.audio.cycle_set = CYCLE_SET_USER;

      option.audio.cycle.ms = 30;
      option.audio.cycle.us = 0;

      option.audio.compensated_cycle.ms = option.audio.cycle.ms;
      option.audio.compensated_cycle.us = option.audio.cycle.us;

      option.audio.write_ahead.ms = 45;
      option.audio.write_ahead.us = 0;

      option.audio.cycle_ahead_left.ms = 0;
      option.audio.cycle_ahead_left.us = 0;

      option.audio.cumulative_latency.ms = 0;
      option.audio.cumulative_latency.us = 0;

      option.audio.sample_size = DEF_SAMPLE_SIZE;
      option.audio.channels = DEF_CHANNELS;
      option.audio.sample_rate = DEF_SAMPLE_RATE;

      option.audio.bytes_per_second = 0;  /* Recalculated later
                                     * in YSoundInit().
                                     */

#ifdef OSS_BUFFRAG
      option.audio.allow_fragments = True;
      option.audio.num_fragments = 2;
      option.audio.fragment_size = YAntiShift(512 - 1);
#endif  /* OSS_BUFFRAG */

      option.audio.flip_stereo = False;
      option.audio.direction = AUDIO_DIRECTION_PLAY;

      option.refresh_int.ms = 30000;            /* 30 seconds. */
      option.refresh_int.us = 0;

      option.midi_play_cmd = strdup(DEF_MIDI_CMD);

#ifdef ALSA_RUN_CONFORM
      option.midi_device_number = -1;     /* Set to -1 to mark as unset. */
#endif      /* ALSA_RUN_CONFORM */

      /* File names. */
      strncpy(fname.rcfile, DEF_RCFILE_NAME, PATH_MAX + NAME_MAX);
      fname.rcfile[PATH_MAX + NAME_MAX - 1] = '\0';

      strptr = getenv("HOME");
      if(strptr == NULL)
          strncpy(
            fname.mixerrcfile,
            PrefixPaths("/", DEF_RCMIXERFILE_NAME),
            PATH_MAX + NAME_MAX
          );
      else
          strncpy(
            fname.mixerrcfile,
            PrefixPaths(strptr, DEF_RCMIXERFILE_NAME),
            PATH_MAX + NAME_MAX
          );

      strncpy(fname.device, DEF_DEVICE, PATH_MAX + NAME_MAX);
      fname.device[PATH_MAX + NAME_MAX - 1] = '\0';

      strncpy(fname.mixer, DEF_MIXER, PATH_MAX + NAME_MAX);
      fname.mixer[PATH_MAX + NAME_MAX - 1] = '\0';

      /* Statistics. */
      ystats.pid = getpid();
      ystats.start_time = time(NULL);
      ystats.cycle_load = 0.0;


      /* Delete any existing sound paths (shouldn't be any). */
      SoundPathDeleteAll();

      /* Allocate current directory as first sound path. */
      i = SoundPathAllocate();
      if(SoundPathIsAllocated(i))
      {
          SoundPath *snd_path_ptr = soundpath[i];

          free(snd_path_ptr->path);
          snd_path_ptr->path = strdup(cwd);
      }


      /* Delete any existing yhosts (shouldn't be any). */
      YHostDeleteAll();

      /* Add localhost (127.0.0.1) to list of allowed hosts to
       * connect to us.
       */
      ip.charaddr[0] = 127;
      ip.charaddr[1] = 0;
      ip.charaddr[2] = 0;
      ip.charaddr[3] = 1;
      YHostAllocate(&ip);


      /* ****************************************************** */
      /* Parse arguments. */
      for(i = 1; i < argc; i++)
      {
          arg_ptr = argv[i];
          if(arg_ptr == NULL)
            continue;

          /* Help. */
          if(strcasepfx(arg_ptr, "--h") ||
             strcasepfx(arg_ptr, "-h") ||
             strcasepfx(arg_ptr, "-?") ||
             strcasepfx(arg_ptr, "/?")
          )
          {
            printf(PROG_HELP_MESG);
            return(-4);
          }
          /* Version. */
          else if(strcasepfx(arg_ptr, "--ver") ||
                strcasepfx(arg_ptr, "-ver")
          )
          {
            printf("%s Version %s\n", PROG_NAME, PROG_VERSION);
            printf("%s\n", PROG_COPYRIGHT);
            return(-4);
          }
          /* Device. */
          else if(strcasepfx(arg_ptr, "--d") ||
                strcasepfx(arg_ptr, "-d")
          )
          {
            i++;
            if(i < argc)
            {
                arg_ptr = argv[i];
                strncpy(device, arg_ptr, PATH_MAX + NAME_MAX);
                device[PATH_MAX + NAME_MAX - 1] = '\0';

                override_device = 1;
            }
            else
            {
                fprintf(stderr,
                  "%s: Requires argument.\n",
                  argv[i - 1]
                );
            }
          }
#ifdef ALSA_RUN_CONFORM
          /* MIDI device port number. */
          else if(strcasepfx(arg_ptr, "--midi_port"))
          {
            i++;
            if(i < argc)
            {
                arg_ptr = argv[i];
                option.midi_device_number = atoi(arg_ptr);
            }
            else
            {
                fprintf(stderr,
                  "%s: Requires argument.\n",
                  argv[i - 1]
                );
            }
          }
#endif      /* ALSA_RUN_CONFORM */
          /* Mixer settings file. */
          else if(strcasepfx(arg_ptr, "--mixer_rc") ||
                strcasepfx(arg_ptr, "-mixer_rc")
          )
          {
            i++;
            if(i < argc)
            {
                arg_ptr = argv[i];
                strncpy(fname.mixerrcfile, arg_ptr, PATH_MAX + NAME_MAX);
                fname.mixerrcfile[PATH_MAX + NAME_MAX - 1] = '\0';
            }
            else
            {   
                fprintf(stderr,
                  "%s: Requires argument.\n",
                  argv[i - 1]
                );
            }
          }
          /* Mixer. */
          else if(strcasepfx(arg_ptr, "--m") ||
                strcasepfx(arg_ptr, "-m")
          )
          {
            i++;
            if(i < argc)
            {
                arg_ptr = argv[i];
                strncpy(mixer, arg_ptr, PATH_MAX + NAME_MAX);
                mixer[PATH_MAX + NAME_MAX - 1] = '\0';

                override_mixer = 1;
            }
            else
            {
                fprintf(stderr,
                  "%s: Requires argument.\n",
                  argv[i - 1]
                );
            }
          }
          /* Port. */
          else if(strcasepfx(arg_ptr, "--po") ||
                strcasepfx(arg_ptr, "-po")
          )
          {
            i++;
            if(i < argc)
            {
                arg_ptr = argv[i];
                port = atoi(arg_ptr);
                override_port = 1;
            }   
            else
            {
                fprintf(stderr,
                  "%s: Requires argument.\n",
                  argv[i - 1]
                );
            }
          }
          /* Sound path. */
          else if(strcasepfx(arg_ptr, "--pa") ||
                strcasepfx(arg_ptr, "-pa")
          )
          {
            i++;
            if(i < argc)
            {
                arg_ptr = argv[i];

                n = SoundPathAllocate();
                if(SoundPathIsAllocated(n))
                {
                  free(soundpath[n]->path);
                  soundpath[n]->path = StringCopyAlloc(arg_ptr);

                  if(stat(soundpath[n]->path, &stat_buf))
                  {
                      fprintf(stderr,
 "%s: Warning: No such directory.\n",
                        soundpath[n]->path
                      );
                  }
                  else
                  {
                      if(!S_ISDIR(stat_buf.st_mode))
                        fprintf(stderr,
 "%s: Warning: Not a directory.\n",
                            soundpath[n]->path
                        );
                  }
                }
            }
            else
            {
                fprintf(stderr,
                  "%s: Requires argument.\n",
                  argv[i - 1]
                );
            }
          }
          /* All else, it's the rcfile. */
          else if(((*arg_ptr) != '\0') &&
                ((*arg_ptr) != '-') &&
                ((*arg_ptr) != '+')
          )
          {
            strncpy(fname.rcfile, arg_ptr, PATH_MAX + NAME_MAX);
            fname.rcfile[PATH_MAX + NAME_MAX - 1] = '\0';
          }
      }

      /* ****************************************************** */
      /* Load configuration. */
      status = RCLoadFromFile(fname.rcfile);
      if(status)
          return(-1);

      /*   Need to set current mode and options values to match
       *   default Audio mode.
       */
      n = 0;      /* Default audio mode is the first one. */
      if(YModeIsAllocated(n))
      {
          /* Set new Audio parameters to option's Audio structure
           * from Audio mode's values.
           */
          Audio *audio_ptr = &option.audio;
          YMode *ymode_ptr = ymode[n];


          /* Audio modes are always considered user set. */
          audio_ptr->cycle_set = CYCLE_SET_USER;

          audio_ptr->cycle.ms = ymode_ptr->cycle.ms;
          audio_ptr->cycle.us = ymode_ptr->cycle.us;

          /* This will be recalculated by YSoundInit() called
           * farther below.
           */
          audio_ptr->compensated_cycle.ms = audio_ptr->cycle.ms;
          audio_ptr->compensated_cycle.us = audio_ptr->cycle.us;

          audio_ptr->write_ahead.ms = ymode_ptr->write_ahead.ms;
          audio_ptr->write_ahead.us = ymode_ptr->write_ahead.us;

          /* This will be reset by YSoundInit(). */
          audio_ptr->cycle_ahead_left.ms = 0;
          audio_ptr->cycle_ahead_left.us = 0;

          /* This will be reset by YSoundInit(). */
          audio_ptr->cumulative_latency.ms = 0;
          audio_ptr->cumulative_latency.us = 0;

          audio_ptr->sample_size = ymode_ptr->sample_size;
          audio_ptr->channels = ymode_ptr->channels;
          audio_ptr->sample_rate = ymode_ptr->sample_rate;

          /* This will be recalculated by YSoundInit(). */
          audio_ptr->bytes_per_second = 0;

#ifdef OSS_BUFFRAG
          audio_ptr->allow_fragments = ymode_ptr->allow_fragments;
          audio_ptr->num_fragments = ymode_ptr->num_fragments;  
          audio_ptr->fragment_size = ymode_ptr->fragment_size;  
#endif  /* OSS_BUFFRAG */

          audio_ptr->flip_stereo = ymode_ptr->flip_stereo;
          audio_ptr->direction = ymode_ptr->direction;
      }
      else
      {
          fprintf(
            stderr,
            "Warning: First Y Audio Mode not defined.\n"
          );
      }

      /* Override device? */
      if(override_device)
      {
          strncpy(fname.device, device, PATH_MAX + NAME_MAX);
          fname.device[PATH_MAX + NAME_MAX - 1] = '\0';
      }
      /* Override mixer? */
      if(override_mixer)
      {
          strncpy(fname.mixer, mixer, PATH_MAX + NAME_MAX);
          fname.mixer[PATH_MAX + NAME_MAX - 1] = '\0';
      }
      /* Override port? */
      if(override_port)
      {
          option.port = port;
      }


      /* ****************************************************** */
      /* Set signals to watch. */
      signal(SIGHUP, YiffSignalHandler);
      signal(SIGINT, YiffSignalHandler);
      signal(SIGKILL, YiffSignalHandler);
      signal(SIGPIPE, YiffSignalHandler);
      signal(SIGSEGV, YiffSignalHandler);
      signal(SIGTERM, YiffSignalHandler);


      /* ****************************************************** */
      /* Reset timers. */
      YiffResetTimers();


      /* ****************************************************** */
      /* Allocate recorder structure and initialize sound. */
      recorder = (Recorder *)calloc(1, sizeof(Recorder));
      if(recorder == NULL)
      {
          fprintf(stderr, "Memory allocation error.");
          return(-1);
      }

      /* Initialize sound with the Audio parameters specified in
       * the option's Audio structure.
       */
      if(YSoundInit(recorder, &option.audio))
          return(-1);


      /* Load mixer settings from file. */
      if(MixerRCLoadFromFile(fname.mixerrcfile, recorder))
      {
          /* Could not load settings from file, so reset mixer
           * values to defaults.
           */
          YiffResetMixer();
      }


      /* ****************************************************** */
      /* Set up listening socket. */
      listen_socket = socket(AF_INET, SOCK_STREAM, 0);
      if(listen_socket < 0)
      {
          fprintf(   
            stderr,
            "Cannot create socket for listening to port %i.\n",
            option.port
          );
          fprintf(
            stderr,
"Make sure machine is network capable and can create AF_INET sockets.\n"
          );
          return(-1);
      }

      addr.sin_family = AF_INET;
      addr.sin_port = htons(option.port);
      addr.sin_addr.s_addr = INADDR_ANY;
      memset(&addr.sin_zero, 0, 8);

      /* Bind the socket. */
      status = bind(
          listen_socket,
          (struct sockaddr *)&addr,
          sizeof(struct sockaddr)
      );
      if(status)
      {
          fprintf(
            stderr,
            "Cannot bind listening socket to port %i.\n",
            option.port
          );
          fprintf(
            stderr,
"Make sure no other servers are currently using the port.\n"
          );
          return(-1);
      }

      status = listen(listen_socket, LISTEN_SOCKET_BACKLOG);
      if(status)
      {
          fprintf(
            stderr,
            "Cannot listen to socket on port %i.\n",
            option.port
          );
          fprintf(
            stderr,
"Make sure no other servers are currently using the port.\n"
          );
          return(-1);
      }

      /* Set listening port socket to nonblocking. */
      fcntl(listen_socket, F_SETFL, O_NONBLOCK);


      /* ********************************************************** */
      /* Update timers (to help sync audio device). */
      YiffUpdateTimers();

      /* Need to sync audio device right after initialization. */
      YSoundSync(recorder, 0);

      /* Reset global next sound delta (use min). */
      next_sound_delta_us = MIN_NEXT_SOUND_DELTA;


      /* schedual next refresh. */
      next.refresh.ms = cur_time.ms + option.refresh_int.ms;
      next.refresh.us = 0;


      return(0);
}



void YiffShutdown(void)
{
      int i;
      YConnection *con;


      /* Save mixer settings. */
      MixerRCSaveToFile(fname.mixerrcfile, recorder);


      /* Notify all connections about YSHM sound closing. */
      YNetDoNotifyAllYSHMSoundClose();

      /* Notify all connections about shutdown by sending a
       * YShutdown event to all connections.
       */
      for(i = 0; i < total_yconnections; i++)
      {
          con = yconnection[i];
          if((con != NULL) ? (con->socket < 0) : True)
            continue;

          YNetSendShutdown(i, 0);
      }


      /* Free all sound paths. */
      SoundPathDeleteAll();

      /* Free all play stacks. */
      PlayStackDeleteAll();

      /* Deallocate recorder structure and shutdown sound. */
      YSoundShutdown(recorder);
      free(recorder);
      recorder = NULL;


      /* Close listening socket. */
      if(listen_socket > -1)
      {
#ifndef SHUT_RDWR
# define SHUT_RDWR      2
#endif
          shutdown(listen_socket, SHUT_RDWR);
          listen_socket = -1;
      }

      /* Disconnect all Y clients and free all Y connection
       * structures. This will NOT send a YDisconnect event to any of
       * the connections however the above YShutdown event should have
       * been recieved by the Y client(s) and they should have handled
       * it properly.
       */
      ConnectionDeleteAll();

      /* Free all yhosts. */
      YHostDeleteAll();

      /* Free all ymodes. */
      YModeDeleteAll();


      /* Begin deallocating options. */

      /* Free midi play command. */
      free(option.midi_play_cmd);
      option.midi_play_cmd = NULL;
}


/*
 *    Check for and handle any incoming connections.
 */
int YiffCheckNewConnection(int socket)
{
      int i;
      YConnection *con_ptr;

      struct timeval timeout;
      fd_set readfds;
      int new_socket;

      int sin_size;
      struct sockaddr_in foreign_addr;


      timeout.tv_sec = 0;
      timeout.tv_usec = 0;
      FD_ZERO(&readfds);
      FD_SET(socket, &readfds);
      select(socket + 1, &readfds, NULL, NULL, &timeout);
  
      if(!FD_ISSET(socket, &readfds))
          return(0);

      /* Handle new connection. */
      sin_size = sizeof(struct sockaddr_in);
      new_socket = accept(
          socket,
          (struct sockaddr *)&foreign_addr,
          &sin_size
      );
      if(new_socket == -1)
          return(-1);
      /* Set new socket non-blocking. */
      fcntl(new_socket, F_SETFL, O_NONBLOCK);


      /* Allocate a new connection structure. */
      i = ConnectionAllocate();
      con_ptr = ConnectionGetPtr(i);
      if(con_ptr == NULL)
      {
          close(new_socket);
          new_socket = -1;

          return(-1);
      }     


      /* Set up connection. */
      con_ptr->socket = new_socket;
      con_ptr->ip.whole = foreign_addr.sin_addr.s_addr;

/*
printf("Server: Got connection from %i.%i.%i.%i.\n",
 con_ptr->ip.charaddr[0],
 con_ptr->ip.charaddr[1],
 con_ptr->ip.charaddr[2],
 con_ptr->ip.charaddr[3]
);
*/

      /* Is incoming connection's host allowed? */
      if(!YHostInList(&con_ptr->ip))
      {
/*
          fprintf(
 "Y Server: Connection %i.%i.%i.%i denied, not in allowed YHosts list.\n",
             con_ptr->ip.charaddr[0],
             con_ptr->ip.charaddr[1],
             con_ptr->ip.charaddr[2],
             con_ptr->ip.charaddr[3]
          );
 */

          /* Send disconnect reason. */
          YNetSendDisconnect(i, 0);

          /* Close and delete connection. */
          close(con_ptr->socket);
          con_ptr->socket = -1;
          ConnectionDelete(i);
      }


      return(0);
}


/*
 *    Closes connection con_num and deallocates its owned
 *    resources.
 */
void YiffCloseConnection(YConnectionNumber con_num)
{
      int i;
      YConnection *con_ptr;
      PlayStack *ps;


      con_ptr = ConnectionGetPtr(con_num);
      if(con_ptr == NULL)
          return;


      /* Delete all playstacks associated with this connection. */
      for(i = 0; i < total_playstacks; i++)
      {
          ps = playstack[i];
          if(ps == NULL)
            continue;

          if(ps->owner == con_num)
            YiffDestroyPlaystack(i);
      }


      /* Notify connection about disconnect. */
      if(con_ptr->socket > -1)
          YNetSendDisconnect(con_num, 0);

      /* Close connection. */
      if(con_ptr->socket > -1)
      {
          close(con_ptr->socket);
          con_ptr->socket = -1;
      }


      /* Delete this connection structure and all its allocated
       * resources.
       */
      ConnectionDelete(con_num);
}

/*
 *    Check for incoming connections, handle them and
 *    manage each existing connection.
 */
void YiffManageConnections(void)
{
      int i;
      YConnection *con;


      /* Check for and handle any new incoming connections. */
      YiffCheckNewConnection(listen_socket);

      /* Handle each connection for incoming data. */
      for(i = 0; i < total_yconnections; i++)
      {
          con = yconnection[i];
          if((con != NULL) ? (con->socket < 0) : True)
            continue;

          YNetRecv(i);
      }
}


/*
 *      Procedure to read next segment of data for the given play
 *      stack. The sound object format must be a SndObjTypeDSP.
 *
 *    Global recorder is assumed not NULL when this function is called.
 *
 *    Return values are as follows:
 *
 *    0     Success
 *    -1    General error or play stack has ended and needs to be
 *          deleted
 */
int YiffReadNextDSP(
      PlayStack *ps_ptr,
      int bytes_per_cycle     /* Actual bytes per cycle. */
)
{
      int status = 0;
      int bytes_read;
      int actual_bytes_per_cycle;
      AFWDataStruct *afw_ptr;
      Coefficient sample_rate_dev_coeff;


      if((ps_ptr == NULL) ||
         (bytes_per_cycle < 1)
      )
          return(-1);

      /* Get pointer to audio file wrapper data structure. */
      afw_ptr = &ps_ptr->afw_data;


      /* Skip if play stack volume is 0.0, just load empty
       * (garbage) data.
       */
      if((ps_ptr->volume_left <= 0.0) &&
         (ps_ptr->volume_right <= 0.0)
      )
      {
          /* Repeats exceeded? */
          if((ps_ptr->total_repeats >= 0) &&
             (ps_ptr->repeats >= ps_ptr->total_repeats)
          )
            return(-1);

          /* Audio file data length allocation differs from
           * current Sound buffer length?
           */
          if(afw_ptr->length != bytes_per_cycle)
          {
            /* Reallocate audio file wrapper structure data
             * buffer to match current Sound buffer length.
             * So it looks like we loaded something (but just garbage).
             */
            afw_ptr->length = bytes_per_cycle;

            afw_ptr->buffer = (SoundBuffer *)realloc(
                afw_ptr->buffer,
                afw_ptr->length * sizeof(SoundBuffer)
            );
            if(afw_ptr->buffer == NULL)
            {
                afw_ptr->length = 0;
                return(-1);
            }
          }

          /* Increment position as if we're still reading. */
          ps_ptr->position += bytes_per_cycle;
          if(ps_ptr->position >= ps_ptr->data_length)
          {
            ps_ptr->position = ps_ptr->position - ps_ptr->data_length;
            ps_ptr->repeats++;
          }

          /* Return success now, no additional mixing or cpu
           * resource waste.
           */
          return(0);
      }


      /* Record actual length of Sound buffer in bytes. */
      actual_bytes_per_cycle = bytes_per_cycle;

      /* Calculate the sample rate deviance adjusted bytes per cycle. */
      sample_rate_dev_coeff = (Coefficient)ps_ptr->applied_sample_rate /
          (Coefficient)MAX(recorder->audio.sample_rate, 1);
      if(sample_rate_dev_coeff < 1.0)
          sample_rate_dev_coeff = 1.0;
      bytes_per_cycle = (int)((Coefficient)bytes_per_cycle *
          sample_rate_dev_coeff
      );
      /* Round up bytes_per_cycle to an even number if it is odd. */
      if(bytes_per_cycle & 1)
          bytes_per_cycle++;
      /* Bytes per cycle cannot be less than actual bytes per cycle. */
      if(bytes_per_cycle < actual_bytes_per_cycle)
          bytes_per_cycle = actual_bytes_per_cycle;

      /* Note: bytes_per_cycle is the sample rate deviance adjusted
       * bytes per cycle which should be equal or greater than
       * actual_bytes_per_cycle. bytes_per_cycle should also
       * indicate the allocated dsp buffer on the playstack.
       *
       * actual_bytes_per_cycle is the actual bytes per cycle
       * of the Sound buffer.
       */


      /* Increase playstack DSP buffer allocation size as needed to fit
       * sample rate deviance applied bytes per cycle.
       */
      if((ps_ptr->dsp_buf == NULL) ||
         (ps_ptr->dsp_buf_len != bytes_per_cycle)
      )
      {
          ps_ptr->dsp_buf_len = bytes_per_cycle;
          ps_ptr->dsp_buf = (SoundBuffer *)realloc(
            ps_ptr->dsp_buf,
            ps_ptr->dsp_buf_len * sizeof(SoundBuffer)
          );
          if(ps_ptr->dsp_buf == NULL)
          {
            ps_ptr->dsp_buf_len = 0;
            ps_ptr->dsp_buf_data_len = 0;
            return(-1);
          }
      }


      /* Is this play stack repeating just once? */
      if(ps_ptr->total_repeats == 1)
      {
          /* Play stack plays once without any repeats. */

          /* Gone through entire segment of data (check inclusive)? */
          if(ps_ptr->position >= ps_ptr->data_length)
            return(-1);

          /* Read next buffer segment. */
          if(
            AFWLoadSegment(
                afw_ptr,
                ps_ptr->position,
                bytes_per_cycle,
                &recorder->audio
            )
          )
              return(-1);

          /* Get number of bytes read by checking the audio file
           * wrapper structure buffer length.
           */
          bytes_read = MAX(afw_ptr->length, 0);
          if(bytes_read == 0)
          {
            /* No bytes read implies finished play. Set playstack
             * position to match length of audio data so it will
             * later be checked and handled as `finished'.
             */
            ps_ptr->position = ps_ptr->data_length;
          }

          /* Need to match Sound buffer length with number of
           * bytes read (which can be less if this is the last
           * segment read on the audio file).
           */
          bytes_per_cycle = bytes_read;

          /* Increment play stack position. */
          ps_ptr->position += bytes_read;


          /* Copy read buffer segment to playstack DSP buffer. */
          if((afw_ptr->buffer != NULL) &&
             (bytes_read > 0)
          )
          {
            /* DSP indicated data length is sample rate deviance
             * added, need to reduce later.
             */
            ps_ptr->dsp_buf_data_len = MIN(bytes_read, ps_ptr->dsp_buf_len);
            memcpy(
                ps_ptr->dsp_buf,
                afw_ptr->buffer,
                ps_ptr->dsp_buf_data_len
            );
          }
      }
      else
      {
          /* Play stack repeats more than once or infinatly. */

          int total_bytes_read;


          /* Repeats finite and exceeded? */
          if((ps_ptr->total_repeats >= 0) &&
             (ps_ptr->repeats >= ps_ptr->total_repeats)
          )
            return(-1);

          /* Read next buffer segment, need to read in a loop
           * so incase current data ends, we loop and keep loading
           * untill the buffer is completely loaded.
           */
          for(total_bytes_read = 0;
            total_bytes_read < bytes_per_cycle;
          )
          {
            /* Gone through entire segment of data (check
             * inclusive)?
             */
            if(ps_ptr->position >= ps_ptr->data_length)
            {
                ps_ptr->position = 0;

                /* Increment repeats. */
                ps_ptr->repeats++;

                /* Repeats exceeded? */
                if((ps_ptr->total_repeats >= 0) &&
                   (ps_ptr->repeats >= ps_ptr->total_repeats)
                )
                {
                  /* Return success this time, next call
                   * to this function will return -1 when
                   * repeats are exceeded.
                   */
                  status = 0;
                  break;
                }
            }

            /* Read next segment. */
            if(
                AFWLoadSegment(
                  afw_ptr,
                  ps_ptr->position,
                  bytes_per_cycle - total_bytes_read,
                  &recorder->audio
                )
            )
            {
                status = -1;
                break;
            }

            /* Get number of bytes read. */
            bytes_read = MAX(afw_ptr->length, 0);

            /* Copy segment of read buffer to tmp buffer. */
            if((afw_ptr->buffer != NULL) &&
               (bytes_read > 0)
            )
                memcpy(
                  &ps_ptr->dsp_buf[total_bytes_read],
                  afw_ptr->buffer,
                  MIN(bytes_read, bytes_per_cycle - total_bytes_read)
                );

            /* Increment positions. */
            total_bytes_read += bytes_read;
            ps_ptr->position += bytes_read;
          }

          /* Adjust playstack DSP buffer to indicate the read data
           * length, remember that the loop may have break'ed
           * prematurly and that total_bytes_read may be less
           * than bytes_per_cycle. Note that DSP buffer data length
           * is sample rate deviance added, will reduce it later.
           */
          ps_ptr->dsp_buf_data_len = MIN(total_bytes_read, ps_ptr->dsp_buf_len);
      }


      /* Shrink buffer if due to applied sample rate. */
      if((bytes_per_cycle > actual_bytes_per_cycle) &&
         (ps_ptr->dsp_buf != NULL)
      )
      {
          if(recorder->audio.sample_size == 16)
            YiffShortenBuffer16(
                (u_int16_t *)ps_ptr->dsp_buf,
                ps_ptr->dsp_buf_len,      /* Buffer len (in u_int8_t). */
                actual_bytes_per_cycle    /* Shorten len (in u_int8_t). */
            );
          else
            YiffShortenBuffer8(
                (u_int8_t *)ps_ptr->dsp_buf,
                ps_ptr->dsp_buf_len,      /* Buffer len. */
                actual_bytes_per_cycle    /* Shorten len. */
            );

          /* Need to reduce playstack's dsp buffer data length since
           * it contains sample rate deviance.
           */
/*
printf("Sample rate deviance %ld (%ld %ld) %f\n",
ps_ptr->dsp_buf_data_len,
ps_ptr->dsp_buf_len,
actual_bytes_per_cycle,
sample_rate_dev_coeff
);
 */
          ps_ptr->dsp_buf_data_len = (YDataLength)(
            (Coefficient)ps_ptr->dsp_buf_data_len /
            sample_rate_dev_coeff
          );

          if(ps_ptr->dsp_buf_data_len > ps_ptr->dsp_buf_len)
            ps_ptr->dsp_buf_data_len = ps_ptr->dsp_buf_len;
      }

      return(status);
}

/*
 *    Attempts to allocate a new playstack, if successful,
 *    then sets it up to the given values and loads it according
 *    to the file specified in path.
 *
 *    owner is the connection number that is to own this
 *    playstack.
 */
int YiffCreatePlaystack(
      const char *path,
      YConnectionNumber owner,
      YID yid,
      YDataPosition pos,
      YVolumeStruct *volume,
      int sample_rate,
      int repeats
)
{
      int i;
      PlayStack *ps_ptr, **ptr;
      int bytes_per_cycle;


      if(!ConnectionIsConnected(owner))
          return(-1);

      if((recorder == NULL) ||
         (path == NULL) ||
         (yid == YIDNULL) ||
         (volume == NULL)
      )
          return(-1);

      /* Allocate new playstack. */
      i = PlayStackAllocate();
      ps_ptr = PlayStackGetPtr(i);
      if(ps_ptr == NULL)
      {
          YNetSendSoundObjectPlay(
            owner, YIDNULL,
            0, 0, 0, 0,
            volume,
            0
          );
          return(-1);
      }

      ps_ptr->yid = yid;
      ps_ptr->owner = owner;
      ps_ptr->position = pos;
      ps_ptr->repeats = 0;
      ps_ptr->total_repeats = ((repeats <= 0) ? -1 : repeats);
      ps_ptr->data_length = 0;

      ps_ptr->volume_left = (Coefficient)volume->left /
          (Coefficient)((u_int16_t)-1);
      ps_ptr->volume_right = (Coefficient)volume->right /
          (Coefficient)((u_int16_t)-1);
      ps_ptr->volume_back_left = (Coefficient)volume->back_left /
          (Coefficient)((u_int16_t)-1);
      ps_ptr->volume_back_right = (Coefficient)volume->back_right /
          (Coefficient)((u_int16_t)-1);

      ps_ptr->applied_sample_rate = sample_rate;
      /* Applied sample rate deviance cannot be slower than current
       * Audio.
       */
      if(ps_ptr->applied_sample_rate < recorder->audio.sample_rate)
          ps_ptr->applied_sample_rate = recorder->audio.sample_rate;

      /* Get bytes per cycle. */
      bytes_per_cycle = recorder->sound.buffer_length;
      if(bytes_per_cycle <= 0)
      {
          fprintf(stderr,
            "YiffCreatePlaystack(): Warning: bytes_per_cycle = %i.\n",
            bytes_per_cycle
          );
      }

      /* Open audio file. */
      if(AFWOpen(path, &ps_ptr->afw_data))
      {
          /* Unable to open audio file, delete playstack and send
           * play sound object error.
           */
          PlayStackDelete(i);
          YNetSendSoundObjectPlay(
            owner, YIDNULL,
            0, 0, 0, 0,
            volume,
            0
          );
          return(-1);
      }

      ps_ptr->data_length = ps_ptr->afw_data.entire_length;

      ps_ptr->block_align = ps_ptr->afw_data.block_align;
      ps_ptr->block_length = ps_ptr->afw_data.block_length;


      /* Check sound object's type. */
      if(ps_ptr->afw_data.sndobj_format == SndObjTypeDSP)
      {
          /* DSP Sound Object. */  

          /* Read next play stack position. */
          YiffReadNextDSP(ps_ptr, bytes_per_cycle);
      }
      else if(ps_ptr->afw_data.sndobj_format == SndObjTypeMIDI)
      {
          /* MIDI Sound Object. */

          /* Check if another playstack is playing this sound. */
          for(i = 0, ptr = playstack;
            i < total_playstacks;
            i++, ptr++
          )
          {
            if(*ptr == NULL)
                continue;

            if((*ptr)->afw_data.sndobj_format != SndObjTypeMIDI)
                continue;

            /* Skip this playstack. */
            if(*ptr == ps_ptr)
                continue;

/* Let's let the AFW handle this and we respond properly to the AFW. */
          }

          /* Begin playing MIDI. */
          AFWLoadSegment(
            &ps_ptr->afw_data,
            0,          /* Position n/a. */
            0,          /* Length n/a. */
            &recorder->audio
          );
      }


      /* Notify connection of successful create. */
      YNetSendSoundObjectPlay(
          ps_ptr->owner, ps_ptr->yid,
          ps_ptr->position, ps_ptr->data_length,
          ps_ptr->repeats, ps_ptr->total_repeats,
          volume,
          ps_ptr->applied_sample_rate
      );


      return(0);
}

/*
 *    Destroys the playstack n, notifies the owner connection
 *    about it, and closes the reffered sound object on file.
 */
void YiffDestroyPlaystack(int n)
{
      PlayStack *ps_ptr;


      /* Check if playstack n is allocated. */
      if(PlayStackIsAllocated(n))
          ps_ptr = playstack[n];
      else
          return;

      /* Check if playstack owner connection is connected. */
      if(ConnectionIsConnected(ps_ptr->owner))
      {
          /* Notify owner of this sound object being destroyed. */
          YNetSendSoundObjectKill(ps_ptr->owner, ps_ptr->yid);
      }

      /*   Delete this playstack, closing the audio file
       *   and deallocating its resources.
       */
      PlayStackDelete(n);


      return;
}

/*
 *    Manage the recorder and the sound related resources.
 */
void YiffManageSound(void)
{
      if(recorder == NULL)
          return;

      if(1)
      {
          /* Record or play? */
          if(recorder->audio.direction == AUDIO_DIRECTION_RECORD)
          {
            /* Record. */

          }
          else    /* AUDIO_DIRECTION_PLAY */
          {
            /* Play. */
            int i, status, bytes_per_cycle;
            AFWDataStruct *afw_ptr;
            PlayStack *ps_ptr;
            SoundBuffer *tar_buf, *src_buf, *tmp_ptr;


              /* Play and reset the buffer, so we play first and
             * (pre)mix the buffer for the next cycle.  This is
             * more efficient because mixing and then playing takes
             * up too much time and causes delay on this cycle.
             */
              YSoundPlayBuffer(recorder);

              bytes_per_cycle = recorder->sound.buffer_length;
              tar_buf = recorder->sound.buffer;

              /* Manage each playstack. */
              for(i = 0; i < total_playstacks; i++)
              {
                ps_ptr = playstack[i];
                if(ps_ptr == NULL)
                    continue;

                afw_ptr = &ps_ptr->afw_data;
                /* Check sound object type. */
                if(afw_ptr->sndobj_format == SndObjTypeDSP)
                {
                  /* DSP Sound Object. */

                    /* Mix loaded DSP data on play stack to the 
                   * Sound buffer (target buffer).
                   */
                  src_buf = ps_ptr->dsp_buf;
                    if(src_buf != NULL)
                    {
                      YDataLength len = MIN(
                        bytes_per_cycle,
                        ps_ptr->dsp_buf_data_len
                      );
                        YiffMixBuffers(
                          &recorder->audio,
                            tar_buf,
                          src_buf,
                          len,
                          ps_ptr->volume_left,
                        ps_ptr->volume_right
                        );
                    }

                  /* Read next play stack position. */
                  status = YiffReadNextDSP(ps_ptr, bytes_per_cycle);
                  if(status == -1)
                  {
                      YiffDestroyPlaystack(i);
                      continue;
                  }

                }
                else if(afw_ptr->sndobj_format == SndObjTypeMIDI)
                {
                    /* MIDI Sound Object. */

                  /*   Call AFWLoadSegment() for MIDI Sound Objects,
                   *   this will check if it is still being played.
                   *   Returns:
                   *    0  on success
                   *    -1 on error
                   *    -2 stopped because another MIDI play started
                   *    -3 stopped normally.
                   */
                  status = AFWLoadSegment(
                      afw_ptr,
                      0,                  /* Position n/a. */
                      0,                  /* Length n/a. */
                      &recorder->audio
                  );
                  if(status == -1)
                  {
                      /* General error, destroy it. */
                      YiffDestroyPlaystack(i);
                      continue;
                  }
                  else if(status == -2)
                  {
                      /* Another play started, thus this one must stop. */
                      YiffDestroyPlaystack(i);
                      continue;
                  }
                  else if(status == -3)
                  {
                      /* Playback stopped normally, repeat as needed. */
                      ps_ptr->repeats++;

                      /* Repeats exceeded? */
                      if((ps_ptr->total_repeats >= 0) &&
                         (ps_ptr->repeats >= ps_ptr->total_repeats)
                      )
                      {
                        YiffDestroyPlaystack(i);
                        continue;
                      }

                      /* Begin playing midi object again. */
                      if(AFWLoadSegment(
                        afw_ptr,
                        0,    /* Position n/a. */
                        0,    /* Length n/a. */
                        &recorder->audio
                      ))
                      {
                        /* Definate error in playing MIDI object. */
                        YiffDestroyPlaystack(i);
                        continue;
                      }
                  }
                  else
                  {
                      /* Still playing MIDI object, do nothing. */
                  }

                }
                else
                {
                  /* Unsupported sound object type. */
                }
            }

            /* Shift target buffer from being signed 8 to unsigned 8. */
              for(i = 0, tmp_ptr = tar_buf;
                i < bytes_per_cycle;
                i++
            )
                  *tmp_ptr++ = (int)(*tmp_ptr) + (int)128;

          }
      }
}


/*
 *    Procedure to update timers.
 */
void YiffUpdateTimers(void)
{
      YTime new_millitime;
      time_t audio_cycle_us, cycle_ahead_us;


      GetCurrentTime(&new_millitime);
      if(new_millitime.ms < cur_time.ms)
      {
          /*   Timers need to be reset. Note that cur_time will
           *   be updated in YiffResetTimers();
           */
          YiffResetTimers();
      }
      else
      {
          if(recorder != NULL)
          {
              /* Get audio cycle in microseconds. */
            audio_cycle_us = (recorder->audio.compensated_cycle.ms *
                1000) + recorder->audio.compensated_cycle.us;

            cycle_ahead_us = (recorder->audio.cycle_ahead_left.ms *
                1000) + recorder->audio.cycle_ahead_left.us;

            /*
             *   Calculate next_sound_delta_us, this is the
             *   delta time from now untill the next sound
             *   sound be played.
             *
             *   audio_device_cycle - (current_time -
             *   time_audio_was_last_played) - cycle_ahead
             *   - 100 us
             */
              next_sound_delta_us = audio_cycle_us -
                (((new_millitime.ms * 1000) + new_millitime.us) -
                ((cur_time.ms * 1000) + cur_time.us)) - cycle_ahead_us
                - 100;

            /* Sanitize. */
            if(next_sound_delta_us > audio_cycle_us)
                next_sound_delta_us = audio_cycle_us;
            if(next_sound_delta_us < MIN_NEXT_SOUND_DELTA)
                next_sound_delta_us = MIN_NEXT_SOUND_DELTA;

            /* Stats: calculate cycle_load. */
            if(audio_cycle_us != 0)
                ystats.cycle_load = (double)(audio_cycle_us -
                    next_sound_delta_us) /
                    (double)audio_cycle_us;

/*
if(cycle_ahead_us > 0) 
      fprintf(stderr,
          "cycle_ahead_us: %ld us\n",
          cycle_ahead_us
      );
 */

            /* Decrease cycle_ahead_left as needed. */
            cycle_ahead_us = MAX(cycle_ahead_us -
                ((recorder->audio.compensated_cycle.ms * 1000) +
                  recorder->audio.compensated_cycle.us),
                0
            );
            recorder->audio.cycle_ahead_left.ms = cycle_ahead_us / 1000;
            recorder->audio.cycle_ahead_left.us = cycle_ahead_us % 1000;


/*
fprintf(stderr, "===> %ld (%ld %ld) us\n",
 next_sound_delta_us,
 recorder->audio.compensated_cycle.ms,
(((new_millitime.ms * 1000) + new_millitime.us) -
                ((cur_time.ms * 1000) + cur_time.us))
);
*/

          }
          else
          {
            /* No audio device connected, set a reasonable
             * value for next_sound_delta_us since it is
             * used for the main loop usleep()
             */
            next_sound_delta_us = 8000;

            /* Status: calculate cycle_load. */
            ystats.cycle_load = 0.00;
          }


          /* Update cur_time as usual. */
          cur_time.ms = new_millitime.ms;
          cur_time.us = new_millitime.us;
      }


      return;
}


/*
 *    Resets all timers to 0.
 */
void YiffResetTimers(void)
{
      /* Directly get current time (for syncing audio device). */
      GetCurrentTime(&cur_time);

      /* Reset next schedual timers. */
      next.refresh.ms = 0;
      next.refresh.us = 0;


      /* Sync audio device. */
      YSoundSync(recorder, 0);

      /* How long to sleep before handling sound again (use min). */
      next_sound_delta_us = MIN_NEXT_SOUND_DELTA;


      return;
}



int main(int argc, char *argv[])
{
      int status;


      runlevel = 1;

      /* Initialize everything. */
      status = YiffInit(argc, argv);
      switch(status)
      {
        case -4:
          YiffShutdown();
          return(0);
          break;

        case 0:
          break;

        default:
          YiffShutdown();
          return(1);
          break;
      }

      /*   Main Y server loop:
       *
       *   |--+----------+-------------------------------|
       *   A  B          C                               D
       *
       *   At point A the curent time is fetched (GetCurrentTime())
       *   and then the Sound buffer (already mixed and ready to play)
       *   is played (YiffManageSound()). During the call to
       *   YiffManageSound(); first the Sound buffer is played, then
       *   it is immediatly mixed again and thus ready for the next
       *   play.
       *
       *   At point B, the YiffManageSound() call has finished and
       *   the call to YiffManageConnections() takes place. This will
       *   handle any client programs connected to us that have sent
       *   data.
       *
       *   At point C, all the management and maintainance (as needed)
       *   calls are performed. Also at point C the function
       *   YiffUpdateTimers() is called. This function updates all
       *   timing and calculates how long it took to process from
       *   point A to point C, subtracts that amount from the
       *   `cycle' interval and sets the value into variable
       *   next_sound_delta_us.  usleep() is then called to sleep
       *   for next_sound_delta_us microseconds. The call to
       *   YiffUpdateTimers() needs to be as fast as possible to get
       *   the most accurate next_sound_delta_us. Process sleeps
       *   untill point D.
       *
       *   At point D, the execution loops back to point A.
       */
      runlevel = 2;
      while(runlevel > 1)
      {
          /* Get new time just after usleep(). */
          GetCurrentTime(&cur_time);


          /* Manage sound. */
          YiffManageSound();

          /* Manage connections. */
          YiffManageConnections();


          /* Time to refresh resources? */
          if(next.refresh.ms < cur_time.ms)
          {
            /* Refresh resources. */
            if(recorder != NULL)
            {
                /* Sync audio device. */
                YSoundSync(recorder, 0);
            }
            next.refresh.ms = cur_time.ms + option.refresh_int.ms;
          }

          /* Update timings. */
          YiffUpdateTimers();  

          /* Sleep for a calculated amount of time, this time is
           * calculated by the above call to YiffUpdateTimers().
           */
          usleep(next_sound_delta_us);
      }

      /* Shutdown everything. */
      YiffShutdown();

      runlevel = 0;

      return(0);
}

Generated by  Doxygen 1.6.0   Back to index