| # 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: |
| 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: |
| 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() |