1 QSIZE = 100000 2 error='Audio_mac.error' 3 4 from warnings import warnpy3k 5 warnpy3k("In 3.x, the Play_Audio_mac module is removed.", stacklevel=2) 6 7 class Play_Audio_mac: 8 9 def __init__(self, qsize=QSIZE): 10 self._chan = None 11 self._qsize = qsize 12 self._outrate = 22254 13 self._sampwidth = 1 14 self._nchannels = 1 15 self._gc = [] 16 self._usercallback = None 17 18 def __del__(self): 19 self.stop() 20 self._usercallback = None 21 22 def wait(self): 23 import time 24 while self.getfilled(): 25 time.sleep(0.1) 26 self._chan = None 27 self._gc = [] 28 29 def stop(self, quietNow = 1): 30 ##chan = self._chan 31 self._chan = None 32 ##chan.SndDisposeChannel(1) 33 self._gc = [] 34 35 def setoutrate(self, outrate): 36 self._outrate = outrate 37 38 def setsampwidth(self, sampwidth): 39 self._sampwidth = sampwidth 40 41 def setnchannels(self, nchannels): 42 self._nchannels = nchannels 43 44 def writeframes(self, data): 45 import time 46 from Carbon.Sound import bufferCmd, callBackCmd, extSH 47 import struct 48 import MacOS 49 if not self._chan: 50 from Carbon import Snd 51 self._chan = Snd.SndNewChannel(5, 0, self._callback) 52 nframes = len(data) / self._nchannels / self._sampwidth 53 if len(data) != nframes * self._nchannels * self._sampwidth: 54 raise error, 'data is not a whole number of frames' 55 while self._gc and \ 56 self.getfilled() + nframes > \ 57 self._qsize / self._nchannels / self._sampwidth: 58 time.sleep(0.1) 59 if self._sampwidth == 1: 60 import audioop 61 data = audioop.add(data, '\x80'*len(data), 1) 62 h1 = struct.pack('llHhllbbl', 63 id(data)+MacOS.string_id_to_buffer, 64 self._nchannels, 65 self._outrate, 0, 66 0, 67 0, 68 extSH, 69 60, 70 nframes) 71 h2 = 22*'\0' 72 h3 = struct.pack('hhlll', 73 self._sampwidth*8, 74 0, 75 0, 76 0, 77 0) 78 header = h1+h2+h3 79 self._gc.append((header, data)) 80 self._chan.SndDoCommand((bufferCmd, 0, header), 0) 81 self._chan.SndDoCommand((callBackCmd, 0, 0), 0) 82 83 def _callback(self, *args): 84 del self._gc[0] 85 if self._usercallback: 86 self._usercallback() 87 88 def setcallback(self, callback): 89 self._usercallback = callback 90 91 def getfilled(self): 92 filled = 0 93 for header, data in self._gc: 94 filled = filled + len(data) 95 return filled / self._nchannels / self._sampwidth 96 97 def getfillable(self): 98 return (self._qsize / self._nchannels / self._sampwidth) - self.getfilled() 99 100 def ulaw2lin(self, data): 101 import audioop 102 return audioop.ulaw2lin(data, 2) 103 104 def test(): 105 import aifc 106 import EasyDialogs 107 fn = EasyDialogs.AskFileForOpen(message="Select an AIFF soundfile", typeList=("AIFF",)) 108 if not fn: return 109 af = aifc.open(fn, 'r') 110 print af.getparams() 111 p = Play_Audio_mac() 112 p.setoutrate(af.getframerate()) 113 p.setsampwidth(af.getsampwidth()) 114 p.setnchannels(af.getnchannels()) 115 BUFSIZ = 10000 116 while 1: 117 data = af.readframes(BUFSIZ) 118 if not data: break 119 p.writeframes(data) 120 print 'wrote', len(data), 'space', p.getfillable() 121 p.wait() 122 123 if __name__ == '__main__': 124 test() 125