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

ynet.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 <math.h>
#include <sys/stat.h>
#include <unistd.h>

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

#include "../include/string.h"

#include "ytypes.h"
#include "soundpaths.h"
#include "ysound.h"
#include "audiocd.h"
#include "ymixer.h"
#include "ymode.h"
#include "yconnection.h"
#include "yhost.h"
#include "playstack.h"
#include "ynet.h"

#include "yiff.h"
#include "options.h"


static u_int64_t htonl64(u_int64_t x);

static void YNetPrintError(
      FILE *stream,
      YConnectionNumber con_num,
      u_int32_t chunk_length,
      u_int16_t major_op_code,
      u_int16_t minor_op_code,
      const char *mesg
);

static int YNetSendToConnection(
      YConnectionNumber con_num, const char *buf, int len
);

int YNetSendAudioChangePreset(
      YConnectionNumber con_num,
      const char *audio_mode_name
);
int YNetSendAudioChangeValues(
      YConnectionNumber con_num,
      int sample_size,
      int channels,
      YDataLength sample_rate,
      int direction,
      int allow_fragmenting,
      int num_fragments,
      int fragment_size
);

int YNetParseAudioChange(YCNP_STD_INPUTS_PROTO);

int YNetSendServerStats(
      YConnectionNumber con_num,
      int protocol_version_major,
      int protocol_version_minor,
      Coefficient cycle_load,
      unsigned long start_time,
      const char *vendor_name,
      const char *server_name,
      const char *sound_name
);
int YNetParseServerStats(YCNP_STD_INPUTS_PROTO);

int YNetSendCycleChange(YConnectionNumber con_num, long cycle_us);
int YNetParseCycleChange(YCNP_STD_INPUTS_PROTO);

int YNetSendDisconnect(YConnectionNumber con_num, u_int16_t reason);
int YNetParseDisconnect(YCNP_STD_INPUTS_PROTO);

int YNetSendSetHost(
      YConnectionNumber con_num,
      u_int16_t minor_op_code,
      const YIPUnion *ip
);
int YNetParseSetHost(YCNP_STD_INPUTS_PROTO);

int YNetSendSetMixerDevice(
      YConnectionNumber con_num,
      int mixer_code,
      Coefficient value1, Coefficient value2,
      Coefficient value3, Coefficient value4
);
int YNetParseMixerDevice(YCNP_STD_INPUTS_PROTO);

int YNetSendSoundObjectPlay(
      YConnectionNumber con_num, YID yid,
      YDataPosition position, YDataLength length,
      int repeats, int total_repeats,
      YVolumeStruct *volume,
      int sample_rate
);
int YNetParseSoundObjectPlay(YCNP_STD_INPUTS_PROTO);

int YNetSendSoundObjectKill(YConnectionNumber con_num, YID yid);
int YNetParseSoundObjectKill(YCNP_STD_INPUTS_PROTO);

int YNetSendSoundObjectAttributes(
      YConnectionNumber con_num,
      const char *path,
      int format,
      int sample_size,
      int channels,
      int sample_rate,
      YDataLength length
);
int YNetParseSoundObjectAttributes(YCNP_STD_INPUTS_PROTO);

int YNetSendShutdown(YConnectionNumber con_num, u_int16_t reason);
int YNetParseShutdown(YCNP_STD_INPUTS_PROTO);

int YNetSendSync(YConnectionNumber con_num, long us);
int YNetParseSync(YCNP_STD_INPUTS_PROTO);

int YNetSendAudioStats(
      YConnectionNumber con_num,
      Audio *audio
);
int YNetParseAudioStats(YCNP_STD_INPUTS_PROTO);

int YNetSendListAudioModes(
      YConnectionNumber con_num,
      const char *audio_mode_name,
      int sample_rate, int channels,
      int sample_size, int fragment_size_bytes,
      char direction, Boolean allow_fragmenting, int num_fragments
);
int YNetParseListAudioModes(YCNP_STD_INPUTS_PROTO);

int YNetSendPlaySoundObjectValues(
      YConnectionNumber con_num, PlayStack *ps_ptr
);
int YNetParsePlaySoundObjectValues(YCNP_STD_INPUTS_PROTO);

int YNetSendClientMessage(
      YConnectionNumber con_num, int format, int type,
      const char *message, YDataLength length
);
int YNetParseClientMessage(YCNP_STD_INPUTS_PROTO);

int YNetSendYSHMSoundOpen(
      YConnectionNumber con_num, int yshm_id
);
int YNetSendYSHMSoundClose(
      YConnectionNumber con_num, int yshm_id
);
int YNetParseYSHMSoundOpen(YCNP_STD_INPUTS_PROTO);
void YNetDoNotifyAllYSHMSoundClose(void);

int YNetSendAudioCDPlayTrack(
      YConnectionNumber con_num,
      int track_number
);
int YNetSendAudioCDStop(
      YConnectionNumber con_num
);
int YNetSendAudioCDEject(
      YConnectionNumber con_num
);
int YNetParseAudioCD(YCNP_STD_INPUTS_PROTO);

int YNetSendListAudioCDTracks(
      YConnectionNumber con_num,
      int track_number,
      unsigned long track_start_time,
      unsigned long track_length,
      const char *track_name
);
int YNetParseListAudioCDTracks(YCNP_STD_INPUTS_PROTO);

static int YNetParse(
      YConnectionNumber con_num,
      const u_int8_t *buf,
      u_int32_t chunk_length,
      u_int16_t major_op_code,
      u_int16_t minor_op_code
);
int YNetRecv(YConnectionNumber con_num);;


#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))


extern int YAntiShift(int in);   /* In main.c */


/*
 *    Marks currently handled connection to be closed.
 */
static Boolean close_this_connection;


static u_int64_t htonl64(u_int64_t x)
{
      return(htonl(x));
#if 0
      u_int8_t *px = (u_int8_t *)&x, *py;
      py[7] = px[0];
      py[6] = px[1];
      py[5] = px[2];
      py[4] = px[3];
      py[3] = px[4];
      py[2] = px[5];
      py[1] = px[6];
      py[0] = px[7];
      return(*(u_int64_t *)py);
#endif
}

/*
 *    Procedure to print network error message.
 */
static void YNetPrintError(
      FILE *stream,
      YConnectionNumber con_num,
      u_int32_t chunk_length,
      u_int16_t major_op_code,
      u_int16_t minor_op_code,
      const char *mesg
)
{
      if(stream == NULL)
          return;

      fprintf(
          stream,
          "Y server protocol error from connection: %i\n",
          con_num
      );
      fprintf(      
          stream,  
          "    Major OP Code: %i\n",
          major_op_code
      );
      fprintf(
          stream,
          "    Minor OP Code: %i\n",
          minor_op_code 
      );
      fprintf(
          stream,
          "    Segment Size: %i bytes\n",
          chunk_length
      );
      if(mesg != NULL)
          fprintf(
            stream,
            "    Remarks: %s\n",   
            mesg
          );
}

/*
 *    Send buffer buf of length len to connection, inputs assued valid
 *    and connection assumed connected.
 *
 *    The connection's socket is checked to see if it's available for
 *    sending data. Returns -1 if it was not able to send the buffer
 *    or the number of bytes sent.
 */
static int YNetSendToConnection(
      YConnectionNumber con_num, const char *buf, int len
)
{
      struct timeval timeout;
      fd_set writefds;
      int bytes_sent;
      YConnection *con = yconnection[con_num];  /* Assumed valid. */
      int s = con->socket;

      timeout.tv_sec = 0;
      timeout.tv_usec = 0;
      FD_ZERO(&writefds);
      FD_SET(s, &writefds);
      select(s + 1, NULL, &writefds, NULL, &timeout);

      if(!FD_ISSET(s, &writefds))
          return(-1);

      bytes_sent = send(s, buf, len, 0);
      return((bytes_sent >= 0) ? bytes_sent : -1);
}


/*
 *    Send Audio mode change.
 */
int YNetSendAudioChangePreset(
      YConnectionNumber con_num,
      const char *audio_mode_name
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <...modename...>
       */
      const YDataLength this_seg_len = 8 + YAudioNameMax;
      YDataLength actual_seg_len;
      int name_len;
      char buf[this_seg_len];


      if(audio_mode_name == NULL)
          return(-1);

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);  
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioChange);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioChangePreset);

      name_len = MIN(strlen(audio_mode_name), YAudioNameMax);
      strncpy(
          &buf[8],
          audio_mode_name,
          name_len
      );
      /* Do not null terminate string. */

      /* Since this is a variable length segment we need to
       * recalculate the segment length after the formatting
       * above and update the segment header's chunk size.
       */
      actual_seg_len = 8 + name_len;
      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

      return(YNetSendToConnection(con_num, buf, actual_seg_len));

}
/*
 *    Sends audio values change.
 */
