diff options
-rwxr-xr-x | Demo/stdwin/jukebox.py | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/Demo/stdwin/jukebox.py b/Demo/stdwin/jukebox.py new file mode 100755 index 0000000..fba5d71 --- /dev/null +++ b/Demo/stdwin/jukebox.py @@ -0,0 +1,314 @@ +#! /ufs/guido/bin/sgi/python + +# XXX This file is being hacked -- some functionality has been taken out! + +# JUKEBOX: browse directories full of sampled sound files. +# +# One or more "list windows" display the files and subdirectories of +# the arguments. Double-clicking on a subdirectory opens a new window +# displaying its contents (and so on recursively). Double clicking +# on a file plays it as a sound file (assuming it is one). +# +# Playing is asynchronous: the application keeps listening to events +# while the sample is playing, so you can change the volume (gain) +# during playing, cancel playing or start a new sample right away. +# +# The control window displays the current output gain and a primitive +# "stop button" to cancel the current play request. +# +# Sound files must currently be in Dik Winter's compressed Mac format. +# Since decompression is costly, decompressed samples are saved in +# /usr/tmp/@j* until the application is left. The files are read +# afresh each time, though. + +import commands +import getopt +import os +import rand +import stdwin +from stdwinevents import * +import string +import sys +import tempfile + +from WindowParent import WindowParent +from HVSplit import VSplit +from Buttons import PushButton +from Sliders import ComplexSlider + +# Pathnames + +DEF_DB = '/usr/local/sounds/aiff' # Default directory of sounds +SOX = '/usr/local/sox' # Sound format conversion program +SFPLAY = '/usr/sbin/sfplay' # Sound playing program + + +# Global variables + +class struct(): pass # Class to define featureless structures + +G = struct() # Holds writable global variables + + +# Main program + +def main(): + G.synchronous = 0 # If set, use synchronous audio.write() + G.debug = 0 # If set, print debug messages + G.busy = 0 # Set while asynchronous playing is active + G.windows = [] # List of open windows, except control + G.mode = '' # File type (default any that sfplay knows) + G.rate = 0 # Sampling rate (default " " " ") + G.tempprefix = tempfile.mktemp() + # + try: + optlist, args = getopt.getopt(sys.argv[1:], 'dr:st:') + except getopt.error, msg: + sys.stdout = sys.stderr + print msg + print 'usage: jukebox [-d] [-s] [-t type] [-r rate]' + print ' -d debugging' + print ' -s synchronous playing' + print ' -t type file type' + print ' -r rate sampling rate' + sys.exit(2) + # + for optname, optarg in optlist: + if optname == '-d': + G.debug = 1 + elif optname == '-r': + G.rate = int(eval(optarg)) + elif optname == '-s': + G.synchronous = 1 + elif optname == '-t': + G.mode = optarg + # + if not args: + args = [DEF_DB] + # + G.cw = opencontrolwindow() + for dirname in args: + G.windows.append(openlistwindow(dirname)) + # + # + try: + maineventloop() + finally: + clearcache() + killchild() + +def maineventloop(): + mouse_events = WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP + while G.windows: + type, w, detail = event = stdwin.getevent() + if w == G.cw.win: + if type == WE_CLOSE: + return + if type == WE_TIMER: + checkchild() + if G.busy: + G.cw.win.settimer(1) + else: + G.cw.dispatch(event) + else: + if type == WE_DRAW: + w.drawproc(w, detail) + elif type in mouse_events: + w.mouse(w, type, detail) + elif type == WE_CLOSE: + w.close(w) + del w, event + else: + if G.debug: print type, w, detail + +def checkchild(): + if G.busy: + waitchild(1) + +def killchild(): + if G.busy: + os.kill(G.busy, 9) + waitchild(0) + +def waitchild(options): + pid, sts = os.wait(G.busy, options) + if pid == G.busy: + G.busy = 0 + G.stop.enable(0) + + +# Control window -- to set gain and cancel play operations in progress + +def opencontrolwindow(): + stdwin.setdefscrollbars(0, 0) + cw = WindowParent().create('Jukebox', (0, 0)) + v = VSplit().create(cw) + # + stop = PushButton().definetext(v, 'Stop') + stop.hook = stop_hook + stop.enable(0) + G.stop = stop + # + cw.realize() + return cw + +def stop_hook(self): + killchild() + + +# List windows -- to display list of files and subdirectories + +def openlistwindow(dirname): + list = os.listdir(dirname) + list.sort() + i = 0 + while i < len(list): + if list[i] == '.' or list[i] == '..': + del list[i] + else: + i = i+1 + for i in range(len(list)): + name = list[i] + if os.path.isdir(os.path.join(dirname, name)): + list[i] = list[i] + '/' + width = maxwidth(list) + # width = width + stdwin.textwidth(' ') # XXX X11 stdwin bug workaround + height = len(list) * stdwin.lineheight() + stdwin.setdefwinsize(width, min(height, 500)) + stdwin.setdefscrollbars(0, 1) + w = stdwin.open(dirname) + stdwin.setdefwinsize(0, 0) + w.setdocsize(width, height) + w.drawproc = drawlistwindow + w.mouse = mouselistwindow + w.close = closelistwindow + w.dirname = dirname + w.list = list + w.selected = -1 + return w + +def maxwidth(list): + width = 1 + for name in list: + w = stdwin.textwidth(name) + if w > width: width = w + return width + +def drawlistwindow(w, area): +## (left, top), (right, bottom) = area + d = w.begindrawing() + d.erase((0, 0), (1000, 10000)) + lh = d.lineheight() + h, v = 0, 0 + for name in w.list: + d.text((h, v), name) + v = v + lh + showselection(w, d) + d.close() + +def hideselection(w, d): + if w.selected >= 0: + invertselection(w, d) + +def showselection(w, d): + if w.selected >= 0: + invertselection(w, d) + +def invertselection(w, d): + lh = d.lineheight() + h1, v1 = p1 = 0, w.selected*lh + h2, v2 = p2 = 1000, v1 + lh + d.invert(p1, p2) + +def mouselistwindow(w, type, detail): + (h, v), clicks, button = detail[:3] + d = w.begindrawing() + lh = d.lineheight() + if 0 <= v < lh*len(w.list): + i = v / lh + else: + i = -1 + if w.selected <> i: + hideselection(w, d) + w.selected = i + showselection(w, d) + d.close() + if type == WE_MOUSE_DOWN and clicks >= 2 and i >= 0: + setcursors('watch') + name = os.path.join(w.dirname, w.list[i]) + if name[-1:] == '/': + if clicks == 2: + G.windows.append(openlistwindow(name[:-1])) + else: + playfile(name) + setcursors('cross') + +def closelistwindow(w): + remove(G.windows, w) + +def remove(list, item): + for i in range(len(list)): + if list[i] == item: + del list[i] + break + +def setcursors(cursor): + for w in G.windows: + w.setwincursor(cursor) + G.cw.win.setwincursor(cursor) + + +# Playing tools + +cache = {} + +def clearcache(): + for x in cache.keys(): + try: + sts = os.system('rm -f ' + cache[x]) + if sts: + print cmd + print 'Exit status', sts + except: + print cmd + print 'Exception?!' + del cache[x] + +def playfile(name): + killchild() + if G.mode in ('', 'au', 'aiff'): + tempname = name + elif cache.has_key(name): + tempname = cache[name] + else: + tempname = G.tempprefix + `rand.rand()` + '.aiff' + cmd = SOX + if G.mode <> '' and G.mode <> 'sox': + cmd = cmd + ' -t ' + G.mode + cmd = cmd + ' ' + commands.mkarg(name) + cmd = cmd + ' -t aiff' + if G.rate: + cmd = cmd + ' -r ' + `G.rate` + cmd = cmd + ' ' + tempname + if G.debug: print cmd + sts = os.system(cmd) + if sts: + print cmd + print 'Exit status', sts + stdwin.fleep() + return + cache[name] = tempname + pid = os.fork() + if pid == 0: + # Child + os.exec(SFPLAY, [SFPLAY, '-r', tempname]) + # NOTREACHED + # Parent + if G.synchronous: + sts = os.wait(pid, 0) + else: + G.busy = pid + G.stop.enable(1) + G.cw.win.settimer(1) + +main() |