
/*
 * This file is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify this file without charge, but are not authorized to
 * license or distribute it to anyone else except as part of a product
 * or program developed by the user.
 * 
 * THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * This file is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS FILE
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even
 * if Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
/*
 *   This file implements the audio X11 extension. Aug 28,89
 *	by phoebe couch
 *
 *     ProcAudioDispatch() Audio extension dispatch for nonswapped clients.
 *     SProcAudioDispatch() Swapping dispatch. (Not Written).
 *     AudioExtensionInit() Inits the Audio extension.
 *
 * Comments, bugs, gripes to phoebe@sun.com
 */

#include "X.h"
#define NEED_REPLIES
#define NEED_EVENTS
#include "Xproto.h"
#include "Xprotostr.h"
#include "dixstruct.h"
#include "extension.h"
#include "input.h"
#include "extnsionst.h"

/********************* device ***********************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/file.h>

#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sbusdev/audioreg.h>
#include <sun/audioio.h>
#include "audio.h"
#include <sys/mman.h>

extern void (* ReplySwapVector[256]) ();
int	Audio_fd;
#ifdef DEVDEBUG
int	debug = 1;
#else
int	debug = 0;
#endif
/*******************dependent ******************************/

int  AudioReqCode = 0;

#define MAX(a,b) ((a) > (b) ? (a) : (b)) 
#define MIN(a,b) ((a) < (b) ? (a) : (b)) 
#define FIXED(a) (((int) (a)) << 16)
#define INT(a) (((a) + (1 << 15)) >> 16 )

#define		LOCAL_SIZE	WRITE_SIZE
int	audio_playing = 0;
unsigned char	clients[256];
int	num_clients;

/*
 *Look and the minor opcode and call the appropiate function
 */
int
ProcAudioDispatch(client)
register ClientPtr client;
{
	REQUEST(xReq);
#ifdef ADEBUG
fprintf(stderr,"Dispatch %d :",stuff->data);
fprintf(stderr,"SProcAudioDispatch minor-opcode = %d :",stuff->data);
#endif DEBUG
	switch(stuff->data)
	{
	case	X_AUDIO_OPEN:
			return(ProcAudioOpen(client));
	case	X_AUDIO_CLOSE:
			return(ProcAudioClose(client));
	case	X_AUDIO_SET_VOLUME:
			return(ProcAudioSetVolume(client));
	case	X_AUDIO_PLAY:
			return(ProcAudioPlay(client));
	case	X_AUDIO_PLAY_OFF:
			return(ProcAudioPlayOff(client));
	case	X_AUDIO_RECORD:
			return(ProcAudioRecord(client));
	case	X_AUDIO_RECORD_OFF:
			return(ProcAudioRecordOff(client));
	default:
		SendErrorToClient(client,
			AudioReqCode, stuff->data, 0, BadRequest);
		return(BadRequest);
	}
}

/* Macros needed for byte-swapping, copied from swapreq.c.  Really
   should be in a header file somewhere. */

#define LengthRestS(stuff) \
    ((stuff->length << 1)  - (sizeof(*stuff) >> 1))

#define SwapRestS(stuff) \
    SwapShorts(stuff + 1, LengthRestS(stuff))

/*
 *Look and the minor opcode and call the appropiate function
 *byte swapping may be neccesary.
 */
int
SProcAudioDispatch(client)
register ClientPtr client;
{
	REQUEST(xReq);
#ifdef DEBUG
fprintf(stderr,"SProcAudioDispatch minor-opcode = %d :",stuff->data);
#endif DEBUG
	switch(stuff->data)
	{
	case	X_AUDIO_OPEN:
			return(ProcAudioOpen(client));
	case	X_AUDIO_CLOSE:
			return(ProcAudioClose(client));
	case	X_AUDIO_SET_VOLUME:
			return(SProcAudioSetVolume(client));
	case	X_AUDIO_PLAY:
			return(SProcAudioPlay(client));
	case	X_AUDIO_PLAY_OFF:
			return(ProcAudioPlayOff(client));
	case	X_AUDIO_RECORD:
			return(ProcAudioRecord(client));
	case	X_AUDIO_RECORD_OFF:
			return(ProcAudioRecordOff(client));
	default:
		SendErrorToClient(client,
			AudioReqCode, stuff->data, 0, BadRequest);
		return(BadRequest);
	}
}

