ftp.nice.ch/pub/next/unix/network/conferences/radio.1p3.s.tar.gz#/radio/radio.c

This is radio.c in view mode; [Download] [Up]

/***********************************************************
Copyright 1991 by Stichting Mathematisch Centrum, Amsterdam, The
Netherlands.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.

STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

******************************************************************/

/* Receive audio UDP packets transmitted by broadcast.

   Command line options:

   -p port	tune to this port number (default 54321)
                (port numbers 1..99 are shorthands for 54321 and up)
   -v volume	output volume (range 0-100; default unchanged)
   -c port	use this control port (default 54320)
   -s		'secure' mode: don't listen to the control port
   -f		work as a filter: send output to stdout instead of
		directly to the audio hardware
   -l addr	listen only for packets to <arg> ip address
   -r addr	listen only for packets from <arg>
   -d		debug packets
   -n           noninterruptable -- by default radio will be interruptable
		by other sound outputting programs, hoping they do not
		take too long.  This option turns off that feature.
*/

#define BCASTCTLPORT      54319
#define RADIOCTLPORT      54320
#define RCVPORT           54321
#define SENDPORT          54318

#define BUFFERSIZE        1400
#define CTLPKTSIZE        100

#ifdef sgi
#define USE_AL
#endif
#ifdef sun
#define USE_SUN
#endif
#ifdef NeXT
#define USE_NX
#endif

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>

#ifdef USE_AL
#include <audio.h>
#include "libst.h"

long savestate[] = {
	AL_OUTPUT_RATE, 0,
	AL_LEFT_SPEAKER_GAIN, 0,
	AL_RIGHT_SPEAKER_GAIN, 0,
};
#endif

#ifdef USE_SUN
#include <stropts.h>
#include <sun/audioio.h>

#define AUDIO_IODEV     "/dev/audio"
#define AUDIO_CTLDEV    "/dev/audioctl"

int interruptable = 1;
int actlfd = -1;
#endif
  
#ifdef USE_NX
#include <sound/sound.h>
#define NUM_BUFFER 10
#endif

/* getopt() interface */
extern int optind;
extern char * optarg;

/* Forward */
void sigpoll_handler();
void cleanup_handler();

/* Globals */
int ofd; /* Output file descriptor */

main(argc, argv)
	int argc;
	char **argv;
{
	int receiveport = RCVPORT;
	int ctlport = RADIOCTLPORT;
	char buf[BUFFERSIZE];
	int s, ctls, curs;
	struct sockaddr from;
	int fromlen;
	int c;
	int filter = 0;
	int nfds;
	fd_set inputset;
	int n;
	int pdebug = 0;
	char *localname = (char *) NULL;
	char *remotename = (char *) NULL;
	int volume = -1;
#ifdef USE_SUN
	audio_info_t info;
#endif
#ifdef USE_AL
	short obuf[BUFFERSIZE];
	ALport aport;
	ALconfig config;
	int i;
	long pvbuf[6];
#endif
#ifdef USE_NX
	int err;
	SNDSoundStruct *snd[NUM_BUFFER];
	int akt_buf;
#endif

/* Always change these two macros together! */
#define OPTIONS "c:dfl:np:r:sv:"
#define USAGE	"usage: %s [-d] [-f] [-n] [-s] [-p port] [-v volume(0-100)] [-c ctlport] [-l localhost] [-r remotehost]\n"

	while ((c = getopt(argc, argv, OPTIONS)) != EOF) {
		switch (c) {
		case '?':
			fprintf(stderr, USAGE, argv[0]);
			exit(2);
		case 'p':
			receiveport = atoi(optarg);
			if (0 < receiveport && receiveport < 100)
				receiveport += RCVPORT-1;
			break;
		case 'c':
			ctlport = atoi(optarg);
			break;
		case 'l':
			localname = optarg;
			break;
		case 'r':
			remotename = optarg;
			break;
		case 'd':
			pdebug = 1;
			break;
#ifdef USE_SUN
		case 'n':
			interruptable = 0;
			break;
#endif
		case 's':
			ctlport = -1;
			break;
		case 'f':
			filter = 1;
			break;
		case 'v':
			volume = atoi(optarg);
			break;
		}
	}

	if (filter) {
		ofd = fileno(stdout);
	}
	else {
#ifdef USE_AL
		/* Fetch the original state */
		ALgetparams(AL_DEFAULT_DEVICE, savestate,
			    sizeof(savestate) / sizeof(long));

		/* Set signal handlers */
		signal(SIGINT, cleanup_handler);
		signal(SIGTERM, cleanup_handler);

		/* Set the device to 8000 Hz */
		pvbuf[0] = AL_OUTPUT_RATE;
		pvbuf[1] = AL_RATE_8000;

		/* Maybe also set the volume */
		if (volume >= 0) {
			pvbuf[2] = AL_LEFT_SPEAKER_GAIN;
			pvbuf[3] = volume*255/100;
			pvbuf[4] = AL_RIGHT_SPEAKER_GAIN;
			pvbuf[5] = volume*255/100;
			ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 6L);
		}
		else
			ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 2L);

		/* Configure and open an SGI audio port */
		config = ALnewconfig();
		ALsetchannels(config, AL_MONO);
		ALsetwidth(config, AL_SAMPLE_16);
		ALsetqueuesize(config, 16000); /* 2 seconds slop */
		aport = ALopenport("radio", "w", config);
		if (aport == NULL) {
			perror("ALopenport");
			exit(1);
		}
