mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 11:49:12 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			321 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			321 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#! /usr/bin/env python
 | 
						|
 | 
						|
# 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 audio
 | 
						|
import sunaudio
 | 
						|
import commands
 | 
						|
import getopt
 | 
						|
import path
 | 
						|
import posix
 | 
						|
import rand
 | 
						|
import stdwin
 | 
						|
from stdwinevents import *
 | 
						|
import string
 | 
						|
import sys
 | 
						|
 | 
						|
from WindowParent import WindowParent
 | 
						|
from HVSplit import VSplit
 | 
						|
from Buttons import PushButton
 | 
						|
from Sliders import ComplexSlider
 | 
						|
 | 
						|
# Pathnames
 | 
						|
 | 
						|
HOME_BIN_SGI = '/ufs/guido/bin/sgi/'	# Directory where macsound2sgi lives
 | 
						|
DEF_DB = '/ufs/dik/sounds/Mac/HCOM'	# Default directory of sounds
 | 
						|
 | 
						|
 | 
						|
# 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.gain = 75		# Output gain
 | 
						|
	G.rate = 3		# Sampling rate
 | 
						|
	G.busy = 0		# Set while asynchronous playing is active
 | 
						|
	G.windows = []		# List of open windows (except control)
 | 
						|
	G.mode = 'mac'		# Macintosh mode
 | 
						|
	G.tempprefix = '/usr/tmp/@j' + `rand.rand()` + '-'
 | 
						|
	#
 | 
						|
	optlist, args = getopt.getopt(sys.argv[1:], 'dg:r:sSa')
 | 
						|
	for optname, optarg in optlist:
 | 
						|
		if   optname == '-d':
 | 
						|
			G.debug = 1
 | 
						|
		elif optname == '-g':
 | 
						|
			G.gain = string.atoi(optarg)
 | 
						|
			if not (0 < G.gain < 256):
 | 
						|
				raise optarg.error, '-g gain out of range'
 | 
						|
		elif optname == '-r':
 | 
						|
			G.rate = string.atoi(optarg)
 | 
						|
			if not (1 <= G.rate <= 3):
 | 
						|
				raise optarg.error, '-r rate out of range'
 | 
						|
		elif optname == '-s':
 | 
						|
			G.synchronous = 1
 | 
						|
		elif optname == '-S':
 | 
						|
			G.mode = 'sgi'
 | 
						|
		elif optname == '-a':
 | 
						|
			G.mode = 'sun'
 | 
						|
	#
 | 
						|
	if not args:
 | 
						|
		args = [DEF_DB]
 | 
						|
	#
 | 
						|
	G.cw = opencontrolwindow()
 | 
						|
	for dirname in args:
 | 
						|
		G.windows.append(openlistwindow(dirname))
 | 
						|
	#
 | 
						|
	#
 | 
						|
	savegain = audio.getoutgain()
 | 
						|
	try:
 | 
						|
		# Initialize stdaudio
 | 
						|
		audio.setoutgain(0)
 | 
						|
		audio.start_playing('')
 | 
						|
		dummy = audio.wait_playing()
 | 
						|
		audio.setoutgain(0)
 | 
						|
		maineventloop()
 | 
						|
	finally:
 | 
						|
		audio.setoutgain(savegain)
 | 
						|
		audio.done()
 | 
						|
		clearcache()
 | 
						|
 | 
						|
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
 | 
						|
			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
 | 
						|
 | 
						|
# Control window -- to set gain and cancel play operations in progress
 | 
						|
 | 
						|
def opencontrolwindow():
 | 
						|
	cw = WindowParent().create('Jukebox', (0, 0))
 | 
						|
	v = VSplit().create(cw)
 | 
						|
	#
 | 
						|
	gain = ComplexSlider().define(v)
 | 
						|
	gain.setminvalmax(0, G.gain, 255)
 | 
						|
	gain.settexts('  ', '  ')
 | 
						|
	gain.sethook(gain_setval_hook)
 | 
						|
	#
 | 
						|
	stop = PushButton().definetext(v, 'Stop')
 | 
						|
	stop.hook = stop_hook
 | 
						|
	#
 | 
						|
	cw.realize()
 | 
						|
	return cw
 | 
						|
 | 
						|
def gain_setval_hook(self):
 | 
						|
	G.gain = self.val
 | 
						|
	if G.busy: audio.setoutgain(G.gain)
 | 
						|
 | 
						|
