diff options
Diffstat (limited to 'Demo/sgi/al/intercom.py')
-rwxr-xr-x | Demo/sgi/al/intercom.py | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/Demo/sgi/al/intercom.py b/Demo/sgi/al/intercom.py new file mode 100755 index 0000000..67c424a --- /dev/null +++ b/Demo/sgi/al/intercom.py @@ -0,0 +1,218 @@ +# 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() |