/*
 * Clean-up function- needed in order to set up extension.
 */
void
AudioReset()
{
	Audio_Close();
}


/*
 * AudioExtensionInit
 *
 * Called from InitExtensions in server/dix/extension.c
 *
 * Audio extension at this time has no events or errors 
 * (other than the core errors)
*/

void
AudioExtensionInit()
{
	ExtensionEntry *extEntry;
	int	ProcAudioDispatch(), SProcAudioDispatch();
	void	AudioReset();

	extEntry = AddExtension(AUDIONAME, 0, 0, ProcAudioDispatch,
				SProcAudioDispatch, AudioReset);
	if (extEntry) {
		AudioReqCode = extEntry->base;
	}
	else
		FatalError("AudioExtensionInit: AddExtensions failed\n");
	bzero( (unsigned char *)clients, 256 );
}          

/*
 * Open up the audio channel, return 1 if audio port
 * opened correctly, otherwise return 0
 */
int
ProcAudioOpen(client)
register ClientPtr client;
{
	xAudio_Reply rep;
	REQUEST(xReq);
	REQUEST_SIZE_MATCH(xReq);

#ifndef NO_SOUND
	rep.opened = AudioOpen();
#else
	rep.opened = 1;
#endif NO_SOUND
#ifdef DEBUG
	fprintf(stderr,"Audio opened =%d\n" , rep.opened );
	fprintf(stderr,"client = %d.\n",client->index);
#endif DEBUG
	rep.type = X_Reply;
	rep.length = 0;
	rep.sequenceNumber = client->sequence;

	enter_client(client->index);
	WriteReplyToClient(client,sizeof(xAudio_Reply),&rep);
	return(client->noClientException);
}

/* Close the audio channel- if there's still data in the
 * queue waiting to be played, it will return 0, otherwise
 * it will return 1. User is reponsible for closing the 
 * audio channel.
 */
int
ProcAudioClose(client)
register ClientPtr client;
{
	xAudio_Reply rep;
	REQUEST(xReq);
	REQUEST_SIZE_MATCH(xReq);
#ifdef DEBUG
	fprintf(stderr,"Audio close: playing = %d.\n",audio_playing);
#endif DEBUG
	remove_client(client->index);
	if ( audio_playing && (num_clients == 1)) /* last one */
	{
			rep.closed = 0; /* don't shut it off yet */
	}
	else
	{
		if (!num_clients)
		{
#ifndef NO_SOUND
			Audio_Close();
#endif NO_SOUND
			rep.closed = 1;
		}
		else
			rep.closed = 1;
#ifdef DEBUG
		fprintf(stderr,"Audio close:num_clients=%d.\n",num_clients);
#endif DEBUG
	}
	rep.type = X_Reply;
	rep.length = 0;
	rep.sequenceNumber = client->sequence;

	WriteReplyToClient(client,sizeof(xAudio_Reply) , &rep);
	return(client->noClientException);
}

/*
 *	Changes the volume on the sound chip.
 */
ProcAudioSetVolume(client)
register ClientPtr client;
{
	REQUEST(xAudio_Set_VolumeReq);
	REQUEST_SIZE_MATCH(xAudio_Set_VolumeReq);
#ifdef DEBUG
fprintf(stderr,"ProcSetVolume:size = %d.\n",stuff->length);
fprintf(stderr,"ProcSetVolume =  %d %d %d\n",stuff->gr, stuff->ger, stuff->gx);
#endif DEBUG
#ifndef NO_SOUND
	AudioSetVolume(stuff->gr, stuff->ger, stuff->gx);
#endif NO_SOUND
	return (Success);
}

/*
 * send a buffer full of sound data to the chip.
 */