#endif
#ifdef USE_SUN
		/* Write to AUDIO_IODEV */
		if ((ofd = open(AUDIO_IODEV, O_WRONLY)) < 0) {
			perror(AUDIO_IODEV);
			exit(1);
		}

		/* Set the volume */
		if (volume >= 0) {
			AUDIO_INITINFO(&info);
			info.play.gain = (AUDIO_MAX_GAIN * volume) / 100;
			if (ioctl(ofd, AUDIO_SETINFO, &info))
				perror("volume setting");
		}

		/* We need to open the audio control port to detect
		   if someone else wants to output to /dev/audio.
		   If this fails (e.g., in SunOS 4.0), print a message
		   but don't exit. */
		if (interruptable) {
			if ((actlfd = open(AUDIO_CTLDEV, O_RDWR)) < 0) {
				perror(AUDIO_CTLDEV);
			}
			else if (ioctl(actlfd, I_SETSIG, S_MSG) < 0) {
				perror("I_SETSIG");
			}
			else if (signal(SIGPOLL, sigpoll_handler) < 0) {
				perror("signal(SIGPOLL)");
				exit(1);
			}
		}
#endif
#ifdef USE_NX
		/* Alloc NUM_BUFFER Sounds */
		for( akt_buf = NUM_BUFFER; akt_buf > 0; akt_buf--) {
			if (err = SNDAlloc(&snd[akt_buf-1], BUFFERSIZE,
					   SND_FORMAT_MULAW_8,
					   SND_RATE_CODEC, 1, 4)) {
				fprintf(stderr,
					"init: %s\n", SNDSoundError(err));
			}
		}
		akt_buf = 0;
