blob: 6775a53cf73e0e29ae5bc6ac7398e9e51823e65c [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')
Antoine Pitrou4d984892010-10-31 21:27:04 +0000157 self._opened = True
158 else:
159 self._opened = False
Tim Peters495ad3c2001-01-15 01:36:40 +0000160 self.initfp(f)
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000161
Tim Peters495ad3c2001-01-15 01:36:40 +0000162 def __del__(self):
163 if self._file:
164 self.close()
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000165
Tim Peters495ad3c2001-01-15 01:36:40 +0000166 def initfp(self, file):
167 self._file = file
168 self._soundpos = 0
169 magic = int(_read_u32(file))
170 if magic != AUDIO_FILE_MAGIC:
Collin Winterce36ad82007-08-30 01:19:48 +0000171 raise Error('bad magic number')
Tim Peters495ad3c2001-01-15 01:36:40 +0000172 self._hdr_size = int(_read_u32(file))
173 if self._hdr_size < 24:
Collin Winterce36ad82007-08-30 01:19:48 +0000174 raise Error('header size too small')
Tim Peters495ad3c2001-01-15 01:36:40 +0000175 if self._hdr_size > 100:
Collin Winterce36ad82007-08-30 01:19:48 +0000176 raise Error('header size ridiculously large')
Tim Peters495ad3c2001-01-15 01:36:40 +0000177 self._data_size = _read_u32(file)
178 if self._data_size != AUDIO_UNKNOWN_SIZE:
179 self._data_size = int(self._data_size)
180 self._encoding = int(_read_u32(file))
181 if self._encoding not in _simple_encodings:
Collin Winterce36ad82007-08-30 01:19:48 +0000182 raise Error('encoding not (yet) supported')
Tim Peters495ad3c2001-01-15 01:36:40 +0000183 if self._encoding in (AUDIO_FILE_ENCODING_MULAW_8,
184 AUDIO_FILE_ENCODING_ALAW_8):
185 self._sampwidth = 2
186 self._framesize = 1
187 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_8:
188 self._framesize = self._sampwidth = 1
189 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_16:
190 self._framesize = self._sampwidth = 2
191 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_24:
192 self._framesize = self._sampwidth = 3
193 elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_32:
194 self._framesize = self._sampwidth = 4
195 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000196 raise Error('unknown encoding')
Tim Peters495ad3c2001-01-15 01:36:40 +0000197 self._framerate = int(_read_u32(file))
198 self._nchannels = int(_read_u32(file))
199 self._framesize = self._framesize * self._nchannels
200 if self._hdr_size > 24:
201 self._info = file.read(self._hdr_size - 24)
202 for i in range(len(self._info)):
Antoine Pitrou7a5dc752008-08-17 00:38:32 +0000203 if self._info[i] == b'\0':
Tim Peters495ad3c2001-01-15 01:36:40 +0000204 self._info = self._info[:i]
205 break
206 else:
207 self._info = ''
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000208
Tim Peters495ad3c2001-01-15 01:36:40 +0000209 def getfp(self):
210 return self._file
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000211
Tim Peters495ad3c2001-01-15 01:36:40 +0000212 def getnchannels(self):
213 return self._nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000214
Tim Peters495ad3c2001-01-15 01:36:40 +0000215 def getsampwidth(self):
216 return self._sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000217
Tim Peters495ad3c2001-01-15 01:36:40 +0000218 def getframerate(self):
219 return self._framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000220
Tim Peters495ad3c2001-01-15 01:36:40 +0000221 def getnframes(self):
222 if self._data_size == AUDIO_UNKNOWN_SIZE:
223 return AUDIO_UNKNOWN_SIZE
224 if self._encoding in _simple_encodings:
225 return self._data_size / self._framesize
226 return 0 # XXX--must do some arithmetic here
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000227
Tim Peters495ad3c2001-01-15 01:36:40 +0000228 def getcomptype(self):
229 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
230 return 'ULAW'
231 elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
232 return 'ALAW'
233 else:
234 return 'NONE'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000235
Tim Peters495ad3c2001-01-15 01:36:40 +0000236 def getcompname(self):
237 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
238 return 'CCITT G.711 u-law'
239 elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
240 return 'CCITT G.711 A-law'
241 else:
242 return 'not compressed'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000243
Tim Peters495ad3c2001-01-15 01:36:40 +0000244 def getparams(self):
245 return self.getnchannels(), self.getsampwidth(), \
246 self.getframerate(), self.getnframes(), \
247 self.getcomptype(), self.getcompname()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000248
Tim Peters495ad3c2001-01-15 01:36:40 +0000249 def getmarkers(self):
250 return None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000251
Tim Peters495ad3c2001-01-15 01:36:40 +0000252 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000253 raise Error('no marks')
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000254
Tim Peters495ad3c2001-01-15 01:36:40 +0000255 def readframes(self, nframes):
256 if self._encoding in _simple_encodings:
257 if nframes == AUDIO_UNKNOWN_SIZE:
258 data = self._file.read()
259 else:
260 data = self._file.read(nframes * self._framesize * self._nchannels)
261 if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
262 import audioop
263 data = audioop.ulaw2lin(data, self._sampwidth)
264 return data
265 return None # XXX--not implemented yet
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000266
Tim Peters495ad3c2001-01-15 01:36:40 +0000267 def rewind(self):
268 self._soundpos = 0
269 self._file.seek(self._hdr_size)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000270
Tim Peters495ad3c2001-01-15 01:36:40 +0000271 def tell(self):
272 return self._soundpos
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000273
Tim Peters495ad3c2001-01-15 01:36:40 +0000274 def setpos(self, pos):
275 if pos < 0 or pos > self.getnframes():
Collin Winterce36ad82007-08-30 01:19:48 +0000276 raise Error('position not in range')
Tim Peters495ad3c2001-01-15 01:36:40 +0000277 self._file.seek(pos * self._framesize + self._hdr_size)
278 self._soundpos = pos
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000279
Tim Peters495ad3c2001-01-15 01:36:40 +0000280 def close(self):
Antoine Pitrou4d984892010-10-31 21:27:04 +0000281 if self._opened and self._file:
282 self._file.close()
Tim Peters495ad3c2001-01-15 01:36:40 +0000283 self._file = None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000284
285class Au_write:
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000286
Tim Peters495ad3c2001-01-15 01:36:40 +0000287 def __init__(self, f):
288 if type(f) == type(''):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000289 import builtins
290 f = builtins.open(f, 'wb')
Antoine Pitrou4d984892010-10-31 21:27:04 +0000291 self._opened = True
292 else:
293 self._opened = False
Tim Peters495ad3c2001-01-15 01:36:40 +0000294 self.initfp(f)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000295
Tim Peters495ad3c2001-01-15 01:36:40 +0000296 def __del__(self):
297 if self._file:
298 self.close()
Antoine Pitrou4d984892010-10-31 21:27:04 +0000299 self._file = None
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000300
Tim Peters495ad3c2001-01-15 01:36:40 +0000301 def initfp(self, file):
302 self._file = file
303 self._framerate = 0
304 self._nchannels = 0
305 self._sampwidth = 0
306 self._framesize = 0
307 self._nframes = AUDIO_UNKNOWN_SIZE
308 self._nframeswritten = 0
309 self._datawritten = 0
310 self._datalength = 0
Victor Stinner7f3652e2010-06-07 20:14:04 +0000311 self._info = b''
Tim Peters495ad3c2001-01-15 01:36:40 +0000312 self._comptype = 'ULAW' # default is U-law
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000313
Tim Peters495ad3c2001-01-15 01:36:40 +0000314 def setnchannels(self, nchannels):
315 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000316 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000317 if nchannels not in (1, 2, 4):
Collin Winterce36ad82007-08-30 01:19:48 +0000318 raise Error('only 1, 2, or 4 channels supported')
Tim Peters495ad3c2001-01-15 01:36:40 +0000319 self._nchannels = nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000320
Tim Peters495ad3c2001-01-15 01:36:40 +0000321 def getnchannels(self):
322 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000323 raise Error('number of channels not set')
Tim Peters495ad3c2001-01-15 01:36:40 +0000324 return self._nchannels
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000325
Tim Peters495ad3c2001-01-15 01:36:40 +0000326 def setsampwidth(self, sampwidth):
327 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000328 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000329 if sampwidth not in (1, 2, 4):
Collin Winterce36ad82007-08-30 01:19:48 +0000330 raise Error('bad sample width')
Tim Peters495ad3c2001-01-15 01:36:40 +0000331 self._sampwidth = sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000332
Tim Peters495ad3c2001-01-15 01:36:40 +0000333 def getsampwidth(self):
334 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000335 raise Error('sample width not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000336 return self._sampwidth
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000337
Tim Peters495ad3c2001-01-15 01:36:40 +0000338 def setframerate(self, framerate):
339 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000340 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000341 self._framerate = framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000342
Tim Peters495ad3c2001-01-15 01:36:40 +0000343 def getframerate(self):
344 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000345 raise Error('frame rate not set')
Tim Peters495ad3c2001-01-15 01:36:40 +0000346 return self._framerate
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000347
Tim Peters495ad3c2001-01-15 01:36:40 +0000348 def setnframes(self, nframes):
349 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000350 raise Error('cannot change parameters after starting to write')
Tim Peters495ad3c2001-01-15 01:36:40 +0000351 if nframes < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000352 raise Error('# of frames cannot be negative')
Tim Peters495ad3c2001-01-15 01:36:40 +0000353 self._nframes = nframes
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000354
Tim Peters495ad3c2001-01-15 01:36:40 +0000355 def getnframes(self):
356 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000357
Tim Peters495ad3c2001-01-15 01:36:40 +0000358 def setcomptype(self, type, name):
359 if type in ('NONE', 'ULAW'):
360 self._comptype = type
361 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000362 raise Error('unknown compression type')
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000363
Tim Peters495ad3c2001-01-15 01:36:40 +0000364 def getcomptype(self):
365 return self._comptype
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000366
Tim Peters495ad3c2001-01-15 01:36:40 +0000367 def getcompname(self):
368 if self._comptype == 'ULAW':
369 return 'CCITT G.711 u-law'
370 elif self._comptype == 'ALAW':
371 return 'CCITT G.711 A-law'
372 else:
373 return 'not compressed'
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000374
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000375 def setparams(self, params):
376 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Tim Peters495ad3c2001-01-15 01:36:40 +0000377 self.setnchannels(nchannels)
378 self.setsampwidth(sampwidth)
379 self.setframerate(framerate)
380 self.setnframes(nframes)
381 self.setcomptype(comptype, compname)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000382
Tim Peters495ad3c2001-01-15 01:36:40 +0000383 def getparams(self):
384 return self.getnchannels(), self.getsampwidth(), \
385 self.getframerate(), self.getnframes(), \
386 self.getcomptype(), self.getcompname()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000387
Tim Peters495ad3c2001-01-15 01:36:40 +0000388 def tell(self):
389 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000390
Tim Peters495ad3c2001-01-15 01:36:40 +0000391 def writeframesraw(self, data):
392 self._ensure_header_written()
393 nframes = len(data) / self._framesize
394 if self._comptype == 'ULAW':
395 import audioop
396 data = audioop.lin2ulaw(data, self._sampwidth)
397 self._file.write(data)
398 self._nframeswritten = self._nframeswritten + nframes
399 self._datawritten = self._datawritten + len(data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000400
Tim Peters495ad3c2001-01-15 01:36:40 +0000401 def writeframes(self, data):
402 self.writeframesraw(data)
403 if self._nframeswritten != self._nframes or \
404 self._datalength != self._datawritten:
405 self._patchheader()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000406
Tim Peters495ad3c2001-01-15 01:36:40 +0000407 def close(self):
408 self._ensure_header_written()
409 if self._nframeswritten != self._nframes or \
410 self._datalength != self._datawritten:
411 self._patchheader()
412 self._file.flush()
Antoine Pitrou4d984892010-10-31 21:27:04 +0000413 if self._opened and self._file:
414 self._file.close()
Tim Peters495ad3c2001-01-15 01:36:40 +0000415 self._file = None
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000416
Tim Peters495ad3c2001-01-15 01:36:40 +0000417 #
418 # private methods
419 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000420
Tim Peters495ad3c2001-01-15 01:36:40 +0000421 def _ensure_header_written(self):
422 if not self._nframeswritten:
423 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000424 raise Error('# of channels not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000425 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000426 raise Error('sample width not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000427 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000428 raise Error('frame rate not specified')
Tim Peters495ad3c2001-01-15 01:36:40 +0000429 self._write_header()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000430
Tim Peters495ad3c2001-01-15 01:36:40 +0000431 def _write_header(self):
432 if self._comptype == 'NONE':
433 if self._sampwidth == 1:
434 encoding = AUDIO_FILE_ENCODING_LINEAR_8
435 self._framesize = 1
436 elif self._sampwidth == 2:
437 encoding = AUDIO_FILE_ENCODING_LINEAR_16
438 self._framesize = 2
439 elif self._sampwidth == 4:
440 encoding = AUDIO_FILE_ENCODING_LINEAR_32
441 self._framesize = 4
442 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000443 raise Error('internal error')
Tim Peters495ad3c2001-01-15 01:36:40 +0000444 elif self._comptype == 'ULAW':
445 encoding = AUDIO_FILE_ENCODING_MULAW_8
446 self._framesize = 1
447 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000448 raise Error('internal error')
Tim Peters495ad3c2001-01-15 01:36:40 +0000449 self._framesize = self._framesize * self._nchannels
450 _write_u32(self._file, AUDIO_FILE_MAGIC)
451 header_size = 25 + len(self._info)
452 header_size = (header_size + 7) & ~7
453 _write_u32(self._file, header_size)
454 if self._nframes == AUDIO_UNKNOWN_SIZE:
455 length = AUDIO_UNKNOWN_SIZE
456 else:
457 length = self._nframes * self._framesize
458 _write_u32(self._file, length)
459 self._datalength = length
460 _write_u32(self._file, encoding)
461 _write_u32(self._file, self._framerate)
462 _write_u32(self._file, self._nchannels)
463 self._file.write(self._info)
Antoine Pitrou7a5dc752008-08-17 00:38:32 +0000464 self._file.write(b'\0'*(header_size - len(self._info) - 24))
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000465
Tim Peters495ad3c2001-01-15 01:36:40 +0000466 def _patchheader(self):
467 self._file.seek(8)
468 _write_u32(self._file, self._datawritten)
469 self._datalength = self._datawritten
470 self._file.seek(0, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000471
Fred Drake43161351999-06-22 21:23:23 +0000472def open(f, mode=None):
Tim Peters495ad3c2001-01-15 01:36:40 +0000473 if mode is None:
474 if hasattr(f, 'mode'):
475 mode = f.mode
476 else:
477 mode = 'rb'
478 if mode in ('r', 'rb'):
479 return Au_read(f)
480 elif mode in ('w', 'wb'):
481 return Au_write(f)
482 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000483 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000484
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000485openfp = open