int
ProcAudioPlay(client)
register ClientPtr client;
{
register	int	nbytes;
xAudio_Reply rep;
	REQUEST(xAudio_PlayReq);
	REQUEST_AT_LEAST_SIZE(xAudio_PlayReq);
	nbytes = (stuff->length << 2) - sizeof(xAudio_PlayReq);
#ifdef ADEBUG
fprintf(stderr,"ProcAudioPlay size %d -> %d\n",stuff->length,nbytes);
#endif DEBUG
	/* send the data to a temp file on the server */
	rep.status = send_to_play( (char *) &stuff[1], nbytes );
	/* if this is the first packet of data, start the
		playing process going */
	if (!audio_playing)
	{
#ifndef NO_SOUND
		start_playing();
#endif NO_SOUND
		audio_playing = 1;
#ifdef DEBUG
	fprintf(stderr,"ProcAudioPlay start to play.\n");
#endif DEBUG
	}
	rep.type = X_Reply;
	rep.sequenceNumber = client->sequence;
	WriteReplyToClient(client,sizeof(xAudio_Reply),&rep);
	return(client->noClientException);
}

static	int	recording_in_progress = 0;
/*
 * Turn off the playing. Stop audio io.
 * One register works for both play and record.
 */
int
ProcAudioPlayOff(client)
register ClientPtr client;
{
	REQUEST(xAudio_Play_OffReq);
#ifndef NO_SOUND
	stop_playing((int)stuff->forced);
#endif NO_SOUND
#ifdef DEBUG
	fprintf(stderr,"Audio Playoff \n");
#endif DEBUG
	recording_in_progress = 0;
	return (Success);
}

/*
 * Stop recording. Turn off audio io only if it is not 
 * also playing.
 */
int
ProcAudioRecordOff(client)
register ClientPtr client;
{
	/* don't shut it off yet */
	/*audio play will turn it off later */
#ifndef NO_SOUND
	if (!audio_playing )
		Audio_stop_recording();
#endif NO_SOUND
	recording_in_progress = 0;
#ifdef DEBUG
	fprintf(stderr,"record off\n");
#endif DEBUG
	return (Success);
}

/*
 * Record a buffer full of music
 */
int
ProcAudioRecord(client)
register ClientPtr client;
{
xAudio_RecordReply	rep;
static	char		buffer[LOCAL_SIZE]; /* used a lot */
static		int	size = 0;
static		int	i = 0;

	REQUEST(xReq);
	REQUEST_SIZE_MATCH(xReq);
	if (!recording_in_progress)
	{
		bzero(buffer,4000);
#ifndef NO_SOUND
		Audio_start_recording();
#endif NO_SOUND
		recording_in_progress = 1;
	}
#ifndef NO_SOUND
	size = AudioRecord(buffer,LOCAL_SIZE);
#else
	size += 4;
	for(;i<size;i++)
		buffer[i]=size;
#endif NO_SOUND
	rep.type = X_Reply;
	rep.length = size >> 2;
	rep.sequenceNumber = client->sequence;
#ifdef DEBUG
	fprintf(stderr,"ProcAudioRecord: size = %d\n",size);
#endif DEBUG
	WriteReplyToClient(client,sizeof(xAudio_RecordReply) , &rep);
	WriteToClient(client, size , buffer );/* no swap */
	return(client->noClientException);
}


/*
 *	Byte swapped set volume
 */
int
SProcAudioSetVolume(client)
register ClientPtr client;
{
register	char	n;
	REQUEST(xAudio_Set_VolumeReq);
	swaps(&stuff->length,n);
	SwapRestS(stuff);
	return(ProcAudioSetVolume(client));
}

/*
 *	Byte swapped play
 */
int
SProcAudioPlay(client)
register ClientPtr client;
{
register	char	n;
	REQUEST(xAudio_PlayReq);
	swaps(&stuff->length,n);
	/*SwapRestS(stuff); only one device , doesn't need it */ 
	return(ProcAudioPlay(client));
}

/************************************************************
	The device dependent part of the extension code
*************************************************************/

/*
 * open up the audio channel for read/write
 */
