#! /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. This program can be driven via the command line, and when done so, no window pops up. 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: --version -v Print the version number and exit. --help -h Print this message and exit. """ import sys import os import string import sunaudiodev from SUNAUDIODEV import * # Milliseconds between interrupt checks KEEPALIVE_TIMER = 500 __version__ = '0.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('', self.__none) root.bind('', 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('', self.__mic) root.bind('', 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('', self.__linein) root.bind('', 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('', self.__cd) root.bind('', 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('', self.__speaker) root.bind('', 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('', self.__headphones) root.bind('', 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('', self.__lineout) root.bind('', 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('', self.__quit) root.bind('', self.__quit) # # 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): # We have to poll because the device could have changed state and the # underlying module does not support the SIGPOLL notification # interface. info = self.__devctl.getinfo() # 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) 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() self.__devctl.setinfo(info) 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 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('', quitfunc) root.bind('', quitfunc) root.bind('', self.__withdraw) root.bind('', 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 = string.rfind(contents, '\f') if i > 0: contents = string.rstrip(contents[:i]) 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(msg='', code=1): 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 for arg in sys.argv[1:]: if arg in ('-h', '--help'): usage(code=0) # does not return 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(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(msg='Invalid option: ' + arg) # now set the values device.setinfo(info) device.close() if __name__ == '__main__': main()