/*

	NetFone: Network sound play server

	Designed and implemented in July of 1991 by John Walker.

*/

#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <signal.h>
#include "ulaw2linear.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

struct soundbuf {
	int compression;
	char sendinghost[16];
	struct {
		int buffer_len;
		char buffer_val[8000];
	} buffer;
};
typedef struct soundbuf soundbuf;

#define TRUE	1
#define FALSE	0

static int audiok = FALSE;	      /* Audio initialised flag */
static int audiotime = 0;	      /* Audio timeout counter */
static int debugging = FALSE;	      /* Debugging enabled */
static char echost[24] = "";          /* Last echo host */

#define TickTock    10		      /* Alarm interval in seconds */

/*  RELEASE  --  Signal-catching function which releases the audio
                 device if we haven't received anything to play in
		 the last minute. */

static void release()
{
    if (debugging) {
        fprintf(stderr, "Tick....\n");
    }
    if (++audiotime >= 2) {
	soundterm();
	audiok = FALSE;
	if (debugging) {
            fprintf(stderr, "Speaker releasing audio device.\n");
	}
    } else {
	alarm(TickTock);	      /* Wind the cat */
    }
}

/*  PLAYBUFFER	--  Send next buffer to audio output. */

static void playbuffer(msg)
  soundbuf *msg;
{
    char *val;
    int len;
    char auxbuf[8002];

    debugging = (msg->compression & 2) ? TRUE : FALSE;

    if (!audiok) {
	if (!soundinit(O_WRONLY)) {
            perror("opening audio output device");
            return;                   /* Can't acquire sound device */
	}
	audiok = TRUE;
	if (debugging) {
            fprintf(stderr, "Speaker opening audio device.\n");
	}
	signal(SIGALRM, release);     /* Set signal to handle timeout */
	alarm(TickTock);	      /* Set alarm clock to free audio device */
	audiotime = 0;		      /* Reset timeout counter */
    }

    if (debugging) {
        printf("Playing %d%s bytes from %s.\n", msg->buffer.buffer_len,
                (msg->compression & 1) ? " compressed" : "", msg->sendinghost);
    }
    len = msg->buffer.buffer_len;
    val = msg->buffer.buffer_val;

    /* If the 4 bit is on, use	the 8 bit to re-route the sound.  This
       is normally used to divert the sound to the speaker to  get  an
       individual's attention.  */

    if (msg->compression & 4) {
	sounddest((msg->compression & 8) ? 1 : 0);
	if (!(msg->compression & 8)) {
	    soundplayvol(50);	      /* Make sure volume high enough */
	}
    }

    /* If the buffer contains compressed sound samples, reconstruct an
       approximation of the original  sound  by  performing  a	linear
       interpolation between each pair of adjacent compressed samples.
       Note that since the samples are logarithmically scaled, we must
       transform  them	to  linear  before interpolating, then back to
       log before storing the calculated sample. */

    if (msg->compression & 1) {
	int i;
	register char *ab = auxbuf;

	assert(len < sizeof auxbuf);
	for (i = 0; i < len; i++) {
	    *ab++ = i == 0 ? *val :
		(audio_s2u((audio_u2s(*val) + audio_u2s(val[-1])) / 2));
	    *ab++ = *val++;
	}
	len *= 2;
	val = auxbuf;
    }

    audiotime = 0;		      /* Reset timeout counter */
    soundplay(len, val);
}

/*  Main program  */

int main()
{
    int sock;
    struct sockaddr_in name;
    struct soundbuf sb;

    /* Create the socket from which to read */

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("opening datagram socket");
	return 1;
    }

    /* Create name with wildcards. */

    name.sin_family = AF_INET;
    name.sin_addr.s_addr = INADDR_ANY;
    name.sin_port = Internet_Port;
    if (bind(sock, (struct sockaddr *) &name, sizeof name) < 0) {
        perror("binding datagram socket");
	return 1;
    }

    /* Read from the socket. */

    while (TRUE) {
	if (read(sock, &sb, sizeof sb) < 0) {
            perror("receiving datagram packet");
	}

	/* If  the packet requests loop-back,  immediately dispatch it
	   back to the host who sent it to us.	To prevent an infinite
	   loop-back  cycle,  we clear the loop-back bit in the header
	   before sending the message.	We leave the  host  of	origin
	   unchanged,  allowing  the  sender to identify the packet as
	   one he originated. */

	if (sb.compression & 16) {

            /* If the host we're bouncing back the packet to isn't the
	       same  as  the  last one we bounced to, look up its host
	       identity and save it as the last host. */

	    if (strcmp(sb.sendinghost, echost) != 0) {
		struct hostent *hp, *gethostbyname();

		hp = gethostbyname(sb.sendinghost);
		if (hp != 0) {
		    bcopy((char *) hp->h_addr, (char *) &(name.sin_addr),
			  hp->h_length);
		    name.sin_family = AF_INET;
		    name.sin_port = htons(Internet_Port);
		    strcpy(echost, sb.sendinghost);
		} else {
		    echost[0] = 0;
		}
	    }
	    sb.compression &= ~16;    /* Prevent infinite loopback */
	    if (sendto(sock, &sb,
		(sizeof(struct soundbuf)) - (8000 - sb.buffer.buffer_len),
		0, (struct sockaddr *) &(name), sizeof name) < 0) {
                perror("sending datagram message");
	   }
	}

	playbuffer(&sb);
    }
}