int
AudioOpen()
{
	struct	audio_ioctl	tmp;

	if ( !Audio_fd)
		if ((Audio_fd = open("/dev/audio",2)) < 0) {
			printf("Can't open /dev/audio.\n");
			return 0;
		}
	/*	Initialize the three gain registers to 0db.
	*/
	audio_set_ger(0);
	audio_set_gr(0);
	audio_set_gx(0);
	
	if(debug)
		fprintf(stderr,"all set up\n");

	/*	Tell the chip to set the gains according to the
		register values we just set.
	*/

	tmp.control = AUDIO_MAP_MMR1;
	tmp.data[0] = AUDIO_MMR1_BITS_LOAD_GX |
	    AUDIO_MMR1_BITS_LOAD_GR |
	    AUDIO_MMR1_BITS_LOAD_GER;

	if (ioctl(Audio_fd,AUDIOSETREG,&tmp) < 0) {
		perror("Set REG MMR1");
		return 0;
	}

	tmp.control = AUDIO_MAP_MMR2;
	if (ioctl(Audio_fd,AUDIOGETREG,&tmp) < 0) {
		perror("Set REG MMR2");
		return 0;
	}

	tmp.data[0] |= AUDIO_MMR2_BITS_LS;
	if (ioctl(Audio_fd,AUDIOSETREG,&tmp) < 0) {
		perror("Set REG MMR2");
		return 0;
	}
	return 1;
}

/*
 *  Turn off the audio channel, free it for others to use.
 */
Audio_Close()
{
	if (Audio_fd)
		close(Audio_fd);
	clean_up();
	if(debug)
		fprintf(stderr, "audio shut off\n");
	Audio_fd = 0;
}



/*
 *	set record and play volume
 */
AudioSetVolume(ger,gr,gx)
{
	if(debug)
		fprintf(stderr,"volume=%d,%d,%d.\n", ger,gr,gx);
	audio_set_ger(ger);
	audio_set_gr(gr);
	audio_set_gx(gx);
}

Audio_start_recording()
{
	int	dummy;
	if (ioctl(Audio_fd,AUDIOREADSTART,&dummy) < 0) {
		perror("AUDIOREADSTART ioctl failed");
		return;
	}
	if(debug)
		fprintf(stderr,"Start recording:\n");
}

/*
 * return buffers read off the sound chip.
 * size of buffer must be multiple of 4 bytes.
 */
int
AudioRecord (buffer,size)
char	*buffer;
int	size;
{
int	rtn;
int	read_size;
	if (ioctl(Audio_fd,AUDIOREADQ,&read_size) < 0)
	{
		perror("AUDIOREADQ ioctl failed");
		return(0);
	}
	if (read_size%4)	read_size -= read_size%4;
	if(debug)
		fprintf(stderr,"read_size= %d :",read_size);
	if (read_size > size )
		read_size = size;
	if ((rtn = read
		(Audio_fd, buffer, read_size))< 0)
		{
		fprintf(stderr,"Can't read the buffer\n");
		return(0);
		}
	if(debug)
		fprintf(stderr,"Read %d bytes ;\n",rtn);
	return( rtn);
}

/*
 *	 turn off audio io recording
 */
Audio_stop_recording()
{
	off();
}

/*
 * List of utility functions called by the above funcs
 */

/*
 *	turn off audio io, also flushes the buffer.
 *	This reg turns off all I.O. - play and record.
 */
off()
{
     int     dummy;
        if (ioctl(Audio_fd,AUDIOSTOP,&dummy) < 0) {
                        perror("AUDIOSTOP ioctl failed");
	}
}

/* These are tables of values to be loaded into various
 gain registers.
 * Note that for the ger entry for -8db, we use the data
 * sheet value for -7.5db.  The data sheet gives values for
 * -8db which are wrong and produce too much gain.
*/

