cpython/Tools/audiopy/audiopy

508 lines
16 KiB
Plaintext
Raw Normal View History

1998-10-30 14:21:52 -04:00
#! /usr/bin/env python
"""audiopy -- a program to control the Solaris audio device.
1998-10-30 14:21:52 -04:00
1998-12-14 17:36:40 -04:00
Contact: Barry Warsaw
Email: bwarsaw@python.org
Version: %(__version__)s
1998-11-17 20:27:14 -04:00
1998-10-30 14:21:52 -04:00
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.
1998-10-30 14:21:52 -04:00
This program can be driven via the command line, and when done so, no window
pops up. Most options have the general form:
1998-10-30 14:21:52 -04:00
--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.
1998-10-30 14:21:52 -04:00
The list of devices and their short options are:
1998-10-30 14:21:52 -04:00
(input)
microphone -- m
linein -- i
cd -- c
(output)
headphones -- p
speaker -- s
lineout -- o
Other options are:
1998-10-30 14:21:52 -04:00
--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.
1998-10-30 14:21:52 -04:00
--help
-h
Print this message and exit.
"""
import sys
import os
import errno
1998-10-30 14:21:52 -04:00
import sunaudiodev
from SUNAUDIODEV import *
# Milliseconds between interrupt checks
KEEPALIVE_TIMER = 500
__version__ = '1.1'
1998-10-30 14:21:52 -04:00
class MainWindow:
def __init__(self, device):
from Tkinter import *
self.__helpwin = None
self.__devctl = device
info = device.getinfo()
#
self.__tkroot = tkroot = Tk(className='Audiopy')
1998-10-30 14:21:52 -04:00
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)
1998-10-30 14:21:52 -04:00
# now create the top level window
root = self.__root = Toplevel(tkroot, class_='Audiopy', menu=menubar)
1998-10-30 14:21:52 -04:00
root.protocol('WM_DELETE_WINDOW', self.__quit)
root.title('audiopy ' + __version__)
root.iconname('audiopy ' + __version__)
1998-10-30 14:21:52 -04:00
root.tk.createtimerhandler(KEEPALIVE_TIMER, self.__keepalive)
#
buttons = []
1998-10-30 14:21:52 -04:00
#
1998-10-30 19:45:41 -04:00
# where does input come from?
frame = Frame(root, bd=1, relief=RAISED)
frame.grid(row=1, column=0, sticky='NSEW')
1998-10-30 19:45:41 -04:00
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,
1998-10-30 19:45:41 -04:00
text='Microphone',
variable=self.__inputvar,
value=MICROPHONE,
1998-10-30 19:45:41 -04:00
command=self.__pushtodev,
underline=0)
btn.grid(row=1, column=1, sticky=W)
1998-10-30 19:45:41 -04:00
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,
1998-10-30 19:45:41 -04:00
text='Line In',
variable=self.__inputvar,
value=LINE_IN,
1998-10-30 19:45:41 -04:00
command=self.__pushtodev,
underline=5)
btn.grid(row=2, column=1, sticky=W)
1998-10-30 19:45:41 -04:00
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
1998-10-30 19:45:41 -04:00
#
# where does output go to?
frame = Frame(root, bd=1, relief=RAISED)
frame.grid(row=2, column=0, sticky='NSEW')
1998-10-30 19:45:41 -04:00
label = Label(frame, text='Output To:')
1998-10-30 14:21:52 -04:00
label.grid(row=0, column=0, sticky=E)
self.__spkvar = IntVar()
1998-10-30 19:45:41 -04:00
btn = Checkbutton(frame,
1998-10-30 14:21:52 -04:00
text='Speaker',
variable=self.__spkvar,
onvalue=SPEAKER,
1998-10-30 19:45:41 -04:00
command=self.__pushtodev,
1998-10-30 14:21:52 -04:00
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)
##
1998-10-30 14:21:52 -04:00
self.__headvar = IntVar()
1998-10-30 19:45:41 -04:00
btn = Checkbutton(frame,
1998-10-30 14:21:52 -04:00
text='Headphones',
variable=self.__headvar,
onvalue=HEADPHONE,
1998-10-30 19:45:41 -04:00
command=self.__pushtodev,
underline=4)
1998-10-30 14:21:52 -04:00
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)
##
1998-10-30 14:21:52 -04:00
self.__linevar = IntVar()
1998-10-30 19:45:41 -04:00
btn = Checkbutton(frame,
1998-10-30 14:21:52 -04:00
variable=self.__linevar,
onvalue=LINE_OUT,
1998-10-30 19:45:41 -04:00
text='Line Out',
command=self.__pushtodev,
1998-10-30 14:21:52 -04:00
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)
1998-10-30 19:45:41 -04:00
#
# 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)
#
1998-10-30 19:45:41 -04:00
# 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()
1998-10-30 14:21:52 -04:00
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
1998-12-14 17:36:40 -04:00
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()
1998-10-30 14:21:52 -04:00
def __keepalive(self):
# Exercise the Python interpreter regularly so keyboard interrupts get
# through.
self.__tkroot.tk.createtimerhandler(KEEPALIVE_TIMER, self.__keepalive)
1998-10-30 19:45:41 -04:00
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
1998-10-30 14:21:52 -04:00
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)
1998-10-30 14:21:52 -04:00
1998-10-30 19:45:41 -04:00
def __pushtodev(self, event=None):
1998-10-30 14:21:52 -04:00
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
1998-10-30 14:21:52 -04:00
def __getset(self, var, onvalue):
if var.get() == onvalue:
1998-10-30 14:21:52 -04:00
var.set(0)
else:
var.set(onvalue)
1998-10-30 19:45:41 -04:00
self.__pushtodev()
def __none(self, event=None):
self.__inputvar.set(0)
self.__pushtodev()
1998-10-30 19:45:41 -04:00
def __mic(self, event=None):
self.__getset(self.__inputvar, MICROPHONE)
1998-10-30 19:45:41 -04:00
def __linein(self, event=None):
self.__getset(self.__inputvar, LINE_IN)
1998-10-30 19:45:41 -04:00
def __cd(self, event=None):
self.__getset(self.__inputvar, CD)
1998-10-30 14:21:52 -04:00
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()
1998-10-30 14:21:52 -04:00
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()
1998-10-30 14:21:52 -04:00
def usage(code, msg=''):
print __doc__ % globals()
1998-11-17 23:32:43 -04:00
if msg:
print msg
1998-10-30 14:21:52 -04:00
sys.exit(code)
def main():
#
# Open up the audio control device and query for the current output
# device
device = sunaudiodev.open('control')
1998-10-30 14:21:52 -04:00
if len(sys.argv) == 1:
# GUI
w = MainWindow(device)
1998-10-30 19:45:41 -04:00
try:
w.start()
except KeyboardInterrupt:
pass
1998-10-30 14:21:52 -04:00
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]
1998-10-30 14:21:52 -04:00
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.
1998-12-14 17:36:40 -04:00
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
1998-10-30 14:21:52 -04:00
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:])
1998-10-30 14:21:52 -04:00
except ValueError:
usage(1, msg='Invalid option: ' + arg)
# does not return
if val == 0:
if io == 0:
info.i_port = info.i_port & ~mask
1998-10-30 14:21:52 -04:00
else:
info.o_port = info.o_port & ~mask
break
elif val == 1:
if io == 0:
info.i_port = info.i_port | mask
1998-10-30 14:21:52 -04:00
else:
info.o_port = info.o_port | mask
break
# else keep trying next option
1998-10-30 14:21:52 -04:00
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()
1998-10-30 14:21:52 -04:00
if __name__ == '__main__':
main()