1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
|
# 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()
|