# 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 import sys, time, posix, gl, fl, FL, al, AL, getopt, rand from socket import * # Hack sys.path so AL can be found LIB = '/ufs/guido/lib/python' if LIB not in sys.path: sys.path.insert(0, LIB) # Python binary to be used on remote machine PYTHON = '/ufs/guido/bin/sgi/python' # Directory where the programs live AUDIODIR = '/ufs/guido/mm/demo/audio' # 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 + '; exec ' + PYTHON + ' intercom.py -r ' for flag in debug: cmd = cmd + flag + ' ' cmd = cmd + gethostname() cmd = 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()