Initial commit of OpenSPARC T2 design and verification files.
[OpenSPARC-T2-DV] / tools / src / nas,5.n2.os.2 / lib / python / lib / python2.4 / sunau.py
CommitLineData
86530b38
AT
1"""Stuff to parse Sun and NeXT audio files.
2
3An audio file consists of a header followed by the data. The structure
4of the header is as follows.
5
6 +---------------+
7 | magic word |
8 +---------------+
9 | header size |
10 +---------------+
11 | data size |
12 +---------------+
13 | encoding |
14 +---------------+
15 | sample rate |
16 +---------------+
17 | # of channels |
18 +---------------+
19 | info |
20 | |
21 +---------------+
22
23The magic word consists of the 4 characters '.snd'. Apart from the
24info field, all header fields are 4 bytes in size. They are all
2532-bit unsigned integers encoded in big-endian byte order.
26
27The header size really gives the start of the data.
28The data size is the physical size of the data. From the other
29parameters the number of frames can be calculated.
30The encoding gives the way in which audio samples are encoded.
31Possible values are listed below.
32The info field currently consists of an ASCII string giving a
33human-readable description of the audio file. The info field is
34padded with NUL bytes to the header size.
35
36Usage.
37
38Reading audio files:
39 f = sunau.open(file, 'r')
40where file is either the name of a file or an open file pointer.
41The open file pointer must have methods read(), seek(), and close().
42When the setpos() and rewind() methods are not used, the seek()
43method is not necessary.
44
45This returns an instance of a class with the following public methods:
46 getnchannels() -- returns number of audio channels (1 for
47 mono, 2 for stereo)
48 getsampwidth() -- returns sample width in bytes
49 getframerate() -- returns sampling frequency
50 getnframes() -- returns number of audio frames
51 getcomptype() -- returns compression type ('NONE' or 'ULAW')
52 getcompname() -- returns human-readable version of
53 compression type ('not compressed' matches 'NONE')
54 getparams() -- returns a tuple consisting of all of the
55 above in the above order
56 getmarkers() -- returns None (for compatibility with the
57 aifc module)
58 getmark(id) -- raises an error since the mark does not
59 exist (for compatibility with the aifc module)
60 readframes(n) -- returns at most n frames of audio
61 rewind() -- rewind to the beginning of the audio stream
62 setpos(pos) -- seek to the specified position
63 tell() -- return the current position
64 close() -- close the instance (make it unusable)
65The position returned by tell() and the position given to setpos()
66are compatible and have nothing to do with the actual position in the
67file.
68The close() method is called automatically when the class instance
69is destroyed.
70
71Writing audio files:
72 f = sunau.open(file, 'w')
73where file is either the name of a file or an open file pointer.
74The open file pointer must have methods write(), tell(), seek(), and
75close().
76
77This returns an instance of a class with the following public methods:
78 setnchannels(n) -- set the number of channels
79 setsampwidth(n) -- set the sample width
80 setframerate(n) -- set the frame rate
81 setnframes(n) -- set the number of frames
82 setcomptype(type, name)
83 -- set the compression type and the
84 human-readable compression type
85 setparams(tuple)-- set all parameters at once
86 tell() -- return current position in output file
87 writeframesraw(data)
88 -- write audio frames without pathing up the
89 file header
90 writeframes(data)
91 -- write audio frames and patch up the file header
92 close() -- patch up the file header and close the
93 output file
94You should set the parameters before the first writeframesraw or
95writeframes. The total number of frames does not need to be set,
96but when it is set to the correct value, the header does not have to
97be patched up.
98It is best to first set all parameters, perhaps possibly the
99compression type, and then write audio frames using writeframesraw.
100When all frames have been written, either call writeframes('') or
101close() to patch up the sizes in the header.
102The close() method is called automatically when the class instance
103is destroyed.
104"""
105
106# from <multimedia/audio_filehdr.h>
107AUDIO_FILE_MAGIC = 0x2e736e64
108AUDIO_FILE_ENCODING_MULAW_8 = 1
109AUDIO_FILE_ENCODING_LINEAR_8 = 2
110AUDIO_FILE_ENCODING_LINEAR_16 = 3
111AUDIO_FILE_ENCODING_LINEAR_24 = 4
112AUDIO_FILE_ENCODING_LINEAR_32 = 5
113AUDIO_FILE_ENCODING_FLOAT = 6
114AUDIO_FILE_ENCODING_DOUBLE = 7
115AUDIO_FILE_ENCODING_ADPCM_G721 = 23
116AUDIO_FILE_ENCODING_ADPCM_G722 = 24
117AUDIO_FILE_ENCODING_ADPCM_G723_3 = 25
118AUDIO_FILE_ENCODING_ADPCM_G723_5 = 26
119AUDIO_FILE_ENCODING_ALAW_8 = 27
120
121# from <multimedia/audio_hdr.h>
122AUDIO_UNKNOWN_SIZE = 0xFFFFFFFFL # ((unsigned)(~0))
123
124_simple_encodings = [AUDIO_FILE_ENCODING_MULAW_8,
125 AUDIO_FILE_ENCODING_LINEAR_8,
126 AUDIO_FILE_ENCODING_LINEAR_16,
127 AUDIO_FILE_ENCODING_LINEAR_24,
128 AUDIO_FILE_ENCODING_LINEAR_32,
129 AUDIO_FILE_ENCODING_ALAW_8]
130
131class Error(Exception):
132 pass
133
134def _read_u32(file):
135 x = 0L
136 for i in range(4):
137 byte = file.read(1)
138 if byte == '':
139 raise EOFError
140 x = x*256 + ord(byte)
141 return x
142
143def _write_u32(file, x):
144 data = []
145 for i in range(4):
146 d, m = divmod(x, 256)
147 data.insert(0, m)
148 x = d
149 for i in range(4):
150 file.write(chr(int(data[i])))
151
152class Au_read:
153
154 def __init__(self, f):
155 if type(f) == type(''):
156 import __builtin__
157 f = __builtin__.open(f, 'rb')
158 self.initfp(f)
159
160 def __del__(self):
161 if self._file:
162 self.close()
163
164 def initfp(self, file):
165 self._file = file
166 self._soundpos = 0
167 magic = int(_read_u32(file))
168 if magic != AUDIO_FILE_MAGIC:
169 raise Error, 'bad magic number'
170 self._hdr_size = int(_read_u32(file))
171 if self._hdr_size < 24:
172 raise Error, 'header size too small'
173 if self._hdr_size > 100:
174 raise Error, 'header size ridiculously large'
175 self._data_size = _read_u32(file)
176 if self._data_size != AUDIO_UNKNOWN_SIZE:
177 self._data_size = int(self._data_size)
178 self._encoding = int(_read_u32(file))
179 if self._encoding not in _simple_encodings:
180 raise Error, 'encoding not (yet) supported'
181 if self._encoding in (AUDIO_FILE_ENCODING_MULAW_8,
182 AUDIO_FILE_ENCODING_ALAW_8):
183 self._sampwidth = 2
184 self._framesize = 1
185 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_8:
186 self._framesize = self._sampwidth = 1
187 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_16:
188 self._framesize = self._sampwidth = 2
189 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_24:
190 self._framesize = self._sampwidth = 3
191 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_32:
192 self._framesize = self._sampwidth = 4
193 else:
194 raise Error, 'unknown encoding'
195 self._framerate = int(_read_u32(file))
196 self._nchannels = int(_read_u32(file))
197 self._framesize = self._framesize * self._nchannels
198 if self._hdr_size > 24:
199 self._info = file.read(self._hdr_size - 24)
200 for i in range(len(self._info)):
201 if self._info[i] == '\0':
202 self._info = self._info[:i]
203 break
204 else:
205 self._info = ''
206
207 def getfp(self):
208 return self._file
209
210 def getnchannels(self):
211 return self._nchannels
212
213 def getsampwidth(self):
214 return self._sampwidth
215
216 def getframerate(self):
217 return self._framerate
218
219 def getnframes(self):
220 if self._data_size == AUDIO_UNKNOWN_SIZE:
221 return AUDIO_UNKNOWN_SIZE
222 if self._encoding in _simple_encodings:
223 return self._data_size / self._framesize
224 return 0 # XXX--must do some arithmetic here
225
226 def getcomptype(self):
227 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
228 return 'ULAW'
229 elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
230 return 'ALAW'
231 else:
232 return 'NONE'
233
234 def getcompname(self):
235 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
236 return 'CCITT G.711 u-law'
237 elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
238 return 'CCITT G.711 A-law'
239 else:
240 return 'not compressed'
241
242 def getparams(self):
243 return self.getnchannels(), self.getsampwidth(), \
244 self.getframerate(), self.getnframes(), \
245 self.getcomptype(), self.getcompname()
246
247 def getmarkers(self):
248 return None
249
250 def getmark(self, id):
251 raise Error, 'no marks'
252
253 def readframes(self, nframes):
254 if self._encoding in _simple_encodings:
255 if nframes == AUDIO_UNKNOWN_SIZE:
256 data = self._file.read()
257 else:
258 data = self._file.read(nframes * self._framesize * self._nchannels)
259 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
260 import audioop
261 data = audioop.ulaw2lin(data, self._sampwidth)
262 return data
263 return None # XXX--not implemented yet
264
265 def rewind(self):
266 self._soundpos = 0
267 self._file.seek(self._hdr_size)
268
269 def tell(self):
270 return self._soundpos
271
272 def setpos(self, pos):
273 if pos < 0 or pos > self.getnframes():
274 raise Error, 'position not in range'
275 self._file.seek(pos * self._framesize + self._hdr_size)
276 self._soundpos = pos
277
278 def close(self):
279 self._file = None
280
281class Au_write:
282
283 def __init__(self, f):
284 if type(f) == type(''):
285 import __builtin__
286 f = __builtin__.open(f, 'wb')
287 self.initfp(f)
288
289 def __del__(self):
290 if self._file:
291 self.close()
292
293 def initfp(self, file):
294 self._file = file
295 self._framerate = 0
296 self._nchannels = 0
297 self._sampwidth = 0
298 self._framesize = 0
299 self._nframes = AUDIO_UNKNOWN_SIZE
300 self._nframeswritten = 0
301 self._datawritten = 0
302 self._datalength = 0
303 self._info = ''
304 self._comptype = 'ULAW' # default is U-law
305
306 def setnchannels(self, nchannels):
307 if self._nframeswritten:
308 raise Error, 'cannot change parameters after starting to write'
309 if nchannels not in (1, 2, 4):
310 raise Error, 'only 1, 2, or 4 channels supported'
311 self._nchannels = nchannels
312
313 def getnchannels(self):
314 if not self._nchannels:
315 raise Error, 'number of channels not set'
316 return self._nchannels
317
318 def setsampwidth(self, sampwidth):
319 if self._nframeswritten:
320 raise Error, 'cannot change parameters after starting to write'
321 if sampwidth not in (1, 2, 4):
322 raise Error, 'bad sample width'
323 self._sampwidth = sampwidth
324
325 def getsampwidth(self):
326 if not self._framerate:
327 raise Error, 'sample width not specified'
328 return self._sampwidth
329
330 def setframerate(self, framerate):
331 if self._nframeswritten:
332 raise Error, 'cannot change parameters after starting to write'
333 self._framerate = framerate
334
335 def getframerate(self):
336 if not self._framerate:
337 raise Error, 'frame rate not set'
338 return self._framerate
339
340 def setnframes(self, nframes):
341 if self._nframeswritten:
342 raise Error, 'cannot change parameters after starting to write'
343 if nframes < 0:
344 raise Error, '# of frames cannot be negative'
345 self._nframes = nframes
346
347 def getnframes(self):
348 return self._nframeswritten
349
350 def setcomptype(self, type, name):
351 if type in ('NONE', 'ULAW'):
352 self._comptype = type
353 else:
354 raise Error, 'unknown compression type'
355
356 def getcomptype(self):
357 return self._comptype
358
359 def getcompname(self):
360 if self._comptype == 'ULAW':
361 return 'CCITT G.711 u-law'
362 elif self._comptype == 'ALAW':
363 return 'CCITT G.711 A-law'
364 else:
365 return 'not compressed'
366
367 def setparams(self, (nchannels, sampwidth, framerate, nframes, comptype, compname)):
368 self.setnchannels(nchannels)
369 self.setsampwidth(sampwidth)
370 self.setframerate(framerate)
371 self.setnframes(nframes)
372 self.setcomptype(comptype, compname)
373
374 def getparams(self):
375 return self.getnchannels(), self.getsampwidth(), \
376 self.getframerate(), self.getnframes(), \
377 self.getcomptype(), self.getcompname()
378
379 def tell(self):
380 return self._nframeswritten
381
382 def writeframesraw(self, data):
383 self._ensure_header_written()
384 nframes = len(data) / self._framesize
385 if self._comptype == 'ULAW':
386 import audioop
387 data = audioop.lin2ulaw(data, self._sampwidth)
388 self._file.write(data)
389 self._nframeswritten = self._nframeswritten + nframes
390 self._datawritten = self._datawritten + len(data)
391
392 def writeframes(self, data):
393 self.writeframesraw(data)
394 if self._nframeswritten != self._nframes or \
395 self._datalength != self._datawritten:
396 self._patchheader()
397
398 def close(self):
399 self._ensure_header_written()
400 if self._nframeswritten != self._nframes or \
401 self._datalength != self._datawritten:
402 self._patchheader()
403 self._file.flush()
404 self._file = None
405
406 #
407 # private methods
408 #
409
410 def _ensure_header_written(self):
411 if not self._nframeswritten:
412 if not self._nchannels:
413 raise Error, '# of channels not specified'
414 if not self._sampwidth:
415 raise Error, 'sample width not specified'
416 if not self._framerate:
417 raise Error, 'frame rate not specified'
418 self._write_header()
419
420 def _write_header(self):
421 if self._comptype == 'NONE':
422 if self._sampwidth == 1:
423 encoding = AUDIO_FILE_ENCODING_LINEAR_8
424 self._framesize = 1
425 elif self._sampwidth == 2:
426 encoding = AUDIO_FILE_ENCODING_LINEAR_16
427 self._framesize = 2
428 elif self._sampwidth == 4:
429 encoding = AUDIO_FILE_ENCODING_LINEAR_32
430 self._framesize = 4
431 else:
432 raise Error, 'internal error'
433 elif self._comptype == 'ULAW':
434 encoding = AUDIO_FILE_ENCODING_MULAW_8
435 self._framesize = 1
436 else:
437 raise Error, 'internal error'
438 self._framesize = self._framesize * self._nchannels
439 _write_u32(self._file, AUDIO_FILE_MAGIC)
440 header_size = 25 + len(self._info)
441 header_size = (header_size + 7) & ~7
442 _write_u32(self._file, header_size)
443 if self._nframes == AUDIO_UNKNOWN_SIZE:
444 length = AUDIO_UNKNOWN_SIZE
445 else:
446 length = self._nframes * self._framesize
447 _write_u32(self._file, length)
448 self._datalength = length
449 _write_u32(self._file, encoding)
450 _write_u32(self._file, self._framerate)
451 _write_u32(self._file, self._nchannels)
452 self._file.write(self._info)
453 self._file.write('\0'*(header_size - len(self._info) - 24))
454
455 def _patchheader(self):
456 self._file.seek(8)
457 _write_u32(self._file, self._datawritten)
458 self._datalength = self._datawritten
459 self._file.seek(0, 2)
460
461def open(f, mode=None):
462 if mode is None:
463 if hasattr(f, 'mode'):
464 mode = f.mode
465 else:
466 mode = 'rb'
467 if mode in ('r', 'rb'):
468 return Au_read(f)
469 elif mode in ('w', 'wb'):
470 return Au_write(f)
471 else:
472 raise Error, "mode must be 'r', 'rb', 'w', or 'wb'"
473
474openfp = open