static	unsigned char ger_table[][2] = {
		0xaa,	0xaa,	/* -10db */
		0x79,	0xac,
		/*0x41,	0x91,*/
		0x31,	0x99,	/* -7.5db */
		0x9c,	0xde,
		0x74,	0x9c,	/* -6db */
		0x6a,	0xae,
		0xab,	0xdf,
		0x64,	0xab,
		0x2a,	0xbd,
		0x5c,	0xce,
		0x00,	0x99,	/* 0db */
		0x43,	0xdd,
		0x52,	0xef,
		0x55,	0x42,
		0x31,	0xdd,
		0x43,	0x1f,
		0x40,	0xdd,	/* 6db */
		0x44,	0x0f,
		0x31,	0x1f,
		0x10,	0xdd,
		0x41,	0x0f,
		0x60,	0x0b,
		0x42,	0x10,	/* 12db */
		0x11,	0x0f,
		0x72,	0x00,
		0x21,	0x10,
		0x22,	0x00,
		0x00,	0x0b,
		0x00,	0x0f,	/* 18db */
};


static	unsigned char gr_gx_table[][2] = {
		0x8b,	0x7c,	/* -18db */
		0x8b,	0x35,
		0x8b,	0x24,
		0x91,	0x23,
		0x91,	0x2a,
		0x91,	0x3b,
		0x91,	0xf9,	/* -12db */
		0x91,	0xb6,
		0x91,	0xa4,
		0x92,	0x32,
		0x92,	0xaa,
		0x93,	0xb3,
		0x9f,	0x91,	/* -6db */
		0x9b,	0xf9,
		0x9a,	0x4a,
		0xa2,	0xa2,
		0xaa,	0xa3,
		0xbb,	0x52,
		0x08,	0x08,	/* 0db */
		0x3d,	0xac,
		0x25,	0x33,
		0x21,	0x22,
		0x12,	0xa2,
		0x11,	0x3b,
		0x10,	0xf2,	/* 6db */
		0x02,	0xca,
		0x01,	0x5a,
		0x01,	0x12,
		0x00,	0x32,
		0x00,	0x13,
		0x00,	0x0e,	/* 12db */
};



audio_set_ger(value)
int	value;
{
	struct	audio_ioctl	tmp;

#	ifdef ADEBUG
	printf("ger is now %d\n",value);
#	endif

	if ((value < -10) || (value > 18)) {
		printf("GER value %d out of range; %d <= GER <=  %d\n",
		    value,0,18);
		return;
	}

	/*  Add 10 to the value to get the index into the table.
	 */
	tmp.control = AUDIO_MAP_GER;
	tmp.data[0] = ger_table[value + 10][1];
	tmp.data[1] = ger_table[value + 10][0];

	if (ioctl(Audio_fd,AUDIOSETREG,&tmp) < 0) {
		perror("Set ger REG");
	}
}

audio_set_gr(value)
int	value;
{
	struct	audio_ioctl	tmp;

#	ifdef ADEBUG
	printf("gr is now %d\n",value);
#	endif

	if ((value < -18) || (value > 12)) {
		printf("GR value %d out of range; %d <= GR <=  %d\n",
		    value,0,12);
		return;
	}

	tmp.control = AUDIO_MAP_GR;
	tmp.data[0] = gr_gx_table[value + 18][1];
	tmp.data[1] = gr_gx_table[value + 18][0];

	if (ioctl(Audio_fd,AUDIOSETREG,&tmp) < 0) {
		perror("Set gr REG");
	}
}



audio_set_gx(value)
int	value;
{
	struct	audio_ioctl	tmp;

	if ((value < -18) || (value > 12)) {
		printf("GX value %d out of range; %d <= GX <=  %d\n",
		    value,0,12);
		return;
	}

	/*  We add 18 to get the index into the table, since entry 0 represents
	 *  -18db.
	 */
	tmp.control = AUDIO_MAP_GX;
	tmp.data[0] = gr_gx_table[value + 18][1];
	tmp.data[1] = gr_gx_table[value + 18][0];

	if (ioctl(Audio_fd,AUDIOSETREG,&tmp) < 0) {
		perror("Set gx REG");
	}
}

/*
 * The below is code for audio play
 * It should be another file, but is added here
 * for simplicity and to avoid externs
 */