int YNetSendAudioChangeValues(
      YConnectionNumber con_num,
      int sample_size,
      int channels,
      YDataLength sample_rate,
      int direction,
      int allow_fragmenting,
      int num_fragments,
      int fragment_size
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u16_samplesize> <u16_channels> <u32_samplerate>
       * <u8_direction>
       * <u8_allowfrags> <u32_numfrags> <u32_fragsize>
       */
      const YDataLength this_seg_len = 8 + 8 + 1 + 9;
      char buf[this_seg_len];


      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioChange);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioChangeValues);

      *(u_int16_t *)(&buf[8]) = htons((u_int16_t)sample_size);
      *(u_int16_t *)(&buf[10]) = htons((u_int16_t)channels);
      *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)sample_rate);

      *(u_int8_t *)(&buf[16]) = (u_int8_t)direction;

      *(u_int8_t *)(&buf[17]) = (u_int8_t)allow_fragmenting;
      *(u_int32_t *)(&buf[18]) = htonl((u_int32_t)num_fragments);
      *(u_int32_t *)(&buf[22]) = htonl((u_int32_t)fragment_size);

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *    Handles Audio mode change.
 */
int YNetParseAudioChange(YCNP_STD_INPUTS_PROTO)
{
      int i;
      YConnection *con;
      YMode *ymode_ptr;
      Audio *audio_ptr;

      /* Change mode values or change to preset mode? */
      if(minor_op_code == YAudioChangeValues)
      {
          /* Parse set Audio values. */

          /* <u32_cs> <u16_majop> <u16_minop>
           * <u16_samplesize> <u16_channels> <u32_samplerate>
           * <u8_direction>
           * <u8_allowfrags> <u32_numfrags> <u32_fragsize>
           */
          const YDataLength this_seg_len = 8 + 8 + 1 + 9;
          int sample_size;
          int channels;
          YDataLength sample_rate;
          int direction;
          int allow_fragmenting;
          int num_fragments;
          int fragment_size_bytes;


          if(chunk_length < this_seg_len)
            return(-1);

          /* Get Audio values. */
          sample_size = CLIP(
            ntohs(*(u_int16_t *)(&buf[8])), 8, 16
          );
          channels = CLIP(
            ntohs(*(u_int16_t *)(&buf[10])), 1, 2
          );
          sample_rate = MAX(
            ntohl(*(u_int32_t *)(&buf[12])), 0
          );

          direction = (int)buf[16];

          allow_fragmenting = (int)buf[17];
          num_fragments = ntohl(*(u_int32_t *)(&buf[18]));
          fragment_size_bytes = ntohl(*(u_int32_t *)(&buf[22]));


          /* Special case: If the sample rate is 0, this implies
           * that the DSP device should be closed (shelled out)
           * to let other applications open it.
           */
          if(sample_rate == 0)
          {
            /* Shell out the recorder. */
            YSoundShellOut(recorder);

            /* Send back shell out response. */
            YNetSendAudioChangeValues(
                con_num,
                sample_size,
                channels,
                0,            /* Sample rate should be 0. */
                direction,
                allow_fragmenting,
                num_fragments,
                fragment_size_bytes
            );

            /* Return now, do not delete playstacks or modify Audio. */
            return(0);
          }

          /* Delete all playstacks and notify their respective owners
           * about the deletion.
           */
          for(i = 0; i < total_playstacks; i++)
            YiffDestroyPlaystack(i);

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

          /* Shutdown and delete the recorder. */
          YSoundShutdown(recorder);
          free(recorder);
          recorder = NULL;


          /* Set new Audio values to option Audio values. */
          audio_ptr = &option.audio;
          audio_ptr->sample_size = sample_size;
          audio_ptr->channels = channels;
          audio_ptr->sample_rate = sample_rate;

#ifdef OSS_BUFFRAG
          audio_ptr->allow_fragments = ((allow_fragmenting) ?
            True : False
          );
          audio_ptr->num_fragments = num_fragments;
          audio_ptr->fragment_size = YAntiShift(fragment_size_bytes - 1);
#endif  /* OSS_BUFFRAG */

          audio_ptr->direction = direction;


          /* Allocate recorder structure. */
          recorder = (Recorder *)calloc(1, sizeof(Recorder));
          if(recorder == NULL)
            return(-1);
          /* Initialize recorder. */
          if(YSoundInit(recorder, &option.audio))
          {
            /* Failed to initialize. */

            /* Shutdown and delete the recorder. */
            YSoundShutdown(recorder);
            free(recorder);
            recorder = NULL;

            /* Send back notification of initialization failure. */
            YNetSendAudioChangeValues(
                con_num,
                0, 0, 0,
                0, 0, 0, 0
            );

            return(-1);
          }

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


          /* Notify all connections about Audio values change. */
          for(i = 0; i < total_yconnections; i++)
          {
            con = yconnection[i];
            if((con != NULL) ? (con->socket < 0) : True)
                continue;

            YNetSendAudioChangeValues(
                i,
                sample_size,
                channels,
                sample_rate,
                direction,
                allow_fragmenting,
                num_fragments,
                fragment_size_bytes
            );
          }
      }
      else
      {
          /* Parse change preset Audio mode. */

          /*   <u32_cs> <u16_majop> <u16_minop>
           *   <...modename...>
           */
          const YDataLength base_seg_len = 8;
          int name_len;
          int mode_num;
          int mode_name_different = 0;
          char mode_name[YAudioNameMax];
          Audio *audio_ptr;


          if(chunk_length < base_seg_len)
            return(-1);

          name_len = (YDataLength)chunk_length - (YDataLength)base_seg_len;
          if(name_len >= YAudioNameMax)
            name_len = YAudioNameMax - 1;

          if(name_len > 0)
          {
            strncpy(
                mode_name,  
                &buf[8],
                name_len
            );
            mode_name[name_len] = '\0';

            /* Check if the given Audio mode name is different from
             * the one specified in the Audio structure.
             */
            if(recorder->audio.audio_mode_name != NULL)
            {
                if(strcasecmp(
                  recorder->audio.audio_mode_name,
                  mode_name
                ))
                    mode_name_different = 1;
            }
            else
            {
                /* Last Audio mode name not recorded on Audio
                 * structure, so assume its different.
                 */
                mode_name_different = 1;
            }
            /* Mode name should always be considered different if
             * Audio is currently shelled out.
             */
            if(recorder->audio.shelled_out)
                mode_name_different = 1;


            /* Match preset Audio mode, skip if current Audio
             * mode matches.
             */
            mode_num = YModeMatch(mode_name);
            if(YModeIsAllocated(mode_num) &&
               mode_name_different
            )
            {
                ymode_ptr = ymode[mode_num];

                /* Delete all playstacks and notify their respective
                 * owners about the deletion.
                 */
                for(i = 0; i < total_playstacks; i++)
                  YiffDestroyPlaystack(i);

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

                /* Shutdown and delete the recorder. */
                YSoundShutdown(recorder);
                free(recorder);
                recorder = NULL;


                /* Set option Audio values to new mode's values. */
                audio_ptr = &option.audio;

                /* 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;

                /* Leave audio_ptr->compensated_cycle alone. */

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

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

#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;


                /* Allocate recorder structure. */
                recorder = (Recorder *)calloc(1, sizeof(Recorder));
                if(recorder == NULL)
                  return(-1);
                /* Initialize recorder. */
                if(YSoundInit(recorder, audio_ptr))
                {
                  /* Failed to initialize. */

                  /* Shutdown and delete the recorder. */
                  YSoundShutdown(recorder);
                  free(recorder);
                  recorder = NULL;

                  /* Send back notification of initialization
                   * failure.
                   */
                  YNetSendAudioChangePreset(con_num, "");

                  return(-1);
                }

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

                /* Record name of preset Audio mode used to
                 * set this Audio.
                 */
                free(recorder->audio.audio_mode_name);
                recorder->audio.audio_mode_name = StringCopyAlloc(
                  ymode_ptr->name
                );

                /* Notify all connections of Audio mode change. */
                for(i = 0; i < total_yconnections; i++)
                {
                  con = yconnection[i];
                  if((con != NULL) ? (con->socket < 0) : True)
                      continue;

                  YNetSendAudioChangePreset(i, mode_name);
                }
            }
            /* Audio mode the same? */
            else if(!mode_name_different)
            {
                /* Send back fake success response. */
                YNetSendAudioChangePreset(con_num, mode_name);
            }
            else
            {
                /* No such preset Audio mode. */

                /* Notify connection of Audio mode change
                 * failure (send back empty Audio mode name).
                 */
                YNetSendAudioChangePreset(con_num, "");
            }
          }
          else
          {
            /* No mode name specified. */

            /* Notify connection of Audio mode change
             * failure (send back empty Audio mode name).
             */
            YNetSendAudioChangePreset(con_num, "");
          }
      }


      return(0);
}

/*
 *    Sends server stats.
 */
