Initial revision
diff --git a/Demo/sgi/cd/README b/Demo/sgi/cd/README
new file mode 100644
index 0000000..0789c7a
--- /dev/null
+++ b/Demo/sgi/cd/README
@@ -0,0 +1,23 @@
+These are some programs to work with the SCSI CD-ROM player's audio
+interface (see cdaudio(3) in IRIX 4.0[.2?] or higher).  At the moment
+the Python code is not very clean, sorry about that...
+
+cdwin.py	A trivial window interface to play a CD over the CD
+		player's audio jack.  More functionality is left as an
+		excersice to the reader.  Uses module stdwin.
+
+listcd.py	List the table-of-contents of a CD (data CDs will
+		appear as a single track).
+
+playcd.py	Read audio data from the CD and play it over the
+		Indigo's built-in speker or audio jack.  Uses module al.
+
+sendcd.py	Read audio data from the CD and send it as UDP packets
+		over the network (to readcd.py).
+
+readcd.py	Receive UDP packets containing CD audio data (from
+		sendcd.py) and play them over the Indigo's built-in
+		speaker or audio jack.  Uses module al.  (Doesn't
+		actually use module cd.)
+
+Note that to read *data* CD-ROMs you must open /dev/rdsk/dks0d4s7...
diff --git a/Demo/sgi/cd/cdwin.py b/Demo/sgi/cd/cdwin.py
new file mode 100755
index 0000000..823866f
--- /dev/null
+++ b/Demo/sgi/cd/cdwin.py
@@ -0,0 +1,93 @@
+import cd
+import stdwin
+from stdwinevents import *
+import mainloop
+
+def main():
+	player = cd.open()
+	stdwin.setdefscrollbars(0, 0)
+	win = stdwin.open('CD')
+	win.player = player
+	win.dispatch = cddispatch
+	mainloop.register(win)
+	win.settimer(10)
+	mainloop.mainloop()
+
+def cddispatch(type, win, detail):
+	if type == WE_NULL:
+		pass
+	elif type == WE_CLOSE:
+		mainloop.unregister(win)
+		win.close()
+	elif type == WE_DRAW:
+		draw(win)
+	elif type == WE_TIMER:
+		update(win)
+	elif type == WE_MOUSE_UP:
+		left, top, right, bottom, v1, v2 = getgeo(win)
+		h, v = detail[0]
+		if left < h < right:
+			if top < v < v1:
+				but1(win)
+			elif v1 < v < v2:
+				but2(win)
+			elif v2 < v < bottom:
+				but3(win)
+			else:
+				stdwin.fleep()
+
+def but1(win):
+	update(win)
+
+def but2(win):
+	win.player.togglepause()
+	update(win)
+
+def but3(win):
+	win.player.stop()
+	update(win)
+
+def update(win):
+	d = win.begindrawing()
+	drawstatus(win, d)
+	d.enddrawing()
+	win.settimer(10)
+
+statedict = ['ERROR', 'NODISK', 'READY', 'PLAYING', 'PAUSED', 'STILL']
+
+def draw(win):
+	left, top, right, bottom, v1, v2 = getgeo(win)
+	d = win.begindrawing()
+	drawstatus(win, d)
+	box(d, left, v1, right, v2, 'Play/Pause')
+	box(d, left, v2, right, bottom, 'Stop')
+	d.enddrawing()
+
+def drawstatus(win, d):
+	left, top, right, bottom, v1, v2 = getgeo(win)
+	status = win.player.getstatus()
+	state = status[0]
+	if 0 <= state < len(statedict):
+		message = statedict[state]
+	else:
+		message = `status`
+	message = message + ' track ' + `status[1]` + ' of ' + `status[12]`
+	d.erase((left, top), (right, v1))
+	box(d, left, top, right, v1, message)
+
+def box(d, left, top, right, bottom, label):
+	R = (left+1, top+1), (right-1, bottom-1)
+	width = d.textwidth(label)
+	height = d.lineheight()
+	h = (left + right - width) / 2
+	v = (top + bottom - height) / 2
+	d.box(R)
+	d.cliprect(R)
+	d.text((h, v), label)
+	d.noclip()
+
+def getgeo(win):
+	(left, top), (right, bottom) = (0, 0), win.getwinsize()
+	v1 = top + (bottom - top) / 3
+	v2 = top + (bottom - top) * 2 / 3
+	return left, top, right, bottom, v1, v2
diff --git a/Demo/sgi/cd/listcd.py b/Demo/sgi/cd/listcd.py
new file mode 100755
index 0000000..299cd98
--- /dev/null
+++ b/Demo/sgi/cd/listcd.py
@@ -0,0 +1,22 @@
+# List track info from CD player.
+
+import cd
+
+def main():
+	c = cd.open()
+	info = []
+	while 1:
+		try:
+			info.append(c.gettrackinfo(len(info) + 1))
+		except RuntimeError:
+			break
+	for i in range(len(info)):
+		start_min, start_sec, start_frame, \
+			total_min, total_sec, total_frame = info[i]
+		print 'Track', z(i+1),
+		print z(start_min) + ':' + z(start_sec) + ':' + z(start_frame),
+		print z(total_min) + ':' + z(total_sec) + ':' + z(total_frame)
+
+def z(n):
+	s = `n`
+	return '0' * (2 - len(s)) + s
diff --git a/Demo/sgi/cd/playcd.py b/Demo/sgi/cd/playcd.py
new file mode 100755
index 0000000..8b24581
--- /dev/null
+++ b/Demo/sgi/cd/playcd.py
@@ -0,0 +1,71 @@
+# Read CD audio data from the SCSI bus and play it back over the
+# built-in speaker or audio jack.
+
+import al
+import AL
+import cd
+import CD
+
+def playaudio(port, type, audio):
+##	print 'playaudio'
+	port.writesamps(audio)
+
+callbacks = ['audio', 'pnum', 'index', 'ptime', 'atime', 'catalog', 'ident', 'control']
+
+def callback(port, type, data):
+	print 'type', callbacks[type], 'data', `data`
+
+def main():
+	player = cd.open()
+	parser = cd.createparser()
+
+	state, track, min, sec, frame, abs_min, abs_sec, abs_frame, \
+		  total_min, total_sec, total_frame, first, last, scsi_audio, \
+		  cur_block, dum1, dum2, dum3 = player.getstatus()
+	print `state, track, min, sec, frame, abs_min, abs_sec, abs_frame, \
+		  total_min, total_sec, total_frame, first, last, scsi_audio, \
+		  cur_block, dum1, dum2, dum3`
+
+	if state <> CD.READY:
+		player.close()
+		raise 'playcd.Error', 'CD not ready'
+	if not scsi_audio:
+		player.close()
+		raise 'playcd.Error', 'not an audio-capable CD-ROM player'
+
+	for i in range(first, last+1):
+		trackinfo = player.gettrackinfo(i)
+		print `trackinfo`
+
+	size = player.bestreadsize()
+
+	try:
+		oldparams = [AL.OUTPUT_RATE, 0]
+		params = oldparams[:]
+		al.getparams(AL.DEFAULT_DEVICE, oldparams)
+		params[1] = AL.RATE_44100
+		al.setparams(AL.DEFAULT_DEVICE, params)
+		config = al.newconfig()
+		config.setwidth(AL.SAMPLE_16)
+		config.setchannels(AL.STEREO)
+		port = al.openport('CD Player', 'w', config)
+
+		parser.setcallback(CD.AUDIO, playaudio, port)
+		for i in range(1, 8):
+			parser.setcallback(i, callback, port)
+		parser.removecallback(CD.ATIME)
+		parser.removecallback(CD.PTIME)
+
+		while 1:
+			frames = player.readda(size)
+			if frames == '':
+				break
+			parser.parseframe(frames)
+	except KeyboardInterrupt:
+		pass
+
+	al.setparams(AL.DEFAULT_DEVICE, oldparams)
+	player.close()
+	parser.deleteparser()
+
+main()
diff --git a/Demo/sgi/cd/recvcd.py b/Demo/sgi/cd/recvcd.py
new file mode 100755
index 0000000..e991aaa
--- /dev/null
+++ b/Demo/sgi/cd/recvcd.py
@@ -0,0 +1,25 @@
+# Receive UDP packets from sendcd.py and play them on the speaker or
+# audio jack.
+
+import al, AL
+from socket import *
+
+PORT = 50505 # Must match the port in sendcd.py
+
+def main():
+	s = socket(AF_INET, SOCK_DGRAM)
+	s.bind('', PORT)
+
+	c = al.newconfig()
+	c.setchannels(2)
+	c.setwidth(2)
+	p = al.openport('Audio from CD', 'w', c)
+	al.setparams(AL.DEFAULT_DEVICE, [AL.OUTPUT_RATE, AL.RATE_44100])
+
+	N = 2352
+	while 1:
+		data = s.recv(N)
+		if not data:
+			print 'EOF'
+			break
+		p.writesamps(data)
diff --git a/Demo/sgi/cd/sendcd.py b/Demo/sgi/cd/sendcd.py
new file mode 100755
index 0000000..10b0faf
--- /dev/null
+++ b/Demo/sgi/cd/sendcd.py
@@ -0,0 +1,143 @@
+# Read CD audio data from the SCSI CD player and send it as UDP
+# packets to "readcd.py" on another host.
+# Option:
+# "-l" lists track info and quits.
+# "-s" displays status and quits.
+
+import cd
+import sys
+from socket import *
+import getopt
+
+HOST = 'voorn.cwi.nl' # The host where readcd.py is run
+PORT = 50505 # Must match the port in readcd.py
+
+def main():
+	try:
+		optlist, args = getopt.getopt(sys.argv[1:], 'ls')
+	except getopt.error, msg:
+		sys.stderr.write(msg + '\n')
+		sys.exit(2)
+
+	player = cd.open()
+	prstatus(player)
+	size = player.bestreadsize()
+
+	if optlist:
+		for opt, arg in optlist:
+			if opt == '-l':
+				prtrackinfo(player)
+			elif opt == '-s':
+				prstatus(player)
+		return
+
+	sys.stdout.write('waiting for socket... ')
+	sys.stdout.flush()
+	port = socket(AF_INET, SOCK_DGRAM)
+	port.connect(HOST, PORT)
+	print 'socket connected'
+
+	parser = cd.createparser()
+	parser.setcallback(0, audiocallback, port)
+	parser.setcallback(1, pnumcallback, player)
+	parser.setcallback(2, indexcallback, None)
+	## 3 = ptime: too many calls
+	## 4 = atime: too many calls
+	parser.setcallback(5, catalogcallback, None)
+	parser.setcallback(6, identcallback, None)
+	parser.setcallback(7, controlcallback, None)
+
+	if len(args) >= 2:
+		if len(args) >= 3:
+			[min, sec, frame] = args[:3]
+		else:
+			[min, sec] = args
+			frame = 0
+		min, sec, frame = eval(min), eval(sec), eval(frame)
+		print 'Seek to', triple(min, sec, frame)
+		dummy = player.seek(min, sec, frame)
+	elif len(args) == 1:
+		track = eval(args[0])
+		print 'Seek to track', track
+		dummy = player.seektrack(track)
+	else:
+		min, sec, frame = player.getstatus()[5:8]
+		print 'Try to seek back to', triple(min, sec, frame)
+		try:
+			player.seek(min, sec, frame)
+		except RuntimeError:
+			print 'Seek failed'
+
+	try:
+		while 1:
+			frames = player.readda(size)
+			if frames == '':
+				print 'END OF CD'
+				break
+			parser.parseframe(frames)
+	except KeyboardInterrupt:
+		print '[Interrupted]'
+		pass
+
+def prtrackinfo(player):
+	info = []
+	while 1:
+		try:
+			info.append(player.gettrackinfo(len(info) + 1))
+		except RuntimeError:
+			break
+	for i in range(len(info)):
+		start_min, start_sec, start_frame, \
+			total_min, total_sec, total_frame = info[i]
+		print 'Track', zfill(i+1), \
+			triple(start_min, start_sec, start_frame), \
+			triple(total_min, total_sec, total_frame)
+
+def audiocallback(port, type, data):
+##	sys.stdout.write('#')
+##	sys.stdout.flush()
+	port.send(data)
+
+def pnumcallback(player, type, data):
+	print 'pnum =', `data`
+	prstatus(player)
+
+def indexcallback(arg, type, data):
+	print 'index =', `data`
+
+def catalogcallback(arg, type, data):
+	print 'catalog =', `data`
+
+def identcallback(arg, type, data):
+	print 'ident =', `data`
+
+def controlcallback(arg, type, data):
+	print 'control =', `data`
+
+statedict = ['ERROR', 'NODISK', 'READY', 'PLAYING', 'PAUSED', 'STILL']
+
+def prstatus(player):
+	state, track, min, sec, frame, abs_min, abs_sec, abs_frame, \
+		total_min, total_sec, total_frame, first, last, scsi_audio, \
+		cur_block, dum1, dum2, dum3 = player.getstatus()
+	print 'Status:',
+	if 0 <= state < len(statedict):
+		print statedict[state]
+	else:
+		print state
+	print 'Track: ', track
+	print 'Time:  ', triple(min, sec, frame)
+	print 'Abs:   ', triple(abs_min, abs_sec, abs_frame)
+	print 'Total: ', triple(total_min, total_sec, total_frame)
+	print 'First: ', first
+	print 'Last:  ', last
+	print 'SCSI:  ', scsi_audio
+	print 'Block: ', cur_block
+	print 'Future:', (dum1, dum2, dum3)
+
+def triple(a, b, c):
+	return zfill(a) + ':' + zfill(b) + ':' + zfill(c)
+
+def zfill(n):
+	s = `n`
+	return '0' * (2 - len(s)) + s