/* Mechanism:
 *	Audio play buffer is very small (16k bytes) and
 * sound is time dependent.
 * 	If clients send in big buffers too fast,
 * the audio play queue will overflow. Too slow, the sound
 * will "stutter". Too many small packets will flood
 * the network with requests and possibly overflow the
 * audio queue. Request for the number of bytes
 * available on the queue before sending the request takes
 * too many packets, and will slow down both the server
 * and client considerably. (Audio has high priority over
 * other processes).
 *	The solution is fork a child to unload the data
 * at a steady pace. The server will read big buffers
 * and put all the data in a temp file,
 * and process other requests while the child
 * read from the temp file. When there is no more data in
 * the temp file. The child will signal the parent and exit.
 * The parent will signal the child, if the client intends
 * to stop sending buffers or playing.
 */


	/* buffer up 10 seconds worth of data for network delays */
#define FILE_SIZE        81920
typedef struct _buffoon
{
	unsigned char   rloop, wloop;
	unsigned int    readpt, writept;
	char    buf[FILE_SIZE];
} Mem_map;
#define FILENAME	"/tmp/xxaudiotmp"
#define MAPFILE		"/tmp/xxaudiomap"
Mem_map  * openfile();

static	int	pid;
static	int	please_keep_playing = 0;
static	int	stop_now = 0;
static	Mem_map  *cache;

/*
 * tell the parent that child has exited -
 * audio is done playing .
 */
int
done()
{
	audio_playing = 0;	/* for the parent */
		/* clean_up cache -    in case of illegal exits */
	if (cache)
		munmap((caddr_t) cache, (int)sizeof(Mem_map));
	cache = NULL ;
#ifdef DEBUG
	fprintf(stderr,"shut up %d. audio_playing->0\n" ,pid);
#endif DEBUG
}

/*
 * tell the child that the client intend to stop 
 * sending data for playing.
 */
int
child_done()
{
	please_keep_playing = 0; /* for the child */
#ifdef DEBUG
	fprintf(stderr,"silence child\n");
#endif DEBUG
}

/*
 * shut up child immediately
 */
int
stop_dead()
{
	please_keep_playing = 0; /* for the child */
	stop_now = 1;
#ifdef DEBUG
	fprintf(stderr,"shut up child\n");
#endif DEBUG
}
/*
 *	Forks off a new process on the server to handle
 * audio IO.
 */
start_playing()
{
#ifdef DEBUG
	fprintf(stderr,"please_keep_playing=%d.\n" ,please_keep_playing);
#endif DEBUG
	pid = fork();
	if (pid == 0)	/* child */
	{
		please_keep_playing = 1;
		stop_now = 0;
		signal(SIGUSR1, child_done);/* wait for signal for child to stop */
		signal(SIGUSR2, stop_dead);/* wait for signal for child to stop immediately */
		if (keep_playing() == -1) /* error */
			sleep(1); /* give time for parent to set signal */
		kill (getppid(), SIGUSR1); /* tell parent to stop*/
		exit(0);
	}
	else		/* parent */
		signal(SIGUSR1, done); /* wait for signal from child */
}

/*
 * send data over to temp buffer for the child process
 * to read in leisure
 */
int
send_to_play(buffer, nbytes)
char	*buffer;
{
	int	status = -1 ;
#ifdef ADEBUG
	fprintf(stderr,"\tdump %d bytes in %s\n",nbytes, MAPFILE);
#endif DEBUG
	if (!cache)
		cache = (Mem_map  *) openfile(1);
	if (cache <= 0 )
		fprintf(stderr,"Audio: error\n");
	else
		status = send_to_file( cache, buffer, nbytes);
	return(status);
}

/*
 * Tell child that no more packets are coming
 * and it's time to stop.
 */
stop_playing(forced)
{
#ifdef DEBUG
	fprintf(stderr," parent?pid = %d,stop playing - close(fd)\n",pid);
	fprintf(stderr," forced =%d\n",forced);
#endif DEBUG
	if ( audio_playing ) /* if playing */
	{
		if (forced)	/* drop everything and halt */
		{
			kill(pid, SIGUSR2);
			audio_playing = 0;
		}
		else		/* finish all the sent packets */
			kill(pid, SIGUSR1);
	}
}


