Committed a more or less working version.
diff --git a/Mac/Demo/resources/copyres.py b/Mac/Demo/resources/copyres.py
new file mode 100644
index 0000000..b401142
--- /dev/null
+++ b/Mac/Demo/resources/copyres.py
@@ -0,0 +1,57 @@
+from Res import *
+from Resources import *
+import MacOS
+
+READ = 1
+WRITE = 2
+smAllScripts = -3
+
+def copyres(src, dst):
+	"""Copy resource from src file to dst file."""
+	
+	cur = CurResFile()
+	ctor, type = MacOS.GetCreatorAndType(src)
+	input = FSpOpenResFile(src, READ)
+	try:
+		FSpCreateResFile(dst, ctor, type, smAllScripts)
+	except:
+		raw_input("%s already exists...  CR to write anyway! " % dst)
+	output = FSpOpenResFile(dst, WRITE)
+	UseResFile(input)
+	ntypes = Count1Types()
+	for itype in range(1, 1+ntypes):
+		type = Get1IndType(itype)
+		nresources = Count1Resources(type)
+		for ires in range(1, 1+nresources):
+			res = Get1IndResource(type, ires)
+			res.LoadResource()
+			id, type, name = res.GetResInfo()
+			size = res.SizeResource()
+			attrs = res.GetResAttrs()
+			print id, type, name, size, hex(attrs)
+			res.DetachResource()
+			UseResFile(output)
+			try:
+				res2 = Get1Resource(type, id)
+			except (RuntimeError, Res.Error), msg:
+				res2 = None
+			if res2:
+				print "Duplicate type+id, not copied"
+				print (res2.size, res2.data)
+				print res2.GetResInfo()
+				if res2.HomeResFile() == output:
+					'OK'
+				elif res2.HomeResFile() == input:
+					'BAD!'
+				else:
+					print 'Home:', res2.HomeResFile()
+			else:
+				res.AddResource(type, id, name)
+				#res.SetResAttrs(attrs)
+				res.WriteResource()
+			UseResFile(input)
+	UseResFile(cur)
+	CloseResFile(output)
+	CloseResFile(input)
+
+copyres('::python.¹.rsrc', '::foo.rsrc')
diff --git a/Mac/Demo/resources/listres.py b/Mac/Demo/resources/listres.py
new file mode 100644
index 0000000..a0b2423
--- /dev/null
+++ b/Mac/Demo/resources/listres.py
@@ -0,0 +1,60 @@
+# List all resources
+
+import Res
+from Resources import *
+
+def list1resources():
+	ntypes = Res.Count1Types()
+	for itype in range(1, 1+ntypes):
+		type = Res.Get1IndType(itype)
+		print "Type:", `type`
+		nresources = Res.Count1Resources(type)
+		for i in range(1, 1 + nresources):
+			Res.SetResLoad(0)
+			res = Res.Get1IndResource(type, i)
+			Res.SetResLoad(1)
+			info(res)
+
+def listresources():
+	ntypes = Res.CountTypes()
+	for itype in range(1, 1+ntypes):
+		type = Res.GetIndType(itype)
+		print "Type:", `type`
+		nresources = Res.CountResources(type)
+		for i in range(1, 1 + nresources):
+			Res.SetResLoad(0)
+			res = Res.GetIndResource(type, i)
+			Res.SetResLoad(1)
+			info(res)
+
+def info(res):
+	print res.GetResInfo(), res.SizeResource(), decodeattrs(res.GetResAttrs())
+
+attrnames = {
+	resChanged:	'Changed',
+	resPreload:	'Preload',
+	resProtected:	'Protected',
+	resLocked:	'Locked',
+	resPurgeable:	'Purgeable',
+	resSysHeap:	'SysHeap',
+}
+
+def decodeattrs(attrs):
+	names = []
+	for bit in range(16):
+		mask = 1<<bit
+		if attrs & mask:
+			if attrnames.has_key(mask):
+				names.append(attrnames[mask])
+			else:
+				names.append(hex(mask))
+	return names
+
+def test():
+	print "=== Local resourcess ==="
+	list1resources()
+	print "=== All resources ==="
+	listresources()
+
+if __name__ == '__main__':
+	test()
diff --git a/Mac/Demo/sound/morse.py b/Mac/Demo/sound/morse.py
new file mode 100644
index 0000000..bf5fa5f
--- /dev/null
+++ b/Mac/Demo/sound/morse.py
@@ -0,0 +1,180 @@
+import sys, math, audiodev
+
+DOT = 30
+DAH = 80
+OCTAVE = 2				# 1 == 441 Hz, 2 == 882 Hz, ...
+SAMPWIDTH = 2
+FRAMERATE = 44100
+BASEFREQ = 441
+QSIZE = 20000
+
+morsetab = {
+	'A': '.-',		'a': '.-',
+	'B': '-...',		'b': '-...',
+	'C': '-.-.',		'c': '-.-.',
+	'D': '-..',		'd': '-..',
+	'E': '.',		'e': '.',
+	'F': '..-.',		'f': '..-.',
+	'G': '--.',		'g': '--.',
+	'H': '....',		'h': '....',
+	'I': '..',		'i': '..',
+	'J': '.---',		'j': '.---',
+	'K': '-.-',		'k': '-.-',
+	'L': '.-..',		'l': '.-..',
+	'M': '--',		'm': '--',
+	'N': '-.',		'n': '-.',
+	'O': '---',		'o': '---',
+	'P': '.--.',		'p': '.--.',
+	'Q': '--.-',		'q': '--.-',
+	'R': '.-.',		'r': '.-.',
+	'S': '...',		's': '...',
+	'T': '-',		't': '-',
+	'U': '..-',		'u': '..-',
+	'V': '...-',		'v': '...-',
+	'W': '.--',		'w': '.--',
+	'X': '-..-',		'x': '-..-',
+	'Y': '-.--',		'y': '-.--',
+	'Z': '--..',		'z': '--..',
+	'0': '-----',
+	'1': '.----',
+	'2': '..---',
+	'3': '...--',
+	'4': '....-',
+	'5': '.....',
+	'6': '-....',
+	'7': '--...',
+	'8': '---..',
+	'9': '----.',
+	',': '--..--',
+	'.': '.-.-.-',
+	'?': '..--..',
+	';': '-.-.-.',
+	':': '---...',
+	"'": '.----.',
+	'-': '-....-',
+	'/': '-..-.',
+	'(': '-.--.-',
+	')': '-.--.-',
+	'_': '..--.-',
+	' ': ' '
+}
+
+# If we play at 44.1 kHz (which we do), then if we produce one sine
+# wave in 100 samples, we get a tone of 441 Hz.  If we produce two
+# sine waves in these 100 samples, we get a tone of 882 Hz.  882 Hz
+# appears to be a nice one for playing morse code.
+def mkwave(octave):
+	global sinewave, nowave
+	sinewave = ''
+	n = int(FRAMERATE / BASEFREQ)
+	for i in range(n):
+		val = int(math.sin(2 * math.pi * i * octave / n) * 0x7fff)
+		sample = chr((val >> 8) & 255) + chr(val & 255)
+		sinewave = sinewave + sample[:SAMPWIDTH]
+	nowave = '\0' * (n*SAMPWIDTH)
+
+mkwave(OCTAVE)
+
+class BufferedAudioDev:
+	def __init__(self, *args):
+		import audiodev
+		self._base = apply(audiodev.AudioDev, args)
+		self._buffer = []
+		self._filled = 0
+		self._addmethods(self._base, self._base.__class__)
+	def _addmethods(self, inst, cls):
+		for name in cls.__dict__.keys():
+			if not hasattr(self, name):
+				try:
+					setattr(self, name, getattr(inst, name))
+				except:
+					pass
+		for basecls in cls.__bases__:
+			self._addmethods(self, inst, basecls)
+	def writeframesraw(self, frames):
+		self._buffer.append(frames)
+		self._filled = self._filled + len(frames)
+		if self._filled >= QSIZE:
+			self.flush()
+	def wait(self):
+		self.flush()
+		self._base.wait()
+	def flush(self):
+		print 'flush: %d blocks, %d bytes' % (len(self._buffer), self._filled)
+		if self._buffer:
+			import string
+			self._base.writeframes(string.joinfields(self._buffer, ''))
+			self._buffer = []
+			self._filled = 0
+
+def main(args = sys.argv[1:]):
+	import getopt, string
+	try:
+		opts, args = getopt.getopt(args, 'o:p:')
+	except getopt.error:
+		sys.stderr.write('Usage ' + sys.argv[0] +
+				 ' [ -o outfile ] [ args ] ...\n')
+		sys.exit(1)
+	dev = None
+	for o, a in opts:
+		if o == '-o':
+			import aifc
+			dev = aifc.open(a, 'w')
+			dev.setframerate(FRAMERATE)
+			dev.setsampwidth(SAMPWIDTH)
+			dev.setnchannels(1)
+		if o == '-p':
+			mkwave(string.atoi(a))
+	if not dev:
+		dev = BufferedAudioDev()
+		dev.setoutrate(FRAMERATE)
+		dev.setsampwidth(SAMPWIDTH)
+		dev.setnchannels(1)
+		dev.close = dev.stop
+	if args:
+		line = string.join(args)
+	else:
+		line = sys.stdin.readline()
+	while line:
+		print line
+		mline = morse(line)
+		print mline
+		play(mline, dev)
+		if hasattr(dev, 'wait'):
+			dev.wait()
+		if not args:
+			line = sys.stdin.readline()
+		else:
+			line = ''
+	dev.close()
+
+# Convert a string to morse code with \001 between the characters in
+# the string.
+def morse(line):
+	res = ''
+	for c in line:
+		try:
+			res = res + morsetab[c] + '\001'
+		except KeyError:
+			pass
+	return res
+
+# Play a line of morse code.
+def play(line, dev):
+	for c in line:
+		if c == '.':
+			sine(dev, DOT)
+		elif c == '-':
+			sine(dev, DAH)
+		else:
+			pause(dev, DAH)
+		pause(dev, DOT)
+
+def sine(dev, length):
+	dev.writeframesraw(sinewave*length)
+
+def pause(dev, length):
+	dev.writeframesraw(nowave*length)
+
+if __name__ == '__main__' or sys.argv[0] == __name__:
+	main()
diff --git a/Mac/Demo/sound/playaiff.py b/Mac/Demo/sound/playaiff.py
new file mode 100644
index 0000000..1022fc8
--- /dev/null
+++ b/Mac/Demo/sound/playaiff.py
@@ -0,0 +1,45 @@
+from Sound import *
+import Snd
+
+import aifc, audioop
+
+fn = 'f:just samples:2ndbeat.aif'
+af = aifc.open(fn, 'r')
+print af.getparams()
+print 'nframes  =', af.getnframes()
+print 'nchannels =', af.getnchannels()
+print 'framerate =', af.getframerate()
+nframes = min(af.getnframes(), 100000)
+frames = af.readframes(nframes)
+print 'len(frames) =', len(frames)
+print repr(frames[:100])
+frames = audioop.add(frames, '\x80'*len(frames), 1)
+print repr(frames[:100])
+
+import struct
+
+header1 = struct.pack('llhhllbbl',
+                      0,
+                      af.getnchannels(),
+                      af.getframerate(),0,
+                      0,
+                      0,
+                      0xFF,
+                      60,
+                      nframes)
+print repr(header1)
+header2 = struct.pack('llhlll', 0, 0, 0, 0, 0, 0)
+header3 = struct.pack('hhlll',
+                      af.getsampwidth()*8,
+                      0,
+                      0,
+                      0,
+                      0)
+print repr(header3)
+header = header1 + header2 + header3
+
+buffer = header + frames
+
+chan = Snd.SndNewChannel(5,0x00C0)
+
+Snd.SndDoCommand(chan, (bufferCmd, 0, buffer), 0)