blob: 5f50e8fb9c96490dc1190e3273c0ef751be2de11 [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)
Antoine Pitrou7a5dc752008-08-17 00:38:32 +0000138 if not byte:
Tim Peters495ad3c2001-01-15 01:36:40 +0000139 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)
Antoine Pitrou7a5dc752008-08-17 00:38:32 +0000147 data.insert(0, int(m))
Tim Peters495ad3c2001-01-15 01:36:40 +0000148 x = d
Antoine Pitrou7a5dc752008-08-17 00:38:32 +0000149 file.write(bytes(data))
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000150
151class Au_read:
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000152
Tim Peters495ad3c2001-01-15 01:36:40 +0000153 def __init__(self, f):
154 if type(f) == type(''):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000155 import builtins
156 f = builtins.open(f, 'rb')
Tim Peters495ad3c2001-01-15 01:36:40 +0000157 self.initfp(f)
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000158
Tim Peters495ad3c2001-01-15 01:36:40 +0000159 def __del__(self):
160 if self._file:
161 self.close()
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000162
Tim Peters495ad3c2001-01-15 01:36:40 +0000163 def initfp(self, file):
164 self._file = file
165 self._soundpos = 0
166 magic = int(_read_u32(file))
167 if magic != AUDIO_FILE_MAGIC:
Collin Winterce36ad82007-08-30 01:19:48 +0000168 raise Error('bad magic number')
Tim Peters495ad3c2001-01-15 01:36:40 +0000169 self._hdr_size = int(_read_u32(file))
170 if self._hdr_size < 24:
Collin Winterce36ad82007-08-30 01:19:48 +0000171 raise Error('header size too small')
Tim Peters495ad3c2001-01-15 01:36:40 +0000172 if self._hdr_size > 100:
Collin Winterce36ad82007-08-30 01:19:48 +0000173 raise Error('header size ridiculously large')
Tim Peters495ad3c2001-01-15 01:36:40 +0000174 self._data_size = _read_u32(file)
175 if self._data_size != AUDIO_UNKNOWN_SIZE:
176 self._data_size = int(self._data_size)
177 self._encoding = int(_read_u32(file))
178 if self._encoding not in _simple_encodings:
Collin Winterce36ad82007-08-30 01:19:48 +0000179 raise Error('encoding not (yet) supported')
Tim Peters495ad3c2001-01-15 01:36:40 +0000180 if self._encoding in (AUDIO_FILE_ENCODING_MULAW_8,
181 AUDIO_FILE_ENCODING_ALAW_8):
182 self._sampwidth = 2
183 self._framesize = 1
184 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_8:
185 self._framesize = self._sampwidth = 1
186 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_16:
187 self._framesize = self._sampwidth = 2
188 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_24:
189 self._framesize = self._sampwidth = 3
190 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_32:
191 self._framesize = self._sampwidth = 4
192 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000193 raise Error('unknown encoding')
Tim Peters495ad3c2001-01-15 01:36:40 +0000194 self._framerate = int(_read_u32(file))
195 self._nchannels = int(_read_u32(file))
196 self._framesize = self._framesize * self._nchannels
197 if self._hdr_size > 24:
198 self._info = file.read(self._hdr_size - 24)
199 for i in range(len(self._info)):
Antoine Pitrou7a5dc752008-08-17 00:38:32 +0000200 if self._info[i] == b'\0':
Tim Peters495ad3c2001-01-15 01:36:40 +0000201 self._info = self._info[:i]
202 break
203 else:
204 self._info = ''
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000205
Tim Peters495ad3c2001-01-15 01:36:40 +0000206 def getfp(self):
207 return self._file
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000208
Tim Peters495ad3c2001-01-15 01:36:40 +0000209 def getnchannels(self):
210 return self._nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000211
Tim Peters495ad3c2001-01-15 01:36:40 +0000212 def getsampwidth(self):
213 return self._sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000214
Tim Peters495ad3c2001-01-15 01:36:40 +0000215 def getframerate(self):
216 return self._framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000217
Tim Peters495ad3c2001-01-15 01:36:40 +0000218 def getnframes(self):
219 if self._data_size == AUDIO_UNKNOWN_SIZE:
220 return AUDIO_UNKNOWN_SIZE
221 if self._encoding in _simple_encodings:
222 return self._data_size / self._framesize
223 return 0 # XXX--must do some arithmetic here
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000224
Tim Peters495ad3c2001-01-15 01:36:40 +0000225 def getcomptype(self):
226 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
227 return 'ULAW'
228 elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
229 return 'ALAW'
230 else:
231 return 'NONE'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000232
Tim Peters495ad3c2001-01-15 01:36:40 +0000233 def getcompname(self):
234 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
235 return 'CCITT G.711 u-law'
236 elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
237 return 'CCITT G.711 A-law'
238 else:
239 return 'not compressed'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000240
Tim Peters495ad3c2001-01-15 01:36:40 +0000241 def getparams(self):
242 return self.getnchannels(), self.getsampwidth(), \
243 self.getframerate(), self.getnframes(), \
244 self.getcomptype(), self.getcompname()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000245
Tim Peters495ad3c2001-01-15 01:36:40 +0000246 def getmarkers(self):
247 return None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000248
Tim Peters495ad3c2001-01-15 01:36:40 +0000249 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000250 raise Error('no marks')
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000251
Tim Peters495ad3c2001-01-15 01:36:40 +0000252 def readframes(self, nframes):
253 if self._encoding in _simple_encodings:
254 if nframes == AUDIO_UNKNOWN_SIZE:
255 data = self._file.read()
256 else:
257 data = self._file.read(nframes * self._framesize * self._nchannels)
258 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
259 import audioop
260 data = audioop.ulaw2lin(data, self._sampwidth)
261 return data
262 return None # XXX--not implemented yet
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000263
Tim Peters495ad3c2001-01-15 01:36:40 +0000264 def rewind(self):
265 self._soundpos = 0
266 self._file.seek(self._hdr_size)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000267
Tim Peters495ad3c2001-01-15 01:36:40 +0000268 def tell(self):
269 return self._soundpos
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000270
Tim Peters495ad3c2001-01-15 01:36:40 +0000271 def setpos(self, pos):
272 if pos < 0 or pos > self.getnframes():
Collin Winterce36ad82007-08-30 01:19:48 +0000273 raise Error('position not in range')
Tim Peters495ad3c2001-01-15 01:36:40 +0000274 self._file.seek(pos * self._framesize + self._hdr_size)
275 self._soundpos = pos
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000276
Tim Peters495ad3c2001-01-15 01:36:40 +0000277 def close(self):
278 self._file = None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000279
280class Au_write:
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000281
Tim Peters495ad3c2001-01-15 01:36:40 +0000282 def __init__(self, f):
283 if type(f) == type(''):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000284 import builtins
285 f = builtins.open(f, 'wb')
Tim Peters495ad3c2001-01-15 01:36:40 +0000286 self.initfp(f)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000287
Tim Peters495ad3c2001-01-15 01:36:40 +0000288 def __del__(self):
289 if self._file:
290 self.close()
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000291
Tim Peters495ad3c2001-01-15 01:36:40 +0000292 def initfp(self, file):
293 self._file = file
294 self._framerate = 0
295 self._nchannels = 0
296 self._sampwidth = 0
297 self._framesize = 0
298 self._nframes = AUDIO_UNKNOWN_SIZE
299 self._nframeswritten = 0
300 self._datawritten = 0
301 self._datalength = 0
Victor Stinner7f3652e2010-06-07 20:14:04 +0000302 self._info = b''
Tim Peters495ad3c2001-01-15 01:36:40 +0000303 self._comptype = 'ULAW' # default is U-law
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000304
Tim Peters495ad3c2001-01-15 01:36:40 +0000305 def setnchannels(self, nchannels):
306 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000307 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000308 if nchannels not in (1, 2, 4):
Collin Winterce36ad82007-08-30 01:19:48 +0000309 raise Error('only 1, 2, or 4 channels supported')
Tim Peters495ad3c2001-01-15 01:36:40 +0000310 self._nchannels = nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000311
Tim Peters495ad3c2001-01-15 01:36:40 +0000312 def getnchannels(self):
313 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000314 raise Error('number of channels not set')
Tim Peters495ad3c2001-01-15 01:36:40 +0000315 return self._nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000316
Tim Peters495ad3c2001-01-15 01:36:40 +0000317 def setsampwidth(self, sampwidth):
318 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000319 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000320 if sampwidth not in (1, 2, 4):
Collin Winterce36ad82007-08-30 01:19:48 +0000321 raise Error('bad sample width')
Tim Peters495ad3c2001-01-15 01:36:40 +0000322 self._sampwidth = sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000323
Tim Peters495ad3c2001-01-15 01:36:40 +0000324 def getsampwidth(self):
325 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000326 raise Error('sample width not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000327 return self._sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000328
Tim Peters495ad3c2001-01-15 01:36:40 +0000329 def setframerate(self, framerate):
330 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000331 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000332 self._framerate = framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000333
Tim Peters495ad3c2001-01-15 01:36:40 +0000334 def getframerate(self):
335 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000336 raise Error('frame rate not set')
Tim Peters495ad3c2001-01-15 01:36:40 +0000337 return self._framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000338
Tim Peters495ad3c2001-01-15 01:36:40 +0000339 def setnframes(self, nframes):
340 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000341 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000342 if nframes < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000343 raise Error('# of frames cannot be negative')
Tim Peters495ad3c2001-01-15 01:36:40 +0000344 self._nframes = nframes
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000345
Tim Peters495ad3c2001-01-15 01:36:40 +0000346 def getnframes(self):
347 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000348
Tim Peters495ad3c2001-01-15 01:36:40 +0000349 def setcomptype(self, type, name):
350 if type in ('NONE', 'ULAW'):
351 self._comptype = type
352 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000353 raise Error('unknown compression type')
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000354
Tim Peters495ad3c2001-01-15 01:36:40 +0000355 def getcomptype(self):
356 return self._comptype
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000357
Tim Peters495ad3c2001-01-15 01:36:40 +0000358 def getcompname(self):
359 if self._comptype == 'ULAW':
360 return 'CCITT G.711 u-law'
361 elif self._comptype == 'ALAW':
362 return 'CCITT G.711 A-law'
363 else:
364 return 'not compressed'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000365
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000366 def setparams(self, params):
367 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Tim Peters495ad3c2001-01-15 01:36:40 +0000368 self.setnchannels(nchannels)
369 self.setsampwidth(sampwidth)
370 self.setframerate(framerate)
371 self.setnframes(nframes)
372 self.setcomptype(comptype, compname)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000373
Tim Peters495ad3c2001-01-15 01:36:40 +0000374 def getparams(self):
375 return self.getnchannels(), self.getsampwidth(), \
376 self.getframerate(), self.getnframes(), \
377 self.getcomptype(), self.getcompname()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000378
Tim Peters495ad3c2001-01-15 01:36:40 +0000379 def tell(self):
380 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000381
Tim Peters495ad3c2001-01-15 01:36:40 +0000382 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)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000391
Tim Peters495ad3c2001-01-15 01:36:40 +0000392 def writeframes(self, data):
393 self.writeframesraw(data)
394 if self._nframeswritten != self._nframes or \
395 self._datalength != self._datawritten:
396 self._patchheader()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000397
Tim Peters495ad3c2001-01-15 01:36:40 +0000398 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
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000405
Tim Peters495ad3c2001-01-15 01:36:40 +0000406 #
407 # private methods
408 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000409
Tim Peters495ad3c2001-01-15 01:36:40 +0000410 def _ensure_header_written(self):
411 if not self._nframeswritten:
412 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000413 raise Error('# of channels not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000414 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000415 raise Error('sample width not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000416 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000417 raise Error('frame rate not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000418 self._write_header()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000419
Tim Peters495ad3c2001-01-15 01:36:40 +0000420 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:
Collin Winterce36ad82007-08-30 01:19:48 +0000432 raise Error('internal error')
Tim Peters495ad3c2001-01-15 01:36:40 +0000433 elif self._comptype == 'ULAW':
434 encoding = AUDIO_FILE_ENCODING_MULAW_8
435 self._framesize = 1
436 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000437 raise Error('internal error')
Tim Peters495ad3c2001-01-15 01:36:40 +0000438 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)
Antoine Pitrou7a5dc752008-08-17 00:38:32 +0000453 self._file.write(b'\0'*(header_size - len(self._info) - 24))
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000454
Tim Peters495ad3c2001-01-15 01:36:40 +0000455 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)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000460
Fred Drake43161351999-06-22 21:23:23 +0000461def open(f, mode=None):
Tim Peters495ad3c2001-01-15 01:36:40 +0000462 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:
Collin Winterce36ad82007-08-30 01:19:48 +0000472 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000473
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000474openfp = open