blob: b53044d22b1648cf0cd680e1e7e3ddebf82f00fd [file] [log] [blame]
Guido van Rossume7b146f2000-02-04 15:28:42 +00001"""Stuff to parse Sun and NeXT audio files.
2
Fred Drake24c532a2000-10-06 20:28:46 +00003An audio file consists of a header followed by the data. The structure
Guido van Rossume7b146f2000-02-04 15:28:42 +00004of 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
Fred Drake24c532a2000-10-06 20:28:46 +000029parameters the number of frames can be calculated.
Guido van Rossume7b146f2000-02-04 15:28:42 +000030The 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:
Tim Peters495ad3c2001-01-15 01:36:40 +000046 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)
Guido van Rossume7b146f2000-02-04 15:28:42 +000065The position returned by tell() and the position given to setpos()
Thomas Wouters7e474022000-07-16 12:04:32 +000066are compatible and have nothing to do with the actual position in the
Guido van Rossume7b146f2000-02-04 15:28:42 +000067file.
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:
Tim Peters495ad3c2001-01-15 01:36:40 +000078 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
Guido van Rossume7b146f2000-02-04 15:28:42 +000082 setcomptype(type, name)
Tim Peters495ad3c2001-01-15 01:36:40 +000083 -- set the compression type and the
84 human-readable compression type
Guido van Rossume7b146f2000-02-04 15:28:42 +000085 setparams(tuple)-- set all parameters at once
Tim Peters495ad3c2001-01-15 01:36:40 +000086 tell() -- return current position in output file
Guido van Rossume7b146f2000-02-04 15:28:42 +000087 writeframesraw(data)
Tim Peters495ad3c2001-01-15 01:36:40 +000088 -- write audio frames without pathing up the
89 file header
Guido van Rossume7b146f2000-02-04 15:28:42 +000090 writeframes(data)
Tim Peters495ad3c2001-01-15 01:36:40 +000091 -- write audio frames and patch up the file header
92 close() -- patch up the file header and close the
93 output file
Guido van Rossume7b146f2000-02-04 15:28:42 +000094You 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"""
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000105
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>
Tim Peters495ad3c2001-01-15 01:36:40 +0000122AUDIO_UNKNOWN_SIZE = 0xFFFFFFFFL # ((unsigned)(~0))
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000123
124_simple_encodings = [AUDIO_FILE_ENCODING_MULAW_8,
Tim Peters495ad3c2001-01-15 01:36:40 +0000125 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]
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000130
Fred Drake9b8d8012000-08-17 04:45:13 +0000131class Error(Exception):
Tim Peters495ad3c2001-01-15 01:36:40 +0000132 pass
Sjoerd Mullenderf33a69f1995-08-14 07:49:51 +0000133
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000134def _read_u32(file):
Tim Peters495ad3c2001-01-15 01:36:40 +0000135 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
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000142
143def _write_u32(file, x):
Tim Peters495ad3c2001-01-15 01:36:40 +0000144 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])))
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000151
152class Au_read:
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000153
Tim Peters495ad3c2001-01-15 01:36:40 +0000154 def __init__(self, f):
155 if type(f) == type(''):
156 import __builtin__
157 f = __builtin__.open(f, 'rb')
158 self.initfp(f)
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000159
Tim Peters495ad3c2001-01-15 01:36:40 +0000160 def __del__(self):
161 if self._file:
162 self.close()
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000163
Tim Peters495ad3c2001-01-15 01:36:40 +0000164 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 = ''
Serhiy Storchakacfa55742013-09-28 21:31:36 +0300206 try:
207 self._data_pos = file.tell()
208 except (AttributeError, IOError):
209 self._data_pos = None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000210
Tim Peters495ad3c2001-01-15 01:36:40 +0000211 def getfp(self):
212 return self._file
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000213
Tim Peters495ad3c2001-01-15 01:36:40 +0000214 def getnchannels(self):
215 return self._nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000216
Tim Peters495ad3c2001-01-15 01:36:40 +0000217 def getsampwidth(self):
218 return self._sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000219
Tim Peters495ad3c2001-01-15 01:36:40 +0000220 def getframerate(self):
221 return self._framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000222
Tim Peters495ad3c2001-01-15 01:36:40 +0000223 def getnframes(self):
224 if self._data_size == AUDIO_UNKNOWN_SIZE:
225 return AUDIO_UNKNOWN_SIZE
226 if self._encoding in _simple_encodings:
Terry Jan Reedy16252722014-01-29 00:15:59 -0500227 return self._data_size // self._framesize
Tim Peters495ad3c2001-01-15 01:36:40 +0000228 return 0 # XXX--must do some arithmetic here
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000229
Tim Peters495ad3c2001-01-15 01:36:40 +0000230 def getcomptype(self):
231 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
232 return 'ULAW'
233 elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
234 return 'ALAW'
235 else:
236 return 'NONE'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000237
Tim Peters495ad3c2001-01-15 01:36:40 +0000238 def getcompname(self):
239 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
240 return 'CCITT G.711 u-law'
241 elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
242 return 'CCITT G.711 A-law'
243 else:
244 return 'not compressed'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000245
Tim Peters495ad3c2001-01-15 01:36:40 +0000246 def getparams(self):
247 return self.getnchannels(), self.getsampwidth(), \
248 self.getframerate(), self.getnframes(), \
249 self.getcomptype(), self.getcompname()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000250
Tim Peters495ad3c2001-01-15 01:36:40 +0000251 def getmarkers(self):
252 return None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000253
Tim Peters495ad3c2001-01-15 01:36:40 +0000254 def getmark(self, id):
255 raise Error, 'no marks'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000256
Tim Peters495ad3c2001-01-15 01:36:40 +0000257 def readframes(self, nframes):
258 if self._encoding in _simple_encodings:
259 if nframes == AUDIO_UNKNOWN_SIZE:
260 data = self._file.read()
261 else:
Serhiy Storchakacfa55742013-09-28 21:31:36 +0300262 data = self._file.read(nframes * self._framesize)
263 self._soundpos += len(data) // self._framesize
Tim Peters495ad3c2001-01-15 01:36:40 +0000264 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
265 import audioop
266 data = audioop.ulaw2lin(data, self._sampwidth)
267 return data
268 return None # XXX--not implemented yet
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000269
Tim Peters495ad3c2001-01-15 01:36:40 +0000270 def rewind(self):
Serhiy Storchakacfa55742013-09-28 21:31:36 +0300271 if self._data_pos is None:
272 raise IOError('cannot seek')
273 self._file.seek(self._data_pos)
Tim Peters495ad3c2001-01-15 01:36:40 +0000274 self._soundpos = 0
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000275
Tim Peters495ad3c2001-01-15 01:36:40 +0000276 def tell(self):
277 return self._soundpos
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000278
Tim Peters495ad3c2001-01-15 01:36:40 +0000279 def setpos(self, pos):
280 if pos < 0 or pos > self.getnframes():
281 raise Error, 'position not in range'
Serhiy Storchakacfa55742013-09-28 21:31:36 +0300282 if self._data_pos is None:
283 raise IOError('cannot seek')
284 self._file.seek(self._data_pos + pos * self._framesize)
Tim Peters495ad3c2001-01-15 01:36:40 +0000285 self._soundpos = pos
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000286
Tim Peters495ad3c2001-01-15 01:36:40 +0000287 def close(self):
288 self._file = None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000289
290class Au_write:
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000291
Tim Peters495ad3c2001-01-15 01:36:40 +0000292 def __init__(self, f):
293 if type(f) == type(''):
294 import __builtin__
295 f = __builtin__.open(f, 'wb')
296 self.initfp(f)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000297
Tim Peters495ad3c2001-01-15 01:36:40 +0000298 def __del__(self):
299 if self._file:
300 self.close()
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000301
Tim Peters495ad3c2001-01-15 01:36:40 +0000302 def initfp(self, file):
303 self._file = file
304 self._framerate = 0
305 self._nchannels = 0
306 self._sampwidth = 0
307 self._framesize = 0
308 self._nframes = AUDIO_UNKNOWN_SIZE
309 self._nframeswritten = 0
310 self._datawritten = 0
311 self._datalength = 0
312 self._info = ''
313 self._comptype = 'ULAW' # default is U-law
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000314
Tim Peters495ad3c2001-01-15 01:36:40 +0000315 def setnchannels(self, nchannels):
316 if self._nframeswritten:
317 raise Error, 'cannot change parameters after starting to write'
318 if nchannels not in (1, 2, 4):
319 raise Error, 'only 1, 2, or 4 channels supported'
320 self._nchannels = nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000321
Tim Peters495ad3c2001-01-15 01:36:40 +0000322 def getnchannels(self):
323 if not self._nchannels:
324 raise Error, 'number of channels not set'
325 return self._nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000326
Tim Peters495ad3c2001-01-15 01:36:40 +0000327 def setsampwidth(self, sampwidth):
328 if self._nframeswritten:
329 raise Error, 'cannot change parameters after starting to write'
330 if sampwidth not in (1, 2, 4):
331 raise Error, 'bad sample width'
332 self._sampwidth = sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000333
Tim Peters495ad3c2001-01-15 01:36:40 +0000334 def getsampwidth(self):
335 if not self._framerate:
336 raise Error, 'sample width not specified'
337 return self._sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000338
Tim Peters495ad3c2001-01-15 01:36:40 +0000339 def setframerate(self, framerate):
340 if self._nframeswritten:
341 raise Error, 'cannot change parameters after starting to write'
342 self._framerate = framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000343
Tim Peters495ad3c2001-01-15 01:36:40 +0000344 def getframerate(self):
345 if not self._framerate:
346 raise Error, 'frame rate not set'
347 return self._framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000348
Tim Peters495ad3c2001-01-15 01:36:40 +0000349 def setnframes(self, nframes):
350 if self._nframeswritten:
351 raise Error, 'cannot change parameters after starting to write'
352 if nframes < 0:
353 raise Error, '# of frames cannot be negative'
354 self._nframes = nframes
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000355
Tim Peters495ad3c2001-01-15 01:36:40 +0000356 def getnframes(self):
357 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000358
Tim Peters495ad3c2001-01-15 01:36:40 +0000359 def setcomptype(self, type, name):
360 if type in ('NONE', 'ULAW'):
361 self._comptype = type
362 else:
363 raise Error, 'unknown compression type'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000364
Tim Peters495ad3c2001-01-15 01:36:40 +0000365 def getcomptype(self):
366 return self._comptype
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000367
Tim Peters495ad3c2001-01-15 01:36:40 +0000368 def getcompname(self):
369 if self._comptype == 'ULAW':
370 return 'CCITT G.711 u-law'
371 elif self._comptype == 'ALAW':
372 return 'CCITT G.711 A-law'
373 else:
374 return 'not compressed'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000375
Antoine Pitroub9d49632010-01-04 23:22:44 +0000376 def setparams(self, params):
377 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Tim Peters495ad3c2001-01-15 01:36:40 +0000378 self.setnchannels(nchannels)
379 self.setsampwidth(sampwidth)
380 self.setframerate(framerate)
381 self.setnframes(nframes)
382 self.setcomptype(comptype, compname)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000383
Tim Peters495ad3c2001-01-15 01:36:40 +0000384 def getparams(self):
385 return self.getnchannels(), self.getsampwidth(), \
386 self.getframerate(), self.getnframes(), \
387 self.getcomptype(), self.getcompname()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000388
Tim Peters495ad3c2001-01-15 01:36:40 +0000389 def tell(self):
390 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000391
Tim Peters495ad3c2001-01-15 01:36:40 +0000392 def writeframesraw(self, data):
393 self._ensure_header_written()
Tim Peters495ad3c2001-01-15 01:36:40 +0000394 if self._comptype == 'ULAW':
395 import audioop
396 data = audioop.lin2ulaw(data, self._sampwidth)
Serhiy Storchakacfa55742013-09-28 21:31:36 +0300397 nframes = len(data) // self._framesize
Tim Peters495ad3c2001-01-15 01:36:40 +0000398 self._file.write(data)
399 self._nframeswritten = self._nframeswritten + nframes
400 self._datawritten = self._datawritten + len(data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000401
Tim Peters495ad3c2001-01-15 01:36:40 +0000402 def writeframes(self, data):
403 self.writeframesraw(data)
404 if self._nframeswritten != self._nframes or \
405 self._datalength != self._datawritten:
406 self._patchheader()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000407
Tim Peters495ad3c2001-01-15 01:36:40 +0000408 def close(self):
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300409 if self._file:
410 try:
411 self._ensure_header_written()
412 if self._nframeswritten != self._nframes or \
413 self._datalength != self._datawritten:
414 self._patchheader()
415 self._file.flush()
416 finally:
417 self._file = None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000418
Tim Peters495ad3c2001-01-15 01:36:40 +0000419 #
420 # private methods
421 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000422
Tim Peters495ad3c2001-01-15 01:36:40 +0000423 def _ensure_header_written(self):
424 if not self._nframeswritten:
425 if not self._nchannels:
426 raise Error, '# of channels not specified'
427 if not self._sampwidth:
428 raise Error, 'sample width not specified'
429 if not self._framerate:
430 raise Error, 'frame rate not specified'
431 self._write_header()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000432
Tim Peters495ad3c2001-01-15 01:36:40 +0000433 def _write_header(self):
434 if self._comptype == 'NONE':
435 if self._sampwidth == 1:
436 encoding = AUDIO_FILE_ENCODING_LINEAR_8
437 self._framesize = 1
438 elif self._sampwidth == 2:
439 encoding = AUDIO_FILE_ENCODING_LINEAR_16
440 self._framesize = 2
441 elif self._sampwidth == 4:
442 encoding = AUDIO_FILE_ENCODING_LINEAR_32
443 self._framesize = 4
444 else:
445 raise Error, 'internal error'
446 elif self._comptype == 'ULAW':
447 encoding = AUDIO_FILE_ENCODING_MULAW_8
448 self._framesize = 1
449 else:
450 raise Error, 'internal error'
451 self._framesize = self._framesize * self._nchannels
452 _write_u32(self._file, AUDIO_FILE_MAGIC)
453 header_size = 25 + len(self._info)
454 header_size = (header_size + 7) & ~7
455 _write_u32(self._file, header_size)
456 if self._nframes == AUDIO_UNKNOWN_SIZE:
457 length = AUDIO_UNKNOWN_SIZE
458 else:
459 length = self._nframes * self._framesize
Serhiy Storchakacfa55742013-09-28 21:31:36 +0300460 try:
461 self._form_length_pos = self._file.tell()
462 except (AttributeError, IOError):
463 self._form_length_pos = None
Tim Peters495ad3c2001-01-15 01:36:40 +0000464 _write_u32(self._file, length)
465 self._datalength = length
466 _write_u32(self._file, encoding)
467 _write_u32(self._file, self._framerate)
468 _write_u32(self._file, self._nchannels)
469 self._file.write(self._info)
470 self._file.write('\0'*(header_size - len(self._info) - 24))
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000471
Tim Peters495ad3c2001-01-15 01:36:40 +0000472 def _patchheader(self):
Serhiy Storchakacfa55742013-09-28 21:31:36 +0300473 if self._form_length_pos is None:
474 raise IOError('cannot seek')
475 self._file.seek(self._form_length_pos)
Tim Peters495ad3c2001-01-15 01:36:40 +0000476 _write_u32(self._file, self._datawritten)
477 self._datalength = self._datawritten
478 self._file.seek(0, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000479
Fred Drake43161351999-06-22 21:23:23 +0000480def open(f, mode=None):
Tim Peters495ad3c2001-01-15 01:36:40 +0000481 if mode is None:
482 if hasattr(f, 'mode'):
483 mode = f.mode
484 else:
485 mode = 'rb'
486 if mode in ('r', 'rb'):
487 return Au_read(f)
488 elif mode in ('w', 'wb'):
489 return Au_write(f)
490 else:
491 raise Error, "mode must be 'r', 'rb', 'w', or 'wb'"
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000492
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000493openfp = open