mirror of
https://github.com/python/cpython.git
synced 2025-10-29 01:22:59 +00:00
(with one small bugfix in bgen/bgen/scantools.py) This replaces string module functions with string methods for the stuff in the Tools directory. Several uses of string.letters etc. are still remaining.
507 lines
16 KiB
Python
Executable file
507 lines
16 KiB
Python
Executable file
#! /usr/bin/env python
|
||
|
||
"""audiopy -- a program to control the Solaris audio device.
|
||
|
||
Contact: Barry Warsaw
|
||
Email: bwarsaw@python.org
|
||
Version: %(__version__)s
|
||
|
||
When no arguments are given, this pops up a graphical window which lets you
|
||
choose the audio input and output devices, and set the output volume.
|
||
|
||
This program can be driven via the command line, and when done so, no window
|
||
pops up. Most options have the general form:
|
||
|
||
--device[={0,1}]
|
||
-d[={0,1}]
|
||
Set the I/O device. With no value, it toggles the specified device.
|
||
With a value, 0 turns the device off and 1 turns the device on.
|
||
|
||
The list of devices and their short options are:
|
||
|
||
(input)
|
||
microphone -- m
|
||
linein -- i
|
||
cd -- c
|
||
|
||
(output)
|
||
headphones -- p
|
||
speaker -- s
|
||
lineout -- o
|
||
|
||
Other options are:
|
||
|
||
--gain volume
|
||
-g volume
|
||
Sets the output gain to the specified volume, which must be an integer
|
||
in the range [%(MIN_GAIN)s..%(MAX_GAIN)s]
|
||
|
||
--version
|
||
-v
|
||
Print the version number and exit.
|
||
|
||
--help
|
||
-h
|
||
Print this message and exit.
|
||
"""
|
||
|
||
import sys
|
||
import os
|
||
import errno
|
||
import sunaudiodev
|
||
from SUNAUDIODEV import *
|
||
|
||
# Milliseconds between interrupt checks
|
||
KEEPALIVE_TIMER = 500
|
||
|
||
__version__ = '1.1'
|
||
|
||
|
||
|
||
class MainWindow:
|
||
def __init__(self, device):
|
||
from Tkinter import *
|
||
self.__helpwin = None
|
||
self.__devctl = device
|
||
info = device.getinfo()
|
||
#
|
||
self.__tkroot = tkroot = Tk(className='Audiopy')
|
||
tkroot.withdraw()
|
||
# create the menubar
|
||
menubar = Menu(tkroot)
|
||
filemenu = Menu(menubar, tearoff=0)
|
||
filemenu.add_command(label='Quit',
|
||
command=self.__quit,
|
||
accelerator='Alt-Q',
|
||
underline=0)
|
||
helpmenu = Menu(menubar, name='help', tearoff=0)
|
||
helpmenu.add_command(label='About Audiopy...',
|
||
command=self.__popup_about,
|
||
underline=0)
|
||
helpmenu.add_command(label='Help...',
|
||
command=self.__popup_using,
|
||
underline=0)
|
||
menubar.add_cascade(label='File',
|
||
menu=filemenu,
|
||
underline=0)
|
||
menubar.add_cascade(label='Help',
|
||
menu=helpmenu,
|
||
underline=0)
|
||
# now create the top level window
|
||
root = self.__root = Toplevel(tkroot, class_='Audiopy', menu=menubar)
|
||
root.protocol('WM_DELETE_WINDOW', self.__quit)
|
||
root.title('audiopy ' + __version__)
|
||
root.iconname('audiopy ' + __version__)
|
||
root.tk.createtimerhandler(KEEPALIVE_TIMER, self.__keepalive)
|
||
#
|
||
buttons = []
|
||
#
|
||
# where does input come from?
|
||
frame = Frame(root, bd=1, relief=RAISED)
|
||
frame.grid(row=1, column=0, sticky='NSEW')
|
||
label = Label(frame, text='Input From:')
|
||
label.grid(row=0, column=0, sticky=E)
|
||
self.__inputvar = IntVar()
|
||
##
|
||
btn = Radiobutton(frame,
|
||
text='None',
|
||
variable=self.__inputvar,
|
||
value=0,
|
||
command=self.__pushtodev,
|
||
underline=0)
|
||
btn.grid(row=0, column=1, sticky=W)
|
||
root.bind('<Alt-n>', self.__none)
|
||
root.bind('<Alt-N>', self.__none)
|
||
if not info.i_avail_ports & MICROPHONE:
|
||
btn.configure(state=DISABLED)
|
||
buttons.append(btn)
|
||
##
|
||
btn = Radiobutton(frame,
|
||
text='Microphone',
|
||
variable=self.__inputvar,
|
||
value=MICROPHONE,
|
||
command=self.__pushtodev,
|
||
underline=0)
|
||
btn.grid(row=1, column=1, sticky=W)
|
||
root.bind('<Alt-m>', self.__mic)
|
||
root.bind('<Alt-M>', self.__mic)
|
||
if not info.i_avail_ports & MICROPHONE:
|
||
btn.configure(state=DISABLED)
|
||
buttons.append(btn)
|
||
##
|
||
btn = Radiobutton(frame,
|
||
text='Line In',
|
||
variable=self.__inputvar,
|
||
value=LINE_IN,
|
||
command=self.__pushtodev,
|
||
underline=5)
|
||
btn.grid(row=2, column=1, sticky=W)
|
||
root.bind('<Alt-i>', self.__linein)
|
||
root.bind('<Alt-I>', self.__linein)
|
||
if not info.i_avail_ports & LINE_IN:
|
||
btn.configure(state=DISABLED)
|
||
buttons.append(btn)
|
||
## if SUNAUDIODEV was built on an older version of Solaris, the CD
|
||
## input device won't exist
|
||
try:
|
||
btn = Radiobutton(frame,
|
||
text='CD',
|
||
variable=self.__inputvar,
|
||
value=CD,
|
||
command=self.__pushtodev,
|
||
underline=0)
|
||
btn.grid(row=3, column=1, sticky=W)
|
||
root.bind('<Alt-c>', self.__cd)
|
||
root.bind('<Alt-C>', self.__cd)
|
||
if not info.i_avail_ports & CD:
|
||
btn.configure(state=DISABLED)
|
||
buttons.append(btn)
|
||
except NameError:
|
||
pass
|
||
#
|
||
# where does output go to?
|
||
frame = Frame(root, bd=1, relief=RAISED)
|
||
frame.grid(row=2, column=0, sticky='NSEW')
|
||
label = Label(frame, text='Output To:')
|
||
label.grid(row=0, column=0, sticky=E)
|
||
self.__spkvar = IntVar()
|
||
btn = Checkbutton(frame,
|
||
text='Speaker',
|
||
variable=self.__spkvar,
|
||
onvalue=SPEAKER,
|
||
command=self.__pushtodev,
|
||
underline=0)
|
||
btn.grid(row=0, column=1, sticky=W)
|
||
root.bind('<Alt-s>', self.__speaker)
|
||
root.bind('<Alt-S>', self.__speaker)
|
||
if not info.o_avail_ports & SPEAKER:
|
||
btn.configure(state=DISABLED)
|
||
buttons.append(btn)
|
||
##
|
||
self.__headvar = IntVar()
|
||
btn = Checkbutton(frame,
|
||
text='Headphones',
|
||
variable=self.__headvar,
|
||
onvalue=HEADPHONE,
|
||
command=self.__pushtodev,
|
||
underline=4)
|
||
btn.grid(row=1, column=1, sticky=W)
|
||
root.bind('<Alt-p>', self.__headphones)
|
||
root.bind('<Alt-P>', self.__headphones)
|
||
if not info.o_avail_ports & HEADPHONE:
|
||
btn.configure(state=DISABLED)
|
||
buttons.append(btn)
|
||
##
|
||
self.__linevar = IntVar()
|
||
btn = Checkbutton(frame,
|
||
variable=self.__linevar,
|
||
onvalue=LINE_OUT,
|
||
text='Line Out',
|
||
command=self.__pushtodev,
|
||
underline=0)
|
||
btn.grid(row=2, column=1, sticky=W)
|
||
root.bind('<Alt-l>', self.__lineout)
|
||
root.bind('<Alt-L>', self.__lineout)
|
||
if not info.o_avail_ports & LINE_OUT:
|
||
btn.configure(state=DISABLED)
|
||
buttons.append(btn)
|
||
#
|
||
# Fix up widths
|
||
widest = 0
|
||
for b in buttons:
|
||
width = b['width']
|
||
if width > widest:
|
||
widest = width
|
||
for b in buttons:
|
||
b.configure(width=widest)
|
||
# root bindings
|
||
root.bind('<Alt-q>', self.__quit)
|
||
root.bind('<Alt-Q>', self.__quit)
|
||
#
|
||
# Volume
|
||
frame = Frame(root, bd=1, relief=RAISED)
|
||
frame.grid(row=3, column=0, sticky='NSEW')
|
||
label = Label(frame, text='Output Volume:')
|
||
label.grid(row=0, column=0, sticky=W)
|
||
self.__scalevar = IntVar()
|
||
self.__scale = Scale(frame,
|
||
orient=HORIZONTAL,
|
||
from_=MIN_GAIN,
|
||
to=MAX_GAIN,
|
||
length=200,
|
||
variable=self.__scalevar,
|
||
command=self.__volume)
|
||
self.__scale.grid(row=1, column=0, sticky=EW)
|
||
#
|
||
# do we need to poll for changes?
|
||
self.__needtopoll = 1
|
||
try:
|
||
fd = self.__devctl.fileno()
|
||
self.__needtopoll = 0
|
||
except AttributeError:
|
||
pass
|
||
else:
|
||
import fcntl
|
||
import signal
|
||
import STROPTS
|
||
# set up the signal handler
|
||
signal.signal(signal.SIGPOLL, self.__update)
|
||
fcntl.ioctl(fd, STROPTS.I_SETSIG, STROPTS.S_MSG)
|
||
self.__update()
|
||
|
||
def __quit(self, event=None):
|
||
self.__devctl.close()
|
||
self.__root.quit()
|
||
|
||
def __popup_about(self, event=None):
|
||
import tkMessageBox
|
||
tkMessageBox.showinfo('About Audiopy ' + __version__,
|
||
'''\
|
||
Audiopy %s
|
||
Control the Solaris audio device
|
||
|
||
For information
|
||
Contact: Barry A. Warsaw
|
||
Email: bwarsaw@python.org''' % __version__)
|
||
|
||
def __popup_using(self, event=None):
|
||
if not self.__helpwin:
|
||
self.__helpwin = Helpwin(self.__tkroot, self.__quit)
|
||
self.__helpwin.deiconify()
|
||
|
||
|
||
def __keepalive(self):
|
||
# Exercise the Python interpreter regularly so keyboard interrupts get
|
||
# through.
|
||
self.__tkroot.tk.createtimerhandler(KEEPALIVE_TIMER, self.__keepalive)
|
||
if self.__needtopoll:
|
||
self.__update()
|
||
|
||
def __update(self, num=None, frame=None):
|
||
# It's possible (although I have never seen it) to get an interrupted
|
||
# system call during the getinfo() call. If so, and we're polling,
|
||
# don't sweat it because we'll come around again later. Otherwise,
|
||
# we'll give it a couple of tries and then give up until next time.
|
||
tries = 0
|
||
while 1:
|
||
try:
|
||
info = self.__devctl.getinfo()
|
||
break
|
||
except sunaudiodev.error:
|
||
if self.__needtopoll or tries > 3:
|
||
return
|
||
tries = tries + 1
|
||
# input
|
||
self.__inputvar.set(info.i_port)
|
||
# output
|
||
self.__spkvar.set(info.o_port & SPEAKER)
|
||
self.__headvar.set(info.o_port & HEADPHONE)
|
||
self.__linevar.set(info.o_port & LINE_OUT)
|
||
# volume
|
||
self.__scalevar.set(info.o_gain)
|
||
|
||
def __pushtodev(self, event=None):
|
||
info = self.__devctl.getinfo()
|
||
info.o_port = self.__spkvar.get() + \
|
||
self.__headvar.get() + \
|
||
self.__linevar.get()
|
||
info.i_port = self.__inputvar.get()
|
||
info.o_gain = self.__scalevar.get()
|
||
try:
|
||
self.__devctl.setinfo(info)
|
||
except sunaudiodev.error, msg:
|
||
# TBD: what to do? it's probably temporary.
|
||
pass
|
||
|
||
def __getset(self, var, onvalue):
|
||
if var.get() == onvalue:
|
||
var.set(0)
|
||
else:
|
||
var.set(onvalue)
|
||
self.__pushtodev()
|
||
|
||
def __none(self, event=None):
|
||
self.__inputvar.set(0)
|
||
self.__pushtodev()
|
||
|
||
def __mic(self, event=None):
|
||
self.__getset(self.__inputvar, MICROPHONE)
|
||
|
||
def __linein(self, event=None):
|
||
self.__getset(self.__inputvar, LINE_IN)
|
||
|
||
def __cd(self, event=None):
|
||
self.__getset(self.__inputvar, CD)
|
||
|
||
def __speaker(self, event=None):
|
||
self.__getset(self.__spkvar, SPEAKER)
|
||
|
||
def __headphones(self, event=None):
|
||
self.__getset(self.__headvar, HEADPHONE)
|
||
|
||
def __lineout(self, event=None):
|
||
self.__getset(self.__linevar, LINE_OUT)
|
||
|
||
def __volume(self, event=None):
|
||
self.__pushtodev()
|
||
|
||
def start(self):
|
||
self.__keepalive()
|
||
self.__tkroot.mainloop()
|
||
|
||
|
||
|
||
class Helpwin:
|
||
def __init__(self, master, quitfunc):
|
||
from Tkinter import *
|
||
self.__root = root = Toplevel(master, class_='Audiopy')
|
||
root.protocol('WM_DELETE_WINDOW', self.__withdraw)
|
||
root.title('Audiopy Help Window')
|
||
root.iconname('Audiopy Help Window')
|
||
root.bind('<Alt-q>', quitfunc)
|
||
root.bind('<Alt-Q>', quitfunc)
|
||
root.bind('<Alt-w>', self.__withdraw)
|
||
root.bind('<Alt-W>', self.__withdraw)
|
||
|
||
# more elaborate help is available in the README file
|
||
readmefile = os.path.join(sys.path[0], 'README')
|
||
try:
|
||
fp = None
|
||
try:
|
||
fp = open(readmefile)
|
||
contents = fp.read()
|
||
# wax the last page, it contains Emacs cruft
|
||
i = contents.rfind('\f')
|
||
if i > 0:
|
||
contents = contents[:i].rstrip()
|
||
finally:
|
||
if fp:
|
||
fp.close()
|
||
except IOError:
|
||
sys.stderr.write("Couldn't open audiopy's README, "
|
||
'using docstring instead.\n')
|
||
contents = __doc__ % globals()
|
||
|
||
self.__text = text = Text(root, relief=SUNKEN,
|
||
width=80, height=24)
|
||
text.insert(0.0, contents)
|
||
scrollbar = Scrollbar(root)
|
||
scrollbar.pack(fill=Y, side=RIGHT)
|
||
text.pack(fill=BOTH, expand=YES)
|
||
text.configure(yscrollcommand=(scrollbar, 'set'))
|
||
scrollbar.configure(command=(text, 'yview'))
|
||
|
||
def __withdraw(self, event=None):
|
||
self.__root.withdraw()
|
||
|
||
def deiconify(self):
|
||
self.__root.deiconify()
|
||
|
||
|
||
|
||
|
||
def usage(code, msg=''):
|
||
print __doc__ % globals()
|
||
if msg:
|
||
print msg
|
||
sys.exit(code)
|
||
|
||
|
||
def main():
|
||
#
|
||
# Open up the audio control device and query for the current output
|
||
# device
|
||
device = sunaudiodev.open('control')
|
||
|
||
if len(sys.argv) == 1:
|
||
# GUI
|
||
w = MainWindow(device)
|
||
try:
|
||
w.start()
|
||
except KeyboardInterrupt:
|
||
pass
|
||
return
|
||
|
||
# spec: LONG OPT, SHORT OPT, 0=input,1=output, MASK
|
||
options = [('--microphone', '-m', 0, MICROPHONE),
|
||
('--linein', '-i', 0, LINE_IN),
|
||
('--headphones', '-p', 1, HEADPHONE),
|
||
('--speaker', '-s', 1, SPEAKER),
|
||
('--lineout', '-o', 1, LINE_OUT),
|
||
]
|
||
# See the comment above about `CD'
|
||
try:
|
||
options.append(('--cd', '-c', 0, CD))
|
||
except NameError:
|
||
pass
|
||
|
||
info = device.getinfo()
|
||
# first get the existing values
|
||
i = 0
|
||
while i < len(sys.argv)-1:
|
||
i = i + 1
|
||
arg = sys.argv[i]
|
||
if arg in ('-h', '--help'):
|
||
usage(0)
|
||
# does not return
|
||
elif arg in ('-g', '--gain'):
|
||
gainspec = '<missing>'
|
||
try:
|
||
gainspec = sys.argv[i+1]
|
||
gain = int(gainspec)
|
||
except (ValueError, IndexError):
|
||
usage(1, 'Bad gain specification: ' + gainspec)
|
||
info.o_gain = gain
|
||
i = i + 1
|
||
continue
|
||
elif arg in ('-v', '--version'):
|
||
print '''\
|
||
audiopy -- a program to control the Solaris audio device.
|
||
Contact: Barry Warsaw
|
||
Email: bwarsaw@python.org
|
||
Version: %s''' % __version__
|
||
sys.exit(0)
|
||
for long, short, io, mask in options:
|
||
if arg in (long, short):
|
||
# toggle the option
|
||
if io == 0:
|
||
info.i_port = info.i_port ^ mask
|
||
else:
|
||
info.o_port = info.o_port ^ mask
|
||
break
|
||
val = None
|
||
try:
|
||
if arg[:len(long)+1] == long+'=':
|
||
val = int(arg[len(long)+1:])
|
||
elif arg[:len(short)+1] == short+'=':
|
||
val = int(arg[len(short)+1:])
|
||
except ValueError:
|
||
usage(1, msg='Invalid option: ' + arg)
|
||
# does not return
|
||
if val == 0:
|
||
if io == 0:
|
||
info.i_port = info.i_port & ~mask
|
||
else:
|
||
info.o_port = info.o_port & ~mask
|
||
break
|
||
elif val == 1:
|
||
if io == 0:
|
||
info.i_port = info.i_port | mask
|
||
else:
|
||
info.o_port = info.o_port | mask
|
||
break
|
||
# else keep trying next option
|
||
else:
|
||
usage(1, msg='Invalid option: ' + arg)
|
||
# now set the values
|
||
try:
|
||
device.setinfo(info)
|
||
except sunaudiodev.error, (code, msg):
|
||
if code <> errno.EINVAL:
|
||
raise
|
||
device.close()
|
||
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|