def stop_hook(self):
 | 
						|
	if G.busy:
 | 
						|
		audio.setoutgain(0)
 | 
						|
		dummy = audio.stop_playing()
 | 
						|
		G.busy = 0
 | 
						|
 | 
						|
 | 
						|
# List windows -- to display list of files and subdirectories
 | 
						|
 | 
						|
def openlistwindow(dirname):
 | 
						|
	list = posix.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 path.isdir(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))
 | 
						|
	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):
 | 
						|
	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)
 | 
						|
 | 
						|
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)
 | 
						|
	if type == WE_MOUSE_DOWN and clicks >= 2 and i >= 0:
 | 
						|
		name = path.join(w.dirname, w.list[i])
 | 
						|
		if name[-1:] == '/':
 | 
						|
			if clicks == 2:
 | 
						|
				G.windows.append(openlistwindow(name[:-1]))
 | 
						|
		else:
 | 
						|
			playfile(name)
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
 | 
						|
# Playing tools
 | 
						|
 | 
						|
cache = {}
 | 
						|
 | 
						|
def clearcache():
 | 
						|
	for x in cache.keys():
 | 
						|
		try:
 | 
						|
			sts = posix.system('rm -f ' + cache[x])
 | 
						|
			if sts:
 | 
						|
				print cmd
 | 
						|
				print 'Exit status', sts
 | 
						|
		except:
 | 
						|
			print cmd
 | 
						|
			print 'Exception?!'
 | 
						|
		del cache[x]
 | 
						|
 | 
						|
def playfile(name):
 | 
						|
	if G.mode <> 'mac':
 | 
						|
		tempname = name
 | 
						|
	elif cache.has_key(name):
 | 
						|
		tempname = cache[name]
 | 
						|
	else:
 | 
						|
		tempname = G.tempprefix + `rand.rand()`
 | 
						|
		cmd = HOME_BIN_SGI + 'macsound2sgi'
 | 
						|
		cmd = cmd + ' ' + commands.mkarg(name)
 | 
						|
		cmd = cmd + ' >' + tempname
 | 
						|
		if G.debug: print cmd
 | 
						|
		sts = posix.system(cmd)
 | 
						|
		if sts:
 | 
						|
			print cmd
 | 
						|
			print 'Exit status', sts
 | 
						|
			stdwin.fleep()
 | 
						|
			return
 | 
						|
		cache[name] = tempname
 | 
						|
	fp = open(tempname, 'r')
 | 
						|
	try:
 | 
						|
		hdr = sunaudio.gethdr(fp)
 | 
						|
	except sunaudio.error, msg:
 | 
						|
		hdr = ()
 | 
						|
	if hdr:
 | 
						|
		data_size = hdr[0]
 | 
						|
		data = fp.read(data_size)
 | 
						|
		# XXX this doesn't work yet, need to convert from uLAW!!!
 | 
						|
		del fp
 | 
						|
	else:
 | 
						|
		del fp
 | 
						|
		data = readfile(tempname)
 | 
						|
	if G.debug: print len(data), 'bytes read from', tempname
 | 
						|
	if G.busy:
 | 
						|
		G.busy = 0
 | 
						|
		dummy = audio.stop_playing()
 | 
						|
	#
 | 
						|
	# Completely reset the audio device
 | 
						|
	audio.setrate(G.rate)
 | 
						|
	audio.setduration(0)
 | 
						|
	audio.setoutgain(G.gain)
 | 
						|
	#
 | 
						|
	if G.synchronous:
 | 
						|
		audio.write(data)
 | 
						|
		audio.setoutgain(0)
 | 
						|
	else:
 | 
						|
		try:
 | 
						|
			audio.start_playing(data)
 | 
						|
			G.busy = 1
 | 
						|
		except:
 | 
						|
			stdwin.fleep()
 | 
						|
	del data
 | 
						|
 | 
						|
def readfile(filename):
 | 
						|
	return readfp(open(filename, 'r'))
 | 
						|
 | 
						|
def readfp(fp):
 | 
						|
	data = ''
 | 
						|
	while 1:
 | 
						|
		buf = fp.read(102400) # Reads most samples in one fell swoop
 | 
						|
		if not buf:
 | 
						|
			return data
 | 
						|
		data = data + buf
 | 
						|
 | 
						|
main()
 |