mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			413 lines
		
	
	
	
		
			9.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			413 lines
		
	
	
	
		
			9.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#! /usr/bin/env python
 | 
						|
 | 
						|
# XXX This only works on SGIs running IRIX 4.0 or higher
 | 
						|
 | 
						|
# 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 for events
 | 
						|
# while the sample is playing, so you can cancel playing or start a
 | 
						|
# new sample right away.  Synchronous playing is available through the
 | 
						|
# -s option.
 | 
						|
#
 | 
						|
# The control window displays a "stop button" that cancel the current
 | 
						|
# play request.
 | 
						|
#
 | 
						|
# Most sound file formats recognized by SOX or SFPLAY are recognized.
 | 
						|
# Since conversion is costly, converted files are cached in
 | 
						|
# /usr/tmp/@j* until the user quits or changes the sampling rate via
 | 
						|
# the Rate menu.
 | 
						|
 | 
						|
import commands
 | 
						|
import getopt
 | 
						|
import os
 | 
						|
from stat import *
 | 
						|
import rand
 | 
						|
import stdwin
 | 
						|
from stdwinevents import *
 | 
						|
import sys
 | 
						|
import tempfile
 | 
						|
import sndhdr
 | 
						|
 | 
						|
from WindowParent import WindowParent
 | 
						|
from Buttons import PushButton
 | 
						|
 | 
						|
# Pathnames
 | 
						|
 | 
						|
DEF_DB = '/usr/local/sounds'		# Default directory of sounds
 | 
						|
SOX = '/usr/local/bin/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 (-dd event 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 = G.debug + 1
 | 
						|
		elif optname == '-r':
 | 
						|
			G.rate = int(eval(optarg))
 | 
						|
		elif optname == '-s':
 | 
						|
			G.synchronous = 1
 | 
						|
		elif optname == '-t':
 | 
						|
			G.mode = optarg
 | 
						|
	#
 | 
						|
	if G.debug:
 | 
						|
		for name in G.__dict__.keys():
 | 
						|
			print 'G.' + name, '=', `G.__dict__[name]`
 | 
						|
	#
 | 
						|
	if not args:
 | 
						|
		args = [DEF_DB]
 | 
						|
	#
 | 
						|
	G.cw = opencontrolwindow()
 | 
						|
	for dirname in args:
 | 
						|
		G.windows.append(openlistwindow(dirname))
 | 
						|
	#
 | 
						|
	#
 | 
						|
	try:
 | 
						|
		maineventloop()
 | 
						|
	finally:
 | 
						|
		clearcache()
 | 
						|
		killchild()
 | 
						|
 | 
						|
# Entries in Rate menu:
 | 
						|
rates = ['default', '7350', \
 | 
						|
	'8000', '11025', '16000', '22050', '32000', '41000', '48000']
 | 
						|
 | 
						|
def maineventloop():
 | 
						|
	mouse_events = WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP
 | 
						|
	while G.windows:
 | 
						|
		try:
 | 
						|
			type, w, detail = event = stdwin.getevent()
 | 
						|
		except KeyboardInterrupt:
 | 
						|
			killchild()
 | 
						|
			continue
 | 
						|
		if w == G.cw.win:
 | 
						|
			if type == WE_CLOSE:
 | 
						|
				return
 | 
						|
			if type == WE_TIMER:
 | 
						|
				checkchild()
 | 
						|
				if G.busy:
 | 
						|
					G.cw.win.settimer(1)
 | 
						|
			elif type == WE_MENU:
 | 
						|
				menu, item = detail
 | 
						|
				if menu is G.ratemenu:
 | 
						|
					clearcache()
 | 
						|
					if item == 0:
 | 
						|
						G.rate = 0
 | 
						|
					else:
 | 
						|
						G.rate = eval(rates[item])
 | 
						|
					for i in range(len(rates)):
 | 
						|
						menu.check(i, (i == item))
 | 
						|
			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 > 1: 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.waitpid(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))
 | 
						|
	#
 | 
						|
	stop = PushButton().definetext(cw, '        Stop        ')
 | 
						|
	stop.hook = stop_hook
 | 
						|
	stop.enable(0)
 | 
						|
	G.stop = stop
 | 
						|
	#
 | 
						|
	cw.realize()
 | 
						|
	#
 | 
						|
	G.ratemenu = cw.win.menucreate('Rate')
 | 
						|
	for r in rates:
 | 
						|
		G.ratemenu.additem(r)
 | 
						|
	if G.rate == 0:
 | 
						|
		G.ratemenu.check(0, 1)
 | 
						|
	else:
 | 
						|
		for i in len(range(rates)):
 | 
						|
			if rates[i] == `G.rate`:
 | 
						|
				G.ratemenu.check(i, 1)
 | 
						|
	#
 | 
						|
	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][0] == '.':
 | 
						|
			del list[i]
 | 
						|
		else:
 | 
						|
			i = i+1
 | 
						|
	for i in range(len(list)):
 | 
						|
		fullname = os.path.join(dirname, list[i])
 | 
						|
		if os.path.isdir(fullname):
 | 
						|
			info = '/'
 | 
						|
		else:
 | 
						|
			try:
 | 
						|
				size = os.stat(fullname)[ST_SIZE]
 | 
						|
				info = `(size + 1023)/1024` + 'k'
 | 
						|
			except IOError:
 | 
						|
				info = '???'
 | 
						|
			info = '(' + info + ')'
 | 
						|
		list[i] = list[i], info
 | 
						|
	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, info in list:
 | 
						|
		w = stdwin.textwidth(name + '  ' + info)
 | 
						|
		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, info in w.list:
 | 
						|
		if info == '/':
 | 
						|
			text = name + '/'
 | 
						|
		else:
 | 
						|
			text = name + '  ' + info
 | 
						|
		d.text((h, v), text)
 | 
						|
		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, info = w.list[i]
 | 
						|
		fullname = os.path.join(w.dirname, name)
 | 
						|
		if info == '/':
 | 
						|
			if clicks == 2:
 | 
						|
				G.windows.append(openlistwindow(fullname))
 | 
						|
		else:
 | 
						|
			playfile(fullname)
 | 
						|
		setcursors('cross')
 | 
						|
 | 
						|