/*
 * This is a tight loop that read and sends packets
 * over to the sound chip. The usleep()s are there so
 * that the packets are a good size and not too
 * many reads need to be done.
 */
keep_playing()
{
int	num_bytes = 1, bytes, space ;
char	buffer[20000];
int	queue_size;
int	empty = 0;	

	if ( !cache)
		cache = (Mem_map  *) openfile(0);
		
	if ( !cache) {
		fprintf(stderr,"cannot open read file %s.\n", MAPFILE);
		return(-1); }

	/* get maximum available space on the queue */
	if (ioctl(Audio_fd,AUDIOGETQSIZE,&queue_size) < 0)
		perror("AUDIOGETQSIZE ioctl failed");
#ifdef ADEBUG
	fprintf(stderr,"--playing: Qsize=%d\n",queue_size);
#endif DEBUG
/*more data to read - 4 consecutive empty packets = done */
	while ( empty < 4 )
	{
		if (num_bytes <= 0 )
		{
			empty++;
			usleep(500000);	/* wait for data */
		}
		else
		{
			if (!please_keep_playing)
				empty = 3; /* one more empty = done */
			else
				empty = 0; /* stop the count again */
		}
		if (stop_now)
		{
#ifdef DEBUG
			fprintf(stderr,"stop now\n");
#endif DEBUG
			break;
		}
#ifdef DEBUG
		fprintf(stderr,"--playing: =%d.--",num_bytes);
#endif DEBUG
		/* get number of stored  bytes on the queue */
		if (ioctl(Audio_fd,AUDIOWRITEQ,&bytes) < 0)
		{
			perror("AUDIOWRITEQ ioctl failed");
			break;
		}	
		/* get available space on the queue */
		space = (queue_size - bytes);
#ifdef DEBUG
		fprintf(stderr,"--queue: space = %d, Q =%d, bytes=%d\n", space , queue_size, bytes);
#endif DEBUG
		if (!space)
			/*can't process- slow down*/
			usleep(500000);
		else
		{
			num_bytes = getdata( cache, buffer, space);
#ifdef DEBUG
			fprintf(stderr,"--read %d bytes.",num_bytes);
#endif DEBUG
			if (num_bytes <= 0)
				continue;
			/* send to audio device */
			if (write(Audio_fd,buffer,num_bytes) < 0)
			{
				perror("/dev/audio");
				break;
			}
		}
		usleep(500000); /* try and get big packages */
	}
	munmap((caddr_t) cache, sizeof(cache)); /* close on child's end */
	cache = 0;
#ifdef DEBUG
	fprintf(stderr,"exit from playing.\n");
#endif DEBUG
	return(1);		/* exit clean */
}

/* mmap code to replace read and write file code to access tmp data file*/
Mem_map  *
openfile(new)
{
caddr_t mem;
int	fd;
	(void)umask(0);
	fd = open( MAPFILE, O_RDWR| O_CREAT, 0666 );
        if ( fd == -1)
        {
                (void)fprintf(stderr, "Audio: bad file - errno = %d.\n", errno);
                return((Mem_map *)-1);
        }
		/* To be sure  */
	lseek ( fd,(int)sizeof(Mem_map), L_SET);
	(void)write( fd, "", 1 );
        mem = mmap((caddr_t)0, sizeof(Mem_map), PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t)0);        if (mem == (caddr_t ) -1)
        {
                (void)fprintf(stderr, "Audio: cannot map memory errno = %d.\n",errno);
                return((Mem_map *)-1);
        }
        if (new)
                bzero( (char *)mem, sizeof(Mem_map) );
        (void)close(fd);
        return ( (Mem_map *) mem );
}