int YNetSendServerStats(
      YConnectionNumber con_num,
      int protocol_version_major,
      int protocol_version_minor,
      Coefficient cycle_load,
      unsigned long start_time,
      const char *vendor_name,
      const char *server_name,
      const char *sound_name
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_protocol_version_major> <u32_protocol_version_minor>
       * <u16_load>
       * <u64_start_time>
       * <...vendor_name, server_name, sound_name...>
       */
      const YDataLength this_seg_len = 8 + 8 + 2 + 8 +
          YVendorNameMax + 1 + YServerNameMax + 1 + YSoundNameMax + 1;
      YDataLength actual_seg_len;
      int len, names_len;
      const char *s;
      char *ts, buf[this_seg_len];

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YServerStats);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YServerStatsSet);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)protocol_version_major);
      *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)protocol_version_minor);

      *(u_int16_t *)(&buf[16]) = htons((u_int16_t)(
          cycle_load * (Coefficient)((u_int16_t)-1)
      ));
      *(u_int64_t *)(&buf[18]) = htonl64((u_int64_t)start_time);

      ts = (char *)&buf[26];
      names_len = 0;
      s = (vendor_name != NULL) ? vendor_name : "";
      len = MIN(strlen(s), YVendorNameMax);
      if(len > 0)
          strncpy(&ts[names_len], s, len);
      ts[names_len + len] = '\0';

      names_len += len + 1;
      s = (server_name != NULL) ? server_name : "";
      len = MIN(strlen(s), YServerNameMax);
      if(len > 0)
          strncpy(&ts[names_len], s, len);
      ts[names_len + len] = '\0';

      names_len += len + 1;
      s = (sound_name != NULL) ? sound_name : "";
      len = MIN(strlen(s), YSoundNameMax);
      if(len > 0)
          strncpy(&ts[names_len], s, len);
      ts[names_len + len] = '\0';

      names_len += len + 1;

      /* Since this is a variable length segment we need to
       * recalculate the segment length after the formatting
       * above and update the segment header's chunk size.
       */
      actual_seg_len = 8 + 8 + 2 + 8 + names_len;
      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

      return(YNetSendToConnection(con_num, buf, actual_seg_len));
}
/*
 *    Parses server stats.
 */
int YNetParseServerStats(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YServerStatsGet)
      {
          /* Parse server stats get. */

          /* <u32_cs> <u16_majop> <u16_minop>
           */
          const YDataLength this_seg_len = 8;
      
          if(chunk_length < this_seg_len)
            return(-1);

          YNetSendServerStats(
            con_num,
            YPROTOCOL_VERSION_MAJOR,
            YPROTOCOL_VERSION_MINOR,
            ystats.cycle_load,
            ystats.start_time,
            PROG_VENDOR_NAME,
            "",
            recorder->audio.sound_name
          );
      }
      else if(minor_op_code == YServerStatsSet)
      {
          /* Parse server stats set. */
 
          /* <u32_cs> <u16_majop> <u16_minop>
           */
          const YDataLength base_seg_len = 8;

          if(chunk_length < base_seg_len)
            return(-1);
      }

      return(0);
}

/*
 *    Sends cycle change.
 */
int YNetSendCycleChange(YConnectionNumber con_num, long cycle_us)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_cycleus>
       */
      const YDataLength this_seg_len = 8 + 4;
      char buf[this_seg_len];


      /* Most UNIXes requires cycles > than 100 us. */
      if(cycle_us < 100)
          cycle_us = 100;

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YCycleChange);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)cycle_us);

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *    Handles cycle change.
 */
int YNetParseCycleChange(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_cycleus>
       */
      const YDataLength this_seg_len = 8 + 4;
      long cycle_us;
      int i;
      YConnection *con;


      if(chunk_length < this_seg_len)
          return(-1);

      cycle_us = ntohl(*(u_int32_t *)(&buf[8]));
      /* Most UNIXes requires this > than 100 us. */
      if(cycle_us < 100)
          cycle_us = 100;

      /* Set new cycle. */
      if(recorder == NULL)
      {
          /* Recorder not available, send error response. */
          YNetSendCycleChange(con_num, 0);
      }
      else
      {
          /* Set new cycle. */
          if(recorder->audio.cycle_set == CYCLE_SET_USER)
          {
            recorder->audio.cycle.ms = cycle_us / 1000;
            recorder->audio.cycle.us = cycle_us % 1000;

            recorder->audio.compensated_cycle.ms =
                recorder->audio.cycle.ms;
            recorder->audio.compensated_cycle.us =
                recorder->audio.cycle.us;

            recorder->audio.write_ahead.ms = (long)(cycle_us * 1.5) /
                1000;
            recorder->audio.write_ahead.us = (long)(cycle_us * 1.5) %
                1000;

            option.audio.write_ahead.ms =
                recorder->audio.write_ahead.ms;
            option.audio.write_ahead.us =
                recorder->audio.write_ahead.us;


            /* Notify all connections of cycle change. */
            for(i = 0; i < total_yconnections; i++)
            {
                con = yconnection[i];
                if((con != NULL) ? (con->socket < 0) : True)
                  continue;

                YNetSendCycleChange(i, cycle_us);
            }
          }
          else
          {
            /* Program or hardware set cycle. */
            cycle_us = (recorder->audio.cycle.ms * 1000) +
                recorder->audio.cycle.us;

            YNetSendCycleChange(con_num, cycle_us);
          }
      }


      return(0);
}


/*
 *    Sends disconnect.
 */
int YNetSendDisconnect(YConnectionNumber con_num, u_int16_t reason)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u16_reason>
       */
      const YDataLength this_seg_len = 8 + 2;
      char buf[this_seg_len];

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YDisconnect);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

      *(u_int16_t *)(&buf[8]) = htons((u_int16_t)reason);

      return(YNetSendToConnection(con_num, buf, this_seg_len));            
}
/*
 *    Handle disconnect.
 */
int YNetParseDisconnect(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u16_reason>
       */
      const YDataLength this_seg_len = 8 + 2;
      u_int16_t reason;


      if(chunk_length < this_seg_len)
          return(-1);

      reason = ntohs(*(u_int16_t *)(&buf[8]));

      /* Mark currently handled connection number to be closed. */
      close_this_connection = True;

      return(0);
}

/*
 *    Sends set host.
 */
int YNetSendSetHost(
      YConnectionNumber con_num,
      u_int16_t minor_op_code,
      const YIPUnion *ip
)
{
      /* <u32_cs> <u16_majop> <u16_minop> 
       * <u8_ipc1> <u8_ipc2> <u8_ipc3> <u8_ipc4>
       */
      const YDataLength this_seg_len = 8 + 4;

      char buf[this_seg_len];

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSetHost);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)minor_op_code);

      *(u_int8_t *)(&buf[8]) = ip->charaddr[0];
      *(u_int8_t *)(&buf[9]) = ip->charaddr[1];
      *(u_int8_t *)(&buf[10]) = ip->charaddr[2];
      *(u_int8_t *)(&buf[11]) = ip->charaddr[3];

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *    Handles set host.
 */
int YNetParseSetHost(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u8_ipc1> <u8_ipc2> <u8_ipc3> <u8_ipc4>
       */
      const YDataLength this_seg_len = 8 + 4;
      YIPUnion ip;
      int status;

      if(chunk_length < this_seg_len)
          return(-1);

      ip.charaddr[0] = (u_int8_t)buf[8];
      ip.charaddr[1] = (u_int8_t)buf[9];
      ip.charaddr[2] = (u_int8_t)buf[10];
      ip.charaddr[3] = (u_int8_t)buf[11];

      switch(minor_op_code)
      {
        case YSetHostAdd:
          status = YHostAllocate(&ip);
          if(status < 0)
            fprintf(stderr,
                "Cannot add YHost %i.%i.%i.%i.\n",
                ip.charaddr[0],
                ip.charaddr[1],
                ip.charaddr[2],          
                ip.charaddr[3]
            );
          break;

        case YSetHostRemove:
          status = YHostDeleteByHost(&ip);
          if(status < 0)
            fprintf(stderr,
                "Cannot remove YHost %i.%i.%i.%i.\n",
                ip.charaddr[0],
                ip.charaddr[1],
                ip.charaddr[2],
                ip.charaddr[3] 
            );
          break;
      }

      /* Notify connection of host add or remove. */
      YNetSendSetHost(
          con_num,
          minor_op_code,
          &ip
      );

      return(0);
}

/*
 *      Sends a mixer device set.
 */
int YNetSendSetMixerDevice(
      YConnectionNumber con_num,
      int mixer_code,
      Coefficient value1, Coefficient value2,
      Coefficient value3, Coefficient value4
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u16_mixercode>
       * <u32_value1> <u32_value2>
       * <u32_value3> <u32_value4>
       */
      const YDataLength this_seg_len = 8 + 2 + 8 + 8;

      char buf[this_seg_len];
      u_int32_t ival[4];


      ival[0] = (u_int32_t)rint(value1 * (Coefficient)((u_int32_t)-1));
      ival[1] = (u_int32_t)rint(value2 * (Coefficient)((u_int32_t)-1));
      ival[2] = (u_int32_t)rint(value3 * (Coefficient)((u_int32_t)-1));
      ival[3] = (u_int32_t)rint(value4 * (Coefficient)((u_int32_t)-1));

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YMixerChannel);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YMixerChannelSet);

      *(u_int16_t *)(&buf[8]) = htons((u_int16_t)mixer_code);

      *(u_int32_t *)(&buf[10]) = htonl((u_int32_t)ival[0]);
      *(u_int32_t *)(&buf[14]) = htonl((u_int32_t)ival[1]);
      *(u_int32_t *)(&buf[18]) = htonl((u_int32_t)ival[2]);
      *(u_int32_t *)(&buf[22]) = htonl((u_int32_t)ival[3]);

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *    Handles set mixer device.
 */
