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