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