def closelistwindow(w):
 | 
						|
	G.windows.remove(w)
 | 
						|
 | 
						|
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():
 | 
						|
		cmd = 'rm -f ' + cache[x]
 | 
						|
		if G.debug: print cmd
 | 
						|
		sts = os.system(cmd)
 | 
						|
		if sts:
 | 
						|
			print cmd
 | 
						|
			print 'Exit status', sts
 | 
						|
		del cache[x]
 | 
						|
 | 
						|
validrates = (8000, 11025, 16000, 22050, 32000, 44100, 48000)
 | 
						|
 | 
						|
def playfile(filename):
 | 
						|
	killchild()
 | 
						|
	try:
 | 
						|
		tuple = sndhdr.what(filename)
 | 
						|
	except IOError, msg:
 | 
						|
		print 'Can\'t open', filename, msg
 | 
						|
		stdwin.fleep()
 | 
						|
		return
 | 
						|
	raw = 0
 | 
						|
	if tuple:
 | 
						|
		mode, rate = tuple[:2]
 | 
						|
		if rate == 0:
 | 
						|
			rate = G.rate
 | 
						|
			if rate == 0:
 | 
						|
				rate = 8000
 | 
						|
	else:
 | 
						|
		mode = G.mode
 | 
						|
		rate = G.rate
 | 
						|
	if G.debug: print 'mode =', mode, 'rate =', rate
 | 
						|
	if mode in ('au', 'aiff', 'wav', 'aifc', 'ul', 'ub', 'sb') and \
 | 
						|
		  rate in validrates:
 | 
						|
		tempname = filename
 | 
						|
		if mode in ('ul', 'ub', 'sb'):
 | 
						|
			raw = 1
 | 
						|
	elif cache.has_key(filename):
 | 
						|
		tempname = cache[filename]
 | 
						|
	else:
 | 
						|
		tempname = G.tempprefix + `rand.rand()` + '.aiff'
 | 
						|
		cmd = SOX
 | 
						|
		if G.debug:
 | 
						|
			cmd = cmd + ' -V'
 | 
						|
		if mode <> '':
 | 
						|
			cmd = cmd + ' -t ' + mode
 | 
						|
		cmd = cmd + ' ' + commands.mkarg(filename)
 | 
						|
		cmd = cmd + ' -t aiff'
 | 
						|
		if rate not in validrates:
 | 
						|
			rate = 32000
 | 
						|
		if rate:
 | 
						|
			cmd = cmd + ' -r ' + `rate`
 | 
						|
		cmd = cmd + ' ' + tempname
 | 
						|
		if G.debug: print cmd
 | 
						|
		sts = os.system(cmd)
 | 
						|
		if sts:
 | 
						|
			print cmd
 | 
						|
			print 'Exit status', sts
 | 
						|
			stdwin.fleep()
 | 
						|
			try:
 | 
						|
				os.unlink(tempname)
 | 
						|
			except:
 | 
						|
				pass
 | 
						|
			return
 | 
						|
		cache[filename] = tempname
 | 
						|
	if raw:
 | 
						|
		pid = sfplayraw(tempname, tuple)
 | 
						|
	else:
 | 
						|
		pid = sfplay(tempname, [])
 | 
						|
	if G.synchronous:
 | 
						|
		sts = os.wait(pid, 0)
 | 
						|
	else:
 | 
						|
		G.busy = pid
 | 
						|
		G.stop.enable(1)
 | 
						|
		G.cw.win.settimer(1)
 | 
						|
 | 
						|
def sfplayraw(filename, tuple):
 | 
						|
	args = ['-i']
 | 
						|
	type, rate, channels, frames, bits = tuple
 | 
						|
	if type == 'ul':
 | 
						|
		args.append('mulaw')
 | 
						|
	elif type == 'ub':
 | 
						|
		args = args + ['integer', '8', 'unsigned']
 | 
						|
	elif type == 'sb':
 | 
						|
		args = args + ['integer', '8', '2scomp']
 | 
						|
	else:
 | 
						|
		print 'sfplayraw: warning: unknown type in', tuple
 | 
						|
	if channels > 1:
 | 
						|
		args = args + ['channels', `channels`]
 | 
						|
	if not rate:
 | 
						|
		rate = G.rate
 | 
						|
	if rate:
 | 
						|
		args = args + ['rate', `rate`]
 | 
						|
	args.append('end')
 | 
						|
	return sfplay(filename, args)
 | 
						|
 | 
						|
def sfplay(filename, args):
 | 
						|
	if G.debug:
 | 
						|
		args = ['-p'] + args
 | 
						|
	args = [SFPLAY, '-r'] + args + [filename]
 | 
						|
	if G.debug: print 'sfplay:', args
 | 
						|
	pid = os.fork()
 | 
						|
	if pid == 0:
 | 
						|
		# Child
 | 
						|
		os.execv(SFPLAY, args)
 | 
						|
		# NOTREACHED
 | 
						|
	else:
 | 
						|
		# Parent
 | 
						|
		return pid
 | 
						|
 | 
						|
main()
 |