#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 STRDUP(s)	(((s) != NULL) ? strdup(s) : NULL)

#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 = STRDUP(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 the listening socket */
	if(listen_socket > -1)
	{
	    shutdown(listen_socket, SHUT_RDWR);
	    close(listen_socket);
	    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)
	{
	    YShutdownSocket(new_socket, SHUT_RDWR, 30l);
	    YCloseSocketWait(new_socket, 30l);
	    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))
	{
#if 0
	    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]
	    );
#endif

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

	    /* Close and delete connection */
            YShutdownSocket(con_ptr->socket, SHUT_RDWR, 30l);
            YCloseSocketWait(con_ptr->socket, 30l);
            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;
	PlayStack *ps;

	con = ConnectionGetPtr(con_num);
	if(con == 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);
	}


	/* Close connection as needed */
	if(con->socket > -1)
	{
	    YNetSendDisconnect(con_num, 0);
            YShutdownSocket(con->socket, SHUT_RDWR, 30l);
            YCloseSocketWait(con->socket, 30l);
            con->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);
}