int YNetParseMixerDevice(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YMixerChannelSet)
      {
          /* <u32_cs> <u16_majop> <u16_minop>
           * <u16_mixercode>
           * <u32_value1> <u32_value2>
           * <u32_value3> <u32_value4>
           */
          const YDataLength this_seg_len = 8 + 2 + 8 + 8;
          int i, status, mixer_code;
          Coefficient value[YMixerValues];
          YConnection *con;


          if(chunk_length < this_seg_len)
            return(-1);

          mixer_code = ntohs(*(u_int16_t *)(&buf[8]));

          if(YMixerValues > 0)
            value[0] =
                (Coefficient)ntohl(*(u_int32_t *)(&buf[10])) /
                (Coefficient)((u_int32_t)-1);
          if(YMixerValues > 1)
            value[1] =
                (Coefficient)ntohl(*(u_int32_t *)(&buf[14])) /
                (Coefficient)((u_int32_t)-1);
          if(YMixerValues > 2)
            value[2] =
                (Coefficient)ntohl(*(u_int32_t *)(&buf[18])) /
                (Coefficient)((u_int32_t)-1);
          if(YMixerValues > 3)
            value[3] =
                (Coefficient)ntohl(*(u_int32_t *)(&buf[22])) /
                (Coefficient)((u_int32_t)-1);

          /* Set mixer device. */
          status = -1;
          if(recorder != NULL)
          {
            status = YMixerSet(recorder, mixer_code, value);
          }
          if(status < 0)
          {
            /* Error setting mixer values, notify error. */
            YNetSendSetMixerDevice(
                con_num, mixer_code,
                0.0, 0.0, 0.0, 0.0
            );
          }
          else
          {
            /* Notify all connections about mixer channel change. */
            for(i = 0; i < total_yconnections; i++)
            {
                con = yconnection[i];
                if((con != NULL) ? (con->socket < 0) : True)
                  continue;

                YNetSendSetMixerDevice(
                    i, mixer_code,
                  (YMixerValues > 0) ? value[0] : 0.0,
                  (YMixerValues > 1) ? value[1] : 0.0,
                  (YMixerValues > 2) ? value[2] : 0.0,
                  (YMixerValues > 3) ? value[3] : 0.0
                );
            }
          }
      }
      else if(minor_op_code == YMixerChannelGet)
      {
          /* <u32_cs> <u16_majop> <u16_minop>
           * <u16_mixercode>
           */
          const YDataLength this_seg_len = 8 + 2;
          int status, mixer_code;
          Coefficient value[YMixerValues];


          if(chunk_length < this_seg_len)
            return(-1);

          mixer_code = ntohs(*(u_int16_t *)(&buf[8]));

          /* Get mixer channel device values. */
          status = -1;
          if(recorder != NULL)
          {
            status = YMixerGet(
                recorder, mixer_code, value
            );
          }
          if(status < 0)
          {
            /* Error getting values, respond with 0 channel values. */
            YNetSendSetMixerDevice(
                con_num, mixer_code,
                0.0, 0.0, 0.0, 0.0
            );
          }
          else
          {
            YNetSendSetMixerDevice(
                con_num, mixer_code,
                (YMixerValues > 0) ? value[0] : 0.0,
                (YMixerValues > 1) ? value[1] : 0.0,
                (YMixerValues > 2) ? value[2] : 0.0,
                (YMixerValues > 3) ? value[3] : 0.0
            );
          }
      }

      return(0);
}


/*
 *      Sends sound play.
 */
int YNetSendSoundObjectPlay(
      YConnectionNumber con_num, YID yid,
      YDataPosition position, YDataLength length,
      int repeats, int total_repeats,
      YVolumeStruct *volume,
      int sample_rate
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_flags> <u32_yid>
       * <u32_position> <u32_length>
       * <u32_repeats> <u32_total_repeats>
       * <u16_volume_left> <u16_volume_right>
       * <u16_volume_back_left> <u16_volume_back_right>
       * <u32_sample_rate>
       */
      const YDataLength this_seg_len = 8 + 8 + 8 + 8 + 4 + 4 + 4;
      char buf[this_seg_len];

      /* Set flags to match client side (see Y2/Ylib.h). */
      unsigned long flags =   (1 << 1) | (1 << 2) | (1 << 3) |
                        (1 << 4) | (1 << 5) | (1 << 6) |
                        (1 << 7);

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlay);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)flags);
      *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)yid);

      *(u_int32_t *)(&buf[16]) = htonl((u_int32_t)position);
      *(u_int32_t *)(&buf[20]) = htonl((u_int32_t)length);

      *(u_int32_t *)(&buf[24]) = htonl((u_int32_t)repeats);
      *(u_int32_t *)(&buf[28]) = htonl((u_int32_t)total_repeats);

      *(u_int16_t *)(&buf[32]) = htons((u_int16_t)(
          (volume != NULL) ? volume->left : 0
      ));
      *(u_int16_t *)(&buf[34]) = htons((u_int16_t)(
          (volume != NULL) ? volume->right : 0
      ));
      *(u_int16_t *)(&buf[36]) = htons((u_int16_t)(
          (volume != NULL) ? volume->back_left : 0
      ));
      *(u_int16_t *)(&buf[38]) = htons((u_int16_t)(
          (volume != NULL) ? volume->back_right : 0
      ));

      *(u_int32_t *)(&buf[40]) = htonl((u_int32_t)sample_rate);

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *      Handles sound play.
 */
int YNetParseSoundObjectPlay(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_yid> <u32_pos>
       * <u16_volume_left> <u16_volume_right>
       * <u16_volume_back_left> <u16_volume_back_right>
       * <u32_sample_rate> <u32_repeats>
       * <...path...>
       */
/*    const YDataLength this_seg_len = 8 + 8 + 4 + 4 + 8 + YPathMax; */
      const YDataLength this_seg_len_base = 8 + 8 + 4 + 4 + 8;

      YID yid;
      YDataPosition pos;
      u_int16_t vol[4];
      int sample_rate;
      int repeats;

      int path_len;
      char path[YPathMax];
      YVolumeStruct volume;
      char *strptr;


      if(chunk_length < this_seg_len_base)
          return(-1);

      yid = ntohl(*(u_int32_t *)(&buf[8]));
      pos = ntohl(*(u_int32_t *)(&buf[12]));
      vol[0] = ntohs(*(u_int16_t *)(&buf[16]));
      vol[1] = ntohs(*(u_int16_t *)(&buf[18]));
      vol[2] = ntohs(*(u_int16_t *)(&buf[20]));
      vol[3] = ntohs(*(u_int16_t *)(&buf[22]));

      sample_rate = ntohl(*(u_int32_t *)(&buf[24]));
      repeats = ntohl(*(u_int32_t *)(&buf[28]));

      path_len = (int)chunk_length - this_seg_len_base;
      if(path_len >= YPathMax)
          path_len = YPathMax - 1;

      if(path_len > 0)
      {
          strncpy(
            path,
            &buf[32],
            path_len
          );
          path[path_len] = '\0';
      }
      else
      {
          /* If path is not specified, then do not continue. */
          YNetSendSoundObjectPlay(
            con_num, YIDNULL,
            0, 0, 0, 0,
            &volume,
            0
          );
          return(0);
      }


      /* Try to match correct complete path. */
      strptr = SoundPathCompletePath(path);
      if(strptr == NULL)
      {
          YNetSendSoundObjectPlay(
            con_num, YIDNULL,
            0, 0, 0, 0,
            &volume,
            0
          );
          return(0);
      }

      /* Do not start playing if recorder is not initialized. */
      if(recorder == NULL)
      {
          YNetSendSoundObjectPlay(
            con_num, YIDNULL,
            0, 0, 0, 0,
            &volume,
            0
          );
          return(0);
      }

      /* Create playstack. */
      volume.left = vol[0];
      volume.right = vol[1];
      volume.back_left = vol[2];
      volume.back_right = vol[3];
      YiffCreatePlaystack( 
          strptr,       /* Path. */
          con_num,            /* Owner. */
          yid,          /* YID. */
          pos,          /* Position. */
          &volume,
          sample_rate,
          repeats       /* 0 means repeat forever. */
      );

      /* YiffCreatePlaystack() will send response. */


      return(0);
}


/*
 *    Sends sound kill.
 */
int YNetSendSoundObjectKill(YConnectionNumber con_num, YID yid)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_yid>
       */
      const YDataLength this_seg_len = 8 + 4;
      char buf[this_seg_len];

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectKill);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yid);

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *    Handles sound kill.
 */
int YNetParseSoundObjectKill(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_yid>
       */
      const YDataLength this_seg_len = 8 + 4;
      YID yid;
      int i;
      PlayStack *ps;


      if(chunk_length < this_seg_len)
          return(-1);

      yid = ntohl(*(u_int32_t *)(&buf[8])); 

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

          if(ps->owner != con_num)
            continue;

          if((ps->yid == YIDNULL) || (ps->yid != yid))
            continue;

          /* Delete playstack and notify owner connection about it. */
          YiffDestroyPlaystack(i);
      }

      return(0);
}

/*
 *    Sends sound object attributes.
 */