#endif
	}

	if (ctlport >= 0)
		ctls = opensock("control", (char *)NULL, ctlport,
				(char *)NULL, 0, 0);
	else
		ctls = -1;

	s = opensock("data", localname, receiveport, remotename, SENDPORT, 0);

	for (;;) {
		/*
		** Wait until one of the sockets becomes ready
		*/
		nfds = (s > ctls ? s : ctls) + 1;
		FD_ZERO(&inputset);
		FD_SET(s, &inputset);
		if (ctls >= 0)
			FD_SET(ctls, &inputset);
		while (select(nfds, &inputset, 0, 0, 0) < 1) {
			if(errno != EINTR) {
				perror("select");
				exit(1);
			}
		}
		if (ctls >= 0 && FD_ISSET(ctls, &inputset))
			curs = ctls;
		else if (FD_ISSET(s, &inputset))
			curs = s;
		/*
		** Read, and check for control packet
		*/
		fromlen = sizeof(from);
		n = recvfrom(curs, buf, BUFFERSIZE, 0, &from, &fromlen);
		if (n <= 0) {
			if (n == 0)
				continue; /* Ignore empty packets */
			perror("read");
			break;
		}
		if (pdebug) {
			if(pdebug == 8) {
				fprintf(stderr, "8 packets received\n");
				pdebug = 1;
			}
			else
				pdebug++;
		}
		if ( n <= CTLPKTSIZE ) {
			/*
			** It looks like a control packet. Check it.
			*/
			if (strncmp(buf, "radio:", 6) == 0) {
				if (pdebug)
					fprintf(stderr, "control packet\n");
				switch(buf[6]) {
				case 'e':		/* Echo */
					buf[6] = 'E';
					sendto(curs, buf, n, 0,
					       &from, fromlen);
					break;
				case 't':		/* Tune */
					if (curs != ctls) {
						if (pdebug)
						  fprintf(stderr,
						    "radio: illegal tune\n");
						break;
					}
#ifdef USE_SUN
					if (!filter) {
						(void) ioctl(ofd, I_FLUSH,
							     FLUSHW);
					}
#endif
					buf[n] = '\0';
					receiveport = atoi(buf+8);
					close(s);
					s = opensock("new data", localname,
						     receiveport, remotename,
						     SENDPORT, 0);
					break;
				case 'i':		/* Info */
					sprintf(buf, "radio:I:%d",
						receiveport);
					sendto(curs, buf, strlen(buf), 0,
					       &from, fromlen);
					break;
				default:
					if (pdebug)
						fprintf(stderr,
						  "radio: illegal cmd 0x%x\n",
						  buf[6]);
				}
			}
			else if (pdebug) {
				fprintf(stderr,
					"radio: ill-formatted command\n");
			}
		}
		else {
#ifdef USE_AL
			if (!filter) {
				for (i = 0; i < n; i++)
					obuf[i] = st_ulaw_to_linear(buf[i]);
				ALwritesamps(aport, obuf, (long)n);
			}
			else
#endif
#ifdef USE_NX
			if (!filter) {
			  int dummy;
			  char *ptr;

			  (void) SNDGetDataPointer(snd[akt_buf], &ptr, &dummy, &dummy);

			  SNDWait( akt_buf+1);
			  memcpy( ptr, buf, n);
			  snd[akt_buf] -> dataSize = n;
			  SNDStartPlaying(snd[akt_buf],akt_buf+1,5,0,0,0 );
			  akt_buf = ( akt_buf + 1) % NUM_BUFFER;
			}
			else
#endif
			if (write(ofd, buf, n) != n) {
				perror("write");
				break;
			}
		}
	}

	exit(0);
}

#ifdef USE_SUN

void sigpoll_handler()
{
	audio_info_t	ap;

	if (ioctl(actlfd, AUDIO_GETINFO, &ap) < 0) {
		perror("AUDIO_GETINFO");
	}
	if (ap.play.waiting) {
		close(ofd);
		if ((ofd = open(AUDIO_IODEV, O_WRONLY)) < 0) {
			perror(AUDIO_IODEV);
			exit(1);
		}
		ap.play.waiting = 0;
		if (ioctl(actlfd, AUDIO_SETINFO, &ap) < 0) {
			perror("AUDIO_SETINFO");
		}
	}
}

#endif /*USE_SUN*/

#ifdef USE_AL

void cleanup_handler(sig)
	int sig;
{
	signal(sig, SIG_DFL);
	ALsetparams(AL_DEFAULT_DEVICE, savestate,
		    sizeof(savestate) / sizeof(long));
	kill(getpid(), sig);
}

#endif /* USE_AL */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.