/*
	read from tmp file
        return # of bytes read
*/
int
getdata( cache, buf, buf_size)
Mem_map  *cache;
char    *buf;
int     buf_size;
{
int     size;
                if  (cache->wloop ^ cache->rloop )
                { /* reads to the end */
                        size = FILE_SIZE - cache->readpt;
                        if (!size)
                                return(0);
                        if (size > buf_size) size = buf_size;
                        get_from_file( cache, buf, size, cache->readpt  );
                        cache->readpt += size;
                }
                else /* reads until it gets to writept*/
                {
                        size = cache->writept - cache->readpt;
                        if (!size)
                                return(0);
                        if (size > buf_size) size = buf_size;
                        get_from_file( cache, buf, size, cache->readpt  );
                        cache->readpt += size;
                }
                buf[size]=0;
                if (cache->readpt == FILE_SIZE)
                {       /* loop around */
                        cache->rloop = !cache->rloop;
                        cache->readpt = 0 ;
                }
#ifdef DEBUG
        (void)fprintf(stderr,"getdata:\trloop=%d; wloop=%d;\t readpt=%d; writept=%d\n",cache->rloop, cache->wloop, cache->readpt, cache->writept);
#endif DEBUG
        return(size);
}

/*
	send buffer to tmp file
	return 0 if ok, -1 if total kaput, # of bytes left unqueued if overflow
*/
int
send_to_file(cache, buffer, nbytes)
Mem_map  *cache;
char    *buffer;
int     nbytes;
{
int     last, end, leftover, status = 0;
        if  (cache->wloop ^ cache->rloop )
        {       /* waits for read if loop over */
                if ( cache->readpt > cache->writept )
                {
                        end = cache->readpt - cache->writept;
                        if ( end < nbytes ) /* throw out the rest */
                        {
#ifdef DEBUG
                                (void)fprintf(stderr," Audio: sound buffer overflow!\n");
#endif DEBUG
                                status = nbytes - end;
                                nbytes = end;
                        }
                        put_file( cache, buffer, nbytes, cache->writept  );
                        cache->writept += nbytes;
                }
                else
                {
#ifdef DEBUG
                        (void)fprintf(stderr,"Audio: sound buffer flooded!\n");
#endif DEBUG
                        status = nbytes;
                }
        }
        else             
        {       /* write till the end */
                if (( cache->writept + nbytes) <= FILE_SIZE )
                {
                        put_file( cache, buffer, nbytes, cache->writept  );
                        cache->writept += nbytes ;
                }
                else
                {       /* wraparound */
                        end = FILE_SIZE - cache->writept;
                        put_file( cache, buffer, end, cache->writept  );
                        cache->wloop = !cache->wloop ;
                        cache->writept = 0 ;
                        leftover = nbytes -  end ;
			if (leftover <= cache->readpt)
				last = leftover;
			else {
				 last = cache->readpt;
				 status = leftover - cache->readpt ;
				 }
			put_file( cache, buffer+end, last, cache->writept  );
                        cache->writept += last ;
                }
        }
        if (cache->writept == FILE_SIZE)
        {        
                cache->wloop = !cache->wloop;
                cache->writept = 0; /* start from 0 again */
        }
#ifdef DEBUG
        (void)fprintf(stderr,"send_to_file\trloop=%d; wloop=%d;\t readpt=%d; writept=%d\n",cache->rloop, cache->wloop, cache->readpt, cache->writept);
#endif DEBUG
        return(status);
}

get_from_file( cache, buf, size, at  )
Mem_map *cache;
char    *buf;
unsigned int at;
{
	bcopy( &(cache->buf[at]), buf, size );
}

put_file( cache, buf, size, at  )
Mem_map *cache;
char    *buf;
unsigned int at;
{
	bcopy( buf,  &(cache->buf[at]), size );
}

clean_up()
{
	if (cache)
		munmap(cache);
	cache = 0;
	unlink(MAPFILE);	/* don't need it anymore */
}

int
find_client(c_num)
{
	if ( clients[c_num] > 0)
		return(c_num);
	return(-1);
}

enter_client(c_num)
{
	if (find_client(c_num) < 0)
	{
		clients[c_num ]= 1;
		num_clients++;
	}
	clients[c_num ]++;
}

remove_client(c_num)
{
	if (clients[c_num] > 0)
		clients[c_num ]--;
	num_clients--;
}