int YNetSendSoundObjectAttributes(
      YConnectionNumber con_num,
      const char *path,
      int format,
      int sample_size,
      int channels,   
      int sample_rate,
      YDataLength length
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u16_format>
       * <u16_samplesize> <u16_channels> <u32_samplerate>
       * <u32_length>
       * <...path...>
       */
      const YDataLength this_seg_len = 8 + 2 + 8 + 4 + YPathMax;
      char buf[this_seg_len];
      YDataLength path_len, actual_seg_len;

      if(path == NULL)
          return(-1);

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectAttributes);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectAttributesSet);

      *(u_int16_t *)(&buf[8]) = htons((u_int16_t)format);
      *(u_int16_t *)(&buf[10]) = htons((u_int16_t)sample_size);
      *(u_int16_t *)(&buf[12]) = htons((u_int16_t)channels);
      *(u_int32_t *)(&buf[14]) = htonl((u_int32_t)sample_rate);
      *(u_int32_t *)(&buf[18]) = htonl((u_int32_t)length);

      path_len = MIN(strlen(path), YPathMax);
      if(path_len > 0)
          strncpy(
            &buf[22],
            path,
            path_len
          );
      /* Do not null terminate string. */

      /* Since this is a variable length segment we need to
       * recalculate the segment length after the formatting
       * above and update the segment header's chunk size.
       */
      actual_seg_len = 8 + 2 + 8 + 4 + path_len;
      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

      return(YNetSendToConnection(con_num, buf, actual_seg_len));
}
/*
 *    Handles sound object attributes.
 */
int YNetParseSoundObjectAttributes(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YSoundObjectAttributesGet)
      {
          /* <u32_cs> <u16_majop> <u16_minop>
           * <...path...>
           */
          const YDataLength base_seg_len = 8;
/*        const YDataLength this_seg_len = 8 + YPathMax; */
          YDataLength path_len;
          char path[YPathMax];
          char *strptr;
          AFWDataStruct af_data;


          if(chunk_length < base_seg_len)
            return(-1);

          path_len = (YDataLength)chunk_length -
            (YDataLength)base_seg_len;
          if(path_len >= YPathMax)
            path_len = YPathMax - 1;

          if(path_len > 0)
          {
              strncpy(
                path,
                (char *)(&buf[8]),
                path_len
              );
            path[path_len] = '\0';
          }
          else
          {
            *path = '\0';
          }


          /* Try to match correct complete path. */
          strptr = SoundPathCompletePath(path);
          if(strptr == NULL)
          {
            YNetSendSoundObjectAttributes(
                con_num,
                "",
                SndObjTypeNone,
                0, 0, 0, 0
            );
            return(0);
          }


          /* Open audio file.*/
          if(AFWOpen(strptr, &af_data))
          {
            /* Error opening audio file, so reset audio file values
             * to indicate error.
             */
            *path = '\0';
            af_data.sndobj_format = SndObjTypeNone;
            af_data.sample_size = 0;
            af_data.channels = 0;
            af_data.sample_rate = 0;
            af_data.entire_length = 0;
          }

          /* Send audio file data. */
          YNetSendSoundObjectAttributes(
            con_num,
            path,       /* Send back original path. */
            af_data.sndobj_format,
            af_data.sample_size,
            af_data.channels,
            af_data.sample_rate,
            af_data.entire_length
          );

          /* Close audio file. */
          AFWClose(
            &af_data,
            (recorder != NULL) ? &recorder->audio : NULL
          );
      }
      else if(minor_op_code == YSoundObjectAttributesSet)
      {

      }

      return(0);
}

/*
 *    Sends shutdown.
 */
int YNetSendShutdown(YConnectionNumber con_num, u_int16_t reason)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u16_reason>
       */
      const YDataLength this_seg_len = 8 + 2;
      char buf[this_seg_len];

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YShutdown);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

      *(u_int16_t *)(&buf[8]) = htons((u_int16_t)reason);

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *    Handles shutdown.
 */
int YNetParseShutdown(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u16_reason>
       */
      const YDataLength this_seg_len = 8 + 2;
      u_int16_t reason;


      if(chunk_length < this_seg_len)
          return(-1);

      reason = ntohs(*(u_int16_t *)(&buf[8]));


/* Check if connection has permission to shut server down? */

      /*   Set runlevel to 1 to shutdown gently. Comfermation
       *   will be sent to all connections just before shutdown.
       */
      runlevel = 1;


      return(0);
}

/*
 *    Sends sync.
 */
int YNetSendSync(YConnectionNumber con_num, long us)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_cycleaheadus>
       */
      const YDataLength this_seg_len = 8 + 4;
      char buf[this_seg_len];
      u_int32_t ca;

      if(us < 0)
          us = 0;
      ca = us;

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSync);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)ca);

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *    Handles sync.
 */
int YNetParseSync(YCNP_STD_INPUTS_PROTO)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_cycleaheadus>
       */
      const YDataLength this_seg_len = 8 + 4;
      long cahead_us;


      if(chunk_length < this_seg_len)
          return(-1);

      cahead_us = ntohl(*(u_int32_t *)(&buf[8]));

      if(recorder != NULL)
      {
          /* Recaliberate cycle if program calculates it. */
          if(recorder->audio.cycle_set == CYCLE_SET_PROGRAM)
            YSoundCaliberateCycle(recorder);

          /* Set new write ahead. */
          if(cahead_us > 0)
          {
            recorder->audio.write_ahead.ms = cahead_us / 1000;
            recorder->audio.write_ahead.us = cahead_us % 1000;
          }

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

      /* Send sync comfermation. */
      YNetSendSync(con_num, cahead_us);

      return(0);
}

/*
 *    Sends set audio stats.
 */
int YNetSendAudioStats(
      YConnectionNumber con_num,
      Audio *audio
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u8_cycleset>
       * <u32_cycleus> <u32_compensatedcycleus>
       * <u32_writeaheadus> <u32_cumulativelatencyus>
       * <u16_samplesize> <u16_channels> <u32_samplerate>
       * <u32_bytespersec>
       * <u8_allowfragments> <u32_numfragments> <u32_fragmentsize>
       * <u8_flipstereo>
       * <u8_direction>
       * <...audiomodename...>
       */
      YDataLength actual_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 + 1 + 1;
      const YDataLength this_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 + 1 + 1 +
                               YAudioNameMax;
      char buf[this_seg_len];

      if(audio == NULL)
      {
          /* Send blank values (implying recorder not available). */
          memset(buf, 0x00, this_seg_len * sizeof(char));

          *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
          *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioStats);
          *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioStatsSet);
      }
      else
      {
          int name_len;
          const char *audio_mode_name = (audio->audio_mode_name != NULL) ?
            audio->audio_mode_name : "";

          *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
          *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioStats);
          *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioStatsSet);

          *(u_int8_t *)(&buf[8]) = (u_int8_t)audio->cycle_set;

          *(u_int32_t *)(&buf[9]) = htonl((u_int32_t)(
            (audio->cycle.ms * 1000) + audio->cycle.us
          ));
          *(u_int32_t *)(&buf[13]) = htonl((u_int32_t)(
            (audio->compensated_cycle.ms * 1000) +
            audio->compensated_cycle.us
          ));

          *(u_int32_t *)(&buf[17]) = htonl((u_int32_t)(
            (audio->write_ahead.ms * 1000) + audio->write_ahead.us
          ));
          *(u_int32_t *)(&buf[21]) = htonl((u_int32_t)(
            (audio->cumulative_latency.ms * 1000) +
            audio->cumulative_latency.us
          ));

          *(u_int16_t *)(&buf[25]) = htons((u_int16_t)audio->sample_size);
          *(u_int16_t *)(&buf[27]) = htons((u_int16_t)audio->channels);
          *(u_int32_t *)(&buf[29]) = htonl((u_int32_t)audio->sample_rate);   

          *(u_int32_t *)(&buf[33]) = htonl((u_int32_t)audio->bytes_per_second);

#ifdef OSS_BUFFRAG
          *(u_int8_t *)(&buf[37]) = (u_int8_t)((audio->allow_fragments) ? 1 : 0);
          *(u_int32_t *)(&buf[38]) = htonl((u_int32_t)audio->num_fragments);
          *(u_int32_t *)(&buf[42]) = htonl((u_int32_t)(
            1 << audio->fragment_size
          ));
#endif      /* OSS_BUFFRAG */

          *(u_int8_t *)(&buf[46]) = (u_int8_t)((audio->flip_stereo) ? 1 : 0);
          *(u_int8_t *)(&buf[47]) = (u_int8_t)audio->direction;

          name_len = MIN(strlen(audio_mode_name), YAudioNameMax);
          strncpy(&buf[48], audio_mode_name, name_len);
          /* Do not null terminate string. */

          /* Since this is a variable length segment we need to
           * recalculate the segment length after the formatting
           * above and update the segment header's chunk size.
           */
          actual_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 + 1 + 1 +
            name_len;
          *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);
      }

      return(YNetSendToConnection(con_num, buf, actual_seg_len));
}
/*
 *    Parses audio stats.
 */
int YNetParseAudioStats(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YAudioStatsGet)
      {
          /* <u32_cs> <u16_majop> <u16_minop>
           */
          const YDataLength this_seg_len = 8;

          if(chunk_length < this_seg_len)
            return(-1);

          if(recorder == NULL)
            YNetSendAudioStats(con_num, NULL);
          else
              YNetSendAudioStats(con_num, &recorder->audio);
      }
      else if(minor_op_code == YAudioStatsSet)
      {

      }


      return(0);
}

/*
 *    Send list audio modes, mode values.
 */
