3 """audiopy -- a program to control the Solaris audio device.
6 Email: bwarsaw@python.org
7 Version: %(__version__)s
9 When no arguments are given, this pops up a graphical window which lets you
10 choose the audio input and output devices, and set the output volume.
12 This program can be driven via the command line, and when done so, no window
13 pops up. Most options have the general form:
17 Set the I/O device. With no value, it toggles the specified device.
18 With a value, 0 turns the device off and 1 turns the device on.
20 The list of devices and their short options are:
36 Sets the output gain to the specified volume, which must be an integer
37 in the range [%(MIN_GAIN)s..%(MAX_GAIN)s]
41 Print the version number and exit.
45 Print this message and exit.
52 from SUNAUDIODEV
import *
54 # Milliseconds between interrupt checks
62 def __init__(self
, device
):
65 self
.__devctl
= device
66 info
= device
.getinfo()
68 self
.__tkroot
= tkroot
= Tk(className
='Audiopy')
71 menubar
= Menu(tkroot
)
72 filemenu
= Menu(menubar
, tearoff
=0)
73 filemenu
.add_command(label
='Quit',
77 helpmenu
= Menu(menubar
, name
='help', tearoff
=0)
78 helpmenu
.add_command(label
='About Audiopy...',
79 command
=self
.__popup
_about
,
81 helpmenu
.add_command(label
='Help...',
82 command
=self
.__popup
_using
,
84 menubar
.add_cascade(label
='File',
87 menubar
.add_cascade(label
='Help',
90 # now create the top level window
91 root
= self
.__root
= Toplevel(tkroot
, class_
='Audiopy', menu
=menubar
)
92 root
.protocol('WM_DELETE_WINDOW', self
.__quit
)
93 root
.title('audiopy ' + __version__
)
94 root
.iconname('audiopy ' + __version__
)
95 root
.tk
.createtimerhandler(KEEPALIVE_TIMER
, self
.__keepalive
)
99 # where does input come from?
100 frame
= Frame(root
, bd
=1, relief
=RAISED
)
101 frame
.grid(row
=1, column
=0, sticky
='NSEW')
102 label
= Label(frame
, text
='Input From:')
103 label
.grid(row
=0, column
=0, sticky
=E
)
104 self
.__inputvar
= IntVar()
106 btn
= Radiobutton(frame
,
108 variable
=self
.__inputvar
,
110 command
=self
.__pushtodev
,
112 btn
.grid(row
=0, column
=1, sticky
=W
)
113 root
.bind('<Alt-n>', self
.__none
)
114 root
.bind('<Alt-N>', self
.__none
)
115 if not info
.i_avail_ports
& MICROPHONE
:
116 btn
.configure(state
=DISABLED
)
119 btn
= Radiobutton(frame
,
121 variable
=self
.__inputvar
,
123 command
=self
.__pushtodev
,
125 btn
.grid(row
=1, column
=1, sticky
=W
)
126 root
.bind('<Alt-m>', self
.__mic
)
127 root
.bind('<Alt-M>', self
.__mic
)
128 if not info
.i_avail_ports
& MICROPHONE
:
129 btn
.configure(state
=DISABLED
)
132 btn
= Radiobutton(frame
,
134 variable
=self
.__inputvar
,
136 command
=self
.__pushtodev
,
138 btn
.grid(row
=2, column
=1, sticky
=W
)
139 root
.bind('<Alt-i>', self
.__linein
)
140 root
.bind('<Alt-I>', self
.__linein
)
141 if not info
.i_avail_ports
& LINE_IN
:
142 btn
.configure(state
=DISABLED
)
144 ## if SUNAUDIODEV was built on an older version of Solaris, the CD
145 ## input device won't exist
147 btn
= Radiobutton(frame
,
149 variable
=self
.__inputvar
,
151 command
=self
.__pushtodev
,
153 btn
.grid(row
=3, column
=1, sticky
=W
)
154 root
.bind('<Alt-c>', self
.__cd
)
155 root
.bind('<Alt-C>', self
.__cd
)
156 if not info
.i_avail_ports
& CD
:
157 btn
.configure(state
=DISABLED
)
162 # where does output go to?
163 frame
= Frame(root
, bd
=1, relief
=RAISED
)
164 frame
.grid(row
=2, column
=0, sticky
='NSEW')
165 label
= Label(frame
, text
='Output To:')
166 label
.grid(row
=0, column
=0, sticky
=E
)
167 self
.__spkvar
= IntVar()
168 btn
= Checkbutton(frame
,
170 variable
=self
.__spkvar
,
172 command
=self
.__pushtodev
,
174 btn
.grid(row
=0, column
=1, sticky
=W
)
175 root
.bind('<Alt-s>', self
.__speaker
)
176 root
.bind('<Alt-S>', self
.__speaker
)
177 if not info
.o_avail_ports
& SPEAKER
:
178 btn
.configure(state
=DISABLED
)
181 self
.__headvar
= IntVar()
182 btn
= Checkbutton(frame
,
184 variable
=self
.__headvar
,
186 command
=self
.__pushtodev
,
188 btn
.grid(row
=1, column
=1, sticky
=W
)
189 root
.bind('<Alt-p>', self
.__headphones
)
190 root
.bind('<Alt-P>', self
.__headphones
)
191 if not info
.o_avail_ports
& HEADPHONE
:
192 btn
.configure(state
=DISABLED
)
195 self
.__linevar
= IntVar()
196 btn
= Checkbutton(frame
,
197 variable
=self
.__linevar
,
200 command
=self
.__pushtodev
,
202 btn
.grid(row
=2, column
=1, sticky
=W
)
203 root
.bind('<Alt-l>', self
.__lineout
)
204 root
.bind('<Alt-L>', self
.__lineout
)
205 if not info
.o_avail_ports
& LINE_OUT
:
206 btn
.configure(state
=DISABLED
)
216 b
.configure(width
=widest
)
218 root
.bind('<Alt-q>', self
.__quit
)
219 root
.bind('<Alt-Q>', self
.__quit
)
222 frame
= Frame(root
, bd
=1, relief
=RAISED
)
223 frame
.grid(row
=3, column
=0, sticky
='NSEW')
224 label
= Label(frame
, text
='Output Volume:')
225 label
.grid(row
=0, column
=0, sticky
=W
)
226 self
.__scalevar
= IntVar()
227 self
.__scale
= Scale(frame
,
232 variable
=self
.__scalevar
,
233 command
=self
.__volume
)
234 self
.__scale
.grid(row
=1, column
=0, sticky
=EW
)
236 # do we need to poll for changes?
237 self
.__needtopoll
= 1
239 fd
= self
.__devctl
.fileno()
240 self
.__needtopoll
= 0
241 except AttributeError:
247 # set up the signal handler
248 signal
.signal(signal
.SIGPOLL
, self
.__update
)
249 fcntl
.ioctl(fd
, STROPTS
.I_SETSIG
, STROPTS
.S_MSG
)
252 def __quit(self
, event
=None):
253 self
.__devctl
.close()
256 def __popup_about(self
, event
=None):
258 tkMessageBox
.showinfo('About Audiopy ' + __version__
,
261 Control the Solaris audio device
264 Contact: Barry A. Warsaw
265 Email: bwarsaw@python.org''' % __version__
)
267 def __popup_using(self
, event
=None):
268 if not self
.__helpwin
:
269 self
.__helpwin
= Helpwin(self
.__tkroot
, self
.__quit
)
270 self
.__helpwin
.deiconify()
273 def __keepalive(self
):
274 # Exercise the Python interpreter regularly so keyboard interrupts get
276 self
.__tkroot
.tk
.createtimerhandler(KEEPALIVE_TIMER
, self
.__keepalive
)
277 if self
.__needtopoll
:
280 def __update(self
, num
=None, frame
=None):
281 # It's possible (although I have never seen it) to get an interrupted
282 # system call during the getinfo() call. If so, and we're polling,
283 # don't sweat it because we'll come around again later. Otherwise,
284 # we'll give it a couple of tries and then give up until next time.
288 info
= self
.__devctl
.getinfo()
290 except sunaudiodev
.error
:
291 if self
.__needtopoll
or tries
> 3:
295 self
.__inputvar
.set(info
.i_port
)
297 self
.__spkvar
.set(info
.o_port
& SPEAKER
)
298 self
.__headvar
.set(info
.o_port
& HEADPHONE
)
299 self
.__linevar
.set(info
.o_port
& LINE_OUT
)
301 self
.__scalevar
.set(info
.o_gain
)
303 def __pushtodev(self
, event
=None):
304 info
= self
.__devctl
.getinfo()
305 info
.o_port
= self
.__spkvar
.get() + \
306 self
.__headvar
.get() + \
308 info
.i_port
= self
.__inputvar
.get()
309 info
.o_gain
= self
.__scalevar
.get()
311 self
.__devctl
.setinfo(info
)
312 except sunaudiodev
.error
, msg
:
313 # TBD: what to do? it's probably temporary.
316 def __getset(self
, var
, onvalue
):
317 if var
.get() == onvalue
:
323 def __none(self
, event
=None):
324 self
.__inputvar
.set(0)
327 def __mic(self
, event
=None):
328 self
.__getset
(self
.__inputvar
, MICROPHONE
)
330 def __linein(self
, event
=None):
331 self
.__getset
(self
.__inputvar
, LINE_IN
)
333 def __cd(self
, event
=None):
334 self
.__getset
(self
.__inputvar
, CD
)
336 def __speaker(self
, event
=None):
337 self
.__getset
(self
.__spkvar
, SPEAKER
)
339 def __headphones(self
, event
=None):
340 self
.__getset
(self
.__headvar
, HEADPHONE
)
342 def __lineout(self
, event
=None):
343 self
.__getset
(self
.__linevar
, LINE_OUT
)
345 def __volume(self
, event
=None):
350 self
.__tkroot
.mainloop()
355 def __init__(self
, master
, quitfunc
):
356 from Tkinter
import *
357 self
.__root
= root
= Toplevel(master
, class_
='Audiopy')
358 root
.protocol('WM_DELETE_WINDOW', self
.__withdraw
)
359 root
.title('Audiopy Help Window')
360 root
.iconname('Audiopy Help Window')
361 root
.bind('<Alt-q>', quitfunc
)
362 root
.bind('<Alt-Q>', quitfunc
)
363 root
.bind('<Alt-w>', self
.__withdraw
)
364 root
.bind('<Alt-W>', self
.__withdraw
)
366 # more elaborate help is available in the README file
367 readmefile
= os
.path
.join(sys
.path
[0], 'README')
371 fp
= open(readmefile
)
373 # wax the last page, it contains Emacs cruft
374 i
= contents
.rfind('\f')
376 contents
= contents
[:i
].rstrip()
381 sys
.stderr
.write("Couldn't open audiopy's README, "
382 'using docstring instead.\n')
383 contents
= __doc__
% globals()
385 self
.__text
= text
= Text(root
, relief
=SUNKEN
,
387 text
.insert(0.0, contents
)
388 scrollbar
= Scrollbar(root
)
389 scrollbar
.pack(fill
=Y
, side
=RIGHT
)
390 text
.pack(fill
=BOTH
, expand
=YES
)
391 text
.configure(yscrollcommand
=(scrollbar
, 'set'))
392 scrollbar
.configure(command
=(text
, 'yview'))
394 def __withdraw(self
, event
=None):
395 self
.__root
.withdraw()
398 self
.__root
.deiconify()
403 def usage(code
, msg
=''):
404 print __doc__
% globals()
412 # Open up the audio control device and query for the current output
414 device
= sunaudiodev
.open('control')
416 if len(sys
.argv
) == 1:
418 w
= MainWindow(device
)
421 except KeyboardInterrupt:
425 # spec: LONG OPT, SHORT OPT, 0=input,1=output, MASK
426 options
= [('--microphone', '-m', 0, MICROPHONE
),
427 ('--linein', '-i', 0, LINE_IN
),
428 ('--headphones', '-p', 1, HEADPHONE
),
429 ('--speaker', '-s', 1, SPEAKER
),
430 ('--lineout', '-o', 1, LINE_OUT
),
432 # See the comment above about `CD'
434 options
.append(('--cd', '-c', 0, CD
))
438 info
= device
.getinfo()
439 # first get the existing values
441 while i
< len(sys
.argv
)-1:
444 if arg
in ('-h', '--help'):
447 elif arg
in ('-g', '--gain'):
448 gainspec
= '<missing>'
450 gainspec
= sys
.argv
[i
+1]
452 except (ValueError, IndexError):
453 usage(1, 'Bad gain specification: ' + gainspec
)
457 elif arg
in ('-v', '--version'):
459 audiopy -- a program to control the Solaris audio device.
460 Contact: Barry Warsaw
461 Email: bwarsaw@python.org
462 Version: %s''' % __version__
464 for long, short
, io
, mask
in options
:
465 if arg
in (long, short
):
468 info
.i_port
= info
.i_port ^ mask
470 info
.o_port
= info
.o_port ^ mask
474 if arg
[:len(long)+1] == long+'=':
475 val
= int(arg
[len(long)+1:])
476 elif arg
[:len(short
)+1] == short
+'=':
477 val
= int(arg
[len(short
)+1:])
479 usage(1, msg
='Invalid option: ' + arg
)
483 info
.i_port
= info
.i_port
& ~mask
485 info
.o_port
= info
.o_port
& ~mask
489 info
.i_port
= info
.i_port | mask
491 info
.o_port
= info
.o_port | mask
493 # else keep trying next option
495 usage(1, msg
='Invalid option: ' + arg
)
499 except sunaudiodev
.error
, (code
, msg
):
500 if code
<> errno
.EINVAL
:
506 if __name__
== '__main__':