Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | """Classes for manipulating audio devices (currently only for Sun and SGI)""" |
2 | ||
3 | __all__ = ["error","AudioDev"] | |
4 | ||
5 | class error(Exception): | |
6 | pass | |
7 | ||
8 | class Play_Audio_sgi: | |
9 | # Private instance variables | |
10 | ## if 0: access frameratelist, nchannelslist, sampwidthlist, oldparams, \ | |
11 | ## params, config, inited_outrate, inited_width, \ | |
12 | ## inited_nchannels, port, converter, classinited: private | |
13 | ||
14 | classinited = 0 | |
15 | frameratelist = nchannelslist = sampwidthlist = None | |
16 | ||
17 | def initclass(self): | |
18 | import AL | |
19 | self.frameratelist = [ | |
20 | (48000, AL.RATE_48000), | |
21 | (44100, AL.RATE_44100), | |
22 | (32000, AL.RATE_32000), | |
23 | (22050, AL.RATE_22050), | |
24 | (16000, AL.RATE_16000), | |
25 | (11025, AL.RATE_11025), | |
26 | ( 8000, AL.RATE_8000), | |
27 | ] | |
28 | self.nchannelslist = [ | |
29 | (1, AL.MONO), | |
30 | (2, AL.STEREO), | |
31 | (4, AL.QUADRO), | |
32 | ] | |
33 | self.sampwidthlist = [ | |
34 | (1, AL.SAMPLE_8), | |
35 | (2, AL.SAMPLE_16), | |
36 | (3, AL.SAMPLE_24), | |
37 | ] | |
38 | self.classinited = 1 | |
39 | ||
40 | def __init__(self): | |
41 | import al, AL | |
42 | if not self.classinited: | |
43 | self.initclass() | |
44 | self.oldparams = [] | |
45 | self.params = [AL.OUTPUT_RATE, 0] | |
46 | self.config = al.newconfig() | |
47 | self.inited_outrate = 0 | |
48 | self.inited_width = 0 | |
49 | self.inited_nchannels = 0 | |
50 | self.converter = None | |
51 | self.port = None | |
52 | return | |
53 | ||
54 | def __del__(self): | |
55 | if self.port: | |
56 | self.stop() | |
57 | if self.oldparams: | |
58 | import al, AL | |
59 | al.setparams(AL.DEFAULT_DEVICE, self.oldparams) | |
60 | self.oldparams = [] | |
61 | ||
62 | def wait(self): | |
63 | if not self.port: | |
64 | return | |
65 | import time | |
66 | while self.port.getfilled() > 0: | |
67 | time.sleep(0.1) | |
68 | self.stop() | |
69 | ||
70 | def stop(self): | |
71 | if self.port: | |
72 | self.port.closeport() | |
73 | self.port = None | |
74 | if self.oldparams: | |
75 | import al, AL | |
76 | al.setparams(AL.DEFAULT_DEVICE, self.oldparams) | |
77 | self.oldparams = [] | |
78 | ||
79 | def setoutrate(self, rate): | |
80 | for (raw, cooked) in self.frameratelist: | |
81 | if rate == raw: | |
82 | self.params[1] = cooked | |
83 | self.inited_outrate = 1 | |
84 | break | |
85 | else: | |
86 | raise error, 'bad output rate' | |
87 | ||
88 | def setsampwidth(self, width): | |
89 | for (raw, cooked) in self.sampwidthlist: | |
90 | if width == raw: | |
91 | self.config.setwidth(cooked) | |
92 | self.inited_width = 1 | |
93 | break | |
94 | else: | |
95 | if width == 0: | |
96 | import AL | |
97 | self.inited_width = 0 | |
98 | self.config.setwidth(AL.SAMPLE_16) | |
99 | self.converter = self.ulaw2lin | |
100 | else: | |
101 | raise error, 'bad sample width' | |
102 | ||
103 | def setnchannels(self, nchannels): | |
104 | for (raw, cooked) in self.nchannelslist: | |
105 | if nchannels == raw: | |
106 | self.config.setchannels(cooked) | |
107 | self.inited_nchannels = 1 | |
108 | break | |
109 | else: | |
110 | raise error, 'bad # of channels' | |
111 | ||
112 | def writeframes(self, data): | |
113 | if not (self.inited_outrate and self.inited_nchannels): | |
114 | raise error, 'params not specified' | |
115 | if not self.port: | |
116 | import al, AL | |
117 | self.port = al.openport('Python', 'w', self.config) | |
118 | self.oldparams = self.params[:] | |
119 | al.getparams(AL.DEFAULT_DEVICE, self.oldparams) | |
120 | al.setparams(AL.DEFAULT_DEVICE, self.params) | |
121 | if self.converter: | |
122 | data = self.converter(data) | |
123 | self.port.writesamps(data) | |
124 | ||
125 | def getfilled(self): | |
126 | if self.port: | |
127 | return self.port.getfilled() | |
128 | else: | |
129 | return 0 | |
130 | ||
131 | def getfillable(self): | |
132 | if self.port: | |
133 | return self.port.getfillable() | |
134 | else: | |
135 | return self.config.getqueuesize() | |
136 | ||
137 | # private methods | |
138 | ## if 0: access *: private | |
139 | ||
140 | def ulaw2lin(self, data): | |
141 | import audioop | |
142 | return audioop.ulaw2lin(data, 2) | |
143 | ||
144 | class Play_Audio_sun: | |
145 | ## if 0: access outrate, sampwidth, nchannels, inited_outrate, inited_width, \ | |
146 | ## inited_nchannels, converter: private | |
147 | ||
148 | def __init__(self): | |
149 | self.outrate = 0 | |
150 | self.sampwidth = 0 | |
151 | self.nchannels = 0 | |
152 | self.inited_outrate = 0 | |
153 | self.inited_width = 0 | |
154 | self.inited_nchannels = 0 | |
155 | self.converter = None | |
156 | self.port = None | |
157 | return | |
158 | ||
159 | def __del__(self): | |
160 | self.stop() | |
161 | ||
162 | def setoutrate(self, rate): | |
163 | self.outrate = rate | |
164 | self.inited_outrate = 1 | |
165 | ||
166 | def setsampwidth(self, width): | |
167 | self.sampwidth = width | |
168 | self.inited_width = 1 | |
169 | ||
170 | def setnchannels(self, nchannels): | |
171 | self.nchannels = nchannels | |
172 | self.inited_nchannels = 1 | |
173 | ||
174 | def writeframes(self, data): | |
175 | if not (self.inited_outrate and self.inited_width and self.inited_nchannels): | |
176 | raise error, 'params not specified' | |
177 | if not self.port: | |
178 | import sunaudiodev, SUNAUDIODEV | |
179 | self.port = sunaudiodev.open('w') | |
180 | info = self.port.getinfo() | |
181 | info.o_sample_rate = self.outrate | |
182 | info.o_channels = self.nchannels | |
183 | if self.sampwidth == 0: | |
184 | info.o_precision = 8 | |
185 | self.o_encoding = SUNAUDIODEV.ENCODING_ULAW | |
186 | # XXX Hack, hack -- leave defaults | |
187 | else: | |
188 | info.o_precision = 8 * self.sampwidth | |
189 | info.o_encoding = SUNAUDIODEV.ENCODING_LINEAR | |
190 | self.port.setinfo(info) | |
191 | if self.converter: | |
192 | data = self.converter(data) | |
193 | self.port.write(data) | |
194 | ||
195 | def wait(self): | |
196 | if not self.port: | |
197 | return | |
198 | self.port.drain() | |
199 | self.stop() | |
200 | ||
201 | def stop(self): | |
202 | if self.port: | |
203 | self.port.flush() | |
204 | self.port.close() | |
205 | self.port = None | |
206 | ||
207 | def getfilled(self): | |
208 | if self.port: | |
209 | return self.port.obufcount() | |
210 | else: | |
211 | return 0 | |
212 | ||
213 | ## # Nobody remembers what this method does, and it's broken. :-( | |
214 | ## def getfillable(self): | |
215 | ## return BUFFERSIZE - self.getfilled() | |
216 | ||
217 | def AudioDev(): | |
218 | # Dynamically try to import and use a platform specific module. | |
219 | try: | |
220 | import al | |
221 | except ImportError: | |
222 | try: | |
223 | import sunaudiodev | |
224 | return Play_Audio_sun() | |
225 | except ImportError: | |
226 | try: | |
227 | import Audio_mac | |
228 | except ImportError: | |
229 | raise error, 'no audio device' | |
230 | else: | |
231 | return Audio_mac.Play_Audio_mac() | |
232 | else: | |
233 | return Play_Audio_sgi() | |
234 | ||
235 | def test(fn = None): | |
236 | import sys | |
237 | if sys.argv[1:]: | |
238 | fn = sys.argv[1] | |
239 | else: | |
240 | fn = 'f:just samples:just.aif' | |
241 | import aifc | |
242 | af = aifc.open(fn, 'r') | |
243 | print fn, af.getparams() | |
244 | p = AudioDev() | |
245 | p.setoutrate(af.getframerate()) | |
246 | p.setsampwidth(af.getsampwidth()) | |
247 | p.setnchannels(af.getnchannels()) | |
248 | BUFSIZ = af.getframerate()/af.getsampwidth()/af.getnchannels() | |
249 | while 1: | |
250 | data = af.readframes(BUFSIZ) | |
251 | if not data: break | |
252 | print len(data) | |
253 | p.writeframes(data) | |
254 | p.wait() | |
255 | ||
256 | if __name__ == '__main__': | |
257 | test() |