int YNetSendListAudioModes(
      YConnectionNumber con_num,
      const char *audio_mode_name,
      int sample_rate, int channels,
      int sample_size, int fragment_size_bytes,
      char direction, Boolean allow_fragmenting, int num_fragments
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_samplerate> <u32_channels>
       * <u32_samplesize> <u32_fragmentsizebytes>
       * <u8_direction> <u8_allowfragmenting> <u32_numfragments>
       * <...audiomodename...>
       */
      const YDataLength this_seg_len = 8 + 8 + 8 + 6 + YAudioNameMax;
      char buf[this_seg_len];
      YDataLength name_len, actual_seg_len;


      if(audio_mode_name == NULL)
          return(-1);

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YListAudioModes);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YListAudioModesSet);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)sample_rate);
      *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)channels);
      *(u_int32_t *)(&buf[16]) = htonl((u_int32_t)sample_size);
      *(u_int32_t *)(&buf[20]) = htonl((u_int32_t)fragment_size_bytes);

      *(u_int8_t *)(&buf[24]) = (u_int8_t)direction;
      *(u_int8_t *)(&buf[25]) = (u_int8_t)((allow_fragmenting) ? 1 : 0);
      *(u_int32_t *)(&buf[26]) = htonl((u_int32_t)num_fragments);

      name_len = MIN(strlen(audio_mode_name), YAudioNameMax);
      strncpy(
          &buf[30],
          audio_mode_name,
          name_len
      );
      /* Do not null terminate string. */

      /* Since this is a variable length segment we need to
       * recalculate the segment length after the formatting
       * above and update the segment header's chunk size.
       */
      actual_seg_len = 8 + 8 + 8 + 6 + name_len;
      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

      return(YNetSendToConnection(con_num, buf, actual_seg_len));
}
/*
 *    Parses list audio modes.
 */
int YNetParseListAudioModes(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YListAudioModesGet)
      {
          /* Parse list audio modes get. */

          /* <u32_cs> <u16_majop> <u16_minop>
           */
          const YDataLength this_seg_len = 8;
          int i;
          YMode *m;

            
          if(chunk_length < this_seg_len)
            return(-1);

          /* Send list of preset audio modes. */
          for(i = 0; i < total_ymodes; i++)
          {
            m = ymode[i];
            if(m == NULL)
                continue;

            YNetSendListAudioModes(
                con_num,   
                m->name,
                m->sample_rate,
                m->channels,
                m->sample_size,
#ifdef OSS_BUFFRAG
                (1 << m->fragment_size),
#else
                1024,
#endif
                m->direction,
#ifdef OSS_BUFFRAG
                m->allow_fragments,
                m->num_fragments
#else
                False,
                1
#endif
            );
          }


          /* End with an audio mode with no name (indicating end). */
          YNetSendListAudioModes(
            con_num,
            "",
            0, 0,
            0, 0,
            0, False, 0
          );
      }
      else if(minor_op_code == YListAudioModesSet)
      {

      }         
      
      return(0);
}

/*
 *    Sends play sound object values.
 */
int YNetSendPlaySoundObjectValues(
      YConnectionNumber con_num, PlayStack *ps_ptr
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_flags> <u32_yid>
       * <u32_position> <u32_length>
       * <u32_repeats> <u32_total_repeats>
       * <u16_volume_left> <u16_volume_right>
       * <u16_volume_back_left> <u16_volume_back_right>
       * <u32_sample_rate>
       */
      const YDataLength this_seg_len = 8 + 8 + 8 + 8 + 4 + 4 + 4;
      char buf[this_seg_len];

      /* Set flags to match client side (see Y2/Ylib.h). */
      unsigned long flags =   (1 << 1) | (1 << 2) | (1 << 3) |
                        (1 << 4) | (1 << 5) | (1 << 6) |
                        (1 << 7); 

      /* If no playstack is given then send error. */
      if(ps_ptr == NULL)
      {
          *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
          *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlayValues);
          *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectPlayValuesSet);
          *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)flags);
          *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)YIDNULL);

          return(YNetSendToConnection(con_num, buf, this_seg_len));
      }

#define COEFFICIENT_TO_BUF16(x) (u_int16_t)( \
 (Coefficient)(x) * (Coefficient)((u_int16_t)-1) \
)
      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlayValues);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectPlayValuesSet);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)flags);
      *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)ps_ptr->yid);

      *(u_int32_t *)(&buf[16]) = htonl((u_int32_t)ps_ptr->position);
      *(u_int32_t *)(&buf[20]) = htonl((u_int32_t)ps_ptr->data_length);

      *(u_int32_t *)(&buf[24]) = htonl((u_int32_t)ps_ptr->repeats);
      *(u_int32_t *)(&buf[28]) = htonl((u_int32_t)ps_ptr->total_repeats);

      *(u_int16_t *)(&buf[32]) = htons(COEFFICIENT_TO_BUF16(
          ps_ptr->volume_left
      ));
      *(u_int16_t *)(&buf[34]) = htons(COEFFICIENT_TO_BUF16(
          ps_ptr->volume_right
      ));
      *(u_int16_t *)(&buf[36]) = htons(COEFFICIENT_TO_BUF16(
          ps_ptr->volume_back_left
      ));
      *(u_int16_t *)(&buf[38]) = htons(COEFFICIENT_TO_BUF16(
          ps_ptr->volume_back_right
      ));

      *(u_int32_t *)(&buf[40]) = htonl((u_int32_t)ps_ptr->applied_sample_rate);
#undef COEFFICIENT_TO_BUF16

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}
/*
 *    Parses play sound object values.
 */
int YNetParsePlaySoundObjectValues(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YSoundObjectPlayValuesGet)
      {
          /* Parse play sound object values get. */

          /* <u32_cs> <u16_majop> <u16_minop>
           * <u32_yid>
           */
          const YDataLength this_seg_len = 8 + 4;
          Boolean response_sent = False;
          YID yid;
          int i;
          PlayStack *ps;

          if(chunk_length < this_seg_len)
            return(-1);

          yid = (YID)ntohl(*(u_int32_t *)(&buf[8]));

          /* Check if yid exists. */
          for(i = 0; i < total_playstacks; i++)
          {
            ps = playstack[i];
            if(ps == NULL)
                continue;

            if((ps->owner != con_num) ||
               (ps->yid == YIDNULL)
            )
                continue;

            if(ps->yid == yid)
            {
                /* Send playing sound object values. */
                YNetSendPlaySoundObjectValues(con_num, ps);
                response_sent = True;
                break;
            }
          }
          if(!response_sent)
            YNetSendPlaySoundObjectValues(con_num, NULL);
      }
      else if(minor_op_code == YSoundObjectPlayValuesSet)
      {
          /* Parse play sound object values set. */

          /* <u32_cs> <u16_majop> <u16_minop>
           * <u32_flags> <u32_yid>
           * <u32_pos> <u32_totalrepeats>
           * <u16_volume_left> <u16_volume_right>
           * <u16_volume_back_left> <u16_volume_back_right>
           * <u32_sample_rate>
           */
          const YDataLength this_seg_len = 8 + 8 + 8 + 4 + 4 + 4;
          int i;
          u_int32_t flags;
          YID yid;
          u_int32_t position;
          u_int32_t total_repeats;
          u_int16_t vol[4];
          u_int32_t sample_rate;
          PlayStack *ps;

          if(chunk_length < this_seg_len)
            return(-1);

          flags = ntohl(*(u_int32_t *)(&buf[8]));
          yid = ntohl(*(u_int32_t *)(&buf[12]));

          position = ntohl(*(u_int32_t *)(&buf[16]));
          total_repeats = ntohl(*(u_int32_t *)(&buf[20]));

          vol[0] = ntohs(*(u_int16_t *)(&buf[24]));
          vol[1] = ntohs(*(u_int16_t *)(&buf[26]));
          vol[2] = ntohs(*(u_int16_t *)(&buf[28]));
          vol[3] = ntohs(*(u_int16_t *)(&buf[30]));

          sample_rate = ntohl(*(u_int32_t *)(&buf[32]));

          /* Check if yid exists. */
          for(i = 0; i < total_playstacks; i++)
          {
            ps = playstack[i];
            if(ps == NULL)
                continue;

            if((ps->owner != con_num) ||
               (ps->yid == YIDNULL)
            )
                continue;

            if(ps->yid == yid)
            {
                /* Set new values to play stack. */

                /* Change position? */
                if(flags & (1 << 2))
                  ps->position = (YDataPosition)position;

                /* Change total repeats? */
                if(flags & (1 << 5))
                  ps->total_repeats = (int)total_repeats;

                /* Change volume? */
                if(flags & (1 << 6))
                {
                  ps->volume_left = (double)vol[0] /
                      (double)((u_int16_t)-1);
                  ps->volume_right = (double)vol[1] /
                      (double)((u_int16_t)-1);
                  ps->volume_back_left = (double)vol[2] /
                      (double)((u_int16_t)-1);
                  ps->volume_back_right = (double)vol[3] /
                      (double)((u_int16_t)-1);
                }

                /* Change sample size? */
                if(flags & (1 << 7))
                  ps->applied_sample_rate = (int)sample_rate;

                /* Send response of values set. */
                YNetSendPlaySoundObjectValues(con_num, ps);
                break;
            }
          }
      }

      return(0);
}

/*
 *    Sends client message.
 */
