/* * 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 #include #include #include #include #include #include #include #include #include #include "audio.h" #include 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> 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--; }