home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
sun
/
volume3
/
netfone
next >
Wrap
Text File
|
1991-10-25
|
38KB
|
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