home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyos2bin.zip / Demo / sgi / audio_stdwin / jukebox.py next >
Text File  |  1996-11-27  |  7KB  |  322 lines

  1. #! /usr/bin/env python
  2.  
  3. # JUKEBOX: browse directories full of sampled sound files.
  4. #
  5. # One or more "list windows" display the files and subdirectories of
  6. # the arguments.  Double-clicking on a subdirectory opens a new window
  7. # displaying its contents (and so on recursively).  Double clicking
  8. # on a file plays it as a sound file (assuming it is one).
  9. #
  10. # Playing is asynchronous: the application keeps listening to events
  11. # while the sample is playing, so you can change the volume (gain)
  12. # during playing, cancel playing or start a new sample right away.
  13. #
  14. # The control window displays the current output gain and a primitive
  15. # "stop button" to cancel the current play request.
  16. #
  17. # Sound files must currently be in Dik Winter's compressed Mac format.
  18. # Since decompression is costly, decompressed samples are saved in
  19. # /usr/tmp/@j* until the application is left.  The files are read
  20. # afresh each time, though.
  21.  
  22. import audio
  23. import sunaudio
  24. import commands
  25. import getopt
  26. import path
  27. import posix
  28. import rand
  29. import stdwin
  30. from stdwinevents import *
  31. import string
  32. import sys
  33.  
  34. from WindowParent import WindowParent
  35. from HVSplit import VSplit
  36. from Buttons import PushButton
  37. from Sliders import ComplexSlider
  38.  
  39. # Pathnames
  40.  
  41. HOME_BIN_SGI = '/ufs/guido/bin/sgi/'    # Directory where macsound2sgi lives
  42. DEF_DB = '/ufs/dik/sounds/Mac/HCOM'    # Default directory of sounds
  43.  
  44.  
  45. # Global variables
  46.  
  47. class struct: pass        # Class to define featureless structures
  48.  
  49. G = struct()            # Holds writable global variables
  50.  
  51.  
  52. # Main program
  53.  
  54. def main():
  55.     G.synchronous = 0    # If set, use synchronous audio.write()
  56.     G.debug = 0        # If set, print debug messages
  57.     G.gain = 75        # Output gain
  58.     G.rate = 3        # Sampling rate
  59.     G.busy = 0        # Set while asynchronous playing is active
  60.     G.windows = []        # List of open windows (except control)
  61.     G.mode = 'mac'        # Macintosh mode
  62.     G.tempprefix = '/usr/tmp/@j' + `rand.rand()` + '-'
  63.     #
  64.     optlist, args = getopt.getopt(sys.argv[1:], 'dg:r:sSa')
  65.     for optname, optarg in optlist:
  66.         if   optname == '-d':
  67.             G.debug = 1
  68.         elif optname == '-g':
  69.             G.gain = string.atoi(optarg)
  70.             if not (0 < G.gain < 256):
  71.                 raise optarg.error, '-g gain out of range'
  72.         elif optname == '-r':
  73.             G.rate = string.atoi(optarg)
  74.             if not (1 <= G.rate <= 3):
  75.                 raise optarg.error, '-r rate out of range'
  76.         elif optname == '-s':
  77.             G.synchronous = 1
  78.         elif optname == '-S':
  79.             G.mode = 'sgi'
  80.         elif optname == '-a':
  81.             G.mode = 'sun'
  82.     #
  83.     if not args:
  84.         args = [DEF_DB]
  85.     #
  86.     G.cw = opencontrolwindow()
  87.     for dirname in args:
  88.         G.windows.append(openlistwindow(dirname))
  89.     #
  90.     #
  91.     savegain = audio.getoutgain()
  92.     try:
  93.         # Initialize stdaudio
  94.         audio.setoutgain(0)
  95.         audio.start_playing('')
  96.         dummy = audio.wait_playing()
  97.         audio.setoutgain(0)
  98.         maineventloop()
  99.     finally:
  100.         audio.setoutgain(savegain)
  101.         audio.done()
  102.         clearcache()
  103.  
  104. def maineventloop():
  105.     mouse_events = WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP
  106.     while G.windows:
  107.         type, w, detail = event = stdwin.getevent()
  108.         if w == G.cw.win:
  109.             if type == WE_CLOSE:
  110.                 return
  111.             G.cw.dispatch(event)
  112.         else:
  113.             if type == WE_DRAW:
  114.                 w.drawproc(w, detail)
  115.             elif type in mouse_events:
  116.                 w.mouse(w, type, detail)
  117.             elif type == WE_CLOSE:
  118.                 w.close(w)
  119.                 del w, event
  120.             else:
  121.                 if G.debug: print type, w, detail
  122.  
  123. # Control window -- to set gain and cancel play operations in progress
  124.  
  125. def opencontrolwindow():
  126.     cw = WindowParent().create('Jukebox', (0, 0))
  127.     v = VSplit().create(cw)
  128.     #
  129.     gain = ComplexSlider().define(v)
  130.     gain.setminvalmax(0, G.gain, 255)
  131.     gain.settexts('  ', '  ')
  132.     gain.sethook(gain_setval_hook)
  133.     #
  134.     stop = PushButton().definetext(v, 'Stop')
  135.     stop.hook = stop_hook
  136.     #
  137.     cw.realize()
  138.     return cw
  139.  
  140. def gain_setval_hook(self):
  141.     G.gain = self.val
  142.     if G.busy: audio.setoutgain(G.gain)
  143.  
  144. def stop_hook(self):
  145.     if G.busy:
  146.         audio.setoutgain(0)
  147.         dummy = audio.stop_playing()
  148.         G.busy = 0
  149.  
  150.  
  151. # List windows -- to display list of files and subdirectories
  152.  
  153. def openlistwindow(dirname):
  154.     list = posix.listdir(dirname)
  155.     list.sort()
  156.     i = 0
  157.     while i < len(list):
  158.         if list[i] == '.' or list[i] == '..':
  159.             del list[i]
  160.         else:
  161.             i = i+1
  162.     for i in range(len(list)):
  163.         name = list[i]
  164.         if path.isdir(path.join(dirname, name)):
  165.             list[i] = list[i] + '/'
  166.     width = maxwidth(list)
  167.     width = width + stdwin.textwidth(' ')    # XXX X11 stdwin bug workaround
  168.     height = len(list) * stdwin.lineheight()
  169.     stdwin.setdefwinsize(width, min(height, 500))
  170.     w = stdwin.open(dirname)
  171.     stdwin.setdefwinsize(0, 0)
  172.     w.setdocsize(width, height)
  173.     w.drawproc = drawlistwindow
  174.     w.mouse = mouselistwindow
  175.     w.close = closelistwindow
  176.     w.dirname = dirname
  177.     w.list = list
  178.     w.selected = -1
  179.     return w
  180.  
  181. def maxwidth(list):
  182.     width = 1
  183.     for name in list:
  184.         w = stdwin.textwidth(name)
  185.         if w > width: width = w
  186.     return width
  187.  
  188. def drawlistwindow(w, area):
  189.     d = w.begindrawing()
  190.     d.erase((0, 0), (1000, 10000))
  191.     lh = d.lineheight()
  192.     h, v = 0, 0
  193.     for name in w.list:
  194.         d.text((h, v), name)
  195.         v = v + lh
  196.     showselection(w, d)
  197.  
  198. def hideselection(w, d):
  199.     if w.selected >= 0:
  200.         invertselection(w, d)
  201.  
  202. def showselection(w, d):
  203.     if w.selected >= 0:
  204.         invertselection(w, d)
  205.  
  206. def invertselection(w, d):
  207.     lh = d.lineheight()
  208.     h1, v1 = p1 = 0, w.selected*lh
  209.     h2, v2 = p2 = 1000, v1 + lh
  210.     d.invert(p1, p2)
  211.  
  212. def mouselistwindow(w, type, detail):
  213.     (h, v), clicks, button = detail[:3]
  214.     d = w.begindrawing()
  215.     lh = d.lineheight()
  216.     if 0 <= v < lh*len(w.list):
  217.         i = v / lh
  218.     else:
  219.         i = -1
  220.     if w.selected <> i:
  221.         hideselection(w, d)
  222.         w.selected = i
  223.         showselection(w, d)
  224.     if type == WE_MOUSE_DOWN and clicks >= 2 and i >= 0:
  225.         name = path.join(w.dirname, w.list[i])
  226.         if name[-1:] == '/':
  227.             if clicks == 2:
  228.                 G.windows.append(openlistwindow(name[:-1]))
  229.         else:
  230.             playfile(name)
  231.  
  232. def closelistwindow(w):
  233.     remove(G.windows, w)
  234.  
  235. def remove(list, item):
  236.     for i in range(len(list)):
  237.         if list[i] == item:
  238.             del list[i]
  239.             break
  240.  
  241.  
  242. # Playing tools
  243.  
  244. cache = {}
  245.  
  246. def clearcache():
  247.     for x in cache.keys():
  248.         try:
  249.             sts = posix.system('rm -f ' + cache[x])
  250.             if sts:
  251.                 print cmd
  252.                 print 'Exit status', sts
  253.         except:
  254.             print cmd
  255.             print 'Exception?!'
  256.         del cache[x]
  257.  
  258. def playfile(name):
  259.     if G.mode <> 'mac':
  260.         tempname = name
  261.     elif cache.has_key(name):
  262.         tempname = cache[name]
  263.     else:
  264.         tempname = G.tempprefix + `rand.rand()`
  265.         cmd = HOME_BIN_SGI + 'macsound2sgi'
  266.         cmd = cmd + ' ' + commands.mkarg(name)
  267.         cmd = cmd + ' >' + tempname
  268.         if G.debug: print cmd
  269.         sts = posix.system(cmd)
  270.         if sts:
  271.             print cmd
  272.             print 'Exit status', sts
  273.             stdwin.fleep()
  274.             return
  275.         cache[name] = tempname
  276.     fp = open(tempname, 'r')
  277.     try:
  278.         hdr = sunaudio.gethdr(fp)
  279.     except sunaudio.error, msg:
  280.         hdr = ()
  281.     if hdr:
  282.         data_size = hdr[0]
  283.         data = fp.read(data_size)
  284.         # XXX this doesn't work yet, need to convert from uLAW!!!
  285.         del fp
  286.     else:
  287.         del fp
  288.         data = readfile(tempname)
  289.     if G.debug: print len(data), 'bytes read from', tempname
  290.     if G.busy:
  291.         G.busy = 0
  292.         dummy = audio.stop_playing()
  293.     #
  294.     # Completely reset the audio device
  295.     audio.setrate(G.rate)
  296.     audio.setduration(0)
  297.     audio.setoutgain(G.gain)
  298.     #
  299.     if G.synchronous:
  300.         audio.write(data)
  301.         audio.setoutgain(0)
  302.     else:
  303.         try:
  304.             audio.start_playing(data)
  305.             G.busy = 1
  306.         except:
  307.             stdwin.fleep()
  308.     del data
  309.  
  310. def readfile(filename):
  311.     return readfp(open(filename, 'r'))
  312.  
  313. def readfp(fp):
  314.     data = ''
  315.     while 1:
  316.         buf = fp.read(102400) # Reads most samples in one fell swoop
  317.         if not buf:
  318.             return data
  319.         data = data + buf
  320.  
  321. main()
  322.