blob: 1c6380eee6538ec43340424b2ec5d0bf9b9919e6 [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)
R David Murray671cd322013-04-10 12:31:43 -040021 getparams() -- returns a namedtuple consisting of all of the
Guido van Rossume7b146f2000-02-04 15:28:42 +000022 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
Georg Brandl1a3284e2007-12-02 09:40:06 +000074import builtins
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000075
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 +000085import struct
Serhiy Storchakad739bda2013-05-29 23:38:00 +030086import sys
Guido van Rossum3601e881999-08-26 15:50:43 +000087from chunk import Chunk
R David Murray671cd322013-04-10 12:31:43 -040088from collections import namedtuple
89
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +030090_wave_params = namedtuple('_wave_params',
R David Murray671cd322013-04-10 12:31:43 -040091 'nchannels sampwidth framerate nframes comptype compname')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000092
93class Wave_read:
Guido van Rossume7b146f2000-02-04 15:28:42 +000094 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000095
Guido van Rossume7b146f2000-02-04 15:28:42 +000096 These variables are available to the user though appropriate
97 methods of this class:
98 _file -- the open file with methods read(), close(), and seek()
99 set through the __init__() method
100 _nchannels -- the number of audio channels
101 available through the getnchannels() method
102 _nframes -- the number of audio frames
103 available through the getnframes() method
104 _sampwidth -- the number of bytes per audio sample
105 available through the getsampwidth() method
106 _framerate -- the sampling frequency
107 available through the getframerate() method
108 _comptype -- the AIFF-C compression type ('NONE' if AIFF)
109 available through the getcomptype() method
110 _compname -- the human-readable AIFF-C compression type
111 available through the getcomptype() method
112 _soundpos -- the position in the audio stream
113 available through the tell() method, set through the
114 setpos() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000115
Guido van Rossume7b146f2000-02-04 15:28:42 +0000116 These variables are used internally only:
117 _fmt_chunk_read -- 1 iff the FMT chunk has been read
118 _data_seek_needed -- 1 iff positioned correctly in audio
119 file for readframes()
120 _data_chunk -- instantiation of a chunk class for the DATA chunk
121 _framesize -- size of one frame in the file
122 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000123
Guido van Rossume7b146f2000-02-04 15:28:42 +0000124 def initfp(self, file):
125 self._convert = None
126 self._soundpos = 0
127 self._file = Chunk(file, bigendian = 0)
Guido van Rossum51a883b2007-07-23 21:28:30 +0000128 if self._file.getname() != b'RIFF':
Collin Winterce36ad82007-08-30 01:19:48 +0000129 raise Error('file does not start with RIFF id')
Guido van Rossum51a883b2007-07-23 21:28:30 +0000130 if self._file.read(4) != b'WAVE':
Collin Winterce36ad82007-08-30 01:19:48 +0000131 raise Error('not a WAVE file')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000132 self._fmt_chunk_read = 0
133 self._data_chunk = None
134 while 1:
135 self._data_seek_needed = 1
136 try:
137 chunk = Chunk(self._file, bigendian = 0)
138 except EOFError:
139 break
140 chunkname = chunk.getname()
Guido van Rossum51a883b2007-07-23 21:28:30 +0000141 if chunkname == b'fmt ':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000142 self._read_fmt_chunk(chunk)
143 self._fmt_chunk_read = 1
Guido van Rossum51a883b2007-07-23 21:28:30 +0000144 elif chunkname == b'data':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000145 if not self._fmt_chunk_read:
Collin Winterce36ad82007-08-30 01:19:48 +0000146 raise Error('data chunk before fmt chunk')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000147 self._data_chunk = chunk
Guido van Rossum54e54c62001-09-04 19:14:14 +0000148 self._nframes = chunk.chunksize // self._framesize
Guido van Rossume7b146f2000-02-04 15:28:42 +0000149 self._data_seek_needed = 0
150 break
151 chunk.skip()
152 if not self._fmt_chunk_read or not self._data_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000153 raise Error('fmt chunk and/or data chunk missing')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000154
Guido van Rossume7b146f2000-02-04 15:28:42 +0000155 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000156 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000157 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000158 f = builtins.open(f, 'rb')
Tim Peterscfc41782000-10-09 23:43:55 +0000159 self._i_opened_the_file = f
Guido van Rossume7b146f2000-02-04 15:28:42 +0000160 # else, assume it is an open file object already
Guido van Rossumd8faa362007-04-27 19:54:29 +0000161 try:
162 self.initfp(f)
163 except:
164 if self._i_opened_the_file:
165 f.close()
166 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000167
Tim Peterscfc41782000-10-09 23:43:55 +0000168 def __del__(self):
169 self.close()
R David Murrayc91d5ee2013-07-31 13:46:08 -0400170
171 def __enter__(self):
172 return self
173
174 def __exit__(self, *args):
175 self.close()
176
Guido van Rossume7b146f2000-02-04 15:28:42 +0000177 #
178 # User visible methods.
179 #
180 def getfp(self):
181 return self._file
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000182
Guido van Rossume7b146f2000-02-04 15:28:42 +0000183 def rewind(self):
184 self._data_seek_needed = 1
185 self._soundpos = 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000186
Guido van Rossume7b146f2000-02-04 15:28:42 +0000187 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000188 if self._i_opened_the_file:
189 self._i_opened_the_file.close()
190 self._i_opened_the_file = None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000191 self._file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000192
Guido van Rossume7b146f2000-02-04 15:28:42 +0000193 def tell(self):
194 return self._soundpos
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000195
Guido van Rossume7b146f2000-02-04 15:28:42 +0000196 def getnchannels(self):
197 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000198
Guido van Rossume7b146f2000-02-04 15:28:42 +0000199 def getnframes(self):
200 return self._nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000201
Guido van Rossume7b146f2000-02-04 15:28:42 +0000202 def getsampwidth(self):
203 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000204
Guido van Rossume7b146f2000-02-04 15:28:42 +0000205 def getframerate(self):
206 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000207
Guido van Rossume7b146f2000-02-04 15:28:42 +0000208 def getcomptype(self):
209 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000210
Guido van Rossume7b146f2000-02-04 15:28:42 +0000211 def getcompname(self):
212 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000213
Guido van Rossume7b146f2000-02-04 15:28:42 +0000214 def getparams(self):
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300215 return _wave_params(self.getnchannels(), self.getsampwidth(),
R David Murray671cd322013-04-10 12:31:43 -0400216 self.getframerate(), self.getnframes(),
217 self.getcomptype(), self.getcompname())
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000218
Guido van Rossume7b146f2000-02-04 15:28:42 +0000219 def getmarkers(self):
220 return None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000221
Guido van Rossume7b146f2000-02-04 15:28:42 +0000222 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000223 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000224
Guido van Rossume7b146f2000-02-04 15:28:42 +0000225 def setpos(self, pos):
226 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000227 raise Error('position not in range')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000228 self._soundpos = pos
229 self._data_seek_needed = 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000230
Guido van Rossume7b146f2000-02-04 15:28:42 +0000231 def readframes(self, nframes):
232 if self._data_seek_needed:
233 self._data_chunk.seek(0, 0)
234 pos = self._soundpos * self._framesize
235 if pos:
236 self._data_chunk.seek(pos, 0)
237 self._data_seek_needed = 0
238 if nframes == 0:
Guido van Rossum51a883b2007-07-23 21:28:30 +0000239 return b''
Serhiy Storchakad739bda2013-05-29 23:38:00 +0300240 if self._sampwidth > 1 and sys.byteorder == 'big':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000241 # unfortunately the fromfile() method does not take
242 # something that only looks like a file object, so
243 # we have to reach into the innards of the chunk object
244 import array
245 chunk = self._data_chunk
246 data = array.array(_array_fmts[self._sampwidth])
247 nitems = nframes * self._nchannels
248 if nitems * self._sampwidth > chunk.chunksize - chunk.size_read:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000249 nitems = (chunk.chunksize - chunk.size_read) // self._sampwidth
Guido van Rossume7b146f2000-02-04 15:28:42 +0000250 data.fromfile(chunk.file.file, nitems)
251 # "tell" data chunk how much was read
252 chunk.size_read = chunk.size_read + nitems * self._sampwidth
253 # do the same for the outermost chunk
254 chunk = chunk.file
255 chunk.size_read = chunk.size_read + nitems * self._sampwidth
256 data.byteswap()
Antoine Pitrou1ce3eb52010-09-01 20:29:34 +0000257 data = data.tobytes()
Guido van Rossume7b146f2000-02-04 15:28:42 +0000258 else:
259 data = self._data_chunk.read(nframes * self._framesize)
260 if self._convert and data:
261 data = self._convert(data)
Guido van Rossum54e54c62001-09-04 19:14:14 +0000262 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000263 return data
264
265 #
266 # Internal methods.
267 #
268
269 def _read_fmt_chunk(self, chunk):
Jesus Ceae4b863982012-11-17 03:41:54 +0100270 wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000271 if wFormatTag == WAVE_FORMAT_PCM:
Jesus Ceae4b863982012-11-17 03:41:54 +0100272 sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
Guido van Rossum54e54c62001-09-04 19:14:14 +0000273 self._sampwidth = (sampwidth + 7) // 8
Guido van Rossume7b146f2000-02-04 15:28:42 +0000274 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000275 raise Error('unknown format: %r' % (wFormatTag,))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000276 self._framesize = self._nchannels * self._sampwidth
277 self._comptype = 'NONE'
278 self._compname = 'not compressed'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000279
280class Wave_write:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000281 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000282
Guido van Rossume7b146f2000-02-04 15:28:42 +0000283 These variables are user settable through appropriate methods
284 of this class:
285 _file -- the open file with methods write(), close(), tell(), seek()
286 set through the __init__() method
287 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
288 set through the setcomptype() or setparams() method
289 _compname -- the human-readable AIFF-C compression type
290 set through the setcomptype() or setparams() method
291 _nchannels -- the number of audio channels
292 set through the setnchannels() or setparams() method
293 _sampwidth -- the number of bytes per audio sample
294 set through the setsampwidth() or setparams() method
295 _framerate -- the sampling frequency
296 set through the setframerate() or setparams() method
297 _nframes -- the number of audio frames written to the header
298 set through the setnframes() or setparams() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000299
Guido van Rossume7b146f2000-02-04 15:28:42 +0000300 These variables are used internally only:
301 _datalength -- the size of the audio samples written to the header
302 _nframeswritten -- the number of frames actually written
303 _datawritten -- the size of the audio samples actually written
304 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000305
Guido van Rossume7b146f2000-02-04 15:28:42 +0000306 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000307 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000308 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000309 f = builtins.open(f, 'wb')
Tim Peterscfc41782000-10-09 23:43:55 +0000310 self._i_opened_the_file = f
Guido van Rossumd8faa362007-04-27 19:54:29 +0000311 try:
312 self.initfp(f)
313 except:
314 if self._i_opened_the_file:
315 f.close()
316 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000317
Guido van Rossume7b146f2000-02-04 15:28:42 +0000318 def initfp(self, file):
319 self._file = file
320 self._convert = None
321 self._nchannels = 0
322 self._sampwidth = 0
323 self._framerate = 0
324 self._nframes = 0
325 self._nframeswritten = 0
326 self._datawritten = 0
327 self._datalength = 0
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000328 self._headerwritten = False
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000329
Guido van Rossume7b146f2000-02-04 15:28:42 +0000330 def __del__(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000331 self.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000332
R David Murrayc91d5ee2013-07-31 13:46:08 -0400333 def __enter__(self):
334 return self
335
336 def __exit__(self, *args):
337 self.close()
338
Guido van Rossume7b146f2000-02-04 15:28:42 +0000339 #
340 # User visible methods.
341 #
342 def setnchannels(self, nchannels):
343 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000344 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000345 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000346 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000347 self._nchannels = nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000348
Guido van Rossume7b146f2000-02-04 15:28:42 +0000349 def getnchannels(self):
350 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000351 raise Error('number of channels not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000352 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000353
Guido van Rossume7b146f2000-02-04 15:28:42 +0000354 def setsampwidth(self, sampwidth):
355 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000356 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000357 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000358 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000359 self._sampwidth = sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000360
Guido van Rossume7b146f2000-02-04 15:28:42 +0000361 def getsampwidth(self):
362 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000363 raise Error('sample width not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000364 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000365
Guido van Rossume7b146f2000-02-04 15:28:42 +0000366 def setframerate(self, framerate):
367 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000368 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000369 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000370 raise Error('bad frame rate')
Mark Dickinson64a38c02010-08-28 17:22:16 +0000371 self._framerate = int(round(framerate))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000372
Guido van Rossume7b146f2000-02-04 15:28:42 +0000373 def getframerate(self):
374 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000375 raise Error('frame rate not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000376 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000377
Guido van Rossume7b146f2000-02-04 15:28:42 +0000378 def setnframes(self, nframes):
379 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000380 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000381 self._nframes = nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000382
Guido van Rossume7b146f2000-02-04 15:28:42 +0000383 def getnframes(self):
384 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000385
Guido van Rossume7b146f2000-02-04 15:28:42 +0000386 def setcomptype(self, comptype, compname):
387 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000388 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000389 if comptype not in ('NONE',):
Collin Winterce36ad82007-08-30 01:19:48 +0000390 raise Error('unsupported compression type')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000391 self._comptype = comptype
392 self._compname = compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000393
Guido van Rossume7b146f2000-02-04 15:28:42 +0000394 def getcomptype(self):
395 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000396
Guido van Rossume7b146f2000-02-04 15:28:42 +0000397 def getcompname(self):
398 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000399
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000400 def setparams(self, params):
401 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossume7b146f2000-02-04 15:28:42 +0000402 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000403 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000404 self.setnchannels(nchannels)
405 self.setsampwidth(sampwidth)
406 self.setframerate(framerate)
407 self.setnframes(nframes)
408 self.setcomptype(comptype, compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000409
Guido van Rossume7b146f2000-02-04 15:28:42 +0000410 def getparams(self):
411 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000412 raise Error('not all parameters set')
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300413 return _wave_params(self._nchannels, self._sampwidth, self._framerate,
R David Murray671cd322013-04-10 12:31:43 -0400414 self._nframes, self._comptype, self._compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000415
Guido van Rossume7b146f2000-02-04 15:28:42 +0000416 def setmark(self, id, pos, name):
Collin Winterce36ad82007-08-30 01:19:48 +0000417 raise Error('setmark() not supported')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000418
Guido van Rossume7b146f2000-02-04 15:28:42 +0000419 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000420 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000421
Guido van Rossume7b146f2000-02-04 15:28:42 +0000422 def getmarkers(self):
423 return None
Tim Peterse1190062001-01-15 03:34:38 +0000424
Guido van Rossume7b146f2000-02-04 15:28:42 +0000425 def tell(self):
426 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000427
Guido van Rossume7b146f2000-02-04 15:28:42 +0000428 def writeframesraw(self, data):
429 self._ensure_header_written(len(data))
Guido van Rossum54e54c62001-09-04 19:14:14 +0000430 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000431 if self._convert:
432 data = self._convert(data)
Serhiy Storchakad739bda2013-05-29 23:38:00 +0300433 if self._sampwidth > 1 and sys.byteorder == 'big':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000434 import array
435 data = array.array(_array_fmts[self._sampwidth], data)
436 data.byteswap()
437 data.tofile(self._file)
438 self._datawritten = self._datawritten + len(data) * self._sampwidth
439 else:
440 self._file.write(data)
441 self._datawritten = self._datawritten + len(data)
442 self._nframeswritten = self._nframeswritten + nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000443
Guido van Rossume7b146f2000-02-04 15:28:42 +0000444 def writeframes(self, data):
445 self.writeframesraw(data)
446 if self._datalength != self._datawritten:
447 self._patchheader()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000448
Guido van Rossume7b146f2000-02-04 15:28:42 +0000449 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000450 if self._file:
R David Murray536ffe12013-07-31 20:48:26 -0400451 try:
452 self._ensure_header_written(0)
453 if self._datalength != self._datawritten:
454 self._patchheader()
455 self._file.flush()
456 finally:
457 self._file = None
Tim Peterscfc41782000-10-09 23:43:55 +0000458 if self._i_opened_the_file:
459 self._i_opened_the_file.close()
460 self._i_opened_the_file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000461
Guido van Rossume7b146f2000-02-04 15:28:42 +0000462 #
463 # Internal methods.
464 #
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000465
Guido van Rossume7b146f2000-02-04 15:28:42 +0000466 def _ensure_header_written(self, datasize):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000467 if not self._headerwritten:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000468 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000469 raise Error('# channels not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000470 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000471 raise Error('sample width not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000472 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000473 raise Error('sampling rate not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000474 self._write_header(datasize)
475
476 def _write_header(self, initlength):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000477 assert not self._headerwritten
Guido van Rossum09549f42007-08-27 20:40:10 +0000478 self._file.write(b'RIFF')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000479 if not self._nframes:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000480 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000481 self._datalength = self._nframes * self._nchannels * self._sampwidth
482 self._form_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100483 self._file.write(struct.pack('<L4s4sLHHLLHH4s',
Victor Stinnerda9ec992010-12-28 13:26:42 +0000484 36 + self._datalength, b'WAVE', b'fmt ', 16,
Guido van Rossume7b146f2000-02-04 15:28:42 +0000485 WAVE_FORMAT_PCM, self._nchannels, self._framerate,
486 self._nchannels * self._framerate * self._sampwidth,
487 self._nchannels * self._sampwidth,
Victor Stinnerda9ec992010-12-28 13:26:42 +0000488 self._sampwidth * 8, b'data'))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000489 self._data_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100490 self._file.write(struct.pack('<L', self._datalength))
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000491 self._headerwritten = True
Guido van Rossume7b146f2000-02-04 15:28:42 +0000492
493 def _patchheader(self):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000494 assert self._headerwritten
Guido van Rossume7b146f2000-02-04 15:28:42 +0000495 if self._datawritten == self._datalength:
496 return
497 curpos = self._file.tell()
498 self._file.seek(self._form_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100499 self._file.write(struct.pack('<L', 36 + self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000500 self._file.seek(self._data_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100501 self._file.write(struct.pack('<L', self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000502 self._file.seek(curpos, 0)
503 self._datalength = self._datawritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000504
Fred Drakef9607821999-06-17 15:18:47 +0000505def open(f, mode=None):
Guido van Rossume7b146f2000-02-04 15:28:42 +0000506 if mode is None:
507 if hasattr(f, 'mode'):
508 mode = f.mode
509 else:
510 mode = 'rb'
511 if mode in ('r', 'rb'):
512 return Wave_read(f)
513 elif mode in ('w', 'wb'):
514 return Wave_write(f)
515 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000516 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000517
518openfp = open # B/W compatibility