int YNetSendClientMessage(
      YConnectionNumber con_num, int format, int type,
      const char *message, YDataLength length
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_format> <u32_type>
       * <...message...>
       */
      const YDataLength this_seg_len = 8 + 8 + YClientMessageMessageMax;
      YDataLength actual_seg_len = 8 + 8;
      char buf[this_seg_len];


      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YClientMessage);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YClientMessageReceive);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)format);
      *(u_int32_t *)(&buf[12]) = htonl((u_int32_t)type);

      if((length > 0) && (message != NULL))
      {
          memcpy(&buf[16], message, length);
          actual_seg_len += length;
      }
      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

      return(YNetSendToConnection(con_num, buf, actual_seg_len));
}
/*
 *    Parses client message.
 */
int YNetParseClientMessage(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YClientMessageSend)
      {
          /* Parse client message send. */

          /* <u32_cs> <u16_majop> <u16_minop>
           * <u32_format> <u32_type>
           * <u8_notify_self>
           */
          const YDataLength base_seg_len = 8 + 8 + 1;
          YConnection *con;
          int i, format, type;
          Boolean notify_self;
          char message[YClientMessageMessageMax];
          YDataLength message_length;

          if(chunk_length < base_seg_len)
            return(-1);

          format = ntohl(*(u_int32_t *)(&buf[8]));
          type = ntohl(*(u_int32_t *)(&buf[12]));
          notify_self = (*(u_int8_t *)(&buf[16])) ? True : False;

          *message = '\0';
          message_length = MIN(
            chunk_length - base_seg_len,
            YClientMessageMessageMax
          );;
          if(message_length > 0)
            memcpy(message, &buf[17], message_length);

          /* Notify connections about client message. */
          for(i = 0; i < total_yconnections; i++)
          {
            con = yconnection[i];
            if((con != NULL) ? (con->socket < 0) : True)
                continue;

            if((i == con_num) && !notify_self)
                continue;

            YNetSendClientMessage(
                i, format, type, message, message_length
            );
          }
      }

      return(0);
}

/*
 *    Sends YSHM sound open.
 */
int YNetSendYSHMSoundOpen(
      YConnectionNumber con_num, int yshm_id
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_shm_id>
       */
      const YDataLength this_seg_len = 8 + 4;
      char buf[this_seg_len];

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSHMSound);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSHMSoundOpen);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yshm_id);

      return(YNetSendToConnection(con_num, buf, this_seg_len));

      return(0);
}

/*
 *      Sends YSHM sound close.
 */
int YNetSendYSHMSoundClose(
      YConnectionNumber con_num, int yshm_id
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_shm_id>
       */
      const YDataLength this_seg_len = 8 + 4;
      char buf[this_seg_len];

      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSHMSound);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSHMSoundClose);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yshm_id);

      return(YNetSendToConnection(con_num, buf, this_seg_len));

      return(0);
}

/*
 *    Parses YSHM sound open.
 */
int YNetParseYSHMSoundOpen(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YSHMSoundOpen)
      {
          /* <u32_cs> <u16_majop> <u16_minop>
           */
          const YDataLength this_seg_len = 8;
          int shm_id = -1;

          if(chunk_length < this_seg_len)
            return(-1);

#ifdef YSHM_SUPPORT
          if(recorder != NULL)
          {
            Sound *sound_ptr = &recorder->sound;
            if(sound_ptr->buffer != NULL)
                shm_id = sound_ptr->buffer_shm_id;
          }
#endif      /* YSHM_SUPPORT */

          YNetSendYSHMSoundOpen(con_num, shm_id);
      }
      else if(minor_op_code == YSHMSoundClose)
      {
          /* Client should never tell the server to do this. */
      }

      return(0);
}

/*
 *    Notify all connections about YSHM sound buffer closing.
 */
void YNetDoNotifyAllYSHMSoundClose(void)
{
      YConnectionNumber i;
      YConnection *con;
      int shm_id = -1;

#ifdef YSHM_SUPPORT
      if(recorder != NULL)
      {
          Sound *sound_ptr = &recorder->sound;
          if(sound_ptr->buffer != NULL)
            shm_id = sound_ptr->buffer_shm_id;
      }
#endif  /* YSHM_SUPPORT */

      for(i = 0; i < total_yconnections; i++)
      {
          con = yconnection[i];
          if((con != NULL) ? (con->socket < 0) : True)
            continue;

          YNetSendYSHMSoundClose(i, shm_id);
      }
}

/*
 *    Sends audio CD play track.
 */
int YNetSendAudioCDPlayTrack(
      YConnectionNumber con_num,
      int track_number
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_track_number>
       */
      const YDataLength this_seg_len = 8 + 4;
      char buf[this_seg_len];


      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDPlayTrack);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)track_number);

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}

/*
 *    Sends audio CD stop.
 */
int YNetSendAudioCDStop(
      YConnectionNumber con_num
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       */
      const YDataLength this_seg_len = 8;
      char buf[this_seg_len];


      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDStop);

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}

/*
 *    Sends audio CD eject.
 */
int YNetSendAudioCDEject(
      YConnectionNumber con_num
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       */
      const YDataLength this_seg_len = 8;
      char buf[this_seg_len];


      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDEject);

      return(YNetSendToConnection(con_num, buf, this_seg_len));
}

/*
 *      Parses audio CD (play track, stop, and eject).
 */
int YNetParseAudioCD(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YAudioCDStatsGet)
      {

      }
      else if(minor_op_code == YAudioCDStatsSet)
      {

      }
      else if(minor_op_code == YAudioCDPlayTrack)
      {
          /* Parse audio CD play track. */

          /* <u32_cs> <u16_majop> <u16_minop>
           * <u32_track_number>
           */
          const YDataLength this_seg_len = 8 + 4;
          int track_number;

          if(chunk_length < this_seg_len)
            return(-1);

          track_number = ntohl(*(u_int32_t *)(&buf[8]));

          if(recorder != NULL)
          {
            int i;
            YConnection *con;
            Audio *audio_ptr = &recorder->audio;

            /* Play audio CD track. */
            AudioCDPlayTrack(audio_ptr->audiocd_context, track_number);

            /* Notify all connections about play audio CD track. */
            for(i = 0; i < total_yconnections; i++)
            {
                con = yconnection[i];
                if((con != NULL) ? (con->socket < 0) : True)
                  continue;

                YNetSendAudioCDPlayTrack(i, track_number);
            }
          }
      }
      else if(minor_op_code == YAudioCDStop)
      {
          /* Parse stop audio CD. */

          /* <u32_cs> <u16_majop> <u16_minop>
           */
          const YDataLength this_seg_len = 8;

          if(chunk_length < this_seg_len)
            return(-1);

          if(recorder != NULL)
          {
            int i;
            YConnection *con;
            Audio *audio_ptr = &recorder->audio;

            /* Stop audio CD. */
            AudioCDStop(audio_ptr->audiocd_context);

            /* Notify all connections about stop audio CD. */
            for(i = 0; i < total_yconnections; i++)
            {
                con = yconnection[i];
                if((con != NULL) ? (con->socket < 0) : True)
                  continue;

                YNetSendAudioCDStop(i);
            }
          }
      }
      else if(minor_op_code == YAudioCDEject)
      {
          /* Parse eject audio CD. */

          /* <u32_cs> <u16_majop> <u16_minop>
           */
          const YDataLength this_seg_len = 8;

          if(chunk_length < this_seg_len)
            return(-1);

          if(recorder != NULL)
          {
            int i;
            YConnection *con;
            Audio *audio_ptr = &recorder->audio;

            /* Eject audio CD. */
            AudioCDEject(audio_ptr->audiocd_context);

            /* Notify all connections about eject audio CD. */
            for(i = 0; i < total_yconnections; i++)
            {
                con = yconnection[i];
                if((con != NULL) ? (con->socket < 0) : True)
                  continue;

                YNetSendAudioCDEject(i);
            }
          }
      }

      return(0);
}

/*
 *    Sends audio CD track.
 */
