Bump version to 0.9.1.
[python/dscho.git] / Demo / sgi / al / intercom.py
blobfd983d39adfbcd80b4b2eece390b1fdba6b9cccf
1 # intercom -- use mike and headset to *talk* to a person on another host.
2 # For SGI 4D/35 or Indigo running IRIX 4.0.
3 # Uses 16 bit sampling at 16000 samples/sec, or 32000 bytes/sec,
4 # tranmitted in 32 1000-byte UDP packets. (In each direction!)
6 # usage:
7 # intercom hostname - start talking to person on other host
8 # intercom -r hostname - called remotely to do the setup
10 from names import *
11 import sys, time, posix, gl, fl, FL, al, AL, getopt, rand
12 from socket import *
14 # UDP port numbers used (one for each direction!)
15 PORT1 = 51042
16 PORT2 = PORT1+1
18 # Figure out the user name
19 try:
20 user = posix.environ['LOGNAME']
21 except:
22 user = posix.environ['USER']
24 # Debug flags (Implemented as a list; non-empty means debugging is on)
25 debug = []
27 def main():
28 remote = 0
29 opts, args = getopt.getopt(sys.argv[1:], 'rd')
30 for opt, arg in opts:
31 if opt == '-r': remote = 1
32 elif opt == '-d': debug.append(opt)
33 if len(args) <> 1:
34 msg = 'usage: intercom [-d] [-r] hostname'
35 msg = msg + ' (-r is for internal use only!)\n'
36 sys.stderr.write(msg)
37 sys.exit(2)
38 if remote:
39 server(args[0])
40 else:
41 client(args[0])
43 def client(hostname):
44 print 'client starting'
45 cmd = 'rsh ' + hostname + ' "cd ' + AUDIODIR
46 cmd = cmd + '; DISPLAY=:0; export DISPLAY'
47 cmd = cmd + '; ' + PYTHON + ' intercom.py -r '
48 for flag in debug: cmd = cmd + flag + ' '
49 cmd = cmd + gethostname()
50 cmd = cmd + '"'
51 if debug: print cmd
52 pipe = posix.popen(cmd, 'r')
53 ack = 0
54 nak = 0
55 while 1:
56 line = pipe.readline()
57 if not line: break
58 sys.stdout.write('remote: ' + line)
59 if line == 'NAK\n':
60 nak = 1
61 break
62 elif line == 'ACK\n':
63 ack = 1
64 break
65 if nak:
66 print 'Remote user doesn\'t want to talk to you.'
67 return
68 if not ack:
69 print 'No acknowledgement (remote side crashed?).'
70 return
72 print 'Ready...'
74 s = socket(AF_INET, SOCK_DGRAM)
75 s.bind('', PORT2)
77 otheraddr = gethostbyname(hostname), PORT1
78 try:
79 try:
80 ioloop(s, otheraddr)
81 except KeyboardInterrupt:
82 log('client got intr')
83 except error:
84 log('client got error')
85 finally:
86 s.sendto('', otheraddr)
87 log('client finished sending empty packet to server')
89 log('client exit')
90 print 'Done.'
92 def server(hostname):
93 print 'server starting'
94 sys.stdout.flush()
96 if not remotedialog():
97 print 'NAK'
98 return
100 print 'ACK'
102 s = socket(AF_INET, SOCK_DGRAM)
103 s.bind('', PORT1)
105 # Close std{in,out,err} so rsh will exit; reopen them as dummies
107 sys.stdin.close()
108 sys.stdin = open('/dev/null', 'r')
109 sys.stdout.close()
110 sys.stdout = open('/dev/null', 'w')
111 sys.stderr.close()
112 if debug:
113 sys.stderr = open('/tmp/intercom.err', 'a')
114 else:
115 sys.stderr = open('/dev/null', 'w')
117 ioloop(s, (gethostbyname(hostname), PORT2))
118 log('server exit')
119 sys.exit(0)
121 def remotedialog():
122 gl.foreground()
123 gl.ringbell()
124 m1 = user + ' wants to talk to you over the audio channel.'
125 m2 = 'If it\'s OK, put on your headset and click Yes.'
126 m3 = 'If you\'re too busy, click No.'
127 return fl.show_question(m1, m2, m3)
129 def ioloop(s, otheraddr):
131 dev = AL.DEFAULT_DEVICE
132 params = al.queryparams(dev)
133 al.getparams(dev, params)
134 time.sleep(1)
135 saveparams = params[:]
136 for i in range(0, len(params), 2):
137 if params[i] in (AL.INPUT_RATE, AL.OUTPUT_RATE):
138 params[i+1] = AL.RATE_16000
139 elif params[i] == AL.INPUT_SOURCE:
140 params[i+1] = AL.INPUT_MIC
141 try:
142 al.setparams(dev, params)
143 ioloop1(s, otheraddr)
144 finally:
145 al.setparams(dev, saveparams)
147 def ioloop1(s, otheraddr):
149 # Watch out! data is in bytes, but the port counts in samples,
150 # which are two bytes each (for 16-bit samples).
151 # Luckily, we use mono, else it would be worse (2 samples/frame...)
153 SAMPSPERBUF = 500
154 BYTESPERSAMP = 2 # AL.SAMPLE_16
155 BUFSIZE = BYTESPERSAMP*SAMPSPERBUF
156 QSIZE = 4*SAMPSPERBUF
158 config = al.newconfig()
159 config.setqueuesize(QSIZE)
160 config.setwidth(AL.SAMPLE_16)
161 config.setchannels(AL.MONO)
163 pid = posix.fork()
164 if pid:
165 # Parent -- speaker/headphones handler
166 log('parent started')
167 spkr = al.openport('spkr', 'w', config)
168 while 1:
169 data = s.recv(BUFSIZE)
170 if len(data) == 0:
171 # EOF packet
172 log('parent got empty packet; killing child')
173 posix.kill(pid, 15)
174 return
175 # Discard whole packet if we are too much behind
176 if spkr.getfillable() > len(data) / BYTESPERSAMP:
177 if len(debug) >= 2:
178 log('parent Q full; dropping packet')
179 spkr.writesamps(data)
180 else:
181 # Child -- microphone handler
182 log('child started')
183 try:
184 try:
185 mike = al.openport('mike', 'r', config)
186 # Sleep a while to let the other side get started
187 time.sleep(1)
188 # Drain the queue before starting to read
189 data = mike.readsamps(mike.getfilled())
190 # Loop, sending packets from the mike to the net
191 while 1:
192 data = mike.readsamps(SAMPSPERBUF)
193 s.sendto(data, otheraddr)
194 except KeyboardInterrupt:
195 log('child got interrupt; exiting')
196 posix._exit(0)
197 except error:
198 log('child got error; exiting')
199 posix._exit(1)
200 finally:
201 log('child got unexpected error; leaving w/ traceback')
203 def log(msg):
204 if not debug: return
205 if type(msg) <> type(''):
206 msg = `msg`
208 f = open('/tmp/intercom.log', 'a')
209 f.write(`sys.argv` + ' ' + `posix.getpid()` + ': ' + msg + '\n')
210 f.close()
212 main()