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.
53 from SUNAUDIODEV
import *
55 # Milliseconds between interrupt checks
63 def __init__(self
, device
):
66 self
.__devctl
= device
67 info
= device
.getinfo()
69 self
.__tkroot
= tkroot
= Tk(className
='Audiopy')
72 menubar
= Menu(tkroot
)
73 filemenu
= Menu(menubar
, tearoff
=0)
74 filemenu
.add_command(label
='Quit',
78 helpmenu
= Menu(menubar
, name
='help', tearoff
=0)
79 helpmenu
.add_command(label
='About Audiopy...',
80 command
=self
.__popup
_about
,
82 helpmenu
.add_command(label
='Help...',
83 command
=self
.__popup
_using
,
85 menubar
.add_cascade(label
='File',
88 menubar
.add_cascade(label
='Help',
91 # now create the top level window
92 root
= self
.__root
= Toplevel(tkroot
, class_
='Audiopy', menu
=menubar
)
93 root
.protocol('WM_DELETE_WINDOW', self
.__quit
)
94 root
.title('audiopy ' + __version__
)
95 root
.iconname('audiopy ' + __version__
)
96 root
.tk
.createtimerhandler(KEEPALIVE_TIMER
, self
.__keepalive
)
100 # where does input come from?
101 frame
= Frame(root
, bd
=1, relief
=RAISED
)
102 frame
.grid(row
=1, column
=0, sticky
='NSEW')
103 label
= Label(frame
, text
='Input From:')
104 label
.grid(row
=0, column
=0, sticky
=E
)
105 self
.__inputvar
= IntVar()
107 btn
= Radiobutton(frame
,
109 variable
=self
.__inputvar
,
111 command
=self
.__pushtodev
,
113 btn
.grid(row
=0, column
=1, sticky
=W
)
114 root
.bind('<Alt-n>', self
.__none
)
115 root
.bind('<Alt-N>', self
.__none
)
116 if not info
.i_avail_ports
& MICROPHONE
:
117 btn
.configure(state
=DISABLED
)
120 btn
= Radiobutton(frame
,
122 variable
=self
.__inputvar
,
124 command
=self
.__pushtodev
,
126 btn
.grid(row
=1, column
=1, sticky
=W
)
127 root
.bind('<Alt-m>', self
.__mic
)
128 root
.bind('<Alt-M>', self
.__mic
)
129 if not info
.i_avail_ports
& MICROPHONE
:
130 btn
.configure(state
=DISABLED
)
133 btn
= Radiobutton(frame
,
135 variable
=self
.__inputvar
,
137 command
=self
.__pushtodev
,
139 btn
.grid(row
=2, column
=1, sticky
=W
)
140 root
.bind('<Alt-i>', self
.__linein
)
141 root
.bind('<Alt-I>', self
.__linein
)
142 if not info
.i_avail_ports
& LINE_IN
:
143 btn
.configure(state
=DISABLED
)
145 ## if SUNAUDIODEV was built on an older version of Solaris, the CD
146 ## input device won't exist
148 btn
= Radiobutton(frame
,
150 variable
=self
.__inputvar
,
152 command
=self
.__pushtodev
,
154 btn
.grid(row
=3, column
=1, sticky
=W
)
155 root
.bind('<Alt-c>', self
.__cd
)
156 root
.bind('<Alt-C>', self
.__cd
)
157 if not info
.i_avail_ports
& CD
:
158 btn
.configure(state
=DISABLED
)
163 # where does output go to?
164 frame
= Frame(root
, bd
=1, relief
=RAISED
)
165 frame
.grid(row
=2, column
=0, sticky
='NSEW')
166 label
= Label(frame
, text
='Output To:')
167 label
.grid(row
=0, column
=0, sticky
=E
)
168 self
.__spkvar
= IntVar()
169 btn
= Checkbutton(frame
,
171 variable
=self
.__spkvar
,
173 command
=self
.__pushtodev
,
175 btn
.grid(row
=0, column
=1, sticky
=W
)
176 root
.bind('<Alt-s>', self
.__speaker
)
177 root
.bind('<Alt-S>', self
.__speaker
)
178 if not info
.o_avail_ports
& SPEAKER
:
179 btn
.configure(state
=DISABLED
)
182 self
.__headvar
= IntVar()
183 btn
= Checkbutton(frame
,
185 variable
=self
.__headvar
,
187 command
=self
.__pushtodev
,
189 btn
.grid(row
=1, column
=1, sticky
=W
)
190 root
.bind('<Alt-p>', self
.__headphones
)
191 root
.bind('<Alt-P>', self
.__headphones
)
192 if not info
.o_avail_ports
& HEADPHONE
:
193 btn
.configure(state
=DISABLED
)
196 self
.__linevar
= IntVar()
197 btn
= Checkbutton(frame
,
198 variable
=self
.__linevar
,
201 command
=self
.__pushtodev
,
203 btn
.grid(row
=2, column
=1, sticky
=W
)
204 root
.bind('<Alt-l>', self
.__lineout
)
205 root
.bind('<Alt-L>', self
.__lineout
)
206 if not info
.o_avail_ports
& LINE_OUT
:
207 btn
.configure(state
=DISABLED
)
217 b
.configure(width
=widest
)
219 root
.bind('<Alt-q>', self
.__quit
)
220 root
.bind('<Alt-Q>', self
.__quit
)
223 frame
= Frame(root
, bd
=1, relief
=RAISED
)
224 frame
.grid(row
=3, column
=0, sticky
='NSEW')
225 label
= Label(frame
, text
='Output Volume:')
226 label
.grid(row
=0, column
=0, sticky
=W
)
227 self
.__scalevar
= IntVar()
228 self
.__scale
= Scale(frame
,
233 variable
=self
.__scalevar
,
234 command
=self
.__volume
)
235 self
.__scale
.grid(row
=1, column
=0, sticky
=EW
)
237 # do we need to poll for changes?
238 self
.__needtopoll
= 1
240 fd
= self
.__devctl
.fileno()
241 self
.__needtopoll
= 0
242 except AttributeError:
248 # set up the signal handler
249 signal
.signal(signal
.SIGPOLL
, self
.__update
)
250 fcntl
.ioctl(fd
, STROPTS
.I_SETSIG
, STROPTS
.S_MSG
)
253 def __quit(self
, event
=None):
254 self
.__devctl
.close()
257 def __popup_about(self
, event
=None):
259 tkMessageBox
.showinfo('About Audiopy ' + __version__
,
262 Control the Solaris audio device
265 Contact: Barry A. Warsaw
266 Email: bwarsaw@python.org''' % __version__
)
268 def __popup_using(self
, event
=None):
269 if not self
.__helpwin
:
270 self
.__helpwin
= Helpwin(self
.__tkroot
, self
.__quit
)
271 self
.__helpwin
.deiconify()
274 def __keepalive(self
):
275 # Exercise the Python interpreter regularly so keyboard interrupts get
277 self
.__tkroot
.tk
.createtimerhandler(KEEPALIVE_TIMER
, self
.__keepalive
)
278 if self
.__needtopoll
:
281 def __update(self
, num
=None, frame
=None):
282 # It's possible (although I have never seen it) to get an interrupted
283 # system call during the getinfo() call. If so, and we're polling,
284 # don't sweat it because we'll come around again later. Otherwise,
285 # we'll give it a couple of tries and then give up until next time.
289 info
= self
.__devctl
.getinfo()
291 except sunaudiodev
.error
:
292 if self
.__needtopoll
or tries
> 3:
296 self
.__inputvar
.set(info
.i_port
)
298 self
.__spkvar
.set(info
.o_port
& SPEAKER
)
299 self
.__headvar
.set(info
.o_port
& HEADPHONE
)
300 self
.__linevar
.set(info
.o_port
& LINE_OUT
)
302 self
.__scalevar
.set(info
.o_gain
)
304 def __pushtodev(self
, event
=None):
305 info
= self
.__devctl
.getinfo()
306 info
.o_port
= self
.__spkvar
.get() + \
307 self
.__headvar
.get() + \
309 info
.i_port
= self
.__inputvar
.get()
310 info
.o_gain
= self
.__scalevar
.get()
311 self
.__devctl
.setinfo(info
)
313 def __getset(self
, var
, onvalue
):
314 if var
.get() == onvalue
:
320 def __none(self
, event
=None):
321 self
.__inputvar
.set(0)
324 def __mic(self
, event
=None):
325 self
.__getset
(self
.__inputvar
, MICROPHONE
)
327 def __linein(self
, event
=None):
328 self
.__getset
(self
.__inputvar
, LINE_IN
)
330 def __cd(self
, event
=None):
331 self
.__getset
(self
.__inputvar
, CD
)
333 def __speaker(self
, event
=None):
334 self
.__getset
(self
.__spkvar
, SPEAKER
)
336 def __headphones(self
, event
=None):
337 self
.__getset
(self
.__headvar
, HEADPHONE
)
339 def __lineout(self
, event
=None):
340 self
.__getset
(self
.__linevar
, LINE_OUT
)
342 def __volume(self
, event
=None):
347 self
.__tkroot
.mainloop()
352 def __init__(self
, master
, quitfunc
):
353 from Tkinter
import *
354 self
.__root
= root
= Toplevel(master
, class_
='Audiopy')
355 root
.protocol('WM_DELETE_WINDOW', self
.__withdraw
)
356 root
.title('Audiopy Help Window')
357 root
.iconname('Audiopy Help Window')
358 root
.bind('<Alt-q>', quitfunc
)
359 root
.bind('<Alt-Q>', quitfunc
)
360 root
.bind('<Alt-w>', self
.__withdraw
)
361 root
.bind('<Alt-W>', self
.__withdraw
)
363 # more elaborate help is available in the README file
364 readmefile
= os
.path
.join(sys
.path
[0], 'README')
368 fp
= open(readmefile
)
370 # wax the last page, it contains Emacs cruft
371 i
= string
.rfind(contents
, '\f')
373 contents
= string
.rstrip(contents
[:i
])
378 sys
.stderr
.write("Couldn't open audiopy's README, "
379 'using docstring instead.\n')
380 contents
= __doc__
% globals()
382 self
.__text
= text
= Text(root
, relief
=SUNKEN
,
384 text
.insert(0.0, contents
)
385 scrollbar
= Scrollbar(root
)
386 scrollbar
.pack(fill
=Y
, side
=RIGHT
)
387 text
.pack(fill
=BOTH
, expand
=YES
)
388 text
.configure(yscrollcommand
=(scrollbar
, 'set'))
389 scrollbar
.configure(command
=(text
, 'yview'))
391 def __withdraw(self
, event
=None):
392 self
.__root
.withdraw()
395 self
.__root
.deiconify()
400 def usage(code
, msg
=''):
401 print __doc__
% globals()
409 # Open up the audio control device and query for the current output
411 device
= sunaudiodev
.open('control')
413 if len(sys
.argv
) == 1:
415 w
= MainWindow(device
)
418 except KeyboardInterrupt:
422 # spec: LONG OPT, SHORT OPT, 0=input,1=output, MASK
423 options
= [('--microphone', '-m', 0, MICROPHONE
),
424 ('--linein', '-i', 0, LINE_IN
),
425 ('--headphones', '-p', 1, HEADPHONE
),
426 ('--speaker', '-s', 1, SPEAKER
),
427 ('--lineout', '-o', 1, LINE_OUT
),
429 # See the comment above about `CD'
431 options
.append(('--cd', '-c', 0, CD
))
435 info
= device
.getinfo()
436 # first get the existing values
438 while i
< len(sys
.argv
)-1:
441 if arg
in ('-h', '--help'):
444 elif arg
in ('-g', '--gain'):
445 gainspec
= '<missing>'
447 gainspec
= sys
.argv
[i
+1]
449 except (ValueError, IndexError):
450 usage(1, 'Bad gain specification: ' + gainspec
)
454 elif arg
in ('-v', '--version'):
456 audiopy -- a program to control the Solaris audio device.
457 Contact: Barry Warsaw
458 Email: bwarsaw@python.org
459 Version: %s''' % __version__
461 for long, short
, io
, mask
in options
:
462 if arg
in (long, short
):
465 info
.i_port
= info
.i_port ^ mask
467 info
.o_port
= info
.o_port ^ mask
471 if arg
[:len(long)+1] == long+'=':
472 val
= int(arg
[len(long)+1:])
473 elif arg
[:len(short
)+1] == short
+'=':
474 val
= int(arg
[len(short
)+1:])
476 usage(1, msg
='Invalid option: ' + arg
)
480 info
.i_port
= info
.i_port
& ~mask
482 info
.o_port
= info
.o_port
& ~mask
486 info
.i_port
= info
.i_port | mask
488 info
.o_port
= info
.o_port | mask
490 # else keep trying next option
492 usage(1, msg
='Invalid option: ' + arg
)
496 except sunaudiodev
.error
, (code
, msg
):
497 if code
<> errno
.EINVAL
:
503 if __name__
== '__main__':