int YNetSendListAudioCDTracks(
      YConnectionNumber con_num,
      int track_number,
      unsigned long track_start_time,
      unsigned long track_length,
      const char *track_name
)
{
      /* <u32_cs> <u16_majop> <u16_minop>
       * <u32_track_number>
       * <u64_track_start_time>
       * <u64_track_length>
       * <...track_name...>
       */
      const YDataLength this_seg_len = 8 + 4 + 8 + 8 + YAudioCDTrackNameMax;
      YDataLength actual_seg_len;
      int len;
      char buf[this_seg_len];


      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
      *(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCDTracksList);
      *(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDTracksListSet);

      *(u_int32_t *)(&buf[8]) = htonl((u_int32_t)track_number);
      *(u_int32_t *)(&buf[12]) = htonl((u_int64_t)track_start_time);
      *(u_int32_t *)(&buf[20]) = htonl((u_int64_t)track_length);

      len = (track_name != NULL) ?
          MIN(strlen(track_name), YAudioCDTrackNameMax - 1) : 0;
      if(len > 0)
          strncpy(&buf[28], track_name, len);
      /* Do not null terminate string. */

      /* Since this is a variable length segment we need to
       * recalculate the segment length after the formatting
       * above and update the segment header's chunk size.
       */
      actual_seg_len = 8 + 4 + 8 + 8 + len;
      *(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

      return(YNetSendToConnection(con_num, buf, actual_seg_len));
}

/*
 *    Parses audio CD tracks list.
 */
int YNetParseListAudioCDTracks(YCNP_STD_INPUTS_PROTO)
{
      if(minor_op_code == YAudioCDTracksListGet)
      {
          /* Parse list audio CD tracks get. */

          /* <u32_cs> <u16_majop> <u16_minop>
           */
          const YDataLength this_seg_len = 8;


          if(chunk_length < this_seg_len)
            return(-1);

          if(recorder != NULL)
          {
            Audio *audio_ptr = &recorder->audio;
            int i, total;
            AudioCDTrack **track, *t;

            /* Get list of audio CD tracks. */
            track = AudioCDListTracks(
                audio_ptr->audiocd_context, &total
            );
            for(i = 0; i < total; i++)
            {
                t = track[i];
                if(t == NULL)
                  continue;

                /* Send this audio CD track. */
                YNetSendListAudioCDTracks(
                  con_num, t->number, t->start_time, t->length, t->name
                );
            }
            AudioCDDeleteTracksList(track, total);
          }
          /* End the list by sending track 0 to indicate end. */
          YNetSendListAudioCDTracks(con_num, 0, 0, 0, NULL);
      }
      else if(minor_op_code == YAudioCDTracksListSet)
      {

      }         
      
      return(0);

}


/*
 *      Parses the data in buf received by the connection con_num.
 *
 *    This function is called by YNetRecv().
 */
static int YNetParse(
      YConnectionNumber con_num,
      const u_int8_t *buf,
      u_int32_t chunk_length,
      u_int16_t major_op_code,
      u_int16_t minor_op_code
)
{
      int status;
      YConnection *con;      


      /* Connection assumed valid. */
      con = yconnection[con_num];


      switch(major_op_code)
      {
        case YAudioChange:
          status = YNetParseAudioChange(YCNP_STD_INPUTS);
          break;

        case YCycleChange:
          status = YNetParseCycleChange(YCNP_STD_INPUTS);
          break;
        
        case YDisconnect:
          status = YNetParseDisconnect(YCNP_STD_INPUTS);
          break;

        case YSetHost:
          status = YNetParseSetHost(YCNP_STD_INPUTS);
          break;

        case YListHosts:
/* Work on this later. */
          break;
 
        case YMixerChannel:
          status = YNetParseMixerDevice(YCNP_STD_INPUTS);
          break;
      
        case YListMixers:
/* Work on this later. */
          break; 
 
        case YSoundObjectPlay:
          status = YNetParseSoundObjectPlay(YCNP_STD_INPUTS);
          break;
 
        case YSoundObjectKill:
          status = YNetParseSoundObjectKill(YCNP_STD_INPUTS);
          break;

        case YSoundObjectAttributes:
          status = YNetParseSoundObjectAttributes(YCNP_STD_INPUTS);
          break;
 
        case YShutdown:
          status = YNetParseShutdown(YCNP_STD_INPUTS);
          break;
 
        case YSync:
          status = YNetParseSync(YCNP_STD_INPUTS);
          break;

        case YAudioStats:
          status = YNetParseAudioStats(YCNP_STD_INPUTS);
          break;

        case YServerStats:
          status = YNetParseServerStats(YCNP_STD_INPUTS);
          break;

        case YListAudioModes:
          status = YNetParseListAudioModes(YCNP_STD_INPUTS);
          break;

        case YSoundObjectPlayValues:
          status = YNetParsePlaySoundObjectValues(YCNP_STD_INPUTS);
          break;

        case YClientMessage:
          status = YNetParseClientMessage(YCNP_STD_INPUTS);
          break;

        case YSHMSound:
          status = YNetParseYSHMSoundOpen(YCNP_STD_INPUTS);
          break;

        case YAudioCD:
          status = YNetParseAudioCD(YCNP_STD_INPUTS);
          break;

        case YAudioCDTracksList:
          status = YNetParseListAudioCDTracks(YCNP_STD_INPUTS);
          break;


        default:
          YNetPrintError(
            stderr,
            con_num,
            chunk_length,
            major_op_code,
            minor_op_code,    
            "Unsupported Major Op Code"
          );
          break;
      }

      return(0);
}

/*
 *    Handles incoming data from connection con_num.
 */
int YNetRecv(YConnectionNumber con_num)
{
      YConnection *con;
      int i, n, s, status, loops;
      struct timeval timeout;
      fd_set readfds;

      u_int8_t *buf_ptr;  
      int bytes_read, segments_handled;

      u_int32_t chunk_length;
      u_int16_t major_op_code, minor_op_code;



      /* Reset close connection marker. */
      close_this_connection = False;


      /* Connection is assumed valid and connected. */
      con = yconnection[con_num];

      /* Get connection's socket number. */
      s = con->socket;


      if(con->buf == NULL)   
          return(0);

      if(con->buf_cont < 0)
          con->buf_cont = 0;


      if((con->buf_len - con->buf_cont) <= 0)
      {
          /* Contents buffer overflowed and thus we are unable to
           * parse the current buffer any more. So reset the
           * contents buffer and and disconnect connection since
           * there is no way to seek the next chunk position.
           */
          con->buf_cont = 0;

          /* Disconnect and delete connection. */
          if(con->socket > -1)
          {
            close(con->socket);
            con->socket = -1;
          }
          YiffCloseConnection(con_num);

          fprintf(
            stderr,
 "YNetRecv(): Connection %i: Contents overflowed buffer length %ld.\n",
            con_num,
            con->buf_len
          );

          return(0);
      }


      /* Do not block, check if there is any data to be read. */
      timeout.tv_sec = 0;
      timeout.tv_usec = 0;
      FD_ZERO(&readfds);
      FD_SET(s, &readfds);
      status = select(s + 1, &readfds, NULL, NULL, &timeout);
      if(status == -1)
          perror("select");

      if(!FD_ISSET(s, &readfds))
          return(0);


      buf_ptr = &(con->buf[con->buf_cont]);
      bytes_read = recv(
          s,
          buf_ptr,
          con->buf_len - con->buf_cont,
          0
      );
      if(bytes_read == 0)
      {
          /* Disconnect and delete connection. */
          if(con->socket > -1)
          {
            close(con->socket);
            con->socket = -1;
          }
          YiffCloseConnection(con_num);

          return(0);
      }
      else if(bytes_read <= 0)
      {
          /* Handle error. */
          switch(errno)
          {
            case EWOULDBLOCK:
            break;

            case EINTR:
            break;

            default:
            /* Disconnect and delete connection. */
            if(con->socket > -1)
            {
                close(con->socket);
                con->socket = -1;
            }
            YiffCloseConnection(con_num);
            break;   
          }
          return(0);
      } 

      /* Increment buffer contents. */
      con->buf_cont += bytes_read;
      if(con->buf_cont > con->buf_len)
          con->buf_cont = con->buf_len;

      segments_handled = 0;
      for(loops = 0; loops < 30; loops++)
      {
          /* Check if we got atleast the first 8 bytes, needed
           * for chunk size, major op code, and minor op code.
           */
          if(con->buf_cont < 8)
            break;

          /* All chunks start off with the same format, that being:
           *   <4 bytes, chunk length> <2 bytes, major op code>
           *   <2 bytes, minor op code>
           */
          chunk_length = (u_int32_t)MAX(
            (long)ntohl(*((u_int32_t *)(&con->buf[0]))), (long)0
          );
          major_op_code = ntohs(*((u_int16_t *)(&con->buf[4])));
          minor_op_code = ntohs(*((u_int16_t *)(&con->buf[6])));


          /* Specified chunk length must be 8 or greater. */
          if(chunk_length < 8)
          {
            YNetPrintError(
                stderr,
                con_num,
                chunk_length,
                major_op_code,
                minor_op_code,
 "Recieved a segment with header specified chunk length less than 8 bytes"
            ); 

            /* Disconnect and delete connection. */
            if(con->socket > -1)
            {
                close(con->socket);
                con->socket = -1;
            }
            YiffCloseConnection(con_num);

            return(0);
          }


          /* Did we get the entire chunk? Is the indicated chunk
           * length greater than the contents buffer length?
           */
          if((YDataLength)chunk_length > (YDataLength)con->buf_cont)
            break;
/*
{
 printf("Server: %i %i %i: Recieved incomplete.\n",
  chunk_length, major_op_code, minor_op_code
 );
            break;
}
else
 printf("Server: %i %i | %i %i: Recieved.\n",
  chunk_length, con->buf_cont, major_op_code, minor_op_code
 );
 */

          /* Parse this segement and put data into event structure. */
          YNetParse(
            con_num,
            con->buf,
            chunk_length,
            major_op_code,
            minor_op_code
          );
          segments_handled++;

          /*   Stop parsing and close this connection? This would
           *   be said if the connection needed to be closed for some
           *   reason determined in a higher call to YNetParse()
           *   above.
           */
          if(close_this_connection)
          {
            /* Disconnect and delete connection. */
            if(con->socket > -1)
            {
                close(con->socket);
                con->socket = -1;
            }
            YiffCloseConnection(con_num);

            break;
          }

          /* Shift the buffer. */
          for(i = 0, n = chunk_length;
            n < con->buf_cont;
            i++, n++
          )
            con->buf[i] = con->buf[n];

          /* Decrease buffer contents. */
          con->buf_cont = (YDataLength)con->buf_cont -
            (YDataLength)chunk_length;
          if(con->buf_cont < 0)
            con->buf_cont = 0;
      }

      /*   Return number of segments handled which implies events
       *   handled.
       */
      return(segments_handled);
}

Generated by  Doxygen 1.6.0   Back to index