blob: 2babb932ae6cfb59728a3a170d379e0274ce1151 [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 Rossum5b397961991-09-15 21:26:44 +000010from names import *
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'
Guido van Rossum488b7d91991-12-24 13:55:16 +000047 cmd = cmd + '; ' + PYTHON + ' intercom.py -r '
Guido van Rossum50692d61991-09-15 21:05:15 +000048 for flag in debug: cmd = cmd + flag + ' '
49 cmd = cmd + gethostname()
50 cmd = cmd + '"'
Guido van Rossum488b7d91991-12-24 13:55:16 +000051 if debug: print cmd
Guido van Rossum50692d61991-09-15 21:05:15 +000052 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
71 #
72 print 'Ready...'
73 #
74 s = socket(AF_INET, SOCK_DGRAM)
75 s.bind('', PORT2)
76 #
77 otheraddr = gethostbyname(hostname), PORT1
78 try:
79 ioloop(s, otheraddr)
80 except KeyboardInterrupt:
81 log('client got intr')
82 except error:
83 log('client got error')
84 finally:
85 s.sendto('', otheraddr)
86 log('client finished sending empty packet to server')
87 #
88 log('client exit')
89 print 'Done.'
90
91def server(hostname):
92 print 'server starting'
93 sys.stdout.flush()
94 #
95 if not remotedialog():
96 print 'NAK'
97 return
98 #
99 print 'ACK'
100 #
101 s = socket(AF_INET, SOCK_DGRAM)
102 s.bind('', PORT1)
103 #
104 # Close std{in,out,err} so rsh will exit; reopen them as dummies
105 #
106 sys.stdin.close()
107 sys.stdin = open('/dev/null', 'r')
108 sys.stdout.close()
109 sys.stdout = open('/dev/null', 'w')
110 sys.stderr.close()
111 if debug:
112 sys.stderr = open('/tmp/intercom.err', 'a')
113 else:
114 sys.stderr = open('/dev/null', 'w')
115 #
116 ioloop(s, (gethostbyname(hostname), PORT2))
117 log('server exit')
118 sys.exit(0)
119
120def remotedialog():
121 gl.foreground()
122 gl.ringbell()
123 m1 = user + ' wants to talk to you over the audio channel.'
124 m2 = 'If it\'s OK, put on your headset and click Yes.'
125 m3 = 'If you\'re too busy, click No.'
126 return fl.show_question(m1, m2, m3)
127
128def ioloop(s, otheraddr):
129 #
130 dev = AL.DEFAULT_DEVICE
131 params = al.queryparams(dev)
132 al.getparams(dev, params)
133 time.sleep(1)
134 saveparams = params[:]
135 for i in range(0, len(params), 2):
136 if params[i] in (AL.INPUT_RATE, AL.OUTPUT_RATE):
137 params[i+1] = AL.RATE_16000
138 elif params[i] = AL.INPUT_SOURCE:
139 params[i+1] = AL.INPUT_MIC
140 try:
141 al.setparams(dev, params)
142 ioloop1(s, otheraddr)
143 finally:
144 al.setparams(dev, saveparams)
145
146def ioloop1(s, otheraddr):
147 #
148 # Watch out! data is in bytes, but the port counts in samples,
149 # which are two bytes each (for 16-bit samples).
150 # Luckily, we use mono, else it would be worse (2 samples/frame...)
151 #
152 SAMPSPERBUF = 500
153 BYTESPERSAMP = 2 # AL.SAMPLE_16
154 BUFSIZE = BYTESPERSAMP*SAMPSPERBUF
155 QSIZE = 4*SAMPSPERBUF
156 #
157 config = al.newconfig()
158 config.setqueuesize(QSIZE)
159 config.setwidth(AL.SAMPLE_16)
160 config.setchannels(AL.MONO)
161 #
162 pid = posix.fork()
163 if pid:
164 # Parent -- speaker/headphones handler
165 log('parent started')
166 spkr = al.openport('spkr', 'w', config)
167 while 1:
168 data = s.recv(BUFSIZE)
169 if len(data) = 0:
170 # EOF packet
171 log('parent got empty packet; killing child')
172 posix.kill(pid, 15)
173 return
174 # Discard whole packet if we are too much behind
175 if spkr.getfillable() > len(data) / BYTESPERSAMP:
176 if len(debug) >= 2:
177 log('parent Q full; dropping packet')
178 spkr.writesamps(data)
179 else:
180 # Child -- microphone handler
181 log('child started')
182 try:
183 mike = al.openport('mike', 'r', config)
184 # Sleep a while to let the other side get started
185 time.sleep(1)
186 # Drain the queue before starting to read
187 data = mike.readsamps(mike.getfilled())
188 # Loop, sending packets from the mike to the net
189 while 1:
190 data = mike.readsamps(SAMPSPERBUF)
191 s.sendto(data, otheraddr)
192 except KeyboardInterrupt:
193 log('child got interrupt; exiting')
194 posix._exit(0)
195 except error:
196 log('child got error; exiting')
197 posix._exit(1)
198 finally:
199 log('child got unexpected error; leaving w/ traceback')
200
201def log(msg):
202 if not debug: return
203 if type(msg) <> type(''):
204 msg = `msg`
205
206 f = open('/tmp/intercom.log', 'a')
207 f.write(`sys.argv` + ' ' + `posix.getpid()` + ': ' + msg + '\n')
208 f.close()
209
210main()