home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-10-25 | 36.9 KB | 1,356 lines |
- Path: uunet!seismo!dimacs.rutgers.edu!aramis.rutgers.edu!athos.rutgers.edu!mcgrew
- From: mcgrew@athos.rutgers.edu (Charles Mcgrew)
- Newsgroups: comp.sources.sun
- Subject: v03i008: netfone - speak over the network via sparcstations
- Message-ID: <Oct.25.11.58.35.1991.27806@athos.rutgers.edu>
- Date: 25 Oct 91 15:58:37 GMT
- Organization: Rutgers Univ., New Brunswick, N.J.
- Lines: 1345
- Approved: mcgrew@aramis.rutgers.edu
-
- Submitted-by: John Walker <autodesk!throop!kelvin@uunet.uu.net>
- Posting-number: Volume 3, Issue 8
- Archive-name: netfone
-
-
- Chatting away over the network, courtesy of SPARCStations and your Ethernet
- or handy-dandy "via satellite" leased line....
-
- NetFone
- Voice Communication Over Data Networks
-
- by John Walker
- Revision 2 -- 12th September 1991
-
- The phone company never gives you a break! You finally spring for a
- leased line so you can be right on the network with everybody else,
- and you *still* have to pay every time you want to chat with somebody,
- even though you're both sitting in front of SPARCStations with digital
- audio capability, linked with a high-bandwidth network.
-
- NetFone uses the workstation audio chip and network to allow
- bidirectional conversations right over the network. Workstations
- linked by Ethernet can generally communicate with excellent audio
- quality and response. Those linked by 56 Kb lines can also talk over
- the network, but with lower fidelity compressed audio that fits within
- the capacity of the data link.
-
- Complete instructions for building, installing, and using NetFone are
- given in the manual page, netfone.1. You can view the manual page on
- your screen with the command:
-
- make manpage
-
- Building NetFone requires the audio library, "libaudio.a", supplied by
- Sun in SunOS 4.1.1 in /usr/demo/SOUND and the include files found in
- /usr/demo/SOUND/multimedia. These files will be present on your
- workstation only if the "Demos" installation option was selected. The
- multimedia support files from Sun appear to be in a rapid state of
- flux and you may need to change the directory names from which these
- files are accessed in the Makefile and/or adapt the source programs to
- accommodate changes made by Sun in subsequent versions of SunOS.
-
- This program is in the public domain: "Do what thou wilt shall be the
- whole of the law". I'd appreciate receiving any bug fixes and/or
- enhancements, which I'll incorporate in future versions of the
- program. Please leave the original attribution information intact so
- that credit and blame may be properly apportioned.
-
- AUTHOR
- John Walker
- Autodesk SA
- Avenue des Champs-Montants 14b
- CH-2074 MARIN
- Suisse/Schweiz/Svizzera/Svizra/Switzerland
- Usenet: kelvin@Autodesk.com
- Fax: 038/33 88 15
- Voice: 038/33 76 33
-
- ----------------------------- Cut here ----------------------- netfone.shar
- #! /bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #! /bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create the files:
- # README
- # Makefile
- # mike.c
- # netfone.1
- # soundbyte.c
- # speaker.c
- # This archive created: Sat Sep 14 00:59:56 1991
- export PATH; PATH=/bin:$PATH
- echo shar: extracting "'README'" '(2296 characters)'
- if test -f 'README'
- then
- echo shar: will not over-write existing file "'README'"
- else
- cat << \SHAR_EOF > 'README'
-
- NetFone
- Voice Communication Over Data Networks
-
- by John Walker
- Revision 2 -- 12th September 1991
-
- The phone company never gives you a break! You finally spring for a
- leased line so you can be right on the network with everybody else,
- and you *still* have to pay every time you want to chat with somebody,
- even though you're both sitting in front of SPARCStations with digital
- audio capability, linked with a high-bandwidth network.
-
- NetFone uses the workstation audio chip and network to allow
- bidirectional conversations right over the network. Workstations
- linked by Ethernet can generally communicate with excellent audio
- quality and response. Those linked by 56 Kb lines can also talk over
- the network, but with lower fidelity compressed audio that fits within
- the capacity of the data link.
-
- Complete instructions for building, installing, and using NetFone are
- given in the manual page, netfone.1. You can view the manual page on
- your screen with the command:
-
- make manpage
-
- Building NetFone requires the audio library, "libaudio.a", supplied by
- Sun in SunOS 4.1.1 in /usr/demo/SOUND and the include files found in
- /usr/demo/SOUND/multimedia. These files will be present on your
- workstation only if the "Demos" installation option was selected. The
- multimedia support files from Sun appear to be in a rapid state of
- flux and you may need to change the directory names from which these
- files are accessed in the Makefile and/or adapt the source programs to
- accommodate changes made by Sun in subsequent versions of SunOS.
-
- This program is in the public domain: "Do what thou wilt shall be the
- whole of the law". I'd appreciate receiving any bug fixes and/or
- enhancements, which I'll incorporate in future versions of the
- program. Please leave the original attribution information intact so
- that credit and blame may be properly apportioned.
-
- AUTHOR
- John Walker
- Autodesk SA
- Avenue des Champs-Montants 14b
- CH-2074 MARIN
- Suisse/Schweiz/Svizzera/Svizra/Switzerland
- Usenet: kelvin@Autodesk.com
- Fax: 038/33 88 15
- Voice: 038/33 76 33
- SHAR_EOF
- fi # end of overwriting check
- echo shar: extracting "'Makefile'" '(1757 characters)'
- if test -f 'Makefile'
- then
- echo shar: will not over-write existing file "'Makefile'"
- else
- cat << \SHAR_EOF > 'Makefile'
-
- # NetFone: Make file
-
- # Directory where the Sun sound utilities, including libaudio.a
- # are installed.
- SOUND = /usr/demo/SOUND
-
- # Directory where the #include files for the Sun sound utilities
- # are installed.
- MULTIMEDIA = $(SOUND)/multimedia
-
- # Internet socket port used by mike and speaker. If you change this,
- # you will not be able to exchange sound with users who've built
- # NetFone with different values.
- INTERNET_PORT = 2074
-
- # Program name (shar will be $(PROG).shar)
- PROG = netfone
-
- # Source files distributed in the .shar archive.
- SFILES = README Makefile mike.c netfone.1 soundbyte.c speaker.c
-
- # Special modes, if any, for C compilations.
- MODES = -O
-
- CARGS = -I$(MULTIMEDIA) -DInternet_Port=$(INTERNET_PORT)
-
- CFLAGS = $(MODES) $(CARGS)
-
- all: speaker mike
-
- SPKROBJS = speaker.o soundbyte.o
-
- speaker: $(SPKROBJS)
- cc $(SPKROBJS) $(SOUND)/libaudio.a -lm -o speaker
-
- MIKEOBJS = mike.o soundbyte.o
-
- mike: $(MIKEOBJS)
- cc $(MIKEOBJS) -lm -o mike
-
- speaker.o: speaker.c
-
- mike.o: mike.c
-
- manpage:
- nroff -man netfone.1 | more
-
- printman:
- ptroff netfone.1 -man
-
- shar:
- shar -b -v $(SFILES) >$(PROG).shar
-
- # Note: the following LINT line feeds both main programs and the the
- # subroutine file they share to LINT so that it can detect
- # unused functions in the library, soundbyte.c This results in
- # a harmless warning about the duplicate definition of main().
- # NetFone is *not* lint-free. I cannot bring myself to
- # butcher the source to the extent that would be required.
- lint:
- lint $(CARGS) mike.c speaker.c soundbyte.c
-
- spell:
- spell -b netfone.1 | fmt
- spell -b README | fmt
-
- clean:
- rm -f core *.out *.o *.bak *.shar speaker mike
-
- backup:
- mt -f $(TAPE) rew
- tar cfbv $(TAPE) 126 *
- mt -f $(TAPE) rew
- SHAR_EOF
- fi # end of overwriting check
- echo shar: extracting "'mike.c'" '(9498 characters)'
- if test -f 'mike.c'
- then
- echo shar: will not over-write existing file "'mike.c'"
- else
- cat << \SHAR_EOF > 'mike.c'
- /*
-
- NetFone: Network sound transmission program
-
- Designed and implemented in July of 1991 by John Walker
-
- */
-
- #include <stdio.h>
- #include <fcntl.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 sock; /* Communication socket */
-
- #define V (void)
-
- /* Destination host descriptor. */
-
- struct destination {
- struct destination *dnext;
- char *server;
- struct sockaddr_in name;
- };
-
- static struct destination *dests = NULL, *dtail;
- static int compressing = FALSE; /* Compress sound buffers */
- static int squelch = 0; /* Squelch level if > 0 */
- static int ring = FALSE; /* Force speaker & level on next pkt ? */
- static int agc = TRUE; /* Automatic gain control active ? */
- static int rgain = 33; /* Current recording gain level */
- static int debugging = FALSE; /* Debugging enabled here and there ? */
- static char hostname[20]; /* Host name to send with packets */
- static int loopback = FALSE; /* Remote loopback mode */
-
- /* ADDEST -- Add destination host to host list. */
-
- static int addest(host)
- char *host;
- {
- struct destination *d;
- struct hostent *hp, *gethostbyname();
-
- hp = gethostbyname(host);
- if (hp == 0) {
- fprintf(stderr, "%s: unknown host\n", host);
- return FALSE;
- }
-
- d = (struct destination *) malloc(sizeof(struct destination));
- d->dnext = NULL;
- d->server = host;
- bcopy((char *) hp->h_addr, (char *) &(d->name.sin_addr), hp->h_length);
- d->name.sin_family = AF_INET;
- d->name.sin_port = htons(Internet_Port);
- if (dests == NULL) {
- dests = d;
- } else {
- dtail->dnext = d;
- }
- dtail = d;
- return TRUE;
- }
-
- /* SENDMSG -- Send a message to all active destinations. */
-
- static int sendmsg(sb)
- struct soundbuf *sb;
- {
- struct destination *d;
-
- for (d = dests; d != NULL; d = d->dnext) {
- if (sendto(sock, sb, (sizeof(struct soundbuf)) - (8000 - sb->buffer.buffer_len),
- 0, (struct sockaddr *) &(d->name), sizeof d->name) < 0) {
- perror("sending datagram message");
- return FALSE;
- }
- }
- return TRUE;
- }
-
- /* SENDFILE -- Send a file or, if the file name is NULL or a
- single period, send real-time sound input. */
-
- static int sendfile(f)
- char *f;
- {
- soundbuf netbuf;
- #define buf netbuf.buffer.buffer_val
- int nread;
- FILE *afile = NULL;
-
- strcpy(netbuf.sendinghost, hostname);
- if (f != NULL && (strcmp(f, ".") != 0)) {
- afile = fopen(f, "r");
- if (afile == NULL) {
- fprintf(stderr, "Unable to open sound file %s.\n", f);
- return 2;
- }
- }
-
- /* Send a file */
-
- if (afile) {
- while ((nread = fread(buf, 1, sizeof buf, afile)) > 0) {
- netbuf.compression = FALSE | (ring ? 4 : 0);
- ring = FALSE;
- netbuf.compression |= debugging ? 2 : 0;
- netbuf.compression |= loopback ? 16 : 0;
- if (compressing) {
- int i;
-
- nread /= 2;
- for (i = 1; i < nread; i++) {
- buf[i] = buf[i * 2];
- }
- netbuf.compression |= 1;
- }
- netbuf.buffer.buffer_len = nread;
- if (!sendmsg(&netbuf)) {
- fclose(afile);
- return 1;
- }
- /* Horrible kludge. Fake flow control by sleeping for about
- long enough for the buffer to play. */
-
- usleep(950 * ((nread * (compressing ? 2 : 1)) / 8L));
- }
-
- if (debugging) {
- fprintf(stderr, "Sent sound file %s.\n", f);
- }
- fclose(afile);
- } else {
-
- /* Send real-time sound. */
-
- if (!soundinit(O_RDONLY /* | O_NDELAY */ )) {
- fprintf(stderr, "Unable to initialise audio.\n");
- return 2;
- }
- if (agc) {
- soundrecgain(rgain); /* Set initial record level */
- }
- if (soundrecord()) {
- while (TRUE) {
- int soundel = soundgrab(buf, sizeof buf);
- unsigned char *bs = (unsigned char *) buf;
-
- if (soundel > 0) {
- register unsigned char *start = bs;
- register int j;
- int squelched = (squelch > 0);
-
- /* If entire buffer is less than squelch, ditch it. */
-
- if (squelch > 0) {
- for (j = 0; j < soundel; j++) {
- if (((*start++ & 0x7F) ^ 0x7F) > squelch) {
- squelched = FALSE;
- break;
- }
- }
- }
-
- if (squelched) {
- if (debugging) {
- printf("Entire buffer squelched.\n");
- }
- } else {
- netbuf.compression = FALSE | (ring ? 4 : 0);
- netbuf.compression |= debugging ? 2 : 0;
- netbuf.compression |= loopback ? 16 : 0;
-
- /* If automatic gain control is enabled,
- ride the gain pot to fill the dynamic range
- optimally. */
-
- if (agc) {
- register unsigned char *start = bs;
- register int j;
- long msamp = 0;
-
- for (j = 0; j < soundel; j++) {
- int tsamp = ((*start++ & 0x7F) ^ 0x7F);
-
- msamp += tsamp;
- }
- msamp /= soundel;
- if (msamp < 0x30) {
- if (rgain < 100) {
- soundrecgain(++rgain);
- }
- } else if (msamp > 0x35) {
- if (rgain > 1) {
- soundrecgain(--rgain);
- }
- }
- }
-
- ring = FALSE;
- if (compressing) {
- int i;
-
- soundel /= 2;
- for (i = 1; i < soundel; i++) {
- buf[i] = buf[i * 2];
- }
- netbuf.compression |= 1;
- }
- netbuf.buffer.buffer_len = soundel;
- if (!sendmsg(&netbuf)) {
- return 1;
- }
- }
- } else {
- usleep(100000L); /* Wait for some sound to arrive */
- }
- }
- } else {
- fprintf(stderr, "Unable to start recording.\n");
- return 2;
- }
- }
- return 0;
- }
-
- /* USAGE -- Print how-to-call information. */
-
- static void usage()
- {
- V fprintf(stderr, "mike -- Sound transmission tool.\n");
- V fprintf(stderr, "\n");
- V fprintf(stderr, "Usage: mike hostname <options> [ file1 / . ]...\n");
- V fprintf(stderr, "Options: (* indicates defaults)\n");
- V fprintf(stderr, " -C Compress subsequent sound\n");
- V fprintf(stderr, " -D Enable debug output\n");
- V fprintf(stderr, " * -G Automatic gain control\n");
- V fprintf(stderr, " -L Remote loopback\n");
- V fprintf(stderr, " -M Manual record gain control\n");
- V fprintf(stderr, " * -N Do not compress subsequent sound\n");
- V fprintf(stderr, " -Phostname Party line, add host to list\n");
- V fprintf(stderr, " * -Q Disable debug output\n");
- V fprintf(stderr, " -R Ring--force volume, output to speaker\n");
- V fprintf(stderr, " -Sn Squelch at level n (0-255)\n");
- V fprintf(stderr, " -U Print this message\n");
- V fprintf(stderr, "\n");
- V fprintf(stderr, "par John Walker\n");
- V fprintf(stderr, " Autodesk SA Neuch\342tel\n");
- V fprintf(stderr, " Avenue des Champs-Montants 14b\n");
- V fprintf(stderr, " CH-2074 MARIN\n");
- V fprintf(stderr, " Suisse/Schweiz/Svizzera/Svizra/Switzerland\n");
- V fprintf(stderr, " Usenet: kelvin@Autodesk.com\n");
- V fprintf(stderr, " Fax: 038/33 88 15\n");
- V fprintf(stderr, " Voice: 038/33 76 33\n");
- }
-
- /* Main program. */
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int i, sentfile = 0;
-
- gethostname(hostname, sizeof hostname);
-
- /* Create the socket used to send data. */
-
- sock = socket(AF_INET, SOCK_DGRAM, 0);
- if (sock < 0) {
- perror("opening datagram socket");
- return 1;
- }
-
- /* Process command line options. */
-
- for (i = 1; i < argc; i++) {
- char *op, opt;
-
- op = argv[i];
- if (*op == '-') {
- opt = *(++op);
- if (islower(opt))
- opt = toupper(opt);
- switch (opt) {
-
- case 'C': /* -C -- Compress sound samples */
- compressing = TRUE;
- break;
-
- case 'D': /* -D -- Enable debug output */
- debugging = TRUE;
- break;
-
- case 'G': /* -G -- Automatic gain control */
- agc = TRUE;
- break;
-
- case 'L': /* -L -- Remote loopback */
- loopback = TRUE;
- break;
-
- case 'M': /* -M -- Manual record gain control */
- agc = FALSE;
- break;
-
- case 'N': /* -N -- Do not compress sound samples */
- compressing = FALSE;
- break;
-
- case 'P': /* -Phost -- Copy output to host */
- if (!addest(op + 1)) {
- return 1;
- }
- break;
-
- case 'Q': /* -Q -- Disable debug output */
- debugging = FALSE;
- break;
-
- case 'R': /* -R -- Ring: divert output to speaker */
- ring = TRUE;
- break;
-
- case 'S': /* -Sn -- Squelch at level n */
- if (strlen(op + 1) == 0) {
- squelch = 50; /* Default squelch */
- } else {
- squelch = atoi(op + 1);
- }
- break;
-
- case 'U': /* -U -- Print usage information */
- case '?': /* -? -- Print usage information */
- usage();
- return 0;
- }
- } else {
- if (dests == NULL) {
- if (!addest(op)) {
- return 1;
- }
- } else {
- int ok = sendfile(op);
- if (ok != 0)
- return ok;
- sentfile++;
- }
- }
- }
-
- if (dests == NULL) {
- usage();
- } else {
- if (sentfile == 0) {
- return sendfile(NULL);
- }
- }
-
- return 0;
- }
- SHAR_EOF
- fi # end of overwriting check
- echo shar: extracting "'netfone.1'" '(10566 characters)'
- if test -f 'netfone.1'
- then
- echo shar: will not over-write existing file "'netfone.1'"
- else
- cat << \SHAR_EOF > 'netfone.1'
- '\" t
- .TH NETFONE 1 "12 SEP 1991"
- .UC 4
- .SH NAME
- mike, speaker \- Voice communication over a network
- .SH SYNOPSIS
- .B speaker
- .LP
- .B mike
- .I hostname
- [
- .B \-cdglmnqru
- ] [
- .BI \-p hostname
- ] [
- .BI \-s level
- ] [
- .I soundfile ...
- ]
- .SH DESCRIPTION
- .B netfone
- is an application that allows users with Sun SPARCStations connected
- by a network to converse, using the audio input and output facilities
- of the SPARCStation to digitise and later reconstruct the sound and
- the network to relay sound packets. Audio files recorded with
- .BR record (6)
- or
- .BR soundtool (6)
- may be transmitted and played on remote workstations as well.
- Optional compression is provided, allowing conversations
- (albeit with lower quality audio) to take place over 56 Kb links as
- well as local Ethernet connections.
-
- .B netfone
- consists of two programs,
- .B mike
- and
- .BR speaker .
- The
- .B speaker
- program must be running on a workstation to allow it to receive
- sound sent with the
- .B mike
- program. You can execute
- .B speaker
- in the background or install it as a standard daemon by
- adding the line:
-
- .B " speaker &"
-
- to the end of your
- .B /etc/rc.local
- file.
-
- You can send audio to workstation
- .I hostname
- running the
- .B speaker
- program with:
-
- .BI " mike " hostname
-
- which sends real-time audio from the microphone jack, or:
-
- .B " mike "
- .I hostname
- .I soundfile
-
- where
- .I soundfile
- is one or more files of prerecorded sound created with
- .BR soundtool (6)
- or
- .BR record (6).
- If
- .I soundfile
- is a single period, real-time audio from the microphone jack is
- selected. This permits you to send one or more sound files, then
- switch to live audio all in a single command.
-
- .BR mike " and " speaker
- are normally used in conjunction with the Sun program
- .BR gaintool (6)
- which allows interactive setting of the audio record and playback
- levels with a SunView tool.
- .B gaintool
- is supplied by Sun as part of the
- .I Demos
- installation option.
-
- It's perfectly valid to send audio to a copy of
- .B speaker
- running on the same workstation as
- .BR mike .
- In fact, it's a very handy way to experiment.
-
- .SH "BUILDING AND INSTALLING"
-
- To install
- .B netfone
- on your Sun workstation, extract it from the
- archive in which it was delivered into a directory of its own.
- With this directory as the current directory, say:
-
- .B " make"
-
- to build the programs. You may wish to install the
- .B mike
- and
- .B speaker
- programs in a library directory so they will be generally accessible.
- This manual page, which may be displayed on the terminal with:
-
- .B " make manpage"
-
- should be copied into your local manual page directory.
-
- .SH OPTIONS
- .B speaker
- has no options and is totally non-interactive. The following
- options may be specified on
- .BR mike .
- Options are processed left to right and sound files are sent with
- the modes specified by options to their left on the command line.
- .TP 10
- .B \-c
- Compress sound.
- .B mike
- normally transmits sound at the standard rate of
- 8000 samples per second. When compression is enabled, this
- rate is halved to 4000 samples per second which reduces the bandwidth
- to that which can be accommodated by a 56 Kb line (which
- has a capacity of a little more than 6000 bytes per second). The
- elided samples are reconstructed at the receiving end by
- .BR speaker .
- Compression reduces the audio quality, but the result is generally
- intelligible and certainly much better than the random pauses
- and lost words that occur if you try to send full bandwidth audio
- over a 56 Kb line. Both sound files and real time audio may be
- compressed. Compression of real time audio works best if the
- input audio signal is subjected to a 2 Khz low-pass filter; if
- higher frequency components are present they will cause aliasing
- in the sampling process, which can do strange things to the sound.
- Using a ``communications microphone'' rather than a full bandwidth
- microphone helps if you don't have access to a signal processor or
- equaliser to limit the bandwidth.
- .TP
- .B \-d
- Enables debug output from
- .I both
- the local copy of
- .B mike
- and the receiving copy of
- .BR speaker .
- .TP
- .B \-g
- Automatic gain control is enabled for real-time audio. The
- recording gain is dynamically adjusted to compensate for the
- amplitude of the sound received, using the maximum dynamic
- range without clipping. If this switch is specified, the
- record gain cannot be manually set with
- .BR gaintool .
- Automatic gain control is on by default.
- .TP
- .B \-l
- Remote loopback is enabled. Each packet received by
- .B speaker
- will be immediately transmitted back to a copy of
- .B speaker
- running on the originating machine. You can use loopback to
- evaluate the quality of transmission over various kinds of
- communication links without the need to have a person at the
- other end.
- .TP
- .B \-m
- Manual gain control. Allows you to manually set the recording level
- with
- .BR gaintool .
- .TP
- .B \-n
- Disables compression of sound. This is the default; the switch
- permits canceling the effect of a previous
- .B \-c
- switch when sending multiple sound files with one
- .B mike
- command.
- .TP
- .BI \-p hostname
- Adds
- .I hostname
- to the list of hosts to which the sound is sent. The first
- host is specified explicitly on the
- .B mike
- command line. You may name additional hosts, all of which must be
- running
- .BR speaker ,
- to create a ``party line''. The same sound will be sent to each
- host you name. If you're on a 56 Kb line instead of an Ethernet,
- transmitting to multiple hosts will result in unacceptable delays
- and lost words; there simply isn't enough bandwidth to send duplicate
- packets.
- .TP
- .B \-q
- Quiet--disables debug output. This is the default; the switch can be
- used to cancel the effect of a prior
- .B \-d
- switch.
- .TP
- .B \-r
- Ring. This is used to get the attention of a user when you're
- trying to establish a connection. The output, which the user may
- have diverted to the headphone jack, is forced to go to the
- workstation speaker and the playback volume is set to mid-level
- to guarantee audibility. The receiving user may subsequently
- switch the output back to the headphones, if desired, with
- .BR gaintool .
- .TP
- .BI \-s level
- Squelch output whenever input volume is below the specified
- .IR level .
- The
- .I level
- specification is an arbitrary number from 1 to 255 with larger
- numbers denoting louder sound. The default squelch value, if none
- is given on the
- .B \-s
- switch, is 50 which works reasonably well unless your computer room is
- very noisy (in which case you might want to avail yourself of a
- headset with a directional boom microphone). Squelch interacts poorly
- with automatic gain control; if you wish to use squelch, specify the
- .B \-m
- switch and set the gain with
- .B gaintool
- for the best results. Squelch is off by default, equivalent to a
- specification of
- .BR \-s0 .
- Enabling squelch allows multiple people to send sound to the same
- destination(s) and, as long as only one speaks at a time, for the
- result to be intelligible. In order for this to work the recording
- and squelch levels must be set so that sound is sent only when you're
- talking. Enabling debugging output with the
- .B \-d
- switch may help to determine the best settings.
- .TP
- .B \-u
- Prints how-to-call information.
- .SH FILES
- Audio is read and written from the
- .B /dev/audio
- device file. The device will be busy for input whenever
- .B mike
- is running. The
- .B speaker
- program acquires the audio device upon receiving sound, but automatically
- releases
- .B /dev/audio
- for output after 20 seconds elapse without any sound having been
- received.
- .SH BUGS
- .PP
- If sound from multiple sources arrives simultaneously at one machine,
- .B speaker
- interleaves the audio packet-by-packet. This usually results in
- unintelligible gibberish, although it's normally adequate to allow
- ``butting into'' a conversation. It might be possible to have
- .B speaker
- mix the sound into one output stream, but I haven't experimented
- with this approach.
- .PP
- No warning is given if the destination workstation is not running
- .BR speaker ;
- sound just disappears.
- .PP
- In order to deliver acceptable (or at least tolerable) performance across
- international satellite links,
- .BR mike " and " speaker
- use ``Internet datagram'' socket protocol which is essentially a
- ``fire and forget'' mechanism; neither flow control nor acknowledgement
- are provided. Since sound most be delivered at the correct time in order
- to be intelligible, for real-time transmission there's little one can
- do anyway if data are lost. Consequently, bogged down lines, transmission
- errors, etc., simply degrade or destroy the quality of the audio without
- providing explicit warnings at either end that anything's amiss.
- In addition, the lack of an end-to-end handshake deprives
- .B speaker
- of backpressure information to control the rate at which it dispatches
- packets when transmitting a sound file. I fake flow control by calculating
- the time it will take to play each packet and causing
- .B speaker
- to
- .B usleep(\|)
- that number of microseconds after sending it. This is, of course,
- utterly beneath contempt, but it actually works quite nicely
- (at least as long as your machine isn't busy). If you're motivated to
- replace all this datagram stuff with nice, clean RPC calls, don't bother.
- That's how I built the initial version of
- .BR netfone ,
- and although it ran OK on an Ethernet, it was a disaster on 56 Kb lines.
- .PP
- Acceptable performance over a 56 Kb line requires the line to be close
- to idle. If file transfers or other bulk traffic are underway, you'll
- probably be disappointed.
- .PP
- SunOS 4.1 or above is required to build and use these programs. As of
- this writing, Sun's multimedia support is in a state of rapid flux.
- You should expect changes from release to release which may require
- modifications to how these programs are built or within the programs
- themselves. This version has been tested on SunOS 4.1.1.
- .PP
- .BR mike " and " speaker
- communicate using Internet port number 2074. It is
- conceivable, although unlikely, that this might conflict with some other
- locally-developed network server. You can change the port number by
- editing the
- .B Makefile
- to change the definition of ``INTERNET_PORT'' and rebuilding, but then your
- versions of
- .BR mike " and " speaker
- won't communicate with others that use the standard port number.
-
- .ne 2
- .SH "SEE ALSO"
- .PD
- .BR audio (4),
- .BR gaintool (6),
- .BR rc (8),
- .BR record (6),
- .BR soundtool (6),
- .BR talk (1),
- .BR usleep (3)
-
- .ne 10
- .SH AUTHOR
- .RS 5
- .nf
- John Walker
- Autodesk SA
- Avenue des Champs-Montants 14b
- CH-2074 MARIN
- Suisse/Schweiz/Svizzera/Svizra/Switzerland
- Usenet: kelvin@Autodesk.com
- Fax: 038/33 88 15
- Voice: 038/33 76 33
- .fi
- .RE
- SHAR_EOF
- fi # end of overwriting check
- echo shar: extracting "'soundbyte.c'" '(3658 characters)'
- if test -f 'soundbyte.c'
- then
- echo shar: will not over-write existing file "'soundbyte.c'"
- else
- cat << \SHAR_EOF > 'soundbyte.c'
- /*
-
- NetFone: Sound interface functions
-
- Designed and implemented in July of 1991 by John Walker
-
- */
-
- #include <sys/types.h>
- #include <sys/dir.h>
- #include <sys/file.h>
-
- #include <math.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <stdio.h>
- #include <assert.h>
-
- #include <sys/ioctl.h>
- #include <sun/audioio.h>
-
- #define SoundFile "/dev/audio"
- #define AUDIO_CTLDEV "/dev/audioctl"
-
- #define MAX_GAIN 100
-
- #define TRUE 1
- #define FALSE 0
-
- #define V (void)
-
- struct sound_buf {
- struct sound_buf *snext; /* Next sound buffer */
- int sblen; /* Length of this sound buffer */
- unsigned char sbtext[2]; /* Actual sampled sound */
- };
-
- /* Local variables */
-
- static int audiof = -1; /* Audio device file descriptor */
- static int Audio_fd; /* Audio control port */
- static audio_info_t Audio_info; /* Current configuration info */
- static int recording = FALSE; /* Recording in progress ? */
-
- /* Forward functions */
-
- void soundpoll();
-
- /* Convert local gain into device parameters */
-
- static unsigned scale_gain(g)
- unsigned g;
- {
- return (AUDIO_MIN_GAIN + (unsigned)
- irint(((double) (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) *
- ((double)g / (double)MAX_GAIN)));
- }
-
-
- /* SOUNDINIT -- Open the sound peripheral and initialise for
- access. Return TRUE if successful, FALSE
- otherwise. */
-
- int soundinit(iomode)
- int iomode;
- {
- assert(audiof == -1);
- if ((audiof = open(SoundFile, iomode)) >= 0) {
-
- if ((Audio_fd = open(AUDIO_CTLDEV, O_RDWR)) < 0) {
- perror(AUDIO_CTLDEV);
- return FALSE;
- }
- return TRUE;
- }
- return FALSE;
- }
-
- /* SOUNDTERM -- Close the sound device. */
-
- void soundterm()
- {
- if (audiof >= 0) {
- V close(audiof);
- V close(Audio_fd);
- audiof = -1;
- }
- }
-
- /* SOUNDRECORD -- Begin recording of sound. */
-
- int soundrecord()
- {
- assert(!recording);
- recording = TRUE;
- return recording;
- }
-
- /* SOUNDPLAY -- Begin playing a sound. */
-
- void soundplay(len, buf)
- int len;
- unsigned char *buf;
- {
- int ios;
-
- assert(audiof != -1);
- while (TRUE) {
- ios = write(audiof, buf, len);
- if (ios == -1) {
- usleep(100000);
- } else {
- if (ios < len) {
- buf += ios;
- len -= ios;
- } else {
- break;
- }
- }
- }
- }
-
- /* SOUNDPLAYVOL -- Set playback volume from 0 (silence) to 100 (full on). */
-
- void soundplayvol(value)
- int value;
- {
- AUDIO_INITINFO(&Audio_info);
- Audio_info.play.gain = scale_gain(value);
- if (ioctl(Audio_fd, AUDIO_SETINFO, &Audio_info) < 0) {
- perror("Set play volume");
- }
- }
-
- /* SOUNDRECGAIN -- Set recording gain from 0 (minimum) to 100 (maximum). */
-
- void soundrecgain(value)
- int value;
- {
- AUDIO_INITINFO(&Audio_info);
- Audio_info.record.gain = scale_gain(value);
- if (ioctl(Audio_fd, AUDIO_SETINFO, &Audio_info) < 0) {
- perror("Set record gain");
- }
- }
-
- /* SOUNDDEST -- Set destination for generated sound. If "where"
- is 0, sound goes to the built-in speaker; if
- 1, to the audio output jack. */
-
- void sounddest(where)
- int where;
- {
- AUDIO_INITINFO(&Audio_info);
- Audio_info.play.port = (where == 0 ? AUDIO_SPEAKER : AUDIO_HEADPHONE);
- if (ioctl(Audio_fd, AUDIO_SETINFO, &Audio_info) < 0) {
- perror("Set output port");
- }
- }
-
- /* SOUNDGRAB -- Return audio information in the record queue. */
-
- int soundgrab(buf, len)
- char *buf;
- int len;
- {
- if (recording) {
- long read_size;
-
- if (ioctl(audiof, FIONREAD, &read_size) < 0) {
- perror("FIONREAD ioctl failed");
- } else {
- if (read_size > len)
- read_size = len;
- read(audiof, buf, read_size);
- return read_size;
- }
- }
- return 0;
- }
- SHAR_EOF
- fi # end of overwriting check
- echo shar: extracting "'speaker.c'" '(5225 characters)'
- if test -f 'speaker.c'
- then
- echo shar: will not over-write existing file "'speaker.c'"
- else
- cat << \SHAR_EOF > 'speaker.c'
- /*
-
- 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);
- }
- }
- SHAR_EOF
- fi # end of overwriting check
- # End of shell archive
- exit 0
-