blob: d41a9bd19d54de3234618e9728d27c2c22a37c8b [file] [log] [blame]
Guido van Rossume7b146f2000-02-04 15:28:42 +00001"""Stuff to parse WAVE files.
2
3Usage.
4
5Reading WAVE files:
6 f = wave.open(file, 'r')
7where file is either the name of a file or an open file pointer.
8The open file pointer must have methods read(), seek(), and close().
9When the setpos() and rewind() methods are not used, the seek()
10method is not necessary.
11
12This returns an instance of a class with the following public methods:
13 getnchannels() -- returns number of audio channels (1 for
14 mono, 2 for stereo)
15 getsampwidth() -- returns sample width in bytes
16 getframerate() -- returns sampling frequency
17 getnframes() -- returns number of audio frames
18 getcomptype() -- returns compression type ('NONE' for linear samples)
19 getcompname() -- returns human-readable version of
20 compression type ('not compressed' linear samples)
21 getparams() -- returns a tuple consisting of all of the
22 above in the above order
23 getmarkers() -- returns None (for compatibility with the
24 aifc module)
25 getmark(id) -- raises an error since the mark does not
26 exist (for compatibility with the aifc module)
27 readframes(n) -- returns at most n frames of audio
28 rewind() -- rewind to the beginning of the audio stream
29 setpos(pos) -- seek to the specified position
30 tell() -- return the current position
31 close() -- close the instance (make it unusable)
32The position returned by tell() and the position given to setpos()
Thomas Wouters7e474022000-07-16 12:04:32 +000033are compatible and have nothing to do with the actual position in the
Guido van Rossume7b146f2000-02-04 15:28:42 +000034file.
35The close() method is called automatically when the class instance
36is destroyed.
37
38Writing WAVE files:
39 f = wave.open(file, 'w')
40where file is either the name of a file or an open file pointer.
41The open file pointer must have methods write(), tell(), seek(), and
42close().
43
44This returns an instance of a class with the following public methods:
45 setnchannels(n) -- set the number of channels
46 setsampwidth(n) -- set the sample width
47 setframerate(n) -- set the frame rate
48 setnframes(n) -- set the number of frames
49 setcomptype(type, name)
50 -- set the compression type and the
51 human-readable compression type
52 setparams(tuple)
53 -- set all parameters at once
54 tell() -- return current position in output file
55 writeframesraw(data)
56 -- write audio frames without pathing up the
57 file header
58 writeframes(data)
59 -- write audio frames and patch up the file header
60 close() -- patch up the file header and close the
61 output file
62You should set the parameters before the first writeframesraw or
63writeframes. The total number of frames does not need to be set,
64but when it is set to the correct value, the header does not have to
65be patched up.
66It is best to first set all parameters, perhaps possibly the
67compression type, and then write audio frames using writeframesraw.
68When all frames have been written, either call writeframes('') or
69close() to patch up the sizes in the header.
70The close() method is called automatically when the class instance
71is destroyed.
72"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000073
74import __builtin__
75
Skip Montanaro40fc1602001-03-01 04:27:19 +000076__all__ = ["open", "openfp", "Error"]
77
Fred Drake9b8d8012000-08-17 04:45:13 +000078class Error(Exception):
79 pass
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000080
81WAVE_FORMAT_PCM = 0x0001
82
83_array_fmts = None, 'b', 'h', None, 'l'
84
Guido van Rossumebb9c921999-02-05 22:28:17 +000085# Determine endian-ness
86import struct
87if struct.pack("h", 1) == "\000\001":
Guido van Rossume7b146f2000-02-04 15:28:42 +000088 big_endian = 1
Guido van Rossumebb9c921999-02-05 22:28:17 +000089else:
Guido van Rossume7b146f2000-02-04 15:28:42 +000090 big_endian = 0
Guido van Rossumebb9c921999-02-05 22:28:17 +000091
Guido van Rossum3601e881999-08-26 15:50:43 +000092from chunk import Chunk
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000093
94class Wave_read:
Guido van Rossume7b146f2000-02-04 15:28:42 +000095 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000096
Guido van Rossume7b146f2000-02-04 15:28:42 +000097 These variables are available to the user though appropriate
98 methods of this class:
99 _file -- the open file with methods read(), close(), and seek()
100 set through the __init__() method
101 _nchannels -- the number of audio channels
102 available through the getnchannels() method
103 _nframes -- the number of audio frames
104 available through the getnframes() method
105 _sampwidth -- the number of bytes per audio sample
106 available through the getsampwidth() method
107 _framerate -- the sampling frequency
108 available through the getframerate() method
109 _comptype -- the AIFF-C compression type ('NONE' if AIFF)
110 available through the getcomptype() method
111 _compname -- the human-readable AIFF-C compression type
112 available through the getcomptype() method
113 _soundpos -- the position in the audio stream
114 available through the tell() method, set through the
115 setpos() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000116
Guido van Rossume7b146f2000-02-04 15:28:42 +0000117 These variables are used internally only:
118 _fmt_chunk_read -- 1 iff the FMT chunk has been read
119 _data_seek_needed -- 1 iff positioned correctly in audio
120 file for readframes()
121 _data_chunk -- instantiation of a chunk class for the DATA chunk
122 _framesize -- size of one frame in the file
123 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000124
Guido van Rossume7b146f2000-02-04 15:28:42 +0000125 def initfp(self, file):
126 self._convert = None
127 self._soundpos = 0
128 self._file = Chunk(file, bigendian = 0)
129 if self._file.getname() != 'RIFF':
130 raise Error, 'file does not start with RIFF id'
131 if self._file.read(4) != 'WAVE':
132 raise Error, 'not a WAVE file'
133 self._fmt_chunk_read = 0
134 self._data_chunk = None
135 while 1:
136 self._data_seek_needed = 1
137 try:
138 chunk = Chunk(self._file, bigendian = 0)
139 except EOFError:
140 break
141 chunkname = chunk.getname()
142 if chunkname == 'fmt ':
143 self._read_fmt_chunk(chunk)
144 self._fmt_chunk_read = 1
145 elif chunkname == 'data':
146 if not self._fmt_chunk_read:
147 raise Error, 'data chunk before fmt chunk'
148 self._data_chunk = chunk
Guido van Rossum54e54c62001-09-04 19:14:14 +0000149 self._nframes = chunk.chunksize // self._framesize
Guido van Rossume7b146f2000-02-04 15:28:42 +0000150 self._data_seek_needed = 0
151 break
152 chunk.skip()
153 if not self._fmt_chunk_read or not self._data_chunk:
154 raise Error, 'fmt chunk and/or data chunk missing'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000155
Guido van Rossume7b146f2000-02-04 15:28:42 +0000156 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000157 self._i_opened_the_file = None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000158 if type(f) == type(''):
159 f = __builtin__.open(f, 'rb')
Tim Peterscfc41782000-10-09 23:43:55 +0000160 self._i_opened_the_file = f
Guido van Rossume7b146f2000-02-04 15:28:42 +0000161 # else, assume it is an open file object already
162 self.initfp(f)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000163
Tim Peterscfc41782000-10-09 23:43:55 +0000164 def __del__(self):
165 self.close()
Guido van Rossume7b146f2000-02-04 15:28:42 +0000166 #
167 # User visible methods.
168 #
169 def getfp(self):
170 return self._file
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000171
Guido van Rossume7b146f2000-02-04 15:28:42 +0000172 def rewind(self):
173 self._data_seek_needed = 1
174 self._soundpos = 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000175
Guido van Rossume7b146f2000-02-04 15:28:42 +0000176 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000177 if self._i_opened_the_file:
178 self._i_opened_the_file.close()
179 self._i_opened_the_file = None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000180 self._file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000181
Guido van Rossume7b146f2000-02-04 15:28:42 +0000182 def tell(self):
183 return self._soundpos
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000184
Guido van Rossume7b146f2000-02-04 15:28:42 +0000185 def getnchannels(self):
186 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000187
Guido van Rossume7b146f2000-02-04 15:28:42 +0000188 def getnframes(self):
189 return self._nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000190
Guido van Rossume7b146f2000-02-04 15:28:42 +0000191 def getsampwidth(self):
192 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000193
Guido van Rossume7b146f2000-02-04 15:28:42 +0000194 def getframerate(self):
195 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000196
Guido van Rossume7b146f2000-02-04 15:28:42 +0000197 def getcomptype(self):
198 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000199
Guido van Rossume7b146f2000-02-04 15:28:42 +0000200 def getcompname(self):
201 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000202
Guido van Rossume7b146f2000-02-04 15:28:42 +0000203 def getparams(self):
204 return self.getnchannels(), self.getsampwidth(), \
205 self.getframerate(), self.getnframes(), \
206 self.getcomptype(), self.getcompname()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000207
Guido van Rossume7b146f2000-02-04 15:28:42 +0000208 def getmarkers(self):
209 return None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000210
Guido van Rossume7b146f2000-02-04 15:28:42 +0000211 def getmark(self, id):
212 raise Error, 'no marks'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000213
Guido van Rossume7b146f2000-02-04 15:28:42 +0000214 def setpos(self, pos):
215 if pos < 0 or pos > self._nframes:
216 raise Error, 'position not in range'
217 self._soundpos = pos
218 self._data_seek_needed = 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000219
Guido van Rossume7b146f2000-02-04 15:28:42 +0000220 def readframes(self, nframes):
221 if self._data_seek_needed:
222 self._data_chunk.seek(0, 0)
223 pos = self._soundpos * self._framesize
224 if pos:
225 self._data_chunk.seek(pos, 0)
226 self._data_seek_needed = 0
227 if nframes == 0:
228 return ''
229 if self._sampwidth > 1 and big_endian:
230 # unfortunately the fromfile() method does not take
231 # something that only looks like a file object, so
232 # we have to reach into the innards of the chunk object
233 import array
234 chunk = self._data_chunk
235 data = array.array(_array_fmts[self._sampwidth])
236 nitems = nframes * self._nchannels
237 if nitems * self._sampwidth > chunk.chunksize - chunk.size_read:
238 nitems = (chunk.chunksize - chunk.size_read) / self._sampwidth
239 data.fromfile(chunk.file.file, nitems)
240 # "tell" data chunk how much was read
241 chunk.size_read = chunk.size_read + nitems * self._sampwidth
242 # do the same for the outermost chunk
243 chunk = chunk.file
244 chunk.size_read = chunk.size_read + nitems * self._sampwidth
245 data.byteswap()
246 data = data.tostring()
247 else:
248 data = self._data_chunk.read(nframes * self._framesize)
249 if self._convert and data:
250 data = self._convert(data)
Guido van Rossum54e54c62001-09-04 19:14:14 +0000251 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000252 return data
253
254 #
255 # Internal methods.
256 #
257
258 def _read_fmt_chunk(self, chunk):
259 wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack('<hhllh', chunk.read(14))
260 if wFormatTag == WAVE_FORMAT_PCM:
261 sampwidth = struct.unpack('<h', chunk.read(2))[0]
Guido van Rossum54e54c62001-09-04 19:14:14 +0000262 self._sampwidth = (sampwidth + 7) // 8
Guido van Rossume7b146f2000-02-04 15:28:42 +0000263 else:
264 raise Error, 'unknown format: ' + `wFormatTag`
265 self._framesize = self._nchannels * self._sampwidth
266 self._comptype = 'NONE'
267 self._compname = 'not compressed'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000268
269class Wave_write:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000270 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000271
Guido van Rossume7b146f2000-02-04 15:28:42 +0000272 These variables are user settable through appropriate methods
273 of this class:
274 _file -- the open file with methods write(), close(), tell(), seek()
275 set through the __init__() method
276 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
277 set through the setcomptype() or setparams() method
278 _compname -- the human-readable AIFF-C compression type
279 set through the setcomptype() or setparams() method
280 _nchannels -- the number of audio channels
281 set through the setnchannels() or setparams() method
282 _sampwidth -- the number of bytes per audio sample
283 set through the setsampwidth() or setparams() method
284 _framerate -- the sampling frequency
285 set through the setframerate() or setparams() method
286 _nframes -- the number of audio frames written to the header
287 set through the setnframes() or setparams() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000288
Guido van Rossume7b146f2000-02-04 15:28:42 +0000289 These variables are used internally only:
290 _datalength -- the size of the audio samples written to the header
291 _nframeswritten -- the number of frames actually written
292 _datawritten -- the size of the audio samples actually written
293 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000294
Guido van Rossume7b146f2000-02-04 15:28:42 +0000295 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000296 self._i_opened_the_file = None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000297 if type(f) == type(''):
298 f = __builtin__.open(f, 'wb')
Tim Peterscfc41782000-10-09 23:43:55 +0000299 self._i_opened_the_file = f
Guido van Rossume7b146f2000-02-04 15:28:42 +0000300 self.initfp(f)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000301
Guido van Rossume7b146f2000-02-04 15:28:42 +0000302 def initfp(self, file):
303 self._file = file
304 self._convert = None
305 self._nchannels = 0
306 self._sampwidth = 0
307 self._framerate = 0
308 self._nframes = 0
309 self._nframeswritten = 0
310 self._datawritten = 0
311 self._datalength = 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000312
Guido van Rossume7b146f2000-02-04 15:28:42 +0000313 def __del__(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000314 self.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000315
Guido van Rossume7b146f2000-02-04 15:28:42 +0000316 #
317 # User visible methods.
318 #
319 def setnchannels(self, nchannels):
320 if self._datawritten:
321 raise Error, 'cannot change parameters after starting to write'
322 if nchannels < 1:
323 raise Error, 'bad # of channels'
324 self._nchannels = nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000325
Guido van Rossume7b146f2000-02-04 15:28:42 +0000326 def getnchannels(self):
327 if not self._nchannels:
328 raise Error, 'number of channels not set'
329 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000330
Guido van Rossume7b146f2000-02-04 15:28:42 +0000331 def setsampwidth(self, sampwidth):
332 if self._datawritten:
333 raise Error, 'cannot change parameters after starting to write'
334 if sampwidth < 1 or sampwidth > 4:
335 raise Error, 'bad sample width'
336 self._sampwidth = sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000337
Guido van Rossume7b146f2000-02-04 15:28:42 +0000338 def getsampwidth(self):
339 if not self._sampwidth:
340 raise Error, 'sample width not set'
341 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000342
Guido van Rossume7b146f2000-02-04 15:28:42 +0000343 def setframerate(self, framerate):
344 if self._datawritten:
345 raise Error, 'cannot change parameters after starting to write'
346 if framerate <= 0:
347 raise Error, 'bad frame rate'
348 self._framerate = framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000349
Guido van Rossume7b146f2000-02-04 15:28:42 +0000350 def getframerate(self):
351 if not self._framerate:
352 raise Error, 'frame rate not set'
353 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000354
Guido van Rossume7b146f2000-02-04 15:28:42 +0000355 def setnframes(self, nframes):
356 if self._datawritten:
357 raise Error, 'cannot change parameters after starting to write'
358 self._nframes = nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000359
Guido van Rossume7b146f2000-02-04 15:28:42 +0000360 def getnframes(self):
361 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000362
Guido van Rossume7b146f2000-02-04 15:28:42 +0000363 def setcomptype(self, comptype, compname):
364 if self._datawritten:
365 raise Error, 'cannot change parameters after starting to write'
366 if comptype not in ('NONE',):
367 raise Error, 'unsupported compression type'
368 self._comptype = comptype
369 self._compname = compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000370
Guido van Rossume7b146f2000-02-04 15:28:42 +0000371 def getcomptype(self):
372 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000373
Guido van Rossume7b146f2000-02-04 15:28:42 +0000374 def getcompname(self):
375 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000376
Guido van Rossume7b146f2000-02-04 15:28:42 +0000377 def setparams(self, (nchannels, sampwidth, framerate, nframes, comptype, compname)):
378 if self._datawritten:
379 raise Error, 'cannot change parameters after starting to write'
380 self.setnchannels(nchannels)
381 self.setsampwidth(sampwidth)
382 self.setframerate(framerate)
383 self.setnframes(nframes)
384 self.setcomptype(comptype, compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000385
Guido van Rossume7b146f2000-02-04 15:28:42 +0000386 def getparams(self):
387 if not self._nchannels or not self._sampwidth or not self._framerate:
388 raise Error, 'not all parameters set'
389 return self._nchannels, self._sampwidth, self._framerate, \
390 self._nframes, self._comptype, self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000391
Guido van Rossume7b146f2000-02-04 15:28:42 +0000392 def setmark(self, id, pos, name):
393 raise Error, 'setmark() not supported'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000394
Guido van Rossume7b146f2000-02-04 15:28:42 +0000395 def getmark(self, id):
396 raise Error, 'no marks'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000397
Guido van Rossume7b146f2000-02-04 15:28:42 +0000398 def getmarkers(self):
399 return None
Tim Peterse1190062001-01-15 03:34:38 +0000400
Guido van Rossume7b146f2000-02-04 15:28:42 +0000401 def tell(self):
402 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000403
Guido van Rossume7b146f2000-02-04 15:28:42 +0000404 def writeframesraw(self, data):
405 self._ensure_header_written(len(data))
Guido van Rossum54e54c62001-09-04 19:14:14 +0000406 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000407 if self._convert:
408 data = self._convert(data)
409 if self._sampwidth > 1 and big_endian:
410 import array
411 data = array.array(_array_fmts[self._sampwidth], data)
412 data.byteswap()
413 data.tofile(self._file)
414 self._datawritten = self._datawritten + len(data) * self._sampwidth
415 else:
416 self._file.write(data)
417 self._datawritten = self._datawritten + len(data)
418 self._nframeswritten = self._nframeswritten + nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000419
Guido van Rossume7b146f2000-02-04 15:28:42 +0000420 def writeframes(self, data):
421 self.writeframesraw(data)
422 if self._datalength != self._datawritten:
423 self._patchheader()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000424
Guido van Rossume7b146f2000-02-04 15:28:42 +0000425 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000426 if self._file:
427 self._ensure_header_written(0)
428 if self._datalength != self._datawritten:
429 self._patchheader()
430 self._file.flush()
431 self._file = None
432 if self._i_opened_the_file:
433 self._i_opened_the_file.close()
434 self._i_opened_the_file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000435
Guido van Rossume7b146f2000-02-04 15:28:42 +0000436 #
437 # Internal methods.
438 #
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000439
Guido van Rossume7b146f2000-02-04 15:28:42 +0000440 def _ensure_header_written(self, datasize):
441 if not self._datawritten:
442 if not self._nchannels:
443 raise Error, '# channels not specified'
444 if not self._sampwidth:
445 raise Error, 'sample width not specified'
446 if not self._framerate:
447 raise Error, 'sampling rate not specified'
448 self._write_header(datasize)
449
450 def _write_header(self, initlength):
451 self._file.write('RIFF')
452 if not self._nframes:
453 self._nframes = initlength / (self._nchannels * self._sampwidth)
454 self._datalength = self._nframes * self._nchannels * self._sampwidth
455 self._form_length_pos = self._file.tell()
Guido van Rossumeca576c2000-10-09 20:01:53 +0000456 self._file.write(struct.pack('<l4s4slhhllhh4s',
Guido van Rossume7b146f2000-02-04 15:28:42 +0000457 36 + self._datalength, 'WAVE', 'fmt ', 16,
458 WAVE_FORMAT_PCM, self._nchannels, self._framerate,
459 self._nchannels * self._framerate * self._sampwidth,
460 self._nchannels * self._sampwidth,
461 self._sampwidth * 8, 'data'))
462 self._data_length_pos = self._file.tell()
463 self._file.write(struct.pack('<l', self._datalength))
464
465 def _patchheader(self):
466 if self._datawritten == self._datalength:
467 return
468 curpos = self._file.tell()
469 self._file.seek(self._form_length_pos, 0)
470 self._file.write(struct.pack('<l', 36 + self._datawritten))
471 self._file.seek(self._data_length_pos, 0)
472 self._file.write(struct.pack('<l', self._datawritten))
473 self._file.seek(curpos, 0)
474 self._datalength = self._datawritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000475
Fred Drakef9607821999-06-17 15:18:47 +0000476def open(f, mode=None):
Guido van Rossume7b146f2000-02-04 15:28:42 +0000477 if mode is None:
478 if hasattr(f, 'mode'):
479 mode = f.mode
480 else:
481 mode = 'rb'
482 if mode in ('r', 'rb'):
483 return Wave_read(f)
484 elif mode in ('w', 'wb'):
485 return Wave_write(f)
486 else:
487 raise Error, "mode must be 'r', 'rb', 'w', or 'wb'"
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000488
489openfp = open # B/W compatibility