blob: fd983d39adfbcd80b4b2eece390b1fdba6b9cccf [file] [log] [blame]
# intercom -- use mike and headset to *talk* to a person on another host.
# For SGI 4D/35 or Indigo running IRIX 4.0.
# Uses 16 bit sampling at 16000 samples/sec, or 32000 bytes/sec,
# tranmitted in 32 1000-byte UDP packets. (In each direction!)
#
# usage:
# intercom hostname - start talking to person on other host
# intercom -r hostname - called remotely to do the setup
from names import *
import sys, time, posix, gl, fl, FL, al, AL, getopt, rand
from socket import *
# UDP port numbers used (one for each direction!)
PORT1 = 51042
PORT2 = PORT1+1
# Figure out the user name
try:
user = posix.environ['LOGNAME']
except:
user = posix.environ['USER']
# Debug flags (Implemented as a list; non-empty means debugging is on)
debug = []
def main():
remote = 0
opts, args = getopt.getopt(sys.argv[1:], 'rd')
for opt, arg in opts:
if opt == '-r': remote = 1
elif opt == '-d': debug.append(opt)
if len(args) <> 1:
msg = 'usage: intercom [-d] [-r] hostname'
msg = msg + ' (-r is for internal use only!)\n'
sys.stderr.write(msg)
sys.exit(2)
if remote:
server(args[0])
else:
client(args[0])
def client(hostname):
print 'client starting'
cmd = 'rsh ' + hostname + ' "cd ' + AUDIODIR
cmd = cmd + '; DISPLAY=:0; export DISPLAY'
cmd = cmd + '; ' + PYTHON + ' intercom.py -r '
for flag in debug: cmd = cmd + flag + ' '
cmd = cmd + gethostname()
cmd = cmd + '"'
if debug: print cmd
pipe = posix.popen(cmd, 'r')
ack = 0
nak = 0
while 1:
line = pipe.readline()
if not line: break
sys.stdout.write('remote: ' + line)
if line == 'NAK\n':
nak = 1
break
elif line == 'ACK\n':
ack = 1
break
if nak:
print 'Remote user doesn\'t want to talk to you.'
return
if not ack:
print 'No acknowledgement (remote side crashed?).'
return
#
print 'Ready...'
#
s = socket(AF_INET, SOCK_DGRAM)
s.bind('', PORT2)
#
otheraddr = gethostbyname(hostname), PORT1
try:
try:
ioloop(s, otheraddr)
except KeyboardInterrupt:
log('client got intr')
except error:
log('client got error')
finally:
s.sendto('', otheraddr)
log('client finished sending empty packet to server')
#
log('client exit')
print 'Done.'
def server(hostname):
print 'server starting'
sys.stdout.flush()
#
if not remotedialog():
print 'NAK'
return
#
print 'ACK'
#
s = socket(AF_INET, SOCK_DGRAM)
s.bind('', PORT1)
#
# Close std{in,out,err} so rsh will exit; reopen them as dummies
#
sys.stdin.close()
sys.stdin = open('/dev/null', 'r')
sys.stdout.close()
sys.stdout = open('/dev/null', 'w')
sys.stderr.close()
if debug:
sys.stderr = open('/tmp/intercom.err', 'a')
else:
sys.stderr = open('/dev/null', 'w')
#
ioloop(s, (gethostbyname(hostname), PORT2))
log('server exit')
sys.exit(0)
def remotedialog():
gl.foreground()
gl.ringbell()
m1 = user + ' wants to talk to you over the audio channel.'
m2 = 'If it\'s OK, put on your headset and click Yes.'
m3 = 'If you\'re too busy, click No.'
return fl.show_question(m1, m2, m3)
def ioloop(s, otheraddr):
#
dev = AL.DEFAULT_DEVICE
params = al.queryparams(dev)
al.getparams(dev, params)
time.sleep(1)
saveparams = params[:]
for i in range(0, len(params), 2):
if params[i] in (AL.INPUT_RATE, AL.OUTPUT_RATE):
params[i+1] = AL.RATE_16000
elif params[i] == AL.INPUT_SOURCE:
params[i+1] = AL.INPUT_MIC
try:
al.setparams(dev, params)
ioloop1(s, otheraddr)
finally:
al.setparams(dev, saveparams)
def ioloop1(s, otheraddr):
#
# Watch out! data is in bytes, but the port counts in samples,
# which are two bytes each (for 16-bit samples).
# Luckily, we use mono, else it would be worse (2 samples/frame...)
#
SAMPSPERBUF = 500
BYTESPERSAMP = 2 # AL.SAMPLE_16
BUFSIZE = BYTESPERSAMP*SAMPSPERBUF
QSIZE = 4*SAMPSPERBUF
#
config = al.newconfig()
config.setqueuesize(QSIZE)
config.setwidth(AL.SAMPLE_16)
config.setchannels(AL.MONO)
#
pid = posix.fork()
if pid:
# Parent -- speaker/headphones handler
log('parent started')
spkr = al.openport('spkr', 'w', config)
while 1:
data = s.recv(BUFSIZE)
if len(data) == 0:
# EOF packet
log('parent got empty packet; killing child')
posix.kill(pid, 15)
return
# Discard whole packet if we are too much behind
if spkr.getfillable() > len(data) / BYTESPERSAMP:
if len(debug) >= 2:
log('parent Q full; dropping packet')
spkr.writesamps(data)
else:
# Child -- microphone handler
log('child started')
try:
try:
mike = al.openport('mike', 'r', config)
# Sleep a while to let the other side get started
time.sleep(1)
# Drain the queue before starting to read
data = mike.readsamps(mike.getfilled())
# Loop, sending packets from the mike to the net
while 1:
data = mike.readsamps(SAMPSPERBUF)
s.sendto(data, otheraddr)
except KeyboardInterrupt:
log('child got interrupt; exiting')
posix._exit(0)
except error:
log('child got error; exiting')
posix._exit(1)
finally:
log('child got unexpected error; leaving w/ traceback')
def log(msg):
if not debug: return
if type(msg) <> type(''):
msg = `msg`
f = open('/tmp/intercom.log', 'a')
f.write(`sys.argv` + ' ' + `posix.getpid()` + ': ' + msg + '\n')
f.close()
main()