blob: b312501b35a42b96b1bb3b23556fda2550b0d232 [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>
Guido van Rossume2a383d2007-01-15 16:59:06 +0000122AUDIO_UNKNOWN_SIZE = 0xFFFFFFFF # ((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):
Guido van Rossume2a383d2007-01-15 16:59:06 +0000135 x = 0
Tim Peters495ad3c2001-01-15 01:36:40 +0000136 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(''):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000156 import builtins
157 f = builtins.open(f, 'rb')
Tim Peters495ad3c2001-01-15 01:36:40 +0000158 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:
Collin Winterce36ad82007-08-30 01:19:48 +0000169 raise Error('bad magic number')
Tim Peters495ad3c2001-01-15 01:36:40 +0000170 self._hdr_size = int(_read_u32(file))
171 if self._hdr_size < 24:
Collin Winterce36ad82007-08-30 01:19:48 +0000172 raise Error('header size too small')
Tim Peters495ad3c2001-01-15 01:36:40 +0000173 if self._hdr_size > 100:
Collin Winterce36ad82007-08-30 01:19:48 +0000174 raise Error('header size ridiculously large')
Tim Peters495ad3c2001-01-15 01:36:40 +0000175 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:
Collin Winterce36ad82007-08-30 01:19:48 +0000180 raise Error('encoding not (yet) supported')
Tim Peters495ad3c2001-01-15 01:36:40 +0000181 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:
Collin Winterce36ad82007-08-30 01:19:48 +0000194 raise Error('unknown encoding')
Tim Peters495ad3c2001-01-15 01:36:40 +0000195 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 = ''
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000206
Tim Peters495ad3c2001-01-15 01:36:40 +0000207 def getfp(self):
208 return self._file
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000209
Tim Peters495ad3c2001-01-15 01:36:40 +0000210 def getnchannels(self):
211 return self._nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000212
Tim Peters495ad3c2001-01-15 01:36:40 +0000213 def getsampwidth(self):
214 return self._sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000215
Tim Peters495ad3c2001-01-15 01:36:40 +0000216 def getframerate(self):
217 return self._framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000218
Tim Peters495ad3c2001-01-15 01:36:40 +0000219 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
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000225
Tim Peters495ad3c2001-01-15 01:36:40 +0000226 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'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000233
Tim Peters495ad3c2001-01-15 01:36:40 +0000234 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'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000241
Tim Peters495ad3c2001-01-15 01:36:40 +0000242 def getparams(self):
243 return self.getnchannels(), self.getsampwidth(), \
244 self.getframerate(), self.getnframes(), \
245 self.getcomptype(), self.getcompname()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000246
Tim Peters495ad3c2001-01-15 01:36:40 +0000247 def getmarkers(self):
248 return None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000249
Tim Peters495ad3c2001-01-15 01:36:40 +0000250 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000251 raise Error('no marks')
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000252
Tim Peters495ad3c2001-01-15 01:36:40 +0000253 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
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000264
Tim Peters495ad3c2001-01-15 01:36:40 +0000265 def rewind(self):
266 self._soundpos = 0
267 self._file.seek(self._hdr_size)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000268
Tim Peters495ad3c2001-01-15 01:36:40 +0000269 def tell(self):
270 return self._soundpos
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000271
Tim Peters495ad3c2001-01-15 01:36:40 +0000272 def setpos(self, pos):
273 if pos < 0 or pos > self.getnframes():
Collin Winterce36ad82007-08-30 01:19:48 +0000274 raise Error('position not in range')
Tim Peters495ad3c2001-01-15 01:36:40 +0000275 self._file.seek(pos * self._framesize + self._hdr_size)
276 self._soundpos = pos
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000277
Tim Peters495ad3c2001-01-15 01:36:40 +0000278 def close(self):
279 self._file = None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000280
281class Au_write:
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000282
Tim Peters495ad3c2001-01-15 01:36:40 +0000283 def __init__(self, f):
284 if type(f) == type(''):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000285 import builtins
286 f = builtins.open(f, 'wb')
Tim Peters495ad3c2001-01-15 01:36:40 +0000287 self.initfp(f)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000288
Tim Peters495ad3c2001-01-15 01:36:40 +0000289 def __del__(self):
290 if self._file:
291 self.close()
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000292
Tim Peters495ad3c2001-01-15 01:36:40 +0000293 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
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000305
Tim Peters495ad3c2001-01-15 01:36:40 +0000306 def setnchannels(self, nchannels):
307 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000308 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000309 if nchannels not in (1, 2, 4):
Collin Winterce36ad82007-08-30 01:19:48 +0000310 raise Error('only 1, 2, or 4 channels supported')
Tim Peters495ad3c2001-01-15 01:36:40 +0000311 self._nchannels = nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000312
Tim Peters495ad3c2001-01-15 01:36:40 +0000313 def getnchannels(self):
314 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000315 raise Error('number of channels not set')
Tim Peters495ad3c2001-01-15 01:36:40 +0000316 return self._nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000317
Tim Peters495ad3c2001-01-15 01:36:40 +0000318 def setsampwidth(self, sampwidth):
319 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000320 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000321 if sampwidth not in (1, 2, 4):
Collin Winterce36ad82007-08-30 01:19:48 +0000322 raise Error('bad sample width')
Tim Peters495ad3c2001-01-15 01:36:40 +0000323 self._sampwidth = sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000324
Tim Peters495ad3c2001-01-15 01:36:40 +0000325 def getsampwidth(self):
326 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000327 raise Error('sample width not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000328 return self._sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000329
Tim Peters495ad3c2001-01-15 01:36:40 +0000330 def setframerate(self, framerate):
331 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000332 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000333 self._framerate = framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000334
Tim Peters495ad3c2001-01-15 01:36:40 +0000335 def getframerate(self):
336 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000337 raise Error('frame rate not set')
Tim Peters495ad3c2001-01-15 01:36:40 +0000338 return self._framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000339
Tim Peters495ad3c2001-01-15 01:36:40 +0000340 def setnframes(self, nframes):
341 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000342 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000343 if nframes < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000344 raise Error('# of frames cannot be negative')
Tim Peters495ad3c2001-01-15 01:36:40 +0000345 self._nframes = nframes
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000346
Tim Peters495ad3c2001-01-15 01:36:40 +0000347 def getnframes(self):
348 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000349
Tim Peters495ad3c2001-01-15 01:36:40 +0000350 def setcomptype(self, type, name):
351 if type in ('NONE', 'ULAW'):
352 self._comptype = type
353 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000354 raise Error('unknown compression type')
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000355
Tim Peters495ad3c2001-01-15 01:36:40 +0000356 def getcomptype(self):
357 return self._comptype
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000358
Tim Peters495ad3c2001-01-15 01:36:40 +0000359 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'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000366
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000367 def setparams(self, params):
368 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Tim Peters495ad3c2001-01-15 01:36:40 +0000369 self.setnchannels(nchannels)
370 self.setsampwidth(sampwidth)
371 self.setframerate(framerate)
372 self.setnframes(nframes)
373 self.setcomptype(comptype, compname)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000374
Tim Peters495ad3c2001-01-15 01:36:40 +0000375 def getparams(self):
376 return self.getnchannels(), self.getsampwidth(), \
377 self.getframerate(), self.getnframes(), \
378 self.getcomptype(), self.getcompname()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000379
Tim Peters495ad3c2001-01-15 01:36:40 +0000380 def tell(self):
381 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000382
Tim Peters495ad3c2001-01-15 01:36:40 +0000383 def writeframesraw(self, data):
384 self._ensure_header_written()
385 nframes = len(data) / self._framesize
386 if self._comptype == 'ULAW':
387 import audioop
388 data = audioop.lin2ulaw(data, self._sampwidth)
389 self._file.write(data)
390 self._nframeswritten = self._nframeswritten + nframes
391 self._datawritten = self._datawritten + len(data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000392
Tim Peters495ad3c2001-01-15 01:36:40 +0000393 def writeframes(self, data):
394 self.writeframesraw(data)
395 if self._nframeswritten != self._nframes or \
396 self._datalength != self._datawritten:
397 self._patchheader()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000398
Tim Peters495ad3c2001-01-15 01:36:40 +0000399 def close(self):
400 self._ensure_header_written()
401 if self._nframeswritten != self._nframes or \
402 self._datalength != self._datawritten:
403 self._patchheader()
404 self._file.flush()
405 self._file = None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000406
Tim Peters495ad3c2001-01-15 01:36:40 +0000407 #
408 # private methods
409 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000410
Tim Peters495ad3c2001-01-15 01:36:40 +0000411 def _ensure_header_written(self):
412 if not self._nframeswritten:
413 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000414 raise Error('# of channels not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000415 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000416 raise Error('sample width not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000417 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000418 raise Error('frame rate not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000419 self._write_header()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000420
Tim Peters495ad3c2001-01-15 01:36:40 +0000421 def _write_header(self):
422 if self._comptype == 'NONE':
423 if self._sampwidth == 1:
424 encoding = AUDIO_FILE_ENCODING_LINEAR_8
425 self._framesize = 1
426 elif self._sampwidth == 2:
427 encoding = AUDIO_FILE_ENCODING_LINEAR_16
428 self._framesize = 2
429 elif self._sampwidth == 4:
430 encoding = AUDIO_FILE_ENCODING_LINEAR_32
431 self._framesize = 4
432 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000433 raise Error('internal error')
Tim Peters495ad3c2001-01-15 01:36:40 +0000434 elif self._comptype == 'ULAW':
435 encoding = AUDIO_FILE_ENCODING_MULAW_8
436 self._framesize = 1
437 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000438 raise Error('internal error')
Tim Peters495ad3c2001-01-15 01:36:40 +0000439 self._framesize = self._framesize * self._nchannels
440 _write_u32(self._file, AUDIO_FILE_MAGIC)
441 header_size = 25 + len(self._info)
442 header_size = (header_size + 7) & ~7
443 _write_u32(self._file, header_size)
444 if self._nframes == AUDIO_UNKNOWN_SIZE:
445 length = AUDIO_UNKNOWN_SIZE
446 else:
447 length = self._nframes * self._framesize
448 _write_u32(self._file, length)
449 self._datalength = length
450 _write_u32(self._file, encoding)
451 _write_u32(self._file, self._framerate)
452 _write_u32(self._file, self._nchannels)
453 self._file.write(self._info)
454 self._file.write('\0'*(header_size - len(self._info) - 24))
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000455
Tim Peters495ad3c2001-01-15 01:36:40 +0000456 def _patchheader(self):
457 self._file.seek(8)
458 _write_u32(self._file, self._datawritten)
459 self._datalength = self._datawritten
460 self._file.seek(0, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000461
Fred Drake43161351999-06-22 21:23:23 +0000462def open(f, mode=None):
Tim Peters495ad3c2001-01-15 01:36:40 +0000463 if mode is None:
464 if hasattr(f, 'mode'):
465 mode = f.mode
466 else:
467 mode = 'rb'
468 if mode in ('r', 'rb'):
469 return Au_read(f)
470 elif mode in ('w', 'wb'):
471 return Au_write(f)
472 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000473 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000474
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000475openfp = open