Class around PixMap objects that allows more python-like access. By Joe Strout.
[python/dscho.git] / Demo / sgi / audio_stdwin / jukebox.py
blob2aaaa831d0f2096d9ad12360276b727e9a3c5104
1 #! /usr/bin/env python
3 # JUKEBOX: browse directories full of sampled sound files.
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).
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.
14 # The control window displays the current output gain and a primitive
15 # "stop button" to cancel the current play request.
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.
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
34 from WindowParent import WindowParent
35 from HVSplit import VSplit
36 from Buttons import PushButton
37 from Sliders import ComplexSlider
39 # Pathnames
41 HOME_BIN_SGI = '/ufs/guido/bin/sgi/' # Directory where macsound2sgi lives
42 DEF_DB = '/ufs/dik/sounds/Mac/HCOM' # Default directory of sounds
45 # Global variables
47 class struct: pass # Class to define featureless structures
49 G = struct() # Holds writable global variables
52 # Main program
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()` + '-'
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'
83 if not args:
84 args = [DEF_DB]
86 G.cw = opencontrolwindow()
87 for dirname in args:
88 G.windows.append(openlistwindow(dirname))
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()
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
123 # Control window -- to set gain and cancel play operations in progress
125 def opencontrolwindow():
126 cw = WindowParent().create('Jukebox', (0, 0))
127 v = VSplit().create(cw)
129 gain = ComplexSlider().define(v)
130 gain.setminvalmax(0, G.gain, 255)
131 gain.settexts(' ', ' ')
132 gain.sethook(gain_setval_hook)
134 stop = PushButton().definetext(v, 'Stop')
135 stop.hook = stop_hook
137 cw.realize()
138 return cw
140 def gain_setval_hook(self):
141 G.gain = self.val
142 if G.busy: audio.setoutgain(G.gain)
144 def stop_hook(self):
145 if G.busy:
146 audio.setoutgain(0)
147 dummy = audio.stop_playing()
148 G.busy = 0
151 # List windows -- to display list of files and subdirectories
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
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
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)
198 def hideselection(w, d):
199 if w.selected >= 0:
200 invertselection(w, d)
202 def showselection(w, d):
203 if w.selected >= 0:
204 invertselection(w, d)
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)
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)
232 def closelistwindow(w):
233 remove(G.windows, w)
235 def remove(list, item):
236 for i in range(len(list)):
237 if list[i] == item:
238 del list[i]
239 break
242 # Playing tools
244 cache = {}
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]
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()
294 # Completely reset the audio device
295 audio.setrate(G.rate)
296 audio.setduration(0)
297 audio.setoutgain(G.gain)
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
310 def readfile(filename):
311 return readfp(open(filename, 'r'))
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
321 main()